@hjmds/react 1.7.0 → 1.9.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.
Files changed (96) hide show
  1. package/README.md +21 -0
  2. package/THIRD_PARTY_NOTICES.md +57 -0
  3. package/dist/advanced-display.d.ts +3 -1
  4. package/dist/advanced-display.d.ts.map +1 -1
  5. package/dist/advanced-display.js +2 -2
  6. package/dist/advanced-display.js.map +1 -1
  7. package/dist/affix.d.ts +10 -0
  8. package/dist/affix.d.ts.map +1 -0
  9. package/dist/affix.js +51 -0
  10. package/dist/affix.js.map +1 -0
  11. package/dist/carousel-motion.d.ts +13 -0
  12. package/dist/carousel-motion.d.ts.map +1 -0
  13. package/dist/carousel-motion.js +35 -0
  14. package/dist/carousel-motion.js.map +1 -0
  15. package/dist/celebration.d.ts +8 -0
  16. package/dist/celebration.d.ts.map +1 -0
  17. package/dist/celebration.js +51 -0
  18. package/dist/celebration.js.map +1 -0
  19. package/dist/color-picker.d.ts +18 -0
  20. package/dist/color-picker.d.ts.map +1 -0
  21. package/dist/color-picker.js +40 -0
  22. package/dist/color-picker.js.map +1 -0
  23. package/dist/content-transition.d.ts +18 -0
  24. package/dist/content-transition.d.ts.map +1 -0
  25. package/dist/content-transition.js +33 -0
  26. package/dist/content-transition.js.map +1 -0
  27. package/dist/context-menu.d.ts.map +1 -1
  28. package/dist/context-menu.js +36 -9
  29. package/dist/context-menu.js.map +1 -1
  30. package/dist/date-picker.d.ts.map +1 -1
  31. package/dist/date-picker.js +39 -1
  32. package/dist/date-picker.js.map +1 -1
  33. package/dist/evidence.d.ts +106 -2
  34. package/dist/evidence.d.ts.map +1 -1
  35. package/dist/evidence.js +136 -64
  36. package/dist/evidence.js.map +1 -1
  37. package/dist/layout.d.ts.map +1 -1
  38. package/dist/layout.js +3 -2
  39. package/dist/layout.js.map +1 -1
  40. package/dist/masonry.d.ts +14 -0
  41. package/dist/masonry.d.ts.map +1 -0
  42. package/dist/masonry.js +13 -0
  43. package/dist/masonry.js.map +1 -0
  44. package/dist/menu-morph.d.ts +12 -0
  45. package/dist/menu-morph.d.ts.map +1 -0
  46. package/dist/menu-morph.js +80 -0
  47. package/dist/menu-morph.js.map +1 -0
  48. package/dist/menubar.d.ts.map +1 -1
  49. package/dist/menubar.js +24 -15
  50. package/dist/menubar.js.map +1 -1
  51. package/dist/modal.d.ts +1 -0
  52. package/dist/modal.d.ts.map +1 -1
  53. package/dist/modal.js.map +1 -1
  54. package/dist/navigation.d.ts.map +1 -1
  55. package/dist/navigation.js +13 -1
  56. package/dist/navigation.js.map +1 -1
  57. package/dist/overlays.d.ts.map +1 -1
  58. package/dist/overlays.js +33 -2
  59. package/dist/overlays.js.map +1 -1
  60. package/dist/qr-code.d.ts +11 -0
  61. package/dist/qr-code.d.ts.map +1 -0
  62. package/dist/qr-code.js +14 -0
  63. package/dist/qr-code.js.map +1 -0
  64. package/dist/sortable.d.ts +14 -0
  65. package/dist/sortable.d.ts.map +1 -0
  66. package/dist/sortable.js +87 -0
  67. package/dist/sortable.js.map +1 -0
  68. package/dist/statistic-motion.d.ts +12 -0
  69. package/dist/statistic-motion.d.ts.map +1 -0
  70. package/dist/statistic-motion.js +23 -0
  71. package/dist/statistic-motion.js.map +1 -0
  72. package/dist/styles.css +166 -134
  73. package/dist/styles.layered.css +166 -134
  74. package/dist/swipe-actions.d.ts +13 -0
  75. package/dist/swipe-actions.d.ts.map +1 -0
  76. package/dist/swipe-actions.js +25 -0
  77. package/dist/swipe-actions.js.map +1 -0
  78. package/dist/theme.d.ts.map +1 -1
  79. package/dist/theme.js +2 -1
  80. package/dist/theme.js.map +1 -1
  81. package/dist/thinking-orb.d.ts +9 -0
  82. package/dist/thinking-orb.d.ts.map +1 -0
  83. package/dist/thinking-orb.js +86 -0
  84. package/dist/thinking-orb.js.map +1 -0
  85. package/dist/tree.d.ts.map +1 -1
  86. package/dist/tree.js +14 -3
  87. package/dist/tree.js.map +1 -1
  88. package/dist/virtual-list.d.ts +14 -0
  89. package/dist/virtual-list.d.ts.map +1 -0
  90. package/dist/virtual-list.js +48 -0
  91. package/dist/virtual-list.js.map +1 -0
  92. package/dist/watermark.d.ts +12 -0
  93. package/dist/watermark.d.ts.map +1 -0
  94. package/dist/watermark.js +10 -0
  95. package/dist/watermark.js.map +1 -0
  96. package/package.json +122 -5
