{"version":3,"file":"checkbox-group.cjs","names":[],"sources":["../src/inputs/checkbox-group/checkbox-group.ts"],"sourcesContent":["import {\n  bind,\n  createContext,\n  createStableId,\n  define,\n  getHost,\n  html,\n  onCleanup,\n  prop,\n  provide,\n  useEmit,\n  useField,\n  useSlots,\n  watchEffect,\n  when,\n} from '@vielzeug/ore';\nimport { computed, type Readable, signal } from '@vielzeug/ripple';\nimport {\n  type ChoiceChangeDetail,\n  createChoiceField,\n  getChoiceLabel,\n  getLightChildrenByTag,\n  lifecycleSignal,\n} from '../../core';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { disabledStateMixin } from '../../styles';\nimport type { ComponentSize, ThemeColor } from '../../types';\nimport componentStyles from './checkbox-group.css?inline';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\nexport type CheckboxGroupContext = {\n  color: Readable<ThemeColor | undefined>;\n  disabled: Readable<boolean>;\n  size: Readable<ComponentSize | undefined>;\n  toggle: (value: string, originalEvent?: Event) => void;\n  values: Readable<string[]>;\n};\n\nexport const CHECKBOX_GROUP_CTX = createContext<CheckboxGroupContext>('CheckboxGroupContext');\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type OreCheckboxGroupProps = {\n  /** Theme color — propagated to all child ore-checkbox elements */\n  color?: ThemeColor;\n  /** Disable all checkboxes in the group */\n  disabled?: boolean;\n  /** Error message shown below the group */\n  error?: string;\n  /** Helper text shown below the group */\n  helper?: string;\n  /** Legend / label for the fieldset. Required for accessibility. */\n  label?: string;\n  /** Form field name used during submission */\n  name?: string;\n  /** Layout direction of the checkbox options */\n  orientation?: 'vertical' | 'horizontal';\n  /** Mark the group as required */\n  required?: boolean;\n  /** Size — propagated to all child ore-checkbox elements */\n  size?: ComponentSize;\n  /** Comma-separated list of currently checked values */\n  values?: string;\n};\n\nexport type OreCheckboxGroupEvents = {\n  change: ChoiceChangeDetail;\n};\n\n/**\n * A fieldset wrapper that groups `ore-checkbox` elements, provides shared\n * `color` and `size` via context, and manages multi-value selection state.\n *\n * @element ore-checkbox-group\n *\n * @attr {string} label - Legend text (required for a11y)\n * @attr {string} values - Comma-separated list of checked values\n * @attr {boolean} disabled - Disable all checkboxes in the group\n * @attr {string} error - Error message\n * @attr {string} helper - Helper text\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when selection changes. detail: { value: string, values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-checkbox` elements here\n *\n * @cssprop --checkbox-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --checkbox-group-gap - Gap between checkbox items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-checkbox-group name=\"fruits\" label=\"Favourite fruits\" required>\n *   <ore-checkbox value=\"apple\">Apple</ore-checkbox>\n *   <ore-checkbox value=\"banana\">Banana</ore-checkbox>\n *   <ore-checkbox value=\"cherry\">Cherry</ore-checkbox>\n * </ore-checkbox-group>\n * <ore-checkbox-group name=\"options\" orientation=\"horizontal\" color=\"primary\">\n *   <ore-checkbox value=\"a\">Option A</ore-checkbox>\n *   <ore-checkbox value=\"b\">Option B</ore-checkbox>\n * </ore-checkbox-group>\n * ```\n */\nexport const CHECKBOX_GROUP_TAG = 'ore-checkbox-group' as const;\ndefine<OreCheckboxGroupProps>(CHECKBOX_GROUP_TAG, {\n  formAssociated: true,\n  props: {\n    ...themableBundle,\n    ...sizableBundle,\n    ...disablableBundle,\n    error: prop.string(),\n    helper: prop.string(),\n    label: prop.string(),\n    name: prop.string(),\n    orientation: prop.string('vertical'),\n    required: prop.bool(false),\n    // Not auto-reflected (`reflect: false`) — the derived, interaction-updated selection\n    // (`choice.formValue`) is the single writer for this attribute, via `bind()` below;\n    // letting `prop.string()`'s own default reflection also write the raw incoming value\n    // would leave two effects racing to set the same attribute from different sources.\n    values: { ...prop.string(), reflect: false },\n  },\n  setup(props) {\n    const el = getHost();\n    const emit = useEmit<OreCheckboxGroupEvents>();\n    const slots = useSlots();\n    const watch = watchEffect;\n\n    const choice = createChoiceField({\n      disabled: props.disabled,\n      error: props.error,\n      helper: props.helper,\n      multiple: signal(true),\n      prefix: 'checkbox-group',\n      required: props.required,\n      signal: lifecycleSignal(onCleanup),\n      value: props.values,\n    });\n\n    choice.attachFormField(\n      useField<string>({\n        disabled: choice.disabled,\n        onReset: choice.reset,\n        toFormValue: (v) => v,\n        validationMessage: choice.validationMessage,\n        validity: choice.validity,\n        value: choice.formValue,\n      }),\n    );\n\n    const checkedValues = choice.selectedValues;\n\n    const getCheckboxes = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-checkbox');\n    const getLabelForValue = (value: string): string => getChoiceLabel(getCheckboxes(), value);\n    const emitChange = (originalEvent?: Event) => {\n      const values = checkedValues.value;\n\n      const labels = values.map(getLabelForValue);\n\n      emit('change', { labels, originalEvent, values });\n    };\n\n    const toggleCheckbox = (val: string, originalEvent?: Event) => {\n      choice.toggleValue(val);\n      choice.triggerValidation('change');\n      emitChange(originalEvent);\n    };\n\n    provide(CHECKBOX_GROUP_CTX, {\n      color: props.color,\n      disabled: computed(() => Boolean(props.disabled.value)),\n      size: props.size,\n      toggle: toggleCheckbox,\n      values: checkedValues,\n    });\n\n    // Sync checked state + color/size/disabled onto slotted ore-checkbox children\n    const syncChildren = () => {\n      const values = checkedValues.value;\n      const color = props.color.value;\n      const size = props.size.value;\n      const disabled = props.disabled.value;\n      const checkboxes = getCheckboxes();\n\n      for (const checkbox of checkboxes) {\n        const val = checkbox.getAttribute('value') ?? '';\n\n        checkbox.toggleAttribute('checked', values.includes(val));\n\n        if (color) checkbox.setAttribute('color', color);\n        else checkbox.removeAttribute('color');\n\n        if (size) checkbox.setAttribute('size', size);\n        else checkbox.removeAttribute('size');\n\n        checkbox.toggleAttribute('disabled', Boolean(disabled));\n      }\n    };\n\n    watch(() => {\n      void slots.elements().value;\n      syncChildren();\n    });\n\n    watch(() => {\n      void slots.elements().value;\n\n      const listeners = getCheckboxes().map((checkbox) => {\n        const handler = (event: Event) => {\n          event.stopPropagation();\n\n          const val = (checkbox.getAttribute('value') ?? '').trim();\n\n          if (!val) return;\n\n          toggleCheckbox(val, event);\n        };\n\n        checkbox.addEventListener('change', handler);\n\n        return () => {\n          checkbox.removeEventListener('change', handler);\n        };\n      });\n\n      return () => {\n        for (const dispose of listeners) dispose();\n      };\n    });\n\n    const legendId = createStableId('checkbox-group-legend');\n    const errorId = `${legendId}-error`;\n    const helperId = `${legendId}-helper`;\n    const hasError = () => Boolean(props.error.value);\n    const hasHelper = () => Boolean(props.helper.value) && !hasError();\n\n    // Reactive, not a one-off `el.setAttribute()` inside the click handler: the host's `values`\n    // attribute must stay in sync with the selection regardless of *why* it changed (a click,\n    // or `reset()` on ancestor form reset — neither of which should need its own copy of this).\n    bind({ attr: { size: props.size, values: choice.formValue } });\n\n    return html`\n      <fieldset\n        role=\"group\"\n        aria-required=\"${() => String(Boolean(props.required.value))}\"\n        aria-invalid=\"${() => String(hasError())}\"\n        aria-errormessage=\"${() => (hasError() ? errorId : null)}\"\n        aria-describedby=\"${() => (hasError() ? errorId : hasHelper() ? helperId : null)}\">\n        <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n          ${props.label}${when(\n            () => Boolean(props.required.value),\n            () => html`\n              <span aria-hidden=\"true\">*</span>\n            `,\n          )}\n        </legend>\n        <div class=\"checkbox-group-items\" part=\"items\">\n          <slot></slot>\n        </div>\n        <div class=\"error-text\" id=\"${errorId}\" role=\"alert\" ?hidden=${() => !hasError()}>${props.error}</div>\n        <div class=\"helper-text\" id=\"${helperId}\" ?hidden=${() => !hasHelper()}>${props.helper}</div>\n      </fieldset>\n    `;\n  },\n  styles: [disabledStateMixin, componentStyles],\n});\n"],"mappings":"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"}