Skip to main content

priv/registry/toggle.json

{
  "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"
  ]
}