diff --git a/docs/api_reference/api_reference.py b/docs/api_reference/api_reference.py index bc2a894..d243321 100644 --- a/docs/api_reference/api_reference.py +++ b/docs/api_reference/api_reference.py @@ -381,12 +381,13 @@ def team_member(name, role, location="Remote"): return Grid(*team, cols_sm=1, cols_md=1, cols_lg=2, cols_xl=3) docs_cards = create_doc_section( - Card, H3("Example Usage"), fn2code_string(ex_card), (*fn2code_string(ex_card2_wide),'uk-visible@s'), (*fn2code_string(ex_card2_tall),'uk-hidden@s'), fn2code_string(ex_card3), + H3("API Reference"), + Card, CardTitle, CardT, P("The remainder of these are only needed if you're doing something really special. They are used in the `Card` function to generate the boilerplate for you.", cls='my-6'), diff --git a/docs/examples/cards.py b/docs/examples/cards.py index 0e26de9..2a526dc 100644 --- a/docs/examples/cards.py +++ b/docs/examples/cards.py @@ -1,97 +1,87 @@ -"""FrankenUI Cards Example""" +"""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 -CreateAccount = Card(Grid(Button(UkIcon('github',cls='uk-margin-small-right'),'Github'), - Button(UkIcon('google',cls='uk-margin-small-right'),'Google'), - cols=2,cls='gap-6'), - DividerSplit("OR CONTINUE WITH", text_cls = (TextT.small, TextT.muted)), +CreateAccount = Card( + Grid(Button(DivLAligned(UkIcon('github'),Div('Github'))),Button('Google')), + DividerSplit("OR CONTINUE WITH", text_cls=TextFont.muted_sm), LabelInput('Email', id='email', placeholder='m@example.com'), LabelInput('Password', id='password',placeholder='Password', type='Password'), - header=(H3('Create an account'),P(cls=TextFont.muted_sm)('Enter your email below to create your account')), - footer=Button(cls=(ButtonT.primary,'w-full'))('Create Account'), - body_cls='space-y-4 py-0') + header=(H3('Create an Account'),P(cls=TextFont.muted_sm)('Enter your email below to create your account')), + footer=Button(cls=(ButtonT.primary,'w-full'))('Create Account')) 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")) -Card2Svg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d="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", fill="currentColor")), +PaypalSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d="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", fill="currentColor")), AppleSvg = Svg(role="img", viewBox="0 0 24 24", cls="h-6 w-6 mr-1")(Path(d="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", fill="currentColor")) PaymentMethod = Card( - Grid( - Button(DivCentered(Card1Svg, "Card"), cls='h-20 w-full border-2 border-primary'), - Button(DivCentered(Card2Svg, "Card"), cls='h-20 w-full'), - Button(DivCentered(AppleSvg, "Apple"), cls='h-20 w-full'), - cols=3,cls='gap-6'), - Div(cls='space-y-4')( - LabelInput('Name', id='name', placeholder='John Doe'), - LabelInput('Card Number', id='card_number', placeholder='m@example.com'), - Grid(LabelUkSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'), - LabelUkSelect(*Options(*range(2024,2030),selected_idx=0), label='Year', id='expire_year'), - LabelInput('CVV', id='cvv',placeholder='CVV'), - cols=3,cls='gap-4')), - header=(H3('Payment Method'),P(cls=TextFont.muted_sm)('Add a new payment method to your account.'))) + 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(LabelUkSelect(*Options(*calendar.month_name[1:],selected_idx=0),label='Expires',id='expire_month'), + LabelUkSelect(*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'),P(cls=TextFont.muted_sm)('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(LabelUkSelect(*Options(*area_opts), label='Area', id='area')), - Div(LabelUkSelect(*Options(*severity_opts),label='Severity',id='area')), - cols=2), - LabelInput( label='Subject', placeholder='I need help with', id='subject'), - LabelTextArea( label='Description',placeholder='Please include all information relevant to your issue', id='description'), + Div(LabelUkSelect(*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 an issue'),P(cls=TextFont.muted_sm)('What area are you having problems with')), - footer = DivFullySpaced(Button(cls=ButtonT.ghost )('Cancel'), - Button(cls=ButtonT.primary)('Submit'))) + header=(H3('Report Issue'),P(cls=TextFont.muted_sm)('What area are you having problems with?')), + footer = DivFullySpaced(Button('Cancel'), Button(cls=ButtonT.primary)('Submit'))) FlexBlockCentered = (FlexT.block,FlexT.center) -franken_desc ="HTML-first, framework-agnostic, beautifully designed components that you can truly copy and paste into your site. Accessible. Customizable. Open Source." -FrankenUI = Card(H4("franken/ui"), - P(cls=TextFont.muted_sm)(franken_desc), - Div(cls=('flex','gap-x-4',TextFont.muted_sm))( - Div(cls=FlexBlockCentered)("TypeScript"), - Div(cls=FlexBlockCentered)(UkIcon('star'),"20k"),"Updated April 2023")) +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"), + P(monster_desc, cls=TextFont.muted_sm), + DivLAligned( + Div("Python"), + DivLAligned(UkIcon('star'),Div("20k"), cls='space-x-1'), + Div(datetime.now().strftime("%B %d, %Y")), + cls=('space-x-4',TextFont.muted_sm))) + +def CookieTableRow(heading, description, active=False): + return Tr(Td(H5(heading)), + Td(P(description, cls=TextFont.muted_sm)), + Td(Switch(checked=active))) CookieSettings = Card( - Div(H5('Strictly Necessary'), - P(cls=(TextFont.muted_sm,TextT.normal))('These cookies are essential in order to use the website and use its features.'), - Switch(), - cls=(*FlexBlockCentered, FlexT.between, 'gap-2')), - Div(H5('Functional Cookies'), - P(cls=(TextFont.muted_sm,TextT.normal))('These cookies allow the website to provide personalized functionality.'), - Switch(), - cls=(*FlexBlockCentered, FlexT.between, 'gap-2')), - Div(H5('Performance Cookies'), - P(cls=(TextFont.muted_sm,TextT.normal))('These cookies help to improve the performance of the website.'), - Switch(), - cls=(*FlexBlockCentered, FlexT.between, 'gap-2')), + 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'),P(cls=(TextFont.muted_sm, 'mt-1.5'))('Manage your cookie settings here.')), - footer=Button(cls=(ButtonT.primary, 'w-full'))('Save Preferences'),) + footer=Button(cls=(ButtonT.primary, 'w-full'))('Save Preferences')) team_members = [("Sofia Davis", "m@example.com", "Owner"),("Jackson Lee", "p@example.com", "Member"),] - -body = [Div(cls=(*FlexBlockCentered, 'space-x-4'))( - DiceBearAvatar(n, 10,10), - Div(cls='flex-1')( - P(n, cls='text-sm font-medium leading-none'), - P(e, cls=TextFont.muted_sm)), - Button(r), +def TeamMemberRow(name, email, role): + return DivFullySpaced( + DivLAligned( + DiceBearAvatar(name, 10,10), + Div(P(name, cls=(TextT.small, 'font-medium')), + P(email, cls=TextFont.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.')))] - )), - ) for n,e,r in team_members] + A(Div('Owner', NavSubtitle('Admin-level access to all resources.')))]))) -TeamMembers = Card(*body, +TeamMembers = Card(*[TeamMemberRow(*member) for member in team_members], header = (H4('Team Members'),Div('Invite your team members to collaborate.', cls=('mt-1.5', TextFont.muted_sm))),) access_roles = ("Read and write access", "Read-only access") @@ -99,18 +89,18 @@ ("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.small, 'font-medium')), + P(email, cls=TextFont.muted_sm))), + UkSelect(*Options(*access_roles, selected_idx=access_roles.index(role)))) + ShareDocument = Card( - Div(cls='flex gap-x-2')( - Input(value='http://example.com/link/to/document',cls='flex-1'), - Button('Copy link')), - Div(cls='uk-divider-icon my-4'), - H4(cls=TextFont.bold_sm)('People with access'), - *[DivLAligned( - DiceBearAvatar(n, 10,10), - Div(cls='flex-1')( - P(n, cls='text-sm font-medium leading-none'), - P(e, cls=TextFont.muted_sm)), - UkSelect(*Options(*access_roles, selected_idx=access_roles.index(r))), cls='gap-4') for n,e,r in team_members], + DivLAligned(Input(value='http://example.com/link/to/document'),Button('Copy link', cls='whitespace-nowrap')), + Divider(), + H4('People with access', cls=TextFont.bold_sm), + *[TeamMemberRow(*member) for member in team_members], header = (H4('Share this document'),Div('Anyone with the link can view this document.', cls=('mt-1.5',TextFont.muted_sm)))) DateCard = Card(Button('Jan 20, 2024 - Feb 09, 2024')) @@ -132,7 +122,7 @@ def page(): *map(lambda x: Div(x, cls='space-y-4'),( (PaymentMethod,CreateAccount), (TeamMembers, ShareDocument,DateCard,Notifications), - (ReportIssue,FrankenUI,CookieSettings))), + (ReportIssue,MonsterUI,CookieSettings))), cols_lg=3, ) diff --git a/docs/examples/music.py b/docs/examples/music.py index e20d5a3..b3b0452 100644 --- a/docs/examples/music.py +++ b/docs/examples/music.py @@ -1,94 +1,80 @@ -"""FrankenUI Music Example""" +"""FrankenUI Music Example build with MonsterUI (Original design by ShadCN)""" from fasthtml.common import * from monsterui.all import * -def SpacedPP(left, right=None): return DivFullySpaced(NavP(left),NavP(right) if right else '') - -def SpacedPPs(*c): return [SpacedPP(*tuplify(o)) for o in c] - -def NavP(*c, cls=TextFont.muted_sm): return P(cls=cls)(*c) - -def LAlignedTxtIcon(txt, icon, width=None, height=None, stroke_width=None, cls='space-x-2', icon_right=True, txt_cls=None): - # Good for navbards - c = (txt if isinstance(txt, FT) else NavP(txt,cls=ifnone(txt_cls,TextFont.muted_sm)),UkIcon(icon=icon, height=height, width=width, stroke_width=stroke_width)) - if not icon_right: c = reversed(c) - return DivLAligned(*c, cls=cls) - def MusicLi(t,hk=''): return Li(A(DivFullySpaced(t,P(hk,cls=TextFont.muted_sm)))) -music_items = [("About Music", ""),("Preferences", "⌘"),("Hide Music", "⌘H"),("Hide Others", "⇧⌘H"),("Quit Music", "⌘Q")] - -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")] - -edit_actions = [("Undo", "⌘Z"),("Redo", "⇧⌘Z"),("Cut", "⌘X"),("Copy", "⌘C"), - ("Paste", "⌘V"),("Select All", "⌘A"),("Deselect All", "⇧⌘A")] +music_items = [("About Music", "" ), + ("Preferences", "⌘" ), + ("Hide Music" , "⌘H" ), + ("Hide Others", "⇧⌘H"), + ("Quit Music" , "⌘Q" )] + +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")] + +edit_actions = [("Undo", "⌘Z"), + ("Redo", "⇧⌘Z"), + ("Cut", "⌘X"), + ("Copy", "⌘C"), + ("Paste", "⌘V"), + ("Select All", "⌘A"), + ("Deselect All", "⇧⌘A")] view_dd_data = ["Show Playing Next", "Show Lyrics", "Show Status Bar", "Hide Sidebar", "Enter Full Screen"] -account_dd_data = [Span("Switch Account", cls="ml-6"), [SpacedPP("Andy"), LAlignedTxtIcon("Benoit", 'plus-circle', 0.5, icon_right=False), SpacedPP("Luis")], - SpacedPPs("Manage Family"), SpacedPPs("Add Account")] - -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"), - MusicLi("Andy"), - MusicLi("Benoit"), - MusicLi("Luis"), - MusicLi("Manage Family"), - MusicLi("Add Account"))), - cls='space-x-4'))) - -def AlbumImg(url): - return Div(cls="overflow-hidden rounded-md")(Img(cls="transition-transform duration-200 hover:scale-105", src=url)) - -def AlbumFooter(title, artist): - return Div(cls='space-y-1')(P(title,cls=TextT.bold),P(artist,cls=TextT.muted)) - -def Album(url,title,artist): - return Div(AlbumImg(url),AlbumFooter(title,artist)) - +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 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')(P(title,cls=TextT.bold),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"),] - - -def create_album_grid(albums, cols=4): - return Grid(*[Div(cls="uk-grid-small")( - Div(cls="overflow-hidden rounded-md")( - Img(cls="transition-transform duration-200 hover:scale-105", src=img_url, alt="")), - Div(cls="space-y-1 text-sm")( - H3(album['title'], cls="font-medium leading-none"), - P(album['artist'], cls="text-xs text-muted-foreground"))) for album in albums], - cols,gap=4) - -_album = lambda t,a: Album('https://ucarecdn.com/e5607eaf-2b2a-43b9-ada9-330824b6afd7/music1.webp',t,a) +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"), P("Top picks for you. Updated daily.",cls=TextFont.muted_sm), DividerLine(), - Grid(*[_album(t,a) for t,a in listen_now_albums], cols=4, cols_lg=4,cls='gap-8'), + 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"), P("Your personal playlists. Updated daily.", cls=TextFont.muted_sm), DividerLine(), - Grid(*[_album(t,a) for t,a in made_for_you_albums], cols=6)) + 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('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', @@ -96,39 +82,35 @@ def create_album_grid(albums, cols=4): def podcast_tab(): return Div( - Div(cls="space-y-3")( + Div(cls='space-y-3 mt-6')( H3("New Episodes"), P("Your favorite podcasts. Updated daily.", cls=TextFont.muted_sm)), - Div(cls="my-4 h-[1px] w-full bg-border"), Div(cls="uk-placeholder flex h-[450px] items-center justify-center rounded-md",uk_placeholder=True)( - Div(cls="text-center space-y-6")( + DivVStacked(cls="space-y-6")( UkIcon("microphone", 3), H4("No episodes added"), P("You have not added any podcasts. Add one below.", cls=TextFont.muted_sm), Button("Add Podcast", cls=ButtonT.primary)))) -def LAlignedIconTxts(ns, icns): return [Li(A(LAlignedIconTxt(n,i))) for n,i in zip(ns,icns)] - -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")] +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),cls='space-x-2'))) -sb = NavContainer( - NavHeaderLi(H3("Discover")),*[MusicSidebarLi(*o) for o in discoved_data], - NavHeaderLi(H3("Library")),*[MusicSidebarLi(*o) for o in library_data], +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'), -) + cls=(NavT.primary,'space-y-3','pl-8')) def page(): return Div(Container(music_headers,cls='py-8'), DividerSplit(), - Grid(sb, + Grid(sidebar, Div(cls="col-span-4 border-l border-border")( Div(cls="px-8 py-6")( - Div(cls="flex items-center justify-between")( + DivFullySpaced( Div(cls="max-w-80")(tabs), - Button(cls=ButtonT.primary)(Span(cls="mr-2 size-4")(UkIcon('circle-plus')),"Add music")), + Button(cls=ButtonT.primary)(DivLAligned(UkIcon('circle-plus')),Div("Add music"))), Ul(id="component-nav", cls="uk-switcher")( Li(*music_content), Li(podcast_tab())))),