Exported source
theme_controller = SingleValueFactory(
"theme-controller",
"For a checkbox or radio input that must change the page theme"
) # Theme controller componenttest_theme_controller_basic_examples ()
Test basic theme controller utility.
def test_theme_controller_basic_examples():
"""Test basic theme controller utility."""
assert str(theme_controller) == "theme-controller"
# With modifiers
assert str(theme_controller.hover) == "hover:theme-controller"
assert str(theme_controller.dark) == "dark:theme-controller"
# Run the tests
test_theme_controller_basic_examples()test_theme_controller_basic_fasthtml_examples ()
Test basic theme controller examples with toggle and checkbox inputs.
def test_theme_controller_basic_fasthtml_examples():
"""Test basic theme controller examples with toggle and checkbox inputs."""
from fasthtml.common import Input, Label, Span, Div
from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import gap, items, flex_display
from cjm_fasthtml_tailwind.utilities.layout import display_tw
from cjm_fasthtml_tailwind.utilities.interactivity import cursor
from cjm_fasthtml_daisyui.components.data_input.toggle import toggle
from cjm_fasthtml_daisyui.components.data_input.checkbox import checkbox
from cjm_fasthtml_daisyui.utilities.semantic_colors import text_dui
# Basic toggle theme controller
toggle_theme = Input(
type="checkbox",
value="synthwave",
cls=combine_classes(toggle, theme_controller)
)
assert toggle_theme.attrs['type'] == "checkbox"
assert toggle_theme.attrs['value'] == "synthwave"
assert "toggle" in toggle_theme.attrs['class']
assert "theme-controller" in toggle_theme.attrs['class']
# Checkbox theme controller
checkbox_theme = Input(
type="checkbox",
value="synthwave",
cls=combine_classes(checkbox, theme_controller)
)
assert "checkbox" in checkbox_theme.attrs['class']
assert "theme-controller" in checkbox_theme.attrs['class']
# Toggle with text labels
toggle_with_text = Label(
Span("Current", cls="label-text"),
Input(
type="checkbox",
value="synthwave",
cls=combine_classes(toggle, theme_controller)
),
Span("Synthwave", cls="label-text"),
cls=combine_classes(flex_display, cursor.pointer, gap._2)
)
assert toggle_with_text.tag == "label"
assert "flex" in toggle_with_text.attrs['class']
assert "cursor-pointer" in toggle_with_text.attrs['class']
assert "gap-2" in toggle_with_text.attrs['class']
assert toggle_with_text.children[1].attrs['type'] == "checkbox"
# Toggle with icons inside (special toggle structure)
toggle_with_icons_inside = Label(
Input(
type="checkbox",
value="synthwave",
cls=str(theme_controller)
),
# SVG icons would be children of the label
cls=combine_classes(toggle, text_dui.base_content)
)
assert "toggle" in toggle_with_icons_inside.attrs['class']
assert "text-base-content" in toggle_with_icons_inside.attrs['class']
assert toggle_with_icons_inside.children[0].attrs['value'] == "synthwave"
# Return all elements in a Div
return Div(
toggle_theme,
checkbox_theme,
toggle_with_text,
toggle_with_icons_inside
)
# Run the tests
test_theme_controller_basic_fasthtml_examples()<div>
<input type="checkbox" value="synthwave" class="toggle theme-controller">
<input type="checkbox" value="synthwave" class="checkbox theme-controller">
<label class="flex cursor-pointer gap-2"><span class="label-text">Current</span> <input type="checkbox" value="synthwave" class="toggle theme-controller">
<span class="label-text">Synthwave</span></label><label class="toggle text-base-content"> <input type="checkbox" value="synthwave" class="theme-controller">
</label></div>test_theme_controller_swap_fasthtml_examples ()
Test theme controller examples using swap component.
def test_theme_controller_swap_fasthtml_examples():
"""Test theme controller examples using swap component."""
from fasthtml.common import Label, Input, Div
from fasthtml.svg import Svg, Path
from cjm_fasthtml_tailwind.utilities.sizing import h, w
from cjm_fasthtml_tailwind.utilities.svg import fill
from cjm_fasthtml_daisyui.components.actions.swap import swap, swap_on, swap_off, swap_styles
# Create sun icon SVG
sun_icon = Svg(
Path(
d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"
),
cls=combine_classes(swap_off, h._10, w._10, fill.current),
xmlns="http://www.w3.org/2000/svg",
viewBox="0 0 24 24"
)
# Create moon icon SVG
moon_icon = Svg(
Path(
d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"
),
cls=combine_classes(swap_on, h._10, w._10, fill.current),
xmlns="http://www.w3.org/2000/svg",
viewBox="0 0 24 24"
)
# Theme controller using swap with rotate effect
swap_theme = Label(
Input(
type="checkbox",
cls=str(theme_controller),
value="synthwave"
),
sun_icon,
moon_icon,
cls=combine_classes(swap, swap_styles.rotate)
)
assert swap_theme.tag == "label"
assert "swap" in swap_theme.attrs['class']
assert "swap-rotate" in swap_theme.attrs['class']
assert swap_theme.children[0].attrs['class'] == "theme-controller"
assert swap_theme.children[0].attrs['value'] == "synthwave"
assert swap_theme.children[1].tag == "svg" # sun icon
assert swap_theme.children[2].tag == "svg" # moon icon
assert "swap-off" in swap_theme.children[1].attrs['class']
assert "swap-on" in swap_theme.children[2].attrs['class']
# Return all elements in a Div
return Div(
swap_theme
)
# Run the tests
test_theme_controller_swap_fasthtml_examples()<div>
<label class="swap swap-rotate"> <input type="checkbox" value="synthwave" class="theme-controller">
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="swap-off h-10 w-10 fill-current"><path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" class="swap-on h-10 w-10 fill-current"><path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z"></path></svg></label></div>test_theme_controller_radio_fasthtml_examples ()
Test theme controller examples using radio inputs.
def test_theme_controller_radio_fasthtml_examples():
"""Test theme controller examples using radio inputs."""
from fasthtml.common import Input, Label, Fieldset, Div
from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import gap, items, flex_display
from cjm_fasthtml_tailwind.utilities.layout import display_tw
from cjm_fasthtml_tailwind.utilities.interactivity import cursor
from cjm_fasthtml_daisyui.components.actions.button import btn
from cjm_fasthtml_daisyui.components.data_input.radio import radio, radio_sizes
from cjm_fasthtml_daisyui.components.data_input.fieldset import fieldset
from cjm_fasthtml_daisyui.components.layout.join import join, join_item, join_directions
# Radio button theme selector
radio_themes = Fieldset(
Label(
Input(
type="radio",
name="theme-radios",
cls=combine_classes(radio, radio_sizes.sm, theme_controller),
value="default"
),
"Default",
cls=combine_classes(flex_display, gap._2, cursor.pointer, items.center)
),
Label(
Input(
type="radio",
name="theme-radios",
cls=combine_classes(radio, radio_sizes.sm, theme_controller),
value="retro"
),
"Retro",
cls=combine_classes(flex_display, gap._2, cursor.pointer, items.center)
),
Label(
Input(
type="radio",
name="theme-radios",
cls=combine_classes(radio, radio_sizes.sm, theme_controller),
value="cyberpunk"
),
"Cyberpunk",
cls=combine_classes(flex_display, gap._2, cursor.pointer, items.center)
),
Label(
Input(
type="radio",
name="theme-radios",
cls=combine_classes(radio, radio_sizes.sm, theme_controller),
value="valentine"
),
"Valentine",
cls=combine_classes(flex_display, gap._2, cursor.pointer, items.center)
),
Label(
Input(
type="radio",
name="theme-radios",
cls=combine_classes(radio, radio_sizes.sm, theme_controller),
value="aqua"
),
"Aqua",
cls=combine_classes(flex_display, gap._2, cursor.pointer, items.center)
),
cls=str(fieldset)
)
# Verify radio button structure
assert radio_themes.tag == "fieldset"
for label in radio_themes.children:
assert label.tag == "label"
radio_input = label.children[0]
assert radio_input.tag == "input"
assert radio_input.attrs['type'] == "radio"
assert radio_input.attrs['name'] == "theme-radios"
assert "radio" in radio_input.attrs['class']
assert "radio-sm" in radio_input.attrs['class']
assert "theme-controller" in radio_input.attrs['class']
# Theme controller as button group
button_themes = Div(
Input(
type="radio",
name="theme-buttons",
cls=combine_classes(btn, theme_controller, join_item),
aria_label="Default",
value="default"
),
Input(
type="radio",
name="theme-buttons",
cls=combine_classes(btn, theme_controller, join_item),
aria_label="Retro",
value="retro"
),
Input(
type="radio",
name="theme-buttons",
cls=combine_classes(btn, theme_controller, join_item),
aria_label="Cyberpunk",
value="cyberpunk"
),
Input(
type="radio",
name="theme-buttons",
cls=combine_classes(btn, theme_controller, join_item),
aria_label="Valentine",
value="valentine"
),
Input(
type="radio",
name="theme-buttons",
cls=combine_classes(btn, theme_controller, join_item),
aria_label="Aqua",
value="aqua"
),
cls=combine_classes(join, join_directions.vertical)
)
# Verify button group structure
assert "join" in button_themes.attrs['class']
assert "join-vertical" in button_themes.attrs['class']
for input_btn in button_themes.children:
assert input_btn.tag == "input"
assert input_btn.attrs['type'] == "radio"
assert input_btn.attrs['name'] == "theme-buttons"
assert "btn" in input_btn.attrs['class']
assert "theme-controller" in input_btn.attrs['class']
assert "join-item" in input_btn.attrs['class']
assert "aria-label" in input_btn.attrs
# Return all elements in a Div
return Div(
radio_themes,
button_themes
)
# Run the tests
test_theme_controller_radio_fasthtml_examples()<div>
<fieldset class="fieldset"><label class="flex gap-2 cursor-pointer items-center"> <input type="radio" name="theme-radios" value="default" class="radio radio-sm theme-controller">
Default</label><label class="flex gap-2 cursor-pointer items-center"> <input type="radio" name="theme-radios" value="retro" class="radio radio-sm theme-controller">
Retro</label><label class="flex gap-2 cursor-pointer items-center"> <input type="radio" name="theme-radios" value="cyberpunk" class="radio radio-sm theme-controller">
Cyberpunk</label><label class="flex gap-2 cursor-pointer items-center"> <input type="radio" name="theme-radios" value="valentine" class="radio radio-sm theme-controller">
Valentine</label><label class="flex gap-2 cursor-pointer items-center"> <input type="radio" name="theme-radios" value="aqua" class="radio radio-sm theme-controller">
Aqua</label></fieldset> <div class="join join-vertical">
<input type="radio" name="theme-buttons" aria-label="Default" value="default" class="btn theme-controller join-item">
<input type="radio" name="theme-buttons" aria-label="Retro" value="retro" class="btn theme-controller join-item">
<input type="radio" name="theme-buttons" aria-label="Cyberpunk" value="cyberpunk" class="btn theme-controller join-item">
<input type="radio" name="theme-buttons" aria-label="Valentine" value="valentine" class="btn theme-controller join-item">
<input type="radio" name="theme-buttons" aria-label="Aqua" value="aqua" class="btn theme-controller join-item">
</div>
</div>test_theme_controller_advanced_fasthtml_examples ()
Test advanced theme controller examples including dropdown and custom styles.
def test_theme_controller_advanced_fasthtml_examples():
"""Test advanced theme controller examples including dropdown and custom styles."""
from fasthtml.common import Input, Label, Div, Ul, Li
from fasthtml.svg import Svg, Path, Circle, G
from cjm_fasthtml_tailwind.utilities.sizing import h, w
from cjm_fasthtml_tailwind.utilities.svg import fill, stroke, stroke_width
from cjm_fasthtml_tailwind.utilities.layout import z, display_tw
from cjm_fasthtml_tailwind.utilities.spacing import m, p
from cjm_fasthtml_tailwind.utilities.effects import shadow, opacity
from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import justify, col_span, col_start, row_start
from cjm_fasthtml_tailwind.utilities.borders import border_color
from cjm_fasthtml_tailwind.utilities.backgrounds import bg
from cjm_fasthtml_tailwind.utilities.interactivity import cursor
from cjm_fasthtml_tailwind.utilities.flexbox_and_grid import gap, flex_display
from cjm_fasthtml_daisyui.utilities.semantic_colors import bg_dui
from cjm_fasthtml_daisyui.utilities.border_radius import border_radius
from cjm_fasthtml_daisyui.components.actions.button import btn, btn_sizes, btn_modifiers, btn_styles
from cjm_fasthtml_daisyui.components.actions.dropdown import dropdown, dropdown_content
from cjm_fasthtml_daisyui.components.data_input.toggle import toggle
# Toggle with external icons
sun_svg = Svg(
Circle(cx="12", cy="12", r="5"),
Path(d="M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4"),
xmlns="http://www.w3.org/2000/svg",
width="20",
height="20",
viewBox="0 0 24 24",
fill="none",
stroke="currentColor",
stroke_width="2",
stroke_linecap="round",
stroke_linejoin="round"
)
moon_svg = Svg(
Path(d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"),
xmlns="http://www.w3.org/2000/svg",
width="20",
height="20",
viewBox="0 0 24 24",
fill="none",
stroke="currentColor",
stroke_width="2",
stroke_linecap="round",
stroke_linejoin="round"
)
toggle_with_icons = Label(
sun_svg,
Input(
type="checkbox",
value="synthwave",
cls=combine_classes(toggle, theme_controller)
),
moon_svg,
cls=combine_classes(flex_display, cursor.pointer, gap._2)
)
assert len(toggle_with_icons.children) == 3
assert toggle_with_icons.children[0].tag == "svg"
assert toggle_with_icons.children[1].tag == "input"
assert toggle_with_icons.children[2].tag == "svg"
# Custom styled toggle (with Tailwind classes)
custom_toggle = Input(
type="checkbox",
value="synthwave",
cls=combine_classes(
toggle,
theme_controller,
col_span(2),
col_start(1),
row_start(1),
border_color.sky._400,
bg.amber._300,
"[--tglbg:var(--color-sky-500)]",
border_color.blue._800.checked,
bg.blue._800.checked,
"checked:[--tglbg:var(--color-blue-900)]"
)
)
assert "toggle" in custom_toggle.attrs['class']
assert "theme-controller" in custom_toggle.attrs['class']
assert "border-sky-400" in custom_toggle.attrs['class']
assert "bg-amber-300" in custom_toggle.attrs['class']
# Theme controller dropdown
dropdown_icon = Svg(
Path(d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z"),
width="12px",
height="12px",
cls=combine_classes(display_tw.inline_block, h._2, w._2, fill.current, opacity._60),
xmlns="http://www.w3.org/2000/svg",
viewBox="0 0 2048 2048"
)
theme_dropdown = Div(
Div(
"Theme",
dropdown_icon,
tabindex="0",
role="button",
cls=combine_classes(btn, m._1)
),
Ul(
Li(
Input(
type="radio",
name="theme-dropdown",
cls=combine_classes(
theme_controller,
w.full,
btn,
btn_sizes.sm,
btn_modifiers.block,
btn_styles.ghost,
justify.start
),
aria_label="Default",
value="default"
)
),
Li(
Input(
type="radio",
name="theme-dropdown",
cls=combine_classes(
theme_controller,
w.full,
btn,
btn_sizes.sm,
btn_modifiers.block,
btn_styles.ghost,
justify.start
),
aria_label="Retro",
value="retro"
)
),
tabindex="0",
cls=combine_classes(
dropdown_content,
bg_dui.base_300,
border_radius.box,
z._1,
w._52,
p._2,
shadow._2xl
)
),
cls=combine_classes(dropdown)
)
# Verify dropdown structure
assert "dropdown" in theme_dropdown.attrs['class']
dropdown_trigger = theme_dropdown.children[0]
assert "btn" in dropdown_trigger.attrs['class']
dropdown_menu = theme_dropdown.children[1]
assert "dropdown-content" in dropdown_menu.attrs['class']
assert dropdown_menu.tag == "ul"
# Verify dropdown items
for li in dropdown_menu.children:
if hasattr(li, 'children') and len(li.children) > 0:
radio = li.children[0]
assert radio.tag == "input"
assert radio.attrs['type'] == "radio"
assert "theme-controller" in radio.attrs['class']
assert "w-full" in radio.attrs['class']
assert "btn-sm" in radio.attrs['class']
assert "btn-block" in radio.attrs['class']
# Return all elements in a Div
return Div(
toggle_with_icons,
custom_toggle,
theme_dropdown
)
# Run the tests
test_theme_controller_advanced_fasthtml_examples()<div>
<label class="flex cursor-pointer gap-2"><svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" height="20" width="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle r="5" cx="12" cy="12"></circle><path d="M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4"></path></svg> <input type="checkbox" value="synthwave" class="toggle theme-controller">
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24" height="20" width="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></label> <input type="checkbox" value="synthwave" class="toggle theme-controller col-span-2 col-start-1 row-start-1 border-sky-400 bg-amber-300 [--tglbg:var(--color-sky-500)] checked:border-blue-800 checked:bg-blue-800 checked:[--tglbg:var(--color-blue-900)]">
<div class="dropdown">
<div tabindex="0" role="button" class="btn m-1">
Theme<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 2048 2048" height="12px" width="12px" class="inline-block h-2 w-2 fill-current opacity-60"><path d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z"></path></svg> </div>
<ul tabindex="0" class="dropdown-content bg-base-300 rounded-box z-1 w-52 p-2 shadow-2xl">
<li>
<input type="radio" name="theme-dropdown" aria-label="Default" value="default" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start">
</li>
<li>
<input type="radio" name="theme-dropdown" aria-label="Retro" value="retro" class="theme-controller w-full btn btn-sm btn-block btn-ghost justify-start">
</li>
</ul>
</div>
</div>