Files
sigpro-ui/docs/demo.md
2026-05-09 19:23:54 +02:00

23 KiB
Raw Permalink Blame History

SigPro Demo Atomic Components


Accordion

const accName = "demo-acc";

mount(
  div({ class: "space-y-2" }, [
    Accordion({ class: "collapse-arrow bg-base-100 border border-base-300" }, [
      AccordionRadio({ name: accName, checked: true }),
      AccordionTitle({}, "What is SigPro?"),
      AccordionContent({}, "A lightweight UI library built on DaisyUI and a finegrained reactivity system."),
    ]),
    Accordion({ class: "collapse-arrow bg-base-100 border border-base-300" }, [
      AccordionRadio({ name: accName }),
      AccordionTitle({}, "Why use it?"),
      AccordionContent({}, "No build step, minimal boilerplate, and all components are just functions."),
    ]),
    Accordion({ class: "collapse-arrow bg-base-100 border border-base-300" }, [
      AccordionRadio({ name: accName }),
      AccordionTitle({}, "Browser support?"),
      AccordionContent({}, "All modern browsers that support ES2020+"),
    ]),
  ]),
  "#demo-accordion"
);

Alert

mount(
  Alert({ class: "alert-info" }, [
    Icon({}, "icon-[lucide--info]"),
    span({}, "You have 2 new messages"),
  ]),
  "#demo-alert"
);

Autocomplete

const fruitQuery = $("");
const fruitResults = $([]);
const fruits = ["Apple", "Banana", "Orange", "Mango", "Carrot", "Broccoli", "Spinach", "Potato"];
Filter(fruitQuery, fruits, fruitResults);

const colorQuery = $("");
const colorResults = $([]);
const colors = ["Red", "Blue", "Green", "Yellow", "Black", "White", "Purple", "Cyan"];
Filter(colorQuery, colors, colorResults);

const autocompleteCombo = (query, results, placeholder) =>
  Combo({ class: "p-2 bg-base-100 rounded-box shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50" },
    LabelInput({}, [
      Icon({}, "icon-[lucide--search]"),
      Input({ placeholder, value: query, oninput: (e) => query(e.target.value) })
    ]),
    { content: Menu({ class: "flex-col flex-nowrap w-full p-0" },
      each(results, (item) =>
        MenuItem({ label: item, onclick: () => { query(item); hide() } })
      )
    )}
  );

mount(
  div({ class: "flex flex-col gap-10" }, [
    div({}, [
      h3({ class: "font-bold mb-2" }, "Fruits:"),
      autocompleteCombo(fruitQuery, fruitResults, "Search fruit..."),
      span({ class: "text-xs" }, () => `Signal: ${fruitQuery()}`),
    ]),
    div({}, [
      h3({ class: "font-bold mb-2" }, "Colors:"),
      autocompleteCombo(colorQuery, colorResults, "Search color..."),
      span({ class: "text-xs" }, () => `Signal: ${colorQuery()}`),
    ]),
  ]),
  "#demo-autocomplete"
);

Avatar & AvatarGroup

mount(
  div({ class: "flex flex-col gap-4" }, [
    Avatar({ class: "w-24 rounded-full" },
      img({ src: "https://img.daisyui.com/images/profile/demo/kenobee@192.webp" })
    ),
    AvatarGroup({}, [
      Avatar({ class: "w-12" }, img({ src: "https://img.daisyui.com/images/profile/demo/kenobee@192.webp" })),
      Avatar({ class: "w-12" }, img({ src: "https://img.daisyui.com/images/profile/demo/anakeen@192.webp" })),
      Avatar({ class: "w-12" }, img({ src: "https://img.daisyui.com/images/profile/demo/kenobee@192.webp" })),
    ]),
  ]),
  "#demo-avatar"
);

Badge

mount(
  div({ class: "flex gap-2" }, [
    Badge({}, "New"),
    Badge({ class: "badge-outline" }, "Sale"),
    Badge({ class: "badge-error" }, "Deleted"),
  ]),
  "#demo-badge"
);

Breadcrumbs

mount(
  Breadcrumbs({}, [
    ul({}, [
      li({}, a({ href: "#" }, "Home")),
      li({}, a({ href: "#" }, "Docs")),
      li({}, span({ class: "font-bold" }, "Components")),
    ]),
  ]),
  "#demo-breadcrumbs"
);

