{
  "name": "questionnaire",
  "type": "registry:ui",
  "dependencies": [
    "reka-ui"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "ui/questionnaire/Questionnaire.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type {\n  ItemRegistration,\n  QuestionnaireItemDefinition,\n  QuestionnaireShortcutMode,\n} from \"./useQuestionnaire\"\nimport { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport {\n  compareDocumentOrder,\n  createQuestionnaireCollection,\n  getInitialItemName,\n  getShortcutFromKey,\n  isAnswerFilled,\n  isRadioTarget,\n  isTextEntryTarget,\n  provideQuestionnaireRootContext,\n} from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  /** Item shown first. Ignored when `item` is provided. */\n  defaultItem?: string\n  /** Controlled active item. Use with `v-model:item`. */\n  item?: string\n  /** Declares the item order, and the choice order shortcuts are assigned in. */\n  items?: readonly QuestionnaireItemDefinition[]\n  /** Set to `false` to run native constraint validation on answered items. */\n  noValidate?: boolean\n  /** Assigns a keyboard shortcut to every choice. */\n  shortcuts?: QuestionnaireShortcutMode\n}>(), {\n  noValidate: true,\n})\n\nconst emits = defineEmits<{\n  \"reset\": [event: Event]\n  \"submit\": [event: Event]\n  \"update:item\": [item: string]\n}>()\n\ninterface PendingFocus {\n  name: string\n  target: \"invalid\" | \"item\"\n}\n\nconst rootElement = ref<HTMLFormElement | null>(null)\nconst registrations = shallowRef<ItemRegistration[]>([])\nconst domVersion = ref(0)\nconst pendingFocus = shallowRef<PendingFocus | null>(null)\n\nconst collection = computed(() => createQuestionnaireCollection(props.items))\nconst uncontrolledItem = ref<string | null>(\n  getInitialItemName(collection.value, props.defaultItem),\n)\nconst controlled = computed(() => props.item !== undefined)\nconst activeItemName = computed(() => (controlled.value ? props.item! : uncontrolledItem.value))\nconst shortcuts = computed(() => props.shortcuts ?? null)\nconst nativeValidation = computed(() => props.noValidate === false)\n\nlet previousActiveItemName = activeItemName.value\n\nconst runtimeItems = computed(() => {\n  // Re-sort whenever items are added to or removed from the DOM.\n  void domVersion.value\n\n  return registrations.value\n    .filter(registration => !registration.isDisabled())\n    .sort((first, second) => compareDocumentOrder(first.element, second.element))\n})\nconst runtimeItemByName = computed(\n  () => new Map(runtimeItems.value.map(runtimeItem => [runtimeItem.name, runtimeItem])),\n)\n/** `items` is authoritative when provided, so items can be declared before they render. */\nconst logicalItems = computed<readonly { name: string }[]>(\n  () => collection.value?.enabledItems ?? runtimeItems.value,\n)\nconst currentIndex = computed(\n  () => logicalItems.value.findIndex(logicalItem => logicalItem.name === activeItemName.value),\n)\nconst activeItem = computed(() => {\n  if (currentIndex.value < 0 || !activeItemName.value) {\n    return null\n  }\n\n  return runtimeItemByName.value.get(activeItemName.value) ?? null\n})\nconst activeDefinition = computed(() =>\n  activeItemName.value ? collection.value?.itemByName.get(activeItemName.value) : undefined,\n)\nconst activeItemRequired = computed(() => {\n  if (currentIndex.value < 0) {\n    return null\n  }\n\n  return activeDefinition.value\n    ? Boolean(activeDefinition.value.required)\n    : (activeItem.value?.isRequired() ?? false)\n})\nconst activeItemStatus = computed(() => {\n  if (currentIndex.value < 0) {\n    return null\n  }\n\n  return activeItem.value?.status() ?? (activeItemName.value ? \"unanswered\" : null)\n})\nconst orderedRegistrations = computed(() => {\n  if (!collection.value) {\n    return runtimeItems.value\n  }\n\n  return collection.value.enabledItems.flatMap((definition) => {\n    const registration = runtimeItemByName.value.get(definition.name)\n\n    return registration ? [registration] : []\n  })\n})\nconst total = computed(() => logicalItems.value.length)\nconst current = computed(() => (currentIndex.value < 0 ? 0 : currentIndex.value + 1))\nconst first = computed(() => total.value > 0 && currentIndex.value === 0)\nconst last = computed(() => total.value > 0 && currentIndex.value === total.value - 1)\n\nfunction setItem(nextItem: string, focusTarget: PendingFocus[\"target\"] = \"item\") {\n  if (nextItem === activeItemName.value) {\n    return\n  }\n\n  pendingFocus.value = { name: nextItem, target: focusTarget }\n\n  if (!controlled.value) {\n    uncontrolledItem.value = nextItem\n  }\n\n  emits(\"update:item\", nextItem)\n}\n\nfunction registerItem(registration: ItemRegistration) {\n  registrations.value = [\n    ...registrations.value.filter(\n      current => current.element !== registration.element && current.name !== registration.name,\n    ),\n    registration,\n  ]\n\n  return () => {\n    registrations.value = registrations.value.filter(current => current !== registration)\n  }\n}\n\nfunction setItemAt(index: number, focusTarget: PendingFocus[\"target\"] = \"item\") {\n  const nextItem = logicalItems.value[index]\n\n  if (nextItem) {\n    setItem(nextItem.name, focusTarget)\n  }\n}\n\nfunction goPrevious() {\n  if (currentIndex.value <= 0) {\n    return\n  }\n\n  setItemAt(currentIndex.value - 1)\n}\n\nfunction goNext() {\n  if (!activeItem.value || currentIndex.value >= total.value - 1) {\n    return\n  }\n\n  if (!activeItem.value.validate()) {\n    activeItem.value.focusInvalid()\n    return\n  }\n\n  setItemAt(currentIndex.value + 1)\n}\n\nfunction confirmCurrent() {\n  if (!activeItem.value) {\n    return\n  }\n\n  if (!activeItem.value.validate()) {\n    activeItem.value.focusInvalid()\n    return\n  }\n\n  if (last.value) {\n    rootElement.value?.requestSubmit()\n    return\n  }\n\n  setItemAt(currentIndex.value + 1)\n}\n\nfunction skipCurrent() {\n  if (!activeItem.value || activeItem.value.isRequired()) {\n    return\n  }\n\n  activeItem.value.skip()\n\n  if (!last.value) {\n    setItemAt(currentIndex.value + 1)\n    return\n  }\n\n  queueMicrotask(() => {\n    rootElement.value?.requestSubmit()\n  })\n}\n\nfunction handleReset(event: Event) {\n  emits(\"reset\", event)\n\n  if (event.defaultPrevented) {\n    return\n  }\n\n  for (const registration of registrations.value) {\n    registration.reset()\n  }\n\n  const resetItemName = collection.value\n    ? getInitialItemName(collection.value, props.defaultItem)\n    : (runtimeItems.value.find(registration => registration.name === props.defaultItem)?.name\n      ?? runtimeItems.value[0]?.name)\n\n  if (resetItemName) {\n    setItem(resetItemName)\n  }\n}\n\nfunction handleSubmit(event: Event) {\n  const firstInvalidItem = orderedRegistrations.value.find(registration => !registration.validate())\n\n  if (firstInvalidItem) {\n    event.preventDefault()\n    setItem(firstInvalidItem.name, \"invalid\")\n\n    if (firstInvalidItem.name === activeItemName.value) {\n      firstInvalidItem.focusInvalid()\n      pendingFocus.value = null\n    }\n\n    return\n  }\n\n  emits(\"submit\", event)\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n  if (\n    event.defaultPrevented\n    || event.isComposing\n    || event.keyCode === 229\n    || !activeItem.value\n    || !(event.target instanceof Element)\n  ) {\n    return\n  }\n\n  if (event.key === \"Enter\" && (event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey) {\n    event.preventDefault()\n\n    if (!event.repeat) {\n      confirmCurrent()\n    }\n\n    return\n  }\n\n  if (event.metaKey || event.ctrlKey || event.altKey) {\n    return\n  }\n\n  if (event.key === \"ArrowUp\" || event.key === \"ArrowDown\") {\n    const moved = activeItem.value.moveAnswerFocus(\n      event.target,\n      event.key === \"ArrowDown\" ? \"next\" : \"previous\",\n    )\n\n    if (moved) {\n      event.preventDefault()\n      return\n    }\n  }\n\n  if (\n    (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\")\n    && !isTextEntryTarget(event.target)\n    && !isRadioTarget(event.target)\n  ) {\n    event.preventDefault()\n\n    if (event.repeat) {\n      return\n    }\n\n    if (event.key === \"ArrowLeft\") {\n      goPrevious()\n    }\n    else if (activeItem.value.status() !== \"unanswered\") {\n      goNext()\n    }\n\n    return\n  }\n\n  if (event.key === \"Enter\") {\n    const answer = activeItem.value.getAnswerByElement(event.target)\n\n    if (!answer) {\n      return\n    }\n\n    event.preventDefault()\n\n    if (!event.repeat && isAnswerFilled(answer)) {\n      confirmCurrent()\n    }\n\n    return\n  }\n\n  if (!shortcuts.value || isTextEntryTarget(event.target)) {\n    return\n  }\n\n  const shortcut = getShortcutFromKey(event.key, shortcuts.value)\n  const answer = shortcut ? activeItem.value.getAnswerByShortcut(shortcut) : null\n\n  if (!answer) {\n    return\n  }\n\n  event.preventDefault()\n\n  if (event.repeat) {\n    return\n  }\n\n  answer.element.focus()\n\n  if (answer.type === \"choice\") {\n    answer.element.click()\n  }\n}\n\nwatch(\n  () => [activeItemName.value, currentIndex.value, total.value, activeItem.value] as const,\n  () => {\n    if (total.value === 0) {\n      return\n    }\n\n    if (currentIndex.value < 0) {\n      const firstItem = logicalItems.value[0]\n\n      if (!firstItem) {\n        return\n      }\n\n      if (!controlled.value && activeItemName.value === null) {\n        uncontrolledItem.value = firstItem.name\n        return\n      }\n\n      setItem(firstItem.name)\n      return\n    }\n\n    const focus = pendingFocus.value\n    const activeItemChanged = previousActiveItemName !== activeItemName.value\n\n    previousActiveItemName = activeItemName.value\n\n    if (!focus || focus.name !== activeItemName.value) {\n      if (controlled.value && activeItemChanged) {\n        pendingFocus.value = null\n        activeItem.value?.focus()\n      }\n\n      return\n    }\n\n    if (focus.target === \"invalid\") {\n      activeItem.value?.focusInvalid()\n    }\n    else {\n      activeItem.value?.focus()\n    }\n\n    pendingFocus.value = null\n  },\n  { flush: \"post\", immediate: true },\n)\n\nlet observer: MutationObserver | null = null\n\nonMounted(() => {\n  if (!rootElement.value || typeof MutationObserver === \"undefined\") {\n    return\n  }\n\n  observer = new MutationObserver(() => {\n    domVersion.value += 1\n  })\n\n  observer.observe(rootElement.value, { childList: true, subtree: true })\n})\n\nonBeforeUnmount(() => {\n  observer?.disconnect()\n  observer = null\n})\n\nprovideQuestionnaireRootContext({\n  activeItem,\n  activeItemName,\n  activeItemRequired,\n  activeItemStatus,\n  current,\n  domVersion,\n  first,\n  goNext,\n  goPrevious,\n  itemDefinitionByName: computed(() => collection.value?.itemByName ?? null),\n  last,\n  nativeValidation,\n  registerItem,\n  shortcuts,\n  skipCurrent,\n  total,\n})\n</script>\n\n<template>\n  <form\n    ref=\"rootElement\"\n    data-slot=\"questionnaire\"\n    :data-shortcuts=\"shortcuts ?? undefined\"\n    :novalidate=\"props.noValidate\"\n    :class=\"cn('cn-questionnaire flex w-full min-w-0 flex-col', props.class)\"\n    @keydown=\"handleKeydown\"\n    @reset=\"handleReset\"\n    @submit=\"handleSubmit\"\n  >\n    <slot :current=\"current\" :first=\"first\" :last=\"last\" :total=\"total\" />\n  </form>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireActions.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  as: \"div\",\n})\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"questionnaire-actions\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :class=\"cn(\n      'cn-questionnaire-actions grid min-h-11 w-full grid-cols-[minmax(0,1fr)_auto_auto] items-center',\n      props.class,\n    )\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireChoice.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { computed, onBeforeUnmount, ref, useId, watch } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport IconPlaceholder from \"@/components/docs/icon-placeholder/IconPlaceholder.vue\"\nimport { getAnswerKeyShortcuts, injectQuestionnaireItemContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<{\n  /** Controlled checked state. Use with `v-model:checked`. */\n  checked?: boolean\n  class?: HTMLAttributes[\"class\"]\n  /** Checks the choice on mount and after a native form reset. */\n  defaultChecked?: boolean\n  disabled?: boolean\n  /** Submitted as the answer of the parent item. */\n  value: string\n}>(), {\n  // `undefined` keeps the choice uncontrolled. Without this default, Vue casts\n  // the absent boolean prop to `false` and every choice looks controlled.\n  checked: undefined,\n  defaultChecked: false,\n  disabled: false,\n})\n\nconst emits = defineEmits<{\n  \"change\": [event: Event]\n  \"update:checked\": [checked: boolean]\n}>()\n\nconst item = injectQuestionnaireItemContext()\n\nconst answerId = useId()\nconst inputElement = ref<HTMLInputElement | null>(null)\nconst initialDefaultChecked = props.defaultChecked\n\nconst controlled = computed(() => props.checked !== undefined)\nconst disabled = computed(() => item.disabled.value || props.disabled)\nconst selected = computed(() => item.selectedAnswerIds.value.includes(answerId))\nconst checked = computed(() => {\n  if (!controlled.value) {\n    return selected.value\n  }\n\n  // A skipped item clears every answer, including controlled ones.\n  return item.status.value === \"skipped\" ? false : props.checked!\n})\nconst type = computed(() => (item.multiple.value ? \"checkbox\" : \"radio\"))\nconst shortcut = computed(() =>\n  item.shortcutByChoiceValue.value?.get(props.value)\n  ?? item.shortcutByAnswerId.value.get(answerId)\n  ?? null)\n\nfunction syncCheckedElement() {\n  if (inputElement.value && inputElement.value.checked !== checked.value) {\n    inputElement.value.checked = checked.value\n  }\n}\n\nfunction handleChange(event: Event) {\n  emits(\"change\", event)\n\n  if (event.defaultPrevented) {\n    syncCheckedElement()\n    return\n  }\n\n  const nextChecked = (event.target as HTMLInputElement).checked\n\n  emits(\"update:checked\", nextChecked)\n\n  if (!controlled.value) {\n    item.setAnswerSelectionFromInteraction(answerId, nextChecked)\n    return\n  }\n\n  // Re-selecting the same controlled choice has to clear the skipped state.\n  if (item.status.value === \"skipped\" && props.checked === nextChecked) {\n    item.setAnswerSelectionFromInteraction(answerId, props.checked)\n  }\n\n  // Checking a radio clears its siblings, so the whole group has to re-sync in\n  // case the host keeps the previous answer.\n  item.requestControlSync()\n}\n\nconst unregisterSelection = item.registerAnswerSelection(answerId, initialDefaultChecked)\n\nlet unregisterControl: (() => void) | null = null\n\nwatch([inputElement, disabled, () => props.disabled, () => props.value], ([element]) => {\n  unregisterControl?.()\n  unregisterControl = null\n\n  if (!element) {\n    return\n  }\n\n  unregisterControl = item.registerAnswerControl({\n    disabled: disabled.value,\n    element,\n    id: answerId,\n    ownDisabled: props.disabled,\n    type: \"choice\",\n    value: props.value,\n  })\n}, { flush: \"post\" })\n\nwatch(() => props.defaultChecked, (defaultChecked) => {\n  item.setAnswerDefault(answerId, defaultChecked)\n})\n\nwatch([() => props.checked, item.resetVersion], () => {\n  if (controlled.value) {\n    item.syncControlledAnswerSelection(answerId, props.checked!)\n  }\n}, { immediate: true })\n\nwatch(item.controlSyncVersion, syncCheckedElement, { flush: \"post\" })\n\nwatch([checked, inputElement, () => props.defaultChecked, item.resetVersion], () => {\n  if (!inputElement.value) {\n    return\n  }\n\n  // Keep the native reset target aligned with the questionnaire owned default,\n  // including controlled choices whose `checked` prop stays authoritative.\n  inputElement.value.defaultChecked = controlled.value ? props.checked! : props.defaultChecked\n\n  syncCheckedElement()\n}, { flush: \"post\" })\n\nonBeforeUnmount(() => {\n  unregisterControl?.()\n  unregisterControl = null\n  unregisterSelection()\n})\n</script>\n\n<template>\n  <label\n    data-slot=\"questionnaire-choice\"\n    :data-checked=\"checked ? '' : undefined\"\n    :data-disabled=\"disabled ? '' : undefined\"\n    :data-invalid=\"item.invalid.value ? '' : undefined\"\n    :data-shortcut=\"shortcut ?? undefined\"\n    :data-type=\"type\"\n    :data-unchecked=\"checked ? undefined : ''\"\n    :class=\"cn(\n      'cn-questionnaire-choice group/questionnaire-choice relative flex min-h-11 cursor-pointer items-start text-start transition-colors outline-none select-none',\n      'data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50',\n      props.class,\n    )\"\n  >\n    <input\n      :id=\"answerId\"\n      ref=\"inputElement\"\n      data-slot=\"questionnaire-choice-input\"\n      class=\"cn-questionnaire-choice-input absolute inset-0 z-10 size-full cursor-pointer opacity-0\"\n      :aria-invalid=\"item.invalid.value || undefined\"\n      :aria-keyshortcuts=\"getAnswerKeyShortcuts(shortcut, !disabled && checked)\"\n      :checked=\"checked\"\n      :data-checked=\"checked ? '' : undefined\"\n      :data-unchecked=\"checked ? undefined : ''\"\n      :disabled=\"disabled\"\n      :name=\"item.status.value === 'skipped' ? undefined : item.name.value\"\n      :required=\"item.required.value && !item.multiple.value && !item.hasInputAnswer.value\"\n      :type=\"type\"\n      :value=\"props.value\"\n      @change=\"handleChange\"\n    >\n    <span\n      aria-hidden=\"true\"\n      data-slot=\"questionnaire-choice-indicator\"\n      class=\"cn-questionnaire-choice-indicator pointer-events-none relative flex shrink-0 items-center justify-center border group-data-[type=radio]/questionnaire-choice:rounded-full\"\n    >\n      <span\n        data-slot=\"questionnaire-choice-indicator-dot\"\n        class=\"cn-questionnaire-choice-indicator-dot hidden rounded-full group-data-[type=checkbox]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block\"\n      />\n      <IconPlaceholder\n        data-slot=\"questionnaire-choice-indicator-check\"\n        class=\"cn-questionnaire-choice-indicator-check hidden group-data-[type=radio]/questionnaire-choice:hidden group-data-checked/questionnaire-choice:block\"\n        lucide=\"CheckIcon\"\n        tabler=\"IconCheck\"\n        hugeicons=\"Tick02Icon\"\n        phosphor=\"CheckIcon\"\n        remixicon=\"RiCheckLine\"\n      />\n    </span>\n    <span\n      data-slot=\"questionnaire-choice-label\"\n      class=\"cn-questionnaire-choice-label cn-questionnaire-choice-content flex min-w-0 flex-1 flex-col leading-snug\"\n    >\n      <slot :checked=\"checked\" :disabled=\"disabled\" :shortcut=\"shortcut\" :type=\"type\" />\n    </span>\n    <span\n      v-if=\"shortcut\"\n      aria-hidden=\"true\"\n      data-slot=\"questionnaire-choice-shortcut\"\n      class=\"cn-questionnaire-choice-shortcut cn-questionnaire-shortcut pointer-events-none ms-auto hidden shrink-0 group-data-[shortcut]/questionnaire-choice:inline-flex\"\n    >\n      {{ shortcut }}\n    </span>\n  </label>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireChoiceDescription.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport { cn } from \"@/lib/utils\"\n\nconst props = defineProps<{\n  class?: HTMLAttributes[\"class\"]\n}>()\n</script>\n\n<template>\n  <span\n    data-slot=\"questionnaire-choice-description\"\n    :class=\"cn('cn-questionnaire-choice-description', props.class)\"\n  >\n    <slot />\n  </span>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireChoices.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { cn } from \"@/lib/utils\"\nimport { injectQuestionnaireItemContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  as: \"div\",\n})\n\nconst item = injectQuestionnaireItemContext()\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"questionnaire-choices\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :data-shortcuts=\"item.shortcuts.value ?? undefined\"\n    :class=\"cn('cn-questionnaire-choices group/questionnaire-choices grid min-w-0', props.class)\"\n  >\n    <slot :shortcuts=\"item.shortcuts.value\" />\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireDescription.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { ComponentPublicInstance, HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { onBeforeUnmount, onMounted, ref, useId } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { injectQuestionnaireItemContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  id?: string\n}>(), {\n  as: \"p\",\n})\n\nconst item = injectQuestionnaireItemContext()\n\nconst primitiveRef = ref<ComponentPublicInstance | null>(null)\nconst fallbackId = props.id ?? useId()\nconst descriptionId = ref(fallbackId)\n\nlet unregisterDescription = item.registerDescription(descriptionId.value)\n\nonMounted(() => {\n  // With `as-child` the rendered child can bring its own id, for example a\n  // DialogDescription. Adopt it so both descriptions point at one element.\n  const element = primitiveRef.value?.$el as HTMLElement | undefined\n  const renderedId = element?.id\n\n  if (!renderedId) {\n    if (element) {\n      element.id = fallbackId\n    }\n\n    return\n  }\n\n  if (renderedId !== descriptionId.value) {\n    unregisterDescription()\n    descriptionId.value = renderedId\n    unregisterDescription = item.registerDescription(renderedId)\n  }\n})\n\nonBeforeUnmount(() => unregisterDescription())\n</script>\n\n<template>\n  <Primitive\n    v-bind=\"props.asChild ? {} : { id: descriptionId }\"\n    ref=\"primitiveRef\"\n    data-slot=\"questionnaire-description\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :class=\"cn('cn-questionnaire-description text-pretty text-muted-foreground', props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireError.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { computed, onBeforeUnmount, useId } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { injectQuestionnaireItemContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  id?: string\n}>(), {\n  as: \"p\",\n})\n\nconst item = injectQuestionnaireItemContext()\n\nconst errorId = props.id ?? useId()\nconst unregisterError = item.registerError(errorId)\n\nconst fallback = computed(() =>\n  item.required.value\n    ? \"Choose an answer to continue.\"\n    : \"Choose an answer or skip this question.\")\n\nonBeforeUnmount(unregisterError)\n</script>\n\n<template>\n  <Primitive\n    :id=\"errorId\"\n    data-slot=\"questionnaire-error\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :data-invalid=\"item.invalid.value ? '' : undefined\"\n    :hidden=\"!item.invalid.value\"\n    :role=\"item.invalid.value ? 'alert' : undefined\"\n    :class=\"cn('cn-questionnaire-error text-destructive', props.class)\"\n  >\n    <slot :invalid=\"item.invalid.value\">\n      {{ fallback }}\n    </slot>\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireInput.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { QuestionnaireInputType } from \"./useQuestionnaire\"\nimport { computed, nextTick, onBeforeUnmount, ref, useId, watch } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport {\n  getAnswerKeyShortcuts,\n  hasInputValue,\n  injectQuestionnaireItemContext,\n} from \"./useQuestionnaire\"\n\ndefineOptions({\n  // The wrapper is the root element, so attributes have to reach the input.\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  /** Fills the answer on mount and after a native form reset. */\n  defaultValue?: string | number\n  disabled?: boolean\n  /** Controlled value. Use with `v-model`. */\n  modelValue?: string | number\n  type?: QuestionnaireInputType\n}>(), {\n  disabled: false,\n  type: \"text\",\n})\n\nconst emits = defineEmits<{\n  \"update:modelValue\": [value: string]\n}>()\n\nconst item = injectQuestionnaireItemContext()\n\nconst answerId = useId()\nconst inputElement = ref<HTMLInputElement | null>(null)\nconst initialDefaultFilled = hasInputValue(props.defaultValue)\nconst uncontrolledValue = ref(String(props.defaultValue ?? \"\"))\n\nconst controlled = computed(() => props.modelValue !== undefined)\nconst defaultFilled = computed(() => hasInputValue(props.defaultValue))\nconst disabled = computed(() => item.disabled.value || props.disabled)\n// Vue re-applies `value` on every render, so the input always renders the value\n// the questionnaire owns instead of an undefined binding that would clear it.\nconst value = computed(() =>\n  controlled.value ? String(props.modelValue ?? \"\") : uncontrolledValue.value)\nconst filled = computed(() => hasInputValue(value.value))\nconst selected = computed(() => item.selectedAnswerIds.value.includes(answerId))\n\nfunction syncValueElement() {\n  if (inputElement.value && inputElement.value.value !== value.value) {\n    inputElement.value.value = value.value\n  }\n}\n\nfunction handleInput(event: Event) {\n  const nextValue = (event.target as HTMLInputElement).value\n\n  emits(\"update:modelValue\", nextValue)\n\n  if (controlled.value) {\n    // The host owns the value, so restore whatever it kept.\n    nextTick(syncValueElement)\n    return\n  }\n\n  uncontrolledValue.value = nextValue\n  item.setAnswerSelectionFromInteraction(answerId, hasInputValue(nextValue))\n}\n\nconst unregisterSelection = item.registerAnswerSelection(answerId, initialDefaultFilled)\n\nlet unregisterControl: (() => void) | null = null\n\nwatch([inputElement, disabled, () => props.disabled], ([element]) => {\n  unregisterControl?.()\n  unregisterControl = null\n\n  if (!element) {\n    return\n  }\n\n  unregisterControl = item.registerAnswerControl({\n    disabled: disabled.value,\n    element,\n    id: answerId,\n    ownDisabled: props.disabled,\n    type: \"input\",\n    value: \"\",\n  })\n}, { flush: \"post\" })\n\nwatch(defaultFilled, (nextDefaultFilled) => {\n  item.setAnswerDefault(answerId, nextDefaultFilled)\n})\n\n// Watching the value as well as `filled` lets a host update clear the skipped\n// state even when the answer stays filled.\nwatch([value, filled], () => {\n  if (controlled.value) {\n    item.syncControlledAnswerSelection(answerId, filled.value)\n  }\n}, { immediate: true })\n\nwatch(item.resetVersion, () => {\n  if (!controlled.value) {\n    uncontrolledValue.value = String(props.defaultValue ?? \"\")\n  }\n})\n\nwatch([value, inputElement], () => {\n  if (!inputElement.value) {\n    return\n  }\n\n  // A native form reset restores `defaultValue`, so keep it in sync with the\n  // value the questionnaire owns.\n  inputElement.value.defaultValue = String(\n    (controlled.value ? props.modelValue : props.defaultValue) ?? \"\",\n  )\n}, { flush: \"post\" })\n\nonBeforeUnmount(() => {\n  unregisterControl?.()\n  unregisterControl = null\n  unregisterSelection()\n})\n</script>\n\n<template>\n  <div\n    data-slot=\"questionnaire-input-wrapper\"\n    class=\"cn-questionnaire-input-wrapper group/questionnaire-input relative min-w-0\"\n  >\n    <input\n      v-bind=\"$attrs\"\n      :id=\"answerId\"\n      ref=\"inputElement\"\n      data-slot=\"questionnaire-input\"\n      :aria-invalid=\"item.invalid.value || undefined\"\n      :aria-keyshortcuts=\"getAnswerKeyShortcuts(null, !disabled && filled && selected)\"\n      :data-disabled=\"disabled ? '' : undefined\"\n      :data-empty=\"filled ? undefined : ''\"\n      :data-filled=\"filled ? '' : undefined\"\n      :data-invalid=\"item.invalid.value ? '' : undefined\"\n      :disabled=\"disabled\"\n      :form=\"selected ? undefined : ''\"\n      :name=\"selected ? item.name.value : undefined\"\n      :type=\"props.type\"\n      :value=\"value\"\n      :class=\"cn(\n        'cn-questionnaire-input min-h-11 w-full min-w-0 transition-[color,box-shadow,background-color] outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 sm:min-h-0',\n        'selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground',\n        props.class,\n      )\"\n      @input=\"handleInput\"\n    >\n  </div>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireItem.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from \"vue\"\nimport type { AnswerControlRegistration, QuestionnaireItemStatus } from \"./useQuestionnaire\"\nimport { computed, onBeforeUnmount, ref, shallowRef, useAttrs, watch } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport {\n  compareDocumentOrder,\n  getShortcutByChoiceValue,\n  getShortcutKeys,\n  injectQuestionnaireRootContext,\n  isAnswerFilled,\n  isEmptyNavigableInput,\n  isRadioTarget,\n  isTextEntryTarget,\n  provideQuestionnaireItemContext,\n} from \"./useQuestionnaire\"\n\ndefineOptions({\n  inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<{\n  class?: HTMLAttributes[\"class\"]\n  /** Excludes the item from the questionnaire without unmounting it. */\n  disabled?: boolean\n  /** Marks the item invalid from outside, for example after schema validation. */\n  invalid?: boolean\n  /** Renders choices as checkboxes and keeps every selected answer. */\n  multiple?: boolean\n  /** Submitted under this name, and used to activate the item. */\n  name: string\n  /** Requires an answer before the questionnaire can continue. */\n  required?: boolean\n}>(), {\n  disabled: false,\n  invalid: false,\n  multiple: false,\n  required: false,\n})\n\nconst emits = defineEmits<{\n  \"update:status\": [status: QuestionnaireItemStatus]\n}>()\n\nconst attrs = useAttrs()\nconst root = injectQuestionnaireRootContext()\n\nconst itemElement = ref<HTMLFieldSetElement | null>(null)\nconst answerControls = shallowRef<AnswerControlRegistration[]>([])\nconst selectedAnswerIds = ref<string[]>([])\nconst validationAttempted = ref(false)\nconst skipped = ref(false)\nconst resetVersion = ref(0)\nconst controlSyncVersion = ref(0)\nconst descriptionIds = ref<string[]>([])\nconst errorIds = ref<string[]>([])\nconst titleIds = ref<string[]>([])\n\nlet defaultSelectedAnswerIds: string[] = []\n\nconst active = computed(() => !props.disabled && root.activeItemName.value === props.name)\nconst orderedAnswerControls = computed(() => {\n  // Re-sort whenever answers are added to or removed from the DOM.\n  void root.domVersion.value\n\n  return [...answerControls.value].sort((first, second) =>\n    compareDocumentOrder(first.element, second.element))\n})\nconst answers = computed(() => orderedAnswerControls.value.filter(answer => !answer.disabled))\nconst answered = computed(() =>\n  answers.value.some(answer => selectedAnswerIds.value.includes(answer.id)))\nconst status = computed<QuestionnaireItemStatus>(() => {\n  if (skipped.value) {\n    return \"skipped\"\n  }\n\n  return answered.value ? \"answered\" : \"unanswered\"\n})\nconst intentionallySkipped = computed(() => status.value === \"skipped\" && !props.required)\nconst valid = computed(() =>\n  props.disabled\n  || intentionallySkipped.value\n  || (!props.invalid && status.value === \"answered\"))\nconst invalid = computed(() =>\n  !props.disabled\n  && !intentionallySkipped.value\n  && (props.invalid || (validationAttempted.value && !valid.value)))\nconst hasInputAnswer = computed(() => answers.value.some(answer => answer.type === \"input\"))\nconst itemDefinition = computed(() => root.itemDefinitionByName.value?.get(props.name))\nconst shortcutByChoiceValue = computed(() =>\n  root.itemDefinitionByName.value\n    ? getShortcutByChoiceValue(itemDefinition.value, root.shortcuts.value)\n    : null)\nconst shortcutByAnswerId = computed(() => {\n  // Choice values from `items` take precedence, so shortcuts stay stable.\n  if (shortcutByChoiceValue.value) {\n    return new Map<string, string>()\n  }\n\n  const keys = getShortcutKeys(root.shortcuts.value)\n  const shortcutAnswers = answers.value.filter(answer => answer.type === \"choice\")\n\n  return new Map(\n    shortcutAnswers\n      .slice(0, keys.length)\n      .flatMap((answer, index) => (keys[index] ? [[answer.id, keys[index]!] as const] : [])),\n  )\n})\n// Only set when the title does not render as the legend, which already names\n// the fieldset on its own.\nconst labelledBy = computed(() =>\n  [...titleIds.value, attrs[\"aria-labelledby\"]].filter(Boolean).join(\" \") || undefined)\nconst describedBy = computed(() =>\n  [...descriptionIds.value, ...(invalid.value ? errorIds.value : []), attrs[\"aria-describedby\"]]\n    .filter(Boolean)\n    .join(\" \") || undefined)\nconst keyShortcuts = computed(() =>\n  [\n    attrs[\"aria-keyshortcuts\"],\n    active.value ? \"Meta+Enter Control+Enter\" : undefined,\n    active.value && answers.value.length ? \"ArrowUp ArrowDown\" : undefined,\n    active.value && !root.first.value ? \"ArrowLeft\" : undefined,\n    active.value && !root.last.value && status.value !== \"unanswered\" ? \"ArrowRight\" : undefined,\n  ]\n    .filter(Boolean)\n    .join(\" \") || undefined)\n\nfunction updateAnswerSelected(answerId: string, selected: boolean) {\n  if (!selected) {\n    selectedAnswerIds.value = selectedAnswerIds.value.filter(current => current !== answerId)\n    return\n  }\n\n  if (!props.multiple) {\n    selectedAnswerIds.value = [answerId]\n    return\n  }\n\n  if (!selectedAnswerIds.value.includes(answerId)) {\n    selectedAnswerIds.value = [...selectedAnswerIds.value, answerId]\n  }\n}\n\nfunction setAnswerSelectionFromInteraction(answerId: string, selected: boolean) {\n  skipped.value = false\n  updateAnswerSelected(answerId, selected)\n}\n\nfunction syncControlledAnswerSelection(answerId: string, selected: boolean) {\n  if (selected) {\n    skipped.value = false\n  }\n\n  updateAnswerSelected(answerId, selected)\n}\n\nfunction registerAnswerSelection(answerId: string, defaultSelected: boolean) {\n  if (defaultSelected) {\n    defaultSelectedAnswerIds = [\n      ...defaultSelectedAnswerIds.filter(current => current !== answerId),\n      answerId,\n    ]\n\n    if (!props.multiple) {\n      if (!selectedAnswerIds.value.length) {\n        selectedAnswerIds.value = [answerId]\n      }\n    }\n    else if (!selectedAnswerIds.value.includes(answerId)) {\n      selectedAnswerIds.value = [...selectedAnswerIds.value, answerId]\n    }\n  }\n\n  return () => {\n    defaultSelectedAnswerIds = defaultSelectedAnswerIds.filter(current => current !== answerId)\n    selectedAnswerIds.value = selectedAnswerIds.value.filter(current => current !== answerId)\n  }\n}\n\nfunction setAnswerDefault(answerId: string, defaultSelected: boolean) {\n  if (defaultSelected) {\n    if (!defaultSelectedAnswerIds.includes(answerId)) {\n      defaultSelectedAnswerIds = [...defaultSelectedAnswerIds, answerId]\n    }\n\n    return\n  }\n\n  defaultSelectedAnswerIds = defaultSelectedAnswerIds.filter(current => current !== answerId)\n}\n\n/**\n * Selecting a controlled choice clears the native checked state of the other\n * choices in the group, so every control re-syncs after an interaction the host\n * may have rejected.\n */\nfunction requestControlSync() {\n  controlSyncVersion.value += 1\n}\n\nfunction registerAnswerControl(registration: AnswerControlRegistration) {\n  answerControls.value = [\n    ...answerControls.value.filter(\n      current => current.element !== registration.element && current.id !== registration.id,\n    ),\n    registration,\n  ]\n\n  return () => {\n    answerControls.value = answerControls.value.filter(current => current !== registration)\n  }\n}\n\nfunction registerDescription(descriptionId: string) {\n  if (!descriptionIds.value.includes(descriptionId)) {\n    descriptionIds.value = [...descriptionIds.value, descriptionId]\n  }\n\n  return () => {\n    descriptionIds.value = descriptionIds.value.filter(current => current !== descriptionId)\n  }\n}\n\nfunction registerTitle(titleId: string) {\n  if (!titleIds.value.includes(titleId)) {\n    titleIds.value = [...titleIds.value, titleId]\n  }\n\n  return () => {\n    titleIds.value = titleIds.value.filter(current => current !== titleId)\n  }\n}\n\nfunction registerError(errorId: string) {\n  if (!errorIds.value.includes(errorId)) {\n    errorIds.value = [...errorIds.value, errorId]\n  }\n\n  return () => {\n    errorIds.value = errorIds.value.filter(current => current !== errorId)\n  }\n}\n\nfunction validate() {\n  validationAttempted.value = true\n\n  if (!valid.value) {\n    return false\n  }\n\n  if (!root.nativeValidation.value) {\n    return true\n  }\n\n  const invalidAnswer = answers.value.find(\n    answer =>\n      isAnswerFilled(answer) && answer.element.willValidate && !answer.element.validity.valid,\n  )\n\n  if (!invalidAnswer) {\n    return true\n  }\n\n  invalidAnswer.element.focus()\n  invalidAnswer.element.reportValidity()\n\n  return false\n}\n\nfunction focus() {\n  itemElement.value?.focus()\n}\n\nfunction focusInvalid() {\n  const selectedInput = itemElement.value?.querySelector<HTMLInputElement>(\n    \"input[data-filled][name]:not(:disabled)\",\n  )\n  const firstControl = itemElement.value?.querySelector<HTMLElement>(\n    \"input:not([type=hidden]):not(:disabled), textarea:not(:disabled)\",\n  )\n\n  ;(selectedInput ?? firstControl ?? itemElement.value)?.focus()\n}\n\nfunction reset() {\n  validationAttempted.value = false\n  skipped.value = false\n  selectedAnswerIds.value = props.multiple\n    ? [...defaultSelectedAnswerIds]\n    : defaultSelectedAnswerIds.slice(0, 1)\n  resetVersion.value += 1\n}\n\nfunction skip() {\n  if (props.required) {\n    return\n  }\n\n  selectedAnswerIds.value = []\n  skipped.value = true\n}\n\nfunction getAnswerByElement(element: Element) {\n  return answers.value.find(answer => answer.element === element) ?? null\n}\n\nfunction getAnswerByShortcut(shortcut: string) {\n  if (shortcutByChoiceValue.value) {\n    const choiceValue = Array.from(shortcutByChoiceValue.value.entries()).find(\n      ([, choiceShortcut]) => choiceShortcut === shortcut,\n    )?.[0]\n\n    return (\n      answers.value.find(answer => answer.type === \"choice\" && answer.value === choiceValue) ?? null\n    )\n  }\n\n  const answerId = Array.from(shortcutByAnswerId.value.entries()).find(\n    ([, answerShortcut]) => answerShortcut === shortcut,\n  )?.[0]\n\n  return answers.value.find(answer => answer.id === answerId) ?? null\n}\n\nfunction moveAnswerFocus(currentElement: Element, direction: \"next\" | \"previous\") {\n  const currentIndex = answers.value.findIndex(answer => answer.element === currentElement)\n  const currentAnswer = currentIndex < 0 ? null : (answers.value[currentIndex] ?? null)\n\n  if (\n    !answers.value.length\n    || (isTextEntryTarget(currentElement) && !isEmptyNavigableInput(currentAnswer))\n    || (currentIndex < 0 && currentElement !== itemElement.value)\n  ) {\n    return false\n  }\n\n  const nextAnswer\n    = currentIndex < 0\n      ? (answers.value.find(isAnswerFilled)\n        ?? (direction === \"next\" ? answers.value[0] : answers.value[answers.value.length - 1]))\n      : answers.value[\n        (currentIndex + (direction === \"next\" ? 1 : -1) + answers.value.length)\n        % answers.value.length\n      ]\n\n  if (!nextAnswer || nextAnswer.element === currentElement) {\n    return false\n  }\n\n  // Radio groups already move focus with the arrow keys.\n  if (\n    currentIndex >= 0\n    && isRadioTarget(currentElement)\n    && isRadioTarget(nextAnswer.element)\n  ) {\n    return false\n  }\n\n  nextAnswer.element.focus()\n\n  if (nextAnswer.type === \"choice\" && isRadioTarget(nextAnswer.element)) {\n    nextAnswer.element.click()\n  }\n\n  return true\n}\n\nwatch(status, (nextStatus) => {\n  emits(\"update:status\", nextStatus)\n})\n\nwatch(() => props.multiple, (multiple, wasMultiple) => {\n  if (!wasMultiple || multiple) {\n    return\n  }\n\n  const selectedAnswer = answers.value.find(answer => selectedAnswerIds.value.includes(answer.id))\n\n  selectedAnswerIds.value = selectedAnswer ? [selectedAnswer.id] : []\n})\n\nlet unregisterItem: (() => void) | null = null\n\nwatch([itemElement, () => props.name], ([element, name]) => {\n  unregisterItem?.()\n  unregisterItem = null\n\n  if (!element) {\n    return\n  }\n\n  unregisterItem = root.registerItem({\n    element,\n    focus,\n    focusInvalid,\n    getAnswerByElement,\n    getAnswerByShortcut,\n    getChoices: () =>\n      orderedAnswerControls.value.flatMap(answer =>\n        answer.type === \"choice\" ? [{ disabled: answer.ownDisabled, value: answer.value }] : []),\n    isDisabled: () => props.disabled,\n    isRequired: () => props.required,\n    moveAnswerFocus,\n    name,\n    reset,\n    skip,\n    status: () => status.value,\n    validate,\n  })\n}, { flush: \"post\" })\n\nonBeforeUnmount(() => {\n  unregisterItem?.()\n  unregisterItem = null\n})\n\nprovideQuestionnaireItemContext({\n  active,\n  controlSyncVersion,\n  disabled: computed(() => props.disabled),\n  hasInputAnswer,\n  invalid,\n  multiple: computed(() => props.multiple),\n  name: computed(() => props.name),\n  registerAnswerControl,\n  registerAnswerSelection,\n  registerDescription,\n  registerError,\n  registerTitle,\n  requestControlSync,\n  required: computed(() => props.required),\n  resetVersion,\n  selectedAnswerIds,\n  setAnswerDefault,\n  setAnswerSelectionFromInteraction,\n  shortcutByAnswerId,\n  shortcutByChoiceValue,\n  shortcuts: root.shortcuts,\n  status,\n  syncControlledAnswerSelection,\n})\n</script>\n\n<template>\n  <fieldset\n    v-bind=\"attrs\"\n    ref=\"itemElement\"\n    data-slot=\"questionnaire-item\"\n    :aria-describedby=\"describedBy\"\n    :aria-invalid=\"invalid || undefined\"\n    :aria-keyshortcuts=\"keyShortcuts\"\n    :aria-labelledby=\"labelledBy\"\n    :data-active=\"active ? '' : undefined\"\n    :data-disabled=\"props.disabled ? '' : undefined\"\n    :data-invalid=\"invalid ? '' : undefined\"\n    :data-multiple=\"props.multiple ? '' : undefined\"\n    :data-required=\"props.required ? '' : undefined\"\n    :data-status=\"status\"\n    :disabled=\"props.disabled\"\n    :hidden=\"!active\"\n    :inert=\"!active\"\n    tabindex=\"-1\"\n    :class=\"cn('cn-questionnaire-item min-w-0 border-0 p-0 outline-none', props.class)\"\n  >\n    <slot :active=\"active\" :invalid=\"invalid\" :status=\"status\" />\n  </fieldset>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireNext.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from \"@/components/ui/button\"\nimport { Primitive } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { injectQuestionnaireRootContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  disabled?: boolean\n  size?: ButtonVariants[\"size\"]\n  variant?: ButtonVariants[\"variant\"]\n}>(), {\n  as: \"button\",\n  disabled: false,\n  size: \"default\",\n  variant: \"default\",\n})\n\nconst emits = defineEmits<{\n  click: [event: MouseEvent]\n}>()\n\nconst root = injectQuestionnaireRootContext()\n\nconst visible = computed(() => root.total.value > 1 && !root.last.value)\nconst shortcut = computed(() => (visible.value && !props.disabled ? \"Enter\" : null))\n\nfunction handleClick(event: MouseEvent) {\n  emits(\"click\", event)\n\n  // `disabled` does not block clicks once `as` or `as-child` renders something\n  // other than a button.\n  if (props.disabled) {\n    event.preventDefault()\n    return\n  }\n\n  if (!event.defaultPrevented) {\n    root.goNext()\n  }\n}\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"questionnaire-next\"\n    type=\"button\"\n    :aria-hidden=\"!visible || undefined\"\n    :aria-disabled=\"props.disabled || undefined\"\n    :aria-keyshortcuts=\"shortcut ?? undefined\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :data-disabled=\"props.disabled ? '' : undefined\"\n    :data-hidden=\"visible ? undefined : ''\"\n    :data-shortcut=\"shortcut ?? undefined\"\n    :data-size=\"props.size\"\n    :data-status=\"root.activeItemStatus.value ?? undefined\"\n    :data-variant=\"props.variant\"\n    :data-visible=\"visible ? '' : undefined\"\n    :disabled=\"props.disabled\"\n    :hidden=\"!visible\"\n    :inert=\"!visible\"\n    :tabindex=\"visible ? undefined : -1\"\n    :class=\"cn(\n      buttonVariants({ size: props.size, variant: props.variant }),\n      'cn-questionnaire-next col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0',\n      props.class,\n    )\"\n    @click=\"handleClick\"\n  >\n    <slot>Next</slot>\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnairePrevious.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from \"@/components/ui/button\"\nimport { Primitive } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { injectQuestionnaireRootContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  disabled?: boolean\n  size?: ButtonVariants[\"size\"]\n  variant?: ButtonVariants[\"variant\"]\n}>(), {\n  as: \"button\",\n  disabled: false,\n  size: \"default\",\n  variant: \"outline\",\n})\n\nconst emits = defineEmits<{\n  click: [event: MouseEvent]\n}>()\n\nconst root = injectQuestionnaireRootContext()\n\nconst visible = computed(() => root.total.value > 1 && !root.first.value)\n\nfunction handleClick(event: MouseEvent) {\n  emits(\"click\", event)\n\n  // `disabled` does not block clicks once `as` or `as-child` renders something\n  // other than a button.\n  if (props.disabled) {\n    event.preventDefault()\n    return\n  }\n\n  if (!event.defaultPrevented) {\n    root.goPrevious()\n  }\n}\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"questionnaire-previous\"\n    type=\"button\"\n    :aria-hidden=\"!visible || undefined\"\n    :aria-disabled=\"props.disabled || undefined\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :data-disabled=\"props.disabled ? '' : undefined\"\n    :data-hidden=\"visible ? undefined : ''\"\n    :data-size=\"props.size\"\n    :data-status=\"root.activeItemStatus.value ?? undefined\"\n    :data-variant=\"props.variant\"\n    :data-visible=\"visible ? '' : undefined\"\n    :disabled=\"props.disabled\"\n    :hidden=\"!visible\"\n    :inert=\"!visible\"\n    :tabindex=\"visible ? undefined : -1\"\n    :class=\"cn(\n      buttonVariants({ size: props.size, variant: props.variant }),\n      'cn-questionnaire-previous col-start-1 row-start-1 min-h-11 justify-self-start sm:min-h-0',\n      props.class,\n    )\"\n    @click=\"handleClick\"\n  >\n    <slot>Previous</slot>\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireProgress.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { injectQuestionnaireRootContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n}>(), {\n  as: \"div\",\n})\n\nconst root = injectQuestionnaireRootContext()\n\nconst label = computed(() =>\n  root.total.value ? `Question ${root.current.value} of ${root.total.value}` : undefined)\n</script>\n\n<template>\n  <Primitive\n    aria-label=\"Questionnaire progress\"\n    aria-live=\"polite\"\n    data-slot=\"questionnaire-progress\"\n    role=\"progressbar\"\n    :aria-valuemax=\"root.total.value || undefined\"\n    :aria-valuemin=\"root.total.value ? 1 : undefined\"\n    :aria-valuenow=\"root.total.value ? root.current.value : undefined\"\n    :aria-valuetext=\"label\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :data-current=\"root.current.value\"\n    :data-first=\"root.first.value ? '' : undefined\"\n    :data-last=\"root.last.value ? '' : undefined\"\n    :data-total=\"root.total.value\"\n    :class=\"cn(\n      'cn-questionnaire-progress min-h-[1lh] w-fit min-w-[14ch] font-medium text-muted-foreground tabular-nums',\n      props.class,\n    )\"\n  >\n    <slot\n      :current=\"root.current.value\"\n      :first=\"root.first.value\"\n      :last=\"root.last.value\"\n      :total=\"root.total.value\"\n    >\n      {{ label }}\n    </slot>\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireSkip.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from \"@/components/ui/button\"\nimport { Primitive } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { injectQuestionnaireRootContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  disabled?: boolean\n  size?: ButtonVariants[\"size\"]\n  variant?: ButtonVariants[\"variant\"]\n}>(), {\n  as: \"button\",\n  disabled: false,\n  size: \"default\",\n  variant: \"outline\",\n})\n\nconst emits = defineEmits<{\n  click: [event: MouseEvent]\n}>()\n\nconst root = injectQuestionnaireRootContext()\n\nconst visible = computed(() => root.activeItemRequired.value === false)\n\nfunction handleClick(event: MouseEvent) {\n  emits(\"click\", event)\n\n  // `disabled` does not block clicks once `as` or `as-child` renders something\n  // other than a button.\n  if (props.disabled) {\n    event.preventDefault()\n    return\n  }\n\n  if (!event.defaultPrevented) {\n    root.skipCurrent()\n  }\n}\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"questionnaire-skip\"\n    type=\"button\"\n    :aria-hidden=\"!visible || undefined\"\n    :aria-disabled=\"props.disabled || undefined\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :data-disabled=\"props.disabled ? '' : undefined\"\n    :data-hidden=\"visible ? undefined : ''\"\n    :data-size=\"props.size\"\n    :data-status=\"root.activeItemStatus.value ?? undefined\"\n    :data-variant=\"props.variant\"\n    :data-visible=\"visible ? '' : undefined\"\n    :disabled=\"props.disabled\"\n    :hidden=\"!visible\"\n    :inert=\"!visible\"\n    :tabindex=\"visible ? undefined : -1\"\n    :class=\"cn(\n      buttonVariants({ size: props.size, variant: props.variant }),\n      'cn-questionnaire-skip col-start-2 row-start-1 min-h-11 justify-self-end sm:min-h-0',\n      props.class,\n    )\"\n    @click=\"handleClick\"\n  >\n    <slot>Skip</slot>\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireSubmit.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { HTMLAttributes } from \"vue\"\nimport type { ButtonVariants } from \"@/components/ui/button\"\nimport { Primitive } from \"reka-ui\"\nimport { computed } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { buttonVariants } from \"@/components/ui/button\"\nimport { injectQuestionnaireRootContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  disabled?: boolean\n  size?: ButtonVariants[\"size\"]\n  variant?: ButtonVariants[\"variant\"]\n}>(), {\n  as: \"button\",\n  disabled: false,\n  size: \"default\",\n  variant: \"default\",\n})\n\nconst root = injectQuestionnaireRootContext()\n\nconst visible = computed(() => root.total.value > 0 && root.last.value)\nconst shortcut = computed(() => (visible.value && !props.disabled ? \"Enter\" : null))\n</script>\n\n<template>\n  <Primitive\n    data-slot=\"questionnaire-submit\"\n    type=\"submit\"\n    :aria-hidden=\"!visible || undefined\"\n    :aria-disabled=\"props.disabled || undefined\"\n    :aria-keyshortcuts=\"shortcut ?? undefined\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :data-disabled=\"props.disabled ? '' : undefined\"\n    :data-hidden=\"visible ? undefined : ''\"\n    :data-shortcut=\"shortcut ?? undefined\"\n    :data-size=\"props.size\"\n    :data-status=\"root.activeItemStatus.value ?? undefined\"\n    :data-variant=\"props.variant\"\n    :data-visible=\"visible ? '' : undefined\"\n    :disabled=\"props.disabled\"\n    :hidden=\"!visible\"\n    :inert=\"!visible\"\n    :tabindex=\"visible ? undefined : -1\"\n    :class=\"cn(\n      buttonVariants({ size: props.size, variant: props.variant }),\n      'cn-questionnaire-submit col-start-3 row-start-1 min-h-11 justify-self-end sm:min-h-0',\n      props.class,\n    )\"\n  >\n    <slot>Submit</slot>\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/QuestionnaireTitle.vue",
      "type": "registry:ui",
      "content": "<script setup lang=\"ts\">\nimport type { PrimitiveProps } from \"reka-ui\"\nimport type { ComponentPublicInstance, HTMLAttributes } from \"vue\"\nimport { Primitive } from \"reka-ui\"\nimport { onBeforeUnmount, onMounted, ref, useId } from \"vue\"\nimport { cn } from \"@/lib/utils\"\nimport { injectQuestionnaireItemContext } from \"./useQuestionnaire\"\n\nconst props = withDefaults(defineProps<PrimitiveProps & {\n  class?: HTMLAttributes[\"class\"]\n  id?: string\n}>(), {\n  as: \"legend\",\n})\n\nconst item = injectQuestionnaireItemContext()\n\nconst primitiveRef = ref<ComponentPublicInstance | null>(null)\nconst fallbackId = props.id ?? useId()\n\nlet unregisterTitle: (() => void) | null = null\n\nonMounted(() => {\n  const element = primitiveRef.value?.$el as HTMLElement | undefined\n\n  // A legend already names the fieldset. Anything else, for example a\n  // DialogTitle rendered through `as-child`, has to name it explicitly.\n  if (!element || element.tagName === \"LEGEND\") {\n    return\n  }\n\n  if (!element.id) {\n    element.id = fallbackId\n  }\n\n  unregisterTitle = item.registerTitle(element.id)\n})\n\nonBeforeUnmount(() => unregisterTitle?.())\n</script>\n\n<template>\n  <Primitive\n    v-bind=\"props.id ? { id: props.id } : {}\"\n    ref=\"primitiveRef\"\n    data-slot=\"questionnaire-title\"\n    :as=\"props.as\"\n    :as-child=\"props.asChild\"\n    :class=\"cn('cn-questionnaire-title cn-font-heading text-pretty', props.class)\"\n  >\n    <slot />\n  </Primitive>\n</template>\n"
    },
    {
      "path": "ui/questionnaire/index.ts",
      "type": "registry:ui",
      "content": "export { default as Questionnaire } from \"./Questionnaire.vue\"\nexport { default as QuestionnaireActions } from \"./QuestionnaireActions.vue\"\nexport { default as QuestionnaireChoice } from \"./QuestionnaireChoice.vue\"\nexport { default as QuestionnaireChoiceDescription } from \"./QuestionnaireChoiceDescription.vue\"\nexport { default as QuestionnaireChoices } from \"./QuestionnaireChoices.vue\"\nexport { default as QuestionnaireDescription } from \"./QuestionnaireDescription.vue\"\nexport { default as QuestionnaireError } from \"./QuestionnaireError.vue\"\nexport { default as QuestionnaireInput } from \"./QuestionnaireInput.vue\"\nexport { default as QuestionnaireItem } from \"./QuestionnaireItem.vue\"\nexport { default as QuestionnaireNext } from \"./QuestionnaireNext.vue\"\nexport { default as QuestionnairePrevious } from \"./QuestionnairePrevious.vue\"\nexport { default as QuestionnaireProgress } from \"./QuestionnaireProgress.vue\"\nexport { default as QuestionnaireSkip } from \"./QuestionnaireSkip.vue\"\nexport { default as QuestionnaireSubmit } from \"./QuestionnaireSubmit.vue\"\nexport { default as QuestionnaireTitle } from \"./QuestionnaireTitle.vue\"\n\nexport type {\n  QuestionnaireChoiceDefinition,\n  QuestionnaireInputType,\n  QuestionnaireItemDefinition,\n  QuestionnaireItemStatus,\n  QuestionnaireShortcutMode,\n} from \"./useQuestionnaire\"\n\nexport {\n  injectQuestionnaireItemContext,\n  injectQuestionnaireRootContext,\n} from \"./useQuestionnaire\"\n"
    },
    {
      "path": "ui/questionnaire/useQuestionnaire.ts",
      "type": "registry:ui",
      "content": "import type { ComputedRef, Ref } from \"vue\"\nimport { createContext } from \"reka-ui\"\n\nexport type QuestionnaireItemStatus = \"unanswered\" | \"answered\" | \"skipped\"\nexport type QuestionnaireShortcutMode = \"letters\" | \"numbers\"\n\nexport type QuestionnaireInputType\n  = | \"date\"\n    | \"datetime-local\"\n    | \"email\"\n    | \"month\"\n    | \"number\"\n    | \"password\"\n    | \"search\"\n    | \"tel\"\n    | \"text\"\n    | \"time\"\n    | \"url\"\n    | \"week\"\n\nexport interface QuestionnaireChoiceDefinition {\n  disabled?: boolean\n  value: string\n}\n\nexport interface QuestionnaireItemDefinition {\n  choices?: readonly QuestionnaireChoiceDefinition[]\n  disabled?: boolean\n  name: string\n  required?: boolean\n}\n\nexport interface ChoiceRegistration {\n  disabled: boolean\n  value: string\n}\n\nexport interface AnswerControlRegistration {\n  disabled: boolean\n  element: HTMLInputElement\n  id: string\n  ownDisabled: boolean\n  type: \"choice\" | \"input\"\n  value: string\n}\n\nexport interface ItemRegistration {\n  element: HTMLFieldSetElement\n  focus: () => void\n  focusInvalid: () => void\n  getAnswerByElement: (element: Element) => AnswerControlRegistration | null\n  getAnswerByShortcut: (shortcut: string) => AnswerControlRegistration | null\n  getChoices: () => ChoiceRegistration[]\n  isDisabled: () => boolean\n  isRequired: () => boolean\n  moveAnswerFocus: (element: Element, direction: \"next\" | \"previous\") => boolean\n  name: string\n  reset: () => void\n  skip: () => void\n  status: () => QuestionnaireItemStatus\n  validate: () => boolean\n}\n\nexport interface QuestionnaireRootContext {\n  activeItem: ComputedRef<ItemRegistration | null>\n  activeItemName: ComputedRef<string | null>\n  activeItemRequired: ComputedRef<boolean | null>\n  activeItemStatus: ComputedRef<QuestionnaireItemStatus | null>\n  current: ComputedRef<number>\n  domVersion: Ref<number>\n  first: ComputedRef<boolean>\n  goNext: () => void\n  goPrevious: () => void\n  itemDefinitionByName: ComputedRef<Map<string, QuestionnaireItemDefinition> | null>\n  last: ComputedRef<boolean>\n  nativeValidation: ComputedRef<boolean>\n  registerItem: (registration: ItemRegistration) => () => void\n  shortcuts: ComputedRef<QuestionnaireShortcutMode | null>\n  skipCurrent: () => void\n  total: ComputedRef<number>\n}\n\nexport interface QuestionnaireItemContext {\n  active: ComputedRef<boolean>\n  /** Bumped after a controlled answer interaction so every control re-syncs. */\n  controlSyncVersion: Ref<number>\n  disabled: ComputedRef<boolean>\n  hasInputAnswer: ComputedRef<boolean>\n  invalid: ComputedRef<boolean>\n  multiple: ComputedRef<boolean>\n  name: ComputedRef<string>\n  registerAnswerControl: (registration: AnswerControlRegistration) => () => void\n  registerAnswerSelection: (answerId: string, defaultSelected: boolean) => () => void\n  registerDescription: (descriptionId: string) => () => void\n  registerError: (errorId: string) => () => void\n  /** Only used when the title does not render as the fieldset legend. */\n  registerTitle: (titleId: string) => () => void\n  requestControlSync: () => void\n  required: ComputedRef<boolean>\n  resetVersion: Ref<number>\n  selectedAnswerIds: Ref<string[]>\n  setAnswerDefault: (answerId: string, defaultSelected: boolean) => void\n  setAnswerSelectionFromInteraction: (answerId: string, selected: boolean) => void\n  shortcutByAnswerId: ComputedRef<Map<string, string>>\n  shortcutByChoiceValue: ComputedRef<Map<string, string> | null>\n  shortcuts: ComputedRef<QuestionnaireShortcutMode | null>\n  status: ComputedRef<QuestionnaireItemStatus>\n  syncControlledAnswerSelection: (answerId: string, selected: boolean) => void\n}\n\nexport const [injectQuestionnaireRootContext, provideQuestionnaireRootContext]\n  = createContext<QuestionnaireRootContext>(\"Questionnaire\")\n\nexport const [injectQuestionnaireItemContext, provideQuestionnaireItemContext]\n  = createContext<QuestionnaireItemContext>(\"QuestionnaireItem\")\n\nexport function hasInputValue(value: unknown) {\n  if (Array.isArray(value)) {\n    return value.some(item => String(item).trim().length > 0)\n  }\n\n  return value !== undefined && value !== null && String(value).trim().length > 0\n}\n\nexport function getShortcutKeys(shortcuts: QuestionnaireShortcutMode | null) {\n  if (shortcuts === \"letters\") {\n    return Array.from({ length: 26 }, (_, index) => String.fromCharCode(65 + index))\n  }\n\n  if (shortcuts === \"numbers\") {\n    return Array.from({ length: 9 }, (_, index) => String(index + 1))\n  }\n\n  return []\n}\n\nexport function getShortcutFromKey(key: string, shortcuts: QuestionnaireShortcutMode) {\n  const normalizedKey = shortcuts === \"letters\" ? key.toUpperCase() : key\n\n  return getShortcutKeys(shortcuts).includes(normalizedKey) ? normalizedKey : null\n}\n\nexport function getAnswerKeyShortcuts(shortcut: string | null, filled: boolean) {\n  return [shortcut, filled ? \"Enter\" : null].filter(Boolean).join(\" \") || undefined\n}\n\nexport function isAnswerFilled(answer: AnswerControlRegistration) {\n  if (answer.type === \"choice\") {\n    return answer.element.checked\n  }\n\n  return answer.element.hasAttribute(\"name\") && hasInputValue(answer.element.value)\n}\n\nexport function isEmptyNavigableInput(answer: AnswerControlRegistration | null) {\n  return (\n    answer?.type === \"input\"\n    && [\"email\", \"password\", \"search\", \"tel\", \"text\", \"url\"].includes(answer.element.type)\n    && !hasInputValue(answer.element.value)\n  )\n}\n\nexport function isTextEntryTarget(element: Element) {\n  if (element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {\n    return true\n  }\n\n  if (element instanceof HTMLInputElement) {\n    return ![\"button\", \"checkbox\", \"radio\", \"reset\", \"submit\"].includes(element.type)\n  }\n\n  return element instanceof HTMLElement && element.isContentEditable\n}\n\nexport function isRadioTarget(element: Element) {\n  return element instanceof HTMLInputElement && element.type === \"radio\"\n}\n\n/**\n * Sort registrations by the position of their element in the document, so that\n * navigation always follows the rendered order instead of the mount order.\n */\nexport function compareDocumentOrder(first: Element, second: Element) {\n  if (first === second) {\n    return 0\n  }\n\n  const position = first.compareDocumentPosition(second)\n\n  if (position & Node.DOCUMENT_POSITION_FOLLOWING) {\n    return -1\n  }\n\n  if (position & Node.DOCUMENT_POSITION_PRECEDING) {\n    return 1\n  }\n\n  return 0\n}\n\nexport interface QuestionnaireCollection {\n  enabledItems: QuestionnaireItemDefinition[]\n  itemByName: Map<string, QuestionnaireItemDefinition>\n  items: readonly QuestionnaireItemDefinition[]\n}\n\nexport function createQuestionnaireCollection(\n  items: readonly QuestionnaireItemDefinition[] | undefined,\n): QuestionnaireCollection | null {\n  if (items === undefined) {\n    return null\n  }\n\n  return {\n    enabledItems: items.filter(item => !item.disabled),\n    itemByName: new Map(items.map(item => [item.name, item])),\n    items,\n  }\n}\n\nexport function getInitialItemName(\n  collection: QuestionnaireCollection | null,\n  defaultItem: string | undefined,\n) {\n  if (!collection) {\n    return defaultItem ?? null\n  }\n\n  const defaultDefinition = defaultItem ? collection.itemByName.get(defaultItem) : undefined\n\n  if (defaultDefinition && !defaultDefinition.disabled) {\n    return defaultDefinition.name\n  }\n\n  return collection.enabledItems[0]?.name ?? null\n}\n\n/**\n * Map every enabled choice of an item definition to a keyboard shortcut, so\n * that shortcuts stay stable regardless of how choices are rendered.\n */\nexport function getShortcutByChoiceValue(\n  item: QuestionnaireItemDefinition | undefined,\n  shortcuts: QuestionnaireShortcutMode | null,\n) {\n  const shortcutByChoiceValue = new Map<string, string>()\n\n  if (!item || !shortcuts) {\n    return shortcutByChoiceValue\n  }\n\n  const keys = getShortcutKeys(shortcuts)\n  let shortcutIndex = 0\n\n  for (const choice of item.choices ?? []) {\n    if (choice.disabled) {\n      continue\n    }\n\n    const shortcut = keys[shortcutIndex]\n\n    if (!shortcut) {\n      break\n    }\n\n    shortcutByChoiceValue.set(choice.value, shortcut)\n    shortcutIndex += 1\n  }\n\n  return shortcutByChoiceValue\n}\n"
    }
  ]
}
