@hjmds/react 1.1.0 → 1.2.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 (199) hide show
  1. package/dist/actions.d.ts.map +1 -1
  2. package/dist/actions.js +12 -6
  3. package/dist/actions.js.map +1 -1
  4. package/dist/advanced-display.d.ts +21 -0
  5. package/dist/advanced-display.d.ts.map +1 -1
  6. package/dist/advanced-display.js +11 -0
  7. package/dist/advanced-display.js.map +1 -1
  8. package/dist/advanced-navigation.d.ts +2 -23
  9. package/dist/advanced-navigation.d.ts.map +1 -1
  10. package/dist/advanced-navigation.js +3 -24
  11. package/dist/advanced-navigation.js.map +1 -1
  12. package/dist/agreement.d.ts +20 -0
  13. package/dist/agreement.d.ts.map +1 -0
  14. package/dist/agreement.js +29 -0
  15. package/dist/agreement.js.map +1 -0
  16. package/dist/anchor.d.ts +24 -0
  17. package/dist/anchor.d.ts.map +1 -0
  18. package/dist/anchor.js +110 -0
  19. package/dist/anchor.js.map +1 -0
  20. package/dist/asset.d.ts +40 -0
  21. package/dist/asset.d.ts.map +1 -0
  22. package/dist/asset.js +26 -0
  23. package/dist/asset.js.map +1 -0
  24. package/dist/bottom-cta.d.ts +32 -0
  25. package/dist/bottom-cta.d.ts.map +1 -0
  26. package/dist/bottom-cta.js +30 -0
  27. package/dist/bottom-cta.js.map +1 -0
  28. package/dist/bottom-info.d.ts +16 -0
  29. package/dist/bottom-info.d.ts.map +1 -0
  30. package/dist/bottom-info.js +12 -0
  31. package/dist/bottom-info.js.map +1 -0
  32. package/dist/breadcrumb.d.ts +9 -0
  33. package/dist/breadcrumb.d.ts.map +1 -0
  34. package/dist/breadcrumb.js +12 -0
  35. package/dist/breadcrumb.js.map +1 -0
  36. package/dist/calendar.d.ts +30 -0
  37. package/dist/calendar.d.ts.map +1 -0
  38. package/dist/calendar.js +69 -0
  39. package/dist/calendar.js.map +1 -0
  40. package/dist/carousel.d.ts +20 -0
  41. package/dist/carousel.d.ts.map +1 -0
  42. package/dist/carousel.js +70 -0
  43. package/dist/carousel.js.map +1 -0
  44. package/dist/clipboard.d.ts +33 -0
  45. package/dist/clipboard.d.ts.map +1 -0
  46. package/dist/clipboard.js +40 -0
  47. package/dist/clipboard.js.map +1 -0
  48. package/dist/collapsible.d.ts +11 -0
  49. package/dist/collapsible.d.ts.map +1 -0
  50. package/dist/collapsible.js +18 -0
  51. package/dist/collapsible.js.map +1 -0
  52. package/dist/command-palette.d.ts +25 -0
  53. package/dist/command-palette.d.ts.map +1 -0
  54. package/dist/command-palette.js +82 -0
  55. package/dist/command-palette.js.map +1 -0
  56. package/dist/context-menu.d.ts +12 -0
  57. package/dist/context-menu.d.ts.map +1 -0
  58. package/dist/context-menu.js +111 -0
  59. package/dist/context-menu.js.map +1 -0
  60. package/dist/data-table.d.ts +30 -0
  61. package/dist/data-table.d.ts.map +1 -0
  62. package/dist/data-table.js +57 -0
  63. package/dist/data-table.js.map +1 -0
  64. package/dist/date-picker.d.ts +3 -1
  65. package/dist/date-picker.d.ts.map +1 -1
  66. package/dist/date-picker.js +16 -31
  67. package/dist/date-picker.js.map +1 -1
  68. package/dist/date-range.d.ts +24 -0
  69. package/dist/date-range.d.ts.map +1 -0
  70. package/dist/date-range.js +29 -0
  71. package/dist/date-range.js.map +1 -0
  72. package/dist/display.d.ts +16 -0
  73. package/dist/display.d.ts.map +1 -1
  74. package/dist/display.js +13 -1
  75. package/dist/display.js.map +1 -1
  76. package/dist/evidence.d.ts +481 -1
  77. package/dist/evidence.d.ts.map +1 -1
  78. package/dist/evidence.js +47 -2
  79. package/dist/evidence.js.map +1 -1
  80. package/dist/feedback.d.ts +18 -2
  81. package/dist/feedback.d.ts.map +1 -1
  82. package/dist/feedback.js +10 -2
  83. package/dist/feedback.js.map +1 -1
  84. package/dist/floating-action-button.d.ts +32 -0
  85. package/dist/floating-action-button.d.ts.map +1 -0
  86. package/dist/floating-action-button.js +58 -0
  87. package/dist/floating-action-button.js.map +1 -0
  88. package/dist/forms.d.ts +3 -3
  89. package/dist/forms.d.ts.map +1 -1
  90. package/dist/forms.js +18 -7
  91. package/dist/forms.js.map +1 -1
  92. package/dist/heading.d.ts +12 -0
  93. package/dist/heading.d.ts.map +1 -0
  94. package/dist/heading.js +25 -0
  95. package/dist/heading.js.map +1 -0
  96. package/dist/index.d.ts +35 -2
  97. package/dist/index.d.ts.map +1 -1
  98. package/dist/index.js +35 -2
  99. package/dist/index.js.map +1 -1
  100. package/dist/layout.d.ts +1 -1
  101. package/dist/mentions.d.ts +28 -0
  102. package/dist/mentions.d.ts.map +1 -0
  103. package/dist/mentions.js +84 -0
  104. package/dist/mentions.js.map +1 -0
  105. package/dist/menubar.d.ts +11 -0
  106. package/dist/menubar.d.ts.map +1 -0
  107. package/dist/menubar.js +137 -0
  108. package/dist/menubar.js.map +1 -0
  109. package/dist/modal.d.ts +67 -0
  110. package/dist/modal.d.ts.map +1 -0
  111. package/dist/modal.js +307 -0
  112. package/dist/modal.js.map +1 -0
  113. package/dist/overlay-stack.d.ts +28 -0
  114. package/dist/overlay-stack.d.ts.map +1 -0
  115. package/dist/overlay-stack.js +47 -0
  116. package/dist/overlay-stack.js.map +1 -0
  117. package/dist/overlays.d.ts +26 -43
  118. package/dist/overlays.d.ts.map +1 -1
  119. package/dist/overlays.js +62 -302
  120. package/dist/overlays.js.map +1 -1
  121. package/dist/pagination.d.ts +17 -0
  122. package/dist/pagination.d.ts.map +1 -0
  123. package/dist/pagination.js +21 -0
  124. package/dist/pagination.js.map +1 -0
  125. package/dist/popover.d.ts +25 -0
  126. package/dist/popover.d.ts.map +1 -0
  127. package/dist/popover.js +226 -0
  128. package/dist/popover.js.map +1 -0
  129. package/dist/portal.d.ts +5 -1
  130. package/dist/portal.d.ts.map +1 -1
  131. package/dist/portal.js +24 -1
  132. package/dist/portal.js.map +1 -1
  133. package/dist/provider-button.d.ts +21 -0
  134. package/dist/provider-button.d.ts.map +1 -0
  135. package/dist/provider-button.js +26 -0
  136. package/dist/provider-button.js.map +1 -0
  137. package/dist/provider.d.ts +13 -1
  138. package/dist/provider.d.ts.map +1 -1
  139. package/dist/provider.js +12 -2
  140. package/dist/provider.js.map +1 -1
  141. package/dist/selection.d.ts +5 -1
  142. package/dist/selection.d.ts.map +1 -1
  143. package/dist/selection.js +2 -2
  144. package/dist/selection.js.map +1 -1
  145. package/dist/side-panel.d.ts +34 -0
  146. package/dist/side-panel.d.ts.map +1 -0
  147. package/dist/side-panel.js +95 -0
  148. package/dist/side-panel.js.map +1 -0
  149. package/dist/sidebar.d.ts +22 -0
  150. package/dist/sidebar.d.ts.map +1 -0
  151. package/dist/sidebar.js +29 -0
  152. package/dist/sidebar.js.map +1 -0
  153. package/dist/skip-nav.d.ts +12 -0
  154. package/dist/skip-nav.d.ts.map +1 -0
  155. package/dist/skip-nav.js +29 -0
  156. package/dist/skip-nav.js.map +1 -0
  157. package/dist/splitter.d.ts +45 -0
  158. package/dist/splitter.d.ts.map +1 -0
  159. package/dist/splitter.js +116 -0
  160. package/dist/splitter.js.map +1 -0
  161. package/dist/styles.css +1068 -26
  162. package/dist/supplemental-display.d.ts +12 -0
  163. package/dist/supplemental-display.d.ts.map +1 -1
  164. package/dist/supplemental-display.js +6 -2
  165. package/dist/supplemental-display.js.map +1 -1
  166. package/dist/tags-input.d.ts +60 -0
  167. package/dist/tags-input.d.ts.map +1 -0
  168. package/dist/tags-input.js +90 -0
  169. package/dist/tags-input.js.map +1 -0
  170. package/dist/text-formats.d.ts +16 -0
  171. package/dist/text-formats.d.ts.map +1 -0
  172. package/dist/text-formats.js +24 -0
  173. package/dist/text-formats.js.map +1 -0
  174. package/dist/toast.d.ts +1 -1
  175. package/dist/toggle-group.d.ts +13 -0
  176. package/dist/toggle-group.d.ts.map +1 -0
  177. package/dist/toggle-group.js +21 -0
  178. package/dist/toggle-group.js.map +1 -0
  179. package/dist/top-bar.d.ts +27 -0
  180. package/dist/top-bar.d.ts.map +1 -0
  181. package/dist/top-bar.js +30 -0
  182. package/dist/top-bar.js.map +1 -0
  183. package/dist/top.d.ts +15 -0
  184. package/dist/top.d.ts.map +1 -0
  185. package/dist/top.js +18 -0
  186. package/dist/top.js.map +1 -0
  187. package/dist/tour.d.ts +21 -0
  188. package/dist/tour.d.ts.map +1 -0
  189. package/dist/tour.js +122 -0
  190. package/dist/tour.js.map +1 -0
  191. package/dist/transfer-list.d.ts +24 -0
  192. package/dist/transfer-list.d.ts.map +1 -0
  193. package/dist/transfer-list.js +109 -0
  194. package/dist/transfer-list.js.map +1 -0
  195. package/dist/tree.d.ts +29 -0
  196. package/dist/tree.d.ts.map +1 -0
  197. package/dist/tree.js +120 -0
  198. package/dist/tree.js.map +1 -0
  199. package/package.json +173 -3