Button

const count = $(0);

mount(
  div({ class: "flex gap-2 items-center" }, [
    Button({ class: "btn-primary", onclick: () => count(count() + 1) },
      () => `Clicked ${count()} times`
    ),
    Button({ class: "btn-outline", disabled: true }, "Disabled"),
  ]),
  "#demo-button"
);

Calendar

const calendarRange = $({ start: "2026-04-01", end: "2026-04-15", startHour: 9, endHour: 18 });

mount(
  Calendar({
    value: calendarRange,
    range: true,
    hour: true,
    onChange: (val) => console.log("Range:", val),
  }),
  "#demo-calendar"
);

Card, CardTitle, CardBody & CardActions

mount(
  Card({ class: "w-80 bg-base-100 shadow-xl" }, [
    CardBody({}, [
      CardTitle({}, "Card Title"),
      p({}, "This is a card with title, body and actions."),
      CardActions({ class: "justify-end" }, [
        Button({ class: "btn-primary btn-sm" }, "Buy"),
      ]),
    ]),
  ]),
  "#demo-card"
);

mount(
  Carousel({ class: "carousel-vertical rounded-box h-96" }, [
    CarouselItem({ class: "h-full" },
      img({ src: "https://img.daisyui.com/images/stock/photo-1559703248-dcaaec9fab78.webp" })
    ),
    CarouselItem({ class: "h-full" },
      img({ src: "https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp" })
    ),
    CarouselItem({ class: "h-full" },
      img({ src: "https://img.daisyui.com/images/stock/photo-1572635148818-ef6fd45eb394.webp" })
    ),
  ]),
  "#demo-carousel"
);

Chat, ChatImage, ChatHeader, ChatBubble & ChatFooter

mount(
  div({ class: "flex flex-col gap-4" }, [
    Chat({ class: "chat-start" }, [
      ChatImage({}, img({ src: "https://img.daisyui.com/images/profile/demo/kenobee@192.webp", alt: "Obi-Wan" })),
      ChatHeader({}, ["Obi-Wan Kenobi", time({ class: "text-xs opacity-50" }, "12:45")]),
      ChatBubble({}, "You were the Chosen One!"),
      ChatFooter({ class: "opacity-50" }, "Delivered"),
    ]),
    Chat({ class: "chat-end" }, [
      ChatImage({}, img({ src: "https://img.daisyui.com/images/profile/demo/anakeen@192.webp", alt: "Anakin" })),
      ChatHeader({}, ["Anakin", time({ class: "text-xs opacity-50" }, "12:46")]),
      ChatBubble({}, "I hate you!"),
      ChatFooter({ class: "opacity-50" }, "Seen at 12:46"),
    ]),
  ]),
  "#demo-chat"
);

Checkbox

const checked = $(false);

mount(
  div({ class: "flex items-center gap-2" }, [
    Checkbox({ checked, onchange: (e) => checked(e.target.checked) }),
    span({}, () => `Accept terms (${checked() ? "Yes" : "No"})`),
  ]),
  "#demo-checkbox"
);

Colorpicker & ColorPalette

const color = $("#000000");

mount(
  div({ class: "flex flex-col gap-4" }, [
    Combo({ class: "p-0" },
      LabelInput({ class: "flex items-center gap-2 cursor-pointer" }, [
        div({ class: "size-5 rounded-sm", style: () => `background-color: ${color() || "#000"}` }),
        span({ class: () => `grow text-left truncate ${!color() ? "opacity-50" : ""}` }, () => color() || "Pick a color"),
        () => color() ? span({ class: "btn-ghost btn-xs btn-circle -mr-2", onmousedown: (e) => { e.stopPropagation(); color(null); } }, Icon({}, "icon-[lucide--x]")) : null
      ]),
      { content: ColorPalette({ value: color, onchange: (c) => { color(c); hide(); } }) }
    ),
    p({}, () => `Selected: ${color()}`),
  ]),
  "#demo-colorpicker"
);

Datepicker

const dateRange = $(null);
const displayRange = $(() => {
  const v = dateRange();
  if (!v) return "";
  if (typeof v === "string") return v;
  if (v.start && v.end) return `${v.start} - ${v.end}`;
  if (v.start) return `${v.start}...`;
  return "";
});

