diff --git a/docs/api_reference/api_reference.py b/docs/api_reference/api_reference.py index 2e233d9..09e2270 100644 --- a/docs/api_reference/api_reference.py +++ b/docs/api_reference/api_reference.py @@ -2,7 +2,7 @@ from fasthtml.common import * from monsterui.all import * -from nbdev.showdoc import * +# from nbdev.showdoc import * from utils import create_flippable_card, fn2code_string from enum import EnumType from collections.abc import Callable @@ -12,17 +12,60 @@ These are automatically added to the docs sidebar so you don't have to do anything other than add the function using create_doc_section. ''' +from inspect import signature, getdoc, getsourcefile, getsourcelines # Utilities +def get_github_url(func): + "Create GitHub URL for function, assuming AnswerDotAI/MonsterUI repo" + file = getsourcefile(func).split('MonsterUI/')[-1] + line = getsourcelines(func)[-1] + return f"https://github.com/AnswerDotAI/MonsterUI/blob/main/{file}#L{line}" + +from fastcore.docments import docments, docstring, get_name +def show_doc(func) -> str: + "Convert a Google-style docstring to markdown" + params = docments(func, args_kwargs=True) + funcname = get_name(func) + doc = docstring(func) + par, ret = None, None + if params: + par = Div(Strong('Params'), + Ul(*[Li(render_md(f"`{name}` {desc if desc else ''}",class_map_mods={'p':'leading-relaxed'}), cls='') for name, desc in params.items() if name != 'return'], cls='uk-list-disc space-y-2 mb-6 ml-6')) + if 'return' in params and params['return']: ret = render_md(f"**Returns:** {params['return']}") + return Div( + # DivFullySpaced(H3(func.__name__, cls='uk-h3 text-2xl font-semibold mt-8 mb-4'),A("Source", href=get_github_url(func), cls='text-primary hover:text-primary-focus underline')), + DivFullySpaced(render_md(f"### {func.__name__}"),A("Source", href=get_github_url(func), cls='text-primary hover:text-primary-focus underline')), + Div(Pre(Code(f"{funcname}{signature(func)}", + cls='hljs language-python px-1 block overflow-x-auto'), + cls='bg-base-200 rounded-lg p-4 mb-6')), + Div(Blockquote(render_md(doc), cls='pl-4 border-l-4 border-primary mb-6'), par, ret, cls='ml-10')) def enum_to_html_table(enum_class): - headers = ["Option", "Value"] - rows = [[name, value.value] for name, value in enum_class.__members__.items()] + "Creates a compact multi-column table display for enum documentation" + items = list(enum_class.__members__.items()) + n_cols = min(4, max(2, round((len(items) ** 0.5)))) + + # Create header/cell pairs with borders + def make_pair(opt, val, i): + border = 'border-l border-base-300 pl-4' if i > 0 else '' + return [Th('Option', cls=border), Th('Value')] if opt == 'header' else [Td(opt, cls=border), Td(val)] + + # Build rows with padding for incomplete final row + rows = [] + for i in range(0, len(items), n_cols): + cells = [] + for j in range(n_cols): + name, val = items[i + j] if i + j < len(items) else ('', '') + cells.extend(make_pair(name, val.value if val else '', j)) + rows.append(Tr(*cells)) + return Div( - Hr(cls='uk-divider-icon my-4'), - H3(enum_class.__name__,cls='my-4'), - P(I(enum_class.__doc__)), - TableFromLists(headers, rows, cls=(TableT.hover, 'uk-table-small')),) + Hr(cls='uk-divider-icon my-2'), + DivFullySpaced(H3(enum_class.__name__, cls='my-2'), P(I(enum_class.__doc__), cls='text-sm')), + Table( + Thead(Tr(*make_pair('header', '', 0) * n_cols)), + Tbody(*rows), + cls=(TableT.hover, 'uk-table-small uk-table-justify uk-table-middle'))) def render_content(c): "Renders content by type" @@ -33,8 +76,9 @@ def render_content(c): extra_cls = c[2] if len(tuple(c)) == 3 else None return create_flippable_card(c[0], c[1], extra_cls) elif isinstance(c, Callable): # Callables are rendered as documentation via show_doc - _html = show_doc(c, renderer=BasicHtmlRenderer)._repr_html_() - return NotStr(apply_classes(_html, class_map_mods={"table":'uk-table uk-table-hover uk-table-small'})) + return show_doc(c) + # _html = show_doc(c, renderer=BasicHtmlRenderer)._repr_html_() + # return NotStr(apply_classes(_html, class_map_mods={"table":'uk-table uk-table-hover uk-table-small'})) else: return c def create_doc_section(*content, title): @@ -66,7 +110,6 @@ def ex_links(): Button, fn2code_string(ex_buttons), ButtonT, - A, AT, fn2code_string(ex_links), title="Buttons & Links") @@ -172,7 +215,7 @@ def ex_other(): TextFont, TextT, H1, H2, H3, H4, Titled, - P, PParagraph, PLarge, PLead, PSmall, PMuted, + PParagraph, PLarge, PLead, PSmall, PMuted, CodeSpan, Blockquote, title="Text Style") @@ -312,7 +355,7 @@ def team_member(name, role, location="Remote"): fn2code_string(ex_card3), CardTitle, CardT, - "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.", + 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'), CardContainer, CardHeader, CardBody, @@ -408,6 +451,14 @@ def ex_form(): fn2code_string(ex_formlabel), Input, fn2code_string(ex_input), + LabelInput, + LabelCheckboxX, + LabelSwitch, + LabelRange, + LabelTextArea, + LabelRadio, + LabelSelect, + LabelUkSelect, Progress, fn2code_string(ex_progress), Radio, @@ -682,9 +733,8 @@ def ex_tabs2(): P("A tabs can use any method of navigation (htmx, or href). However, often these are use in conjunction with switchers do to this client side", cls=TextFont.muted_sm), fn2code_string(ex_tabs1), H1("API Docs"), - Nav, - NavT, NavContainer, + NavT, NavCloseLi, NavSubtitle, NavHeaderLi, @@ -772,10 +822,7 @@ def body_render(k, v): TableT, Tbody, Th, - Td, - Tfoot, - Thead, - Tr, + Td, title="Tables") # Icons diff --git a/docs/main.py b/docs/main.py index 43d4b8d..c972ce1 100644 --- a/docs/main.py +++ b/docs/main.py @@ -22,7 +22,7 @@ def _create_page(content, # The content to display (without the layout/sidebar) def with_layout(sidebar_section, content): "Puts the sidebar and content into a layout" - return Title(f"MonsterUI {sidebar_section.removeprefix('docs_')}"), Div(cls="flex flex-col md:flex-row w-full")( + return Title(f"MonsterUI"), Div(cls="flex flex-col md:flex-row w-full")( Button(UkIcon("menu",50,50,cls='mt-4'), cls="md:hidden mb-4", uk_toggle="target: #mobile-sidebar"), Div(sidebar(sidebar_section), id='mobile-sidebar', hidden=True), Div(cls="md:flex w-full")( diff --git a/docs/requirements.txt b/docs/requirements.txt index 900e8d0..e2b9a68 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,7 +6,6 @@ fastcore fh_matplotlib numpy matplotlib -nbdev pysymbol_llm mistletoe lxml diff --git a/docs/utils.py b/docs/utils.py index d5d62b1..2405ebe 100644 --- a/docs/utils.py +++ b/docs/utils.py @@ -14,38 +14,46 @@ def get_last_statement(code): return ast.unparse(ast.parse(code).body[-1]) from pathlib import Path -hjs = (Style('html.dark .hljs-copy-button {background-color: #e0e0e0; color: #2d2b57;}'), - Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-dark.css', disabled=True), - Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/atom-one-light.css', disabled=True), - Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js'), - Script(src='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.js'), - Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.css'), - Style('.hljs-copy-button {background-color: #2d2b57;}'), - Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/languages/python.min.js'), - Script("hljs.addPlugin(new CopyButtonPlugin());\r\nhljs.configure({'cssSelector': 'pre code'});\r\nhtmx.onLoad(hljs.highlightAll);", type='module'), - Script('''htmx.on("htmx:beforeHistorySave", () => {document.querySelectorAll("uk-icon").forEach((elt) => {elt.innerHTML = '';});});'''), - - Script('''hljs.configure({ - ignoreUnescapedHTML: true - });'''), - Script('''const observer = new MutationObserver(mutations => { - mutations.forEach(mutation => { - if (mutation.target.tagName === 'HTML' && mutation.attributeName === 'class') { - const isDark = mutation.target.classList.contains('dark'); - document.querySelector('link[href*="atom-one-dark.css"]').disabled = !isDark; - document.querySelector('link[href*="atom-one-light.css"]').disabled = isDark; - } - }); - }); +hjs = ( + # Core highlight.js and copy plugin + Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js'), + Script(src='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/languages/python.min.js'), + Script(src='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.js'), + + # Themes and styles + Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/atom-one-dark.css', id='hljs-dark'), + Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/atom-one-light.css', id='hljs-light'), + Link(rel='stylesheet', href='https://cdn.jsdelivr.net/gh/arronhunt/highlightjs-copy/dist/highlightjs-copy.min.css'), + + + # Initialization script + Script(''' + // Initialize highlight.js with copy plugin + hljs.addPlugin(new CopyButtonPlugin()); + hljs.configure({ + cssSelector: 'pre code', + languages: ['python'], + ignoreUnescapedHTML: true + }); - observer.observe(document.documentElement, { attributes: true }); + // Theme switching logic + function updateTheme() { + const isDark = document.documentElement.classList.contains('dark'); + document.getElementById('hljs-dark').disabled = !isDark; + document.getElementById('hljs-light').disabled = isDark; + } - // Initial setup - const isDark = document.documentElement.classList.contains('dark'); - document.querySelector('link[href*="atom-one-dark.css"]').disabled = !isDark; - document.querySelector('link[href*="atom-one-light.css"]').disabled = isDark; - ''')) + // Watch for theme changes + new MutationObserver(mutations => + mutations.forEach(m => m.target.tagName === 'HTML' && + m.attributeName === 'class' && updateTheme()) + ).observe(document.documentElement, { attributes: true }); + // Initial setup + updateTheme(); + htmx.onLoad(hljs.highlightAll); + ''', type='module') +) def create_flippable_card(content, source_code, extra_cls=None): "Creates a card that flips between content and source code" _id = 'f'+str(unqid()) @@ -57,24 +65,11 @@ def create_flippable_card(content, source_code, extra_cls=None): DivFullySpaced(UkIcon('corner-down-right', 20, 20, 3),"See Output"), uk_toggle=f"target: #{_id}", id=_id, cls=ButtonT.primary, hidden=True), Div(content, id=_id), - Div(Pre(Code(source_code, cls="hljs language-python")), id=_id, hidden=True, cls="mockup-code"), + Div(Pre(Code(source_code, cls="hljs language-python")), id=_id, hidden=True), cls='my-8') return Div(_card, cls=extra_cls) if extra_cls else _card -def fn2code_string(fn: Callable) -> tuple: return fn(), extract_function_body(fn) - -def extract_function_body(func): - source = inspect.getsource(func) - return source - body_start = source.index(':') + 1 - body = source[body_start:] - lines = body.split('\n') - # Remove empty lines at the start - while lines and not lines[0].strip(): - lines.pop(0) - # Remove first 4 spaces from each line - body = '\n'.join(line[4:] if line.startswith(' ') else line for line in lines) - return body.replace('return ', '', 1) +def fn2code_string(fn: Callable) -> tuple: return fn(), inspect.getsource(fn) def render_nb(path): diff --git a/monsterui/_modidx.py b/monsterui/_modidx.py index daedb37..c04f7b4 100644 --- a/monsterui/_modidx.py +++ b/monsterui/_modidx.py @@ -98,7 +98,6 @@ 'monsterui.franken.H2': ('franken.html#h2', 'monsterui/franken.py'), 'monsterui.franken.H3': ('franken.html#h3', 'monsterui/franken.py'), 'monsterui.franken.H4': ('franken.html#h4', 'monsterui/franken.py'), - 'monsterui.franken.HTMXModalCloseButton': ('franken.html#htmxmodalclosebutton', 'monsterui/franken.py'), 'monsterui.franken.Input': ('franken.html#input', 'monsterui/franken.py'), 'monsterui.franken.Label': ('franken.html#label', 'monsterui/franken.py'), 'monsterui.franken.LabelCheckboxX': ('franken.html#labelcheckboxx', 'monsterui/franken.py'), @@ -116,7 +115,6 @@ 'monsterui.franken.ListT': ('franken.html#listt', 'monsterui/franken.py'), 'monsterui.franken.ListT._generate_next_value_': ( 'franken.html#listt._generate_next_value_', 'monsterui/franken.py'), - 'monsterui.franken.Main': ('franken.html#main', 'monsterui/franken.py'), 'monsterui.franken.Modal': ('franken.html#modal', 'monsterui/franken.py'), 'monsterui.franken.ModalBody': ('franken.html#modalbody', 'monsterui/franken.py'), 'monsterui.franken.ModalCloseButton': ('franken.html#modalclosebutton', 'monsterui/franken.py'), @@ -182,9 +180,7 @@ 'monsterui.franken.TextT._generate_next_value_': ( 'franken.html#textt._generate_next_value_', 'monsterui/franken.py'), 'monsterui.franken.Th': ('franken.html#th', 'monsterui/franken.py'), - 'monsterui.franken.Thead': ('franken.html#thead', 'monsterui/franken.py'), 'monsterui.franken.Titled': ('franken.html#titled', 'monsterui/franken.py'), - 'monsterui.franken.Tr': ('franken.html#tr', 'monsterui/franken.py'), 'monsterui.franken.UkFormSection': ('franken.html#ukformsection', 'monsterui/franken.py'), 'monsterui.franken.UkIcon': ('franken.html#ukicon', 'monsterui/franken.py'), 'monsterui.franken.UkIconLink': ('franken.html#ukiconlink', 'monsterui/franken.py'), diff --git a/monsterui/franken.py b/monsterui/franken.py index 7de0ba0..f5d6cb0 100644 --- a/monsterui/franken.py +++ b/monsterui/franken.py @@ -2,20 +2,19 @@ # %% auto 0 __all__ = ['franken_class_map', 'TextT', 'TextFont', 'PParagraph', 'PLarge', 'PLead', 'PSmall', 'PMuted', 'CodeSpan', - 'Blockquote', 'H1', 'H2', 'H3', 'H4', 'ButtonT', 'Button', 'Main', 'ContainerT', 'Container', 'Titled', - 'DividerT', 'Divider', 'DividerSplit', 'DividerLine', 'Article', 'ArticleTitle', 'ArticleMeta', 'SectionT', - 'Section', 'Form', 'Fieldset', 'Legend', 'Input', 'Select', 'Radio', 'CheckboxX', 'Range', 'TextArea', - 'Switch', 'FormLabel', 'LabelT', 'Label', 'UkFormSection', 'GenericLabelInput', 'LabelInput', 'LabelRadio', - 'LabelCheckboxX', 'LabelRange', 'LabelTextArea', 'LabelSwitch', 'LabelSelect', 'Options', 'UkSelect', + 'Blockquote', 'H1', 'H2', 'H3', 'H4', 'ButtonT', 'Button', 'ContainerT', 'Container', 'Titled', 'DividerT', + 'Divider', 'DividerSplit', 'DividerLine', 'Article', 'ArticleTitle', 'ArticleMeta', 'SectionT', 'Section', + 'Form', 'Fieldset', 'Legend', 'Input', 'Select', 'Radio', 'CheckboxX', 'Range', 'TextArea', 'Switch', + 'FormLabel', 'LabelT', 'Label', 'UkFormSection', 'GenericLabelInput', 'LabelInput', 'LabelRange', + 'LabelTextArea', 'LabelSwitch', 'LabelRadio', 'LabelCheckboxX', 'LabelSelect', 'Options', 'UkSelect', 'LabelUkSelect', 'AT', 'ListT', 'UkList', 'ModalContainer', 'ModalDialog', 'ModalHeader', 'ModalBody', - 'ModalFooter', 'ModalTitle', 'ModalCloseButton', 'HTMXModalCloseButton', 'Modal', 'PaddingT', 'PositionT', - 'Placeholder', 'Progress', 'UkIcon', 'UkIconLink', 'DiceBearAvatar', 'FlexT', 'Grid', 'DivFullySpaced', - 'DivCentered', 'DivLAligned', 'DivRAligned', 'DivVStacked', 'DivHStacked', 'NavT', 'NavContainer', - 'NavParentLi', 'NavDividerLi', 'NavHeaderLi', 'NavSubtitle', 'NavCloseLi', 'NavBarContainer', 'NavBarLSide', - 'NavBarRSide', 'NavBarCenter', 'NavBarNav', 'NavBarSubtitle', 'NavBarNavContainer', 'NavBarParentIcon', - 'DropDownNavContainer', 'TabContainer', 'CardT', 'CardTitle', 'CardHeader', 'CardBody', 'CardFooter', - 'CardContainer', 'Card', 'TableT', 'Table', 'Td', 'Th', 'Tr', 'Thead', 'Tbody', 'TableFromLists', - 'TableFromDicts', 'apply_classes', 'render_md'] + 'ModalFooter', 'ModalTitle', 'ModalCloseButton', 'Modal', 'PaddingT', 'PositionT', 'Placeholder', 'Progress', + 'UkIcon', 'UkIconLink', 'DiceBearAvatar', 'FlexT', 'Grid', 'DivFullySpaced', 'DivCentered', 'DivLAligned', + 'DivRAligned', 'DivVStacked', 'DivHStacked', 'NavT', 'NavContainer', 'NavParentLi', 'NavDividerLi', + 'NavHeaderLi', 'NavSubtitle', 'NavCloseLi', 'NavBarContainer', 'NavBarLSide', 'NavBarRSide', 'NavBarCenter', + 'NavBarNav', 'NavBarSubtitle', 'NavBarNavContainer', 'NavBarParentIcon', 'DropDownNavContainer', + 'TabContainer', 'CardT', 'CardTitle', 'CardHeader', 'CardBody', 'CardFooter', 'CardContainer', 'Card', + 'TableT', 'Table', 'Td', 'Th', 'Tbody', 'TableFromLists', 'TableFromDicts', 'apply_classes', 'render_md'] # %% ../nbs/02_franken.ipynb import fasthtml.common as fh @@ -69,50 +68,83 @@ def __str__(self): return self.value bold_sm = stringify((TextT.bold, TextT.small)) # %% ../nbs/02_franken.ipynb -def PParagraph(*args, cls='', **kwargs): - "P Tag with uk-paragraph style applied" - return fh.P(*args, cls=('uk-paragraph',stringify(cls)), **kwargs) -def PLarge(*args, cls='', **kwargs): - "P Tag with uk-text-large style applied" - return fh.P(*args, cls=('uk-text-large', stringify(cls)), **kwargs) -def PLead(*args, cls='', **kwargs): - "P Tag with uk-text-lead style applied" - return fh.P(*args, cls=('uk-text-lead', stringify(cls)), **kwargs) -def PSmall(*args, cls='', **kwargs): - "P Tag with uk-text-small style applied" - return fh.P(*args, cls=('uk-text-small', stringify(cls)), **kwargs) -def PMuted(*args, cls='', **kwargs): - "P Tag with uk-text-muted style applied" - return fh.P(*args, cls=('uk-text-muted', stringify(cls)), **kwargs) - -# %% ../nbs/02_franken.ipynb -def CodeSpan(*args, **kwargs): +def PParagraph(*c, # Contents of P tag (often text) + cls='', # Classes in addition to paragraph styling + **kwargs # Additional args for P tag + ): # P(..., cls='uk-paragraph') + "P Tag with paragraph style applied" + return fh.P(*c, cls=('uk-paragraph', stringify(cls)), **kwargs) +def PLarge(*c, # Contents of P tag (often text) + cls='', # Classes in addition to paragraph styling + **kwargs # Additional args for P tag + ): # P(..., cls='uk-text-large') + "P Tag with large style applied" + return fh.P(*c, cls=('uk-text-large', stringify(cls)), **kwargs) +def PLead(*c, # Contents of P tag (often text) + cls='', # Classes in addition to paragraph styling + **kwargs # Additional args for P tag + ): # P(..., cls='uk-text-lead') + "P Tag with lead style applied" + return fh.P(*c, cls=('uk-text-lead', stringify(cls)), **kwargs) +def PSmall(*c, # Contents of P tag (often text) + cls='', # Classes in addition to paragraph styling + **kwargs # Additional args for P tag + ): # P(..., cls='uk-text-small') + "P Tag with small style applied" + return fh.P(*c, cls=('uk-text-small', stringify(cls)), **kwargs) +def PMuted(*c, # Contents of P tag (often text) + cls='', # Classes in addition to paragraph styling + **kwargs # Additional args for P tag + ): # P(..., cls='uk-text-muted') + "P Tag with muted style applied" + return fh.P(*c, cls=('uk-text-muted', stringify(cls)), **kwargs) + +# %% ../nbs/02_franken.ipynb +def CodeSpan(*c, # Contents of CodeSpan tag (inline text code snippets) + cls=(), # Classes in addition to CodeSpan styling + **kwargs # Additional args for CodeSpan tag + ): # Code(..., cls='uk-codespan') "A CodeSpan with Styling" - return fh.Code(*args, cls=('uk-codespan"'), **kwargs) + return fh.Code(*c, cls=('uk-codespan', stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def Blockquote(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: - "A Blockquote with Styling" +def Blockquote(*c:FT|str, # Contents of Blockquote tag (often text) + cls:Enum|str|tuple=(), # Classes in addition to Blockquote styling + **kwargs # Additional args for Blockquote tag + ): # Blockquote(..., cls='uk-blockquote') + "Blockquote with Styling" return fh.Blockquote(*c, cls=('uk-blockquote',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def H1(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: - "A H1 with Styling" +def H1(*c:FT|str, # Contents of H1 tag (often text) + cls:Enum|str|tuple=(), # Classes in addition to H1 styling + **kwargs # Additional args for H1 tag + ): # H1(..., cls='uk-h1') + "H1 with styling and appropriate size" return fh.H1(*c, cls=('uk-h1',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def H2(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: - "A H2 with Styling" +def H2(*c:FT|str, # Contents of H2 tag (often text) + cls:Enum|str|tuple=(), # Classes in addition to H2 styling + **kwargs # Additional args for H2 tag + ): # H2(..., cls='uk-h2') + "H2 with styling and appropriate size" return fh.H2(*c, cls=('uk-h2',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def H3(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: - "A H3 with Styling" +def H3(*c:FT|str, # Contents of H3 tag (often text) + cls:Enum|str|tuple=(), # Classes in addition to H3 styling + **kwargs # Additional args for H3 tag + ): # H3(..., cls='uk-h3') + "H3 with styling and appropriate size" return fh.H3(*c, cls=('uk-h3',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def H4(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: - "A H4 with Styling" +def H4(*c:FT|str, # Contents of H4 tag (often text) + cls:Enum|str|tuple=(), # Classes in addition to H4 styling + **kwargs # Additional args for H4 tag + ): # H4(..., cls='uk-h4') + "H4 with styling and appropriate size" return fh.H4(*c, cls=('uk-h4',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb @@ -128,19 +160,15 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('bu ghost = auto() # %% ../nbs/02_franken.ipynb -def Button(*c: Union[str, FT], - cls: Union[str, Enum]=ButtonT.default, - submit=True, - **kwargs - ) -> FT: - "A Button with Styling (defaults to `submit` for form submission)" - if 'type' not in kwargs: - kwargs['type'] = 'submit' if submit else 'button' +def Button(*c: Union[str, FT], # Contents of `Button` tag (often text) + cls: Union[str, Enum]=ButtonT.default, # Classes in addition to `Button` styling (use `ButtonT` for built in styles) + submit=True, # Whether the button should submit a form + **kwargs # Additional args for `Button` tag + ) -> FT: # Button(..., cls='uk-button') + "Button with Styling (defaults to `submit` for form submission)" + if 'type' not in kwargs: kwargs['type'] = 'submit' if submit else 'button' return fh.Button(*c, cls=('uk-button', stringify(cls)), **kwargs) -# %% ../nbs/02_franken.ipynb -def Main(*args, **kwargs): return fh.Main(*args, **kwargs) - # %% ../nbs/02_franken.ipynb class ContainerT(VEnum): 'Max width container sizes from https://franken-ui.dev/docs/container' @@ -152,29 +180,39 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('co expand = auto() # %% ../nbs/02_franken.ipynb -def Container(*c, cls=('mt-5', ContainerT.xlarge), **kwargs): - "A Div to be used as a container that often wraps large sections or a page of content" +def Container(*c, # Contents of Container tag (often other FT Components) + cls=('mt-5', ContainerT.xlarge), # Classes in addition to Container styling + **kwargs # Additional args for Container (`Div` tag) + ): # Container(..., cls='uk-container') + "Div to be used as a container that often wraps large sections or a page of content" return Div(*c, cls=('uk-container',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def Titled(title:str="FastHTML app", *args, cls='uk-container-xlarge', **kwargs)->FT: +def Titled(title:str="FastHTML app", # Title of the page + *c, # Contents of the page (often other tags) + cls=ContainerT.xlarge, # Classes in addition to Container styling + **kwargs # Additional args for Container (`Div` tag) + )->FT: # Title, Main(Container(H1(title), content)) "Creates a standard page structure for titled page. Main(Container(title, content))" - return fh.Title(title), fh.Main(Container(H1(title), *args, cls=cls, **kwargs)) + return fh.Title(title), fh.Main(Container(H1(title), *c, cls=cls, **kwargs)) # %% ../nbs/02_franken.ipynb class DividerT(VEnum): - " Divider Styles from https://franken-ui.dev/docs/divider" + "Divider Styles from https://franken-ui.dev/docs/divider" def _generate_next_value_(name, start, count, last_values): return str2ukcls('divider', name) icon=auto() small=auto() vertical=auto() # %% ../nbs/02_franken.ipynb -def Divider(*args, cls=('my-4', DividerT.icon), **kwargs): +def Divider(*c, # contents of Divider tag (often nothing) + cls=('my-4', DividerT.icon), # Classes in addition to Divider styling + **kwargs # Additional args for Divider tag + ): # Hr(..., cls='uk-divider-icon') or Div(..., cls='uk-divider-vertical') "Divider with default styling and margin" cls = stringify(cls) container = Div if 'uk-divider-vertical' in cls else Hr - return container(*args, cls=cls, **kwargs) + return container(*c, cls=cls, **kwargs) # %% ../nbs/02_franken.ipynb def DividerSplit(*c, cls=(), line_cls=(), text_cls=()): @@ -188,32 +226,24 @@ def DividerSplit(*c, cls=(), line_cls=(), text_cls=()): def DividerLine(lwidth=2, y_space=4): return Hr(cls=f"my-{y_space} h-[{lwidth}px] w-full bg-secondary") # %% ../nbs/02_franken.ipynb -# def Alert(*args, cls=(), **kwargs): -# "A styled alert component that can contain a AlertTitle, AlertDescription and AlertCloseButton" -# return Div(*args, cls=('uk-alert', stringify(cls)), uk_alert=True, **kwargs) - -# def AlertCloseButton(*args, cls=(), **kwargs): -# "A button component for closing an Alert" -# return A(*args, cls=('uk-alert-close', stringify(cls)), **kwargs) - -# def AlertTitle(*args, cls='', **kwargs): -# "A title component for use within an Alert" -# return Div(*args, cls=('uk-alert-title', stringify(cls)), **kwargs) - -# def AlertDescription(*args, cls='', **kwargs): -# "A description component for use within an Alert" -# return Div(*args, cls=('uk-alert-description', stringify(cls)), **kwargs) - -# %% ../nbs/02_franken.ipynb -def Article(*c, cls=(), **kwargs): +def Article(*c, # contents of Article tag (often other tags) + cls=(), # Classes in addition to Article styling + **kwargs # Additional args for Article tag + ): # Article(..., cls='uk-article') "A styled article container for blog posts or similar content" return fh.Article(*c, cls=('uk-article',stringify(cls)), **kwargs) -def ArticleTitle(*c, cls=(), **kwargs): +def ArticleTitle(*c, # contents of ArticleTitle tag (often other tags) + cls=(), # Classes in addition to ArticleTitle styling + **kwargs # Additional args for ArticleTitle tag + ): # H1(..., cls='uk-article-title') "A title component for use within an Article" return H1(*c, cls=('uk-article-title',stringify(cls)), **kwargs) -def ArticleMeta(*c, cls=(), **kwargs): +def ArticleMeta(*c, # contents of ArticleMeta tag (often other tags) + cls=(), # Classes in addition to ArticleMeta styling + **kwargs # Additional args for ArticleMeta tag + ): # P(..., cls='uk-article-meta') "A metadata component for use within an Article showing things like date, author etc" return P(*c, cls=('uk-article-meta',stringify(cls)), **kwargs) @@ -232,48 +262,86 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('se remove_vertical = auto() # %% ../nbs/02_franken.ipynb -def Section(*c, cls=(), **kwargs): +def Section(*c, # contents of Section tag (often other tags) + cls=(), # Classes in addition to Section styling + **kwargs # Additional args for Section tag + ): # Div(..., cls='uk-section') + "Section with styling and margins" return fh.Div(*c, cls=('uk-section',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def Form(*c, cls='space-y-3', **kwargs): +def Form(*c, # contents of Form tag (often Buttons, FormLabels, and LabelInputs) + cls='space-y-3', # Classes in addition to Form styling (default is 'space-y-3' to prevent scrunched up form elements) + **kwargs # Additional args for Form tag + ): # Form(..., cls='space-y-3') "A Form with default spacing between form elements" return fh.Form(*c, cls=stringify(cls), **kwargs) # %% ../nbs/02_franken.ipynb -def Fieldset(*c, cls=(), **kwargs): +def Fieldset(*c, # contents of Fieldset tag (often other tags) + cls=(), # Classes in addition to Fieldset styling + **kwargs # Additional args for Fieldset tag + ): # Fieldset(..., cls='uk-fieldset') "A Fieldset with default styling" return fh.Fieldset(*c, cls=('uk-fieldset',stringify(cls)), **kwargs) -def Legend(*c, cls=(), **kwargs): +def Legend(*c, # contents of Legend tag (often other tags) + cls=(), # Classes in addition to Legend styling + **kwargs # Additional args for Legend tag + ): # Legend(..., cls='uk-legend') "A Legend with default styling" return fh.Legend(*c, cls=('uk-legend',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def Input(*c, cls=(), **kwargs): +def Input(*c, # contents of Input tag (often nothing) + cls=(), # Classes in addition to Input styling + **kwargs # Additional args for Input tag + ): # Input(..., cls='uk-input') "An Input with default styling" return fh.Input(*c, cls=('uk-input',stringify(cls)), **kwargs) -def Select(*option, cls=(), **kwargs): - "A Select with default styling" + +def Select(*option, # options for the select dropdown + cls=(), # Classes in addition to Select styling + **kwargs # Additional args for Select tag + ): # Select(..., cls='uk-select') + "A Select with default styling, though often `UkSelect` is a better choice" return fh.Select(*option, cls=('uk-select',stringify(cls)), **kwargs) -def Radio(*c, cls=(), **kwargs): +def Radio(*c, # contents of Radio tag (often nothing) + cls=(), # Classes in addition to Radio styling + **kwargs # Additional args for Radio tag + ): # Input(..., cls='uk-radio', type='radio') "A Radio with default styling" return fh.Input(*c, cls=('uk-radio',stringify(cls)), type='radio', **kwargs) -def CheckboxX(*c, cls=(), **kwargs): +def CheckboxX(*c, # contents of CheckboxX tag (often nothing) + cls=(), # Classes in addition to CheckboxX styling + **kwargs # Additional args for CheckboxX tag + ): # Input(..., cls='uk-checkbox', type='checkbox') "A Checkbox with default styling" return fh.Input(*c, cls=('uk-checkbox',stringify(cls)), type='checkbox', **kwargs) -def Range(*c, cls=(), **kwargs): +def Range(*c, # contents of Range tag (often nothing) + cls=(), # Classes in addition to Range styling + **kwargs # Additional args for Range tag + ): # Input(..., cls='uk-range', type='range') "A Range with default styling" return fh.Input(*c, cls=('uk-range',stringify(cls)), type='range', **kwargs) -def TextArea(*c, cls=(), **kwargs): +def TextArea(*c, # contents of TextArea tag (often text) + cls=(), # Classes in addition to TextArea styling + **kwargs # Additional args for TextArea tag + ): # TextArea(..., cls='uk-textarea') "A Textarea with default styling" return fh.Textarea(*c, cls=('uk-textarea',stringify(cls)), **kwargs) -def Switch(*c, cls=(), **kwargs): +def Switch(*c, # contents of Switch tag (often nothing) + cls=(), # Classes in addition to Switch styling + **kwargs # Additional args for Switch tag + ): # Input(..., cls='uk-toggle-switch uk-toggle-switch-primary min-w-9', type='checkbox') "A Switch with default styling" return fh.Input(*c, cls=('uk-toggle-switch uk-toggle-switch-primary min-w-9',stringify(cls)), type='checkbox', **kwargs) # %% ../nbs/02_franken.ipynb -def FormLabel(*c, cls=(), **kwargs): +def FormLabel(*c, # contents of FormLabel tag (often text) + cls=(), # Classes in addition to FormLabel styling + **kwargs # Additional args for FormLabel tag + ): # Label(..., cls='uk-form-label') "A Label with default styling" return fh.Label(*c, cls=('uk-form-label',stringify(cls)), **kwargs) @@ -285,7 +353,10 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('la danger = auto() # %% ../nbs/02_franken.ipynb -def Label(*c, cls=(), **kwargs): +def Label(*c, # contents of Label tag (often text) + cls=(), # Classes in addition to Label styling + **kwargs # Additional args for Label tag + ): # Label(..., cls='uk-label') "FrankenUI labels, which look like pills" return fh.Label(*c, cls=('uk-label',stringify(cls)), **kwargs) @@ -299,38 +370,81 @@ def UkFormSection(title, description, *c, button_txt='Update', outer_margin=6, i # %% ../nbs/02_franken.ipynb def GenericLabelInput( - label:str|FT, - lbl_cls='', - input_cls='', - container=Div, - cls='', - id='', - input_fn=noop, - **kwargs - ): - "`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `UkTextArea` which is created for you in this library" + label:str|FT, # FormLabel content (often text) + lbl_cls='', # Additional classes for FormLabel + input_cls='', # Additional classes for user input (Input, Select, etc) + container=Div, # Container to wrap label and input in (default is Div) + cls='', # Classes on container (default is '') + id='', # id for label and input (`id`, `name` and `for` attributes are set to this value) + input_fn=noop, # User input FT component + **kwargs # Additional args for user input + ): + "`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `LabelInput` which is created for you in this library" if isinstance(label, str) or label.tag != 'label': label = FormLabel(cls=stringify(lbl_cls), fr=id)(label) inp = input_fn(id=id, cls=stringify(input_cls), **kwargs) if container: return container(label, inp, cls=stringify(cls)) return label, inp +# %% ../nbs/02_franken.ipynb +def LabelInput(label:str|FT, # FormLabel content (often text) + lbl_cls='', # Additional classes for `FormLabel` + input_cls='', # Additional classes for `Input` + cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements) + id='', # id for `FormLabel` and `Input` (`id`, `name` and `for` attributes are set to this value) + **kwargs # Additional args for `Input` + ): # Div(cls='space-y-2')(`FormLabel`, `Input`) + "A `FormLabel` and `Input` pair that provides default spacing and links/names them based on id" + return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls, + container=Div, cls=cls, id=id, input_fn=Input, **kwargs) + +# %% ../nbs/02_franken.ipynb +def LabelRange(label:str|FT, # FormLabel content (often text) + lbl_cls='', # Additional classes for `FormLabel` + input_cls='', # Additional classes for `Range` + cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements) + id='', # id for `FormLabel` and `Range` (`id`, `name` and `for` attributes are set to this value) + **kwargs # Additional args for `Range` + ): # Div(cls='space-y-2')(`FormLabel`, `Range`) + "A `FormLabel` and `Range` pair that provides default spacing and links/names them based on id" + return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls, + container=Div, cls=cls, id=id, input_fn=Range, **kwargs) + +# %% ../nbs/02_franken.ipynb +def LabelTextArea(label:str|FT, # FormLabel content (often text) + value='', # Value for the textarea + lbl_cls='', # Additional classes for `FormLabel` + input_cls='', # Additional classes for `TextArea` + cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements) + id='', # id for `FormLabel` and `TextArea` (`id`, `name` and `for` attributes are set to this value) + **kwargs # Additional args for `TextArea` + ): # Div(cls='space-y-2')(`FormLabel`, `TextArea`) + def text_area_with_value(**kw): return TextArea(value, **kw) + return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls, + container=Div, cls=cls, id=id, input_fn=text_area_with_value, **kwargs) + # %% ../nbs/02_franken.ipynb @delegates(GenericLabelInput, but=['input_fn','cls']) -def LabelInput(*args, cls='space-y-2', **kwargs): - "A Label and Input pair that provides default spacing and links/names them based on id" - return GenericLabelInput(*args, cls=stringify(cls),input_fn=Input, **kwargs) - -# %% ../nbs/02_franken.ipynb -def LabelRadio(label:str|FT, - lbl_cls='', - input_cls='', - container=Div, - cls='flex items-center space-x-2', - id='', - **kwargs - ): - "A Label and Radio pair that provides default spacing and links/names them based on id" +def LabelSwitch(label:str|FT, # FormLabel content (often text) + lbl_cls='', # Additional classes for `FormLabel` + input_cls='', # Additional classes for `Switch` + cls='space-x-2', # Classes on container (default is `'space-x-2'` to prevent scrunched up form elements) + id='', # id for `FormLabel` and `Switch` (`id`, `name` and `for` attributes are set to this value) + **kwargs # Additional args for `Switch` + ): # Div(cls='space-y-2')(`FormLabel`, `Switch`) + return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls, + container=Div, cls=cls, id=id, input_fn=Switch, **kwargs) + +# %% ../nbs/02_franken.ipynb +def LabelRadio(label:str|FT, # FormLabel content (often text) + lbl_cls='', # Additional classes for `FormLabel` + input_cls='', # Additional classes for `Radio` + container=Div, # Container to wrap label and input in (default is Div) + cls='flex items-center space-x-2', # Classes on container (default is 'flex items-center space-x-2') + id='', # id for `FormLabel` and `Radio` (`id`, `name` and `for` attributes are set to this value) + **kwargs # Additional args for `Radio` + ): # Div(cls='flex items-center space-x-2')(`FormLabel`, `Radio`) + "A FormLabel and Radio pair that provides default spacing and links/names them based on id" if isinstance(label, str) or label.tag != 'label': label = FormLabel(cls=stringify(lbl_cls), fr=id)(label) inp = Radio(id=id, cls=stringify(input_cls), **kwargs) @@ -338,15 +452,15 @@ def LabelRadio(label:str|FT, return inp, label # %% ../nbs/02_franken.ipynb -def LabelCheckboxX(label:str|FT, - lbl_cls='', - input_cls='', - container=Div, - cls='flex items-center space-x-2', - **kwargs - ): - "`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `UkTextArea` which is created for you in this library" - +def LabelCheckboxX(label:str|FT, # FormLabel content (often text) + lbl_cls='', # Additional classes for `FormLabel` + input_cls='', # Additional classes for `CheckboxX` + container=Div, # Container to wrap label and input in (default is Div) + cls='flex items-center space-x-2', # Classes on container (default is 'flex items-center space-x-2') + id='', # id for `FormLabel` and `CheckboxX` (`id`, `name` and `for` attributes are set to this value) + **kwargs # Additional args for `CheckboxX` + ): # Div(cls='flex items-center space-x-2')(`FormLabel`, `CheckboxX`) + "A FormLabel and CheckboxX pair that provides default spacing and links/names them based on id" id = kwargs.pop('id', fh.unqid()) if isinstance(label, str) or label.tag != 'label': label = FormLabel(cls=stringify(lbl_cls), fr=id)(label) @@ -355,39 +469,16 @@ def LabelCheckboxX(label:str|FT, return inp, label # %% ../nbs/02_franken.ipynb -# @delegates(GenericLabelInput, but=['input_fn','cls']) -# def LabelCheckboxX(*args, cls='space-x-2', **kwargs): return GenericLabelInput(*args, cls=stringify(cls), input_fn=CheckboxX, **kwargs) - -# %% ../nbs/02_franken.ipynb -@delegates(GenericLabelInput, but=['input_fn','cls']) -def LabelRange(*args, cls='space-y-2', **kwargs): - "Creates a labeled range input with nice default spacing and links/names them based on id" - return GenericLabelInput(*args, cls=stringify(cls), input_fn=Range, **kwargs) - -# %% ../nbs/02_franken.ipynb -@delegates(GenericLabelInput, but=['input_fn','cls']) -def LabelTextArea(*args, cls='space-y-2', value='', **kwargs): - "Creates a labeled textarea with nice default spacing and links/names them based on id" - def text_area_with_value(**kw): return TextArea(value, **kw) - return GenericLabelInput(*args, cls=stringify(cls), input_fn=text_area_with_value, **kwargs) - -# %% ../nbs/02_franken.ipynb -@delegates(GenericLabelInput, but=['input_fn','cls']) -def LabelSwitch(*args, cls='space-x-2', **kwargs): - "Creates a labeled switch with nice default spacing and links/names them based on id" - return GenericLabelInput(*args, cls=stringify(cls), input_fn=Switch, **kwargs) - -# %% ../nbs/02_franken.ipynb -def LabelSelect(*option, - label:str|FT, - lbl_cls='', - input_cls='', - container=Div, - cls='space-y-2', - id='', - **kwargs +def LabelSelect(*option, # Options for the select dropdown (can use `Options` helper function to create) + label:str|FT, # FormLabel content (often text) + lbl_cls='', # Additional classes for `FormLabel` + input_cls='', # Additional classes for `Select` + container=Div, # Container to wrap label and input in (default is Div) + cls='space-y-2', # Classes on container (default is 'space-y-2') + id='', # id for `FormLabel` and `Select` (`id`, `name` and `for` attributes are set to this value) + **kwargs # Additional args for `Select` ): - "Creates a Labeled Select - Generally you should use LabelUkSelect for a more full features and styled select" + "A FormLabel and Select pair that provides default spacing and links/names them based on id (usually UkLabelSelect is a better choice)" if isinstance(label, str) or label.tag != 'label': label = FormLabel(lbl_cls=stringify(lbl_cls), fr=id)(label) inp = Select(*option, id=id, cls=stringify(input_cls), **kwargs) @@ -428,7 +519,7 @@ def LabelUkSelect(*option, # Options for the select dropdown (can use placeholder="", # Placeholder text for the select input searchable=False, # Whether the select should be searchable **kwargs): # Additional arguments passed to Uk_select - "Creates a select dropdown with uk styling and a label and provides option for adding a search box" + "A FormLabel and Select pair that provides default spacing and links/names them based on id" lbl_cls, inp_cls, cls = map(stringify, (lbl_cls, inp_cls, cls)) if label: lbl = FormLabel(cls=f'{lbl_cls}', fr=id)(label) @@ -461,59 +552,75 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('li striped = auto() # %% ../nbs/02_franken.ipynb -def UkList(*c, cls=(), **kwargs): +def UkList(*li, # `Li` tags to put in the list + cls=(), # Additional classes on the list + **kwargs # Additional args for `Ul` tag + ): # Ul(..., cls='uk-list') "Creates a list with styling" - return fh.Ul(*c, cls=('uk-list',stringify(cls)), **kwargs) + return fh.Ul(*li, cls=('uk-list',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def ModalContainer(*c, cls=(), **kwargs): +def ModalContainer(*c, # Components to put in the modal (often `ModalDialog`) + cls=(), # Additional classes on the `ModalContainer` + **kwargs # Additional args for `Div` tag + ): # Div(..., cls='uk-modal uk-modal-container') "Creates a modal container that components go in" return fh.Div(*c, cls=('uk-modal uk-modal-container',stringify(cls)), uk_modal=True, **kwargs) -def ModalDialog(*c, cls=(), **kwargs): +def ModalDialog(*c, # Components to put in the `ModalDialog` (often `ModalBody`, `ModalHeader`, etc) + cls=(), # Additional classes on the `ModalDialog` + **kwargs # Additional args for `Div` tag + ): # Div(..., cls='uk-modal-dialog') "Creates a modal dialog" return fh.Div(*c, cls=('uk-modal-dialog', stringify(cls)), **kwargs) -def ModalHeader(*c, cls=(), **kwargs): +def ModalHeader(*c, # Components to put in the `ModalHeader` + cls=(), # Additional classes on the `ModalHeader` + **kwargs # Additional args for `Div` tag + ): # Div(..., cls='uk-modal-header') "Creates a modal header" return fh.Div(*c, cls=('uk-modal-header', stringify(cls)), **kwargs) -def ModalBody(*c, cls=(), **kwargs): +def ModalBody(*c, # Components to put in the `ModalBody` (often forms, sign in buttons, images, etc.) + cls=(), # Additional classes on the `ModalBody` + **kwargs # Additional args for `Div` tag + ): # Div(..., cls='uk-modal-body') "Creates a modal body" return fh.Div(*c, cls=('uk-modal-body', stringify(cls)), **kwargs) -def ModalFooter(*c, cls=(), **kwargs): +def ModalFooter(*c, # Components to put in the `ModalFooter` (often buttons) + cls=(), # Additional classes on the `ModalFooter` + **kwargs # Additional args for `Div` tag + ): # Div(..., cls='uk-modal-footer') "Creates a modal footer" return fh.Div(*c, cls=('uk-modal-footer', stringify(cls)), **kwargs) -def ModalTitle(*c, cls=(), **kwargs): +def ModalTitle(*c, # Components to put in the `ModalTitle` (often text) + cls=(), # Additional classes on the `ModalTitle` + **kwargs # Additional args for `H2` tag + ): # H2(..., cls='uk-modal-title') "Creates a modal title" - return fh.H2(*c, cls=('uk-modal-title', stringify(cls)), **kwargs) -def ModalCloseButton(*c, cls=(), **kwargs): + return fh.H2(*c, cls=('uk-modal-title', stringify(cls)), **kwargs) +def ModalCloseButton(*c, # Components to put in the button (often text and/or an icon) + cls=(), # Additional classes on the button + htmx=False, # Whether to use HTMX to close the modal (must add hx_get to a route that closes the modal) + **kwargs # Additional args for `Button` tag + ): # Button(..., cls='uk-modal-close') + `hx_target` and `hx_swap` if htmx is True "Creates a button that closes a modal with js" - return Button(*c, cls=('uk-modal-close', stringify(cls)), **kwargs) - -# %% ../nbs/02_franken.ipynb -def HTMXModalCloseButton(*c, cls=(), target=None, **kwargs): - "Creates a button that closes a modal using HTMX" - return Button( - *c, - cls=('uk-modal-close', stringify(cls)), - hx_target='closest .uk-modal', - hx_swap='delete', - type='button', - **kwargs - ) - -# %% ../nbs/02_franken.ipynb -def Modal(*c, - header=None, # Components that go in the header - footer=None, # Components that go in the footer - cls=(), # class for outermost container - dialog_cls=(), # classes for the dialog - header_cls='p-6', # classes for the header - body_cls='space-y-6', # classes for the body - footer_cls=(), # classes for the footer + if htmx: + kwargs['hx_target'] = 'closest .uk-modal' + kwargs['hx_swap'] = 'delete' + return Button(*c, cls=('uk-modal-close', stringify(cls)), **kwargs) + +# %% ../nbs/02_franken.ipynb +def Modal(*c, # Components to put in the `ModalBody` (often forms, sign in buttons, images, etc.) + header=None, # Components that go in the `ModalHeader` (often a `ModalTitle`) + footer=None, # Components that go in the `ModalFooter` (often a `ModalCloseButton`) + cls=(), # Additional classes on the outermost `ModalContainer` + dialog_cls=(), # Additional classes on the `ModalDialog` + header_cls='p-6', # Additional classes on the `ModalHeader` + body_cls='space-y-6', # Additional classes on the `ModalBody` + footer_cls=(), # Additional classes on the `ModalFooter` id='', # id for the outermost container - open=False, - **kwargs # classes for the outermost container - ): # Modal - "Create a Modal using the appropriate Modal classes to put the boilerplate in the appropriate places for you" + open=False, # Whether the modal is open (typically used for HTMX controlled modals) + **kwargs # Additional args for the outermost `Div` tag + ): # Fully styled modal FT Component + "Creates a modal with the appropriate classes to put the boilerplate in the appropriate places for you" if open: cls = stringify((cls, 'uk-open')) kwargs['style'] = stringify((kwargs.get('style',''), 'display: block;')) @@ -563,35 +670,52 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('po center_vertical = auto() # %% ../nbs/02_franken.ipynb -def Placeholder(*c, cls=(), **kwargs): +def Placeholder(*c, # Components to put in the placeholder + cls=(), # Additional classes on the placeholder + **kwargs # Additional args for `Div` tag + ): # Div(..., cls='uk-placeholder') + "Creates a placeholder" return fh.Div(*c, cls=('uk-placeholder',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def Progress(*c, cls=(), value="", max="", **kwargs): +def Progress(*c, # Components to put in the progress bar (often nothing) + cls=(), # Additional classes on the progress bar + value="", # Value of the progress bar + max="100", # Max value of the progress bar (defaults to 100 for percentage) + **kwargs # Additional args for `Progress` tag + ): # Progress(..., cls='uk-progress') + "Creates a progress bar" return fh.Progress(*c, value=value, max=max, cls=('uk-progress',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def UkIcon(icon,height=None,width=None,stroke_width=None,cls=(), **kwargs): +def UkIcon(icon:str, # Icon name from [lucide icons](https://lucide.dev/icons/) + height:int=None, + width:int=None, + stroke_width:int=None, # Thickness of lines + cls=(), # Additional classes on the `Uk_icon` tag + **kwargs # Additional args for `Uk_icon` tag + ): # a lucide icon of the specified size "Creates an icon using lucide icons" return Uk_icon(icon=icon, height=height, width=width, stroke_width=stroke_width, cls=cls, **kwargs) # %% ../nbs/02_franken.ipynb -def UkIconLink(icon, - height=None, - width=None, - stroke_width=None, - cls=(), - button=False, - **kwargs): +def UkIconLink(icon:str, # Icon name from [lucide icons](https://lucide.dev/icons/) + height:int=None, + width:int=None, + stroke_width:int=None, # Thickness of lines + cls=(), # Additional classes on the icon + button:bool=False, # Whether to use a button (defaults to a link) + **kwargs # Additional args for `A` or `Button` tag + ): # a lucide icon button or link of the specified size "Creates an icon link using lucide icons" fn = fh.Button if button else fh.A return fn(cls=(f"uk-icon-{'button' if button else 'link'}", stringify(cls)), **kwargs)( UkIcon(icon=icon, height=height, width=width, stroke_width=stroke_width)) # %% ../nbs/02_franken.ipynb -def DiceBearAvatar(seed_name, # Seed name (ie 'Isaac Flath') - h=20, # Height - w=20, # Width +def DiceBearAvatar(seed_name:str, # Seed name (ie 'Isaac Flath') + h:int=20, # Height + w:int=20, # Width ): # Span with Avatar "Creates an Avatar using https://dicebear.com/" url = 'https://api.dicebear.com/8.x/lorelei/svg?seed=' @@ -615,12 +739,22 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('fl nowrap, wrap, wrap_reverse = auto(), auto(), auto() # %% ../nbs/02_franken.ipynb -def Grid(*div, cols_min=1, cols_max=None, cols_sm=None, cols_md=None, cols_lg=None, cols_xl=None, cols=None, cls='gap-4', **kwargs): +def Grid(*div, # `Div` components to put in the grid + cols_min:int=1, # Minimum number of columns at any screen size + cols_max:int=4, # Maximum number of columns allowed at any screen size + cols_sm:int=None, # Number of columns on small screens + cols_md:int=None, # Number of columns on medium screens + cols_lg:int=None, # Number of columns on large screens + cols_xl:int=None, # Number of columns on extra large screens + cols:int=None, # Number of columns on all screens + cls='gap-4', # Additional classes on the grid (tip: `gap` provides spacing for grids) + **kwargs # Additional args for `Div` tag + ): # Responsive grid component "Creates a responsive grid layout with smart defaults based on content" if cols: cols_min = cols_sm = cols_md = cols_lg = cols_xl = cols else: n = len(div) - cols_max = min(n, cols_max or 4) + cols_max = min(n, cols_max) cols_sm = cols_sm or min(n, cols_min, cols_max) cols_md = cols_md or min(n, cols_min+1, cols_max) cols_lg = cols_lg or min(n, cols_min+2, cols_max) @@ -629,7 +763,7 @@ def Grid(*div, cols_min=1, cols_max=None, cols_sm=None, cols_md=None, cols_lg=No # %% ../nbs/02_franken.ipynb def DivFullySpaced(*c, # Components - cls='uk-width-1-1',# Classes for outer div + cls='uk-width-1-1',# Classes for outer div (`uk-width-1-1` makes it use all available width) **kwargs # Additional args for outer div ): # Div with spaced components via flex classes "Creates a flex div with it's components having as much space between them as possible" @@ -638,15 +772,16 @@ def DivFullySpaced(*c, # Components # %% ../nbs/02_franken.ipynb def DivCentered(*c, # Components - cls='space-y-4', # Classes for outer div + cls='space--4', # Classes for outer div (`space-y-4` provides spacing between components) + vstack=True, # Whether to stack the components vertically **kwargs # Additional args for outer div ): # Div with components centered in it "Creates a flex div with it's components centered in it" cls=stringify(cls) - return Div(cls=(FlexT.block,FlexT.column,FlexT.middle,FlexT.center,cls),**kwargs)(*c) + return Div(cls=(FlexT.block,(FlexT.column if vstack else FlexT.row),FlexT.middle,FlexT.center,cls),**kwargs)(*c) # %% ../nbs/02_franken.ipynb -def DivLAligned(*c, # Components +def DivLAligned(*c, # Components cls='space-x-4', # Classes for outer div **kwargs # Additional args for outer div ): # Div with components aligned to the left @@ -655,7 +790,7 @@ def DivLAligned(*c, # Components return Div(cls=(FlexT.block,FlexT.left,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/02_franken.ipynb -def DivRAligned(*c, # Components +def DivRAligned(*c, # Components cls='space-x-4', # Classes for outer div **kwargs # Additional args for outer div ): # Div with components aligned to the right @@ -664,13 +799,19 @@ def DivRAligned(*c, # Components return Div(cls=(FlexT.block,FlexT.right,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/02_franken.ipynb -def DivVStacked(*c, cls='space-y-4', **kwargs): +def DivVStacked(*c, # Components + cls='space-y-4', # Additional classes on the div (tip: `space-y-4` provides spacing between components) + **kwargs # Additional args for the div + ): # Div with components stacked vertically "Creates a flex div with it's components stacked vertically" cls=stringify(cls) return Div(cls=(FlexT.block,FlexT.column,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/02_franken.ipynb -def DivHStacked(*c, cls='space-x-4', **kwargs): +def DivHStacked(*c, # Components + cls='space-x-4', # Additional classes on the div (`space-x-4` provides spacing between components) + **kwargs # Additional args for the div + ): # Div with components stacked horizontally "Creates a flex div with it's components stacked horizontally" cls=stringify(cls) return Div(cls=(FlexT.block,FlexT.row,FlexT.middle,cls), **kwargs)(*c) @@ -683,66 +824,124 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('na secondary = auto() # %% ../nbs/02_franken.ipynb -def NavContainer(*li, - cls=NavT.primary, - parent=True, - uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options - **kwargs): - "Creates a navigation container. A Nav is a list (NavBar is something different)" +def NavContainer(*li, # List items are navigation elements (Special `Li` such as `NavParentLi`, `NavDividerLi`, `NavHeaderLi`, `NavSubtitle`, `NavCloseLi` can also be used) + cls=NavT.primary, # Additional classes on the nav + parent=True, # Whether this nav is a *parent* or *sub* nav + uk_nav=False, #True for default collapsible behavior, see [frankenui docs](https://franken-ui.dev/docs/nav#component-options) for more advanced options + **kwargs # Additional args + ): # FT Component that is a list of `Li` styled for a sidebar navigation menu + "Creates a navigation container (useful for creating a sidebar navigation). A Nav is a list (NavBar is something different)" return fh.Ul(*li, uk_nav=uk_nav, cls=(f"uk-nav{'' if parent else '-sub'}", stringify(cls)),**kwargs) # %% ../nbs/02_franken.ipynb -def NavParentLi(*nav_container, cls=(), **kwargs): return fh.Li(*nav_container, cls=('uk-parent', stringify(cls)),**kwargs) -def NavDividerLi(*c,cls=(), **kwargs): return fh.Li(*c, cls=('uk-nav-divider', stringify(cls)),**kwargs) -def NavHeaderLi(*c,cls=(), **kwargs): return fh.Li(*c, cls=('uk-nav-header', stringify(cls)),**kwargs) -def NavSubtitle(*c,cls=TextFont.muted_sm, **kwargs): return fh.Div(*c, cls=('uk-nav-subtitle', stringify(cls)),**kwargs) -def NavCloseLi(*c,cls=(), **kwargs): return fh.Li(*c, cls=('uk-drop-close', stringify(cls)),**kwargs) - -# %% ../nbs/02_franken.ipynb -def NavBarContainer(*c, - cls=(), - container_cls=ContainerT.expand, - uk_navbar=True, - **kwargs): +def NavParentLi(*nav_container, # `NavContainer` container for a nested nav with `parent=False`) + cls=(), # Additional classes on the li + **kwargs # Additional args for the li + ): # Navigation list item + "Creates a navigation list item with a parent nav for nesting" + return fh.Li(*nav_container, cls=('uk-parent', stringify(cls)),**kwargs) +def NavDividerLi(*c, # Components + cls=(), # Additional classes on the li + **kwargs # Additional args for the li + ): # Navigation list item with a divider + "Creates a navigation list item with a divider" + return fh.Li(*c, cls=('uk-nav-divider', stringify(cls)),**kwargs) +def NavHeaderLi(*c, # Components + cls=(), # Additional classes on the li + **kwargs # Additional args for the li + ): # Navigation list item with a header + "Creates a navigation list item with a header" + return fh.Li(*c, cls=('uk-nav-header', stringify(cls)),**kwargs) +def NavSubtitle(*c, # Components + cls=TextFont.muted_sm, # Additional classes on the div + **kwargs # Additional args for the div + ): # Navigation subtitle + "Creates a navigation subtitle" + return fh.Div(*c, cls=('uk-nav-subtitle', stringify(cls)),**kwargs) +def NavCloseLi(*c, # Components + cls=(), # Additional classes on the li + **kwargs # Additional args for the li + ): # Navigation list item with a close button + "Creates a navigation list item with a close button" + return fh.Li(*c, cls=('uk-drop-close', stringify(cls)),**kwargs) + +# %% ../nbs/02_franken.ipynb +def NavBarContainer(*navbarside, # Components (typically `NavBarLSide` or `NavBarRSide` or `NavBarCenter`) + cls=(), # Additional classes on the container + container_cls=ContainerT.expand, # Additional classes on the container + uk_navbar=True, # Whether to use a navbar + **kwargs # Additional args for the container + ): # NavBar container "Create a NavBarContainer to put NavBar sides in" - return fh.Div(Container(Div(*c, uk_navbar=uk_navbar),cls=stringify(container_cls)), cls=('',stringify(cls)), **kwargs) #uk-navbar-container -def NavBarLSide(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-navbar-left', stringify(cls)), **kwargs) -def NavBarRSide(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-navbar-right', stringify(cls)), **kwargs) -def NavBarCenter(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-navbar-center',stringify(cls)), **kwargs) - -# %% ../nbs/02_franken.ipynb -def NavBarNav(*li, cls=(), **kwargs): - "A Nav that is part of a NavBar" + return fh.Div(Container(Div(*navbarside, uk_navbar=uk_navbar),cls=stringify(container_cls)), cls=('',stringify(cls)), **kwargs) #uk-navbar-container + +def NavBarLSide(*c, # Components + cls=(), # Additional classes on the div + **kwargs # Additional args for the div + ): # NavBar left side + "Creates a NavBar left side" + return fh.Div(*c, cls=('uk-navbar-left', stringify(cls)), **kwargs) +def NavBarRSide(*c, # Components + cls=(), # Additional classes on the div + **kwargs # Additional args for the div + ): # NavBar right side + "Creates a NavBar right side" + return fh.Div(*c, cls=('uk-navbar-right', stringify(cls)), **kwargs) +def NavBarCenter(*c, # Components + cls=(), # Additional classes on the div + **kwargs # Additional args for the div + ): # NavBar center + "Creates a NavBar center" + return fh.Div(*c, cls=('uk-navbar-center',stringify(cls)), **kwargs) + +# %% ../nbs/02_franken.ipynb +def NavBarNav(*li, # Components + cls=(), # Additional classes on the nav + **kwargs # Additional args for the nav + ): # Nav that is part of a NavBar + "A Nav that is part of a NavBar that could go in a `NavBarLSide`, `NavBarRSide`, or `NavBarCenter`" return fh.Nav(*li, cls=('uk-navbar-nav', stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def NavBarSubtitle(title, subtitle, cls=(), subtitle_cls=TextFont.muted_sm, **kwargs): +def NavBarSubtitle(title, # Title + subtitle, # Subtitle + cls=(), # Additional classes on the div + subtitle_cls=TextFont.muted_sm, # Additional classes on the subtitle + **kwargs # Additional args for the div + ): # NavBar subtitle + "Creates a NavBar subtitle" return fh.Div(title,fh.Div(subtitle, cls=('uk-navbar-subtitle', stringify(subtitle_cls))), cls=stringify(cls), **kwargs) # %% ../nbs/02_franken.ipynb -def NavBarNavContainer(*li, - cls=NavT.primary, - parent=True, - uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options - **kwargs): +def NavBarNavContainer(*li, # Components + cls=NavT.primary, # Additional classes on the nav + parent=True, # Whether to use a parent nav + uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options + **kwargs # Additional args for the nav + ): # NavBar nav container return Div(cls="uk-navbar-dropdown")(NavContainer(*li, cls=('uk-navbar-dropdown-nav',stringify(cls)), uk_nav=uk_nav, parent=parent, **kwargs)) # %% ../nbs/02_franken.ipynb def NavBarParentIcon(): return Span(uk_navbar_parent_icon=True) # %% ../nbs/02_franken.ipynb -def DropDownNavContainer(*li, - cls=NavT.primary, - parent=True, +def DropDownNavContainer(*li, # Components + cls=NavT.primary, # Additional classes on the nav + parent=True, # Whether to use a parent nav uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options - uk_dropdown=True, - **kwargs): + uk_dropdown=True, # Whether to use a dropdown + **kwargs # Additional args for the nav + ): # DropDown nav container "A Nav that is part of a DropDown" return Div(cls = 'uk-drop uk-dropdown',uk_dropdown=uk_dropdown)(NavContainer(*li, cls=('uk-dropdown-nav',stringify(cls)), uk_nav=uk_nav, parent=parent, **kwargs)) # %% ../nbs/02_franken.ipynb -def TabContainer(*li,cls='', alt=False, **kwargs): - "A Tab that is part of a TabContainer" +def TabContainer(*li, # Components + cls='', # Additional classes on the `Ul` + alt=False, # Whether to use an alternative tab style + **kwargs # Additional args for the `Ul` + ): # Tab container + "A TabContainer where children will be different tabs" cls = stringify(cls) return Ul(cls=(f"uk-tab{'-alt' if alt else ''}",stringify(cls)),**kwargs)(*li) @@ -756,31 +955,51 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('ca danger = auto() # %% ../nbs/02_franken.ipynb -def CardTitle(*c, cls=(), **kwargs): +def CardTitle(*c, # Components (often a string) + cls=(), # Additional classes on the div + **kwargs # Additional args for the div + ): + "Creates a card title" return fh.Div(*c, cls=('uk-card-title',stringify(cls)), **kwargs) -def CardHeader(*c, cls=(), **kwargs): +def CardHeader(*c, # Components that goes in the header (often a `ModalTitle` and description) + cls=(), # Additional classes on the div + **kwargs # Additional args for the div + ): # Container for the header of a card + "Creates a card header" return fh.Div(*c, cls=('uk-card-header',stringify(cls)), **kwargs) -def CardBody(*c, cls=(), **kwargs): +def CardBody(*c, # Components that go in the body (Main content of the card such as a form, and image, a signin form, etc.) + cls=(), # Additional classes on the div + **kwargs # Additional args for the div + ): # Container for the body of a card + "Creates a card body" return fh.Div(*c, cls=('uk-card-body',stringify(cls)), **kwargs) -def CardFooter(*c, cls=(), **kwargs): +def CardFooter(*c, # Components that go in the footer (often a `ModalCloseButton`) + cls=(), # Additional classes on the div + **kwargs # Additional args for the div + ): # Container for the footer of a card + "Creates a card footer" return fh.Div(*c, cls=('uk-card-footer',stringify(cls)), **kwargs) -def CardContainer(*c, cls=CardT.default, **kwargs): +def CardContainer(*c, # Components (typically `CardHeader`, `CardBody`, `CardFooter`) + cls=CardT.default, # Additional classes on the div + **kwargs # Additional args for the div + ): # Container for a card + "Creates a card container" return fh.Div(*c, cls=('uk-card',stringify(cls)), **kwargs) # %% ../nbs/02_franken.ipynb -def Card(*c, # Components that go in the body - header=None, # Components that go in the header - footer=None, # Components that go in the footer +def Card(*c, # Components that go in the body (Main content of the card such as a form, and image, a signin form, etc.) + header=None, # A component that goes in the header (often a `ModalTitle` and description) + footer=None, # A component that goes in the footer (often a `ModalCloseButton`) body_cls='space-y-6', # classes for the body header_cls=(), # classes for the header footer_cls=(), # classes for the footer cls=(), #class for outermost component - **kwargs # classes that for the card itself - ): + **kwargs # additional arguments for the `CardContainer` + ): # Card component "Creates a Card with a header, body, and footer" header_cls, footer_cls, body_cls, cls = map(stringify, (header_cls, footer_cls, body_cls, cls)) res = [] @@ -802,33 +1021,47 @@ def _generate_next_value_(name, start, count, last_values): return str2ukcls('ta responsive = auto() # %% ../nbs/02_franken.ipynb -def Table(*args, cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), **kwargs): +def Table(*c, # Components (typically `Thead`, `Tbody`, `Tfoot`) + cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), # Additional classes on the table + **kwargs # Additional args for the table + ): # Table component "Creates a table" - return fh.Table(cls=('uk-table', stringify(cls)), *args, **kwargs) + return fh.Table(cls=('uk-table', stringify(cls)), *c, **kwargs) # %% ../nbs/02_franken.ipynb -def _TableCell(Component, *args, cls=(), shrink=False, expand=False, small=False, **kwargs): +def _TableCell(Component, + *c, # Components that go in the cell + cls=(), # Additional classes on the cell container + shrink=False, # Whether to shrink the cell + expand=False, # Whether to expand the cell + small=False, # Whether to use a small table + **kwargs # Additional args for the cell + ): # Table cell "Creates a table cell" cls = stringify(cls) if shrink: cls += ' uk-table-shrink' if expand: cls += ' uk-table-expand' if small: cls += ' uk-table-small' - return Component(*args,cls=cls, **kwargs) + return Component(*c,cls=cls, **kwargs) @delegates(_TableCell, but=['Component']) -def Td(*args,**kwargs): return _TableCell(fh.Td, *args, **kwargs) +def Td(*c,**kwargs): return _TableCell(fh.Td, *c, **kwargs) @delegates(_TableCell, but=['Component']) -def Th(*args,**kwargs): return _TableCell(fh.Th, *args, **kwargs) +def Th(*c,**kwargs): return _TableCell(fh.Th, *c, **kwargs) -def Tr(*cells, cls=(), **kwargs): return fh.Tr(*cells, cls=stringify(cls), **kwargs) -def Thead(*rows, cls=(), **kwargs): return fh.Thead(*rows, cls=stringify(cls), **kwargs) def Tbody(*rows, cls=(), sortable=False, **kwargs): return fh.Tbody(*rows, cls=stringify(cls), uk_sortable=sortable, **kwargs) # %% ../nbs/02_franken.ipynb -def TableFromLists(header_data, body_data, footer_data=None, - header_cell_render=Th,body_cell_render=Td, footer_cell_render=Td, - cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), - sortable=False, **kwargs): +def TableFromLists(header_data:Sequence, # List of header data + body_data:Sequence[Sequence], # List of lists of body data + footer_data=None, # List of footer data + header_cell_render=Th, # Function(content) -> FT that renders header cells + body_cell_render=Td, # Function(key, content) -> FT that renders body cells + footer_cell_render=Td, # Function(key, content) -> FT that renders footer cells + cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), # Additional classes on the table + sortable=False, # Whether to use sortable table + **kwargs # Additional args for the table + ): # Table from lists "Creates a Table from a list of header data and a list of lists of body data" return Table( Thead(Tr(*map(header_cell_render, header_data))), @@ -838,11 +1071,16 @@ def TableFromLists(header_data, body_data, footer_data=None, **kwargs) # %% ../nbs/02_franken.ipynb -def TableFromDicts(header_data:Sequence, body_data:Sequence[dict], footer_data=None, - header_cell_render=Th, body_cell_render=lambda k,v : Td(v), footer_cell_render=lambda k,v : Td(v), - cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), - sortable=False, - **kwargs): +def TableFromDicts(header_data:Sequence, # List of header data + body_data:Sequence[dict], # List of dicts of body data + footer_data=None, # List of footer data + header_cell_render=Th, # Function(content) -> FT that renders header cells + body_cell_render=lambda k,v : Td(v), # Function(key, content) -> FT that renders body cells + footer_cell_render=lambda k,v : Td(v), # Function(key, content) -> FT that renders footer cells + cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), # Additional classes on the table + sortable=False, # Whether to use sortable table + **kwargs # Additional args for the table + ): # Styled Table "Creates a Table from a list of header data and a list of dicts of body data" return Table( Thead(Tr(*[header_cell_render(h) for h in header_data])), @@ -869,8 +1107,9 @@ def TableFromDicts(header_data:Sequence, body_data:Sequence[dict], footer_data=N 'li': 'leading-relaxed', # Code and quotes - 'pre': 'bg-base-200 rounded-lg p-4 mb-6 overflow-x-auto', + 'pre': 'bg-base-200 rounded-lg p-4 mb-6', 'code': 'uk-codespan px-1', + 'pre code': 'uk-codespan px-1 block overflow-x-auto', 'blockquote': 'uk-blockquote pl-4 border-l-4 border-primary italic mb-6', # Tables @@ -884,9 +1123,10 @@ def TableFromDicts(header_data:Sequence, body_data:Sequence[dict], footer_data=N } # %% ../nbs/02_franken.ipynb -def apply_classes(html_str:str, - class_map=None, - class_map_mods=None): +def apply_classes(html_str:str, # Html string + class_map=None, # Class map + class_map_mods=None # Class map that will modify the class map map (useful for small changes to a base class map) + ): # Html string with classes applied "Apply classes to html string" if not html_str: return html_str try: @@ -895,8 +1135,10 @@ def apply_classes(html_str:str, if class_map_mods: class_map = {**class_map, **class_map_mods} html_str = html.fromstring(html_str) - for tag, classes in class_map.items(): - for element in html_str.xpath(f'//{tag}'): + for selector, classes in class_map.items(): + # Handle descendant selectors (e.g., 'pre code') + xpath = '//' + '/descendant::'.join(selector.split()) + for element in html_str.xpath(xpath): existing_class = element.get('class', '') new_class = f"{existing_class} {classes}".strip() element.set('class', new_class) @@ -907,7 +1149,10 @@ def apply_classes(html_str:str, return html_str # %% ../nbs/02_franken.ipynb -def render_md(md_content:str, class_map=None, class_map_mods=None): +def render_md(md_content:str, # Markdown content + class_map=None, # Class map + class_map_mods=None # Additional class map + ): # Rendered markdown "Renders markdown using mistletoe and lxml" if md_content=='': return md_content # Check for required dependencies diff --git a/nbs/02_franken.ipynb b/nbs/02_franken.ipynb index a051e87..6ed3234 100644 --- a/nbs/02_franken.ipynb +++ b/nbs/02_franken.ipynb @@ -117,26 +117,41 @@ { "cell_type": "code", "execution_count": null, - "id": "ff8e03e2", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def PParagraph(*args, cls='', **kwargs): \n", - " \"P Tag with uk-paragraph style applied\"\n", - " return fh.P(*args, cls=('uk-paragraph',stringify(cls)), **kwargs)\n", - "def PLarge(*args, cls='', **kwargs):\n", - " \"P Tag with uk-text-large style applied\"\n", - " return fh.P(*args, cls=('uk-text-large', stringify(cls)), **kwargs)\n", - "def PLead(*args, cls='', **kwargs):\n", - " \"P Tag with uk-text-lead style applied\"\n", - " return fh.P(*args, cls=('uk-text-lead', stringify(cls)), **kwargs)\n", - "def PSmall(*args, cls='', **kwargs):\n", - " \"P Tag with uk-text-small style applied\"\n", - " return fh.P(*args, cls=('uk-text-small', stringify(cls)), **kwargs)\n", - "def PMuted(*args, cls='', **kwargs):\n", - " \"P Tag with uk-text-muted style applied\"\n", - " return fh.P(*args, cls=('uk-text-muted', stringify(cls)), **kwargs)" + "id": "312480d0", + "metadata": {}, + "outputs": [], + "source": [ + "#| export\n", + "def PParagraph(*c, # Contents of P tag (often text)\n", + " cls='', # Classes in addition to paragraph styling\n", + " **kwargs # Additional args for P tag\n", + " ): # P(..., cls='uk-paragraph')\n", + " \"P Tag with paragraph style applied\"\n", + " return fh.P(*c, cls=('uk-paragraph', stringify(cls)), **kwargs)\n", + "def PLarge(*c, # Contents of P tag (often text)\n", + " cls='', # Classes in addition to paragraph styling\n", + " **kwargs # Additional args for P tag\n", + " ): # P(..., cls='uk-text-large')\n", + " \"P Tag with large style applied\"\n", + " return fh.P(*c, cls=('uk-text-large', stringify(cls)), **kwargs)\n", + "def PLead(*c, # Contents of P tag (often text)\n", + " cls='', # Classes in addition to paragraph styling\n", + " **kwargs # Additional args for P tag\n", + " ): # P(..., cls='uk-text-lead')\n", + " \"P Tag with lead style applied\"\n", + " return fh.P(*c, cls=('uk-text-lead', stringify(cls)), **kwargs)\n", + "def PSmall(*c, # Contents of P tag (often text)\n", + " cls='', # Classes in addition to paragraph styling\n", + " **kwargs # Additional args for P tag\n", + " ): # P(..., cls='uk-text-small')\n", + " \"P Tag with small style applied\"\n", + " return fh.P(*c, cls=('uk-text-small', stringify(cls)), **kwargs)\n", + "def PMuted(*c, # Contents of P tag (often text)\n", + " cls='', # Classes in addition to paragraph styling\n", + " **kwargs # Additional args for P tag\n", + " ): # P(..., cls='uk-text-muted')\n", + " \"P Tag with muted style applied\"\n", + " return fh.P(*c, cls=('uk-text-muted', stringify(cls)), **kwargs)" ] }, { @@ -147,9 +162,12 @@ "outputs": [], "source": [ "#| export\n", - "def CodeSpan(*args, **kwargs):\n", + "def CodeSpan(*c, # Contents of CodeSpan tag (inline text code snippets)\n", + " cls=(), # Classes in addition to CodeSpan styling\n", + " **kwargs # Additional args for CodeSpan tag\n", + " ): # Code(..., cls='uk-codespan')\n", " \"A CodeSpan with Styling\"\n", - " return fh.Code(*args, cls=('uk-codespan\"'), **kwargs)" + " return fh.Code(*c, cls=('uk-codespan', stringify(cls)), **kwargs)" ] }, { @@ -160,8 +178,11 @@ "outputs": [], "source": [ "#| export\n", - "def Blockquote(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT:\n", - " \"A Blockquote with Styling\"\n", + "def Blockquote(*c:FT|str, # Contents of Blockquote tag (often text)\n", + " cls:Enum|str|tuple=(), # Classes in addition to Blockquote styling\n", + " **kwargs # Additional args for Blockquote tag\n", + " ): # Blockquote(..., cls='uk-blockquote')\n", + " \"Blockquote with Styling\"\n", " return fh.Blockquote(*c, cls=('uk-blockquote',stringify(cls)), **kwargs)" ] }, @@ -173,8 +194,11 @@ "outputs": [], "source": [ "#| export\n", - "def H1(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: \n", - " \"A H1 with Styling\"\n", + "def H1(*c:FT|str, # Contents of H1 tag (often text)\n", + " cls:Enum|str|tuple=(), # Classes in addition to H1 styling\n", + " **kwargs # Additional args for H1 tag\n", + " ): # H1(..., cls='uk-h1')\n", + " \"H1 with styling and appropriate size\"\n", " return fh.H1(*c, cls=('uk-h1',stringify(cls)), **kwargs)" ] }, @@ -186,8 +210,11 @@ "outputs": [], "source": [ "#| export\n", - "def H2(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: \n", - " \"A H2 with Styling\"\n", + "def H2(*c:FT|str, # Contents of H2 tag (often text)\n", + " cls:Enum|str|tuple=(), # Classes in addition to H2 styling\n", + " **kwargs # Additional args for H2 tag\n", + " ): # H2(..., cls='uk-h2')\n", + " \"H2 with styling and appropriate size\"\n", " return fh.H2(*c, cls=('uk-h2',stringify(cls)), **kwargs)" ] }, @@ -199,8 +226,11 @@ "outputs": [], "source": [ "#| export\n", - "def H3(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: \n", - " \"A H3 with Styling\"\n", + "def H3(*c:FT|str, # Contents of H3 tag (often text)\n", + " cls:Enum|str|tuple=(), # Classes in addition to H3 styling\n", + " **kwargs # Additional args for H3 tag\n", + " ): # H3(..., cls='uk-h3')\n", + " \"H3 with styling and appropriate size\"\n", " return fh.H3(*c, cls=('uk-h3',stringify(cls)), **kwargs)" ] }, @@ -212,8 +242,11 @@ "outputs": [], "source": [ "#| export\n", - "def H4(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT:\n", - " \"A H4 with Styling\"\n", + "def H4(*c:FT|str, # Contents of H4 tag (often text)\n", + " cls:Enum|str|tuple=(), # Classes in addition to H4 styling\n", + " **kwargs # Additional args for H4 tag\n", + " ): # H4(..., cls='uk-h4')\n", + " \"H4 with styling and appropriate size\"\n", " return fh.H4(*c, cls=('uk-h4',stringify(cls)), **kwargs)" ] }, @@ -253,14 +286,13 @@ "outputs": [], "source": [ "#| export\n", - "def Button(*c: Union[str, FT],\n", - " cls: Union[str, Enum]=ButtonT.default,\n", - " submit=True,\n", - " **kwargs\n", - " ) -> FT:\n", - " \"A Button with Styling (defaults to `submit` for form submission)\"\n", - " if 'type' not in kwargs:\n", - " kwargs['type'] = 'submit' if submit else 'button'\n", + "def Button(*c: Union[str, FT], # Contents of `Button` tag (often text)\n", + " cls: Union[str, Enum]=ButtonT.default, # Classes in addition to `Button` styling (use `ButtonT` for built in styles)\n", + " submit=True, # Whether the button should submit a form\n", + " **kwargs # Additional args for `Button` tag\n", + " ) -> FT: # Button(..., cls='uk-button')\n", + " \"Button with Styling (defaults to `submit` for form submission)\"\n", + " if 'type' not in kwargs: kwargs['type'] = 'submit' if submit else 'button'\n", " return fh.Button(*c, cls=('uk-button', stringify(cls)), **kwargs)" ] }, @@ -272,17 +304,6 @@ "## Headings" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "29968ad4", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def Main(*args, **kwargs): return fh.Main(*args, **kwargs)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -309,8 +330,11 @@ "outputs": [], "source": [ "#| export\n", - "def Container(*c, cls=('mt-5', ContainerT.xlarge), **kwargs): \n", - " \"A Div to be used as a container that often wraps large sections or a page of content\"\n", + "def Container(*c, # Contents of Container tag (often other FT Components)\n", + " cls=('mt-5', ContainerT.xlarge), # Classes in addition to Container styling\n", + " **kwargs # Additional args for Container (`Div` tag)\n", + " ): # Container(..., cls='uk-container')\n", + " \"Div to be used as a container that often wraps large sections or a page of content\"\n", " return Div(*c, cls=('uk-container',stringify(cls)), **kwargs)" ] }, @@ -322,9 +346,13 @@ "outputs": [], "source": [ "#| export\n", - "def Titled(title:str=\"FastHTML app\", *args, cls='uk-container-xlarge', **kwargs)->FT:\n", + "def Titled(title:str=\"FastHTML app\", # Title of the page\n", + " *c, # Contents of the page (often other tags)\n", + " cls=ContainerT.xlarge, # Classes in addition to Container styling\n", + " **kwargs # Additional args for Container (`Div` tag)\n", + " )->FT: # Title, Main(Container(H1(title), content))\n", " \"Creates a standard page structure for titled page. Main(Container(title, content))\"\n", - " return fh.Title(title), fh.Main(Container(H1(title), *args, cls=cls, **kwargs))" + " return fh.Title(title), fh.Main(Container(H1(title), *c, cls=cls, **kwargs))" ] }, { @@ -344,7 +372,7 @@ "source": [ "#| export\n", "class DividerT(VEnum):\n", - " \" Divider Styles from https://franken-ui.dev/docs/divider\"\n", + " \"Divider Styles from https://franken-ui.dev/docs/divider\"\n", " def _generate_next_value_(name, start, count, last_values): return str2ukcls('divider', name)\n", " icon=auto()\n", " small=auto()\n", @@ -359,11 +387,14 @@ "outputs": [], "source": [ "#| export\n", - "def Divider(*args, cls=('my-4', DividerT.icon), **kwargs):\n", + "def Divider(*c, # contents of Divider tag (often nothing)\n", + " cls=('my-4', DividerT.icon), # Classes in addition to Divider styling\n", + " **kwargs # Additional args for Divider tag\n", + " ): # Hr(..., cls='uk-divider-icon') or Div(..., cls='uk-divider-vertical')\n", " \"Divider with default styling and margin\"\n", " cls = stringify(cls)\n", " container = Div if 'uk-divider-vertical' in cls else Hr\n", - " return container(*args, cls=cls, **kwargs)" + " return container(*c, cls=cls, **kwargs)" ] }, { @@ -401,31 +432,6 @@ "## Articles & Containers & Sections" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "b7c048a8", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "# def Alert(*args, cls=(), **kwargs): \n", - "# \"A styled alert component that can contain a AlertTitle, AlertDescription and AlertCloseButton\"\n", - "# return Div(*args, cls=('uk-alert', stringify(cls)), uk_alert=True, **kwargs)\n", - "\n", - "# def AlertCloseButton(*args, cls=(), **kwargs): \n", - "# \"A button component for closing an Alert\"\n", - "# return A(*args, cls=('uk-alert-close', stringify(cls)), **kwargs)\n", - "\n", - "# def AlertTitle(*args, cls='', **kwargs): \n", - "# \"A title component for use within an Alert\"\n", - "# return Div(*args, cls=('uk-alert-title', stringify(cls)), **kwargs)\n", - "\n", - "# def AlertDescription(*args, cls='', **kwargs): \n", - "# \"A description component for use within an Alert\"\n", - "# return Div(*args, cls=('uk-alert-description', stringify(cls)), **kwargs)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -434,15 +440,24 @@ "outputs": [], "source": [ "#| export\n", - "def Article(*c, cls=(), **kwargs):\n", + "def Article(*c, # contents of Article tag (often other tags)\n", + " cls=(), # Classes in addition to Article styling\n", + " **kwargs # Additional args for Article tag\n", + " ): # Article(..., cls='uk-article')\n", " \"A styled article container for blog posts or similar content\"\n", " return fh.Article(*c, cls=('uk-article',stringify(cls)), **kwargs)\n", "\n", - "def ArticleTitle(*c, cls=(), **kwargs):\n", + "def ArticleTitle(*c, # contents of ArticleTitle tag (often other tags)\n", + " cls=(), # Classes in addition to ArticleTitle styling\n", + " **kwargs # Additional args for ArticleTitle tag\n", + " ): # H1(..., cls='uk-article-title')\n", " \"A title component for use within an Article\"\n", " return H1(*c, cls=('uk-article-title',stringify(cls)), **kwargs)\n", "\n", - "def ArticleMeta(*c, cls=(), **kwargs):\n", + "def ArticleMeta(*c, # contents of ArticleMeta tag (often other tags)\n", + " cls=(), # Classes in addition to ArticleMeta styling\n", + " **kwargs # Additional args for ArticleMeta tag\n", + " ): # P(..., cls='uk-article-meta')\n", " \"A metadata component for use within an Article showing things like date, author etc\"\n", " return P(*c, cls=('uk-article-meta',stringify(cls)), **kwargs)" ] @@ -452,60 +467,9 @@ "execution_count": null, "id": "b2b0abaa", "metadata": {}, - "outputs": [ - { - "data": { - "text/markdown": [ - "```html\n", - "
\n", - "

