{"version":3,"sources":["../src/toggle/toggle.tsx"],"names":["forwardRef","Toggle","useState","usePermissions","resolvePermissions","useAudit","evaluatePermission","CANONICAL_ACTIONS","jsx"],"mappings":"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","file":"toggle.cjs","sourcesContent":["/**\n * Toggle — headless proprietary primitive.\n *\n * A binary on/off control rendered as `<button role=\"switch\">` with\n * `aria-checked`. Following WAI-ARIA Authoring Practices 1.3 §3.23\n * (\"Switch Pattern\") — a switch is keyboard-equivalent to a checkbox\n * (Space/Enter toggles, Tab navigates) but communicates a distinct\n * \"on/off state of a single option\" semantic to assistive tech.\n *\n * Client component (consumes `PermissionContext` + `AuditContext`).\n *\n * Contracts honored:\n *   - Permission gate via `can('toggle', resource, attrs)`. Denied\n *     presentations collapse like Button: `disabled` → native disabled,\n *     `readOnly` → visible + non-mutating, `hidden` → null.\n *   - Canonical `toggle:change` audit on state flip; `permission:denied`\n *     on blocked flip in readOnly mode.\n *   - `data-oshon-primitive=\"toggle\"` + `data-permission-state` styling\n *     hooks; `data-state=\"on\"|\"off\"` for the two states.\n *\n * Proprietary rather than Radix-wrapped because switch semantics are\n * narrow enough that a native `<button>` + `aria-checked` is a complete,\n * well-specified pattern. Same rubric as Button (ADR-001).\n */\n\n'use client';\n\nimport { forwardRef, useState } from 'react';\nimport type {\n  ButtonHTMLAttributes,\n  MouseEventHandler,\n  ReactNode,\n  Ref,\n} from 'react';\nimport {\n  evaluatePermission,\n  resolvePermissions,\n  usePermissions,\n  useAudit,\n  type PermissionContext,\n} from '../context.js';\nimport { CANONICAL_ACTIONS } from '../actions.js';\n\ntype NativeButtonProps = Omit<\n  ButtonHTMLAttributes<HTMLButtonElement>,\n  'type' | 'disabled' | 'role' | 'aria-checked' | 'aria-pressed'\n>;\n\nexport interface ToggleProps extends NativeButtonProps {\n  /**\n   * Controlled on/off state. Mutually exclusive with `defaultChecked`.\n   * When provided, the primitive is fully controlled — the consumer\n   * manages state and responds to `onCheckedChange` to update it.\n   */\n  checked?: boolean;\n  /**\n   * Initial state for uncontrolled mode. Primitive owns its own state;\n   * consumer reads it only via `onCheckedChange`.\n   */\n  defaultChecked?: boolean;\n  /**\n   * Fires whenever the switch flips. `next` is the new state. Not called\n   * when a flip attempt is denied by permissions or blocked by\n   * disabled/loading.\n   */\n  onCheckedChange?: (next: boolean) => void;\n  /**\n   * Disable the toggle. Renders with native `disabled` + `aria-disabled`.\n   * No flip, no audit.\n   */\n  disabled?: boolean;\n  /**\n   * Visible label for assistive tech. Either pass `children` (the label\n   * is read as the button's accessible name) or an `aria-label` / use an\n   * external `<label htmlFor>` + `id`. For icon-only toggles, always use\n   * `aria-label`.\n   */\n  children?: ReactNode;\n  /** Per-instance permission override; merges over context. */\n  permissions?: Partial<PermissionContext>;\n  /**\n   * Resource name for `permissions.can('toggle', resource, attrs)`.\n   * Default `'toggle'`. Override for specific semantics, e.g.\n   * `resource=\"settings:notifications\"`.\n   */\n  resource?: string;\n  /** Attribute bag for attribute-based access control. */\n  permissionAttrs?: Record<string, unknown>;\n}\n\n/**\n * Headless switch/toggle. Accessible `<button role=\"switch\">` with:\n *   - `aria-checked` synchronised with state\n *   - `disabled` + `aria-disabled` synchronised\n *   - Permission gating via `can('toggle', resource, attrs)` with three\n *     modes (disabled / readOnly / hidden)\n *   - Canonical `toggle:change` audit on flip; `permission:denied` on\n *     blocked flip in readOnly mode\n *   - `data-oshon-primitive=\"toggle\"` + `data-state=\"on\"|\"off\"` +\n *     `data-permission-state` styling hooks\n *   - No visual styling — consumers supply their own className / styles.\n *\n * Keyboard: native browser behavior — Enter + Space activate (flip).\n *\n * Controlled vs. uncontrolled: pass `checked` for controlled,\n * `defaultChecked` for uncontrolled. Only one at a time.\n */\nexport const Toggle = forwardRef<HTMLButtonElement, ToggleProps>(\n  function Toggle(\n    {\n      checked,\n      defaultChecked = false,\n      onCheckedChange,\n      disabled = false,\n      children,\n      permissions,\n      resource = 'toggle',\n      permissionAttrs,\n      onClick,\n      ...rest\n    }: ToggleProps,\n    ref: Ref<HTMLButtonElement>,\n  ) {\n    const isControlled = checked !== undefined;\n    const [internal, setInternal] = useState(defaultChecked);\n    const current = isControlled ? (checked as boolean) : internal;\n\n    const contextPermissions = usePermissions();\n    const effective = resolvePermissions(permissions, contextPermissions);\n    const mode = effective.mode ?? 'disabled';\n    const emit = useAudit();\n\n    const { granted, denyReason } = evaluatePermission(\n      effective,\n      'toggle',\n      resource,\n      permissionAttrs,\n    );\n\n    if (!granted && mode === 'hidden') return null;\n\n    const permissionBlocks = !granted && mode !== 'readOnly';\n    const isDisabled = disabled || permissionBlocks;\n\n    const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n      if (disabled) return;\n\n      if (!granted) {\n        emit({\n          component: 'toggle',\n          action: CANONICAL_ACTIONS.PERMISSION_DENIED,\n          permissionResult: 'denied',\n          ...(denyReason ? { denyReason } : {}),\n          details: { attemptedAction: 'toggle', resource },\n        });\n        // readOnly: visible but non-mutating. No state change, no\n        // onCheckedChange call, no onClick forward.\n        return;\n      }\n\n      const next = !current;\n      if (!isControlled) setInternal(next);\n\n      emit({\n        component: 'toggle',\n        action: CANONICAL_ACTIONS.TOGGLE_CHANGE,\n        permissionResult: 'granted',\n        details: { from: current, to: next },\n      });\n\n      onCheckedChange?.(next);\n      onClick?.(event);\n    };\n\n    const permissionDataState =\n      !granted && mode !== 'hidden' ? 'denied' : undefined;\n\n    const resolvedTitle =\n      rest.title ?? (permissionDataState && denyReason ? denyReason : undefined);\n\n    return (\n      <button\n        {...rest}\n        ref={ref}\n        type=\"button\"\n        role=\"switch\"\n        aria-checked={current}\n        disabled={isDisabled}\n        aria-disabled={isDisabled || undefined}\n        data-oshon-primitive=\"toggle\"\n        data-state={current ? 'on' : 'off'}\n        data-permission-state={permissionDataState}\n        title={resolvedTitle}\n        onClick={handleClick}\n      >\n        {children}\n      </button>\n    );\n  },\n);\n"]}