mount(
  Combo({ class: "p-0" },
    LabelInput({ class: "flex items-center gap-2 cursor-pointer" }, [
      Icon({}, "icon-[lucide--calendar]"),
      span({ class: () => `grow text-left truncate ${!displayRange() ? "opacity-50" : ""}` }, () => displayRange() || "Select date range"),
      () => displayRange() ? span({ class: "btn-ghost btn-xs btn-circle -mr-2", onmousedown: (e) => { e.stopPropagation(); dateRange(null); } }, Icon({}, "icon-[lucide--x]")) : null
    ]),
    { content: Calendar({ value: dateRange, range: true, hour: true, onChange: (v) => { dateRange(v); if (v?.end) hide(); } }) }
  ),
  "#demo-datepicker"
);

Divider

mount(
  div({ class: "flex flex-col gap-2" }, [
    p({ class: "card bg-base-300 rounded-box grid h-20 place-items-center" }, "Above"),
    Divider({}, "OR"),
    p({ class: "card bg-base-300 rounded-box grid h-20 place-items-center" }, "Below"),
  ]),
  "#demo-divider"
);

Drawer

const leftOpen = $(false);
const rightOpen = $(false);

mount(
  Drawer({ class: () => leftOpen() ? "drawer-open" : "" }, [
    DrawerToggle({ id: "left-drawer", checked: leftOpen }),
    DrawerContent({}, [
      Drawer({ class: () => `drawer-end ${rightOpen() ? "drawer-open" : ""}` }, [
        DrawerToggle({ id: "right-drawer", checked: rightOpen }),
        DrawerContent({}, [
          div({ class: "p-4" }, [
            h3({ class: "text-lg font-bold" }, "Central Panel"),
            label({ for: "left-drawer", class: "btn" }, "Open Left"),
            label({ for: "right-drawer", class: "btn ml-2" }, "Open Right"),
            p({}, "Main Content..."),
          ]),
        ]),
        DrawerSide({}, [
          DrawerOverlay({ for: "right-drawer" }),
          div({ class: "min-h-full bg-base-200 w-60 p-4" }, h4({ class: "font-semibold" }, "Right Menu")),
        ]),
      ]),
    ]),
    DrawerSide({}, [
      DrawerOverlay({ for: "left-drawer" }),
      div({ class: "min-h-full bg-base-200 w-60 p-4" }, h4({ class: "font-semibold" }, "Left Menu")),
    ]),
  ]),
  "#demo-drawer"
);

Dropdown, DropdownButton & DropdownContent

mount(
  div({ class: "flex gap-4" }, [
    Dropdown({}, [
      DropdownButton({}, "Options"),
      DropdownContent({ class: "menu bg-base-100 rounded-box w-52 p-2 shadow" }, [
        Menu({}, [
          MenuItem({ label: "Edit", onclick: () => hide() }),
          MenuItem({ label: "Delete", onclick: () => hide() }),
          MenuItem({ label: "Archive", onclick: () => hide() }),
        ]),
      ]),
    ]),
    Dropdown({ class: "dropdown-bottom dropdown-end" }, [
      DropdownButton({}, "More"),
      DropdownContent({ class: "menu bg-base-100 rounded-box w-40 p-2 shadow" },
        Menu({}, [
          MenuItem({ label: "Profile", onclick: (e) => { e.preventDefault(); hide(); } }),
          MenuItem({ label: "Logout", onclick: (e) => { e.preventDefault(); hide(); } }),
        ])
      ),
    ]),
  ]),
  "#demo-dropdown"
);

Fab

mount(
  Fab({ icon: "R", class: "btn-lg btn-circle btn-secondary" }, [
    Button({}, "C"),
    Button({}, "B"),
    Button({}, "A"),
  ]),
  "#demo-fab"
);

Fieldset

mount(
  div({ class: "flex gap-4" }, [
    Fieldset({ class: "bg-base-200 border-base-300 rounded-box w-xs border gap-3 p-4", label: "Personal Info" }, [
      LabelFloating({}, [
        span({}, "Name"),
        Input({ placeholder: "", value: $("") }),
      ]),
      LabelFloating({}, [
        span({}, "Country"),
        Select({}, [
          SelectOption({}, "Spain"),
          SelectOption({}, "France"),
          SelectOption({}, "Italy"),
        ]),
      ]),
    ]),
    Fieldset({ class: "bg-base-200 p-4 rounded-box", label: "Any content" }, [
      div({}, "Any content"),
    ]),
  ]),
  "#demo-fieldset"
);

