{"version":3,"file":"keyboard.cjs","names":[],"sources":["../../src/core/keyboard.ts"],"sourcesContent":["import type { Readable } from '@vielzeug/ripple';\n\n// ── Keyboard dispatch ─────────────────────────────────────────────────────────\n\nexport type KeyboardDispatchOptions = {\n  disabled?: Readable<boolean | undefined>;\n  keymap: Record<string, (event: KeyboardEvent) => boolean | void>;\n  preventDefault?: 'after' | 'before' | false;\n};\n\nexport const dispatchKeyboardAction = (event: KeyboardEvent, options: KeyboardDispatchOptions): boolean => {\n  if (options.disabled?.value) return false;\n\n  // `event.key` is attacker-influenceable on a synthetic KeyboardEvent (e.g. `new\n  // KeyboardEvent('keydown', { key: '__proto__' })`). Guard with `Object.hasOwn` so a\n  // crafted key name can never resolve to an inherited `Object.prototype` member\n  // (`__proto__`, `constructor`, `toString`, …) instead of `undefined`.\n  const action = Object.hasOwn(options.keymap, event.key) ? options.keymap[event.key] : undefined;\n\n  if (!action) return false;\n\n  const preventDefaultMode = options.preventDefault ?? 'before';\n\n  if (preventDefaultMode === 'before') event.preventDefault();\n\n  const handled = action(event) !== false;\n\n  if (preventDefaultMode === 'after' && handled) event.preventDefault();\n\n  return handled;\n};\n\n// ── Press control / Interaction ───────────────────────────────────────────────\n\nexport type PressTrigger = 'keyboard' | 'pointer';\n\n/**\n * Options for `createInteraction` — a unified press + focus/blur interaction\n * handler for accessible interactive elements.\n */\nexport type InteractionOptions = {\n  /** `true` when the element is disabled. All handlers become no-ops. */\n  disabled?: Readable<boolean | undefined>;\n  /** Keys that trigger a press. Defaults to `['Enter', ' ']`. */\n  keys?: string[];\n  /** Called when the element loses focus. */\n  onBlur?: (event: FocusEvent) => void;\n  /** Called when the element receives focus. */\n  onFocus?: (event: FocusEvent) => void;\n  /** Called when the element is pressed via keyboard or pointer. */\n  onPress?: (event: KeyboardEvent | MouseEvent, trigger: PressTrigger) => void;\n};\n\nexport type Interaction = {\n  handleBlur: (event: FocusEvent) => void;\n  handleClick: (event: MouseEvent) => boolean;\n  handleFocus: (event: FocusEvent) => void;\n  handleKeydown: (event: KeyboardEvent) => boolean;\n};\n\n/**\n * Creates a set of event handlers for an interactive element that responds to\n * keyboard and pointer presses, and optionally focus/blur events.\n *\n * @example\n * ```ts\n * const interaction = createInteraction({\n *   disabled: props.disabled,\n *   onPress: (event, trigger) => toggle(event),\n *   onFocus: (event) => setFocused(true),\n *   onBlur: (event) => setFocused(false),\n * });\n *\n * // In template:\n * // @click=\"${interaction.handleClick}\"\n * // @keydown=\"${interaction.handleKeydown}\"\n * // @focus=\"${interaction.handleFocus}\"\n * // @blur=\"${interaction.handleBlur}\"\n * ```\n */\nexport const createInteraction = (options: InteractionOptions): Interaction => {\n  const isDisabled = (): boolean => Boolean(options.disabled?.value);\n  const keyboardKeys = options.keys ?? ['Enter', ' '];\n  const keymap = Object.fromEntries(\n    keyboardKeys.map((key) => [key, (keyboardEvent: KeyboardEvent) => options.onPress?.(keyboardEvent, 'keyboard')]),\n  );\n\n  return {\n    handleBlur: (event: FocusEvent): void => {\n      if (!isDisabled()) options.onBlur?.(event);\n    },\n    handleClick: (event: MouseEvent): boolean => {\n      if (isDisabled()) return false;\n\n      options.onPress?.(event, 'pointer');\n\n      return true;\n    },\n    handleFocus: (event: FocusEvent): void => {\n      if (!isDisabled()) options.onFocus?.(event);\n    },\n    handleKeydown: (event: KeyboardEvent): boolean =>\n      dispatchKeyboardAction(event, { disabled: options.disabled, keymap }),\n  };\n};\n"],"mappings":"AAUA,IAAa,GAA0B,EAAsB,IAA8C,CACzG,GAAI,EAAQ,UAAU,MAAO,MAAO,GAMpC,IAAM,EAAS,OAAO,OAAO,EAAQ,OAAQ,EAAM,GAAG,EAAI,EAAQ,OAAO,EAAM,KAAO,IAAA,GAEtF,GAAI,CAAC,EAAQ,MAAO,GAEpB,IAAM,EAAqB,EAAQ,gBAAkB,SAEjD,IAAuB,UAAU,EAAM,eAAe,EAE1D,IAAM,EAAU,EAAO,CAAK,IAAM,GAIlC,OAFI,IAAuB,SAAW,GAAS,EAAM,eAAe,EAE7D,CACT,EAkDa,EAAqB,GAA6C,CAC7E,IAAM,MAA4B,EAAQ,EAAQ,UAAU,MACtD,EAAe,EAAQ,MAAQ,CAAC,QAAS,GAAG,EAC5C,EAAS,OAAO,YACpB,EAAa,IAAK,GAAQ,CAAC,EAAM,GAAiC,EAAQ,UAAU,EAAe,UAAU,CAAC,CAAC,CACjH,EAEA,MAAO,CACL,WAAa,GAA4B,CAClC,EAAW,GAAG,EAAQ,SAAS,CAAK,CAC3C,EACA,YAAc,GACZ,CAAI,EAAW,IAEf,EAAQ,UAAU,EAAO,SAAS,EAE3B,IAET,YAAc,GAA4B,CACnC,EAAW,GAAG,EAAQ,UAAU,CAAK,CAC5C,EACA,cAAgB,GACd,EAAuB,EAAO,CAAE,SAAU,EAAQ,SAAU,QAAO,CAAC,CACxE,CACF"}