@hjmds/react 1.13.1 → 1.15.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 (127) hide show
  1. package/dist/agreement.d.ts.map +1 -1
  2. package/dist/agreement.js +5 -3
  3. package/dist/agreement.js.map +1 -1
  4. package/dist/asset.d.ts.map +1 -1
  5. package/dist/asset.js +4 -1
  6. package/dist/asset.js.map +1 -1
  7. package/dist/clipboard.d.ts.map +1 -1
  8. package/dist/clipboard.js +39 -5
  9. package/dist/clipboard.js.map +1 -1
  10. package/dist/code-block.d.ts.map +1 -1
  11. package/dist/code-block.js +3 -1
  12. package/dist/code-block.js.map +1 -1
  13. package/dist/collapsible.d.ts +4 -0
  14. package/dist/collapsible.d.ts.map +1 -1
  15. package/dist/collapsible.js +4 -4
  16. package/dist/collapsible.js.map +1 -1
  17. package/dist/command-palette.d.ts.map +1 -1
  18. package/dist/command-palette.js +3 -1
  19. package/dist/command-palette.js.map +1 -1
  20. package/dist/content-transition.d.ts +7 -3
  21. package/dist/content-transition.d.ts.map +1 -1
  22. package/dist/content-transition.js +47 -3
  23. package/dist/content-transition.js.map +1 -1
  24. package/dist/context-menu.d.ts.map +1 -1
  25. package/dist/context-menu.js +3 -1
  26. package/dist/context-menu.js.map +1 -1
  27. package/dist/date-entry.d.ts +9 -0
  28. package/dist/date-entry.d.ts.map +1 -0
  29. package/dist/date-entry.js +13 -0
  30. package/dist/date-entry.js.map +1 -0
  31. package/dist/design-profile.d.ts +18 -0
  32. package/dist/design-profile.d.ts.map +1 -0
  33. package/dist/design-profile.js +27 -0
  34. package/dist/design-profile.js.map +1 -0
  35. package/dist/document-resource.d.ts +3 -0
  36. package/dist/document-resource.d.ts.map +1 -0
  37. package/dist/document-resource.js +2 -0
  38. package/dist/document-resource.js.map +1 -0
  39. package/dist/effect-surface.d.ts.map +1 -1
  40. package/dist/effect-surface.js +2 -2
  41. package/dist/effect-surface.js.map +1 -1
  42. package/dist/evidence.d.ts +1 -1
  43. package/dist/evidence.js +1 -1
  44. package/dist/evidence.js.map +1 -1
  45. package/dist/field-group.d.ts +22 -0
  46. package/dist/field-group.d.ts.map +1 -0
  47. package/dist/field-group.js +20 -0
  48. package/dist/field-group.js.map +1 -0
  49. package/dist/floating-action-button.d.ts +1 -1
  50. package/dist/forms.d.ts +1 -0
  51. package/dist/forms.d.ts.map +1 -1
  52. package/dist/forms.js +5 -3
  53. package/dist/forms.js.map +1 -1
  54. package/dist/heading.d.ts.map +1 -1
  55. package/dist/heading.js +3 -1
  56. package/dist/heading.js.map +1 -1
  57. package/dist/image-comparison.d.ts +12 -0
  58. package/dist/image-comparison.d.ts.map +1 -0
  59. package/dist/image-comparison.js +14 -0
  60. package/dist/image-comparison.js.map +1 -0
  61. package/dist/internal/document-resource.d.ts +9 -0
  62. package/dist/internal/document-resource.d.ts.map +1 -0
  63. package/dist/internal/document-resource.js +37 -0
  64. package/dist/internal/document-resource.js.map +1 -0
  65. package/dist/internal/overlay-origin-motion.d.ts +13 -0
  66. package/dist/internal/overlay-origin-motion.d.ts.map +1 -0
  67. package/dist/internal/overlay-origin-motion.js +76 -0
  68. package/dist/internal/overlay-origin-motion.js.map +1 -0
  69. package/dist/internal.d.ts +54 -0
  70. package/dist/internal.d.ts.map +1 -1
  71. package/dist/internal.js +7 -1
  72. package/dist/internal.js.map +1 -1
  73. package/dist/layout.d.ts.map +1 -1
  74. package/dist/layout.js +6 -3
  75. package/dist/layout.js.map +1 -1
  76. package/dist/menubar.d.ts.map +1 -1
  77. package/dist/menubar.js +3 -1
  78. package/dist/menubar.js.map +1 -1
  79. package/dist/navigation.d.ts.map +1 -1
  80. package/dist/navigation.js +30 -9
  81. package/dist/navigation.js.map +1 -1
  82. package/dist/overlays.d.ts +3 -0
  83. package/dist/overlays.d.ts.map +1 -1
  84. package/dist/overlays.js +16 -12
  85. package/dist/overlays.js.map +1 -1
  86. package/dist/popover.d.ts +3 -0
  87. package/dist/popover.d.ts.map +1 -1
  88. package/dist/popover.js +11 -5
  89. package/dist/popover.js.map +1 -1
  90. package/dist/portal.d.ts +3 -1
  91. package/dist/portal.d.ts.map +1 -1
  92. package/dist/portal.js +5 -2
  93. package/dist/portal.js.map +1 -1
  94. package/dist/progressive-blur.d.ts +7 -0
  95. package/dist/progressive-blur.d.ts.map +1 -0
  96. package/dist/progressive-blur.js +20 -0
  97. package/dist/progressive-blur.js.map +1 -0
  98. package/dist/provider.d.ts +4 -0
  99. package/dist/provider.d.ts.map +1 -1
  100. package/dist/provider.js +5 -2
  101. package/dist/provider.js.map +1 -1
  102. package/dist/rating.d.ts +19 -0
  103. package/dist/rating.d.ts.map +1 -0
  104. package/dist/rating.js +34 -0
  105. package/dist/rating.js.map +1 -0
  106. package/dist/screens.d.ts +7 -2
  107. package/dist/screens.d.ts.map +1 -1
  108. package/dist/screens.js +20 -7
  109. package/dist/screens.js.map +1 -1
  110. package/dist/selection.d.ts +4 -0
  111. package/dist/selection.d.ts.map +1 -1
  112. package/dist/selection.js +48 -3
  113. package/dist/selection.js.map +1 -1
  114. package/dist/styles.css +69 -9
  115. package/dist/styles.layered.css +69 -9
  116. package/dist/task-list.d.ts +6 -1
  117. package/dist/task-list.d.ts.map +1 -1
  118. package/dist/task-list.js +8 -3
  119. package/dist/task-list.js.map +1 -1
  120. package/dist/text-annotation.d.ts +10 -0
  121. package/dist/text-annotation.d.ts.map +1 -0
  122. package/dist/text-annotation.js +107 -0
  123. package/dist/text-annotation.js.map +1 -0
  124. package/dist/theme.d.ts.map +1 -1
  125. package/dist/theme.js +28 -7
  126. package/dist/theme.js.map +1 -1
  127. package/package.json +38 -3