@@ -0,0 +1,24 @@
1
+ import { type HTMLAttributes, type MouseEvent } from "react";
2
+ import { type AnchorItem } from "@hjmds/design-contracts/components/anchor";
3
+ export type AnchorProps = Omit<HTMLAttributes<HTMLElement>, "children"> & Readonly<{
4
+ label: string;
5
+ items: readonly AnchorItem[];
6
+ /** undefined observes the document; null waits for a custom scroll ref. */
7
+ container?: HTMLElement | null;
8
+ offset?: number;
9
+ orientation?: "vertical" | "horizontal";
10
+ historyMode?: "push" | "replace" | "none";
11
+ onNavigate?: (id: string, event: MouseEvent<HTMLAnchorElement>) => void;
12
+ }>;
13
+ /** Same-document table of contents; sticky positioning belongs to its host layout. */
14
+ export declare const Anchor: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLElement>, "children"> & Readonly<{
15
+ label: string;
16
+ items: readonly AnchorItem[];
17
+ /** undefined observes the document; null waits for a custom scroll ref. */
18
+ container?: HTMLElement | null;
19
+ offset?: number;
20
+ orientation?: "vertical" | "horizontal";
21
+ historyMode?: "push" | "replace" | "none";
22
+ onNavigate?: (id: string, event: MouseEvent<HTMLAnchorElement>) => void;
23
+ }> & import("react").RefAttributes<HTMLElement>>;
24
+ //# sourceMappingURL=anchor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor.d.ts","sourceRoot":"","sources":["../src/anchor.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,OAAO,CAAC;AACtG,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAGpH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC;IACjF,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;IAC7B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAC1C,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;CACzE,CAAC,CAAC;AAEH,sFAAsF;AACtF,eAAO,MAAM,MAAM;WAXV,MAAM;WACN,SAAS,UAAU,EAAE;IAC5B,2EAA2E;gBAC/D,WAAW,GAAG,IAAI;aACrB,MAAM;kBACD,UAAU,GAAG,YAAY;kBACzB,MAAM,GAAG,SAAS,GAAG,MAAM;iBAC5B,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,iBAAiB,CAAC,KAAK,IAAI;gDA2EvE,CAAC"}
package/dist/anchor.js ADDED
@@ -0,0 +1,110 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useEffect, useRef, useState } from "react";
3
+ import { getAnchorCurrentId, resolveAnchorItems } from "@hjmds/design-contracts/components/anchor";
4
+ import { classNames } from "./internal.js";
5
+ import { useOptionalHjmTheme } from "./provider.js";
6
+ /** Same-document table of contents; sticky positioning belongs to its host layout. */
7
+ export const Anchor = forwardRef(function Anchor({ label, items, container, offset = 0, orientation = "vertical", historyMode = "push", onNavigate, className, ...props }, ref) {
8
+ const resolved = resolveAnchorItems(items);
9
+ if (!label.trim())
10
+ throw new TypeError("Anchor label must not be empty");
11
+ getAnchorCurrentId([], offset);
12
+ const theme = useOptionalHjmTheme();
13
+ const [current, setCurrent] = useState();
14
+ const restoredTargets = useRef(new Map());
15
+ const idsKey = JSON.stringify(resolved.map(({ id }) => id));
16
+ const findTarget = (id) => {
17
+ const target = document.getElementById(id);
18
+ return target && (container === undefined || container?.contains(target)) ? target : null;
19
+ };
20
+ function scrollToTarget(target, behavior) {
21
+ const top = container === undefined ? window.scrollY + target.getBoundingClientRect().top - offset
22
+ : container === null ? 0 : container.scrollTop + target.getBoundingClientRect().top - container.getBoundingClientRect().top - container.clientTop - offset;
23
+ if (container === undefined)
24
+ window.scrollTo({ top, behavior });
25
+ else
26
+ container?.scrollTo({ top, behavior });
27
+ }
28
+ function focusTarget(target) {
29
+ if (!target.hasAttribute("tabindex")) {
30
+ // Headings/sections are often not focusable. Restore our temporary attribute
31
+ // on blur/unmount so navigation does not permanently change product markup.
32
+ restoredTargets.current.get(target)?.();
33
+ target.setAttribute("tabindex", "-1");
34
+ const restore = () => { if (target.getAttribute("tabindex") === "-1")
35
+ target.removeAttribute("tabindex"); target.removeEventListener("blur", restore); restoredTargets.current.delete(target); };
36
+ restoredTargets.current.set(target, restore);
37
+ target.addEventListener("blur", restore, { once: true });
38
+ }
39
+ target.focus({ preventScroll: true });
40
+ }
41
+ useEffect(() => () => { for (const restore of [...restoredTargets.current.values()])
42
+ restore(); }, []);
43
+ useEffect(() => {
44
+ if (container === null)
45
+ return;
46
+ const source = container ?? window;
47
+ const content = container ?? document.body;
48
+ let frame = 0;
49
+ const update = () => {
50
+ frame = 0;
51
+ const top = container ? container.getBoundingClientRect().top + container.clientTop : 0;
52
+ const scroll = container ?? document.scrollingElement;
53
+ // One CSS pixel covers scroll-height rounding without prematurely selecting a short final section.
54
+ const atEnd = !!scroll && scroll.scrollHeight > scroll.clientHeight && scroll.scrollTop + scroll.clientHeight >= scroll.scrollHeight - 1;
55
+ const positions = resolved.flatMap(({ id }) => { const node = findTarget(id); return node ? [{ id, top: node.getBoundingClientRect().top - top }] : []; });
56
+ setCurrent(getAnchorCurrentId(positions, offset, atEnd));
57
+ };
58
+ const schedule = () => { if (!frame)
59
+ frame = requestAnimationFrame(update); };
60
+ const resize = new ResizeObserver(schedule);
61
+ const observe = () => { resize.disconnect(); resize.observe(content); for (const item of resolved) {
62
+ const node = findTarget(item.id);
63
+ if (node) {
64
+ resize.observe(node);
65
+ if (node.parentElement)
66
+ resize.observe(node.parentElement);
67
+ }
68
+ } };
69
+ const mutation = new MutationObserver(() => { observe(); schedule(); });
70
+ mutation.observe(content, { childList: true, subtree: true, characterData: true });
71
+ const restoreHash = () => {
72
+ let id;
73
+ try {
74
+ id = decodeURIComponent(window.location.hash.slice(1));
75
+ }
76
+ catch {
77
+ return;
78
+ }
79
+ if (!resolved.some((item) => item.id === id))
80
+ return;
81
+ const target = findTarget(id);
82
+ if (target)
83
+ scrollToTarget(target, "instant");
84
+ schedule();
85
+ };
86
+ observe();
87
+ update();
88
+ restoreHash();
89
+ source.addEventListener("scroll", schedule, { passive: true });
90
+ window.addEventListener("resize", schedule);
91
+ window.addEventListener("hashchange", restoreHash);
92
+ window.addEventListener("popstate", restoreHash);
93
+ return () => { cancelAnimationFrame(frame); resize.disconnect(); mutation.disconnect(); source.removeEventListener("scroll", schedule); window.removeEventListener("resize", schedule); window.removeEventListener("hashchange", restoreHash); window.removeEventListener("popstate", restoreHash); };
94
+ }, [idsKey, container, offset]);
95
+ return _jsx("nav", { ...props, ref: ref, "aria-label": label, className: classNames("hjm-anchor", className), "data-orientation": orientation, children: _jsx("ul", { className: "hjm-anchor__list", children: resolved.map((item) => _jsx("li", { children: _jsx("a", { href: item.href, className: "hjm-anchor__link", "aria-current": current === item.id ? "location" : undefined, onClick: (event) => {
96
+ onNavigate?.(item.id, event);
97
+ if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey)
98
+ return;
99
+ const target = findTarget(item.id);
100
+ if (!target)
101
+ return;
102
+ event.preventDefault();
103
+ const reduced = theme?.environment.reducedMotion ?? window.matchMedia("(prefers-reduced-motion: reduce)").matches;
104
+ scrollToTarget(target, reduced ? "instant" : "smooth");
105
+ focusTarget(target);
106
+ if (historyMode !== "none" && window.location.hash !== item.href)
107
+ window.history[historyMode === "replace" ? "replaceState" : "pushState"](window.history.state, "", item.href);
108
+ }, children: item.label }) }, item.id)) }) });
109
+ });
110
+ //# sourceMappingURL=anchor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../src/anchor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { forwardRef, useEffect, useRef, useState, type HTMLAttributes, type MouseEvent } from \"react\";\nimport { getAnchorCurrentId, resolveAnchorItems, type AnchorItem } from \"@hjmds/design-contracts/components/anchor\";\nimport { classNames } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\nexport type AnchorProps = Omit<HTMLAttributes<HTMLElement>, \"children\"> & Readonly<{\n label: string;\n items: readonly AnchorItem[];\n /** undefined observes the document; null waits for a custom scroll ref. */\n container?: HTMLElement | null;\n offset?: number;\n orientation?: \"vertical\" | \"horizontal\";\n historyMode?: \"push\" | \"replace\" | \"none\";\n onNavigate?: (id: string, event: MouseEvent<HTMLAnchorElement>) => void;\n}>;\n\n/** Same-document table of contents; sticky positioning belongs to its host layout. */\nexport const Anchor = forwardRef<HTMLElement, AnchorProps>(function Anchor({ label, items, container, offset = 0,\n orientation = \"vertical\", historyMode = \"push\", onNavigate, className, ...props }, ref) {\n const resolved = resolveAnchorItems(items);\n if (!label.trim()) throw new TypeError(\"Anchor label must not be empty\");\n getAnchorCurrentId([], offset);\n const theme = useOptionalHjmTheme();\n const [current, setCurrent] = useState<string>();\n const restoredTargets = useRef(new Map<HTMLElement, () => void>());\n const idsKey = JSON.stringify(resolved.map(({ id }) => id));\n const findTarget = (id: string) => {\n const target = document.getElementById(id);\n return target && (container === undefined || container?.contains(target)) ? target : null;\n };\n function scrollToTarget(target: HTMLElement, behavior: ScrollBehavior) {\n const top = container === undefined ? window.scrollY + target.getBoundingClientRect().top - offset\n : container === null ? 0 : container.scrollTop + target.getBoundingClientRect().top - container.getBoundingClientRect().top - container.clientTop - offset;\n if (container === undefined) window.scrollTo({ top, behavior });\n else container?.scrollTo({ top, behavior });\n }\n function focusTarget(target: HTMLElement) {\n if (!target.hasAttribute(\"tabindex\")) {\n // Headings/sections are often not focusable. Restore our temporary attribute\n // on blur/unmount so navigation does not permanently change product markup.\n restoredTargets.current.get(target)?.();\n target.setAttribute(\"tabindex\", \"-1\");\n const restore = () => { if (target.getAttribute(\"tabindex\") === \"-1\") target.removeAttribute(\"tabindex\"); target.removeEventListener(\"blur\", restore); restoredTargets.current.delete(target); };\n restoredTargets.current.set(target, restore); target.addEventListener(\"blur\", restore, { once: true });\n }\n target.focus({ preventScroll: true });\n }\n useEffect(() => () => { for (const restore of [...restoredTargets.current.values()]) restore(); }, []);\n useEffect(() => {\n if (container === null) return;\n const source = container ?? window;\n const content = container ?? document.body;\n let frame = 0;\n const update = () => {\n frame = 0;\n const top = container ? container.getBoundingClientRect().top + container.clientTop : 0;\n const scroll = container ?? document.scrollingElement;\n // One CSS pixel covers scroll-height rounding without prematurely selecting a short final section.\n const atEnd = !!scroll && scroll.scrollHeight > scroll.clientHeight && scroll.scrollTop + scroll.clientHeight >= scroll.scrollHeight - 1;\n const positions = resolved.flatMap(({ id }) => { const node = findTarget(id); return node ? [{ id, top: node.getBoundingClientRect().top - top }] : []; });\n setCurrent(getAnchorCurrentId(positions, offset, atEnd));\n };\n const schedule = () => { if (!frame) frame = requestAnimationFrame(update); };\n const resize = new ResizeObserver(schedule);\n const observe = () => { resize.disconnect(); resize.observe(content); for (const item of resolved) { const node = findTarget(item.id); if (node) { resize.observe(node); if (node.parentElement) resize.observe(node.parentElement); } } };\n const mutation = new MutationObserver(() => { observe(); schedule(); });\n mutation.observe(content, { childList: true, subtree: true, characterData: true });\n const restoreHash = () => {\n let id: string; try { id = decodeURIComponent(window.location.hash.slice(1)); } catch { return; }\n if (!resolved.some((item) => item.id === id)) return;\n const target = findTarget(id); if (target) scrollToTarget(target, \"instant\"); schedule();\n };\n observe(); update(); restoreHash(); source.addEventListener(\"scroll\", schedule, { passive: true });\n window.addEventListener(\"resize\", schedule); window.addEventListener(\"hashchange\", restoreHash); window.addEventListener(\"popstate\", restoreHash);\n return () => { cancelAnimationFrame(frame); resize.disconnect(); mutation.disconnect(); source.removeEventListener(\"scroll\", schedule); window.removeEventListener(\"resize\", schedule); window.removeEventListener(\"hashchange\", restoreHash); window.removeEventListener(\"popstate\", restoreHash); };\n }, [idsKey, container, offset]);\n return <nav {...props} ref={ref} aria-label={label} className={classNames(\"hjm-anchor\", className)} data-orientation={orientation}>\n <ul className=\"hjm-anchor__list\">{resolved.map((item) => <li key={item.id}><a href={item.href} className=\"hjm-anchor__link\" aria-current={current === item.id ? \"location\" : undefined}\n onClick={(event) => {\n onNavigate?.(item.id, event);\n if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n const target = findTarget(item.id); if (!target) return;\n event.preventDefault();\n const reduced = theme?.environment.reducedMotion ?? window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n scrollToTarget(target, reduced ? \"instant\" : \"smooth\"); focusTarget(target);\n if (historyMode !== \"none\" && window.location.hash !== item.href) window.history[historyMode === \"replace\" ? \"replaceState\" : \"pushState\"](window.history.state, \"\", item.href);\n }}>{item.label}</a></li>)}</ul>\n </nav>;\n});\n"]}
@@ -0,0 +1,40 @@
1
+ import { type AssetDescriptor } from "@hjmds/design-contracts/components/asset";
2
+ import { type ReactNode } from "react";
3
+ export type AssetProps = Readonly<{
4
+ descriptor: AssetDescriptor;
5
+ /**
6
+ * The media itself. A player is the product's dependency, never this
7
+ * package's — the frame is all the contract owns. `animate` is the
8
+ * already-resolved answer to "may this move right now".
9
+ */
10
+ children: ReactNode | ((state: Readonly<{
11
+ animate: boolean;
12
+ }>) => ReactNode);
13
+ /** A small mark on the frame's outer corner (play, status dot). */
14
+ accessory?: ReactNode;
15
+ className?: string;
16
+ }>;
17
+ export declare const Asset: import("react").ForwardRefExoticComponent<Readonly<{
18
+ descriptor: AssetDescriptor;
19
+ /**
20
+ * The media itself. A player is the product's dependency, never this
21
+ * package's — the frame is all the contract owns. `animate` is the
22
+ * already-resolved answer to "may this move right now".
23
+ */
24
+ children: ReactNode | ((state: Readonly<{
25
+ animate: boolean;
26
+ }>) => ReactNode);
27
+ /** A small mark on the frame's outer corner (play, status dot). */
28
+ accessory?: ReactNode;
29
+ className?: string;
30
+ }> & import("react").RefAttributes<HTMLDivElement>>;
31
+ export type AssetGroupProps = Readonly<{
32
+ /** Accessible name for the group; the overlap alone does not say what it is. */
33
+ label: string;
34
+ size?: AssetDescriptor["size"];
35
+ children: ReactNode;
36
+ className?: string;
37
+ }>;
38
+ /** Overlaps assets with the same ratio Avatar uses — they share a row on purpose. */
39
+ export declare function AssetGroup({ label, size, children, className }: AssetGroupProps): import("react").JSX.Element;
40
+ //# sourceMappingURL=asset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset.d.ts","sourceRoot":"","sources":["../src/asset.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAkC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvE,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,UAAU,EAAE,eAAe,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,SAAS,CAAC,CAAC;IAC7E,mEAAmE;IACnE,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,KAAK;gBAZJ,eAAe;IAC3B;;;;OAIG;cACO,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,SAAS,CAAC;IAC5E,mEAAmE;gBACvD,SAAS;gBACT,MAAM;mDAoClB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,IAAgC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,eAAe,+BAY3G"}
package/dist/asset.js ADDED
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { assetRecipe, shouldAnimateAsset, validateAssetDescriptor, } from "@hjmds/design-contracts/components/asset";
3
+ import { forwardRef } from "react";
4
+ import { classNames } from "./internal.js";
5
+ import { useOptionalHjmTheme } from "./provider.js";
6
+ export const Asset = forwardRef(function Asset({ descriptor, children, accessory, className }, forwardedRef) {
7
+ validateAssetDescriptor(descriptor);
8
+ const theme = useOptionalHjmTheme();
9
+ const size = assetRecipe.sizes[descriptor.size ?? assetRecipe.defaults.size];
10
+ const shape = assetRecipe.shapes[descriptor.shape ?? assetRecipe.defaults.shape];
11
+ const decorative = descriptor.decorative ?? false;
12
+ // The frame reports whether motion is allowed so the product's player can
13
+ // read one answer instead of re-deriving the preference per surface.
14
+ const animate = shouldAnimateAsset(descriptor.kind, theme?.environment.reducedMotion ?? false);
15
+ return (_jsxs("div", { ref: forwardedRef, className: classNames("hjm-asset", className), "data-kind": descriptor.kind, "data-animate": animate || undefined, role: decorative ? "presentation" : "img", "aria-hidden": decorative || undefined, "aria-label": decorative ? undefined : descriptor.accessibilityLabel, style: {
16
+ "--hjm-asset-size": `${size}px`,
17
+ "--hjm-asset-radius": `${shape}px`,
18
+ "--hjm-asset-accessory-offset": `${assetRecipe.accessory.offset}px`,
19
+ }, children: [_jsx("div", { className: "hjm-asset__frame", children: _jsx("div", { className: "hjm-asset__media", children: typeof children === "function" ? children({ animate }) : children }) }), accessory ? _jsx("span", { className: "hjm-asset__accessory", children: accessory }) : null] }));
20
+ });
21
+ /** Overlaps assets with the same ratio Avatar uses — they share a row on purpose. */
22
+ export function AssetGroup({ label, size = assetRecipe.defaults.size, children, className }) {
23
+ const pixels = assetRecipe.sizes[size];
24
+ return (_jsx("div", { className: classNames("hjm-asset-group", className), role: "group", "aria-label": label, style: { "--hjm-asset-overlap": `${-Math.round(pixels * assetRecipe.overlapRatio)}px` }, children: children }));
25
+ }
26
+ //# sourceMappingURL=asset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset.js","sourceRoot":"","sources":["../src/asset.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,GAExB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,UAAU,EAAsC,MAAM,OAAO,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAepD,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAA6B,SAAS,KAAK,CACxE,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,EAC9C,YAAY;IAEZ,uBAAuB,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,IAAI,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7E,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,IAAI,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACjF,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,IAAI,KAAK,CAAC;IAClD,0EAA0E;IAC1E,qEAAqE;IACrE,MAAM,OAAO,GAAG,kBAAkB,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,aAAa,IAAI,KAAK,CAAC,CAAC;IAC/F,OAAO,CACL,eACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,UAAU,CAAC,WAAW,EAAE,SAAS,CAAC,eAClC,UAAU,CAAC,IAAI,kBACZ,OAAO,IAAI,SAAS,EAClC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,iBAC5B,UAAU,IAAI,SAAS,gBACxB,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,kBAAkB,EAClE,KAAK,EAAE;YACL,kBAAkB,EAAE,GAAG,IAAI,IAAI;YAC/B,oBAAoB,EAAE,GAAG,KAAK,IAAI;YAClC,8BAA8B,EAAE,GAAG,WAAW,CAAC,SAAS,CAAC,MAAM,IAAI;SACnD,aAElB,cAAK,SAAS,EAAC,kBAAkB,YAC/B,cAAK,SAAS,EAAC,kBAAkB,YAAE,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAO,GACvG,EACL,SAAS,CAAC,CAAC,CAAC,eAAM,SAAS,EAAC,sBAAsB,YAAE,SAAS,GAAQ,CAAC,CAAC,CAAC,IAAI,IACzE,CACP,CAAC;AACJ,CAAC,CAAC,CAAC;AAUH,qFAAqF;AACrF,MAAM,UAAU,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAmB;IAC1G,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,CACL,cACE,SAAS,EAAE,UAAU,CAAC,iBAAiB,EAAE,SAAS,CAAC,EACnD,IAAI,EAAC,OAAO,gBACA,KAAK,EACjB,KAAK,EAAE,EAAE,qBAAqB,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,IAAI,EAAmB,YAEvG,QAAQ,GACL,CACP,CAAC;AACJ,CAAC","sourcesContent":["import {\n assetRecipe,\n shouldAnimateAsset,\n validateAssetDescriptor,\n type AssetDescriptor,\n} from \"@hjmds/design-contracts/components/asset\";\nimport { forwardRef, type CSSProperties, type ReactNode } from \"react\";\nimport { classNames } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\n\nexport type AssetProps = Readonly<{\n descriptor: AssetDescriptor;\n /**\n * The media itself. A player is the product's dependency, never this\n * package's — the frame is all the contract owns. `animate` is the\n * already-resolved answer to \"may this move right now\".\n */\n children: ReactNode | ((state: Readonly<{ animate: boolean }>) => ReactNode);\n /** A small mark on the frame's outer corner (play, status dot). */\n accessory?: ReactNode;\n className?: string;\n}>;\n\nexport const Asset = forwardRef<HTMLDivElement, AssetProps>(function Asset(\n { descriptor, children, accessory, className },\n forwardedRef,\n) {\n validateAssetDescriptor(descriptor);\n const theme = useOptionalHjmTheme();\n const size = assetRecipe.sizes[descriptor.size ?? assetRecipe.defaults.size];\n const shape = assetRecipe.shapes[descriptor.shape ?? assetRecipe.defaults.shape];\n const decorative = descriptor.decorative ?? false;\n // The frame reports whether motion is allowed so the product's player can\n // read one answer instead of re-deriving the preference per surface.\n const animate = shouldAnimateAsset(descriptor.kind, theme?.environment.reducedMotion ?? false);\n return (\n <div\n ref={forwardedRef}\n className={classNames(\"hjm-asset\", className)}\n data-kind={descriptor.kind}\n data-animate={animate || undefined}\n role={decorative ? \"presentation\" : \"img\"}\n aria-hidden={decorative || undefined}\n aria-label={decorative ? undefined : descriptor.accessibilityLabel}\n style={{\n \"--hjm-asset-size\": `${size}px`,\n \"--hjm-asset-radius\": `${shape}px`,\n \"--hjm-asset-accessory-offset\": `${assetRecipe.accessory.offset}px`,\n } as CSSProperties}\n >\n <div className=\"hjm-asset__frame\">\n <div className=\"hjm-asset__media\">{typeof children === \"function\" ? children({ animate }) : children}</div>\n </div>\n {accessory ? <span className=\"hjm-asset__accessory\">{accessory}</span> : null}\n </div>\n );\n});\n\nexport type AssetGroupProps = Readonly<{\n /** Accessible name for the group; the overlap alone does not say what it is. */\n label: string;\n size?: AssetDescriptor[\"size\"];\n children: ReactNode;\n className?: string;\n}>;\n\n/** Overlaps assets with the same ratio Avatar uses — they share a row on purpose. */\nexport function AssetGroup({ label, size = assetRecipe.defaults.size, children, className }: AssetGroupProps) {\n const pixels = assetRecipe.sizes[size];\n return (\n <div\n className={classNames(\"hjm-asset-group\", className)}\n role=\"group\"\n aria-label={label}\n style={{ \"--hjm-asset-overlap\": `${-Math.round(pixels * assetRecipe.overlapRatio)}px` } as CSSProperties}\n >\n {children}\n </div>\n );\n}\n"]}
@@ -0,0 +1,32 @@
1
+ import { type HTMLAttributes, type MouseEventHandler, type ReactNode } from "react";
2
+ import { type ButtonSize, type ButtonTone } from "./actions.js";
3
+ export type BottomCTAAction = Readonly<{
4
+ label: string;
5
+ onClick: MouseEventHandler<HTMLButtonElement>;
6
+ accessibilityLabel?: string;
7
+ disabled?: boolean;
8
+ loading?: boolean;
9
+ loadingLabel?: string;
10
+ size?: ButtonSize;
11
+ tone?: ButtonTone;
12
+ }>;
13
+ export type BottomCTAProps = Omit<HTMLAttributes<HTMLDivElement>, "children"> & Readonly<{
14
+ primaryAction: BottomCTAAction;
15
+ secondaryAction?: BottomCTAAction | ReactNode;
16
+ description?: string;
17
+ accessibilityLabel?: string;
18
+ safeAreaBottom?: number;
19
+ /** Sticky remains in document flow; fixed overlays would need a measured content spacer. */
20
+ position?: "flow" | "sticky";
21
+ }>;
22
+ /** One primary action with optional supporting copy and a secondary action, matching Native's slots. */
23
+ export declare const BottomCTA: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & Readonly<{
24
+ primaryAction: BottomCTAAction;
25
+ secondaryAction?: BottomCTAAction | ReactNode;
26
+ description?: string;
27
+ accessibilityLabel?: string;
28
+ safeAreaBottom?: number;
29
+ /** Sticky remains in document flow; fixed overlays would need a measured content spacer. */
30
+ position?: "flow" | "sticky";
31
+ }> & import("react").RefAttributes<HTMLDivElement>>;
32
+ //# sourceMappingURL=bottom-cta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottom-cta.d.ts","sourceRoot":"","sources":["../src/bottom-cta.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAkC,KAAK,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACpH,OAAO,EAAU,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAIxE,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,CAAC;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC;IACvF,aAAa,EAAE,eAAe,CAAC;IAC/B,eAAe,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC9B,CAAC,CAAC;AAeH,wGAAwG;AACxG,eAAO,MAAM,SAAS;mBAvBL,eAAe;sBACZ,eAAe,GAAG,SAAS;kBAC/B,MAAM;yBACC,MAAM;qBACV,MAAM;IACvB,4FAA4F;eACjF,MAAM,GAAG,QAAQ;mDAwC5B,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { bottomCtaRecipe } from "@hjmds/design-contracts/recipes";
3
+ import { isLargeTextScale } from "@hjmds/design-contracts/components/design-system-provider";
4
+ import { forwardRef } from "react";
5
+ import { Button } from "./actions.js";
6
+ import { classNames } from "./internal.js";
7
+ import { useOptionalHjmTheme } from "./provider.js";
8
+ function Action({ action, tone }) {
9
+ if (!action.label.trim())
10
+ throw new TypeError("BottomCTA action label must not be empty");
11
+ return _jsx(Button, { onClick: action.onClick, tone: action.tone ?? tone, size: action.size ?? "medium", disabled: action.disabled, loading: action.loading ?? false, "aria-label": action.loading ? action.loadingLabel ?? action.accessibilityLabel ?? action.label : action.accessibilityLabel, children: action.label });
12
+ }
13
+ function isAction(value) {
14
+ return typeof value === "object" && value !== null && "label" in value && "onClick" in value;
15
+ }
16
+ /** One primary action with optional supporting copy and a secondary action, matching Native's slots. */
17
+ export const BottomCTA = forwardRef(function BottomCTA({ primaryAction, secondaryAction, description, accessibilityLabel, safeAreaBottom = 0, position = "flow", className, style, ...props }, ref) {
18
+ const theme = useOptionalHjmTheme();
19
+ if (!Number.isFinite(safeAreaBottom) || safeAreaBottom < 0)
20
+ throw new RangeError("BottomCTA safeAreaBottom must be non-negative");
21
+ return _jsxs("div", { ...props, ref: ref, role: "group", "aria-label": accessibilityLabel, className: classNames("hjm-bottom-cta", className), "data-position": position, "data-large-text": isLargeTextScale(theme?.environment.textScale ?? 1), style: {
22
+ "--hjm-bottom-cta-min-height": `${bottomCtaRecipe.minHeight}px`,
23
+ "--hjm-bottom-cta-padding-inline": `${bottomCtaRecipe.paddingHorizontal}px`,
24
+ "--hjm-bottom-cta-padding-top": `${bottomCtaRecipe.paddingTop}px`,
25
+ "--hjm-bottom-cta-padding-bottom": `${bottomCtaRecipe.paddingBottom}px`,
26
+ "--hjm-bottom-cta-gap": `${bottomCtaRecipe.gap}px`,
27
+ "--hjm-bottom-cta-safe-area": `${safeAreaBottom}px`, ...style,
28
+ }, children: [description ? _jsx("p", { className: "hjm-bottom-cta__description", children: description }) : null, _jsxs("div", { className: "hjm-bottom-cta__actions", children: [secondaryAction == null ? null : _jsx("div", { className: "hjm-bottom-cta__secondary", children: isAction(secondaryAction) ? _jsx(Action, { action: secondaryAction, tone: "secondary" }) : secondaryAction }), _jsx("div", { className: "hjm-bottom-cta__primary", children: _jsx(Action, { action: primaryAction, tone: "primary" }) })] })] });
29
+ });
30
+ //# sourceMappingURL=bottom-cta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottom-cta.js","sourceRoot":"","sources":["../src/bottom-cta.tsx"],"names":[],"mappings":"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","sourcesContent":["import { bottomCtaRecipe } from \"@hjmds/design-contracts/recipes\";\nimport { isLargeTextScale } from \"@hjmds/design-contracts/components/design-system-provider\";\nimport { forwardRef, type CSSProperties, type HTMLAttributes, type MouseEventHandler, type ReactNode } from \"react\";\nimport { Button, type ButtonSize, type ButtonTone } from \"./actions.js\";\nimport { classNames } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\n\nexport type BottomCTAAction = Readonly<{\n label: string;\n onClick: MouseEventHandler<HTMLButtonElement>;\n accessibilityLabel?: string;\n disabled?: boolean;\n loading?: boolean;\n loadingLabel?: string;\n size?: ButtonSize;\n tone?: ButtonTone;\n}>;\n\nexport type BottomCTAProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\"> & Readonly<{\n primaryAction: BottomCTAAction;\n secondaryAction?: BottomCTAAction | ReactNode;\n description?: string;\n accessibilityLabel?: string;\n safeAreaBottom?: number;\n /** Sticky remains in document flow; fixed overlays would need a measured content spacer. */\n position?: \"flow\" | \"sticky\";\n}>;\n\nfunction Action({ action, tone }: { action: BottomCTAAction; tone: ButtonTone }) {\n if (!action.label.trim()) throw new TypeError(\"BottomCTA action label must not be empty\");\n return <Button onClick={action.onClick} tone={action.tone ?? tone} size={action.size ?? \"medium\"}\n disabled={action.disabled} loading={action.loading ?? false}\n aria-label={action.loading ? action.loadingLabel ?? action.accessibilityLabel ?? action.label : action.accessibilityLabel}>\n {action.label}\n </Button>;\n}\n\nfunction isAction(value: BottomCTAAction | ReactNode): value is BottomCTAAction {\n return typeof value === \"object\" && value !== null && \"label\" in value && \"onClick\" in value;\n}\n\n/** One primary action with optional supporting copy and a secondary action, matching Native's slots. */\nexport const BottomCTA = forwardRef<HTMLDivElement, BottomCTAProps>(function BottomCTA({\n primaryAction, secondaryAction, description, accessibilityLabel, safeAreaBottom = 0,\n position = \"flow\", className, style, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n if (!Number.isFinite(safeAreaBottom) || safeAreaBottom < 0) throw new RangeError(\"BottomCTA safeAreaBottom must be non-negative\");\n return <div {...props} ref={ref} role=\"group\" aria-label={accessibilityLabel}\n className={classNames(\"hjm-bottom-cta\", className)} data-position={position}\n data-large-text={isLargeTextScale(theme?.environment.textScale ?? 1)}\n style={{\n \"--hjm-bottom-cta-min-height\": `${bottomCtaRecipe.minHeight}px`,\n \"--hjm-bottom-cta-padding-inline\": `${bottomCtaRecipe.paddingHorizontal}px`,\n \"--hjm-bottom-cta-padding-top\": `${bottomCtaRecipe.paddingTop}px`,\n \"--hjm-bottom-cta-padding-bottom\": `${bottomCtaRecipe.paddingBottom}px`,\n \"--hjm-bottom-cta-gap\": `${bottomCtaRecipe.gap}px`,\n \"--hjm-bottom-cta-safe-area\": `${safeAreaBottom}px`, ...style,\n } as CSSProperties}>\n {description ? <p className=\"hjm-bottom-cta__description\">{description}</p> : null}\n <div className=\"hjm-bottom-cta__actions\">\n {secondaryAction == null ? null : <div className=\"hjm-bottom-cta__secondary\">{isAction(secondaryAction) ? <Action action={secondaryAction} tone=\"secondary\" /> : secondaryAction}</div>}\n <div className=\"hjm-bottom-cta__primary\"><Action action={primaryAction} tone=\"primary\" /></div>\n </div>\n </div>;\n});\n"]}
@@ -0,0 +1,16 @@
1
+ import { type BottomInfoDescriptor } from "@hjmds/design-contracts/components/bottom-info";
2
+ import { type HTMLAttributes, type ReactNode } from "react";
3
+ export type BottomInfoProps = Omit<HTMLAttributes<HTMLElement>, "children"> & BottomInfoDescriptor & Readonly<{
4
+ /** Replaces one line with rich copy (a link inside the sentence, for example). */
5
+ renderItem?: (item: string, index: number) => ReactNode;
6
+ className?: string;
7
+ }>;
8
+ export declare const BottomInfo: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLElement>, "children"> & Readonly<{
9
+ items: readonly string[];
10
+ tone?: import("@hjmds/design-contracts/components/bottom-info").BottomInfoTone;
11
+ }> & Readonly<{
12
+ /** Replaces one line with rich copy (a link inside the sentence, for example). */
13
+ renderItem?: (item: string, index: number) => ReactNode;
14
+ className?: string;
15
+ }> & import("react").RefAttributes<HTMLElement>>;
16
+ //# sourceMappingURL=bottom-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottom-info.d.ts","sourceRoot":"","sources":["../src/bottom-info.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAAc,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,GACzE,oBAAoB,GACpB,QAAQ,CAAC;IACP,kFAAkF;IAClF,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEL,eAAO,MAAM,UAAU;;;;IALnB,kFAAkF;iBACrE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS;gBAC3C,MAAM;gDA8BpB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { bottomInfoRecipe, validateBottomInfoDescriptor, } from "@hjmds/design-contracts/components/bottom-info";
3
+ import { forwardRef } from "react";
4
+ import { classNames } from "./internal.js";
5
+ export const BottomInfo = forwardRef(function BottomInfo({ items, tone = bottomInfoRecipe.defaults.tone, renderItem, className, ...props }, forwardedRef) {
6
+ validateBottomInfoDescriptor({ items, tone });
7
+ // A single line reads as a sentence; several read as a list. Marking one line
8
+ // with a bullet is noise, which is why the recipe carries the threshold.
9
+ const asList = items.length >= bottomInfoRecipe.listMarkerFrom;
10
+ return (_jsx("aside", { ...props, ref: forwardedRef, className: classNames("hjm-bottom-info", className), "data-tone": tone, "data-list": asList || undefined, children: asList ? (_jsx("ul", { className: "hjm-bottom-info__list", children: items.map((item, index) => (_jsx("li", { className: "hjm-bottom-info__item", children: renderItem?.(item, index) ?? item }, item))) })) : (_jsx("p", { className: "hjm-bottom-info__item", children: renderItem?.(items[0], 0) ?? items[0] })) }));
11
+ });
12
+ //# sourceMappingURL=bottom-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bottom-info.js","sourceRoot":"","sources":["../src/bottom-info.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,gBAAgB,EAChB,4BAA4B,GAE7B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAAE,UAAU,EAAuC,MAAM,OAAO,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAU3C,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAA+B,SAAS,UAAU,CACpF,EAAE,KAAK,EAAE,IAAI,GAAG,gBAAgB,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EACjF,YAAY;IAEZ,4BAA4B,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,8EAA8E;IAC9E,yEAAyE;IACzE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,gBAAgB,CAAC,cAAc,CAAC;IAC/D,OAAO,CACL,mBACM,KAAK,EACT,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,UAAU,CAAC,iBAAiB,EAAE,SAAS,CAAC,eACxC,IAAI,eACJ,MAAM,IAAI,SAAS,YAE7B,MAAM,CAAC,CAAC,CAAC,CACR,aAAI,SAAS,EAAC,uBAAuB,YAClC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC1B,aAAe,SAAS,EAAC,uBAAuB,YAAE,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAA1E,IAAI,CAA4E,CAC1F,CAAC,GACC,CACN,CAAC,CAAC,CAAC,CACF,YAAG,SAAS,EAAC,uBAAuB,YAAE,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,GAAK,CAClF,GACK,CACT,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import {\n bottomInfoRecipe,\n validateBottomInfoDescriptor,\n type BottomInfoDescriptor,\n} from \"@hjmds/design-contracts/components/bottom-info\";\nimport { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport { classNames } from \"./internal.js\";\n\nexport type BottomInfoProps = Omit<HTMLAttributes<HTMLElement>, \"children\"> &\n BottomInfoDescriptor &\n Readonly<{\n /** Replaces one line with rich copy (a link inside the sentence, for example). */\n renderItem?: (item: string, index: number) => ReactNode;\n className?: string;\n }>;\n\nexport const BottomInfo = forwardRef<HTMLElement, BottomInfoProps>(function BottomInfo(\n { items, tone = bottomInfoRecipe.defaults.tone, renderItem, className, ...props },\n forwardedRef,\n) {\n validateBottomInfoDescriptor({ items, tone });\n // A single line reads as a sentence; several read as a list. Marking one line\n // with a bullet is noise, which is why the recipe carries the threshold.\n const asList = items.length >= bottomInfoRecipe.listMarkerFrom;\n return (\n <aside\n {...props}\n ref={forwardedRef}\n className={classNames(\"hjm-bottom-info\", className)}\n data-tone={tone}\n data-list={asList || undefined}\n >\n {asList ? (\n <ul className=\"hjm-bottom-info__list\">\n {items.map((item, index) => (\n <li key={item} className=\"hjm-bottom-info__item\">{renderItem?.(item, index) ?? item}</li>\n ))}\n </ul>\n ) : (\n <p className=\"hjm-bottom-info__item\">{renderItem?.(items[0]!, 0) ?? items[0]}</p>\n )}\n </aside>\n );\n});\n"]}
@@ -0,0 +1,9 @@
1
+ import { type BreadcrumbItemDescriptor } from "@hjmds/design-contracts/components/breadcrumb";
2
+ import { type HTMLAttributes, type ReactElement, type ReactNode, type RefAttributes } from "react";
3
+ export type BreadcrumbProps<Id extends string = string> = Omit<HTMLAttributes<HTMLElement>, "children"> & Readonly<{
4
+ label: string;
5
+ items: readonly BreadcrumbItemDescriptor<Id>[];
6
+ separator?: ReactNode;
7
+ }>;
8
+ export declare const Breadcrumb: <Id extends string = string>(props: BreadcrumbProps<Id> & RefAttributes<HTMLElement>) => ReactElement;
9
+ //# sourceMappingURL=breadcrumb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumb.d.ts","sourceRoot":"","sources":["../src/breadcrumb.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AAC3H,OAAO,EAAiC,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAGlI,MAAM,MAAM,eAAe,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,IAAI,CAC5D,cAAc,CAAC,WAAW,CAAC,EAC3B,UAAU,CACX,GACC,QAAQ,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,wBAAwB,CAAC,EAAE,CAAC,EAAE,CAAC;IAC/C,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC,CAAC;AAiDL,eAAO,MAAM,UAAU,EAAkC,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAClF,KAAK,EAAE,eAAe,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,KACpD,YAAY,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveBreadcrumbDescriptor } from "@hjmds/design-contracts/components/breadcrumb";
3
+ import { forwardRef } from "react";
4
+ import { classNames } from "./internal.js";
5
+ function BreadcrumbInner({ label, items, separator, className, ...props }, ref) {
6
+ if (label.trim().length === 0)
7
+ throw new TypeError("Breadcrumb label must not be empty");
8
+ const descriptor = resolveBreadcrumbDescriptor({ items });
9
+ return (_jsx("nav", { ...props, ref: ref, className: classNames("hjm-breadcrumb", className), "aria-label": label, children: _jsx("ol", { className: "hjm-breadcrumb__list", children: descriptor.items.map((item, index) => (_jsxs("li", { className: "hjm-breadcrumb__item", children: [item.current ? (_jsx("span", { className: "hjm-breadcrumb__current", "aria-current": "page", children: item.label })) : (_jsx("a", { className: "hjm-breadcrumb__link", href: item.destination.href, "data-destination": item.destination.kind, children: item.label })), index < descriptor.items.length - 1 ? (_jsx("span", { className: "hjm-breadcrumb__separator", "aria-hidden": "true", "data-default": separator === undefined, children: separator === undefined ? "›" : separator })) : null] }, item.id))) }) }));
10
+ }
11
+ export const Breadcrumb = forwardRef(BreadcrumbInner);
12
+ //# sourceMappingURL=breadcrumb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breadcrumb.js","sourceRoot":"","sources":["../src/breadcrumb.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,2BAA2B,EAAiC,MAAM,+CAA+C,CAAC;AAC3H,OAAO,EAAE,UAAU,EAAiG,MAAM,OAAO,CAAC;AAClI,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAY3C,SAAS,eAAe,CACtB,EACE,KAAK,EACL,KAAK,EACL,SAAS,EACT,SAAS,EACT,GAAG,KAAK,EACY,EACtB,GAA8B;IAE9B,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,oCAAoC,CAAC,CAAC;IACzF,MAAM,UAAU,GAAG,2BAA2B,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1D,OAAO,CACL,iBACM,KAAK,EACT,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,UAAU,CAAC,gBAAgB,EAAE,SAAS,CAAC,gBACtC,KAAK,YAEjB,aAAI,SAAS,EAAC,sBAAsB,YACjC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CACrC,cAAkB,SAAS,EAAC,sBAAsB,aAC/C,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CACd,eAAM,SAAS,EAAC,yBAAyB,kBAAc,MAAM,YAC1D,IAAI,CAAC,KAAK,GACN,CACR,CAAC,CAAC,CAAC,CACF,YACE,SAAS,EAAC,sBAAsB,EAChC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,sBACT,IAAI,CAAC,WAAW,CAAC,IAAI,YAEtC,IAAI,CAAC,KAAK,GACT,CACL,EACA,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CACrC,eAAM,SAAS,EAAC,2BAA2B,iBAAa,MAAM,kBAAe,SAAS,KAAK,SAAS,YACjG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,GACrC,CACR,CAAC,CAAC,CAAC,IAAI,KAlBD,IAAI,CAAC,EAAE,CAmBX,CACN,CAAC,GACC,GACD,CACP,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,eAAe,CAEnC,CAAC","sourcesContent":["import { resolveBreadcrumbDescriptor, type BreadcrumbItemDescriptor } from \"@hjmds/design-contracts/components/breadcrumb\";\nimport { forwardRef, type ForwardedRef, type HTMLAttributes, type ReactElement, type ReactNode, type RefAttributes } from \"react\";\nimport { classNames } from \"./internal.js\";\n\nexport type BreadcrumbProps<Id extends string = string> = Omit<\n HTMLAttributes<HTMLElement>,\n \"children\"\n> &\n Readonly<{\n label: string;\n items: readonly BreadcrumbItemDescriptor<Id>[];\n separator?: ReactNode;\n }>;\n\nfunction BreadcrumbInner<Id extends string>(\n {\n label,\n items,\n separator,\n className,\n ...props\n }: BreadcrumbProps<Id>,\n ref: ForwardedRef<HTMLElement>,\n) {\n if (label.trim().length === 0) throw new TypeError(\"Breadcrumb label must not be empty\");\n const descriptor = resolveBreadcrumbDescriptor({ items });\n return (\n <nav\n {...props}\n ref={ref}\n className={classNames(\"hjm-breadcrumb\", className)}\n aria-label={label}\n >\n <ol className=\"hjm-breadcrumb__list\">\n {descriptor.items.map((item, index) => (\n <li key={item.id} className=\"hjm-breadcrumb__item\">\n {item.current ? (\n <span className=\"hjm-breadcrumb__current\" aria-current=\"page\">\n {item.label}\n </span>\n ) : (\n <a\n className=\"hjm-breadcrumb__link\"\n href={item.destination.href}\n data-destination={item.destination.kind}\n >\n {item.label}\n </a>\n )}\n {index < descriptor.items.length - 1 ? (\n <span className=\"hjm-breadcrumb__separator\" aria-hidden=\"true\" data-default={separator === undefined}>\n {separator === undefined ? \"›\" : separator}\n </span>\n ) : null}\n </li>\n ))}\n </ol>\n </nav>\n );\n}\n\nexport const Breadcrumb = forwardRef(BreadcrumbInner) as <Id extends string = string>(\n props: BreadcrumbProps<Id> & RefAttributes<HTMLElement>,\n) => ReactElement;\n\n"]}
@@ -0,0 +1,30 @@
1
+ import { type ReactNode, type Ref } from "react";
2
+ import { type CalendarDescriptor, type CalendarSize, type CalendarNavigationIntent, type ComposeCalendarAccessibleName, type ResolvedCalendarDateCell } from "@hjmds/design-contracts/components/calendar";
3
+ export type CalendarMonthAction = Readonly<{
4
+ month: string;
5
+ label: string;
6
+ }>;
7
+ export type CalendarHandle = Readonly<{
8
+ focusDate(date: string): void;
9
+ }>;
10
+ export type CalendarOverflow = Readonly<{
11
+ date: string;
12
+ intent: CalendarNavigationIntent;
13
+ overflow: "before" | "after";
14
+ }>;
15
+ export type CalendarProps<Content = unknown> = Readonly<{
16
+ descriptor: CalendarDescriptor<Content>;
17
+ composeAccessibleName: ComposeCalendarAccessibleName<Content>;
18
+ previousMonth?: CalendarMonthAction;
19
+ nextMonth?: CalendarMonthAction;
20
+ /** The product owns adjacent month/date arithmetic. Call focusDate after requesting its page. */
21
+ onNavigateBeyondGrid?: (detail: CalendarOverflow, focusDate: CalendarHandle["focusDate"]) => void;
22
+ renderCellContent?: (cell: ResolvedCalendarDateCell<Content>) => ReactNode;
23
+ size?: CalendarSize;
24
+ autoFocus?: boolean;
25
+ className?: string;
26
+ ref?: Ref<CalendarHandle>;
27
+ }>;
28
+ /** Inline single-date grid shared with DatePicker; month data remains product-owned. */
29
+ export declare function Calendar<Content>({ descriptor, composeAccessibleName, previousMonth, nextMonth, onNavigateBeyondGrid, renderCellContent, size, autoFocus, className, ref }: CalendarProps<Content>): import("react").JSX.Element;
30
+ //# sourceMappingURL=calendar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+E,KAAK,SAAS,EAAE,KAAK,GAAG,EAAE,MAAM,OAAO,CAAC;AAC9H,OAAO,EAGL,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAE,KAAK,wBAAwB,EACzE,KAAK,6BAA6B,EAAE,KAAK,wBAAwB,EAClE,MAAM,6CAA6C,CAAC;AAIrD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC7E,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC;AACzE,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,wBAAwB,CAAC;IAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,CAAC,CAAC;AAC1H,MAAM,MAAM,aAAa,CAAC,OAAO,GAAG,OAAO,IAAI,QAAQ,CAAC;IACtD,UAAU,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACxC,qBAAqB,EAAE,6BAA6B,CAAC,OAAO,CAAC,CAAC;IAC9D,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,SAAS,CAAC,EAAE,mBAAmB,CAAC;IAChC,iGAAiG;IACjG,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,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC;CAC3B,CAAC,CAAC;AAEH,wFAAwF;AACxF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,qBAAqB,EAAE,aAAa,EAAE,SAAS,EAC7F,oBAAoB,EAAE,iBAAiB,EAAE,IAAe,EAAE,SAAiB,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,aAAa,CAAC,OAAO,CAAC,+BA6EtH"}
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useId, useImperativeHandle, useRef, useState } from "react";
3
+ import { calendarRecipe as recipe, assertIsoCalendarMonth, validateCalendarDescriptor, resolveCalendarGridDescriptor, getCalendarNavigationIntent, getCalendarNavigationTarget, } from "@hjmds/design-contracts/components/calendar";
4
+ import { classNames, useControllableState } from "./internal.js";
5
+ import { useOptionalHjmTheme } from "./provider.js";
6
+ /** Inline single-date grid shared with DatePicker; month data remains product-owned. */
7
+ export function Calendar({ descriptor, composeAccessibleName, previousMonth, nextMonth, onNavigateBeyondGrid, renderCellContent, size = "medium", autoFocus = false, className, ref }) {
8
+ validateCalendarDescriptor(descriptor);
9
+ for (const action of [previousMonth, nextMonth]) {
10
+ if (!action)
11
+ continue;
12
+ assertIsoCalendarMonth(action.month, "month action");
13
+ if (!action.label.trim())
14
+ throw new TypeError("Calendar month action label must not be empty");
15
+ }
16
+ const theme = useOptionalHjmTheme();
17
+ const [selected, setSelected] = useControllableState({
18
+ ...(descriptor.selectedDate === undefined ? {} : { value: descriptor.selectedDate }),
19
+ defaultValue: descriptor.defaultSelectedDate ?? null,
20
+ ...(descriptor.onSelectionChange ? { onChange: descriptor.onSelectionChange } : {}),
21
+ });
22
+ const cells = resolveCalendarGridDescriptor(descriptor.grid, selected, { composeAccessibleName });
23
+ const dates = cells.filter((cell) => !("filler" in cell));
24
+ const initial = dates.find((cell) => cell.isSelected) ?? dates.find((cell) => cell.isToday) ?? dates[0];
25
+ const [focused, setFocused] = useState(initial?.date);
26
+ const active = dates.some((cell) => cell.date === focused) ? focused : initial?.date;
27
+ const root = useRef(null);
28
+ const pending = useRef(undefined);
29
+ const [request, setRequest] = useState(0);
30
+ const focusDate = (date) => { pending.current = date; setFocused(date); setRequest((value) => value + 1); };
31
+ useImperativeHandle(ref, () => ({ focusDate }), []);
32
+ const gridKey = dates.map((cell) => cell.date).join(",");
33
+ const mountedFocus = useRef(false);
34
+ useEffect(() => {
35
+ if (autoFocus && !mountedFocus.current && active)
36
+ pending.current = active;
37
+ mountedFocus.current = true;
38
+ const date = pending.current;
39
+ if (!date)
40
+ return;
41
+ // A requested adjacent date may arrive in a later render; retain it until that page exists.
42
+ const button = Array.from(root.current?.querySelectorAll("[data-date]") ?? []).find((node) => node.dataset.date === date);
43
+ if (button) {
44
+ button.focus();
45
+ setFocused(date);
46
+ pending.current = undefined;
47
+ }
48
+ }, [gridKey, request, autoFocus, active]);
49
+ const titleId = `hjm-calendar-${useId()}`;
50
+ return _jsxs("div", { ref: root, className: classNames("hjm-calendar", className), "data-size": size, style: { "--hjm-calendar-cell-size": `${recipe.sizes[size].cellDiameter}px`,
51
+ "--hjm-calendar-disabled-opacity": recipe.day.disabledOpacity,
52
+ "--hjm-calendar-outside-opacity": recipe.day.outsideFocusedMonthOpacity }, children: [_jsxs("div", { className: "hjm-calendar__header", children: [previousMonth ? _jsx("button", { type: "button", "aria-label": previousMonth.label, disabled: !descriptor.onFocusedMonthChange, onClick: () => descriptor.onFocusedMonthChange?.(previousMonth.month, "previous"), children: _jsx("span", { "aria-hidden": "true", children: theme?.environment.direction === "rtl" ? "›" : "‹" }) }) : _jsx("span", {}), _jsx("strong", { id: titleId, "aria-live": "polite", "aria-atomic": "true", children: descriptor.monthLabel }), nextMonth ? _jsx("button", { type: "button", "aria-label": nextMonth.label, disabled: !descriptor.onFocusedMonthChange, onClick: () => descriptor.onFocusedMonthChange?.(nextMonth.month, "next"), children: _jsx("span", { "aria-hidden": "true", children: theme?.environment.direction === "rtl" ? "‹" : "›" }) }) : _jsx("span", {})] }), _jsx("div", { className: "hjm-calendar__viewport", children: _jsxs("div", { role: "grid", "aria-labelledby": titleId, className: "hjm-calendar__grid", children: [_jsx("div", { role: "row", className: "hjm-calendar__week", children: descriptor.grid.weekdayLabels.map((label, index) => _jsx("span", { role: "columnheader", className: "hjm-calendar__weekday", children: label }, index)) }), Array.from({ length: cells.length / 7 }, (_, row) => _jsx("div", { role: "row", className: "hjm-calendar__week", children: cells.slice(row * 7, row * 7 + 7).map((cell, column) => "filler" in cell
53
+ ? _jsx("span", { role: "gridcell", "aria-hidden": "true" }, `filler-${column}`)
54
+ : _jsxs("button", { type: "button", role: "gridcell", className: "hjm-calendar__day", "data-date": cell.date, "data-focus-date": cell.date === active || undefined, "data-today": cell.isToday || undefined, "data-selected": cell.isSelected || undefined, "data-disabled": !cell.selectable || undefined, "data-outside": cell.outsideFocusedMonth || undefined, "aria-label": cell.accessibleName, "aria-selected": cell.isSelected, "aria-disabled": !cell.selectable || undefined, "aria-current": cell.isToday ? "date" : undefined, tabIndex: cell.date === active ? 0 : -1, onFocus: () => setFocused(cell.date), onClick: () => { if (cell.selectable)
55
+ setSelected(cell.date); }, onKeyDown: (event) => {
56
+ if (event.altKey || event.ctrlKey || event.metaKey)
57
+ return;
58
+ const intent = getCalendarNavigationIntent(event.key, theme?.environment.direction ?? "ltr");
59
+ if (!intent)
60
+ return;
61
+ event.preventDefault();
62
+ const target = getCalendarNavigationTarget(descriptor.grid, cell.date, intent);
63
+ if (target.date)
64
+ focusDate(target.date);
65
+ else if (target.overflow)
66
+ onNavigateBeyondGrid?.({ date: cell.date, intent, overflow: target.overflow }, focusDate);
67
+ }, children: [_jsx("span", { "aria-hidden": "true", className: "hjm-calendar__day-label", children: Number(cell.date.slice(-2)) }), renderCellContent ? _jsx("span", { "aria-hidden": "true", className: "hjm-calendar__content", children: renderCellContent(cell) }) : null] }, cell.date)) }, row))] }) })] });
68
+ }
69
+ //# sourceMappingURL=calendar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calendar.js","sourceRoot":"","sources":["../src/calendar.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useId, useImperativeHandle, useRef, useState, type CSSProperties, type ReactNode, type Ref } from \"react\";\nimport {\n calendarRecipe as recipe, assertIsoCalendarMonth, validateCalendarDescriptor, resolveCalendarGridDescriptor,\n getCalendarNavigationIntent, getCalendarNavigationTarget,\n type CalendarDescriptor, type CalendarSize, type CalendarNavigationIntent,\n type ComposeCalendarAccessibleName, type ResolvedCalendarDateCell,\n} from \"@hjmds/design-contracts/components/calendar\";\nimport { classNames, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\n\nexport type CalendarMonthAction = Readonly<{ month: string; label: string }>;\nexport type CalendarHandle = Readonly<{ focusDate(date: string): void }>;\nexport type CalendarOverflow = Readonly<{ date: string; intent: CalendarNavigationIntent; overflow: \"before\" | \"after\" }>;\nexport type CalendarProps<Content = unknown> = Readonly<{\n descriptor: CalendarDescriptor<Content>;\n composeAccessibleName: ComposeCalendarAccessibleName<Content>;\n previousMonth?: CalendarMonthAction;\n nextMonth?: CalendarMonthAction;\n /** The product owns adjacent month/date arithmetic. Call focusDate after requesting its page. */\n onNavigateBeyondGrid?: (detail: CalendarOverflow, focusDate: CalendarHandle[\"focusDate\"]) => void;\n renderCellContent?: (cell: ResolvedCalendarDateCell<Content>) => ReactNode;\n size?: CalendarSize;\n autoFocus?: boolean;\n className?: string;\n ref?: Ref<CalendarHandle>;\n}>;\n\n/** Inline single-date grid shared with DatePicker; month data remains product-owned. */\nexport function Calendar<Content>({ descriptor, composeAccessibleName, previousMonth, nextMonth,\n onNavigateBeyondGrid, renderCellContent, size = \"medium\", autoFocus = false, className, ref }: CalendarProps<Content>) {\n validateCalendarDescriptor(descriptor);\n for (const action of [previousMonth, nextMonth]) {\n if (!action) continue;\n assertIsoCalendarMonth(action.month, \"month action\");\n if (!action.label.trim()) throw new TypeError(\"Calendar month action label must not be empty\");\n }\n const theme = useOptionalHjmTheme();\n const [selected, setSelected] = useControllableState<string | null>({\n ...(descriptor.selectedDate === undefined ? {} : { value: descriptor.selectedDate }),\n defaultValue: descriptor.defaultSelectedDate ?? null,\n ...(descriptor.onSelectionChange ? { onChange: descriptor.onSelectionChange } : {}),\n });\n const cells = resolveCalendarGridDescriptor(descriptor.grid, selected, { composeAccessibleName });\n const dates = cells.filter((cell): cell is ResolvedCalendarDateCell<Content> => !(\"filler\" in cell));\n const initial = dates.find((cell) => cell.isSelected) ?? dates.find((cell) => cell.isToday) ?? dates[0];\n const [focused, setFocused] = useState(initial?.date);\n const active = dates.some((cell) => cell.date === focused) ? focused : initial?.date;\n const root = useRef<HTMLDivElement>(null);\n const pending = useRef<string | undefined>(undefined);\n const [request, setRequest] = useState(0);\n const focusDate = (date: string) => { pending.current = date; setFocused(date); setRequest((value) => value + 1); };\n useImperativeHandle(ref, () => ({ focusDate }), []);\n const gridKey = dates.map((cell) => cell.date).join(\",\");\n const mountedFocus = useRef(false);\n useEffect(() => {\n if (autoFocus && !mountedFocus.current && active) pending.current = active;\n mountedFocus.current = true;\n const date = pending.current;\n if (!date) return;\n // A requested adjacent date may arrive in a later render; retain it until that page exists.\n const button = Array.from(root.current?.querySelectorAll<HTMLButtonElement>(\"[data-date]\") ?? []).find((node) => node.dataset.date === date);\n if (button) { button.focus(); setFocused(date); pending.current = undefined; }\n }, [gridKey, request, autoFocus, active]);\n const titleId = `hjm-calendar-${useId()}`;\n return <div ref={root} className={classNames(\"hjm-calendar\", className)} data-size={size}\n style={{ \"--hjm-calendar-cell-size\": `${recipe.sizes[size].cellDiameter}px`,\n \"--hjm-calendar-disabled-opacity\": recipe.day.disabledOpacity,\n \"--hjm-calendar-outside-opacity\": recipe.day.outsideFocusedMonthOpacity } as CSSProperties}>\n <div className=\"hjm-calendar__header\">\n {previousMonth ? <button type=\"button\" aria-label={previousMonth.label} disabled={!descriptor.onFocusedMonthChange}\n onClick={() => descriptor.onFocusedMonthChange?.(previousMonth.month, \"previous\")}><span aria-hidden=\"true\">{theme?.environment.direction === \"rtl\" ? \"›\" : \"‹\"}</span></button> : <span />}\n <strong id={titleId} aria-live=\"polite\" aria-atomic=\"true\">{descriptor.monthLabel}</strong>\n {nextMonth ? <button type=\"button\" aria-label={nextMonth.label} disabled={!descriptor.onFocusedMonthChange}\n onClick={() => descriptor.onFocusedMonthChange?.(nextMonth.month, \"next\")}><span aria-hidden=\"true\">{theme?.environment.direction === \"rtl\" ? \"‹\" : \"›\"}</span></button> : <span />}\n </div>\n <div className=\"hjm-calendar__viewport\">\n <div role=\"grid\" aria-labelledby={titleId} className=\"hjm-calendar__grid\">\n <div role=\"row\" className=\"hjm-calendar__week\">\n {descriptor.grid.weekdayLabels.map((label, index) => <span role=\"columnheader\" className=\"hjm-calendar__weekday\" key={index}>{label}</span>)}\n </div>\n {Array.from({ length: cells.length / 7 }, (_, row) => <div role=\"row\" className=\"hjm-calendar__week\" key={row}>\n {cells.slice(row * 7, row * 7 + 7).map((cell, column) => \"filler\" in cell\n ? <span role=\"gridcell\" aria-hidden=\"true\" key={`filler-${column}`} />\n : <button type=\"button\" role=\"gridcell\" key={cell.date} className=\"hjm-calendar__day\"\n data-date={cell.date} data-focus-date={cell.date === active || undefined}\n data-today={cell.isToday || undefined} data-selected={cell.isSelected || undefined}\n data-disabled={!cell.selectable || undefined} data-outside={cell.outsideFocusedMonth || undefined}\n aria-label={cell.accessibleName} aria-selected={cell.isSelected} aria-disabled={!cell.selectable || undefined}\n aria-current={cell.isToday ? \"date\" : undefined} tabIndex={cell.date === active ? 0 : -1}\n onFocus={() => setFocused(cell.date)} onClick={() => { if (cell.selectable) setSelected(cell.date); }}\n onKeyDown={(event) => {\n if (event.altKey || event.ctrlKey || event.metaKey) return;\n const intent = getCalendarNavigationIntent(event.key as Parameters<typeof getCalendarNavigationIntent>[0], theme?.environment.direction ?? \"ltr\");\n if (!intent) return;\n event.preventDefault();\n const target = getCalendarNavigationTarget(descriptor.grid, cell.date, intent);\n if (target.date) focusDate(target.date);\n else if (target.overflow) onNavigateBeyondGrid?.({ date: cell.date, intent, overflow: target.overflow }, focusDate);\n }}>\n <span aria-hidden=\"true\" className=\"hjm-calendar__day-label\">{Number(cell.date.slice(-2))}</span>\n {renderCellContent ? <span aria-hidden=\"true\" className=\"hjm-calendar__content\">{renderCellContent(cell)}</span> : null}\n </button>)}\n </div>)}\n </div>\n </div>\n </div>;\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ import { type CarouselSlideDescriptor, type CarouselSelection, type CarouselAutoplayConfig, type ComposeCarouselAccessibleName } from "@hjmds/design-contracts/components/carousel";
3
+ export type CarouselLabels = Readonly<{
4
+ previous: string;
5
+ next: string;
6
+ pause: string;
7
+ resume: string;
8
+ navigation: string;
9
+ }>;
10
+ export type CarouselProps = Omit<HTMLAttributes<HTMLDivElement>, "children" | "onChange"> & CarouselSelection & Readonly<{
11
+ label: string;
12
+ slides: readonly CarouselSlideDescriptor[];
13
+ renderSlide: (slide: CarouselSlideDescriptor) => ReactNode;
14
+ composeAccessibleName: ComposeCarouselAccessibleName;
15
+ labels: CarouselLabels;
16
+ autoplay?: CarouselAutoplayConfig;
17
+ }>;
18
+ /** Finite keyed cards. Hidden slides stay mounted but cannot receive focus. */
19
+ export declare const Carousel: import("react").ForwardRefExoticComponent<CarouselProps & import("react").RefAttributes<HTMLDivElement>>;
20
+ //# sourceMappingURL=carousel.d.ts.map