diff --git a/docs/llms-ctx-full.txt b/docs/llms-ctx-full.txt index 1fe32d2..a5856fc 100644 --- a/docs/llms-ctx-full.txt +++ b/docs/llms-ctx-full.txt @@ -502,7 +502,101 @@ - `def ThemePicker(color, radii, shadows, font, mode, cls)` Theme picker component with configurable sections -"MonsterUI Scrollspy Example application" +"""MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components""" +from fasthtml.common import * +from monsterui.all import * +from datetime import datetime + +app, rt = fast_app(hdrs=Theme.blue.headers(daisy=True)) + +def TicketSteps(step): + return Steps( + LiStep("Submitted", data_content="๐Ÿ“", + cls=StepT.success if step > 0 else StepT.primary if step == 0 else StepT.neutral), + LiStep("In Review", data_content="๐Ÿ”Ž", + cls=StepT.success if step > 1 else StepT.primary if step == 1 else StepT.neutral), + LiStep("Processing", data_content="โš™๏ธ", + cls=StepT.success if step > 2 else StepT.primary if step == 2 else StepT.neutral), + LiStep("Resolved", data_content="โœ…", + cls=StepT.success if step > 3 else StepT.primary if step == 3 else StepT.neutral), + cls="w-full") + +def StatusBadge(status): + styles = {'high': AlertT.error, 'medium': AlertT.warning,'low': AlertT.info} + alert_type = styles.get(status, AlertT.info) + return Alert(f"{status.title()} Priority", cls=(alert_type,"w-32 shadow-sm")) + +def TicketCard(id, title, description, status, step, department): + return Card( + CardHeader( + DivFullySpaced( + Div(H3(f"#{id}", cls=TextT.muted), + H4(title), + cls='space-y-2'), + StatusBadge(status))), + CardBody( + P(description, cls=(TextT.muted, "mb-6")), + DividerSplit(cls="my-6"), + TicketSteps(step), + DividerSplit(cls="my-6"), + DivFullySpaced( + Div(Strong("Department"), + P(department), + cls=('space-y-3', TextPresets.muted_sm)), + Div(Strong("Last Updated"), + P(Time(datetime.now().strftime('%b %d, %H:%M'))), + cls=('space-y-3', TextPresets.muted_sm)), + Button("View Details", cls=ButtonT.primary), + cls='mt-6')), + cls=CardT.hover) + +def NewTicketModal(): + return Modal( + ModalHeader(H3("Create New Support Ticket")), + ModalBody( + Alert( + DivLAligned(UkIcon("info"), Span("Please provide as much detail as possible to help us assist you quickly.")), + cls=(AlertT.info,"mb-4")), + Form( + Grid(LabelInput("Title", id="title", placeholder="Brief description of your issue"), + LabelSelect(Options("IT Support", "HR", "Facilities", "Finance"), label="Department", id="department")), + LabelSelect(Options("Low", "Medium", "High"), label="Priority Level", id="priority"), + LabelTextArea("Description", id="description", placeholder="Please provide detailed information about your issue"), + DivRAligned( + Button("Cancel", cls=ButtonT.ghost, data_uk_toggle="target: #new-ticket"), + Button(Loading(cls=LoadingT.spinner), "Submit Ticket", cls=ButtonT.primary, data_uk_toggle="target: #success-toast; target: #new-ticket")), + cls='space-y-8')), + id="new-ticket") + +@rt +def index(): + tickets = [ + {'id': "TK-1001", 'title': "Cloud Storage Access Error", + 'description': "Unable to access cloud storage with persistent authorization errors. Multiple users affected across marketing department.", + 'status': 'high', 'step': 2, 'department': 'IT Support'}, + {'id': "TK-1002", 'title': "Email Integration Issue", + 'description': "Exchange server not syncing with mobile devices. Affecting external client communications.", + 'status': 'medium', 'step': 1, 'department': 'IT Support'}, + {'id': "TK-1003", 'title': "Office Equipment Setup", + 'description': "New department printer needs configuration and network integration. Required for upcoming client presentation.", + 'status': 'low', 'step': 0, 'department': 'Facilities'} + ] + + return Title("Help Desk Dashboard"), Container( + Section( + DivFullySpaced( + H2("Active Tickets"), + Button(UkIcon("plus-circle", cls="mr-2"), "New Ticket", cls=ButtonT.primary, data_uk_toggle="target: #new-ticket"), + cls='mb-8'), + Grid(*[TicketCard(**ticket) for ticket in tickets], cols=1), + cls="my-6"), + NewTicketModal(), + Toast(DivLAligned(UkIcon('check-circle', cls='mr-2'), "Ticket submitted successfully! Our team will review it shortly."), id="success-toast", alert_cls=AlertT.success, cls=(ToastHT.end, ToastVT.bottom)), + Loading(htmx_indicator=True, type=LoadingT.dots, cls="fixed top-0 right-0 m-4"), + cls="mx-auto max-w-7xl" + ) + +serve()"MonsterUI Scrollspy Example application" from fasthtml.common import * from monsterui.all import * @@ -731,234 +825,255 @@ def index(): gap=4,cols_xl=7,cols_lg=7,cols_md=1,cols_sm=1,cols_xs=1), cls=('space-y-4', ContainerT.xl)) -serve()"""MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components""" +serve()"""FrankenUI Mail Example built with MonsterUI (original design by ShadCN)""" + from fasthtml.common import * from monsterui.all import * +from fasthtml.svg import * +import pathlib, json from datetime import datetime -app, rt = fast_app(hdrs=Theme.blue.headers(daisy=True)) - -def TicketSteps(step): - return Steps( - LiStep("Submitted", data_content="๐Ÿ“", - cls=StepT.success if step > 0 else StepT.primary if step == 0 else StepT.neutral), - LiStep("In Review", data_content="๐Ÿ”Ž", - cls=StepT.success if step > 1 else StepT.primary if step == 1 else StepT.neutral), - LiStep("Processing", data_content="โš™๏ธ", - cls=StepT.success if step > 2 else StepT.primary if step == 2 else StepT.neutral), - LiStep("Resolved", data_content="โœ…", - cls=StepT.success if step > 3 else StepT.primary if step == 3 else StepT.neutral), - cls="w-full") - -def StatusBadge(status): - styles = {'high': AlertT.error, 'medium': AlertT.warning,'low': AlertT.info} - alert_type = styles.get(status, AlertT.info) - return Alert(f"{status.title()} Priority", cls=(alert_type,"w-32 shadow-sm")) - -def TicketCard(id, title, description, status, step, department): - return Card( - CardHeader( - DivFullySpaced( - Div(H3(f"#{id}", cls=TextT.muted), - H4(title), - cls='space-y-2'), - StatusBadge(status))), - CardBody( - P(description, cls=(TextT.muted, "mb-6")), - DividerSplit(cls="my-6"), - TicketSteps(step), - DividerSplit(cls="my-6"), - DivFullySpaced( - Div(Strong("Department"), - P(department), - cls=('space-y-3', TextPresets.muted_sm)), - Div(Strong("Last Updated"), - P(Time(datetime.now().strftime('%b %d, %H:%M'))), - cls=('space-y-3', TextPresets.muted_sm)), - Button("View Details", cls=ButtonT.primary), - cls='mt-6')), - cls=CardT.hover) - -def NewTicketModal(): - return Modal( - ModalHeader(H3("Create New Support Ticket")), - ModalBody( - Alert( - DivLAligned(UkIcon("info"), Span("Please provide as much detail as possible to help us assist you quickly.")), - cls=(AlertT.info,"mb-4")), - Form( - Grid(LabelInput("Title", id="title", placeholder="Brief description of your issue"), - LabelSelect(Options("IT Support", "HR", "Facilities", "Finance"), label="Department", id="department")), - LabelSelect(Options("Low", "Medium", "High"), label="Priority Level", id="priority"), - LabelTextArea("Description", id="description", placeholder="Please provide detailed information about your issue"), - DivRAligned( - Button("Cancel", cls=ButtonT.ghost, data_uk_toggle="target: #new-ticket"), - Button(Loading(cls=LoadingT.spinner), "Submit Ticket", cls=ButtonT.primary, data_uk_toggle="target: #success-toast; target: #new-ticket")), - cls='space-y-8')), - id="new-ticket") - -@rt -def index(): - tickets = [ - {'id': "TK-1001", 'title': "Cloud Storage Access Error", - 'description': "Unable to access cloud storage with persistent authorization errors. Multiple users affected across marketing department.", - 'status': 'high', 'step': 2, 'department': 'IT Support'}, - {'id': "TK-1002", 'title': "Email Integration Issue", - 'description': "Exchange server not syncing with mobile devices. Affecting external client communications.", - 'status': 'medium', 'step': 1, 'department': 'IT Support'}, - {'id': "TK-1003", 'title': "Office Equipment Setup", - 'description': "New department printer needs configuration and network integration. Required for upcoming client presentation.", - 'status': 'low', 'step': 0, 'department': 'Facilities'} - ] - - return Title("Help Desk Dashboard"), Container( - Section( - DivFullySpaced( - H2("Active Tickets"), - Button(UkIcon("plus-circle", cls="mr-2"), "New Ticket", cls=ButtonT.primary, data_uk_toggle="target: #new-ticket"), - cls='mb-8'), - Grid(*[TicketCard(**ticket) for ticket in tickets], cols=1), - cls="my-6"), - NewTicketModal(), - Toast(DivLAligned(UkIcon('check-circle', cls='mr-2'), "Ticket submitted successfully! Our team will review it shortly."), id="success-toast", alert_cls=AlertT.success, cls=(ToastHT.end, ToastVT.bottom)), - Loading(htmx_indicator=True, type=LoadingT.dots, cls="fixed top-0 right-0 m-4"), - cls="mx-auto max-w-7xl" - ) - -serve()"""FrankenUI Music Example build with MonsterUI (Original design by ShadCN)""" - -from fasthtml.common import * -from monsterui.all import * - app, rt = fast_app(hdrs=Theme.blue.headers()) -def MusicLi(t,hk=''): return Li(A(DivFullySpaced(t,P(hk,cls=TextPresets.muted_sm)))) +sidebar_group1 = (('home', 'Inbox', '128'), ('file-text', 'Drafts', '9'), (' arrow-up-right', 'Sent', ''), + ('ban', 'Junk', '23'), ('trash', 'Trash', ''), ('folder', 'Archive', '')) -music_items = [("About Music", "" ), - ("Preferences", "โŒ˜" ), - ("Hide Music" , "โŒ˜H" ), - ("Hide Others", "โ‡งโŒ˜H"), - ("Quit Music" , "โŒ˜Q" )] +sidebar_group2 = (('globe','Social','972'),('info','Updates','342'),('messages-square','Forums','128'), + ('shopping-cart','Shopping','8'),('shopping-bag','Promotions','21'),) -file_dd_items = [("New", ""), - ("Open Stream URL", "โŒ˜U"), - ("Close Window", "โŒ˜W"), - ("Library", ""), - ("Import", "โŒ˜O"), - ("Burn Playlist to Disc", ""), - ("Show in Finder", "โ‡งโŒ˜R"), - ("Convert", ""), - ("Page Setup", "Print")] +def MailSbLi(icon, title, cnt): + return Li(A(DivLAligned(Span(UkIcon(icon)),Span(title),P(cnt, cls=TextPresets.muted_sm)),href='#', cls='hover:bg-secondary p-4')) -edit_actions = [("Undo", "โŒ˜Z"), - ("Redo", "โ‡งโŒ˜Z"), - ("Cut", "โŒ˜X"), - ("Copy", "โŒ˜C"), - ("Paste", "โŒ˜V"), - ("Select All", "โŒ˜A"), - ("Deselect All", "โ‡งโŒ˜A")] +sidebar = NavContainer( + NavHeaderLi(H3("Email"), cls='p-3'), + Li(Select(map(Option, ('alicia@example.com','alicia@gmail.com', 'alicia@yahoo.com')))), + *[MailSbLi(i, t, c) for i, t, c in sidebar_group1], + Li(Hr()), + *[MailSbLi(i, t, c) for i, t, c in sidebar_group2], + cls='mt-3') -view_dd_data = ["Show Playing Next", "Show Lyrics", "Show Status Bar", "Hide Sidebar", "Enter Full Screen"] +mail_data = json.load(open(pathlib.Path('data/mail.json'))) +def format_date(date_str): + date_obj = datetime.fromisoformat(date_str) + return date_obj.strftime("%Y-%m-%d %I:%M %p") -music_headers = NavBar( - Button("Music", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("Music"),NavContainer(map(lambda x: MusicLi(*x), music_items)))), - Button("File", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("File"), NavContainer(map(lambda x: MusicLi(*x), file_dd_items)))), - Button("Edit", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("Edit")),NavContainer( - *map(lambda x: MusicLi(*x), edit_actions), - Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), - Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe")))))), - Button("View", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("View"),NavContainer(map(lambda x: MusicLi(x), view_dd_data)))), - ) +def MailItem(mail): + cls_base = 'relative rounded-lg border border-border p-3 text-sm hover:bg-secondary space-y-2' + cls = f"{cls_base} {'bg-muted' if mail == mail_data[0] else ''} {'tag-unread' if not mail['read'] else 'tag-mail'}" + return Li( + DivFullySpaced( + DivLAligned( + Strong(mail['name']), + Span(cls='flex h-2 w-2 rounded-full bg-blue-600') if not mail['read'] else ''), + Time(format_date(mail['date']), cls='text-xs')), + Small(mail['subject'], href=f"#mail-{mail['id']}"), + Div(mail['text'][:100] + '...', cls=TextPresets.muted_sm), + DivLAligned( + *[Label(A(label, href='#'), cls='uk-label-primary' if label == 'work' else '') for label in mail['labels']]), + cls=cls) +def MailList(mails): return Ul(cls='js-filter space-y-2 p-4 pt-0')(*[MailItem(mail) for mail in mails]) +def MailContent(): + return Div(cls='flex flex-col',uk_filter="target: .js-filter")( + Div(cls='flex px-4 py-2 ')( + H3('Inbox'), + TabContainer(Li(A("All Mail",href='#', role='button'),cls='uk-active', uk_filter_control="filter: .tag-mail"), + Li(A("Unread",href='#', role='button'), uk_filter_control="filter: .tag-unread"), + alt=True, cls='ml-auto max-w-40', )), + Div(cls='flex flex-1 flex-col')( + Div(cls='p-4')( + Div(cls='uk-inline w-full')( + Span(cls='uk-form-icon text-muted-foreground')(UkIcon('search')), + Input(placeholder='Search'))), + Div(cls='flex-1 overflow-y-auto max-h-[600px]')(MailList(mail_data)))) -# music_headers = NavBarContainer( -# NavBarLSide( -# NavBarNav( -# Li(A("Music"),NavBarNavContainer(map(lambda x: MusicLi(*x), music_items))), -# Li(A("File"), NavBarNavContainer(map(lambda x: MusicLi(*x), file_dd_items))), -# Li(A("Edit")), -# NavBarNavContainer( -# *map(lambda x: MusicLi(*x), edit_actions), -# Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), -# Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe"))))), -# Li(A("View"), -# NavBarNavContainer(map(lambda x: MusicLi(x), view_dd_data))), -# Li(A("Account"), -# NavBarNavContainer( -# NavHeaderLi("Switch Account"), -# *map(MusicLi, ("Andy", "Benoit", "Luis", "Manage Family", "Add Account"))))))) - +def IconNavItem(*d): return [Li(A(UkIcon(o[0],uk_tooltip=o[1]))) for o in d] +def IconNav(*c,cls=''): return Ul(cls=f'uk-iconnav {cls}')(*c) -def Album(title,artist): - img_url = 'https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp' - return Div( - Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=img_url)), - Div(cls='space-y-1')(Strong(title),P(artist,cls=TextT.muted))) - -listen_now_albums = (("Roar", "Catty Perry"), ("Feline on a Prayer", "Cat Jovi"),("Fur Elise", "Ludwig van Beethovpurr"),("Purrple Rain", "Prince's Cat")) +def MailDetailView(mail): + top_icons = [('folder','Archive'), ('ban','Move to junk'), ('trash','Move to trash')] + reply_icons = [('reply','Reply'), ('reply','Reply all'), ('forward','Forward')] + dropdown_items = ['Mark as unread', 'Star read', 'Add Label', 'Mute Thread'] + + return Container( + DivFullySpaced( + DivLAligned( + DivLAligned(*[UkIcon(o[0],uk_tooltip=o[1]) for o in top_icons]), + Div(UkIcon('clock', uk_tooltip='Snooze'), cls='pl-2'), + cls='space-x-2 divide-x divide-border'), + DivLAligned( + *[UkIcon(o[0],uk_tooltip=o[1]) for o in reply_icons], + Div(UkIcon('ellipsis-vertical',button=True)), + DropDownNavContainer(*map(lambda x: Li(A(x)), dropdown_items)))), + DivLAligned( + Span(mail['name'][:2], cls='flex h-10 w-10 items-center justify-center rounded-full bg-muted'), + Div(Strong(mail['name']), + Div(mail['subject']), + DivLAligned(P('Reply-To:'), A(mail['email'], href=f"mailto:{mail['email']}"), cls='space-x-1'), + P(Time(format_date(mail['date']))), + cls='space-y-1'+TextT.sm), + cls='m-4 space-x-4'), + DividerLine(), + P(mail['text'], cls=TextT.sm +'p-4'), + DividerLine(), + Div(TextArea(id='message', placeholder=f"Reply {mail['name']}"), + DivFullySpaced( + LabelSwitch('Mute this thread',id='mute'), + Button('Send', cls=ButtonT.primary)), + cls='space-y-4')) -made_for_you_albums = [("Like a Feline", "Catdonna"), - ("Livin' La Vida Purrda", "Ricky Catin"), - ("Meow Meow Rocket", "Elton Cat"), - ("Rolling in the Purr", "Catdelle"), - ("Purrs of Silence", "Cat Garfunkel"), - ("Meow Me Maybe", "Carly Rae Purrsen"),] +@rt +def index(): + return Title("Mail Example"),Container( + Grid(Div(sidebar, cls='col-span-1'), + Div(MailContent(), cls='col-span-2'), + Div(MailDetailView(mail_data[0]), cls='col-span-2'), + cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5, + gap=0, cls='flex-1'), + cls=('flex', ContainerT.xl)) -music_content = (Div(H3("Listen Now"), cls="mt-6 space-y-1"), - Subtitle("Top picks for you. Updated daily."), - DividerLine(), - Grid(*[Album(t,a) for t,a in listen_now_albums], cls='gap-8'), - Div(H3("Made for You"), cls="mt-6 space-y-1"), - Subtitle("Your personal playlists. Updated daily."), - DividerLine(), - Grid(*[Album(t,a) for t,a in made_for_you_albums], cols_xl=6)) +serve()"""FrankenUI Cards Example built with MonsterUI (original design by ShadCN)""" -tabs = TabContainer( - Li(A('Music', href='#'), cls='uk-active'), - Li(A('Podcasts', href='#')), - Li(A('Live', cls='opacity-50'), cls='uk-disabled'), - uk_switcher='connect: #component-nav; animation: uk-animation-fade', - alt=True) +from fasthtml.common import * +from fasthtml.components import Uk_input_tag +from fasthtml.svg import * +from monsterui.all import * +import calendar +from datetime import datetime -def podcast_tab(): - return Div( - Div(cls='space-y-3 mt-6')( - H3("New Episodes"), - Subtitle("Your favorite podcasts. Updated daily.")), - Div(cls="uk-placeholder flex h-[450px] items-center justify-center rounded-md mt-4",uk_placeholder=True)( - DivVStacked(cls="space-y-6")( - UkIcon("microphone", 3), - H4("No episodes added"), - Subtitle("You have not added any podcasts. Add one below."), - Button("Add Podcast", cls=ButtonT.primary)))) +app, rt = fast_app(hdrs=Theme.blue.headers()) -discoved_data = [("play-circle","Listen Now"), ("binoculars", "Browse"), ("rss","Radio")] -library_data = [("play-circle", "Playlists"), ("music", "Songs"), ("user", "Made for You"), ("users", "Artists"), ("bookmark", "Albums")] -playlists_data = [("library","Recently Added"), ("library","Recently Played")] +CreateAccount = Card( + Grid(Button(DivLAligned(UkIcon('github'),Div('Github'))),Button('Google')), + DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm), + LabelInput('Email', id='email', placeholder='m@example.com'), + LabelInput('Password', id='password',placeholder='Password', type='Password'), + header=(H3('Create an Account'),Subtitle('Enter your email below to create your account')), + footer=Button('Create Account',cls=(ButtonT.primary,'w-full'))) -def MusicSidebarLi(icon, text): return Li(A(DivLAligned(UkIcon(icon), P(text)))) -sidebar = NavContainer( - NavHeaderLi(H3("Discover")), *[MusicSidebarLi(*o) for o in discoved_data], - NavHeaderLi(H3("Library")), *[MusicSidebarLi(*o) for o in library_data], - NavHeaderLi(H3("Playlists")),*[MusicSidebarLi(*o) for o in playlists_data], - cls=(NavT.primary,'space-y-3','pl-8')) +PaypalSVG_data = "M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z" +AppleSVG_data = "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701" +Card1Svg = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(Rect(width="20", height="14", x="2", y="5", rx="2"),Path(d="M2 10h20")) +PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor")), +AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor")) + +PaymentMethod = Card( + Grid(Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary'), + Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20'), + Button(DivCentered(AppleSvg, "Apple"), cls='h-20')), + Form(LabelInput('Name', id='name', placeholder='John Doe'), + LabelInput('Card Number', id='card_number', placeholder='m@example.com'), + Grid(LabelSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'), + LabelSelect(*Options(*range(2024,2030),selected_idx=0), label='Year', id='expire_year'), + LabelInput('CVV', id='cvv',placeholder='CVV', cls='mt-0'))), + header=(H3('Payment Method'),Subtitle('Add a new payment method to your account.'))) + +area_opts = ('Team','Billing','Account','Deployment','Support') +severity_opts = ('Severity 1 (Highest)', 'Severity 2', 'Severity 3', 'Severity 4 (Lowest)') +ReportIssue = Card( + Grid(Div(LabelSelect(*Options(*area_opts), label='Area', id='area')), + Div(LabelSelect(*Options(*severity_opts),label='Severity',id='area'))), + LabelInput( label='Subject', id='subject', placeholder='I need help with'), + LabelTextArea( label='Description', id='description',placeholder='Please include all information relevant to your issue'), + Div(FormLabel('Tags', fr='#tags'), + Uk_input_tag(name="Tags",state="danger", value="Spam,Invalid", uk_cloak=True, id='tags')), + header=(H3('Report Issue'),Subtitle('What area are you having problems with?')), + footer = DivFullySpaced(Button('Cancel'), Button(cls=ButtonT.primary)('Submit'))) + +monster_desc ="Python-first beautifully designed components because you deserve to focus on features that matter and your app deserves to be beautiful from day one." +MonsterUI = Card(H4("Monster UI"), + Subtitle(monster_desc), + DivLAligned( + Div("Python"), + DivLAligned(UkIcon('star'),Div("20k"), cls='space-x-1'), + Div(datetime.now().strftime("%B %d, %Y")), + cls=('space-x-4',TextPresets.muted_sm))) + +def CookieTableRow(heading, description, active=False): + return Tr(Td(H5(heading)), + Td(P(description, cls=TextPresets.muted_sm)), + Td(Switch(checked=active))) + +CookieSettings = Card( + Table(Tbody( + CookieTableRow('Strictly Necessary', 'These cookies are essential in order to use the website and use its features.', True), + CookieTableRow('Functional Cookies', 'These cookies allow the website to provide personalized functionality.'), + CookieTableRow('Performance Cookies', 'These cookies help to improve the performance of the website.'))), + header=(H4('Cookie Settings'),Subtitle('Manage your cookie settings here.')), + footer=Button('Save Preferences', cls=(ButtonT.primary, 'w-full'))) + +team_members = [("Sofia Davis", "m@example.com", "Owner"),("Jackson Lee", "p@example.com", "Member"),] +def TeamMemberRow(name, email, role): + return DivFullySpaced( + DivLAligned( + DiceBearAvatar(name, 10,10), + Div(P(name, cls=(TextT.sm, TextT.medium)), + P(email, cls=TextPresets.muted_sm))), + Button(role, UkIcon('chevron-down', cls='ml-4')), + DropDownNavContainer(map(NavCloseLi, [ + A(Div('Viewer', NavSubtitle('Can view and comment.'))), + A(Div('Developer', NavSubtitle('Can view, comment and edit.'))), + A(Div('Billing', NavSubtitle('Can view, comment and manage billing.'))), + A(Div('Owner', NavSubtitle('Admin-level access to all resources.')))]))) + +TeamMembers = Card(*[TeamMemberRow(*member) for member in team_members], + header = (H4('Team Members'),Subtitle('Invite your team members to collaborate.'))) + +access_roles = ("Read and write access", "Read-only access") +team_members = [("Olivia Martin", "m@example.com", "Read and write access"), + ("Isabella Nguyen", "b@example.com", "Read-only access"), + ("Sofia Davis", "p@example.com", "Read-only access")] + +def TeamMemberRow(name, email, role): + return DivFullySpaced( + DivLAligned(DiceBearAvatar(name, 10,10), + Div(P(name, cls=(TextT.sm, TextT.medium)), + P(email, cls=TextPresets.muted_sm))), + Select(*Options(*access_roles, selected_idx=access_roles.index(role)))) + +ShareDocument = Card( + DivLAligned(Input(value='http://example.com/link/to/document'),Button('Copy link', cls='whitespace-nowrap')), + Divider(), + H4('People with access', cls=TextPresets.bold_sm), + *[TeamMemberRow(*member) for member in team_members], + header = (H4('Share this document'),Subtitle('Anyone with the link can view this document.'))) + +DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024')) + +section_content =(('bell','Everything',"Email digest, mentions & all activity."), + ('user',"Available","Only mentions and comments"), + ('ban', "Ignoring","Turn of all notifications")) + +def NotificationRow(icon, name, desc): + return Li(cls='-mx-1')(A(DivLAligned(UkIcon(icon),Div(P(name),P(desc, cls=TextPresets.muted_sm))))) + +Notifications = Card( + NavContainer( + *[NotificationRow(*row) for row in section_content], + cls=NavT.secondary), + header = (H4('Notification'),Subtitle('Choose what you want to be notified about.')), + body_cls='pt-0') + +TeamCard = Card( + DivLAligned( + DiceBearAvatar("Isaac Flath", h=24, w=24), + Div(H3("Isaac Flath"), P("Library Creator"))), + footer=DivFullySpaced( + DivHStacked(UkIcon("map-pin", height=16), P("Alexandria, VA")), + DivHStacked(*(UkIconLink(icon, height=16) for icon in ("mail", "linkedin", "github")))), + cls=CardT.hover) @rt def index(): - return Title("Music Example"),Container(music_headers, DividerSplit(), - Grid(sidebar, - Div(cls="col-span-4 border-l border-border")( - Div(cls="px-8 py-6")( - DivFullySpaced( - Div(cls="max-w-80")(tabs), - Button(cls=ButtonT.primary)(DivLAligned(UkIcon('circle-plus')),Div("Add music"))), - Ul(id="component-nav", cls="uk-switcher")( - Li(*music_content), - Li(podcast_tab())))), - cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5)) + return Title("Cards Example"),Container(Grid( + *map(Div,( + Div(PaymentMethod,CreateAccount, TeamCard, cls='space-y-4'), + Div(TeamMembers, ShareDocument,DateCard,Notifications, cls='space-y-4'), + Div(ReportIssue,MonsterUI,CookieSettings, cls='space-y-4'))), + cols_md=1, cols_lg=2, cols_xl=3)) serve()"""FrankenUI Playground Example built with MonsterUI (original design by ShadCN)""" @@ -1113,287 +1228,172 @@ def header_render(col): def cell_render(col, val): def _Td(*args,cls='', **kwargs): return Td(*args, cls=f'p-2 {cls}',**kwargs) match col: - case "Done": return _Td(shrink=True)(CheckboxX(selected=val)) - case "Task": return _Td(val, cls='uk-visible@s') # Hide on small screens - case "Title": return _Td(val, cls='font-medium', expand=True) - case "Status" | "Priority": return _Td(cls='uk-visible@m uk-text-nowrap capitalize')(Span(val)) - case "Actions": return _Td(task_dropdown(), shrink=True) - case _: raise ValueError(f"Unknown column: {col}") - -task_columns = ["Done", 'Task', 'Title', 'Status', 'Priority', 'Actions'] - -tasks_table = Div(cls='mt-4')( - TableFromDicts( - header_data=task_columns, - body_data=paginated_data, - body_cell_render=cell_render, - header_cell_render=header_render, - sortable=True, - cls=(TableT.responsive, TableT.sm, TableT.divider))) - - -def footer(): - total_pages = (len(data) + page_size - 1) // page_size - return DivFullySpaced( - Div('1 of 100 row(s) selected.', cls=TextPresets.muted_sm), - DivLAligned( - DivCentered(f'Page {current_page + 1} of {total_pages}', cls=TextT.sm), - DivLAligned(*[UkIconLink(icon=i, button=True) for i in ('chevrons-left', 'chevron-left', 'chevron-right', 'chevrons-right')]))) - -tasks_ui = Div(DivFullySpaced(DivLAligned(table_controls), cls='mt-8'), tasks_table, footer()) - -@rt -def index(): return Container(page_heading, tasks_ui, CreateTaskModal()) - -serve()"""FrankenUI Cards Example built with MonsterUI (original design by ShadCN)""" - -from fasthtml.common import * -from fasthtml.components import Uk_input_tag -from fasthtml.svg import * -from monsterui.all import * -import calendar -from datetime import datetime - -app, rt = fast_app(hdrs=Theme.blue.headers()) - -CreateAccount = Card( - Grid(Button(DivLAligned(UkIcon('github'),Div('Github'))),Button('Google')), - DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm), - LabelInput('Email', id='email', placeholder='m@example.com'), - LabelInput('Password', id='password',placeholder='Password', type='Password'), - header=(H3('Create an Account'),Subtitle('Enter your email below to create your account')), - footer=Button('Create Account',cls=(ButtonT.primary,'w-full'))) - -PaypalSVG_data = "M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z" -AppleSVG_data = "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701" -Card1Svg = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(Rect(width="20", height="14", x="2", y="5", rx="2"),Path(d="M2 10h20")) -PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor")), -AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor")) - -PaymentMethod = Card( - Grid(Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary'), - Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20'), - Button(DivCentered(AppleSvg, "Apple"), cls='h-20')), - Form(LabelInput('Name', id='name', placeholder='John Doe'), - LabelInput('Card Number', id='card_number', placeholder='m@example.com'), - Grid(LabelSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'), - LabelSelect(*Options(*range(2024,2030),selected_idx=0), label='Year', id='expire_year'), - LabelInput('CVV', id='cvv',placeholder='CVV', cls='mt-0'))), - header=(H3('Payment Method'),Subtitle('Add a new payment method to your account.'))) - -area_opts = ('Team','Billing','Account','Deployment','Support') -severity_opts = ('Severity 1 (Highest)', 'Severity 2', 'Severity 3', 'Severity 4 (Lowest)') -ReportIssue = Card( - Grid(Div(LabelSelect(*Options(*area_opts), label='Area', id='area')), - Div(LabelSelect(*Options(*severity_opts),label='Severity',id='area'))), - LabelInput( label='Subject', id='subject', placeholder='I need help with'), - LabelTextArea( label='Description', id='description',placeholder='Please include all information relevant to your issue'), - Div(FormLabel('Tags', fr='#tags'), - Uk_input_tag(name="Tags",state="danger", value="Spam,Invalid", uk_cloak=True, id='tags')), - header=(H3('Report Issue'),Subtitle('What area are you having problems with?')), - footer = DivFullySpaced(Button('Cancel'), Button(cls=ButtonT.primary)('Submit'))) - -monster_desc ="Python-first beautifully designed components because you deserve to focus on features that matter and your app deserves to be beautiful from day one." -MonsterUI = Card(H4("Monster UI"), - Subtitle(monster_desc), - DivLAligned( - Div("Python"), - DivLAligned(UkIcon('star'),Div("20k"), cls='space-x-1'), - Div(datetime.now().strftime("%B %d, %Y")), - cls=('space-x-4',TextPresets.muted_sm))) - -def CookieTableRow(heading, description, active=False): - return Tr(Td(H5(heading)), - Td(P(description, cls=TextPresets.muted_sm)), - Td(Switch(checked=active))) - -CookieSettings = Card( - Table(Tbody( - CookieTableRow('Strictly Necessary', 'These cookies are essential in order to use the website and use its features.', True), - CookieTableRow('Functional Cookies', 'These cookies allow the website to provide personalized functionality.'), - CookieTableRow('Performance Cookies', 'These cookies help to improve the performance of the website.'))), - header=(H4('Cookie Settings'),Subtitle('Manage your cookie settings here.')), - footer=Button('Save Preferences', cls=(ButtonT.primary, 'w-full'))) - -team_members = [("Sofia Davis", "m@example.com", "Owner"),("Jackson Lee", "p@example.com", "Member"),] -def TeamMemberRow(name, email, role): - return DivFullySpaced( - DivLAligned( - DiceBearAvatar(name, 10,10), - Div(P(name, cls=(TextT.sm, TextT.medium)), - P(email, cls=TextPresets.muted_sm))), - Button(role, UkIcon('chevron-down', cls='ml-4')), - DropDownNavContainer(map(NavCloseLi, [ - A(Div('Viewer', NavSubtitle('Can view and comment.'))), - A(Div('Developer', NavSubtitle('Can view, comment and edit.'))), - A(Div('Billing', NavSubtitle('Can view, comment and manage billing.'))), - A(Div('Owner', NavSubtitle('Admin-level access to all resources.')))]))) - -TeamMembers = Card(*[TeamMemberRow(*member) for member in team_members], - header = (H4('Team Members'),Subtitle('Invite your team members to collaborate.'))) - -access_roles = ("Read and write access", "Read-only access") -team_members = [("Olivia Martin", "m@example.com", "Read and write access"), - ("Isabella Nguyen", "b@example.com", "Read-only access"), - ("Sofia Davis", "p@example.com", "Read-only access")] - -def TeamMemberRow(name, email, role): - return DivFullySpaced( - DivLAligned(DiceBearAvatar(name, 10,10), - Div(P(name, cls=(TextT.sm, TextT.medium)), - P(email, cls=TextPresets.muted_sm))), - Select(*Options(*access_roles, selected_idx=access_roles.index(role)))) - -ShareDocument = Card( - DivLAligned(Input(value='http://example.com/link/to/document'),Button('Copy link', cls='whitespace-nowrap')), - Divider(), - H4('People with access', cls=TextPresets.bold_sm), - *[TeamMemberRow(*member) for member in team_members], - header = (H4('Share this document'),Subtitle('Anyone with the link can view this document.'))) + case "Done": return _Td(shrink=True)(CheckboxX(selected=val)) + case "Task": return _Td(val, cls='uk-visible@s') # Hide on small screens + case "Title": return _Td(val, cls='font-medium', expand=True) + case "Status" | "Priority": return _Td(cls='uk-visible@m uk-text-nowrap capitalize')(Span(val)) + case "Actions": return _Td(task_dropdown(), shrink=True) + case _: raise ValueError(f"Unknown column: {col}") -DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024')) +task_columns = ["Done", 'Task', 'Title', 'Status', 'Priority', 'Actions'] -section_content =(('bell','Everything',"Email digest, mentions & all activity."), - ('user',"Available","Only mentions and comments"), - ('ban', "Ignoring","Turn of all notifications")) +tasks_table = Div(cls='mt-4')( + TableFromDicts( + header_data=task_columns, + body_data=paginated_data, + body_cell_render=cell_render, + header_cell_render=header_render, + sortable=True, + cls=(TableT.responsive, TableT.sm, TableT.divider))) -def NotificationRow(icon, name, desc): - return Li(cls='-mx-1')(A(DivLAligned(UkIcon(icon),Div(P(name),P(desc, cls=TextPresets.muted_sm))))) -Notifications = Card( - NavContainer( - *[NotificationRow(*row) for row in section_content], - cls=NavT.secondary), - header = (H4('Notification'),Subtitle('Choose what you want to be notified about.')), - body_cls='pt-0') +def footer(): + total_pages = (len(data) + page_size - 1) // page_size + return DivFullySpaced( + Div('1 of 100 row(s) selected.', cls=TextPresets.muted_sm), + DivLAligned( + DivCentered(f'Page {current_page + 1} of {total_pages}', cls=TextT.sm), + DivLAligned(*[UkIconLink(icon=i, button=True) for i in ('chevrons-left', 'chevron-left', 'chevron-right', 'chevrons-right')]))) -TeamCard = Card( - DivLAligned( - DiceBearAvatar("Isaac Flath", h=24, w=24), - Div(H3("Isaac Flath"), P("Library Creator"))), - footer=DivFullySpaced( - DivHStacked(UkIcon("map-pin", height=16), P("Alexandria, VA")), - DivHStacked(*(UkIconLink(icon, height=16) for icon in ("mail", "linkedin", "github")))), - cls=CardT.hover) +tasks_ui = Div(DivFullySpaced(DivLAligned(table_controls), cls='mt-8'), tasks_table, footer()) @rt -def index(): - return Title("Cards Example"),Container(Grid( - *map(Div,( - Div(PaymentMethod,CreateAccount, TeamCard, cls='space-y-4'), - Div(TeamMembers, ShareDocument,DateCard,Notifications, cls='space-y-4'), - Div(ReportIssue,MonsterUI,CookieSettings, cls='space-y-4'))), - cols_md=1, cols_lg=2, cols_xl=3)) +def index(): return Container(page_heading, tasks_ui, CreateTaskModal()) -serve()"""FrankenUI Mail Example built with MonsterUI (original design by ShadCN)""" +serve()"""FrankenUI Music Example build with MonsterUI (Original design by ShadCN)""" from fasthtml.common import * from monsterui.all import * -from fasthtml.svg import * -import pathlib, json -from datetime import datetime - + app, rt = fast_app(hdrs=Theme.blue.headers()) -sidebar_group1 = (('home', 'Inbox', '128'), ('file-text', 'Drafts', '9'), (' arrow-up-right', 'Sent', ''), - ('ban', 'Junk', '23'), ('trash', 'Trash', ''), ('folder', 'Archive', '')) +def MusicLi(t,hk=''): return Li(A(DivFullySpaced(t,P(hk,cls=TextPresets.muted_sm)))) -sidebar_group2 = (('globe','Social','972'),('info','Updates','342'),('messages-square','Forums','128'), - ('shopping-cart','Shopping','8'),('shopping-bag','Promotions','21'),) +music_items = [("About Music", "" ), + ("Preferences", "โŒ˜" ), + ("Hide Music" , "โŒ˜H" ), + ("Hide Others", "โ‡งโŒ˜H"), + ("Quit Music" , "โŒ˜Q" )] -def MailSbLi(icon, title, cnt): - return Li(A(DivLAligned(Span(UkIcon(icon)),Span(title),P(cnt, cls=TextPresets.muted_sm)),href='#', cls='hover:bg-secondary p-4')) +file_dd_items = [("New", ""), + ("Open Stream URL", "โŒ˜U"), + ("Close Window", "โŒ˜W"), + ("Library", ""), + ("Import", "โŒ˜O"), + ("Burn Playlist to Disc", ""), + ("Show in Finder", "โ‡งโŒ˜R"), + ("Convert", ""), + ("Page Setup", "Print")] -sidebar = NavContainer( - NavHeaderLi(H3("Email"), cls='p-3'), - Li(Select(map(Option, ('alicia@example.com','alicia@gmail.com', 'alicia@yahoo.com')))), - *[MailSbLi(i, t, c) for i, t, c in sidebar_group1], - Li(Hr()), - *[MailSbLi(i, t, c) for i, t, c in sidebar_group2], - cls='mt-3') +edit_actions = [("Undo", "โŒ˜Z"), + ("Redo", "โ‡งโŒ˜Z"), + ("Cut", "โŒ˜X"), + ("Copy", "โŒ˜C"), + ("Paste", "โŒ˜V"), + ("Select All", "โŒ˜A"), + ("Deselect All", "โ‡งโŒ˜A")] -mail_data = json.load(open(pathlib.Path('data/mail.json'))) +view_dd_data = ["Show Playing Next", "Show Lyrics", "Show Status Bar", "Hide Sidebar", "Enter Full Screen"] -def format_date(date_str): - date_obj = datetime.fromisoformat(date_str) - return date_obj.strftime("%Y-%m-%d %I:%M %p") -def MailItem(mail): - cls_base = 'relative rounded-lg border border-border p-3 text-sm hover:bg-secondary space-y-2' - cls = f"{cls_base} {'bg-muted' if mail == mail_data[0] else ''} {'tag-unread' if not mail['read'] else 'tag-mail'}" +music_headers = NavBar( + Button("Music", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("Music"),NavContainer(map(lambda x: MusicLi(*x), music_items)))), + Button("File", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("File"), NavContainer(map(lambda x: MusicLi(*x), file_dd_items)))), + Button("Edit", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("Edit")),NavContainer( + *map(lambda x: MusicLi(*x), edit_actions), + Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), + Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe")))))), + Button("View", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("View"),NavContainer(map(lambda x: MusicLi(x), view_dd_data)))), + ) - return Li( - DivFullySpaced( - DivLAligned( - Strong(mail['name']), - Span(cls='flex h-2 w-2 rounded-full bg-blue-600') if not mail['read'] else ''), - Time(format_date(mail['date']), cls='text-xs')), - Small(mail['subject'], href=f"#mail-{mail['id']}"), - Div(mail['text'][:100] + '...', cls=TextPresets.muted_sm), - DivLAligned( - *[Label(A(label, href='#'), cls='uk-label-primary' if label == 'work' else '') for label in mail['labels']]), - cls=cls) -def MailList(mails): return Ul(cls='js-filter space-y-2 p-4 pt-0')(*[MailItem(mail) for mail in mails]) -def MailContent(): - return Div(cls='flex flex-col',uk_filter="target: .js-filter")( - Div(cls='flex px-4 py-2 ')( - H3('Inbox'), - TabContainer(Li(A("All Mail",href='#', role='button'),cls='uk-active', uk_filter_control="filter: .tag-mail"), - Li(A("Unread",href='#', role='button'), uk_filter_control="filter: .tag-unread"), - alt=True, cls='ml-auto max-w-40', )), - Div(cls='flex flex-1 flex-col')( - Div(cls='p-4')( - Div(cls='uk-inline w-full')( - Span(cls='uk-form-icon text-muted-foreground')(UkIcon('search')), - Input(placeholder='Search'))), - Div(cls='flex-1 overflow-y-auto max-h-[600px]')(MailList(mail_data)))) -def IconNavItem(*d): return [Li(A(UkIcon(o[0],uk_tooltip=o[1]))) for o in d] -def IconNav(*c,cls=''): return Ul(cls=f'uk-iconnav {cls}')(*c) +# music_headers = NavBarContainer( +# NavBarLSide( +# NavBarNav( +# Li(A("Music"),NavBarNavContainer(map(lambda x: MusicLi(*x), music_items))), +# Li(A("File"), NavBarNavContainer(map(lambda x: MusicLi(*x), file_dd_items))), +# Li(A("Edit")), +# NavBarNavContainer( +# *map(lambda x: MusicLi(*x), edit_actions), +# Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), +# Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe"))))), +# Li(A("View"), +# NavBarNavContainer(map(lambda x: MusicLi(x), view_dd_data))), +# Li(A("Account"), +# NavBarNavContainer( +# NavHeaderLi("Switch Account"), +# *map(MusicLi, ("Andy", "Benoit", "Luis", "Manage Family", "Add Account"))))))) -def MailDetailView(mail): - top_icons = [('folder','Archive'), ('ban','Move to junk'), ('trash','Move to trash')] - reply_icons = [('reply','Reply'), ('reply','Reply all'), ('forward','Forward')] - dropdown_items = ['Mark as unread', 'Star read', 'Add Label', 'Mute Thread'] - - return Container( - DivFullySpaced( - DivLAligned( - DivLAligned(*[UkIcon(o[0],uk_tooltip=o[1]) for o in top_icons]), - Div(UkIcon('clock', uk_tooltip='Snooze'), cls='pl-2'), - cls='space-x-2 divide-x divide-border'), - DivLAligned( - *[UkIcon(o[0],uk_tooltip=o[1]) for o in reply_icons], - Div(UkIcon('ellipsis-vertical',button=True)), - DropDownNavContainer(*map(lambda x: Li(A(x)), dropdown_items)))), - DivLAligned( - Span(mail['name'][:2], cls='flex h-10 w-10 items-center justify-center rounded-full bg-muted'), - Div(Strong(mail['name']), - Div(mail['subject']), - DivLAligned(P('Reply-To:'), A(mail['email'], href=f"mailto:{mail['email']}"), cls='space-x-1'), - P(Time(format_date(mail['date']))), - cls='space-y-1'+TextT.sm), - cls='m-4 space-x-4'), - DividerLine(), - P(mail['text'], cls=TextT.sm +'p-4'), - DividerLine(), - Div(TextArea(id='message', placeholder=f"Reply {mail['name']}"), - DivFullySpaced( - LabelSwitch('Mute this thread',id='mute'), - Button('Send', cls=ButtonT.primary)), - cls='space-y-4')) + +def Album(title,artist): + img_url = 'https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp' + return Div( + Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=img_url)), + Div(cls='space-y-1')(Strong(title),P(artist,cls=TextT.muted))) + +listen_now_albums = (("Roar", "Catty Perry"), ("Feline on a Prayer", "Cat Jovi"),("Fur Elise", "Ludwig van Beethovpurr"),("Purrple Rain", "Prince's Cat")) + +made_for_you_albums = [("Like a Feline", "Catdonna"), + ("Livin' La Vida Purrda", "Ricky Catin"), + ("Meow Meow Rocket", "Elton Cat"), + ("Rolling in the Purr", "Catdelle"), + ("Purrs of Silence", "Cat Garfunkel"), + ("Meow Me Maybe", "Carly Rae Purrsen"),] + +music_content = (Div(H3("Listen Now"), cls="mt-6 space-y-1"), + Subtitle("Top picks for you. Updated daily."), + DividerLine(), + Grid(*[Album(t,a) for t,a in listen_now_albums], cls='gap-8'), + Div(H3("Made for You"), cls="mt-6 space-y-1"), + Subtitle("Your personal playlists. Updated daily."), + DividerLine(), + Grid(*[Album(t,a) for t,a in made_for_you_albums], cols_xl=6)) + +tabs = TabContainer( + Li(A('Music', href='#'), cls='uk-active'), + Li(A('Podcasts', href='#')), + Li(A('Live', cls='opacity-50'), cls='uk-disabled'), + uk_switcher='connect: #component-nav; animation: uk-animation-fade', + alt=True) + +def podcast_tab(): + return Div( + Div(cls='space-y-3 mt-6')( + H3("New Episodes"), + Subtitle("Your favorite podcasts. Updated daily.")), + Div(cls="uk-placeholder flex h-[450px] items-center justify-center rounded-md mt-4",uk_placeholder=True)( + DivVStacked(cls="space-y-6")( + UkIcon("microphone", 3), + H4("No episodes added"), + Subtitle("You have not added any podcasts. Add one below."), + Button("Add Podcast", cls=ButtonT.primary)))) + +discoved_data = [("play-circle","Listen Now"), ("binoculars", "Browse"), ("rss","Radio")] +library_data = [("play-circle", "Playlists"), ("music", "Songs"), ("user", "Made for You"), ("users", "Artists"), ("bookmark", "Albums")] +playlists_data = [("library","Recently Added"), ("library","Recently Played")] + +def MusicSidebarLi(icon, text): return Li(A(DivLAligned(UkIcon(icon), P(text)))) +sidebar = NavContainer( + NavHeaderLi(H3("Discover")), *[MusicSidebarLi(*o) for o in discoved_data], + NavHeaderLi(H3("Library")), *[MusicSidebarLi(*o) for o in library_data], + NavHeaderLi(H3("Playlists")),*[MusicSidebarLi(*o) for o in playlists_data], + cls=(NavT.primary,'space-y-3','pl-8')) @rt def index(): - return Title("Mail Example"),Container( - Grid(Div(sidebar, cls='col-span-1'), - Div(MailContent(), cls='col-span-2'), - Div(MailDetailView(mail_data[0]), cls='col-span-2'), - cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5, - gap=0, cls='flex-1'), - cls=('flex', ContainerT.xl)) + return Title("Music Example"),Container(music_headers, DividerSplit(), + Grid(sidebar, + Div(cls="col-span-4 border-l border-border")( + Div(cls="px-8 py-6")( + DivFullySpaced( + Div(cls="max-w-80")(tabs), + Button(cls=ButtonT.primary)(DivLAligned(UkIcon('circle-plus')),Div("Add music"))), + Ul(id="component-nav", cls="uk-switcher")( + Li(*music_content), + Li(podcast_tab())))), + cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5)) serve()"""FrankenUI Forms Example built with MonsterUI (original design by ShadCN)""" diff --git a/docs/llms-ctx.txt b/docs/llms-ctx.txt index 5722d50..454bb9f 100644 --- a/docs/llms-ctx.txt +++ b/docs/llms-ctx.txt @@ -502,7 +502,101 @@ - `def ThemePicker(color, radii, shadows, font, mode, cls)` Theme picker component with configurable sections -"MonsterUI Scrollspy Example application" +"""MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components""" +from fasthtml.common import * +from monsterui.all import * +from datetime import datetime + +app, rt = fast_app(hdrs=Theme.blue.headers(daisy=True)) + +def TicketSteps(step): + return Steps( + LiStep("Submitted", data_content="๐Ÿ“", + cls=StepT.success if step > 0 else StepT.primary if step == 0 else StepT.neutral), + LiStep("In Review", data_content="๐Ÿ”Ž", + cls=StepT.success if step > 1 else StepT.primary if step == 1 else StepT.neutral), + LiStep("Processing", data_content="โš™๏ธ", + cls=StepT.success if step > 2 else StepT.primary if step == 2 else StepT.neutral), + LiStep("Resolved", data_content="โœ…", + cls=StepT.success if step > 3 else StepT.primary if step == 3 else StepT.neutral), + cls="w-full") + +def StatusBadge(status): + styles = {'high': AlertT.error, 'medium': AlertT.warning,'low': AlertT.info} + alert_type = styles.get(status, AlertT.info) + return Alert(f"{status.title()} Priority", cls=(alert_type,"w-32 shadow-sm")) + +def TicketCard(id, title, description, status, step, department): + return Card( + CardHeader( + DivFullySpaced( + Div(H3(f"#{id}", cls=TextT.muted), + H4(title), + cls='space-y-2'), + StatusBadge(status))), + CardBody( + P(description, cls=(TextT.muted, "mb-6")), + DividerSplit(cls="my-6"), + TicketSteps(step), + DividerSplit(cls="my-6"), + DivFullySpaced( + Div(Strong("Department"), + P(department), + cls=('space-y-3', TextPresets.muted_sm)), + Div(Strong("Last Updated"), + P(Time(datetime.now().strftime('%b %d, %H:%M'))), + cls=('space-y-3', TextPresets.muted_sm)), + Button("View Details", cls=ButtonT.primary), + cls='mt-6')), + cls=CardT.hover) + +def NewTicketModal(): + return Modal( + ModalHeader(H3("Create New Support Ticket")), + ModalBody( + Alert( + DivLAligned(UkIcon("info"), Span("Please provide as much detail as possible to help us assist you quickly.")), + cls=(AlertT.info,"mb-4")), + Form( + Grid(LabelInput("Title", id="title", placeholder="Brief description of your issue"), + LabelSelect(Options("IT Support", "HR", "Facilities", "Finance"), label="Department", id="department")), + LabelSelect(Options("Low", "Medium", "High"), label="Priority Level", id="priority"), + LabelTextArea("Description", id="description", placeholder="Please provide detailed information about your issue"), + DivRAligned( + Button("Cancel", cls=ButtonT.ghost, data_uk_toggle="target: #new-ticket"), + Button(Loading(cls=LoadingT.spinner), "Submit Ticket", cls=ButtonT.primary, data_uk_toggle="target: #success-toast; target: #new-ticket")), + cls='space-y-8')), + id="new-ticket") + +@rt +def index(): + tickets = [ + {'id': "TK-1001", 'title': "Cloud Storage Access Error", + 'description': "Unable to access cloud storage with persistent authorization errors. Multiple users affected across marketing department.", + 'status': 'high', 'step': 2, 'department': 'IT Support'}, + {'id': "TK-1002", 'title': "Email Integration Issue", + 'description': "Exchange server not syncing with mobile devices. Affecting external client communications.", + 'status': 'medium', 'step': 1, 'department': 'IT Support'}, + {'id': "TK-1003", 'title': "Office Equipment Setup", + 'description': "New department printer needs configuration and network integration. Required for upcoming client presentation.", + 'status': 'low', 'step': 0, 'department': 'Facilities'} + ] + + return Title("Help Desk Dashboard"), Container( + Section( + DivFullySpaced( + H2("Active Tickets"), + Button(UkIcon("plus-circle", cls="mr-2"), "New Ticket", cls=ButtonT.primary, data_uk_toggle="target: #new-ticket"), + cls='mb-8'), + Grid(*[TicketCard(**ticket) for ticket in tickets], cols=1), + cls="my-6"), + NewTicketModal(), + Toast(DivLAligned(UkIcon('check-circle', cls='mr-2'), "Ticket submitted successfully! Our team will review it shortly."), id="success-toast", alert_cls=AlertT.success, cls=(ToastHT.end, ToastVT.bottom)), + Loading(htmx_indicator=True, type=LoadingT.dots, cls="fixed top-0 right-0 m-4"), + cls="mx-auto max-w-7xl" + ) + +serve()"MonsterUI Scrollspy Example application" from fasthtml.common import * from monsterui.all import * @@ -731,234 +825,255 @@ def index(): gap=4,cols_xl=7,cols_lg=7,cols_md=1,cols_sm=1,cols_xs=1), cls=('space-y-4', ContainerT.xl)) -serve()"""MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components""" +serve()"""FrankenUI Mail Example built with MonsterUI (original design by ShadCN)""" + from fasthtml.common import * from monsterui.all import * +from fasthtml.svg import * +import pathlib, json from datetime import datetime -app, rt = fast_app(hdrs=Theme.blue.headers(daisy=True)) - -def TicketSteps(step): - return Steps( - LiStep("Submitted", data_content="๐Ÿ“", - cls=StepT.success if step > 0 else StepT.primary if step == 0 else StepT.neutral), - LiStep("In Review", data_content="๐Ÿ”Ž", - cls=StepT.success if step > 1 else StepT.primary if step == 1 else StepT.neutral), - LiStep("Processing", data_content="โš™๏ธ", - cls=StepT.success if step > 2 else StepT.primary if step == 2 else StepT.neutral), - LiStep("Resolved", data_content="โœ…", - cls=StepT.success if step > 3 else StepT.primary if step == 3 else StepT.neutral), - cls="w-full") - -def StatusBadge(status): - styles = {'high': AlertT.error, 'medium': AlertT.warning,'low': AlertT.info} - alert_type = styles.get(status, AlertT.info) - return Alert(f"{status.title()} Priority", cls=(alert_type,"w-32 shadow-sm")) - -def TicketCard(id, title, description, status, step, department): - return Card( - CardHeader( - DivFullySpaced( - Div(H3(f"#{id}", cls=TextT.muted), - H4(title), - cls='space-y-2'), - StatusBadge(status))), - CardBody( - P(description, cls=(TextT.muted, "mb-6")), - DividerSplit(cls="my-6"), - TicketSteps(step), - DividerSplit(cls="my-6"), - DivFullySpaced( - Div(Strong("Department"), - P(department), - cls=('space-y-3', TextPresets.muted_sm)), - Div(Strong("Last Updated"), - P(Time(datetime.now().strftime('%b %d, %H:%M'))), - cls=('space-y-3', TextPresets.muted_sm)), - Button("View Details", cls=ButtonT.primary), - cls='mt-6')), - cls=CardT.hover) - -def NewTicketModal(): - return Modal( - ModalHeader(H3("Create New Support Ticket")), - ModalBody( - Alert( - DivLAligned(UkIcon("info"), Span("Please provide as much detail as possible to help us assist you quickly.")), - cls=(AlertT.info,"mb-4")), - Form( - Grid(LabelInput("Title", id="title", placeholder="Brief description of your issue"), - LabelSelect(Options("IT Support", "HR", "Facilities", "Finance"), label="Department", id="department")), - LabelSelect(Options("Low", "Medium", "High"), label="Priority Level", id="priority"), - LabelTextArea("Description", id="description", placeholder="Please provide detailed information about your issue"), - DivRAligned( - Button("Cancel", cls=ButtonT.ghost, data_uk_toggle="target: #new-ticket"), - Button(Loading(cls=LoadingT.spinner), "Submit Ticket", cls=ButtonT.primary, data_uk_toggle="target: #success-toast; target: #new-ticket")), - cls='space-y-8')), - id="new-ticket") - -@rt -def index(): - tickets = [ - {'id': "TK-1001", 'title': "Cloud Storage Access Error", - 'description': "Unable to access cloud storage with persistent authorization errors. Multiple users affected across marketing department.", - 'status': 'high', 'step': 2, 'department': 'IT Support'}, - {'id': "TK-1002", 'title': "Email Integration Issue", - 'description': "Exchange server not syncing with mobile devices. Affecting external client communications.", - 'status': 'medium', 'step': 1, 'department': 'IT Support'}, - {'id': "TK-1003", 'title': "Office Equipment Setup", - 'description': "New department printer needs configuration and network integration. Required for upcoming client presentation.", - 'status': 'low', 'step': 0, 'department': 'Facilities'} - ] - - return Title("Help Desk Dashboard"), Container( - Section( - DivFullySpaced( - H2("Active Tickets"), - Button(UkIcon("plus-circle", cls="mr-2"), "New Ticket", cls=ButtonT.primary, data_uk_toggle="target: #new-ticket"), - cls='mb-8'), - Grid(*[TicketCard(**ticket) for ticket in tickets], cols=1), - cls="my-6"), - NewTicketModal(), - Toast(DivLAligned(UkIcon('check-circle', cls='mr-2'), "Ticket submitted successfully! Our team will review it shortly."), id="success-toast", alert_cls=AlertT.success, cls=(ToastHT.end, ToastVT.bottom)), - Loading(htmx_indicator=True, type=LoadingT.dots, cls="fixed top-0 right-0 m-4"), - cls="mx-auto max-w-7xl" - ) - -serve()"""FrankenUI Music Example build with MonsterUI (Original design by ShadCN)""" - -from fasthtml.common import * -from monsterui.all import * - app, rt = fast_app(hdrs=Theme.blue.headers()) -def MusicLi(t,hk=''): return Li(A(DivFullySpaced(t,P(hk,cls=TextPresets.muted_sm)))) +sidebar_group1 = (('home', 'Inbox', '128'), ('file-text', 'Drafts', '9'), (' arrow-up-right', 'Sent', ''), + ('ban', 'Junk', '23'), ('trash', 'Trash', ''), ('folder', 'Archive', '')) -music_items = [("About Music", "" ), - ("Preferences", "โŒ˜" ), - ("Hide Music" , "โŒ˜H" ), - ("Hide Others", "โ‡งโŒ˜H"), - ("Quit Music" , "โŒ˜Q" )] +sidebar_group2 = (('globe','Social','972'),('info','Updates','342'),('messages-square','Forums','128'), + ('shopping-cart','Shopping','8'),('shopping-bag','Promotions','21'),) -file_dd_items = [("New", ""), - ("Open Stream URL", "โŒ˜U"), - ("Close Window", "โŒ˜W"), - ("Library", ""), - ("Import", "โŒ˜O"), - ("Burn Playlist to Disc", ""), - ("Show in Finder", "โ‡งโŒ˜R"), - ("Convert", ""), - ("Page Setup", "Print")] +def MailSbLi(icon, title, cnt): + return Li(A(DivLAligned(Span(UkIcon(icon)),Span(title),P(cnt, cls=TextPresets.muted_sm)),href='#', cls='hover:bg-secondary p-4')) -edit_actions = [("Undo", "โŒ˜Z"), - ("Redo", "โ‡งโŒ˜Z"), - ("Cut", "โŒ˜X"), - ("Copy", "โŒ˜C"), - ("Paste", "โŒ˜V"), - ("Select All", "โŒ˜A"), - ("Deselect All", "โ‡งโŒ˜A")] +sidebar = NavContainer( + NavHeaderLi(H3("Email"), cls='p-3'), + Li(Select(map(Option, ('alicia@example.com','alicia@gmail.com', 'alicia@yahoo.com')))), + *[MailSbLi(i, t, c) for i, t, c in sidebar_group1], + Li(Hr()), + *[MailSbLi(i, t, c) for i, t, c in sidebar_group2], + cls='mt-3') -view_dd_data = ["Show Playing Next", "Show Lyrics", "Show Status Bar", "Hide Sidebar", "Enter Full Screen"] +mail_data = json.load(open(pathlib.Path('data/mail.json'))) +def format_date(date_str): + date_obj = datetime.fromisoformat(date_str) + return date_obj.strftime("%Y-%m-%d %I:%M %p") -music_headers = NavBar( - Button("Music", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("Music"),NavContainer(map(lambda x: MusicLi(*x), music_items)))), - Button("File", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("File"), NavContainer(map(lambda x: MusicLi(*x), file_dd_items)))), - Button("Edit", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("Edit")),NavContainer( - *map(lambda x: MusicLi(*x), edit_actions), - Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), - Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe")))))), - Button("View", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("View"),NavContainer(map(lambda x: MusicLi(x), view_dd_data)))), - ) +def MailItem(mail): + cls_base = 'relative rounded-lg border border-border p-3 text-sm hover:bg-secondary space-y-2' + cls = f"{cls_base} {'bg-muted' if mail == mail_data[0] else ''} {'tag-unread' if not mail['read'] else 'tag-mail'}" + return Li( + DivFullySpaced( + DivLAligned( + Strong(mail['name']), + Span(cls='flex h-2 w-2 rounded-full bg-blue-600') if not mail['read'] else ''), + Time(format_date(mail['date']), cls='text-xs')), + Small(mail['subject'], href=f"#mail-{mail['id']}"), + Div(mail['text'][:100] + '...', cls=TextPresets.muted_sm), + DivLAligned( + *[Label(A(label, href='#'), cls='uk-label-primary' if label == 'work' else '') for label in mail['labels']]), + cls=cls) +def MailList(mails): return Ul(cls='js-filter space-y-2 p-4 pt-0')(*[MailItem(mail) for mail in mails]) +def MailContent(): + return Div(cls='flex flex-col',uk_filter="target: .js-filter")( + Div(cls='flex px-4 py-2 ')( + H3('Inbox'), + TabContainer(Li(A("All Mail",href='#', role='button'),cls='uk-active', uk_filter_control="filter: .tag-mail"), + Li(A("Unread",href='#', role='button'), uk_filter_control="filter: .tag-unread"), + alt=True, cls='ml-auto max-w-40', )), + Div(cls='flex flex-1 flex-col')( + Div(cls='p-4')( + Div(cls='uk-inline w-full')( + Span(cls='uk-form-icon text-muted-foreground')(UkIcon('search')), + Input(placeholder='Search'))), + Div(cls='flex-1 overflow-y-auto max-h-[600px]')(MailList(mail_data)))) -# music_headers = NavBarContainer( -# NavBarLSide( -# NavBarNav( -# Li(A("Music"),NavBarNavContainer(map(lambda x: MusicLi(*x), music_items))), -# Li(A("File"), NavBarNavContainer(map(lambda x: MusicLi(*x), file_dd_items))), -# Li(A("Edit")), -# NavBarNavContainer( -# *map(lambda x: MusicLi(*x), edit_actions), -# Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), -# Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe"))))), -# Li(A("View"), -# NavBarNavContainer(map(lambda x: MusicLi(x), view_dd_data))), -# Li(A("Account"), -# NavBarNavContainer( -# NavHeaderLi("Switch Account"), -# *map(MusicLi, ("Andy", "Benoit", "Luis", "Manage Family", "Add Account"))))))) - +def IconNavItem(*d): return [Li(A(UkIcon(o[0],uk_tooltip=o[1]))) for o in d] +def IconNav(*c,cls=''): return Ul(cls=f'uk-iconnav {cls}')(*c) -def Album(title,artist): - img_url = 'https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp' - return Div( - Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=img_url)), - Div(cls='space-y-1')(Strong(title),P(artist,cls=TextT.muted))) - -listen_now_albums = (("Roar", "Catty Perry"), ("Feline on a Prayer", "Cat Jovi"),("Fur Elise", "Ludwig van Beethovpurr"),("Purrple Rain", "Prince's Cat")) +def MailDetailView(mail): + top_icons = [('folder','Archive'), ('ban','Move to junk'), ('trash','Move to trash')] + reply_icons = [('reply','Reply'), ('reply','Reply all'), ('forward','Forward')] + dropdown_items = ['Mark as unread', 'Star read', 'Add Label', 'Mute Thread'] + + return Container( + DivFullySpaced( + DivLAligned( + DivLAligned(*[UkIcon(o[0],uk_tooltip=o[1]) for o in top_icons]), + Div(UkIcon('clock', uk_tooltip='Snooze'), cls='pl-2'), + cls='space-x-2 divide-x divide-border'), + DivLAligned( + *[UkIcon(o[0],uk_tooltip=o[1]) for o in reply_icons], + Div(UkIcon('ellipsis-vertical',button=True)), + DropDownNavContainer(*map(lambda x: Li(A(x)), dropdown_items)))), + DivLAligned( + Span(mail['name'][:2], cls='flex h-10 w-10 items-center justify-center rounded-full bg-muted'), + Div(Strong(mail['name']), + Div(mail['subject']), + DivLAligned(P('Reply-To:'), A(mail['email'], href=f"mailto:{mail['email']}"), cls='space-x-1'), + P(Time(format_date(mail['date']))), + cls='space-y-1'+TextT.sm), + cls='m-4 space-x-4'), + DividerLine(), + P(mail['text'], cls=TextT.sm +'p-4'), + DividerLine(), + Div(TextArea(id='message', placeholder=f"Reply {mail['name']}"), + DivFullySpaced( + LabelSwitch('Mute this thread',id='mute'), + Button('Send', cls=ButtonT.primary)), + cls='space-y-4')) -made_for_you_albums = [("Like a Feline", "Catdonna"), - ("Livin' La Vida Purrda", "Ricky Catin"), - ("Meow Meow Rocket", "Elton Cat"), - ("Rolling in the Purr", "Catdelle"), - ("Purrs of Silence", "Cat Garfunkel"), - ("Meow Me Maybe", "Carly Rae Purrsen"),] +@rt +def index(): + return Title("Mail Example"),Container( + Grid(Div(sidebar, cls='col-span-1'), + Div(MailContent(), cls='col-span-2'), + Div(MailDetailView(mail_data[0]), cls='col-span-2'), + cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5, + gap=0, cls='flex-1'), + cls=('flex', ContainerT.xl)) -music_content = (Div(H3("Listen Now"), cls="mt-6 space-y-1"), - Subtitle("Top picks for you. Updated daily."), - DividerLine(), - Grid(*[Album(t,a) for t,a in listen_now_albums], cls='gap-8'), - Div(H3("Made for You"), cls="mt-6 space-y-1"), - Subtitle("Your personal playlists. Updated daily."), - DividerLine(), - Grid(*[Album(t,a) for t,a in made_for_you_albums], cols_xl=6)) +serve()"""FrankenUI Cards Example built with MonsterUI (original design by ShadCN)""" -tabs = TabContainer( - Li(A('Music', href='#'), cls='uk-active'), - Li(A('Podcasts', href='#')), - Li(A('Live', cls='opacity-50'), cls='uk-disabled'), - uk_switcher='connect: #component-nav; animation: uk-animation-fade', - alt=True) +from fasthtml.common import * +from fasthtml.components import Uk_input_tag +from fasthtml.svg import * +from monsterui.all import * +import calendar +from datetime import datetime -def podcast_tab(): - return Div( - Div(cls='space-y-3 mt-6')( - H3("New Episodes"), - Subtitle("Your favorite podcasts. Updated daily.")), - Div(cls="uk-placeholder flex h-[450px] items-center justify-center rounded-md mt-4",uk_placeholder=True)( - DivVStacked(cls="space-y-6")( - UkIcon("microphone", 3), - H4("No episodes added"), - Subtitle("You have not added any podcasts. Add one below."), - Button("Add Podcast", cls=ButtonT.primary)))) +app, rt = fast_app(hdrs=Theme.blue.headers()) -discoved_data = [("play-circle","Listen Now"), ("binoculars", "Browse"), ("rss","Radio")] -library_data = [("play-circle", "Playlists"), ("music", "Songs"), ("user", "Made for You"), ("users", "Artists"), ("bookmark", "Albums")] -playlists_data = [("library","Recently Added"), ("library","Recently Played")] +CreateAccount = Card( + Grid(Button(DivLAligned(UkIcon('github'),Div('Github'))),Button('Google')), + DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm), + LabelInput('Email', id='email', placeholder='m@example.com'), + LabelInput('Password', id='password',placeholder='Password', type='Password'), + header=(H3('Create an Account'),Subtitle('Enter your email below to create your account')), + footer=Button('Create Account',cls=(ButtonT.primary,'w-full'))) -def MusicSidebarLi(icon, text): return Li(A(DivLAligned(UkIcon(icon), P(text)))) -sidebar = NavContainer( - NavHeaderLi(H3("Discover")), *[MusicSidebarLi(*o) for o in discoved_data], - NavHeaderLi(H3("Library")), *[MusicSidebarLi(*o) for o in library_data], - NavHeaderLi(H3("Playlists")),*[MusicSidebarLi(*o) for o in playlists_data], - cls=(NavT.primary,'space-y-3','pl-8')) +PaypalSVG_data = "M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z" +AppleSVG_data = "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701" +Card1Svg = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(Rect(width="20", height="14", x="2", y="5", rx="2"),Path(d="M2 10h20")) +PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor")), +AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor")) + +PaymentMethod = Card( + Grid(Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary'), + Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20'), + Button(DivCentered(AppleSvg, "Apple"), cls='h-20')), + Form(LabelInput('Name', id='name', placeholder='John Doe'), + LabelInput('Card Number', id='card_number', placeholder='m@example.com'), + Grid(LabelSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'), + LabelSelect(*Options(*range(2024,2030),selected_idx=0), label='Year', id='expire_year'), + LabelInput('CVV', id='cvv',placeholder='CVV', cls='mt-0'))), + header=(H3('Payment Method'),Subtitle('Add a new payment method to your account.'))) + +area_opts = ('Team','Billing','Account','Deployment','Support') +severity_opts = ('Severity 1 (Highest)', 'Severity 2', 'Severity 3', 'Severity 4 (Lowest)') +ReportIssue = Card( + Grid(Div(LabelSelect(*Options(*area_opts), label='Area', id='area')), + Div(LabelSelect(*Options(*severity_opts),label='Severity',id='area'))), + LabelInput( label='Subject', id='subject', placeholder='I need help with'), + LabelTextArea( label='Description', id='description',placeholder='Please include all information relevant to your issue'), + Div(FormLabel('Tags', fr='#tags'), + Uk_input_tag(name="Tags",state="danger", value="Spam,Invalid", uk_cloak=True, id='tags')), + header=(H3('Report Issue'),Subtitle('What area are you having problems with?')), + footer = DivFullySpaced(Button('Cancel'), Button(cls=ButtonT.primary)('Submit'))) + +monster_desc ="Python-first beautifully designed components because you deserve to focus on features that matter and your app deserves to be beautiful from day one." +MonsterUI = Card(H4("Monster UI"), + Subtitle(monster_desc), + DivLAligned( + Div("Python"), + DivLAligned(UkIcon('star'),Div("20k"), cls='space-x-1'), + Div(datetime.now().strftime("%B %d, %Y")), + cls=('space-x-4',TextPresets.muted_sm))) + +def CookieTableRow(heading, description, active=False): + return Tr(Td(H5(heading)), + Td(P(description, cls=TextPresets.muted_sm)), + Td(Switch(checked=active))) + +CookieSettings = Card( + Table(Tbody( + CookieTableRow('Strictly Necessary', 'These cookies are essential in order to use the website and use its features.', True), + CookieTableRow('Functional Cookies', 'These cookies allow the website to provide personalized functionality.'), + CookieTableRow('Performance Cookies', 'These cookies help to improve the performance of the website.'))), + header=(H4('Cookie Settings'),Subtitle('Manage your cookie settings here.')), + footer=Button('Save Preferences', cls=(ButtonT.primary, 'w-full'))) + +team_members = [("Sofia Davis", "m@example.com", "Owner"),("Jackson Lee", "p@example.com", "Member"),] +def TeamMemberRow(name, email, role): + return DivFullySpaced( + DivLAligned( + DiceBearAvatar(name, 10,10), + Div(P(name, cls=(TextT.sm, TextT.medium)), + P(email, cls=TextPresets.muted_sm))), + Button(role, UkIcon('chevron-down', cls='ml-4')), + DropDownNavContainer(map(NavCloseLi, [ + A(Div('Viewer', NavSubtitle('Can view and comment.'))), + A(Div('Developer', NavSubtitle('Can view, comment and edit.'))), + A(Div('Billing', NavSubtitle('Can view, comment and manage billing.'))), + A(Div('Owner', NavSubtitle('Admin-level access to all resources.')))]))) + +TeamMembers = Card(*[TeamMemberRow(*member) for member in team_members], + header = (H4('Team Members'),Subtitle('Invite your team members to collaborate.'))) + +access_roles = ("Read and write access", "Read-only access") +team_members = [("Olivia Martin", "m@example.com", "Read and write access"), + ("Isabella Nguyen", "b@example.com", "Read-only access"), + ("Sofia Davis", "p@example.com", "Read-only access")] + +def TeamMemberRow(name, email, role): + return DivFullySpaced( + DivLAligned(DiceBearAvatar(name, 10,10), + Div(P(name, cls=(TextT.sm, TextT.medium)), + P(email, cls=TextPresets.muted_sm))), + Select(*Options(*access_roles, selected_idx=access_roles.index(role)))) + +ShareDocument = Card( + DivLAligned(Input(value='http://example.com/link/to/document'),Button('Copy link', cls='whitespace-nowrap')), + Divider(), + H4('People with access', cls=TextPresets.bold_sm), + *[TeamMemberRow(*member) for member in team_members], + header = (H4('Share this document'),Subtitle('Anyone with the link can view this document.'))) + +DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024')) + +section_content =(('bell','Everything',"Email digest, mentions & all activity."), + ('user',"Available","Only mentions and comments"), + ('ban', "Ignoring","Turn of all notifications")) + +def NotificationRow(icon, name, desc): + return Li(cls='-mx-1')(A(DivLAligned(UkIcon(icon),Div(P(name),P(desc, cls=TextPresets.muted_sm))))) + +Notifications = Card( + NavContainer( + *[NotificationRow(*row) for row in section_content], + cls=NavT.secondary), + header = (H4('Notification'),Subtitle('Choose what you want to be notified about.')), + body_cls='pt-0') + +TeamCard = Card( + DivLAligned( + DiceBearAvatar("Isaac Flath", h=24, w=24), + Div(H3("Isaac Flath"), P("Library Creator"))), + footer=DivFullySpaced( + DivHStacked(UkIcon("map-pin", height=16), P("Alexandria, VA")), + DivHStacked(*(UkIconLink(icon, height=16) for icon in ("mail", "linkedin", "github")))), + cls=CardT.hover) @rt def index(): - return Title("Music Example"),Container(music_headers, DividerSplit(), - Grid(sidebar, - Div(cls="col-span-4 border-l border-border")( - Div(cls="px-8 py-6")( - DivFullySpaced( - Div(cls="max-w-80")(tabs), - Button(cls=ButtonT.primary)(DivLAligned(UkIcon('circle-plus')),Div("Add music"))), - Ul(id="component-nav", cls="uk-switcher")( - Li(*music_content), - Li(podcast_tab())))), - cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5)) + return Title("Cards Example"),Container(Grid( + *map(Div,( + Div(PaymentMethod,CreateAccount, TeamCard, cls='space-y-4'), + Div(TeamMembers, ShareDocument,DateCard,Notifications, cls='space-y-4'), + Div(ReportIssue,MonsterUI,CookieSettings, cls='space-y-4'))), + cols_md=1, cols_lg=2, cols_xl=3)) serve()"""FrankenUI Playground Example built with MonsterUI (original design by ShadCN)""" @@ -1113,287 +1228,172 @@ def header_render(col): def cell_render(col, val): def _Td(*args,cls='', **kwargs): return Td(*args, cls=f'p-2 {cls}',**kwargs) match col: - case "Done": return _Td(shrink=True)(CheckboxX(selected=val)) - case "Task": return _Td(val, cls='uk-visible@s') # Hide on small screens - case "Title": return _Td(val, cls='font-medium', expand=True) - case "Status" | "Priority": return _Td(cls='uk-visible@m uk-text-nowrap capitalize')(Span(val)) - case "Actions": return _Td(task_dropdown(), shrink=True) - case _: raise ValueError(f"Unknown column: {col}") - -task_columns = ["Done", 'Task', 'Title', 'Status', 'Priority', 'Actions'] - -tasks_table = Div(cls='mt-4')( - TableFromDicts( - header_data=task_columns, - body_data=paginated_data, - body_cell_render=cell_render, - header_cell_render=header_render, - sortable=True, - cls=(TableT.responsive, TableT.sm, TableT.divider))) - - -def footer(): - total_pages = (len(data) + page_size - 1) // page_size - return DivFullySpaced( - Div('1 of 100 row(s) selected.', cls=TextPresets.muted_sm), - DivLAligned( - DivCentered(f'Page {current_page + 1} of {total_pages}', cls=TextT.sm), - DivLAligned(*[UkIconLink(icon=i, button=True) for i in ('chevrons-left', 'chevron-left', 'chevron-right', 'chevrons-right')]))) - -tasks_ui = Div(DivFullySpaced(DivLAligned(table_controls), cls='mt-8'), tasks_table, footer()) - -@rt -def index(): return Container(page_heading, tasks_ui, CreateTaskModal()) - -serve()"""FrankenUI Cards Example built with MonsterUI (original design by ShadCN)""" - -from fasthtml.common import * -from fasthtml.components import Uk_input_tag -from fasthtml.svg import * -from monsterui.all import * -import calendar -from datetime import datetime - -app, rt = fast_app(hdrs=Theme.blue.headers()) - -CreateAccount = Card( - Grid(Button(DivLAligned(UkIcon('github'),Div('Github'))),Button('Google')), - DividerSplit("OR CONTINUE WITH", text_cls=TextPresets.muted_sm), - LabelInput('Email', id='email', placeholder='m@example.com'), - LabelInput('Password', id='password',placeholder='Password', type='Password'), - header=(H3('Create an Account'),Subtitle('Enter your email below to create your account')), - footer=Button('Create Account',cls=(ButtonT.primary,'w-full'))) - -PaypalSVG_data = "M7.076 21.337H2.47a.641.641 0 0 1-.633-.74L4.944.901C5.026.382 5.474 0 5.998 0h7.46c2.57 0 4.578.543 5.69 1.81 1.01 1.15 1.304 2.42 1.012 4.287-.023.143-.047.288-.077.437-.983 5.05-4.349 6.797-8.647 6.797h-2.19c-.524 0-.968.382-1.05.9l-1.12 7.106zm14.146-14.42a3.35 3.35 0 0 0-.607-.541c-.013.076-.026.175-.041.254-.93 4.778-4.005 7.201-9.138 7.201h-2.19a.563.563 0 0 0-.556.479l-1.187 7.527h-.506l-.24 1.516a.56.56 0 0 0 .554.647h3.882c.46 0 .85-.334.922-.788.06-.26.76-4.852.816-5.09a.932.932 0 0 1 .923-.788h.58c3.76 0 6.705-1.528 7.565-5.946.36-1.847.174-3.388-.777-4.471z" -AppleSVG_data = "M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.546 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701" -Card1Svg = Svg(viewBox="0 0 24 24", fill="none", stroke="currentColor", stroke_linecap="round", stroke_linejoin="round", stroke_width="2", cls="h-6 w-6 mr-1")(Rect(width="20", height="14", x="2", y="5", rx="2"),Path(d="M2 10h20")) -PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=PaypalSVG_data, fill="currentColor")), -AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d=AppleSVG_data, fill="currentColor")) - -PaymentMethod = Card( - Grid(Button(DivCentered(Card1Svg, "Card"), cls='h-20 border-2 border-primary'), - Button(DivCentered(PaypalSvg, "PayPal"), cls='h-20'), - Button(DivCentered(AppleSvg, "Apple"), cls='h-20')), - Form(LabelInput('Name', id='name', placeholder='John Doe'), - LabelInput('Card Number', id='card_number', placeholder='m@example.com'), - Grid(LabelSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'), - LabelSelect(*Options(*range(2024,2030),selected_idx=0), label='Year', id='expire_year'), - LabelInput('CVV', id='cvv',placeholder='CVV', cls='mt-0'))), - header=(H3('Payment Method'),Subtitle('Add a new payment method to your account.'))) - -area_opts = ('Team','Billing','Account','Deployment','Support') -severity_opts = ('Severity 1 (Highest)', 'Severity 2', 'Severity 3', 'Severity 4 (Lowest)') -ReportIssue = Card( - Grid(Div(LabelSelect(*Options(*area_opts), label='Area', id='area')), - Div(LabelSelect(*Options(*severity_opts),label='Severity',id='area'))), - LabelInput( label='Subject', id='subject', placeholder='I need help with'), - LabelTextArea( label='Description', id='description',placeholder='Please include all information relevant to your issue'), - Div(FormLabel('Tags', fr='#tags'), - Uk_input_tag(name="Tags",state="danger", value="Spam,Invalid", uk_cloak=True, id='tags')), - header=(H3('Report Issue'),Subtitle('What area are you having problems with?')), - footer = DivFullySpaced(Button('Cancel'), Button(cls=ButtonT.primary)('Submit'))) - -monster_desc ="Python-first beautifully designed components because you deserve to focus on features that matter and your app deserves to be beautiful from day one." -MonsterUI = Card(H4("Monster UI"), - Subtitle(monster_desc), - DivLAligned( - Div("Python"), - DivLAligned(UkIcon('star'),Div("20k"), cls='space-x-1'), - Div(datetime.now().strftime("%B %d, %Y")), - cls=('space-x-4',TextPresets.muted_sm))) - -def CookieTableRow(heading, description, active=False): - return Tr(Td(H5(heading)), - Td(P(description, cls=TextPresets.muted_sm)), - Td(Switch(checked=active))) - -CookieSettings = Card( - Table(Tbody( - CookieTableRow('Strictly Necessary', 'These cookies are essential in order to use the website and use its features.', True), - CookieTableRow('Functional Cookies', 'These cookies allow the website to provide personalized functionality.'), - CookieTableRow('Performance Cookies', 'These cookies help to improve the performance of the website.'))), - header=(H4('Cookie Settings'),Subtitle('Manage your cookie settings here.')), - footer=Button('Save Preferences', cls=(ButtonT.primary, 'w-full'))) - -team_members = [("Sofia Davis", "m@example.com", "Owner"),("Jackson Lee", "p@example.com", "Member"),] -def TeamMemberRow(name, email, role): - return DivFullySpaced( - DivLAligned( - DiceBearAvatar(name, 10,10), - Div(P(name, cls=(TextT.sm, TextT.medium)), - P(email, cls=TextPresets.muted_sm))), - Button(role, UkIcon('chevron-down', cls='ml-4')), - DropDownNavContainer(map(NavCloseLi, [ - A(Div('Viewer', NavSubtitle('Can view and comment.'))), - A(Div('Developer', NavSubtitle('Can view, comment and edit.'))), - A(Div('Billing', NavSubtitle('Can view, comment and manage billing.'))), - A(Div('Owner', NavSubtitle('Admin-level access to all resources.')))]))) - -TeamMembers = Card(*[TeamMemberRow(*member) for member in team_members], - header = (H4('Team Members'),Subtitle('Invite your team members to collaborate.'))) - -access_roles = ("Read and write access", "Read-only access") -team_members = [("Olivia Martin", "m@example.com", "Read and write access"), - ("Isabella Nguyen", "b@example.com", "Read-only access"), - ("Sofia Davis", "p@example.com", "Read-only access")] - -def TeamMemberRow(name, email, role): - return DivFullySpaced( - DivLAligned(DiceBearAvatar(name, 10,10), - Div(P(name, cls=(TextT.sm, TextT.medium)), - P(email, cls=TextPresets.muted_sm))), - Select(*Options(*access_roles, selected_idx=access_roles.index(role)))) - -ShareDocument = Card( - DivLAligned(Input(value='http://example.com/link/to/document'),Button('Copy link', cls='whitespace-nowrap')), - Divider(), - H4('People with access', cls=TextPresets.bold_sm), - *[TeamMemberRow(*member) for member in team_members], - header = (H4('Share this document'),Subtitle('Anyone with the link can view this document.'))) + case "Done": return _Td(shrink=True)(CheckboxX(selected=val)) + case "Task": return _Td(val, cls='uk-visible@s') # Hide on small screens + case "Title": return _Td(val, cls='font-medium', expand=True) + case "Status" | "Priority": return _Td(cls='uk-visible@m uk-text-nowrap capitalize')(Span(val)) + case "Actions": return _Td(task_dropdown(), shrink=True) + case _: raise ValueError(f"Unknown column: {col}") -DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024')) +task_columns = ["Done", 'Task', 'Title', 'Status', 'Priority', 'Actions'] -section_content =(('bell','Everything',"Email digest, mentions & all activity."), - ('user',"Available","Only mentions and comments"), - ('ban', "Ignoring","Turn of all notifications")) +tasks_table = Div(cls='mt-4')( + TableFromDicts( + header_data=task_columns, + body_data=paginated_data, + body_cell_render=cell_render, + header_cell_render=header_render, + sortable=True, + cls=(TableT.responsive, TableT.sm, TableT.divider))) -def NotificationRow(icon, name, desc): - return Li(cls='-mx-1')(A(DivLAligned(UkIcon(icon),Div(P(name),P(desc, cls=TextPresets.muted_sm))))) -Notifications = Card( - NavContainer( - *[NotificationRow(*row) for row in section_content], - cls=NavT.secondary), - header = (H4('Notification'),Subtitle('Choose what you want to be notified about.')), - body_cls='pt-0') +def footer(): + total_pages = (len(data) + page_size - 1) // page_size + return DivFullySpaced( + Div('1 of 100 row(s) selected.', cls=TextPresets.muted_sm), + DivLAligned( + DivCentered(f'Page {current_page + 1} of {total_pages}', cls=TextT.sm), + DivLAligned(*[UkIconLink(icon=i, button=True) for i in ('chevrons-left', 'chevron-left', 'chevron-right', 'chevrons-right')]))) -TeamCard = Card( - DivLAligned( - DiceBearAvatar("Isaac Flath", h=24, w=24), - Div(H3("Isaac Flath"), P("Library Creator"))), - footer=DivFullySpaced( - DivHStacked(UkIcon("map-pin", height=16), P("Alexandria, VA")), - DivHStacked(*(UkIconLink(icon, height=16) for icon in ("mail", "linkedin", "github")))), - cls=CardT.hover) +tasks_ui = Div(DivFullySpaced(DivLAligned(table_controls), cls='mt-8'), tasks_table, footer()) @rt -def index(): - return Title("Cards Example"),Container(Grid( - *map(Div,( - Div(PaymentMethod,CreateAccount, TeamCard, cls='space-y-4'), - Div(TeamMembers, ShareDocument,DateCard,Notifications, cls='space-y-4'), - Div(ReportIssue,MonsterUI,CookieSettings, cls='space-y-4'))), - cols_md=1, cols_lg=2, cols_xl=3)) +def index(): return Container(page_heading, tasks_ui, CreateTaskModal()) -serve()"""FrankenUI Mail Example built with MonsterUI (original design by ShadCN)""" +serve()"""FrankenUI Music Example build with MonsterUI (Original design by ShadCN)""" from fasthtml.common import * from monsterui.all import * -from fasthtml.svg import * -import pathlib, json -from datetime import datetime - + app, rt = fast_app(hdrs=Theme.blue.headers()) -sidebar_group1 = (('home', 'Inbox', '128'), ('file-text', 'Drafts', '9'), (' arrow-up-right', 'Sent', ''), - ('ban', 'Junk', '23'), ('trash', 'Trash', ''), ('folder', 'Archive', '')) +def MusicLi(t,hk=''): return Li(A(DivFullySpaced(t,P(hk,cls=TextPresets.muted_sm)))) -sidebar_group2 = (('globe','Social','972'),('info','Updates','342'),('messages-square','Forums','128'), - ('shopping-cart','Shopping','8'),('shopping-bag','Promotions','21'),) +music_items = [("About Music", "" ), + ("Preferences", "โŒ˜" ), + ("Hide Music" , "โŒ˜H" ), + ("Hide Others", "โ‡งโŒ˜H"), + ("Quit Music" , "โŒ˜Q" )] -def MailSbLi(icon, title, cnt): - return Li(A(DivLAligned(Span(UkIcon(icon)),Span(title),P(cnt, cls=TextPresets.muted_sm)),href='#', cls='hover:bg-secondary p-4')) +file_dd_items = [("New", ""), + ("Open Stream URL", "โŒ˜U"), + ("Close Window", "โŒ˜W"), + ("Library", ""), + ("Import", "โŒ˜O"), + ("Burn Playlist to Disc", ""), + ("Show in Finder", "โ‡งโŒ˜R"), + ("Convert", ""), + ("Page Setup", "Print")] -sidebar = NavContainer( - NavHeaderLi(H3("Email"), cls='p-3'), - Li(Select(map(Option, ('alicia@example.com','alicia@gmail.com', 'alicia@yahoo.com')))), - *[MailSbLi(i, t, c) for i, t, c in sidebar_group1], - Li(Hr()), - *[MailSbLi(i, t, c) for i, t, c in sidebar_group2], - cls='mt-3') +edit_actions = [("Undo", "โŒ˜Z"), + ("Redo", "โ‡งโŒ˜Z"), + ("Cut", "โŒ˜X"), + ("Copy", "โŒ˜C"), + ("Paste", "โŒ˜V"), + ("Select All", "โŒ˜A"), + ("Deselect All", "โ‡งโŒ˜A")] -mail_data = json.load(open(pathlib.Path('data/mail.json'))) +view_dd_data = ["Show Playing Next", "Show Lyrics", "Show Status Bar", "Hide Sidebar", "Enter Full Screen"] -def format_date(date_str): - date_obj = datetime.fromisoformat(date_str) - return date_obj.strftime("%Y-%m-%d %I:%M %p") -def MailItem(mail): - cls_base = 'relative rounded-lg border border-border p-3 text-sm hover:bg-secondary space-y-2' - cls = f"{cls_base} {'bg-muted' if mail == mail_data[0] else ''} {'tag-unread' if not mail['read'] else 'tag-mail'}" +music_headers = NavBar( + Button("Music", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("Music"),NavContainer(map(lambda x: MusicLi(*x), music_items)))), + Button("File", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("File"), NavContainer(map(lambda x: MusicLi(*x), file_dd_items)))), + Button("Edit", cls=ButtonT.ghost+TextT.gray), DropDownNavContainer(Li(A("Edit")),NavContainer( + *map(lambda x: MusicLi(*x), edit_actions), + Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), + Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe")))))), + Button("View", cls=ButtonT.ghost+TextT.gray),DropDownNavContainer(Li(A("View"),NavContainer(map(lambda x: MusicLi(x), view_dd_data)))), + ) - return Li( - DivFullySpaced( - DivLAligned( - Strong(mail['name']), - Span(cls='flex h-2 w-2 rounded-full bg-blue-600') if not mail['read'] else ''), - Time(format_date(mail['date']), cls='text-xs')), - Small(mail['subject'], href=f"#mail-{mail['id']}"), - Div(mail['text'][:100] + '...', cls=TextPresets.muted_sm), - DivLAligned( - *[Label(A(label, href='#'), cls='uk-label-primary' if label == 'work' else '') for label in mail['labels']]), - cls=cls) -def MailList(mails): return Ul(cls='js-filter space-y-2 p-4 pt-0')(*[MailItem(mail) for mail in mails]) -def MailContent(): - return Div(cls='flex flex-col',uk_filter="target: .js-filter")( - Div(cls='flex px-4 py-2 ')( - H3('Inbox'), - TabContainer(Li(A("All Mail",href='#', role='button'),cls='uk-active', uk_filter_control="filter: .tag-mail"), - Li(A("Unread",href='#', role='button'), uk_filter_control="filter: .tag-unread"), - alt=True, cls='ml-auto max-w-40', )), - Div(cls='flex flex-1 flex-col')( - Div(cls='p-4')( - Div(cls='uk-inline w-full')( - Span(cls='uk-form-icon text-muted-foreground')(UkIcon('search')), - Input(placeholder='Search'))), - Div(cls='flex-1 overflow-y-auto max-h-[600px]')(MailList(mail_data)))) -def IconNavItem(*d): return [Li(A(UkIcon(o[0],uk_tooltip=o[1]))) for o in d] -def IconNav(*c,cls=''): return Ul(cls=f'uk-iconnav {cls}')(*c) +# music_headers = NavBarContainer( +# NavBarLSide( +# NavBarNav( +# Li(A("Music"),NavBarNavContainer(map(lambda x: MusicLi(*x), music_items))), +# Li(A("File"), NavBarNavContainer(map(lambda x: MusicLi(*x), file_dd_items))), +# Li(A("Edit")), +# NavBarNavContainer( +# *map(lambda x: MusicLi(*x), edit_actions), +# Li(A(DivFullySpaced("Smart Dictation",UkIcon("mic")))), +# Li(A(DivFullySpaced("Emojis & Symbols",UkIcon("globe"))))), +# Li(A("View"), +# NavBarNavContainer(map(lambda x: MusicLi(x), view_dd_data))), +# Li(A("Account"), +# NavBarNavContainer( +# NavHeaderLi("Switch Account"), +# *map(MusicLi, ("Andy", "Benoit", "Luis", "Manage Family", "Add Account"))))))) -def MailDetailView(mail): - top_icons = [('folder','Archive'), ('ban','Move to junk'), ('trash','Move to trash')] - reply_icons = [('reply','Reply'), ('reply','Reply all'), ('forward','Forward')] - dropdown_items = ['Mark as unread', 'Star read', 'Add Label', 'Mute Thread'] - - return Container( - DivFullySpaced( - DivLAligned( - DivLAligned(*[UkIcon(o[0],uk_tooltip=o[1]) for o in top_icons]), - Div(UkIcon('clock', uk_tooltip='Snooze'), cls='pl-2'), - cls='space-x-2 divide-x divide-border'), - DivLAligned( - *[UkIcon(o[0],uk_tooltip=o[1]) for o in reply_icons], - Div(UkIcon('ellipsis-vertical',button=True)), - DropDownNavContainer(*map(lambda x: Li(A(x)), dropdown_items)))), - DivLAligned( - Span(mail['name'][:2], cls='flex h-10 w-10 items-center justify-center rounded-full bg-muted'), - Div(Strong(mail['name']), - Div(mail['subject']), - DivLAligned(P('Reply-To:'), A(mail['email'], href=f"mailto:{mail['email']}"), cls='space-x-1'), - P(Time(format_date(mail['date']))), - cls='space-y-1'+TextT.sm), - cls='m-4 space-x-4'), - DividerLine(), - P(mail['text'], cls=TextT.sm +'p-4'), - DividerLine(), - Div(TextArea(id='message', placeholder=f"Reply {mail['name']}"), - DivFullySpaced( - LabelSwitch('Mute this thread',id='mute'), - Button('Send', cls=ButtonT.primary)), - cls='space-y-4')) + +def Album(title,artist): + img_url = 'https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp' + return Div( + Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=img_url)), + Div(cls='space-y-1')(Strong(title),P(artist,cls=TextT.muted))) + +listen_now_albums = (("Roar", "Catty Perry"), ("Feline on a Prayer", "Cat Jovi"),("Fur Elise", "Ludwig van Beethovpurr"),("Purrple Rain", "Prince's Cat")) + +made_for_you_albums = [("Like a Feline", "Catdonna"), + ("Livin' La Vida Purrda", "Ricky Catin"), + ("Meow Meow Rocket", "Elton Cat"), + ("Rolling in the Purr", "Catdelle"), + ("Purrs of Silence", "Cat Garfunkel"), + ("Meow Me Maybe", "Carly Rae Purrsen"),] + +music_content = (Div(H3("Listen Now"), cls="mt-6 space-y-1"), + Subtitle("Top picks for you. Updated daily."), + DividerLine(), + Grid(*[Album(t,a) for t,a in listen_now_albums], cls='gap-8'), + Div(H3("Made for You"), cls="mt-6 space-y-1"), + Subtitle("Your personal playlists. Updated daily."), + DividerLine(), + Grid(*[Album(t,a) for t,a in made_for_you_albums], cols_xl=6)) + +tabs = TabContainer( + Li(A('Music', href='#'), cls='uk-active'), + Li(A('Podcasts', href='#')), + Li(A('Live', cls='opacity-50'), cls='uk-disabled'), + uk_switcher='connect: #component-nav; animation: uk-animation-fade', + alt=True) + +def podcast_tab(): + return Div( + Div(cls='space-y-3 mt-6')( + H3("New Episodes"), + Subtitle("Your favorite podcasts. Updated daily.")), + Div(cls="uk-placeholder flex h-[450px] items-center justify-center rounded-md mt-4",uk_placeholder=True)( + DivVStacked(cls="space-y-6")( + UkIcon("microphone", 3), + H4("No episodes added"), + Subtitle("You have not added any podcasts. Add one below."), + Button("Add Podcast", cls=ButtonT.primary)))) + +discoved_data = [("play-circle","Listen Now"), ("binoculars", "Browse"), ("rss","Radio")] +library_data = [("play-circle", "Playlists"), ("music", "Songs"), ("user", "Made for You"), ("users", "Artists"), ("bookmark", "Albums")] +playlists_data = [("library","Recently Added"), ("library","Recently Played")] + +def MusicSidebarLi(icon, text): return Li(A(DivLAligned(UkIcon(icon), P(text)))) +sidebar = NavContainer( + NavHeaderLi(H3("Discover")), *[MusicSidebarLi(*o) for o in discoved_data], + NavHeaderLi(H3("Library")), *[MusicSidebarLi(*o) for o in library_data], + NavHeaderLi(H3("Playlists")),*[MusicSidebarLi(*o) for o in playlists_data], + cls=(NavT.primary,'space-y-3','pl-8')) @rt def index(): - return Title("Mail Example"),Container( - Grid(Div(sidebar, cls='col-span-1'), - Div(MailContent(), cls='col-span-2'), - Div(MailDetailView(mail_data[0]), cls='col-span-2'), - cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5, - gap=0, cls='flex-1'), - cls=('flex', ContainerT.xl)) + return Title("Music Example"),Container(music_headers, DividerSplit(), + Grid(sidebar, + Div(cls="col-span-4 border-l border-border")( + Div(cls="px-8 py-6")( + DivFullySpaced( + Div(cls="max-w-80")(tabs), + Button(cls=ButtonT.primary)(DivLAligned(UkIcon('circle-plus')),Div("Add music"))), + Ul(id="component-nav", cls="uk-switcher")( + Li(*music_content), + Li(podcast_tab())))), + cols_sm=1, cols_md=1, cols_lg=5, cols_xl=5)) serve()"""FrankenUI Forms Example built with MonsterUI (original design by ShadCN)""" diff --git a/docs/llms.txt b/docs/llms.txt index b9c229a..8ddc261 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -6,14 +6,14 @@ - [API List](https://raw.githubusercontent.com/AnswerDotAI/MonsterUI/refs/heads/main/docs/apilist.txt): Complete API Reference ## Examples +- [Ticket](https://monsterui.answer.ai/ticket/md): MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components - [Scrollspy](https://monsterui.answer.ai/scrollspy/md): MonsterUI Scrollspy Example application - [Dashboard](https://monsterui.answer.ai/dashboard/md): FrankenUI Dashboard Example built with MonsterUI (original design by ShadCN) -- [Ticket](https://monsterui.answer.ai/ticket/md): MonsterUI Help Desk Example - Professional Dashboard with DaisyUI components -- [Music](https://monsterui.answer.ai/music/md): FrankenUI Music Example build with MonsterUI (Original design by ShadCN) +- [Mail](https://monsterui.answer.ai/mail/md): FrankenUI Mail Example built with MonsterUI (original design by ShadCN) +- [Cards](https://monsterui.answer.ai/cards/md): FrankenUI Cards Example built with MonsterUI (original design by ShadCN) - [Playground](https://monsterui.answer.ai/playground/md): FrankenUI Playground Example built with MonsterUI (original design by ShadCN) - [Tasks](https://monsterui.answer.ai/tasks/md): FrankenUI Tasks Example built with MonsterUI (original design by ShadCN) -- [Cards](https://monsterui.answer.ai/cards/md): FrankenUI Cards Example built with MonsterUI (original design by ShadCN) -- [Mail](https://monsterui.answer.ai/mail/md): FrankenUI Mail Example built with MonsterUI (original design by ShadCN) +- [Music](https://monsterui.answer.ai/music/md): FrankenUI Music Example build with MonsterUI (Original design by ShadCN) - [Forms](https://monsterui.answer.ai/forms/md): FrankenUI Forms Example built with MonsterUI (original design by ShadCN) - [Auth](https://monsterui.answer.ai/auth/md): FrankenUI Auth Example built with MonsterUI (original design by ShadCN)