@vielzeug/refine 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chip.cjs +9 -9
- package/dist/chip.cjs.map +1 -1
- package/dist/chip.js +38 -35
- package/dist/chip.js.map +1 -1
- package/dist/command-palette.cjs +5 -5
- package/dist/command-palette.cjs.map +1 -1
- package/dist/command-palette.js +5 -5
- 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/index.d.ts +4 -0
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/keyboard-key/keyboard-key.css.cjs +2 -0
- package/dist/content/keyboard-key/keyboard-key.css.cjs.map +1 -0
- package/dist/content/keyboard-key/keyboard-key.css.js +6 -0
- package/dist/content/keyboard-key/keyboard-key.css.js.map +1 -0
- package/dist/content/keyboard-key/keyboard-key.d.ts +12 -0
- package/dist/content/keyboard-key/keyboard-key.d.ts.map +1 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.cjs +2 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.cjs.map +1 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.js +6 -0
- package/dist/content/keyboard-key/keyboard-shortcut.css.js.map +1 -0
- package/dist/content/qr-code/qr-code.css.cjs +2 -0
- package/dist/content/qr-code/qr-code.css.cjs.map +1 -0
- package/dist/content/qr-code/qr-code.css.js +6 -0
- package/dist/content/qr-code/qr-code.css.js.map +1 -0
- package/dist/content/qr-code/qr-code.d.ts +74 -0
- package/dist/content/qr-code/qr-code.d.ts.map +1 -0
- package/dist/counter.cjs +91 -0
- package/dist/counter.cjs.map +1 -0
- package/dist/counter.js +180 -0
- package/dist/counter.js.map +1 -0
- package/dist/custom-elements.json +4183 -3462
- 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/feedback/chip/chip.css.cjs +1 -1
- package/dist/feedback/chip/chip.css.cjs.map +1 -1
- package/dist/feedback/chip/chip.css.js +1 -1
- package/dist/feedback/chip/chip.css.js.map +1 -1
- package/dist/feedback/chip/chip.d.ts +6 -2
- package/dist/feedback/chip/chip.d.ts.map +1 -1
- package/dist/feedback/progress/progress.css.cjs +1 -1
- package/dist/feedback/progress/progress.css.cjs.map +1 -1
- package/dist/feedback/progress/progress.css.js +1 -1
- package/dist/feedback/progress/progress.css.js.map +1 -1
- package/dist/feedback/progress/progress.d.ts +6 -0
- package/dist/feedback/progress/progress.d.ts.map +1 -1
- package/dist/frameworks/elements.d.ts +1 -1
- package/dist/inputs/button/button.css.cjs +1 -1
- package/dist/inputs/button/button.css.cjs.map +1 -1
- package/dist/inputs/button/button.css.js +1 -1
- package/dist/inputs/button/button.css.js.map +1 -1
- package/dist/inputs/counter/counter.css.cjs +2 -0
- package/dist/inputs/counter/counter.css.cjs.map +1 -0
- package/dist/inputs/counter/counter.css.js +6 -0
- package/dist/inputs/counter/counter.css.js.map +1 -0
- package/dist/inputs/counter/counter.d.ts +100 -0
- package/dist/inputs/counter/counter.d.ts.map +1 -0
- package/dist/inputs/index.d.ts +4 -0
- package/dist/inputs/index.d.ts.map +1 -1
- package/dist/inputs/qr-scanner/qr-scanner.css.cjs +2 -0
- package/dist/inputs/qr-scanner/qr-scanner.css.cjs.map +1 -0
- package/dist/inputs/qr-scanner/qr-scanner.css.js +6 -0
- package/dist/inputs/qr-scanner/qr-scanner.css.js.map +1 -0
- package/dist/inputs/qr-scanner/qr-scanner.d.ts +87 -0
- package/dist/inputs/qr-scanner/qr-scanner.d.ts.map +1 -0
- package/dist/keyboard-key.cjs +9 -0
- package/dist/keyboard-key.cjs.map +1 -0
- package/dist/keyboard-key.js +36 -0
- package/dist/keyboard-key.js.map +1 -0
- package/dist/menu.cjs +1 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +2 -1
- package/dist/menu.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.css.cjs +1 -1
- package/dist/overlay/command-palette/command-palette.css.cjs.map +1 -1
- package/dist/overlay/command-palette/command-palette.css.js +1 -1
- package/dist/overlay/command-palette/command-palette.css.js.map +1 -1
- package/dist/overlay/command-palette/command-palette.d.ts +1 -0
- package/dist/overlay/command-palette/command-palette.d.ts.map +1 -1
- package/dist/progress.cjs +14 -6
- package/dist/progress.cjs.map +1 -1
- package/dist/progress.js +19 -7
- package/dist/progress.js.map +1 -1
- package/dist/qr-code.cjs +15 -0
- package/dist/qr-code.cjs.map +1 -0
- package/dist/qr-code.js +70 -0
- package/dist/qr-code.js.map +1 -0
- package/dist/qr-scanner.cjs +46 -0
- package/dist/qr-scanner.cjs.map +1 -0
- package/dist/qr-scanner.js +129 -0
- package/dist/qr-scanner.js.map +1 -0
- package/dist/refine.cjs +327 -166
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +355 -194
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +298 -137
- package/dist/refine.js.map +1 -1
- package/dist/tabs.cjs +2 -2
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +37 -34
- package/dist/tabs.js.map +1 -1
- package/dist/types/elements.d.ts +9 -0
- package/package.json +34 -1
package/dist/chip.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/shape.css.cjs"),t=require("./styles/mixins/states.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./feedback/chip/chip.css.cjs");let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./styles/mixins/shape.css.cjs"),t=require("./styles/mixins/states.css.cjs"),n=require("./styles/mixins/theme.css.cjs"),r=require("./styles/mixins/variants.css.cjs"),i=require("./feedback/chip/chip.css.cjs");let a=require("@vielzeug/ore"),o=require("@vielzeug/ripple");var s=`ore-chip`;(0,a.define)(s,{props:{checked:{default:void 0,parse:e=>e==null?void 0:e!==`false`,reflect:!1},color:a.prop.string(),"default-checked":a.prop.bool(!1),disabled:a.prop.bool(!1),label:a.prop.string(),layout:a.prop.oneOf([`inline`,`stacked`],`inline`),mode:a.prop.oneOf([`static`,`removable`,`selectable`,`action`],`static`),rounded:a.prop.string(),size:a.prop.string(),value:a.prop.string(),variant:a.prop.string()},setup(e){let t=(0,a.useEmit)(),n=(0,o.signal)(e.checked.value!==void 0),r=(0,o.signal)(!n.value&&e[`default-checked`].value);(0,o.watch)(e.checked,e=>{e!==void 0&&(n.value=!0)});let i=(0,o.computed)(()=>e.mode.value===`selectable`?n.value?e.checked.value??!1:r.value:!1);(0,a.bind)({attr:{checked:()=>e.mode.value===`selectable`&&i.value?!0:void 0}});function s(n){e.disabled.value||t(`remove`,{originalEvent:n,value:e.value.value})}function c(a){if(a.stopPropagation(),e.disabled.value)return;let o=!i.value;n.value||(r.value=o),t(`change`,{checked:o,originalEvent:a,value:e.value.value})}function l(n){e.disabled.value||t(`click`,{originalEvent:n,value:e.value.value})}let u=()=>a.html`
|
|
2
2
|
<slot name="icon"></slot>
|
|
3
3
|
<span class="label"><slot></slot></span>
|
|
4
|
-
`,d=()=>{let t=e.label.value||e.value.value;return t?`Remove ${t}`:`Remove`},f=()=>
|
|
4
|
+
`,d=()=>{let t=e.label.value||e.value.value;return t?`Remove ${t}`:`Remove`},f=()=>a.html`
|
|
5
5
|
<button
|
|
6
6
|
class="remove-btn"
|
|
7
7
|
part="remove-btn"
|
|
@@ -12,19 +12,19 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
12
12
|
@click="${s}">
|
|
13
13
|
<ore-icon name="x" size="12" stroke-width="2.5" aria-hidden="true"></ore-icon>
|
|
14
14
|
</button>
|
|
15
|
-
`,p=()=>
|
|
15
|
+
`,p=()=>a.html`
|
|
16
16
|
<button
|
|
17
17
|
class="chip-btn"
|
|
18
18
|
part="chip-btn"
|
|
19
19
|
type="button"
|
|
20
20
|
role="checkbox"
|
|
21
|
-
aria-checked="${()=>String(
|
|
21
|
+
aria-checked="${()=>String(i.value)}"
|
|
22
22
|
aria-label="${e.label}"
|
|
23
23
|
disabled="${e.disabled}"
|
|
24
24
|
@click="${c}">
|
|
25
25
|
<span class="chip" part="chip">${u()}</span>
|
|
26
26
|
</button>
|
|
27
|
-
`,m=()=>
|
|
27
|
+
`,m=()=>a.html`
|
|
28
28
|
<button
|
|
29
29
|
class="chip-btn"
|
|
30
30
|
part="chip-btn"
|
|
@@ -34,11 +34,11 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
34
34
|
@click="${l}">
|
|
35
35
|
<span class="chip" part="chip">${u()}</span>
|
|
36
36
|
</button>
|
|
37
|
-
`,h=()=>
|
|
37
|
+
`,h=()=>a.html`
|
|
38
38
|
<span class="chip" part="chip">${u()}</span>
|
|
39
|
-
`,g=()=>
|
|
39
|
+
`,g=()=>a.html`
|
|
40
40
|
<span class="chip" part="chip">${u()} ${f()}</span>
|
|
41
|
-
`;return
|
|
41
|
+
`;return a.html`
|
|
42
42
|
${()=>{let t=e.mode.value;return t===`selectable`?p():t===`action`?m():t===`removable`?g():h()}}
|
|
43
|
-
`},styles:[n.colorThemeMixin,t.disabledStateMixin,e.roundedVariantMixin,e.sizeVariantMixin({lg:{"--_font-size":`var(--text-sm)`,"--_gap":`var(--size-1-5)`,"--_padding-x":`var(--size-3)`,"--_padding-y":`var(--size-1)`},sm:{"--_font-size":`var(--text-xs)`,"--_gap":`var(--size-0-5)`,"--_padding-x":`var(--size-2-5)`,"--_padding-y":`var(--size-px)`}}),n.forcedColorsMixin,
|
|
43
|
+
`},styles:[n.colorThemeMixin,t.disabledStateMixin,e.roundedVariantMixin,e.sizeVariantMixin({lg:{"--_font-size":`var(--text-sm)`,"--_gap":`var(--size-1-5)`,"--_padding-x":`var(--size-3)`,"--_padding-y":`var(--size-1)`},sm:{"--_font-size":`var(--text-xs)`,"--_gap":`var(--size-0-5)`,"--_padding-x":`var(--size-2-5)`,"--_padding-y":`var(--size-px)`}}),r.frostVariantMixin(`.chip`),n.forcedColorsMixin,i.default]}),exports.CHIP_TAG=s;
|
|
44
44
|
//# sourceMappingURL=chip.cjs.map
|
package/dist/chip.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chip.cjs","names":[],"sources":["../src/feedback/chip/chip.ts"],"sourcesContent":["import { bind, define, html, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\n\nimport type { ComponentSize, RoundedSize, ThemeColor, VisualVariant } from '../../types';\n\nimport '../../content/icon/icon';\nimport {\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\n// ============================================\n// Styles\n// ============================================\nimport componentStyles from './chip.css?inline';\n\n// ============================================\n// Types\n// ============================================\n\n/** Chip component properties */\ntype ChipBaseProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interactions */\n disabled?: boolean;\n /** Accessible label (required for icon-only chips) */\n label?: string;\n /** Border radius override */\n rounded?: RoundedSize | '';\n /** Component size */\n size?: ComponentSize;\n /** Value associated with this chip — included in emitted event detail */\n value?: string;\n /** Visual style variant */\n variant?: Exclude<VisualVariant, 'text' | 'frost'>;\n};\n\ntype OreChipMode = 'static' | 'removable' | 'selectable' | 'action';\n\n/** Read-only presentation chip */\ntype StaticChipProps = {\n mode?: Extract<OreChipMode, 'static'>;\n};\n\n/** Removable chip mode */\ntype RemovableChipProps = {\n mode: Extract<OreChipMode, 'removable'>;\n};\n\n/** Selectable chip mode */\ntype SelectableChipProps = {\n /** Controlled checked state for `mode=\"selectable\"` */\n checked?: boolean | undefined;\n /** Initial checked state for uncontrolled `mode=\"selectable\"` */\n 'default-checked'?: boolean;\n mode: Extract<OreChipMode, 'selectable'>;\n};\n\n/** Action chip mode — behaves like a button, fires a click event without maintaining state */\ntype ActionChipProps = {\n mode: Extract<OreChipMode, 'action'>;\n};\n\ntype OreChipComponentProps = ChipBaseProps & {\n checked?: boolean | undefined;\n 'default-checked'?: boolean;\n mode?: OreChipMode;\n};\n\nexport type OreChipEvents = {\n change: { checked: boolean; originalEvent: Event; value: string | undefined };\n click: { originalEvent: MouseEvent; value: string | undefined };\n remove: { originalEvent: MouseEvent; value: string | undefined };\n};\n\nexport type OreChipProps = ChipBaseProps &\n (StaticChipProps | RemovableChipProps | SelectableChipProps | ActionChipProps);\n\n/**\n * A compact, styled label element. Supports icons, a remove button, colors, sizes, and variants.\n * Commonly used to represent tags, filters, or selected options in a multiselect field.\n *\n * @element ore-chip\n *\n * @attr {string} label - Accessible label (required for icon-only chips)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} mode - Interaction mode: 'static' | 'removable' | 'selectable' | 'action'\n * @attr {boolean} disabled - Disable the chip\n * @attr {string} value - Value included in emitted event detail\n * @attr {boolean} checked - Controlled checked state for selectable chips\n * @attr {boolean} default-checked - Initial checked state for uncontrolled selectable chips\n *\n * @fires remove - Fired when the remove button is clicked. detail: { value: string, originalEvent: MouseEvent }\n * @fires change - Fired when a selectable chip toggles. detail: { checked: boolean, value: string, originalEvent: Event }\n * @fires click - Fired when an action chip is clicked. detail: { value: string, originalEvent: MouseEvent }\n *\n * @slot - Chip label text\n * @slot icon - Leading icon or decoration\n *\n * @cssprop --chip-bg - Background color\n * @cssprop --chip-color - Text color\n * @cssprop --chip-border-color - Border color\n * @cssprop --chip-radius - Border radius\n * @cssprop --chip-font-size - Font size\n * @cssprop --chip-font-weight - Font weight\n * @cssprop --chip-padding-x - Horizontal padding\n * @cssprop --chip-padding-y - Vertical padding\n * @cssprop --chip-gap - Gap between icon, label and remove button\n * @cssprop --chip-hover-bg - Background on hover (interactive modes)\n * @cssprop --chip-hover-color - Text color on hover (interactive modes)\n * @cssprop --chip-hover-border-color - Border color on hover (interactive modes)\n * @cssprop --chip-active-bg - Background when pressed (interactive modes)\n * @cssprop --chip-active-color - Text color when pressed (interactive modes)\n * @cssprop --chip-remove-bg - Remove button background (bordered/outline variants)\n * @cssprop --chip-remove-color - Remove button icon color (bordered/outline variants)\n * @cssprop --chip-remove-hover-bg - Remove button background on hover\n *\n * @part remove-btn - Remove action button.\n * @part chip-btn - Shadow part for the `chip-btn` element.\n * @part chip - Shadow part for the `chip` element.\n * @example\n * ```html\n * <!-- Static chip (read-only) -->\n * <ore-chip color=\"primary\">Design</ore-chip>\n *\n * <!-- Removable chip -->\n * <ore-chip color=\"success\" variant=\"flat\" mode=\"removable\" value=\"ts\">\n * TypeScript\n * </ore-chip>\n *\n * <!-- Selectable chip (controlled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Selectable chip (uncontrolled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" default-checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Action chip (acts like a button) -->\n * <ore-chip color=\"warning\" mode=\"action\" value=\"add\">\n * <svg slot=\"icon\" ...></svg>\n * Add Item\n * </ore-chip>\n *\n * <!-- Icon-only chip -->\n * <ore-chip color=\"error\" mode=\"action\" label=\"Delete\">\n * <svg slot=\"icon\" ...></svg>\n * </ore-chip>\n * ```\n */\nexport const CHIP_TAG = 'ore-chip' as const;\ndefine<OreChipComponentProps>(CHIP_TAG, {\n props: {\n checked: {\n default: undefined as OreChipComponentProps['checked'],\n parse: (value: string | null) => (value == null ? undefined : value !== 'false'),\n reflect: false,\n },\n color: prop.string<ThemeColor>(),\n 'default-checked': prop.bool(false),\n disabled: prop.bool(false),\n label: prop.string(),\n mode: prop.oneOf(['static', 'removable', 'selectable', 'action'] as const, 'static'),\n rounded: prop.string<RoundedSize | ''>(),\n size: prop.string<ComponentSize>(),\n value: prop.string(),\n variant: prop.string<Exclude<VisualVariant, 'text' | 'frost'>>(),\n },\n\n setup(props) {\n const emit = useEmit<OreChipEvents>();\n\n // ============================================\n // State Management\n // ============================================\n // Once a checked prop is provided, treat the chip as controlled for the rest of its lifecycle.\n const isControlled = signal(props.checked.value !== undefined);\n // Internal tracking for uncontrolled selectable chips; seeded from default-checked.\n const checkedState = signal(!isControlled.value && props['default-checked'].value);\n\n watch(props.checked, (value) => {\n if (value !== undefined) {\n isControlled.value = true;\n }\n });\n\n // Effective checked value — reactive to checked prop changes in controlled mode.\n const isChecked = computed(() => {\n if (props.mode.value !== 'selectable') return false;\n\n if (isControlled.value) {\n return props.checked.value ?? false;\n }\n\n return checkedState.value;\n });\n\n bind({\n attr: {\n checked: () => (props.mode.value === 'selectable' && isChecked.value ? true : undefined),\n },\n });\n // ============================================\n // Event Handlers\n // ============================================\n function handleRemove(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('remove', { originalEvent: e, value: props.value.value });\n }\n\n function handleSelectableActivate(e: MouseEvent) {\n e.stopPropagation();\n\n if (props.disabled.value) return;\n\n const nextChecked = !isChecked.value;\n\n if (!isControlled.value) {\n checkedState.value = nextChecked;\n }\n\n emit('change', { checked: nextChecked, originalEvent: e, value: props.value.value });\n }\n\n function handleActionClick(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('click', { originalEvent: e, value: props.value.value });\n }\n\n // ============================================\n // Template Helpers\n // ============================================\n const renderChipContent = () => html`\n <slot name=\"icon\"></slot>\n <span class=\"label\"><slot></slot></span>\n `;\n\n const removeBtnLabel = () => {\n const label = props.label.value || props.value.value;\n\n return label ? `Remove ${label}` : 'Remove';\n };\n\n const renderRemoveButton = () => html`\n <button\n class=\"remove-btn\"\n part=\"remove-btn\"\n type=\"button\"\n aria-label=\"${removeBtnLabel}\"\n ?hidden=\"${() => props.mode.value !== 'removable'}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleRemove}\">\n <ore-icon name=\"x\" size=\"12\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n `;\n\n const renderSelectableChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n role=\"checkbox\"\n aria-checked=\"${() => String(isChecked.value)}\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleSelectableActivate}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderActionChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleActionClick}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderStaticChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n `;\n\n const renderRemovableChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()} ${renderRemoveButton()}</span>\n `;\n\n // ============================================\n // Render\n // ============================================\n return html`\n ${() => {\n const mode = props.mode.value;\n\n if (mode === 'selectable') return renderSelectableChip();\n\n if (mode === 'action') return renderActionChip();\n\n if (mode === 'removable') return renderRemovableChip();\n\n return renderStaticChip();\n }}\n `;\n },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n roundedVariantMixin,\n sizeVariantMixin({\n lg: {\n '--_font-size': 'var(--text-sm)',\n '--_gap': 'var(--size-1-5)',\n '--_padding-x': 'var(--size-3)',\n '--_padding-y': 'var(--size-1)',\n },\n sm: {\n '--_font-size': 'var(--text-xs)',\n '--_gap': 'var(--size-0-5)',\n '--_padding-x': 'var(--size-2-5)',\n '--_padding-y': 'var(--size-px)',\n },\n }),\n forcedColorsMixin,\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chip.cjs","names":[],"sources":["../src/feedback/chip/chip.ts"],"sourcesContent":["import { bind, define, html, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\n\nimport type { ComponentSize, RoundedSize, ThemeColor, VisualVariant } from '../../types';\n\nimport '../../content/icon/icon';\nimport {\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsMixin,\n frostVariantMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\n// ============================================\n// Styles\n// ============================================\nimport componentStyles from './chip.css?inline';\n\n// ============================================\n// Types\n// ============================================\n\n/** Chip component properties */\ntype ChipBaseProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interactions */\n disabled?: boolean;\n /** Accessible label (required for icon-only chips) */\n label?: string;\n /** Content layout: 'inline' (default) keeps icon and label on one line; 'stacked' places the icon above a wrapping label */\n layout?: 'inline' | 'stacked';\n /** Border radius override */\n rounded?: RoundedSize | '';\n /** Component size */\n size?: ComponentSize;\n /** Value associated with this chip — included in emitted event detail */\n value?: string;\n /** Visual style variant */\n variant?: Exclude<VisualVariant, 'text'>;\n};\n\ntype OreChipMode = 'static' | 'removable' | 'selectable' | 'action';\n\n/** Read-only presentation chip */\ntype StaticChipProps = {\n mode?: Extract<OreChipMode, 'static'>;\n};\n\n/** Removable chip mode */\ntype RemovableChipProps = {\n mode: Extract<OreChipMode, 'removable'>;\n};\n\n/** Selectable chip mode */\ntype SelectableChipProps = {\n /** Controlled checked state for `mode=\"selectable\"` */\n checked?: boolean | undefined;\n /** Initial checked state for uncontrolled `mode=\"selectable\"` */\n 'default-checked'?: boolean;\n mode: Extract<OreChipMode, 'selectable'>;\n};\n\n/** Action chip mode — behaves like a button, fires a click event without maintaining state */\ntype ActionChipProps = {\n mode: Extract<OreChipMode, 'action'>;\n};\n\ntype OreChipComponentProps = ChipBaseProps & {\n checked?: boolean | undefined;\n 'default-checked'?: boolean;\n mode?: OreChipMode;\n};\n\nexport type OreChipEvents = {\n change: { checked: boolean; originalEvent: Event; value: string | undefined };\n click: { originalEvent: MouseEvent; value: string | undefined };\n remove: { originalEvent: MouseEvent; value: string | undefined };\n};\n\nexport type OreChipProps = ChipBaseProps &\n (StaticChipProps | RemovableChipProps | SelectableChipProps | ActionChipProps);\n\n/**\n * A compact, styled label element. Supports icons, a remove button, colors, sizes, and variants.\n * Commonly used to represent tags, filters, or selected options in a multiselect field.\n *\n * @element ore-chip\n *\n * @attr {string} label - Accessible label (required for icon-only chips)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'frost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} mode - Interaction mode: 'static' | 'removable' | 'selectable' | 'action'\n * @attr {string} layout - Content layout: 'inline' (default) | 'stacked' — stacked places the icon above a wrapping label, for tile-like selectable chips\n * @attr {boolean} disabled - Disable the chip\n * @attr {string} value - Value included in emitted event detail\n * @attr {boolean} checked - Controlled checked state for selectable chips\n * @attr {boolean} default-checked - Initial checked state for uncontrolled selectable chips\n *\n * @fires remove - Fired when the remove button is clicked. detail: { value: string, originalEvent: MouseEvent }\n * @fires change - Fired when a selectable chip toggles. detail: { checked: boolean, value: string, originalEvent: Event }\n * @fires click - Fired when an action chip is clicked. detail: { value: string, originalEvent: MouseEvent }\n *\n * @slot - Chip label text\n * @slot icon - Leading icon or decoration\n *\n * @cssprop --chip-bg - Background color\n * @cssprop --chip-color - Text color\n * @cssprop --chip-border-color - Border color\n * @cssprop --chip-radius - Border radius\n * @cssprop --chip-font-size - Font size\n * @cssprop --chip-font-weight - Font weight\n * @cssprop --chip-padding-x - Horizontal padding\n * @cssprop --chip-padding-y - Vertical padding\n * @cssprop --chip-gap - Gap between icon, label and remove button\n * @cssprop --chip-icon-size - Size of the slotted icon (default: 1em)\n * @cssprop --chip-hover-bg - Background on hover (interactive modes)\n * @cssprop --chip-hover-color - Text color on hover (interactive modes)\n * @cssprop --chip-hover-border-color - Border color on hover (interactive modes)\n * @cssprop --chip-active-bg - Background when pressed (interactive modes)\n * @cssprop --chip-active-color - Text color when pressed (interactive modes)\n * @cssprop --chip-remove-bg - Remove button background (bordered/outline variants)\n * @cssprop --chip-remove-color - Remove button icon color (bordered/outline variants)\n * @cssprop --chip-remove-hover-bg - Remove button background on hover\n *\n * @part remove-btn - Remove action button.\n * @part chip-btn - Shadow part for the `chip-btn` element.\n * @part chip - Shadow part for the `chip` element.\n * @example\n * ```html\n * <!-- Static chip (read-only) -->\n * <ore-chip color=\"primary\">Design</ore-chip>\n *\n * <!-- Removable chip -->\n * <ore-chip color=\"success\" variant=\"flat\" mode=\"removable\" value=\"ts\">\n * TypeScript\n * </ore-chip>\n *\n * <!-- Selectable chip (controlled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Selectable chip (uncontrolled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" default-checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Action chip (acts like a button) -->\n * <ore-chip color=\"warning\" mode=\"action\" value=\"add\">\n * <svg slot=\"icon\" ...></svg>\n * Add Item\n * </ore-chip>\n *\n * <!-- Icon-only chip -->\n * <ore-chip color=\"error\" mode=\"action\" label=\"Delete\">\n * <svg slot=\"icon\" ...></svg>\n * </ore-chip>\n * ```\n */\nexport const CHIP_TAG = 'ore-chip' as const;\ndefine<OreChipComponentProps>(CHIP_TAG, {\n props: {\n checked: {\n default: undefined as OreChipComponentProps['checked'],\n parse: (value: string | null) => (value == null ? undefined : value !== 'false'),\n reflect: false,\n },\n color: prop.string<ThemeColor>(),\n 'default-checked': prop.bool(false),\n disabled: prop.bool(false),\n label: prop.string(),\n layout: prop.oneOf(['inline', 'stacked'] as const, 'inline'),\n mode: prop.oneOf(['static', 'removable', 'selectable', 'action'] as const, 'static'),\n rounded: prop.string<RoundedSize | ''>(),\n size: prop.string<ComponentSize>(),\n value: prop.string(),\n variant: prop.string<Exclude<VisualVariant, 'text'>>(),\n },\n\n setup(props) {\n const emit = useEmit<OreChipEvents>();\n\n // ============================================\n // State Management\n // ============================================\n // Once a checked prop is provided, treat the chip as controlled for the rest of its lifecycle.\n const isControlled = signal(props.checked.value !== undefined);\n // Internal tracking for uncontrolled selectable chips; seeded from default-checked.\n const checkedState = signal(!isControlled.value && props['default-checked'].value);\n\n watch(props.checked, (value) => {\n if (value !== undefined) {\n isControlled.value = true;\n }\n });\n\n // Effective checked value — reactive to checked prop changes in controlled mode.\n const isChecked = computed(() => {\n if (props.mode.value !== 'selectable') return false;\n\n if (isControlled.value) {\n return props.checked.value ?? false;\n }\n\n return checkedState.value;\n });\n\n bind({\n attr: {\n checked: () => (props.mode.value === 'selectable' && isChecked.value ? true : undefined),\n },\n });\n // ============================================\n // Event Handlers\n // ============================================\n function handleRemove(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('remove', { originalEvent: e, value: props.value.value });\n }\n\n function handleSelectableActivate(e: MouseEvent) {\n e.stopPropagation();\n\n if (props.disabled.value) return;\n\n const nextChecked = !isChecked.value;\n\n if (!isControlled.value) {\n checkedState.value = nextChecked;\n }\n\n emit('change', { checked: nextChecked, originalEvent: e, value: props.value.value });\n }\n\n function handleActionClick(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('click', { originalEvent: e, value: props.value.value });\n }\n\n // ============================================\n // Template Helpers\n // ============================================\n const renderChipContent = () => html`\n <slot name=\"icon\"></slot>\n <span class=\"label\"><slot></slot></span>\n `;\n\n const removeBtnLabel = () => {\n const label = props.label.value || props.value.value;\n\n return label ? `Remove ${label}` : 'Remove';\n };\n\n const renderRemoveButton = () => html`\n <button\n class=\"remove-btn\"\n part=\"remove-btn\"\n type=\"button\"\n aria-label=\"${removeBtnLabel}\"\n ?hidden=\"${() => props.mode.value !== 'removable'}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleRemove}\">\n <ore-icon name=\"x\" size=\"12\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n `;\n\n const renderSelectableChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n role=\"checkbox\"\n aria-checked=\"${() => String(isChecked.value)}\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleSelectableActivate}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderActionChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleActionClick}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderStaticChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n `;\n\n const renderRemovableChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()} ${renderRemoveButton()}</span>\n `;\n\n // ============================================\n // Render\n // ============================================\n return html`\n ${() => {\n const mode = props.mode.value;\n\n if (mode === 'selectable') return renderSelectableChip();\n\n if (mode === 'action') return renderActionChip();\n\n if (mode === 'removable') return renderRemovableChip();\n\n return renderStaticChip();\n }}\n `;\n },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n roundedVariantMixin,\n sizeVariantMixin({\n lg: {\n '--_font-size': 'var(--text-sm)',\n '--_gap': 'var(--size-1-5)',\n '--_padding-x': 'var(--size-3)',\n '--_padding-y': 'var(--size-1)',\n },\n sm: {\n '--_font-size': 'var(--text-xs)',\n '--_gap': 'var(--size-0-5)',\n '--_padding-x': 'var(--size-2-5)',\n '--_padding-y': 'var(--size-px)',\n },\n }),\n frostVariantMixin('.chip'),\n forcedColorsMixin,\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
package/dist/chip.js
CHANGED
|
@@ -1,47 +1,49 @@
|
|
|
1
1
|
import { roundedVariantMixin as e, sizeVariantMixin as t } from "./styles/mixins/shape.css.js";
|
|
2
2
|
import { disabledStateMixin as n } from "./styles/mixins/states.css.js";
|
|
3
3
|
import { colorThemeMixin as r, forcedColorsMixin as i } from "./styles/mixins/theme.css.js";
|
|
4
|
-
import a from "./
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
4
|
+
import { frostVariantMixin as a } from "./styles/mixins/variants.css.js";
|
|
5
|
+
import o from "./feedback/chip/chip.css.js";
|
|
6
|
+
import { bind as s, define as c, html as l, prop as u, useEmit as d } from "@vielzeug/ore";
|
|
7
|
+
import { computed as f, signal as p, watch as m } from "@vielzeug/ripple";
|
|
7
8
|
//#region src/feedback/chip/chip.ts
|
|
8
|
-
var
|
|
9
|
-
|
|
9
|
+
var h = "ore-chip";
|
|
10
|
+
c(h, {
|
|
10
11
|
props: {
|
|
11
12
|
checked: {
|
|
12
13
|
default: void 0,
|
|
13
14
|
parse: (e) => e == null ? void 0 : e !== "false",
|
|
14
15
|
reflect: !1
|
|
15
16
|
},
|
|
16
|
-
color:
|
|
17
|
-
"default-checked":
|
|
18
|
-
disabled:
|
|
19
|
-
label:
|
|
20
|
-
|
|
17
|
+
color: u.string(),
|
|
18
|
+
"default-checked": u.bool(!1),
|
|
19
|
+
disabled: u.bool(!1),
|
|
20
|
+
label: u.string(),
|
|
21
|
+
layout: u.oneOf(["inline", "stacked"], "inline"),
|
|
22
|
+
mode: u.oneOf([
|
|
21
23
|
"static",
|
|
22
24
|
"removable",
|
|
23
25
|
"selectable",
|
|
24
26
|
"action"
|
|
25
27
|
], "static"),
|
|
26
|
-
rounded:
|
|
27
|
-
size:
|
|
28
|
-
value:
|
|
29
|
-
variant:
|
|
28
|
+
rounded: u.string(),
|
|
29
|
+
size: u.string(),
|
|
30
|
+
value: u.string(),
|
|
31
|
+
variant: u.string()
|
|
30
32
|
},
|
|
31
33
|
setup(e) {
|
|
32
|
-
let t =
|
|
33
|
-
|
|
34
|
+
let t = d(), n = p(e.checked.value !== void 0), r = p(!n.value && e["default-checked"].value);
|
|
35
|
+
m(e.checked, (e) => {
|
|
34
36
|
e !== void 0 && (n.value = !0);
|
|
35
37
|
});
|
|
36
|
-
let i =
|
|
37
|
-
|
|
38
|
+
let i = f(() => e.mode.value === "selectable" ? n.value ? e.checked.value ?? !1 : r.value : !1);
|
|
39
|
+
s({ attr: { checked: () => e.mode.value === "selectable" && i.value ? !0 : void 0 } });
|
|
38
40
|
function a(n) {
|
|
39
41
|
e.disabled.value || t("remove", {
|
|
40
42
|
originalEvent: n,
|
|
41
43
|
value: e.value.value
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
|
-
function
|
|
46
|
+
function o(a) {
|
|
45
47
|
if (a.stopPropagation(), e.disabled.value) return;
|
|
46
48
|
let o = !i.value;
|
|
47
49
|
n.value || (r.value = o), t("change", {
|
|
@@ -50,19 +52,19 @@ s(m, {
|
|
|
50
52
|
value: e.value.value
|
|
51
53
|
});
|
|
52
54
|
}
|
|
53
|
-
function
|
|
55
|
+
function c(n) {
|
|
54
56
|
e.disabled.value || t("click", {
|
|
55
57
|
originalEvent: n,
|
|
56
58
|
value: e.value.value
|
|
57
59
|
});
|
|
58
60
|
}
|
|
59
|
-
let
|
|
61
|
+
let u = () => l`
|
|
60
62
|
<slot name="icon"></slot>
|
|
61
63
|
<span class="label"><slot></slot></span>
|
|
62
64
|
`, h = () => {
|
|
63
65
|
let t = e.label.value || e.value.value;
|
|
64
66
|
return t ? `Remove ${t}` : "Remove";
|
|
65
|
-
}, g = () =>
|
|
67
|
+
}, g = () => l`
|
|
66
68
|
<button
|
|
67
69
|
class="remove-btn"
|
|
68
70
|
part="remove-btn"
|
|
@@ -73,7 +75,7 @@ s(m, {
|
|
|
73
75
|
@click="${a}">
|
|
74
76
|
<ore-icon name="x" size="12" stroke-width="2.5" aria-hidden="true"></ore-icon>
|
|
75
77
|
</button>
|
|
76
|
-
`, _ = () =>
|
|
78
|
+
`, _ = () => l`
|
|
77
79
|
<button
|
|
78
80
|
class="chip-btn"
|
|
79
81
|
part="chip-btn"
|
|
@@ -82,25 +84,25 @@ s(m, {
|
|
|
82
84
|
aria-checked="${() => String(i.value)}"
|
|
83
85
|
aria-label="${e.label}"
|
|
84
86
|
disabled="${e.disabled}"
|
|
85
|
-
@click="${
|
|
86
|
-
<span class="chip" part="chip">${
|
|
87
|
+
@click="${o}">
|
|
88
|
+
<span class="chip" part="chip">${u()}</span>
|
|
87
89
|
</button>
|
|
88
|
-
`, v = () =>
|
|
90
|
+
`, v = () => l`
|
|
89
91
|
<button
|
|
90
92
|
class="chip-btn"
|
|
91
93
|
part="chip-btn"
|
|
92
94
|
type="button"
|
|
93
95
|
aria-label="${e.label}"
|
|
94
96
|
disabled="${e.disabled}"
|
|
95
|
-
@click="${
|
|
96
|
-
<span class="chip" part="chip">${
|
|
97
|
+
@click="${c}">
|
|
98
|
+
<span class="chip" part="chip">${u()}</span>
|
|
97
99
|
</button>
|
|
98
|
-
`, y = () =>
|
|
99
|
-
<span class="chip" part="chip">${
|
|
100
|
-
`, b = () =>
|
|
101
|
-
<span class="chip" part="chip">${
|
|
100
|
+
`, y = () => l`
|
|
101
|
+
<span class="chip" part="chip">${u()}</span>
|
|
102
|
+
`, b = () => l`
|
|
103
|
+
<span class="chip" part="chip">${u()} ${g()}</span>
|
|
102
104
|
`;
|
|
103
|
-
return
|
|
105
|
+
return l`
|
|
104
106
|
${() => {
|
|
105
107
|
let t = e.mode.value;
|
|
106
108
|
return t === "selectable" ? _() : t === "action" ? v() : t === "removable" ? b() : y();
|
|
@@ -125,11 +127,12 @@ s(m, {
|
|
|
125
127
|
"--_padding-y": "var(--size-px)"
|
|
126
128
|
}
|
|
127
129
|
}),
|
|
130
|
+
a(".chip"),
|
|
128
131
|
i,
|
|
129
|
-
|
|
132
|
+
o
|
|
130
133
|
]
|
|
131
134
|
});
|
|
132
135
|
//#endregion
|
|
133
|
-
export {
|
|
136
|
+
export { h as CHIP_TAG };
|
|
134
137
|
|
|
135
138
|
//# sourceMappingURL=chip.js.map
|
package/dist/chip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chip.js","names":[],"sources":["../src/feedback/chip/chip.ts"],"sourcesContent":["import { bind, define, html, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\n\nimport type { ComponentSize, RoundedSize, ThemeColor, VisualVariant } from '../../types';\n\nimport '../../content/icon/icon';\nimport {\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\n// ============================================\n// Styles\n// ============================================\nimport componentStyles from './chip.css?inline';\n\n// ============================================\n// Types\n// ============================================\n\n/** Chip component properties */\ntype ChipBaseProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interactions */\n disabled?: boolean;\n /** Accessible label (required for icon-only chips) */\n label?: string;\n /** Border radius override */\n rounded?: RoundedSize | '';\n /** Component size */\n size?: ComponentSize;\n /** Value associated with this chip — included in emitted event detail */\n value?: string;\n /** Visual style variant */\n variant?: Exclude<VisualVariant, 'text' | 'frost'>;\n};\n\ntype OreChipMode = 'static' | 'removable' | 'selectable' | 'action';\n\n/** Read-only presentation chip */\ntype StaticChipProps = {\n mode?: Extract<OreChipMode, 'static'>;\n};\n\n/** Removable chip mode */\ntype RemovableChipProps = {\n mode: Extract<OreChipMode, 'removable'>;\n};\n\n/** Selectable chip mode */\ntype SelectableChipProps = {\n /** Controlled checked state for `mode=\"selectable\"` */\n checked?: boolean | undefined;\n /** Initial checked state for uncontrolled `mode=\"selectable\"` */\n 'default-checked'?: boolean;\n mode: Extract<OreChipMode, 'selectable'>;\n};\n\n/** Action chip mode — behaves like a button, fires a click event without maintaining state */\ntype ActionChipProps = {\n mode: Extract<OreChipMode, 'action'>;\n};\n\ntype OreChipComponentProps = ChipBaseProps & {\n checked?: boolean | undefined;\n 'default-checked'?: boolean;\n mode?: OreChipMode;\n};\n\nexport type OreChipEvents = {\n change: { checked: boolean; originalEvent: Event; value: string | undefined };\n click: { originalEvent: MouseEvent; value: string | undefined };\n remove: { originalEvent: MouseEvent; value: string | undefined };\n};\n\nexport type OreChipProps = ChipBaseProps &\n (StaticChipProps | RemovableChipProps | SelectableChipProps | ActionChipProps);\n\n/**\n * A compact, styled label element. Supports icons, a remove button, colors, sizes, and variants.\n * Commonly used to represent tags, filters, or selected options in a multiselect field.\n *\n * @element ore-chip\n *\n * @attr {string} label - Accessible label (required for icon-only chips)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} mode - Interaction mode: 'static' | 'removable' | 'selectable' | 'action'\n * @attr {boolean} disabled - Disable the chip\n * @attr {string} value - Value included in emitted event detail\n * @attr {boolean} checked - Controlled checked state for selectable chips\n * @attr {boolean} default-checked - Initial checked state for uncontrolled selectable chips\n *\n * @fires remove - Fired when the remove button is clicked. detail: { value: string, originalEvent: MouseEvent }\n * @fires change - Fired when a selectable chip toggles. detail: { checked: boolean, value: string, originalEvent: Event }\n * @fires click - Fired when an action chip is clicked. detail: { value: string, originalEvent: MouseEvent }\n *\n * @slot - Chip label text\n * @slot icon - Leading icon or decoration\n *\n * @cssprop --chip-bg - Background color\n * @cssprop --chip-color - Text color\n * @cssprop --chip-border-color - Border color\n * @cssprop --chip-radius - Border radius\n * @cssprop --chip-font-size - Font size\n * @cssprop --chip-font-weight - Font weight\n * @cssprop --chip-padding-x - Horizontal padding\n * @cssprop --chip-padding-y - Vertical padding\n * @cssprop --chip-gap - Gap between icon, label and remove button\n * @cssprop --chip-hover-bg - Background on hover (interactive modes)\n * @cssprop --chip-hover-color - Text color on hover (interactive modes)\n * @cssprop --chip-hover-border-color - Border color on hover (interactive modes)\n * @cssprop --chip-active-bg - Background when pressed (interactive modes)\n * @cssprop --chip-active-color - Text color when pressed (interactive modes)\n * @cssprop --chip-remove-bg - Remove button background (bordered/outline variants)\n * @cssprop --chip-remove-color - Remove button icon color (bordered/outline variants)\n * @cssprop --chip-remove-hover-bg - Remove button background on hover\n *\n * @part remove-btn - Remove action button.\n * @part chip-btn - Shadow part for the `chip-btn` element.\n * @part chip - Shadow part for the `chip` element.\n * @example\n * ```html\n * <!-- Static chip (read-only) -->\n * <ore-chip color=\"primary\">Design</ore-chip>\n *\n * <!-- Removable chip -->\n * <ore-chip color=\"success\" variant=\"flat\" mode=\"removable\" value=\"ts\">\n * TypeScript\n * </ore-chip>\n *\n * <!-- Selectable chip (controlled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Selectable chip (uncontrolled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" default-checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Action chip (acts like a button) -->\n * <ore-chip color=\"warning\" mode=\"action\" value=\"add\">\n * <svg slot=\"icon\" ...></svg>\n * Add Item\n * </ore-chip>\n *\n * <!-- Icon-only chip -->\n * <ore-chip color=\"error\" mode=\"action\" label=\"Delete\">\n * <svg slot=\"icon\" ...></svg>\n * </ore-chip>\n * ```\n */\nexport const CHIP_TAG = 'ore-chip' as const;\ndefine<OreChipComponentProps>(CHIP_TAG, {\n props: {\n checked: {\n default: undefined as OreChipComponentProps['checked'],\n parse: (value: string | null) => (value == null ? undefined : value !== 'false'),\n reflect: false,\n },\n color: prop.string<ThemeColor>(),\n 'default-checked': prop.bool(false),\n disabled: prop.bool(false),\n label: prop.string(),\n mode: prop.oneOf(['static', 'removable', 'selectable', 'action'] as const, 'static'),\n rounded: prop.string<RoundedSize | ''>(),\n size: prop.string<ComponentSize>(),\n value: prop.string(),\n variant: prop.string<Exclude<VisualVariant, 'text' | 'frost'>>(),\n },\n\n setup(props) {\n const emit = useEmit<OreChipEvents>();\n\n // ============================================\n // State Management\n // ============================================\n // Once a checked prop is provided, treat the chip as controlled for the rest of its lifecycle.\n const isControlled = signal(props.checked.value !== undefined);\n // Internal tracking for uncontrolled selectable chips; seeded from default-checked.\n const checkedState = signal(!isControlled.value && props['default-checked'].value);\n\n watch(props.checked, (value) => {\n if (value !== undefined) {\n isControlled.value = true;\n }\n });\n\n // Effective checked value — reactive to checked prop changes in controlled mode.\n const isChecked = computed(() => {\n if (props.mode.value !== 'selectable') return false;\n\n if (isControlled.value) {\n return props.checked.value ?? false;\n }\n\n return checkedState.value;\n });\n\n bind({\n attr: {\n checked: () => (props.mode.value === 'selectable' && isChecked.value ? true : undefined),\n },\n });\n // ============================================\n // Event Handlers\n // ============================================\n function handleRemove(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('remove', { originalEvent: e, value: props.value.value });\n }\n\n function handleSelectableActivate(e: MouseEvent) {\n e.stopPropagation();\n\n if (props.disabled.value) return;\n\n const nextChecked = !isChecked.value;\n\n if (!isControlled.value) {\n checkedState.value = nextChecked;\n }\n\n emit('change', { checked: nextChecked, originalEvent: e, value: props.value.value });\n }\n\n function handleActionClick(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('click', { originalEvent: e, value: props.value.value });\n }\n\n // ============================================\n // Template Helpers\n // ============================================\n const renderChipContent = () => html`\n <slot name=\"icon\"></slot>\n <span class=\"label\"><slot></slot></span>\n `;\n\n const removeBtnLabel = () => {\n const label = props.label.value || props.value.value;\n\n return label ? `Remove ${label}` : 'Remove';\n };\n\n const renderRemoveButton = () => html`\n <button\n class=\"remove-btn\"\n part=\"remove-btn\"\n type=\"button\"\n aria-label=\"${removeBtnLabel}\"\n ?hidden=\"${() => props.mode.value !== 'removable'}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleRemove}\">\n <ore-icon name=\"x\" size=\"12\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n `;\n\n const renderSelectableChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n role=\"checkbox\"\n aria-checked=\"${() => String(isChecked.value)}\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleSelectableActivate}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderActionChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleActionClick}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderStaticChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n `;\n\n const renderRemovableChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()} ${renderRemoveButton()}</span>\n `;\n\n // ============================================\n // Render\n // ============================================\n return html`\n ${() => {\n const mode = props.mode.value;\n\n if (mode === 'selectable') return renderSelectableChip();\n\n if (mode === 'action') return renderActionChip();\n\n if (mode === 'removable') return renderRemovableChip();\n\n return renderStaticChip();\n }}\n `;\n },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n roundedVariantMixin,\n sizeVariantMixin({\n lg: {\n '--_font-size': 'var(--text-sm)',\n '--_gap': 'var(--size-1-5)',\n '--_padding-x': 'var(--size-3)',\n '--_padding-y': 'var(--size-1)',\n },\n sm: {\n '--_font-size': 'var(--text-xs)',\n '--_gap': 'var(--size-0-5)',\n '--_padding-x': 'var(--size-2-5)',\n '--_padding-y': 'var(--size-px)',\n },\n }),\n forcedColorsMixin,\n componentStyles,\n ],\n});\n"],"mappings":";;;;;;;AA8JA,IAAa,IAAW;AACxB,EAA8B,GAAU;CACtC,OAAO;EACL,SAAS;GACP,SAAS,KAAA;GACT,QAAQ,MAA0B,KAAS,OAAO,KAAA,IAAY,MAAU;GACxE,SAAS;EACX;EACA,OAAO,EAAK,OAAmB;EAC/B,mBAAmB,EAAK,KAAK,EAAK;EAClC,UAAU,EAAK,KAAK,EAAK;EACzB,OAAO,EAAK,OAAO;EACnB,MAAM,EAAK,MAAM;GAAC;GAAU;GAAa;GAAc;EAAQ,GAAY,QAAQ;EACnF,SAAS,EAAK,OAAyB;EACvC,MAAM,EAAK,OAAsB;EACjC,OAAO,EAAK,OAAO;EACnB,SAAS,EAAK,OAAiD;CACjE;CAEA,MAAM,GAAO;EACX,IAAM,IAAO,EAAuB,GAM9B,IAAe,EAAO,EAAM,QAAQ,UAAU,KAAA,CAAS,GAEvD,IAAe,EAAO,CAAC,EAAa,SAAS,EAAM,kBAAkB,CAAC,KAAK;EAEjF,EAAM,EAAM,UAAU,MAAU;GAC9B,AAAI,MAAU,KAAA,MACZ,EAAa,QAAQ;EAEzB,CAAC;EAGD,IAAM,IAAY,QACZ,EAAM,KAAK,UAAU,eAErB,EAAa,QACR,EAAM,QAAQ,SAAS,KAGzB,EAAa,QAN0B,EAO/C;EAED,EAAK,EACH,MAAM,EACJ,eAAgB,EAAM,KAAK,UAAU,gBAAgB,EAAU,QAAQ,KAAO,KAAA,EAChF,EACF,CAAC;EAID,SAAS,EAAa,GAAe;GAC/B,EAAM,SAAS,SAEnB,EAAK,UAAU;IAAE,eAAe;IAAG,OAAO,EAAM,MAAM;GAAM,CAAC;EAC/D;EAEA,SAAS,EAAyB,GAAe;GAG/C,IAFA,EAAE,gBAAgB,GAEd,EAAM,SAAS,OAAO;GAE1B,IAAM,IAAc,CAAC,EAAU;GAM/B,AAJK,EAAa,UAChB,EAAa,QAAQ,IAGvB,EAAK,UAAU;IAAE,SAAS;IAAa,eAAe;IAAG,OAAO,EAAM,MAAM;GAAM,CAAC;EACrF;EAEA,SAAS,EAAkB,GAAe;GACpC,EAAM,SAAS,SAEnB,EAAK,SAAS;IAAE,eAAe;IAAG,OAAO,EAAM,MAAM;GAAM,CAAC;EAC9D;EAKA,IAAM,UAA0B,CAAI;;;OAK9B,UAAuB;GAC3B,IAAM,IAAQ,EAAM,MAAM,SAAS,EAAM,MAAM;GAE/C,OAAO,IAAQ,UAAU,MAAU;EACrC,GAEM,UAA2B,CAAI;;;;;sBAKnB,EAAe;yBACZ,EAAM,KAAK,UAAU,YAAY;oBACtC,EAAM,SAAS;kBACjB,EAAa;;;OAKrB,UAA6B,CAAI;;;;;;8BAMb,OAAO,EAAU,KAAK,EAAE;sBAChC,EAAM,MAAM;oBACd,EAAM,SAAS;kBACjB,EAAyB;yCACF,EAAkB,EAAE;;OAInD,UAAyB,CAAI;;;;;sBAKjB,EAAM,MAAM;oBACd,EAAM,SAAS;kBACjB,EAAkB;yCACK,EAAkB,EAAE;;OAInD,UAAyB,CAAI;uCACA,EAAkB,EAAE;OAGjD,UAA4B,CAAI;uCACH,EAAkB,EAAE,GAAG,EAAmB,EAAE;;EAM/E,OAAO,CAAI;cACD;GACN,IAAM,IAAO,EAAM,KAAK;GAQxB,OANI,MAAS,eAAqB,EAAqB,IAEnD,MAAS,WAAiB,EAAiB,IAE3C,MAAS,cAAoB,EAAoB,IAE9C,EAAiB;EAC1B,EAAE;;CAEN;CACA,QAAQ;EACN;EACA;EACA;EACA,EAAiB;GACf,IAAI;IACF,gBAAgB;IAChB,UAAU;IACV,gBAAgB;IAChB,gBAAgB;GAClB;GACA,IAAI;IACF,gBAAgB;IAChB,UAAU;IACV,gBAAgB;IAChB,gBAAgB;GAClB;EACF,CAAC;EACD;EACA;CACF;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"chip.js","names":[],"sources":["../src/feedback/chip/chip.ts"],"sourcesContent":["import { bind, define, html, prop, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\n\nimport type { ComponentSize, RoundedSize, ThemeColor, VisualVariant } from '../../types';\n\nimport '../../content/icon/icon';\nimport {\n colorThemeMixin,\n disabledStateMixin,\n forcedColorsMixin,\n frostVariantMixin,\n roundedVariantMixin,\n sizeVariantMixin,\n} from '../../styles';\n// ============================================\n// Styles\n// ============================================\nimport componentStyles from './chip.css?inline';\n\n// ============================================\n// Types\n// ============================================\n\n/** Chip component properties */\ntype ChipBaseProps = {\n /** Theme color */\n color?: ThemeColor;\n /** Disable interactions */\n disabled?: boolean;\n /** Accessible label (required for icon-only chips) */\n label?: string;\n /** Content layout: 'inline' (default) keeps icon and label on one line; 'stacked' places the icon above a wrapping label */\n layout?: 'inline' | 'stacked';\n /** Border radius override */\n rounded?: RoundedSize | '';\n /** Component size */\n size?: ComponentSize;\n /** Value associated with this chip — included in emitted event detail */\n value?: string;\n /** Visual style variant */\n variant?: Exclude<VisualVariant, 'text'>;\n};\n\ntype OreChipMode = 'static' | 'removable' | 'selectable' | 'action';\n\n/** Read-only presentation chip */\ntype StaticChipProps = {\n mode?: Extract<OreChipMode, 'static'>;\n};\n\n/** Removable chip mode */\ntype RemovableChipProps = {\n mode: Extract<OreChipMode, 'removable'>;\n};\n\n/** Selectable chip mode */\ntype SelectableChipProps = {\n /** Controlled checked state for `mode=\"selectable\"` */\n checked?: boolean | undefined;\n /** Initial checked state for uncontrolled `mode=\"selectable\"` */\n 'default-checked'?: boolean;\n mode: Extract<OreChipMode, 'selectable'>;\n};\n\n/** Action chip mode — behaves like a button, fires a click event without maintaining state */\ntype ActionChipProps = {\n mode: Extract<OreChipMode, 'action'>;\n};\n\ntype OreChipComponentProps = ChipBaseProps & {\n checked?: boolean | undefined;\n 'default-checked'?: boolean;\n mode?: OreChipMode;\n};\n\nexport type OreChipEvents = {\n change: { checked: boolean; originalEvent: Event; value: string | undefined };\n click: { originalEvent: MouseEvent; value: string | undefined };\n remove: { originalEvent: MouseEvent; value: string | undefined };\n};\n\nexport type OreChipProps = ChipBaseProps &\n (StaticChipProps | RemovableChipProps | SelectableChipProps | ActionChipProps);\n\n/**\n * A compact, styled label element. Supports icons, a remove button, colors, sizes, and variants.\n * Commonly used to represent tags, filters, or selected options in a multiselect field.\n *\n * @element ore-chip\n *\n * @attr {string} label - Accessible label (required for icon-only chips)\n * @attr {string} color - Theme color: 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'\n * @attr {string} variant - Visual variant: 'solid' | 'flat' | 'bordered' | 'outline' | 'ghost' | 'frost'\n * @attr {string} size - Component size: 'sm' | 'md' | 'lg'\n * @attr {string} rounded - Border radius: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full'\n * @attr {string} mode - Interaction mode: 'static' | 'removable' | 'selectable' | 'action'\n * @attr {string} layout - Content layout: 'inline' (default) | 'stacked' — stacked places the icon above a wrapping label, for tile-like selectable chips\n * @attr {boolean} disabled - Disable the chip\n * @attr {string} value - Value included in emitted event detail\n * @attr {boolean} checked - Controlled checked state for selectable chips\n * @attr {boolean} default-checked - Initial checked state for uncontrolled selectable chips\n *\n * @fires remove - Fired when the remove button is clicked. detail: { value: string, originalEvent: MouseEvent }\n * @fires change - Fired when a selectable chip toggles. detail: { checked: boolean, value: string, originalEvent: Event }\n * @fires click - Fired when an action chip is clicked. detail: { value: string, originalEvent: MouseEvent }\n *\n * @slot - Chip label text\n * @slot icon - Leading icon or decoration\n *\n * @cssprop --chip-bg - Background color\n * @cssprop --chip-color - Text color\n * @cssprop --chip-border-color - Border color\n * @cssprop --chip-radius - Border radius\n * @cssprop --chip-font-size - Font size\n * @cssprop --chip-font-weight - Font weight\n * @cssprop --chip-padding-x - Horizontal padding\n * @cssprop --chip-padding-y - Vertical padding\n * @cssprop --chip-gap - Gap between icon, label and remove button\n * @cssprop --chip-icon-size - Size of the slotted icon (default: 1em)\n * @cssprop --chip-hover-bg - Background on hover (interactive modes)\n * @cssprop --chip-hover-color - Text color on hover (interactive modes)\n * @cssprop --chip-hover-border-color - Border color on hover (interactive modes)\n * @cssprop --chip-active-bg - Background when pressed (interactive modes)\n * @cssprop --chip-active-color - Text color when pressed (interactive modes)\n * @cssprop --chip-remove-bg - Remove button background (bordered/outline variants)\n * @cssprop --chip-remove-color - Remove button icon color (bordered/outline variants)\n * @cssprop --chip-remove-hover-bg - Remove button background on hover\n *\n * @part remove-btn - Remove action button.\n * @part chip-btn - Shadow part for the `chip-btn` element.\n * @part chip - Shadow part for the `chip` element.\n * @example\n * ```html\n * <!-- Static chip (read-only) -->\n * <ore-chip color=\"primary\">Design</ore-chip>\n *\n * <!-- Removable chip -->\n * <ore-chip color=\"success\" variant=\"flat\" mode=\"removable\" value=\"ts\">\n * TypeScript\n * </ore-chip>\n *\n * <!-- Selectable chip (controlled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Selectable chip (uncontrolled) -->\n * <ore-chip color=\"info\" variant=\"flat\" mode=\"selectable\" default-checked value=\"ui\">\n * UI\n * </ore-chip>\n *\n * <!-- Action chip (acts like a button) -->\n * <ore-chip color=\"warning\" mode=\"action\" value=\"add\">\n * <svg slot=\"icon\" ...></svg>\n * Add Item\n * </ore-chip>\n *\n * <!-- Icon-only chip -->\n * <ore-chip color=\"error\" mode=\"action\" label=\"Delete\">\n * <svg slot=\"icon\" ...></svg>\n * </ore-chip>\n * ```\n */\nexport const CHIP_TAG = 'ore-chip' as const;\ndefine<OreChipComponentProps>(CHIP_TAG, {\n props: {\n checked: {\n default: undefined as OreChipComponentProps['checked'],\n parse: (value: string | null) => (value == null ? undefined : value !== 'false'),\n reflect: false,\n },\n color: prop.string<ThemeColor>(),\n 'default-checked': prop.bool(false),\n disabled: prop.bool(false),\n label: prop.string(),\n layout: prop.oneOf(['inline', 'stacked'] as const, 'inline'),\n mode: prop.oneOf(['static', 'removable', 'selectable', 'action'] as const, 'static'),\n rounded: prop.string<RoundedSize | ''>(),\n size: prop.string<ComponentSize>(),\n value: prop.string(),\n variant: prop.string<Exclude<VisualVariant, 'text'>>(),\n },\n\n setup(props) {\n const emit = useEmit<OreChipEvents>();\n\n // ============================================\n // State Management\n // ============================================\n // Once a checked prop is provided, treat the chip as controlled for the rest of its lifecycle.\n const isControlled = signal(props.checked.value !== undefined);\n // Internal tracking for uncontrolled selectable chips; seeded from default-checked.\n const checkedState = signal(!isControlled.value && props['default-checked'].value);\n\n watch(props.checked, (value) => {\n if (value !== undefined) {\n isControlled.value = true;\n }\n });\n\n // Effective checked value — reactive to checked prop changes in controlled mode.\n const isChecked = computed(() => {\n if (props.mode.value !== 'selectable') return false;\n\n if (isControlled.value) {\n return props.checked.value ?? false;\n }\n\n return checkedState.value;\n });\n\n bind({\n attr: {\n checked: () => (props.mode.value === 'selectable' && isChecked.value ? true : undefined),\n },\n });\n // ============================================\n // Event Handlers\n // ============================================\n function handleRemove(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('remove', { originalEvent: e, value: props.value.value });\n }\n\n function handleSelectableActivate(e: MouseEvent) {\n e.stopPropagation();\n\n if (props.disabled.value) return;\n\n const nextChecked = !isChecked.value;\n\n if (!isControlled.value) {\n checkedState.value = nextChecked;\n }\n\n emit('change', { checked: nextChecked, originalEvent: e, value: props.value.value });\n }\n\n function handleActionClick(e: MouseEvent) {\n if (props.disabled.value) return;\n\n emit('click', { originalEvent: e, value: props.value.value });\n }\n\n // ============================================\n // Template Helpers\n // ============================================\n const renderChipContent = () => html`\n <slot name=\"icon\"></slot>\n <span class=\"label\"><slot></slot></span>\n `;\n\n const removeBtnLabel = () => {\n const label = props.label.value || props.value.value;\n\n return label ? `Remove ${label}` : 'Remove';\n };\n\n const renderRemoveButton = () => html`\n <button\n class=\"remove-btn\"\n part=\"remove-btn\"\n type=\"button\"\n aria-label=\"${removeBtnLabel}\"\n ?hidden=\"${() => props.mode.value !== 'removable'}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleRemove}\">\n <ore-icon name=\"x\" size=\"12\" stroke-width=\"2.5\" aria-hidden=\"true\"></ore-icon>\n </button>\n `;\n\n const renderSelectableChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n role=\"checkbox\"\n aria-checked=\"${() => String(isChecked.value)}\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleSelectableActivate}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderActionChip = () => html`\n <button\n class=\"chip-btn\"\n part=\"chip-btn\"\n type=\"button\"\n aria-label=\"${props.label}\"\n disabled=\"${props.disabled}\"\n @click=\"${handleActionClick}\">\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n </button>\n `;\n\n const renderStaticChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()}</span>\n `;\n\n const renderRemovableChip = () => html`\n <span class=\"chip\" part=\"chip\">${renderChipContent()} ${renderRemoveButton()}</span>\n `;\n\n // ============================================\n // Render\n // ============================================\n return html`\n ${() => {\n const mode = props.mode.value;\n\n if (mode === 'selectable') return renderSelectableChip();\n\n if (mode === 'action') return renderActionChip();\n\n if (mode === 'removable') return renderRemovableChip();\n\n return renderStaticChip();\n }}\n `;\n },\n styles: [\n colorThemeMixin,\n disabledStateMixin,\n roundedVariantMixin,\n sizeVariantMixin({\n lg: {\n '--_font-size': 'var(--text-sm)',\n '--_gap': 'var(--size-1-5)',\n '--_padding-x': 'var(--size-3)',\n '--_padding-y': 'var(--size-1)',\n },\n sm: {\n '--_font-size': 'var(--text-xs)',\n '--_gap': 'var(--size-0-5)',\n '--_padding-x': 'var(--size-2-5)',\n '--_padding-y': 'var(--size-px)',\n },\n }),\n frostVariantMixin('.chip'),\n forcedColorsMixin,\n componentStyles,\n ],\n});\n"],"mappings":"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"}
|
package/dist/command-palette.cjs
CHANGED
|
@@ -54,7 +54,7 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
54
54
|
${e.item.shortcut?s.html`
|
|
55
55
|
<span class="item-shortcut">
|
|
56
56
|
${a.splitShortcutKeys(e.item.shortcut).map(e=>s.html`
|
|
57
|
-
<
|
|
57
|
+
<ore-keyboard-key>${e}</ore-keyboard-key>
|
|
58
58
|
`)}
|
|
59
59
|
</span>
|
|
60
60
|
`:``}
|
|
@@ -63,16 +63,16 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=requi
|
|
|
63
63
|
</div>
|
|
64
64
|
<div class="footer" part="footer">
|
|
65
65
|
<span class="footer-hint">
|
|
66
|
-
<
|
|
67
|
-
<
|
|
66
|
+
<ore-keyboard-key>↑</ore-keyboard-key>
|
|
67
|
+
<ore-keyboard-key>↓</ore-keyboard-key>
|
|
68
68
|
Navigate
|
|
69
69
|
</span>
|
|
70
70
|
<span class="footer-hint">
|
|
71
|
-
<
|
|
71
|
+
<ore-keyboard-key>Enter</ore-keyboard-key>
|
|
72
72
|
Select
|
|
73
73
|
</span>
|
|
74
74
|
<span class="footer-hint">
|
|
75
|
-
<
|
|
75
|
+
<ore-keyboard-key>Esc</ore-keyboard-key>
|
|
76
76
|
Close
|
|
77
77
|
</span>
|
|
78
78
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-palette.cjs","names":[],"sources":["../src/overlay/command-palette/command-palette.ts"],"sourcesContent":["import { createKeymap } from '@vielzeug/keymap';\nimport { define, getHost, html, onCleanup, onEvent, onMounted, prop, ref, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { warn } from '../../_dev';\nimport { announce, createListControl, lifecycleSignal } from '../../core';\nimport { reducedMotionMixin } from '../../styles';\nimport { useDialogControl } from '../shared/use-dialog';\nimport type { CommandPaletteItem, OreCommandPaletteEvents, OreCommandPaletteProps } from './command-palette.types';\nimport { buildRows, filterItems, normalizeItem, parseSlottedItems, splitShortcutKeys } from './command-palette-items';\nimport '../../content/icon/icon';\nimport componentStyles from './command-palette.css?inline';\n\nexport type { OreCommandPaletteEvents, OreCommandPaletteProps } from './command-palette.types';\n\nconst parseOptionalBool = (value: string | null): boolean | undefined =>\n value == null ? undefined : value === '' || value === 'true';\n\n/**\n * A pure data node describing one command. Never rendered directly — `ore-command-palette`\n * reads its attributes/text content and renders the visible row itself.\n *\n * @element ore-command-palette-item\n *\n * @attr {string} value - Value emitted by the `select` event and matched against the search query\n * @attr {string} label - Explicit label text; falls back to the element's text content\n * @attr {string} group - Group heading the item is clustered under\n * @attr {string} shortcut - Display-only keyboard hint rendered at the end of the row, one `<kbd>` per `+`-separated key (e.g. `\"⌘+S\"` renders two keycaps; a literal `+` key isn't representable — spell it out, e.g. `\"Ctrl+Plus\"`)\n * @attr {boolean} disabled - Excludes the item from keyboard navigation and selection\n *\n * @slot icon - Optional leading icon content\n *\n * @example\n * ```html\n * <ore-command-palette-item value=\"new-file\" group=\"File\" shortcut=\"⌘+N\">New File</ore-command-palette-item>\n * ```\n */\nexport const COMMAND_PALETTE_ITEM_TAG = 'ore-command-palette-item' as const;\ndefine(COMMAND_PALETTE_ITEM_TAG, {\n setup() {\n return html``;\n },\n});\n\n/**\n * A searchable, keyboard-driven list of commands presented in a centered modal —\n * the \"⌘K\" pattern popularized by editors and productivity apps. Built on the\n * native `<dialog>` element (focus trap, top-layer stacking, `Escape`-to-close)\n * and `@vielzeug/keymap` for the global shortcut that opens it.\n *\n * @element ore-command-palette\n * @element ore-command-palette-item - Slotted command definition (place in default slot)\n *\n * @attr {boolean} open - Controls the open state of the palette\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} label - Accessible label for the dialog (screen-reader-only heading)\n * @attr {string} placeholder - Placeholder text for the search input\n * @attr {string} shortcut - Global keyboard shortcut (keymap syntax) that toggles the palette. Default `\"mod+k\"`; set to `\"\"` to disable\n * @attr {boolean} no-filter - Disable built-in client-side filtering (for server-driven search)\n * @attr {boolean} loading - Shows a loading row below the search input\n * @attr {string} empty-text - Message shown when no item matches the query\n * @attr {boolean} keep-open-on-select - Keep the palette open after an item is selected\n *\n * @fires open-change - Fired when the palette state changes. detail: `{ open, reason }`\n * @fires search - Fired on every keystroke in the search input. detail: `{ query }`\n * @fires select - Fired when a command is chosen (click or `Enter`). detail: `{ value, label, item }`\n *\n * @slot - `<ore-command-palette-item>` elements (alternative/supplement to the `items` prop)\n *\n * @cssprop --command-palette-bg - Panel background color\n * @cssprop --command-palette-border-color - Panel border color\n * @cssprop --command-palette-radius - Panel border radius\n * @cssprop --command-palette-shadow - Panel drop shadow\n * @cssprop --command-palette-max-width - Maximum panel width\n * @cssprop --command-palette-backdrop - Backdrop overlay color\n * @cssprop --command-palette-option-hover-bg - Item background on hover\n * @cssprop --command-palette-option-focus-bg - Item background when keyboard-focused\n *\n * @part dialog - Dialog root container\n * @part panel - Panel container\n * @part input - Search input\n * @part listbox - Listbox of matching commands\n *\n * @example\n * ```html\n * <ore-command-palette label=\"Command palette\" placeholder=\"Type a command…\">\n * <ore-command-palette-item value=\"new-file\" group=\"File\" shortcut=\"⌘+N\">New File</ore-command-palette-item>\n * <ore-command-palette-item value=\"open-file\" group=\"File\" shortcut=\"⌘+O\">Open File…</ore-command-palette-item>\n * <ore-command-palette-item value=\"toggle-theme\" group=\"View\">Toggle Theme</ore-command-palette-item>\n * </ore-command-palette>\n * ```\n */\nexport const COMMAND_PALETTE_TAG = 'ore-command-palette' as const;\ndefine<OreCommandPaletteProps>(COMMAND_PALETTE_TAG, {\n props: {\n 'default-open': prop.bool(false),\n 'empty-text': prop.string('No results found.'),\n items: prop.data<OreCommandPaletteProps['items']>(),\n 'keep-open-on-select': prop.bool(false),\n label: prop.string('Command palette'),\n loading: prop.bool(false),\n 'no-filter': prop.bool(false),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placeholder: prop.string('Type a command or search…'),\n shortcut: prop.string('mod+k'),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCommandPaletteEvents>();\n const abortSignal = lifecycleSignal(onCleanup);\n\n const dialogRef = ref<HTMLDialogElement>();\n const searchInputRef = ref<HTMLInputElement>();\n const listboxId = 'cmdk-listbox';\n\n let listboxEl: HTMLElement | null = null;\n\n const query = signal('');\n\n // ── Items: slotted <ore-command-palette-item> elements, merged with the `items` prop ──\n // Items are pure data nodes (see `ore-command-palette-item`'s own doc comment) — they're\n // never projected through a `<slot>`, just read directly off the host's light DOM. That\n // sidesteps `useSlots()`/`slotchange` entirely: a plain MutationObserver on the host\n // already covers everything `slotchange` would (an item added or removed) *and* the case\n // it can't — an already-assigned item's own text/attributes changing. For items written\n // as static, inline HTML, the browser's parser can insert the last item before appending\n // its text-node child; without watching `characterData`, that would permanently cache an\n // empty label, since nothing else re-triggers once the element itself stops changing.\n const slottedItems = signal<CommandPaletteItem[]>([]);\n\n const reparseSlottedItems = (): void => {\n slottedItems.value = parseSlottedItems([...el.children]);\n };\n\n reparseSlottedItems();\n\n const lightDomObserver = new MutationObserver(reparseSlottedItems);\n\n lightDomObserver.observe(el, { characterData: true, childList: true, subtree: true });\n onCleanup(() => lightDomObserver.disconnect());\n\n const allItems = computed<CommandPaletteItem[]>(() => {\n const explicit = props.items.value;\n\n return Array.isArray(explicit) ? explicit.map(normalizeItem) : slottedItems.value;\n });\n\n const filteredItems = computed(() => filterItems(allItems.value, query.value, Boolean(props['no-filter'].value)));\n const rows = computed(() => buildRows(filteredItems.value));\n\n // ── Keyboard navigation over the filtered list (Up/Down/Home/End) ──\n const scrollFocusedIntoView = (): void => {\n listboxEl?.querySelector<HTMLElement>('[data-focused]')?.scrollIntoView({ block: 'nearest' });\n };\n\n const list = createListControl<CommandPaletteItem>({\n getItems: () => filteredItems.value,\n isItemDisabled: (item) => item.disabled,\n onNavigate: scrollFocusedIntoView,\n signal: abortSignal,\n });\n const { focusedIndex } = list;\n\n // ── Dialog chrome: native <dialog>, focus trap, Escape, backdrop click ──\n const resetSearch = (): void => {\n query.value = '';\n list.reset();\n };\n\n const { closeWithAnimation, overlay, requestClose, setupNativeListeners } = useDialogControl({\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => dialogRef.value?.querySelector<HTMLElement>('.panel'),\n host: el,\n initialFocus: computed(() => '.search-input'),\n isPersistent: () => false,\n onCleanup,\n onEvent,\n onNativeClose: (reason) => {\n emit('open-change', { open: false, reason });\n resetSearch();\n },\n onOpen: (reason) => {\n emit('open-change', { open: true, reason });\n list.navigate('first');\n },\n openProp: props.open,\n performClose: () => closeWithAnimation(),\n returnFocus: computed(() => true),\n });\n\n // ── Global keyboard shortcut (opens/closes the palette from anywhere) ──\n const globalKeymap = createKeymap();\n\n globalKeymap.mount(window);\n onCleanup(() => globalKeymap.dispose());\n\n let unbindShortcut: (() => void) | null = null;\n\n watch(\n props.shortcut,\n (shortcutStr) => {\n unbindShortcut?.();\n unbindShortcut = null;\n\n const trimmed = (shortcutStr ?? '').trim();\n\n if (!trimmed) return;\n\n try {\n unbindShortcut = globalKeymap.bind({\n handler: () => {\n overlay.toggle('keyboard', 'trigger');\n },\n id: 'command-palette-toggle',\n shortcut: trimmed,\n });\n } catch (error) {\n warn(`invalid \"shortcut\" value \"${trimmed}\" — ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n { immediate: true },\n );\n\n onMounted(() => {\n setupNativeListeners();\n });\n\n // ── Selection ────────────────────────────────────────────────────────────\n function selectItem(item: CommandPaletteItem): void {\n if (item.disabled) return;\n\n emit('select', { item, label: item.label, value: item.value });\n\n if (props['keep-open-on-select'].value) {\n searchInputRef.value?.focus();\n } else {\n requestClose('trigger');\n }\n }\n\n function handleInput(e: Event): void {\n const value = (e.target as HTMLInputElement).value;\n\n query.value = value;\n list.navigate('first');\n emit('search', { query: value });\n\n const count = filteredItems.value.length;\n\n announce(count === 0 ? props['empty-text'].value! : `${count} result${count === 1 ? '' : 's'} found`, {\n document: el.ownerDocument,\n });\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (list.handleKeydown(e)) return;\n\n if (e.key === 'Enter') {\n // `list.navigate('first')` (called on open and on every keystroke) already keeps\n // the focused row off a disabled item, so this fallback is only ever reached when\n // nothing has been focused yet — but it mirrors `selectItem`'s own disabled guard\n // rather than blindly grabbing index 0, in case that navigate-on-open wiring ever\n // changes.\n const active = list.getActiveItem() ?? filteredItems.value.find((item) => !item.disabled);\n\n if (active) {\n e.preventDefault();\n selectItem(active);\n }\n }\n }\n\n return html`\n <dialog ref=\"${dialogRef}\" class=\"dialog\" part=\"dialog\" aria-label=\"${props.label}\" aria-modal=\"true\">\n <div class=\"panel\" part=\"panel\">\n <div class=\"search\" part=\"search\">\n <span class=\"search-icon\" aria-hidden=\"true\"><ore-icon name=\"search\" size=\"18\"></ore-icon></span>\n <input\n ref=\"${searchInputRef}\"\n class=\"search-input\"\n part=\"input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n aria-expanded=\"true\"\n aria-autocomplete=\"list\"\n aria-controls=\"${listboxId}\"\n aria-activedescendant=\"${() => (focusedIndex.value >= 0 ? `${listboxId}-opt-${focusedIndex.value}` : '')}\"\n placeholder=\"${props.placeholder}\"\n value=\"${query}\"\n @input=\"${handleInput}\"\n @keydown=\"${handleKeydown}\" />\n <span class=\"search-loader\" ?hidden=\"${() => !props.loading.value}\" aria-hidden=\"true\"></span>\n </div>\n <div\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=\"${listboxId}\"\n aria-label=\"Commands\"\n ref=\"${(listEl: HTMLElement) => {\n listboxEl = listEl;\n }}\">\n <div class=\"empty\" role=\"presentation\" ?hidden=\"${() => props.loading.value || rows.value.length > 0}\">\n ${() => props['empty-text'].value}\n </div>\n ${() =>\n rows.value.map((row) =>\n row.type === 'group'\n ? html`\n <div class=\"group-heading\" role=\"presentation\">${row.group}</div>\n `\n : html`\n <div\n class=\"item\"\n part=\"item\"\n role=\"option\"\n id=\"${`${listboxId}-opt-${row.idx}`}\"\n aria-selected=\"${() => String(focusedIndex.value === row.idx)}\"\n aria-disabled=\"${() => String(row.item.disabled)}\"\n ?data-focused=\"${() => focusedIndex.value === row.idx}\"\n ?data-disabled=\"${() => row.item.disabled}\"\n @click=\"${(e: MouseEvent) => {\n e.stopPropagation();\n selectItem(row.item);\n }}\"\n @pointerenter=\"${() => {\n list.set(row.idx);\n }}\">\n ${\n row.item.icon\n ? html`\n <span class=\"item-icon\" aria-hidden=\"true\">\n <ore-icon name=\"${row.item.icon}\" size=\"16\"></ore-icon>\n </span>\n `\n : ''\n }\n <span class=\"item-label\">${row.item.label}</span>\n ${\n row.item.shortcut\n ? html`\n <span class=\"item-shortcut\">\n ${splitShortcutKeys(row.item.shortcut).map(\n (key) => html`\n <kbd>${key}</kbd>\n `,\n )}\n </span>\n `\n : ''\n }\n </div>\n `,\n )}\n </div>\n <div class=\"footer\" part=\"footer\">\n <span class=\"footer-hint\">\n <kbd>↑</kbd>\n <kbd>↓</kbd>\n Navigate\n </span>\n <span class=\"footer-hint\">\n <kbd>Enter</kbd>\n Select\n </span>\n <span class=\"footer-hint\">\n <kbd>Esc</kbd>\n Close\n </span>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"command-palette.cjs","names":[],"sources":["../src/overlay/command-palette/command-palette.ts"],"sourcesContent":["import { createKeymap } from '@vielzeug/keymap';\nimport { define, getHost, html, onCleanup, onEvent, onMounted, prop, ref, useEmit } from '@vielzeug/ore';\nimport { computed, signal, watch } from '@vielzeug/ripple';\nimport { warn } from '../../_dev';\nimport '../../content/keyboard-key/keyboard-key';\nimport { announce, createListControl, lifecycleSignal } from '../../core';\nimport { reducedMotionMixin } from '../../styles';\nimport { useDialogControl } from '../shared/use-dialog';\nimport type { CommandPaletteItem, OreCommandPaletteEvents, OreCommandPaletteProps } from './command-palette.types';\nimport { buildRows, filterItems, normalizeItem, parseSlottedItems, splitShortcutKeys } from './command-palette-items';\nimport '../../content/icon/icon';\nimport componentStyles from './command-palette.css?inline';\n\nexport type { OreCommandPaletteEvents, OreCommandPaletteProps } from './command-palette.types';\n\nconst parseOptionalBool = (value: string | null): boolean | undefined =>\n value == null ? undefined : value === '' || value === 'true';\n\n/**\n * A pure data node describing one command. Never rendered directly — `ore-command-palette`\n * reads its attributes/text content and renders the visible row itself.\n *\n * @element ore-command-palette-item\n *\n * @attr {string} value - Value emitted by the `select` event and matched against the search query\n * @attr {string} label - Explicit label text; falls back to the element's text content\n * @attr {string} group - Group heading the item is clustered under\n * @attr {string} shortcut - Display-only keyboard hint rendered at the end of the row, one `<kbd>` per `+`-separated key (e.g. `\"⌘+S\"` renders two keycaps; a literal `+` key isn't representable — spell it out, e.g. `\"Ctrl+Plus\"`)\n * @attr {boolean} disabled - Excludes the item from keyboard navigation and selection\n *\n * @slot icon - Optional leading icon content\n *\n * @example\n * ```html\n * <ore-command-palette-item value=\"new-file\" group=\"File\" shortcut=\"⌘+N\">New File</ore-command-palette-item>\n * ```\n */\nexport const COMMAND_PALETTE_ITEM_TAG = 'ore-command-palette-item' as const;\ndefine(COMMAND_PALETTE_ITEM_TAG, {\n setup() {\n return html``;\n },\n});\n\n/**\n * A searchable, keyboard-driven list of commands presented in a centered modal —\n * the \"⌘K\" pattern popularized by editors and productivity apps. Built on the\n * native `<dialog>` element (focus trap, top-layer stacking, `Escape`-to-close)\n * and `@vielzeug/keymap` for the global shortcut that opens it.\n *\n * @element ore-command-palette\n * @element ore-command-palette-item - Slotted command definition (place in default slot)\n *\n * @attr {boolean} open - Controls the open state of the palette\n * @attr {boolean} default-open - Initial uncontrolled open state\n * @attr {string} label - Accessible label for the dialog (screen-reader-only heading)\n * @attr {string} placeholder - Placeholder text for the search input\n * @attr {string} shortcut - Global keyboard shortcut (keymap syntax) that toggles the palette. Default `\"mod+k\"`; set to `\"\"` to disable\n * @attr {boolean} no-filter - Disable built-in client-side filtering (for server-driven search)\n * @attr {boolean} loading - Shows a loading row below the search input\n * @attr {string} empty-text - Message shown when no item matches the query\n * @attr {boolean} keep-open-on-select - Keep the palette open after an item is selected\n *\n * @fires open-change - Fired when the palette state changes. detail: `{ open, reason }`\n * @fires search - Fired on every keystroke in the search input. detail: `{ query }`\n * @fires select - Fired when a command is chosen (click or `Enter`). detail: `{ value, label, item }`\n *\n * @slot - `<ore-command-palette-item>` elements (alternative/supplement to the `items` prop)\n *\n * @cssprop --command-palette-bg - Panel background color\n * @cssprop --command-palette-border-color - Panel border color\n * @cssprop --command-palette-radius - Panel border radius\n * @cssprop --command-palette-shadow - Panel drop shadow\n * @cssprop --command-palette-max-width - Maximum panel width\n * @cssprop --command-palette-backdrop - Backdrop overlay color\n * @cssprop --command-palette-option-hover-bg - Item background on hover\n * @cssprop --command-palette-option-focus-bg - Item background when keyboard-focused\n *\n * @part dialog - Dialog root container\n * @part panel - Panel container\n * @part input - Search input\n * @part listbox - Listbox of matching commands\n *\n * @example\n * ```html\n * <ore-command-palette label=\"Command palette\" placeholder=\"Type a command…\">\n * <ore-command-palette-item value=\"new-file\" group=\"File\" shortcut=\"⌘+N\">New File</ore-command-palette-item>\n * <ore-command-palette-item value=\"open-file\" group=\"File\" shortcut=\"⌘+O\">Open File…</ore-command-palette-item>\n * <ore-command-palette-item value=\"toggle-theme\" group=\"View\">Toggle Theme</ore-command-palette-item>\n * </ore-command-palette>\n * ```\n */\nexport const COMMAND_PALETTE_TAG = 'ore-command-palette' as const;\ndefine<OreCommandPaletteProps>(COMMAND_PALETTE_TAG, {\n props: {\n 'default-open': prop.bool(false),\n 'empty-text': prop.string('No results found.'),\n items: prop.data<OreCommandPaletteProps['items']>(),\n 'keep-open-on-select': prop.bool(false),\n label: prop.string('Command palette'),\n loading: prop.bool(false),\n 'no-filter': prop.bool(false),\n open: { default: undefined as boolean | undefined, parse: parseOptionalBool },\n placeholder: prop.string('Type a command or search…'),\n shortcut: prop.string('mod+k'),\n },\n setup(props) {\n const el = getHost();\n const emit = useEmit<OreCommandPaletteEvents>();\n const abortSignal = lifecycleSignal(onCleanup);\n\n const dialogRef = ref<HTMLDialogElement>();\n const searchInputRef = ref<HTMLInputElement>();\n const listboxId = 'cmdk-listbox';\n\n let listboxEl: HTMLElement | null = null;\n\n const query = signal('');\n\n // ── Items: slotted <ore-command-palette-item> elements, merged with the `items` prop ──\n // Items are pure data nodes (see `ore-command-palette-item`'s own doc comment) — they're\n // never projected through a `<slot>`, just read directly off the host's light DOM. That\n // sidesteps `useSlots()`/`slotchange` entirely: a plain MutationObserver on the host\n // already covers everything `slotchange` would (an item added or removed) *and* the case\n // it can't — an already-assigned item's own text/attributes changing. For items written\n // as static, inline HTML, the browser's parser can insert the last item before appending\n // its text-node child; without watching `characterData`, that would permanently cache an\n // empty label, since nothing else re-triggers once the element itself stops changing.\n const slottedItems = signal<CommandPaletteItem[]>([]);\n\n const reparseSlottedItems = (): void => {\n slottedItems.value = parseSlottedItems([...el.children]);\n };\n\n reparseSlottedItems();\n\n const lightDomObserver = new MutationObserver(reparseSlottedItems);\n\n lightDomObserver.observe(el, { characterData: true, childList: true, subtree: true });\n onCleanup(() => lightDomObserver.disconnect());\n\n const allItems = computed<CommandPaletteItem[]>(() => {\n const explicit = props.items.value;\n\n return Array.isArray(explicit) ? explicit.map(normalizeItem) : slottedItems.value;\n });\n\n const filteredItems = computed(() => filterItems(allItems.value, query.value, Boolean(props['no-filter'].value)));\n const rows = computed(() => buildRows(filteredItems.value));\n\n // ── Keyboard navigation over the filtered list (Up/Down/Home/End) ──\n const scrollFocusedIntoView = (): void => {\n listboxEl?.querySelector<HTMLElement>('[data-focused]')?.scrollIntoView({ block: 'nearest' });\n };\n\n const list = createListControl<CommandPaletteItem>({\n getItems: () => filteredItems.value,\n isItemDisabled: (item) => item.disabled,\n onNavigate: scrollFocusedIntoView,\n signal: abortSignal,\n });\n const { focusedIndex } = list;\n\n // ── Dialog chrome: native <dialog>, focus trap, Escape, backdrop click ──\n const resetSearch = (): void => {\n query.value = '';\n list.reset();\n };\n\n const { closeWithAnimation, overlay, requestClose, setupNativeListeners } = useDialogControl({\n defaultOpen: props['default-open'],\n dialogRef,\n getPanelEl: () => dialogRef.value?.querySelector<HTMLElement>('.panel'),\n host: el,\n initialFocus: computed(() => '.search-input'),\n isPersistent: () => false,\n onCleanup,\n onEvent,\n onNativeClose: (reason) => {\n emit('open-change', { open: false, reason });\n resetSearch();\n },\n onOpen: (reason) => {\n emit('open-change', { open: true, reason });\n list.navigate('first');\n },\n openProp: props.open,\n performClose: () => closeWithAnimation(),\n returnFocus: computed(() => true),\n });\n\n // ── Global keyboard shortcut (opens/closes the palette from anywhere) ──\n const globalKeymap = createKeymap();\n\n globalKeymap.mount(window);\n onCleanup(() => globalKeymap.dispose());\n\n let unbindShortcut: (() => void) | null = null;\n\n watch(\n props.shortcut,\n (shortcutStr) => {\n unbindShortcut?.();\n unbindShortcut = null;\n\n const trimmed = (shortcutStr ?? '').trim();\n\n if (!trimmed) return;\n\n try {\n unbindShortcut = globalKeymap.bind({\n handler: () => {\n overlay.toggle('keyboard', 'trigger');\n },\n id: 'command-palette-toggle',\n shortcut: trimmed,\n });\n } catch (error) {\n warn(`invalid \"shortcut\" value \"${trimmed}\" — ${error instanceof Error ? error.message : String(error)}`);\n }\n },\n { immediate: true },\n );\n\n onMounted(() => {\n setupNativeListeners();\n });\n\n // ── Selection ────────────────────────────────────────────────────────────\n function selectItem(item: CommandPaletteItem): void {\n if (item.disabled) return;\n\n emit('select', { item, label: item.label, value: item.value });\n\n if (props['keep-open-on-select'].value) {\n searchInputRef.value?.focus();\n } else {\n requestClose('trigger');\n }\n }\n\n function handleInput(e: Event): void {\n const value = (e.target as HTMLInputElement).value;\n\n query.value = value;\n list.navigate('first');\n emit('search', { query: value });\n\n const count = filteredItems.value.length;\n\n announce(count === 0 ? props['empty-text'].value! : `${count} result${count === 1 ? '' : 's'} found`, {\n document: el.ownerDocument,\n });\n }\n\n function handleKeydown(e: KeyboardEvent): void {\n if (list.handleKeydown(e)) return;\n\n if (e.key === 'Enter') {\n // `list.navigate('first')` (called on open and on every keystroke) already keeps\n // the focused row off a disabled item, so this fallback is only ever reached when\n // nothing has been focused yet — but it mirrors `selectItem`'s own disabled guard\n // rather than blindly grabbing index 0, in case that navigate-on-open wiring ever\n // changes.\n const active = list.getActiveItem() ?? filteredItems.value.find((item) => !item.disabled);\n\n if (active) {\n e.preventDefault();\n selectItem(active);\n }\n }\n }\n\n return html`\n <dialog ref=\"${dialogRef}\" class=\"dialog\" part=\"dialog\" aria-label=\"${props.label}\" aria-modal=\"true\">\n <div class=\"panel\" part=\"panel\">\n <div class=\"search\" part=\"search\">\n <span class=\"search-icon\" aria-hidden=\"true\"><ore-icon name=\"search\" size=\"18\"></ore-icon></span>\n <input\n ref=\"${searchInputRef}\"\n class=\"search-input\"\n part=\"input\"\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n aria-expanded=\"true\"\n aria-autocomplete=\"list\"\n aria-controls=\"${listboxId}\"\n aria-activedescendant=\"${() => (focusedIndex.value >= 0 ? `${listboxId}-opt-${focusedIndex.value}` : '')}\"\n placeholder=\"${props.placeholder}\"\n value=\"${query}\"\n @input=\"${handleInput}\"\n @keydown=\"${handleKeydown}\" />\n <span class=\"search-loader\" ?hidden=\"${() => !props.loading.value}\" aria-hidden=\"true\"></span>\n </div>\n <div\n class=\"listbox\"\n part=\"listbox\"\n role=\"listbox\"\n id=\"${listboxId}\"\n aria-label=\"Commands\"\n ref=\"${(listEl: HTMLElement) => {\n listboxEl = listEl;\n }}\">\n <div class=\"empty\" role=\"presentation\" ?hidden=\"${() => props.loading.value || rows.value.length > 0}\">\n ${() => props['empty-text'].value}\n </div>\n ${() =>\n rows.value.map((row) =>\n row.type === 'group'\n ? html`\n <div class=\"group-heading\" role=\"presentation\">${row.group}</div>\n `\n : html`\n <div\n class=\"item\"\n part=\"item\"\n role=\"option\"\n id=\"${`${listboxId}-opt-${row.idx}`}\"\n aria-selected=\"${() => String(focusedIndex.value === row.idx)}\"\n aria-disabled=\"${() => String(row.item.disabled)}\"\n ?data-focused=\"${() => focusedIndex.value === row.idx}\"\n ?data-disabled=\"${() => row.item.disabled}\"\n @click=\"${(e: MouseEvent) => {\n e.stopPropagation();\n selectItem(row.item);\n }}\"\n @pointerenter=\"${() => {\n list.set(row.idx);\n }}\">\n ${\n row.item.icon\n ? html`\n <span class=\"item-icon\" aria-hidden=\"true\">\n <ore-icon name=\"${row.item.icon}\" size=\"16\"></ore-icon>\n </span>\n `\n : ''\n }\n <span class=\"item-label\">${row.item.label}</span>\n ${\n row.item.shortcut\n ? html`\n <span class=\"item-shortcut\">\n ${splitShortcutKeys(row.item.shortcut).map(\n (key) => html`\n <ore-keyboard-key>${key}</ore-keyboard-key>\n `,\n )}\n </span>\n `\n : ''\n }\n </div>\n `,\n )}\n </div>\n <div class=\"footer\" part=\"footer\">\n <span class=\"footer-hint\">\n <ore-keyboard-key>↑</ore-keyboard-key>\n <ore-keyboard-key>↓</ore-keyboard-key>\n Navigate\n </span>\n <span class=\"footer-hint\">\n <ore-keyboard-key>Enter</ore-keyboard-key>\n Select\n </span>\n <span class=\"footer-hint\">\n <ore-keyboard-key>Esc</ore-keyboard-key>\n Close\n </span>\n </div>\n </div>\n </dialog>\n `;\n },\n styles: [reducedMotionMixin, componentStyles],\n});\n"],"mappings":"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"}
|
package/dist/command-palette.js
CHANGED
|
@@ -179,7 +179,7 @@ d(E, {
|
|
|
179
179
|
${e.item.shortcut ? p`
|
|
180
180
|
<span class="item-shortcut">
|
|
181
181
|
${l(e.item.shortcut).map((e) => p`
|
|
182
|
-
<
|
|
182
|
+
<ore-keyboard-key>${e}</ore-keyboard-key>
|
|
183
183
|
`)}
|
|
184
184
|
</span>
|
|
185
185
|
` : ""}
|
|
@@ -188,16 +188,16 @@ d(E, {
|
|
|
188
188
|
</div>
|
|
189
189
|
<div class="footer" part="footer">
|
|
190
190
|
<span class="footer-hint">
|
|
191
|
-
<
|
|
192
|
-
<
|
|
191
|
+
<ore-keyboard-key>↑</ore-keyboard-key>
|
|
192
|
+
<ore-keyboard-key>↓</ore-keyboard-key>
|
|
193
193
|
Navigate
|
|
194
194
|
</span>
|
|
195
195
|
<span class="footer-hint">
|
|
196
|
-
<
|
|
196
|
+
<ore-keyboard-key>Enter</ore-keyboard-key>
|
|
197
197
|
Select
|
|
198
198
|
</span>
|
|
199
199
|
<span class="footer-hint">
|
|
200
|
-
<
|
|
200
|
+
<ore-keyboard-key>Esc</ore-keyboard-key>
|
|
201
201
|
Close
|
|
202
202
|
</span>
|
|
203
203
|
</div>
|