Article Title

\n", - "

By: John Doe

\n", - "
\n", - "\n", - "```" - ], - "text/plain": [ - "article((h1(('Article Title',),{'class': 'uk-h1 uk-article-title '}), p(('By: John Doe',),{'class': 'uk-article-meta '})),{'class': 'uk-article '})" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "Article(ArticleTitle(\"Article Title\"), ArticleMeta(\"By: John Doe\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3a886534", - "metadata": {}, - "outputs": [ - { - "data": { - "text/markdown": [ - "```html\n", - "
This is a container with a custom background color.
\n", - "\n", - "```" - ], - "text/plain": [ - "div(('This is a container with a custom background color.',),{'class': 'uk-container uk-container-xsmall', 'style': 'background-color: #e6f2ff; padding: 20px; border-radius: 5px;'})" - ] - }, - "execution_count": null, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "Container(\n", - " \"This is a container with a custom background color.\",\n", - " cls=ContainerT.xsmall,\n", - " style=\"background-color: #e6f2ff; padding: 20px; border-radius: 5px;\"\n", - ")" + "# Article(ArticleTitle(\"Article Title\"), ArticleMeta(\"By: John Doe\"))" ] }, { @@ -548,7 +512,11 @@ "outputs": [], "source": [ "#| export\n", - "def Section(*c, cls=(), **kwargs):\n", + "def Section(*c, # contents of Section tag (often other tags)\n", + " cls=(), # Classes in addition to Section styling\n", + " **kwargs # Additional args for Section tag\n", + " ): # Div(..., cls='uk-section')\n", + " \"Section with styling and margins\"\n", " return fh.Div(*c, cls=('uk-section',stringify(cls)), **kwargs)" ] }, @@ -568,7 +536,10 @@ "outputs": [], "source": [ "#| export\n", - "def Form(*c, cls='space-y-3', **kwargs):\n", + "def Form(*c, # contents of Form tag (often Buttons, FormLabels, and LabelInputs)\n", + " cls='space-y-3', # Classes in addition to Form styling (default is 'space-y-3' to prevent scrunched up form elements)\n", + " **kwargs # Additional args for Form tag\n", + " ): # Form(..., cls='space-y-3')\n", " \"A Form with default spacing between form elements\"\n", " return fh.Form(*c, cls=stringify(cls), **kwargs)" ] @@ -581,11 +552,17 @@ "outputs": [], "source": [ "#|export\n", - "def Fieldset(*c, cls=(), **kwargs): \n", + "def Fieldset(*c, # contents of Fieldset tag (often other tags)\n", + " cls=(), # Classes in addition to Fieldset styling\n", + " **kwargs # Additional args for Fieldset tag\n", + " ): # Fieldset(..., cls='uk-fieldset')\n", " \"A Fieldset with default styling\"\n", " return fh.Fieldset(*c, cls=('uk-fieldset',stringify(cls)), **kwargs)\n", "\n", - "def Legend(*c, cls=(), **kwargs): \n", + "def Legend(*c, # contents of Legend tag (often other tags)\n", + " cls=(), # Classes in addition to Legend styling\n", + " **kwargs # Additional args for Legend tag\n", + " ): # Legend(..., cls='uk-legend')\n", " \"A Legend with default styling\"\n", " return fh.Legend(*c, cls=('uk-legend',stringify(cls)), **kwargs)" ] @@ -598,25 +575,47 @@ "outputs": [], "source": [ "#| export\n", - "def Input(*c, cls=(), **kwargs): \n", + "def Input(*c, # contents of Input tag (often nothing)\n", + " cls=(), # Classes in addition to Input styling\n", + " **kwargs # Additional args for Input tag\n", + " ): # Input(..., cls='uk-input')\n", " \"An Input with default styling\"\n", " return fh.Input(*c, cls=('uk-input',stringify(cls)), **kwargs)\n", - "def Select(*option, cls=(), **kwargs):\n", - " \"A Select with default styling\"\n", + "\n", + "def Select(*option, # options for the select dropdown\n", + " cls=(), # Classes in addition to Select styling\n", + " **kwargs # Additional args for Select tag\n", + " ): # Select(..., cls='uk-select')\n", + " \"A Select with default styling, though often `UkSelect` is a better choice\"\n", " return fh.Select(*option, cls=('uk-select',stringify(cls)), **kwargs)\n", - "def Radio(*c, cls=(), **kwargs): \n", + "def Radio(*c, # contents of Radio tag (often nothing)\n", + " cls=(), # Classes in addition to Radio styling\n", + " **kwargs # Additional args for Radio tag\n", + " ): # Input(..., cls='uk-radio', type='radio')\n", " \"A Radio with default styling\"\n", " return fh.Input(*c, cls=('uk-radio',stringify(cls)), type='radio', **kwargs)\n", - "def CheckboxX(*c, cls=(), **kwargs): \n", + "def CheckboxX(*c, # contents of CheckboxX tag (often nothing)\n", + " cls=(), # Classes in addition to CheckboxX styling\n", + " **kwargs # Additional args for CheckboxX tag\n", + " ): # Input(..., cls='uk-checkbox', type='checkbox')\n", " \"A Checkbox with default styling\"\n", " return fh.Input(*c, cls=('uk-checkbox',stringify(cls)), type='checkbox', **kwargs)\n", - "def Range(*c, cls=(), **kwargs): \n", + "def Range(*c, # contents of Range tag (often nothing)\n", + " cls=(), # Classes in addition to Range styling\n", + " **kwargs # Additional args for Range tag\n", + " ): # Input(..., cls='uk-range', type='range')\n", " \"A Range with default styling\"\n", " return fh.Input(*c, cls=('uk-range',stringify(cls)), type='range', **kwargs)\n", - "def TextArea(*c, cls=(), **kwargs): \n", + "def TextArea(*c, # contents of TextArea tag (often text)\n", + " cls=(), # Classes in addition to TextArea styling\n", + " **kwargs # Additional args for TextArea tag\n", + " ): # TextArea(..., cls='uk-textarea')\n", " \"A Textarea with default styling\"\n", " return fh.Textarea(*c, cls=('uk-textarea',stringify(cls)), **kwargs)\n", - "def Switch(*c, cls=(), **kwargs): \n", + "def Switch(*c, # contents of Switch tag (often nothing)\n", + " cls=(), # Classes in addition to Switch styling\n", + " **kwargs # Additional args for Switch tag\n", + " ): # Input(..., cls='uk-toggle-switch uk-toggle-switch-primary min-w-9', type='checkbox')\n", " \"A Switch with default styling\"\n", " return fh.Input(*c, cls=('uk-toggle-switch uk-toggle-switch-primary min-w-9',stringify(cls)), type='checkbox', **kwargs)" ] @@ -629,7 +628,10 @@ "outputs": [], "source": [ "#|export\n", - "def FormLabel(*c, cls=(), **kwargs): \n", + "def FormLabel(*c, # contents of FormLabel tag (often text)\n", + " cls=(), # Classes in addition to FormLabel styling\n", + " **kwargs # Additional args for FormLabel tag\n", + " ): # Label(..., cls='uk-form-label')\n", " \"A Label with default styling\"\n", " return fh.Label(*c, cls=('uk-form-label',stringify(cls)), **kwargs)" ] @@ -657,7 +659,10 @@ "outputs": [], "source": [ "#| export\n", - "def Label(*c, cls=(), **kwargs):\n", + "def Label(*c, # contents of Label tag (often text)\n", + " cls=(), # Classes in addition to Label styling\n", + " **kwargs # Additional args for Label tag\n", + " ): # Label(..., cls='uk-label')\n", " \"FrankenUI labels, which look like pills\"\n", " return fh.Label(*c, cls=('uk-label',stringify(cls)), **kwargs)" ] @@ -703,16 +708,16 @@ "source": [ "#| export\n", "def GenericLabelInput(\n", - " label:str|FT,\n", - " lbl_cls='',\n", - " input_cls='',\n", - " container=Div, \n", - " cls='',\n", - " id='',\n", - " input_fn=noop, \n", - " **kwargs\n", - " ):\n", - " \"`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `UkTextArea` which is created for you in this library\"\n", + " label:str|FT, # FormLabel content (often text)\n", + " lbl_cls='', # Additional classes for FormLabel\n", + " input_cls='', # Additional classes for user input (Input, Select, etc)\n", + " container=Div, # Container to wrap label and input in (default is Div)\n", + " cls='', # Classes on container (default is '')\n", + " id='', # id for label and input (`id`, `name` and `for` attributes are set to this value)\n", + " input_fn=noop, # User input FT component \n", + " **kwargs # Additional args for user input\n", + " ): \n", + " \"`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `LabelInput` which is created for you in this library\"\n", " if isinstance(label, str) or label.tag != 'label': \n", " label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)\n", " inp = input_fn(id=id, cls=stringify(input_cls), **kwargs) \n", @@ -728,114 +733,126 @@ "outputs": [], "source": [ "#| export\n", - "@delegates(GenericLabelInput, but=['input_fn','cls'])\n", - "def LabelInput(*args, cls='space-y-2', **kwargs): \n", - " \"A Label and Input pair that provides default spacing and links/names them based on id\"\n", - " return GenericLabelInput(*args, cls=stringify(cls),input_fn=Input, **kwargs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9fdc52d0", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def LabelRadio(label:str|FT,\n", - " lbl_cls='',\n", - " input_cls='',\n", - " container=Div, \n", - " cls='flex items-center space-x-2',\n", - " id='',\n", - " **kwargs\n", - " ):\n", - " \"A Label and Radio pair that provides default spacing and links/names them based on id\"\n", - " if isinstance(label, str) or label.tag != 'label': \n", - " label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)\n", - " inp = Radio(id=id, cls=stringify(input_cls), **kwargs) \n", - " if container: return container(inp, label, cls=stringify(cls))\n", - " return inp, label" + "def LabelInput(label:str|FT, # FormLabel content (often text)\n", + " lbl_cls='', # Additional classes for `FormLabel`\n", + " input_cls='', # Additional classes for `Input`\n", + " cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements)\n", + " id='', # id for `FormLabel` and `Input` (`id`, `name` and `for` attributes are set to this value)\n", + " **kwargs # Additional args for `Input`\n", + " ): # Div(cls='space-y-2')(`FormLabel`, `Input`)\n", + " \"A `FormLabel` and `Input` pair that provides default spacing and links/names them based on id\"\n", + " return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,\n", + " container=Div, cls=cls, id=id, input_fn=Input, **kwargs)" ] }, { "cell_type": "code", "execution_count": null, - "id": "914769f7", + "id": "2a78b41e", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "def LabelCheckboxX(label:str|FT,\n", - " lbl_cls='',\n", - " input_cls='',\n", - " container=Div, \n", - " cls='flex items-center space-x-2',\n", - " **kwargs\n", - " ):\n", - " \"`Div(Label,Input)` component with Uk styling injected appropriately. Generally you should higher level API, such as `UkTextArea` which is created for you in this library\"\n", - " \n", - " id = kwargs.pop('id', fh.unqid())\n", - " if isinstance(label, str) or label.tag != 'label': \n", - " label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)\n", - " inp = CheckboxX(id=id, cls=stringify(input_cls), **kwargs) \n", - " if container: return container(inp, label, cls=stringify(cls))\n", - " return inp, label" + "def LabelRange(label:str|FT, # FormLabel content (often text)\n", + " lbl_cls='', # Additional classes for `FormLabel`\n", + " input_cls='', # Additional classes for `Range`\n", + " cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements)\n", + " id='', # id for `FormLabel` and `Range` (`id`, `name` and `for` attributes are set to this value)\n", + " **kwargs # Additional args for `Range`\n", + " ): # Div(cls='space-y-2')(`FormLabel`, `Range`)\n", + " \"A `FormLabel` and `Range` pair that provides default spacing and links/names them based on id\"\n", + " return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,\n", + " container=Div, cls=cls, id=id, input_fn=Range, **kwargs)" ] }, { "cell_type": "code", "execution_count": null, - "id": "4fb8ef69", + "id": "ea4c8a92", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "# @delegates(GenericLabelInput, but=['input_fn','cls'])\n", - "# def LabelCheckboxX(*args, cls='space-x-2', **kwargs): return GenericLabelInput(*args, cls=stringify(cls), input_fn=CheckboxX, **kwargs)" + "def LabelTextArea(label:str|FT, # FormLabel content (often text)\n", + " value='', # Value for the textarea\n", + " lbl_cls='', # Additional classes for `FormLabel`\n", + " input_cls='', # Additional classes for `TextArea`\n", + " cls='space-y-2', # Classes on container (default is `'space-y-2'` to prevent scrunched up form elements)\n", + " id='', # id for `FormLabel` and `TextArea` (`id`, `name` and `for` attributes are set to this value)\n", + " **kwargs # Additional args for `TextArea`\n", + " ): # Div(cls='space-y-2')(`FormLabel`, `TextArea`)\n", + " def text_area_with_value(**kw): return TextArea(value, **kw)\n", + " return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,\n", + " container=Div, cls=cls, id=id, input_fn=text_area_with_value, **kwargs)" ] }, { "cell_type": "code", "execution_count": null, - "id": "3a1eb1d5", + "id": "c5809ab0", "metadata": {}, "outputs": [], "source": [ "#| export\n", "@delegates(GenericLabelInput, but=['input_fn','cls'])\n", - "def LabelRange(*args, cls='space-y-2', **kwargs): \n", - " \"Creates a labeled range input with nice default spacing and links/names them based on id\"\n", - " return GenericLabelInput(*args, cls=stringify(cls), input_fn=Range, **kwargs)" + "def LabelSwitch(label:str|FT, # FormLabel content (often text)\n", + " lbl_cls='', # Additional classes for `FormLabel`\n", + " input_cls='', # Additional classes for `Switch`\n", + " cls='space-x-2', # Classes on container (default is `'space-x-2'` to prevent scrunched up form elements)\n", + " id='', # id for `FormLabel` and `Switch` (`id`, `name` and `for` attributes are set to this value)\n", + " **kwargs # Additional args for `Switch`\n", + " ): # Div(cls='space-y-2')(`FormLabel`, `Switch`)\n", + " return GenericLabelInput(label=label, lbl_cls=lbl_cls, input_cls=input_cls,\n", + " container=Div, cls=cls, id=id, input_fn=Switch, **kwargs)" ] }, { "cell_type": "code", "execution_count": null, - "id": "271dcb31", + "id": "9fdc52d0", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "@delegates(GenericLabelInput, but=['input_fn','cls'])\n", - "def LabelTextArea(*args, cls='space-y-2', value='', **kwargs): \n", - " \"Creates a labeled textarea with nice default spacing and links/names them based on id\"\n", - " def text_area_with_value(**kw): return TextArea(value, **kw)\n", - " return GenericLabelInput(*args, cls=stringify(cls), input_fn=text_area_with_value, **kwargs)" + "def LabelRadio(label:str|FT, # FormLabel content (often text)\n", + " lbl_cls='', # Additional classes for `FormLabel`\n", + " input_cls='', # Additional classes for `Radio`\n", + " container=Div, # Container to wrap label and input in (default is Div)\n", + " cls='flex items-center space-x-2', # Classes on container (default is 'flex items-center space-x-2')\n", + " id='', # id for `FormLabel` and `Radio` (`id`, `name` and `for` attributes are set to this value)\n", + " **kwargs # Additional args for `Radio`\n", + " ): # Div(cls='flex items-center space-x-2')(`FormLabel`, `Radio`)\n", + " \"A FormLabel and Radio pair that provides default spacing and links/names them based on id\"\n", + " if isinstance(label, str) or label.tag != 'label': \n", + " label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)\n", + " inp = Radio(id=id, cls=stringify(input_cls), **kwargs) \n", + " if container: return container(inp, label, cls=stringify(cls))\n", + " return inp, label" ] }, { "cell_type": "code", "execution_count": null, - "id": "0fce5f7f", + "id": "914769f7", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "@delegates(GenericLabelInput, but=['input_fn','cls'])\n", - "def LabelSwitch(*args, cls='space-x-2', **kwargs): \n", - " \"Creates a labeled switch with nice default spacing and links/names them based on id\"\n", - " return GenericLabelInput(*args, cls=stringify(cls), input_fn=Switch, **kwargs)" + "def LabelCheckboxX(label:str|FT, # FormLabel content (often text)\n", + " lbl_cls='', # Additional classes for `FormLabel`\n", + " input_cls='', # Additional classes for `CheckboxX`\n", + " container=Div, # Container to wrap label and input in (default is Div)\n", + " cls='flex items-center space-x-2', # Classes on container (default is 'flex items-center space-x-2')\n", + " id='', # id for `FormLabel` and `CheckboxX` (`id`, `name` and `for` attributes are set to this value)\n", + " **kwargs # Additional args for `CheckboxX`\n", + " ): # Div(cls='flex items-center space-x-2')(`FormLabel`, `CheckboxX`)\n", + " \"A FormLabel and CheckboxX pair that provides default spacing and links/names them based on id\"\n", + " id = kwargs.pop('id', fh.unqid())\n", + " if isinstance(label, str) or label.tag != 'label': \n", + " label = FormLabel(cls=stringify(lbl_cls), fr=id)(label)\n", + " inp = CheckboxX(id=id, cls=stringify(input_cls), **kwargs) \n", + " if container: return container(inp, label, cls=stringify(cls))\n", + " return inp, label" ] }, { @@ -846,16 +863,16 @@ "outputs": [], "source": [ "#| export\n", - "def LabelSelect(*option,\n", - " label:str|FT,\n", - " lbl_cls='',\n", - " input_cls='',\n", - " container=Div, \n", - " cls='space-y-2',\n", - " id='',\n", - " **kwargs\n", + "def LabelSelect(*option, # Options for the select dropdown (can use `Options` helper function to create)\n", + " label:str|FT, # FormLabel content (often text)\n", + " lbl_cls='', # Additional classes for `FormLabel`\n", + " input_cls='', # Additional classes for `Select`\n", + " container=Div, # Container to wrap label and input in (default is Div)\n", + " cls='space-y-2', # Classes on container (default is 'space-y-2')\n", + " id='', # id for `FormLabel` and `Select` (`id`, `name` and `for` attributes are set to this value)\n", + " **kwargs # Additional args for `Select`\n", " ):\n", - " \"Creates a Labeled Select - Generally you should use LabelUkSelect for a more full features and styled select\"\n", + " \"A FormLabel and Select pair that provides default spacing and links/names them based on id (usually UkLabelSelect is a better choice)\"\n", " if isinstance(label, str) or label.tag != 'label': \n", " label = FormLabel(lbl_cls=stringify(lbl_cls), fr=id)(label)\n", " inp = Select(*option, id=id, cls=stringify(input_cls), **kwargs) \n", @@ -920,7 +937,7 @@ " placeholder=\"\", # Placeholder text for the select input\n", " searchable=False, # Whether the select should be searchable\n", " **kwargs): # Additional arguments passed to Uk_select\n", - " \"Creates a select dropdown with uk styling and a label and provides option for adding a search box\"\n", + " \"A FormLabel and Select pair that provides default spacing and links/names them based on id\"\n", " lbl_cls, inp_cls, cls = map(stringify, (lbl_cls, inp_cls, cls))\n", " if label: \n", " lbl = FormLabel(cls=f'{lbl_cls}', fr=id)(label) \n", @@ -993,9 +1010,12 @@ "outputs": [], "source": [ "#| export\n", - "def UkList(*c, cls=(), **kwargs): \n", + "def UkList(*li, # `Li` tags to put in the list\n", + " cls=(), # Additional classes on the list\n", + " **kwargs # Additional args for `Ul` tag\n", + " ): # Ul(..., cls='uk-list')\n", " \"Creates a list with styling\"\n", - " return fh.Ul(*c, cls=('uk-list',stringify(cls)), **kwargs)" + " return fh.Ul(*li, cls=('uk-list',stringify(cls)), **kwargs)" ] }, { @@ -1014,47 +1034,52 @@ "outputs": [], "source": [ "#| export\n", - "def ModalContainer(*c, cls=(), **kwargs): \n", + "def ModalContainer(*c, # Components to put in the modal (often `ModalDialog`)\n", + " cls=(), # Additional classes on the `ModalContainer`\n", + " **kwargs # Additional args for `Div` tag\n", + " ): # Div(..., cls='uk-modal uk-modal-container')\n", " \"Creates a modal container that components go in\"\n", " return fh.Div(*c, cls=('uk-modal uk-modal-container',stringify(cls)), uk_modal=True, **kwargs)\n", - "def ModalDialog(*c, cls=(), **kwargs): \n", + "def ModalDialog(*c, # Components to put in the `ModalDialog` (often `ModalBody`, `ModalHeader`, etc)\n", + " cls=(), # Additional classes on the `ModalDialog`\n", + " **kwargs # Additional args for `Div` tag\n", + " ): # Div(..., cls='uk-modal-dialog')\n", " \"Creates a modal dialog\"\n", " return fh.Div(*c, cls=('uk-modal-dialog', stringify(cls)), **kwargs)\n", - "def ModalHeader(*c, cls=(), **kwargs): \n", + "def ModalHeader(*c, # Components to put in the `ModalHeader`\n", + " cls=(), # Additional classes on the `ModalHeader`\n", + " **kwargs # Additional args for `Div` tag\n", + " ): # Div(..., cls='uk-modal-header')\n", " \"Creates a modal header\"\n", " return fh.Div(*c, cls=('uk-modal-header', stringify(cls)), **kwargs)\n", - "def ModalBody(*c, cls=(), **kwargs): \n", + "def ModalBody(*c, # Components to put in the `ModalBody` (often forms, sign in buttons, images, etc.)\n", + " cls=(), # Additional classes on the `ModalBody`\n", + " **kwargs # Additional args for `Div` tag\n", + " ): # Div(..., cls='uk-modal-body')\n", " \"Creates a modal body\"\n", " return fh.Div(*c, cls=('uk-modal-body', stringify(cls)), **kwargs)\n", - "def ModalFooter(*c, cls=(), **kwargs): \n", + "def ModalFooter(*c, # Components to put in the `ModalFooter` (often buttons)\n", + " cls=(), # Additional classes on the `ModalFooter`\n", + " **kwargs # Additional args for `Div` tag\n", + " ): # Div(..., cls='uk-modal-footer')\n", " \"Creates a modal footer\"\n", " return fh.Div(*c, cls=('uk-modal-footer', stringify(cls)), **kwargs)\n", - "def ModalTitle(*c, cls=(), **kwargs): \n", + "def ModalTitle(*c, # Components to put in the `ModalTitle` (often text)\n", + " cls=(), # Additional classes on the `ModalTitle`\n", + " **kwargs # Additional args for `H2` tag\n", + " ): # H2(..., cls='uk-modal-title')\n", " \"Creates a modal title\"\n", - " return fh.H2(*c, cls=('uk-modal-title', stringify(cls)), **kwargs)\n", - "def ModalCloseButton(*c, cls=(), **kwargs): \n", + " return fh.H2(*c, cls=('uk-modal-title', stringify(cls)), **kwargs)\n", + "def ModalCloseButton(*c, # Components to put in the button (often text and/or an icon)\n", + " cls=(), # Additional classes on the button\n", + " htmx=False, # Whether to use HTMX to close the modal (must add hx_get to a route that closes the modal)\n", + " **kwargs # Additional args for `Button` tag\n", + " ): # Button(..., cls='uk-modal-close') + `hx_target` and `hx_swap` if htmx is True\n", " \"Creates a button that closes a modal with js\"\n", - " return Button(*c, cls=('uk-modal-close', stringify(cls)), **kwargs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "048fb346", - "metadata": {}, - "outputs": [], - "source": [ - "#| export\n", - "def HTMXModalCloseButton(*c, cls=(), target=None, **kwargs):\n", - " \"Creates a button that closes a modal using HTMX\"\n", - " return Button(\n", - " *c, \n", - " cls=('uk-modal-close', stringify(cls)),\n", - " hx_target='closest .uk-modal', \n", - " hx_swap='delete',\n", - " type='button',\n", - " **kwargs\n", - " )" + " if htmx: \n", + " kwargs['hx_target'] = 'closest .uk-modal'\n", + " kwargs['hx_swap'] = 'delete'\n", + " return Button(*c, cls=('uk-modal-close', stringify(cls)), **kwargs)" ] }, { @@ -1065,19 +1090,19 @@ "outputs": [], "source": [ "#| export\n", - "def Modal(*c,\n", - " header=None, # Components that go in the header\n", - " footer=None, # Components that go in the footer\n", - " cls=(), # class for outermost container\n", - " dialog_cls=(), # classes for the dialog\n", - " header_cls='p-6', # classes for the header\n", - " body_cls='space-y-6', # classes for the body\n", - " footer_cls=(), # classes for the footer\n", + "def Modal(*c, # Components to put in the `ModalBody` (often forms, sign in buttons, images, etc.)\n", + " header=None, # Components that go in the `ModalHeader` (often a `ModalTitle`)\n", + " footer=None, # Components that go in the `ModalFooter` (often a `ModalCloseButton`)\n", + " cls=(), # Additional classes on the outermost `ModalContainer` \n", + " dialog_cls=(), # Additional classes on the `ModalDialog` \n", + " header_cls='p-6', # Additional classes on the `ModalHeader`\n", + " body_cls='space-y-6', # Additional classes on the `ModalBody`\n", + " footer_cls=(), # Additional classes on the `ModalFooter`\n", " id='', # id for the outermost container\n", - " open=False,\n", - " **kwargs # classes for the outermost container\n", - " ): # Modal\n", - " \"Create a Modal using the appropriate Modal classes to put the boilerplate in the appropriate places for you\"\n", + " open=False, # Whether the modal is open (typically used for HTMX controlled modals)\n", + " **kwargs # Additional args for the outermost `Div` tag\n", + " ): # Fully styled modal FT Component\n", + " \"Creates a modal with the appropriate classes to put the boilerplate in the appropriate places for you\"\n", " if open:\n", " cls = stringify((cls, 'uk-open'))\n", " kwargs['style'] = stringify((kwargs.get('style',''), 'display: block;'))\n", @@ -1167,7 +1192,11 @@ "outputs": [], "source": [ "#| export\n", - "def Placeholder(*c, cls=(), **kwargs):\n", + "def Placeholder(*c, # Components to put in the placeholder\n", + " cls=(), # Additional classes on the placeholder\n", + " **kwargs # Additional args for `Div` tag\n", + " ): # Div(..., cls='uk-placeholder')\n", + " \"Creates a placeholder\"\n", " return fh.Div(*c, cls=('uk-placeholder',stringify(cls)), **kwargs)" ] }, @@ -1179,7 +1208,13 @@ "outputs": [], "source": [ "#| export\n", - "def Progress(*c, cls=(), value=\"\", max=\"\", **kwargs):\n", + "def Progress(*c, # Components to put in the progress bar (often nothing)\n", + " cls=(), # Additional classes on the progress bar\n", + " value=\"\", # Value of the progress bar\n", + " max=\"100\", # Max value of the progress bar (defaults to 100 for percentage)\n", + " **kwargs # Additional args for `Progress` tag\n", + " ): # Progress(..., cls='uk-progress')\n", + " \"Creates a progress bar\"\n", " return fh.Progress(*c, value=value, max=max, cls=('uk-progress',stringify(cls)), **kwargs)" ] }, @@ -1199,7 +1234,13 @@ "outputs": [], "source": [ "#| export\n", - "def UkIcon(icon,height=None,width=None,stroke_width=None,cls=(), **kwargs):\n", + "def UkIcon(icon:str, # Icon name from [lucide icons](https://lucide.dev/icons/)\n", + " height:int=None, \n", + " width:int=None, \n", + " stroke_width:int=None, # Thickness of lines\n", + " cls=(), # Additional classes on the `Uk_icon` tag\n", + " **kwargs # Additional args for `Uk_icon` tag\n", + " ): # a lucide icon of the specified size \n", " \"Creates an icon using lucide icons\"\n", " return Uk_icon(icon=icon, height=height, width=width, stroke_width=stroke_width, cls=cls, **kwargs)" ] @@ -1212,13 +1253,14 @@ "outputs": [], "source": [ "#| export\n", - "def UkIconLink(icon, \n", - " height=None,\n", - " width=None,\n", - " stroke_width=None,\n", - " cls=(), \n", - " button=False, \n", - " **kwargs):\n", + "def UkIconLink(icon:str, # Icon name from [lucide icons](https://lucide.dev/icons/)\n", + " height:int=None, \n", + " width:int=None, \n", + " stroke_width:int=None, # Thickness of lines\n", + " cls=(), # Additional classes on the icon\n", + " button:bool=False, # Whether to use a button (defaults to a link)\n", + " **kwargs # Additional args for `A` or `Button` tag\n", + " ): # a lucide icon button or link of the specified size\n", " \"Creates an icon link using lucide icons\"\n", " fn = fh.Button if button else fh.A\n", " return fn(cls=(f\"uk-icon-{'button' if button else 'link'}\", stringify(cls)), **kwargs)(\n", @@ -1246,9 +1288,9 @@ "outputs": [], "source": [ "#| export\n", - "def DiceBearAvatar(seed_name, # Seed name (ie 'Isaac Flath')\n", - " h=20, # Height \n", - " w=20, # Width\n", + "def DiceBearAvatar(seed_name:str, # Seed name (ie 'Isaac Flath')\n", + " h:int=20, # Height \n", + " w:int=20, # Width\n", " ): # Span with Avatar\n", " \"Creates an Avatar using https://dicebear.com/\"\n", " url = 'https://api.dicebear.com/8.x/lorelei/svg?seed='\n", @@ -1308,12 +1350,22 @@ "outputs": [], "source": [ "#|export\n", - "def Grid(*div, cols_min=1, cols_max=None, cols_sm=None, cols_md=None, cols_lg=None, cols_xl=None, cols=None, cls='gap-4', **kwargs):\n", + "def Grid(*div, # `Div` components to put in the grid\n", + " cols_min:int=1, # Minimum number of columns at any screen size\n", + " cols_max:int=4, # Maximum number of columns allowed at any screen size\n", + " cols_sm:int=None, # Number of columns on small screens\n", + " cols_md:int=None, # Number of columns on medium screens\n", + " cols_lg:int=None, # Number of columns on large screens\n", + " cols_xl:int=None, # Number of columns on extra large screens\n", + " cols:int=None, # Number of columns on all screens\n", + " cls='gap-4', # Additional classes on the grid (tip: `gap` provides spacing for grids)\n", + " **kwargs # Additional args for `Div` tag\n", + " ): # Responsive grid component\n", " \"Creates a responsive grid layout with smart defaults based on content\"\n", " if cols: cols_min = cols_sm = cols_md = cols_lg = cols_xl = cols\n", " else:\n", " n = len(div)\n", - " cols_max = min(n, cols_max or 4)\n", + " cols_max = min(n, cols_max)\n", " cols_sm = cols_sm or min(n, cols_min, cols_max)\n", " cols_md = cols_md or min(n, cols_min+1, cols_max) \n", " cols_lg = cols_lg or min(n, cols_min+2, cols_max) \n", @@ -1340,7 +1392,7 @@ "source": [ "#| exports\n", "def DivFullySpaced(*c, # Components\n", - " cls='uk-width-1-1',# Classes for outer div\n", + " cls='uk-width-1-1',# Classes for outer div (`uk-width-1-1` makes it use all available width)\n", " **kwargs # Additional args for outer div\n", " ): # Div with spaced components via flex classes\n", " \"Creates a flex div with it's components having as much space between them as possible\"\n", @@ -1357,12 +1409,13 @@ "source": [ "#| export\n", "def DivCentered(*c, # Components\n", - " cls='space-y-4', # Classes for outer div\n", + " cls='space--4', # Classes for outer div (`space-y-4` provides spacing between components)\n", + " vstack=True, # Whether to stack the components vertically\n", " **kwargs # Additional args for outer div\n", " ): # Div with components centered in it\n", " \"Creates a flex div with it's components centered in it\"\n", " cls=stringify(cls)\n", - " return Div(cls=(FlexT.block,FlexT.column,FlexT.middle,FlexT.center,cls),**kwargs)(*c)" + " return Div(cls=(FlexT.block,(FlexT.column if vstack else FlexT.row),FlexT.middle,FlexT.center,cls),**kwargs)(*c)" ] }, { @@ -1373,7 +1426,7 @@ "outputs": [], "source": [ "#| export\n", - "def DivLAligned(*c, # Components\n", + "def DivLAligned(*c, # Components\n", " cls='space-x-4', # Classes for outer div\n", " **kwargs # Additional args for outer div\n", " ): # Div with components aligned to the left\n", @@ -1390,7 +1443,7 @@ "outputs": [], "source": [ "#| export\n", - "def DivRAligned(*c, # Components\n", + "def DivRAligned(*c, # Components\n", " cls='space-x-4', # Classes for outer div\n", " **kwargs # Additional args for outer div\n", " ): # Div with components aligned to the right\n", @@ -1407,7 +1460,10 @@ "outputs": [], "source": [ "#| export\n", - "def DivVStacked(*c, cls='space-y-4', **kwargs):\n", + "def DivVStacked(*c, # Components\n", + " cls='space-y-4', # Additional classes on the div (tip: `space-y-4` provides spacing between components)\n", + " **kwargs # Additional args for the div\n", + " ): # Div with components stacked vertically\n", " \"Creates a flex div with it's components stacked vertically\"\n", " cls=stringify(cls)\n", " return Div(cls=(FlexT.block,FlexT.column,FlexT.middle,cls), **kwargs)(*c)" @@ -1421,7 +1477,10 @@ "outputs": [], "source": [ "#| export\n", - "def DivHStacked(*c, cls='space-x-4', **kwargs):\n", + "def DivHStacked(*c, # Components\n", + " cls='space-x-4', # Additional classes on the div (`space-x-4` provides spacing between components)\n", + " **kwargs # Additional args for the div\n", + " ): # Div with components stacked horizontally\n", " \"Creates a flex div with it's components stacked horizontally\"\n", " cls=stringify(cls)\n", " return Div(cls=(FlexT.block,FlexT.row,FlexT.middle,cls), **kwargs)(*c)" @@ -1470,12 +1529,13 @@ "outputs": [], "source": [ "#| export\n", - "def NavContainer(*li, \n", - " cls=NavT.primary,\n", - " parent=True, \n", - " uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options\n", - " **kwargs):\n", - " \"Creates a navigation container. A Nav is a list (NavBar is something different)\"\n", + "def NavContainer(*li, # List items are navigation elements (Special `Li` such as `NavParentLi`, `NavDividerLi`, `NavHeaderLi`, `NavSubtitle`, `NavCloseLi` can also be used)\n", + " cls=NavT.primary, # Additional classes on the nav\n", + " parent=True, # Whether this nav is a *parent* or *sub* nav\n", + " uk_nav=False, #True for default collapsible behavior, see [frankenui docs](https://franken-ui.dev/docs/nav#component-options) for more advanced options\n", + " **kwargs # Additional args\n", + " ): # FT Component that is a list of `Li` styled for a sidebar navigation menu\n", + " \"Creates a navigation container (useful for creating a sidebar navigation). A Nav is a list (NavBar is something different)\"\n", " return fh.Ul(*li, uk_nav=uk_nav, cls=(f\"uk-nav{'' if parent else '-sub'}\", stringify(cls)),**kwargs)" ] }, @@ -1487,11 +1547,36 @@ "outputs": [], "source": [ "#| export\n", - "def NavParentLi(*nav_container, cls=(), **kwargs): return fh.Li(*nav_container, cls=('uk-parent', stringify(cls)),**kwargs)\n", - "def NavDividerLi(*c,cls=(), **kwargs): return fh.Li(*c, cls=('uk-nav-divider', stringify(cls)),**kwargs)\n", - "def NavHeaderLi(*c,cls=(), **kwargs): return fh.Li(*c, cls=('uk-nav-header', stringify(cls)),**kwargs)\n", - "def NavSubtitle(*c,cls=TextFont.muted_sm, **kwargs): return fh.Div(*c, cls=('uk-nav-subtitle', stringify(cls)),**kwargs)\n", - "def NavCloseLi(*c,cls=(), **kwargs): return fh.Li(*c, cls=('uk-drop-close', stringify(cls)),**kwargs)" + "def NavParentLi(*nav_container, # `NavContainer` container for a nested nav with `parent=False`)\n", + " cls=(), # Additional classes on the li\n", + " **kwargs # Additional args for the li\n", + " ): # Navigation list item\n", + " \"Creates a navigation list item with a parent nav for nesting\"\n", + " return fh.Li(*nav_container, cls=('uk-parent', stringify(cls)),**kwargs)\n", + "def NavDividerLi(*c, # Components\n", + " cls=(), # Additional classes on the li\n", + " **kwargs # Additional args for the li\n", + " ): # Navigation list item with a divider\n", + " \"Creates a navigation list item with a divider\"\n", + " return fh.Li(*c, cls=('uk-nav-divider', stringify(cls)),**kwargs)\n", + "def NavHeaderLi(*c, # Components\n", + " cls=(), # Additional classes on the li\n", + " **kwargs # Additional args for the li\n", + " ): # Navigation list item with a header\n", + " \"Creates a navigation list item with a header\"\n", + " return fh.Li(*c, cls=('uk-nav-header', stringify(cls)),**kwargs)\n", + "def NavSubtitle(*c, # Components\n", + " cls=TextFont.muted_sm, # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # Navigation subtitle\n", + " \"Creates a navigation subtitle\"\n", + " return fh.Div(*c, cls=('uk-nav-subtitle', stringify(cls)),**kwargs)\n", + "def NavCloseLi(*c, # Components\n", + " cls=(), # Additional classes on the li\n", + " **kwargs # Additional args for the li\n", + " ): # Navigation list item with a close button\n", + " \"Creates a navigation list item with a close button\"\n", + " return fh.Li(*c, cls=('uk-drop-close', stringify(cls)),**kwargs)" ] }, { @@ -1520,16 +1605,33 @@ "outputs": [], "source": [ "#| export\n", - "def NavBarContainer(*c, \n", - " cls=(),\n", - " container_cls=ContainerT.expand,\n", - " uk_navbar=True,\n", - " **kwargs): \n", + "def NavBarContainer(*navbarside, # Components (typically `NavBarLSide` or `NavBarRSide` or `NavBarCenter`)\n", + " cls=(), # Additional classes on the container\n", + " container_cls=ContainerT.expand, # Additional classes on the container\n", + " uk_navbar=True, # Whether to use a navbar\n", + " **kwargs # Additional args for the container\n", + " ): # NavBar container\n", " \"Create a NavBarContainer to put NavBar sides in\"\n", - " return fh.Div(Container(Div(*c, uk_navbar=uk_navbar),cls=stringify(container_cls)), cls=('',stringify(cls)), **kwargs) #uk-navbar-container\n", - "def NavBarLSide(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-navbar-left', stringify(cls)), **kwargs)\n", - "def NavBarRSide(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-navbar-right', stringify(cls)), **kwargs)\n", - "def NavBarCenter(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-navbar-center',stringify(cls)), **kwargs)" + " return fh.Div(Container(Div(*navbarside, uk_navbar=uk_navbar),cls=stringify(container_cls)), cls=('',stringify(cls)), **kwargs) #uk-navbar-container\n", + "\n", + "def NavBarLSide(*c, # Components\n", + " cls=(), # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # NavBar left side\n", + " \"Creates a NavBar left side\"\n", + " return fh.Div(*c, cls=('uk-navbar-left', stringify(cls)), **kwargs)\n", + "def NavBarRSide(*c, # Components\n", + " cls=(), # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # NavBar right side\n", + " \"Creates a NavBar right side\"\n", + " return fh.Div(*c, cls=('uk-navbar-right', stringify(cls)), **kwargs)\n", + "def NavBarCenter(*c, # Components\n", + " cls=(), # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # NavBar center\n", + " \"Creates a NavBar center\"\n", + " return fh.Div(*c, cls=('uk-navbar-center',stringify(cls)), **kwargs)" ] }, { @@ -1540,8 +1642,11 @@ "outputs": [], "source": [ "#| export\n", - "def NavBarNav(*li, cls=(), **kwargs):\n", - " \"A Nav that is part of a NavBar\"\n", + "def NavBarNav(*li, # Components\n", + " cls=(), # Additional classes on the nav\n", + " **kwargs # Additional args for the nav\n", + " ): # Nav that is part of a NavBar\n", + " \"A Nav that is part of a NavBar that could go in a `NavBarLSide`, `NavBarRSide`, or `NavBarCenter`\"\n", " return fh.Nav(*li, cls=('uk-navbar-nav', stringify(cls)), **kwargs)" ] }, @@ -1553,7 +1658,13 @@ "outputs": [], "source": [ "#| export\n", - "def NavBarSubtitle(title, subtitle, cls=(), subtitle_cls=TextFont.muted_sm, **kwargs): \n", + "def NavBarSubtitle(title, # Title\n", + " subtitle, # Subtitle\n", + " cls=(), # Additional classes on the div\n", + " subtitle_cls=TextFont.muted_sm, # Additional classes on the subtitle\n", + " **kwargs # Additional args for the div\n", + " ): # NavBar subtitle\n", + " \"Creates a NavBar subtitle\"\n", " return fh.Div(title,fh.Div(subtitle, cls=('uk-navbar-subtitle', stringify(subtitle_cls))), cls=stringify(cls), **kwargs)" ] }, @@ -1565,11 +1676,12 @@ "outputs": [], "source": [ "#| export\n", - "def NavBarNavContainer(*li, \n", - " cls=NavT.primary,\n", - " parent=True, \n", - " uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options\n", - " **kwargs):\n", + "def NavBarNavContainer(*li, # Components\n", + " cls=NavT.primary, # Additional classes on the nav\n", + " parent=True, # Whether to use a parent nav\n", + " uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options\n", + " **kwargs # Additional args for the nav\n", + " ): # NavBar nav container\n", " return Div(cls=\"uk-navbar-dropdown\")(NavContainer(*li, cls=('uk-navbar-dropdown-nav',stringify(cls)), uk_nav=uk_nav, parent=parent, **kwargs))" ] }, @@ -1600,12 +1712,13 @@ "outputs": [], "source": [ "#| export\n", - "def DropDownNavContainer(*li, \n", - " cls=NavT.primary,\n", - " parent=True, \n", + "def DropDownNavContainer(*li, # Components\n", + " cls=NavT.primary, # Additional classes on the nav\n", + " parent=True, # Whether to use a parent nav\n", " uk_nav=False, #True for default collapsible behavior, see https://franken-ui.dev/docs/nav#component-options for more advanced options\n", - " uk_dropdown=True,\n", - " **kwargs):\n", + " uk_dropdown=True, # Whether to use a dropdown\n", + " **kwargs # Additional args for the nav\n", + " ): # DropDown nav container\n", " \"A Nav that is part of a DropDown\"\n", " return Div(cls = 'uk-drop uk-dropdown',uk_dropdown=uk_dropdown)(NavContainer(*li, cls=('uk-dropdown-nav',stringify(cls)), uk_nav=uk_nav, parent=parent, **kwargs))" ] @@ -1626,8 +1739,12 @@ "outputs": [], "source": [ "#| export\n", - "def TabContainer(*li,cls='', alt=False, **kwargs):\n", - " \"A Tab that is part of a TabContainer\"\n", + "def TabContainer(*li, # Components\n", + " cls='', # Additional classes on the `Ul`\n", + " alt=False, # Whether to use an alternative tab style\n", + " **kwargs # Additional args for the `Ul`\n", + " ): # Tab container\n", + " \"A TabContainer where children will be different tabs\"\n", " cls = stringify(cls)\n", " return Ul(cls=(f\"uk-tab{'-alt' if alt else ''}\",stringify(cls)),**kwargs)(*li)" ] @@ -1665,19 +1782,39 @@ "outputs": [], "source": [ "#| export\n", - "def CardTitle(*c, cls=(), **kwargs):\n", + "def CardTitle(*c, # Components (often a string)\n", + " cls=(), # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): \n", + " \"Creates a card title\"\n", " return fh.Div(*c, cls=('uk-card-title',stringify(cls)), **kwargs)\n", "\n", - "def CardHeader(*c, cls=(), **kwargs):\n", + "def CardHeader(*c, # Components that goes in the header (often a `ModalTitle` and description)\n", + " cls=(), # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # Container for the header of a card\n", + " \"Creates a card header\"\n", " return fh.Div(*c, cls=('uk-card-header',stringify(cls)), **kwargs)\n", "\n", - "def CardBody(*c, cls=(), **kwargs):\n", + "def CardBody(*c, # Components that go in the body (Main content of the card such as a form, and image, a signin form, etc.)\n", + " cls=(), # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # Container for the body of a card\n", + " \"Creates a card body\"\n", " return fh.Div(*c, cls=('uk-card-body',stringify(cls)), **kwargs)\n", "\n", - "def CardFooter(*c, cls=(), **kwargs):\n", + "def CardFooter(*c, # Components that go in the footer (often a `ModalCloseButton`)\n", + " cls=(), # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # Container for the footer of a card\n", + " \"Creates a card footer\"\n", " return fh.Div(*c, cls=('uk-card-footer',stringify(cls)), **kwargs)\n", "\n", - "def CardContainer(*c, cls=CardT.default, **kwargs):\n", + "def CardContainer(*c, # Components (typically `CardHeader`, `CardBody`, `CardFooter`)\n", + " cls=CardT.default, # Additional classes on the div\n", + " **kwargs # Additional args for the div\n", + " ): # Container for a card\n", + " \"Creates a card container\"\n", " return fh.Div(*c, cls=('uk-card',stringify(cls)), **kwargs)" ] }, @@ -1689,15 +1826,15 @@ "outputs": [], "source": [ "#|export\n", - "def Card(*c, # Components that go in the body\n", - " header=None, # Components that go in the header\n", - " footer=None, # Components that go in the footer\n", + "def Card(*c, # Components that go in the body (Main content of the card such as a form, and image, a signin form, etc.)\n", + " header=None, # A component that goes in the header (often a `ModalTitle` and description)\n", + " footer=None, # A component that goes in the footer (often a `ModalCloseButton`)\n", " body_cls='space-y-6', # classes for the body\n", " header_cls=(), # classes for the header\n", " footer_cls=(), # classes for the footer\n", " cls=(), #class for outermost component\n", - " **kwargs # classes that for the card itself\n", - " ):\n", + " **kwargs # additional arguments for the `CardContainer`\n", + " ): # Card component\n", " \"Creates a Card with a header, body, and footer\"\n", " header_cls, footer_cls, body_cls, cls = map(stringify, (header_cls, footer_cls, body_cls, cls))\n", " res = []\n", @@ -1743,9 +1880,12 @@ "outputs": [], "source": [ "#| export\n", - "def Table(*args, cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), **kwargs): \n", + "def Table(*c, # Components (typically `Thead`, `Tbody`, `Tfoot`)\n", + " cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), # Additional classes on the table\n", + " **kwargs # Additional args for the table\n", + " ): # Table component\n", " \"Creates a table\"\n", - " return fh.Table(cls=('uk-table', stringify(cls)), *args, **kwargs)" + " return fh.Table(cls=('uk-table', stringify(cls)), *c, **kwargs)" ] }, { @@ -1756,21 +1896,26 @@ "outputs": [], "source": [ "#| export\n", - "def _TableCell(Component, *args, cls=(), shrink=False, expand=False, small=False, **kwargs):\n", + "def _TableCell(Component, \n", + " *c, # Components that go in the cell\n", + " cls=(), # Additional classes on the cell container\n", + " shrink=False, # Whether to shrink the cell\n", + " expand=False, # Whether to expand the cell\n", + " small=False, # Whether to use a small table\n", + " **kwargs # Additional args for the cell\n", + " ): # Table cell\n", " \"Creates a table cell\"\n", " cls = stringify(cls)\n", " if shrink: cls += ' uk-table-shrink'\n", " if expand: cls += ' uk-table-expand'\n", " if small: cls += ' uk-table-small'\n", - " return Component(*args,cls=cls, **kwargs)\n", + " return Component(*c,cls=cls, **kwargs)\n", "\n", "@delegates(_TableCell, but=['Component'])\n", - "def Td(*args,**kwargs): return _TableCell(fh.Td, *args, **kwargs)\n", + "def Td(*c,**kwargs): return _TableCell(fh.Td, *c, **kwargs)\n", "@delegates(_TableCell, but=['Component'])\n", - "def Th(*args,**kwargs): return _TableCell(fh.Th, *args, **kwargs)\n", + "def Th(*c,**kwargs): return _TableCell(fh.Th, *c, **kwargs)\n", "\n", - "def Tr(*cells, cls=(), **kwargs): return fh.Tr(*cells, cls=stringify(cls), **kwargs)\n", - "def Thead(*rows, cls=(), **kwargs): return fh.Thead(*rows, cls=stringify(cls), **kwargs)\n", "def Tbody(*rows, cls=(), sortable=False, **kwargs): return fh.Tbody(*rows, cls=stringify(cls), uk_sortable=sortable, **kwargs)" ] }, @@ -1782,10 +1927,16 @@ "outputs": [], "source": [ "#|export\n", - "def TableFromLists(header_data, body_data, footer_data=None, \n", - " header_cell_render=Th,body_cell_render=Td, footer_cell_render=Td,\n", - " cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), \n", - " sortable=False, **kwargs):\n", + "def TableFromLists(header_data:Sequence, # List of header data\n", + " body_data:Sequence[Sequence], # List of lists of body data\n", + " footer_data=None, # List of footer data\n", + " header_cell_render=Th, # Function(content) -> FT that renders header cells\n", + " body_cell_render=Td, # Function(key, content) -> FT that renders body cells\n", + " footer_cell_render=Td, # Function(key, content) -> FT that renders footer cells\n", + " cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), # Additional classes on the table\n", + " sortable=False, # Whether to use sortable table\n", + " **kwargs # Additional args for the table\n", + " ): # Table from lists\n", " \"Creates a Table from a list of header data and a list of lists of body data\"\n", " return Table(\n", " Thead(Tr(*map(header_cell_render, header_data))),\n", @@ -1803,11 +1954,16 @@ "outputs": [], "source": [ "#| export\n", - "def TableFromDicts(header_data:Sequence, body_data:Sequence[dict], footer_data=None, \n", - " header_cell_render=Th, body_cell_render=lambda k,v : Td(v), footer_cell_render=lambda k,v : Td(v),\n", - " cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small),\n", - " sortable=False,\n", - " **kwargs):\n", + "def TableFromDicts(header_data:Sequence, # List of header data\n", + " body_data:Sequence[dict], # List of dicts of body data\n", + " footer_data=None, # List of footer data\n", + " header_cell_render=Th, # Function(content) -> FT that renders header cells\n", + " body_cell_render=lambda k,v : Td(v), # Function(key, content) -> FT that renders body cells\n", + " footer_cell_render=lambda k,v : Td(v), # Function(key, content) -> FT that renders footer cells\n", + " cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), # Additional classes on the table\n", + " sortable=False, # Whether to use sortable table\n", + " **kwargs # Additional args for the table\n", + " ): # Styled Table\n", " \"Creates a Table from a list of header data and a list of dicts of body data\"\n", " return Table(\n", " Thead(Tr(*[header_cell_render(h) for h in header_data])),\n", @@ -1850,8 +2006,9 @@ " 'li': 'leading-relaxed',\n", " \n", " # Code and quotes\n", - " 'pre': 'bg-base-200 rounded-lg p-4 mb-6 overflow-x-auto',\n", + " 'pre': 'bg-base-200 rounded-lg p-4 mb-6',\n", " 'code': 'uk-codespan px-1',\n", + " 'pre code': 'uk-codespan px-1 block overflow-x-auto',\n", " 'blockquote': 'uk-blockquote pl-4 border-l-4 border-primary italic mb-6',\n", " \n", " # Tables\n", @@ -1868,14 +2025,32 @@ { "cell_type": "code", "execution_count": null, - "id": "15a51b01", + "id": "1f310bf0", + "metadata": {}, + "outputs": [], + "source": [ + "def enum_to_html_table(enum_class):\n", + " headers = [\"Option\", \"Value\"]\n", + " rows = [[name, value.value] for name, value in enum_class.__members__.items()]\n", + " return Div(\n", + " Hr(cls='uk-divider-icon my-4'),\n", + " H3(enum_class.__name__,cls='my-4'),\n", + " P(I(enum_class.__doc__)),\n", + " TableFromLists(headers, rows, cls=(TableT.hover, 'uk-table-small')),)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b4bcb8c6", "metadata": {}, "outputs": [], "source": [ "#| export\n", - "def apply_classes(html_str:str, \n", - " class_map=None, \n", - " class_map_mods=None):\n", + "def apply_classes(html_str:str, # Html string\n", + " class_map=None, # Class map\n", + " class_map_mods=None # Class map that will modify the class map map (useful for small changes to a base class map)\n", + " ): # Html string with classes applied\n", " \"Apply classes to html string\"\n", " if not html_str: return html_str\n", " try:\n", @@ -1884,8 +2059,10 @@ "\n", " if class_map_mods: class_map = {**class_map, **class_map_mods}\n", " html_str = html.fromstring(html_str)\n", - " for tag, classes in class_map.items():\n", - " for element in html_str.xpath(f'//{tag}'):\n", + " for selector, classes in class_map.items():\n", + " # Handle descendant selectors (e.g., 'pre code')\n", + " xpath = '//' + '/descendant::'.join(selector.split())\n", + " for element in html_str.xpath(xpath):\n", " existing_class = element.get('class', '')\n", " new_class = f\"{existing_class} {classes}\".strip()\n", " element.set('class', new_class)\n", @@ -1948,7 +2125,10 @@ "outputs": [], "source": [ "#| export\n", - "def render_md(md_content:str, class_map=None, class_map_mods=None):\n", + "def render_md(md_content:str, # Markdown content\n", + " class_map=None, # Class map\n", + " class_map_mods=None # Additional class map\n", + " ): # Rendered markdown\n", " \"Renders markdown using mistletoe and lxml\"\n", " if md_content=='': return md_content\n", " # Check for required dependencies\n", @@ -1974,7 +2154,7 @@ { "data": { "text/plain": [ - "'

Look here:

\\n\\n
a = \"aa\"\\n
\\n
'" + "'

Look here:

\\n\\n
a = \"aa\"\\n
\\n
'" ] }, "execution_count": null,