{"version":3,"sources":["../src/button/button.tsx"],"names":["forwardRef","Button","usePermissions","resolvePermissions","useAudit","evaluatePermission","CANONICAL_ACTIONS","jsx"],"mappings":";;;;;;;;AAwGO,IAAM,MAAA,GAASA,gBAAA;AAAA,EACpB,SAASC,OAAAA,CACP;AAAA,IACE,QAAA;AAAA,IACA,IAAA,GAAO,QAAA;AAAA,IACP,QAAA,GAAW,KAAA;AAAA,IACX,OAAA,GAAU,KAAA;AAAA,IACV,WAAA;AAAA,IACA,QAAA,GAAW,QAAA;AAAA,IACX,eAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAG;AAAA,KAEL,GAAA,EACA;AACA,IAAA,MAAM,qBAAqBC,yBAAA,EAAe;AAC1C,IAAA,MAAM,SAAA,GAAYC,6BAAA,CAAmB,WAAA,EAAa,kBAAkB,CAAA;AACpE,IAAA,MAAM,IAAA,GAAO,UAAU,IAAA,IAAQ,UAAA;AAC/B,IAAA,MAAM,OAAOC,mBAAA,EAAS;AAEtB,IAAA,MAAM,EAAE,OAAA,EAAS,UAAA,EAAW,GAAIC,6BAAA;AAAA,MAC9B,SAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACF;AAGA,IAAA,IAAI,CAAC,OAAA,IAAW,IAAA,KAAS,QAAA,EAAU,OAAO,IAAA;AAK1C,IAAA,MAAM,gBAAA,GAAmB,CAAC,OAAA,IAAW,IAAA,KAAS,UAAA;AAC9C,IAAA,MAAM,UAAA,GAAa,YAAY,OAAA,IAAW,gBAAA;AAE1C,IAAA,MAAM,WAAA,GAAoD,CAAC,KAAA,KAAU;AAEnE,MAAA,IAAI,YAAY,OAAA,EAAS;AAEzB,MAAA,IAAI,CAAC,OAAA,EAAS;AAIZ,QAAA,IAAA,CAAK;AAAA,UACH,SAAA,EAAW,QAAA;AAAA,UACX,QAAQC,4BAAA,CAAkB,iBAAA;AAAA,UAC1B,gBAAA,EAAkB,QAAA;AAAA,UAClB,GAAI,UAAA,GAAa,EAAE,UAAA,KAAe,EAAC;AAAA,UACnC,OAAA,EAAS,EAAE,eAAA,EAAiB,OAAA,EAAS,QAAA;AAAS,SAC/C,CAAA;AAID,QAAA;AAAA,MACF;AAEA,MAAA,IAAA,CAAK;AAAA,QACH,SAAA,EAAW,QAAA;AAAA,QACX,QAAQA,4BAAA,CAAkB,YAAA;AAAA,QAC1B,gBAAA,EAAkB;AAAA,OACnB,CAAA;AACD,MAAA,OAAA,GAAU,KAAK,CAAA;AAAA,IACjB,CAAA;AAEA,IAAA,MAAM,mBAAA,GACJ,CAAC,OAAA,IAAW,IAAA,KAAS,WAAW,QAAA,GAAW,MAAA;AAG7C,IAAA,MAAM,aAAA,GACJ,IAAA,CAAK,KAAA,KAAU,mBAAA,IAAuB,aAAa,UAAA,GAAa,MAAA,CAAA;AAElE,IAAA,uBACEC,cAAA;AAAA,MAAC,QAAA;AAAA,MAAA;AAAA,QACE,GAAG,IAAA;AAAA,QACJ,GAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA,EAAU,UAAA;AAAA,QACV,iBAAe,UAAA,IAAc,MAAA;AAAA,QAC7B,aAAW,OAAA,IAAW,MAAA;AAAA,QACtB,sBAAA,EAAqB,QAAA;AAAA,QACrB,cAAA,EAAc,UAAU,MAAA,GAAS,MAAA;AAAA,QACjC,uBAAA,EAAuB,mBAAA;AAAA,QACvB,KAAA,EAAO,aAAA;AAAA,QACP,OAAA,EAAS,WAAA;AAAA,QAER;AAAA;AAAA,KACH;AAAA,EAEJ;AACF","file":"button.cjs","sourcesContent":["/**\n * Button — headless proprietary primitive.\n *\n * Client component (consumes `PermissionContext` + `AuditContext` via\n * `useContext`). See `docs/adr/primitives/001-button-rsc-default.md` for\n * the full trade-off analysis.\n *\n * Contracts honored:\n *   - Permission gate (ARCHITECTURE §10). The button asks the ambient\n *     policy `can('click', 'button', attrs?)`. Denied → the `mode` field\n *     decides presentation (`'disabled'`, `'hidden'`, or `'readOnly'`).\n *   - Audit event (§10). Every successful activation emits\n *     `PRIMITIVE_ACTIONS.BUTTON_CLICK` with ISO-8601 timestamp. Denied\n *     activations emit `DATA_ACTIONS.PERMISSION_DENIED` with the deny\n *     reason so governance can index on them.\n */\n\n'use client';\n\nimport { forwardRef } 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' | 'aria-busy'\n>;\n\nexport interface ButtonProps extends NativeButtonProps {\n  /**\n   * Button contents. Unstyled — wrap with icons / labels as needed. The\n   * visual layer in `@oshon-ai/components` adds spacing and iconography\n   * on top of this primitive.\n   */\n  children: ReactNode;\n  /**\n   * Native `type` attribute. Defaults to `'button'` so the primitive\n   * never accidentally submits a form. Pass `'submit'` explicitly for\n   * form buttons.\n   */\n  type?: 'button' | 'submit' | 'reset';\n  /**\n   * Disable the button. Renders with `aria-disabled=\"true\"` and the\n   * native `disabled` attribute so screen readers and keyboard users\n   * get consistent signal. When `disabled`, `onClick` does not fire and\n   * no audit event is emitted.\n   */\n  disabled?: boolean;\n  /**\n   * In-flight state. Sets `aria-busy=\"true\"` + `disabled` so assistive\n   * tech announces the button as unavailable while the action resolves.\n   * The visual layer renders a spinner on top; the primitive itself does\n   * not.\n   */\n  loading?: boolean;\n  /**\n   * Per-instance permission override. Merges over the ambient\n   * `PermissionContext`. Example: `permissions={{ mode: 'hidden' }}`\n   * switches this one instance to render-as-null when denied.\n   */\n  permissions?: Partial<PermissionContext>;\n  /**\n   * Resource name passed to `permissions.can(action, resource, attrs)`.\n   * Default `'button'`. Consumers override for specific semantics, e.g.\n   * `resource=\"row:edit\"` for a row-level action button.\n   */\n  resource?: string;\n  /**\n   * Attribute bag forwarded to `permissions.can(..., attrs)` for\n   * attribute-based access control (row ID, tenant, owner). Primitives\n   * never invent values here — the consumer supplies them.\n   */\n  permissionAttrs?: Record<string, unknown>;\n}\n\n/**\n * Headless button. Accessible `<button>` with:\n *   - `type=\"button\"` default (no accidental form submits)\n *   - `disabled` + `aria-disabled` + `aria-busy` synchronised\n *   - Permission gating via `can('click', resource, attrs)` with `mode`\n *     controlling the denied-state presentation\n *   - Canonical `button:click` audit event on activation; `permission:denied`\n *     with deny reason on blocked activation\n *   - `data-oshon-primitive=\"button\"` styling hook for the components layer\n *   - `data-permission-state=\"denied\"` hook when mode is `'disabled'` or\n *     `'readOnly'` and the policy denied the action\n *   - No visual styling — consumers supply their own className / styles.\n *\n * Keyboard: native browser behavior — Enter + Space activate.\n *\n * @see docs/adr/primitives/001-button-rsc-default.md\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n  function Button(\n    {\n      children,\n      type = 'button',\n      disabled = false,\n      loading = false,\n      permissions,\n      resource = 'button',\n      permissionAttrs,\n      onClick,\n      ...rest\n    }: ButtonProps,\n    ref: Ref<HTMLButtonElement>,\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      'click',\n      resource,\n      permissionAttrs,\n    );\n\n    // Permission denied + mode='hidden' → the affordance must not exist.\n    if (!granted && mode === 'hidden') return null;\n\n    // `readOnly` denies the click but keeps the affordance visible +\n    // focusable. `disabled` denies + visibly disables. Both emit the\n    // permission:denied audit trail on interaction attempts.\n    const permissionBlocks = !granted && mode !== 'readOnly';\n    const isDisabled = disabled || loading || permissionBlocks;\n\n    const handleClick: MouseEventHandler<HTMLButtonElement> = (event) => {\n      // Local disabled/loading — silent drop, no audit.\n      if (disabled || loading) return;\n\n      if (!granted) {\n        // Record the denial regardless of `mode`. Governance wants these\n        // events to surface over-permissive UIs as much as under-permissive\n        // ones.\n        emit({\n          component: 'button',\n          action: CANONICAL_ACTIONS.PERMISSION_DENIED,\n          permissionResult: 'denied',\n          ...(denyReason ? { denyReason } : {}),\n          details: { attemptedAction: 'click', resource },\n        });\n        // `readOnly` means \"visible, non-mutating\" — we don't invoke\n        // onClick but we also don't `preventDefault` the browser's click\n        // (it has no meaningful default behavior on a plain button).\n        return;\n      }\n\n      emit({\n        component: 'button',\n        action: CANONICAL_ACTIONS.BUTTON_CLICK,\n        permissionResult: 'granted',\n      });\n      onClick?.(event);\n    };\n\n    const permissionDataState =\n      !granted && mode !== 'hidden' ? 'denied' : undefined;\n\n    // Consumer title always wins; deny reason fills in only when no title.\n    const resolvedTitle =\n      rest.title ?? (permissionDataState && denyReason ? denyReason : undefined);\n\n    return (\n      <button\n        {...rest}\n        ref={ref}\n        type={type}\n        disabled={isDisabled}\n        aria-disabled={isDisabled || undefined}\n        aria-busy={loading || undefined}\n        data-oshon-primitive=\"button\"\n        data-loading={loading ? 'true' : undefined}\n        data-permission-state={permissionDataState}\n        title={resolvedTitle}\n        onClick={handleClick}\n      >\n        {children}\n      </button>\n    );\n  },\n);\n"]}