@vielzeug/refine 1.3.1 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checkbox-group.cjs +7 -7
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +35 -26
- package/dist/checkbox-group.js.map +1 -1
- package/dist/checkbox.cjs +3 -3
- package/dist/checkbox.cjs.map +1 -1
- package/dist/checkbox.js +24 -19
- package/dist/checkbox.js.map +1 -1
- package/dist/combobox.cjs +24 -24
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +133 -129
- package/dist/combobox.js.map +1 -1
- package/dist/custom-elements.json +5473 -4925
- package/dist/headless/auto-resize.cjs +2 -0
- package/dist/headless/auto-resize.cjs.map +1 -0
- package/dist/headless/auto-resize.d.ts +29 -0
- package/dist/headless/auto-resize.d.ts.map +1 -0
- package/dist/headless/auto-resize.js +16 -0
- package/dist/headless/auto-resize.js.map +1 -0
- package/dist/headless/checkable.cjs +1 -1
- package/dist/headless/checkable.cjs.map +1 -1
- package/dist/headless/checkable.d.ts +25 -7
- package/dist/headless/checkable.d.ts.map +1 -1
- package/dist/headless/checkable.js +19 -14
- package/dist/headless/checkable.js.map +1 -1
- package/dist/headless/choice-field.cjs +1 -1
- package/dist/headless/choice-field.cjs.map +1 -1
- package/dist/headless/choice-field.d.ts +21 -0
- package/dist/headless/choice-field.d.ts.map +1 -1
- package/dist/headless/choice-field.js +27 -19
- package/dist/headless/choice-field.js.map +1 -1
- package/dist/headless/composer.cjs +2 -0
- package/dist/headless/composer.cjs.map +1 -0
- package/dist/headless/composer.d.ts +44 -0
- package/dist/headless/composer.d.ts.map +1 -0
- package/dist/headless/composer.js +21 -0
- package/dist/headless/composer.js.map +1 -0
- package/dist/headless/field-base.cjs +1 -1
- package/dist/headless/field-base.cjs.map +1 -1
- package/dist/headless/field-base.d.ts +97 -84
- package/dist/headless/field-base.d.ts.map +1 -1
- package/dist/headless/field-base.js +43 -32
- package/dist/headless/field-base.js.map +1 -1
- package/dist/headless/index.d.ts +3 -1
- package/dist/headless/index.d.ts.map +1 -1
- package/dist/headless/text-field.cjs +1 -1
- package/dist/headless/text-field.cjs.map +1 -1
- package/dist/headless/text-field.d.ts +40 -0
- package/dist/headless/text-field.d.ts.map +1 -1
- package/dist/headless/text-field.js +30 -25
- package/dist/headless/text-field.js.map +1 -1
- package/dist/headless.cjs +1 -1
- package/dist/headless.js +27 -25
- package/dist/index.cjs +1 -1
- package/dist/index.js +61 -60
- package/dist/input.cjs +9 -9
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +49 -44
- package/dist/input.js.map +1 -1
- package/dist/inputs/checkbox/checkbox.d.ts +3 -0
- package/dist/inputs/checkbox/checkbox.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.d.ts +1 -0
- package/dist/inputs/combobox/combobox.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.types.d.ts +2 -0
- package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
- package/dist/inputs/index.d.ts +2 -0
- package/dist/inputs/index.d.ts.map +1 -1
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/message-composer/message-composer.css.cjs +2 -0
- package/dist/inputs/message-composer/message-composer.css.cjs.map +1 -0
- package/dist/inputs/message-composer/message-composer.css.js +6 -0
- package/dist/inputs/message-composer/message-composer.css.js.map +1 -0
- package/dist/inputs/message-composer/message-composer.d.ts +139 -0
- package/dist/inputs/message-composer/message-composer.d.ts.map +1 -0
- package/dist/inputs/shared/field-binding.cjs +1 -1
- package/dist/inputs/shared/field-binding.cjs.map +1 -1
- package/dist/inputs/shared/field-binding.d.ts +15 -0
- package/dist/inputs/shared/field-binding.d.ts.map +1 -1
- package/dist/inputs/shared/field-binding.js +4 -3
- package/dist/inputs/shared/field-binding.js.map +1 -1
- package/dist/inputs/shared/form-context.cjs.map +1 -1
- package/dist/inputs/shared/form-context.d.ts +6 -0
- package/dist/inputs/shared/form-context.d.ts.map +1 -1
- package/dist/inputs/shared/form-context.js.map +1 -1
- package/dist/inputs/textarea/textarea.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.css.cjs +1 -1
- package/dist/layout/navbar/navbar.css.cjs.map +1 -1
- package/dist/layout/navbar/navbar.css.js +1 -1
- package/dist/layout/navbar/navbar.css.js.map +1 -1
- package/dist/message-composer.cjs +52 -0
- package/dist/message-composer.cjs.map +1 -0
- package/dist/message-composer.js +168 -0
- package/dist/message-composer.js.map +1 -0
- package/dist/ore/dist/base-element.cjs +1 -1
- package/dist/ore/dist/base-element.cjs.map +1 -1
- package/dist/ore/dist/base-element.js +13 -3
- package/dist/ore/dist/base-element.js.map +1 -1
- package/dist/ore/dist/forms/field.cjs +1 -1
- package/dist/ore/dist/forms/field.cjs.map +1 -1
- package/dist/ore/dist/forms/field.js +29 -22
- package/dist/ore/dist/forms/field.js.map +1 -1
- package/dist/ore/dist/runtime.cjs +1 -1
- package/dist/ore/dist/runtime.cjs.map +1 -1
- package/dist/ore/dist/runtime.js +3 -1
- package/dist/ore/dist/runtime.js.map +1 -1
- package/dist/radio-group.cjs +9 -9
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +44 -38
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs +3 -3
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +23 -24
- package/dist/radio.js.map +1 -1
- package/dist/refine.cjs +228 -178
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +228 -178
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +251 -201
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs +31 -31
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +125 -122
- package/dist/select.js.map +1 -1
- package/dist/shared/size-presets.cjs +1 -1
- package/dist/shared/size-presets.cjs.map +1 -1
- package/dist/shared/size-presets.d.ts +23 -0
- package/dist/shared/size-presets.d.ts.map +1 -1
- package/dist/shared/size-presets.js +17 -1
- package/dist/shared/size-presets.js.map +1 -1
- package/dist/switch.cjs +3 -3
- package/dist/switch.cjs.map +1 -1
- package/dist/switch.js +17 -18
- package/dist/switch.js.map +1 -1
- package/dist/textarea.cjs +15 -15
- package/dist/textarea.cjs.map +1 -1
- package/dist/textarea.js +64 -60
- package/dist/textarea.js.map +1 -1
- package/package.json +22 -10
package/dist/radio-group.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-group.cjs","names":[],"sources":["../src/inputs/radio-group/radio-group.ts"],"sourcesContent":["import {\n createContext,\n createStableId,\n define,\n html,\n inject,\n prop,\n bind,\n getHost,\n onCleanup,\n provide,\n useEmit,\n useSlots,\n watchEffect,\n} from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { type Readable } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\n createListControl,\n getChoiceLabel,\n getLightChildrenByTag,\n} from '../../headless';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, sizeVariantMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './radio-group.css?inline';\n\n/** Radio group component properties */\nexport type OreRadioGroupProps = {\n /** Theme color tint */\n color?: ThemeColor;\n /** Disabled state */\n disabled?: boolean;\n /** Error message text */\n error?: string;\n /** Helper text displayed below the items */\n helper?: string;\n /** Group label text */\n label?: string;\n /** Form field name */\n name?: string;\n /** Layout orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Required field */\n required?: boolean;\n /** Items size preset */\n size?: ComponentSize;\n /** Initial selected value */\n value?: string;\n};\n\nexport type RadioGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n name: Readable<string | undefined>;\n select: (value: string, originalEvent?: Event) => void;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n};\n\nexport const RADIO_GROUP_CTX = createContext<RadioGroupContext | undefined>('OreRadioGroup');\n\n/** Events emitted by the radio-group component */\nexport type OreRadioGroupEvents = {\n /** Emitted when the selection changes */\n change: ChoiceChangeDetail;\n};\n\n/**\n * A group of radio buttons that allows users to select a single option from a set.\n * Supports keyboard navigation (arrows) and automatic value management.\n *\n * @element ore-radio-group\n *\n * @attr {string} value - Selected value\n * @attr {string} name - Form field name\n * @attr {string} label - Group label\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when a radio is selected. detail: { values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-radio` elements here\n *\n * @cssprop --radio-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --radio-group-gap - Gap between radio items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-radio-group name=\"plan\" label=\"Choose a plan\" value=\"free\" required>\n * <ore-radio value=\"free\">Free</ore-radio>\n * <ore-radio value=\"pro\">Pro</ore-radio>\n * <ore-radio value=\"enterprise\">Enterprise</ore-radio>\n * </ore-radio-group>\n * <ore-radio-group name=\"direction\" orientation=\"horizontal\" color=\"primary\">\n * <ore-radio value=\"left\">Left</ore-radio>\n * <ore-radio value=\"right\">Right</ore-radio>\n * </ore-radio-group>\n * ```\n */\nexport const RADIO_GROUP_TAG = 'ore-radio-group' as const;\ndefine<OreRadioGroupProps>(RADIO_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.oneOf(['horizontal', 'vertical'] as const, 'vertical'),\n required: prop.bool(false),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioGroupEvents>();\n const slots = useSlots();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n let _formField: { reportValidity(): void } | null = null;\n const choice = createChoiceField({\n disabled: fCtxProps.disabled,\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n label: props.label,\n prefix: 'radio-group',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string>({ disabled: choice.disabled, toFormValue: (v) => v, value: choice.formValue });\n\n const selectedValue = choice.selectedValue;\n const isDisabled = fCtxProps.disabled;\n\n bind({\n attr: {\n size: fCtxProps.size,\n value: () => selectedValue.value || null,\n },\n });\n\n const getSlottedRadios = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-radio');\n\n const getEnabledRadios = (): HTMLElement[] =>\n isDisabled.value ? [] : getSlottedRadios().filter((radio) => !radio.hasAttribute('disabled'));\n\n const getLabelForValue = (value: string): string => getChoiceLabel(getSlottedRadios(), value);\n\n const selectRadio = (val: string, originalEvent?: Event): void => {\n choice.setValues(val ? [val] : []);\n\n const labels = val ? [getLabelForValue(val)] : [];\n const values = val ? [val] : [];\n\n emit('change', { labels, originalEvent, values });\n choice.triggerValidation('blur');\n };\n\n provide(RADIO_GROUP_CTX, {\n color: props.color as Readable<ThemeColor | undefined>,\n disabled: isDisabled,\n name: props.name,\n select: selectRadio,\n size: props.size as Readable<ComponentSize | undefined>,\n value: selectedValue,\n });\n\n // Sync name/color/size/disabled/checked onto slotted ore-radio children.\n watch(() => {\n void slots.elements().value;\n void selectedValue.value;\n\n const radios = getSlottedRadios();\n\n for (const radio of radios) {\n const val = radio.getAttribute('value') ?? '';\n\n radio.toggleAttribute('checked', val === selectedValue.value);\n\n if (props.name.value) radio.setAttribute('name', props.name.value);\n else radio.removeAttribute('name');\n\n if (props.color.value) radio.setAttribute('color', props.color.value);\n else radio.removeAttribute('color');\n\n if (props.size.value) radio.setAttribute('size', props.size.value);\n else radio.removeAttribute('size');\n\n radio.toggleAttribute('disabled', isDisabled.value);\n }\n });\n\n // Roving tabindex: only the selected (or first) radio is tabbable.\n watch(() => {\n void slots.elements().value;\n\n const radios = getSlottedRadios();\n let hasFocusable = false;\n\n for (const radio of radios) {\n const isSelected = radio.getAttribute('value') === selectedValue.value;\n\n radio.setAttribute('tabindex', isSelected && !isDisabled.value ? '0' : '-1');\n\n if (isSelected && !isDisabled.value) hasFocusable = true;\n }\n\n if (!hasFocusable && radios.length > 0) {\n const first = radios.find((r) => !r.hasAttribute('disabled'));\n\n if (first) first.setAttribute('tabindex', '0');\n }\n });\n\n const listControl = createListControl<HTMLElement>({\n getItems: getEnabledRadios,\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const radio = getEnabledRadios()[index];\n\n if (!radio) return;\n\n radio.focus();\n\n if (radio.tagName === 'ORE-RADIO') {\n selectRadio(radio.getAttribute('value') ?? '', event);\n }\n },\n });\n\n bind({\n on: {\n change: (e: Event) => {\n if (e.target === el) return;\n\n e.stopPropagation();\n selectRadio((e.target as HTMLElement).getAttribute('value') ?? '', e);\n },\n keydown: (e: KeyboardEvent) => {\n const radios = getEnabledRadios();\n\n if (!radios.length) return;\n\n const focused = radios.indexOf(document.activeElement as HTMLElement);\n\n if (focused === -1) return;\n\n listControl.set(focused);\n listControl.handleKeydown(e);\n },\n },\n });\n\n const legendId = createStableId('radio-group-legend');\n\n return html`\n <fieldset\n role=\"radiogroup\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n :aria-invalid=\"${choice.ariaInvalid}\"\n :aria-errormessage=\"${choice.ariaErrorMessage}\"\n :aria-describedby=\"${choice.ariaDescribedBy}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"radio-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div\n class=\"helper-text\"\n part=\"helper\"\n id=\"${choice.assistiveId}\"\n :role=\"${() => (choice.errorText.value ? 'alert' : null)}\"\n aria-live=\"polite\"\n ?hidden=\"${() => !choice.errorText.value && !choice.helperText.value}\">\n ${() => choice.errorText.value || choice.helperText.value}\n </div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"qsBAmEA,IAAa,GAAA,EAAA,EAAA,cAAA,CAA+D,eAAe,EAwC9E,EAAkB,+BACJ,EAAiB,CAC1C,eAAgB,GAChB,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,iBACH,MAAO,EAAA,KAAK,OAAO,EACnB,OAAQ,EAAA,KAAK,OAAO,EACpB,MAAO,EAAA,KAAK,OAAO,EACnB,KAAM,EAAA,KAAK,OAAO,EAClB,YAAa,EAAA,KAAK,MAAM,CAAC,aAAc,UAAU,EAAY,UAAU,EACvE,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,MAAO,EAAA,KAAK,OAAO,CACrB,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAAa,EACb,GAAA,EAAA,EAAA,QAAA,CAAoC,EACpC,GAAA,EAAA,EAAA,SAAA,CAAiB,EACjB,EAAQ,EAAA,YAER,GAAA,EAAA,EAAA,OAAA,CAAiB,EAAA,QAAQ,EACzB,EAAY,EAAA,eAAe,EAAO,CAAO,EAE3C,EAAgD,KAC9C,EAAS,EAAA,kBAAkB,CAC/B,SAAU,EAAU,SACpB,MAAO,EAAM,MACb,iBAAoB,EACpB,OAAQ,EAAM,OACd,MAAO,EAAM,MACb,OAAQ,cACR,OAAQ,EAAA,gBAAgB,EAAA,SAAS,EACjC,WAAY,GAAS,WACrB,MAAO,EAAM,KACf,CAAC,EAED,EAAa,EAAA,SAAiB,CAAE,SAAU,EAAO,SAAU,YAAc,GAAM,EAAG,MAAO,EAAO,SAAU,CAAC,EAE3G,IAAM,EAAgB,EAAO,cACvB,EAAa,EAAU,UAE7B,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,KAAM,EAAU,KAChB,UAAa,EAAc,OAAS,IACtC,CACF,CAAC,EAED,IAAM,MAAwC,EAAA,sBAAsB,EAAI,WAAW,EAE7E,MACJ,EAAW,MAAQ,CAAC,EAAI,EAAiB,CAAC,CAAC,OAAQ,GAAU,CAAC,EAAM,aAAa,UAAU,CAAC,EAExF,EAAoB,GAA0B,EAAA,eAAe,EAAiB,EAAG,CAAK,EAEtF,GAAe,EAAa,IAAgC,CAChE,EAAO,UAAU,EAAM,CAAC,CAAG,EAAI,CAAC,CAAC,EAEjC,IAAM,EAAS,EAAM,CAAC,EAAiB,CAAG,CAAC,EAAI,CAAC,EAGhD,EAAK,SAAU,CAAE,SAAQ,gBAAe,OAFzB,EAAM,CAAC,CAAG,EAAI,CAAC,CAEiB,CAAC,EAChD,EAAO,kBAAkB,MAAM,CACjC,GAEA,EAAA,EAAA,QAAA,CAAQ,EAAiB,CACvB,MAAO,EAAM,MACb,SAAU,EACV,KAAM,EAAM,KACZ,OAAQ,EACR,KAAM,EAAM,KACZ,MAAO,CACT,CAAC,EAGD,MAAY,CACV,EAAW,SAAS,CAAC,CAAC,MACtB,EAAmB,MAEnB,IAAM,EAAS,EAAiB,EAEhC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAM,EAAM,aAAa,OAAO,GAAK,GAE3C,EAAM,gBAAgB,UAAW,IAAQ,EAAc,KAAK,EAExD,EAAM,KAAK,MAAO,EAAM,aAAa,OAAQ,EAAM,KAAK,KAAK,EAC5D,EAAM,gBAAgB,MAAM,EAE7B,EAAM,MAAM,MAAO,EAAM,aAAa,QAAS,EAAM,MAAM,KAAK,EAC/D,EAAM,gBAAgB,OAAO,EAE9B,EAAM,KAAK,MAAO,EAAM,aAAa,OAAQ,EAAM,KAAK,KAAK,EAC5D,EAAM,gBAAgB,MAAM,EAEjC,EAAM,gBAAgB,WAAY,EAAW,KAAK,CACpD,CACF,CAAC,EAGD,MAAY,CACV,EAAW,SAAS,CAAC,CAAC,MAEtB,IAAM,EAAS,EAAiB,EAC5B,EAAe,GAEnB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAa,EAAM,aAAa,OAAO,IAAM,EAAc,MAEjE,EAAM,aAAa,WAAY,GAAc,CAAC,EAAW,MAAQ,IAAM,IAAI,EAEvE,GAAc,CAAC,EAAW,QAAO,EAAe,GACtD,CAEA,GAAI,CAAC,GAAgB,EAAO,OAAS,EAAG,CACtC,IAAM,EAAQ,EAAO,KAAM,GAAM,CAAC,EAAE,aAAa,UAAU,CAAC,EAExD,GAAO,EAAM,aAAa,WAAY,GAAG,CAC/C,CACF,CAAC,EAED,IAAM,EAAc,EAAA,kBAA+B,CACjD,SAAU,EACV,KAAM,CAAE,KAAM,CAAC,YAAa,YAAY,EAAG,KAAM,CAAC,UAAW,WAAW,CAAE,EAC1E,KAAM,GACN,YAAa,EAAS,EAAO,IAAU,CACrC,IAAM,EAAQ,EAAiB,CAAC,CAAC,GAE5B,IAEL,EAAM,MAAM,EAER,EAAM,UAAY,aACpB,EAAY,EAAM,aAAa,OAAO,GAAK,GAAI,CAAK,EAExD,CACF,CAAC,GAED,EAAA,EAAA,KAAA,CAAK,CACH,GAAI,CACF,OAAS,GAAa,CAChB,EAAE,SAAW,IAEjB,EAAE,gBAAgB,EAClB,EAAa,EAAE,OAAuB,aAAa,OAAO,GAAK,GAAI,CAAC,EACtE,EACA,QAAU,GAAqB,CAC7B,IAAM,EAAS,EAAiB,EAEhC,GAAI,CAAC,EAAO,OAAQ,OAEpB,IAAM,EAAU,EAAO,QAAQ,SAAS,aAA4B,EAEhE,IAAY,KAEhB,EAAY,IAAI,CAAO,EACvB,EAAY,cAAc,CAAC,EAC7B,CACF,CACF,CAAC,EAED,IAAM,GAAA,EAAA,EAAA,eAAA,CAA0B,oBAAoB,EAEpD,MAAO,GAAA,IAAI;;;6BAGgB,OAAO,EAAQ,EAAM,SAAS,KAAM,EAAE;yBAC5C,EAAO,YAAY;8BACd,EAAO,iBAAiB;6BACzB,EAAO,gBAAgB;sBAC9B,EAAS,gBAAkB,CAAC,EAAM,MAAM,MAAM;YACxD,EAAM,QAAQ,EAAA,SACR,EAAQ,EAAM,SAAS,UACvB,EAAA,IAAI,oCACZ,EAAE;;;;;;;;gBAQI,EAAO,YAAY;uBACT,EAAO,UAAU,MAAQ,QAAU,KAAM;;yBAExC,CAAC,EAAO,UAAU,OAAS,CAAC,EAAO,WAAW,MAAM;gBAC7D,EAAO,UAAU,OAAS,EAAO,WAAW,MAAM;;;KAIlE,EACA,OAAQ,CAAC,EAAA,gBAAiB,EAAA,iBAAiB,EAAG,EAAA,mBAAoB,EAAA,OAAe,CACnF,CAAC"}
|
|
1
|
+
{"version":3,"file":"radio-group.cjs","names":[],"sources":["../src/inputs/radio-group/radio-group.ts"],"sourcesContent":["import {\n createContext,\n createStableId,\n define,\n html,\n inject,\n prop,\n bind,\n getHost,\n onCleanup,\n provide,\n useEmit,\n useSlots,\n watchEffect,\n} from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { type Readable } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\n createListControl,\n getChoiceLabel,\n getLightChildrenByTag,\n} from '../../headless';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, sizeVariantMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './radio-group.css?inline';\n\n/** Radio group component properties */\nexport type OreRadioGroupProps = {\n /** Theme color tint */\n color?: ThemeColor;\n /** Disabled state */\n disabled?: boolean;\n /** Error message text */\n error?: string;\n /** Helper text displayed below the items */\n helper?: string;\n /** Group label text */\n label?: string;\n /** Form field name */\n name?: string;\n /** Layout orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Required field */\n required?: boolean;\n /** Items size preset */\n size?: ComponentSize;\n /** Initial selected value */\n value?: string;\n};\n\nexport type RadioGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n name: Readable<string | undefined>;\n select: (value: string, originalEvent?: Event) => void;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n};\n\nexport const RADIO_GROUP_CTX = createContext<RadioGroupContext | undefined>('OreRadioGroup');\n\n/** Events emitted by the radio-group component */\nexport type OreRadioGroupEvents = {\n /** Emitted when the selection changes */\n change: ChoiceChangeDetail;\n};\n\n/**\n * A group of radio buttons that allows users to select a single option from a set.\n * Supports keyboard navigation (arrows) and automatic value management.\n *\n * @element ore-radio-group\n *\n * @attr {string} value - Selected value\n * @attr {string} name - Form field name\n * @attr {string} label - Group label\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when a radio is selected. detail: { values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-radio` elements here\n *\n * @cssprop --radio-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --radio-group-gap - Gap between radio items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-radio-group name=\"plan\" label=\"Choose a plan\" value=\"free\" required>\n * <ore-radio value=\"free\">Free</ore-radio>\n * <ore-radio value=\"pro\">Pro</ore-radio>\n * <ore-radio value=\"enterprise\">Enterprise</ore-radio>\n * </ore-radio-group>\n * <ore-radio-group name=\"direction\" orientation=\"horizontal\" color=\"primary\">\n * <ore-radio value=\"left\">Left</ore-radio>\n * <ore-radio value=\"right\">Right</ore-radio>\n * </ore-radio-group>\n * ```\n */\nexport const RADIO_GROUP_TAG = 'ore-radio-group' as const;\ndefine<OreRadioGroupProps>(RADIO_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.oneOf(['horizontal', 'vertical'] as const, 'vertical'),\n required: prop.bool(false),\n // Not auto-reflected (`reflect: false`) — the derived, interaction-updated selection\n // (`selectedValue`) is the single writer for this attribute, via `bind()` below; letting\n // `prop.string()`'s own default reflection also write the raw incoming value would leave\n // two effects racing to set the same attribute from different sources.\n value: { ...prop.string(), reflect: false },\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioGroupEvents>();\n const slots = useSlots();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const choice = createChoiceField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n prefix: 'radio-group',\n required: props.required,\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\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 selectedValue = choice.selectedValue;\n const isDisabled = fCtxProps.disabled;\n\n bind({\n attr: {\n size: fCtxProps.size,\n value: () => selectedValue.value || null,\n },\n });\n\n const getSlottedRadios = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-radio');\n\n const getEnabledRadios = (): HTMLElement[] =>\n isDisabled.value ? [] : getSlottedRadios().filter((radio) => !radio.hasAttribute('disabled'));\n\n const getLabelForValue = (value: string): string => getChoiceLabel(getSlottedRadios(), value);\n\n const selectRadio = (val: string, originalEvent?: Event): void => {\n choice.setValues(val ? [val] : []);\n\n const labels = val ? [getLabelForValue(val)] : [];\n const values = val ? [val] : [];\n\n emit('change', { labels, originalEvent, values });\n choice.triggerValidation('blur');\n };\n\n provide(RADIO_GROUP_CTX, {\n color: props.color as Readable<ThemeColor | undefined>,\n disabled: isDisabled,\n name: props.name,\n select: selectRadio,\n size: props.size as Readable<ComponentSize | undefined>,\n value: selectedValue,\n });\n\n // Sync name/color/size/disabled/checked onto slotted ore-radio children.\n watch(() => {\n void slots.elements().value;\n void selectedValue.value;\n\n const radios = getSlottedRadios();\n\n for (const radio of radios) {\n const val = radio.getAttribute('value') ?? '';\n\n radio.toggleAttribute('checked', val === selectedValue.value);\n\n if (props.name.value) radio.setAttribute('name', props.name.value);\n else radio.removeAttribute('name');\n\n if (props.color.value) radio.setAttribute('color', props.color.value);\n else radio.removeAttribute('color');\n\n if (props.size.value) radio.setAttribute('size', props.size.value);\n else radio.removeAttribute('size');\n\n radio.toggleAttribute('disabled', isDisabled.value);\n }\n });\n\n // Roving tabindex: only the selected (or first) radio is tabbable.\n watch(() => {\n void slots.elements().value;\n\n const radios = getSlottedRadios();\n let hasFocusable = false;\n\n for (const radio of radios) {\n const isSelected = radio.getAttribute('value') === selectedValue.value;\n\n radio.setAttribute('tabindex', isSelected && !isDisabled.value ? '0' : '-1');\n\n if (isSelected && !isDisabled.value) hasFocusable = true;\n }\n\n if (!hasFocusable && radios.length > 0) {\n const first = radios.find((r) => !r.hasAttribute('disabled'));\n\n if (first) first.setAttribute('tabindex', '0');\n }\n });\n\n const listControl = createListControl<HTMLElement>({\n getItems: getEnabledRadios,\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const radio = getEnabledRadios()[index];\n\n if (!radio) return;\n\n radio.focus();\n\n if (radio.tagName === 'ORE-RADIO') {\n selectRadio(radio.getAttribute('value') ?? '', event);\n }\n },\n });\n\n bind({\n on: {\n change: (e: Event) => {\n if (e.target === el) return;\n\n e.stopPropagation();\n selectRadio((e.target as HTMLElement).getAttribute('value') ?? '', e);\n },\n keydown: (e: KeyboardEvent) => {\n const radios = getEnabledRadios();\n\n if (!radios.length) return;\n\n const focused = radios.indexOf(document.activeElement as HTMLElement);\n\n if (focused === -1) return;\n\n listControl.set(focused);\n listControl.handleKeydown(e);\n },\n },\n });\n\n const legendId = createStableId('radio-group-legend');\n\n return html`\n <fieldset\n role=\"radiogroup\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n :aria-invalid=\"${choice.ariaInvalid}\"\n :aria-errormessage=\"${choice.ariaErrorMessage}\"\n :aria-describedby=\"${choice.ariaDescribedBy}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"radio-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div\n class=\"helper-text\"\n part=\"helper\"\n id=\"${choice.assistiveId}\"\n :role=\"${() => (choice.errorText.value ? 'alert' : null)}\"\n aria-live=\"polite\"\n ?hidden=\"${() => !choice.errorText.value && !choice.helperText.value}\">\n ${() => choice.errorText.value || choice.helperText.value}\n </div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"qsBAmEA,IAAa,GAAA,EAAA,EAAA,cAAA,CAA+D,eAAe,EAwC9E,EAAkB,+BACJ,EAAiB,CAC1C,eAAgB,GAChB,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,iBACH,MAAO,EAAA,KAAK,OAAO,EACnB,OAAQ,EAAA,KAAK,OAAO,EACpB,MAAO,EAAA,KAAK,OAAO,EACnB,KAAM,EAAA,KAAK,OAAO,EAClB,YAAa,EAAA,KAAK,MAAM,CAAC,aAAc,UAAU,EAAY,UAAU,EACvE,SAAU,EAAA,KAAK,KAAK,EAAK,EAKzB,MAAO,CAAE,GAAG,EAAA,KAAK,OAAO,EAAG,QAAS,EAAM,CAC5C,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAAa,EACb,GAAA,EAAA,EAAA,QAAA,CAAoC,EACpC,GAAA,EAAA,EAAA,SAAA,CAAiB,EACjB,EAAQ,EAAA,YAER,GAAA,EAAA,EAAA,OAAA,CAAiB,EAAA,QAAQ,EACzB,EAAY,EAAA,eAAe,EAAO,CAAO,EAEzC,EAAS,EAAA,kBAAkB,CAC/B,SAAU,EAAU,SACpB,MAAO,EAAM,MACb,OAAQ,EAAM,OACd,MAAO,EAAM,MACb,OAAQ,cACR,SAAU,EAAM,SAChB,OAAQ,EAAA,gBAAgB,EAAA,SAAS,EACjC,WAAY,GAAS,WACrB,MAAO,EAAM,KACf,CAAC,EAED,EAAO,gBACL,EAAA,SAAiB,CACf,SAAU,EAAO,SACjB,QAAS,EAAO,MAChB,YAAc,GAAM,EACpB,kBAAmB,EAAO,kBAC1B,SAAU,EAAO,SACjB,MAAO,EAAO,SAChB,CAAC,CACH,EAEA,IAAM,EAAgB,EAAO,cACvB,EAAa,EAAU,UAE7B,EAAA,EAAA,KAAA,CAAK,CACH,KAAM,CACJ,KAAM,EAAU,KAChB,UAAa,EAAc,OAAS,IACtC,CACF,CAAC,EAED,IAAM,MAAwC,EAAA,sBAAsB,EAAI,WAAW,EAE7E,MACJ,EAAW,MAAQ,CAAC,EAAI,EAAiB,CAAC,CAAC,OAAQ,GAAU,CAAC,EAAM,aAAa,UAAU,CAAC,EAExF,EAAoB,GAA0B,EAAA,eAAe,EAAiB,EAAG,CAAK,EAEtF,GAAe,EAAa,IAAgC,CAChE,EAAO,UAAU,EAAM,CAAC,CAAG,EAAI,CAAC,CAAC,EAEjC,IAAM,EAAS,EAAM,CAAC,EAAiB,CAAG,CAAC,EAAI,CAAC,EAGhD,EAAK,SAAU,CAAE,SAAQ,gBAAe,OAFzB,EAAM,CAAC,CAAG,EAAI,CAAC,CAEiB,CAAC,EAChD,EAAO,kBAAkB,MAAM,CACjC,GAEA,EAAA,EAAA,QAAA,CAAQ,EAAiB,CACvB,MAAO,EAAM,MACb,SAAU,EACV,KAAM,EAAM,KACZ,OAAQ,EACR,KAAM,EAAM,KACZ,MAAO,CACT,CAAC,EAGD,MAAY,CACV,EAAW,SAAS,CAAC,CAAC,MACtB,EAAmB,MAEnB,IAAM,EAAS,EAAiB,EAEhC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAM,EAAM,aAAa,OAAO,GAAK,GAE3C,EAAM,gBAAgB,UAAW,IAAQ,EAAc,KAAK,EAExD,EAAM,KAAK,MAAO,EAAM,aAAa,OAAQ,EAAM,KAAK,KAAK,EAC5D,EAAM,gBAAgB,MAAM,EAE7B,EAAM,MAAM,MAAO,EAAM,aAAa,QAAS,EAAM,MAAM,KAAK,EAC/D,EAAM,gBAAgB,OAAO,EAE9B,EAAM,KAAK,MAAO,EAAM,aAAa,OAAQ,EAAM,KAAK,KAAK,EAC5D,EAAM,gBAAgB,MAAM,EAEjC,EAAM,gBAAgB,WAAY,EAAW,KAAK,CACpD,CACF,CAAC,EAGD,MAAY,CACV,EAAW,SAAS,CAAC,CAAC,MAEtB,IAAM,EAAS,EAAiB,EAC5B,EAAe,GAEnB,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAa,EAAM,aAAa,OAAO,IAAM,EAAc,MAEjE,EAAM,aAAa,WAAY,GAAc,CAAC,EAAW,MAAQ,IAAM,IAAI,EAEvE,GAAc,CAAC,EAAW,QAAO,EAAe,GACtD,CAEA,GAAI,CAAC,GAAgB,EAAO,OAAS,EAAG,CACtC,IAAM,EAAQ,EAAO,KAAM,GAAM,CAAC,EAAE,aAAa,UAAU,CAAC,EAExD,GAAO,EAAM,aAAa,WAAY,GAAG,CAC/C,CACF,CAAC,EAED,IAAM,EAAc,EAAA,kBAA+B,CACjD,SAAU,EACV,KAAM,CAAE,KAAM,CAAC,YAAa,YAAY,EAAG,KAAM,CAAC,UAAW,WAAW,CAAE,EAC1E,KAAM,GACN,YAAa,EAAS,EAAO,IAAU,CACrC,IAAM,EAAQ,EAAiB,CAAC,CAAC,GAE5B,IAEL,EAAM,MAAM,EAER,EAAM,UAAY,aACpB,EAAY,EAAM,aAAa,OAAO,GAAK,GAAI,CAAK,EAExD,CACF,CAAC,GAED,EAAA,EAAA,KAAA,CAAK,CACH,GAAI,CACF,OAAS,GAAa,CAChB,EAAE,SAAW,IAEjB,EAAE,gBAAgB,EAClB,EAAa,EAAE,OAAuB,aAAa,OAAO,GAAK,GAAI,CAAC,EACtE,EACA,QAAU,GAAqB,CAC7B,IAAM,EAAS,EAAiB,EAEhC,GAAI,CAAC,EAAO,OAAQ,OAEpB,IAAM,EAAU,EAAO,QAAQ,SAAS,aAA4B,EAEhE,IAAY,KAEhB,EAAY,IAAI,CAAO,EACvB,EAAY,cAAc,CAAC,EAC7B,CACF,CACF,CAAC,EAED,IAAM,GAAA,EAAA,EAAA,eAAA,CAA0B,oBAAoB,EAEpD,MAAO,GAAA,IAAI;;;6BAGgB,OAAO,EAAQ,EAAM,SAAS,KAAM,EAAE;yBAC5C,EAAO,YAAY;8BACd,EAAO,iBAAiB;6BACzB,EAAO,gBAAgB;sBAC9B,EAAS,gBAAkB,CAAC,EAAM,MAAM,MAAM;YACxD,EAAM,QAAQ,EAAA,SACR,EAAQ,EAAM,SAAS,UACvB,EAAA,IAAI,oCACZ,EAAE;;;;;;;;gBAQI,EAAO,YAAY;uBACT,EAAO,UAAU,MAAQ,QAAU,KAAM;;yBAExC,CAAC,EAAO,UAAU,OAAS,CAAC,EAAO,WAAW,MAAM;gBAC7D,EAAO,UAAU,OAAS,EAAO,WAAW,MAAM;;;KAIlE,EACA,OAAQ,CAAC,EAAA,gBAAiB,EAAA,iBAAiB,EAAG,EAAA,mBAAoB,EAAA,OAAe,CACnF,CAAC"}
|
package/dist/radio-group.js
CHANGED
|
@@ -28,97 +28,103 @@ y(A, {
|
|
|
28
28
|
name: w.string(),
|
|
29
29
|
orientation: w.oneOf(["horizontal", "vertical"], "vertical"),
|
|
30
30
|
required: w.bool(!1),
|
|
31
|
-
value:
|
|
31
|
+
value: {
|
|
32
|
+
...w.string(),
|
|
33
|
+
reflect: !1
|
|
34
|
+
}
|
|
32
35
|
},
|
|
33
36
|
setup(t) {
|
|
34
|
-
let n = b(), r = E(), i = D(), a = O, o = S(p), h = m(t, o), _ =
|
|
37
|
+
let n = b(), r = E(), i = D(), a = O, o = S(p), h = m(t, o), _ = c({
|
|
35
38
|
disabled: h.disabled,
|
|
36
39
|
error: t.error,
|
|
37
|
-
getFormField: () => _,
|
|
38
40
|
helper: t.helper,
|
|
39
41
|
label: t.label,
|
|
40
42
|
prefix: "radio-group",
|
|
43
|
+
required: t.required,
|
|
41
44
|
signal: s(C),
|
|
42
45
|
validateOn: o?.validateOn,
|
|
43
46
|
value: t.value
|
|
44
47
|
});
|
|
45
|
-
_
|
|
46
|
-
disabled:
|
|
48
|
+
_.attachFormField(f({
|
|
49
|
+
disabled: _.disabled,
|
|
50
|
+
onReset: _.reset,
|
|
47
51
|
toFormValue: (e) => e,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
validationMessage: _.validationMessage,
|
|
53
|
+
validity: _.validity,
|
|
54
|
+
value: _.formValue
|
|
55
|
+
}));
|
|
56
|
+
let y = _.selectedValue, w = h.disabled;
|
|
51
57
|
g({ attr: {
|
|
52
58
|
size: h.size,
|
|
53
|
-
value: () =>
|
|
59
|
+
value: () => y.value || null
|
|
54
60
|
} });
|
|
55
|
-
let
|
|
56
|
-
|
|
57
|
-
let n = e ? [
|
|
61
|
+
let A = () => d(n, "ore-radio"), j = () => w.value ? [] : A().filter((e) => !e.hasAttribute("disabled")), M = (e) => u(A(), e), N = (e, t) => {
|
|
62
|
+
_.setValues(e ? [e] : []);
|
|
63
|
+
let n = e ? [M(e)] : [];
|
|
58
64
|
r("change", {
|
|
59
65
|
labels: n,
|
|
60
66
|
originalEvent: t,
|
|
61
67
|
values: e ? [e] : []
|
|
62
|
-
}),
|
|
68
|
+
}), _.triggerValidation("blur");
|
|
63
69
|
};
|
|
64
70
|
T(k, {
|
|
65
71
|
color: t.color,
|
|
66
|
-
disabled:
|
|
72
|
+
disabled: w,
|
|
67
73
|
name: t.name,
|
|
68
|
-
select:
|
|
74
|
+
select: N,
|
|
69
75
|
size: t.size,
|
|
70
|
-
value:
|
|
76
|
+
value: y
|
|
71
77
|
}), a(() => {
|
|
72
|
-
i.elements().value,
|
|
73
|
-
let e =
|
|
78
|
+
i.elements().value, y.value;
|
|
79
|
+
let e = A();
|
|
74
80
|
for (let n of e) {
|
|
75
81
|
let e = n.getAttribute("value") ?? "";
|
|
76
|
-
n.toggleAttribute("checked", e ===
|
|
82
|
+
n.toggleAttribute("checked", e === y.value), t.name.value ? n.setAttribute("name", t.name.value) : n.removeAttribute("name"), t.color.value ? n.setAttribute("color", t.color.value) : n.removeAttribute("color"), t.size.value ? n.setAttribute("size", t.size.value) : n.removeAttribute("size"), n.toggleAttribute("disabled", w.value);
|
|
77
83
|
}
|
|
78
84
|
}), a(() => {
|
|
79
85
|
i.elements().value;
|
|
80
|
-
let e =
|
|
86
|
+
let e = A(), t = !1;
|
|
81
87
|
for (let n of e) {
|
|
82
|
-
let e = n.getAttribute("value") ===
|
|
83
|
-
n.setAttribute("tabindex", e && !
|
|
88
|
+
let e = n.getAttribute("value") === y.value;
|
|
89
|
+
n.setAttribute("tabindex", e && !w.value ? "0" : "-1"), e && !w.value && (t = !0);
|
|
84
90
|
}
|
|
85
91
|
if (!t && e.length > 0) {
|
|
86
92
|
let t = e.find((e) => !e.hasAttribute("disabled"));
|
|
87
93
|
t && t.setAttribute("tabindex", "0");
|
|
88
94
|
}
|
|
89
95
|
});
|
|
90
|
-
let
|
|
91
|
-
getItems:
|
|
96
|
+
let P = l({
|
|
97
|
+
getItems: j,
|
|
92
98
|
keys: {
|
|
93
99
|
next: ["ArrowDown", "ArrowRight"],
|
|
94
100
|
prev: ["ArrowUp", "ArrowLeft"]
|
|
95
101
|
},
|
|
96
102
|
loop: !0,
|
|
97
103
|
onNavigate: (e, t, n) => {
|
|
98
|
-
let r =
|
|
99
|
-
r && (r.focus(), r.tagName === "ORE-RADIO" &&
|
|
104
|
+
let r = j()[t];
|
|
105
|
+
r && (r.focus(), r.tagName === "ORE-RADIO" && N(r.getAttribute("value") ?? "", n));
|
|
100
106
|
}
|
|
101
107
|
});
|
|
102
108
|
g({ on: {
|
|
103
109
|
change: (e) => {
|
|
104
|
-
e.target !== n && (e.stopPropagation(),
|
|
110
|
+
e.target !== n && (e.stopPropagation(), N(e.target.getAttribute("value") ?? "", e));
|
|
105
111
|
},
|
|
106
112
|
keydown: (e) => {
|
|
107
|
-
let t =
|
|
113
|
+
let t = j();
|
|
108
114
|
if (!t.length) return;
|
|
109
115
|
let n = t.indexOf(document.activeElement);
|
|
110
|
-
n !== -1 && (
|
|
116
|
+
n !== -1 && (P.set(n), P.handleKeydown(e));
|
|
111
117
|
}
|
|
112
118
|
} });
|
|
113
|
-
let
|
|
119
|
+
let F = v("radio-group-legend");
|
|
114
120
|
return x`
|
|
115
121
|
<fieldset
|
|
116
122
|
role="radiogroup"
|
|
117
123
|
aria-required="${() => String(!!t.required.value)}"
|
|
118
|
-
:aria-invalid="${
|
|
119
|
-
:aria-errormessage="${
|
|
120
|
-
:aria-describedby="${
|
|
121
|
-
<legend id="${
|
|
124
|
+
:aria-invalid="${_.ariaInvalid}"
|
|
125
|
+
:aria-errormessage="${_.ariaErrorMessage}"
|
|
126
|
+
:aria-describedby="${_.ariaDescribedBy}">
|
|
127
|
+
<legend id="${F}" ?hidden=${() => !t.label.value}>
|
|
122
128
|
${t.label}${e(() => !!t.required.value, () => x`<span aria-hidden="true"> *</span>`)}
|
|
123
129
|
</legend>
|
|
124
130
|
<div class="radio-group-items" part="items">
|
|
@@ -127,11 +133,11 @@ y(A, {
|
|
|
127
133
|
<div
|
|
128
134
|
class="helper-text"
|
|
129
135
|
part="helper"
|
|
130
|
-
id="${
|
|
131
|
-
:role="${() =>
|
|
136
|
+
id="${_.assistiveId}"
|
|
137
|
+
:role="${() => _.errorText.value ? "alert" : null}"
|
|
132
138
|
aria-live="polite"
|
|
133
|
-
?hidden="${() => !
|
|
134
|
-
${() =>
|
|
139
|
+
?hidden="${() => !_.errorText.value && !_.helperText.value}">
|
|
140
|
+
${() => _.errorText.value || _.helperText.value}
|
|
135
141
|
</div>
|
|
136
142
|
</fieldset>
|
|
137
143
|
`;
|
package/dist/radio-group.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio-group.js","names":[],"sources":["../src/inputs/radio-group/radio-group.ts"],"sourcesContent":["import {\n createContext,\n createStableId,\n define,\n html,\n inject,\n prop,\n bind,\n getHost,\n onCleanup,\n provide,\n useEmit,\n useSlots,\n watchEffect,\n} from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { type Readable } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\n createListControl,\n getChoiceLabel,\n getLightChildrenByTag,\n} from '../../headless';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, sizeVariantMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './radio-group.css?inline';\n\n/** Radio group component properties */\nexport type OreRadioGroupProps = {\n /** Theme color tint */\n color?: ThemeColor;\n /** Disabled state */\n disabled?: boolean;\n /** Error message text */\n error?: string;\n /** Helper text displayed below the items */\n helper?: string;\n /** Group label text */\n label?: string;\n /** Form field name */\n name?: string;\n /** Layout orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Required field */\n required?: boolean;\n /** Items size preset */\n size?: ComponentSize;\n /** Initial selected value */\n value?: string;\n};\n\nexport type RadioGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n name: Readable<string | undefined>;\n select: (value: string, originalEvent?: Event) => void;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n};\n\nexport const RADIO_GROUP_CTX = createContext<RadioGroupContext | undefined>('OreRadioGroup');\n\n/** Events emitted by the radio-group component */\nexport type OreRadioGroupEvents = {\n /** Emitted when the selection changes */\n change: ChoiceChangeDetail;\n};\n\n/**\n * A group of radio buttons that allows users to select a single option from a set.\n * Supports keyboard navigation (arrows) and automatic value management.\n *\n * @element ore-radio-group\n *\n * @attr {string} value - Selected value\n * @attr {string} name - Form field name\n * @attr {string} label - Group label\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when a radio is selected. detail: { values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-radio` elements here\n *\n * @cssprop --radio-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --radio-group-gap - Gap between radio items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-radio-group name=\"plan\" label=\"Choose a plan\" value=\"free\" required>\n * <ore-radio value=\"free\">Free</ore-radio>\n * <ore-radio value=\"pro\">Pro</ore-radio>\n * <ore-radio value=\"enterprise\">Enterprise</ore-radio>\n * </ore-radio-group>\n * <ore-radio-group name=\"direction\" orientation=\"horizontal\" color=\"primary\">\n * <ore-radio value=\"left\">Left</ore-radio>\n * <ore-radio value=\"right\">Right</ore-radio>\n * </ore-radio-group>\n * ```\n */\nexport const RADIO_GROUP_TAG = 'ore-radio-group' as const;\ndefine<OreRadioGroupProps>(RADIO_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.oneOf(['horizontal', 'vertical'] as const, 'vertical'),\n required: prop.bool(false),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioGroupEvents>();\n const slots = useSlots();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n let _formField: { reportValidity(): void } | null = null;\n const choice = createChoiceField({\n disabled: fCtxProps.disabled,\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n label: props.label,\n prefix: 'radio-group',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string>({ disabled: choice.disabled, toFormValue: (v) => v, value: choice.formValue });\n\n const selectedValue = choice.selectedValue;\n const isDisabled = fCtxProps.disabled;\n\n bind({\n attr: {\n size: fCtxProps.size,\n value: () => selectedValue.value || null,\n },\n });\n\n const getSlottedRadios = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-radio');\n\n const getEnabledRadios = (): HTMLElement[] =>\n isDisabled.value ? [] : getSlottedRadios().filter((radio) => !radio.hasAttribute('disabled'));\n\n const getLabelForValue = (value: string): string => getChoiceLabel(getSlottedRadios(), value);\n\n const selectRadio = (val: string, originalEvent?: Event): void => {\n choice.setValues(val ? [val] : []);\n\n const labels = val ? [getLabelForValue(val)] : [];\n const values = val ? [val] : [];\n\n emit('change', { labels, originalEvent, values });\n choice.triggerValidation('blur');\n };\n\n provide(RADIO_GROUP_CTX, {\n color: props.color as Readable<ThemeColor | undefined>,\n disabled: isDisabled,\n name: props.name,\n select: selectRadio,\n size: props.size as Readable<ComponentSize | undefined>,\n value: selectedValue,\n });\n\n // Sync name/color/size/disabled/checked onto slotted ore-radio children.\n watch(() => {\n void slots.elements().value;\n void selectedValue.value;\n\n const radios = getSlottedRadios();\n\n for (const radio of radios) {\n const val = radio.getAttribute('value') ?? '';\n\n radio.toggleAttribute('checked', val === selectedValue.value);\n\n if (props.name.value) radio.setAttribute('name', props.name.value);\n else radio.removeAttribute('name');\n\n if (props.color.value) radio.setAttribute('color', props.color.value);\n else radio.removeAttribute('color');\n\n if (props.size.value) radio.setAttribute('size', props.size.value);\n else radio.removeAttribute('size');\n\n radio.toggleAttribute('disabled', isDisabled.value);\n }\n });\n\n // Roving tabindex: only the selected (or first) radio is tabbable.\n watch(() => {\n void slots.elements().value;\n\n const radios = getSlottedRadios();\n let hasFocusable = false;\n\n for (const radio of radios) {\n const isSelected = radio.getAttribute('value') === selectedValue.value;\n\n radio.setAttribute('tabindex', isSelected && !isDisabled.value ? '0' : '-1');\n\n if (isSelected && !isDisabled.value) hasFocusable = true;\n }\n\n if (!hasFocusable && radios.length > 0) {\n const first = radios.find((r) => !r.hasAttribute('disabled'));\n\n if (first) first.setAttribute('tabindex', '0');\n }\n });\n\n const listControl = createListControl<HTMLElement>({\n getItems: getEnabledRadios,\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const radio = getEnabledRadios()[index];\n\n if (!radio) return;\n\n radio.focus();\n\n if (radio.tagName === 'ORE-RADIO') {\n selectRadio(radio.getAttribute('value') ?? '', event);\n }\n },\n });\n\n bind({\n on: {\n change: (e: Event) => {\n if (e.target === el) return;\n\n e.stopPropagation();\n selectRadio((e.target as HTMLElement).getAttribute('value') ?? '', e);\n },\n keydown: (e: KeyboardEvent) => {\n const radios = getEnabledRadios();\n\n if (!radios.length) return;\n\n const focused = radios.indexOf(document.activeElement as HTMLElement);\n\n if (focused === -1) return;\n\n listControl.set(focused);\n listControl.handleKeydown(e);\n },\n },\n });\n\n const legendId = createStableId('radio-group-legend');\n\n return html`\n <fieldset\n role=\"radiogroup\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n :aria-invalid=\"${choice.ariaInvalid}\"\n :aria-errormessage=\"${choice.ariaErrorMessage}\"\n :aria-describedby=\"${choice.ariaDescribedBy}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"radio-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div\n class=\"helper-text\"\n part=\"helper\"\n id=\"${choice.assistiveId}\"\n :role=\"${() => (choice.errorText.value ? 'alert' : null)}\"\n aria-live=\"polite\"\n ?hidden=\"${() => !choice.errorText.value && !choice.helperText.value}\">\n ${() => choice.errorText.value || choice.helperText.value}\n </div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"radio-group.js","names":[],"sources":["../src/inputs/radio-group/radio-group.ts"],"sourcesContent":["import {\n createContext,\n createStableId,\n define,\n html,\n inject,\n prop,\n bind,\n getHost,\n onCleanup,\n provide,\n useEmit,\n useSlots,\n watchEffect,\n} from '@vielzeug/ore';\nimport { when } from '@vielzeug/ore/directives';\nimport { useField } from '@vielzeug/ore/forms';\nimport { type Readable } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\n createListControl,\n getChoiceLabel,\n getLightChildrenByTag,\n} from '../../headless';\nimport { disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport { colorThemeMixin, disabledStateMixin, sizeVariantMixin } from '../../styles';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport componentStyles from './radio-group.css?inline';\n\n/** Radio group component properties */\nexport type OreRadioGroupProps = {\n /** Theme color tint */\n color?: ThemeColor;\n /** Disabled state */\n disabled?: boolean;\n /** Error message text */\n error?: string;\n /** Helper text displayed below the items */\n helper?: string;\n /** Group label text */\n label?: string;\n /** Form field name */\n name?: string;\n /** Layout orientation */\n orientation?: 'horizontal' | 'vertical';\n /** Required field */\n required?: boolean;\n /** Items size preset */\n size?: ComponentSize;\n /** Initial selected value */\n value?: string;\n};\n\nexport type RadioGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n name: Readable<string | undefined>;\n select: (value: string, originalEvent?: Event) => void;\n size: Readable<ComponentSize | undefined>;\n value: Readable<string | undefined>;\n};\n\nexport const RADIO_GROUP_CTX = createContext<RadioGroupContext | undefined>('OreRadioGroup');\n\n/** Events emitted by the radio-group component */\nexport type OreRadioGroupEvents = {\n /** Emitted when the selection changes */\n change: ChoiceChangeDetail;\n};\n\n/**\n * A group of radio buttons that allows users to select a single option from a set.\n * Supports keyboard navigation (arrows) and automatic value management.\n *\n * @element ore-radio-group\n *\n * @attr {string} value - Selected value\n * @attr {string} name - Form field name\n * @attr {string} label - Group label\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when a radio is selected. detail: { values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-radio` elements here\n *\n * @cssprop --radio-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --radio-group-gap - Gap between radio items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-radio-group name=\"plan\" label=\"Choose a plan\" value=\"free\" required>\n * <ore-radio value=\"free\">Free</ore-radio>\n * <ore-radio value=\"pro\">Pro</ore-radio>\n * <ore-radio value=\"enterprise\">Enterprise</ore-radio>\n * </ore-radio-group>\n * <ore-radio-group name=\"direction\" orientation=\"horizontal\" color=\"primary\">\n * <ore-radio value=\"left\">Left</ore-radio>\n * <ore-radio value=\"right\">Right</ore-radio>\n * </ore-radio-group>\n * ```\n */\nexport const RADIO_GROUP_TAG = 'ore-radio-group' as const;\ndefine<OreRadioGroupProps>(RADIO_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.oneOf(['horizontal', 'vertical'] as const, 'vertical'),\n required: prop.bool(false),\n // Not auto-reflected (`reflect: false`) — the derived, interaction-updated selection\n // (`selectedValue`) is the single writer for this attribute, via `bind()` below; letting\n // `prop.string()`'s own default reflection also write the raw incoming value would leave\n // two effects racing to set the same attribute from different sources.\n value: { ...prop.string(), reflect: false },\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioGroupEvents>();\n const slots = useSlots();\n const watch = watchEffect;\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const choice = createChoiceField({\n disabled: fCtxProps.disabled,\n error: props.error,\n helper: props.helper,\n label: props.label,\n prefix: 'radio-group',\n required: props.required,\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\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 selectedValue = choice.selectedValue;\n const isDisabled = fCtxProps.disabled;\n\n bind({\n attr: {\n size: fCtxProps.size,\n value: () => selectedValue.value || null,\n },\n });\n\n const getSlottedRadios = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-radio');\n\n const getEnabledRadios = (): HTMLElement[] =>\n isDisabled.value ? [] : getSlottedRadios().filter((radio) => !radio.hasAttribute('disabled'));\n\n const getLabelForValue = (value: string): string => getChoiceLabel(getSlottedRadios(), value);\n\n const selectRadio = (val: string, originalEvent?: Event): void => {\n choice.setValues(val ? [val] : []);\n\n const labels = val ? [getLabelForValue(val)] : [];\n const values = val ? [val] : [];\n\n emit('change', { labels, originalEvent, values });\n choice.triggerValidation('blur');\n };\n\n provide(RADIO_GROUP_CTX, {\n color: props.color as Readable<ThemeColor | undefined>,\n disabled: isDisabled,\n name: props.name,\n select: selectRadio,\n size: props.size as Readable<ComponentSize | undefined>,\n value: selectedValue,\n });\n\n // Sync name/color/size/disabled/checked onto slotted ore-radio children.\n watch(() => {\n void slots.elements().value;\n void selectedValue.value;\n\n const radios = getSlottedRadios();\n\n for (const radio of radios) {\n const val = radio.getAttribute('value') ?? '';\n\n radio.toggleAttribute('checked', val === selectedValue.value);\n\n if (props.name.value) radio.setAttribute('name', props.name.value);\n else radio.removeAttribute('name');\n\n if (props.color.value) radio.setAttribute('color', props.color.value);\n else radio.removeAttribute('color');\n\n if (props.size.value) radio.setAttribute('size', props.size.value);\n else radio.removeAttribute('size');\n\n radio.toggleAttribute('disabled', isDisabled.value);\n }\n });\n\n // Roving tabindex: only the selected (or first) radio is tabbable.\n watch(() => {\n void slots.elements().value;\n\n const radios = getSlottedRadios();\n let hasFocusable = false;\n\n for (const radio of radios) {\n const isSelected = radio.getAttribute('value') === selectedValue.value;\n\n radio.setAttribute('tabindex', isSelected && !isDisabled.value ? '0' : '-1');\n\n if (isSelected && !isDisabled.value) hasFocusable = true;\n }\n\n if (!hasFocusable && radios.length > 0) {\n const first = radios.find((r) => !r.hasAttribute('disabled'));\n\n if (first) first.setAttribute('tabindex', '0');\n }\n });\n\n const listControl = createListControl<HTMLElement>({\n getItems: getEnabledRadios,\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const radio = getEnabledRadios()[index];\n\n if (!radio) return;\n\n radio.focus();\n\n if (radio.tagName === 'ORE-RADIO') {\n selectRadio(radio.getAttribute('value') ?? '', event);\n }\n },\n });\n\n bind({\n on: {\n change: (e: Event) => {\n if (e.target === el) return;\n\n e.stopPropagation();\n selectRadio((e.target as HTMLElement).getAttribute('value') ?? '', e);\n },\n keydown: (e: KeyboardEvent) => {\n const radios = getEnabledRadios();\n\n if (!radios.length) return;\n\n const focused = radios.indexOf(document.activeElement as HTMLElement);\n\n if (focused === -1) return;\n\n listControl.set(focused);\n listControl.handleKeydown(e);\n },\n },\n });\n\n const legendId = createStableId('radio-group-legend');\n\n return html`\n <fieldset\n role=\"radiogroup\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n :aria-invalid=\"${choice.ariaInvalid}\"\n :aria-errormessage=\"${choice.ariaErrorMessage}\"\n :aria-describedby=\"${choice.ariaDescribedBy}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"radio-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div\n class=\"helper-text\"\n part=\"helper\"\n id=\"${choice.assistiveId}\"\n :role=\"${() => (choice.errorText.value ? 'alert' : null)}\"\n aria-live=\"polite\"\n ?hidden=\"${() => !choice.errorText.value && !choice.helperText.value}\">\n ${() => choice.errorText.value || choice.helperText.value}\n </div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/radio.cjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/states.css.cjs"),r=require("./styles/mixins/theme.css.cjs"),i=require("./shared/prop-bundles.cjs"),a=require("./shared/size-presets.cjs"),o=require("./headless/ore.cjs"),s=require("./headless/checkable.cjs"),c=require("./headless/nav.cjs"),l=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const u=require("./inputs/shared/form-context.cjs"),d=require("./inputs/shared/templates.cjs"),f=require("./radio-group.cjs"),p=require("./inputs/radio/radio.css.cjs");let m=require("@vielzeug/ore"),h=require("@vielzeug/ripple");var g=`ore-radio`;(0,m.define)(g,{formAssociated:!0,props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,checked:m.prop.bool(!1),error:m.prop.string(),helper:m.prop.string(),name:m.prop.string(),value:m.prop.string()},setup(e){let t=(0,m.getHost)(),n=(0,m.useEmit)(),r=(0,m.inject)(f.RADIO_GROUP_CTX),i=(0,m.inject)(u.FORM_CTX),a=u.useFormContext(e,i),p=(0,h.computed)(()=>r?.name.value||e.name.value||``),g=(0,h.computed)(()=>r?.size.value??a.size.value),_=(0,h.computed)(()=>r?.color.value??e.color.value),v=(0,h.computed)(()=>r?r.value.value===e.value.value:!!e.checked.value),y=()=>{let e=p.value;return e?Array.from(document.querySelectorAll(`ore-radio[name="${e}"]`)).filter(e=>!e.hasAttribute(`disabled`)):[]},b=(e,t)=>{if(r){r.select(e.getAttribute(`value`)??``,t);return}e.click()},x=c.createListControl({getItems:()=>y(),keys:{next:[`ArrowDown`,`ArrowRight`],prev:[`ArrowUp`,`ArrowLeft`]},loop:!0,onNavigate:(e,t,n)=>{let r=y()[t];r&&(r.focus(),b(r,n))}}),S=t=>{if(!
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/accessibility.css.cjs"),t=require("./styles/mixins/shape.css.cjs"),n=require("./styles/mixins/states.css.cjs"),r=require("./styles/mixins/theme.css.cjs"),i=require("./shared/prop-bundles.cjs"),a=require("./shared/size-presets.cjs"),o=require("./headless/ore.cjs"),s=require("./headless/checkable.cjs"),c=require("./headless/nav.cjs"),l=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const u=require("./inputs/shared/form-context.cjs"),d=require("./inputs/shared/templates.cjs"),f=require("./radio-group.cjs"),p=require("./inputs/radio/radio.css.cjs");let m=require("@vielzeug/ore"),h=require("@vielzeug/ripple");var g=`ore-radio`;(0,m.define)(g,{formAssociated:!0,props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,checked:m.prop.bool(!1),error:m.prop.string(),helper:m.prop.string(),name:m.prop.string(),value:m.prop.string()},setup(e){let t=(0,m.getHost)(),n=(0,m.useEmit)(),r=(0,m.inject)(f.RADIO_GROUP_CTX),i=(0,m.inject)(u.FORM_CTX),a=u.useFormContext(e,i),p=(0,h.computed)(()=>r?.name.value||e.name.value||``),g=(0,h.computed)(()=>r?.size.value??a.size.value),_=(0,h.computed)(()=>r?.color.value??e.color.value),v=(0,h.computed)(()=>r?r.value.value===e.value.value:!!e.checked.value),y=()=>{let e=p.value;return e?Array.from(document.querySelectorAll(`ore-radio[name="${e}"]`)).filter(e=>!e.hasAttribute(`disabled`)):[]},b=(e,t)=>{if(r){r.select(e.getAttribute(`value`)??``,t);return}e.click()},x=c.createListControl({getItems:()=>y(),keys:{next:[`ArrowDown`,`ArrowRight`],prev:[`ArrowUp`,`ArrowLeft`]},loop:!0,onNavigate:(e,t,n)=>{let r=y()[t];r&&(r.focus(),b(r,n))}}),S=t=>{if(!C.checked.value){if(r){r.select(e.value.value??``,t);return}C.toggle(t??new Event(`change`))}},C=s.createCheckable({checked:v,disabled:(0,h.computed)(()=>a.disabled.value||!!r?.disabled.value),error:e.error,helper:e.helper,onToggle:e=>{n(`change`,e)},prefix:`radio`,signal:o.lifecycleSignal(m.onCleanup),validateOn:i?.validateOn,value:e.value});C.attachFormField(l.useField({disabled:C.disabled,toFormValue:e=>e,value:C.checkableFormValue}));let{assistiveId:w,checked:T,disabled:E,errorText:D,helperText:O,labelId:k,toggle:A}=C;return(0,m.bind)({attr:{ariaChecked:()=>T.value?`true`:`false`,ariaDescribedby:()=>D.value||O.value?w:null,ariaDisabled:()=>E.value?`true`:null,ariaInvalid:()=>D.value?`true`:null,ariaLabelledby:k,checked:T,color:_,disabled:()=>E.value?!0:void 0,name:()=>p.value||void 0,role:`radio`,size:g,tabindex:()=>{if(!E.value)return T.value?0:-1}},class:()=>({"is-checked":T.value,"is-disabled":E.value}),on:{click:n=>{if(!E.value)if(r)r.select(e.value.value??``,n);else{if(!p.value)return;if(!T.value){let r=e.name.value;document.querySelectorAll(`ore-radio[name="${r}"]`).forEach(e=>{e!==t&&e.removeAttribute(`checked`)}),A(n)}}},keydown:e=>{if(e.key===` `||e.key===`Enter`){e.preventDefault(),S(e);return}let n=y();if(n.length===0)return;let r=n.indexOf(t);r!==-1&&(x.set(r),x.handleKeydown(e))}}}),m.html`
|
|
2
2
|
<div class="radio-wrapper" part="radio">
|
|
3
3
|
<div class="circle" part="circle">
|
|
4
4
|
<div class="dot" part="dot"></div>
|
|
5
5
|
</div>
|
|
6
6
|
</div>
|
|
7
|
-
<span class="label" part="label" id="${
|
|
8
|
-
${d.renderHelperRegion(
|
|
7
|
+
<span class="label" part="label" id="${k}"><slot></slot></span>
|
|
8
|
+
${d.renderHelperRegion(w,D,O)}
|
|
9
9
|
`},styles:[r.colorThemeMixin,r.forcedColorsFormControlMixin,n.disabledStateMixin,e.coarsePointerMixin,t.sizeVariantMixin(a.CONTROL_SIZE_PRESET),p.default]}),exports.RADIO_TAG=g;
|
|
10
10
|
//# sourceMappingURL=radio.cjs.map
|
package/dist/radio.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio.cjs","names":[],"sources":["../src/inputs/radio/radio.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable, createListControl } from '../../headless';\nimport { CONTROL_SIZE_PRESET, disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsFormControlMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { RADIO_GROUP_CTX } from '../radio-group/radio-group';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './radio.css?inline';\n\n/** Radio component properties */\n\nexport type OreRadioEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreRadioProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the radio */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A customizable radio button component for mutually exclusive selections.\n *\n * @element ore-radio\n *\n * @attr {boolean} checked - Checked state\n * @attr {boolean} disabled - Disable radio interaction\n * @attr {string} value - Field value (required for radio groups)\n * @attr {string} name - Form field name (required for radio groups)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Radio size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the radio\n *\n * @fires change - Emitted when radio is selected. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Radio button label text\n *\n * @cssprop --radio-size - Control size (width and height)\n * @cssprop --radio-bg - Unchecked background color\n * @cssprop --radio-border-color - Unchecked border color\n * @cssprop --radio-checked-bg - Selected indicator background color\n * @cssprop --radio-color - Selected indicator dot color\n * @cssprop --radio-font-size - Label font size\n * @part radio - The radio wrapper element\n * @part circle - The visual radio circle\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-radio name=\"plan\" value=\"free\">Free</ore-radio>\n * <ore-radio name=\"plan\" value=\"pro\" checked color=\"primary\">Pro</ore-radio>\n * <ore-radio name=\"plan\" value=\"enterprise\" disabled>Enterprise</ore-radio>\n * ```\n */\nexport const RADIO_TAG = 'ore-radio' as const;\ndefine<OreRadioProps>(RADIO_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioEvents>();\n\n const groupCtx = inject(RADIO_GROUP_CTX);\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const effectiveName = computed(() => groupCtx?.name.value || props.name.value || '');\n const effectiveSize = computed(() => groupCtx?.size.value ?? fCtxProps.size.value);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const checkedFromState = computed(() => {\n if (groupCtx) return groupCtx.value.value === props.value.value;\n\n return Boolean(props.checked.value);\n });\n\n const getRadioGroup = (): HTMLElement[] => {\n const radioName = effectiveName.value;\n\n if (!radioName) return [];\n\n return Array.from(document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`)).filter(\n (r) => !r.hasAttribute('disabled'),\n );\n };\n\n const selectRadio = (radio: HTMLElement, originalEvent?: Event): void => {\n if (groupCtx) {\n groupCtx.select(radio.getAttribute('value') ?? '', originalEvent);\n\n return;\n }\n\n radio.click();\n };\n\n const listControl = createListControl({\n getItems: () => getRadioGroup(),\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const nextRadio = getRadioGroup()[index];\n\n if (!nextRadio) return;\n\n nextRadio.focus();\n selectRadio(nextRadio, event);\n },\n });\n\n const activateSelf = (originalEvent?: Event): void => {\n if (checkable.checked.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', originalEvent);\n\n return;\n }\n\n checkable.toggle(originalEvent ?? new Event('change'));\n };\n\n let _formField: { reportValidity(): void } | null = null;\n const checkable = createCheckable({\n checked: checkedFromState,\n disabled: computed(() => fCtxProps.disabled.value || Boolean(groupCtx?.disabled.value)),\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n onToggle: (payload) => {\n emit('change', payload);\n },\n prefix: 'radio',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n });\n\n const { assistiveId, checked, disabled, errorText, helperText, labelId, toggle } = checkable;\n\n bind({\n attr: {\n ariaChecked: () => (checked.value ? 'true' : 'false'),\n ariaDescribedby: () => (errorText.value || helperText.value ? assistiveId : null),\n ariaDisabled: () => (disabled.value ? 'true' : null),\n ariaInvalid: () => (errorText.value ? 'true' : null),\n ariaLabelledby: labelId,\n checked,\n color: effectiveColor,\n disabled: () => (disabled.value ? true : undefined),\n name: () => effectiveName.value || undefined,\n role: 'radio',\n size: effectiveSize,\n tabindex: () => {\n if (disabled.value) return undefined;\n\n return checked.value ? 0 : -1;\n },\n },\n class: () => ({\n 'is-checked': checked.value,\n 'is-disabled': disabled.value,\n }),\n on: {\n click: (e: MouseEvent) => {\n if (disabled.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', e);\n } else {\n if (!effectiveName.value) return;\n\n if (!checked.value) {\n const radioName = props.name.value;\n const allRadios = document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`);\n\n allRadios.forEach((radio) => {\n if (radio !== el) radio.removeAttribute('checked');\n });\n\n toggle(e);\n }\n }\n },\n keydown: (e: KeyboardEvent) => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n activateSelf(e);\n\n return;\n }\n\n const radios = getRadioGroup();\n\n if (radios.length === 0) return;\n\n const selfIndex = radios.indexOf(el);\n\n if (selfIndex === -1) return;\n\n listControl.set(selfIndex);\n listControl.handleKeydown(e);\n },\n },\n });\n\n return html`\n <div class=\"radio-wrapper\" part=\"radio\">\n <div class=\"circle\" part=\"circle\">\n <div class=\"dot\" part=\"dot\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n coarsePointerMixin,\n sizeVariantMixin(CONTROL_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"radio.cjs","names":[],"sources":["../src/inputs/radio/radio.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable, createListControl } from '../../headless';\nimport { CONTROL_SIZE_PRESET, disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsFormControlMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { RADIO_GROUP_CTX } from '../radio-group/radio-group';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './radio.css?inline';\n\n/** Radio component properties */\n\nexport type OreRadioEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreRadioProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the radio */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A customizable radio button component for mutually exclusive selections.\n *\n * @element ore-radio\n *\n * @attr {boolean} checked - Checked state\n * @attr {boolean} disabled - Disable radio interaction\n * @attr {string} value - Field value (required for radio groups)\n * @attr {string} name - Form field name (required for radio groups)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Radio size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the radio\n *\n * @fires change - Emitted when radio is selected. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Radio button label text\n *\n * @cssprop --radio-size - Control size (width and height)\n * @cssprop --radio-bg - Unchecked background color\n * @cssprop --radio-border-color - Unchecked border color\n * @cssprop --radio-checked-bg - Selected indicator background color\n * @cssprop --radio-color - Selected indicator dot color\n * @cssprop --radio-font-size - Label font size\n * @part radio - The radio wrapper element\n * @part circle - The visual radio circle\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-radio name=\"plan\" value=\"free\">Free</ore-radio>\n * <ore-radio name=\"plan\" value=\"pro\" checked color=\"primary\">Pro</ore-radio>\n * <ore-radio name=\"plan\" value=\"enterprise\" disabled>Enterprise</ore-radio>\n * ```\n */\nexport const RADIO_TAG = 'ore-radio' as const;\ndefine<OreRadioProps>(RADIO_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioEvents>();\n\n const groupCtx = inject(RADIO_GROUP_CTX);\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const effectiveName = computed(() => groupCtx?.name.value || props.name.value || '');\n const effectiveSize = computed(() => groupCtx?.size.value ?? fCtxProps.size.value);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const checkedFromState = computed(() => {\n if (groupCtx) return groupCtx.value.value === props.value.value;\n\n return Boolean(props.checked.value);\n });\n\n const getRadioGroup = (): HTMLElement[] => {\n const radioName = effectiveName.value;\n\n if (!radioName) return [];\n\n return Array.from(document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`)).filter(\n (r) => !r.hasAttribute('disabled'),\n );\n };\n\n const selectRadio = (radio: HTMLElement, originalEvent?: Event): void => {\n if (groupCtx) {\n groupCtx.select(radio.getAttribute('value') ?? '', originalEvent);\n\n return;\n }\n\n radio.click();\n };\n\n const listControl = createListControl({\n getItems: () => getRadioGroup(),\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const nextRadio = getRadioGroup()[index];\n\n if (!nextRadio) return;\n\n nextRadio.focus();\n selectRadio(nextRadio, event);\n },\n });\n\n const activateSelf = (originalEvent?: Event): void => {\n if (checkable.checked.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', originalEvent);\n\n return;\n }\n\n checkable.toggle(originalEvent ?? new Event('change'));\n };\n\n const checkable = createCheckable({\n checked: checkedFromState,\n disabled: computed(() => fCtxProps.disabled.value || Boolean(groupCtx?.disabled.value)),\n error: props.error,\n helper: props.helper,\n onToggle: (payload) => {\n emit('change', payload);\n },\n prefix: 'radio',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n checkable.attachFormField(\n useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n }),\n );\n\n const { assistiveId, checked, disabled, errorText, helperText, labelId, toggle } = checkable;\n\n bind({\n attr: {\n ariaChecked: () => (checked.value ? 'true' : 'false'),\n ariaDescribedby: () => (errorText.value || helperText.value ? assistiveId : null),\n ariaDisabled: () => (disabled.value ? 'true' : null),\n ariaInvalid: () => (errorText.value ? 'true' : null),\n ariaLabelledby: labelId,\n checked,\n color: effectiveColor,\n disabled: () => (disabled.value ? true : undefined),\n name: () => effectiveName.value || undefined,\n role: 'radio',\n size: effectiveSize,\n tabindex: () => {\n if (disabled.value) return undefined;\n\n return checked.value ? 0 : -1;\n },\n },\n class: () => ({\n 'is-checked': checked.value,\n 'is-disabled': disabled.value,\n }),\n on: {\n click: (e: MouseEvent) => {\n if (disabled.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', e);\n } else {\n if (!effectiveName.value) return;\n\n if (!checked.value) {\n const radioName = props.name.value;\n const allRadios = document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`);\n\n allRadios.forEach((radio) => {\n if (radio !== el) radio.removeAttribute('checked');\n });\n\n toggle(e);\n }\n }\n },\n keydown: (e: KeyboardEvent) => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n activateSelf(e);\n\n return;\n }\n\n const radios = getRadioGroup();\n\n if (radios.length === 0) return;\n\n const selfIndex = radios.indexOf(el);\n\n if (selfIndex === -1) return;\n\n listControl.set(selfIndex);\n listControl.handleKeydown(e);\n },\n },\n });\n\n return html`\n <div class=\"radio-wrapper\" part=\"radio\">\n <div class=\"circle\" part=\"circle\">\n <div class=\"dot\" part=\"dot\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n coarsePointerMixin,\n sizeVariantMixin(CONTROL_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
package/dist/radio.js
CHANGED
|
@@ -51,18 +51,17 @@ y(D, {
|
|
|
51
51
|
r && (r.focus(), w(r, n));
|
|
52
52
|
}
|
|
53
53
|
}), O = (t) => {
|
|
54
|
-
if (!
|
|
54
|
+
if (!k.checked.value) {
|
|
55
55
|
if (r) {
|
|
56
56
|
r.select(e.value.value ?? "", t);
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
k.toggle(t ?? new Event("change"));
|
|
60
60
|
}
|
|
61
|
-
}, k =
|
|
61
|
+
}, k = u({
|
|
62
62
|
checked: _,
|
|
63
63
|
disabled: E(() => a.disabled.value || !!r?.disabled.value),
|
|
64
64
|
error: e.error,
|
|
65
|
-
getFormField: () => k,
|
|
66
65
|
helper: e.helper,
|
|
67
66
|
onToggle: (e) => {
|
|
68
67
|
n("change", e);
|
|
@@ -72,43 +71,43 @@ y(D, {
|
|
|
72
71
|
validateOn: i?.validateOn,
|
|
73
72
|
value: e.value
|
|
74
73
|
});
|
|
75
|
-
k
|
|
76
|
-
disabled:
|
|
74
|
+
k.attachFormField(f({
|
|
75
|
+
disabled: k.disabled,
|
|
77
76
|
toFormValue: (e) => e,
|
|
78
|
-
value:
|
|
79
|
-
});
|
|
80
|
-
let { assistiveId:
|
|
77
|
+
value: k.checkableFormValue
|
|
78
|
+
}));
|
|
79
|
+
let { assistiveId: A, checked: j, disabled: M, errorText: N, helperText: P, labelId: F, toggle: I } = k;
|
|
81
80
|
return v({
|
|
82
81
|
attr: {
|
|
83
|
-
ariaChecked: () =>
|
|
84
|
-
ariaDescribedby: () =>
|
|
85
|
-
ariaDisabled: () =>
|
|
86
|
-
ariaInvalid: () =>
|
|
87
|
-
ariaLabelledby:
|
|
88
|
-
checked:
|
|
82
|
+
ariaChecked: () => j.value ? "true" : "false",
|
|
83
|
+
ariaDescribedby: () => N.value || P.value ? A : null,
|
|
84
|
+
ariaDisabled: () => M.value ? "true" : null,
|
|
85
|
+
ariaInvalid: () => N.value ? "true" : null,
|
|
86
|
+
ariaLabelledby: F,
|
|
87
|
+
checked: j,
|
|
89
88
|
color: c,
|
|
90
|
-
disabled: () =>
|
|
89
|
+
disabled: () => M.value ? !0 : void 0,
|
|
91
90
|
name: () => o.value || void 0,
|
|
92
91
|
role: "radio",
|
|
93
92
|
size: s,
|
|
94
93
|
tabindex: () => {
|
|
95
|
-
if (!
|
|
94
|
+
if (!M.value) return j.value ? 0 : -1;
|
|
96
95
|
}
|
|
97
96
|
},
|
|
98
97
|
class: () => ({
|
|
99
|
-
"is-checked":
|
|
100
|
-
"is-disabled":
|
|
98
|
+
"is-checked": j.value,
|
|
99
|
+
"is-disabled": M.value
|
|
101
100
|
}),
|
|
102
101
|
on: {
|
|
103
102
|
click: (n) => {
|
|
104
|
-
if (!
|
|
103
|
+
if (!M.value) if (r) r.select(e.value.value ?? "", n);
|
|
105
104
|
else {
|
|
106
105
|
if (!o.value) return;
|
|
107
|
-
if (!
|
|
106
|
+
if (!j.value) {
|
|
108
107
|
let r = e.name.value;
|
|
109
108
|
document.querySelectorAll(`ore-radio[name="${r}"]`).forEach((e) => {
|
|
110
109
|
e !== t && e.removeAttribute("checked");
|
|
111
|
-
}),
|
|
110
|
+
}), I(n);
|
|
112
111
|
}
|
|
113
112
|
}
|
|
114
113
|
},
|
|
@@ -129,8 +128,8 @@ y(D, {
|
|
|
129
128
|
<div class="dot" part="dot"></div>
|
|
130
129
|
</div>
|
|
131
130
|
</div>
|
|
132
|
-
<span class="label" part="label" id="${
|
|
133
|
-
${h(
|
|
131
|
+
<span class="label" part="label" id="${F}"><slot></slot></span>
|
|
132
|
+
${h(A, N, P)}
|
|
134
133
|
`;
|
|
135
134
|
},
|
|
136
135
|
styles: [
|
package/dist/radio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radio.js","names":[],"sources":["../src/inputs/radio/radio.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable, createListControl } from '../../headless';\nimport { CONTROL_SIZE_PRESET, disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsFormControlMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { RADIO_GROUP_CTX } from '../radio-group/radio-group';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './radio.css?inline';\n\n/** Radio component properties */\n\nexport type OreRadioEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreRadioProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the radio */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A customizable radio button component for mutually exclusive selections.\n *\n * @element ore-radio\n *\n * @attr {boolean} checked - Checked state\n * @attr {boolean} disabled - Disable radio interaction\n * @attr {string} value - Field value (required for radio groups)\n * @attr {string} name - Form field name (required for radio groups)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Radio size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the radio\n *\n * @fires change - Emitted when radio is selected. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Radio button label text\n *\n * @cssprop --radio-size - Control size (width and height)\n * @cssprop --radio-bg - Unchecked background color\n * @cssprop --radio-border-color - Unchecked border color\n * @cssprop --radio-checked-bg - Selected indicator background color\n * @cssprop --radio-color - Selected indicator dot color\n * @cssprop --radio-font-size - Label font size\n * @part radio - The radio wrapper element\n * @part circle - The visual radio circle\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-radio name=\"plan\" value=\"free\">Free</ore-radio>\n * <ore-radio name=\"plan\" value=\"pro\" checked color=\"primary\">Pro</ore-radio>\n * <ore-radio name=\"plan\" value=\"enterprise\" disabled>Enterprise</ore-radio>\n * ```\n */\nexport const RADIO_TAG = 'ore-radio' as const;\ndefine<OreRadioProps>(RADIO_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioEvents>();\n\n const groupCtx = inject(RADIO_GROUP_CTX);\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const effectiveName = computed(() => groupCtx?.name.value || props.name.value || '');\n const effectiveSize = computed(() => groupCtx?.size.value ?? fCtxProps.size.value);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const checkedFromState = computed(() => {\n if (groupCtx) return groupCtx.value.value === props.value.value;\n\n return Boolean(props.checked.value);\n });\n\n const getRadioGroup = (): HTMLElement[] => {\n const radioName = effectiveName.value;\n\n if (!radioName) return [];\n\n return Array.from(document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`)).filter(\n (r) => !r.hasAttribute('disabled'),\n );\n };\n\n const selectRadio = (radio: HTMLElement, originalEvent?: Event): void => {\n if (groupCtx) {\n groupCtx.select(radio.getAttribute('value') ?? '', originalEvent);\n\n return;\n }\n\n radio.click();\n };\n\n const listControl = createListControl({\n getItems: () => getRadioGroup(),\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const nextRadio = getRadioGroup()[index];\n\n if (!nextRadio) return;\n\n nextRadio.focus();\n selectRadio(nextRadio, event);\n },\n });\n\n const activateSelf = (originalEvent?: Event): void => {\n if (checkable.checked.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', originalEvent);\n\n return;\n }\n\n checkable.toggle(originalEvent ?? new Event('change'));\n };\n\n let _formField: { reportValidity(): void } | null = null;\n const checkable = createCheckable({\n checked: checkedFromState,\n disabled: computed(() => fCtxProps.disabled.value || Boolean(groupCtx?.disabled.value)),\n error: props.error,\n getFormField: () => _formField,\n helper: props.helper,\n onToggle: (payload) => {\n emit('change', payload);\n },\n prefix: 'radio',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n _formField = useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n });\n\n const { assistiveId, checked, disabled, errorText, helperText, labelId, toggle } = checkable;\n\n bind({\n attr: {\n ariaChecked: () => (checked.value ? 'true' : 'false'),\n ariaDescribedby: () => (errorText.value || helperText.value ? assistiveId : null),\n ariaDisabled: () => (disabled.value ? 'true' : null),\n ariaInvalid: () => (errorText.value ? 'true' : null),\n ariaLabelledby: labelId,\n checked,\n color: effectiveColor,\n disabled: () => (disabled.value ? true : undefined),\n name: () => effectiveName.value || undefined,\n role: 'radio',\n size: effectiveSize,\n tabindex: () => {\n if (disabled.value) return undefined;\n\n return checked.value ? 0 : -1;\n },\n },\n class: () => ({\n 'is-checked': checked.value,\n 'is-disabled': disabled.value,\n }),\n on: {\n click: (e: MouseEvent) => {\n if (disabled.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', e);\n } else {\n if (!effectiveName.value) return;\n\n if (!checked.value) {\n const radioName = props.name.value;\n const allRadios = document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`);\n\n allRadios.forEach((radio) => {\n if (radio !== el) radio.removeAttribute('checked');\n });\n\n toggle(e);\n }\n }\n },\n keydown: (e: KeyboardEvent) => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n activateSelf(e);\n\n return;\n }\n\n const radios = getRadioGroup();\n\n if (radios.length === 0) return;\n\n const selfIndex = radios.indexOf(el);\n\n if (selfIndex === -1) return;\n\n listControl.set(selfIndex);\n listControl.handleKeydown(e);\n },\n },\n });\n\n return html`\n <div class=\"radio-wrapper\" part=\"radio\">\n <div class=\"circle\" part=\"circle\">\n <div class=\"dot\" part=\"dot\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n coarsePointerMixin,\n sizeVariantMixin(CONTROL_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"radio.js","names":[],"sources":["../src/inputs/radio/radio.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost, onCleanup, useEmit } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { CheckableProps, ComponentSize, ThemeColor } from '../../types';\n\nimport { type CheckableChangePayload, lifecycleSignal, createCheckable, createListControl } from '../../headless';\nimport { CONTROL_SIZE_PRESET, disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsFormControlMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { RADIO_GROUP_CTX } from '../radio-group/radio-group';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './radio.css?inline';\n\n/** Radio component properties */\n\nexport type OreRadioEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreRadioProps = CheckableProps & {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Error message (marks field as invalid) */\n error?: string;\n /** Helper text displayed below the radio */\n helper?: string;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A customizable radio button component for mutually exclusive selections.\n *\n * @element ore-radio\n *\n * @attr {boolean} checked - Checked state\n * @attr {boolean} disabled - Disable radio interaction\n * @attr {string} value - Field value (required for radio groups)\n * @attr {string} name - Form field name (required for radio groups)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Radio size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the radio\n *\n * @fires change - Emitted when radio is selected. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Radio button label text\n *\n * @cssprop --radio-size - Control size (width and height)\n * @cssprop --radio-bg - Unchecked background color\n * @cssprop --radio-border-color - Unchecked border color\n * @cssprop --radio-checked-bg - Selected indicator background color\n * @cssprop --radio-color - Selected indicator dot color\n * @cssprop --radio-font-size - Label font size\n * @part radio - The radio wrapper element\n * @part circle - The visual radio circle\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-radio name=\"plan\" value=\"free\">Free</ore-radio>\n * <ore-radio name=\"plan\" value=\"pro\" checked color=\"primary\">Pro</ore-radio>\n * <ore-radio name=\"plan\" value=\"enterprise\" disabled>Enterprise</ore-radio>\n * ```\n */\nexport const RADIO_TAG = 'ore-radio' as const;\ndefine<OreRadioProps>(RADIO_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n checked: prop.bool(false),\n error: prop.string(),\n helper: prop.string(),\n name: prop.string(),\n value: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreRadioEvents>();\n\n const groupCtx = inject(RADIO_GROUP_CTX);\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n\n const effectiveName = computed(() => groupCtx?.name.value || props.name.value || '');\n const effectiveSize = computed(() => groupCtx?.size.value ?? fCtxProps.size.value);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const checkedFromState = computed(() => {\n if (groupCtx) return groupCtx.value.value === props.value.value;\n\n return Boolean(props.checked.value);\n });\n\n const getRadioGroup = (): HTMLElement[] => {\n const radioName = effectiveName.value;\n\n if (!radioName) return [];\n\n return Array.from(document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`)).filter(\n (r) => !r.hasAttribute('disabled'),\n );\n };\n\n const selectRadio = (radio: HTMLElement, originalEvent?: Event): void => {\n if (groupCtx) {\n groupCtx.select(radio.getAttribute('value') ?? '', originalEvent);\n\n return;\n }\n\n radio.click();\n };\n\n const listControl = createListControl({\n getItems: () => getRadioGroup(),\n keys: { next: ['ArrowDown', 'ArrowRight'], prev: ['ArrowUp', 'ArrowLeft'] },\n loop: true,\n onNavigate: (_action, index, event) => {\n const nextRadio = getRadioGroup()[index];\n\n if (!nextRadio) return;\n\n nextRadio.focus();\n selectRadio(nextRadio, event);\n },\n });\n\n const activateSelf = (originalEvent?: Event): void => {\n if (checkable.checked.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', originalEvent);\n\n return;\n }\n\n checkable.toggle(originalEvent ?? new Event('change'));\n };\n\n const checkable = createCheckable({\n checked: checkedFromState,\n disabled: computed(() => fCtxProps.disabled.value || Boolean(groupCtx?.disabled.value)),\n error: props.error,\n helper: props.helper,\n onToggle: (payload) => {\n emit('change', payload);\n },\n prefix: 'radio',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.value,\n });\n\n checkable.attachFormField(\n useField<string | null>({\n disabled: checkable.disabled,\n toFormValue: (v) => v,\n value: checkable.checkableFormValue,\n }),\n );\n\n const { assistiveId, checked, disabled, errorText, helperText, labelId, toggle } = checkable;\n\n bind({\n attr: {\n ariaChecked: () => (checked.value ? 'true' : 'false'),\n ariaDescribedby: () => (errorText.value || helperText.value ? assistiveId : null),\n ariaDisabled: () => (disabled.value ? 'true' : null),\n ariaInvalid: () => (errorText.value ? 'true' : null),\n ariaLabelledby: labelId,\n checked,\n color: effectiveColor,\n disabled: () => (disabled.value ? true : undefined),\n name: () => effectiveName.value || undefined,\n role: 'radio',\n size: effectiveSize,\n tabindex: () => {\n if (disabled.value) return undefined;\n\n return checked.value ? 0 : -1;\n },\n },\n class: () => ({\n 'is-checked': checked.value,\n 'is-disabled': disabled.value,\n }),\n on: {\n click: (e: MouseEvent) => {\n if (disabled.value) return;\n\n if (groupCtx) {\n groupCtx.select(props.value.value ?? '', e);\n } else {\n if (!effectiveName.value) return;\n\n if (!checked.value) {\n const radioName = props.name.value;\n const allRadios = document.querySelectorAll<HTMLElement>(`ore-radio[name=\"${radioName}\"]`);\n\n allRadios.forEach((radio) => {\n if (radio !== el) radio.removeAttribute('checked');\n });\n\n toggle(e);\n }\n }\n },\n keydown: (e: KeyboardEvent) => {\n if (e.key === ' ' || e.key === 'Enter') {\n e.preventDefault();\n activateSelf(e);\n\n return;\n }\n\n const radios = getRadioGroup();\n\n if (radios.length === 0) return;\n\n const selfIndex = radios.indexOf(el);\n\n if (selfIndex === -1) return;\n\n listControl.set(selfIndex);\n listControl.handleKeydown(e);\n },\n },\n });\n\n return html`\n <div class=\"radio-wrapper\" part=\"radio\">\n <div class=\"circle\" part=\"circle\">\n <div class=\"dot\" part=\"dot\"></div>\n </div>\n </div>\n <span class=\"label\" part=\"label\" id=\"${labelId}\"><slot></slot></span>\n ${renderHelperRegion(assistiveId, errorText, helperText)}\n `;\n },\n styles: [\n colorThemeMixin,\n forcedColorsFormControlMixin,\n disabledStateMixin,\n coarsePointerMixin,\n sizeVariantMixin(CONTROL_SIZE_PRESET),\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AA2EA,IAAa,IAAY;AACzB,EAAsB,GAAW;CAC/B,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,GAAG;EACH,GAAG;EACH,SAAS,EAAK,KAAK,EAAK;EACxB,OAAO,EAAK,OAAO;EACnB,QAAQ,EAAK,OAAO;EACpB,MAAM,EAAK,OAAO;EAClB,OAAO,EAAK,OAAO;CACrB;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GACb,IAAO,EAAwB,GAE/B,IAAW,EAAO,CAAe,GACjC,IAAU,EAAO,CAAQ,GACzB,IAAY,EAAe,GAAO,CAAO,GAEzC,IAAgB,QAAe,GAAU,KAAK,SAAS,EAAM,KAAK,SAAS,EAAE,GAC7E,IAAgB,QAAe,GAAU,KAAK,SAAS,EAAU,KAAK,KAAK,GAC3E,IAAiB,QAAe,GAAU,MAAM,SAAS,EAAM,MAAM,KAAK,GAC1E,IAAmB,QACnB,IAAiB,EAAS,MAAM,UAAU,EAAM,MAAM,QAEnD,EAAQ,EAAM,QAAQ,KAC9B,GAEK,UAAqC;GACzC,IAAM,IAAY,EAAc;GAIhC,OAFK,IAEE,MAAM,KAAK,SAAS,iBAA8B,mBAAmB,EAAU,GAAG,CAAC,CAAC,CAAC,QACzF,MAAM,CAAC,EAAE,aAAa,UAAU,CACnC,IAJuB,CAAC;EAK1B,GAEM,KAAe,GAAoB,MAAgC;GACvE,IAAI,GAAU;IACZ,EAAS,OAAO,EAAM,aAAa,OAAO,KAAK,IAAI,CAAa;IAEhE;GACF;GAEA,EAAM,MAAM;EACd,GAEM,IAAc,EAAkB;GACpC,gBAAgB,EAAc;GAC9B,MAAM;IAAE,MAAM,CAAC,aAAa,YAAY;IAAG,MAAM,CAAC,WAAW,WAAW;GAAE;GAC1E,MAAM;GACN,aAAa,GAAS,GAAO,MAAU;IACrC,IAAM,IAAY,EAAc,CAAC,CAAC;IAE7B,MAEL,EAAU,MAAM,GAChB,EAAY,GAAW,CAAK;GAC9B;EACF,CAAC,GAEK,KAAgB,MAAgC;GAChD,OAAU,QAAQ,OAEtB;QAAI,GAAU;KACZ,EAAS,OAAO,EAAM,MAAM,SAAS,IAAI,CAAa;KAEtD;IACF;IAEA,EAAU,OAAO,KAAiB,IAAI,MAAM,QAAQ,CAAC;GAFrD;EAGF,GAEM,IAAY,EAAgB;GAChC,SAAS;GACT,UAAU,QAAe,EAAU,SAAS,SAAS,EAAQ,GAAU,SAAS,KAAM;GACtF,OAAO,EAAM;GACb,QAAQ,EAAM;GACd,WAAW,MAAY;IACrB,EAAK,UAAU,CAAO;GACxB;GACA,QAAQ;GACR,QAAQ,EAAgB,CAAS;GACjC,YAAY,GAAS;GACrB,OAAO,EAAM;EACf,CAAC;EAED,EAAU,gBACR,EAAwB;GACtB,UAAU,EAAU;GACpB,cAAc,MAAM;GACpB,OAAO,EAAU;EACnB,CAAC,CACH;EAEA,IAAM,EAAE,gBAAa,YAAS,aAAU,cAAW,eAAY,YAAS,cAAW;EAoEnF,OAlEA,EAAK;GACH,MAAM;IACJ,mBAAoB,EAAQ,QAAQ,SAAS;IAC7C,uBAAwB,EAAU,SAAS,EAAW,QAAQ,IAAc;IAC5E,oBAAqB,EAAS,QAAQ,SAAS;IAC/C,mBAAoB,EAAU,QAAQ,SAAS;IAC/C,gBAAgB;IAChB;IACA,OAAO;IACP,gBAAiB,EAAS,QAAQ,KAAO,KAAA;IACzC,YAAY,EAAc,SAAS,KAAA;IACnC,MAAM;IACN,MAAM;IACN,gBAAgB;KACV,OAAS,OAEb,OAAO,EAAQ,QAAQ,IAAI;IAC7B;GACF;GACA,cAAc;IACZ,cAAc,EAAQ;IACtB,eAAe,EAAS;GAC1B;GACA,IAAI;IACF,QAAQ,MAAkB;KACpB,OAAS,OAEb,IAAI,GACF,EAAS,OAAO,EAAM,MAAM,SAAS,IAAI,CAAC;UACrC;MACL,IAAI,CAAC,EAAc,OAAO;MAE1B,IAAI,CAAC,EAAQ,OAAO;OAClB,IAAM,IAAY,EAAM,KAAK;OAO7B,AAJA,SAF2B,iBAA8B,mBAAmB,EAAU,GAEtF,CAAA,CAAU,SAAS,MAAU;QAC3B,AAAI,MAAU,KAAI,EAAM,gBAAgB,SAAS;OACnD,CAAC,GAED,EAAO,CAAC;MACV;KACF;IACF;IACA,UAAU,MAAqB;KAC7B,IAAI,EAAE,QAAQ,OAAO,EAAE,QAAQ,SAAS;MAEtC,AADA,EAAE,eAAe,GACjB,EAAa,CAAC;MAEd;KACF;KAEA,IAAM,IAAS,EAAc;KAE7B,IAAI,EAAO,WAAW,GAAG;KAEzB,IAAM,IAAY,EAAO,QAAQ,CAAE;KAE/B,MAAc,OAElB,EAAY,IAAI,CAAS,GACzB,EAAY,cAAc,CAAC;IAC7B;GACF;EACF,CAAC,GAEM,CAAI;;;;;;6CAM8B,EAAQ;QAC7C,EAAmB,GAAa,GAAW,CAAU,EAAE;;CAE7D;CACA,QAAQ;EACN;EACA;EACA;EACA;EACA,EAAiB,CAAmB;EACpC;CACF;AACF,CAAC"}
|