{"version":3,"file":"checkable.cjs","names":[],"sources":["../../src/core/checkable.ts"],"sourcesContent":["import { computed, type Readable, type Signal, signal } from '@vielzeug/ripple';\n\nimport { type ControlValidationMode, createDirtyTracker, createField, type FieldHandle } from './field-base';\nimport { createInteraction } from './keyboard';\nimport { syncedSignal } from './signals';\n\n// ── Checkable control ─────────────────────────────────────────────────────────\n\nexport type CheckableChangePayload = {\n  checked: boolean;\n  originalEvent?: Event;\n  value: string;\n};\n\nexport type CheckableOptions = {\n  checked: Readable<boolean | undefined>;\n  clearIndeterminateFirst?: boolean;\n  disabled?: Readable<boolean | undefined>;\n  error?: Readable<string | undefined>;\n  group?: { toggle: (value: string, originalEvent?: Event) => void };\n  helper?: Readable<string | undefined>;\n  indeterminate?: Readable<boolean | undefined>;\n  onToggle?: (payload: CheckableChangePayload) => void;\n  prefix?: string;\n  /** Marks an unchecked control invalid — feeds `validity`/`validationMessage` (see below). */\n  required?: Readable<boolean | undefined>;\n  /** Message for the unchecked+required case. Defaults to `'This field is required.'`. */\n  requiredMessage?: Readable<string | undefined>;\n  /** `AbortSignal` from the component lifecycle. All reactive subscriptions are torn down on abort. */\n  signal: AbortSignal;\n  validateOn?: Readable<ControlValidationMode>;\n  value: Readable<string | undefined>;\n};\n\nexport type CheckableHandle = FieldHandle & {\n  /** The form submission value: null when unchecked/indeterminate, the value string when checked. */\n  checkableFormValue: Readable<string | null>;\n  checked: Signal<boolean>;\n  handleClick: (event: MouseEvent) => boolean;\n  handleKeydown: (event: KeyboardEvent) => boolean;\n  indeterminate: Signal<boolean>;\n  /**\n   * Restores `checked`/`indeterminate` to their native \"default\" value. See `DirtyTracker`\n   * for the two-state rationale. Wire into `useField({ onReset: checkable.reset })`.\n   */\n  reset: () => void;\n  toggle: (event: Event) => void;\n  /** Reactive validation message paired with `validity`. Empty string when valid. */\n  validationMessage: Readable<string>;\n  /**\n   * Reactive `ValidityStateFlags` — `{ valueMissing: true }` while `required` and unchecked\n   * (indeterminate counts as unchecked), `null` (valid) otherwise. Pass straight to\n   * `useField({ validity: checkable.validity })`.\n   */\n  validity: Readable<ValidityStateFlags | null>;\n};\n\nexport const createCheckable = (options: CheckableOptions): CheckableHandle => {\n  const checked = syncedSignal(options.checked, options.signal, Boolean);\n  const indeterminate = options.indeterminate\n    ? syncedSignal(options.indeterminate, options.signal, Boolean)\n    : signal(false);\n  const initialChecked = checked.value;\n  const initialIndeterminate = indeterminate.value;\n  const dirtyTracker = createDirtyTracker();\n\n  const checkableFormValue = computed<string | null>(() =>\n    indeterminate.value ? null : checked.value ? (options.value.value ?? '') : null,\n  );\n\n  const field = createField(options);\n\n  const validity = computed<ValidityStateFlags | null>(() =>\n    options.required?.value && (!checked.value || indeterminate.value) ? { valueMissing: true } : null,\n  );\n  const validationMessage = computed(() =>\n    validity.value ? options.requiredMessage?.value || 'This field is required.' : '',\n  );\n\n  const reset = (): void => {\n    checked.value = dirtyTracker.isDirty ? initialChecked : Boolean(options.checked.value);\n    indeterminate.value = dirtyTracker.isDirty ? initialIndeterminate : Boolean(options.indeterminate?.value);\n    dirtyTracker.clear();\n    field.triggerValidation('change');\n  };\n\n  const createPayload = (event: Event): CheckableChangePayload => ({\n    checked: checked.value,\n    originalEvent: event,\n    value: options.value.value ?? '',\n  });\n\n  const toggle = (event: Event): void => {\n    if (field.disabled.value) return;\n\n    dirtyTracker.markDirty();\n\n    if (options.group) {\n      indeterminate.value = false;\n      options.group.toggle(options.value.value ?? '', event);\n      options.onToggle?.(createPayload(event));\n\n      return;\n    }\n\n    if (options.clearIndeterminateFirst && indeterminate.value) {\n      indeterminate.value = false;\n    } else {\n      checked.value = !checked.value;\n      indeterminate.value = false;\n    }\n\n    options.onToggle?.(createPayload(event));\n  };\n\n  const interaction = createInteraction({\n    disabled: field.disabled,\n    onPress: (event) => toggle(event),\n  });\n\n  return {\n    ...field,\n    checkableFormValue,\n    checked,\n    handleClick: interaction.handleClick,\n    handleKeydown: interaction.handleKeydown,\n    indeterminate,\n    reset,\n    toggle,\n    validationMessage,\n    validity,\n  };\n};\n"],"mappings":"6HAyDA,IAAa,EAAmB,GAA+C,CAC7E,IAAM,EAAU,EAAA,aAAa,EAAQ,QAAS,EAAQ,OAAQ,OAAO,EAC/D,EAAgB,EAAQ,cAC1B,EAAA,aAAa,EAAQ,cAAe,EAAQ,OAAQ,OAAO,GAAA,EAC3D,EAAA,OAAA,CAAO,EAAK,EACV,EAAiB,EAAQ,MACzB,EAAuB,EAAc,MACrC,EAAe,EAAA,mBAAmB,EAElC,GAAA,EAAqB,EAAA,SAAA,KACzB,EAAc,MAAQ,KAAO,EAAQ,MAAS,EAAQ,MAAM,OAAS,GAAM,IAC7E,EAEM,EAAQ,EAAA,YAAY,CAAO,EAE3B,GAAA,EAAW,EAAA,SAAA,KACf,EAAQ,UAAU,QAAU,CAAC,EAAQ,OAAS,EAAc,OAAS,CAAE,aAAc,EAAK,EAAI,IAChG,EACM,GAAA,EAAoB,EAAA,SAAA,KACxB,EAAS,MAAQ,EAAQ,iBAAiB,OAAS,0BAA4B,EACjF,EAEM,MAAoB,CACxB,EAAQ,MAAQ,EAAa,QAAU,EAAiB,EAAQ,EAAQ,QAAQ,MAChF,EAAc,MAAQ,EAAa,QAAU,EAAuB,EAAQ,EAAQ,eAAe,MACnG,EAAa,MAAM,EACnB,EAAM,kBAAkB,QAAQ,CAClC,EAEM,EAAiB,IAA0C,CAC/D,QAAS,EAAQ,MACjB,cAAe,EACf,MAAO,EAAQ,MAAM,OAAS,EAChC,GAEM,EAAU,GAAuB,CACjC,MAAM,SAAS,MAInB,IAFA,EAAa,UAAU,EAEnB,EAAQ,MAAO,CACjB,EAAc,MAAQ,GACtB,EAAQ,MAAM,OAAO,EAAQ,MAAM,OAAS,GAAI,CAAK,EACrD,EAAQ,WAAW,EAAc,CAAK,CAAC,EAEvC,MACF,CAEI,EAAQ,yBAA2B,EAAc,QAGnD,EAAQ,MAAQ,CAAC,EAAQ,OAFzB,EAAc,MAAQ,GAMxB,EAAQ,WAAW,EAAc,CAAK,CAAC,CATvC,CAUF,EAEM,EAAc,EAAA,kBAAkB,CACpC,SAAU,EAAM,SAChB,QAAU,GAAU,EAAO,CAAK,CAClC,CAAC,EAED,MAAO,CACL,GAAG,EACH,qBACA,UACA,YAAa,EAAY,YACzB,cAAe,EAAY,cAC3B,gBACA,QACA,SACA,oBACA,UACF,CACF"}