Things to remembers when using Fh_Frankenui: - While Pico CSS is commonly used in FastHTML apps, that is *completely incompatible* with Fh_Frankenui. - Fh_Frankenui is *not* compatible with FastAPI syntax and is not targeted at creating API services.# Tailwind CSS Cheat Sheet ## Spacing ``` px 0.5 1 1.5 2 2.5 3 3.5 4 5 6 7 8 9 10 11 12 14 16 20 24 28 32 36 40 44 48 52 56 60 64 72 80 96 ``` ## Breakpoints ``` <640px - flex 640px - sm:flex 768px - md:flex 1024px - lg:flex 1280px - xl:flex 1536px - 2xl:flex ``` ## Pseudo Class ``` hover focus focus-within focus-visible active visited target first last only odd even first-of-type last-of-type only-of-type empty disabled enabled checked indeterminate default required valid invalid in-range out-of-range placeholder-shown autofill read-only before after first-letter first-line marker selection file backdrop placeholder supports(-,_) data(-,_) ltr rtl ``` ## Pseudo Class - Media ``` sm md lg xl 2xl min-[] max-sm max-md max-lg max-xl max-2xl []-dark portrait landscape motion-safe motion-reduce contrast-more contrast-less print ``` ## Functions & Directives ``` detailwind @apply eApply @config theme() screen() ``` ## Opacity ``` 0 5 10 20 25 30 38 40 50 60 70 75 80 89 90 95 100 ``` ## Colors ``` slate gray zinc neutral stone amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose red orange 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 300 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 400 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 500 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 600 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 700 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 800 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 900 ``` ## Flex ``` flex flex-basis basis-auto basis-1/2 basis-1/3 basis-1/4 basis-1/5 basis-1/6 basis-1/12 basis-full flex-initial flex-none flex-auto grow grow-0 grow-1 shrink shrink-0 shrink-1 flex-dir flex-row flex-row-reverse flex-col flex-col-reverse flex-wrap wrap flex-wrap-reverse order-first order-last order-none order-1 order-2 ``` ## Grid ``` grid-template-rows grid-rows-none grid-rows-1 grid-rows-2 grid-rows-3 grid-rows-4 grid-rows-5 grid-rows-6 grid-rows-auto grid-template-columns grid-cols-1 grid-cols-2 grid-cols-3 grid-cols-4 grid-cols-5 grid-cols-6 grid-cols-7 grid-cols-8 grid-cols-9 grid-cols-10 grid-cols-11 grid-cols-12 grid-cols-auto grid-auto-flow grid-flow-row grid-flow-col grid-flow-row-dense grid-flow-col-dense grid-auto-rows auto-rows-auto auto-rows-min auto-rows-max auto-rows-fr grid-auto-cols auto-cols-auto auto-cols-min auto-cols-max auto-cols-fr grid-row-start row-span-full row-span-1 row-span-2 row-start-auto row-end-auto row-start-1 row-end-1 ``` ## Spacing ``` padding p-[spacing] p-t-[spacing] p-r-[spacing] p-b-[spacing] p-l-[spacing] margin m-auto m-[spacing] m-t-[spacing] m-r-[spacing] m-b-[spacing] m-l-[spacing] space-between space-x-[spacing] space-y-[spacing] space-x-reverse space-y-reverse ``` ## FlexGrid ``` gap gap-[spacing] gap-x-[spacing] gap-y-[spacing] justify-content justify-start justify-end justify-center justify-between justify-around justify-evenly justify-items justify-items-start justify-items-end justify-items-center justify-items-stretch justify-items-baseline justify-items-auto justify-self justify-self-auto justify-self-start justify-self-end justify-self-center justify-self-stretch align-content content-start content-end content-center content-between content-around content-evenly content-baseline align-items items-start items-end items-center items-stretch items-baseline items-auto place-content place-content-start place-content-end place-content-center place-content-between place-content-around place-content-evenly place-content-baseline place-items place-items-auto place-items-start place-items-end place-items-center place-items-stretch place-items-baseline place-self place-self-auto place-self-start place-self-end place-self-center place-self-stretch ``` ## Border ``` border-divide-outline-ring-offset-color border-divide-ring-offset-fast-1 border-divide-ring-offset-others-current border-divide-ring-offset-white border-divide-ring-offset-black border-divide-ring-offset-[color] border-divide-outline-ring-style border-divide-outline border-divide-dotted border-divide-dashed border-divide-solid border-divide-double border-hidden outline border-width border border-[1|2|3|4] border-x-[1|2|3|4] border-y-[1|2|3|4] border-t-[1|2|3|4] border-r-[1|2|3|4] border-b-[1|2|3|4] border-l-[1|2|3|4] divide-width divide divide-x divide-y divide-reverse ring-width ring ring-inset ring-offset-width ring-offset-[1|2|3|4] outline-width outline outline-offset-width outline-offset-[1|2|3|4] border-radius rounded rounded-sm rounded-md rounded-lg rounded-xl rounded-2xl rounded-full rounded-[1|2|3|4] rounded-t-[1|2|3|4] rounded-r-[1|2|3|4] rounded-b-[1|2|3|4] rounded-l-[1|2|3|4] rounded-tr-[1|2|3|4] rounded-br-[1|2|3|4] rounded-bl-[1|2|3|4] rounded-tl-[1|2|3|4] ``` ## Layout ``` container columns columns-1 columns-2 columns-3 columns-4 columns-5 columns-6 columns-7 columns-auto columns-[...nums] box-sizing box-border box-content float float-left float-right float-none clear clear-left clear-right clear-both clear-none positions static relative absolute fixed sticky visible invisible collapse display block inline-block inline flex grid inline-flex table table-caption table-cell table-row table-row-group table-header-group table-footer-group table-col table-col-group hidden contents flow-root positions-t-b [top|right|bottom|left] [top|right|bottom|left]-[spacing] [top|right|bottom|left]-auto [top|right|bottom|left]-full inset inset-auto inset-[spacing] inset-0 inset-x-[1|2|3|4] inset-y-[1|2|3|4] inset-t-[1|2|3|4] inset-r-[1|2|3|4] inset-b-[1|2|3|4] inset-l-[1|2|3|4] break-before break-before:auto break-before:column break-before:page break-before:avoid-page break-before:left break-before:right break-before:avoid-column break-inside break-inside:auto break-inside:avoid-page break-inside:avoid-column overflow overflow-auto overflow-hidden overflow-visible overflow-x-auto overflow-x-hidden overflow-x-visible overflow-y-auto overflow-y-hidden overflow-y-visible overflow-clip overflow-scroll overscroll-behavior overscroll-auto overscroll-none overscroll-contain overscroll-x-auto overscroll-x-none overscroll-x-contain overscroll-y-auto overscroll-y-none overscroll-y-contain aspect-ratio aspect-auto aspect-video aspect-square box-decoration-break box-decoration-clone box-decoration-slice isolation isolate isolation-auto object-fit object-contain object-cover object-fill object-none object-scale-down object-positions object-center object-left object-right object-top object-bottom object-[left|top|right|bottom] z-index z-0 z-10 z-20 z-30 z-40 z-50 z-auto ``` ## Typography ``` family font-sans font-serif font-mono font-size text-xs text-sm text-base text-lg text-xl text-2xl text-3xl text-4xl text-5xl text-6xl text-7xl text-8xl text-9xl text-[] line-height leading-none leading-tight leading-snug leading-normal leading-relaxed leading-loose text-align text-left text-center text-right text-justify text-start text-end text-transform uppercase lowercase capitalize normal-case text-overflow truncate text-ellipsis text-clip style italic not-italic weight font-thin font-light font-medium font-semibold font-bold font-extrabold font-black variant-numeric ordinal slashed-zero lining-nums oldstyle-nums proportional-nums tabular-nums diagonal-fractions stacked-fractions letter-spacing tracking-tight tracking-tighter tracking-normal tracking-wide tracking-wider placeholder-color [placeholder-color]-[color] [placeholder-color]-[transparent] [placeholder-color]-[current] vertical-align align-baseline align-top align-middle align-bottom align-text-bottom align-text-top align-sub align-super text-indent indent-[spacing] text-opacity opacity-[opacity] text-decoration underline line-through overline no-underline text-decoration-color decoration-current decoration-transparent decoration-inherit decoration-white decoration-black decoration-[color] text-decoration-style decoration-solid decoration-dashed decoration-dotted decoration-wavy text-decoration-thickness decoration-from-font decoration-auto decoration-[1|2|3|4] text-decoration-offset underline-offset-auto underline-offset-[1|2|3|4] smoothing antialiased subpixel-antialiased list-style-type list-none list-disc list-decimal list-inside list-outside list-style-position list-inside list-outside white-space whitespace-normal whitespace-nowrap whitespace-pre whitespace-pre-wrap whitespace-pre-line word-break break-normal break-words break-all break-keep content content-none ``` ## Background ``` bg-attachment bg-fixed bg-local bg-scroll bg-clip bg-clip-border bg-clip-padding bg-clip-content bg-clip-text bg-color [bg-color]-[color] [bg-color]-[transparent] [bg-color]-[current] [bg-color]-[inherit] [bg-color]-[black] [bg-color]-[white] [bg-color]-[color] bg-position bg-center bg-left bg-right bg-top bg-bottom bg-[left|right|top|bottom] bg-image bg-none bg-gradient-to-[direction] bg-gradient-to-[t|r|b|l] bg-gradient-to-t bg-gradient-to-r bg-gradient-to-b bg-gradient-to-l bg-repeat bg-no-repeat bg-repeat-x bg-repeat-y bg-repeat-round bg-repeat-space bg-size bg-auto bg-cover bg-contain bg-origin bg-border bg-padding bg-content bg-origin-border ``` ## Interactivity ``` accent-color accent-transparent accent-current accent-white accent-black accent-[color] appearance appearance-none cursor cursor-auto cursor-default cursor-pointer cursor-grab cursor-grabbing cursor-crosshair cursor-move cursor-not-allowed cursor-context-menu cursor-help cursor-wait cursor-text cursor-resize cursor-ns-resize cursor-ew-resize cursor-ne-resize cursor-nw-resize cursor-se-resize cursor-sw-resize cursor-zoom-in cursor-zoom-out pointer-events pointer-events-none pointer-events-auto scroll-behavior scroll-auto scroll-smooth scroll-margin [scroll-margin]-[spacing] [scroll-margin]-[spacing] [scroll-margin]-[spacing] [scroll-margin]-[spacing] scroll-padding [scroll-padding]-[spacing] [scroll-padding]-[spacing] [scroll-padding]-[spacing] [scroll-padding]-[spacing] scroll-snap-align start end center snap-align-none scroll-snap-stop normal always scroll-snap-type snap-none snap-x snap-y snap-both snap-mandatory snap-proximity resize resize resize-none resize-x resize-y touch-action touch-auto touch-none touch-pan-x touch-pan-y touch-pan-left touch-pan-right touch-pan-down touch-pan-up touch-manipulation user-select user-select-none select-text select-all select-auto will-change will-change-auto will-change-scroll will-change-contents will-change-transform ``` ## Filters ``` brightness brightness-[0|1|2|3|4|5|6|7|8|9] brightness-[100|150|200] backdrop-brightness-[0|1|2|3|4|5|6|7|8|9] backdrop-brightness-[100|150|200] contrast contrast-[0|1|2|3|4|5|6|7|8|9] contrast-[100|150|200] backdrop-contrast-[0|1|2|3|4|5|6|7|8|9] backdrop-contrast-[100|150|200] hue-rotate hue-rotate-[0|1|2|3|4|5|6|7|8|9] hue-rotate-[100|150|200] backdrop-hue-rotate-[0|1|2|3|4|5|6|7|8|9] backdrop-hue-rotate-[100|150|200] backdrop-opacity backdrop-opacity-[opacity] saturate saturate-[0|1|2|3|4|5|6|7|8|9] saturate-[100|150|200] backdrop-saturate-[0|1|2|3|4|5|6|7|8|9] backdrop-saturate-[100|150|200] blur blur-blur blur-blur-2xl blur-blur-xl blur-blur-lg blur-blur-md blur-blur-sm drop-shadow drop-shadow drop-shadow-[sm|md|lg|xl|2xl] grayscale grayscale grayscale-50 grayscale-75 grayscale-100 grayscale-invert grayscale-invert grayscale-invert-50 grayscale-invert-75 grayscale-invert-100 backdrop-grayscale-invert-[sepia] ``` ## Table ``` border-collapse border-collapse border-separate border-spacing border-spacing-[spacing] border-spacing-[spacing] border-spacing-[spacing] border-spacing-[spacing] table-layout table-auto table-fixed ``` ## Sizing ``` width|height w-[width|height] w-[spacing] w-auto w-full w-screen w-1/2 w-1/3 w-1/4 w-1/5 w-1/6 w-1/12 w-2/3 w-2/4 w-2/5 w-2/6 w-3/4 w-3/5 w-3/6 w-4/5 w-4/6 w-5/6 w-11/12 w-full min-width|height min-w-[width|height] min-w-[spacing] min-w-0 min-w-full min-w-screen min-w-[0|1|2|3|4|5|6] min-w-max max-width max-w-0 max-w-none max-w-xs max-w-sm max-w-md max-w-lg max-w-xl max-w-2xl max-w-screen-sm max-w-screen-md max-w-screen-lg max-w-screen-xl max-w-full max-w-[spacing] max-height max-h-[height] max-h-[spacing] max-h-0 max-h-full max-h-screen max-h-[spacing] max-h-max ``` ## Transition & Animation ``` property transition-none transition-all transition-colors transition-opacity transition-shadow transition-transform duration|delay [duration|delay]-[75|100|150|200|300|500|700|1000] [duration|delay]-[75|100|150|200|300|500|700|1000] [duration|delay]-[75|100|150|200|300|500|700|1000] timing function ease-linear ease-in ease-out ease-in-out animation animate-none animate-spin animate-ping animate-pulse animate-bounce ``` ## Transform ``` transform-origin origin-top origin-right origin-bottom origin-left origin-top-right origin-bottom-right origin-bottom-left origin-top-left origin-center scale scale-0 scale-50 scale-75 scale-90 scale-95 scale-100 scale-105 scale-110 scale-125 scale-150 scale-x-[0|1|2|3|4|5|6|7|8|9] scale-x-[100|150|200] scale-y-[0|1|2|3|4|5|6|7|8|9] scale-y-[100|150|200] rotate rotate-0 rotate-1 rotate-2 rotate-3 rotate-6 rotate-12 rotate-45 rotate-90 rotate-180 rotate-[0|1|2|3|4|5|6|7|8|9] rotate-[100|150|200] translate translate-x-[spacing] translate-x-[spacing] translate-x-[spacing] translate-x-[spacing] translate-y-[spacing] translate-y-[spacing] translate-y-[spacing] translate-y-[spacing] skew skew-x-[0|1|2|3|4|5|6|7|8|9] skew-x-[100|150|200] skew-y-[0|1|2|3|4|5|6|7|8|9] skew-y-[100|150|200] ``` ## Effect ``` mix-blend-mode mix-blend-normal mix-blend-multiply mix-blend-screen mix-blend-overlay mix-blend-darken mix-blend-lighten mix-blend-color-dodge mix-blend-color-burn mix-blend-hard-light mix-blend-soft-light mix-blend-difference mix-blend-exclusion mix-blend-hue mix-blend-saturation mix-blend-luminosity mix-blend-color mix-blend-plus-lighter box-shadow shadow shadow-[sm|md|lg|xl|2xl] shadow-none shadow-inner shadow-lg shadow-xl shadow-2xl box-shadow-color shadow-transparent shadow-inherit shadow-current shadow-white shadow-black shadow-[color] opacity opacity-[opacity] ``` ## Svg ``` fill [fill|stroke]-none [fill|stroke]-transparent [fill|stroke]-current [fill|stroke]-inherit [fill|stroke]-black [fill|stroke]-white [fill|stroke]-[color] stroke-width stroke stroke-[0|1|2] ``` ## Accessibility ``` screen-reader sr-only not-sr-only ```# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_core.ipynb. # %% auto 0 __all__ = ['franken_class_map', 'enum_to_markdown_table', 'fast_app', 'FastHTML', 'Theme', 'TextT', 'TextFont', 'ButtonT', 'Button', 'ButtonSubmit', 'H1', 'H2', 'H3', 'H4', 'Main', 'Titled', 'DividerT', 'Divider', 'DividerSplit', 'DividerLine', 'Article', 'ArticleTitle', 'ArticleMeta', 'ContainerT', 'Container', 'SectionT', 'Section', 'Fieldset', 'Legend', 'Input', 'Select', 'Radio', 'CheckboxX', 'Range', 'Toggle_switch', 'TextArea', 'Switch', 'FormLabel', 'LabelT', 'Label', 'UkFormSection', 'GenericLabelInput', 'LabelInput', 'LabelRadio', 'LabelCheckboxX', 'LabelRange', 'LabelTextArea', 'LabelSwitch', 'LabelSelect', 'Options', 'UkSelect', 'LabelUkSelect', 'AT', 'ListT', 'List', 'ModalContainer', 'ModalDialog', 'ModalHeader', 'ModalBody', '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', 'Tr', 'Thead', 'Tbody', 'TableFromLists', 'TableFromDicts', 'apply_classes', 'render_md'] # %% ../nbs/01_core.ipynb import fasthtml.common as fh from .foundations import * from fasthtml.common import is_listy, Div, P, Span, Script, FastHTML, FT, to_xml, show,fast_app from fasthtml.svg import Svg from fasthtml.components import Uk_theme_switcher, Main from enum import Enum, auto from fasthtml.components import Uk_select,Uk_input_tag,Uk_icon from functools import partial from itertools import zip_longest from typing import Union, Tuple, Optional from fastcore.all import * import copy, re # %% ../nbs/01_core.ipynb def enum_to_markdown_table(enum_class): headers = ["Option", "Value"] rows = [[name, value.value] for name, value in enum_class.__members__.items()] max_name_len = max(len(row[0]) for row in rows) max_value_len = max(len(row[1]) for row in rows) header = f"| {'Option':<{max_name_len}} | {'Value':<{max_value_len}} |" separator = f"|{'-'*(max_name_len+2)}|{'-'*(max_value_len+2)}|" body = "\n".join(f"| {row[0]:<{max_name_len}} | {row[1]:<{max_value_len}} |" for row in rows) return f"{header}\n{separator}\n{body}" # %% ../nbs/01_core.ipynb @delegates(fh.fast_app, but=['pico']) def fast_app(*args, pico=False, **kwargs): "Adds `bg-background text-foreground` to bodykw for frankenui themes" if 'bodykw' not in kwargs: kwargs['bodykw'] = {} if 'class' not in kwargs['bodykw']: kwargs['bodykw']['class'] = '' kwargs['bodykw']['class'] = stringify((kwargs['bodykw']['class'],'bg-background text-foreground')) return fh.fast_app(*args, pico=False, **kwargs) # %% ../nbs/01_core.ipynb @delegates(fh.FastHTML, but=['pico']) def FastHTML(*args, pico=False, **kwargs): "Adds `bg-background text-foreground` to bodykw for frankenui themes" if 'bodykw' not in kwargs: kwargs['bodykw'] = {} if 'class' not in kwargs['bodykw']: kwargs['bodykw']['class'] = '' kwargs['bodykw']['class'] = stringify((kwargs['bodykw']['class'],'bg-background text-foreground')) return fh.FastHTML(*args, pico=False, **kwargs) # %% ../nbs/01_core.ipynb def _headers_theme(color): return fh.Script(f'''const htmlElement = document.documentElement; if ( localStorage.getItem("mode") === "dark" || (!("mode" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches) ) {{ htmlElement.classList.add("dark"); }} else {{ htmlElement.classList.remove("dark"); }} htmlElement.classList.add(localStorage.getItem("theme") || "uk-theme-{color}"); ''') # %% ../nbs/01_core.ipynb class Theme(Enum): "Selector to choose theme and get all headers needed for app. Includes frankenui + tailwind" def _generate_next_value_(name, start, count, last_values): return name slate = auto() stone = auto() gray = auto() neutral = auto() red = auto() rose = auto() orange = auto() green = auto() blue = auto() yellow = auto() violet = auto() zinc = auto() def headers(self): return (fh.Link(rel="stylesheet", href="https://unpkg.com/franken-ui@1.1.0/dist/css/core.min.css"), fh.Script( type="module", src ="https://unpkg.com/franken-ui@1.1.0/dist/js/core.iife.js"), # fh.Script( type="module", src ="https://unpkg.com/franken-ui@1.1.0/dist/js/icon.iife.js"), fh.Script( type="module", src = "https://cdn.jsdelivr.net/gh/answerdotai/fh-frankenui@main/fh_frankenui/icon.iife.js"), fh.Script(src="https://cdn.tailwindcss.com"), _headers_theme(self.value),) # %% ../nbs/01_core.ipynb class TextT(VEnum): 'Text Styles from https://franken-ui.dev/docs/text' def _generate_next_value_(name, start, count, last_values): return str2ukcls('text', name) # Text Style lead,meta, italic = auto(), auto(), auto() # Text Size small, default, large = auto(), 'uk-text', auto() # Text Weight light, normal, bold, lighter, bolder = auto(),auto(),auto(),auto(),auto() # Text Transform capitalize,uppercase, lowercase = auto(),auto(),auto() # Text Decoration decoration_none = auto() # Text Color muted,primary,secondary, success,warning, danger = auto(),auto(),auto(),auto(),auto(),auto() # Text Alignment left, right,center,justify = auto(), auto(), auto(), auto() # Vertical Alignment top,middle,bottom, baseline = auto(),auto(),auto(),auto() # Text Wrapping truncate,break_,nowrap = auto(),auto(),auto() # %% ../nbs/01_core.ipynb class TextFont(Enum): "Combinations of TextT that are particularly useful" def __add__(self, other): return stringify((self, other)) def __radd__(self, other): return stringify((other, self)) def __str__(self): return self.value muted_sm = stringify((TextT.muted, TextT.small)) muted_lg = stringify((TextT.muted, TextT.large)) bold_sm = stringify((TextT.bold, TextT.small)) # %% ../nbs/01_core.ipynb class ButtonT(VEnum): "Options for styling Buttons" def _generate_next_value_(name, start, count, last_values): return str2ukcls('button', name) default = auto() primary = auto() secondary = auto() danger = auto() text = auto() link = auto() ghost = auto() # %% ../nbs/01_core.ipynb def Button(*c: Union[str, FT], cls: Union[str, Enum]=ButtonT.default, submit=True, **kwargs ) -> FT: "A Button with Styling (defaults to `type=submit` for form submission)" return fh.Button(*c, cls=('uk-button', stringify(cls)), type='submit' if submit else 'button', **kwargs) # %% ../nbs/01_core.ipynb def ButtonSubmit(*c: Union[str, FT], cls: Union[str, Enum]=ButtonT.default, **kwargs ) -> FT: "A Button with Styling to be used for submitting forms" return fh.Button(*c, cls=('uk-button', stringify(cls)), type='submit', **kwargs) # %% ../nbs/01_core.ipynb def H1(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: "A H1 with Styling" return fh.H1(*c, cls=('uk-h1',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def H2(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: "A H2 with Styling" return fh.H2(*c, cls=('uk-h2',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def H3(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: "A H3 with Styling" return fh.H3(*c, cls=('uk-h3',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def H4(*c:FT|str, cls:Enum|str|tuple=(), **kwargs)->FT: "A H4 with Styling" return fh.H4(*c, cls=('uk-h4',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def Main(*args, **kwargs): return fh.Main(*args, **kwargs) # %% ../nbs/01_core.ipynb def Titled(title:str="FastHTML app", *args, cls="container", **kwargs)->FT: "A H1 with styling, whose title is also used in the page's title tag" return fh.Title(title), fh.Main(H1(title), *args, cls=cls, **kwargs) # %% ../nbs/01_core.ipynb class DividerT(VEnum): " 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/01_core.ipynb def Divider(*args, cls=('my-4', DividerT.icon), **kwargs): "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) # %% ../nbs/01_core.ipynb def DividerSplit(*c, cls=(), line_cls=(), text_cls=()): cls, line_cls, text_cls = map(stringify,(cls, line_cls, text_cls)) return Div(cls='relative ' + cls)( Div(cls="absolute inset-0 flex items-center " + line_cls)(Span(cls="w-full border-t border-border")), Div(cls="relative flex justify-center " + text_cls)(Span(cls="bg-background px-2 ")(*c))) # %% ../nbs/01_core.ipynb def DividerLine(lwidth=2, y_space=4): return Hr(cls=f"my-{y_space} h-[{lwidth}px] w-full bg-secondary") # %% ../nbs/01_core.ipynb def Article(*c, cls=(), **kwargs): return fh.Article(*c, cls=('uk-article',stringify(cls)), **kwargs) def ArticleTitle(*c, cls=(), **kwargs): return H1(*c, cls=('uk-article-title',stringify(cls)), **kwargs) def ArticleMeta(*c, cls=(), **kwargs): return P(*c, cls=('uk-article-meta',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb class ContainerT(VEnum): 'Max width container sizes from https://franken-ui.dev/docs/container' def _generate_next_value_(name, start, count, last_values): return str2ukcls('container', name) xsmall = auto() small = auto() large = auto() xlarge = auto() expand = auto() # %% ../nbs/01_core.ipynb def Container(*c, cls=(), **kwargs): "A 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/01_core.ipynb class SectionT(VEnum): 'Section styles from https://franken-ui.dev/docs/section' def _generate_next_value_(name, start, count, last_values): return str2ukcls('section', name) default = auto() muted = auto() primary = auto() secondary = auto() xsmall = auto() small = auto() large = auto() xlarge = auto() remove_vertical = auto() # %% ../nbs/01_core.ipynb def Section(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-section',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def Fieldset(*c, cls=(), **kwargs): return fh.Fieldset(*c, cls=('uk-fieldset',stringify(cls)), **kwargs) def Legend(*c, cls=(), **kwargs): return fh.Legend(*c, cls=('uk-legend',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def Input(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-input',stringify(cls)), **kwargs) def Select(*option, cls=(), **kwargs): return fh.Select(*option, cls=('uk-select',stringify(cls)), **kwargs) def Radio(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-radio',stringify(cls)), type='radio', **kwargs) def CheckboxX(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-checkbox',stringify(cls)), type='checkbox', **kwargs) def Range(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-range',stringify(cls)), type='range', **kwargs) def Toggle_switch(*c, cls=(), **kwargs): return fh.Input(*c, cls=('uk-toggle-switch uk-toggle-switch-primary',stringify(cls)), type='checkbox', **kwargs) def TextArea(*c, cls=(), **kwargs): return fh.Textarea(*c, cls=('uk-textarea',stringify(cls)), **kwargs) def Switch(*c, cls='min-w-9', **kwargs): return fh.Input(*c, cls=('uk-toggle-switch uk-toggle-switch-primary',stringify(cls)), type='checkbox', **kwargs) # %% ../nbs/01_core.ipynb def FormLabel(*c, cls=(), **kwargs): return fh.Label(*c, cls=('uk-form-label',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb class LabelT(VEnum): def _generate_next_value_(name, start, count, last_values): return str2ukcls('label', name) primary = auto() secondary = auto() danger = auto() # %% ../nbs/01_core.ipynb def Label(*c, cls=(), **kwargs): "FrankenUI labels, which look like pills" return fh.Label(*c, cls=('uk-label',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def UkFormSection(title, description, *c, button_txt='Update', outer_margin=6, inner_margin=6): return Div(cls=f'space-y-{inner_margin} py-{outer_margin}')( Div(H3(title), P(description, cls=TextFont.muted_sm)), DividerSplit(), *c, Div(Button(button_txt, cls=ButtonT.primary)) if button_txt else None) # %% ../nbs/01_core.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" 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/01_core.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/01_core.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" 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) if container: return container(inp, label, cls=stringify(cls)) return inp, label # %% ../nbs/01_core.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" id = kwargs.pop('id', fh.unqid()) if isinstance(label, str) or label.tag != 'label': label = FormLabel(cls=stringify(lbl_cls), fr=id)(label) inp = CheckboxX(id=id, cls=stringify(input_cls), **kwargs) if container: return container(inp, label, cls=stringify(cls)) return inp, label # %% ../nbs/01_core.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/01_core.ipynb @delegates(GenericLabelInput, but=['input_fn','cls']) def LabelRange(*args, cls='space-y-2', **kwargs): return GenericLabelInput(*args, cls=stringify(cls), input_fn=Range, **kwargs) # %% ../nbs/01_core.ipynb @delegates(GenericLabelInput, but=['input_fn','cls']) def LabelTextArea(*args, cls='space-y-2', **kwargs): return GenericLabelInput(*args, cls=stringify(cls), input_fn=TextArea, **kwargs) # %% ../nbs/01_core.ipynb @delegates(GenericLabelInput, but=['input_fn','cls']) def LabelSwitch(*args, cls='space-x-2', **kwargs): return GenericLabelInput(*args, cls=stringify(cls), input_fn=Toggle_switch, **kwargs) # %% ../nbs/01_core.ipynb def LabelSelect(*option, label:str|FT, lbl_cls='', input_cls='', container=Div, cls='space-y-2', id='', **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" 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) if container: return container(label, inp, cls=stringify(cls)) return label, inp # %% ../nbs/01_core.ipynb def Options(*c, # Content for an `Option` selected_idx:int=None, # Index location of selected `Option` disabled_idxs:set=None # Idex locations of disabled `Options` ): "Helper function to wrap things into `Option`s for use in `UkSelect`" return [fh.Option(o,selected=i==selected_idx, disabled=disabled_idxs and i in disabled_idxs) for i,o in enumerate(c)] # %% ../nbs/01_core.ipynb def UkSelect(*option, # Options for the select dropdown (can use `Options` helper function to create) inp_cls=(), # Additional classes for the select input cls=('space-y-2',), # Classes for the outer div id="", # ID for the select input name="", # Name attribute for the select input 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" inp_cls, cls = map(stringify, (inp_cls, cls)) select = Uk_select(*option, cls=inp_cls, uk_cloak=True, id=id, name=name, placeholder=placeholder, searchable=searchable, **kwargs) return Div(cls=cls)(select) # %% ../nbs/01_core.ipynb def LabelUkSelect(*option, # Options for the select dropdown (can use `Options` helper function to create) label=(), # String or FT component for the label lbl_cls=(), # Additional classes for the label inp_cls=(), # Additional classes for the select input cls=('space-y-2',), # Classes for the outer div id="", # ID for the select input name="", # Name attribute for the select input 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" lbl_cls, inp_cls, cls = map(stringify, (lbl_cls, inp_cls, cls)) if label: lbl = FormLabel(cls=f'{lbl_cls}', fr=id)(label) select = Uk_select(*option, cls=inp_cls, uk_cloak=True, id=id, name=name, placeholder=placeholder, searchable=searchable, **kwargs) return Div(cls=cls)(lbl, select) if label else Div(cls=cls)(select) # %% ../nbs/01_core.ipynb class AT(VEnum): 'Link styles from https://franken-ui.dev/docs/link' def _generate_next_value_(name, start, count, last_values): return str2ukcls('link', name) muted = auto() text = auto() reset = auto() # %% ../nbs/01_core.ipynb # def Link(*c, cls=(), **kwargs): # return fh.A(*c, cls=('uk-link',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb class ListT(VEnum): 'List styles from https://franken-ui.dev/docs/list' def _generate_next_value_(name, start, count, last_values): return str2ukcls('list', name) disc = auto() circle = auto() square = auto() decimal = auto() hyphen = auto() muted = auto() primary = auto() secondary = auto() bullet = auto() divider = auto() striped = auto() # %% ../nbs/01_core.ipynb def List(*c, cls=(), **kwargs): return fh.Ul(*c, cls=('uk-list',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def ModalContainer(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-modal-container',stringify(cls)), uk_modal=True, **kwargs) def ModalDialog(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-modal-dialog', stringify(cls)), **kwargs) def ModalHeader(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-modal-header', stringify(cls)), **kwargs) def ModalBody(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-modal-body', stringify(cls)), **kwargs) def ModalFooter(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-modal-footer', stringify(cls)), **kwargs) def ModalTitle(*c, cls=(), **kwargs): return fh.H2(*c, cls=('uk-modal-title', stringify(cls)), **kwargs) def ModalCloseButton(*c, cls=(), **kwargs): return Button(*c, cls=('uk-modal-close', stringify(cls)), **kwargs) # %% ../nbs/01_core.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 id='', # id for the outermost container **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" cls, dialog_cls, header_cls, body_cls, footer_cls = map(stringify, (cls, dialog_cls, header_cls, body_cls, footer_cls)) res = [] if header: res.append(ModalHeader(cls=header_cls)(header)) res.append(ModalBody(cls=body_cls)(*c)) if footer: res.append(ModalFooter(cls=footer_cls)(footer)) return ModalContainer(ModalDialog(*res, cls=dialog_cls), cls=cls, id=id, **kwargs) # %% ../nbs/01_core.ipynb class PaddingT(VEnum): 'Padding Modifiers from https://franken-ui.dev/docs/padding' def _generate_next_value_(name, start, count, last_values): return str2ukcls('padding', name) xsmall = auto() small = auto() default = '' medium = auto() large = auto() xlarge = auto() remove = auto() remove_top = auto() remove_bottom = auto() remove_left = auto() remove_right = auto() remove_vertical = auto() remove_horizontal = auto() # %% ../nbs/01_core.ipynb class PositionT(VEnum): 'Position modifiers from https://franken-ui.dev/docs/position' def _generate_next_value_(name, start, count, last_values): return str2ukcls('position', name) top = auto() bottom = auto() left = auto() right = auto() top_left = auto() top_center = auto() top_right = auto() center = auto() center_left = auto() center_right = auto() bottom_left = auto() bottom_center = auto() bottom_right = auto() center_horizontal = auto() center_vertical = auto() # %% ../nbs/01_core.ipynb def Placeholder(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-placeholder',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def Progress(*c, cls=(), value="", max="", **kwargs): return fh.Progress(*c, value=value, max=max, cls=('uk-progress',stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def UkIcon(icon,height=None,width=None,stroke_width=None,cls=(), **kwargs): return Uk_icon(icon=icon, height=height, width=width, stroke_width=stroke_width, cls=cls, **kwargs) # %% ../nbs/01_core.ipynb def UkIconLink(icon, height=None, width=None, stroke_width=None, cls=(), button=False, **kwargs): fn = fh.Button if button else fh.A return fh.A(cls=(f"uk-icon-{'button' if button else 'link'}", stringify(cls)), **kwargs)( UkIcon(icon=icon, height=height, width=width, stroke_width=stroke_width)) # %% ../nbs/01_core.ipynb def DiceBearAvatar(seed_name, # Seed name (ie 'Isaac Flath') h=20, # Height w=20, # Width ): # Span with Avatar url = 'https://api.dicebear.com/8.x/lorelei/svg?seed=' return Span(cls=f"relative flex h-{h} w-{w} shrink-0 overflow-hidden rounded-full bg-accent")( fh.Img(cls=f"aspect-square h-{h} w-{w}", alt="Avatar", loading="lazy", src=f"{url}{seed_name}")) # %% ../nbs/01_core.ipynb class FlexT(VEnum): 'Flexbox modifiers from UIkit' def _generate_next_value_(name, start, count, last_values): return str2ukcls('flex', name) # Display block, inline = 'uk-flex', auto() # Horizontal Alignment left, center, right, between, around = auto(), auto(), auto(), auto(), auto() # Vertical Alignment stretch, top, middle, bottom = auto(), auto(), auto(), auto() # Direction row, row_reverse, column, column_reverse = auto(), auto(), auto(), auto() # Wrap nowrap, wrap, wrap_reverse = auto(), auto(), auto() # %% ../nbs/01_core.ipynb def Grid(*div, # Divs/Containers that should be divided into a grid cols_min=None, # defaults to min(len(div),1) cols_sm=None, # defaults to min(len(div),2) cols_md=None, # defaults to min(len(div),3) cols_lg=None, # defaults to min(len(div),4) cols=None, # Size at every breakpoint cls='gap-4', # Additional classes for Grid Div **kwargs # Additional args for Grid Div ): """Creates a grid with the given number of columns, often used for a grid of cards""" cols_min = cols_min if cols_min else cols if cols else 1 cols_sm = cols_sm if cols_sm else cols if cols else max(min(len(div), 2), cols_min+1) cols_md = cols_md if cols_md else cols if cols else max(min(len(div), 3), cols_min+2) cols_lg = cols_lg if cols_lg else cols if cols else max(min(len(div), 3), cols_min+2) cls = stringify(cls) return Div(cls=(f'grid grid-cols-{cols_min} sm:grid-cols-{cols_sm} md:grid-cols-{cols_md} lg:grid-cols-{cols_lg}',cls), **kwargs)(*div) # %% ../nbs/01_core.ipynb def DivFullySpaced(*c, # Components cls='uk-width-1-1',# Classes for outer div **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" cls = stringify(cls) return Div(cls=(FlexT.block,FlexT.between,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/01_core.ipynb def DivCentered(*c, # Components cls='space-y-4', # Classes for outer div **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) # %% ../nbs/01_core.ipynb 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 "Creates a flex div with it's components aligned to the left" cls=stringify(cls) return Div(cls=(FlexT.block,FlexT.left,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/01_core.ipynb 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 "Creates a flex div with it's components aligned to the right" cls=stringify(cls) return Div(cls=(FlexT.block,FlexT.right,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/01_core.ipynb def DivVStacked(*c, cls='space-y-4', **kwargs): cls=stringify(cls) return Div(cls=(FlexT.block,FlexT.column,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/01_core.ipynb def DivHStacked(*c, cls='space-x-4', **kwargs): cls=stringify(cls) return Div(cls=(FlexT.block,FlexT.row,FlexT.middle,cls), **kwargs)(*c) # %% ../nbs/01_core.ipynb class NavT(VEnum): def _generate_next_value_(name, start, count, last_values): return str2ukcls('nav', name) default = auto() primary = auto() secondary = auto() # %% ../nbs/01_core.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): return fh.Ul(*li, uk_nav=uk_nav, cls=(f"uk-nav{'' if parent else '-sub'}", stringify(cls)),**kwargs) # %% ../nbs/01_core.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/01_core.ipynb def NavBarContainer(*c, cls=(), container_cls=ContainerT.expand, uk_navbar=True, **kwargs): 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/01_core.ipynb def NavBarNav(*li, cls=(), **kwargs): return fh.Nav(*li, cls=('uk-navbar-nav', stringify(cls)), **kwargs) # %% ../nbs/01_core.ipynb def NavBarSubtitle(title, subtitle, cls=(), subtitle_cls=TextFont.muted_sm, **kwargs): return fh.Div(title,fh.Div(subtitle, cls=('uk-navbar-subtitle', stringify(subtitle_cls))), cls=stringify(cls), **kwargs) # %% ../nbs/01_core.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): return Div(cls="uk-navbar-dropdown")(NavContainer(*li, cls=('uk-navbar-dropdown-nav',stringify(cls)), uk_nav=uk_nav, parent=parent, **kwargs)) # %% ../nbs/01_core.ipynb def NavBarParentIcon(): return Span(uk_navbar_parent_icon=True) # %% ../nbs/01_core.ipynb def DropDownNavContainer(*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 uk_dropdown=True, **kwargs): 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/01_core.ipynb def TabContainer(*li,cls='', alt=False, **kwargs): cls = stringify(cls) return Ul(cls=(f"uk-tab{'-alt' if alt else ''}",stringify(cls)),**kwargs)(*li) # %% ../nbs/01_core.ipynb class CardT(VEnum): 'Card styles from UIkit' def _generate_next_value_(name, start, count, last_values): return str2ukcls('card', name) default = auto() primary = auto() secondary = auto() danger = auto() # %% ../nbs/01_core.ipynb def CardTitle(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-card-title',stringify(cls)), **kwargs) def CardHeader(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-card-header',stringify(cls)), **kwargs) def CardBody(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-card-body',stringify(cls)), **kwargs) def CardFooter(*c, cls=(), **kwargs): return fh.Div(*c, cls=('uk-card-footer',stringify(cls)), **kwargs) def CardContainer(*c, cls=CardT.default, **kwargs): return fh.Div(*c, cls=('uk-card',stringify(cls)), **kwargs) # %% ../nbs/01_core.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 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 ): header_cls, footer_cls, body_cls, cls = map(stringify, (header_cls, footer_cls, body_cls, cls)) res = [] if header: res.append(CardHeader(cls=header_cls)(header)) res.append(CardBody(cls=body_cls)(*c)) if footer: res.append(CardFooter(cls=footer_cls)(footer)) return CardContainer(cls=cls, **kwargs)(*res) # %% ../nbs/01_core.ipynb class TableT(VEnum): def _generate_next_value_(name, start, count, last_values): return str2ukcls('table', name) divider = auto() striped = auto() hover = auto() small = auto() large = auto() justify = auto() middle = auto() responsive = auto() # %% ../nbs/01_core.ipynb def Table(*args, cls=(TableT.middle, TableT.divider, TableT.hover, TableT.small), **kwargs): return fh.Table(cls=('uk-table', stringify(cls)), *args, **kwargs) # %% ../nbs/01_core.ipynb def _TableCell(Component, *args, cls=(), shrink=False, expand=False, small=False, **kwargs): 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) @delegates(_TableCell, but=['Component']) def Td(*args,**kwargs): return _TableCell(fh.Td, *args, **kwargs) @delegates(_TableCell, but=['Component']) def Th(*args,**kwargs): return _TableCell(fh.Th, *args, **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/01_core.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): return Table( Thead(Tr(*map(header_cell_render, header_data))), Tbody(*[Tr(*map(body_cell_render, r)) for r in body_data], sortable=sortable), Tfoot(Tr(*map(footer_cell_render, footer_data))) if footer_data else '', cls=stringify(cls), **kwargs) # %% ../nbs/01_core.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): return Table( Thead(Tr(*[header_cell_render(h) for h in header_data])), Tbody(*[Tr(*[body_cell_render(k, r.get(k, '')) for k in header_data]) for r in body_data], sortable=sortable), Tfoot(Tr(*[footer_cell_render(k, footer_data.get(k.lower(), '')) for k in header_data])) if footer_data else '', cls=stringify(cls), **kwargs ) # %% ../nbs/01_core.ipynb franken_class_map = { 'h1': 'uk-h1 my-4 mb-4', 'h2': 'uk-h2 my-8', 'h3': 'uk-h3 my-8', 'h4': 'uk-h4 my-8', 'ul': "uk-list uk-list-disc mb-4", 'a': "uk-link", 'p': 'my-1', 'blockquote': "uk-blockquote mb-8", 'hr':'uk-divider-icon my-4', 'table':'uk-table-middle uk-table-divider uk-table-hover uk-table-small', 'ol': 'uk-list-decimal', 'li': 'uk-list-disc', } # %% ../nbs/01_core.ipynb def apply_classes(html_str:str, class_map=None, class_map_mods=None): from lxml import html, etree class_map = ifnone(class_map, franken_class_map) 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}'): existing_class = element.get('class', '') new_class = f"{existing_class} {classes}".strip() element.set('class', new_class) return etree.tostring(html_str, encoding='unicode', method='html') # %% ../nbs/01_core.ipynb def render_md(md_content, class_map=None, class_map_mods=None): try: import markdown2 except ImportError: raise ImportError("Install 'markdown2' to use the 'render_md' function") html_content = markdown2.markdown(md_content, extras={'fenced-code-blocks':None})#, 'html-classes': franken_class_map}) return NotStr(apply_classes(html_content, class_map, class_map_mods))404: Not Found404: Not Found404: Not Found404: Not Found404: Not Found404: Not Found404: Not Found404: Not Found404: Not Found