@hjmds/react 1.1.1 → 1.3.4

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 (203) 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/auth-screen.d.ts +29 -0
  25. package/dist/auth-screen.d.ts.map +1 -0
  26. package/dist/auth-screen.js +26 -0
  27. package/dist/auth-screen.js.map +1 -0
  28. package/dist/bottom-cta.d.ts +32 -0
  29. package/dist/bottom-cta.d.ts.map +1 -0
  30. package/dist/bottom-cta.js +30 -0
  31. package/dist/bottom-cta.js.map +1 -0
  32. package/dist/bottom-info.d.ts +16 -0
  33. package/dist/bottom-info.d.ts.map +1 -0
  34. package/dist/bottom-info.js +12 -0
  35. package/dist/bottom-info.js.map +1 -0
  36. package/dist/breadcrumb.d.ts +9 -0
  37. package/dist/breadcrumb.d.ts.map +1 -0
  38. package/dist/breadcrumb.js +12 -0
  39. package/dist/breadcrumb.js.map +1 -0
  40. package/dist/calendar.d.ts +30 -0
  41. package/dist/calendar.d.ts.map +1 -0
  42. package/dist/calendar.js +69 -0
  43. package/dist/calendar.js.map +1 -0
  44. package/dist/carousel.d.ts +20 -0
  45. package/dist/carousel.d.ts.map +1 -0
  46. package/dist/carousel.js +70 -0
  47. package/dist/carousel.js.map +1 -0
  48. package/dist/clipboard.d.ts +33 -0
  49. package/dist/clipboard.d.ts.map +1 -0
  50. package/dist/clipboard.js +40 -0
  51. package/dist/clipboard.js.map +1 -0
  52. package/dist/collapsible.d.ts +11 -0
  53. package/dist/collapsible.d.ts.map +1 -0
  54. package/dist/collapsible.js +18 -0
  55. package/dist/collapsible.js.map +1 -0
  56. package/dist/command-palette.d.ts +25 -0
  57. package/dist/command-palette.d.ts.map +1 -0
  58. package/dist/command-palette.js +82 -0
  59. package/dist/command-palette.js.map +1 -0
  60. package/dist/context-menu.d.ts +12 -0
  61. package/dist/context-menu.d.ts.map +1 -0
  62. package/dist/context-menu.js +111 -0
  63. package/dist/context-menu.js.map +1 -0
  64. package/dist/data-table.d.ts +30 -0
  65. package/dist/data-table.d.ts.map +1 -0
  66. package/dist/data-table.js +57 -0
  67. package/dist/data-table.js.map +1 -0
  68. package/dist/date-picker.d.ts +3 -1
  69. package/dist/date-picker.d.ts.map +1 -1
  70. package/dist/date-picker.js +16 -31
  71. package/dist/date-picker.js.map +1 -1
  72. package/dist/date-range.d.ts +24 -0
  73. package/dist/date-range.d.ts.map +1 -0
  74. package/dist/date-range.js +29 -0
  75. package/dist/date-range.js.map +1 -0
  76. package/dist/display.d.ts +16 -0
  77. package/dist/display.d.ts.map +1 -1
  78. package/dist/display.js +13 -1
  79. package/dist/display.js.map +1 -1
  80. package/dist/evidence.d.ts +496 -1
  81. package/dist/evidence.d.ts.map +1 -1
  82. package/dist/evidence.js +48 -2
  83. package/dist/evidence.js.map +1 -1
  84. package/dist/feedback.d.ts +18 -2
  85. package/dist/feedback.d.ts.map +1 -1
  86. package/dist/feedback.js +10 -2
  87. package/dist/feedback.js.map +1 -1
  88. package/dist/floating-action-button.d.ts +32 -0
  89. package/dist/floating-action-button.d.ts.map +1 -0
  90. package/dist/floating-action-button.js +58 -0
  91. package/dist/floating-action-button.js.map +1 -0
  92. package/dist/forms.d.ts +3 -3
  93. package/dist/forms.d.ts.map +1 -1
  94. package/dist/forms.js +18 -7
  95. package/dist/forms.js.map +1 -1
  96. package/dist/heading.d.ts +12 -0
  97. package/dist/heading.d.ts.map +1 -0
  98. package/dist/heading.js +25 -0
  99. package/dist/heading.js.map +1 -0
  100. package/dist/index.d.ts +36 -2
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +36 -2
  103. package/dist/index.js.map +1 -1
  104. package/dist/layout.d.ts +1 -1
  105. package/dist/mentions.d.ts +28 -0
  106. package/dist/mentions.d.ts.map +1 -0
  107. package/dist/mentions.js +84 -0
  108. package/dist/mentions.js.map +1 -0
  109. package/dist/menubar.d.ts +11 -0
  110. package/dist/menubar.d.ts.map +1 -0
  111. package/dist/menubar.js +137 -0
  112. package/dist/menubar.js.map +1 -0
  113. package/dist/modal.d.ts +67 -0
  114. package/dist/modal.d.ts.map +1 -0
  115. package/dist/modal.js +307 -0
  116. package/dist/modal.js.map +1 -0
  117. package/dist/overlay-stack.d.ts +28 -0
  118. package/dist/overlay-stack.d.ts.map +1 -0
  119. package/dist/overlay-stack.js +47 -0
  120. package/dist/overlay-stack.js.map +1 -0
  121. package/dist/overlays.d.ts +26 -43
  122. package/dist/overlays.d.ts.map +1 -1
  123. package/dist/overlays.js +62 -302
  124. package/dist/overlays.js.map +1 -1
  125. package/dist/pagination.d.ts +17 -0
  126. package/dist/pagination.d.ts.map +1 -0
  127. package/dist/pagination.js +21 -0
  128. package/dist/pagination.js.map +1 -0
  129. package/dist/popover.d.ts +25 -0
  130. package/dist/popover.d.ts.map +1 -0
  131. package/dist/popover.js +226 -0
  132. package/dist/popover.js.map +1 -0
  133. package/dist/portal.d.ts +5 -1
  134. package/dist/portal.d.ts.map +1 -1
  135. package/dist/portal.js +24 -1
  136. package/dist/portal.js.map +1 -1
  137. package/dist/provider-button.d.ts +21 -0
  138. package/dist/provider-button.d.ts.map +1 -0
  139. package/dist/provider-button.js +26 -0
  140. package/dist/provider-button.js.map +1 -0
  141. package/dist/provider.d.ts +13 -1
  142. package/dist/provider.d.ts.map +1 -1
  143. package/dist/provider.js +12 -2
  144. package/dist/provider.js.map +1 -1
  145. package/dist/selection.d.ts +5 -1
  146. package/dist/selection.d.ts.map +1 -1
  147. package/dist/selection.js +2 -2
  148. package/dist/selection.js.map +1 -1
  149. package/dist/side-panel.d.ts +34 -0
  150. package/dist/side-panel.d.ts.map +1 -0
  151. package/dist/side-panel.js +95 -0
  152. package/dist/side-panel.js.map +1 -0
  153. package/dist/sidebar.d.ts +22 -0
  154. package/dist/sidebar.d.ts.map +1 -0
  155. package/dist/sidebar.js +29 -0
  156. package/dist/sidebar.js.map +1 -0
  157. package/dist/skip-nav.d.ts +12 -0
  158. package/dist/skip-nav.d.ts.map +1 -0
  159. package/dist/skip-nav.js +29 -0
  160. package/dist/skip-nav.js.map +1 -0
  161. package/dist/splitter.d.ts +45 -0
  162. package/dist/splitter.d.ts.map +1 -0
  163. package/dist/splitter.js +116 -0
  164. package/dist/splitter.js.map +1 -0
  165. package/dist/styles.css +1105 -26
  166. package/dist/supplemental-display.d.ts +12 -0
  167. package/dist/supplemental-display.d.ts.map +1 -1
  168. package/dist/supplemental-display.js +6 -2
  169. package/dist/supplemental-display.js.map +1 -1
  170. package/dist/tags-input.d.ts +60 -0
  171. package/dist/tags-input.d.ts.map +1 -0
  172. package/dist/tags-input.js +90 -0
  173. package/dist/tags-input.js.map +1 -0
  174. package/dist/text-formats.d.ts +16 -0
  175. package/dist/text-formats.d.ts.map +1 -0
  176. package/dist/text-formats.js +24 -0
  177. package/dist/text-formats.js.map +1 -0
  178. package/dist/toast.d.ts +1 -1
  179. package/dist/toggle-group.d.ts +13 -0
  180. package/dist/toggle-group.d.ts.map +1 -0
  181. package/dist/toggle-group.js +21 -0
  182. package/dist/toggle-group.js.map +1 -0
  183. package/dist/top-bar.d.ts +27 -0
  184. package/dist/top-bar.d.ts.map +1 -0
  185. package/dist/top-bar.js +30 -0
  186. package/dist/top-bar.js.map +1 -0
  187. package/dist/top.d.ts +15 -0
  188. package/dist/top.d.ts.map +1 -0
  189. package/dist/top.js +18 -0
  190. package/dist/top.js.map +1 -0
  191. package/dist/tour.d.ts +21 -0
  192. package/dist/tour.d.ts.map +1 -0
  193. package/dist/tour.js +122 -0
  194. package/dist/tour.js.map +1 -0
  195. package/dist/transfer-list.d.ts +24 -0
  196. package/dist/transfer-list.d.ts.map +1 -0
  197. package/dist/transfer-list.js +109 -0
  198. package/dist/transfer-list.js.map +1 -0
  199. package/dist/tree.d.ts +29 -0
  200. package/dist/tree.d.ts.map +1 -0
  201. package/dist/tree.js +120 -0
  202. package/dist/tree.js.map +1 -0
  203. package/package.json +178 -3
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carousel.d.ts","sourceRoot":"","sources":["../src/carousel.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,cAAc,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACrG,OAAO,EAEL,KAAK,uBAAuB,EAAE,KAAK,iBAAiB,EAAE,KAAK,sBAAsB,EACjF,KAAK,6BAA6B,EACnC,MAAM,6CAA6C,CAAC;AAMrD,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC7H,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,GAAG,iBAAiB,GAAG,QAAQ,CAAC;IACvH,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,uBAAuB,EAAE,CAAC;IAC3C,WAAW,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,SAAS,CAAC;IAC3D,qBAAqB,EAAE,6BAA6B,CAAC;IACrD,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CACnC,CAAC,CAAC;AAEH,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,0GAgFnB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState, forwardRef } from "react";
3
+ import { resolveCarouselDescriptor, getCarouselNavigationTarget, isCarouselAutoplayActive, } from "@hjmds/design-contracts/components/carousel";
4
+ import { Button } from "./actions.js";
5
+ import { isLargeTextScale } from "@hjmds/design-contracts/components/design-system-provider";
6
+ import { classNames, useControllableState } from "./internal.js";
7
+ import { useOptionalHjmTheme } from "./provider.js";
8
+ /** Finite keyed cards. Hidden slides stay mounted but cannot receive focus. */
9
+ export const Carousel = forwardRef(function Carousel({ label, slides, renderSlide, composeAccessibleName, labels, autoplay, currentKey, defaultCurrentKey, onCurrentKeyChange, className, onFocusCapture, onMouseEnter, onMouseLeave, onKeyDown, ...props }, ref) {
10
+ const theme = useOptionalHjmTheme();
11
+ const [current, setCurrent] = useControllableState({
12
+ ...(currentKey === undefined ? {} : { value: currentKey }),
13
+ defaultValue: defaultCurrentKey ?? slides[0]?.id ?? "",
14
+ ...(onCurrentKeyChange === undefined ? {} : { onChange: onCurrentKeyChange }),
15
+ });
16
+ const descriptor = { slides, currentKey: current, ...(autoplay ? { autoplay } : {}) };
17
+ const resolved = resolveCarouselDescriptor(descriptor, { composeAccessibleName });
18
+ if (!label.trim() || Object.values(labels).some((value) => !value.trim()))
19
+ throw new TypeError("Carousel labels must not be empty");
20
+ const [rotating, setRotating] = useState(autoplay !== undefined);
21
+ const [hovered, setHovered] = useState(false);
22
+ const [hidden, setHidden] = useState(false);
23
+ const pointerRotation = useRef(null);
24
+ const last = current === slides.at(-1).id;
25
+ const playing = isCarouselAutoplayActive(autoplay, {
26
+ reducedMotion: theme?.environment.reducedMotion ?? false,
27
+ paused: !rotating || hovered || hidden || last,
28
+ });
29
+ useEffect(() => {
30
+ const update = () => setHidden(document.hidden);
31
+ update();
32
+ document.addEventListener("visibilitychange", update);
33
+ return () => document.removeEventListener("visibilitychange", update);
34
+ }, []);
35
+ useEffect(() => {
36
+ if (!playing || !autoplay)
37
+ return;
38
+ const timer = setTimeout(() => setCurrent(getCarouselNavigationTarget(descriptor, "next")), autoplay.intervalMs);
39
+ return () => clearTimeout(timer);
40
+ }, [playing, autoplay?.intervalMs, current, slides, setCurrent]);
41
+ function select(key) { setRotating(false); if (key !== current)
42
+ setCurrent(key); }
43
+ function move(intent) { select(getCarouselNavigationTarget(descriptor, intent)); }
44
+ const rotationRequested = rotating && !last && !theme?.environment.reducedMotion;
45
+ return _jsxs("div", { ...props, ref: ref, role: "region", "aria-label": label, "aria-roledescription": "carousel", className: classNames("hjm-carousel", className), "data-large-text": isLargeTextScale(theme?.environment.textScale ?? 1), onFocusCapture: (event) => {
46
+ // APG: leaving focus must not silently restart rotation. Explicit resume is required.
47
+ if (!event.currentTarget.contains(event.relatedTarget))
48
+ setRotating(false);
49
+ onFocusCapture?.(event);
50
+ }, onMouseEnter: (event) => { setHovered(true); onMouseEnter?.(event); }, onMouseLeave: (event) => { setHovered(false); onMouseLeave?.(event); }, onKeyDown: (event) => {
51
+ onKeyDown?.(event);
52
+ if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey)
53
+ return;
54
+ // Do not steal editing/navigation keys from links, inputs or widgets inside a card.
55
+ if (!(event.target instanceof Element) || !event.target.closest("[data-carousel-controls]"))
56
+ return;
57
+ if (event.key !== "ArrowLeft" && event.key !== "ArrowRight")
58
+ return;
59
+ event.preventDefault();
60
+ move((event.key === "ArrowRight") !== (theme?.environment.direction === "rtl") ? "next" : "previous");
61
+ }, children: [autoplay ? _jsx(Button, { tone: "ghost", className: "hjm-carousel__rotation", disabled: theme?.environment.reducedMotion, onPointerDown: () => { pointerRotation.current = !rotationRequested; }, onPointerCancel: () => { pointerRotation.current = null; }, onClick: (event) => {
62
+ // An abandoned pointer gesture must not override a later keyboard activation.
63
+ const next = event.detail > 0 ? pointerRotation.current ?? !rotationRequested : !rotationRequested;
64
+ pointerRotation.current = null;
65
+ if (next && last)
66
+ setCurrent(slides[0].id);
67
+ setRotating(next);
68
+ }, children: rotationRequested ? labels.pause : labels.resume }) : null, _jsx("div", { className: "hjm-carousel__track", "aria-live": playing ? "off" : "polite", "aria-atomic": false, children: resolved.map((slide) => _jsx("div", { className: "hjm-carousel__slide", role: "group", "aria-roledescription": "slide", "aria-label": slide.accessibleName, hidden: slide.inert, inert: slide.inert, children: renderSlide(slide) }, slide.id)) }), _jsxs("div", { className: "hjm-carousel__controls", "data-carousel-controls": true, role: "group", "aria-label": labels.navigation, children: [_jsx(Button, { tone: "ghost", "aria-disabled": current === slides[0].id, onClick: () => move("previous"), children: labels.previous }), _jsx("div", { className: "hjm-carousel__dots", children: resolved.map((slide) => _jsx("button", { type: "button", "aria-label": slide.accessibleName, "aria-disabled": slide.current, "data-current": slide.current, className: "hjm-carousel__dot", onClick: () => select(slide.id), children: _jsx("span", { "aria-hidden": "true" }) }, slide.id)) }), _jsx(Button, { tone: "ghost", "aria-disabled": last, onClick: () => move("next"), children: labels.next })] })] });
69
+ });
70
+ //# sourceMappingURL=carousel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"carousel.js","sourceRoot":"","sources":["../src/carousel.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef, useState, forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport {\n resolveCarouselDescriptor, getCarouselNavigationTarget, isCarouselAutoplayActive,\n type CarouselSlideDescriptor, type CarouselSelection, type CarouselAutoplayConfig,\n type ComposeCarouselAccessibleName,\n} from \"@hjmds/design-contracts/components/carousel\";\nimport { Button } from \"./actions.js\";\nimport { isLargeTextScale } from \"@hjmds/design-contracts/components/design-system-provider\";\nimport { classNames, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\n\nexport type CarouselLabels = Readonly<{ previous: string; next: string; pause: string; resume: string; navigation: string }>;\nexport type CarouselProps = Omit<HTMLAttributes<HTMLDivElement>, \"children\" | \"onChange\"> & CarouselSelection & Readonly<{\n label: string;\n slides: readonly CarouselSlideDescriptor[];\n renderSlide: (slide: CarouselSlideDescriptor) => ReactNode;\n composeAccessibleName: ComposeCarouselAccessibleName;\n labels: CarouselLabels;\n autoplay?: CarouselAutoplayConfig;\n}>;\n\n/** Finite keyed cards. Hidden slides stay mounted but cannot receive focus. */\nexport const Carousel = forwardRef<HTMLDivElement, CarouselProps>(function Carousel({\n label, slides, renderSlide, composeAccessibleName, labels, autoplay,\n currentKey, defaultCurrentKey, onCurrentKeyChange, className,\n onFocusCapture, onMouseEnter, onMouseLeave, onKeyDown, ...props\n}, ref) {\n const theme = useOptionalHjmTheme();\n const [current, setCurrent] = useControllableState({\n ...(currentKey === undefined ? {} : { value: currentKey }),\n defaultValue: defaultCurrentKey ?? slides[0]?.id ?? \"\",\n ...(onCurrentKeyChange === undefined ? {} : { onChange: onCurrentKeyChange }),\n });\n const descriptor = { slides, currentKey: current, ...(autoplay ? { autoplay } : {}) };\n const resolved = resolveCarouselDescriptor(descriptor, { composeAccessibleName });\n if (!label.trim() || Object.values(labels).some((value) => !value.trim())) throw new TypeError(\"Carousel labels must not be empty\");\n const [rotating, setRotating] = useState(autoplay !== undefined);\n const [hovered, setHovered] = useState(false);\n const [hidden, setHidden] = useState(false);\n const pointerRotation = useRef<boolean | null>(null);\n const last = current === slides.at(-1)!.id;\n const playing = isCarouselAutoplayActive(autoplay, {\n reducedMotion: theme?.environment.reducedMotion ?? false,\n paused: !rotating || hovered || hidden || last,\n });\n useEffect(() => {\n const update = () => setHidden(document.hidden);\n update(); document.addEventListener(\"visibilitychange\", update);\n return () => document.removeEventListener(\"visibilitychange\", update);\n }, []);\n useEffect(() => {\n if (!playing || !autoplay) return;\n const timer = setTimeout(() => setCurrent(getCarouselNavigationTarget(descriptor, \"next\")), autoplay.intervalMs);\n return () => clearTimeout(timer);\n }, [playing, autoplay?.intervalMs, current, slides, setCurrent]);\n function select(key: string) { setRotating(false); if (key !== current) setCurrent(key); }\n function move(intent: \"next\" | \"previous\") { select(getCarouselNavigationTarget(descriptor, intent)); }\n const rotationRequested = rotating && !last && !theme?.environment.reducedMotion;\n return <div {...props} ref={ref} role=\"region\" aria-label={label} aria-roledescription=\"carousel\"\n className={classNames(\"hjm-carousel\", className)} data-large-text={isLargeTextScale(theme?.environment.textScale ?? 1)}\n onFocusCapture={(event) => {\n // APG: leaving focus must not silently restart rotation. Explicit resume is required.\n if (!event.currentTarget.contains(event.relatedTarget)) setRotating(false);\n onFocusCapture?.(event);\n }}\n onMouseEnter={(event) => { setHovered(true); onMouseEnter?.(event); }}\n onMouseLeave={(event) => { setHovered(false); onMouseLeave?.(event); }}\n onKeyDown={(event) => {\n onKeyDown?.(event);\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n // Do not steal editing/navigation keys from links, inputs or widgets inside a card.\n if (!(event.target instanceof Element) || !event.target.closest(\"[data-carousel-controls]\")) return;\n if (event.key !== \"ArrowLeft\" && event.key !== \"ArrowRight\") return;\n event.preventDefault();\n move((event.key === \"ArrowRight\") !== (theme?.environment.direction === \"rtl\") ? \"next\" : \"previous\");\n }}>\n {autoplay ? <Button tone=\"ghost\" className=\"hjm-carousel__rotation\" disabled={theme?.environment.reducedMotion}\n onPointerDown={() => { pointerRotation.current = !rotationRequested; }}\n onPointerCancel={() => { pointerRotation.current = null; }}\n onClick={(event) => {\n // An abandoned pointer gesture must not override a later keyboard activation.\n const next = event.detail > 0 ? pointerRotation.current ?? !rotationRequested : !rotationRequested;\n pointerRotation.current = null;\n if (next && last) setCurrent(slides[0]!.id);\n setRotating(next);\n }}>{rotationRequested ? labels.pause : labels.resume}</Button> : null}\n <div className=\"hjm-carousel__track\" aria-live={playing ? \"off\" : \"polite\"} aria-atomic={false}>\n {resolved.map((slide) => <div key={slide.id} className=\"hjm-carousel__slide\" role=\"group\"\n aria-roledescription=\"slide\" aria-label={slide.accessibleName} hidden={slide.inert} inert={slide.inert}>\n {renderSlide(slide)}\n </div>)}\n </div>\n <div className=\"hjm-carousel__controls\" data-carousel-controls role=\"group\" aria-label={labels.navigation}>\n <Button tone=\"ghost\" aria-disabled={current === slides[0]!.id} onClick={() => move(\"previous\")}>{labels.previous}</Button>\n <div className=\"hjm-carousel__dots\">\n {resolved.map((slide) => <button key={slide.id} type=\"button\" aria-label={slide.accessibleName}\n aria-disabled={slide.current} data-current={slide.current} className=\"hjm-carousel__dot\"\n onClick={() => select(slide.id)}><span aria-hidden=\"true\" /></button>)}\n </div>\n <Button tone=\"ghost\" aria-disabled={last} onClick={() => move(\"next\")}>{labels.next}</Button>\n </div>\n </div>;\n});\n"]}
@@ -0,0 +1,33 @@
1
+ import { type ReactNode } from "react";
2
+ import { type ButtonProps } from "./actions.js";
3
+ export type ClipboardButtonProps = Omit<ButtonProps, "children" | "onClick"> & Readonly<{
4
+ value: string;
5
+ /** Localized copy for both states; the renderer invents neither. */
6
+ labels: Readonly<{
7
+ idle: ReactNode;
8
+ copied: ReactNode;
9
+ }>;
10
+ /** How long the copied state stays, in ms. */
11
+ feedbackDuration?: number;
12
+ onCopy?: (value: string) => void;
13
+ onCopyError?: (error: unknown) => void;
14
+ }>;
15
+ /**
16
+ * Copying is three things products kept re-deriving: the async clipboard call,
17
+ * the temporary "copied" state, and announcing that state to a screen reader.
18
+ * The last one is the part that was always missing — a label that only changes
19
+ * visually tells a non-sighted user nothing happened.
20
+ */
21
+ export declare const ClipboardButton: import("react").ForwardRefExoticComponent<Omit<ButtonProps, "children" | "onClick"> & Readonly<{
22
+ value: string;
23
+ /** Localized copy for both states; the renderer invents neither. */
24
+ labels: Readonly<{
25
+ idle: ReactNode;
26
+ copied: ReactNode;
27
+ }>;
28
+ /** How long the copied state stays, in ms. */
29
+ feedbackDuration?: number;
30
+ onCopy?: (value: string) => void;
31
+ onCopyError?: (error: unknown) => void;
32
+ }> & import("react").RefAttributes<HTMLButtonElement>>;
33
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../src/clipboard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAwD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC7F,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,SAAS,CAAC,GAC1E,QAAQ,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,MAAM,EAAE,QAAQ,CAAC;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,MAAM,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;IACzD,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,CAAC,CAAC;AAIL;;;;;GAKG;AACH,eAAO,MAAM,eAAe;WAjBjB,MAAM;IACb,oEAAoE;YAC5D,QAAQ,CAAC;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,MAAM,EAAE,SAAS,CAAA;KAAE,CAAC;IACxD,8CAA8C;uBAC3B,MAAM;aAChB,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;kBAClB,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;sDAkDzC,CAAC"}
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
3
+ import { Button } from "./actions.js";
4
+ const defaultFeedbackDuration = 2000;
5
+ /**
6
+ * Copying is three things products kept re-deriving: the async clipboard call,
7
+ * the temporary "copied" state, and announcing that state to a screen reader.
8
+ * The last one is the part that was always missing — a label that only changes
9
+ * visually tells a non-sighted user nothing happened.
10
+ */
11
+ export const ClipboardButton = forwardRef(function ClipboardButton({ value, labels, feedbackDuration = defaultFeedbackDuration, onCopy, onCopyError, ...props }, forwardedRef) {
12
+ const [copied, setCopied] = useState(false);
13
+ const timer = useRef(undefined);
14
+ useEffect(() => () => { if (timer.current !== undefined)
15
+ clearTimeout(timer.current); }, []);
16
+ useEffect(() => {
17
+ // A new value makes the previous "copied" claim false: it referred to the
18
+ // old string, and leaving it up tells the user something untrue.
19
+ setCopied(false);
20
+ if (timer.current !== undefined)
21
+ clearTimeout(timer.current);
22
+ }, [value]);
23
+ const copy = useCallback(async () => {
24
+ try {
25
+ await navigator.clipboard.writeText(value);
26
+ setCopied(true);
27
+ onCopy?.(value);
28
+ if (timer.current !== undefined)
29
+ clearTimeout(timer.current);
30
+ timer.current = setTimeout(() => setCopied(false), feedbackDuration);
31
+ }
32
+ catch (error) {
33
+ // Clipboard access can be denied; swallowing that would leave the user
34
+ // believing the value was copied.
35
+ onCopyError?.(error);
36
+ }
37
+ }, [value, feedbackDuration, onCopy, onCopyError]);
38
+ return (_jsxs(_Fragment, { children: [_jsx(Button, { ...props, ref: forwardedRef, onClick: () => { void copy(); }, children: copied ? labels.copied : labels.idle }), _jsx("span", { role: "status", className: "hjm-visually-hidden", children: copied ? labels.copied : "" })] }));
39
+ });
40
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../src/clipboard.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAC7F,OAAO,EAAE,MAAM,EAAoB,MAAM,cAAc,CAAC;AAaxD,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAErC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CACvC,SAAS,eAAe,CACtB,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,GAAG,uBAAuB,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,EAC5F,YAAY;IAEZ,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,CAA4C,SAAS,CAAC,CAAC;IAC3E,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;QAAE,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC7F,SAAS,CAAC,GAAG,EAAE;QACb,0EAA0E;QAC1E,iEAAiE;QACjE,SAAS,CAAC,KAAK,CAAC,CAAC;QACjB,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/D,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAC3C,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;YAChB,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS;gBAAE,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7D,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACvE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,uEAAuE;YACvE,kCAAkC;YAClC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAEnD,OAAO,CACL,8BACE,KAAC,MAAM,OAAK,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,YAClE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAC9B,EAET,eAAM,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,qBAAqB,YAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAQ,IACvF,CACJ,CAAC;AACJ,CAAC,CACF,CAAC","sourcesContent":["import { forwardRef, useCallback, useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { Button, type ButtonProps } from \"./actions.js\";\n\nexport type ClipboardButtonProps = Omit<ButtonProps, \"children\" | \"onClick\"> &\n Readonly<{\n value: string;\n /** Localized copy for both states; the renderer invents neither. */\n labels: Readonly<{ idle: ReactNode; copied: ReactNode }>;\n /** How long the copied state stays, in ms. */\n feedbackDuration?: number;\n onCopy?: (value: string) => void;\n onCopyError?: (error: unknown) => void;\n }>;\n\nconst defaultFeedbackDuration = 2000;\n\n/**\n * Copying is three things products kept re-deriving: the async clipboard call,\n * the temporary \"copied\" state, and announcing that state to a screen reader.\n * The last one is the part that was always missing — a label that only changes\n * visually tells a non-sighted user nothing happened.\n */\nexport const ClipboardButton = forwardRef<HTMLButtonElement, ClipboardButtonProps>(\n function ClipboardButton(\n { value, labels, feedbackDuration = defaultFeedbackDuration, onCopy, onCopyError, ...props },\n forwardedRef,\n ) {\n const [copied, setCopied] = useState(false);\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n useEffect(() => () => { if (timer.current !== undefined) clearTimeout(timer.current); }, []);\n useEffect(() => {\n // A new value makes the previous \"copied\" claim false: it referred to the\n // old string, and leaving it up tells the user something untrue.\n setCopied(false);\n if (timer.current !== undefined) clearTimeout(timer.current);\n }, [value]);\n\n const copy = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n onCopy?.(value);\n if (timer.current !== undefined) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), feedbackDuration);\n } catch (error) {\n // Clipboard access can be denied; swallowing that would leave the user\n // believing the value was copied.\n onCopyError?.(error);\n }\n }, [value, feedbackDuration, onCopy, onCopyError]);\n\n return (\n <>\n <Button {...props} ref={forwardedRef} onClick={() => { void copy(); }}>\n {copied ? labels.copied : labels.idle}\n </Button>\n {/* The state change is announced, not only painted. */}\n <span role=\"status\" className=\"hjm-visually-hidden\">{copied ? labels.copied : \"\"}</span>\n </>\n );\n },\n);\n"]}
@@ -0,0 +1,11 @@
1
+ import { type CollapsibleOpenState } from "@hjmds/design-contracts/components/collapsible";
2
+ import { type ReactNode } from "react";
3
+ export type CollapsibleProps = CollapsibleOpenState & Readonly<{
4
+ /** The control's label; it also tells the user what will appear. */
5
+ trigger: ReactNode;
6
+ children: ReactNode;
7
+ disabled?: boolean;
8
+ className?: string;
9
+ }>;
10
+ export declare const Collapsible: import("react").ForwardRefExoticComponent<CollapsibleProps & import("react").RefAttributes<HTMLDivElement>>;
11
+ //# sourceMappingURL=collapsible.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapsible.d.ts","sourceRoot":"","sources":["../src/collapsible.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAAmD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxF,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GACjD,QAAQ,CAAC;IACP,oEAAoE;IACpE,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEL,eAAO,MAAM,WAAW,6GAyCtB,CAAC"}
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { collapsibleRecipe, validateCollapsibleOpenState, } from "@hjmds/design-contracts/components/collapsible";
3
+ import { forwardRef, useId, useState } from "react";
4
+ import { classNames } from "./internal.js";
5
+ export const Collapsible = forwardRef(function Collapsible({ trigger, children, disabled = false, className, ...openState }, forwardedRef) {
6
+ validateCollapsibleOpenState(openState);
7
+ const controlled = openState.open !== undefined;
8
+ const [internalOpen, setInternalOpen] = useState(openState.defaultOpen ?? false);
9
+ const open = openState.open ?? internalOpen;
10
+ const id = `${useId().replaceAll(":", "")}-collapsible`;
11
+ return (_jsxs("div", { ref: forwardedRef, className: classNames("hjm-collapsible", className), "data-state": open ? "open" : "closed", style: { "--hjm-collapsible-gap": `${collapsibleRecipe.gap}px` }, children: [_jsxs("button", { type: "button", className: "hjm-collapsible__trigger", "aria-expanded": open, "aria-controls": `${id}-content`, disabled: disabled, onClick: () => {
12
+ const next = !open;
13
+ if (!controlled)
14
+ setInternalOpen(next);
15
+ openState.onOpenChange?.(next);
16
+ }, children: [trigger, _jsx("span", { "aria-hidden": "true", className: "hjm-collapsible__marker", children: open ? "▾" : "▸" })] }), open ? (_jsx("div", { id: `${id}-content`, role: "region", className: "hjm-collapsible__content", children: children })) : null] }));
17
+ });
18
+ //# sourceMappingURL=collapsible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collapsible.js","sourceRoot":"","sources":["../src/collapsible.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,iBAAiB,EACjB,4BAA4B,GAE7B,MAAM,gDAAgD,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAsC,MAAM,OAAO,CAAC;AACxF,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAW3C,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAmC,SAAS,WAAW,CAC1F,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,KAAK,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,EAChE,YAAY;IAEZ,4BAA4B,CAAC,SAAiC,CAAC,CAAC;IAChE,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;IAChD,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC;IACjF,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,IAAI,YAAY,CAAC;IAC5C,MAAM,EAAE,GAAG,GAAG,KAAK,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC;IACxD,OAAO,CACL,eACE,GAAG,EAAE,YAAY,EACjB,SAAS,EAAE,UAAU,CAAC,iBAAiB,EAAE,SAAS,CAAC,gBACvC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EACpC,KAAK,EAAE,EAAE,uBAAuB,EAAE,GAAG,iBAAiB,CAAC,GAAG,IAAI,EAAmB,aAEjF,kBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,0BAA0B,mBACrB,IAAI,mBACJ,GAAG,EAAE,UAAU,EAC9B,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,GAAG,EAAE;oBACZ,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC;oBACnB,IAAI,CAAC,UAAU;wBAAE,eAAe,CAAC,IAAI,CAAC,CAAC;oBACvC,SAAS,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC;gBACjC,CAAC,aAEA,OAAO,EACR,8BAAkB,MAAM,EAAC,SAAS,EAAC,yBAAyB,YAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAQ,IAC/E,EAMR,IAAI,CAAC,CAAC,CAAC,CACN,cAAK,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAC,QAAQ,EAAC,SAAS,EAAC,0BAA0B,YAAE,QAAQ,GAAO,CAC9F,CAAC,CAAC,CAAC,IAAI,IACJ,CACP,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import {\n collapsibleRecipe,\n validateCollapsibleOpenState,\n type CollapsibleOpenState,\n} from \"@hjmds/design-contracts/components/collapsible\";\nimport { forwardRef, useId, useState, type CSSProperties, type ReactNode } from \"react\";\nimport { classNames } from \"./internal.js\";\n\nexport type CollapsibleProps = CollapsibleOpenState &\n Readonly<{\n /** The control's label; it also tells the user what will appear. */\n trigger: ReactNode;\n children: ReactNode;\n disabled?: boolean;\n className?: string;\n }>;\n\nexport const Collapsible = forwardRef<HTMLDivElement, CollapsibleProps>(function Collapsible(\n { trigger, children, disabled = false, className, ...openState },\n forwardedRef,\n) {\n validateCollapsibleOpenState(openState as CollapsibleOpenState);\n const controlled = openState.open !== undefined;\n const [internalOpen, setInternalOpen] = useState(openState.defaultOpen ?? false);\n const open = openState.open ?? internalOpen;\n const id = `${useId().replaceAll(\":\", \"\")}-collapsible`;\n return (\n <div\n ref={forwardedRef}\n className={classNames(\"hjm-collapsible\", className)}\n data-state={open ? \"open\" : \"closed\"}\n style={{ \"--hjm-collapsible-gap\": `${collapsibleRecipe.gap}px` } as CSSProperties}\n >\n <button\n type=\"button\"\n className=\"hjm-collapsible__trigger\"\n aria-expanded={open}\n aria-controls={`${id}-content`}\n disabled={disabled}\n onClick={() => {\n const next = !open;\n if (!controlled) setInternalOpen(next);\n openState.onOpenChange?.(next);\n }}\n >\n {trigger}\n <span aria-hidden=\"true\" className=\"hjm-collapsible__marker\">{open ? \"▾\" : \"▸\"}</span>\n </button>\n {/*\n Unmounted when closed rather than visually hidden: content left in the\n tree stays reachable by search and by a screen reader, which contradicts\n what the collapsed state says.\n */}\n {open ? (\n <div id={`${id}-content`} role=\"region\" className=\"hjm-collapsible__content\">{children}</div>\n ) : null}\n </div>\n );\n});\n"]}
@@ -0,0 +1,25 @@
1
+ import { type CommandPaletteActivateHandler, type CommandPaletteDescriptor, type CommandPaletteDismissPolicy, type CommandPaletteOpenChangeDetails, type CommandPaletteQueryState, type CommandPaletteSource } from "@hjmds/design-contracts/components/command-palette";
2
+ import { type ReactNode } from "react";
3
+ import { type OverlayTrigger } from "./modal.js";
4
+ export type CommandPaletteProps<Key extends string = string> = Readonly<{
5
+ descriptor: CommandPaletteDescriptor;
6
+ source: CommandPaletteSource<Key, string>;
7
+ query: string;
8
+ onQueryChange: (query: string) => void;
9
+ onActivate: CommandPaletteActivateHandler<Key>;
10
+ /** Runs after the palette is gone, for a command that opens the next surface. */
11
+ onActivateAfterDismiss?: CommandPaletteActivateHandler<Key>;
12
+ queryState?: CommandPaletteQueryState;
13
+ dismissPolicy?: Partial<CommandPaletteDismissPolicy>;
14
+ open?: boolean;
15
+ defaultOpen?: boolean;
16
+ onOpenChange?: (open: boolean, details: CommandPaletteOpenChangeDetails) => void;
17
+ trigger?: OverlayTrigger;
18
+ renderLeading?: (itemId: Key) => ReactNode;
19
+ portalContainer?: HTMLElement;
20
+ className?: string;
21
+ }>;
22
+ export declare const CommandPalette: <Key extends string = string>(props: CommandPaletteProps<Key> & {
23
+ ref?: React.Ref<HTMLDivElement>;
24
+ }) => React.ReactElement | null;
25
+ //# sourceMappingURL=command-palette.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-palette.d.ts","sourceRoot":"","sources":["../src/command-palette.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,6BAA6B,EAElC,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAEhC,KAAK,+BAA+B,EACpC,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EAC1B,MAAM,oDAAoD,CAAC;AAM5D,OAAO,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAEzG,MAAM,MAAM,mBAAmB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACtE,UAAU,EAAE,wBAAwB,CAAC;IACrC,MAAM,EAAE,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAC/C,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAC5D,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,+BAA+B,KAAK,IAAI,CAAC;IACjF,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,SAAS,CAAC;IAC3C,eAAe,CAAC,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH,eAAO,MAAM,cAAc,EAiKrB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,EAChC,KAAK,EAAE,mBAAmB,CAAC,GAAG,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KAClE,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
@@ -0,0 +1,82 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { canDismissCommandPalette, commandPaletteBehaviorDefaults, commandPaletteRecipe, validateCommandPaletteDescriptor, } from "@hjmds/design-contracts/components/command-palette";
3
+ import { flattenCollectionItems, getCollectionNavigationIntent, getCollectionNavigationTarget, } from "@hjmds/design-contracts/components/collection";
4
+ import { forwardRef, useEffect, useId, useMemo, useRef, useState, } from "react";
5
+ import { classNames, composeRefs } from "./internal.js";
6
+ import { getModalLayer, HjmPortal, renderTrigger, useModalFocus } from "./modal.js";
7
+ export const CommandPalette = forwardRef(function CommandPalette({ descriptor, source, query, onQueryChange, onActivate, onActivateAfterDismiss, queryState, dismissPolicy, open: openProp, defaultOpen, onOpenChange, trigger, renderLeading, portalContainer, className, }, forwardedRef) {
8
+ validateCommandPaletteDescriptor(descriptor);
9
+ const policy = { ...commandPaletteBehaviorDefaults, ...dismissPolicy };
10
+ const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
11
+ const open = openProp ?? internalOpen;
12
+ const items = useMemo(() => flattenCollectionItems(source), [source]);
13
+ const enabled = items.filter((item) => !item.disabled);
14
+ // The query state is Combobox's, so the message to show is its async slice —
15
+ // the palette adds no second loading vocabulary of its own.
16
+ const asyncState = queryState?.asyncState ?? { status: "idle" };
17
+ const [activeId, setActiveId] = useState(null);
18
+ const contentRef = useRef(null);
19
+ const triggerRef = useRef(null);
20
+ const inputRef = useRef(null);
21
+ const id = `${useId().replaceAll(":", "")}-command-palette`;
22
+ const active = enabled.find((item) => item.id === activeId) ?? enabled[0] ?? null;
23
+ useEffect(() => {
24
+ // A new query builds a new result list; the first enabled row becomes active
25
+ // so Enter always has an unambiguous target.
26
+ setActiveId(null);
27
+ }, [query, source]);
28
+ const change = (next, reason) => {
29
+ if (openProp === undefined)
30
+ setInternalOpen(next);
31
+ onOpenChange?.(next, { reason });
32
+ };
33
+ const requestClose = (reason) => {
34
+ if (!canDismissCommandPalette(reason, policy))
35
+ return;
36
+ change(false, reason);
37
+ };
38
+ const activate = (itemId, reason) => {
39
+ onActivate(itemId, reason);
40
+ // Running a command always closes the palette — `activation` is a dismiss
41
+ // reason no policy can veto — and the follow-up surface opens after that.
42
+ change(false, "activation");
43
+ if (onActivateAfterDismiss)
44
+ queueMicrotask(() => onActivateAfterDismiss(itemId, reason));
45
+ };
46
+ useModalFocus({
47
+ active: open,
48
+ contentRef,
49
+ initialFocusRef: inputRef,
50
+ ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),
51
+ onEscape: () => requestClose("escape"),
52
+ });
53
+ const onKeyDown = (event) => {
54
+ const intent = getCollectionNavigationIntent(event.key);
55
+ if (intent) {
56
+ const target = getCollectionNavigationTarget({ items: enabled }, active?.id ?? null, intent, true);
57
+ if (target === undefined)
58
+ return;
59
+ event.preventDefault();
60
+ setActiveId(target);
61
+ return;
62
+ }
63
+ if (event.key === "Enter" && active) {
64
+ event.preventDefault();
65
+ activate(active.id, "keyboard");
66
+ }
67
+ };
68
+ const renderedTrigger = trigger === undefined
69
+ ? null
70
+ : renderTrigger(trigger, triggerRef, open, id, "dialog", () => change(true, "trigger"));
71
+ if (!open)
72
+ return renderedTrigger;
73
+ const rows = (source.sections ?? [{ id: "__all", label: undefined, items: source.items ?? [] }]);
74
+ return (_jsxs(_Fragment, { children: [renderedTrigger, _jsx(HjmPortal, { ...(portalContainer === undefined ? {} : { container: portalContainer }), children: _jsx("div", { className: "hjm-overlay hjm-command-palette-positioner", "data-kind": "command-palette", style: { zIndex: getModalLayer(0) }, onMouseDown: (event) => { if (event.target === event.currentTarget)
75
+ requestClose("outside"); }, children: _jsxs("div", { ref: composeRefs(contentRef, forwardedRef), id: id, role: "dialog", "aria-modal": "true", "aria-label": descriptor.accessibilityLabel, "data-hjm-modal-content": "", className: classNames("hjm-command-palette", className), style: { maxInlineSize: commandPaletteRecipe.content.maxWidth, maxBlockSize: commandPaletteRecipe.content.maxHeight }, children: [_jsx("input", { ref: inputRef, type: "text", role: "combobox", "aria-expanded": true, "aria-controls": `${id}-results`, "aria-activedescendant": active ? `${id}-${active.id}` : undefined, "aria-label": descriptor.accessibilityLabel, placeholder: descriptor.searchPlaceholder, className: "hjm-command-palette__search", value: query, onChange: (event) => onQueryChange(event.target.value), onKeyDown: onKeyDown }), _jsxs("div", { id: `${id}-results`, role: "listbox", "aria-label": descriptor.accessibilityLabel, className: "hjm-command-palette__viewport", children: [asyncState.status === "idle" ? null : (_jsx("p", { className: "hjm-command-palette__state", role: asyncState.status === "error" ? "alert" : "status", children: asyncState.message })), rows.map((section) => (_jsxs("div", { className: "hjm-command-palette__section", role: "group", "aria-label": section.label, children: [section.label ? _jsx("p", { className: "hjm-command-palette__section-label", children: section.label }) : null, section.items.map((item) => (_jsxs("div", { id: `${id}-${item.id}`, role: "option", "aria-selected": item.id === active?.id, "aria-disabled": item.disabled || undefined, className: "hjm-command-palette__item", "data-tone": item.tone, onMouseDown: (event) => {
76
+ event.preventDefault();
77
+ if (!item.disabled)
78
+ activate(item.id, "pointer");
79
+ }, onMouseEnter: () => { if (!item.disabled)
80
+ setActiveId(item.id); }, children: [renderLeading?.(item.id), _jsxs("span", { className: "hjm-command-palette__copy", children: [_jsx("span", { children: item.label }), item.description ? _jsx("span", { className: "hjm-command-palette__description", children: item.description }) : null] }), item.shortcut ? _jsx("kbd", { className: "hjm-command-palette__shortcut", children: item.shortcut }) : null] }, item.id)))] }, section.id)))] })] }) }) })] }));
81
+ });
82
+ //# sourceMappingURL=command-palette.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command-palette.js","sourceRoot":"","sources":["../src/command-palette.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n canDismissCommandPalette,\n commandPaletteBehaviorDefaults,\n commandPaletteRecipe,\n validateCommandPaletteDescriptor,\n type CommandPaletteActivateHandler,\n type CommandPaletteActivateReason,\n type CommandPaletteDescriptor,\n type CommandPaletteDismissPolicy,\n type CommandPaletteDismissReason,\n type CommandPaletteOpenChangeDetails,\n type CommandPaletteQueryState,\n type CommandPaletteSource,\n} from \"@hjmds/design-contracts/components/command-palette\";\nimport {\n flattenCollectionItems,\n getCollectionNavigationIntent,\n getCollectionNavigationTarget,\n} from \"@hjmds/design-contracts/components/collection\";\nimport {\n forwardRef,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { classNames, composeRefs } from \"./internal.js\";\nimport { getModalLayer, HjmPortal, renderTrigger, useModalFocus, type OverlayTrigger } from \"./modal.js\";\n\nexport type CommandPaletteProps<Key extends string = string> = Readonly<{\n descriptor: CommandPaletteDescriptor;\n source: CommandPaletteSource<Key, string>;\n query: string;\n onQueryChange: (query: string) => void;\n onActivate: CommandPaletteActivateHandler<Key>;\n /** Runs after the palette is gone, for a command that opens the next surface. */\n onActivateAfterDismiss?: CommandPaletteActivateHandler<Key>;\n queryState?: CommandPaletteQueryState;\n dismissPolicy?: Partial<CommandPaletteDismissPolicy>;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean, details: CommandPaletteOpenChangeDetails) => void;\n trigger?: OverlayTrigger;\n renderLeading?: (itemId: Key) => ReactNode;\n portalContainer?: HTMLElement;\n className?: string;\n}>;\n\nexport const CommandPalette = forwardRef(function CommandPalette<Key extends string = string>(\n {\n descriptor,\n source,\n query,\n onQueryChange,\n onActivate,\n onActivateAfterDismiss,\n queryState,\n dismissPolicy,\n open: openProp,\n defaultOpen,\n onOpenChange,\n trigger,\n renderLeading,\n portalContainer,\n className,\n }: CommandPaletteProps<Key>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n validateCommandPaletteDescriptor(descriptor);\n const policy: CommandPaletteDismissPolicy = { ...commandPaletteBehaviorDefaults, ...dismissPolicy };\n const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);\n const open = openProp ?? internalOpen;\n const items = useMemo(() => flattenCollectionItems<Key, string>(source), [source]);\n const enabled = items.filter((item) => !item.disabled);\n // The query state is Combobox's, so the message to show is its async slice —\n // the palette adds no second loading vocabulary of its own.\n const asyncState = queryState?.asyncState ?? { status: \"idle\" as const };\n const [activeId, setActiveId] = useState<Key | null>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n const id = `${useId().replaceAll(\":\", \"\")}-command-palette`;\n const active = enabled.find((item) => item.id === activeId) ?? enabled[0] ?? null;\n\n useEffect(() => {\n // A new query builds a new result list; the first enabled row becomes active\n // so Enter always has an unambiguous target.\n setActiveId(null);\n }, [query, source]);\n\n const change = (next: boolean, reason: CommandPaletteOpenChangeDetails[\"reason\"]) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next, { reason });\n };\n const requestClose = (reason: CommandPaletteDismissReason) => {\n if (!canDismissCommandPalette(reason, policy)) return;\n change(false, reason);\n };\n const activate = (itemId: Key, reason: CommandPaletteActivateReason) => {\n onActivate(itemId, reason);\n // Running a command always closes the palette — `activation` is a dismiss\n // reason no policy can veto — and the follow-up surface opens after that.\n change(false, \"activation\");\n if (onActivateAfterDismiss) queueMicrotask(() => onActivateAfterDismiss(itemId, reason));\n };\n\n useModalFocus({\n active: open,\n contentRef,\n initialFocusRef: inputRef,\n ...(trigger === undefined ? {} : { fallbackReturnRef: triggerRef }),\n onEscape: () => requestClose(\"escape\"),\n });\n\n const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {\n const intent = getCollectionNavigationIntent(event.key as never);\n if (intent) {\n const target = getCollectionNavigationTarget({ items: enabled }, active?.id ?? null, intent, true);\n if (target === undefined) return;\n event.preventDefault();\n setActiveId(target as Key);\n return;\n }\n if (event.key === \"Enter\" && active) {\n event.preventDefault();\n activate(active.id as Key, \"keyboard\");\n }\n };\n\n const renderedTrigger = trigger === undefined\n ? null\n : renderTrigger(trigger, triggerRef, open, id, \"dialog\", () => change(true, \"trigger\"));\n if (!open) return renderedTrigger;\n\n const rows = (source.sections ?? [{ id: \"__all\", label: undefined, items: source.items ?? [] }]) as readonly {\n id: string; label?: string; items: readonly (typeof items)[number][];\n }[];\n return (\n <>\n {renderedTrigger}\n <HjmPortal {...(portalContainer === undefined ? {} : { container: portalContainer })}>\n <div\n className=\"hjm-overlay hjm-command-palette-positioner\"\n data-kind=\"command-palette\"\n style={{ zIndex: getModalLayer(0) }}\n onMouseDown={(event) => { if (event.target === event.currentTarget) requestClose(\"outside\"); }}\n >\n <div\n ref={composeRefs(contentRef, forwardedRef)}\n id={id}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={descriptor.accessibilityLabel}\n data-hjm-modal-content=\"\"\n className={classNames(\"hjm-command-palette\", className)}\n style={{ maxInlineSize: commandPaletteRecipe.content.maxWidth, maxBlockSize: commandPaletteRecipe.content.maxHeight } as CSSProperties}\n >\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded\n aria-controls={`${id}-results`}\n aria-activedescendant={active ? `${id}-${active.id}` : undefined}\n aria-label={descriptor.accessibilityLabel}\n placeholder={descriptor.searchPlaceholder}\n className=\"hjm-command-palette__search\"\n value={query}\n onChange={(event) => onQueryChange(event.target.value)}\n onKeyDown={onKeyDown}\n />\n <div id={`${id}-results`} role=\"listbox\" aria-label={descriptor.accessibilityLabel} className=\"hjm-command-palette__viewport\">\n {asyncState.status === \"idle\" ? null : (\n <p className=\"hjm-command-palette__state\" role={asyncState.status === \"error\" ? \"alert\" : \"status\"}>{asyncState.message}</p>\n )}\n {rows.map((section) => (\n <div key={section.id} className=\"hjm-command-palette__section\" role=\"group\" aria-label={section.label}>\n {section.label ? <p className=\"hjm-command-palette__section-label\">{section.label}</p> : null}\n {section.items.map((item) => (\n <div\n key={item.id}\n id={`${id}-${item.id}`}\n role=\"option\"\n aria-selected={item.id === active?.id}\n aria-disabled={item.disabled || undefined}\n className=\"hjm-command-palette__item\"\n data-tone={item.tone}\n onMouseDown={(event) => {\n event.preventDefault();\n if (!item.disabled) activate(item.id as Key, \"pointer\");\n }}\n onMouseEnter={() => { if (!item.disabled) setActiveId(item.id as Key); }}\n >\n {renderLeading?.(item.id as Key)}\n <span className=\"hjm-command-palette__copy\">\n <span>{item.label}</span>\n {item.description ? <span className=\"hjm-command-palette__description\">{item.description}</span> : null}\n </span>\n {item.shortcut ? <kbd className=\"hjm-command-palette__shortcut\">{item.shortcut}</kbd> : null}\n </div>\n ))}\n </div>\n ))}\n </div>\n </div>\n </div>\n </HjmPortal>\n </>\n );\n}) as <Key extends string = string>(\n props: CommandPaletteProps<Key> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
@@ -0,0 +1,12 @@
1
+ import { type ContextMenuItemDescriptor } from "@hjmds/design-contracts/components/context-menu";
2
+ import { type ReactNode } from "react";
3
+ export type ContextMenuProps<Key extends string = string> = Readonly<{
4
+ /** The region the menu belongs to; right-click and long-press are bound here. */
5
+ children: ReactNode;
6
+ items: readonly ContextMenuItemDescriptor<Key>[];
7
+ accessibilityLabel: string;
8
+ onAction: (id: Key) => void;
9
+ className?: string;
10
+ }>;
11
+ export declare function ContextMenu<Key extends string = string>({ children, items, accessibilityLabel, onAction, className, }: ContextMenuProps<Key>): import("react").JSX.Element;
12
+ //# sourceMappingURL=context-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-menu.d.ts","sourceRoot":"","sources":["../src/context-menu.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,yBAAyB,EAE/B,MAAM,iDAAiD,CAAC;AAEzD,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,MAAM,MAAM,gBAAgB,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IACnE,iFAAiF;IACjF,QAAQ,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,SAAS,yBAAyB,CAAC,GAAG,CAAC,EAAE,CAAC;IACjD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAIH,wBAAgB,WAAW,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,EAAE,EACvD,QAAQ,EACR,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,SAAS,GACV,EAAE,gBAAgB,CAAC,GAAG,CAAC,+BAyIvB"}