@vielzeug/refine 2.4.0 → 3.0.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/README.md +5 -18
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js.map +1 -1
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js.map +1 -1
- package/dist/async.cjs +1 -1
- package/dist/async.cjs.map +1 -1
- package/dist/async.js +3 -3
- package/dist/async.js.map +1 -1
- package/dist/box.cjs.map +1 -1
- package/dist/box.js.map +1 -1
- package/dist/card.cjs +1 -1
- package/dist/card.cjs.map +1 -1
- package/dist/card.js +2 -2
- package/dist/card.js.map +1 -1
- package/dist/checkbox-group.cjs +1 -1
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +1 -1
- package/dist/checkbox-group.js.map +1 -1
- package/dist/combobox.cjs +2 -2
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +7 -3
- package/dist/combobox.js.map +1 -1
- package/dist/command-palette.cjs +1 -1
- package/dist/command-palette.cjs.map +1 -1
- package/dist/command-palette.js +6 -2
- package/dist/command-palette.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/card/card.d.ts +2 -2
- package/dist/content/card/card.d.ts.map +1 -1
- package/dist/content/pagination/pagination.css.cjs +1 -1
- package/dist/content/pagination/pagination.css.cjs.map +1 -1
- package/dist/content/pagination/pagination.css.js +1 -1
- package/dist/content/pagination/pagination.css.js.map +1 -1
- package/dist/core/field-base.cjs +1 -1
- package/dist/core/field-base.cjs.map +1 -1
- package/dist/core/field-base.d.ts +1 -1
- package/dist/core/field-base.js +1 -1
- package/dist/core/field-base.js.map +1 -1
- package/dist/core/nav.cjs +1 -1
- package/dist/core/nav.cjs.map +1 -1
- package/dist/core/nav.d.ts +4 -1
- package/dist/core/nav.d.ts.map +1 -1
- package/dist/core/nav.js +41 -28
- package/dist/core/nav.js.map +1 -1
- package/dist/core/option-list.cjs +1 -1
- package/dist/core/option-list.cjs.map +1 -1
- package/dist/core/option-list.d.ts.map +1 -1
- package/dist/core/option-list.js +30 -28
- package/dist/core/option-list.js.map +1 -1
- package/dist/core/slider.cjs +1 -1
- package/dist/core/slider.cjs.map +1 -1
- package/dist/core/slider.js +1 -1
- package/dist/core/slider.js.map +1 -1
- package/dist/core/spinner.cjs +1 -1
- package/dist/core/spinner.cjs.map +1 -1
- package/dist/core/spinner.js +1 -1
- package/dist/core/spinner.js.map +1 -1
- package/dist/custom-elements.json +215 -17
- package/dist/datagrid.cjs +73 -73
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +331 -332
- package/dist/datagrid.js.map +1 -1
- package/dist/disclosure/accordion/accordion.css.cjs +1 -1
- package/dist/disclosure/accordion/accordion.css.cjs.map +1 -1
- package/dist/disclosure/accordion/accordion.css.js +1 -1
- package/dist/disclosure/accordion/accordion.css.js.map +1 -1
- package/dist/disclosure/accordion/accordion.d.ts +5 -5
- package/dist/disclosure/accordion/accordion.d.ts.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.d.ts +3 -3
- package/dist/disclosure/accordion-item/accordion-item.d.ts.map +1 -1
- package/dist/disclosure/tab-item/tab-item.css.cjs +1 -1
- package/dist/disclosure/tab-item/tab-item.css.cjs.map +1 -1
- package/dist/disclosure/tab-item/tab-item.css.js +1 -1
- package/dist/disclosure/tab-item/tab-item.css.js.map +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts +3 -3
- package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
- package/dist/disclosure/tabs/tabs.css.cjs +1 -1
- package/dist/disclosure/tabs/tabs.css.cjs.map +1 -1
- package/dist/disclosure/tabs/tabs.css.js +1 -1
- package/dist/disclosure/tabs/tabs.css.js.map +1 -1
- package/dist/disclosure/tabs/tabs.d.ts +5 -5
- package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
- package/dist/drawer.cjs +1 -1
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js +1 -1
- package/dist/drawer.js.map +1 -1
- package/dist/feedback/toast/toast.d.ts.map +1 -1
- package/dist/file-input.cjs +1 -1
- package/dist/file-input.cjs.map +1 -1
- package/dist/file-input.js +2 -2
- package/dist/file-input.js.map +1 -1
- package/dist/frameworks/elements.d.ts +4 -86
- package/dist/frameworks/react.d.ts +5 -4
- package/dist/frameworks/vue.d.ts +4 -4
- package/dist/grid.cjs +2 -2
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +21 -18
- package/dist/grid.js.map +1 -1
- package/dist/inputs/combobox/combobox.d.ts +12 -1
- package/dist/inputs/combobox/combobox.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +24 -32
- package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
- package/dist/inputs/file-input/file-input-upload.js +1 -1
- package/dist/inputs/file-input/file-input-upload.js.map +1 -1
- package/dist/inputs/index.d.ts +2 -3
- package/dist/inputs/index.d.ts.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.js +6 -6
- package/dist/inputs/shared/field-binding.js.map +1 -1
- package/dist/layout/box/box.css.cjs +1 -1
- package/dist/layout/box/box.css.cjs.map +1 -1
- package/dist/layout/box/box.css.js +1 -1
- package/dist/layout/box/box.css.js.map +1 -1
- package/dist/layout/box/box.d.ts +3 -3
- package/dist/layout/box/box.d.ts.map +1 -1
- package/dist/layout/grid/grid.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/navbar/navbar.d.ts +1 -1
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/menu.cjs +1 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +6 -6
- package/dist/menu.js.map +1 -1
- package/dist/navbar.cjs +1 -1
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +87 -85
- package/dist/navbar.js.map +1 -1
- package/dist/navigation-menu.cjs +1 -1
- package/dist/navigation-menu.cjs.map +1 -1
- package/dist/navigation-menu.js +1 -1
- package/dist/navigation-menu.js.map +1 -1
- package/dist/number-input.cjs +1 -1
- package/dist/number-input.cjs.map +1 -1
- package/dist/number-input.js +1 -1
- package/dist/number-input.js.map +1 -1
- package/dist/overlay/popover/popover.d.ts.map +1 -1
- package/dist/overlay/tooltip/tooltip.d.ts.map +1 -1
- package/dist/popover.cjs +1 -1
- package/dist/popover.cjs.map +1 -1
- package/dist/popover.js +6 -6
- package/dist/popover.js.map +1 -1
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs +1 -1
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +5 -5
- package/dist/radio.js.map +1 -1
- package/dist/rating.cjs +1 -1
- package/dist/rating.cjs.map +1 -1
- package/dist/rating.js +1 -1
- package/dist/rating.js.map +1 -1
- package/dist/refine.cjs +207 -207
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +268 -268
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +222 -222
- package/dist/refine.js.map +1 -1
- package/dist/shared/types.d.ts +0 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/sidebar.cjs +1 -1
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +83 -83
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs +1 -1
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js +11 -9
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.cjs +1 -1
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js +20 -20
- package/dist/slider.js.map +1 -1
- package/dist/stats.cjs +1 -1
- package/dist/stats.cjs.map +1 -1
- package/dist/stats.js +2 -2
- package/dist/stats.js.map +1 -1
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js.map +1 -1
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js.map +1 -1
- package/dist/toast.cjs +8 -8
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js +32 -33
- package/dist/toast.js.map +1 -1
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.cjs.map +1 -1
- package/dist/tooltip.js +2 -2
- package/dist/tooltip.js.map +1 -1
- package/dist/types/elements.d.ts +207 -0
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.css.js","names":[],"sources":["../../../src/content/card/card.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_bg: var(--card-bg, var(--color-canvas));\n --_color: var(--card-color, var(--color-contrast-900));\n --_border: var(--card-border, var(--border));\n --_border-color: var(--card-border-color, var(--color-divider));\n --_radius: var(--card-radius, var(--rounded-lg));\n --_padding: var(--card-padding, var(--size-4));\n --_shadow: var(--card-shadow, var(--shadow-sm));\n --_hover-shadow: var(--card-hover-shadow, var(--shadow-md));\n\n position: relative;\n display: flex;\n flex-direction: column;\n }\n\n .card {\n position: relative;\n display: flex;\n flex: 1;\n flex-direction: column;\n overflow: hidden;\n color: var(--_color);\n background: var(--_bg);\n border-radius: var(--_radius);\n backdrop-filter: blur(var(--blur-md));\n transition:\n box-shadow var(--transition-normal),\n transform var(--transition-normal),\n background var(--transition-normal),\n border-color var(--transition-normal);\n }\n\n /* ── Card Structure ───────────────────────── */\n\n .card-media {\n flex-shrink: 0;\n overflow: hidden;\n }\n\n .card-media img {\n display: block;\n width: 100%;\n height: auto;\n object-fit: cover;\n }\n\n .card-body {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-height: 0;\n }\n\n .card-header,\n .card-content,\n .card-footer {\n padding: var(--_padding);\n }\n\n .card-header {\n padding-bottom: calc(var(--_padding) / 2);\n }\n\n .card-footer {\n padding-top: calc(var(--_padding) / 2);\n }\n\n .card-content {\n flex: 1;\n }\n\n .card-actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--size-2);\n align-items: center;\n padding: var(--_padding);\n padding-top: calc(var(--_padding) / 2);\n }\n\n [hidden] {\n display: none;\n }\n}\n\n@layer refine.variants {\n /* Default — bordered card with shadow */\n :host(:not([variant], [color])) {\n --_shadow: var(--card-shadow, var(--shadow-sm));\n }\n\n :host(:not([variant], [color])) .card {\n border: var(--_border) solid var(--_border-color);\n box-shadow: var(--_shadow);\n }\n\n /* Default + color — tinted backdrop */\n :host(:not([variant])[color]) {\n --_bg: var(--card-bg, var(--_theme-backdrop));\n --_border-color: var(--card-border-color, var(--_theme-border));\n }\n\n :host(:not([variant])[color]) .card {\n border: var(--_border) solid var(--_border-color);\n }\n\n :host(:not([variant])[color]:hover) {\n --_bg: var(--card-bg, color-mix(in oklch, var(--_theme-base) 16%, var(--color-contrast-50)));\n --_border-color: var(--card-border-color, var(--_theme-focus));\n }\n\n /* Solid — fully filled with theme color */\n :host([variant='solid']) {\n --_bg: var(--card-bg, var(--_theme-base));\n --_color: var(--card-color, var(--_theme-contrast));\n --_border-color: var(--card-border-color, var(--_theme-base));\n --_shadow: var(--card-shadow, var(--shadow-sm));\n }\n\n :host([variant='solid']) .card {\n border: var(--_border) solid var(--_border-color);\n box-shadow: var(--_shadow);\n }\n\n /* Flat — tinted backdrop, subtle border */\n :host([variant='flat']) {\n --_border-color: var(--card-border-color, color-mix(in oklch, var(--_theme-border) 50%, transparent));\n }\n\n :host([variant='flat']) .card {\n border: var(--_border) solid var(--_border-color);\n }\n
|
|
1
|
+
{"version":3,"file":"card.css.js","names":[],"sources":["../../../src/content/card/card.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n --_bg: var(--card-bg, var(--color-canvas));\n --_color: var(--card-color, var(--color-contrast-900));\n --_border: var(--card-border, var(--border));\n --_border-color: var(--card-border-color, var(--color-divider));\n --_radius: var(--card-radius, var(--rounded-lg));\n --_padding: var(--card-padding, var(--size-4));\n --_shadow: var(--card-shadow, var(--shadow-sm));\n --_hover-shadow: var(--card-hover-shadow, var(--shadow-md));\n\n position: relative;\n display: flex;\n flex-direction: column;\n }\n\n .card {\n position: relative;\n display: flex;\n flex: 1;\n flex-direction: column;\n overflow: hidden;\n color: var(--_color);\n background: var(--_bg);\n border-radius: var(--_radius);\n backdrop-filter: blur(var(--blur-md));\n transition:\n box-shadow var(--transition-normal),\n transform var(--transition-normal),\n background var(--transition-normal),\n border-color var(--transition-normal);\n }\n\n /* ── Card Structure ───────────────────────── */\n\n .card-media {\n flex-shrink: 0;\n overflow: hidden;\n }\n\n .card-media img {\n display: block;\n width: 100%;\n height: auto;\n object-fit: cover;\n }\n\n .card-body {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-height: 0;\n }\n\n .card-header,\n .card-content,\n .card-footer {\n padding: var(--_padding);\n }\n\n .card-header {\n padding-bottom: calc(var(--_padding) / 2);\n }\n\n .card-footer {\n padding-top: calc(var(--_padding) / 2);\n }\n\n .card-content {\n flex: 1;\n }\n\n .card-actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--size-2);\n align-items: center;\n padding: var(--_padding);\n padding-top: calc(var(--_padding) / 2);\n }\n\n [hidden] {\n display: none;\n }\n}\n\n@layer refine.variants {\n /* Default — bordered card with shadow */\n :host(:not([variant], [color])) {\n --_shadow: var(--card-shadow, var(--shadow-sm));\n }\n\n :host(:not([variant], [color])) .card {\n border: var(--_border) solid var(--_border-color);\n box-shadow: var(--_shadow);\n }\n\n /* Default + color — tinted backdrop */\n :host(:not([variant])[color]) {\n --_bg: var(--card-bg, var(--_theme-backdrop));\n --_border-color: var(--card-border-color, var(--_theme-border));\n }\n\n :host(:not([variant])[color]) .card {\n border: var(--_border) solid var(--_border-color);\n }\n\n :host(:not([variant])[color]:hover) {\n --_bg: var(--card-bg, color-mix(in oklch, var(--_theme-base) 16%, var(--color-contrast-50)));\n --_border-color: var(--card-border-color, var(--_theme-focus));\n }\n\n /* Solid — fully filled with theme color */\n :host([variant='solid']) {\n --_bg: var(--card-bg, var(--_theme-base));\n --_color: var(--card-color, var(--_theme-contrast));\n --_border-color: var(--card-border-color, var(--_theme-base));\n --_shadow: var(--card-shadow, var(--shadow-sm));\n }\n\n :host([variant='solid']) .card {\n border: var(--_border) solid var(--_border-color);\n box-shadow: var(--_shadow);\n }\n\n /* Flat — tinted backdrop, subtle border */\n :host([variant='flat']) {\n --_border-color: var(--card-border-color, color-mix(in oklch, var(--_theme-border) 50%, transparent));\n }\n\n :host([variant='flat']) .card {\n border: var(--_border) solid var(--_border-color);\n }\n}\n\n@layer refine.overrides {\n /* ── Loading bar ──────────────────────────── */\n\n /* Bar lives inside .card which already has overflow:hidden,\n so translateX can sweep freely without any overflow. */\n .loading-bar {\n position: absolute;\n inset-inline-start: 0;\n top: 0;\n display: none;\n width: 65%;\n height: 3px;\n background: linear-gradient(90deg, transparent 0%, var(--_theme-base, var(--color-primary)) 50%, transparent 100%);\n }\n\n :host([loading]) .loading-bar {\n display: block;\n animation: var(--_motion-animation, loading-bar 1.4s linear infinite);\n }\n\n /* In RTL the loading bar sweeps right-to-left */\n :host(:dir(rtl)[loading]) .loading-bar {\n animation-direction: reverse;\n }\n\n /* ── Disabled ─────────────────────────────── */\n\n :host([disabled]) {\n pointer-events: none;\n opacity: 0.6;\n }\n\n :host([disabled][interactive]) .card {\n cursor: not-allowed;\n }\n\n /* ── Interactive hover/active ─────────────── */\n\n :host([interactive]:not([disabled]):hover) .card {\n box-shadow: var(--_hover-shadow);\n transform: translateY(-2px) scale(1.01);\n }\n\n :host([interactive]) .card {\n cursor: pointer;\n will-change: transform;\n }\n\n /* `:active` bubbles to every ancestor of whatever element is actually being pressed, so\n without the `:not(:has(...))` guard, pressing a nested interactive descendant (a swatch\n button, a slotted `<ore-button>`, a link, etc. — kept in sync with card.ts's\n `INTERACTIVE_DESCENDANT_SELECTOR`) would *also* visually \"press\" the whole card even though\n `handleClick` already ignores the click for activation purposes. `:host(:has(...))`\n (`:has()` as `:host()`'s functional argument) is invalid — `:host()` only accepts a compound\n selector and doesn't accept `:has()` inside it, so the bare `:host` pseudo-class chained\n directly with the rest (no parentheses around it) is used instead — same reasoning as\n list-item.css's identical `:host:has(...)` rules. */\n :host[interactive]:not([disabled]):active:not(\n :has(\n button:active,\n a[href]:active,\n input:active,\n select:active,\n textarea:active,\n summary:active,\n [role='button']:active,\n [role='link']:active\n )\n ) .card {\n box-shadow: var(--_shadow);\n transform: translateY(0) scale(0.99);\n transition:\n box-shadow var(--transition-fast),\n transform 120ms var(--ease-spring);\n }\n\n :host([interactive]:focus-visible) .card {\n outline: var(--border-2) solid var(--_theme-focus, var(--color-primary));\n outline-offset: var(--border-2);\n }\n\n /* ── Horizontal orientation ───────────────── */\n\n :host([orientation='horizontal']) .card {\n flex-direction: row;\n }\n\n :host([orientation='horizontal']) .card-media {\n width: 30%;\n min-width: 192px;\n max-width: 320px;\n }\n\n :host([orientation='horizontal']) .card-media img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n :host([orientation='horizontal']) .card-body {\n flex: 1;\n }\n}\n\n@keyframes loading-bar {\n 0% {\n transform: translateX(-100%);\n }\n\n 100% {\n transform: translateX(200%);\n }\n}\n"],"mappings":""}
|
|
@@ -22,14 +22,14 @@ export type OreCardProps = {
|
|
|
22
22
|
/** Internal padding size */
|
|
23
23
|
padding?: PaddingSize;
|
|
24
24
|
/** Visual style variant */
|
|
25
|
-
variant?: 'solid' | 'flat' | '
|
|
25
|
+
variant?: 'solid' | 'flat' | 'frost';
|
|
26
26
|
};
|
|
27
27
|
/**
|
|
28
28
|
* A versatile card container with semantic slots for media, header, body, footer, and actions.
|
|
29
29
|
*
|
|
30
30
|
* @element ore-card
|
|
31
31
|
*
|
|
32
|
-
* @attr {string} variant - Visual variant: 'solid' | 'flat' | '
|
|
32
|
+
* @attr {string} variant - Visual variant: 'solid' | 'flat' | 'frost'
|
|
33
33
|
* @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'
|
|
34
34
|
* @attr {string} padding - Internal padding: 'none' | 'sm' | 'md' | 'lg' | 'xl'
|
|
35
35
|
* @attr {string} elevation - Shadow elevation: '0' | '1' | '2' | '3' | '4' | '5'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/content/card/card.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoB3E,gCAAgC;AAEhC,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE;QAAE,aAAa,EAAE,UAAU,GAAG,aAAa,CAAC;QAAC,OAAO,EAAE,SAAS,GAAG,UAAU,CAAA;KAAE,CAAC;CAC1F,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,kBAAkB;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uBAAuB;IACvB,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,4BAA4B;IAC5B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/content/card/card.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoB3E,gCAAgC;AAEhC,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE;QAAE,aAAa,EAAE,UAAU,GAAG,aAAa,CAAC;QAAC,OAAO,EAAE,SAAS,GAAG,UAAU,CAAA;KAAE,CAAC;CAC1F,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,kBAAkB;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uBAAuB;IACvB,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,4BAA4B;IAC5B,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;CACtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,eAAO,MAAM,QAAQ,EAAG,UAAmB,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=`@layer refine.base{:host{gap:var(--size-1);align-items:center;display:flex}.pagination{gap:var(--pagination-gap,var(--size-1));align-items:center;margin:0;padding:0;list-style:none;display:flex}li{display:contents}button{all:unset;box-sizing:border-box;min-width:var(--pagination-item-size,var(--size-9));height:var(--pagination-item-size,var(--size-9));min-height:var(--_touch-target);padding:0 var(--size-2);font-size:var(--_font-size,var(--text-sm));font-weight:var(--font-medium);color:var(--color-contrast-700);cursor:pointer;-webkit-user-select:none;user-select:none;border:var(--border) solid transparent;border-radius:var(--pagination-radius,var(--rounded-lg));transition:background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);justify-content:center;align-items:center;display:inline-flex}:host(:not([variant])) button,:host([variant=ghost]) button{color:var(--color-contrast-700);background:0 0;border-width:0;border-color:#0000}:host(:not([variant])) button:hover:not(:is(:disabled,[aria-current=page])),:host([variant=ghost]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop)}:host([variant=solid]) button{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=solid]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-focus)}:host([variant=flat]) button{color:var(--_theme-base);background:color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));border-color:color-mix(in oklch, var(--_theme-focus) 20%, transparent);box-shadow:var(--inset-shadow-2xs)}:host([variant=flat]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus)}:host([variant=bordered]) button{color:var(--_theme-base);background:var(--_theme-backdrop);border-color:var(--_theme-border);box-shadow:var(--inset-shadow-xs)}:host([variant=bordered]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=outline]) button{color:var(--_theme-base);border-color:var(--_theme-base);background:0 0}:host([variant=outline]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop);border-color:var(--_theme-focus)}:host([variant=text]) button{color:var(--_theme-base);background:0 0;border-width:0;border-color:#0000}:host([variant=text]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-focus);background:0 0}:host([variant=frost]) button{color:var(--_theme-content);background:color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);border-color:color-mix(in oklch, var(--_theme-border) 45%, transparent);-webkit-backdrop-filter:blur(var(--blur-xl)) saturate(200%);backdrop-filter:blur(var(--blur-xl)) saturate(200%)}:host([variant=frost]) button:hover:not(:is(:disabled,[aria-current=page])){background:color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);border-color:color-mix(in oklch, var(--_theme-focus) 55%, transparent)}
|
|
1
|
+
var e=`@layer refine.base{:host{gap:var(--size-1);align-items:center;display:flex}.pagination{gap:var(--pagination-gap,var(--size-1));align-items:center;margin:0;padding:0;list-style:none;display:flex}li{display:contents}button{all:unset;box-sizing:border-box;min-width:var(--pagination-item-size,var(--size-9));height:var(--pagination-item-size,var(--size-9));min-height:var(--_touch-target);padding:0 var(--size-2);font-size:var(--_font-size,var(--text-sm));font-weight:var(--font-medium);color:var(--color-contrast-700);cursor:pointer;-webkit-user-select:none;user-select:none;border:var(--border) solid transparent;border-radius:var(--pagination-radius,var(--rounded-lg));transition:background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);justify-content:center;align-items:center;display:inline-flex}:host(:not([variant])) button,:host([variant=ghost]) button{color:var(--color-contrast-700);background:0 0;border-width:0;border-color:#0000}:host(:not([variant])) button:hover:not(:is(:disabled,[aria-current=page])),:host([variant=ghost]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop)}:host([variant=solid]) button{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=solid]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-focus)}:host([variant=flat]) button{color:var(--_theme-base);background:color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));border-color:color-mix(in oklch, var(--_theme-focus) 20%, transparent);box-shadow:var(--inset-shadow-2xs)}:host([variant=flat]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus)}:host([variant=bordered]) button{color:var(--_theme-base);background:var(--_theme-backdrop);border-color:var(--_theme-border);box-shadow:var(--inset-shadow-xs)}:host([variant=bordered]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=outline]) button{color:var(--_theme-base);border-color:var(--_theme-base);background:0 0}:host([variant=outline]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop);border-color:var(--_theme-focus)}:host([variant=text]) button{color:var(--_theme-base);background:0 0;border-width:0;border-color:#0000}:host([variant=text]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-focus);background:0 0}:host([variant=frost]) button{color:var(--_theme-content);background:color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);border-color:color-mix(in oklch, var(--_theme-border) 45%, transparent);-webkit-backdrop-filter:blur(var(--blur-xl)) saturate(200%);backdrop-filter:blur(var(--blur-xl)) saturate(200%)}:host([variant=frost]) button:hover:not(:is(:disabled,[aria-current=page])){background:color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);border-color:color-mix(in oklch, var(--_theme-focus) 55%, transparent)}button:hover:not(:is(:disabled,[aria-current=page])){background:var(--color-contrast-100)}button:focus-visible{outline:var(--border-2) solid var(--_theme-base,var(--color-contrast-500));outline-offset:var(--border-2)}button[aria-current=page]{font-weight:var(--font-bold);color:var(--_theme-contrast,var(--color-canvas));cursor:default;background:var(--_theme-base,var(--color-contrast-800));border-color:var(--_theme-base,var(--color-contrast-800))}:host([variant=ghost]) button[aria-current=page],:host(:not([variant])) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=solid]) button[aria-current=page]{background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=flat]) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=bordered]) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=outline]) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=text]) button[aria-current=page]{color:var(--_theme-focus);text-underline-offset:4px;text-decoration-line:underline;text-decoration-thickness:2px}:host([variant=frost]) button[aria-current=page]{color:var(--_theme-contrast);background:color-mix(in oklch, var(--_theme-base) 70%, transparent);border-color:var(--_theme-base)}button:disabled{cursor:not-allowed;opacity:.4}.ellipsis{min-width:var(--pagination-item-size,var(--size-9));height:var(--pagination-item-size,var(--size-9));font-size:var(--_font-size,var(--text-sm));color:var(--color-contrast-400);cursor:default;justify-content:center;align-items:center;display:inline-flex}}@layer refine.utilities{:host([size=sm]){--pagination-item-size:var(--size-7);--pagination-gap:var(--size-0_5);--_font-size:var(--text-xs)}:host([size=lg]){--pagination-item-size:var(--size-11);--_font-size:var(--text-base)}}`;exports.default=e;
|
|
2
2
|
//# sourceMappingURL=pagination.css.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.css.cjs","names":[],"sources":["../../../src/content/pagination/pagination.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .pagination {\n display: flex;\n gap: var(--pagination-gap, var(--size-1));\n align-items: center;\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n li {\n display: contents;\n }\n\n button {\n all: unset;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--pagination-item-size, var(--size-9));\n height: var(--pagination-item-size, var(--size-9));\n min-height: var(--_touch-target);\n padding: 0 var(--size-2);\n font-size: var(--_font-size, var(--text-sm));\n font-weight: var(--font-medium);\n color: var(--color-contrast-700);\n cursor: pointer;\n user-select: none;\n border: var(--border) solid transparent;\n border-radius: var(--pagination-radius, var(--rounded-lg));\n transition:\n background var(--transition-fast),\n color var(--transition-fast),\n border-color var(--transition-fast);\n }\n\n /* Default (ghost-like) behavior unless variant is specified */\n :host(:not([variant])) button,\n :host([variant='ghost']) button {\n color: var(--color-contrast-700);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host(:not([variant])) button:hover:not(:disabled, [aria-current='page']),\n :host([variant='ghost']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n }\n\n :host([variant='solid']) button {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='solid']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-focus);\n }\n\n :host([variant='flat']) button {\n color: var(--_theme-base);\n background: color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));\n border-color: color-mix(in oklch, var(--_theme-focus) 20%, transparent);\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n }\n\n :host([variant='bordered']) button {\n color: var(--_theme-base);\n background: var(--_theme-backdrop);\n border-color: var(--_theme-border);\n box-shadow: var(--inset-shadow-xs);\n }\n\n :host([variant='bordered']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='outline']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: var(--_theme-base);\n }\n\n :host([variant='outline']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='text']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host([variant='text']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-focus);\n background: transparent;\n }\n\n :host([variant='frost']) button {\n color: var(--_theme-content);\n background: color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);\n border-color: color-mix(in oklch, var(--_theme-border) 45%, transparent);\n backdrop-filter: blur(var(--blur-xl)) saturate(200%);\n }\n\n :host([variant='frost']) button:hover:not(:disabled, [aria-current='page']) {\n background: color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);\n border-color: color-mix(in oklch, var(--_theme-focus) 55%, transparent);\n }\n\n
|
|
1
|
+
{"version":3,"file":"pagination.css.cjs","names":[],"sources":["../../../src/content/pagination/pagination.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .pagination {\n display: flex;\n gap: var(--pagination-gap, var(--size-1));\n align-items: center;\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n li {\n display: contents;\n }\n\n button {\n all: unset;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--pagination-item-size, var(--size-9));\n height: var(--pagination-item-size, var(--size-9));\n min-height: var(--_touch-target);\n padding: 0 var(--size-2);\n font-size: var(--_font-size, var(--text-sm));\n font-weight: var(--font-medium);\n color: var(--color-contrast-700);\n cursor: pointer;\n user-select: none;\n border: var(--border) solid transparent;\n border-radius: var(--pagination-radius, var(--rounded-lg));\n transition:\n background var(--transition-fast),\n color var(--transition-fast),\n border-color var(--transition-fast);\n }\n\n /* Default (ghost-like) behavior unless variant is specified */\n :host(:not([variant])) button,\n :host([variant='ghost']) button {\n color: var(--color-contrast-700);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host(:not([variant])) button:hover:not(:disabled, [aria-current='page']),\n :host([variant='ghost']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n }\n\n :host([variant='solid']) button {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='solid']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-focus);\n }\n\n :host([variant='flat']) button {\n color: var(--_theme-base);\n background: color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));\n border-color: color-mix(in oklch, var(--_theme-focus) 20%, transparent);\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n }\n\n :host([variant='bordered']) button {\n color: var(--_theme-base);\n background: var(--_theme-backdrop);\n border-color: var(--_theme-border);\n box-shadow: var(--inset-shadow-xs);\n }\n\n :host([variant='bordered']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='outline']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: var(--_theme-base);\n }\n\n :host([variant='outline']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='text']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host([variant='text']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-focus);\n background: transparent;\n }\n\n :host([variant='frost']) button {\n color: var(--_theme-content);\n background: color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);\n border-color: color-mix(in oklch, var(--_theme-border) 45%, transparent);\n backdrop-filter: blur(var(--blur-xl)) saturate(200%);\n }\n\n :host([variant='frost']) button:hover:not(:disabled, [aria-current='page']) {\n background: color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);\n border-color: color-mix(in oklch, var(--_theme-focus) 55%, transparent);\n }\n\n button:hover:not(:disabled, [aria-current='page']) {\n background: var(--color-contrast-100);\n }\n\n button:focus-visible {\n outline: var(--border-2) solid var(--_theme-base, var(--color-contrast-500));\n outline-offset: var(--border-2);\n }\n\n /* Selected page styling - applies to all variants */\n button[aria-current='page'] {\n font-weight: var(--font-bold);\n color: var(--_theme-contrast, var(--color-canvas));\n cursor: default;\n background: var(--_theme-base, var(--color-contrast-800));\n border-color: var(--_theme-base, var(--color-contrast-800));\n }\n\n :host([variant='ghost']) button[aria-current='page'],\n :host(:not([variant])) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='solid']) button[aria-current='page'] {\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='flat']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='bordered']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='outline']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='text']) button[aria-current='page'] {\n color: var(--_theme-focus);\n text-decoration-line: underline;\n text-decoration-thickness: 2px;\n text-underline-offset: 4px;\n }\n\n :host([variant='frost']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: color-mix(in oklch, var(--_theme-base) 70%, transparent);\n border-color: var(--_theme-base);\n }\n\n button:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n .ellipsis {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--pagination-item-size, var(--size-9));\n height: var(--pagination-item-size, var(--size-9));\n font-size: var(--_font-size, var(--text-sm));\n color: var(--color-contrast-400);\n cursor: default;\n }\n}\n\n@layer refine.utilities {\n :host([size='sm']) {\n --pagination-item-size: var(--size-7);\n --pagination-gap: var(--size-0_5);\n --_font-size: var(--text-xs);\n }\n\n :host([size='lg']) {\n --pagination-item-size: var(--size-11);\n --_font-size: var(--text-base);\n }\n}\n"],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/content/pagination/pagination.css?inline
|
|
2
|
-
var e = "@layer refine.base{:host{gap:var(--size-1);align-items:center;display:flex}.pagination{gap:var(--pagination-gap,var(--size-1));align-items:center;margin:0;padding:0;list-style:none;display:flex}li{display:contents}button{all:unset;box-sizing:border-box;min-width:var(--pagination-item-size,var(--size-9));height:var(--pagination-item-size,var(--size-9));min-height:var(--_touch-target);padding:0 var(--size-2);font-size:var(--_font-size,var(--text-sm));font-weight:var(--font-medium);color:var(--color-contrast-700);cursor:pointer;-webkit-user-select:none;user-select:none;border:var(--border) solid transparent;border-radius:var(--pagination-radius,var(--rounded-lg));transition:background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);justify-content:center;align-items:center;display:inline-flex}:host(:not([variant])) button,:host([variant=ghost]) button{color:var(--color-contrast-700);background:0 0;border-width:0;border-color:#0000}:host(:not([variant])) button:hover:not(:is(:disabled,[aria-current=page])),:host([variant=ghost]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop)}:host([variant=solid]) button{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=solid]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-focus)}:host([variant=flat]) button{color:var(--_theme-base);background:color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));border-color:color-mix(in oklch, var(--_theme-focus) 20%, transparent);box-shadow:var(--inset-shadow-2xs)}:host([variant=flat]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus)}:host([variant=bordered]) button{color:var(--_theme-base);background:var(--_theme-backdrop);border-color:var(--_theme-border);box-shadow:var(--inset-shadow-xs)}:host([variant=bordered]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=outline]) button{color:var(--_theme-base);border-color:var(--_theme-base);background:0 0}:host([variant=outline]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop);border-color:var(--_theme-focus)}:host([variant=text]) button{color:var(--_theme-base);background:0 0;border-width:0;border-color:#0000}:host([variant=text]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-focus);background:0 0}:host([variant=frost]) button{color:var(--_theme-content);background:color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);border-color:color-mix(in oklch, var(--_theme-border) 45%, transparent);-webkit-backdrop-filter:blur(var(--blur-xl)) saturate(200%);backdrop-filter:blur(var(--blur-xl)) saturate(200%)}:host([variant=frost]) button:hover:not(:is(:disabled,[aria-current=page])){background:color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);border-color:color-mix(in oklch, var(--_theme-focus) 55%, transparent)}
|
|
2
|
+
var e = "@layer refine.base{:host{gap:var(--size-1);align-items:center;display:flex}.pagination{gap:var(--pagination-gap,var(--size-1));align-items:center;margin:0;padding:0;list-style:none;display:flex}li{display:contents}button{all:unset;box-sizing:border-box;min-width:var(--pagination-item-size,var(--size-9));height:var(--pagination-item-size,var(--size-9));min-height:var(--_touch-target);padding:0 var(--size-2);font-size:var(--_font-size,var(--text-sm));font-weight:var(--font-medium);color:var(--color-contrast-700);cursor:pointer;-webkit-user-select:none;user-select:none;border:var(--border) solid transparent;border-radius:var(--pagination-radius,var(--rounded-lg));transition:background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);justify-content:center;align-items:center;display:inline-flex}:host(:not([variant])) button,:host([variant=ghost]) button{color:var(--color-contrast-700);background:0 0;border-width:0;border-color:#0000}:host(:not([variant])) button:hover:not(:is(:disabled,[aria-current=page])),:host([variant=ghost]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop)}:host([variant=solid]) button{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=solid]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-focus)}:host([variant=flat]) button{color:var(--_theme-base);background:color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));border-color:color-mix(in oklch, var(--_theme-focus) 20%, transparent);box-shadow:var(--inset-shadow-2xs)}:host([variant=flat]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus)}:host([variant=bordered]) button{color:var(--_theme-base);background:var(--_theme-backdrop);border-color:var(--_theme-border);box-shadow:var(--inset-shadow-xs)}:host([variant=bordered]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=outline]) button{color:var(--_theme-base);border-color:var(--_theme-base);background:0 0}:host([variant=outline]) button:hover:not(:is(:disabled,[aria-current=page])){background:var(--_theme-backdrop);border-color:var(--_theme-focus)}:host([variant=text]) button{color:var(--_theme-base);background:0 0;border-width:0;border-color:#0000}:host([variant=text]) button:hover:not(:is(:disabled,[aria-current=page])){color:var(--_theme-focus);background:0 0}:host([variant=frost]) button{color:var(--_theme-content);background:color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);border-color:color-mix(in oklch, var(--_theme-border) 45%, transparent);-webkit-backdrop-filter:blur(var(--blur-xl)) saturate(200%);backdrop-filter:blur(var(--blur-xl)) saturate(200%)}:host([variant=frost]) button:hover:not(:is(:disabled,[aria-current=page])){background:color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);border-color:color-mix(in oklch, var(--_theme-focus) 55%, transparent)}button:hover:not(:is(:disabled,[aria-current=page])){background:var(--color-contrast-100)}button:focus-visible{outline:var(--border-2) solid var(--_theme-base,var(--color-contrast-500));outline-offset:var(--border-2)}button[aria-current=page]{font-weight:var(--font-bold);color:var(--_theme-contrast,var(--color-canvas));cursor:default;background:var(--_theme-base,var(--color-contrast-800));border-color:var(--_theme-base,var(--color-contrast-800))}:host([variant=ghost]) button[aria-current=page],:host(:not([variant])) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=solid]) button[aria-current=page]{background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=flat]) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=bordered]) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-focus);border-color:var(--_theme-focus)}:host([variant=outline]) button[aria-current=page]{color:var(--_theme-contrast);background:var(--_theme-base);border-color:var(--_theme-base)}:host([variant=text]) button[aria-current=page]{color:var(--_theme-focus);text-underline-offset:4px;text-decoration-line:underline;text-decoration-thickness:2px}:host([variant=frost]) button[aria-current=page]{color:var(--_theme-contrast);background:color-mix(in oklch, var(--_theme-base) 70%, transparent);border-color:var(--_theme-base)}button:disabled{cursor:not-allowed;opacity:.4}.ellipsis{min-width:var(--pagination-item-size,var(--size-9));height:var(--pagination-item-size,var(--size-9));font-size:var(--_font-size,var(--text-sm));color:var(--color-contrast-400);cursor:default;justify-content:center;align-items:center;display:inline-flex}}@layer refine.utilities{:host([size=sm]){--pagination-item-size:var(--size-7);--pagination-gap:var(--size-0_5);--_font-size:var(--text-xs)}:host([size=lg]){--pagination-item-size:var(--size-11);--_font-size:var(--text-base)}}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.css.js","names":[],"sources":["../../../src/content/pagination/pagination.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .pagination {\n display: flex;\n gap: var(--pagination-gap, var(--size-1));\n align-items: center;\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n li {\n display: contents;\n }\n\n button {\n all: unset;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--pagination-item-size, var(--size-9));\n height: var(--pagination-item-size, var(--size-9));\n min-height: var(--_touch-target);\n padding: 0 var(--size-2);\n font-size: var(--_font-size, var(--text-sm));\n font-weight: var(--font-medium);\n color: var(--color-contrast-700);\n cursor: pointer;\n user-select: none;\n border: var(--border) solid transparent;\n border-radius: var(--pagination-radius, var(--rounded-lg));\n transition:\n background var(--transition-fast),\n color var(--transition-fast),\n border-color var(--transition-fast);\n }\n\n /* Default (ghost-like) behavior unless variant is specified */\n :host(:not([variant])) button,\n :host([variant='ghost']) button {\n color: var(--color-contrast-700);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host(:not([variant])) button:hover:not(:disabled, [aria-current='page']),\n :host([variant='ghost']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n }\n\n :host([variant='solid']) button {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='solid']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-focus);\n }\n\n :host([variant='flat']) button {\n color: var(--_theme-base);\n background: color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));\n border-color: color-mix(in oklch, var(--_theme-focus) 20%, transparent);\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n }\n\n :host([variant='bordered']) button {\n color: var(--_theme-base);\n background: var(--_theme-backdrop);\n border-color: var(--_theme-border);\n box-shadow: var(--inset-shadow-xs);\n }\n\n :host([variant='bordered']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='outline']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: var(--_theme-base);\n }\n\n :host([variant='outline']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='text']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host([variant='text']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-focus);\n background: transparent;\n }\n\n :host([variant='frost']) button {\n color: var(--_theme-content);\n background: color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);\n border-color: color-mix(in oklch, var(--_theme-border) 45%, transparent);\n backdrop-filter: blur(var(--blur-xl)) saturate(200%);\n }\n\n :host([variant='frost']) button:hover:not(:disabled, [aria-current='page']) {\n background: color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);\n border-color: color-mix(in oklch, var(--_theme-focus) 55%, transparent);\n }\n\n
|
|
1
|
+
{"version":3,"file":"pagination.css.js","names":[],"sources":["../../../src/content/pagination/pagination.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .pagination {\n display: flex;\n gap: var(--pagination-gap, var(--size-1));\n align-items: center;\n padding: 0;\n margin: 0;\n list-style: none;\n }\n\n li {\n display: contents;\n }\n\n button {\n all: unset;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--pagination-item-size, var(--size-9));\n height: var(--pagination-item-size, var(--size-9));\n min-height: var(--_touch-target);\n padding: 0 var(--size-2);\n font-size: var(--_font-size, var(--text-sm));\n font-weight: var(--font-medium);\n color: var(--color-contrast-700);\n cursor: pointer;\n user-select: none;\n border: var(--border) solid transparent;\n border-radius: var(--pagination-radius, var(--rounded-lg));\n transition:\n background var(--transition-fast),\n color var(--transition-fast),\n border-color var(--transition-fast);\n }\n\n /* Default (ghost-like) behavior unless variant is specified */\n :host(:not([variant])) button,\n :host([variant='ghost']) button {\n color: var(--color-contrast-700);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host(:not([variant])) button:hover:not(:disabled, [aria-current='page']),\n :host([variant='ghost']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n }\n\n :host([variant='solid']) button {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='solid']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-focus);\n }\n\n :host([variant='flat']) button {\n color: var(--_theme-base);\n background: color-mix(in oklch, var(--_theme-backdrop) 8%, var(--color-contrast-100));\n border-color: color-mix(in oklch, var(--_theme-focus) 20%, transparent);\n box-shadow: var(--inset-shadow-2xs);\n }\n\n :host([variant='flat']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n }\n\n :host([variant='bordered']) button {\n color: var(--_theme-base);\n background: var(--_theme-backdrop);\n border-color: var(--_theme-border);\n box-shadow: var(--inset-shadow-xs);\n }\n\n :host([variant='bordered']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='outline']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: var(--_theme-base);\n }\n\n :host([variant='outline']) button:hover:not(:disabled, [aria-current='page']) {\n background: var(--_theme-backdrop);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='text']) button {\n color: var(--_theme-base);\n background: transparent;\n border-color: transparent;\n border-width: 0;\n }\n\n :host([variant='text']) button:hover:not(:disabled, [aria-current='page']) {\n color: var(--_theme-focus);\n background: transparent;\n }\n\n :host([variant='frost']) button {\n color: var(--_theme-content);\n background: color-mix(in oklch, var(--_theme-backdrop) 45%, transparent);\n border-color: color-mix(in oklch, var(--_theme-border) 45%, transparent);\n backdrop-filter: blur(var(--blur-xl)) saturate(200%);\n }\n\n :host([variant='frost']) button:hover:not(:disabled, [aria-current='page']) {\n background: color-mix(in oklch, var(--_theme-backdrop) 65%, transparent);\n border-color: color-mix(in oklch, var(--_theme-focus) 55%, transparent);\n }\n\n button:hover:not(:disabled, [aria-current='page']) {\n background: var(--color-contrast-100);\n }\n\n button:focus-visible {\n outline: var(--border-2) solid var(--_theme-base, var(--color-contrast-500));\n outline-offset: var(--border-2);\n }\n\n /* Selected page styling - applies to all variants */\n button[aria-current='page'] {\n font-weight: var(--font-bold);\n color: var(--_theme-contrast, var(--color-canvas));\n cursor: default;\n background: var(--_theme-base, var(--color-contrast-800));\n border-color: var(--_theme-base, var(--color-contrast-800));\n }\n\n :host([variant='ghost']) button[aria-current='page'],\n :host(:not([variant])) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='solid']) button[aria-current='page'] {\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='flat']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='bordered']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-focus);\n border-color: var(--_theme-focus);\n }\n\n :host([variant='outline']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: var(--_theme-base);\n border-color: var(--_theme-base);\n }\n\n :host([variant='text']) button[aria-current='page'] {\n color: var(--_theme-focus);\n text-decoration-line: underline;\n text-decoration-thickness: 2px;\n text-underline-offset: 4px;\n }\n\n :host([variant='frost']) button[aria-current='page'] {\n color: var(--_theme-contrast);\n background: color-mix(in oklch, var(--_theme-base) 70%, transparent);\n border-color: var(--_theme-base);\n }\n\n button:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n .ellipsis {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: var(--pagination-item-size, var(--size-9));\n height: var(--pagination-item-size, var(--size-9));\n font-size: var(--_font-size, var(--text-sm));\n color: var(--color-contrast-400);\n cursor: default;\n }\n}\n\n@layer refine.utilities {\n :host([size='sm']) {\n --pagination-item-size: var(--size-7);\n --pagination-gap: var(--size-0_5);\n --_font-size: var(--text-xs);\n }\n\n :host([size='lg']) {\n --pagination-item-size: var(--size-11);\n --_font-size: var(--text-base);\n }\n}\n"],"mappings":""}
|
package/dist/core/field-base.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require("../_dev.cjs");let e=require("@vielzeug/ore"),t=require("@vielzeug/ripple");var n=e=>(0,t.computed)(()=>({errorText:e.error?.value??``,helperText:e.helper?.value??``})),r=e=>(0,t.computed)(()=>{let t=e.value?.value??``,n=e.maxLength?.value,r=Number(n),i=Number.isFinite(r)&&r>0?r:null,a=i!==null,o=a?`${t.length} / ${i}`:``,s=a?t.length/i:0;return{counterAtLimit:a?s>=1:!1,counterNearLimit:a?s>=.9&&s<1:!1,counterText:o}}),i=(e,t=`counter`)=>e?e.counterAtLimit?`${t} at-limit`:e.counterNearLimit?`${t} near-limit`:t:t;function a(){let e=!1;return{clear:()=>{e=!1},get isDirty(){return e},markDirty:()=>{e=!0}}}var o=r=>{let i=(0,t.computed)(()=>!!r.disabled?.value),a=(0,e.
|
|
1
|
+
require("../_dev.cjs");let e=require("@vielzeug/ore"),t=require("@vielzeug/ripple");var n=e=>(0,t.computed)(()=>({errorText:e.error?.value??``,helperText:e.helper?.value??``})),r=e=>(0,t.computed)(()=>{let t=e.value?.value??``,n=e.maxLength?.value,r=Number(n),i=Number.isFinite(r)&&r>0?r:null,a=i!==null,o=a?`${t.length} / ${i}`:``,s=a?t.length/i:0;return{counterAtLimit:a?s>=1:!1,counterNearLimit:a?s>=.9&&s<1:!1,counterText:o}}),i=(e,t=`counter`)=>e?e.counterAtLimit?`${t} at-limit`:e.counterNearLimit?`${t} near-limit`:t:t;function a(){let e=!1;return{clear:()=>{e=!1},get isDirty(){return e},markDirty:()=>{e=!0}}}var o=r=>{let i=(0,t.computed)(()=>!!r.disabled?.value),a=(0,e.createId)(`helper`),o=(0,e.createId)(`error`),s=n({error:r.error,helper:r.helper}),c=(0,t.computed)(()=>s.value.errorText||s.value.helperText?a:null),l=(0,t.computed)(()=>s.value.errorText?o:null),u=(0,t.computed)(()=>s.value.errorText?`true`:null),d=null;return{ariaDescribedBy:c,ariaErrorMessage:l,ariaInvalid:u,assistiveId:a,attachFormField:e=>{d=e},disabled:i,errorId:o,errorText:(0,t.computed)(()=>s.value.errorText),helperText:(0,t.computed)(()=>s.value.helperText),triggerValidation:e=>{r.validateOn?.value===e&&d&&d.reportValidity()}}},s=n=>{let r=n.id??(0,e.createId)(n.prefix??`field`),i=(0,e.createId)(`label`),a=n.label??(0,t.computed)(()=>void 0),o=n.hasLabel??(0,t.computed)(()=>!!a.value);return{ariaLabelledBy:(0,t.computed)(()=>o.value?i:null),fieldId:r,labelId:i,labelVisible:o}},c=e=>({...o(e),...s(e)});exports.counterClassName=i,exports.createAssistiveState=o,exports.createCounterState=r,exports.createDirtyTracker=a,exports.createErrorHelperState=n,exports.createField=c,exports.createLabelState=s;
|
|
2
2
|
//# sourceMappingURL=field-base.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-base.cjs","names":[],"sources":["../../src/core/field-base.ts"],"sourcesContent":["import { createStableId } from '@vielzeug/ore';\nimport { computed, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\n\n// ── Validation / context types ────────────────────────────────────────────────\n\nexport type ValidationTrigger = 'blur' | 'change' | 'input' | 'submit';\nexport type ControlValidationMode = ValidationTrigger | undefined;\n\n// ── Error/helper assistive state ──────────────────────────────────────────────\n\nexport type ErrorHelperState = {\n errorText: string;\n helperText: string;\n};\n\nexport type ErrorHelperOptions = {\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n};\n\nexport const createErrorHelperState = (options: ErrorHelperOptions): Readable<ErrorHelperState> =>\n computed(() => ({\n errorText: options.error?.value ?? '',\n helperText: options.helper?.value ?? '',\n }));\n\n// ── Counter state (opt-in) ─────────────────────────────────────────────────────\n\n/** Counter state for text fields with `maxLength`. */\nexport type CounterState = {\n counterAtLimit: boolean;\n counterNearLimit: boolean;\n counterText: string;\n};\n\nexport type CounterOptions = {\n maxLength?: Readable<number | undefined>;\n value: Readable<string | undefined>;\n};\n\n/**\n * Creates a reactive counter state signal for text fields with `maxLength`.\n * Only call this when `maxLength` may be set — the state is genuinely opt-in.\n *\n * @example\n * ```ts\n * const counter = createCounterState({ value: tf.value, maxLength: props.maxlength });\n * ```\n */\nexport const createCounterState = (options: CounterOptions): Readable<CounterState> =>\n computed<CounterState>(() => {\n const value = options.value?.value ?? '';\n const maxLength = options.maxLength?.value;\n const parsedMaxLength = Number(maxLength);\n const validMaxLength = Number.isFinite(parsedMaxLength) && parsedMaxLength > 0 ? parsedMaxLength : null;\n const hasCounter = validMaxLength !== null;\n const counterText = hasCounter ? `${value.length} / ${validMaxLength}` : '';\n const ratio = hasCounter ? value.length / validMaxLength : 0;\n\n return {\n counterAtLimit: hasCounter ? ratio >= 1 : false,\n counterNearLimit: hasCounter ? ratio >= 0.9 && ratio < 1 : false,\n counterText,\n };\n });\n\n/**\n * The single implementation of \"which modifier class does this counter state map to\" —\n * every field with a character counter (`ore-input`, `ore-textarea`, `ore-message-composer`)\n * renders the identical near-limit/at-limit styling, so this is the one place that decides it.\n *\n * @example\n * ```ts\n * html`<span class=\"${() => counterClassName(counter?.value)}\">...</span>`\n * ```\n */\nexport const counterClassName = (counter: CounterState | undefined, base = 'counter'): string => {\n if (!counter) return base;\n\n if (counter.counterAtLimit) return `${base} at-limit`;\n\n if (counter.counterNearLimit) return `${base} near-limit`;\n\n return base;\n};\n\n// ── Dirty tracking (two-state reset) ───────────────────────────────────────────\n\n/**\n * Tracks whether a control's value has ever been changed by user interaction.\n *\n * Backs the \"two-state reset()\" pattern used by `createCheckable` (`checked`/\n * `indeterminate`) and `createChoiceField` (`selectedValues`): before the first\n * interaction, the live prop is still a reliable \"current default\" to resync from\n * (e.g. an async-loaded value arriving after mount); after it, the prop is\n * contaminated by interaction-driven attribute reflection, so only a snapshot taken\n * at creation still represents \"the default\" in the native sense. `reset()` callers\n * check `isDirty` to decide which source to revert to, then call `clear()`.\n */\nexport type DirtyTracker = {\n clear: () => void;\n readonly isDirty: boolean;\n markDirty: () => void;\n};\n\nexport function createDirtyTracker(): DirtyTracker {\n let dirty = false;\n\n return {\n clear: () => {\n dirty = false;\n },\n get isDirty() {\n return dirty;\n },\n markDirty: () => {\n dirty = true;\n },\n };\n}\n\n// ── Label placement ───────────────────────────────────────────────────────────\n\nexport type LabelPlacement = 'inset' | 'outside' | undefined;\n\n// ── Assistive state (error/helper text, describedby/errormessage/invalid) ─────\n\n/**\n * A field's error/helper/disabled/validation-trigger state — everything an input\n * needs *except* a visible `<label>`. Split out from label state (below) because\n * not every field has one: `ore-message-composer` names itself via `aria-label`\n * (a chat composer's accessible name conventionally comes from context/placeholder,\n * not a floating `<label>`), so it composes this directly instead of pulling in\n * label plumbing it can't use. `createField()` composes both for fields that do\n * render a `<label>`.\n */\nexport type AssistiveStateHandle = {\n /** `aria-describedby` value. Non-null when helper or error text is present. */\n ariaDescribedBy: Readable<string | null>;\n /** `aria-errormessage` value. Non-null when error text is present. */\n ariaErrorMessage: Readable<string | null>;\n /** `aria-invalid` value. `'true'` when error text is present, otherwise `null`. */\n ariaInvalid: Readable<'true' | null>;\n /**\n * The stable `id` used for `aria-describedby` on the input. Points at the\n * assistive-text region (covers both helper text and error text per WAI-ARIA).\n */\n assistiveId: string;\n /**\n * Registers the real form field handle (the return value of `useField()`) so that\n * `triggerValidation()` can call its `reportValidity()`. See `TextFieldHandle.attachFormField`\n * for why this is a post-hoc call instead of a constructor option — the same forward\n * reference applies here for choice fields.\n */\n attachFormField: (formField: { reportValidity(): void }) => void;\n disabled: Readable<boolean>;\n /** Stable `id` for the inline error message element (`aria-errormessage`). */\n errorId: string;\n /** Reactive error text. Empty string when no error is set. */\n errorText: Readable<string>;\n /** Reactive helper text. Empty string when no helper is set. */\n helperText: Readable<string>;\n triggerValidation: (on: Extract<ValidationTrigger, 'blur' | 'change'>) => void;\n};\n\nexport type AssistiveStateOptions = {\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n validateOn?: Readable<ControlValidationMode>;\n};\n\nexport const createAssistiveState = (options: AssistiveStateOptions): AssistiveStateHandle => {\n const disabled = computed(() => Boolean(options.disabled?.value));\n const assistiveId = createStableId('helper');\n const errorId = createStableId('error');\n const resolvedAssistive = createErrorHelperState({ error: options.error, helper: options.helper });\n\n const ariaDescribedBy = computed(() =>\n resolvedAssistive.value.errorText || resolvedAssistive.value.helperText ? assistiveId : null,\n );\n const ariaErrorMessage = computed(() => (resolvedAssistive.value.errorText ? errorId : null));\n const ariaInvalid = computed<'true' | null>(() => (resolvedAssistive.value.errorText ? 'true' : null));\n\n let formField: { reportValidity(): void } | null = null;\n const attachFormField = (nextFormField: { reportValidity(): void }): void => {\n formField = nextFormField;\n };\n\n const triggerValidation = (on: Extract<ValidationTrigger, 'blur' | 'change'>): void => {\n if (options.validateOn?.value !== on) return;\n\n if (!formField) {\n // Not user-facing — this only fires when a component author wired `validateOn` but\n // forgot the matching `attachFormField(useField(...))` call, so validation silently\n // never runs instead of erroring where the mistake actually is.\n warn(\n \"triggerValidation() called before attachFormField() — validation will not run. See createTextField()/createChoiceField()/createCheckable()'s attachFormField doc comment.\",\n );\n\n return;\n }\n\n formField.reportValidity();\n };\n\n return {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n assistiveId,\n attachFormField,\n disabled,\n errorId,\n errorText: computed(() => resolvedAssistive.value.errorText),\n helperText: computed(() => resolvedAssistive.value.helperText),\n triggerValidation,\n };\n};\n\n// ── Label state (visible `<label>` + `aria-labelledby`) ────────────────────────\n\nexport type LabelStateHandle = {\n /** `aria-labelledby` value. Non-null when a label is visible. */\n ariaLabelledBy: Readable<string | null>;\n /** Stable `id` for the underlying form control — the `<label for=\"...\">` target. */\n fieldId: string;\n /** Stable `id` for the label element. Stamp this on your `<label id=\"...\">`. */\n labelId: string;\n /** Whether the label should be visible. Use to toggle `hidden` on the `<label>`. */\n labelVisible: Readable<boolean>;\n};\n\nexport type LabelStateOptions = {\n /**\n * Override for label visibility that takes precedence over deriving it from\n * the `label` text signal. Pass a computed signal that checks both prop and\n * slot presence to enable slot-first composition.\n *\n * @example\n * ```ts\n * const hasLabel = computed(() => !!props.label.value || slots.has('label').value);\n * const label = createLabelState({ ...options, hasLabel });\n * ```\n */\n hasLabel?: Readable<boolean>;\n /**\n * When provided, used directly as `fieldId` instead of generating one via\n * `createStableId`. Useful in tests for deterministic ID assertions.\n */\n id?: string;\n /**\n * Label text signal. When provided, `labelVisible` and `ariaLabelledBy` are\n * computed reactively from this value (see `hasLabel` for the slot-first override).\n */\n label?: Readable<string | undefined>;\n /**\n * Label placement signal. Defaults to `'inset'`.\n *\n * Not consumed by `createLabelState()` itself — visibility is driven solely by\n * `hasLabel`/`label`. Threaded through so component authors can apply\n * placement-specific styling (`label-placement` attribute) without a second\n * option; if placement-dependent visibility is ever needed, wire it into\n * `labelVisible` here instead of adding a new option.\n */\n labelPlacement?: Readable<LabelPlacement>;\n prefix?: string;\n};\n\nexport const createLabelState = (options: LabelStateOptions): LabelStateHandle => {\n const fieldId = options.id ?? createStableId(options.prefix ?? 'field');\n const labelId = createStableId('label');\n const label$ = options.label ?? computed(() => undefined);\n\n // Slot-first composition: if the caller provides `hasLabel`, use it instead\n // of deriving visibility purely from the label text signal. This allows\n // components with a `<slot name=\"label\">` to stay visible even when the\n // `label` prop is empty but a slotted label element is present.\n const labelVisible = options.hasLabel ?? computed(() => Boolean(label$.value));\n const ariaLabelledBy = computed(() => (labelVisible.value ? labelId : null));\n\n return { ariaLabelledBy, fieldId, labelId, labelVisible };\n};\n\n// ── Field handle (assistive + label, composed) ──────────────────────────────\n\n/**\n * Common handle returned by `createField` — the base for both `TextFieldHandle`\n * and `ChoiceFieldHandle`. Composes `createAssistiveState` + `createLabelState`;\n * call those directly instead if a field has no visible `<label>` (see\n * `createAssistiveState`'s doc comment).\n *\n * ARIA signals and label state are flat on the handle — no nested `aria` or\n * `label` sub-objects. `labelId`/`labelVisible`/`ariaLabelledBy` are always\n * present but only meaningful when `label`/`hasLabel` was passed — omit both\n * and they resolve to an always-hidden, unreferenced label.\n *\n * @example\n * ```html\n * <label id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\">...</label>\n * <input\n * aria-labelledby=\"${ariaLabelledBy}\"\n * aria-describedby=\"${ariaDescribedBy}\"\n * aria-invalid=\"${ariaInvalid}\" />\n * ```\n */\nexport type FieldHandle = AssistiveStateHandle & LabelStateHandle;\n\n/** Options shared by both `createTextField` and `createChoiceField`. */\nexport type FieldOptions = AssistiveStateOptions & LabelStateOptions;\n\nexport const createField = (options: FieldOptions): FieldHandle => ({\n ...createAssistiveState(options),\n ...createLabelState(options),\n});\n"],"mappings":"oFAsBA,IAAa,EAA0B,IAAA,EACrC,EAAA,SAAA,MAAgB,CACd,UAAW,EAAQ,OAAO,OAAS,GACnC,WAAY,EAAQ,QAAQ,OAAS,EACvC,EAAE,EAyBS,EAAsB,IAAA,EACjC,EAAA,SAAA,KAA6B,CAC3B,IAAM,EAAQ,EAAQ,OAAO,OAAS,GAChC,EAAY,EAAQ,WAAW,MAC/B,EAAkB,OAAO,CAAS,EAClC,EAAiB,OAAO,SAAS,CAAe,GAAK,EAAkB,EAAI,EAAkB,KAC7F,EAAa,IAAmB,KAChC,EAAc,EAAa,GAAG,EAAM,OAAO,KAAK,IAAmB,GACnE,EAAQ,EAAa,EAAM,OAAS,EAAiB,EAE3D,MAAO,CACL,eAAgB,EAAa,GAAS,EAAI,GAC1C,iBAAkB,EAAa,GAAS,IAAO,EAAQ,EAAI,GAC3D,aACF,CACF,CAAC,EAYU,GAAoB,EAAmC,EAAO,YACpE,EAED,EAAQ,eAAuB,GAAG,EAAK,WAEvC,EAAQ,iBAAyB,GAAG,EAAK,aAEtC,EANc,EA4BvB,SAAgB,GAAmC,CACjD,IAAI,EAAQ,GAEZ,MAAO,CACL,UAAa,CACX,EAAQ,EACV,EACA,IAAI,SAAU,CACZ,OAAO,CACT,EACA,cAAiB,CACf,EAAQ,EACV,CACF,CACF,CAqDA,IAAa,EAAwB,GAAyD,CAC5F,IAAM,GAAA,EAAW,EAAA,SAAA,KAAe,EAAQ,EAAQ,UAAU,KAAM,EAC1D,GAAA,EAAc,EAAA,eAAA,CAAe,QAAQ,EACrC,GAAA,EAAU,EAAA,eAAA,CAAe,OAAO,EAChC,EAAoB,EAAuB,CAAE,MAAO,EAAQ,MAAO,OAAQ,EAAQ,MAAO,CAAC,EAE3F,GAAA,EAAkB,EAAA,SAAA,KACtB,EAAkB,MAAM,WAAa,EAAkB,MAAM,WAAa,EAAc,IAC1F,EACM,GAAA,EAAmB,EAAA,SAAA,KAAgB,EAAkB,MAAM,UAAY,EAAU,IAAK,EACtF,GAAA,EAAc,EAAA,SAAA,KAA+B,EAAkB,MAAM,UAAY,OAAS,IAAK,EAEjG,EAA+C,KAsBnD,MAAO,CACL,kBACA,mBACA,cACA,cACA,gBA1BuB,GAAoD,CAC3E,EAAY,CACd,EAyBE,WACA,UACA,WAAA,EAAW,EAAA,SAAA,KAAe,EAAkB,MAAM,SAAS,EAC3D,YAAA,EAAY,EAAA,SAAA,KAAe,EAAkB,MAAM,UAAU,EAC7D,kBA3ByB,GAA4D,CACjF,EAAQ,YAAY,QAAU,GAE7B,GAWL,EAAU,eAAe,CAC3B,CAaA,CACF,EAmDa,EAAoB,GAAiD,CAChF,IAAM,EAAU,EAAQ,KAAA,EAAM,EAAA,eAAA,CAAe,EAAQ,QAAU,OAAO,EAChE,GAAA,EAAU,EAAA,eAAA,CAAe,OAAO,EAChC,EAAS,EAAQ,QAAA,EAAS,EAAA,SAAA,KAAe,IAAA,EAAS,EAMlD,EAAe,EAAQ,WAAA,EAAY,EAAA,SAAA,KAAe,EAAQ,EAAO,KAAM,EAG7E,MAAO,CAAE,gBAAA,EAFc,EAAA,SAAA,KAAgB,EAAa,MAAQ,EAAU,IAE7D,EAAgB,UAAS,UAAS,cAAa,CAC1D,EA6Ba,EAAe,IAAwC,CAClE,GAAG,EAAqB,CAAO,EAC/B,GAAG,EAAiB,CAAO,CAC7B"}
|
|
1
|
+
{"version":3,"file":"field-base.cjs","names":[],"sources":["../../src/core/field-base.ts"],"sourcesContent":["import { createId } from '@vielzeug/ore';\nimport { computed, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\n\n// ── Validation / context types ────────────────────────────────────────────────\n\nexport type ValidationTrigger = 'blur' | 'change' | 'input' | 'submit';\nexport type ControlValidationMode = ValidationTrigger | undefined;\n\n// ── Error/helper assistive state ──────────────────────────────────────────────\n\nexport type ErrorHelperState = {\n errorText: string;\n helperText: string;\n};\n\nexport type ErrorHelperOptions = {\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n};\n\nexport const createErrorHelperState = (options: ErrorHelperOptions): Readable<ErrorHelperState> =>\n computed(() => ({\n errorText: options.error?.value ?? '',\n helperText: options.helper?.value ?? '',\n }));\n\n// ── Counter state (opt-in) ─────────────────────────────────────────────────────\n\n/** Counter state for text fields with `maxLength`. */\nexport type CounterState = {\n counterAtLimit: boolean;\n counterNearLimit: boolean;\n counterText: string;\n};\n\nexport type CounterOptions = {\n maxLength?: Readable<number | undefined>;\n value: Readable<string | undefined>;\n};\n\n/**\n * Creates a reactive counter state signal for text fields with `maxLength`.\n * Only call this when `maxLength` may be set — the state is genuinely opt-in.\n *\n * @example\n * ```ts\n * const counter = createCounterState({ value: tf.value, maxLength: props.maxlength });\n * ```\n */\nexport const createCounterState = (options: CounterOptions): Readable<CounterState> =>\n computed<CounterState>(() => {\n const value = options.value?.value ?? '';\n const maxLength = options.maxLength?.value;\n const parsedMaxLength = Number(maxLength);\n const validMaxLength = Number.isFinite(parsedMaxLength) && parsedMaxLength > 0 ? parsedMaxLength : null;\n const hasCounter = validMaxLength !== null;\n const counterText = hasCounter ? `${value.length} / ${validMaxLength}` : '';\n const ratio = hasCounter ? value.length / validMaxLength : 0;\n\n return {\n counterAtLimit: hasCounter ? ratio >= 1 : false,\n counterNearLimit: hasCounter ? ratio >= 0.9 && ratio < 1 : false,\n counterText,\n };\n });\n\n/**\n * The single implementation of \"which modifier class does this counter state map to\" —\n * every field with a character counter (`ore-input`, `ore-textarea`, `ore-message-composer`)\n * renders the identical near-limit/at-limit styling, so this is the one place that decides it.\n *\n * @example\n * ```ts\n * html`<span class=\"${() => counterClassName(counter?.value)}\">...</span>`\n * ```\n */\nexport const counterClassName = (counter: CounterState | undefined, base = 'counter'): string => {\n if (!counter) return base;\n\n if (counter.counterAtLimit) return `${base} at-limit`;\n\n if (counter.counterNearLimit) return `${base} near-limit`;\n\n return base;\n};\n\n// ── Dirty tracking (two-state reset) ───────────────────────────────────────────\n\n/**\n * Tracks whether a control's value has ever been changed by user interaction.\n *\n * Backs the \"two-state reset()\" pattern used by `createCheckable` (`checked`/\n * `indeterminate`) and `createChoiceField` (`selectedValues`): before the first\n * interaction, the live prop is still a reliable \"current default\" to resync from\n * (e.g. an async-loaded value arriving after mount); after it, the prop is\n * contaminated by interaction-driven attribute reflection, so only a snapshot taken\n * at creation still represents \"the default\" in the native sense. `reset()` callers\n * check `isDirty` to decide which source to revert to, then call `clear()`.\n */\nexport type DirtyTracker = {\n clear: () => void;\n readonly isDirty: boolean;\n markDirty: () => void;\n};\n\nexport function createDirtyTracker(): DirtyTracker {\n let dirty = false;\n\n return {\n clear: () => {\n dirty = false;\n },\n get isDirty() {\n return dirty;\n },\n markDirty: () => {\n dirty = true;\n },\n };\n}\n\n// ── Label placement ───────────────────────────────────────────────────────────\n\nexport type LabelPlacement = 'inset' | 'outside' | undefined;\n\n// ── Assistive state (error/helper text, describedby/errormessage/invalid) ─────\n\n/**\n * A field's error/helper/disabled/validation-trigger state — everything an input\n * needs *except* a visible `<label>`. Split out from label state (below) because\n * not every field has one: `ore-message-composer` names itself via `aria-label`\n * (a chat composer's accessible name conventionally comes from context/placeholder,\n * not a floating `<label>`), so it composes this directly instead of pulling in\n * label plumbing it can't use. `createField()` composes both for fields that do\n * render a `<label>`.\n */\nexport type AssistiveStateHandle = {\n /** `aria-describedby` value. Non-null when helper or error text is present. */\n ariaDescribedBy: Readable<string | null>;\n /** `aria-errormessage` value. Non-null when error text is present. */\n ariaErrorMessage: Readable<string | null>;\n /** `aria-invalid` value. `'true'` when error text is present, otherwise `null`. */\n ariaInvalid: Readable<'true' | null>;\n /**\n * The stable `id` used for `aria-describedby` on the input. Points at the\n * assistive-text region (covers both helper text and error text per WAI-ARIA).\n */\n assistiveId: string;\n /**\n * Registers the real form field handle (the return value of `useField()`) so that\n * `triggerValidation()` can call its `reportValidity()`. See `TextFieldHandle.attachFormField`\n * for why this is a post-hoc call instead of a constructor option — the same forward\n * reference applies here for choice fields.\n */\n attachFormField: (formField: { reportValidity(): void }) => void;\n disabled: Readable<boolean>;\n /** Stable `id` for the inline error message element (`aria-errormessage`). */\n errorId: string;\n /** Reactive error text. Empty string when no error is set. */\n errorText: Readable<string>;\n /** Reactive helper text. Empty string when no helper is set. */\n helperText: Readable<string>;\n triggerValidation: (on: Extract<ValidationTrigger, 'blur' | 'change'>) => void;\n};\n\nexport type AssistiveStateOptions = {\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n validateOn?: Readable<ControlValidationMode>;\n};\n\nexport const createAssistiveState = (options: AssistiveStateOptions): AssistiveStateHandle => {\n const disabled = computed(() => Boolean(options.disabled?.value));\n const assistiveId = createId('helper');\n const errorId = createId('error');\n const resolvedAssistive = createErrorHelperState({ error: options.error, helper: options.helper });\n\n const ariaDescribedBy = computed(() =>\n resolvedAssistive.value.errorText || resolvedAssistive.value.helperText ? assistiveId : null,\n );\n const ariaErrorMessage = computed(() => (resolvedAssistive.value.errorText ? errorId : null));\n const ariaInvalid = computed<'true' | null>(() => (resolvedAssistive.value.errorText ? 'true' : null));\n\n let formField: { reportValidity(): void } | null = null;\n const attachFormField = (nextFormField: { reportValidity(): void }): void => {\n formField = nextFormField;\n };\n\n const triggerValidation = (on: Extract<ValidationTrigger, 'blur' | 'change'>): void => {\n if (options.validateOn?.value !== on) return;\n\n if (!formField) {\n // Not user-facing — this only fires when a component author wired `validateOn` but\n // forgot the matching `attachFormField(useField(...))` call, so validation silently\n // never runs instead of erroring where the mistake actually is.\n warn(\n \"triggerValidation() called before attachFormField() — validation will not run. See createTextField()/createChoiceField()/createCheckable()'s attachFormField doc comment.\",\n );\n\n return;\n }\n\n formField.reportValidity();\n };\n\n return {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n assistiveId,\n attachFormField,\n disabled,\n errorId,\n errorText: computed(() => resolvedAssistive.value.errorText),\n helperText: computed(() => resolvedAssistive.value.helperText),\n triggerValidation,\n };\n};\n\n// ── Label state (visible `<label>` + `aria-labelledby`) ────────────────────────\n\nexport type LabelStateHandle = {\n /** `aria-labelledby` value. Non-null when a label is visible. */\n ariaLabelledBy: Readable<string | null>;\n /** Stable `id` for the underlying form control — the `<label for=\"...\">` target. */\n fieldId: string;\n /** Stable `id` for the label element. Stamp this on your `<label id=\"...\">`. */\n labelId: string;\n /** Whether the label should be visible. Use to toggle `hidden` on the `<label>`. */\n labelVisible: Readable<boolean>;\n};\n\nexport type LabelStateOptions = {\n /**\n * Override for label visibility that takes precedence over deriving it from\n * the `label` text signal. Pass a computed signal that checks both prop and\n * slot presence to enable slot-first composition.\n *\n * @example\n * ```ts\n * const hasLabel = computed(() => !!props.label.value || slots.has('label').value);\n * const label = createLabelState({ ...options, hasLabel });\n * ```\n */\n hasLabel?: Readable<boolean>;\n /**\n * When provided, used directly as `fieldId` instead of generating one via\n * `createId`. Useful in tests for deterministic ID assertions.\n */\n id?: string;\n /**\n * Label text signal. When provided, `labelVisible` and `ariaLabelledBy` are\n * computed reactively from this value (see `hasLabel` for the slot-first override).\n */\n label?: Readable<string | undefined>;\n /**\n * Label placement signal. Defaults to `'inset'`.\n *\n * Not consumed by `createLabelState()` itself — visibility is driven solely by\n * `hasLabel`/`label`. Threaded through so component authors can apply\n * placement-specific styling (`label-placement` attribute) without a second\n * option; if placement-dependent visibility is ever needed, wire it into\n * `labelVisible` here instead of adding a new option.\n */\n labelPlacement?: Readable<LabelPlacement>;\n prefix?: string;\n};\n\nexport const createLabelState = (options: LabelStateOptions): LabelStateHandle => {\n const fieldId = options.id ?? createId(options.prefix ?? 'field');\n const labelId = createId('label');\n const label$ = options.label ?? computed(() => undefined);\n\n // Slot-first composition: if the caller provides `hasLabel`, use it instead\n // of deriving visibility purely from the label text signal. This allows\n // components with a `<slot name=\"label\">` to stay visible even when the\n // `label` prop is empty but a slotted label element is present.\n const labelVisible = options.hasLabel ?? computed(() => Boolean(label$.value));\n const ariaLabelledBy = computed(() => (labelVisible.value ? labelId : null));\n\n return { ariaLabelledBy, fieldId, labelId, labelVisible };\n};\n\n// ── Field handle (assistive + label, composed) ──────────────────────────────\n\n/**\n * Common handle returned by `createField` — the base for both `TextFieldHandle`\n * and `ChoiceFieldHandle`. Composes `createAssistiveState` + `createLabelState`;\n * call those directly instead if a field has no visible `<label>` (see\n * `createAssistiveState`'s doc comment).\n *\n * ARIA signals and label state are flat on the handle — no nested `aria` or\n * `label` sub-objects. `labelId`/`labelVisible`/`ariaLabelledBy` are always\n * present but only meaningful when `label`/`hasLabel` was passed — omit both\n * and they resolve to an always-hidden, unreferenced label.\n *\n * @example\n * ```html\n * <label id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\">...</label>\n * <input\n * aria-labelledby=\"${ariaLabelledBy}\"\n * aria-describedby=\"${ariaDescribedBy}\"\n * aria-invalid=\"${ariaInvalid}\" />\n * ```\n */\nexport type FieldHandle = AssistiveStateHandle & LabelStateHandle;\n\n/** Options shared by both `createTextField` and `createChoiceField`. */\nexport type FieldOptions = AssistiveStateOptions & LabelStateOptions;\n\nexport const createField = (options: FieldOptions): FieldHandle => ({\n ...createAssistiveState(options),\n ...createLabelState(options),\n});\n"],"mappings":"oFAsBA,IAAa,EAA0B,IAAA,EACrC,EAAA,SAAA,MAAgB,CACd,UAAW,EAAQ,OAAO,OAAS,GACnC,WAAY,EAAQ,QAAQ,OAAS,EACvC,EAAE,EAyBS,EAAsB,IAAA,EACjC,EAAA,SAAA,KAA6B,CAC3B,IAAM,EAAQ,EAAQ,OAAO,OAAS,GAChC,EAAY,EAAQ,WAAW,MAC/B,EAAkB,OAAO,CAAS,EAClC,EAAiB,OAAO,SAAS,CAAe,GAAK,EAAkB,EAAI,EAAkB,KAC7F,EAAa,IAAmB,KAChC,EAAc,EAAa,GAAG,EAAM,OAAO,KAAK,IAAmB,GACnE,EAAQ,EAAa,EAAM,OAAS,EAAiB,EAE3D,MAAO,CACL,eAAgB,EAAa,GAAS,EAAI,GAC1C,iBAAkB,EAAa,GAAS,IAAO,EAAQ,EAAI,GAC3D,aACF,CACF,CAAC,EAYU,GAAoB,EAAmC,EAAO,YACpE,EAED,EAAQ,eAAuB,GAAG,EAAK,WAEvC,EAAQ,iBAAyB,GAAG,EAAK,aAEtC,EANc,EA4BvB,SAAgB,GAAmC,CACjD,IAAI,EAAQ,GAEZ,MAAO,CACL,UAAa,CACX,EAAQ,EACV,EACA,IAAI,SAAU,CACZ,OAAO,CACT,EACA,cAAiB,CACf,EAAQ,EACV,CACF,CACF,CAqDA,IAAa,EAAwB,GAAyD,CAC5F,IAAM,GAAA,EAAW,EAAA,SAAA,KAAe,EAAQ,EAAQ,UAAU,KAAM,EAC1D,GAAA,EAAc,EAAA,SAAA,CAAS,QAAQ,EAC/B,GAAA,EAAU,EAAA,SAAA,CAAS,OAAO,EAC1B,EAAoB,EAAuB,CAAE,MAAO,EAAQ,MAAO,OAAQ,EAAQ,MAAO,CAAC,EAE3F,GAAA,EAAkB,EAAA,SAAA,KACtB,EAAkB,MAAM,WAAa,EAAkB,MAAM,WAAa,EAAc,IAC1F,EACM,GAAA,EAAmB,EAAA,SAAA,KAAgB,EAAkB,MAAM,UAAY,EAAU,IAAK,EACtF,GAAA,EAAc,EAAA,SAAA,KAA+B,EAAkB,MAAM,UAAY,OAAS,IAAK,EAEjG,EAA+C,KAsBnD,MAAO,CACL,kBACA,mBACA,cACA,cACA,gBA1BuB,GAAoD,CAC3E,EAAY,CACd,EAyBE,WACA,UACA,WAAA,EAAW,EAAA,SAAA,KAAe,EAAkB,MAAM,SAAS,EAC3D,YAAA,EAAY,EAAA,SAAA,KAAe,EAAkB,MAAM,UAAU,EAC7D,kBA3ByB,GAA4D,CACjF,EAAQ,YAAY,QAAU,GAE7B,GAWL,EAAU,eAAe,CAC3B,CAaA,CACF,EAmDa,EAAoB,GAAiD,CAChF,IAAM,EAAU,EAAQ,KAAA,EAAM,EAAA,SAAA,CAAS,EAAQ,QAAU,OAAO,EAC1D,GAAA,EAAU,EAAA,SAAA,CAAS,OAAO,EAC1B,EAAS,EAAQ,QAAA,EAAS,EAAA,SAAA,KAAe,IAAA,EAAS,EAMlD,EAAe,EAAQ,WAAA,EAAY,EAAA,SAAA,KAAe,EAAQ,EAAO,KAAM,EAG7E,MAAO,CAAE,gBAAA,EAFc,EAAA,SAAA,KAAgB,EAAa,MAAQ,EAAU,IAE7D,EAAgB,UAAS,UAAS,cAAa,CAC1D,EA6Ba,EAAe,IAAwC,CAClE,GAAG,EAAqB,CAAO,EAC/B,GAAG,EAAiB,CAAO,CAC7B"}
|
|
@@ -130,7 +130,7 @@ export type LabelStateOptions = {
|
|
|
130
130
|
hasLabel?: Readable<boolean>;
|
|
131
131
|
/**
|
|
132
132
|
* When provided, used directly as `fieldId` instead of generating one via
|
|
133
|
-
* `
|
|
133
|
+
* `createId`. Useful in tests for deterministic ID assertions.
|
|
134
134
|
*/
|
|
135
135
|
id?: string;
|
|
136
136
|
/**
|
package/dist/core/field-base.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field-base.js","names":[],"sources":["../../src/core/field-base.ts"],"sourcesContent":["import { createStableId } from '@vielzeug/ore';\nimport { computed, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\n\n// ── Validation / context types ────────────────────────────────────────────────\n\nexport type ValidationTrigger = 'blur' | 'change' | 'input' | 'submit';\nexport type ControlValidationMode = ValidationTrigger | undefined;\n\n// ── Error/helper assistive state ──────────────────────────────────────────────\n\nexport type ErrorHelperState = {\n errorText: string;\n helperText: string;\n};\n\nexport type ErrorHelperOptions = {\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n};\n\nexport const createErrorHelperState = (options: ErrorHelperOptions): Readable<ErrorHelperState> =>\n computed(() => ({\n errorText: options.error?.value ?? '',\n helperText: options.helper?.value ?? '',\n }));\n\n// ── Counter state (opt-in) ─────────────────────────────────────────────────────\n\n/** Counter state for text fields with `maxLength`. */\nexport type CounterState = {\n counterAtLimit: boolean;\n counterNearLimit: boolean;\n counterText: string;\n};\n\nexport type CounterOptions = {\n maxLength?: Readable<number | undefined>;\n value: Readable<string | undefined>;\n};\n\n/**\n * Creates a reactive counter state signal for text fields with `maxLength`.\n * Only call this when `maxLength` may be set — the state is genuinely opt-in.\n *\n * @example\n * ```ts\n * const counter = createCounterState({ value: tf.value, maxLength: props.maxlength });\n * ```\n */\nexport const createCounterState = (options: CounterOptions): Readable<CounterState> =>\n computed<CounterState>(() => {\n const value = options.value?.value ?? '';\n const maxLength = options.maxLength?.value;\n const parsedMaxLength = Number(maxLength);\n const validMaxLength = Number.isFinite(parsedMaxLength) && parsedMaxLength > 0 ? parsedMaxLength : null;\n const hasCounter = validMaxLength !== null;\n const counterText = hasCounter ? `${value.length} / ${validMaxLength}` : '';\n const ratio = hasCounter ? value.length / validMaxLength : 0;\n\n return {\n counterAtLimit: hasCounter ? ratio >= 1 : false,\n counterNearLimit: hasCounter ? ratio >= 0.9 && ratio < 1 : false,\n counterText,\n };\n });\n\n/**\n * The single implementation of \"which modifier class does this counter state map to\" —\n * every field with a character counter (`ore-input`, `ore-textarea`, `ore-message-composer`)\n * renders the identical near-limit/at-limit styling, so this is the one place that decides it.\n *\n * @example\n * ```ts\n * html`<span class=\"${() => counterClassName(counter?.value)}\">...</span>`\n * ```\n */\nexport const counterClassName = (counter: CounterState | undefined, base = 'counter'): string => {\n if (!counter) return base;\n\n if (counter.counterAtLimit) return `${base} at-limit`;\n\n if (counter.counterNearLimit) return `${base} near-limit`;\n\n return base;\n};\n\n// ── Dirty tracking (two-state reset) ───────────────────────────────────────────\n\n/**\n * Tracks whether a control's value has ever been changed by user interaction.\n *\n * Backs the \"two-state reset()\" pattern used by `createCheckable` (`checked`/\n * `indeterminate`) and `createChoiceField` (`selectedValues`): before the first\n * interaction, the live prop is still a reliable \"current default\" to resync from\n * (e.g. an async-loaded value arriving after mount); after it, the prop is\n * contaminated by interaction-driven attribute reflection, so only a snapshot taken\n * at creation still represents \"the default\" in the native sense. `reset()` callers\n * check `isDirty` to decide which source to revert to, then call `clear()`.\n */\nexport type DirtyTracker = {\n clear: () => void;\n readonly isDirty: boolean;\n markDirty: () => void;\n};\n\nexport function createDirtyTracker(): DirtyTracker {\n let dirty = false;\n\n return {\n clear: () => {\n dirty = false;\n },\n get isDirty() {\n return dirty;\n },\n markDirty: () => {\n dirty = true;\n },\n };\n}\n\n// ── Label placement ───────────────────────────────────────────────────────────\n\nexport type LabelPlacement = 'inset' | 'outside' | undefined;\n\n// ── Assistive state (error/helper text, describedby/errormessage/invalid) ─────\n\n/**\n * A field's error/helper/disabled/validation-trigger state — everything an input\n * needs *except* a visible `<label>`. Split out from label state (below) because\n * not every field has one: `ore-message-composer` names itself via `aria-label`\n * (a chat composer's accessible name conventionally comes from context/placeholder,\n * not a floating `<label>`), so it composes this directly instead of pulling in\n * label plumbing it can't use. `createField()` composes both for fields that do\n * render a `<label>`.\n */\nexport type AssistiveStateHandle = {\n /** `aria-describedby` value. Non-null when helper or error text is present. */\n ariaDescribedBy: Readable<string | null>;\n /** `aria-errormessage` value. Non-null when error text is present. */\n ariaErrorMessage: Readable<string | null>;\n /** `aria-invalid` value. `'true'` when error text is present, otherwise `null`. */\n ariaInvalid: Readable<'true' | null>;\n /**\n * The stable `id` used for `aria-describedby` on the input. Points at the\n * assistive-text region (covers both helper text and error text per WAI-ARIA).\n */\n assistiveId: string;\n /**\n * Registers the real form field handle (the return value of `useField()`) so that\n * `triggerValidation()` can call its `reportValidity()`. See `TextFieldHandle.attachFormField`\n * for why this is a post-hoc call instead of a constructor option — the same forward\n * reference applies here for choice fields.\n */\n attachFormField: (formField: { reportValidity(): void }) => void;\n disabled: Readable<boolean>;\n /** Stable `id` for the inline error message element (`aria-errormessage`). */\n errorId: string;\n /** Reactive error text. Empty string when no error is set. */\n errorText: Readable<string>;\n /** Reactive helper text. Empty string when no helper is set. */\n helperText: Readable<string>;\n triggerValidation: (on: Extract<ValidationTrigger, 'blur' | 'change'>) => void;\n};\n\nexport type AssistiveStateOptions = {\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n validateOn?: Readable<ControlValidationMode>;\n};\n\nexport const createAssistiveState = (options: AssistiveStateOptions): AssistiveStateHandle => {\n const disabled = computed(() => Boolean(options.disabled?.value));\n const assistiveId = createStableId('helper');\n const errorId = createStableId('error');\n const resolvedAssistive = createErrorHelperState({ error: options.error, helper: options.helper });\n\n const ariaDescribedBy = computed(() =>\n resolvedAssistive.value.errorText || resolvedAssistive.value.helperText ? assistiveId : null,\n );\n const ariaErrorMessage = computed(() => (resolvedAssistive.value.errorText ? errorId : null));\n const ariaInvalid = computed<'true' | null>(() => (resolvedAssistive.value.errorText ? 'true' : null));\n\n let formField: { reportValidity(): void } | null = null;\n const attachFormField = (nextFormField: { reportValidity(): void }): void => {\n formField = nextFormField;\n };\n\n const triggerValidation = (on: Extract<ValidationTrigger, 'blur' | 'change'>): void => {\n if (options.validateOn?.value !== on) return;\n\n if (!formField) {\n // Not user-facing — this only fires when a component author wired `validateOn` but\n // forgot the matching `attachFormField(useField(...))` call, so validation silently\n // never runs instead of erroring where the mistake actually is.\n warn(\n \"triggerValidation() called before attachFormField() — validation will not run. See createTextField()/createChoiceField()/createCheckable()'s attachFormField doc comment.\",\n );\n\n return;\n }\n\n formField.reportValidity();\n };\n\n return {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n assistiveId,\n attachFormField,\n disabled,\n errorId,\n errorText: computed(() => resolvedAssistive.value.errorText),\n helperText: computed(() => resolvedAssistive.value.helperText),\n triggerValidation,\n };\n};\n\n// ── Label state (visible `<label>` + `aria-labelledby`) ────────────────────────\n\nexport type LabelStateHandle = {\n /** `aria-labelledby` value. Non-null when a label is visible. */\n ariaLabelledBy: Readable<string | null>;\n /** Stable `id` for the underlying form control — the `<label for=\"...\">` target. */\n fieldId: string;\n /** Stable `id` for the label element. Stamp this on your `<label id=\"...\">`. */\n labelId: string;\n /** Whether the label should be visible. Use to toggle `hidden` on the `<label>`. */\n labelVisible: Readable<boolean>;\n};\n\nexport type LabelStateOptions = {\n /**\n * Override for label visibility that takes precedence over deriving it from\n * the `label` text signal. Pass a computed signal that checks both prop and\n * slot presence to enable slot-first composition.\n *\n * @example\n * ```ts\n * const hasLabel = computed(() => !!props.label.value || slots.has('label').value);\n * const label = createLabelState({ ...options, hasLabel });\n * ```\n */\n hasLabel?: Readable<boolean>;\n /**\n * When provided, used directly as `fieldId` instead of generating one via\n * `createStableId`. Useful in tests for deterministic ID assertions.\n */\n id?: string;\n /**\n * Label text signal. When provided, `labelVisible` and `ariaLabelledBy` are\n * computed reactively from this value (see `hasLabel` for the slot-first override).\n */\n label?: Readable<string | undefined>;\n /**\n * Label placement signal. Defaults to `'inset'`.\n *\n * Not consumed by `createLabelState()` itself — visibility is driven solely by\n * `hasLabel`/`label`. Threaded through so component authors can apply\n * placement-specific styling (`label-placement` attribute) without a second\n * option; if placement-dependent visibility is ever needed, wire it into\n * `labelVisible` here instead of adding a new option.\n */\n labelPlacement?: Readable<LabelPlacement>;\n prefix?: string;\n};\n\nexport const createLabelState = (options: LabelStateOptions): LabelStateHandle => {\n const fieldId = options.id ?? createStableId(options.prefix ?? 'field');\n const labelId = createStableId('label');\n const label$ = options.label ?? computed(() => undefined);\n\n // Slot-first composition: if the caller provides `hasLabel`, use it instead\n // of deriving visibility purely from the label text signal. This allows\n // components with a `<slot name=\"label\">` to stay visible even when the\n // `label` prop is empty but a slotted label element is present.\n const labelVisible = options.hasLabel ?? computed(() => Boolean(label$.value));\n const ariaLabelledBy = computed(() => (labelVisible.value ? labelId : null));\n\n return { ariaLabelledBy, fieldId, labelId, labelVisible };\n};\n\n// ── Field handle (assistive + label, composed) ──────────────────────────────\n\n/**\n * Common handle returned by `createField` — the base for both `TextFieldHandle`\n * and `ChoiceFieldHandle`. Composes `createAssistiveState` + `createLabelState`;\n * call those directly instead if a field has no visible `<label>` (see\n * `createAssistiveState`'s doc comment).\n *\n * ARIA signals and label state are flat on the handle — no nested `aria` or\n * `label` sub-objects. `labelId`/`labelVisible`/`ariaLabelledBy` are always\n * present but only meaningful when `label`/`hasLabel` was passed — omit both\n * and they resolve to an always-hidden, unreferenced label.\n *\n * @example\n * ```html\n * <label id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\">...</label>\n * <input\n * aria-labelledby=\"${ariaLabelledBy}\"\n * aria-describedby=\"${ariaDescribedBy}\"\n * aria-invalid=\"${ariaInvalid}\" />\n * ```\n */\nexport type FieldHandle = AssistiveStateHandle & LabelStateHandle;\n\n/** Options shared by both `createTextField` and `createChoiceField`. */\nexport type FieldOptions = AssistiveStateOptions & LabelStateOptions;\n\nexport const createField = (options: FieldOptions): FieldHandle => ({\n ...createAssistiveState(options),\n ...createLabelState(options),\n});\n"],"mappings":";;;;AAsBA,IAAa,KAA0B,MACrC,SAAgB;CACd,WAAW,EAAQ,OAAO,SAAS;CACnC,YAAY,EAAQ,QAAQ,SAAS;AACvC,EAAE,GAyBS,KAAsB,MACjC,QAA6B;CAC3B,IAAM,IAAQ,EAAQ,OAAO,SAAS,IAChC,IAAY,EAAQ,WAAW,OAC/B,IAAkB,OAAO,CAAS,GAClC,IAAiB,OAAO,SAAS,CAAe,KAAK,IAAkB,IAAI,IAAkB,MAC7F,IAAa,MAAmB,MAChC,IAAc,IAAa,GAAG,EAAM,OAAO,KAAK,MAAmB,IACnE,IAAQ,IAAa,EAAM,SAAS,IAAiB;CAE3D,OAAO;EACL,gBAAgB,IAAa,KAAS,IAAI;EAC1C,kBAAkB,IAAa,KAAS,MAAO,IAAQ,IAAI;EAC3D;CACF;AACF,CAAC,GAYU,KAAoB,GAAmC,IAAO,cACpE,IAED,EAAQ,iBAAuB,GAAG,EAAK,aAEvC,EAAQ,mBAAyB,GAAG,EAAK,eAEtC,IANc;AA4BvB,SAAgB,IAAmC;CACjD,IAAI,IAAQ;CAEZ,OAAO;EACL,aAAa;GACX,IAAQ;EACV;EACA,IAAI,UAAU;GACZ,OAAO;EACT;EACA,iBAAiB;GACf,IAAQ;EACV;CACF;AACF;AAqDA,IAAa,KAAwB,MAAyD;CAC5F,IAAM,IAAW,QAAe,EAAQ,EAAQ,UAAU,KAAM,GAC1D,IAAc,EAAe,QAAQ,GACrC,IAAU,EAAe,OAAO,GAChC,IAAoB,EAAuB;EAAE,OAAO,EAAQ;EAAO,QAAQ,EAAQ;CAAO,CAAC,GAE3F,IAAkB,QACtB,EAAkB,MAAM,aAAa,EAAkB,MAAM,aAAa,IAAc,IAC1F,GACM,IAAmB,QAAgB,EAAkB,MAAM,YAAY,IAAU,IAAK,GACtF,IAAc,QAA+B,EAAkB,MAAM,YAAY,SAAS,IAAK,GAEjG,IAA+C;CAsBnD,OAAO;EACL;EACA;EACA;EACA;EACA,kBA1BuB,MAAoD;GAC3E,IAAY;EACd;EAyBE;EACA;EACA,WAAW,QAAe,EAAkB,MAAM,SAAS;EAC3D,YAAY,QAAe,EAAkB,MAAM,UAAU;EAC7D,oBA3ByB,MAA4D;GACjF,EAAQ,YAAY,UAAU,KAE7B,KAWL,EAAU,eAAe;EAC3B;CAaA;AACF,GAmDa,KAAoB,MAAiD;CAChF,IAAM,IAAU,EAAQ,MAAM,EAAe,EAAQ,UAAU,OAAO,GAChE,IAAU,EAAe,OAAO,GAChC,IAAS,EAAQ,SAAS,QAAe,KAAA,CAAS,GAMlD,IAAe,EAAQ,YAAY,QAAe,EAAQ,EAAO,KAAM;CAG7E,OAAO;EAAE,gBAFc,QAAgB,EAAa,QAAQ,IAAU,IAE7D;EAAgB;EAAS;EAAS;CAAa;AAC1D,GA6Ba,KAAe,OAAwC;CAClE,GAAG,EAAqB,CAAO;CAC/B,GAAG,EAAiB,CAAO;AAC7B"}
|
|
1
|
+
{"version":3,"file":"field-base.js","names":[],"sources":["../../src/core/field-base.ts"],"sourcesContent":["import { createId } from '@vielzeug/ore';\nimport { computed, type Readable } from '@vielzeug/ripple';\n\nimport { warn } from '../_dev';\n\n// ── Validation / context types ────────────────────────────────────────────────\n\nexport type ValidationTrigger = 'blur' | 'change' | 'input' | 'submit';\nexport type ControlValidationMode = ValidationTrigger | undefined;\n\n// ── Error/helper assistive state ──────────────────────────────────────────────\n\nexport type ErrorHelperState = {\n errorText: string;\n helperText: string;\n};\n\nexport type ErrorHelperOptions = {\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n};\n\nexport const createErrorHelperState = (options: ErrorHelperOptions): Readable<ErrorHelperState> =>\n computed(() => ({\n errorText: options.error?.value ?? '',\n helperText: options.helper?.value ?? '',\n }));\n\n// ── Counter state (opt-in) ─────────────────────────────────────────────────────\n\n/** Counter state for text fields with `maxLength`. */\nexport type CounterState = {\n counterAtLimit: boolean;\n counterNearLimit: boolean;\n counterText: string;\n};\n\nexport type CounterOptions = {\n maxLength?: Readable<number | undefined>;\n value: Readable<string | undefined>;\n};\n\n/**\n * Creates a reactive counter state signal for text fields with `maxLength`.\n * Only call this when `maxLength` may be set — the state is genuinely opt-in.\n *\n * @example\n * ```ts\n * const counter = createCounterState({ value: tf.value, maxLength: props.maxlength });\n * ```\n */\nexport const createCounterState = (options: CounterOptions): Readable<CounterState> =>\n computed<CounterState>(() => {\n const value = options.value?.value ?? '';\n const maxLength = options.maxLength?.value;\n const parsedMaxLength = Number(maxLength);\n const validMaxLength = Number.isFinite(parsedMaxLength) && parsedMaxLength > 0 ? parsedMaxLength : null;\n const hasCounter = validMaxLength !== null;\n const counterText = hasCounter ? `${value.length} / ${validMaxLength}` : '';\n const ratio = hasCounter ? value.length / validMaxLength : 0;\n\n return {\n counterAtLimit: hasCounter ? ratio >= 1 : false,\n counterNearLimit: hasCounter ? ratio >= 0.9 && ratio < 1 : false,\n counterText,\n };\n });\n\n/**\n * The single implementation of \"which modifier class does this counter state map to\" —\n * every field with a character counter (`ore-input`, `ore-textarea`, `ore-message-composer`)\n * renders the identical near-limit/at-limit styling, so this is the one place that decides it.\n *\n * @example\n * ```ts\n * html`<span class=\"${() => counterClassName(counter?.value)}\">...</span>`\n * ```\n */\nexport const counterClassName = (counter: CounterState | undefined, base = 'counter'): string => {\n if (!counter) return base;\n\n if (counter.counterAtLimit) return `${base} at-limit`;\n\n if (counter.counterNearLimit) return `${base} near-limit`;\n\n return base;\n};\n\n// ── Dirty tracking (two-state reset) ───────────────────────────────────────────\n\n/**\n * Tracks whether a control's value has ever been changed by user interaction.\n *\n * Backs the \"two-state reset()\" pattern used by `createCheckable` (`checked`/\n * `indeterminate`) and `createChoiceField` (`selectedValues`): before the first\n * interaction, the live prop is still a reliable \"current default\" to resync from\n * (e.g. an async-loaded value arriving after mount); after it, the prop is\n * contaminated by interaction-driven attribute reflection, so only a snapshot taken\n * at creation still represents \"the default\" in the native sense. `reset()` callers\n * check `isDirty` to decide which source to revert to, then call `clear()`.\n */\nexport type DirtyTracker = {\n clear: () => void;\n readonly isDirty: boolean;\n markDirty: () => void;\n};\n\nexport function createDirtyTracker(): DirtyTracker {\n let dirty = false;\n\n return {\n clear: () => {\n dirty = false;\n },\n get isDirty() {\n return dirty;\n },\n markDirty: () => {\n dirty = true;\n },\n };\n}\n\n// ── Label placement ───────────────────────────────────────────────────────────\n\nexport type LabelPlacement = 'inset' | 'outside' | undefined;\n\n// ── Assistive state (error/helper text, describedby/errormessage/invalid) ─────\n\n/**\n * A field's error/helper/disabled/validation-trigger state — everything an input\n * needs *except* a visible `<label>`. Split out from label state (below) because\n * not every field has one: `ore-message-composer` names itself via `aria-label`\n * (a chat composer's accessible name conventionally comes from context/placeholder,\n * not a floating `<label>`), so it composes this directly instead of pulling in\n * label plumbing it can't use. `createField()` composes both for fields that do\n * render a `<label>`.\n */\nexport type AssistiveStateHandle = {\n /** `aria-describedby` value. Non-null when helper or error text is present. */\n ariaDescribedBy: Readable<string | null>;\n /** `aria-errormessage` value. Non-null when error text is present. */\n ariaErrorMessage: Readable<string | null>;\n /** `aria-invalid` value. `'true'` when error text is present, otherwise `null`. */\n ariaInvalid: Readable<'true' | null>;\n /**\n * The stable `id` used for `aria-describedby` on the input. Points at the\n * assistive-text region (covers both helper text and error text per WAI-ARIA).\n */\n assistiveId: string;\n /**\n * Registers the real form field handle (the return value of `useField()`) so that\n * `triggerValidation()` can call its `reportValidity()`. See `TextFieldHandle.attachFormField`\n * for why this is a post-hoc call instead of a constructor option — the same forward\n * reference applies here for choice fields.\n */\n attachFormField: (formField: { reportValidity(): void }) => void;\n disabled: Readable<boolean>;\n /** Stable `id` for the inline error message element (`aria-errormessage`). */\n errorId: string;\n /** Reactive error text. Empty string when no error is set. */\n errorText: Readable<string>;\n /** Reactive helper text. Empty string when no helper is set. */\n helperText: Readable<string>;\n triggerValidation: (on: Extract<ValidationTrigger, 'blur' | 'change'>) => void;\n};\n\nexport type AssistiveStateOptions = {\n disabled?: Readable<boolean | undefined>;\n error?: Readable<string | undefined>;\n helper?: Readable<string | undefined>;\n validateOn?: Readable<ControlValidationMode>;\n};\n\nexport const createAssistiveState = (options: AssistiveStateOptions): AssistiveStateHandle => {\n const disabled = computed(() => Boolean(options.disabled?.value));\n const assistiveId = createId('helper');\n const errorId = createId('error');\n const resolvedAssistive = createErrorHelperState({ error: options.error, helper: options.helper });\n\n const ariaDescribedBy = computed(() =>\n resolvedAssistive.value.errorText || resolvedAssistive.value.helperText ? assistiveId : null,\n );\n const ariaErrorMessage = computed(() => (resolvedAssistive.value.errorText ? errorId : null));\n const ariaInvalid = computed<'true' | null>(() => (resolvedAssistive.value.errorText ? 'true' : null));\n\n let formField: { reportValidity(): void } | null = null;\n const attachFormField = (nextFormField: { reportValidity(): void }): void => {\n formField = nextFormField;\n };\n\n const triggerValidation = (on: Extract<ValidationTrigger, 'blur' | 'change'>): void => {\n if (options.validateOn?.value !== on) return;\n\n if (!formField) {\n // Not user-facing — this only fires when a component author wired `validateOn` but\n // forgot the matching `attachFormField(useField(...))` call, so validation silently\n // never runs instead of erroring where the mistake actually is.\n warn(\n \"triggerValidation() called before attachFormField() — validation will not run. See createTextField()/createChoiceField()/createCheckable()'s attachFormField doc comment.\",\n );\n\n return;\n }\n\n formField.reportValidity();\n };\n\n return {\n ariaDescribedBy,\n ariaErrorMessage,\n ariaInvalid,\n assistiveId,\n attachFormField,\n disabled,\n errorId,\n errorText: computed(() => resolvedAssistive.value.errorText),\n helperText: computed(() => resolvedAssistive.value.helperText),\n triggerValidation,\n };\n};\n\n// ── Label state (visible `<label>` + `aria-labelledby`) ────────────────────────\n\nexport type LabelStateHandle = {\n /** `aria-labelledby` value. Non-null when a label is visible. */\n ariaLabelledBy: Readable<string | null>;\n /** Stable `id` for the underlying form control — the `<label for=\"...\">` target. */\n fieldId: string;\n /** Stable `id` for the label element. Stamp this on your `<label id=\"...\">`. */\n labelId: string;\n /** Whether the label should be visible. Use to toggle `hidden` on the `<label>`. */\n labelVisible: Readable<boolean>;\n};\n\nexport type LabelStateOptions = {\n /**\n * Override for label visibility that takes precedence over deriving it from\n * the `label` text signal. Pass a computed signal that checks both prop and\n * slot presence to enable slot-first composition.\n *\n * @example\n * ```ts\n * const hasLabel = computed(() => !!props.label.value || slots.has('label').value);\n * const label = createLabelState({ ...options, hasLabel });\n * ```\n */\n hasLabel?: Readable<boolean>;\n /**\n * When provided, used directly as `fieldId` instead of generating one via\n * `createId`. Useful in tests for deterministic ID assertions.\n */\n id?: string;\n /**\n * Label text signal. When provided, `labelVisible` and `ariaLabelledBy` are\n * computed reactively from this value (see `hasLabel` for the slot-first override).\n */\n label?: Readable<string | undefined>;\n /**\n * Label placement signal. Defaults to `'inset'`.\n *\n * Not consumed by `createLabelState()` itself — visibility is driven solely by\n * `hasLabel`/`label`. Threaded through so component authors can apply\n * placement-specific styling (`label-placement` attribute) without a second\n * option; if placement-dependent visibility is ever needed, wire it into\n * `labelVisible` here instead of adding a new option.\n */\n labelPlacement?: Readable<LabelPlacement>;\n prefix?: string;\n};\n\nexport const createLabelState = (options: LabelStateOptions): LabelStateHandle => {\n const fieldId = options.id ?? createId(options.prefix ?? 'field');\n const labelId = createId('label');\n const label$ = options.label ?? computed(() => undefined);\n\n // Slot-first composition: if the caller provides `hasLabel`, use it instead\n // of deriving visibility purely from the label text signal. This allows\n // components with a `<slot name=\"label\">` to stay visible even when the\n // `label` prop is empty but a slotted label element is present.\n const labelVisible = options.hasLabel ?? computed(() => Boolean(label$.value));\n const ariaLabelledBy = computed(() => (labelVisible.value ? labelId : null));\n\n return { ariaLabelledBy, fieldId, labelId, labelVisible };\n};\n\n// ── Field handle (assistive + label, composed) ──────────────────────────────\n\n/**\n * Common handle returned by `createField` — the base for both `TextFieldHandle`\n * and `ChoiceFieldHandle`. Composes `createAssistiveState` + `createLabelState`;\n * call those directly instead if a field has no visible `<label>` (see\n * `createAssistiveState`'s doc comment).\n *\n * ARIA signals and label state are flat on the handle — no nested `aria` or\n * `label` sub-objects. `labelId`/`labelVisible`/`ariaLabelledBy` are always\n * present but only meaningful when `label`/`hasLabel` was passed — omit both\n * and they resolve to an always-hidden, unreferenced label.\n *\n * @example\n * ```html\n * <label id=\"${labelId}\" ?hidden=\"${() => !labelVisible.value}\">...</label>\n * <input\n * aria-labelledby=\"${ariaLabelledBy}\"\n * aria-describedby=\"${ariaDescribedBy}\"\n * aria-invalid=\"${ariaInvalid}\" />\n * ```\n */\nexport type FieldHandle = AssistiveStateHandle & LabelStateHandle;\n\n/** Options shared by both `createTextField` and `createChoiceField`. */\nexport type FieldOptions = AssistiveStateOptions & LabelStateOptions;\n\nexport const createField = (options: FieldOptions): FieldHandle => ({\n ...createAssistiveState(options),\n ...createLabelState(options),\n});\n"],"mappings":";;;;AAsBA,IAAa,KAA0B,MACrC,SAAgB;CACd,WAAW,EAAQ,OAAO,SAAS;CACnC,YAAY,EAAQ,QAAQ,SAAS;AACvC,EAAE,GAyBS,KAAsB,MACjC,QAA6B;CAC3B,IAAM,IAAQ,EAAQ,OAAO,SAAS,IAChC,IAAY,EAAQ,WAAW,OAC/B,IAAkB,OAAO,CAAS,GAClC,IAAiB,OAAO,SAAS,CAAe,KAAK,IAAkB,IAAI,IAAkB,MAC7F,IAAa,MAAmB,MAChC,IAAc,IAAa,GAAG,EAAM,OAAO,KAAK,MAAmB,IACnE,IAAQ,IAAa,EAAM,SAAS,IAAiB;CAE3D,OAAO;EACL,gBAAgB,IAAa,KAAS,IAAI;EAC1C,kBAAkB,IAAa,KAAS,MAAO,IAAQ,IAAI;EAC3D;CACF;AACF,CAAC,GAYU,KAAoB,GAAmC,IAAO,cACpE,IAED,EAAQ,iBAAuB,GAAG,EAAK,aAEvC,EAAQ,mBAAyB,GAAG,EAAK,eAEtC,IANc;AA4BvB,SAAgB,IAAmC;CACjD,IAAI,IAAQ;CAEZ,OAAO;EACL,aAAa;GACX,IAAQ;EACV;EACA,IAAI,UAAU;GACZ,OAAO;EACT;EACA,iBAAiB;GACf,IAAQ;EACV;CACF;AACF;AAqDA,IAAa,KAAwB,MAAyD;CAC5F,IAAM,IAAW,QAAe,EAAQ,EAAQ,UAAU,KAAM,GAC1D,IAAc,EAAS,QAAQ,GAC/B,IAAU,EAAS,OAAO,GAC1B,IAAoB,EAAuB;EAAE,OAAO,EAAQ;EAAO,QAAQ,EAAQ;CAAO,CAAC,GAE3F,IAAkB,QACtB,EAAkB,MAAM,aAAa,EAAkB,MAAM,aAAa,IAAc,IAC1F,GACM,IAAmB,QAAgB,EAAkB,MAAM,YAAY,IAAU,IAAK,GACtF,IAAc,QAA+B,EAAkB,MAAM,YAAY,SAAS,IAAK,GAEjG,IAA+C;CAsBnD,OAAO;EACL;EACA;EACA;EACA;EACA,kBA1BuB,MAAoD;GAC3E,IAAY;EACd;EAyBE;EACA;EACA,WAAW,QAAe,EAAkB,MAAM,SAAS;EAC3D,YAAY,QAAe,EAAkB,MAAM,UAAU;EAC7D,oBA3ByB,MAA4D;GACjF,EAAQ,YAAY,UAAU,KAE7B,KAWL,EAAU,eAAe;EAC3B;CAaA;AACF,GAmDa,KAAoB,MAAiD;CAChF,IAAM,IAAU,EAAQ,MAAM,EAAS,EAAQ,UAAU,OAAO,GAC1D,IAAU,EAAS,OAAO,GAC1B,IAAS,EAAQ,SAAS,QAAe,KAAA,CAAS,GAMlD,IAAe,EAAQ,YAAY,QAAe,EAAQ,EAAO,KAAM;CAG7E,OAAO;EAAE,gBAFc,QAAgB,EAAa,QAAQ,IAAU,IAE7D;EAAgB;EAAS;EAAS;CAAa;AAC1D,GA6Ba,KAAe,OAAwC;CAClE,GAAG,EAAqB,CAAO;CAC/B,GAAG,EAAiB,CAAO;AAC7B"}
|
package/dist/core/nav.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
let e=require("@vielzeug/ripple"),t=require("@vielzeug/focus");var n=n=>{let r=(0,e.signal)(-1),
|
|
1
|
+
let e=require("@vielzeug/ripple"),t=require("@vielzeug/focus");var n=n=>{let r=new AbortController,i=(0,e.signal)(-1),a=!1,o,s=(0,t.createListNavigation)({direction:n.direction,disabled:()=>!!n.disabled?.value,getItems:n.getItems,isItemDisabled:n.isItemDisabled,keys:n.keys,loop:n.loop,orientation:n.orientation,typeahead:n.typeahead}),c=()=>{i.value=s.getIndex()},l=e=>{if(e){i.value=e.index;try{n.onNavigate?.(e)}finally{c()}}return s.getIndex()},u=e=>{if(a)return-1;let t=s.set(e);return c(),t},d=e=>{if(a)return-1;let t=s.navigate(e);return l(t)},f=()=>{a||(s.reset(),c())},p=e=>{if(a)return!1;let t=s.handleKeydown(e);return t?(t.change&&l(t.change),t.handled):!1},m=()=>{a||(a=!0,s.reset(),i.value=-1,r.abort(),o?.(),o=void 0)};return n.signal?.aborted?m():n.signal&&(n.signal?.addEventListener(`abort`,m,{once:!0}),o=()=>n.signal?.removeEventListener(`abort`,m)),{get disposalSignal(){return r.signal},dispose:m,get disposed(){return a},focusedIndex:i,getActiveItem:()=>a?void 0:s.getActiveItem(),handleKeydown:p,navigate:d,reset:f,set:u,[Symbol.dispose]:m}};exports.createListControl=n;
|
|
2
2
|
//# sourceMappingURL=nav.cjs.map
|
package/dist/core/nav.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nav.cjs","names":[],"sources":["../../src/core/nav.ts"],"sourcesContent":["import {\n createListNavigation,\n type ListNavigationOptions as FocusListNavigationOptions,\n type ListKeyAction,\n type ListNavigation,\n type ListNavigationAction,\n type ListNavigationChange,\n type ListNavigationTypeaheadOptions,\n} from '@vielzeug/focus';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nexport type ListNavigationOptions<T> = Omit<FocusListNavigationOptions<T>, 'disabled'> & {\n disabled?: Readable<boolean | undefined>;\n};\n\nexport type ListControl<T> = {\n [Symbol.dispose](): void;\n dispose(): void;\n readonly disposed: boolean;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n navigate(action: ListNavigationAction): number;\n reset(): void;\n set(index: number): number;\n};\n\nexport
|
|
1
|
+
{"version":3,"file":"nav.cjs","names":[],"sources":["../../src/core/nav.ts"],"sourcesContent":["import {\n createListNavigation,\n type ListNavigationOptions as FocusListNavigationOptions,\n type ListKeyAction,\n type ListNavigation,\n type ListNavigationAction,\n type ListNavigationChange,\n type ListNavigationTypeaheadOptions,\n} from '@vielzeug/focus';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nexport type ListNavigationOptions<T> = Omit<FocusListNavigationOptions<T>, 'disabled'> & {\n disabled?: Readable<boolean | undefined>;\n onNavigate?: (change: ListNavigationChange<T>) => void;\n signal?: AbortSignal;\n};\n\nexport type { ListKeyAction, ListNavigationAction, ListNavigationChange, ListNavigationTypeaheadOptions };\n\nexport type ListControl<T> = {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n navigate(action: ListNavigationAction): number;\n reset(): void;\n set(index: number): number;\n};\n\nexport const createListControl = <T>(options: ListNavigationOptions<T>): ListControl<T> => {\n const disposalController = new AbortController();\n const focusedIndex = signal(-1);\n let disposed = false;\n let removeAbortListener: (() => void) | undefined;\n const navigation: ListNavigation<T> = createListNavigation<T>({\n direction: options.direction,\n disabled: () => Boolean(options.disabled?.value),\n getItems: options.getItems,\n isItemDisabled: options.isItemDisabled,\n keys: options.keys,\n loop: options.loop,\n orientation: options.orientation,\n typeahead: options.typeahead,\n });\n\n const syncIndex = (): void => {\n focusedIndex.value = navigation.getIndex();\n };\n\n const applyChange = (change: ListNavigationChange<T> | null): number => {\n if (change) {\n focusedIndex.value = change.index;\n try {\n options.onNavigate?.(change);\n } finally {\n syncIndex();\n }\n }\n\n return navigation.getIndex();\n };\n\n const set = (index: number): number => {\n if (disposed) return -1;\n const next = navigation.set(index);\n\n syncIndex();\n\n return next;\n };\n\n const navigate = (action: ListNavigationAction): number => {\n if (disposed) return -1;\n const change = navigation.navigate(action);\n\n return applyChange(change);\n };\n\n const reset = (): void => {\n if (disposed) return;\n navigation.reset();\n syncIndex();\n };\n\n const handleKeydown = (event: KeyboardEvent): boolean => {\n if (disposed) return false;\n\n const result = navigation.handleKeydown(event);\n if (!result) return false;\n if (result.change) applyChange(result.change);\n return result.handled;\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n navigation.reset();\n focusedIndex.value = -1;\n disposalController.abort();\n removeAbortListener?.();\n removeAbortListener = undefined;\n };\n\n if (options.signal?.aborted) {\n dispose();\n } else if (options.signal) {\n options.signal?.addEventListener('abort', dispose, { once: true });\n removeAbortListener = () => options.signal?.removeEventListener('abort', dispose);\n }\n\n return {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n focusedIndex,\n getActiveItem: () => (disposed ? undefined : navigation.getActiveItem()),\n handleKeydown,\n navigate,\n reset,\n set,\n [Symbol.dispose]: dispose,\n };\n};\n"],"mappings":"+DAgCA,IAAa,EAAwB,GAAsD,CACzF,IAAM,EAAqB,IAAI,gBACzB,GAAA,EAAe,EAAA,OAAA,CAAO,EAAE,EAC1B,EAAW,GACX,EACE,GAAA,EAAgC,EAAA,qBAAA,CAAwB,CAC5D,UAAW,EAAQ,UACnB,aAAgB,EAAQ,EAAQ,UAAU,MAC1C,SAAU,EAAQ,SAClB,eAAgB,EAAQ,eACxB,KAAM,EAAQ,KACd,KAAM,EAAQ,KACd,YAAa,EAAQ,YACrB,UAAW,EAAQ,SACrB,CAAC,EAEK,MAAwB,CAC5B,EAAa,MAAQ,EAAW,SAAS,CAC3C,EAEM,EAAe,GAAmD,CACtE,GAAI,EAAQ,CACV,EAAa,MAAQ,EAAO,MAC5B,GAAI,CACF,EAAQ,aAAa,CAAM,CAC7B,QAAU,CACR,EAAU,CACZ,CACF,CAEA,OAAO,EAAW,SAAS,CAC7B,EAEM,EAAO,GAA0B,CACrC,GAAI,EAAU,MAAO,GACrB,IAAM,EAAO,EAAW,IAAI,CAAK,EAIjC,OAFA,EAAU,EAEH,CACT,EAEM,EAAY,GAAyC,CACzD,GAAI,EAAU,MAAO,GACrB,IAAM,EAAS,EAAW,SAAS,CAAM,EAEzC,OAAO,EAAY,CAAM,CAC3B,EAEM,MAAoB,CACpB,IACJ,EAAW,MAAM,EACjB,EAAU,EACZ,EAEM,EAAiB,GAAkC,CACvD,GAAI,EAAU,MAAO,GAErB,IAAM,EAAS,EAAW,cAAc,CAAK,EAG7C,OAFK,GACD,EAAO,QAAQ,EAAY,EAAO,MAAM,EACrC,EAAO,SAFM,EAGtB,EAEM,MAAsB,CACtB,IAEJ,EAAW,GACX,EAAW,MAAM,EACjB,EAAa,MAAQ,GACrB,EAAmB,MAAM,EACzB,IAAsB,EACtB,EAAsB,IAAA,GACxB,EASA,OAPI,EAAQ,QAAQ,QAClB,EAAQ,EACC,EAAQ,SACjB,EAAQ,QAAQ,iBAAiB,QAAS,EAAS,CAAE,KAAM,EAAK,CAAC,EACjE,MAA4B,EAAQ,QAAQ,oBAAoB,QAAS,CAAO,GAG3E,CACL,IAAI,gBAAiB,CACnB,OAAO,EAAmB,MAC5B,EACA,UACA,IAAI,UAAW,CACb,OAAO,CACT,EACA,eACA,kBAAsB,EAAW,IAAA,GAAY,EAAW,cAAc,EACtE,gBACA,WACA,QACA,OACC,OAAO,SAAU,CACpB,CACF"}
|
package/dist/core/nav.d.ts
CHANGED
|
@@ -2,9 +2,13 @@ import { type ListNavigationOptions as FocusListNavigationOptions, type ListKeyA
|
|
|
2
2
|
import { type Readable } from '@vielzeug/ripple';
|
|
3
3
|
export type ListNavigationOptions<T> = Omit<FocusListNavigationOptions<T>, 'disabled'> & {
|
|
4
4
|
disabled?: Readable<boolean | undefined>;
|
|
5
|
+
onNavigate?: (change: ListNavigationChange<T>) => void;
|
|
6
|
+
signal?: AbortSignal;
|
|
5
7
|
};
|
|
8
|
+
export type { ListKeyAction, ListNavigationAction, ListNavigationChange, ListNavigationTypeaheadOptions };
|
|
6
9
|
export type ListControl<T> = {
|
|
7
10
|
[Symbol.dispose](): void;
|
|
11
|
+
readonly disposalSignal: AbortSignal;
|
|
8
12
|
dispose(): void;
|
|
9
13
|
readonly disposed: boolean;
|
|
10
14
|
readonly focusedIndex: Readable<number>;
|
|
@@ -14,6 +18,5 @@ export type ListControl<T> = {
|
|
|
14
18
|
reset(): void;
|
|
15
19
|
set(index: number): number;
|
|
16
20
|
};
|
|
17
|
-
export type { ListKeyAction, ListNavigationAction, ListNavigationChange, ListNavigationTypeaheadOptions };
|
|
18
21
|
export declare const createListControl: <T>(options: ListNavigationOptions<T>) => ListControl<T>;
|
|
19
22
|
//# sourceMappingURL=nav.d.ts.map
|
package/dist/core/nav.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../../src/core/nav.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,qBAAqB,IAAI,0BAA0B,EACxD,KAAK,aAAa,EAElB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,8BAA8B,EACpC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,QAAQ,EAAU,MAAM,kBAAkB,CAAC;AAEzD,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG;IACvF,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"nav.d.ts","sourceRoot":"","sources":["../../src/core/nav.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,qBAAqB,IAAI,0BAA0B,EACxD,KAAK,aAAa,EAElB,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,8BAA8B,EACpC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,QAAQ,EAAU,MAAM,kBAAkB,CAAC;AAEzD,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG;IACvF,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;IACzC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IACvD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,8BAA8B,EAAE,CAAC;AAE1G,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;IAChB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxC,aAAa,IAAI,CAAC,GAAG,SAAS,CAAC;IAC/B,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC;IAC7C,QAAQ,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAAC;IAC/C,KAAK,IAAI,IAAI,CAAC;IACd,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,CAAC,EAAE,SAAS,qBAAqB,CAAC,CAAC,CAAC,KAAG,WAAW,CAAC,CAAC,CAkGrF,CAAC"}
|
package/dist/core/nav.js
CHANGED
|
@@ -2,46 +2,59 @@ import { signal as e } from "@vielzeug/ripple";
|
|
|
2
2
|
import { createListNavigation as t } from "@vielzeug/focus";
|
|
3
3
|
//#region src/core/nav.ts
|
|
4
4
|
var n = (n) => {
|
|
5
|
-
let r = e(-1),
|
|
5
|
+
let r = new AbortController(), i = e(-1), a = !1, o, s = t({
|
|
6
6
|
direction: n.direction,
|
|
7
7
|
disabled: () => !!n.disabled?.value,
|
|
8
8
|
getItems: n.getItems,
|
|
9
9
|
isItemDisabled: n.isItemDisabled,
|
|
10
10
|
keys: n.keys,
|
|
11
11
|
loop: n.loop,
|
|
12
|
-
onNavigate: (e) => {
|
|
13
|
-
r.value = e.index, n.onNavigate?.(e);
|
|
14
|
-
},
|
|
15
12
|
orientation: n.orientation,
|
|
16
13
|
typeahead: n.typeahead
|
|
17
|
-
}),
|
|
18
|
-
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
14
|
+
}), c = () => {
|
|
15
|
+
i.value = s.getIndex();
|
|
16
|
+
}, l = (e) => {
|
|
17
|
+
if (e) {
|
|
18
|
+
i.value = e.index;
|
|
19
|
+
try {
|
|
20
|
+
n.onNavigate?.(e);
|
|
21
|
+
} finally {
|
|
22
|
+
c();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return s.getIndex();
|
|
27
26
|
}, u = (e) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
if (a) return -1;
|
|
28
|
+
let t = s.set(e);
|
|
29
|
+
return c(), t;
|
|
30
|
+
}, d = (e) => {
|
|
31
|
+
if (a) return -1;
|
|
32
|
+
let t = s.navigate(e);
|
|
33
|
+
return l(t);
|
|
34
|
+
}, f = () => {
|
|
35
|
+
a || (s.reset(), c());
|
|
36
|
+
}, p = (e) => {
|
|
37
|
+
if (a) return !1;
|
|
38
|
+
let t = s.handleKeydown(e);
|
|
39
|
+
return t ? (t.change && l(t.change), t.handled) : !1;
|
|
40
|
+
}, m = () => {
|
|
41
|
+
a || (a = !0, s.reset(), i.value = -1, r.abort(), o?.(), o = void 0);
|
|
32
42
|
};
|
|
33
|
-
return n.signal?.aborted ?
|
|
34
|
-
|
|
43
|
+
return n.signal?.aborted ? m() : n.signal && (n.signal?.addEventListener("abort", m, { once: !0 }), o = () => n.signal?.removeEventListener("abort", m)), {
|
|
44
|
+
get disposalSignal() {
|
|
45
|
+
return r.signal;
|
|
46
|
+
},
|
|
47
|
+
dispose: m,
|
|
35
48
|
get disposed() {
|
|
36
|
-
return a
|
|
49
|
+
return a;
|
|
37
50
|
},
|
|
38
|
-
focusedIndex:
|
|
39
|
-
getActiveItem: () => a.getActiveItem(),
|
|
40
|
-
handleKeydown:
|
|
41
|
-
navigate:
|
|
42
|
-
reset:
|
|
43
|
-
set:
|
|
44
|
-
[Symbol.dispose]:
|
|
51
|
+
focusedIndex: i,
|
|
52
|
+
getActiveItem: () => a ? void 0 : s.getActiveItem(),
|
|
53
|
+
handleKeydown: p,
|
|
54
|
+
navigate: d,
|
|
55
|
+
reset: f,
|
|
56
|
+
set: u,
|
|
57
|
+
[Symbol.dispose]: m
|
|
45
58
|
};
|
|
46
59
|
};
|
|
47
60
|
//#endregion
|
package/dist/core/nav.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nav.js","names":[],"sources":["../../src/core/nav.ts"],"sourcesContent":["import {\n createListNavigation,\n type ListNavigationOptions as FocusListNavigationOptions,\n type ListKeyAction,\n type ListNavigation,\n type ListNavigationAction,\n type ListNavigationChange,\n type ListNavigationTypeaheadOptions,\n} from '@vielzeug/focus';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nexport type ListNavigationOptions<T> = Omit<FocusListNavigationOptions<T>, 'disabled'> & {\n disabled?: Readable<boolean | undefined>;\n};\n\nexport type ListControl<T> = {\n [Symbol.dispose](): void;\n dispose(): void;\n readonly disposed: boolean;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n navigate(action: ListNavigationAction): number;\n reset(): void;\n set(index: number): number;\n};\n\nexport
|
|
1
|
+
{"version":3,"file":"nav.js","names":[],"sources":["../../src/core/nav.ts"],"sourcesContent":["import {\n createListNavigation,\n type ListNavigationOptions as FocusListNavigationOptions,\n type ListKeyAction,\n type ListNavigation,\n type ListNavigationAction,\n type ListNavigationChange,\n type ListNavigationTypeaheadOptions,\n} from '@vielzeug/focus';\nimport { type Readable, signal } from '@vielzeug/ripple';\n\nexport type ListNavigationOptions<T> = Omit<FocusListNavigationOptions<T>, 'disabled'> & {\n disabled?: Readable<boolean | undefined>;\n onNavigate?: (change: ListNavigationChange<T>) => void;\n signal?: AbortSignal;\n};\n\nexport type { ListKeyAction, ListNavigationAction, ListNavigationChange, ListNavigationTypeaheadOptions };\n\nexport type ListControl<T> = {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n readonly focusedIndex: Readable<number>;\n getActiveItem(): T | undefined;\n handleKeydown(event: KeyboardEvent): boolean;\n navigate(action: ListNavigationAction): number;\n reset(): void;\n set(index: number): number;\n};\n\nexport const createListControl = <T>(options: ListNavigationOptions<T>): ListControl<T> => {\n const disposalController = new AbortController();\n const focusedIndex = signal(-1);\n let disposed = false;\n let removeAbortListener: (() => void) | undefined;\n const navigation: ListNavigation<T> = createListNavigation<T>({\n direction: options.direction,\n disabled: () => Boolean(options.disabled?.value),\n getItems: options.getItems,\n isItemDisabled: options.isItemDisabled,\n keys: options.keys,\n loop: options.loop,\n orientation: options.orientation,\n typeahead: options.typeahead,\n });\n\n const syncIndex = (): void => {\n focusedIndex.value = navigation.getIndex();\n };\n\n const applyChange = (change: ListNavigationChange<T> | null): number => {\n if (change) {\n focusedIndex.value = change.index;\n try {\n options.onNavigate?.(change);\n } finally {\n syncIndex();\n }\n }\n\n return navigation.getIndex();\n };\n\n const set = (index: number): number => {\n if (disposed) return -1;\n const next = navigation.set(index);\n\n syncIndex();\n\n return next;\n };\n\n const navigate = (action: ListNavigationAction): number => {\n if (disposed) return -1;\n const change = navigation.navigate(action);\n\n return applyChange(change);\n };\n\n const reset = (): void => {\n if (disposed) return;\n navigation.reset();\n syncIndex();\n };\n\n const handleKeydown = (event: KeyboardEvent): boolean => {\n if (disposed) return false;\n\n const result = navigation.handleKeydown(event);\n if (!result) return false;\n if (result.change) applyChange(result.change);\n return result.handled;\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n navigation.reset();\n focusedIndex.value = -1;\n disposalController.abort();\n removeAbortListener?.();\n removeAbortListener = undefined;\n };\n\n if (options.signal?.aborted) {\n dispose();\n } else if (options.signal) {\n options.signal?.addEventListener('abort', dispose, { once: true });\n removeAbortListener = () => options.signal?.removeEventListener('abort', dispose);\n }\n\n return {\n get disposalSignal() {\n return disposalController.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n focusedIndex,\n getActiveItem: () => (disposed ? undefined : navigation.getActiveItem()),\n handleKeydown,\n navigate,\n reset,\n set,\n [Symbol.dispose]: dispose,\n };\n};\n"],"mappings":";;;AAgCA,IAAa,KAAwB,MAAsD;CACzF,IAAM,IAAqB,IAAI,gBAAgB,GACzC,IAAe,EAAO,EAAE,GAC1B,IAAW,IACX,GACE,IAAgC,EAAwB;EAC5D,WAAW,EAAQ;EACnB,gBAAgB,EAAQ,EAAQ,UAAU;EAC1C,UAAU,EAAQ;EAClB,gBAAgB,EAAQ;EACxB,MAAM,EAAQ;EACd,MAAM,EAAQ;EACd,aAAa,EAAQ;EACrB,WAAW,EAAQ;CACrB,CAAC,GAEK,UAAwB;EAC5B,EAAa,QAAQ,EAAW,SAAS;CAC3C,GAEM,KAAe,MAAmD;EACtE,IAAI,GAAQ;GACV,EAAa,QAAQ,EAAO;GAC5B,IAAI;IACF,EAAQ,aAAa,CAAM;GAC7B,UAAU;IACR,EAAU;GACZ;EACF;EAEA,OAAO,EAAW,SAAS;CAC7B,GAEM,KAAO,MAA0B;EACrC,IAAI,GAAU,OAAO;EACrB,IAAM,IAAO,EAAW,IAAI,CAAK;EAIjC,OAFA,EAAU,GAEH;CACT,GAEM,KAAY,MAAyC;EACzD,IAAI,GAAU,OAAO;EACrB,IAAM,IAAS,EAAW,SAAS,CAAM;EAEzC,OAAO,EAAY,CAAM;CAC3B,GAEM,UAAoB;EACpB,MACJ,EAAW,MAAM,GACjB,EAAU;CACZ,GAEM,KAAiB,MAAkC;EACvD,IAAI,GAAU,OAAO;EAErB,IAAM,IAAS,EAAW,cAAc,CAAK;EAG7C,OAFK,KACD,EAAO,UAAQ,EAAY,EAAO,MAAM,GACrC,EAAO,WAFM;CAGtB,GAEM,UAAsB;EACtB,MAEJ,IAAW,IACX,EAAW,MAAM,GACjB,EAAa,QAAQ,IACrB,EAAmB,MAAM,GACzB,IAAsB,GACtB,IAAsB,KAAA;CACxB;CASA,OAPI,EAAQ,QAAQ,UAClB,EAAQ,IACC,EAAQ,WACjB,EAAQ,QAAQ,iBAAiB,SAAS,GAAS,EAAE,MAAM,GAAK,CAAC,GACjE,UAA4B,EAAQ,QAAQ,oBAAoB,SAAS,CAAO,IAG3E;EACL,IAAI,iBAAiB;GACnB,OAAO,EAAmB;EAC5B;EACA;EACA,IAAI,WAAW;GACb,OAAO;EACT;EACA;EACA,qBAAsB,IAAW,KAAA,IAAY,EAAW,cAAc;EACtE;EACA;EACA;EACA;GACC,OAAO,UAAU;CACpB;AACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../errors.cjs"),t=require("./overlay.cjs"),n=require("./nav.cjs"),r=require("./positioner.cjs");let i=require("@vielzeug/ripple"),a=require("@vielzeug/focus");var o=o=>{if(typeof o.getBoundary!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getBoundary is required`);if(typeof o.getPanel!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getPanel is required`);if(typeof o.getReference!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getReference is required`);let s=(0,i.signal)(!1),c=(0,i.computed)(()=>String(s.value)),l=null,
|
|
1
|
+
const e=require("../errors.cjs"),t=require("./overlay.cjs"),n=require("./nav.cjs"),r=require("./positioner.cjs");let i=require("@vielzeug/ripple"),a=require("@vielzeug/focus");var o=o=>{if(typeof o.getBoundary!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getBoundary is required`);if(typeof o.getPanel!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getPanel is required`);if(typeof o.getReference!=`function`)throw new e.RefineConfigError(`createListboxDropdown: getReference is required`);let s=(0,i.signal)(!1),c=(0,i.computed)(()=>String(s.value)),l=!1,u=null,d=r.createDropdownPositioner({...o.positioning,getFloating:o.getPanel,getReference:o.getReference}),f=()=>{l||o.getFocusedOptionElement?.()?.scrollIntoView({block:`nearest`})},p=n.createListControl({disabled:(0,i.computed)(()=>!s.value),getItems:o.getItems,isItemDisabled:o.isItemDisabled,keys:o.keys,loop:o.loop??!0,onNavigate:e=>{f(),o.onNavigate?.(e)},orientation:o.orientation,signal:o.signal,typeahead:o.typeahead}),m=(0,i.computed)(()=>{let e=p.focusedIndex.value;return o.getOptionId&&s.value&&e>=0?o.getOptionId(e):null}),h=()=>typeof o.restoreFocus==`function`?o.restoreFocus():o.restoreFocus??!0,g=(e=`programmatic`,t=!0)=>{!l&&s.value&&(s.value=!1,p.reset(),u?.(),u=null,t&&h()&&o.getTrigger&&(0,a.restoreFocus)(o.getTrigger),o.onClose?.(e))},_=(e=`programmatic`)=>{l||o.isDisabled?.()||s.value||(s.value=!0,d.update(),u=d.startAutoUpdate?.()??null,o.onOpen?.(e))},v=(e=`click`,t=`trigger`)=>{l||(s.value?g(t):_(e))};t.createOutsidePointerDismissal({getTargets:()=>[o.getBoundary(),o.getPanel()],isActive:()=>s.value,onDismiss:()=>g(`outsideClick`),signal:o.signal});let y=()=>{l||(l=!0,p.dispose(),s.value&&(s.value=!1,p.reset(),u?.(),u=null))};return o.signal.addEventListener(`abort`,y,{once:!0}),{ariaActiveDescendant:m,ariaExpanded:c,close:g,dispose:y,get disposed(){return l},focusedIndex:p.focusedIndex,getActiveItem:p.getActiveItem,handleKeydown:e=>l||!s.value?!1:e.key===`Escape`?(e.preventDefault(),g(`escape`),!0):p.handleKeydown(e),isOpen:s,navigate:p.navigate,open:_,scrollFocusedIntoView:f,set:p.set,[Symbol.dispose]:y,toggle:v,updatePosition:()=>{l||d.update()}}};exports.createListboxDropdown=o;
|
|
2
2
|
//# sourceMappingURL=option-list.cjs.map
|