@@ -6,10 +6,52 @@ import { easing, motion as timing } from "@hjmds/design-contracts/foundations";
6
6
  import { useHjmTheme } from "./provider.js";
7
7
  /** Motion Primitives' keyed transition pattern, adapted to HJM's single active subtree.
8
8
  * No exiting interactive copy: it would duplicate fields and focus targets. See THIRD_PARTY_NOTICES. */
9
- export function ContentTransition({ stateKey, children, motion: preference = "system", preset = "fade", focusTarget, layoutStyle }) {
10
- const { environment } = useHjmTheme();
9
+ export function ContentTransition({ stateKey, children, motion: preference = "system", preset: suppliedPreset, focusTarget, animateHeight = false, enterOnMount = false, layoutStyle }) {
10
+ const { environment, designProfile } = useHjmTheme();
11
+ const preset = suppliedPreset ?? designProfile?.interactions.contentTransition ?? "fade";
11
12
  const from = resolveContentTransition(preset, environment.direction);
12
13
  const enabled = preference !== "none" && !environment.reducedMotion;
14
+ const frame = useRef(null);
15
+ const measure = useRef(null);
16
+ useLayoutEffect(() => {
17
+ const outer = frame.current;
18
+ const inner = measure.current;
19
+ if (!outer || !inner || !animateHeight || !enabled || typeof ResizeObserver === "undefined")
20
+ return;
21
+ let previous = inner.getBoundingClientRect().height;
22
+ let animation;
23
+ const settle = () => { animation?.cancel(); animation = undefined; outer.style.height = ""; outer.style.overflow = ""; previous = inner.getBoundingClientRect().height; };
24
+ const observer = new ResizeObserver(() => {
25
+ const next = inner.getBoundingClientRect().height;
26
+ if (Math.abs(next - previous) < 0.5)
27
+ return;
28
+ const fromHeight = animation ? outer.getBoundingClientRect().height : previous;
29
+ animation?.cancel();
30
+ previous = next;
31
+ if (document.hidden || typeof outer.animate !== "function") {
32
+ settle();
33
+ return;
34
+ }
35
+ // Measure the unconfined inner flow; transform/layout projection would scale
36
+ // text or duplicate input subtrees. WAAPI affects only the surrounding height.
37
+ outer.style.height = `${next}px`;
38
+ outer.style.overflow = "hidden";
39
+ try {
40
+ const current = outer.animate([{ height: `${fromHeight}px` }, { height: `${next}px` }], { duration: timing.normal, easing: `cubic-bezier(${easing.enter.join(",")})` });
41
+ animation = current;
42
+ current.onfinish = () => { if (animation === current)
43
+ settle(); };
44
+ }
45
+ catch {
46
+ settle();
47
+ }
48
+ });
49
+ observer.observe(inner);
50
+ const visibility = () => { if (document.hidden)
51
+ settle(); };
52
+ document.addEventListener("visibilitychange", visibility);
53
+ return () => { observer.disconnect(); document.removeEventListener("visibilitychange", visibility); settle(); };
54
+ }, [animateHeight, enabled]);
13
55
  const first = useRef(true);
14
56
  const host = useRef(null);
15
57
  const restore = useRef(false);
@@ -26,7 +68,9 @@ export function ContentTransition({ stateKey, children, motion: preference = "sy
26
68
  restore.current = false;
27
69
  first.current = false;
28
70
  }, [stateKey, focusTarget]);
29
- return _jsx("div", { style: layoutStyle, children: _jsx(motion.div, { ref: capturePanel, initial: enabled && !first.current ? { opacity: from.opacity, x: from.translateX, y: from.translateY, scale: from.scale } : false, animate: { opacity: 1, x: 0, y: 0, scale: 1 }, transition: { duration: enabled ? timing.normal / 1000 : 0, ease: [...easing.enter] }, children: children }, stateKey) });
71
+ // Caller placement stays on a separate stable wrapper so measuring and
72
+ // animating the inner flow does not include the product margin or flex placement.
73
+ return _jsx("div", { style: layoutStyle, children: _jsx("div", { ref: frame, children: _jsx("div", { ref: measure, style: { display: "flow-root" }, children: _jsx(motion.div, { ref: capturePanel, initial: enabled && (enterOnMount || !first.current) ? { opacity: from.opacity, x: from.translateX, y: from.translateY, scale: from.scale } : false, animate: { opacity: 1, x: 0, y: 0, scale: 1 }, transition: { duration: enabled ? timing.normal / 1000 : 0, ease: [...easing.enter] }, children: children }, stateKey) }) }) });
30
74
  }
31
75
  /** Whole-text fade preserves graphemes, text wrapping, selection and a single spoken value. */
32
76
  export function TextTransition({ text, motion: preference, preset, layoutStyle }) {
@@ -1 +1 @@
1
- {"version":3,"file":"content-transition.js","sourceRoot":"","sources":["../src/content-transition.tsx"],"names":[],"mappings":"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","sourcesContent":["import { resolveContentTransition, type ContentTransitionPreset } from \"@hjmds/design-contracts/content-transition\";\nimport { 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\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type ContentTransitionProps = {\n preset?: ContentTransitionPreset;\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 /** Canonical layout-only placement on the stable outer wrapper, not the keyed panel. */\n layoutStyle?: HjmCompositionStyleProp;\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\", preset = \"fade\", focusTarget, layoutStyle }: ContentTransitionProps) {\n const { environment } = useHjmTheme();\n const from = resolveContentTransition(preset, environment.direction);\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 style={layoutStyle}><motion.div ref={capturePanel} key={stateKey} initial={enabled && !first.current ? { opacity: from.opacity, x: from.translateX, y: from.translateY, scale: from.scale } : false}\n animate={{ opacity: 1, x: 0, y: 0, scale: 1 }} transition={{ duration: enabled ? timing.normal / 1000 : 0, ease: [...easing.enter] }}>{children}</motion.div></div>;\n}\nexport type TextTransitionProps = { preset?: ContentTransitionPreset; text: string; motion?: \"system\" | \"none\"; layoutStyle?: HjmCompositionStyleProp };\n/** Whole-text fade preserves graphemes, text wrapping, selection and a single spoken value. */\nexport function TextTransition({ text, motion: preference, preset, layoutStyle }: TextTransitionProps) {\n return <ContentTransition stateKey={text} {...(preset ? { preset } : {})} {...(preference ? { motion: preference } : {})} {...(layoutStyle ? { layoutStyle } : {})}>{text}</ContentTransition>;\n}\n"]}
1
+ {"version":3,"file":"content-transition.js","sourceRoot":"","sources":["../src/content-transition.tsx"],"names":[],"mappings":"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","sourcesContent":["import { resolveContentTransition, type ContentTransitionPreset } from \"@hjmds/design-contracts/content-transition\";\nimport { 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\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type ContentTransitionProps = {\n preset?: ContentTransitionPreset;\n stateKey: string; children: ReactNode; motion?: \"system\" | \"none\";\n /** Opt-in measured height transition; never renders a second interactive panel. */\n animateHeight?: boolean;\n /** Animate newly inserted content without changing its stable key or delaying its data. */\n enterOnMount?: boolean;\n /** Layout belongs to the stable outer frame, not the keyed panel. */\n layoutStyle?: HjmCompositionStyleProp;\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\", preset: suppliedPreset, focusTarget, animateHeight = false, enterOnMount = false, layoutStyle }: ContentTransitionProps) {\n const { environment, designProfile } = useHjmTheme();\n const preset = suppliedPreset ?? designProfile?.interactions.contentTransition ?? \"fade\";\n const from = resolveContentTransition(preset, environment.direction);\n const enabled = preference !== \"none\" && !environment.reducedMotion;\n const frame = useRef<HTMLDivElement>(null);\n const measure = useRef<HTMLDivElement>(null);\n useLayoutEffect(() => {\n const outer = frame.current; const inner = measure.current;\n if (!outer || !inner || !animateHeight || !enabled || typeof ResizeObserver === \"undefined\") return;\n let previous = inner.getBoundingClientRect().height;\n let animation: Animation | undefined;\n const settle = () => { animation?.cancel(); animation = undefined; outer.style.height = \"\"; outer.style.overflow = \"\"; previous = inner.getBoundingClientRect().height; };\n const observer = new ResizeObserver(() => {\n const next = inner.getBoundingClientRect().height;\n if (Math.abs(next - previous) < 0.5) return;\n const fromHeight = animation ? outer.getBoundingClientRect().height : previous;\n animation?.cancel(); previous = next;\n if (document.hidden || typeof outer.animate !== \"function\") { settle(); return; }\n // Measure the unconfined inner flow; transform/layout projection would scale\n // text or duplicate input subtrees. WAAPI affects only the surrounding height.\n outer.style.height = `${next}px`; outer.style.overflow = \"hidden\";\n try {\n const current = outer.animate([{ height: `${fromHeight}px` }, { height: `${next}px` }], { duration: timing.normal, easing: `cubic-bezier(${easing.enter.join(\",\")})` });\n animation = current;\n current.onfinish = () => { if (animation === current) settle(); };\n } catch { settle(); }\n });\n observer.observe(inner);\n const visibility = () => { if (document.hidden) settle(); };\n document.addEventListener(\"visibilitychange\", visibility);\n return () => { observer.disconnect(); document.removeEventListener(\"visibilitychange\", visibility); settle(); };\n }, [animateHeight, enabled]);\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 // Caller placement stays on a separate stable wrapper so measuring and\n // animating the inner flow does not include the product margin or flex placement.\n return <div style={layoutStyle}><div ref={frame}><div ref={measure} style={{ display: \"flow-root\" }}><motion.div ref={capturePanel} key={stateKey} initial={enabled && (enterOnMount || !first.current) ? { opacity: from.opacity, x: from.translateX, y: from.translateY, scale: from.scale } : false}\n animate={{ opacity: 1, x: 0, y: 0, scale: 1 }} transition={{ duration: enabled ? timing.normal / 1000 : 0, ease: [...easing.enter] }}>{children}</motion.div></div></div></div>;\n}\nexport type TextTransitionProps = { preset?: ContentTransitionPreset; text: string; motion?: \"system\" | \"none\"; layoutStyle?: HjmCompositionStyleProp };\n/** Whole-text fade preserves graphemes, text wrapping, selection and a single spoken value. */\nexport function TextTransition({ text, motion: preference, preset, layoutStyle }: TextTransitionProps) {\n return <ContentTransition stateKey={text} {...(preset ? { preset } : {})} {...(preference ? { motion: preference } : {})} {...(layoutStyle ? { layoutStyle } : {})}>{text}</ContentTransition>;\n}\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;AAGf,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,+BAyKvB"}
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;AAGf,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,+BA2KvB"}
@@ -123,8 +123,10 @@ export function ContextMenu({ children, items, accessibilityLabel, onAction, cla
123
123
  zIndex: getModalLayer(0),
124
124
  minInlineSize: menuRecipe.minWidth,
125
125
  }, children: items.map((item) => {
126
+ // A newly positioned menu can enter a stationary cursor; that
127
+ // boundary event must not undo the user's keyboard selection.
126
128
  const index = enabled.findIndex((candidate) => candidate.id === item.id);
127
- 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)
129
+ 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", onMouseMove: () => { if (index >= 0)
128
130
  setActiveIndex(index); }, onClick: () => {
129
131
  if (item.disabled)
130
132
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"context-menu.js","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EACL,wBAAwB,GAIzB,MAAM,iDAAiD,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EACL,WAAW,EACX,SAAS,EACT,KAAK,EACL,eAAe,EACf,MAAM,EACN,QAAQ,GAGT,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AActD,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,MAAM,UAAU,WAAW,CAA8B,EACvD,QAAQ,EACR,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,WAAW,GACW;IACtB,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;IAC7C,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,KAAK,EAAE,CAAC;IACvB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAA2B,IAAI,CAAC,CAAC;IACrE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,MAAM,CAA4C,SAAS,CAAC,CAAC;IAC/E,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEvD,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,MAA6B,EAAE,OAAiC,EAAE,EAAE;QAC5F,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC;QAC7C,cAAc,CAAC,OAAO,GAAG,aAAa,YAAY,WAAW,IAAI,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC;YACvG,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACpB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,qBAAqB,EAAE,IAAI,IAAI,CAAC;QAC9D,SAAS,CAAC,wBAAwB,CAChC,MAAM,EACN,OAAO,EACP,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAChE,CAAC,CAAC;QACH,cAAc,CAAC,CAAC,CAAC,CAAC;QAClB,SAAS,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAC7C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,IAA2B,EAAE,EAAE;QAC7D,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,cAAc,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAChC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,eAAe,CAAC,GAAG,EAAE;QACnB,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;YAAE,OAAO;QACxE,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QACvF,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACzF,2JAA2J;QAC3J,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC;YAAE,SAAS,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5D,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAE1B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO;QAC5B,MAAM,SAAS,GAAG,CAAC,KAAoB,EAAE,EAAE;YACzC,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC3B,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,SAAS,CAAC,IAAI,CAAC,CAAC;gBAChB,mEAAmE;gBACnE,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;gBAChC,OAAO;YACT,CAAC;YACD,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;gBACzD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,cAAc,CAAC,CAAC,QAAQ,EAAE,EAAE;oBAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC;oBACrE,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBAC/D,CAAC,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBAChD,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,cAAc,CAAC,KAAK,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC3E,OAAO;YACT,CAAC;YACD,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;gBACrG,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBACpG,SAAS,CAAC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC;gBACjC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;oBAC/B,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC/B,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBAC/C,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;gBAClC,IAAI,IAAI,EAAE,CAAC;oBAAC,QAAQ,CAAC,IAAI,CAAC,EAAS,CAAC,CAAC;oBAAC,SAAS,CAAC,IAAI,CAAC,CAAC;oBAAC,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;gBAAC,CAAC;gBACzF,OAAO;YACT,CAAC;YACD,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBACxB,SAAS,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QACF,MAAM,aAAa,GAAG,CAAC,KAAmB,EAAE,EAAE;YAC5C,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBAChF,SAAS,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;QACH,CAAC,CAAC;QACF,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAChD,QAAQ,CAAC,gBAAgB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QACxD,OAAO,GAAG,EAAE;YACV,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACnD,QAAQ,CAAC,mBAAmB,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QAC7D,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE7C,OAAO,CACL,eACE,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,CAAC,EACX,SAAS,EAAE,UAAU,CAAC,uBAAuB,EAAE,SAAS,CAAC,EACzD,KAAK,EAAE,WAAW,EAClB,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;YACvB,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC,EACD,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YACnB,sEAAsE;YACtE,6DAA6D;YAC7D,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,GAAG,KAAK,aAAa,EAAE,CAAC;gBAC3E,KAAK,CAAC,cAAc,EAAE,CAAC;gBACvB,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YACzB,CAAC;QACH,CAAC,EACD,aAAa,EAAE,CAAC,KAAK,EAAE,EAAE;YACvB,IAAI,KAAK,CAAC,WAAW,KAAK,OAAO;gBAAE,OAAO;YAC1C,SAAS,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,cAAc,CAAC,CAAC;QAClH,CAAC,EACD,WAAW,EAAE,GAAG,EAAE,GAAG,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS;YAAE,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAC5F,eAAe,EAAE,GAAG,EAAE,GAAG,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS;YAAE,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAE/F,QAAQ,EACR,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,CACjB,KAAC,SAAS,cACR,cACE,IAAI,EAAC,MAAM,EACX,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,CAAC,CAAC,2BACW,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,SAAS,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,gBAC7E,kBAAkB,EAC9B,SAAS,EAAC,kBAAkB,EAC5B,KAAK,EAAE;wBACL,GAAG,EAAE,MAAM,CAAC,CAAC;wBACb,IAAI,EAAE,MAAM,CAAC,CAAC;wBACd,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;wBACxB,aAAa,EAAE,UAAU,CAAC,QAAQ;qBAClB,YAEjB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;wBAClB,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;wBACzE,OAAO,CACL,eAEE,EAAE,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,EACtD,IAAI,EAAC,UAAU,mBACA,IAAI,CAAC,QAAQ,IAAI,SAAS,iBAC5B,KAAK,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,eAC1D,IAAI,CAAC,IAAI,EACpB,SAAS,EAAC,wBAAwB,EAClC,YAAY,EAAE,GAAG,EAAE,GAAG,IAAI,KAAK,IAAI,CAAC;gCAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAC9D,OAAO,EAAE,GAAG,EAAE;gCACZ,IAAI,IAAI,CAAC,QAAQ;oCAAE,OAAO;gCAC1B,QAAQ,CAAC,IAAI,CAAC,EAAS,CAAC,CAAC;gCACzB,SAAS,CAAC,IAAI,CAAC,CAAC;gCAChB,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;4BAClC,CAAC,aAED,yBAAO,IAAI,CAAC,KAAK,GAAQ,EACxB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAK,SAAS,EAAC,4BAA4B,YAAE,IAAI,CAAC,QAAQ,GAAO,CAAC,CAAC,CAAC,IAAI,KAhBpF,IAAI,CAAC,EAAE,CAiBR,CACP,CAAC;oBACJ,CAAC,CAAC,GACE,GACI,CACb,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC","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 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
+ {"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"]}
@@ -0,0 +1,9 @@
1
+ import { type DateEntryControlProps } from "@hjmds/design-contracts/date-entry";
2
+ import type { HjmCompositionStyleProp } from "./composition-style.js";
3
+ export type DateEntryProps = DateEntryControlProps & Readonly<{
4
+ className?: string;
5
+ layoutStyle?: HjmCompositionStyleProp;
6
+ }>;
7
+ /** Direct date drafts reuse TextField; Calendar/DatePicker continue to own calendar selection. */
8
+ export declare function DateEntry(props: DateEntryProps): import("react").JSX.Element;
9
+ //# sourceMappingURL=date-entry.d.ts.map
@@ -0,0 +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;AAE/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,kGAAkG;AAClG,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,+BAoB9C"}
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId } from "react";
3
+ import { resolveDateEntryControl, updateDateEntryDraft } from "@hjmds/design-contracts/date-entry";
4
+ import { TextField } from "./forms.js";
5
+ /** Direct date drafts reuse TextField; Calendar/DatePicker continue to own calendar selection. */
6
+ export function DateEntry(props) {
7
+ const id = useId();
8
+ const resolved = resolveDateEntryControl(props);
9
+ const support = [props.description ? `${id}-hint` : undefined, resolved.error ? `${id}-error` : undefined].filter(Boolean).join(" ") || undefined;
10
+ return _jsxs("fieldset", { className: props.className, disabled: props.disabled, "aria-describedby": support, style: { border: 0, margin: 0, padding: 0, minWidth: 0, ...props.layoutStyle }, children: [_jsx("legend", { style: { marginBlockEnd: "var(--hjm-space-sm)" }, children: props.labels.label }), props.description ? _jsx("p", { id: `${id}-hint`, children: props.description }) : null, resolved.error ? _jsx("p", { id: `${id}-error`, role: "alert", style: { color: "var(--hjm-color-danger)" }, children: resolved.error }) : null, _jsx("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 10ch), 1fr))", gap: "var(--hjm-space-md)" }, children: resolved.fields.map(field => _jsx(TextField, { id: `${id}-${field.part}`, label: props.labels[field.part], 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, disabled: props.disabled ?? false, readOnly: props.readOnly ?? false, "aria-describedby": support, "aria-invalid": Boolean(resolved.error && field.invalid), onBlur: () => props.onBlur?.(field.part), onValueChange: value => { if (!props.disabled && !props.readOnly)
11
+ props.onValueChange(updateDateEntryDraft(props.value, field.part, value)); } }, field.part)) })] });
12
+ }
13
+ //# sourceMappingURL=date-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-entry.js","sourceRoot":"","sources":["../src/date-entry.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useId } from \"react\";\nimport { resolveDateEntryControl, updateDateEntryDraft, type DateEntryControlProps } from \"@hjmds/design-contracts/date-entry\";\nimport { TextField } from \"./forms.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\nexport type DateEntryProps = DateEntryControlProps & Readonly<{ className?: string; layoutStyle?: HjmCompositionStyleProp }>;\n/** Direct date drafts reuse TextField; Calendar/DatePicker continue to own calendar selection. */\nexport function DateEntry(props: DateEntryProps) {\n const id = useId();\n const resolved = resolveDateEntryControl(props);\n const support = [props.description ? `${id}-hint` : undefined, resolved.error ? `${id}-error` : undefined].filter(Boolean).join(\" \") || undefined;\n return <fieldset className={props.className} disabled={props.disabled}\n aria-describedby={support} style={{ border: 0, margin: 0, padding: 0, minWidth: 0, ...props.layoutStyle }}>\n <legend style={{ marginBlockEnd: \"var(--hjm-space-sm)\" }}>{props.labels.label}</legend>\n {props.description ? <p id={`${id}-hint`}>{props.description}</p> : null}\n {resolved.error ? <p id={`${id}-error`} role=\"alert\" style={{ color: \"var(--hjm-color-danger)\" }}>{resolved.error}</p> : null}\n <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(auto-fit, minmax(min(100%, 10ch), 1fr))\", gap: \"var(--hjm-space-md)\" }}>\n {resolved.fields.map(field => <TextField key={field.part} id={`${id}-${field.part}`}\n label={props.labels[field.part]} 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} disabled={props.disabled ?? false} readOnly={props.readOnly ?? false}\n aria-describedby={support} aria-invalid={Boolean(resolved.error && field.invalid)}\n onBlur={() => props.onBlur?.(field.part)}\n onValueChange={value => { if (!props.disabled && !props.readOnly) props.onValueChange(updateDateEntryDraft(props.value, field.part, value)); }} />)}\n </div>\n </fieldset>;\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { type ReactNode } from "react";
2
+ import { type ScreenLayoutProps } from "./screens.js";
3
+ import type { HjmDesignProfile } from "@hjmds/design-contracts/design-profile";
4
+ export type OverviewScreenProps = Omit<ScreenLayoutProps, "children"> & Readonly<{
5
+ items: readonly Readonly<{
6
+ id: string;
7
+ children: ReactNode;
8
+ }>[];
9
+ toolbar?: ReactNode;
10
+ toolbarLabel: string;
11
+ collection?: HjmDesignProfile["compositions"]["collection"];
12
+ toolbarPresentation?: HjmDesignProfile["compositions"]["toolbar"];
13
+ }>;
14
+ /** Optional collection screen; ScreenLayout still owns loading/error/scrolling.
15
+ * Unlike settings/inbox it owns a profile-selected collection and persistent tools.
16
+ * It never owns product mutations or chooses a different component for each theme. */
17
+ export declare function OverviewScreen({ items, toolbar, toolbarLabel, collection: suppliedCollection, toolbarPresentation: suppliedToolbarPresentation, ...screen }: OverviewScreenProps): import("react").JSX.Element;
18
+ //# sourceMappingURL=design-profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-profile.d.ts","sourceRoot":"","sources":["../src/design-profile.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjD,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAMpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAE/E,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC;IAC/E,KAAK,EAAE,SAAS,QAAQ,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,EAAE,CAAC;IAChE,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC,YAAY,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC;CACnE,CAAC,CAAC;AAEH;;sFAEsF;AACtF,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,2BAA2B,EAAE,GAAG,MAAM,EAAE,EAAE,mBAAmB,+BAqBhL"}
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { ScreenLayout } from "./screens.js";
4
+ import { Collapsible } from "./collapsible.js";
5
+ import { EffectSurface } from "./effect-surface.js";
6
+ import { Grid, Stack, Surface } from "./layout.js";
7
+ import { useHjmTheme } from "./provider.js";
8
+ import { resolveDesignProfileCollection } from "@hjmds/design-contracts/design-profile-layout";
9
+ /** Optional collection screen; ScreenLayout still owns loading/error/scrolling.
10
+ * Unlike settings/inbox it owns a profile-selected collection and persistent tools.
11
+ * It never owns product mutations or chooses a different component for each theme. */
12
+ export function OverviewScreen({ items, toolbar, toolbarLabel, collection: suppliedCollection, toolbarPresentation: suppliedToolbarPresentation, ...screen }) {
13
+ const { designProfile } = useHjmTheme();
14
+ const collection = suppliedCollection ?? designProfile?.compositions.collection ?? "rows";
15
+ const toolbarPresentation = suppliedToolbarPresentation ?? designProfile?.compositions.toolbar ?? "inline";
16
+ const [toolsOpen, setToolsOpen] = useState(true);
17
+ if (!toolbarLabel.trim())
18
+ throw new TypeError("Overview tools need a localized label");
19
+ const ids = new Set(items.map(item => item.id));
20
+ if (ids.size !== items.length || items.some(item => !item.id.trim()))
21
+ throw new TypeError("Overview items need unique stable ids");
22
+ // Retain the same effect/grid/surface subtree when the profile changes: optional
23
+ // decoration can disappear without remounting fields, focus or local state.
24
+ const canvas = designProfile?.material.canvas ?? { layers: ["mesh"], intensity: 0, active: false };
25
+ return _jsx(EffectSurface, { descriptor: canvas, layoutStyle: { width: "100%" }, children: _jsx(ScreenLayout, { ...screen, children: _jsxs(Stack, { gap: "xl", children: [toolbar ? _jsx(Collapsible, { trigger: toolbarLabel, open: toolsOpen, onOpenChange: setToolsOpen, presentation: toolbarPresentation === "inline" ? "inline" : "disclosure", keepMounted: true, children: toolbar }) : null, _jsx(Grid, { ...resolveDesignProfileCollection(collection), children: items.map(item => _jsx(Surface, { padding: collection === "rows" ? "md" : "xl", tone: collection === "rows" ? "default" : "raised", bordered: true, radius: "lg", children: _jsx(EffectSurface, { descriptor: designProfile?.material.card ?? { layers: ["mesh"], intensity: 0, active: false }, className: "hjm-profile-card-effect", children: item.children }) }, item.id)) })] }) }) });
26
+ }
27
+ //# sourceMappingURL=design-profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-profile.js","sourceRoot":"","sources":["../src/design-profile.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, type ReactNode } from \"react\";\nimport { ScreenLayout, type ScreenLayoutProps } from \"./screens.js\";\nimport { Collapsible } from \"./collapsible.js\";\nimport { EffectSurface } from \"./effect-surface.js\";\nimport { Grid, Stack, Surface } from \"./layout.js\";\nimport { useHjmTheme } from \"./provider.js\";\nimport { resolveDesignProfileCollection } from \"@hjmds/design-contracts/design-profile-layout\";\nimport type { HjmDesignProfile } from \"@hjmds/design-contracts/design-profile\";\n\nexport type OverviewScreenProps = Omit<ScreenLayoutProps, \"children\"> & Readonly<{\n items: readonly Readonly<{ id: string; children: ReactNode }>[];\n toolbar?: ReactNode;\n toolbarLabel: string;\n collection?: HjmDesignProfile[\"compositions\"][\"collection\"];\n toolbarPresentation?: HjmDesignProfile[\"compositions\"][\"toolbar\"];\n}>;\n\n/** Optional collection screen; ScreenLayout still owns loading/error/scrolling.\n * Unlike settings/inbox it owns a profile-selected collection and persistent tools.\n * It never owns product mutations or chooses a different component for each theme. */\nexport function OverviewScreen({ items, toolbar, toolbarLabel, collection: suppliedCollection, toolbarPresentation: suppliedToolbarPresentation, ...screen }: OverviewScreenProps) {\n const { designProfile } = useHjmTheme();\n const collection = suppliedCollection ?? designProfile?.compositions.collection ?? \"rows\";\n const toolbarPresentation = suppliedToolbarPresentation ?? designProfile?.compositions.toolbar ?? \"inline\";\n const [toolsOpen, setToolsOpen] = useState(true);\n if (!toolbarLabel.trim()) throw new TypeError(\"Overview tools need a localized label\");\n const ids = new Set(items.map(item => item.id));\n if (ids.size !== items.length || items.some(item => !item.id.trim())) throw new TypeError(\"Overview items need unique stable ids\");\n // Retain the same effect/grid/surface subtree when the profile changes: optional\n // decoration can disappear without remounting fields, focus or local state.\n const canvas = designProfile?.material.canvas ?? { layers: [\"mesh\"] as const, intensity: 0, active: false };\n return <EffectSurface descriptor={canvas} layoutStyle={{ width: \"100%\" }}>\n <ScreenLayout {...screen}>\n <Stack gap=\"xl\">\n {toolbar ? <Collapsible trigger={toolbarLabel} open={toolsOpen} onOpenChange={setToolsOpen} presentation={toolbarPresentation === \"inline\" ? \"inline\" : \"disclosure\"} keepMounted>{toolbar}</Collapsible> : null}\n <Grid {...resolveDesignProfileCollection(collection)}>\n {items.map(item => <Surface key={item.id} padding={collection === \"rows\" ? \"md\" : \"xl\"} tone={collection === \"rows\" ? \"default\" : \"raised\"} bordered radius=\"lg\"><EffectSurface descriptor={designProfile?.material.card ?? { layers: [\"mesh\"], intensity: 0, active: false }} className=\"hjm-profile-card-effect\">{item.children}</EffectSurface></Surface>)}\n </Grid>\n </Stack>\n </ScreenLayout>\n </EffectSurface>;\n}\n"]}
@@ -0,0 +1,3 @@
1
+ export { DocumentResource } from "./internal/document-resource.js";
2
+ export type { DocumentResourceProps } from "./internal/document-resource.js";
3
+ //# sourceMappingURL=document-resource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-resource.d.ts","sourceRoot":"","sources":["../src/document-resource.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,YAAY,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { DocumentResource } from "./internal/document-resource.js";
2
+ //# sourceMappingURL=document-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-resource.js","sourceRoot":"","sources":["../src/document-resource.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC","sourcesContent":["export { DocumentResource } from \"./internal/document-resource.js\";\nexport type { DocumentResourceProps } from \"./internal/document-resource.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"effect-surface.d.ts","sourceRoot":"","sources":["../src/effect-surface.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,EAAwB,KAAK,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AAE5G,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,UAAU,CAAC,EAAE,uBAAuB,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,gGAAgG;IAAC,WAAW,CAAC,EAAE,uBAAuB,CAAC;CAAE,CAAC,CAAC;AACtQ,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,EAAE,UAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,kBAAkB,+BA2CtG"}
1
+ {"version":3,"file":"effect-surface.d.ts","sourceRoot":"","sources":["../src/effect-surface.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,EAAwB,KAAK,uBAAuB,EAAE,MAAM,wCAAwC,CAAC;AAE5G,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IAAE,UAAU,CAAC,EAAE,uBAAuB,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,gGAAgG;IAAC,WAAW,CAAC,EAAE,uBAAuB,CAAC;CAAE,CAAC,CAAC;AACtQ,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,EAAE,UAAe,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,kBAAkB,+BAgDtG"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useEffect, useId, useRef, useState } from "react";
3
3
  import { resolveEffectSurface } from "@hjmds/design-contracts/effect-surface";
4
4
  import { resolveColorReference } from "@hjmds/design-contracts/color-references";
@@ -52,6 +52,6 @@ export function EffectSurface({ descriptor = {}, children, className, layoutStyl
52
52
  return () => { animation?.cancel(); document.removeEventListener("visibilitychange", sync); };
53
53
  }, [effect.active, effect.period, environment.reducedMotion, visible]);
54
54
  const colors = effect.colors.map(color => resolveColorReference(color, palette));
55
- return _jsxs("div", { ref: host, className: className, "data-hjm-effect-surface": "", style: { ...layoutStyle, position: "relative", isolation: "isolate", overflow: "hidden", background: palette.theme.bg }, children: [_jsxs("svg", { ref: layer, "aria-hidden": "true", focusable: "false", viewBox: "0 0 100 100", preserveAspectRatio: "none", style: { position: "absolute", inset: 0, width: "100%", height: "100%", pointerEvents: "none", opacity: effect.intensity }, children: [_jsxs("defs", { children: [colors.map((color, index) => _jsxs("radialGradient", { id: `${id}-${index}`, children: [_jsx("stop", { offset: "0%", stopColor: color }), _jsx("stop", { offset: "100%", stopColor: color, stopOpacity: 0 })] }, index)), _jsx("pattern", { id: `${id}-grain`, width: 4, height: 4, patternUnits: "userSpaceOnUse", children: effect.points.map((point, index) => _jsx("circle", { cx: point.x / 25, cy: point.y / 25, r: point.radius / 10, fill: palette.theme.text, opacity: 0.28 }, index)) })] }), effect.layers.includes("mesh") && effect.anchors.map((anchor, index) => _jsx("ellipse", { cx: anchor.x, cy: anchor.y, rx: 65, ry: 70, fill: `url(#${id}-${index})` }, index)), effect.layers.includes("glow") && _jsx("ellipse", { cx: 50, cy: 10, rx: 70, ry: 95, fill: `url(#${id}-0)` }), effect.layers.includes("grain") && _jsx("rect", { width: 100, height: 100, fill: `url(#${id}-grain)` })] }), _jsx("div", { style: { position: "relative" }, children: children })] });
55
+ return _jsxs("div", { ref: host, className: className, "data-hjm-effect-surface": "", style: { ...layoutStyle, position: "relative", isolation: "isolate", overflow: "hidden", background: palette.theme.bg }, children: [_jsxs("svg", { ref: layer, "aria-hidden": "true", focusable: "false", style: { position: "absolute", inset: 0, width: "100%", height: "100%", pointerEvents: "none", opacity: effect.intensity }, children: [_jsx("defs", { children: effect.noise && _jsxs(_Fragment, { children: [_jsx("pattern", { id: `${id}-noise-tile`, width: effect.noise.size, height: effect.noise.size, x: effect.noise.offset, patternUnits: "userSpaceOnUse", children: _jsx("image", { href: effect.noise.uri, width: effect.noise.size, height: effect.noise.size }) }), _jsx("mask", { id: `${id}-noise-mask`, x: 0, y: 0, width: "100%", height: "100%", maskUnits: "userSpaceOnUse", children: _jsx("rect", { width: "100%", height: "100%", fill: `url(#${id}-noise-tile)` }) })] }) }), effect.noise && _jsx("rect", { width: "100%", height: "100%", fill: palette.theme.text, mask: `url(#${id}-noise-mask)` }), _jsxs("svg", { width: "100%", height: "100%", viewBox: "0 0 100 100", preserveAspectRatio: "none", children: [_jsxs("defs", { children: [colors.map((color, index) => _jsxs("radialGradient", { id: `${id}-${index}`, children: [_jsx("stop", { offset: "0%", stopColor: color }), _jsx("stop", { offset: "100%", stopColor: color, stopOpacity: 0 })] }, index)), _jsx("pattern", { id: `${id}-grain`, width: 4, height: 4, patternUnits: "userSpaceOnUse", children: effect.points.map((point, index) => _jsx("circle", { cx: point.x / 25, cy: point.y / 25, r: point.radius / 10, fill: palette.theme.text, opacity: 0.28 }, index)) })] }), effect.layers.includes("mesh") && effect.anchors.map((anchor, index) => _jsx("ellipse", { cx: anchor.x, cy: anchor.y, rx: 65, ry: 70, fill: `url(#${id}-${index})` }, index)), effect.layers.includes("glow") && _jsx("ellipse", { cx: 50, cy: 10, rx: 70, ry: 95, fill: `url(#${id}-0)` }), effect.layers.includes("grain") && _jsx("rect", { width: 100, height: 100, fill: `url(#${id}-grain)` })] })] }), _jsx("div", { style: { position: "relative" }, children: children })] });
56
56
  }
57
57
  //# sourceMappingURL=effect-surface.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"effect-surface.js","sourceRoot":"","sources":["../src/effect-surface.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { resolveEffectSurface, type EffectSurfaceDescriptor } from \"@hjmds/design-contracts/effect-surface\";\nimport { resolveColorReference } from \"@hjmds/design-contracts/color-references\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\nimport { useHjmTheme } from \"./provider.js\";\nexport type EffectSurfaceProps = Readonly<{ descriptor?: EffectSurfaceDescriptor; children: ReactNode; className?: string; /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */ layoutStyle?: HjmCompositionStyleProp; }>;\n/** Decorative layers never receive pointer events or own the content's name. */\nexport function EffectSurface({ descriptor = {}, children, className, layoutStyle }: EffectSurfaceProps) {\n const { palette, environment } = useHjmTheme();\n const effect = resolveEffectSurface(descriptor);\n const id = useId().replace(/:/g, \"\");\n const host = useRef<HTMLDivElement>(null);\n const layer = useRef<SVGSVGElement>(null);\n const [visible, setVisible] = useState(false);\n useEffect(() => {\n if (!host.current) return;\n if (typeof IntersectionObserver === \"undefined\") { setVisible(true); return; }\n const observer = new IntersectionObserver(([entry]) => setVisible(entry?.isIntersecting ?? false));\n observer.observe(host.current); return () => observer.disconnect();\n }, []);\n useEffect(() => {\n const node = layer.current;\n if (!node || !effect.active || environment.reducedMotion || !visible || typeof node.animate !== \"function\") return;\n // WAAPI stays outside React renders; visibility changes cancel the loop and\n // expose its static composition rather than scheduling invisible frames.\n let animation: Animation | undefined;\n let failed = false;\n const sync = () => { animation?.cancel(); if (document.hidden || failed) return;\n // A host may expose WAAPI but reject animation creation. Keep the static SVG\n // and content instead of propagating a decorative failure to the application.\n try { animation = node.animate([\n { transform: \"scale(1.08) translate(-1%, 0%)\" },\n { transform: \"scale(1.12) translate(1%, 1%)\" },\n { transform: \"scale(1.08) translate(-1%, 0%)\" },\n ], { duration: effect.period * 1000, iterations: Infinity, easing: \"ease-in-out\" }); } catch { failed = true; } };\n sync(); document.addEventListener(\"visibilitychange\", sync);\n return () => { animation?.cancel(); document.removeEventListener(\"visibilitychange\", sync); };\n }, [effect.active, effect.period, environment.reducedMotion, visible]);\n const colors = effect.colors.map(color => resolveColorReference(color, palette));\n return <div ref={host} className={className} data-hjm-effect-surface=\"\" style={{ ...layoutStyle, position: \"relative\", isolation: \"isolate\", overflow: \"hidden\", background: palette.theme.bg }}>\n <svg ref={layer} aria-hidden=\"true\" focusable=\"false\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\" style={{ position: \"absolute\", inset: 0, width: \"100%\", height: \"100%\", pointerEvents: \"none\", opacity: effect.intensity }}>\n <defs>{colors.map((color, index) => <radialGradient id={`${id}-${index}`} key={index}><stop offset=\"0%\" stopColor={color} /><stop offset=\"100%\" stopColor={color} stopOpacity={0} /></radialGradient>)}\n <pattern id={`${id}-grain`} width={4} height={4} patternUnits=\"userSpaceOnUse\">{effect.points.map((point, index) => <circle key={index} cx={point.x / 25} cy={point.y / 25} r={point.radius / 10} fill={palette.theme.text} opacity={0.28} />)}</pattern>\n </defs>\n {effect.layers.includes(\"mesh\") && effect.anchors.map((anchor, index) => <ellipse key={index} cx={anchor.x} cy={anchor.y} rx={65} ry={70} fill={`url(#${id}-${index})`} />)}\n {effect.layers.includes(\"glow\") && <ellipse cx={50} cy={10} rx={70} ry={95} fill={`url(#${id}-0)`} />}\n {effect.layers.includes(\"grain\") && <rect width={100} height={100} fill={`url(#${id}-grain)`} />}\n </svg>\n <div style={{ position: \"relative\" }}>{children}</div>\n </div>;\n}\n"]}
1
+ {"version":3,"file":"effect-surface.js","sourceRoot":"","sources":["../src/effect-surface.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useRef, useState, type ReactNode } from \"react\";\nimport { resolveEffectSurface, type EffectSurfaceDescriptor } from \"@hjmds/design-contracts/effect-surface\";\nimport { resolveColorReference } from \"@hjmds/design-contracts/color-references\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\nimport { useHjmTheme } from \"./provider.js\";\nexport type EffectSurfaceProps = Readonly<{ descriptor?: EffectSurfaceDescriptor; children: ReactNode; className?: string; /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */ layoutStyle?: HjmCompositionStyleProp; }>;\n/** Decorative layers never receive pointer events or own the content's name. */\nexport function EffectSurface({ descriptor = {}, children, className, layoutStyle }: EffectSurfaceProps) {\n const { palette, environment } = useHjmTheme();\n const effect = resolveEffectSurface(descriptor);\n const id = useId().replace(/:/g, \"\");\n const host = useRef<HTMLDivElement>(null);\n const layer = useRef<SVGSVGElement>(null);\n const [visible, setVisible] = useState(false);\n useEffect(() => {\n if (!host.current) return;\n if (typeof IntersectionObserver === \"undefined\") { setVisible(true); return; }\n const observer = new IntersectionObserver(([entry]) => setVisible(entry?.isIntersecting ?? false));\n observer.observe(host.current); return () => observer.disconnect();\n }, []);\n useEffect(() => {\n const node = layer.current;\n if (!node || !effect.active || environment.reducedMotion || !visible || typeof node.animate !== \"function\") return;\n // WAAPI stays outside React renders; visibility changes cancel the loop and\n // expose its static composition rather than scheduling invisible frames.\n let animation: Animation | undefined;\n let failed = false;\n const sync = () => { animation?.cancel(); if (document.hidden || failed) return;\n // A host may expose WAAPI but reject animation creation. Keep the static SVG\n // and content instead of propagating a decorative failure to the application.\n try { animation = node.animate([\n { transform: \"scale(1.08) translate(-1%, 0%)\" },\n { transform: \"scale(1.12) translate(1%, 1%)\" },\n { transform: \"scale(1.08) translate(-1%, 0%)\" },\n ], { duration: effect.period * 1000, iterations: Infinity, easing: \"ease-in-out\" }); } catch { failed = true; } };\n sync(); document.addEventListener(\"visibilitychange\", sync);\n return () => { animation?.cancel(); document.removeEventListener(\"visibilitychange\", sync); };\n }, [effect.active, effect.period, environment.reducedMotion, visible]);\n const colors = effect.colors.map(color => resolveColorReference(color, palette));\n return <div ref={host} className={className} data-hjm-effect-surface=\"\" style={{ ...layoutStyle, position: \"relative\", isolation: \"isolate\", overflow: \"hidden\", background: palette.theme.bg }}>\n <svg ref={layer} aria-hidden=\"true\" focusable=\"false\" style={{ position: \"absolute\", inset: 0, width: \"100%\", height: \"100%\", pointerEvents: \"none\", opacity: effect.intensity }}>\n {/* Keep noise in CSS-pixel units; the existing geometric layers retain their viewBox. */}\n <defs>{effect.noise && <><pattern id={`${id}-noise-tile`} width={effect.noise.size} height={effect.noise.size} x={effect.noise.offset} patternUnits=\"userSpaceOnUse\"><image href={effect.noise.uri} width={effect.noise.size} height={effect.noise.size} /></pattern><mask id={`${id}-noise-mask`} x={0} y={0} width=\"100%\" height=\"100%\" maskUnits=\"userSpaceOnUse\"><rect width=\"100%\" height=\"100%\" fill={`url(#${id}-noise-tile)`} /></mask></>}</defs>\n {effect.noise && <rect width=\"100%\" height=\"100%\" fill={palette.theme.text} mask={`url(#${id}-noise-mask)`} />}\n <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 100 100\" preserveAspectRatio=\"none\">\n <defs>{colors.map((color, index) => <radialGradient id={`${id}-${index}`} key={index}><stop offset=\"0%\" stopColor={color} /><stop offset=\"100%\" stopColor={color} stopOpacity={0} /></radialGradient>)}\n <pattern id={`${id}-grain`} width={4} height={4} patternUnits=\"userSpaceOnUse\">{effect.points.map((point, index) => <circle key={index} cx={point.x / 25} cy={point.y / 25} r={point.radius / 10} fill={palette.theme.text} opacity={0.28} />)}</pattern>\n </defs>\n {effect.layers.includes(\"mesh\") && effect.anchors.map((anchor, index) => <ellipse key={index} cx={anchor.x} cy={anchor.y} rx={65} ry={70} fill={`url(#${id}-${index})`} />)}\n {effect.layers.includes(\"glow\") && <ellipse cx={50} cy={10} rx={70} ry={95} fill={`url(#${id}-0)`} />}\n {effect.layers.includes(\"grain\") && <rect width={100} height={100} fill={`url(#${id}-grain)`} />}\n </svg>\n </svg>\n <div style={{ position: \"relative\" }}>{children}</div>\n </div>;\n}\n"]}
@@ -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.13.1";
37
+ readonly packageVersion: "1.15.0";
38
38
  readonly surface: "web";
39
39
  readonly components: readonly [Readonly<{
40
40
  /** Canonical component id from the design-contracts catalog. */
package/dist/evidence.js CHANGED
@@ -155,7 +155,7 @@ function toastClaim() {
155
155
  export const reactRendererEvidence = {
156
156
  schemaVersion: reactRendererEvidenceSchemaVersion,
157
157
  packageName: "@hjmds/react",
158
- packageVersion: "1.13.1",
158
+ packageVersion: "1.15.0",
159
159
  surface: "web",
160
160
  components: [
161
161
  defaultClaim("top-bar", ["TopBar"], "./top-bar"),