@vielzeug/refine 1.3.0 → 1.4.0
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/button.cjs +1 -1
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +3 -3
- package/dist/button.js.map +1 -1
- 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/content/card/card.css.cjs +1 -1
- package/dist/content/card/card.css.cjs.map +1 -1
- package/dist/content/card/card.css.js +1 -1
- package/dist/content/card/card.css.js.map +1 -1
- package/dist/content/icon/icon.css.cjs +1 -1
- package/dist/content/icon/icon.css.cjs.map +1 -1
- package/dist/content/icon/icon.css.js +1 -1
- package/dist/content/icon/icon.css.js.map +1 -1
- package/dist/custom-elements.json +4711 -4209
- package/dist/feedback/toast/toast.css.cjs +1 -1
- package/dist/feedback/toast/toast.css.cjs.map +1 -1
- package/dist/feedback/toast/toast.css.js +1 -1
- package/dist/feedback/toast/toast.css.js.map +1 -1
- 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/button/button.css.cjs +1 -1
- package/dist/inputs/button/button.css.cjs.map +1 -1
- package/dist/inputs/button/button.css.js +1 -1
- package/dist/inputs/button/button.css.js.map +1 -1
- package/dist/inputs/checkbox/checkbox.css.cjs +1 -1
- package/dist/inputs/checkbox/checkbox.css.cjs.map +1 -1
- package/dist/inputs/checkbox/checkbox.css.js +1 -1
- package/dist/inputs/checkbox/checkbox.css.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/checkbox-group/checkbox-group.css.cjs +1 -1
- package/dist/inputs/checkbox-group/checkbox-group.css.cjs.map +1 -1
- package/dist/inputs/checkbox-group/checkbox-group.css.js +1 -1
- package/dist/inputs/checkbox-group/checkbox-group.css.js.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/file-input/file-input.css.cjs +1 -1
- package/dist/inputs/file-input/file-input.css.cjs.map +1 -1
- package/dist/inputs/file-input/file-input.css.js +1 -1
- package/dist/inputs/file-input/file-input.css.js.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.css.cjs +1 -1
- package/dist/inputs/input/input.css.cjs.map +1 -1
- package/dist/inputs/input/input.css.js +1 -1
- package/dist/inputs/input/input.css.js.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/otp-input/otp-input.css.cjs +1 -1
- package/dist/inputs/otp-input/otp-input.css.cjs.map +1 -1
- package/dist/inputs/otp-input/otp-input.css.js +1 -1
- package/dist/inputs/otp-input/otp-input.css.js.map +1 -1
- package/dist/inputs/radio/radio.css.cjs +1 -1
- package/dist/inputs/radio/radio.css.cjs.map +1 -1
- package/dist/inputs/radio/radio.css.js +1 -1
- package/dist/inputs/radio/radio.css.js.map +1 -1
- package/dist/inputs/radio-group/radio-group.css.cjs +1 -1
- package/dist/inputs/radio-group/radio-group.css.cjs.map +1 -1
- package/dist/inputs/radio-group/radio-group.css.js +1 -1
- package/dist/inputs/radio-group/radio-group.css.js.map +1 -1
- package/dist/inputs/rating/rating.css.cjs +1 -1
- package/dist/inputs/rating/rating.css.cjs.map +1 -1
- package/dist/inputs/rating/rating.css.js +1 -1
- package/dist/inputs/rating/rating.css.js.map +1 -1
- 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/slider/slider.css.cjs +1 -1
- package/dist/inputs/slider/slider.css.cjs.map +1 -1
- package/dist/inputs/slider/slider.css.js +1 -1
- package/dist/inputs/slider/slider.css.js.map +1 -1
- package/dist/inputs/switch/switch.css.cjs +1 -1
- package/dist/inputs/switch/switch.css.cjs.map +1 -1
- package/dist/inputs/switch/switch.css.js +1 -1
- package/dist/inputs/switch/switch.css.js.map +1 -1
- package/dist/inputs/textarea/textarea.css.cjs +1 -1
- package/dist/inputs/textarea/textarea.css.cjs.map +1 -1
- package/dist/inputs/textarea/textarea.css.js +1 -1
- package/dist/inputs/textarea/textarea.css.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/layout/sidebar/sidebar.css.cjs +1 -1
- package/dist/layout/sidebar/sidebar.css.cjs.map +1 -1
- package/dist/layout/sidebar/sidebar.css.js +1 -1
- package/dist/layout/sidebar/sidebar.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/overlay/menu/menu.css.cjs +1 -1
- package/dist/overlay/menu/menu.css.cjs.map +1 -1
- package/dist/overlay/menu/menu.css.js +1 -1
- package/dist/overlay/menu/menu.css.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 -181
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +228 -181
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +251 -204
- 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/styles/mixins/animation.css.cjs +0 -3
- package/dist/styles/mixins/animation.css.cjs.map +1 -1
- package/dist/styles/mixins/animation.css.d.ts +12 -4
- package/dist/styles/mixins/animation.css.d.ts.map +1 -1
- package/dist/styles/mixins/animation.css.js +0 -3
- package/dist/styles/mixins/animation.css.js.map +1 -1
- package/dist/styles/mixins/variants.css.cjs.map +1 -1
- package/dist/styles/mixins/variants.css.d.ts +7 -2
- package/dist/styles/mixins/variants.css.d.ts.map +1 -1
- package/dist/styles/mixins/variants.css.js.map +1 -1
- package/dist/styles/theme.css +341 -132
- 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 +21 -9
package/dist/button.cjs
CHANGED
|
@@ -14,5 +14,5 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
14
14
|
@click="${g}">
|
|
15
15
|
${_}
|
|
16
16
|
</a>`:f.html`<span part="button" role="presentation" @click="${g}">${_}</span>`}
|
|
17
|
-
`},shadow:{delegatesFocus:!1},styles:[r.colorThemeMixin,e.coarsePointerMixin,e.reducedMotionMixin,t.roundedVariantMixin,r.forcedColorsMixin,t.sizeVariantMixin({lg:{fontSize:`var(--text-base)`,gap:`var(--size-2-5)`,height:`var(--size-12)`,iconSize:`var(--size-6)`,lineHeight:`var(--leading-relaxed)`,padding:`var(--size-2-5) var(--size-5)`},sm:{fontSize:`var(--text-sm)`,gap:`var(--size-1-5)`,height:`var(--size-8)`,iconSize:`var(--size-4)`,lineHeight:`var(--leading-tight)`,padding:`var(--size-1-5) var(--size-3)`}}),a.frostVariantMixin(`button`),i.rainbowEffectMixin(`button`),i.shineEffectMixin(`button`),n.disabledLoadingMixin,d.default]}),exports.BUTTON_TAG=h,exports.BUTTON_VARIANTS=m;
|
|
17
|
+
`},shadow:{delegatesFocus:!1},styles:[r.colorThemeMixin,e.coarsePointerMixin,e.reducedMotionMixin,t.roundedVariantMixin,r.forcedColorsMixin,t.sizeVariantMixin({lg:{fontSize:`var(--text-base)`,gap:`var(--size-2-5)`,height:`var(--size-12)`,iconSize:`var(--size-6)`,lineHeight:`var(--leading-relaxed)`,padding:`var(--size-2-5) var(--size-5)`},sm:{fontSize:`var(--text-sm)`,gap:`var(--size-1-5)`,height:`var(--size-8)`,iconSize:`var(--size-4)`,lineHeight:`var(--leading-tight)`,padding:`var(--size-1-5) var(--size-3)`}}),a.frostVariantMixin(`[part="button"]`),i.rainbowEffectMixin(`[part="button"]`),i.shineEffectMixin(`[part="button"]`),n.disabledLoadingMixin,d.default]}),exports.BUTTON_TAG=h,exports.BUTTON_VARIANTS=m;
|
|
18
18
|
//# sourceMappingURL=button.cjs.map
|
package/dist/button.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.cjs","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('button'),\n rainbowEffectMixin('button'),\n shineEffectMixin('button'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"button.cjs","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('[part=\"button\"]'),\n rainbowEffectMixin('[part=\"button\"]'),\n shineEffectMixin('[part=\"button\"]'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
package/dist/button.js
CHANGED
package/dist/button.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button.js","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('button'),\n rainbowEffectMixin('button'),\n shineEffectMixin('button'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,IAAa,IAAkB;CAAC;CAAS;CAAQ;CAAY;CAAW;CAAS;CAAQ;AAAO,GAsFnF,IAAa;AAC1B,EAAuB,GAAY;CACjC,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,QAAQ,EAAK,OAAqB;EAClC,WAAW,EAAK,KAAK,EAAK;EAC1B,MAAM,EAAK,OAAO;EAClB,UAAU,EAAK,KAAK,EAAK;EACzB,OAAO,EAAK,OAAO;EACnB,KAAK,EAAK,OAAO;EACjB,QAAQ,EAAK,OAAmB;EAChC,MAAM,EAAK,MAAM;GAAC;GAAU;GAAU;EAAO,GAAY,QAAQ;EACjE,SAAS,EAAK,MAAM,GAAiB,OAAO;CAC9C;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GAGb,IAAW,EAAO,CAAgB,GAClC,IAAiB,QAAe,GAAU,MAAM,SAAS,EAAM,MAAM,KAAK,GAC1E,IAAgB,QAAe,GAAU,KAAK,SAAS,EAAM,KAAK,KAAK,GACvE,IAAmB,QAAe,GAAU,QAAQ,SAAS,EAAM,QAAQ,KAAK,GAEhF,IAAa,QAAe,CAAC,EAAE,EAAM,SAAS,SAAS,EAAM,QAAQ,MAAM,GAG3E,EAAE,iBAAc,cAAW,EAAa,EAAM,MAAM,EAAM,KAAK,EAAM,MAAM,GAK3E,IAAY,EAAS;GACzB,UAAU;GACV,mBAAmB;GACnB,OAAO,QAAe,EAAE;EAC1B,CAAC,GAEK,KAAe,MAAkB;GACrC,IAAI,EAAW,OAAO;IACpB,EAAE,eAAe;IAEjB;GACF;GAIA,QAAqB,EAAO,QAAQ,OAAO,EAAU,UAAU,MAAO,EAAM,MAAM,GAAY,CAAE,CAAC,CAAC,CAAC;EACrG;EAUA,EAAK;GACH,MAAM;IACJ,aAAa,EAAM;IACnB,iBAAiB;IACjB,cAAc,EAAM;IACpB,OAAO;IACP,QAAQ,EAAM;IACd,MAAM,QAAe,EAAG,aAAa,MAAM,MAAM,EAAO,QAAQ,SAAS,SAAS;IAClF,MAAM;IACN,UAAU,QAAgB,EAAW,QAAQ,OAAO,GAAI;IACxD,SAAS;GACX;GACA,IAAI,EACF,UArBmB,MAAqB;IAC1C,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAe,IAChB,EAAG,YAAY,cAAc,mBAAiB,EAAA,EAAmB,MAAM;GAE5E,EAiBE;EACF,CAAC;EAED,IAAM,IAAgB,CAAI;8EACgD,CAAC,EAAM,QAAQ,MAAM;;;;;EAY/F,OAAO,CAAI;cAEP,EAAO,QACH,CAAI;;;;sBAIM,EAAM,KAAK;sBACX,EAAa;yBACV,EAAM,OAAO;wBACd,EAAY;gBACpB,EAAc;oBAElB,CAAI,mDAAmD,EAAY,IAAI,EAAc,SAAS;;CAExG;CACA,QAAQ,EAAE,gBAAgB,GAAM;CAChC,QAAQ;EACN;EACA;EACA;EACA;EACA;EACA,EAAiB;GACf,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;GACA,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;EACF,CAAC;EACD,EAAkB,QAAQ;EAC1B,EAAmB,QAAQ;EAC3B,EAAiB,QAAQ;EACzB;EACA;CACF;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"button.js","names":[],"sources":["../src/inputs/button/button.ts"],"sourcesContent":["import { define, html, inject, prop, bind, getHost } from '@vielzeug/ore';\nimport { useField } from '@vielzeug/ore/forms';\nimport { computed } from '@vielzeug/ripple';\n\nimport type { ButtonType, ComponentSize, LinkTarget, RoundedSize, ThemeColor } from '../../types';\n\nimport { commonProps } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledLoadingMixin,\n forcedColorsMixin,\n frostVariantMixin,\n rainbowEffectMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n shineEffectMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { useLinkProps } from '../../utils';\nimport { BUTTON_GROUP_CTX } from '../button-group/button-group';\nimport { useFormAction } from '../shared';\nimport componentStyles from './button.css?inline';\n\nexport const BUTTON_VARIANTS = ['solid', 'flat', 'bordered', 'outline', 'ghost', 'text', 'frost'] as const;\n\n/** Visual variant for ore-button — derived from BUTTON_VARIANTS for a single source of truth. */\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\n\n/** Animated border effect for ore-button. */\nexport type ButtonEffect = 'shine' | 'rainbow';\n\n/** Button component properties */\nexport type OreButtonProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interaction */\n disabled?: boolean;\n /** Animated border effect: 'shine' (color-aware neon sweep) or 'rainbow' */\n effect?: ButtonEffect;\n /** Full width button (100% of container) */\n fullwidth?: boolean;\n /** When set, renders an `<a>` instead of `<button>` */\n href?: string;\n /** Icon-only mode (square aspect ratio, no padding) */\n iconOnly?: boolean;\n /** Accessible label for the inner button — required for icon-only buttons */\n label?: string;\n /** Show loading state with spinner */\n loading?: boolean;\n /** Link rel attribute (requires href) */\n rel?: string;\n /** Border radius size */\n rounded?: RoundedSize;\n /** Component size */\n size?: ComponentSize;\n /** Link target (requires href) */\n target?: LinkTarget;\n /** HTML button type attribute */\n type?: ButtonType;\n /** Visual style variant */\n variant?: ButtonVariant;\n};\n\n/**\n * A customizable button component with multiple variants, sizes, and states.\n * Supports icons, loading states, and animated border effects.\n *\n * @element ore-button\n *\n * @attr {string} type - HTML button type: 'button' | 'submit' | 'reset'\n * @attr {boolean} disabled - Disable button interaction\n * @attr {boolean} loading - Show loading state with spinner\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'text' | 'frost'\n * @attr {string} size - Button size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} effect - Animated border effect: 'shine' | 'rainbow'\n * @attr {boolean} icon-only - Icon-only mode (square aspect ratio, no padding)\n * @attr {boolean} fullwidth - Full width button (100% of container)\n *\n * @fires click - Emitted when button is clicked (unless disabled/loading)\n *\n * @slot - Button content (text, icons, etc.)\n * @slot prefix - Content before the button text (e.g., icons)\n * @slot suffix - Content after the button text (e.g., icons, badges)\n *\n * @part button - The inner button or anchor element\n * @part loader - The loading spinner element\n * @part content - The button content wrapper\n *\n * @cssprop --button-bg - Background color override\n * @cssprop --button-color - Text color override\n * @cssprop --button-border - Border width override\n * @cssprop --button-border-color - Border color override\n * @cssprop --button-radius - Border radius override\n * @cssprop --button-padding - Inner padding override\n * @cssprop --button-gap - Gap between icon and text override\n * @cssprop --button-font-size - Font size override\n * @cssprop --button-frost-active-bg - Background when pressed (frost variant)\n * @cssprop --button-frost-active-border-color - Border color when pressed (frost variant)\n *\n * @example\n * ```html\n * <ore-button variant=\"solid\" color=\"primary\">Click me</ore-button>\n * <ore-button loading color=\"success\">Processing...</ore-button>\n * <ore-button effect=\"shine\" color=\"primary\">Shine</ore-button>\n * <ore-button effect=\"rainbow\" variant=\"frost\">Rainbow</ore-button>\n * ```\n */\nexport const BUTTON_TAG = 'ore-button' as const;\ndefine<OreButtonProps>(BUTTON_TAG, {\n formAssociated: true,\n props: {\n ...commonProps,\n effect: prop.string<ButtonEffect>(),\n fullwidth: prop.bool(false),\n href: prop.string(),\n iconOnly: prop.bool(false),\n label: prop.string(),\n rel: prop.string(),\n target: prop.string<LinkTarget>(),\n type: prop.oneOf(['button', 'submit', 'reset'] as const, 'button'),\n variant: prop.oneOf(BUTTON_VARIANTS, 'solid'),\n },\n setup(props) {\n const el = getHost();\n\n // Prefer group context over own props for color/size/variant.\n const groupCtx = inject(BUTTON_GROUP_CTX);\n const effectiveColor = computed(() => groupCtx?.color.value ?? props.color.value);\n const effectiveSize = computed(() => groupCtx?.size.value ?? props.size.value);\n const effectiveVariant = computed(() => groupCtx?.variant.value ?? props.variant.value);\n\n const isDisabled = computed(() => !!(props.disabled.value || props.loading.value));\n\n // isLink and effectiveRel are computed from signals — correct even if href changes at runtime.\n const { effectiveRel, isLink } = useLinkProps(props.href, props.rel, props.target);\n\n // Form association: relay submit/reset clicks to the associated form.\n // The inner <button> always has type=\"button\" so shadow DOM never drives native form actions.\n // getForm() returns null for link mode at runtime, so no form actions fire.\n const formField = useField({\n disabled: isDisabled,\n toFormValue: () => null,\n value: computed(() => ''),\n });\n\n const handleClick = (e: MouseEvent) => {\n if (isDisabled.value) {\n e.preventDefault();\n\n return;\n }\n\n // Link mode renders a real <a href> below — the browser handles left/middle/ctrl-click\n // navigation, target, and rel natively. No manual window.open/location.href needed.\n useFormAction(() => (isLink.value ? null : formField.internals.form), props.type, isDisabled, el)(e);\n };\n\n const handleKeydown = (e: KeyboardEvent) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n (el.shadowRoot?.querySelector('[part=\"button\"]') as HTMLElement)?.click();\n }\n };\n\n // ARIA attributes live on the host.\n bind({\n attr: {\n 'aria-busy': props.loading,\n 'aria-disabled': isDisabled,\n 'aria-label': props.label,\n color: effectiveColor,\n effect: props.effect,\n role: computed(() => el.getAttribute('role') ?? (isLink.value ? 'link' : 'button')),\n size: effectiveSize,\n tabindex: computed(() => (isDisabled.value ? '-1' : '0')),\n variant: effectiveVariant,\n },\n on: {\n keydown: handleKeydown,\n },\n });\n\n const buttonContent = html`\n <span class=\"loader\" part=\"loader\" aria-label=\"Loading\" ?hidden=${() => !props.loading.value}></span>\n <slot name=\"prefix\"></slot>\n <span class=\"content\" part=\"content\"><slot></slot></span>\n <slot name=\"suffix\"></slot>\n `;\n\n // The inner element (<a> or <span>) is deliberately non-focusable/decorative — this\n // component's real interactive semantics (role, tabindex, aria-label) live on the\n // custom-element host itself (see the `bind()` block above), since ore-button is\n // formAssociated and needs the host, not an inner element, to carry ElementInternals.\n // This differs from ore-navbar-item / ore-sidebar-item, which aren't form-associated\n // and so render their <a> as the one real focusable/semantic element.\n return html`\n ${() =>\n isLink.value\n ? html`<a\n part=\"button\"\n role=\"presentation\"\n tabindex=\"-1\"\n href=\"${props.href}\"\n :rel=\"${effectiveRel}\"\n :target=\"${props.target}\"\n @click=\"${handleClick}\">\n ${buttonContent}\n </a>`\n : html`<span part=\"button\" role=\"presentation\" @click=\"${handleClick}\">${buttonContent}</span>`}\n `;\n },\n shadow: { delegatesFocus: false },\n styles: [\n colorThemeMixin,\n coarsePointerMixin,\n reducedMotionMixin,\n roundedVariantMixin,\n forcedColorsMixin,\n sizeVariantMixin({\n lg: {\n fontSize: 'var(--text-base)',\n gap: 'var(--size-2-5)',\n height: 'var(--size-12)',\n iconSize: 'var(--size-6)',\n lineHeight: 'var(--leading-relaxed)',\n padding: 'var(--size-2-5) var(--size-5)',\n },\n sm: {\n fontSize: 'var(--text-sm)',\n gap: 'var(--size-1-5)',\n height: 'var(--size-8)',\n iconSize: 'var(--size-4)',\n lineHeight: 'var(--leading-tight)',\n padding: 'var(--size-1-5) var(--size-3)',\n },\n }),\n frostVariantMixin('[part=\"button\"]'),\n rainbowEffectMixin('[part=\"button\"]'),\n shineEffectMixin('[part=\"button\"]'),\n disabledLoadingMixin,\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,IAAa,IAAkB;CAAC;CAAS;CAAQ;CAAY;CAAW;CAAS;CAAQ;AAAO,GAsFnF,IAAa;AAC1B,EAAuB,GAAY;CACjC,gBAAgB;CAChB,OAAO;EACL,GAAG;EACH,QAAQ,EAAK,OAAqB;EAClC,WAAW,EAAK,KAAK,EAAK;EAC1B,MAAM,EAAK,OAAO;EAClB,UAAU,EAAK,KAAK,EAAK;EACzB,OAAO,EAAK,OAAO;EACnB,KAAK,EAAK,OAAO;EACjB,QAAQ,EAAK,OAAmB;EAChC,MAAM,EAAK,MAAM;GAAC;GAAU;GAAU;EAAO,GAAY,QAAQ;EACjE,SAAS,EAAK,MAAM,GAAiB,OAAO;CAC9C;CACA,MAAM,GAAO;EACX,IAAM,IAAK,EAAQ,GAGb,IAAW,EAAO,CAAgB,GAClC,IAAiB,QAAe,GAAU,MAAM,SAAS,EAAM,MAAM,KAAK,GAC1E,IAAgB,QAAe,GAAU,KAAK,SAAS,EAAM,KAAK,KAAK,GACvE,IAAmB,QAAe,GAAU,QAAQ,SAAS,EAAM,QAAQ,KAAK,GAEhF,IAAa,QAAe,CAAC,EAAE,EAAM,SAAS,SAAS,EAAM,QAAQ,MAAM,GAG3E,EAAE,iBAAc,cAAW,EAAa,EAAM,MAAM,EAAM,KAAK,EAAM,MAAM,GAK3E,IAAY,EAAS;GACzB,UAAU;GACV,mBAAmB;GACnB,OAAO,QAAe,EAAE;EAC1B,CAAC,GAEK,KAAe,MAAkB;GACrC,IAAI,EAAW,OAAO;IACpB,EAAE,eAAe;IAEjB;GACF;GAIA,QAAqB,EAAO,QAAQ,OAAO,EAAU,UAAU,MAAO,EAAM,MAAM,GAAY,CAAE,CAAC,CAAC,CAAC;EACrG;EAUA,EAAK;GACH,MAAM;IACJ,aAAa,EAAM;IACnB,iBAAiB;IACjB,cAAc,EAAM;IACpB,OAAO;IACP,QAAQ,EAAM;IACd,MAAM,QAAe,EAAG,aAAa,MAAM,MAAM,EAAO,QAAQ,SAAS,SAAS;IAClF,MAAM;IACN,UAAU,QAAgB,EAAW,QAAQ,OAAO,GAAI;IACxD,SAAS;GACX;GACA,IAAI,EACF,UArBmB,MAAqB;IAC1C,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAe,IAChB,EAAG,YAAY,cAAc,mBAAiB,EAAA,EAAmB,MAAM;GAE5E,EAiBE;EACF,CAAC;EAED,IAAM,IAAgB,CAAI;8EACgD,CAAC,EAAM,QAAQ,MAAM;;;;;EAY/F,OAAO,CAAI;cAEP,EAAO,QACH,CAAI;;;;sBAIM,EAAM,KAAK;sBACX,EAAa;yBACV,EAAM,OAAO;wBACd,EAAY;gBACpB,EAAc;oBAElB,CAAI,mDAAmD,EAAY,IAAI,EAAc,SAAS;;CAExG;CACA,QAAQ,EAAE,gBAAgB,GAAM;CAChC,QAAQ;EACN;EACA;EACA;EACA;EACA;EACA,EAAiB;GACf,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;GACA,IAAI;IACF,UAAU;IACV,KAAK;IACL,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,SAAS;GACX;EACF,CAAC;EACD,EAAkB,mBAAiB;EACnC,EAAmB,mBAAiB;EACpC,EAAiB,mBAAiB;EAClC;EACA;CACF;AACF,CAAC"}
|
package/dist/checkbox-group.cjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./ore/dist/directives/when.cjs");require("./ore/dist/directives.cjs");const 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("./headless/ore.cjs"),o=require("./headless/choice-field.cjs"),s=require("./headless/light-dom.cjs"),c=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const l=require("./inputs/shared/form-context.cjs"),u=require("./inputs/checkbox-group/checkbox-group.css.cjs");let d=require("@vielzeug/ore"),f=require("@vielzeug/ripple");var p=(0,d.createContext)(`CheckboxGroupContext`),m=`ore-checkbox-group`;(0,d.define)(m,{formAssociated:!0,props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,error:d.prop.string(),helper:d.prop.string(),label:d.prop.string(),name:d.prop.string(),orientation:d.prop.string(`vertical`),required:d.prop.bool(!1),values:d.prop.string()},setup(t){let n=(0,d.getHost)(),r=(0,d.useEmit)(),i=(0,d.useSlots)(),u=d.watchEffect,m=(0,d.inject)(l.FORM_CTX),h=l.useFormContext(t,m),g=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./ore/dist/directives/when.cjs");require("./ore/dist/directives.cjs");const 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("./headless/ore.cjs"),o=require("./headless/choice-field.cjs"),s=require("./headless/light-dom.cjs"),c=require("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const l=require("./inputs/shared/form-context.cjs"),u=require("./inputs/checkbox-group/checkbox-group.css.cjs");let d=require("@vielzeug/ore"),f=require("@vielzeug/ripple");var p=(0,d.createContext)(`CheckboxGroupContext`),m=`ore-checkbox-group`;(0,d.define)(m,{formAssociated:!0,props:{...i.themableBundle,...i.sizableBundle,...i.disablableBundle,error:d.prop.string(),helper:d.prop.string(),label:d.prop.string(),name:d.prop.string(),orientation:d.prop.string(`vertical`),required:d.prop.bool(!1),values:{...d.prop.string(),reflect:!1}},setup(t){let n=(0,d.getHost)(),r=(0,d.useEmit)(),i=(0,d.useSlots)(),u=d.watchEffect,m=(0,d.inject)(l.FORM_CTX),h=l.useFormContext(t,m),g=o.createChoiceField({disabled:h.disabled,error:t.error,helper:t.helper,multiple:(0,f.signal)(!0),prefix:`checkbox-group`,required:t.required,signal:a.lifecycleSignal(d.onCleanup),validateOn:m?.validateOn,value:t.values});g.attachFormField(c.useField({disabled:g.disabled,onReset:g.reset,toFormValue:e=>e,validationMessage:g.validationMessage,validity:g.validity,value:g.formValue}));let _=g.selectedValues,v=()=>s.getLightChildrenByTag(n,`ore-checkbox`),y=e=>s.getChoiceLabel(v(),e),b=e=>{let t=_.value,n=t.map(y);r(`change`,{labels:n,originalEvent:e,values:t})},x=(e,t)=>{g.toggleValue(e),g.triggerValidation(`change`),b(t)};(0,d.provide)(p,{color:t.color,disabled:(0,f.computed)(()=>!!t.disabled.value),size:t.size,toggle:x,values:_});let S=()=>{let e=_.value,n=t.color.value,r=t.size.value,i=t.disabled.value,a=v();for(let t of a){let a=t.getAttribute(`value`)??``;t.toggleAttribute(`checked`,e.includes(a)),n?t.setAttribute(`color`,n):t.removeAttribute(`color`),r?t.setAttribute(`size`,r):t.removeAttribute(`size`),t.toggleAttribute(`disabled`,!!i)}};u(()=>{i.elements().value,S()}),u(()=>{i.elements().value;let e=v().map(e=>{let t=t=>{t.stopPropagation();let n=(e.getAttribute(`value`)??``).trim();n&&x(n,t)};return e.addEventListener(`change`,t),()=>{e.removeEventListener(`change`,t)}});return()=>{for(let t of e)t()}});let C=(0,d.createStableId)(`checkbox-group-legend`),w=`${C}-error`,T=`${C}-helper`,E=()=>!!t.error.value,D=()=>!!t.helper.value&&!E();return(0,d.bind)({attr:{size:h.size,values:g.formValue}}),d.html`
|
|
2
2
|
<fieldset
|
|
3
3
|
role="group"
|
|
4
4
|
aria-required="${()=>String(!!t.required.value)}"
|
|
5
|
-
aria-invalid="${()=>String(
|
|
6
|
-
aria-errormessage="${()=>
|
|
7
|
-
aria-describedby="${()=>
|
|
8
|
-
<legend id="${
|
|
5
|
+
aria-invalid="${()=>String(E())}"
|
|
6
|
+
aria-errormessage="${()=>E()?w:null}"
|
|
7
|
+
aria-describedby="${()=>E()?w:D()?T:null}">
|
|
8
|
+
<legend id="${C}" ?hidden=${()=>!t.label.value}>
|
|
9
9
|
${t.label}${e.when(()=>!!t.required.value,()=>d.html`<span aria-hidden="true"> *</span>`)}
|
|
10
10
|
</legend>
|
|
11
11
|
<div class="checkbox-group-items" part="items">
|
|
12
12
|
<slot></slot>
|
|
13
13
|
</div>
|
|
14
|
-
<div class="error-text" id="${
|
|
15
|
-
<div class="helper-text" id="${
|
|
14
|
+
<div class="error-text" id="${w}" role="alert" ?hidden=${()=>!E()}>${t.error}</div>
|
|
15
|
+
<div class="helper-text" id="${T}" ?hidden=${()=>!D()}>${t.helper}</div>
|
|
16
16
|
</fieldset>
|
|
17
17
|
`},styles:[r.colorThemeMixin,t.sizeVariantMixin(),n.disabledStateMixin,u.default]}),exports.CHECKBOX_GROUP_CTX=p,exports.CHECKBOX_GROUP_TAG=m;
|
|
18
18
|
//# sourceMappingURL=checkbox-group.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox-group.cjs","names":[],"sources":["../src/inputs/checkbox-group/checkbox-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 { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\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 './checkbox-group.css?inline';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\nexport type CheckboxGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n size: Readable<ComponentSize | undefined>;\n toggle: (value: string, originalEvent?: Event) => void;\n values: Readable<string[]>;\n};\n\nexport const CHECKBOX_GROUP_CTX = createContext<CheckboxGroupContext>('CheckboxGroupContext');\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type OreCheckboxGroupProps = {\n /** Theme color — propagated to all child ore-checkbox elements */\n color?: ThemeColor;\n /** Disable all checkboxes in the group */\n disabled?: boolean;\n /** Error message shown below the group */\n error?: string;\n /** Helper text shown below the group */\n helper?: string;\n /** Legend / label for the fieldset. Required for accessibility. */\n label?: string;\n /** Form field name used during submission */\n name?: string;\n /** Layout direction of the checkbox options */\n orientation?: 'vertical' | 'horizontal';\n /** Mark the group as required */\n required?: boolean;\n /** Size — propagated to all child ore-checkbox elements */\n size?: ComponentSize;\n /** Comma-separated list of currently checked values */\n values?: string;\n};\n\nexport type OreCheckboxGroupEvents = {\n change: ChoiceChangeDetail;\n};\n\n/**\n * A fieldset wrapper that groups `ore-checkbox` elements, provides shared\n * `color` and `size` via context, and manages multi-value selection state.\n *\n * @element ore-checkbox-group\n *\n * @attr {string} label - Legend text (required for a11y)\n * @attr {string} values - Comma-separated list of checked values\n * @attr {boolean} disabled - Disable all checkboxes in the group\n * @attr {string} error - Error message\n * @attr {string} helper - Helper text\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when selection changes. detail: { value: string, values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-checkbox` elements here\n *\n * @cssprop --checkbox-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --checkbox-group-gap - Gap between checkbox items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-checkbox-group name=\"fruits\" label=\"Favourite fruits\" required>\n * <ore-checkbox value=\"apple\">Apple</ore-checkbox>\n * <ore-checkbox value=\"banana\">Banana</ore-checkbox>\n * <ore-checkbox value=\"cherry\">Cherry</ore-checkbox>\n * </ore-checkbox-group>\n * <ore-checkbox-group name=\"options\" orientation=\"horizontal\" color=\"primary\">\n * <ore-checkbox value=\"a\">Option A</ore-checkbox>\n * <ore-checkbox value=\"b\">Option B</ore-checkbox>\n * </ore-checkbox-group>\n * ```\n */\nexport const CHECKBOX_GROUP_TAG = 'ore-checkbox-group' as const;\ndefine<OreCheckboxGroupProps>(CHECKBOX_GROUP_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n helper: prop.string(),\n label: prop.string(),\n name: prop.string(),\n orientation: prop.string('vertical'),\n required: prop.bool(false),\n values: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCheckboxGroupEvents>();\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 multiple: signal(true),\n prefix: 'checkbox-group',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.values,\n });\n\n _formField = useField<string>({ disabled: choice.disabled, toFormValue: (v) => v, value: choice.formValue });\n\n const checkedValues = choice.selectedValues;\n\n const getCheckboxes = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-checkbox');\n const getLabelForValue = (value: string): string => getChoiceLabel(getCheckboxes(), value);\n const emitChange = (originalEvent?: Event) => {\n const values = checkedValues.value;\n\n const labels = values.map(getLabelForValue);\n\n emit('change', { labels, originalEvent, values });\n };\n\n const toggleCheckbox = (val: string, originalEvent?: Event) => {\n choice.toggleValue(val);\n el.setAttribute('values', choice.formValue.value);\n choice.triggerValidation('change');\n emitChange(originalEvent);\n };\n\n provide(CHECKBOX_GROUP_CTX, {\n color: props.color,\n disabled: computed(() => Boolean(props.disabled.value)),\n size: props.size,\n toggle: toggleCheckbox,\n values: checkedValues,\n });\n\n // Sync checked state + color/size/disabled onto slotted ore-checkbox children\n const syncChildren = () => {\n const values = checkedValues.value;\n const color = props.color.value;\n const size = props.size.value;\n const disabled = props.disabled.value;\n const checkboxes = getCheckboxes();\n\n for (const checkbox of checkboxes) {\n const val = checkbox.getAttribute('value') ?? '';\n\n checkbox.toggleAttribute('checked', values.includes(val));\n\n if (color) checkbox.setAttribute('color', color);\n else checkbox.removeAttribute('color');\n\n if (size) checkbox.setAttribute('size', size);\n else checkbox.removeAttribute('size');\n\n checkbox.toggleAttribute('disabled', Boolean(disabled));\n }\n };\n\n watch(() => {\n void slots.elements().value;\n syncChildren();\n });\n\n watch(() => {\n void slots.elements().value;\n\n const listeners = getCheckboxes().map((checkbox) => {\n const handler = (event: Event) => {\n event.stopPropagation();\n\n const val = (checkbox.getAttribute('value') ?? '').trim();\n\n if (!val) return;\n\n toggleCheckbox(val, event);\n };\n\n checkbox.addEventListener('change', handler);\n\n return () => {\n checkbox.removeEventListener('change', handler);\n };\n });\n\n return () => {\n for (const dispose of listeners) dispose();\n };\n });\n\n const legendId = createStableId('checkbox-group-legend');\n const errorId = `${legendId}-error`;\n const helperId = `${legendId}-helper`;\n const hasError = () => Boolean(props.error.value);\n const hasHelper = () => Boolean(props.helper.value) && !hasError();\n\n bind({ attr: { size: fCtxProps.size } });\n\n return html`\n <fieldset\n role=\"group\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n aria-invalid=\"${() => String(hasError())}\"\n aria-errormessage=\"${() => (hasError() ? errorId : null)}\"\n aria-describedby=\"${() => (hasError() ? errorId : hasHelper() ? helperId : null)}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"checkbox-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div class=\"error-text\" id=\"${errorId}\" role=\"alert\" ?hidden=${() => !hasError()}>${props.error}</div>\n <div class=\"helper-text\" id=\"${helperId}\" ?hidden=${() => !hasHelper()}>${props.helper}</div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"checkbox-group.cjs","names":[],"sources":["../src/inputs/checkbox-group/checkbox-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 { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\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 './checkbox-group.css?inline';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\nexport type CheckboxGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n size: Readable<ComponentSize | undefined>;\n toggle: (value: string, originalEvent?: Event) => void;\n values: Readable<string[]>;\n};\n\nexport const CHECKBOX_GROUP_CTX = createContext<CheckboxGroupContext>('CheckboxGroupContext');\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type OreCheckboxGroupProps = {\n /** Theme color — propagated to all child ore-checkbox elements */\n color?: ThemeColor;\n /** Disable all checkboxes in the group */\n disabled?: boolean;\n /** Error message shown below the group */\n error?: string;\n /** Helper text shown below the group */\n helper?: string;\n /** Legend / label for the fieldset. Required for accessibility. */\n label?: string;\n /** Form field name used during submission */\n name?: string;\n /** Layout direction of the checkbox options */\n orientation?: 'vertical' | 'horizontal';\n /** Mark the group as required */\n required?: boolean;\n /** Size — propagated to all child ore-checkbox elements */\n size?: ComponentSize;\n /** Comma-separated list of currently checked values */\n values?: string;\n};\n\nexport type OreCheckboxGroupEvents = {\n change: ChoiceChangeDetail;\n};\n\n/**\n * A fieldset wrapper that groups `ore-checkbox` elements, provides shared\n * `color` and `size` via context, and manages multi-value selection state.\n *\n * @element ore-checkbox-group\n *\n * @attr {string} label - Legend text (required for a11y)\n * @attr {string} values - Comma-separated list of checked values\n * @attr {boolean} disabled - Disable all checkboxes in the group\n * @attr {string} error - Error message\n * @attr {string} helper - Helper text\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when selection changes. detail: { value: string, values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-checkbox` elements here\n *\n * @cssprop --checkbox-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --checkbox-group-gap - Gap between checkbox items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-checkbox-group name=\"fruits\" label=\"Favourite fruits\" required>\n * <ore-checkbox value=\"apple\">Apple</ore-checkbox>\n * <ore-checkbox value=\"banana\">Banana</ore-checkbox>\n * <ore-checkbox value=\"cherry\">Cherry</ore-checkbox>\n * </ore-checkbox-group>\n * <ore-checkbox-group name=\"options\" orientation=\"horizontal\" color=\"primary\">\n * <ore-checkbox value=\"a\">Option A</ore-checkbox>\n * <ore-checkbox value=\"b\">Option B</ore-checkbox>\n * </ore-checkbox-group>\n * ```\n */\nexport const CHECKBOX_GROUP_TAG = 'ore-checkbox-group' as const;\ndefine<OreCheckboxGroupProps>(CHECKBOX_GROUP_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n helper: prop.string(),\n label: prop.string(),\n name: prop.string(),\n orientation: prop.string('vertical'),\n required: prop.bool(false),\n // Not auto-reflected (`reflect: false`) — the derived, interaction-updated selection\n // (`choice.formValue`) is the single writer for this attribute, via `bind()` below;\n // letting `prop.string()`'s own default reflection also write the raw incoming value\n // would leave two effects racing to set the same attribute from different sources.\n values: { ...prop.string(), reflect: false },\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCheckboxGroupEvents>();\n const slots = useSlots();\n const watch = watchEffect;\n\n const 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 multiple: signal(true),\n prefix: 'checkbox-group',\n required: props.required,\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.values,\n });\n\n choice.attachFormField(\n useField<string>({\n disabled: choice.disabled,\n onReset: choice.reset,\n toFormValue: (v) => v,\n validationMessage: choice.validationMessage,\n validity: choice.validity,\n value: choice.formValue,\n }),\n );\n\n const checkedValues = choice.selectedValues;\n\n const getCheckboxes = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-checkbox');\n const getLabelForValue = (value: string): string => getChoiceLabel(getCheckboxes(), value);\n const emitChange = (originalEvent?: Event) => {\n const values = checkedValues.value;\n\n const labels = values.map(getLabelForValue);\n\n emit('change', { labels, originalEvent, values });\n };\n\n const toggleCheckbox = (val: string, originalEvent?: Event) => {\n choice.toggleValue(val);\n choice.triggerValidation('change');\n emitChange(originalEvent);\n };\n\n provide(CHECKBOX_GROUP_CTX, {\n color: props.color,\n disabled: computed(() => Boolean(props.disabled.value)),\n size: props.size,\n toggle: toggleCheckbox,\n values: checkedValues,\n });\n\n // Sync checked state + color/size/disabled onto slotted ore-checkbox children\n const syncChildren = () => {\n const values = checkedValues.value;\n const color = props.color.value;\n const size = props.size.value;\n const disabled = props.disabled.value;\n const checkboxes = getCheckboxes();\n\n for (const checkbox of checkboxes) {\n const val = checkbox.getAttribute('value') ?? '';\n\n checkbox.toggleAttribute('checked', values.includes(val));\n\n if (color) checkbox.setAttribute('color', color);\n else checkbox.removeAttribute('color');\n\n if (size) checkbox.setAttribute('size', size);\n else checkbox.removeAttribute('size');\n\n checkbox.toggleAttribute('disabled', Boolean(disabled));\n }\n };\n\n watch(() => {\n void slots.elements().value;\n syncChildren();\n });\n\n watch(() => {\n void slots.elements().value;\n\n const listeners = getCheckboxes().map((checkbox) => {\n const handler = (event: Event) => {\n event.stopPropagation();\n\n const val = (checkbox.getAttribute('value') ?? '').trim();\n\n if (!val) return;\n\n toggleCheckbox(val, event);\n };\n\n checkbox.addEventListener('change', handler);\n\n return () => {\n checkbox.removeEventListener('change', handler);\n };\n });\n\n return () => {\n for (const dispose of listeners) dispose();\n };\n });\n\n const legendId = createStableId('checkbox-group-legend');\n const errorId = `${legendId}-error`;\n const helperId = `${legendId}-helper`;\n const hasError = () => Boolean(props.error.value);\n const hasHelper = () => Boolean(props.helper.value) && !hasError();\n\n // Reactive, not a one-off `el.setAttribute()` inside the click handler: the host's `values`\n // attribute must stay in sync with the selection regardless of *why* it changed (a click,\n // or `reset()` on ancestor form reset — neither of which should need its own copy of this).\n bind({ attr: { size: fCtxProps.size, values: choice.formValue } });\n\n return html`\n <fieldset\n role=\"group\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n aria-invalid=\"${() => String(hasError())}\"\n aria-errormessage=\"${() => (hasError() ? errorId : null)}\"\n aria-describedby=\"${() => (hasError() ? errorId : hasHelper() ? helperId : null)}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"checkbox-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div class=\"error-text\" id=\"${errorId}\" role=\"alert\" ?hidden=${() => !hasError()}>${props.error}</div>\n <div class=\"helper-text\" id=\"${helperId}\" ?hidden=${() => !hasHelper()}>${props.helper}</div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/checkbox-group.js
CHANGED
|
@@ -27,58 +27,64 @@ v(j, {
|
|
|
27
27
|
name: C.string(),
|
|
28
28
|
orientation: C.string("vertical"),
|
|
29
29
|
required: C.bool(!1),
|
|
30
|
-
values:
|
|
30
|
+
values: {
|
|
31
|
+
...C.string(),
|
|
32
|
+
reflect: !1
|
|
33
|
+
}
|
|
31
34
|
},
|
|
32
35
|
setup(t) {
|
|
33
|
-
let n = y(), r = T(), i = E(), a = D, o = x(f), m = p(t, o), g =
|
|
36
|
+
let n = y(), r = T(), i = E(), a = D, o = x(f), m = p(t, o), g = c({
|
|
34
37
|
disabled: m.disabled,
|
|
35
38
|
error: t.error,
|
|
36
|
-
getFormField: () => g,
|
|
37
39
|
helper: t.helper,
|
|
38
40
|
multiple: k(!0),
|
|
39
41
|
prefix: "checkbox-group",
|
|
42
|
+
required: t.required,
|
|
40
43
|
signal: s(S),
|
|
41
44
|
validateOn: o?.validateOn,
|
|
42
45
|
value: t.values
|
|
43
46
|
});
|
|
44
|
-
g
|
|
45
|
-
disabled:
|
|
47
|
+
g.attachFormField(d({
|
|
48
|
+
disabled: g.disabled,
|
|
49
|
+
onReset: g.reset,
|
|
46
50
|
toFormValue: (e) => e,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
validationMessage: g.validationMessage,
|
|
52
|
+
validity: g.validity,
|
|
53
|
+
value: g.formValue
|
|
54
|
+
}));
|
|
55
|
+
let v = g.selectedValues, C = () => u(n, "ore-checkbox"), j = (e) => l(C(), e), M = (e) => {
|
|
56
|
+
let t = v.value, n = t.map(j);
|
|
51
57
|
r("change", {
|
|
52
58
|
labels: n,
|
|
53
59
|
originalEvent: e,
|
|
54
60
|
values: t
|
|
55
61
|
});
|
|
56
|
-
},
|
|
57
|
-
|
|
62
|
+
}, N = (e, t) => {
|
|
63
|
+
g.toggleValue(e), g.triggerValidation("change"), M(t);
|
|
58
64
|
};
|
|
59
65
|
w(A, {
|
|
60
66
|
color: t.color,
|
|
61
67
|
disabled: O(() => !!t.disabled.value),
|
|
62
68
|
size: t.size,
|
|
63
|
-
toggle:
|
|
64
|
-
values:
|
|
69
|
+
toggle: N,
|
|
70
|
+
values: v
|
|
65
71
|
});
|
|
66
|
-
let
|
|
67
|
-
let e =
|
|
72
|
+
let P = () => {
|
|
73
|
+
let e = v.value, n = t.color.value, r = t.size.value, i = t.disabled.value, a = C();
|
|
68
74
|
for (let t of a) {
|
|
69
75
|
let a = t.getAttribute("value") ?? "";
|
|
70
76
|
t.toggleAttribute("checked", e.includes(a)), n ? t.setAttribute("color", n) : t.removeAttribute("color"), r ? t.setAttribute("size", r) : t.removeAttribute("size"), t.toggleAttribute("disabled", !!i);
|
|
71
77
|
}
|
|
72
78
|
};
|
|
73
79
|
a(() => {
|
|
74
|
-
i.elements().value,
|
|
80
|
+
i.elements().value, P();
|
|
75
81
|
}), a(() => {
|
|
76
82
|
i.elements().value;
|
|
77
|
-
let e =
|
|
83
|
+
let e = C().map((e) => {
|
|
78
84
|
let t = (t) => {
|
|
79
85
|
t.stopPropagation();
|
|
80
86
|
let n = (e.getAttribute("value") ?? "").trim();
|
|
81
|
-
n &&
|
|
87
|
+
n && N(n, t);
|
|
82
88
|
};
|
|
83
89
|
return e.addEventListener("change", t), () => {
|
|
84
90
|
e.removeEventListener("change", t);
|
|
@@ -88,22 +94,25 @@ v(j, {
|
|
|
88
94
|
for (let t of e) t();
|
|
89
95
|
};
|
|
90
96
|
});
|
|
91
|
-
let
|
|
92
|
-
return h({ attr: {
|
|
97
|
+
let F = _("checkbox-group-legend"), I = `${F}-error`, L = `${F}-helper`, R = () => !!t.error.value, z = () => !!t.helper.value && !R();
|
|
98
|
+
return h({ attr: {
|
|
99
|
+
size: m.size,
|
|
100
|
+
values: g.formValue
|
|
101
|
+
} }), b`
|
|
93
102
|
<fieldset
|
|
94
103
|
role="group"
|
|
95
104
|
aria-required="${() => String(!!t.required.value)}"
|
|
96
|
-
aria-invalid="${() => String(
|
|
97
|
-
aria-errormessage="${() =>
|
|
98
|
-
aria-describedby="${() =>
|
|
99
|
-
<legend id="${
|
|
105
|
+
aria-invalid="${() => String(R())}"
|
|
106
|
+
aria-errormessage="${() => R() ? I : null}"
|
|
107
|
+
aria-describedby="${() => R() ? I : z() ? L : null}">
|
|
108
|
+
<legend id="${F}" ?hidden=${() => !t.label.value}>
|
|
100
109
|
${t.label}${e(() => !!t.required.value, () => b`<span aria-hidden="true"> *</span>`)}
|
|
101
110
|
</legend>
|
|
102
111
|
<div class="checkbox-group-items" part="items">
|
|
103
112
|
<slot></slot>
|
|
104
113
|
</div>
|
|
105
|
-
<div class="error-text" id="${
|
|
106
|
-
<div class="helper-text" id="${
|
|
114
|
+
<div class="error-text" id="${I}" role="alert" ?hidden=${() => !R()}>${t.error}</div>
|
|
115
|
+
<div class="helper-text" id="${L}" ?hidden=${() => !z()}>${t.helper}</div>
|
|
107
116
|
</fieldset>
|
|
108
117
|
`;
|
|
109
118
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox-group.js","names":[],"sources":["../src/inputs/checkbox-group/checkbox-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 { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\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 './checkbox-group.css?inline';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\nexport type CheckboxGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n size: Readable<ComponentSize | undefined>;\n toggle: (value: string, originalEvent?: Event) => void;\n values: Readable<string[]>;\n};\n\nexport const CHECKBOX_GROUP_CTX = createContext<CheckboxGroupContext>('CheckboxGroupContext');\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type OreCheckboxGroupProps = {\n /** Theme color — propagated to all child ore-checkbox elements */\n color?: ThemeColor;\n /** Disable all checkboxes in the group */\n disabled?: boolean;\n /** Error message shown below the group */\n error?: string;\n /** Helper text shown below the group */\n helper?: string;\n /** Legend / label for the fieldset. Required for accessibility. */\n label?: string;\n /** Form field name used during submission */\n name?: string;\n /** Layout direction of the checkbox options */\n orientation?: 'vertical' | 'horizontal';\n /** Mark the group as required */\n required?: boolean;\n /** Size — propagated to all child ore-checkbox elements */\n size?: ComponentSize;\n /** Comma-separated list of currently checked values */\n values?: string;\n};\n\nexport type OreCheckboxGroupEvents = {\n change: ChoiceChangeDetail;\n};\n\n/**\n * A fieldset wrapper that groups `ore-checkbox` elements, provides shared\n * `color` and `size` via context, and manages multi-value selection state.\n *\n * @element ore-checkbox-group\n *\n * @attr {string} label - Legend text (required for a11y)\n * @attr {string} values - Comma-separated list of checked values\n * @attr {boolean} disabled - Disable all checkboxes in the group\n * @attr {string} error - Error message\n * @attr {string} helper - Helper text\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when selection changes. detail: { value: string, values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-checkbox` elements here\n *\n * @cssprop --checkbox-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --checkbox-group-gap - Gap between checkbox items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-checkbox-group name=\"fruits\" label=\"Favourite fruits\" required>\n * <ore-checkbox value=\"apple\">Apple</ore-checkbox>\n * <ore-checkbox value=\"banana\">Banana</ore-checkbox>\n * <ore-checkbox value=\"cherry\">Cherry</ore-checkbox>\n * </ore-checkbox-group>\n * <ore-checkbox-group name=\"options\" orientation=\"horizontal\" color=\"primary\">\n * <ore-checkbox value=\"a\">Option A</ore-checkbox>\n * <ore-checkbox value=\"b\">Option B</ore-checkbox>\n * </ore-checkbox-group>\n * ```\n */\nexport const CHECKBOX_GROUP_TAG = 'ore-checkbox-group' as const;\ndefine<OreCheckboxGroupProps>(CHECKBOX_GROUP_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n helper: prop.string(),\n label: prop.string(),\n name: prop.string(),\n orientation: prop.string('vertical'),\n required: prop.bool(false),\n values: prop.string(),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCheckboxGroupEvents>();\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 multiple: signal(true),\n prefix: 'checkbox-group',\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.values,\n });\n\n _formField = useField<string>({ disabled: choice.disabled, toFormValue: (v) => v, value: choice.formValue });\n\n const checkedValues = choice.selectedValues;\n\n const getCheckboxes = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-checkbox');\n const getLabelForValue = (value: string): string => getChoiceLabel(getCheckboxes(), value);\n const emitChange = (originalEvent?: Event) => {\n const values = checkedValues.value;\n\n const labels = values.map(getLabelForValue);\n\n emit('change', { labels, originalEvent, values });\n };\n\n const toggleCheckbox = (val: string, originalEvent?: Event) => {\n choice.toggleValue(val);\n el.setAttribute('values', choice.formValue.value);\n choice.triggerValidation('change');\n emitChange(originalEvent);\n };\n\n provide(CHECKBOX_GROUP_CTX, {\n color: props.color,\n disabled: computed(() => Boolean(props.disabled.value)),\n size: props.size,\n toggle: toggleCheckbox,\n values: checkedValues,\n });\n\n // Sync checked state + color/size/disabled onto slotted ore-checkbox children\n const syncChildren = () => {\n const values = checkedValues.value;\n const color = props.color.value;\n const size = props.size.value;\n const disabled = props.disabled.value;\n const checkboxes = getCheckboxes();\n\n for (const checkbox of checkboxes) {\n const val = checkbox.getAttribute('value') ?? '';\n\n checkbox.toggleAttribute('checked', values.includes(val));\n\n if (color) checkbox.setAttribute('color', color);\n else checkbox.removeAttribute('color');\n\n if (size) checkbox.setAttribute('size', size);\n else checkbox.removeAttribute('size');\n\n checkbox.toggleAttribute('disabled', Boolean(disabled));\n }\n };\n\n watch(() => {\n void slots.elements().value;\n syncChildren();\n });\n\n watch(() => {\n void slots.elements().value;\n\n const listeners = getCheckboxes().map((checkbox) => {\n const handler = (event: Event) => {\n event.stopPropagation();\n\n const val = (checkbox.getAttribute('value') ?? '').trim();\n\n if (!val) return;\n\n toggleCheckbox(val, event);\n };\n\n checkbox.addEventListener('change', handler);\n\n return () => {\n checkbox.removeEventListener('change', handler);\n };\n });\n\n return () => {\n for (const dispose of listeners) dispose();\n };\n });\n\n const legendId = createStableId('checkbox-group-legend');\n const errorId = `${legendId}-error`;\n const helperId = `${legendId}-helper`;\n const hasError = () => Boolean(props.error.value);\n const hasHelper = () => Boolean(props.helper.value) && !hasError();\n\n bind({ attr: { size: fCtxProps.size } });\n\n return html`\n <fieldset\n role=\"group\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n aria-invalid=\"${() => String(hasError())}\"\n aria-errormessage=\"${() => (hasError() ? errorId : null)}\"\n aria-describedby=\"${() => (hasError() ? errorId : hasHelper() ? helperId : null)}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"checkbox-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div class=\"error-text\" id=\"${errorId}\" role=\"alert\" ?hidden=${() => !hasError()}>${props.error}</div>\n <div class=\"helper-text\" id=\"${helperId}\" ?hidden=${() => !hasHelper()}>${props.helper}</div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"checkbox-group.js","names":[],"sources":["../src/inputs/checkbox-group/checkbox-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 { computed, type Readable, signal } from '@vielzeug/ripple';\n\nimport type { ComponentSize, ThemeColor } from '../../types';\n\nimport {\n type ChoiceChangeDetail,\n lifecycleSignal,\n createChoiceField,\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 './checkbox-group.css?inline';\n\n// ─── Context ──────────────────────────────────────────────────────────────────\n\nexport type CheckboxGroupContext = {\n color: Readable<ThemeColor | undefined>;\n disabled: Readable<boolean>;\n size: Readable<ComponentSize | undefined>;\n toggle: (value: string, originalEvent?: Event) => void;\n values: Readable<string[]>;\n};\n\nexport const CHECKBOX_GROUP_CTX = createContext<CheckboxGroupContext>('CheckboxGroupContext');\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type OreCheckboxGroupProps = {\n /** Theme color — propagated to all child ore-checkbox elements */\n color?: ThemeColor;\n /** Disable all checkboxes in the group */\n disabled?: boolean;\n /** Error message shown below the group */\n error?: string;\n /** Helper text shown below the group */\n helper?: string;\n /** Legend / label for the fieldset. Required for accessibility. */\n label?: string;\n /** Form field name used during submission */\n name?: string;\n /** Layout direction of the checkbox options */\n orientation?: 'vertical' | 'horizontal';\n /** Mark the group as required */\n required?: boolean;\n /** Size — propagated to all child ore-checkbox elements */\n size?: ComponentSize;\n /** Comma-separated list of currently checked values */\n values?: string;\n};\n\nexport type OreCheckboxGroupEvents = {\n change: ChoiceChangeDetail;\n};\n\n/**\n * A fieldset wrapper that groups `ore-checkbox` elements, provides shared\n * `color` and `size` via context, and manages multi-value selection state.\n *\n * @element ore-checkbox-group\n *\n * @attr {string} label - Legend text (required for a11y)\n * @attr {string} values - Comma-separated list of checked values\n * @attr {boolean} disabled - Disable all checkboxes in the group\n * @attr {string} error - Error message\n * @attr {string} helper - Helper text\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} orientation - Layout: 'vertical' | 'horizontal'\n * @attr {boolean} required - Required field\n *\n * @fires change - Emitted when selection changes. detail: { value: string, values: string[], labels: string[], originalEvent?: Event }\n *\n * @slot - Place `ore-checkbox` elements here\n *\n * @cssprop --checkbox-group-direction - Flex direction of the items list ('row' | 'column')\n * @cssprop --checkbox-group-gap - Gap between checkbox items\n * @part items - Items container.\n * @example\n * ```html\n * <ore-checkbox-group name=\"fruits\" label=\"Favourite fruits\" required>\n * <ore-checkbox value=\"apple\">Apple</ore-checkbox>\n * <ore-checkbox value=\"banana\">Banana</ore-checkbox>\n * <ore-checkbox value=\"cherry\">Cherry</ore-checkbox>\n * </ore-checkbox-group>\n * <ore-checkbox-group name=\"options\" orientation=\"horizontal\" color=\"primary\">\n * <ore-checkbox value=\"a\">Option A</ore-checkbox>\n * <ore-checkbox value=\"b\">Option B</ore-checkbox>\n * </ore-checkbox-group>\n * ```\n */\nexport const CHECKBOX_GROUP_TAG = 'ore-checkbox-group' as const;\ndefine<OreCheckboxGroupProps>(CHECKBOX_GROUP_TAG, {\n formAssociated: true,\n props: {\n ...themableBundle,\n ...sizableBundle,\n ...disablableBundle,\n error: prop.string(),\n helper: prop.string(),\n label: prop.string(),\n name: prop.string(),\n orientation: prop.string('vertical'),\n required: prop.bool(false),\n // Not auto-reflected (`reflect: false`) — the derived, interaction-updated selection\n // (`choice.formValue`) is the single writer for this attribute, via `bind()` below;\n // letting `prop.string()`'s own default reflection also write the raw incoming value\n // would leave two effects racing to set the same attribute from different sources.\n values: { ...prop.string(), reflect: false },\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCheckboxGroupEvents>();\n const slots = useSlots();\n const watch = watchEffect;\n\n const 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 multiple: signal(true),\n prefix: 'checkbox-group',\n required: props.required,\n signal: lifecycleSignal(onCleanup),\n validateOn: formCtx?.validateOn,\n value: props.values,\n });\n\n choice.attachFormField(\n useField<string>({\n disabled: choice.disabled,\n onReset: choice.reset,\n toFormValue: (v) => v,\n validationMessage: choice.validationMessage,\n validity: choice.validity,\n value: choice.formValue,\n }),\n );\n\n const checkedValues = choice.selectedValues;\n\n const getCheckboxes = (): HTMLElement[] => getLightChildrenByTag(el, 'ore-checkbox');\n const getLabelForValue = (value: string): string => getChoiceLabel(getCheckboxes(), value);\n const emitChange = (originalEvent?: Event) => {\n const values = checkedValues.value;\n\n const labels = values.map(getLabelForValue);\n\n emit('change', { labels, originalEvent, values });\n };\n\n const toggleCheckbox = (val: string, originalEvent?: Event) => {\n choice.toggleValue(val);\n choice.triggerValidation('change');\n emitChange(originalEvent);\n };\n\n provide(CHECKBOX_GROUP_CTX, {\n color: props.color,\n disabled: computed(() => Boolean(props.disabled.value)),\n size: props.size,\n toggle: toggleCheckbox,\n values: checkedValues,\n });\n\n // Sync checked state + color/size/disabled onto slotted ore-checkbox children\n const syncChildren = () => {\n const values = checkedValues.value;\n const color = props.color.value;\n const size = props.size.value;\n const disabled = props.disabled.value;\n const checkboxes = getCheckboxes();\n\n for (const checkbox of checkboxes) {\n const val = checkbox.getAttribute('value') ?? '';\n\n checkbox.toggleAttribute('checked', values.includes(val));\n\n if (color) checkbox.setAttribute('color', color);\n else checkbox.removeAttribute('color');\n\n if (size) checkbox.setAttribute('size', size);\n else checkbox.removeAttribute('size');\n\n checkbox.toggleAttribute('disabled', Boolean(disabled));\n }\n };\n\n watch(() => {\n void slots.elements().value;\n syncChildren();\n });\n\n watch(() => {\n void slots.elements().value;\n\n const listeners = getCheckboxes().map((checkbox) => {\n const handler = (event: Event) => {\n event.stopPropagation();\n\n const val = (checkbox.getAttribute('value') ?? '').trim();\n\n if (!val) return;\n\n toggleCheckbox(val, event);\n };\n\n checkbox.addEventListener('change', handler);\n\n return () => {\n checkbox.removeEventListener('change', handler);\n };\n });\n\n return () => {\n for (const dispose of listeners) dispose();\n };\n });\n\n const legendId = createStableId('checkbox-group-legend');\n const errorId = `${legendId}-error`;\n const helperId = `${legendId}-helper`;\n const hasError = () => Boolean(props.error.value);\n const hasHelper = () => Boolean(props.helper.value) && !hasError();\n\n // Reactive, not a one-off `el.setAttribute()` inside the click handler: the host's `values`\n // attribute must stay in sync with the selection regardless of *why* it changed (a click,\n // or `reset()` on ancestor form reset — neither of which should need its own copy of this).\n bind({ attr: { size: fCtxProps.size, values: choice.formValue } });\n\n return html`\n <fieldset\n role=\"group\"\n aria-required=\"${() => String(Boolean(props.required.value))}\"\n aria-invalid=\"${() => String(hasError())}\"\n aria-errormessage=\"${() => (hasError() ? errorId : null)}\"\n aria-describedby=\"${() => (hasError() ? errorId : hasHelper() ? helperId : null)}\">\n <legend id=\"${legendId}\" ?hidden=${() => !props.label.value}>\n ${props.label}${when(\n () => Boolean(props.required.value),\n () => html`<span aria-hidden=\"true\"> *</span>`,\n )}\n </legend>\n <div class=\"checkbox-group-items\" part=\"items\">\n <slot></slot>\n </div>\n <div class=\"error-text\" id=\"${errorId}\" role=\"alert\" ?hidden=${() => !hasError()}>${props.error}</div>\n <div class=\"helper-text\" id=\"${helperId}\" ?hidden=${() => !hasHelper()}>${props.helper}</div>\n </fieldset>\n `;\n },\n styles: [colorThemeMixin, sizeVariantMixin(), disabledStateMixin, componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/checkbox.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const l=require("./inputs/shared/field-binding.cjs"),u=require("./inputs/shared/form-context.cjs"),d=require("./inputs/shared/templates.cjs"),f=require("./checkbox-group.cjs"),p=require("./inputs/checkbox/checkbox.css.cjs");let m=require("@vielzeug/ore"),h=require("@vielzeug/ripple");var g=`ore-checkbox`;(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(),indeterminate:m.prop.bool(!1),name:m.prop.string(),value:m.prop.string(`on`)},setup(e){let t=(0,m.useEmit)(),n=(0,m.inject)(u.FORM_CTX),r=u.useFormContext(e,n),i=(0,m.inject)(f.CHECKBOX_GROUP_CTX),a=
|
|
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("./ore/dist/forms/field.cjs");require("./ore/dist/forms.cjs");const l=require("./inputs/shared/field-binding.cjs"),u=require("./inputs/shared/form-context.cjs"),d=require("./inputs/shared/templates.cjs"),f=require("./checkbox-group.cjs"),p=require("./inputs/checkbox/checkbox.css.cjs");let m=require("@vielzeug/ore"),h=require("@vielzeug/ripple");var g=`ore-checkbox`;(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(),indeterminate:m.prop.bool(!1),name:m.prop.string(),required:m.prop.bool(!1),value:m.prop.string(`on`)},setup(e){let t=(0,m.useEmit)(),n=(0,m.inject)(u.FORM_CTX),r=u.useFormContext(e,n),i=(0,m.inject)(f.CHECKBOX_GROUP_CTX),a=s.createCheckable({checked:e.checked,clearIndeterminateFirst:!0,disabled:(0,h.computed)(()=>r.disabled.value||!!i?.disabled.value),error:e.error,group:i,helper:e.helper,indeterminate:e.indeterminate,onToggle:e=>{a.triggerValidation(`change`),!i&&t(`change`,e)},prefix:`checkbox`,required:e.required,signal:o.lifecycleSignal(m.onCleanup),validateOn:n?.validateOn,value:e.value});a.attachFormField(c.useField({disabled:a.disabled,onReset:a.reset,toFormValue:e=>e,validationMessage:a.validationMessage,validity:a.validity,value:a.checkableFormValue}));let{assistiveId:p,checked:g,disabled:_,errorText:v,handleClick:y,handleKeydown:b,helperText:x,indeterminate:S,labelId:C}=a;return l.applyCheckableBinding(r.size,{assistiveId:p,checked:g,disabled:_,errorText:v,handleClick:y,handleKeydown:b,helperText:x,indeterminate:S,labelId:C,required:e.required},`checkbox`),m.html`
|
|
2
2
|
<div class="checkbox-wrapper" part="checkbox">
|
|
3
3
|
<div class="box" part="box">
|
|
4
4
|
<ore-icon class="checkmark" name="check" size="14" stroke-width="2" aria-hidden="true"></ore-icon>
|
|
5
5
|
<ore-icon class="dash" name="minus" size="14" stroke-width="2" aria-hidden="true"></ore-icon>
|
|
6
6
|
</div>
|
|
7
7
|
</div>
|
|
8
|
-
<span class="label" part="label" id="${
|
|
9
|
-
${d.renderHelperRegion(
|
|
8
|
+
<span class="label" part="label" id="${C}"><slot></slot></span>
|
|
9
|
+
${d.renderHelperRegion(p,v,x)}
|
|
10
10
|
`},styles:[r.colorThemeMixin,r.forcedColorsFormControlMixin,n.disabledStateMixin,e.coarsePointerMixin,t.sizeVariantMixin(a.CONTROL_SIZE_PRESET),p.default]}),exports.CHECKBOX_TAG=g;
|
|
11
11
|
//# sourceMappingURL=checkbox.cjs.map
|
package/dist/checkbox.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkbox.cjs","names":[],"sources":["../src/inputs/checkbox/checkbox.ts"],"sourcesContent":["import { define, html, inject, prop, 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 } from '../../headless';\nimport '../../content/icon/icon';\nimport { CONTROL_SIZE_PRESET, disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsFormControlMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { CHECKBOX_GROUP_CTX } from '../checkbox-group/checkbox-group';\nimport { applyCheckableBinding } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './checkbox.css?inline';\n\nexport type OreCheckboxEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreCheckboxProps = 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 checkbox */\n helper?: string;\n /** Indeterminate state (partially checked) */\n indeterminate?: boolean;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A customizable checkbox component with theme colors, sizes, and indeterminate state support.\n *\n * @element ore-checkbox\n *\n * @attr {boolean} checked - Checked state\n * @attr {boolean} disabled - Disable checkbox interaction\n * @attr {boolean} indeterminate - Indeterminate (partially checked) state\n * @attr {string} value - Field value submitted with forms\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Checkbox size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the checkbox\n *\n * @fires change - Emitted when checkbox is toggled. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Checkbox label text\n *\n * @cssprop --checkbox-size - Control size (width and height)\n * @cssprop --checkbox-radius - Control border radius\n * @cssprop --checkbox-bg - Unchecked background color\n * @cssprop --checkbox-border-color - Unchecked border color\n * @cssprop --checkbox-checked-bg - Checked/indeterminate background color\n * @cssprop --checkbox-color - Checkmark icon color\n * @cssprop --checkbox-font-size - Label font size\n * @part checkbox - The checkbox wrapper element\n * @part box - The visual checkbox box\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-checkbox name=\"agree\" required>I agree to the terms</ore-checkbox>\n * <ore-checkbox checked color=\"primary\" size=\"lg\">Enabled by default</ore-checkbox>\n * <ore-checkbox error=\"This field is required\" helper=\"Check to continue\">Accept</ore-checkbox>\n * ```\n */\nexport const CHECKBOX_TAG = 'ore-checkbox' as const;\ndefine<OreCheckboxProps>(CHECKBOX_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 indeterminate: prop.bool(false),\n name: prop.string(),\n value: prop.string('on'),\n },\n setup(props) {\n const emit = useEmit<OreCheckboxEvents>();\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n const groupCtx = inject(CHECKBOX_GROUP_CTX);\n\n
|
|
1
|
+
{"version":3,"file":"checkbox.cjs","names":[],"sources":["../src/inputs/checkbox/checkbox.ts"],"sourcesContent":["import { define, html, inject, prop, 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 } from '../../headless';\nimport '../../content/icon/icon';\nimport { CONTROL_SIZE_PRESET, disablableBundle, sizableBundle, themableBundle } from '../../shared';\nimport {\n coarsePointerMixin,\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsFormControlMixin,\n sizeVariantMixin,\n} from '../../styles';\nimport { CHECKBOX_GROUP_CTX } from '../checkbox-group/checkbox-group';\nimport { applyCheckableBinding } from '../shared/field-binding';\nimport { FORM_CTX, useFormContext } from '../shared/form-context';\nimport { renderHelperRegion } from '../shared/templates';\nimport componentStyles from './checkbox.css?inline';\n\nexport type OreCheckboxEvents = {\n change: CheckableChangePayload;\n};\n\nexport type OreCheckboxProps = 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 checkbox */\n helper?: string;\n /** Indeterminate state (partially checked) */\n indeterminate?: boolean;\n /** Require this checkbox to be checked for `<ore-form>` validation (e.g. a consent checkbox) */\n required?: boolean;\n /** Component size */\n size?: ComponentSize;\n};\n\n/**\n * A customizable checkbox component with theme colors, sizes, and indeterminate state support.\n *\n * @element ore-checkbox\n *\n * @attr {boolean} checked - Checked state\n * @attr {boolean} disabled - Disable checkbox interaction\n * @attr {boolean} indeterminate - Indeterminate (partially checked) state\n * @attr {boolean} required - Require this checkbox to be checked for `<ore-form>` validation\n * @attr {string} value - Field value submitted with forms\n * @attr {string} name - Form field name\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} size - Checkbox size: 'sm' | 'md' | 'lg'\n * @attr {string} error - Error message (marks field as invalid)\n * @attr {string} helper - Helper text displayed below the checkbox\n *\n * @fires change - Emitted when checkbox is toggled. detail: { checked: boolean, value: string, originalEvent?: Event }\n *\n * @slot - Checkbox label text\n *\n * @cssprop --checkbox-size - Control size (width and height)\n * @cssprop --checkbox-radius - Control border radius\n * @cssprop --checkbox-bg - Unchecked background color\n * @cssprop --checkbox-border-color - Unchecked border color\n * @cssprop --checkbox-checked-bg - Checked/indeterminate background color\n * @cssprop --checkbox-color - Checkmark icon color\n * @cssprop --checkbox-font-size - Label font size\n * @part checkbox - The checkbox wrapper element\n * @part box - The visual checkbox box\n * @part label - The label element\n * @part helper-text - The helper/error text element\n *\n * @example\n * ```html\n * <ore-checkbox name=\"agree\" required>I agree to the terms</ore-checkbox>\n * <ore-checkbox checked color=\"primary\" size=\"lg\">Enabled by default</ore-checkbox>\n * <ore-checkbox error=\"This field is required\" helper=\"Check to continue\">Accept</ore-checkbox>\n * ```\n */\nexport const CHECKBOX_TAG = 'ore-checkbox' as const;\ndefine<OreCheckboxProps>(CHECKBOX_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 indeterminate: prop.bool(false),\n name: prop.string(),\n required: prop.bool(false),\n value: prop.string('on'),\n },\n setup(props) {\n const emit = useEmit<OreCheckboxEvents>();\n\n const formCtx = inject(FORM_CTX);\n const fCtxProps = useFormContext(props, formCtx);\n const groupCtx = inject(CHECKBOX_GROUP_CTX);\n\n const checkable = createCheckable({\n checked: props.checked,\n clearIndeterminateFirst: true,\n disabled: computed(() => fCtxProps.disabled.value || Boolean(groupCtx?.disabled.value)),\n error: props.error,\n group: groupCtx,\n helper: props.helper,\n indeterminate: props.indeterminate,\n onToggle: (payload) => {\n checkable.triggerValidation('change');\n\n // In a checkbox-group, the group owns change emission/state updates.\n // Emitting here would bubble to the group and toggle a second time.\n if (groupCtx) return;\n\n emit('change', payload);\n },\n prefix: 'checkbox',\n required: props.required,\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 onReset: checkable.reset,\n toFormValue: (v) => v,\n validationMessage: checkable.validationMessage,\n validity: checkable.validity,\n value: checkable.checkableFormValue,\n }),\n );\n\n const {\n assistiveId,\n checked,\n disabled,\n errorText,\n handleClick,\n handleKeydown,\n helperText,\n indeterminate,\n labelId,\n } = checkable;\n\n applyCheckableBinding(\n fCtxProps.size,\n {\n assistiveId,\n checked,\n disabled,\n errorText,\n handleClick,\n handleKeydown,\n helperText,\n indeterminate,\n labelId,\n required: props.required,\n },\n 'checkbox',\n );\n\n return html`\n <div class=\"checkbox-wrapper\" part=\"checkbox\">\n <div class=\"box\" part=\"box\">\n <ore-icon class=\"checkmark\" name=\"check\" size=\"14\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\n <ore-icon class=\"dash\" name=\"minus\" size=\"14\" stroke-width=\"2\" aria-hidden=\"true\"></ore-icon>\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":"uvBAkFA,IAAa,EAAe,4BACH,EAAc,CACrC,eAAgB,GAChB,MAAO,CACL,GAAG,EAAA,eACH,GAAG,EAAA,cACH,GAAG,EAAA,iBACH,QAAS,EAAA,KAAK,KAAK,EAAK,EACxB,MAAO,EAAA,KAAK,OAAO,EACnB,OAAQ,EAAA,KAAK,OAAO,EACpB,cAAe,EAAA,KAAK,KAAK,EAAK,EAC9B,KAAM,EAAA,KAAK,OAAO,EAClB,SAAU,EAAA,KAAK,KAAK,EAAK,EACzB,MAAO,EAAA,KAAK,OAAO,IAAI,CACzB,EACA,MAAM,EAAO,CACX,IAAM,GAAA,EAAA,EAAA,QAAA,CAAkC,EAElC,GAAA,EAAA,EAAA,OAAA,CAAiB,EAAA,QAAQ,EACzB,EAAY,EAAA,eAAe,EAAO,CAAO,EACzC,GAAA,EAAA,EAAA,OAAA,CAAkB,EAAA,kBAAkB,EAEpC,EAAY,EAAA,gBAAgB,CAChC,QAAS,EAAM,QACf,wBAAyB,GACzB,UAAA,EAAA,EAAA,SAAA,KAAyB,EAAU,SAAS,OAAS,EAAQ,GAAU,SAAS,KAAM,EACtF,MAAO,EAAM,MACb,MAAO,EACP,OAAQ,EAAM,OACd,cAAe,EAAM,cACrB,SAAW,GAAY,CACrB,EAAU,kBAAkB,QAAQ,EAIhC,IAEJ,EAAK,SAAU,CAAO,CACxB,EACA,OAAQ,WACR,SAAU,EAAM,SAChB,OAAQ,EAAA,gBAAgB,EAAA,SAAS,EACjC,WAAY,GAAS,WACrB,MAAO,EAAM,KACf,CAAC,EAED,EAAU,gBACR,EAAA,SAAwB,CACtB,SAAU,EAAU,SACpB,QAAS,EAAU,MACnB,YAAc,GAAM,EACpB,kBAAmB,EAAU,kBAC7B,SAAU,EAAU,SACpB,MAAO,EAAU,kBACnB,CAAC,CACH,EAEA,GAAM,CACJ,cACA,UACA,WACA,YACA,cACA,gBACA,aACA,gBACA,WACE,EAmBJ,OAjBA,EAAA,sBACE,EAAU,KACV,CACE,cACA,UACA,WACA,YACA,cACA,gBACA,aACA,gBACA,UACA,SAAU,EAAM,QAClB,EACA,UACF,EAEO,EAAA,IAAI;;;;;;;6CAO8B,EAAQ;QAC7C,EAAA,mBAAmB,EAAa,EAAW,CAAU,EAAE;KAE7D,EACA,OAAQ,CACN,EAAA,gBACA,EAAA,6BACA,EAAA,mBACA,EAAA,mBACA,EAAA,iBAAiB,EAAA,mBAAmB,EACpC,EAAA,OACF,CACF,CAAC"}
|
package/dist/checkbox.js
CHANGED
|
@@ -28,42 +28,47 @@ v(T, {
|
|
|
28
28
|
helper: S.string(),
|
|
29
29
|
indeterminate: S.bool(!1),
|
|
30
30
|
name: S.string(),
|
|
31
|
+
required: S.bool(!1),
|
|
31
32
|
value: S.string("on")
|
|
32
33
|
},
|
|
33
34
|
setup(e) {
|
|
34
|
-
let t = C(), n = b(p), r = m(e, n), i = b(g), a =
|
|
35
|
+
let t = C(), n = b(p), r = m(e, n), i = b(g), a = u({
|
|
35
36
|
checked: e.checked,
|
|
36
37
|
clearIndeterminateFirst: !0,
|
|
37
38
|
disabled: w(() => r.disabled.value || !!i?.disabled.value),
|
|
38
39
|
error: e.error,
|
|
39
|
-
getFormField: () => a,
|
|
40
40
|
group: i,
|
|
41
41
|
helper: e.helper,
|
|
42
42
|
indeterminate: e.indeterminate,
|
|
43
43
|
onToggle: (e) => {
|
|
44
|
-
|
|
44
|
+
a.triggerValidation("change"), !i && t("change", e);
|
|
45
45
|
},
|
|
46
46
|
prefix: "checkbox",
|
|
47
|
+
required: e.required,
|
|
47
48
|
signal: l(x),
|
|
48
49
|
validateOn: n?.validateOn,
|
|
49
50
|
value: e.value
|
|
50
51
|
});
|
|
51
|
-
a
|
|
52
|
-
disabled:
|
|
52
|
+
a.attachFormField(d({
|
|
53
|
+
disabled: a.disabled,
|
|
54
|
+
onReset: a.reset,
|
|
53
55
|
toFormValue: (e) => e,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
validationMessage: a.validationMessage,
|
|
57
|
+
validity: a.validity,
|
|
58
|
+
value: a.checkableFormValue
|
|
59
|
+
}));
|
|
60
|
+
let { assistiveId: o, checked: s, disabled: c, errorText: _, handleClick: v, handleKeydown: S, helperText: T, indeterminate: E, labelId: D } = a;
|
|
57
61
|
return f(r.size, {
|
|
58
|
-
assistiveId:
|
|
59
|
-
checked:
|
|
60
|
-
disabled:
|
|
61
|
-
errorText:
|
|
62
|
-
handleClick:
|
|
63
|
-
handleKeydown:
|
|
64
|
-
helperText:
|
|
65
|
-
indeterminate:
|
|
66
|
-
labelId:
|
|
62
|
+
assistiveId: o,
|
|
63
|
+
checked: s,
|
|
64
|
+
disabled: c,
|
|
65
|
+
errorText: _,
|
|
66
|
+
handleClick: v,
|
|
67
|
+
handleKeydown: S,
|
|
68
|
+
helperText: T,
|
|
69
|
+
indeterminate: E,
|
|
70
|
+
labelId: D,
|
|
71
|
+
required: e.required
|
|
67
72
|
}, "checkbox"), y`
|
|
68
73
|
<div class="checkbox-wrapper" part="checkbox">
|
|
69
74
|
<div class="box" part="box">
|
|
@@ -71,8 +76,8 @@ v(T, {
|
|
|
71
76
|
<ore-icon class="dash" name="minus" size="14" stroke-width="2" aria-hidden="true"></ore-icon>
|
|
72
77
|
</div>
|
|
73
78
|
</div>
|
|
74
|
-
<span class="label" part="label" id="${
|
|
75
|
-
${h(
|
|
79
|
+
<span class="label" part="label" id="${D}"><slot></slot></span>
|
|
80
|
+
${h(o, _, T)}
|
|
76
81
|
`;
|
|
77
82
|
},
|
|
78
83
|
styles: [
|