Fileinput

const files = $([]);
const drag = $(false);
const error = $(null);
const maxMB = 5;

const processFiles = (fileList) => {
  const arr = [...fileList];
  if (arr.some(f => f.size > maxMB * (1 << 20))) return error(`Max ${maxMB}MB`);
  error(null);
  files([...files(), ...arr]);
};

mount(
  div({ class: "w-full" }, [
    FileDrag({
      drag: drag(),
      ondrag: (v) => drag(v),
      ondrop: processFiles
    }, [
      div({ class: "flex items-center gap-3 w-full text-sm opacity-70" }, [
        Icon({}, "icon-[lucide--upload]"),
        span({ class: "grow truncate" }, "Upload files"),
        span({ class: "text-[10px] opacity-40" }, maxMB + "MB"),
      ]),
      FileInput({ onchange: processFiles }),
    ]),
    () => error() && FileError({ message: error() }),
    () => files().length > 0 && FilePreview({
      files: files(),
      onremove: (i) => files(files().filter((_, j) => j !== i))
    }),
  ]),
  "#demo-fileinput"
);

Icon

mount(
  div({ class: "flex gap-4 text-xl" }, [
    Icon({}, "icon-[lucide--home]"),
    Icon({}, "icon-[lucide--settings]"),
    "❤️",
  ]),
  "#demo-icon"
);

Indicator

mount(
  Indicator({ value: "3" }, [
    Button({ class: "btn-circle" }, Icon({}, "icon-[lucide--bell]")),
  ]),
  "#demo-indicator"
);

Input

const username = $("");
const password = $("");

mount(
  div({ class: "flex flex-col gap-4 w-80" }, [
    LabelFloating({}, [
      span({}, "Username"),
      div({ class: "input" }, [
        Icon({}, "icon-[lucide--user]"),
        Input({ class: "grow border-none", placeholder: "", value: username }),
      ]),
    ]),
    LabelFloating({}, [
      span({}, "Password"),
      div({ class: "input" }, [
        Icon({}, "icon-[lucide--lock]"),
        InputPass({ class: "grow border-none", placeholder: "", value: password }),
      ]),
    ]),
  ]),
  "#demo-input"
);

Kbd

mount(
  p({}, ["Press ", Kbd({}, "Ctrl"), " + ", Kbd({}, "S"), " to save"]),
  "#demo-kbd"
);

List & ListRows

const people = $([
  { name: "Alice", online: true },
  { name: "Bob", online: false },
]);

mount(
  div({ class: "flex flex-col gap-4" }, [
    List({ class: "bg-base-100 rounded-box shadow-md" }, [
      each(people, (p) =>
        li({ class: "list-row" }, [
          Badge({ class: p.online ? "badge-success" : "badge-ghost" }),
          p.name,
        ])
      ),
    ]),
    Divider({}, "OR"),
    List({ class: "bg-base-100 rounded-box shadow-md" }, [
      ListRows({
        items: people,
        render: (p) => [
          Badge({ class: p.online ? "badge-success" : "badge-ghost" }),
          p.name,
        ],
      }),
    ]),
  ]),
  "#demo-list"
);

Loading

mount(Loading({ class: "loading-lg text-primary" }), "#demo-loading");

Menu

mount(
  Menu({ class: "w-56 bg-base-200 rounded-box" }, [
    MenuItem({ label: "Dashboard", onclick: () => hide() }),
    li({}, details({ open: true }, [
      summary({}, "Settings"),
      Menu({}, [
        MenuItem({ label: "Profile", onclick: () => hide() }),
        MenuItem({ label: "Account", onclick: () => hide() }),
      ]),
    ])),
    MenuItem({ label: "Logout", onclick: () => alert("Logout") }),
  ]),
  "#demo-menu"
);

Modal

const modalRef = { current: null };

mount(
  div({}, [
    Button({ class: "btn", onclick: () => modalRef.current?.showModal() }, "Open modal"),
    Modal({ ref: (el) => modalRef.current = el }, [
      ModalBox({}, [
        h3({ class: "text-lg font-bold" }, "Congratulations!"),
        p({ class: "py-4" }, "You have successfully created a reactive DaisyUI modal with SigPro."),
        ModalAction({}, [
          form({ method: "dialog" }, [
            Button({}, "Close"),
          ]),
        ]),
      ]),
      ModalBackdrop(),
    ]),
  ]),
  "#demo-modal"
);

