@hjmds/react 1.18.0 → 1.20.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/advanced-display.d.ts.map +1 -1
- package/dist/advanced-display.js +41 -9
- package/dist/advanced-display.js.map +1 -1
- package/dist/advanced-forms.d.ts.map +1 -1
- package/dist/advanced-forms.js +22 -9
- package/dist/advanced-forms.js.map +1 -1
- package/dist/agreement.d.ts.map +1 -1
- package/dist/agreement.js +17 -11
- package/dist/agreement.js.map +1 -1
- package/dist/auth-screen.d.ts.map +1 -1
- package/dist/auth-screen.js +15 -2
- package/dist/auth-screen.js.map +1 -1
- package/dist/avatar-blobatar-motion.d.ts.map +1 -1
- package/dist/avatar-blobatar-motion.js +10 -2
- package/dist/avatar-blobatar-motion.js.map +1 -1
- package/dist/bottom-navigation.d.ts.map +1 -1
- package/dist/bottom-navigation.js +1 -1
- package/dist/bottom-navigation.js.map +1 -1
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +10 -2
- package/dist/calendar.js.map +1 -1
- package/dist/carousel-motion.d.ts +1 -0
- package/dist/carousel-motion.d.ts.map +1 -1
- package/dist/carousel-motion.js +4 -3
- package/dist/carousel-motion.js.map +1 -1
- package/dist/carousel.d.ts.map +1 -1
- package/dist/carousel.js +22 -4
- package/dist/carousel.js.map +1 -1
- package/dist/collapsible.d.ts.map +1 -1
- package/dist/collapsible.js +28 -4
- package/dist/collapsible.js.map +1 -1
- package/dist/collection-rail.d.ts.map +1 -1
- package/dist/collection-rail.js +19 -1
- package/dist/collection-rail.js.map +1 -1
- package/dist/color-picker.d.ts.map +1 -1
- package/dist/color-picker.js +12 -4
- package/dist/color-picker.js.map +1 -1
- package/dist/command-palette.d.ts.map +1 -1
- package/dist/command-palette.js +14 -2
- package/dist/command-palette.js.map +1 -1
- package/dist/context-menu.d.ts.map +1 -1
- package/dist/context-menu.js +31 -10
- package/dist/context-menu.js.map +1 -1
- package/dist/date-entry.d.ts +1 -1
- package/dist/date-entry.d.ts.map +1 -1
- package/dist/date-entry.js +20 -7
- package/dist/date-entry.js.map +1 -1
- package/dist/date-picker.d.ts.map +1 -1
- package/dist/date-picker.js +22 -6
- package/dist/date-picker.js.map +1 -1
- package/dist/display.d.ts +5 -1
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +3 -2
- package/dist/display.js.map +1 -1
- package/dist/duration-field.d.ts +2 -2
- package/dist/duration-field.d.ts.map +1 -1
- package/dist/duration-field.js +11 -3
- package/dist/duration-field.js.map +1 -1
- package/dist/evidence.d.ts +1 -1
- package/dist/evidence.js +1 -1
- package/dist/evidence.js.map +1 -1
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +3 -2
- package/dist/feedback.js.map +1 -1
- package/dist/field-group.d.ts +4 -2
- package/dist/field-group.d.ts.map +1 -1
- package/dist/field-group.js +4 -3
- package/dist/field-group.js.map +1 -1
- package/dist/file-picker.d.ts.map +1 -1
- package/dist/file-picker.js +15 -5
- package/dist/file-picker.js.map +1 -1
- package/dist/forms.d.ts +2 -2
- package/dist/forms.js +1 -1
- package/dist/forms.js.map +1 -1
- package/dist/gravity-letters.d.ts.map +1 -1
- package/dist/gravity-letters.js +9 -1
- package/dist/gravity-letters.js.map +1 -1
- package/dist/grid-reveal.d.ts.map +1 -1
- package/dist/grid-reveal.js +9 -1
- package/dist/grid-reveal.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/inline-confirm.d.ts.map +1 -1
- package/dist/inline-confirm.js +25 -4
- package/dist/inline-confirm.js.map +1 -1
- package/dist/internal/document-resource.d.ts.map +1 -1
- package/dist/internal/document-resource.js +2 -2
- package/dist/internal/document-resource.js.map +1 -1
- package/dist/masonry.d.ts.map +1 -1
- package/dist/masonry.js +2 -1
- package/dist/masonry.js.map +1 -1
- package/dist/mentions.d.ts.map +1 -1
- package/dist/mentions.js +12 -4
- package/dist/mentions.js.map +1 -1
- package/dist/menu-morph.js +1 -1
- package/dist/menu-morph.js.map +1 -1
- package/dist/menubar.d.ts.map +1 -1
- package/dist/menubar.js +43 -19
- package/dist/menubar.js.map +1 -1
- package/dist/modal.d.ts.map +1 -1
- package/dist/modal.js +15 -4
- package/dist/modal.js.map +1 -1
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +8 -2
- package/dist/navigation.js.map +1 -1
- package/dist/number-field.d.ts.map +1 -1
- package/dist/number-field.js +3 -1
- package/dist/number-field.js.map +1 -1
- package/dist/overlay-stack.d.ts.map +1 -1
- package/dist/overlay-stack.js +23 -6
- package/dist/overlay-stack.js.map +1 -1
- package/dist/overlays.d.ts.map +1 -1
- package/dist/overlays.js +8 -0
- package/dist/overlays.js.map +1 -1
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +16 -4
- package/dist/pagination.js.map +1 -1
- package/dist/saved-items.d.ts.map +1 -1
- package/dist/saved-items.js +6 -1
- package/dist/saved-items.js.map +1 -1
- package/dist/screen-flows.d.ts +1 -2
- package/dist/screen-flows.d.ts.map +1 -1
- package/dist/screen-flows.js +120 -16
- package/dist/screen-flows.js.map +1 -1
- package/dist/screens.d.ts.map +1 -1
- package/dist/screens.js +32 -7
- package/dist/screens.js.map +1 -1
- package/dist/select.d.ts.map +1 -1
- package/dist/select.js +6 -1
- package/dist/select.js.map +1 -1
- package/dist/side-panel.d.ts.map +1 -1
- package/dist/side-panel.js +11 -2
- package/dist/side-panel.js.map +1 -1
- package/dist/sidebar.d.ts.map +1 -1
- package/dist/sidebar.js +8 -1
- package/dist/sidebar.js.map +1 -1
- package/dist/skip-nav.d.ts.map +1 -1
- package/dist/skip-nav.js +16 -3
- package/dist/skip-nav.js.map +1 -1
- package/dist/sortable.d.ts.map +1 -1
- package/dist/sortable.js +2 -1
- package/dist/sortable.js.map +1 -1
- package/dist/splitter.d.ts.map +1 -1
- package/dist/splitter.js +23 -11
- package/dist/splitter.js.map +1 -1
- package/dist/statistic-motion.d.ts.map +1 -1
- package/dist/statistic-motion.js +30 -2
- package/dist/statistic-motion.js.map +1 -1
- package/dist/status-text.d.ts +11 -0
- package/dist/status-text.d.ts.map +1 -0
- package/dist/status-text.js +7 -0
- package/dist/status-text.js.map +1 -0
- package/dist/styles.css +19 -4
- package/dist/styles.layered.css +19 -4
- package/dist/supplemental-display.d.ts.map +1 -1
- package/dist/supplemental-display.js +19 -8
- package/dist/supplemental-display.js.map +1 -1
- package/dist/supplemental-navigation.d.ts.map +1 -1
- package/dist/supplemental-navigation.js +10 -1
- package/dist/supplemental-navigation.js.map +1 -1
- package/dist/tags-input.d.ts.map +1 -1
- package/dist/tags-input.js +18 -8
- package/dist/tags-input.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +1 -0
- package/dist/theme.js.map +1 -1
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +31 -16
- package/dist/toast.js.map +1 -1
- package/dist/toggle-group.d.ts.map +1 -1
- package/dist/toggle-group.js +6 -1
- package/dist/toggle-group.js.map +1 -1
- package/dist/tour.d.ts.map +1 -1
- package/dist/tour.js +15 -2
- package/dist/tour.js.map +1 -1
- package/dist/transfer-list.d.ts +1 -1
- package/dist/transfer-list.d.ts.map +1 -1
- package/dist/transfer-list.js +52 -7
- package/dist/transfer-list.js.map +1 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +41 -8
- package/dist/tree.js.map +1 -1
- package/dist/virtual-list.d.ts.map +1 -1
- package/dist/virtual-list.js +44 -7
- package/dist/virtual-list.js.map +1 -1
- package/package.json +8 -3
package/dist/color-picker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-picker.js","sourceRoot":"","sources":["../src/color-picker.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAsB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"color-picker.js","sourceRoot":"","sources":["../src/color-picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useRef, useState, type CSSProperties } from \"react\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\nimport { colorPickerRecipe, normalizePickerColor, pickerOpacity, withPickerOpacity } from \"@hjmds/design-contracts/components/color-picker\";\nexport type ColorPickerLabels = Readonly<{ color: string; hex: string; opacity: string; invalid: string }>;\nexport type ColorPickerProps = Readonly<{\n label: string; labels: ColorPickerLabels; value: string; onValueChange: (value: string) => void;\n alpha?: boolean; disabled?: boolean; presets?: readonly string[];\n /** Canonical layout-only placement on the root `<fieldset>`. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n/** Controlled value; invalid text stays local until corrected or escaped. Native color UI supplies RGB only. */\nexport function ColorPicker({ label, labels, value, onValueChange, alpha = false, disabled = false, presets = [], layoutStyle }: ColorPickerProps) {\n const color = normalizePickerColor(value, alpha);\n // HEX is an ordered code, so isolate its LTR text even when the surrounding labels use RTL.\n const palette = [...new Set(presets.map(preset => normalizePickerColor(preset, alpha)))];\n if (![label, ...Object.values(labels)].every(text => text.trim())) throw new TypeError(\"ColorPicker labels must not be empty\");\n const id = useId();\n const [draft, setDraft] = useState(color);\n const [invalid, setInvalid] = useState(false);\n // Enter consumes this edit; its following blur must not resend a deferred controlled request.\n const draftChanged = useRef(false);\n useEffect(() => { setDraft(color); setInvalid(false); draftChanged.current = false; }, [color]);\n function emit(next: string) { if (disabled) return; draftChanged.current = false; setDraft(next); setInvalid(false); if (next !== color) onValueChange(next); }\n function commit() {\n if (disabled) return;\n let next: string;\n try {\n // A typed 3/6-digit HEX names only the hue. Keep the current opacity instead of resetting it to 100%,\n // matching the native color input; an 8-digit HEX still sets alpha explicitly (2026-09-30 review).\n const digits = draft.trim().replace(/^#/, \"\").length;\n next = alpha && (digits === 3 || digits === 6) ? normalizePickerColor(draft, false) + color.slice(7) : normalizePickerColor(draft, alpha);\n } catch { setInvalid(true); return; }\n emit(next);\n }\n return <fieldset className=\"hjm-color-picker\" style={{ ...layoutStyle, \"--hjm-color-picker-target\": `${colorPickerRecipe.minTargetSize}px` } as CSSProperties} data-hjm-color-picker disabled={disabled}>\n <legend>{label}</legend>\n <div className=\"hjm-color-picker__row\">\n <label className=\"hjm-color-picker__native\"><span>{labels.color}</span><input type=\"color\" value={color.slice(0, 7)} onChange={event => emit(event.target.value + (alpha ? color.slice(7) : \"\"))} /></label>\n <label className=\"hjm-color-picker__hex\"><span>{labels.hex}</span><input type=\"text\" dir=\"ltr\" value={draft} spellCheck={false} autoComplete=\"off\" aria-invalid={invalid || undefined} aria-describedby={invalid ? `${id}-error` : undefined}\n onChange={event => { draftChanged.current = true; setDraft(event.target.value); setInvalid(false); }} onBlur={() => { if (draftChanged.current) commit(); }}\n onKeyDown={event => { if (event.nativeEvent.isComposing || event.keyCode === 229) return; if (event.key === \"Enter\") { event.preventDefault(); commit(); } if (event.key === \"Escape\") { event.preventDefault(); draftChanged.current = false; setDraft(color); setInvalid(false); } }} /></label>\n </div>\n {invalid && <p id={`${id}-error`} role=\"alert\" className=\"hjm-color-picker__error\">{labels.invalid}</p>}\n {alpha && <label className=\"hjm-color-picker__opacity\"><span>{labels.opacity} <output>{pickerOpacity(color)}%</output></span><input type=\"range\" min={0} max={100} step={1} value={pickerOpacity(color)} aria-label={labels.opacity} aria-valuetext={`${pickerOpacity(color)}%`} onChange={event => emit(withPickerOpacity(color, Number(event.target.value)))} /></label>}\n <div className=\"hjm-color-picker__preview\" aria-hidden=\"true\" style={{ backgroundColor: color }} />\n {palette.length > 0 && <div className=\"hjm-color-picker__presets\">{palette.map(preset => <button type=\"button\" key={preset} aria-pressed={preset === color} onClick={() => emit(preset)}><span aria-hidden=\"true\" className=\"hjm-color-picker__swatch\" style={{ backgroundColor: preset }} /><bdi dir=\"ltr\">{preset}</bdi></button>)}</div>}\n </fieldset>;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-palette.d.ts","sourceRoot":"","sources":["../src/command-palette.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,6BAA6B,EAElC,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAEhC,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EAC1B,MAAM,oDAAoD,CAAC;AAO5D,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA4CzG,MAAM,MAAM,mBAAmB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACtE,UAAU,EAAE,wBAAwB,CAAC;IACrC,MAAM,EAAE,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAC/C,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAC5D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,+BAA+B,KAAK,IAAI,CAAC;IACjF,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,cAAc,
|
|
1
|
+
{"version":3,"file":"command-palette.d.ts","sourceRoot":"","sources":["../src/command-palette.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,6BAA6B,EAElC,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAEhC,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EAC1B,MAAM,oDAAoD,CAAC;AAO5D,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA4CzG,MAAM,MAAM,mBAAmB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACtE,UAAU,EAAE,wBAAwB,CAAC;IACrC,MAAM,EAAE,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAC/C,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAC5D;;;;;OAKG;IACH,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,+BAA+B,KAAK,IAAI,CAAC;IACjF,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,cAAc,EAmNrB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,EAChC,KAAK,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAClE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
|
package/dist/command-palette.js
CHANGED
|
@@ -40,6 +40,7 @@ export const CommandPalette = forwardRef(function CommandPalette({ descriptor, s
|
|
|
40
40
|
// Stale external rows stay visible but inert, as in the Native Combobox.
|
|
41
41
|
const enabled = results.current ? results.items.filter((item) => !item.disabled) : [];
|
|
42
42
|
const [activeId, setActiveId] = useState(null);
|
|
43
|
+
const pendingActivation = useRef(null);
|
|
43
44
|
const contentRef = useRef(null);
|
|
44
45
|
const triggerRef = useRef(null);
|
|
45
46
|
const inputRef = useRef(null);
|
|
@@ -68,9 +69,8 @@ export const CommandPalette = forwardRef(function CommandPalette({ descriptor, s
|
|
|
68
69
|
onActivate(itemId, reason);
|
|
69
70
|
// Running a command always closes the palette — `activation` is a dismiss
|
|
70
71
|
// reason no policy can veto — and the follow-up surface opens after that.
|
|
72
|
+
pendingActivation.current = { itemId, reason };
|
|
71
73
|
change(false, "activation");
|
|
72
|
-
if (onActivateAfterDismiss)
|
|
73
|
-
queueMicrotask(() => onActivateAfterDismiss(itemId, reason));
|
|
74
74
|
};
|
|
75
75
|
useModalFocus({
|
|
76
76
|
active: open,
|
|
@@ -79,7 +79,19 @@ export const CommandPalette = forwardRef(function CommandPalette({ descriptor, s
|
|
|
79
79
|
...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),
|
|
80
80
|
onEscape: () => requestClose("escape"),
|
|
81
81
|
});
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (open || !pendingActivation.current)
|
|
84
|
+
return;
|
|
85
|
+
const { itemId, reason } = pendingActivation.current;
|
|
86
|
+
pendingActivation.current = null;
|
|
87
|
+
// A controlled owner may delay closing; wait for teardown and focus release.
|
|
88
|
+
if (onActivateAfterDismiss)
|
|
89
|
+
queueMicrotask(() => onActivateAfterDismiss(itemId, reason));
|
|
90
|
+
}, [open, onActivateAfterDismiss]);
|
|
82
91
|
const onKeyDown = (event) => {
|
|
92
|
+
// IME confirmation and candidate navigation belong to the text input.
|
|
93
|
+
if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229)
|
|
94
|
+
return;
|
|
83
95
|
const intent = getCollectionNavigationIntent(event.key);
|
|
84
96
|
if (intent) {
|
|
85
97
|
const target = getCollectionNavigationTarget({ items: enabled }, active?.id ?? null, intent, true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-palette.js","sourceRoot":"","sources":["../src/command-palette.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n canDismissCommandPalette,\n commandPaletteBehaviorDefaults,\n commandPaletteRecipe,\n validateCommandPaletteDescriptor,\n type CommandPaletteActivateHandler,\n type CommandPaletteActivateReason,\n type CommandPaletteDescriptor,\n type CommandPaletteDismissPolicy,\n type CommandPaletteDismissReason,\n type CommandPaletteOpenChangeDetails,\n type CommandPaletteQueryState,\n type CommandPaletteSource,\n} from \"@hjmds/design-contracts/components/command-palette\";\nimport {\n getCollectionNavigationIntent,\n getCollectionNavigationTarget,\n isComboboxResultCurrent,\n} from \"@hjmds/design-contracts/components/collection\";\nimport type { CommandPaletteItemDescriptor } from \"@hjmds/design-contracts/components/command-palette\";\nimport {\n forwardRef,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { classNames, composeRefs } from \"./internal.js\";\nimport { getModalLayer, HjmPortal, renderTrigger, useModalFocus, type OverlayTrigger } from \"./modal.js\";\n\ntype ResultSection<Key extends string> = Readonly<{\n id: string;\n label: string | undefined;\n name: string | undefined;\n items: readonly CommandPaletteItemDescriptor<Key>[];\n}>;\n\n// Local filter = Native Combobox substring rule; why not prefix/contracts:\n// docs/command-palette.md \"2026-10-06\" (comments ship in dist byte budgets).\nfunction resolveResults<Key extends string>(\n source: CommandPaletteSource<Key, string>,\n query: string,\n queryState: CommandPaletteQueryState | undefined,\n emptyMessage: string | undefined,\n) {\n const external = queryState?.filtering === \"external\";\n const needle = query.trim().toLocaleLowerCase();\n const keep = (item: CommandPaletteItemDescriptor<Key>) =>\n external || `${item.label} ${item.textValue}`.toLocaleLowerCase().includes(needle);\n const sections: ResultSection<Key>[] = (source.sections\n ? source.sections.map((section) => ({\n id: section.id,\n label: section.label,\n // validateCollection's precedence.\n name: section.accessibilityLabel?.trim() || section.label?.trim() || undefined,\n items: section.items.filter(keep),\n }))\n : [{ id: \"__all\", label: undefined, name: undefined, items: source.items.filter(keep) }]\n ).filter((section) => section.items.length > 0);\n const items = sections.flatMap((section) => section.items);\n const asyncState = queryState?.asyncState ?? { status: \"idle\" as const };\n const status = asyncState.status !== \"idle\"\n ? { kind: asyncState.status, message: asyncState.message }\n : items.length > 0 || emptyMessage === undefined ? null : { kind: \"empty\" as const, message: emptyMessage };\n return {\n sections,\n items,\n current: !external || isComboboxResultCurrent(queryState.queryValue, queryState.resultQuery),\n status,\n };\n}\n\nexport type CommandPaletteProps<Key extends string = string> = Readonly<{\n descriptor: CommandPaletteDescriptor;\n source: CommandPaletteSource<Key, string>;\n query: string;\n onQueryChange: (query: string) => void;\n onActivate: CommandPaletteActivateHandler<Key>;\n /** Runs after the palette is gone, for a command that opens the next surface. */\n onActivateAfterDismiss?: CommandPaletteActivateHandler<Key>;\n /**\n * Combobox's collection state. Omitted or `filtering: \"local\"` filters\n * `source` by `query` in the renderer (label/textValue substring); pass\n * `filtering: \"external\"` with `queryValue`/`resultQuery` when the product\n * already filtered or ranked the results (server or fuzzy search).\n */\n queryState?: CommandPaletteQueryState;\n dismissPolicy?: Partial<CommandPaletteDismissPolicy>;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, details: CommandPaletteOpenChangeDetails) => void;\n trigger?: OverlayTrigger;\n renderLeading?: (itemId: Key) => ReactNode;\n portalContainer?: HTMLElement;\n className?: string;\n}>;\n\nexport const CommandPalette = forwardRef(function CommandPalette<Key extends string = string>(\n {\n descriptor,\n source,\n query,\n onQueryChange,\n onActivate,\n onActivateAfterDismiss,\n queryState,\n dismissPolicy,\n open: openProp,\n defaultOpen,\n onOpenChange,\n trigger,\n renderLeading,\n portalContainer,\n className,\n }: CommandPaletteProps<Key>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n validateCommandPaletteDescriptor(descriptor);\n const policy: CommandPaletteDismissPolicy = { ...commandPaletteBehaviorDefaults, ...dismissPolicy };\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const open = openProp ?? internalOpen;\n const results = useMemo(\n () => resolveResults<Key>(source, query, queryState, descriptor.emptyMessage),\n [source, query, queryState, descriptor.emptyMessage],\n );\n // Stale external rows stay visible but inert, as in the Native Combobox.\n const enabled = results.current ? results.items.filter((item) => !item.disabled) : [];\n const [activeId, setActiveId] = useState<Key | null>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const id = `${useId().replaceAll(\":\", \"\")}-command-palette`;\n const active = enabled.find((item) => item.id === activeId) ?? enabled[0] ?? null;\n\n useEffect(() => {\n // A new query builds a new result list; the first enabled row becomes active\n // so Enter always has an unambiguous target. Only the query resets it: products\n // often pass `source`/`queryState` as inline objects, and keying on their\n // identity snapped the active row back to the first item on every render\n // (2026-10-06 usage-guide audit). A row that disappears from the results\n // already falls back to the first enabled row through `active` above.\n setActiveId(null);\n }, [query]);\n\n const change = (next: boolean, reason: CommandPaletteOpenChangeDetails[\"reason\"]) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next, { reason });\n };\n const requestClose = (reason: CommandPaletteDismissReason) => {\n if (!canDismissCommandPalette(reason, policy)) return;\n change(false, reason);\n };\n const activate = (itemId: Key, reason: CommandPaletteActivateReason) => {\n onActivate(itemId, reason);\n // Running a command always closes the palette — `activation` is a dismiss\n // reason no policy can veto — and the follow-up surface opens after that.\n change(false, \"activation\");\n if (onActivateAfterDismiss) queueMicrotask(() => onActivateAfterDismiss(itemId, reason));\n };\n\n useModalFocus({\n active: open,\n contentRef,\n initialFocusRef: inputRef,\n ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),\n onEscape: () => requestClose(\"escape\"),\n });\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n const intent = getCollectionNavigationIntent(event.key as never);\n if (intent) {\n const target = getCollectionNavigationTarget({ items: enabled }, active?.id ?? null, intent, true);\n if (target === undefined) return;\n event.preventDefault();\n setActiveId(target as Key);\n return;\n }\n if (event.key === \"Enter\" && active) {\n event.preventDefault();\n activate(active.id as Key, \"keyboard\");\n }\n };\n\n const searchInput = (\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded\n aria-controls={`${id}-results`}\n aria-activedescendant={active ? `${id}-${active.id}` : undefined}\n aria-label={descriptor.accessibilityLabel}\n placeholder={descriptor.searchPlaceholder}\n className=\"hjm-command-palette__search\"\n value={query}\n onChange={(event) => onQueryChange(event.target.value)}\n onKeyDown={onKeyDown}\n />\n );\n\n const renderedTrigger = trigger === undefined\n ? null\n : renderTrigger(trigger, triggerRef, open, id, \"dialog\", () => change(true, \"trigger\"));\n if (!open) return renderedTrigger;\n\n return (\n <>\n {renderedTrigger}\n <HjmPortal {...(portalContainer === undefined ? {} : { container: portalContainer })}>\n <div\n className=\"hjm-overlay hjm-command-palette-positioner\"\n data-kind=\"command-palette\"\n style={{ zIndex: getModalLayer(0) }}\n onMouseDown={(event) => { if (event.target === event.currentTarget) requestClose(\"outside\"); }}\n >\n <div\n ref={composeRefs(contentRef, forwardedRef)}\n id={id}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={descriptor.accessibilityLabel}\n data-hjm-modal-content=\"\"\n className={classNames(\"hjm-command-palette\", className)}\n style={{ maxInlineSize: commandPaletteRecipe.content.maxWidth, maxBlockSize: commandPaletteRecipe.content.maxHeight } as CSSProperties}\n >\n {/*\n Opt-in through descriptor.closeLabel because the renderer must not\n invent untranslated copy. Reuses Dialog's close chrome instead of a\n second close-button style (styles.css is at its byte budget).\n */}\n {descriptor.closeLabel ? (\n <div className=\"hjm-command-palette__search-row\">\n {searchInput}\n <button\n type=\"button\"\n className=\"hjm-dialog__close hjm-command-palette__close\"\n aria-label={descriptor.closeLabel}\n onClick={() => requestClose(\"close-action\")}\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n ) : searchInput}\n <div id={`${id}-results`} role=\"listbox\" aria-label={descriptor.accessibilityLabel} className=\"hjm-command-palette__viewport\">\n {results.status === null ? null : (\n // Once for the whole list, never per section.\n <p\n className=\"hjm-command-palette__state\"\n data-kind={results.status.kind}\n role={results.status.kind === \"error\" ? \"alert\" : \"status\"}\n >\n {results.status.message}\n </p>\n )}\n {results.sections.map((section) => (\n <div\n key={section.id}\n className=\"hjm-command-palette__section\"\n {...(section.name === undefined ? { role: \"presentation\" } : { role: \"group\", \"aria-label\": section.name })}\n >\n {section.label ? <p className=\"hjm-command-palette__section-label\" aria-hidden=\"true\">{section.label}</p> : null}\n {section.items.map((item) => {\n // Positioning or filtering can enter a stationary cursor;\n // only mouse movement should override keyboard navigation.\n const inert = item.disabled === true || !results.current;\n return (\n <div\n key={item.id}\n id={`${id}-${item.id}`}\n role=\"option\"\n aria-selected={item.id === active?.id}\n aria-disabled={inert || undefined}\n className=\"hjm-command-palette__item\"\n data-tone={item.tone}\n onMouseDown={(event) => {\n event.preventDefault();\n if (!inert) activate(item.id as Key, \"pointer\");\n }}\n onMouseMove={() => { if (!inert) setActiveId(item.id as Key); }}\n >\n {renderLeading?.(item.id as Key)}\n <span className=\"hjm-command-palette__copy\">\n <span>{item.label}</span>\n {item.description ? <span className=\"hjm-command-palette__description\">{item.description}</span> : null}\n </span>\n {item.shortcut ? <kbd className=\"hjm-command-palette__shortcut\">{item.shortcut}</kbd> : null}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n </div>\n </HjmPortal>\n </>\n );\n}) as <Key extends string = string>(\n props: CommandPaletteProps<Key> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
1
|
+
{"version":3,"file":"command-palette.js","sourceRoot":"","sources":["../src/command-palette.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n canDismissCommandPalette,\n commandPaletteBehaviorDefaults,\n commandPaletteRecipe,\n validateCommandPaletteDescriptor,\n type CommandPaletteActivateHandler,\n type CommandPaletteActivateReason,\n type CommandPaletteDescriptor,\n type CommandPaletteDismissPolicy,\n type CommandPaletteDismissReason,\n type CommandPaletteOpenChangeDetails,\n type CommandPaletteQueryState,\n type CommandPaletteSource,\n} from \"@hjmds/design-contracts/components/command-palette\";\nimport {\n getCollectionNavigationIntent,\n getCollectionNavigationTarget,\n isComboboxResultCurrent,\n} from \"@hjmds/design-contracts/components/collection\";\nimport type { CommandPaletteItemDescriptor } from \"@hjmds/design-contracts/components/command-palette\";\nimport {\n forwardRef,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { classNames, composeRefs } from \"./internal.js\";\nimport { getModalLayer, HjmPortal, renderTrigger, useModalFocus, type OverlayTrigger } from \"./modal.js\";\n\ntype ResultSection<Key extends string> = Readonly<{\n id: string;\n label: string | undefined;\n name: string | undefined;\n items: readonly CommandPaletteItemDescriptor<Key>[];\n}>;\n\n// Local filter = Native Combobox substring rule; why not prefix/contracts:\n// docs/command-palette.md \"2026-10-06\" (comments ship in dist byte budgets).\nfunction resolveResults<Key extends string>(\n source: CommandPaletteSource<Key, string>,\n query: string,\n queryState: CommandPaletteQueryState | undefined,\n emptyMessage: string | undefined,\n) {\n const external = queryState?.filtering === \"external\";\n const needle = query.trim().toLocaleLowerCase();\n const keep = (item: CommandPaletteItemDescriptor<Key>) =>\n external || `${item.label} ${item.textValue}`.toLocaleLowerCase().includes(needle);\n const sections: ResultSection<Key>[] = (source.sections\n ? source.sections.map((section) => ({\n id: section.id,\n label: section.label,\n // validateCollection's precedence.\n name: section.accessibilityLabel?.trim() || section.label?.trim() || undefined,\n items: section.items.filter(keep),\n }))\n : [{ id: \"__all\", label: undefined, name: undefined, items: source.items.filter(keep) }]\n ).filter((section) => section.items.length > 0);\n const items = sections.flatMap((section) => section.items);\n const asyncState = queryState?.asyncState ?? { status: \"idle\" as const };\n const status = asyncState.status !== \"idle\"\n ? { kind: asyncState.status, message: asyncState.message }\n : items.length > 0 || emptyMessage === undefined ? null : { kind: \"empty\" as const, message: emptyMessage };\n return {\n sections,\n items,\n current: !external || isComboboxResultCurrent(queryState.queryValue, queryState.resultQuery),\n status,\n };\n}\n\nexport type CommandPaletteProps<Key extends string = string> = Readonly<{\n descriptor: CommandPaletteDescriptor;\n source: CommandPaletteSource<Key, string>;\n query: string;\n onQueryChange: (query: string) => void;\n onActivate: CommandPaletteActivateHandler<Key>;\n /** Runs after the palette is gone, for a command that opens the next surface. */\n onActivateAfterDismiss?: CommandPaletteActivateHandler<Key>;\n /**\n * Combobox's collection state. Omitted or `filtering: \"local\"` filters\n * `source` by `query` in the renderer (label/textValue substring); pass\n * `filtering: \"external\"` with `queryValue`/`resultQuery` when the product\n * already filtered or ranked the results (server or fuzzy search).\n */\n queryState?: CommandPaletteQueryState;\n dismissPolicy?: Partial<CommandPaletteDismissPolicy>;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, details: CommandPaletteOpenChangeDetails) => void;\n trigger?: OverlayTrigger;\n renderLeading?: (itemId: Key) => ReactNode;\n portalContainer?: HTMLElement;\n className?: string;\n}>;\n\nexport const CommandPalette = forwardRef(function CommandPalette<Key extends string = string>(\n {\n descriptor,\n source,\n query,\n onQueryChange,\n onActivate,\n onActivateAfterDismiss,\n queryState,\n dismissPolicy,\n open: openProp,\n defaultOpen,\n onOpenChange,\n trigger,\n renderLeading,\n portalContainer,\n className,\n }: CommandPaletteProps<Key>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n validateCommandPaletteDescriptor(descriptor);\n const policy: CommandPaletteDismissPolicy = { ...commandPaletteBehaviorDefaults, ...dismissPolicy };\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const open = openProp ?? internalOpen;\n const results = useMemo(\n () => resolveResults<Key>(source, query, queryState, descriptor.emptyMessage),\n [source, query, queryState, descriptor.emptyMessage],\n );\n // Stale external rows stay visible but inert, as in the Native Combobox.\n const enabled = results.current ? results.items.filter((item) => !item.disabled) : [];\n const [activeId, setActiveId] = useState<Key | null>(null);\n const pendingActivation = useRef<{ itemId: Key; reason: CommandPaletteActivateReason } | null>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const id = `${useId().replaceAll(\":\", \"\")}-command-palette`;\n const active = enabled.find((item) => item.id === activeId) ?? enabled[0] ?? null;\n\n useEffect(() => {\n // A new query builds a new result list; the first enabled row becomes active\n // so Enter always has an unambiguous target. Only the query resets it: products\n // often pass `source`/`queryState` as inline objects, and keying on their\n // identity snapped the active row back to the first item on every render\n // (2026-10-06 usage-guide audit). A row that disappears from the results\n // already falls back to the first enabled row through `active` above.\n setActiveId(null);\n }, [query]);\n\n const change = (next: boolean, reason: CommandPaletteOpenChangeDetails[\"reason\"]) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next, { reason });\n };\n const requestClose = (reason: CommandPaletteDismissReason) => {\n if (!canDismissCommandPalette(reason, policy)) return;\n change(false, reason);\n };\n const activate = (itemId: Key, reason: CommandPaletteActivateReason) => {\n onActivate(itemId, reason);\n // Running a command always closes the palette — `activation` is a dismiss\n // reason no policy can veto — and the follow-up surface opens after that.\n pendingActivation.current = { itemId, reason };\n change(false, \"activation\");\n };\n\n useModalFocus({\n active: open,\n contentRef,\n initialFocusRef: inputRef,\n ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),\n onEscape: () => requestClose(\"escape\"),\n });\n\n useEffect(() => {\n if (open || !pendingActivation.current) return;\n const { itemId, reason } = pendingActivation.current;\n pendingActivation.current = null;\n // A controlled owner may delay closing; wait for teardown and focus release.\n if (onActivateAfterDismiss) queueMicrotask(() => onActivateAfterDismiss(itemId, reason));\n }, [open, onActivateAfterDismiss]);\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n // IME confirmation and candidate navigation belong to the text input.\n if (event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) return;\n const intent = getCollectionNavigationIntent(event.key as never);\n if (intent) {\n const target = getCollectionNavigationTarget({ items: enabled }, active?.id ?? null, intent, true);\n if (target === undefined) return;\n event.preventDefault();\n setActiveId(target as Key);\n return;\n }\n if (event.key === \"Enter\" && active) {\n event.preventDefault();\n activate(active.id as Key, \"keyboard\");\n }\n };\n\n const searchInput = (\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded\n aria-controls={`${id}-results`}\n aria-activedescendant={active ? `${id}-${active.id}` : undefined}\n aria-label={descriptor.accessibilityLabel}\n placeholder={descriptor.searchPlaceholder}\n className=\"hjm-command-palette__search\"\n value={query}\n onChange={(event) => onQueryChange(event.target.value)}\n onKeyDown={onKeyDown}\n />\n );\n\n const renderedTrigger = trigger === undefined\n ? null\n : renderTrigger(trigger, triggerRef, open, id, \"dialog\", () => change(true, \"trigger\"));\n if (!open) return renderedTrigger;\n\n return (\n <>\n {renderedTrigger}\n <HjmPortal {...(portalContainer === undefined ? {} : { container: portalContainer })}>\n <div\n className=\"hjm-overlay hjm-command-palette-positioner\"\n data-kind=\"command-palette\"\n style={{ zIndex: getModalLayer(0) }}\n onMouseDown={(event) => { if (event.target === event.currentTarget) requestClose(\"outside\"); }}\n >\n <div\n ref={composeRefs(contentRef, forwardedRef)}\n id={id}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={descriptor.accessibilityLabel}\n data-hjm-modal-content=\"\"\n className={classNames(\"hjm-command-palette\", className)}\n style={{ maxInlineSize: commandPaletteRecipe.content.maxWidth, maxBlockSize: commandPaletteRecipe.content.maxHeight } as CSSProperties}\n >\n {/*\n Opt-in through descriptor.closeLabel because the renderer must not\n invent untranslated copy. Reuses Dialog's close chrome instead of a\n second close-button style (styles.css is at its byte budget).\n */}\n {descriptor.closeLabel ? (\n <div className=\"hjm-command-palette__search-row\">\n {searchInput}\n <button\n type=\"button\"\n className=\"hjm-dialog__close hjm-command-palette__close\"\n aria-label={descriptor.closeLabel}\n onClick={() => requestClose(\"close-action\")}\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n ) : searchInput}\n <div id={`${id}-results`} role=\"listbox\" aria-label={descriptor.accessibilityLabel} className=\"hjm-command-palette__viewport\">\n {results.status === null ? null : (\n // Once for the whole list, never per section.\n <p\n className=\"hjm-command-palette__state\"\n data-kind={results.status.kind}\n role={results.status.kind === \"error\" ? \"alert\" : \"status\"}\n >\n {results.status.message}\n </p>\n )}\n {results.sections.map((section) => (\n <div\n key={section.id}\n className=\"hjm-command-palette__section\"\n {...(section.name === undefined ? { role: \"presentation\" } : { role: \"group\", \"aria-label\": section.name })}\n >\n {section.label ? <p className=\"hjm-command-palette__section-label\" aria-hidden=\"true\">{section.label}</p> : null}\n {section.items.map((item) => {\n // Positioning or filtering can enter a stationary cursor;\n // only mouse movement should override keyboard navigation.\n const inert = item.disabled === true || !results.current;\n return (\n <div\n key={item.id}\n id={`${id}-${item.id}`}\n role=\"option\"\n aria-selected={item.id === active?.id}\n aria-disabled={inert || undefined}\n className=\"hjm-command-palette__item\"\n data-tone={item.tone}\n onMouseDown={(event) => {\n event.preventDefault();\n if (!inert) activate(item.id as Key, \"pointer\");\n }}\n onMouseMove={() => { if (!inert) setActiveId(item.id as Key); }}\n >\n {renderLeading?.(item.id as Key)}\n <span className=\"hjm-command-palette__copy\">\n <span>{item.label}</span>\n {item.description ? <span className=\"hjm-command-palette__description\">{item.description}</span> : null}\n </span>\n {item.shortcut ? <kbd className=\"hjm-command-palette__shortcut\">{item.shortcut}</kbd> : null}\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n </div>\n </HjmPortal>\n </>\n );\n}) as <Key extends string = string>(\n props: CommandPaletteProps<Key> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-menu.d.ts","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,yBAAyB,EAE/B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"context-menu.d.ts","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,yBAAyB,EAE/B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,gBAAgB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACnE,iFAAiF;IACjF,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,SAAS,yBAAyB,CAAC,GAAG,CAAC,EAAE,CAAC;IACjD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0GAA0G;IAC1G,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAIH,wBAAgB,WAAW,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,EACvD,QAAQ,EACR,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,WAAW,GACZ,EAAE,gBAAgB,CAAC,GAAG,CAAC,+BA+LvB"}
|
package/dist/context-menu.js
CHANGED
|
@@ -4,7 +4,8 @@ import { resolveContextMenuAnchor, } from "@hjmds/design-contracts/components/co
|
|
|
4
4
|
import { menuRecipe } from "@hjmds/design-contracts/recipes";
|
|
5
5
|
import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
|
|
6
6
|
import { classNames } from "./internal.js";
|
|
7
|
-
import {
|
|
7
|
+
import { AnchoredPortal } from "./portal.js";
|
|
8
|
+
import { getModalLayer } from "./modal.js";
|
|
8
9
|
const longPressDelay = 500;
|
|
9
10
|
export function ContextMenu({ children, items, accessibilityLabel, onAction, className, layoutStyle, }) {
|
|
10
11
|
const hostRef = useRef(null);
|
|
@@ -15,9 +16,23 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
|
|
|
15
16
|
const [anchor, setAnchor] = useState(null);
|
|
16
17
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
17
18
|
const longPress = useRef(undefined);
|
|
19
|
+
const pressOrigin = useRef(null);
|
|
20
|
+
const cancelLongPress = useCallback(() => {
|
|
21
|
+
if (longPress.current !== undefined)
|
|
22
|
+
clearTimeout(longPress.current);
|
|
23
|
+
longPress.current = undefined;
|
|
24
|
+
pressOrigin.current = null;
|
|
25
|
+
}, []);
|
|
26
|
+
useEffect(() => cancelLongPress, [cancelLongPress]);
|
|
18
27
|
const typeahead = useRef({ value: "", time: 0 });
|
|
19
28
|
const enabled = items.filter((item) => !item.disabled);
|
|
29
|
+
const resolvedActiveIndex = Math.min(activeIndex, Math.max(0, enabled.length - 1));
|
|
30
|
+
useLayoutEffect(() => {
|
|
31
|
+
if (activeIndex !== resolvedActiveIndex)
|
|
32
|
+
setActiveIndex(resolvedActiveIndex);
|
|
33
|
+
}, [activeIndex, resolvedActiveIndex]);
|
|
20
34
|
const open = useCallback((reason, pointer) => {
|
|
35
|
+
cancelLongPress();
|
|
21
36
|
const activeElement = document.activeElement;
|
|
22
37
|
returnFocusRef.current = activeElement instanceof HTMLElement && hostRef.current?.contains(activeElement)
|
|
23
38
|
? activeElement
|
|
@@ -26,7 +41,7 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
|
|
|
26
41
|
setAnchor(resolveContextMenuAnchor(reason, pointer, rect === null ? null : { left: rect.left, bottom: rect.bottom }));
|
|
27
42
|
setActiveIndex(0);
|
|
28
43
|
typeahead.current = { value: "", time: 0 };
|
|
29
|
-
}, []);
|
|
44
|
+
}, [cancelLongPress]);
|
|
30
45
|
const setMenuRef = useCallback((node) => {
|
|
31
46
|
menuRef.current = node;
|
|
32
47
|
setMenuMounted(node !== null);
|
|
@@ -67,7 +82,7 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
|
|
|
67
82
|
return;
|
|
68
83
|
}
|
|
69
84
|
if (event.key.length === 1 && event.key !== " " && !event.ctrlKey && !event.metaKey && !event.altKey) {
|
|
70
|
-
const result = resolveMenuTypeahead(enabled,
|
|
85
|
+
const result = resolveMenuTypeahead(enabled, resolvedActiveIndex, event.key, typeahead.current, Date.now());
|
|
71
86
|
typeahead.current = result.state;
|
|
72
87
|
if (result.index !== undefined) {
|
|
73
88
|
event.preventDefault();
|
|
@@ -77,7 +92,7 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
|
|
|
77
92
|
}
|
|
78
93
|
if (event.key === "Enter" || event.key === " ") {
|
|
79
94
|
event.preventDefault();
|
|
80
|
-
const item = enabled[
|
|
95
|
+
const item = enabled[resolvedActiveIndex];
|
|
81
96
|
if (item) {
|
|
82
97
|
onAction(item.id);
|
|
83
98
|
setAnchor(null);
|
|
@@ -100,7 +115,7 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
|
|
|
100
115
|
document.removeEventListener("keydown", onKeyDown);
|
|
101
116
|
document.removeEventListener("pointerdown", onPointerDown);
|
|
102
117
|
};
|
|
103
|
-
}, [anchor,
|
|
118
|
+
}, [anchor, resolvedActiveIndex, enabled, onAction]);
|
|
104
119
|
return (_jsxs("div", { ref: hostRef, tabIndex: 0, className: classNames("hjm-context-menu-host", className), style: layoutStyle, onContextMenu: (event) => {
|
|
105
120
|
event.preventDefault();
|
|
106
121
|
open("pointer", { x: event.clientX, y: event.clientY });
|
|
@@ -114,10 +129,16 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
|
|
|
114
129
|
}, onPointerDown: (event) => {
|
|
115
130
|
if (event.pointerType !== "touch")
|
|
116
131
|
return;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
132
|
+
cancelLongPress();
|
|
133
|
+
const origin = { x: event.clientX, y: event.clientY };
|
|
134
|
+
pressOrigin.current = origin;
|
|
135
|
+
longPress.current = setTimeout(() => open("longPress", origin), longPressDelay);
|
|
136
|
+
}, onPointerMove: (event) => {
|
|
137
|
+
const origin = pressOrigin.current;
|
|
138
|
+
// Treat a 10px move as scrolling, while tolerating small touch jitter.
|
|
139
|
+
if (origin && Math.hypot(event.clientX - origin.x, event.clientY - origin.y) >= 10)
|
|
140
|
+
cancelLongPress();
|
|
141
|
+
}, onPointerUp: cancelLongPress, onPointerCancel: cancelLongPress, children: [children, anchor !== null ? (_jsx(AnchoredPortal, { anchorRef: hostRef, children: _jsx("div", { role: "menu", id: menuId, ref: setMenuRef, tabIndex: -1, "aria-activedescendant": enabled[resolvedActiveIndex] ? `${menuId}-item-${resolvedActiveIndex}` : undefined, "aria-label": accessibilityLabel, className: "hjm-context-menu", style: {
|
|
121
142
|
top: anchor.y,
|
|
122
143
|
left: anchor.x,
|
|
123
144
|
zIndex: getModalLayer(0),
|
|
@@ -126,7 +147,7 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
|
|
|
126
147
|
// A newly positioned menu can enter a stationary cursor; that
|
|
127
148
|
// boundary event must not undo the user's keyboard selection.
|
|
128
149
|
const index = enabled.findIndex((candidate) => candidate.id === item.id);
|
|
129
|
-
return (_jsxs("div", { id: index >= 0 ? `${menuId}-item-${index}` : undefined, role: "menuitem", "aria-disabled": item.disabled || undefined, "data-active": index ===
|
|
150
|
+
return (_jsxs("div", { id: index >= 0 ? `${menuId}-item-${index}` : undefined, role: "menuitem", "aria-disabled": item.disabled || undefined, "data-active": index === resolvedActiveIndex && !item.disabled ? "" : undefined, "data-tone": item.tone, className: "hjm-context-menu__item", onMouseMove: () => { if (index >= 0)
|
|
130
151
|
setActiveIndex(index); }, onClick: () => {
|
|
131
152
|
if (item.disabled)
|
|
132
153
|
return;
|
package/dist/context-menu.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-menu.js","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"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","sourcesContent":["import { resolveMenuTypeahead } from \"./menu-typeahead.js\";\nimport {\n resolveContextMenuAnchor,\n type ContextMenuAnchor,\n type ContextMenuItemDescriptor,\n type ContextMenuOpenReason,\n} from \"@hjmds/design-contracts/components/context-menu\";\nimport { menuRecipe } from \"@hjmds/design-contracts/recipes\";\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { classNames } from \"./internal.js\";\nimport { HjmPortal, getModalLayer } from \"./modal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type ContextMenuProps<Key extends string = string> = Readonly<{\n /** The region the menu belongs to; right-click and long-press are bound here. */\n children: ReactNode;\n items: readonly ContextMenuItemDescriptor<Key>[];\n accessibilityLabel: string;\n onAction: (id: Key) => void;\n className?: string;\n /** Canonical layout-only placement on the in-flow host; the menu popup itself is anchored, not placed. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst longPressDelay = 500;\n\nexport function ContextMenu<Key extends string = string>({\n children,\n items,\n accessibilityLabel,\n onAction,\n className,\n layoutStyle,\n}: ContextMenuProps<Key>) {\n const hostRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const [menuMounted, setMenuMounted] = useState(false);\n const returnFocusRef = useRef<HTMLElement | null>(null);\n const menuId = useId();\n const [anchor, setAnchor] = useState<ContextMenuAnchor | null>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const longPress = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const typeahead = useRef({ value: \"\", time: 0 });\n const enabled = items.filter((item) => !item.disabled);\n\n const open = useCallback((reason: ContextMenuOpenReason, pointer: ContextMenuAnchor | null) => {\n const activeElement = document.activeElement;\n returnFocusRef.current = activeElement instanceof HTMLElement && hostRef.current?.contains(activeElement)\n ? activeElement\n : hostRef.current;\n const rect = hostRef.current?.getBoundingClientRect() ?? null;\n setAnchor(resolveContextMenuAnchor(\n reason,\n pointer,\n rect === null ? null : { left: rect.left, bottom: rect.bottom },\n ));\n setActiveIndex(0);\n typeahead.current = { value: \"\", time: 0 };\n }, []);\n\n const setMenuRef = useCallback((node: HTMLDivElement | null) => {\n menuRef.current = node;\n setMenuMounted(node !== null);\n }, []);\n\n useLayoutEffect(() => {\n if (anchor === null || !menuMounted || menuRef.current === null) return;\n menuRef.current.focus();\n const rect = menuRef.current.getBoundingClientRect();\n const x = Math.min(Math.max(anchor.x, 0), Math.max(window.innerWidth - rect.width, 0));\n const y = Math.min(Math.max(anchor.y, 0), Math.max(window.innerHeight - rect.height, 0));\n // The contract promises viewport coordinates; post-render measurement keeps long labels onscreen, where guessed dimensions could not account for wrapping.\n if (x !== anchor.x || y !== anchor.y) setAnchor({ x, y });\n }, [anchor, menuMounted]);\n\n useEffect(() => {\n if (anchor === null) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n setAnchor(null);\n // Focus goes back where the menu was opened from, not to the body.\n returnFocusRef.current?.focus();\n return;\n }\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((previous) => {\n const next = event.key === \"ArrowDown\" ? previous + 1 : previous - 1;\n return (next + enabled.length) % Math.max(enabled.length, 1);\n });\n return;\n }\n if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n setActiveIndex(event.key === \"Home\" ? 0 : Math.max(enabled.length - 1, 0));\n return;\n }\n if (event.key.length === 1 && event.key !== \" \" && !event.ctrlKey && !event.metaKey && !event.altKey) {\n const result = resolveMenuTypeahead(enabled, activeIndex, event.key, typeahead.current, Date.now());\n typeahead.current = result.state;\n if (result.index !== undefined) {\n event.preventDefault();\n setActiveIndex(result.index);\n }\n return;\n }\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n const item = enabled[activeIndex];\n if (item) { onAction(item.id as Key); setAnchor(null); returnFocusRef.current?.focus(); }\n return;\n }\n if (event.key === \"Tab\") {\n setAnchor(null);\n }\n };\n const onPointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node) || !menuRef.current?.contains(event.target)) {\n setAnchor(null);\n }\n };\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"pointerdown\", onPointerDown);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"pointerdown\", onPointerDown);\n };\n }, [anchor, activeIndex, enabled, onAction]);\n\n return (\n <div\n ref={hostRef}\n tabIndex={0}\n className={classNames(\"hjm-context-menu-host\", className)}\n style={layoutStyle}\n onContextMenu={(event) => {\n event.preventDefault();\n open(\"pointer\", { x: event.clientX, y: event.clientY });\n }}\n onKeyDown={(event) => {\n // Shift+F10 and the Menu key are how a keyboard opens a context menu;\n // without them the feature does not exist without a pointer.\n if ((event.shiftKey && event.key === \"F10\") || event.key === \"ContextMenu\") {\n event.preventDefault();\n open(\"keyboard\", null);\n }\n }}\n onPointerDown={(event) => {\n if (event.pointerType !== \"touch\") return;\n longPress.current = setTimeout(() => open(\"longPress\", { x: event.clientX, y: event.clientY }), longPressDelay);\n }}\n onPointerUp={() => { if (longPress.current !== undefined) clearTimeout(longPress.current); }}\n onPointerCancel={() => { if (longPress.current !== undefined) clearTimeout(longPress.current); }}\n >\n {children}\n {anchor !== null ? (\n <HjmPortal>\n <div\n role=\"menu\"\n id={menuId}\n ref={setMenuRef}\n tabIndex={-1}\n aria-activedescendant={enabled[activeIndex] ? `${menuId}-item-${activeIndex}` : undefined}\n aria-label={accessibilityLabel}\n className=\"hjm-context-menu\"\n style={{\n top: anchor.y,\n left: anchor.x,\n zIndex: getModalLayer(0),\n minInlineSize: menuRecipe.minWidth,\n } as CSSProperties}\n >\n {items.map((item) => {\n // A newly positioned menu can enter a stationary cursor; that\n // boundary event must not undo the user's keyboard selection.\n const index = enabled.findIndex((candidate) => candidate.id === item.id);\n return (\n <div\n key={item.id}\n id={index >= 0 ? `${menuId}-item-${index}` : undefined}\n role=\"menuitem\"\n aria-disabled={item.disabled || undefined}\n data-active={index === activeIndex && !item.disabled ? \"\" : undefined}\n data-tone={item.tone}\n className=\"hjm-context-menu__item\"\n onMouseMove={() => { if (index >= 0) setActiveIndex(index); }}\n onClick={() => {\n if (item.disabled) return;\n onAction(item.id as Key);\n setAnchor(null);\n returnFocusRef.current?.focus();\n }}\n >\n <span>{item.label}</span>\n {item.shortcut ? <kbd className=\"hjm-context-menu__shortcut\">{item.shortcut}</kbd> : null}\n </div>\n );\n })}\n </div>\n </HjmPortal>\n ) : null}\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"context-menu.js","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"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","sourcesContent":["import { resolveMenuTypeahead } from \"./menu-typeahead.js\";\nimport {\n resolveContextMenuAnchor,\n type ContextMenuAnchor,\n type ContextMenuItemDescriptor,\n type ContextMenuOpenReason,\n} from \"@hjmds/design-contracts/components/context-menu\";\nimport { menuRecipe } from \"@hjmds/design-contracts/recipes\";\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { classNames } from \"./internal.js\";\nimport { AnchoredPortal } from \"./portal.js\";\nimport { getModalLayer } from \"./modal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type ContextMenuProps<Key extends string = string> = Readonly<{\n /** The region the menu belongs to; right-click and long-press are bound here. */\n children: ReactNode;\n items: readonly ContextMenuItemDescriptor<Key>[];\n accessibilityLabel: string;\n onAction: (id: Key) => void;\n className?: string;\n /** Canonical layout-only placement on the in-flow host; the menu popup itself is anchored, not placed. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst longPressDelay = 500;\n\nexport function ContextMenu<Key extends string = string>({\n children,\n items,\n accessibilityLabel,\n onAction,\n className,\n layoutStyle,\n}: ContextMenuProps<Key>) {\n const hostRef = useRef<HTMLDivElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n const [menuMounted, setMenuMounted] = useState(false);\n const returnFocusRef = useRef<HTMLElement | null>(null);\n const menuId = useId();\n const [anchor, setAnchor] = useState<ContextMenuAnchor | null>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const longPress = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const pressOrigin = useRef<ContextMenuAnchor | null>(null);\n const cancelLongPress = useCallback(() => {\n if (longPress.current !== undefined) clearTimeout(longPress.current);\n longPress.current = undefined;\n pressOrigin.current = null;\n }, []);\n useEffect(() => cancelLongPress, [cancelLongPress]);\n const typeahead = useRef({ value: \"\", time: 0 });\n const enabled = items.filter((item) => !item.disabled);\n const resolvedActiveIndex = Math.min(activeIndex, Math.max(0, enabled.length - 1));\n useLayoutEffect(() => {\n if (activeIndex !== resolvedActiveIndex) setActiveIndex(resolvedActiveIndex);\n }, [activeIndex, resolvedActiveIndex]);\n\n const open = useCallback((reason: ContextMenuOpenReason, pointer: ContextMenuAnchor | null) => {\n cancelLongPress();\n const activeElement = document.activeElement;\n returnFocusRef.current = activeElement instanceof HTMLElement && hostRef.current?.contains(activeElement)\n ? activeElement\n : hostRef.current;\n const rect = hostRef.current?.getBoundingClientRect() ?? null;\n setAnchor(resolveContextMenuAnchor(\n reason,\n pointer,\n rect === null ? null : { left: rect.left, bottom: rect.bottom },\n ));\n setActiveIndex(0);\n typeahead.current = { value: \"\", time: 0 };\n }, [cancelLongPress]);\n\n const setMenuRef = useCallback((node: HTMLDivElement | null) => {\n menuRef.current = node;\n setMenuMounted(node !== null);\n }, []);\n\n useLayoutEffect(() => {\n if (anchor === null || !menuMounted || menuRef.current === null) return;\n menuRef.current.focus();\n const rect = menuRef.current.getBoundingClientRect();\n const x = Math.min(Math.max(anchor.x, 0), Math.max(window.innerWidth - rect.width, 0));\n const y = Math.min(Math.max(anchor.y, 0), Math.max(window.innerHeight - rect.height, 0));\n // The contract promises viewport coordinates; post-render measurement keeps long labels onscreen, where guessed dimensions could not account for wrapping.\n if (x !== anchor.x || y !== anchor.y) setAnchor({ x, y });\n }, [anchor, menuMounted]);\n\n useEffect(() => {\n if (anchor === null) return;\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n setAnchor(null);\n // Focus goes back where the menu was opened from, not to the body.\n returnFocusRef.current?.focus();\n return;\n }\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n setActiveIndex((previous) => {\n const next = event.key === \"ArrowDown\" ? previous + 1 : previous - 1;\n return (next + enabled.length) % Math.max(enabled.length, 1);\n });\n return;\n }\n if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n setActiveIndex(event.key === \"Home\" ? 0 : Math.max(enabled.length - 1, 0));\n return;\n }\n if (event.key.length === 1 && event.key !== \" \" && !event.ctrlKey && !event.metaKey && !event.altKey) {\n const result = resolveMenuTypeahead(enabled, resolvedActiveIndex, event.key, typeahead.current, Date.now());\n typeahead.current = result.state;\n if (result.index !== undefined) {\n event.preventDefault();\n setActiveIndex(result.index);\n }\n return;\n }\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n const item = enabled[resolvedActiveIndex];\n if (item) { onAction(item.id as Key); setAnchor(null); returnFocusRef.current?.focus(); }\n return;\n }\n if (event.key === \"Tab\") {\n setAnchor(null);\n }\n };\n const onPointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node) || !menuRef.current?.contains(event.target)) {\n setAnchor(null);\n }\n };\n document.addEventListener(\"keydown\", onKeyDown);\n document.addEventListener(\"pointerdown\", onPointerDown);\n return () => {\n document.removeEventListener(\"keydown\", onKeyDown);\n document.removeEventListener(\"pointerdown\", onPointerDown);\n };\n }, [anchor, resolvedActiveIndex, enabled, onAction]);\n\n return (\n <div\n ref={hostRef}\n tabIndex={0}\n className={classNames(\"hjm-context-menu-host\", className)}\n style={layoutStyle}\n onContextMenu={(event) => {\n event.preventDefault();\n open(\"pointer\", { x: event.clientX, y: event.clientY });\n }}\n onKeyDown={(event) => {\n // Shift+F10 and the Menu key are how a keyboard opens a context menu;\n // without them the feature does not exist without a pointer.\n if ((event.shiftKey && event.key === \"F10\") || event.key === \"ContextMenu\") {\n event.preventDefault();\n open(\"keyboard\", null);\n }\n }}\n onPointerDown={(event) => {\n if (event.pointerType !== \"touch\") return;\n cancelLongPress();\n const origin = { x: event.clientX, y: event.clientY };\n pressOrigin.current = origin;\n longPress.current = setTimeout(() => open(\"longPress\", origin), longPressDelay);\n }}\n onPointerMove={(event) => {\n const origin = pressOrigin.current;\n // Treat a 10px move as scrolling, while tolerating small touch jitter.\n if (origin && Math.hypot(event.clientX - origin.x, event.clientY - origin.y) >= 10) cancelLongPress();\n }}\n onPointerUp={cancelLongPress}\n onPointerCancel={cancelLongPress}\n >\n {children}\n {anchor !== null ? (\n <AnchoredPortal anchorRef={hostRef}>\n <div\n role=\"menu\"\n id={menuId}\n ref={setMenuRef}\n tabIndex={-1}\n aria-activedescendant={enabled[resolvedActiveIndex] ? `${menuId}-item-${resolvedActiveIndex}` : undefined}\n aria-label={accessibilityLabel}\n className=\"hjm-context-menu\"\n style={{\n top: anchor.y,\n left: anchor.x,\n zIndex: getModalLayer(0),\n minInlineSize: menuRecipe.minWidth,\n } as CSSProperties}\n >\n {items.map((item) => {\n // A newly positioned menu can enter a stationary cursor; that\n // boundary event must not undo the user's keyboard selection.\n const index = enabled.findIndex((candidate) => candidate.id === item.id);\n return (\n <div\n key={item.id}\n id={index >= 0 ? `${menuId}-item-${index}` : undefined}\n role=\"menuitem\"\n aria-disabled={item.disabled || undefined}\n data-active={index === resolvedActiveIndex && !item.disabled ? \"\" : undefined}\n data-tone={item.tone}\n className=\"hjm-context-menu__item\"\n onMouseMove={() => { if (index >= 0) setActiveIndex(index); }}\n onClick={() => {\n if (item.disabled) return;\n onAction(item.id as Key);\n setAnchor(null);\n returnFocusRef.current?.focus();\n }}\n >\n <span>{item.label}</span>\n {item.shortcut ? <kbd className=\"hjm-context-menu__shortcut\">{item.shortcut}</kbd> : null}\n </div>\n );\n })}\n </div>\n </AnchoredPortal>\n ) : null}\n </div>\n );\n}\n"]}
|
package/dist/date-entry.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export type DateEntryProps = DateEntryControlProps & Readonly<{
|
|
|
4
4
|
className?: string;
|
|
5
5
|
layoutStyle?: HjmCompositionStyleProp;
|
|
6
6
|
}>;
|
|
7
|
-
/**
|
|
7
|
+
/** Date policy stays here; FieldGroup owns related-input semantics, feedback and edit lifetime. */
|
|
8
8
|
export declare function DateEntry(props: DateEntryProps): import("react").JSX.Element;
|
|
9
9
|
//# sourceMappingURL=date-entry.d.ts.map
|
package/dist/date-entry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-entry.d.ts","sourceRoot":"","sources":["../src/date-entry.tsx"],"names":[],"mappings":"AACA,OAAO,EAAiD,KAAK,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"date-entry.d.ts","sourceRoot":"","sources":["../src/date-entry.tsx"],"names":[],"mappings":"AACA,OAAO,EAAiD,KAAK,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAG/H,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,MAAM,MAAM,cAAc,GAAG,qBAAqB,GAAG,QAAQ,CAAC;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAC,CAAC;AAC7H,mGAAmG;AACnG,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAuB9C"}
|
package/dist/date-entry.js
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from "react";
|
|
3
3
|
import { resolveDateEntryControl, updateDateEntryDraft } from "@hjmds/design-contracts/date-entry";
|
|
4
|
+
import { FieldGroup } from "./field-group.js";
|
|
4
5
|
import { TextField } from "./forms.js";
|
|
5
|
-
/**
|
|
6
|
+
/** Date policy stays here; FieldGroup owns related-input semantics, feedback and edit lifetime. */
|
|
6
7
|
export function DateEntry(props) {
|
|
7
|
-
const id = useId();
|
|
8
8
|
const resolved = resolveDateEntryControl(props);
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const current = useRef(props);
|
|
10
|
+
current.current = props;
|
|
11
|
+
return _jsx(FieldGroup, { layout: "inline", ...(props.className === undefined ? {} : { className: props.className }), ...(props.layoutStyle === undefined ? {} : { layoutStyle: props.layoutStyle }), descriptor: { label: props.labels.label, disabled: props.disabled ?? false,
|
|
12
|
+
...(props.description ? { description: props.description } : {}),
|
|
13
|
+
...(resolved.error ? { error: { message: resolved.error, fieldIds: resolved.fields.filter(field => field.invalid).map(field => field.part) } } : {}),
|
|
14
|
+
fields: resolved.fields.map(field => ({ id: field.part, label: props.labels[field.part] })) }, renderField: ({ id, controlProps, guardChange }) => {
|
|
15
|
+
const field = resolved.fields.find(field => field.part === id);
|
|
16
|
+
if (!field)
|
|
17
|
+
return null;
|
|
18
|
+
return _jsx(TextField, { ...controlProps, value: field.value, type: "text", inputMode: field.part === "month" && props.monthInput !== "numeric" ? "text" : "numeric", autoComplete: props.purpose === "birthdate" ? `bday-${field.part}` : "off", required: props.required ?? false, readOnly: props.readOnly ?? false, onBlur: () => current.current.onBlur?.(field.part), onValueChange: guardChange((value) => {
|
|
19
|
+
// Retained host callbacks must not edit an old draft or bypass a new read-only policy.
|
|
20
|
+
const latest = current.current;
|
|
21
|
+
if (!latest.readOnly)
|
|
22
|
+
latest.onValueChange(updateDateEntryDraft(latest.value, field.part, value));
|
|
23
|
+
}) });
|
|
24
|
+
} });
|
|
12
25
|
}
|
|
13
26
|
//# sourceMappingURL=date-entry.js.map
|
package/dist/date-entry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-entry.js","sourceRoot":"","sources":["../src/date-entry.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"date-entry.js","sourceRoot":"","sources":["../src/date-entry.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAA8B,MAAM,oCAAoC,CAAC;AAC/H,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,mGAAmG;AACnG,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,MAAM,QAAQ,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;IACxB,OAAO,KAAC,UAAU,IAAC,MAAM,EAAC,QAAQ,KAAK,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,KAAM,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,EAC3L,UAAU,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK;YACxE,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAChE,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpJ,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAC/F,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,EAAE,EAAE;YACjD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;YAC/D,IAAI,CAAC,KAAK;gBAAE,OAAO,IAAI,CAAC;YACxB,OAAO,KAAC,SAAS,OAAK,YAAY,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAC,MAAM,EACjE,SAAS,EAAE,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACxF,YAAY,EAAE,KAAK,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,EAC1E,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,EACpE,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAClD,aAAa,EAAE,WAAW,CAAC,CAAC,KAAa,EAAE,EAAE;oBAC3C,uFAAuF;oBACvF,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;oBAC/B,IAAI,CAAC,MAAM,CAAC,QAAQ;wBAAE,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;gBACpG,CAAC,CAAC,GAAI,CAAC;QACX,CAAC,GAAI,CAAC;AACV,CAAC","sourcesContent":["import { useRef } from \"react\";\nimport { resolveDateEntryControl, updateDateEntryDraft, type DateEntryControlProps } from \"@hjmds/design-contracts/date-entry\";\nimport { FieldGroup } from \"./field-group.js\";\nimport { TextField } from \"./forms.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\nexport type DateEntryProps = DateEntryControlProps & Readonly<{ className?: string; layoutStyle?: HjmCompositionStyleProp }>;\n/** Date policy stays here; FieldGroup owns related-input semantics, feedback and edit lifetime. */\nexport function DateEntry(props: DateEntryProps) {\n const resolved = resolveDateEntryControl(props);\n const current = useRef(props);\n current.current = props;\n return <FieldGroup layout=\"inline\" {...(props.className === undefined ? {} : { className: props.className })} {...(props.layoutStyle === undefined ? {} : { layoutStyle: props.layoutStyle })}\n descriptor={{ label: props.labels.label, disabled: props.disabled ?? false,\n ...(props.description ? { description: props.description } : {}),\n ...(resolved.error ? { error: { message: resolved.error, fieldIds: resolved.fields.filter(field => field.invalid).map(field => field.part) } } : {}),\n fields: resolved.fields.map(field => ({ id: field.part, label: props.labels[field.part] })) }}\n renderField={({ id, controlProps, guardChange }) => {\n const field = resolved.fields.find(field => field.part === id);\n if (!field) return null;\n return <TextField {...controlProps} value={field.value} type=\"text\"\n inputMode={field.part === \"month\" && props.monthInput !== \"numeric\" ? \"text\" : \"numeric\"}\n autoComplete={props.purpose === \"birthdate\" ? `bday-${field.part}` : \"off\"}\n required={props.required ?? false} readOnly={props.readOnly ?? false}\n onBlur={() => current.current.onBlur?.(field.part)}\n onValueChange={guardChange((value: string) => {\n // Retained host callbacks must not edit an old draft or bypass a new read-only policy.\n const latest = current.current;\n if (!latest.readOnly) latest.onValueChange(updateDateEntryDraft(latest.value, field.part, value));\n })} />;\n }} />;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../src/date-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC9B,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,cAAc,EACpB,MAAM,gDAAgD,CAAC;AACxD,OAAO,
|
|
1
|
+
{"version":3,"file":"date-picker.d.ts","sourceRoot":"","sources":["../src/date-picker.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC9B,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAGL,KAAK,oBAAoB,EAEzB,KAAK,cAAc,EACpB,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAY,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAErF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAItE,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE/E,MAAM,MAAM,eAAe,CAAC,OAAO,GAAG,OAAO,IAAI,QAAQ,CAAC;IACxD,UAAU,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,qBAAqB,EAAE,6BAA6B,CAAC,OAAO,CAAC,CAAC;IAC9D,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,oBAAoB,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,cAAc,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IAClG,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,wBAAwB,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC;IAC3E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAEH,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,OAAO,EAAE,EAClC,UAAU,EACV,UAAU,EACV,qBAAqB,EACrB,aAAa,EACb,SAAS,EACT,UAAU,EACV,UAAU,EACV,IAAe,EACf,WAAW,EACX,KAAK,EACL,iBAAiB,EACjB,oBAAoB,EACpB,SAAS,EACT,WAAW,GACZ,EAAE,eAAe,CAAC,OAAO,CAAC,+BA0I1B"}
|
package/dist/date-picker.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import {} from "@hjmds/design-contracts/components/calendar";
|
|
3
3
|
import { resolveDatePickerTriggerText, validateDatePickerDescriptor, } from "@hjmds/design-contracts/components/date-picker";
|
|
4
|
-
import { useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
|
|
4
|
+
import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
|
|
5
5
|
import { Calendar } from "./calendar.js";
|
|
6
6
|
import { classNames } from "./internal.js";
|
|
7
7
|
const popoverViewportInset = 16;
|
|
@@ -18,11 +18,17 @@ export function DatePicker({ descriptor, monthLabel, composeAccessibleName, prev
|
|
|
18
18
|
const controlledSelection = descriptor.selectedDate !== undefined;
|
|
19
19
|
const [internalSelection, setInternalSelection] = useState(descriptor.defaultSelectedDate ?? null);
|
|
20
20
|
const selectedDate = controlledSelection ? descriptor.selectedDate ?? null : internalSelection;
|
|
21
|
-
const
|
|
21
|
+
const closeReasonRef = useRef("programmatic");
|
|
22
|
+
const requestOpen = useCallback((next, reason) => {
|
|
23
|
+
closeReasonRef.current = next ? "programmatic" : reason;
|
|
22
24
|
if (!controlledOpen)
|
|
23
25
|
setInternalOpen(next);
|
|
24
26
|
descriptor.onOpenChange?.(next, reason);
|
|
25
|
-
};
|
|
27
|
+
}, [controlledOpen, descriptor.onOpenChange]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (open && (descriptor.disabled || descriptor.readOnly))
|
|
30
|
+
requestOpen(false, "programmatic");
|
|
31
|
+
}, [descriptor.disabled, descriptor.readOnly, open, requestOpen]);
|
|
26
32
|
const commit = (date, reason) => {
|
|
27
33
|
// A controlled open field can become read-only while its grid remains mounted.
|
|
28
34
|
if (descriptor.disabled || descriptor.readOnly)
|
|
@@ -31,13 +37,17 @@ export function DatePicker({ descriptor, monthLabel, composeAccessibleName, prev
|
|
|
31
37
|
setInternalSelection(date);
|
|
32
38
|
descriptor.onSelectionChange?.(date, reason);
|
|
33
39
|
requestOpen(false, reason === "activate" ? "selection" : "clear");
|
|
40
|
+
// Clearing a closed field removes the focused clear button without an open transition.
|
|
41
|
+
if (reason === "clear")
|
|
42
|
+
triggerRef.current?.focus();
|
|
34
43
|
};
|
|
35
44
|
const wasOpenRef = useRef(open);
|
|
36
45
|
useEffect(() => {
|
|
37
46
|
const wasOpen = wasOpenRef.current;
|
|
38
47
|
wasOpenRef.current = open;
|
|
39
48
|
if (!open) {
|
|
40
|
-
|
|
49
|
+
// Outside/Tab dismissal keeps the user's new target; confirmation/Escape returns here.
|
|
50
|
+
if (wasOpen && closeReasonRef.current !== "outside" && closeReasonRef.current !== "blur")
|
|
41
51
|
triggerRef.current?.focus();
|
|
42
52
|
return;
|
|
43
53
|
}
|
|
@@ -51,7 +61,7 @@ export function DatePicker({ descriptor, monthLabel, composeAccessibleName, prev
|
|
|
51
61
|
return () => {
|
|
52
62
|
document.removeEventListener("pointerdown", outside);
|
|
53
63
|
};
|
|
54
|
-
}, [open]);
|
|
64
|
+
}, [open, requestOpen]);
|
|
55
65
|
// Keep the popover inside the viewport (2026-09-30 audit WR-0930-3): shift inline, flip up
|
|
56
66
|
// when short. Not portaled: Calendar autoFocus runs before a portal popup is measured.
|
|
57
67
|
useLayoutEffect(() => {
|
|
@@ -96,7 +106,13 @@ export function DatePicker({ descriptor, monthLabel, composeAccessibleName, prev
|
|
|
96
106
|
: descriptor.grid;
|
|
97
107
|
const label = descriptor.label ?? descriptor.accessibilityLabel;
|
|
98
108
|
const triggerText = resolveDatePickerTriggerText(descriptor);
|
|
99
|
-
return (_jsxs("div", { style: layoutStyle, className: classNames("hjm-date-picker", className),
|
|
109
|
+
return (_jsxs("div", { style: layoutStyle, className: classNames("hjm-date-picker", className), onBlur: (event) => {
|
|
110
|
+
const field = event.currentTarget;
|
|
111
|
+
queueMicrotask(() => {
|
|
112
|
+
if (open && dialogRef.current && !field.contains(document.activeElement))
|
|
113
|
+
requestOpen(false, "blur");
|
|
114
|
+
});
|
|
115
|
+
}, "data-disabled": descriptor.disabled || undefined, "data-invalid": descriptor.invalid || error !== undefined || undefined, "data-size": size, children: [descriptor.label === undefined ? null : _jsx("span", { className: "hjm-date-picker__label", children: descriptor.label }), _jsxs("div", { className: "hjm-date-picker__anchor", children: [_jsxs("button", { "aria-controls": open ? dialogId : undefined, "aria-expanded": open, "aria-haspopup": "dialog", "aria-label": descriptor.accessibilityLabel ?? `${label}, ${triggerText}`, className: "hjm-date-picker__trigger", disabled: descriptor.disabled, onClick: () => !descriptor.readOnly && requestOpen(!open, "trigger"), ref: triggerRef, type: "button", children: [_jsx("span", { "aria-hidden": "true", children: "\u25A3" }), _jsx("span", { "data-placeholder": descriptor.displayValue === null || undefined, children: triggerText })] }), selectedDate === null ? null : (_jsx("button", { "aria-label": clearLabel, className: "hjm-date-picker__clear", disabled: descriptor.disabled || descriptor.readOnly, onClick: () => commit(null, "clear"), type: "button", children: "\u00D7" })), open ? (_jsxs("div", { "aria-label": label, className: "hjm-date-picker__popover", id: dialogId, ref: dialogRef, role: "dialog", onKeyDown: (event) => { if (event.key === "Escape") {
|
|
100
116
|
event.preventDefault();
|
|
101
117
|
requestOpen(false, "escape");
|
|
102
118
|
} }, children: [_jsx("button", { "aria-label": closeLabel, className: "hjm-date-picker__calendar-close", onClick: () => requestOpen(false, "trigger"), type: "button", children: "\u00D7" }), _jsx(Calendar, { descriptor: { ...calendarDescriptor, grid: calendarGrid, monthLabel, selectedDate, onSelectionChange: (date) => commit(date, "activate") }, composeAccessibleName: composeAccessibleName, size: size, autoFocus: true, ...(previousMonth ? { previousMonth } : {}), ...(nextMonth ? { nextMonth } : {}), ...(renderCellContent ? { renderCellContent } : {}), ...(onNavigateBeyondGrid ? { onNavigateBeyondGrid } : {}) })] })) : null] }), description === undefined ? null : _jsx("span", { className: "hjm-date-picker__description", children: description }), error === undefined ? null : _jsx("span", { className: "hjm-date-picker__error", role: "alert", children: error })] }));
|