@@ -0,0 +1,18 @@
1
+ export type ColorPickerLabels = Readonly<{
2
+ color: string;
3
+ hex: string;
4
+ opacity: string;
5
+ invalid: string;
6
+ }>;
7
+ export type ColorPickerProps = Readonly<{
8
+ label: string;
9
+ labels: ColorPickerLabels;
10
+ value: string;
11
+ onValueChange: (value: string) => void;
12
+ alpha?: boolean;
13
+ disabled?: boolean;
14
+ presets?: readonly string[];
15
+ }>;
16
+ /** Controlled value; invalid text stays local until corrected or escaped. Native color UI supplies RGB only. */
17
+ export declare function ColorPicker({ label, labels, value, onValueChange, alpha, disabled, presets }: ColorPickerProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=color-picker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-picker.d.ts","sourceRoot":"","sources":["../src/color-picker.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC3G,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChG,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClE,CAAC,CAAC;AACH,gHAAgH;AAChH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAa,EAAE,QAAgB,EAAE,OAAY,EAAE,EAAE,gBAAgB,+BAiCnI"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useState } from "react";
3
+ import { colorPickerRecipe, normalizePickerColor, pickerOpacity, withPickerOpacity } from "@hjmds/design-contracts/components/color-picker";
4
+ /** Controlled value; invalid text stays local until corrected or escaped. Native color UI supplies RGB only. */
5
+ export function ColorPicker({ label, labels, value, onValueChange, alpha = false, disabled = false, presets = [] }) {
6
+ const color = normalizePickerColor(value, alpha);
7
+ // HEX is an ordered code, so isolate its LTR text even when the surrounding labels use RTL.
8
+ const palette = [...new Set(presets.map(preset => normalizePickerColor(preset, alpha)))];
9
+ if (![label, ...Object.values(labels)].every(text => text.trim()))
10
+ throw new TypeError("ColorPicker labels must not be empty");
11
+ const id = useId();
12
+ const [draft, setDraft] = useState(color);
13
+ const [invalid, setInvalid] = useState(false);
14
+ useEffect(() => { setDraft(color); setInvalid(false); }, [color]);
15
+ function emit(next) { setDraft(next); setInvalid(false); if (next !== color)
16
+ onValueChange(next); }
17
+ function commit() {
18
+ let next;
19
+ try {
20
+ // A typed 3/6-digit HEX names only the hue. Keep the current opacity instead of resetting it to 100%,
21
+ // matching the native color input; an 8-digit HEX still sets alpha explicitly (2026-09-30 review).
22
+ const digits = draft.trim().replace(/^#/, "").length;
23
+ next = alpha && (digits === 3 || digits === 6) ? normalizePickerColor(draft, false) + color.slice(7) : normalizePickerColor(draft, alpha);
24
+ }
25
+ catch {
26
+ setInvalid(true);
27
+ return;
28
+ }
29
+ emit(next);
30
+ }
31
+ return _jsxs("fieldset", { className: "hjm-color-picker", style: { "--hjm-color-picker-target": `${colorPickerRecipe.minTargetSize}px` }, "data-hjm-color-picker": true, disabled: disabled, children: [_jsx("legend", { children: label }), _jsxs("div", { className: "hjm-color-picker__row", children: [_jsxs("label", { className: "hjm-color-picker__native", children: [_jsx("span", { children: labels.color }), _jsx("input", { type: "color", value: color.slice(0, 7), onChange: event => emit(event.target.value + (alpha ? color.slice(7) : "")) })] }), _jsxs("label", { className: "hjm-color-picker__hex", children: [_jsx("span", { children: labels.hex }), _jsx("input", { type: "text", dir: "ltr", value: draft, spellCheck: false, autoComplete: "off", "aria-invalid": invalid || undefined, "aria-describedby": invalid ? `${id}-error` : undefined, onChange: event => { setDraft(event.target.value); setInvalid(false); }, onBlur: commit, onKeyDown: event => { if (event.key === "Enter") {
32
+ event.preventDefault();
33
+ commit();
34
+ } if (event.key === "Escape") {
35
+ event.preventDefault();
36
+ setDraft(color);
37
+ setInvalid(false);
38
+ } } })] })] }), invalid && _jsx("p", { id: `${id}-error`, role: "alert", className: "hjm-color-picker__error", children: labels.invalid }), alpha && _jsxs("label", { className: "hjm-color-picker__opacity", children: [_jsxs("span", { children: [labels.opacity, " ", _jsxs("output", { children: [pickerOpacity(color), "%"] })] }), _jsx("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))) })] }), _jsx("div", { className: "hjm-color-picker__preview", "aria-hidden": "true", style: { backgroundColor: color } }), palette.length > 0 && _jsx("div", { className: "hjm-color-picker__presets", children: palette.map(preset => _jsxs("button", { type: "button", "aria-pressed": preset === color, onClick: () => emit(preset), children: [_jsx("span", { "aria-hidden": "true", className: "hjm-color-picker__swatch", style: { backgroundColor: preset } }), _jsx("bdi", { dir: "ltr", children: preset })] }, preset)) })] });
39
+ }
40
+ //# sourceMappingURL=color-picker.js.map
@@ -0,0 +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;AACvE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAM5I,gHAAgH;AAChH,MAAM,UAAU,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,GAAG,KAAK,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,GAAG,EAAE,EAAoB;IAClI,MAAM,KAAK,GAAG,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACjD,4FAA4F;IAC5F,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACzF,IAAI,CAAC,CAAC,KAAK,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC/H,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,SAAS,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAClE,SAAS,IAAI,CAAC,IAAY,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,KAAK,KAAK;QAAE,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3G,SAAS,MAAM;QACb,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,sGAAsG;YACtG,mGAAmG;YACnG,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;YACrD,IAAI,GAAG,KAAK,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC5I,CAAC;QAAC,MAAM,CAAC;YAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,CAAC;IACD,OAAO,oBAAU,SAAS,EAAC,kBAAkB,EAAC,KAAK,EAAE,EAAE,2BAA2B,EAAE,GAAG,iBAAiB,CAAC,aAAa,IAAI,EAAmB,iCAAwB,QAAQ,EAAE,QAAQ,aACrL,2BAAS,KAAK,GAAU,EACxB,eAAK,SAAS,EAAC,uBAAuB,aACpC,iBAAO,SAAS,EAAC,0BAA0B,aAAC,yBAAO,MAAM,CAAC,KAAK,GAAQ,EAAA,gBAAO,IAAI,EAAC,OAAO,EAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAI,IAAQ,EAC5M,iBAAO,SAAS,EAAC,uBAAuB,aAAC,yBAAO,MAAM,CAAC,GAAG,GAAQ,EAAA,gBAAO,IAAI,EAAC,MAAM,EAAC,GAAG,EAAC,KAAK,EAAC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAC,KAAK,kBAAe,OAAO,IAAI,SAAS,sBAAoB,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,EAC1O,QAAQ,EAAE,KAAK,CAAC,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EACvF,SAAS,EAAE,KAAK,CAAC,EAAE,GAAG,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;oCAAC,KAAK,CAAC,cAAc,EAAE,CAAC;oCAAC,MAAM,EAAE,CAAC;gCAAC,CAAC,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;oCAAC,KAAK,CAAC,cAAc,EAAE,CAAC;oCAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;oCAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gCAAC,CAAC,CAAC,CAAC,GAAI,IAAQ,IAC9L,EACL,OAAO,IAAI,YAAG,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAC,OAAO,EAAC,SAAS,EAAC,yBAAyB,YAAE,MAAM,CAAC,OAAO,GAAK,EACtG,KAAK,IAAI,iBAAO,SAAS,EAAC,2BAA2B,aAAC,2BAAO,MAAM,CAAC,OAAO,OAAE,6BAAS,aAAa,CAAC,KAAK,CAAC,SAAW,IAAO,EAAA,gBAAO,IAAI,EAAC,OAAO,EAAC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,gBAAc,MAAM,CAAC,OAAO,oBAAkB,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAI,IAAQ,EAC1W,cAAK,SAAS,EAAC,2BAA2B,iBAAa,MAAM,EAAC,KAAK,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,GAAI,EAClG,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,cAAK,SAAS,EAAC,2BAA2B,YAAE,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,kBAAQ,IAAI,EAAC,QAAQ,kBAA4B,MAAM,KAAK,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,aAAE,8BAAkB,MAAM,EAAC,SAAS,EAAC,0BAA0B,EAAC,KAAK,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,GAAI,EAAA,cAAK,GAAG,EAAC,KAAK,YAAE,MAAM,GAAO,KAAtM,MAAM,CAAyM,CAAC,GAAO,IAClU,CAAC;AACd,CAAC","sourcesContent":["import { useEffect, useId, useState, type CSSProperties } from \"react\";\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}>;\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 = [] }: 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 useEffect(() => { setDraft(color); setInvalid(false); }, [color]);\n function emit(next: string) { setDraft(next); setInvalid(false); if (next !== color) onValueChange(next); }\n function commit() {\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={{ \"--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 => { setDraft(event.target.value); setInvalid(false); }} onBlur={commit}\n onKeyDown={event => { if (event.key === \"Enter\") { event.preventDefault(); commit(); } if (event.key === \"Escape\") { event.preventDefault(); 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"]}
@@ -0,0 +1,18 @@
1
+ import { type ReactNode, type RefObject } from "react";
2
+ export type ContentTransitionProps = {
3
+ stateKey: string;
4
+ children: ReactNode;
5
+ motion?: "system" | "none";
6
+ /** Host chooses a meaningful focus destination, e.g. the new panel heading. */
7
+ focusTarget?: RefObject<HTMLElement | null>;
8
+ };
9
+ /** Motion Primitives' keyed transition pattern, adapted to HJM's single active subtree.
10
+ * No exiting interactive copy: it would duplicate fields and focus targets. See THIRD_PARTY_NOTICES. */
11
+ export declare function ContentTransition({ stateKey, children, motion: preference, focusTarget }: ContentTransitionProps): import("react").JSX.Element;
12
+ export type TextTransitionProps = {
13
+ text: string;
14
+ motion?: "system" | "none";
15
+ };
16
+ /** Whole-text fade preserves graphemes, text wrapping, selection and a single spoken value. */
17
+ export declare function TextTransition({ text, motion: preference }: TextTransitionProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=content-transition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-transition.d.ts","sourceRoot":"","sources":["../src/content-transition.tsx"],"names":[],"mappings":"AACA,OAAO,EAAwC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAI7F,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAClE,+EAA+E;IAC/E,WAAW,CAAC,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;CAC7C,CAAC;AACF;wGACwG;AACxG,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAqB,EAAE,WAAW,EAAE,EAAE,sBAAsB,+BAmB3H;AACD,MAAM,MAAM,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAA;CAAE,CAAC;AAC/E,+FAA+F;AAC/F,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,mBAAmB,+BAE/E"}
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { motion } from "framer-motion";
3
+ import { useCallback, useRef, useLayoutEffect } from "react";
4
+ import { easing, motion as timing } from "@hjmds/design-contracts/foundations";
5
+ import { useHjmTheme } from "./provider.js";
6
+ /** Motion Primitives' keyed transition pattern, adapted to HJM's single active subtree.
7
+ * No exiting interactive copy: it would duplicate fields and focus targets. See THIRD_PARTY_NOTICES. */
8
+ export function ContentTransition({ stateKey, children, motion: preference = "system", focusTarget }) {
9
+ const { environment } = useHjmTheme();
10
+ const enabled = preference !== "none" && !environment.reducedMotion;
11
+ const first = useRef(true);
12
+ const host = useRef(null);
13
+ const restore = useRef(false);
14
+ const capturePanel = useCallback((node) => {
15
+ // The keyed node detaches before DOM removal. Parent effect cleanup runs too
16
+ // late: the browser has already moved focus to body by then.
17
+ if (!node)
18
+ restore.current = Boolean(host.current?.contains(document.activeElement));
19
+ host.current = node;
20
+ }, []);
21
+ useLayoutEffect(() => {
22
+ if (restore.current)
23
+ focusTarget?.current?.focus();
24
+ restore.current = false;
25
+ first.current = false;
26
+ }, [stateKey, focusTarget]);
27
+ return _jsx("div", { children: _jsx(motion.div, { ref: capturePanel, initial: enabled && !first.current ? { opacity: 0 } : false, animate: { opacity: 1 }, transition: { duration: enabled ? timing.normal / 1000 : 0, ease: [...easing.enter] }, children: children }, stateKey) });
28
+ }
29
+ /** Whole-text fade preserves graphemes, text wrapping, selection and a single spoken value. */
30
+ export function TextTransition({ text, motion: preference }) {
31
+ return _jsx(ContentTransition, { stateKey: text, ...(preference ? { motion: preference } : {}), children: text });
32
+ }
33
+ //# sourceMappingURL=content-transition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-transition.js","sourceRoot":"","sources":["../src/content-transition.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAkC,MAAM,OAAO,CAAC;AAC7F,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO5C;wGACwG;AACxG,MAAM,UAAU,iBAAiB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,EAAE,WAAW,EAA0B;IAC1H,MAAM,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAAC;IACtC,MAAM,OAAO,GAAG,UAAU,KAAK,MAAM,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;IACpE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,YAAY,GAAG,WAAW,CAAC,CAAC,IAA2B,EAAE,EAAE;QAC/D,6EAA6E;QAC7E,6DAA6D;QAC7D,IAAI,CAAC,IAAI;YAAE,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,eAAe,CAAC,GAAG,EAAE;QACnB,IAAI,OAAO,CAAC,OAAO;YAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACnD,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;QACxB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;IACxB,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;IAC5B,OAAO,wBAAK,KAAC,MAAM,CAAC,GAAG,IAAC,GAAG,EAAE,YAAY,EAAiB,OAAO,EAAE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EACnH,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,YAAG,QAAQ,IAD3E,QAAQ,CACiF,GAAM,CAAC;AAClJ,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,cAAc,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAuB;IAC9E,OAAO,KAAC,iBAAiB,IAAC,QAAQ,EAAE,IAAI,KAAM,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,YAAG,IAAI,GAAqB,CAAC;AACvH,CAAC","sourcesContent":["import { motion } from \"framer-motion\";\nimport { useCallback, useRef, useLayoutEffect, type ReactNode, type RefObject } from \"react\";\nimport { easing, motion as timing } from \"@hjmds/design-contracts/foundations\";\nimport { useHjmTheme } from \"./provider.js\";\n\nexport type ContentTransitionProps = {\n stateKey: string; children: ReactNode; motion?: \"system\" | \"none\";\n /** Host chooses a meaningful focus destination, e.g. the new panel heading. */\n focusTarget?: RefObject<HTMLElement | null>;\n};\n/** Motion Primitives' keyed transition pattern, adapted to HJM's single active subtree.\n * No exiting interactive copy: it would duplicate fields and focus targets. See THIRD_PARTY_NOTICES. */\nexport function ContentTransition({ stateKey, children, motion: preference = \"system\", focusTarget }: ContentTransitionProps) {\n const { environment } = useHjmTheme();\n const enabled = preference !== \"none\" && !environment.reducedMotion;\n const first = useRef(true);\n const host = useRef<HTMLDivElement>(null);\n const restore = useRef(false);\n const capturePanel = useCallback((node: HTMLDivElement | null) => {\n // The keyed node detaches before DOM removal. Parent effect cleanup runs too\n // late: the browser has already moved focus to body by then.\n if (!node) restore.current = Boolean(host.current?.contains(document.activeElement));\n host.current = node;\n }, []);\n useLayoutEffect(() => {\n if (restore.current) focusTarget?.current?.focus();\n restore.current = false;\n first.current = false;\n }, [stateKey, focusTarget]);\n return <div><motion.div ref={capturePanel} key={stateKey} initial={enabled && !first.current ? { opacity: 0 } : false}\n animate={{ opacity: 1 }} transition={{ duration: enabled ? timing.normal / 1000 : 0, ease: [...easing.enter] }}>{children}</motion.div></div>;\n}\nexport type TextTransitionProps = { text: string; motion?: \"system\" | \"none\" };\n/** Whole-text fade preserves graphemes, text wrapping, selection and a single spoken value. */\nexport function TextTransition({ text, motion: preference }: TextTransitionProps) {\n return <ContentTransition stateKey={text} {...(preference ? { motion: preference } : {})}>{text}</ContentTransition>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"context-menu.d.ts","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,yBAAyB,EAE/B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,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;CACpB,CAAC,CAAC;AAIH,wBAAgB,WAAW,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,EACvD,QAAQ,EACR,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,SAAS,GACV,EAAE,gBAAgB,CAAC,GAAG,CAAC,+BAyIvB"}
1
+ {"version":3,"file":"context-menu.d.ts","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,yBAAyB,EAE/B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,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;CACpB,CAAC,CAAC;AAIH,wBAAgB,WAAW,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,EACvD,QAAQ,EACR,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,SAAS,GACV,EAAE,gBAAgB,CAAC,GAAG,CAAC,+BAyKvB"}
@@ -1,12 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { resolveContextMenuAnchor, } from "@hjmds/design-contracts/components/context-menu";
3
3
  import { menuRecipe } from "@hjmds/design-contracts/recipes";
4
- import { useCallback, useEffect, useRef, useState, } from "react";
4
+ import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
5
5
  import { classNames } from "./internal.js";
6
6
  import { HjmPortal, getModalLayer } from "./modal.js";
7
7
  const longPressDelay = 500;
8
8
  export function ContextMenu({ children, items, accessibilityLabel, onAction, className, }) {
9
9
  const hostRef = useRef(null);
10
+ const menuRef = useRef(null);
11
+ const [menuMounted, setMenuMounted] = useState(false);
12
+ const returnFocusRef = useRef(null);
13
+ const menuId = useId();
10
14
  const [anchor, setAnchor] = useState(null);
11
15
  const [activeIndex, setActiveIndex] = useState(0);
12
16
  const longPress = useRef(undefined);
@@ -14,10 +18,29 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
14
18
  const typeaheadTimer = useRef(undefined);
15
19
  const enabled = items.filter((item) => !item.disabled);
16
20
  const open = useCallback((reason, pointer) => {
21
+ const activeElement = document.activeElement;
22
+ returnFocusRef.current = activeElement instanceof HTMLElement && hostRef.current?.contains(activeElement)
23
+ ? activeElement
24
+ : hostRef.current;
17
25
  const rect = hostRef.current?.getBoundingClientRect() ?? null;
18
26
  setAnchor(resolveContextMenuAnchor(reason, pointer, rect === null ? null : { left: rect.left, bottom: rect.bottom }));
19
27
  setActiveIndex(0);
20
28
  }, []);
29
+ const setMenuRef = useCallback((node) => {
30
+ menuRef.current = node;
31
+ setMenuMounted(node !== null);
32
+ }, []);
33
+ useLayoutEffect(() => {
34
+ if (anchor === null || !menuMounted || menuRef.current === null)
35
+ return;
36
+ menuRef.current.focus();
37
+ const rect = menuRef.current.getBoundingClientRect();
38
+ const x = Math.min(Math.max(anchor.x, 0), Math.max(window.innerWidth - rect.width, 0));
39
+ const y = Math.min(Math.max(anchor.y, 0), Math.max(window.innerHeight - rect.height, 0));
40
+ // The contract promises viewport coordinates; post-render measurement keeps long labels onscreen, where guessed dimensions could not account for wrapping.
41
+ if (x !== anchor.x || y !== anchor.y)
42
+ setAnchor({ x, y });
43
+ }, [anchor, menuMounted]);
21
44
  useEffect(() => {
22
45
  if (anchor === null)
23
46
  return;
@@ -26,7 +49,7 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
26
49
  event.preventDefault();
27
50
  setAnchor(null);
28
51
  // Focus goes back where the menu was opened from, not to the body.
29
- hostRef.current?.focus();
52
+ returnFocusRef.current?.focus();
30
53
  return;
31
54
  }
32
55
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
@@ -59,12 +82,16 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
59
82
  if (item) {
60
83
  onAction(item.id);
61
84
  setAnchor(null);
62
- hostRef.current?.focus();
85
+ returnFocusRef.current?.focus();
63
86
  }
87
+ return;
88
+ }
89
+ if (event.key === "Tab") {
90
+ setAnchor(null);
64
91
  }
65
92
  };
66
93
  const onPointerDown = (event) => {
67
- if (!(event.target instanceof Node) || !document.querySelector(".hjm-context-menu")?.contains(event.target)) {
94
+ if (!(event.target instanceof Node) || !menuRef.current?.contains(event.target)) {
68
95
  setAnchor(null);
69
96
  }
70
97
  };
@@ -91,20 +118,20 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
91
118
  longPress.current = setTimeout(() => open("longPress", { x: event.clientX, y: event.clientY }), longPressDelay);
92
119
  }, onPointerUp: () => { if (longPress.current !== undefined)
93
120
  clearTimeout(longPress.current); }, onPointerCancel: () => { if (longPress.current !== undefined)
94
- clearTimeout(longPress.current); }, children: [children, anchor !== null ? (_jsx(HjmPortal, { children: _jsx("div", { role: "menu", "aria-label": accessibilityLabel, className: "hjm-context-menu", style: {
95
- insetBlockStart: anchor.y,
96
- insetInlineStart: anchor.x,
121
+ clearTimeout(longPress.current); }, children: [children, anchor !== null ? (_jsx(HjmPortal, { children: _jsx("div", { role: "menu", id: menuId, ref: setMenuRef, tabIndex: -1, "aria-activedescendant": enabled[activeIndex] ? `${menuId}-item-${activeIndex}` : undefined, "aria-label": accessibilityLabel, className: "hjm-context-menu", style: {
122
+ top: anchor.y,
123
+ left: anchor.x,
97
124
  zIndex: getModalLayer(0),
98
125
  minInlineSize: menuRecipe.minWidth,
99
126
  }, children: items.map((item) => {
100
127
  const index = enabled.findIndex((candidate) => candidate.id === item.id);
101
- return (_jsxs("div", { role: "menuitem", "aria-disabled": item.disabled || undefined, "data-active": index === activeIndex && !item.disabled ? "" : undefined, "data-tone": item.tone, className: "hjm-context-menu__item", onMouseEnter: () => { if (index >= 0)
128
+ return (_jsxs("div", { id: index >= 0 ? `${menuId}-item-${index}` : undefined, role: "menuitem", "aria-disabled": item.disabled || undefined, "data-active": index === activeIndex && !item.disabled ? "" : undefined, "data-tone": item.tone, className: "hjm-context-menu__item", onMouseEnter: () => { if (index >= 0)
102
129
  setActiveIndex(index); }, onClick: () => {
103
130
  if (item.disabled)
104
131
  return;
105
132
  onAction(item.id);
106
133
  setAnchor(null);
107
- hostRef.current?.focus();
134
+ returnFocusRef.current?.focus();
108
135
  }, children: [_jsx("span", { children: item.label }), item.shortcut ? _jsx("kbd", { className: "hjm-context-menu__shortcut", children: item.shortcut }) : null] }, item.id));
109
136
  }) }) })) : null] }));
110
137
  }
@@ -1 +1 @@
1
- {"version":3,"file":"context-menu.js","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\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 useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport { classNames } from \"./internal.js\";\nimport { HjmPortal, getModalLayer } from \"./modal.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}>;\n\nconst longPressDelay = 500;\n\nexport function ContextMenu<Key extends string = string>({\n children,\n items,\n accessibilityLabel,\n onAction,\n className,\n}: ContextMenuProps<Key>) {\n const hostRef = useRef<HTMLDivElement>(null);\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(\"\");\n const typeaheadTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const enabled = items.filter((item) => !item.disabled);\n\n const open = useCallback((reason: ContextMenuOpenReason, pointer: ContextMenuAnchor | null) => {\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 }, []);\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 hostRef.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 !== \" \") {\n // Typeahead matches `textValue`, not the rendered label: a label may hold\n // markup or a formatted number that nobody would type.\n const query = (typeahead.current += event.key.toLowerCase());\n clearTimeout(typeaheadTimer.current);\n typeaheadTimer.current = setTimeout(() => { typeahead.current = \"\"; }, 700);\n const match = enabled.findIndex((item) => item.textValue.toLowerCase().startsWith(query));\n if (match >= 0) setActiveIndex(match);\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); hostRef.current?.focus(); }\n }\n };\n const onPointerDown = (event: PointerEvent) => {\n if (!(event.target instanceof Node) || !document.querySelector(\".hjm-context-menu\")?.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 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 aria-label={accessibilityLabel}\n className=\"hjm-context-menu\"\n style={{\n insetBlockStart: anchor.y,\n insetInlineStart: anchor.x,\n zIndex: getModalLayer(0),\n minInlineSize: menuRecipe.minWidth,\n } as CSSProperties}\n >\n {items.map((item) => {\n const index = enabled.findIndex((candidate) => candidate.id === item.id);\n return (\n <div\n key={item.id}\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 onMouseEnter={() => { if (index >= 0) setActiveIndex(index); }}\n onClick={() => {\n if (item.disabled) return;\n onAction(item.id as Key);\n setAnchor(null);\n hostRef.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 {\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\";\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}>;\n\nconst longPressDelay = 500;\n\nexport function ContextMenu<Key extends string = string>({\n children,\n items,\n accessibilityLabel,\n onAction,\n className,\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(\"\");\n const typeaheadTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\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 }, []);\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 !== \" \") {\n // Typeahead matches `textValue`, not the rendered label: a label may hold\n // markup or a formatted number that nobody would type.\n const query = (typeahead.current += event.key.toLowerCase());\n clearTimeout(typeaheadTimer.current);\n typeaheadTimer.current = setTimeout(() => { typeahead.current = \"\"; }, 700);\n const match = enabled.findIndex((item) => item.textValue.toLowerCase().startsWith(query));\n if (match >= 0) setActiveIndex(match);\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 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 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 onMouseEnter={() => { 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 +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,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAY,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAGrF,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;CACpB,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,GACV,EAAE,eAAe,CAAC,OAAO,CAAC,+BAyF1B"}
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,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAAY,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AAKrF,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;CACpB,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,GACV,EAAE,eAAe,CAAC,OAAO,CAAC,+BA6H1B"}
@@ -1,9 +1,10 @@
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, useRef, useState, } from "react";
4
+ import { useEffect, useId, useLayoutEffect, useRef, useState, } from "react";
5
5
  import { Calendar } from "./calendar.js";
6
6
  import { classNames } from "./internal.js";
7
+ const popoverViewportInset = 16;
7
8
  /** Single-date field with an anchored, focus-bearing calendar dialog. */
8
9
  export function DatePicker({ descriptor, monthLabel, composeAccessibleName, previousMonth, nextMonth, clearLabel, closeLabel, size = "medium", description, error, renderCellContent, onNavigateBeyondGrid, className, }) {
9
10
  validateDatePickerDescriptor(descriptor);
@@ -51,6 +52,43 @@ export function DatePicker({ descriptor, monthLabel, composeAccessibleName, prev
51
52
  document.removeEventListener("pointerdown", outside);
52
53
  };
53
54
  }, [open]);
55
+ // Keep the popover inside the viewport (2026-09-30 audit WR-0930-3): shift inline, flip up
56
+ // when short. Not portaled: Calendar autoFocus runs before a portal popup is measured.
57
+ useLayoutEffect(() => {
58
+ const popover = dialogRef.current;
59
+ if (!open || !popover)
60
+ return;
61
+ const place = () => {
62
+ const viewport = window.visualViewport;
63
+ const left = (viewport?.offsetLeft ?? 0) + popoverViewportInset;
64
+ const right = (viewport?.offsetLeft ?? 0) + (viewport?.width ?? document.documentElement.clientWidth) - popoverViewportInset;
65
+ // Layout offset, not translate: mobile Chromium widened the page for a translated box.
66
+ popover.style.insetInlineStart = "";
67
+ popover.style.maxInlineSize = `${Math.max(0, right - left)}px`;
68
+ const rect = popover.getBoundingClientRect();
69
+ let shift = rect.right > right ? Math.floor(right - rect.right) : 0;
70
+ if (rect.left + shift < left)
71
+ shift = Math.ceil(left - rect.left);
72
+ const logical = getComputedStyle(popover).direction === "rtl" ? -shift : shift;
73
+ popover.style.insetInlineStart = shift === 0 ? "" : `${logical}px`;
74
+ popover.style.insetBlockStart = "";
75
+ popover.style.insetBlockEnd = "";
76
+ const box = popover.getBoundingClientRect();
77
+ const bottom = (viewport?.offsetTop ?? 0) + (viewport?.height ?? window.innerHeight) - popoverViewportInset;
78
+ const anchor = popover.parentElement?.getBoundingClientRect();
79
+ if (anchor && box.bottom > bottom && anchor.top - box.height - popoverViewportInset >= (viewport?.offsetTop ?? 0)) {
80
+ popover.style.insetBlockStart = "auto";
81
+ popover.style.insetBlockEnd = "calc(100% + var(--hjm-space-xs))";
82
+ }
83
+ };
84
+ place();
85
+ window.addEventListener("resize", place);
86
+ window.visualViewport?.addEventListener("resize", place);
87
+ return () => {
88
+ window.removeEventListener("resize", place);
89
+ window.visualViewport?.removeEventListener("resize", place);
90
+ };
91
+ }, [open]);
54
92
  // The outer field owns selection; the shared grid receives only its resolved controlled value.
55
93
  const { defaultSelectedDate: _defaultSelectedDate, ...calendarDescriptor } = descriptor;
56
94
  const calendarGrid = descriptor.disabled || descriptor.readOnly
@@ -1 +1 @@
1
- {"version":3,"file":"date-picker.js","sourceRoot":"","sources":["../src/date-picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n type ComposeCalendarAccessibleName,\n type ResolvedCalendarDateCell,\n} from \"@hjmds/design-contracts/components/calendar\";\nimport {\n resolveDatePickerTriggerText,\n validateDatePickerDescriptor,\n type DatePickerDescriptor,\n type DatePickerOpenChangeReason,\n type DatePickerSize,\n} from \"@hjmds/design-contracts/components/date-picker\";\nimport {\n useEffect,\n useId,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport { Calendar, type CalendarOverflow, type CalendarHandle } from \"./calendar.js\";\nimport { classNames } from \"./internal.js\";\n\nexport type DatePickerMonthAction = Readonly<{ month: string; label: string }>;\n\nexport type DatePickerProps<Content = unknown> = Readonly<{\n descriptor: DatePickerDescriptor<Content>;\n monthLabel: string;\n composeAccessibleName: ComposeCalendarAccessibleName<Content>;\n previousMonth?: DatePickerMonthAction;\n nextMonth?: DatePickerMonthAction;\n clearLabel: string;\n closeLabel: string;\n size?: DatePickerSize;\n description?: ReactNode;\n error?: ReactNode;\n onNavigateBeyondGrid?: (detail: CalendarOverflow, focusDate: CalendarHandle[\"focusDate\"]) => void;\n renderCellContent?: (cell: ResolvedCalendarDateCell<Content>) => ReactNode;\n className?: string;\n}>;\n\n/** Single-date field with an anchored, focus-bearing calendar dialog. */\nexport function DatePicker<Content>({\n descriptor,\n monthLabel,\n composeAccessibleName,\n previousMonth,\n nextMonth,\n clearLabel,\n closeLabel,\n size = \"medium\",\n description,\n error,\n renderCellContent,\n onNavigateBeyondGrid,\n className,\n}: DatePickerProps<Content>) {\n validateDatePickerDescriptor(descriptor);\n const generatedId = useId().replaceAll(\":\", \"\");\n const dialogId = `hjm-date-picker-${generatedId}`;\n const triggerRef = useRef<HTMLButtonElement>(null);\n const dialogRef = useRef<HTMLDivElement>(null);\n const controlledOpen = descriptor.open !== undefined;\n const [internalOpen, setInternalOpen] = useState(descriptor.defaultOpen ?? false);\n const open = controlledOpen ? descriptor.open === true : internalOpen;\n const controlledSelection = descriptor.selectedDate !== undefined;\n const [internalSelection, setInternalSelection] = useState(\n descriptor.defaultSelectedDate ?? null,\n );\n const selectedDate = controlledSelection ? descriptor.selectedDate ?? null : internalSelection;\n\n const requestOpen = (next: boolean, reason: DatePickerOpenChangeReason) => {\n if (!controlledOpen) setInternalOpen(next);\n descriptor.onOpenChange?.(next, reason);\n };\n const commit = (date: string | null, reason: \"activate\" | \"clear\") => {\n // A controlled open field can become read-only while its grid remains mounted.\n if (descriptor.disabled || descriptor.readOnly) return;\n if (!controlledSelection) setInternalSelection(date);\n descriptor.onSelectionChange?.(date, reason);\n requestOpen(false, reason === \"activate\" ? \"selection\" : \"clear\");\n };\n\n const wasOpenRef = useRef(open);\n useEffect(() => {\n const wasOpen = wasOpenRef.current;\n wasOpenRef.current = open;\n if (!open) {\n if (wasOpen) triggerRef.current?.focus();\n return;\n }\n const outside = (event: PointerEvent) => {\n const target = event.target as Node;\n if (!dialogRef.current?.contains(target) && !triggerRef.current?.contains(target)) {\n requestOpen(false, \"outside\");\n }\n };\n document.addEventListener(\"pointerdown\", outside);\n return () => {\n document.removeEventListener(\"pointerdown\", outside);\n };\n }, [open]);\n\n // The outer field owns selection; the shared grid receives only its resolved controlled value.\n const { defaultSelectedDate: _defaultSelectedDate, ...calendarDescriptor } = descriptor;\n const calendarGrid = descriptor.disabled || descriptor.readOnly\n ? { ...descriptor.grid, cells: descriptor.grid.cells.map((cell) => cell.date ? { ...cell, disabled: true } : cell) }\n : descriptor.grid;\n const label = descriptor.label ?? descriptor.accessibilityLabel;\n const triggerText = resolveDatePickerTriggerText(descriptor);\n return (\n <div className={classNames(\"hjm-date-picker\", className)} data-invalid={descriptor.invalid || error !== undefined || undefined} data-size={size}>\n {descriptor.label === undefined ? null : <span className=\"hjm-date-picker__label\">{descriptor.label}</span>}\n <div className=\"hjm-date-picker__anchor\">\n <button\n aria-controls={open ? dialogId : undefined}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-label={descriptor.accessibilityLabel}\n className=\"hjm-date-picker__trigger\"\n disabled={descriptor.disabled}\n onClick={() => !descriptor.readOnly && requestOpen(!open, \"trigger\")}\n ref={triggerRef}\n type=\"button\"\n >\n <span aria-hidden=\"true\">▣</span>\n <span data-placeholder={descriptor.displayValue === null || undefined}>{triggerText}</span>\n </button>\n {selectedDate === null ? null : (\n <button aria-label={clearLabel} className=\"hjm-date-picker__clear\" disabled={descriptor.disabled || descriptor.readOnly} onClick={() => commit(null, \"clear\")} type=\"button\">×</button>\n )}\n {open ? (\n <div aria-label={label} className=\"hjm-date-picker__popover\" id={dialogId} ref={dialogRef} role=\"dialog\" onKeyDown={(event) => { if (event.key === \"Escape\") { event.preventDefault(); requestOpen(false, \"escape\"); } }}>\n <button aria-label={closeLabel} className=\"hjm-date-picker__calendar-close\" onClick={() => requestOpen(false, \"trigger\")} type=\"button\">×</button>\n <Calendar descriptor={{ ...calendarDescriptor, grid: calendarGrid, monthLabel, selectedDate, onSelectionChange: (date) => commit(date, \"activate\") }}\n composeAccessibleName={composeAccessibleName} size={size} autoFocus\n {...(previousMonth ? { previousMonth } : {})} {...(nextMonth ? { nextMonth } : {})}\n {...(renderCellContent ? { renderCellContent } : {})} {...(onNavigateBeyondGrid ? { onNavigateBeyondGrid } : {})} />\n </div>\n ) : null}\n </div>\n {description === undefined ? null : <span className=\"hjm-date-picker__description\">{description}</span>}\n {error === undefined ? null : <span className=\"hjm-date-picker__error\" role=\"alert\">{error}</span>}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"date-picker.js","sourceRoot":"","sources":["../src/date-picker.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n type ComposeCalendarAccessibleName,\n type ResolvedCalendarDateCell,\n} from \"@hjmds/design-contracts/components/calendar\";\nimport {\n resolveDatePickerTriggerText,\n validateDatePickerDescriptor,\n type DatePickerDescriptor,\n type DatePickerOpenChangeReason,\n type DatePickerSize,\n} from \"@hjmds/design-contracts/components/date-picker\";\nimport {\n useEffect,\n useId,\n useLayoutEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport { Calendar, type CalendarOverflow, type CalendarHandle } from \"./calendar.js\";\nimport { classNames } from \"./internal.js\";\n\nconst popoverViewportInset = 16;\n\nexport type DatePickerMonthAction = Readonly<{ month: string; label: string }>;\n\nexport type DatePickerProps<Content = unknown> = Readonly<{\n descriptor: DatePickerDescriptor<Content>;\n monthLabel: string;\n composeAccessibleName: ComposeCalendarAccessibleName<Content>;\n previousMonth?: DatePickerMonthAction;\n nextMonth?: DatePickerMonthAction;\n clearLabel: string;\n closeLabel: string;\n size?: DatePickerSize;\n description?: ReactNode;\n error?: ReactNode;\n onNavigateBeyondGrid?: (detail: CalendarOverflow, focusDate: CalendarHandle[\"focusDate\"]) => void;\n renderCellContent?: (cell: ResolvedCalendarDateCell<Content>) => ReactNode;\n className?: string;\n}>;\n\n/** Single-date field with an anchored, focus-bearing calendar dialog. */\nexport function DatePicker<Content>({\n descriptor,\n monthLabel,\n composeAccessibleName,\n previousMonth,\n nextMonth,\n clearLabel,\n closeLabel,\n size = \"medium\",\n description,\n error,\n renderCellContent,\n onNavigateBeyondGrid,\n className,\n}: DatePickerProps<Content>) {\n validateDatePickerDescriptor(descriptor);\n const generatedId = useId().replaceAll(\":\", \"\");\n const dialogId = `hjm-date-picker-${generatedId}`;\n const triggerRef = useRef<HTMLButtonElement>(null);\n const dialogRef = useRef<HTMLDivElement>(null);\n const controlledOpen = descriptor.open !== undefined;\n const [internalOpen, setInternalOpen] = useState(descriptor.defaultOpen ?? false);\n const open = controlledOpen ? descriptor.open === true : internalOpen;\n const controlledSelection = descriptor.selectedDate !== undefined;\n const [internalSelection, setInternalSelection] = useState(\n descriptor.defaultSelectedDate ?? null,\n );\n const selectedDate = controlledSelection ? descriptor.selectedDate ?? null : internalSelection;\n\n const requestOpen = (next: boolean, reason: DatePickerOpenChangeReason) => {\n if (!controlledOpen) setInternalOpen(next);\n descriptor.onOpenChange?.(next, reason);\n };\n const commit = (date: string | null, reason: \"activate\" | \"clear\") => {\n // A controlled open field can become read-only while its grid remains mounted.\n if (descriptor.disabled || descriptor.readOnly) return;\n if (!controlledSelection) setInternalSelection(date);\n descriptor.onSelectionChange?.(date, reason);\n requestOpen(false, reason === \"activate\" ? \"selection\" : \"clear\");\n };\n\n const wasOpenRef = useRef(open);\n useEffect(() => {\n const wasOpen = wasOpenRef.current;\n wasOpenRef.current = open;\n if (!open) {\n if (wasOpen) triggerRef.current?.focus();\n return;\n }\n const outside = (event: PointerEvent) => {\n const target = event.target as Node;\n if (!dialogRef.current?.contains(target) && !triggerRef.current?.contains(target)) {\n requestOpen(false, \"outside\");\n }\n };\n document.addEventListener(\"pointerdown\", outside);\n return () => {\n document.removeEventListener(\"pointerdown\", outside);\n };\n }, [open]);\n\n // Keep the popover inside the viewport (2026-09-30 audit WR-0930-3): shift inline, flip up\n // when short. Not portaled: Calendar autoFocus runs before a portal popup is measured.\n useLayoutEffect(() => {\n const popover = dialogRef.current;\n if (!open || !popover) return;\n const place = () => {\n const viewport = window.visualViewport;\n const left = (viewport?.offsetLeft ?? 0) + popoverViewportInset;\n const right = (viewport?.offsetLeft ?? 0) + (viewport?.width ?? document.documentElement.clientWidth) - popoverViewportInset;\n // Layout offset, not translate: mobile Chromium widened the page for a translated box.\n popover.style.insetInlineStart = \"\";\n popover.style.maxInlineSize = `${Math.max(0, right - left)}px`;\n const rect = popover.getBoundingClientRect();\n let shift = rect.right > right ? Math.floor(right - rect.right) : 0;\n if (rect.left + shift < left) shift = Math.ceil(left - rect.left);\n const logical = getComputedStyle(popover).direction === \"rtl\" ? -shift : shift;\n popover.style.insetInlineStart = shift === 0 ? \"\" : `${logical}px`;\n popover.style.insetBlockStart = \"\";\n popover.style.insetBlockEnd = \"\";\n const box = popover.getBoundingClientRect();\n const bottom = (viewport?.offsetTop ?? 0) + (viewport?.height ?? window.innerHeight) - popoverViewportInset;\n const anchor = popover.parentElement?.getBoundingClientRect();\n if (anchor && box.bottom > bottom && anchor.top - box.height - popoverViewportInset >= (viewport?.offsetTop ?? 0)) {\n popover.style.insetBlockStart = \"auto\";\n popover.style.insetBlockEnd = \"calc(100% + var(--hjm-space-xs))\";\n }\n };\n place();\n window.addEventListener(\"resize\", place);\n window.visualViewport?.addEventListener(\"resize\", place);\n return () => {\n window.removeEventListener(\"resize\", place);\n window.visualViewport?.removeEventListener(\"resize\", place);\n };\n }, [open]);\n\n // The outer field owns selection; the shared grid receives only its resolved controlled value.\n const { defaultSelectedDate: _defaultSelectedDate, ...calendarDescriptor } = descriptor;\n const calendarGrid = descriptor.disabled || descriptor.readOnly\n ? { ...descriptor.grid, cells: descriptor.grid.cells.map((cell) => cell.date ? { ...cell, disabled: true } : cell) }\n : descriptor.grid;\n const label = descriptor.label ?? descriptor.accessibilityLabel;\n const triggerText = resolveDatePickerTriggerText(descriptor);\n return (\n <div className={classNames(\"hjm-date-picker\", className)} data-invalid={descriptor.invalid || error !== undefined || undefined} data-size={size}>\n {descriptor.label === undefined ? null : <span className=\"hjm-date-picker__label\">{descriptor.label}</span>}\n <div className=\"hjm-date-picker__anchor\">\n <button\n aria-controls={open ? dialogId : undefined}\n aria-expanded={open}\n aria-haspopup=\"dialog\"\n aria-label={descriptor.accessibilityLabel}\n className=\"hjm-date-picker__trigger\"\n disabled={descriptor.disabled}\n onClick={() => !descriptor.readOnly && requestOpen(!open, \"trigger\")}\n ref={triggerRef}\n type=\"button\"\n >\n <span aria-hidden=\"true\">▣</span>\n <span data-placeholder={descriptor.displayValue === null || undefined}>{triggerText}</span>\n </button>\n {selectedDate === null ? null : (\n <button aria-label={clearLabel} className=\"hjm-date-picker__clear\" disabled={descriptor.disabled || descriptor.readOnly} onClick={() => commit(null, \"clear\")} type=\"button\">×</button>\n )}\n {open ? (\n <div aria-label={label} className=\"hjm-date-picker__popover\" id={dialogId} ref={dialogRef} role=\"dialog\" onKeyDown={(event) => { if (event.key === \"Escape\") { event.preventDefault(); requestOpen(false, \"escape\"); } }}>\n <button aria-label={closeLabel} className=\"hjm-date-picker__calendar-close\" onClick={() => requestOpen(false, \"trigger\")} type=\"button\">×</button>\n <Calendar descriptor={{ ...calendarDescriptor, grid: calendarGrid, monthLabel, selectedDate, onSelectionChange: (date) => commit(date, \"activate\") }}\n composeAccessibleName={composeAccessibleName} size={size} autoFocus\n {...(previousMonth ? { previousMonth } : {})} {...(nextMonth ? { nextMonth } : {})}\n {...(renderCellContent ? { renderCellContent } : {})} {...(onNavigateBeyondGrid ? { onNavigateBeyondGrid } : {})} />\n </div>\n ) : null}\n </div>\n {description === undefined ? null : <span className=\"hjm-date-picker__description\">{description}</span>}\n {error === undefined ? null : <span className=\"hjm-date-picker__error\" role=\"alert\">{error}</span>}\n </div>\n );\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import type { ShowcaseScenarioId } from "@hjmds/design-contracts/showcase";
2
2
  export declare const reactRendererEvidenceSchemaVersion: 2;
3
- export type ReactRendererEvidenceScenario = Exclude<ShowcaseScenarioId, "contract">;
3
+ export type ReactRendererEvidenceScenario = Exclude<ShowcaseScenarioId, "contract" | "native-actions">;
4
4
  export type ReactRendererEvidenceComponent = Readonly<{
5
5
  /** Canonical component id from the design-contracts catalog. */
6
6
  componentId: string;
@@ -34,7 +34,7 @@ export type ReactRendererEvidenceManifest = Readonly<{
34
34
  export declare const reactRendererEvidence: {
35
35
  readonly schemaVersion: 2;
36
36
  readonly packageName: "@hjmds/react";
37
- readonly packageVersion: "1.7.0";
37
+ readonly packageVersion: "1.9.0";
38
38
  readonly surface: "web";
39
39
  readonly components: readonly [Readonly<{
40
40
  /** Canonical component id from the design-contracts catalog. */
@@ -1296,6 +1296,110 @@ export declare const reactRendererEvidence: {
1296
1296
  file: `test/${string}.test.tsx`;
1297
1297
  caseId: string;
1298
1298
  }>[];
1299
+ }>, {
1300
+ readonly componentId: "thinking-orb";
1301
+ readonly exportNames: readonly ["ThinkingOrb"];
1302
+ readonly subpath: "./thinking-orb";
1303
+ readonly scenarios: readonly ["default", "dark", "large-text", "rtl", "reduced-motion", "accessibility"];
1304
+ readonly proofs: readonly [{
1305
+ readonly scenarios: readonly ["default"];
1306
+ readonly file: "test/default-render.ssr.test.tsx";
1307
+ readonly caseId: "thinking-orb";
1308
+ }, {
1309
+ readonly scenarios: readonly ["dark", "large-text", "rtl", "reduced-motion", "accessibility"];
1310
+ readonly file: "test/thinking-orb.browser.test.tsx";
1311
+ readonly caseId: "thinking-orb";
1312
+ }];
1313
+ }, Readonly<{
1314
+ /** Canonical component id from the design-contracts catalog. */
1315
+ componentId: string;
1316
+ /** Public symbols that implement this contract on the declared subpath. */
1317
+ exportNames: readonly string[];
1318
+ /** Granular @hjmds/react export used by consumers. */
1319
+ subpath: `./${string}`;
1320
+ /** Scenarios supported by automated first-party renderer evidence. */
1321
+ scenarios: readonly ReactRendererEvidenceScenario[];
1322
+ /** Repository-local executable proof for every claimed scenario. */
1323
+ proofs: readonly Readonly<{
1324
+ scenarios: readonly ReactRendererEvidenceScenario[];
1325
+ file: `test/${string}.test.tsx`;
1326
+ caseId: string;
1327
+ }>[];
1328
+ }>, Readonly<{
1329
+ /** Canonical component id from the design-contracts catalog. */
1330
+ componentId: string;
1331
+ /** Public symbols that implement this contract on the declared subpath. */
1332
+ exportNames: readonly string[];
1333
+ /** Granular @hjmds/react export used by consumers. */
1334
+ subpath: `./${string}`;
1335
+ /** Scenarios supported by automated first-party renderer evidence. */
1336
+ scenarios: readonly ReactRendererEvidenceScenario[];
1337
+ /** Repository-local executable proof for every claimed scenario. */
1338
+ proofs: readonly Readonly<{
1339
+ scenarios: readonly ReactRendererEvidenceScenario[];
1340
+ file: `test/${string}.test.tsx`;
1341
+ caseId: string;
1342
+ }>[];
1343
+ }>, Readonly<{
1344
+ /** Canonical component id from the design-contracts catalog. */
1345
+ componentId: string;
1346
+ /** Public symbols that implement this contract on the declared subpath. */
1347
+ exportNames: readonly string[];
1348
+ /** Granular @hjmds/react export used by consumers. */
1349
+ subpath: `./${string}`;
1350
+ /** Scenarios supported by automated first-party renderer evidence. */
1351
+ scenarios: readonly ReactRendererEvidenceScenario[];
1352
+ /** Repository-local executable proof for every claimed scenario. */
1353
+ proofs: readonly Readonly<{
1354
+ scenarios: readonly ReactRendererEvidenceScenario[];
1355
+ file: `test/${string}.test.tsx`;
1356
+ caseId: string;
1357
+ }>[];
1358
+ }>, Readonly<{
1359
+ /** Canonical component id from the design-contracts catalog. */
1360
+ componentId: string;
1361
+ /** Public symbols that implement this contract on the declared subpath. */
1362
+ exportNames: readonly string[];
1363
+ /** Granular @hjmds/react export used by consumers. */
1364
+ subpath: `./${string}`;
1365
+ /** Scenarios supported by automated first-party renderer evidence. */
1366
+ scenarios: readonly ReactRendererEvidenceScenario[];
1367
+ /** Repository-local executable proof for every claimed scenario. */
1368
+ proofs: readonly Readonly<{
1369
+ scenarios: readonly ReactRendererEvidenceScenario[];
1370
+ file: `test/${string}.test.tsx`;
1371
+ caseId: string;
1372
+ }>[];
1373
+ }>, Readonly<{
1374
+ /** Canonical component id from the design-contracts catalog. */
1375
+ componentId: string;
1376
+ /** Public symbols that implement this contract on the declared subpath. */
1377
+ exportNames: readonly string[];
1378
+ /** Granular @hjmds/react export used by consumers. */
1379
+ subpath: `./${string}`;
1380
+ /** Scenarios supported by automated first-party renderer evidence. */
1381
+ scenarios: readonly ReactRendererEvidenceScenario[];
1382
+ /** Repository-local executable proof for every claimed scenario. */
1383
+ proofs: readonly Readonly<{
1384
+ scenarios: readonly ReactRendererEvidenceScenario[];
1385
+ file: `test/${string}.test.tsx`;
1386
+ caseId: string;
1387
+ }>[];
1388
+ }>, Readonly<{
1389
+ /** Canonical component id from the design-contracts catalog. */
1390
+ componentId: string;
1391
+ /** Public symbols that implement this contract on the declared subpath. */
1392
+ exportNames: readonly string[];
1393
+ /** Granular @hjmds/react export used by consumers. */
1394
+ subpath: `./${string}`;
1395
+ /** Scenarios supported by automated first-party renderer evidence. */
1396
+ scenarios: readonly ReactRendererEvidenceScenario[];
1397
+ /** Repository-local executable proof for every claimed scenario. */
1398
+ proofs: readonly Readonly<{
1399
+ scenarios: readonly ReactRendererEvidenceScenario[];
1400
+ file: `test/${string}.test.tsx`;
1401
+ caseId: string;
1402
+ }>[];
1299
1403
  }>, Readonly<{
1300
1404
  /** Canonical component id from the design-contracts catalog. */
1301
1405
  componentId: string;