Navbar

mount(
  Navbar({ class: "bg-base-300 rounded-box" }, [
    div({ class: "flex-1" }, a({ class: "btn btn-ghost text-xl" }, "SigPro")),
    div({ class: "flex-none" }, Button({ class: "btn-square btn-ghost" }, Icon({}, "icon-[lucide--menu]"))),
  ]),
  "#demo-navbar"
);

Progress

const progressVal = $(35);

mount(
  div({ class: "flex flex-col gap-2" }, [
    span({}, () => `${progressVal()}%`),
    Progress({ value: progressVal, max: 100, class: "w-56" }),
  ]),
  "#demo-progress"
);

Radial Progress

const radialVal = $(70);

mount(
  Radial({ value: radialVal, class: "text-primary" }, () => `${radialVal()}%`),
  "#demo-radial"
);

Radio

const option = $("a");

mount(
  div({ class: "flex gap-4" }, [
    label({ class: "flex items-center gap-2" }, [
      Radio({ name: "demo-radio", value: "a", checked: () => option() === "a", onchange: () => option("a") }),
      "Option A",
    ]),
    label({ class: "flex items-center gap-2" }, [
      Radio({ name: "demo-radio", value: "b", checked: () => option() === "b", onchange: () => option("b") }),
      "Option B",
    ]),
  ]),
  "#demo-radio"
);

Range

const rangeValue = $(25);

mount(
  div({ class: "flex flex-col gap-2 w-60" }, [
    Range({ min: 0, max: 100, value: rangeValue, class: "range-sm" }),
    span({}, () => `Value: ${rangeValue()}`),
  ]),
  "#demo-range"
);

Rating & RatingItems

const stars = $(3);

mount(
  Rating({}, [
    RatingItems({ count: 5, value: stars, name: "demo-rating", class: "mask-star-2" }),
  ]),
  "#demo-rating"
);

Select

const choice = $("css");

mount(
  LabelFloating({}, [
    span({}, "Tech"),
    LabelSelect({}, [
      Select({ value: choice, onchange: (e) => choice(e.target.value) }, [
        SelectOption({ value: "" }, "Pick a language"),
        SelectOption({ value: "html" }, "HTML"),
        SelectOption({ value: "css" }, "CSS"),
        SelectOption({ value: "js" }, "JavaScript"),
      ]),
    ]),
  ]),
  "#demo-select"
);

Skeleton & SkeletonText

mount(
  div({ class: "flex flex-col gap-4 w-52" }, [
    Skeleton({ class: "h-32" }),
    SkeletonText({ class: "h-4" }),
    SkeletonText({ class: "h-4 w-3/4" }),
  ]),
  "#demo-skeleton"
);

Stack

mount(
  Stack({ class: "w-48" }, [
    img({ src: "https://img.daisyui.com/images/stock/photo-1572635148818-ef6fd45eb394.webp", class: "rounded-box" }),
    img({ src: "https://img.daisyui.com/images/stock/photo-1565098772267-60af42b81ef2.webp", class: "rounded-box" }),
    img({ src: "https://img.daisyui.com/images/stock/photo-1559703248-dcaaec9fab78.webp", class: "rounded-box" }),
  ]),
  "#demo-stack"
);

Stats & Stat

mount(
  Stats({ class: "w-full" }, [
    Stat({}, [
      StatTitle({}, "Downloads"),
      StatValue({}, "1,200"),
      StatDesc({}, "↑ 21% from last month"),
    ]),
    Stat({}, [
      StatTitle({}, "New Users"),
      StatValue({}, "450"),
      StatDesc({}, "↑ 14% from last month"),
    ]),
    Stat({}, [
      StatTitle({}, "Revenue"),
      StatValue({}, "$89k"),
      StatDesc({}, "↓ 1% from last month"),
    ]),
  ]),
  "#demo-stats"
);

Steps & Step

mount(
  Steps({ class: "steps-horizontal w-full" }, [
    Step({ dataContent: "1", class: "step-primary" }, "Register"),
    Step({ dataContent: "2", class: "step-primary" }, "Choose plan"),
    Step({ dataContent: "3" }, "Purchase"),
    Step({ dataContent: "4" }, "Enjoy"),
  ]),
  "#demo-steps"
);

