{
"files": [
{
"content": "defmodule Shadix.Components.Toggle do\n @moduledoc \"\"\"\n Toggle component translated from shadcn/ui (new-york-v4).\n\n A two-state toggle button. Unlike a native checkbox, the pressed state lives in\n ARIA (`aria-pressed`) and a `data-state` of `\"on\"`/`\"off\"` that the `cva` styles\n key off of (the on-state gets `bg-accent`). It renders a plain\n `<button type=\"button\">` and is *uncontrolled*: `@pressed` only seeds the initial\n markup. The small `ShadixToggle` hook (assets/ts/toggle.ts) flips both\n `aria-pressed` and `data-state` on click by reading the current DOM value, so it\n stays independent of the server-rendered initial state.\n\n Caller-supplied `class` is appended last; Tailwind cascade layers ensure it wins\n over the defaults.\n \"\"\"\n use Phoenix.Component\n\n import Shadix.Cn\n\n @base \"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\"\n\n @variants %{\n \"default\" => \"bg-transparent\",\n \"outline\" =>\n \"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground\"\n }\n\n @sizes %{\n \"default\" => \"h-9 min-w-9 px-2\",\n \"sm\" => \"h-8 min-w-8 px-1.5\",\n \"lg\" => \"h-10 min-w-10 px-2.5\"\n }\n\n @doc \"\"\"\n Renders an uncontrolled two-state toggle button.\n\n `:pressed` seeds the initial `aria-pressed` / `data-state`; thereafter the\n `ShadixToggle` hook owns the state and flips it on click.\n \"\"\"\n attr(:variant, :string, default: \"default\", values: ~w(default outline))\n attr(:size, :string, default: \"default\", values: ~w(default sm lg))\n attr(:pressed, :boolean, default: false)\n attr(:class, :string, default: nil)\n attr(:rest, :global)\n slot(:inner_block, required: true)\n\n def toggle(assigns) do\n assigns =\n assign(\n assigns,\n :computed_class,\n cn([@base, @variants[assigns.variant], @sizes[assigns.size], assigns.class])\n )\n\n ~H\"\"\"\n <button\n type=\"button\"\n data-slot=\"toggle\"\n aria-pressed={to_string(@pressed)}\n data-state={if @pressed, do: \"on\", else: \"off\"}\n phx-hook=\"ShadixToggle\"\n class={@computed_class}\n {@rest}\n >\n {render_slot(@inner_block)}\n </button>\n \"\"\"\n end\nend\n",
"path": "toggle.ex"
}
],
"hooks": [
{
"content": "interface ToggleHook {\n el: HTMLElement;\n mounted(): void;\n destroyed(): void;\n}\n\nexport const ShadixToggle = {\n mounted(this: ToggleHook) {\n const el = this.el;\n\n const onClick = () => {\n const next = el.getAttribute(\"aria-pressed\") !== \"true\";\n el.setAttribute(\"aria-pressed\", String(next));\n el.setAttribute(\"data-state\", next ? \"on\" : \"off\");\n };\n\n el.addEventListener(\"click\", onClick);\n (el as HTMLElement & { __shadixToggle?: () => void }).__shadixToggle = onClick;\n },\n\n destroyed(this: ToggleHook) {\n const el = this.el as HTMLElement & { __shadixToggle?: () => void };\n if (el.__shadixToggle) {\n el.removeEventListener(\"click\", el.__shadixToggle);\n }\n },\n};\n",
"name": "ShadixToggle",
"path": "toggle.ts"
}
],
"name": "toggle",
"npm_deps": [],
"registry_deps": [
"cn"
]
}