Swap (Toggle)

const isDark = $(false);

mount(
  Swap({ class: "text-2xl" }, [
    SwapToggle({ value: isDark, class: "swap-rotate" }),
    SwapOn({}, Icon({}, "icon-[lucide--moon]")),
    SwapOff({}, Icon({}, "icon-[lucide--sun]")),
  ]),
  "#demo-swap"
);

Table

const tableData = $([
  { id: 1, name: "Alice", role: "Admin" },
  { id: 2, name: "Bob", role: "User" },
  { id: 3, name: "Charlie", role: "User" },
]);

mount(
  Table({ class: "w-full" }, [
    TableHead({}, TableRow({}, [
      TableTh({}, "Name"),
      TableTh({}, "Role"),
      TableTh({}, "Action"),
    ])),
    TableBody({}, 
      each(tableData, (item) =>
        TableRow({}, [
          TableTd({ class: "font-bold" }, item.name),
          TableTd({}, item.role),
          TableTd({}, Button({ class: "btn-xs", onclick: () => alert(`Edit ${item.name}`) }, "Edit")),
        ])
      )
    ),
  ]),
  "#demo-table"
);

Tabs

const tabsSignal = $([
  { label: "Tab A", content: "Content of tab A", open: true },
  { label: "Tab B", content: "Content of tab B", closable: true },
  { label: "Tab C", content: "Content of tab C" },
]);

mount(
  Tabs({ class: "tabs-box" },
    each(tabsSignal, (tab, i) =>
      Tab({
        name: "demo-tabs",
        label: tab.label,
        content: tab.content,
        checked: tab.open || false,
        tabs: tabsSignal,
        index: i,
        closable: tab.closable || false,
      })
    )
  ),
  "#demo-tabs"
);

Textarea

const bio = $("");

mount(
  Textarea({ class: "w-80", placeholder: "Write something...", value: bio }),
  "#demo-textarea"
);

Textrotate

mount(
  Textrotate({ class: "text-2xl font-bold" }, [
    span({}, "Hello"),
    span({}, "Bonjour"),
    span({}, "Hola"),
  ]),
  "#demo-textrotate"
);

Timeline

mount(
  Timeline({ class: "timeline-vertical" }, [
    li({}, [
      div({ class: "timeline-start" }, "2024"),
      div({ class: "timeline-middle" }, Icon({}, "icon-[lucide--check]")),
      div({ class: "timeline-end timeline-box" }, "Project started"),
    ]),
    li({}, [
      div({ class: "timeline-start" }, "2025"),
      div({ class: "timeline-middle" }, Icon({}, "icon-[lucide--clock]")),
      div({ class: "timeline-end timeline-box" }, "First prototype"),
    ]),
  ]),
  "#demo-timeline"
);

Toast

mount(
  div({ class: "flex flex-wrap gap-2" }, [
    Button({ class: "btn", onclick: () => Toast("File saved!") }, "Simple"),
    Button({ class: "btn", onclick: () => Toast("Error!", "alert-error", 5000) }, "Error (5s)"),
    Button({
      class: "btn",
      onclick: () =>
        Toast(
          div({ class: "flex items-center gap-2" }, [
            Icon({}, "icon-[lucide--check]"),
            span({}, "Report generated"),
          ]),
          "alert-success"
        ),
    }, "With icon"),
    Button({
      class: "btn",
      onclick: () =>
        Toast(
          div({ class: "flex flex-col" }, [
            strong({}, "ATTENTION!"),
            span({}, "Error saving!"),
            button({ class: "btn btn-xs mt-1", onclick: () => console.log("Retry") }, "Retry"),
          ]),
          "alert-warning",
          7000
        ),
    }, "Complex"),
  ]),
  "#demo-toast"
);

Toggle

const toggleActive = $(false);

mount(
  Toggle({
    checked: toggleActive,
    onchange: (e) => toggleActive(e.target.checked),
    class: "toggle-primary",
  }),
  "#demo-toggle"
);

Tooltip

mount(
  Tooltip({ tip: "Save changes", class: "tooltip-right tooltip-primary" },
    Button({ class: "btn" }, "Save")
  ),
  "#demo-tooltip"
);