@hjmds/react 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +4 -0
  2. package/THIRD_PARTY_NOTICES.md +29 -0
  3. package/dist/affix.d.ts +10 -0
  4. package/dist/affix.d.ts.map +1 -0
  5. package/dist/affix.js +51 -0
  6. package/dist/affix.js.map +1 -0
  7. package/dist/carousel-motion.d.ts +13 -0
  8. package/dist/carousel-motion.d.ts.map +1 -0
  9. package/dist/carousel-motion.js +35 -0
  10. package/dist/carousel-motion.js.map +1 -0
  11. package/dist/celebration.d.ts +8 -0
  12. package/dist/celebration.d.ts.map +1 -0
  13. package/dist/celebration.js +51 -0
  14. package/dist/celebration.js.map +1 -0
  15. package/dist/color-picker.d.ts +18 -0
  16. package/dist/color-picker.d.ts.map +1 -0
  17. package/dist/color-picker.js +40 -0
  18. package/dist/color-picker.js.map +1 -0
  19. package/dist/content-transition.d.ts +18 -0
  20. package/dist/content-transition.d.ts.map +1 -0
  21. package/dist/content-transition.js +33 -0
  22. package/dist/content-transition.js.map +1 -0
  23. package/dist/date-picker.d.ts.map +1 -1
  24. package/dist/date-picker.js +39 -1
  25. package/dist/date-picker.js.map +1 -1
  26. package/dist/evidence.d.ts +46 -1
  27. package/dist/evidence.d.ts.map +1 -1
  28. package/dist/evidence.js +5 -2
  29. package/dist/evidence.js.map +1 -1
  30. package/dist/menu-morph.d.ts.map +1 -1
  31. package/dist/menu-morph.js +2 -1
  32. package/dist/menu-morph.js.map +1 -1
  33. package/dist/menubar.d.ts.map +1 -1
  34. package/dist/menubar.js +24 -15
  35. package/dist/menubar.js.map +1 -1
  36. package/dist/modal.d.ts +1 -0
  37. package/dist/modal.d.ts.map +1 -1
  38. package/dist/modal.js.map +1 -1
  39. package/dist/overlays.d.ts.map +1 -1
  40. package/dist/overlays.js +33 -2
  41. package/dist/overlays.js.map +1 -1
  42. package/dist/sortable.d.ts +14 -0
  43. package/dist/sortable.d.ts.map +1 -0
  44. package/dist/sortable.js +87 -0
  45. package/dist/sortable.js.map +1 -0
  46. package/dist/styles.css +30 -1
  47. package/dist/styles.layered.css +30 -1
  48. package/dist/swipe-actions.d.ts +13 -0
  49. package/dist/swipe-actions.d.ts.map +1 -0
  50. package/dist/swipe-actions.js +25 -0
  51. package/dist/swipe-actions.js.map +1 -0
  52. package/dist/tree.d.ts.map +1 -1
  53. package/dist/tree.js +14 -3
  54. package/dist/tree.js.map +1 -1
  55. package/dist/watermark.d.ts +12 -0
  56. package/dist/watermark.d.ts.map +1 -0
  57. package/dist/watermark.js +10 -0
  58. package/dist/watermark.js.map +1 -0
  59. package/package.json +65 -4
package/dist/styles.css CHANGED
@@ -343,7 +343,7 @@
343
343
  .hjm-field__affix { display: inline-flex; align-items: center; color: var(--hjm-color-text-muted); }
344
344
  .hjm-field__description, .hjm-choice__description { color: var(--hjm-color-text-muted); font-size: var(--hjm-type-label-size); line-height: var(--hjm-type-label-line-height); }
345
345
  .hjm-field__error { color: var(--hjm-color-danger); font-size: var(--hjm-type-label-size); line-height: var(--hjm-type-label-line-height); }
346
- .hjm-search-field__clear { inline-size: 32px; block-size: 32px; display: inline-grid; place-items: center; border: 0; border-radius: 50%; color: inherit; background: transparent; font: inherit; cursor: pointer; }
346
+ .hjm-search-field__clear { inline-size: var(--hjm-control-min-touch-target); block-size: var(--hjm-control-min-touch-target); display: inline-grid; place-items: center; border: 0; border-radius: 50%; color: inherit; background: transparent; font: inherit; cursor: pointer; }
347
347
  .hjm-search-field__spinner { inline-size: 32px; block-size: 32px; display: inline-grid; place-items: center; color: var(--hjm-color-content-brand); }
348
348
  .hjm-search-field__spinner:empty::before { content: ""; inline-size: 1em; block-size: 1em; border: 2px solid currentColor; border-inline-end-color: transparent; border-radius: 50%; animation: hjm-spin 800ms linear infinite; }
349
349
  .hjm-password-field--large .hjm-field__control { min-block-size: var(--hjm-control-button-large); padding-inline: var(--hjm-space-lg); }
@@ -1113,6 +1113,9 @@ button.hjm-list-row, a.hjm-list-row { cursor: pointer; }
1113
1113
  color: var(--hjm-color-on-primary);
1114
1114
  font-size: var(--hjm-type-label-size);
1115
1115
  }
1116
+ /* 44px hit slop without growing the mark (2026-09-30 audit WR-0930-5/7) */
1117
+ .hjm-data-table__check, .hjm-data-table__sort, .hjm-transfer-list__select-all { position: relative; }
1118
+ .hjm-data-table__check::after, .hjm-data-table__sort::after, .hjm-transfer-list__select-all::after { content: ""; position: absolute; inset: calc(50% - var(--hjm-control-min-touch-target) / 2); }
1116
1119
  .hjm-data-table__check[aria-checked="true"],
1117
1120
  .hjm-data-table__check[aria-checked="mixed"] { border-color: var(--hjm-color-primary); background: var(--hjm-color-primary); }
1118
1121
  .hjm-data-table__sort:focus-visible,
@@ -2595,3 +2598,29 @@ button.hjm-list-row, a.hjm-list-row { cursor: pointer; }
2595
2598
  .hjm-menu-morph__item:hover, .hjm-menu-morph__item:focus { background: var(--hjm-color-bg); }
2596
2599
  .hjm-menu-morph__item[data-tone="danger"] { color: var(--hjm-color-danger); }
2597
2600
  .hjm-menu-morph__item:disabled { opacity: 0.5; cursor: not-allowed; }
2601
+
2602
+ /* Selected colors are user data; control surfaces keep the same canvas tokens as other inputs. */
2603
+ .hjm-color-picker { border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); background: var(--hjm-color-bg); color: var(--hjm-color-text); padding: 1rem; margin: 0; min-inline-size: 0; overflow-wrap: anywhere; }
2604
+ .hjm-color-picker legend { max-inline-size: 100%; overflow-wrap: anywhere; }
2605
+ .hjm-color-picker__row { display: flex; flex-wrap: wrap; gap: 0.75rem; }
2606
+ .hjm-color-picker label { display: flex; flex-direction: column; gap: 0.5rem; min-inline-size: 0; }
2607
+ .hjm-color-picker__hex { flex: 1 1 10rem; }
2608
+ .hjm-color-picker input { box-sizing: border-box; min-block-size: var(--hjm-color-picker-target); min-inline-size: 0; max-inline-size: 100%; border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); background: var(--hjm-color-bg); color: var(--hjm-color-text); font: inherit; }
2609
+ .hjm-color-picker input[type="text"] { inline-size: 100%; padding-inline: 0.5rem; }
2610
+ .hjm-color-picker input[type="color"] { inline-size: 3rem; padding: 0.25rem; }
2611
+ .hjm-color-picker__opacity { margin-block-start: 0.75rem; }
2612
+ .hjm-color-picker__opacity input[type="range"] { accent-color: var(--hjm-color-primary); }
2613
+ .hjm-color-picker__preview { block-size: 1.5rem; margin-block-start: 0.75rem; border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); }
2614
+ .hjm-color-picker__presets { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-block-start: 0.75rem; }
2615
+ .hjm-color-picker__presets button { display: inline-flex; align-items: center; gap: 0.5rem; min-block-size: var(--hjm-color-picker-target); max-inline-size: 100%; overflow-wrap: anywhere; border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); padding: 0.5rem; background: var(--hjm-color-bg); color: var(--hjm-color-text); font: inherit; cursor: pointer; }
2616
+ .hjm-color-picker__presets button[aria-pressed="true"] { border-color: var(--hjm-color-primary); }
2617
+ .hjm-color-picker__swatch { inline-size: 1rem; block-size: 1rem; flex-shrink: 0; border: 1px solid var(--hjm-color-border); }
2618
+ .hjm-color-picker :is(input, button):focus-visible { outline: 2px solid var(--hjm-color-primary); outline-offset: 2px; }
2619
+ .hjm-color-picker__error { color: var(--hjm-color-danger); margin-block: 0.5rem; }
2620
+ .hjm-color-picker:disabled { opacity: 0.5; }
2621
+ /* SVG labels are decorative and print with the content; neither selection nor pointer input is intercepted. */
2622
+ .hjm-watermark { position: relative; isolation: isolate; }
2623
+ .hjm-watermark__content { position: relative; z-index: 0; overflow-wrap: anywhere; }
2624
+ .hjm-watermark__overlay { position: absolute; inset: 0; z-index: 1; pointer-events: none; user-select: none; overflow: hidden; color: var(--hjm-color-text-sub); font: inherit; }
2625
+ .hjm-affix { color: var(--hjm-color-text); background: var(--hjm-color-bg); overflow-wrap: anywhere; }
2626
+ .hjm-affix__marker { display: block; block-size: 0; pointer-events: none; }
@@ -344,7 +344,7 @@
344
344
  .hjm-field__affix { display: inline-flex; align-items: center; color: var(--hjm-color-text-muted); }
345
345
  .hjm-field__description, .hjm-choice__description { color: var(--hjm-color-text-muted); font-size: var(--hjm-type-label-size); line-height: var(--hjm-type-label-line-height); }
346
346
  .hjm-field__error { color: var(--hjm-color-danger); font-size: var(--hjm-type-label-size); line-height: var(--hjm-type-label-line-height); }
347
- .hjm-search-field__clear { inline-size: 32px; block-size: 32px; display: inline-grid; place-items: center; border: 0; border-radius: 50%; color: inherit; background: transparent; font: inherit; cursor: pointer; }
347
+ .hjm-search-field__clear { inline-size: var(--hjm-control-min-touch-target); block-size: var(--hjm-control-min-touch-target); display: inline-grid; place-items: center; border: 0; border-radius: 50%; color: inherit; background: transparent; font: inherit; cursor: pointer; }
348
348
  .hjm-search-field__spinner { inline-size: 32px; block-size: 32px; display: inline-grid; place-items: center; color: var(--hjm-color-content-brand); }
349
349
  .hjm-search-field__spinner:empty::before { content: ""; inline-size: 1em; block-size: 1em; border: 2px solid currentColor; border-inline-end-color: transparent; border-radius: 50%; animation: hjm-spin 800ms linear infinite; }
350
350
  .hjm-password-field--large .hjm-field__control { min-block-size: var(--hjm-control-button-large); padding-inline: var(--hjm-space-lg); }
@@ -1114,6 +1114,9 @@ button.hjm-list-row, a.hjm-list-row { cursor: pointer; }
1114
1114
  color: var(--hjm-color-on-primary);
1115
1115
  font-size: var(--hjm-type-label-size);
1116
1116
  }
1117
+ /* 44px hit slop without growing the mark (2026-09-30 audit WR-0930-5/7) */
1118
+ .hjm-data-table__check, .hjm-data-table__sort, .hjm-transfer-list__select-all { position: relative; }
1119
+ .hjm-data-table__check::after, .hjm-data-table__sort::after, .hjm-transfer-list__select-all::after { content: ""; position: absolute; inset: calc(50% - var(--hjm-control-min-touch-target) / 2); }
1117
1120
  .hjm-data-table__check[aria-checked="true"],
1118
1121
  .hjm-data-table__check[aria-checked="mixed"] { border-color: var(--hjm-color-primary); background: var(--hjm-color-primary); }
1119
1122
  .hjm-data-table__sort:focus-visible,
@@ -2597,4 +2600,30 @@ button.hjm-list-row, a.hjm-list-row { cursor: pointer; }
2597
2600
  .hjm-menu-morph__item[data-tone="danger"] { color: var(--hjm-color-danger); }
2598
2601
  .hjm-menu-morph__item:disabled { opacity: 0.5; cursor: not-allowed; }
2599
2602
 
2603
+ /* Selected colors are user data; control surfaces keep the same canvas tokens as other inputs. */
2604
+ .hjm-color-picker { border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); background: var(--hjm-color-bg); color: var(--hjm-color-text); padding: 1rem; margin: 0; min-inline-size: 0; overflow-wrap: anywhere; }
2605
+ .hjm-color-picker legend { max-inline-size: 100%; overflow-wrap: anywhere; }
2606
+ .hjm-color-picker__row { display: flex; flex-wrap: wrap; gap: 0.75rem; }
2607
+ .hjm-color-picker label { display: flex; flex-direction: column; gap: 0.5rem; min-inline-size: 0; }
2608
+ .hjm-color-picker__hex { flex: 1 1 10rem; }
2609
+ .hjm-color-picker input { box-sizing: border-box; min-block-size: var(--hjm-color-picker-target); min-inline-size: 0; max-inline-size: 100%; border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); background: var(--hjm-color-bg); color: var(--hjm-color-text); font: inherit; }
2610
+ .hjm-color-picker input[type="text"] { inline-size: 100%; padding-inline: 0.5rem; }
2611
+ .hjm-color-picker input[type="color"] { inline-size: 3rem; padding: 0.25rem; }
2612
+ .hjm-color-picker__opacity { margin-block-start: 0.75rem; }
2613
+ .hjm-color-picker__opacity input[type="range"] { accent-color: var(--hjm-color-primary); }
2614
+ .hjm-color-picker__preview { block-size: 1.5rem; margin-block-start: 0.75rem; border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); }
2615
+ .hjm-color-picker__presets { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-block-start: 0.75rem; }
2616
+ .hjm-color-picker__presets button { display: inline-flex; align-items: center; gap: 0.5rem; min-block-size: var(--hjm-color-picker-target); max-inline-size: 100%; overflow-wrap: anywhere; border: 1px solid var(--hjm-color-border); border-radius: var(--hjm-radius-md); padding: 0.5rem; background: var(--hjm-color-bg); color: var(--hjm-color-text); font: inherit; cursor: pointer; }
2617
+ .hjm-color-picker__presets button[aria-pressed="true"] { border-color: var(--hjm-color-primary); }
2618
+ .hjm-color-picker__swatch { inline-size: 1rem; block-size: 1rem; flex-shrink: 0; border: 1px solid var(--hjm-color-border); }
2619
+ .hjm-color-picker :is(input, button):focus-visible { outline: 2px solid var(--hjm-color-primary); outline-offset: 2px; }
2620
+ .hjm-color-picker__error { color: var(--hjm-color-danger); margin-block: 0.5rem; }
2621
+ .hjm-color-picker:disabled { opacity: 0.5; }
2622
+ /* SVG labels are decorative and print with the content; neither selection nor pointer input is intercepted. */
2623
+ .hjm-watermark { position: relative; isolation: isolate; }
2624
+ .hjm-watermark__content { position: relative; z-index: 0; overflow-wrap: anywhere; }
2625
+ .hjm-watermark__overlay { position: absolute; inset: 0; z-index: 1; pointer-events: none; user-select: none; overflow: hidden; color: var(--hjm-color-text-sub); font: inherit; }
2626
+ .hjm-affix { color: var(--hjm-color-text); background: var(--hjm-color-bg); overflow-wrap: anywhere; }
2627
+ .hjm-affix__marker { display: block; block-size: 0; pointer-events: none; }
2628
+
2600
2629
  }
@@ -0,0 +1,13 @@
1
+ import { type ReactNode } from "react";
2
+ import { type RowAction } from "@hjmds/design-contracts/components/interaction-adapters";
3
+ export type SwipeActionsProps = {
4
+ children: ReactNode;
5
+ label: string;
6
+ actions: readonly RowAction[];
7
+ busy?: boolean;
8
+ onAction(id: string): void | Promise<void>;
9
+ onError(error: unknown): void;
10
+ };
11
+ /** Desktop exposes the same actions directly; no hidden swipe-only functionality. */
12
+ export declare function SwipeActions({ children, label, actions, busy, onAction, onError }: SwipeActionsProps): import("react").JSX.Element;
13
+ //# sourceMappingURL=swipe-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swipe-actions.d.ts","sourceRoot":"","sources":["../src/swipe-actions.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,yDAAyD,CAAC;AAG1G,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAClF,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3E,CAAC;AACF,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,iBAAiB,+BAcpG"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import { validateActions } from "@hjmds/design-contracts/components/interaction-adapters";
4
+ import { Button } from "./actions.js";
5
+ /** Desktop exposes the same actions directly; no hidden swipe-only functionality. */
6
+ export function SwipeActions({ children, label, actions, busy, onAction, onError }) {
7
+ validateActions(actions);
8
+ const pending = useRef(false);
9
+ async function run(action) {
10
+ if (busy || pending.current || action.disabled)
11
+ return;
12
+ pending.current = true;
13
+ try {
14
+ await onAction(action.id);
15
+ }
16
+ catch (error) {
17
+ onError(error);
18
+ }
19
+ finally {
20
+ pending.current = false;
21
+ }
22
+ }
23
+ return _jsxs("div", { role: "group", "aria-label": label, "aria-busy": busy, children: [children, _jsx("div", { style: { display: "flex", gap: "var(--hjm-space-xs)", flexWrap: "wrap" }, children: actions.map(action => _jsx(Button, { tone: action.intent === "danger" ? "danger" : "ghost", disabled: Boolean(busy || action.disabled), onClick: () => void run(action), children: action.label }, action.id)) })] });
24
+ }
25
+ //# sourceMappingURL=swipe-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swipe-actions.js","sourceRoot":"","sources":["../src/swipe-actions.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAkB,MAAM,yDAAyD,CAAC;AAC1G,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAMtC,qFAAqF;AACrF,MAAM,UAAU,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAqB;IACnG,eAAe,CAAC,OAAO,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,KAAK,UAAU,GAAG,CAAC,MAAiB;QAClC,IAAI,IAAI,IAAI,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ;YAAE,OAAO;QACvD,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC;YAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAAC,CAAC;gBAAS,CAAC;YAAC,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC;QAAC,CAAC;IAC3G,CAAC;IACD,OAAO,eAAK,IAAI,EAAC,OAAO,gBAAa,KAAK,eAAa,IAAI,aACxD,QAAQ,EAAC,cAAK,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,EAAE,YACpF,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,KAAC,MAAM,IAAiB,IAAI,EAAE,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAClG,QAAQ,EAAE,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,YAAG,MAAM,CAAC,KAAK,IADxD,MAAM,CAAC,EAAE,CACyD,CAAC,GACnG,IACF,CAAC;AACT,CAAC","sourcesContent":["import { useRef, type ReactNode } from \"react\";\nimport { validateActions, type RowAction } from \"@hjmds/design-contracts/components/interaction-adapters\";\nimport { Button } from \"./actions.js\";\n\nexport type SwipeActionsProps = {\n children: ReactNode; label: string; actions: readonly RowAction[]; busy?: boolean;\n onAction(id: string): void | Promise<void>; onError(error: unknown): void;\n};\n/** Desktop exposes the same actions directly; no hidden swipe-only functionality. */\nexport function SwipeActions({ children, label, actions, busy, onAction, onError }: SwipeActionsProps) {\n validateActions(actions);\n const pending = useRef(false);\n async function run(action: RowAction) {\n if (busy || pending.current || action.disabled) return;\n pending.current = true;\n try { await onAction(action.id); } catch (error) { onError(error); } finally { pending.current = false; }\n }\n return <div role=\"group\" aria-label={label} aria-busy={busy}>\n {children}<div style={{ display: \"flex\", gap: \"var(--hjm-space-xs)\", flexWrap: \"wrap\" }}>\n {actions.map(action => <Button key={action.id} tone={action.intent === \"danger\" ? \"danger\" : \"ghost\"}\n disabled={Boolean(busy || action.disabled)} onClick={() => void run(action)}>{action.label}</Button>)}\n </div>\n </div>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAE9B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;IACzC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,YAAY,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/B,mBAAmB,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACtC,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IACvD,SAAS,CAAC,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,WAAW,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,sFAAsF;IACtF,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,SAAS,CAAC;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAaH,eAAO,MAAM,IAAI,EAuJX,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC/B,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KACvD,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,yBAAyB,EAE9B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACxB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,MAAM,MAAM,SAAS,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,SAAS,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;IACzC,qBAAqB,EAAE,yBAAyB,CAAC;IACjD,YAAY,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IAC/B,mBAAmB,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACtC,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;IACvD,SAAS,CAAC,EAAE,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,WAAW,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC/C,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;IACnC,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,sFAAsF;IACtF,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,KAAK,SAAS,CAAC;IACrE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAaH,eAAO,MAAM,IAAI,EA8JX,CAAC,EAAE,SAAS,MAAM,GAAG,MAAM,EAC/B,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;CAAE,KACvD,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC"}
package/dist/tree.js CHANGED
@@ -107,14 +107,25 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
107
107
  return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), role: "tree", "aria-label": label, "aria-busy": asyncState.status === "loading" || asyncState.status === "loadingMore" || undefined, "aria-multiselectable": selection?.mode === "multiple" || undefined, className: classNames("hjm-tree", className), style: { "--hjm-tree-indent": `${treeRecipe.indentPerLevel}px` }, children: [asyncState.status === "empty" || asyncState.status === "error" || asyncState.status === "loading" ? (_jsx("p", { className: "hjm-tree__state", role: asyncState.status === "error" ? "alert" : "status", children: asyncState.message })) : null, visible.map((node) => (_jsxs("div", { role: "treeitem", "data-hjm-tree-node": node.id, "aria-level": node.depth, "aria-posinset": node.position, "aria-setsize": node.siblingCount, "aria-expanded": node.hasChildren ? node.expanded : undefined, "aria-selected": selection && selection.mode !== "none" ? selected.has(node.id) : undefined, "aria-checked": checkedStates ? (checkedStates.get(node.id) === "mixed" ? "mixed" : String(checkedStates.get(node.id) === true)) : undefined, "aria-disabled": node.disabled || undefined, "aria-label": node.accessibleName,
108
108
  // One tab stop for the whole tree; the glyph is decorative, not a
109
109
  // nested control, so a node is the only focusable thing in a row.
110
- tabIndex: node.id === activeId ? 0 : -1, className: "hjm-tree__node", style: { "--hjm-tree-depth": node.depth - 1 }, onClick: () => {
110
+ tabIndex: node.id === activeId ? 0 : -1, className: "hjm-tree__node", style: { "--hjm-tree-depth": node.depth - 1 }, onClick: (event) => {
111
111
  setFocusedId(node.id);
112
+ // Pointer users had no way to expand: only arrow keys toggled (2026-09-30
113
+ // audit WR-0930-2). The contract lets a row click change expansion; the
114
+ // glyph toggles only, and in check mode the row click stays the check.
115
+ const onGlyph = node.hasChildren &&
116
+ event.target.closest?.(".hjm-tree__toggle") !== null;
117
+ if (onGlyph) {
118
+ setExpanded(node.id, !node.expanded);
119
+ return;
120
+ }
112
121
  if (checkedStates && onCheckedToggle) {
113
122
  if (!node.disabled)
114
123
  onCheckedToggle(node.id);
124
+ return;
115
125
  }
116
- else
117
- toggleSelection(node);
126
+ if (node.hasChildren)
127
+ setExpanded(node.id, !node.expanded);
128
+ toggleSelection(node);
118
129
  }, onFocus: () => setFocusedId(node.id), onKeyDown: (event) => onKeyDown(event, node), children: [_jsx("span", { "aria-hidden": "true", className: "hjm-tree__indent" }), checkedStates ? (_jsx("span", { "aria-hidden": "true", className: "hjm-tree__check", "data-state": String(checkedStates.get(node.id) ?? false), children: checkedStates.get(node.id) === true ? "✓" : checkedStates.get(node.id) === "mixed" ? "–" : "" })) : null, _jsx("span", { "aria-hidden": "true", className: "hjm-tree__toggle", "data-visible": node.hasChildren || undefined, children: node.hasChildren ? renderToggle?.({ expanded: node.expanded }) ?? (node.expanded ? "▾" : "▸") : null }), _jsxs("span", { "aria-hidden": "true", className: "hjm-tree__label", children: [node.label, node.description ? _jsx("span", { className: "hjm-tree__description", children: node.description }) : null] })] }, node.id))), asyncState.status === "loadingMore" ? _jsx("p", { className: "hjm-tree__state", role: "status", children: asyncState.message }) : null] }));
119
130
  });
120
131
  //# sourceMappingURL=tree.js.map
package/dist/tree.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n getTreeArrowKeyIntent,\n getTreeArrowResult,\n getVisibleTreeNavigationTarget,\n getVisibleTreeTypeaheadMatch,\n reconcileTreeExpansion,\n resolveTreeDescriptor,\n treeRecipe,\n type ComposeTreeAccessibleName,\n type ResolvedTreeNodeDescriptor,\n type TreeAsyncState,\n type TreeNodeDescriptor,\n type TreeSelectionModel,\n} from \"@hjmds/design-contracts/components/tree\";\nimport type { CheckboxState, WebKeyboardKey } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\n\nexport type TreeProps<Id extends string = string> = Readonly<{\n label: string;\n nodes: readonly TreeNodeDescriptor<Id>[];\n composeAccessibleName: ComposeTreeAccessibleName;\n expandedKeys?: ReadonlySet<Id>;\n defaultExpandedKeys?: ReadonlySet<Id>;\n onExpandedKeysChange?: (keys: ReadonlySet<Id>) => void;\n selection?: TreeSelectionModel<Id>;\n /**\n * Tri-state check marks per node, as `resolveTreeCheckedStates` derives them.\n * Checkboxes are not nested controls in a tree: the state rides on the node\n * itself as `aria-checked`, keeping the contract's one-tab-stop-per-node rule.\n */\n checkedStates?: ReadonlyMap<Id, CheckboxState>;\n onCheckedToggle?: (id: Id) => void;\n asyncState?: TreeAsyncState;\n /** Product-owned glyph for the expand/collapse affordance; decorative by contract. */\n renderToggle?: (state: Readonly<{ expanded: boolean }>) => ReactNode;\n className?: string;\n}>;\n\nconst typeaheadResetMs = 500;\n\nfunction selectedIds<Id extends string>(selection: TreeSelectionModel<Id> | undefined): ReadonlySet<Id> {\n if (!selection || selection.mode === \"none\") return new Set<Id>();\n if (selection.mode === \"single\") {\n const key = selection.selectedKey ?? selection.defaultSelectedKey ?? null;\n return key === null ? new Set<Id>() : new Set<Id>([key]);\n }\n return selection.selectedKeys ?? selection.defaultSelectedKeys ?? new Set<Id>();\n}\n\nexport const Tree = forwardRef(function Tree<Id extends string = string>(\n {\n label,\n nodes,\n composeAccessibleName,\n expandedKeys: controlledExpanded,\n defaultExpandedKeys,\n onExpandedKeysChange,\n selection,\n checkedStates,\n onCheckedToggle,\n asyncState = { status: \"idle\" },\n renderToggle,\n className,\n }: TreeProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [expandedKeys, setExpandedKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledExpanded === undefined ? {} : { value: controlledExpanded }),\n defaultValue: defaultExpandedKeys ?? new Set<Id>(),\n ...(onExpandedKeysChange === undefined ? {} : { onChange: onExpandedKeysChange }),\n });\n const direction = useOptionalHjmTheme()?.environment.direction ?? \"ltr\";\n // Expansion keys survive a node list change only where the node still exists\n // and can still hold children; the contract owns that reconciliation.\n const liveExpanded = useMemo(() => reconcileTreeExpansion(nodes, expandedKeys), [nodes, expandedKeys]);\n const resolved = useMemo(\n () => resolveTreeDescriptor(nodes, liveExpanded, { composeAccessibleName }),\n [nodes, liveExpanded, composeAccessibleName],\n );\n const visible = useMemo(() => resolved.filter((node) => node.visible), [resolved]);\n const selected = selectedIds(selection);\n const [focusedId, setFocusedId] = useState<Id | null>(null);\n // Roving tab stop: one node is tabbable, and it must stay a real visible node\n // after a collapse removed the previously focused descendant.\n const activeId = visible.some((node) => node.id === focusedId) ? focusedId : visible[0]?.id ?? null;\n const rootRef = useRef<HTMLDivElement>(null);\n const typeahead = useRef<Readonly<{ query: string; at: number }>>({ query: \"\", at: 0 });\n\n const focusNode = (id: Id) => {\n setFocusedId(id);\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-tree-node=\"${CSS.escape(id)}\"]`)?.focus();\n };\n const setExpanded = (id: Id, expanded: boolean) => {\n const next = new Set(liveExpanded);\n if (expanded) next.add(id); else next.delete(id);\n setExpandedKeys(next);\n };\n const toggleSelection = (node: ResolvedTreeNodeDescriptor<Id>) => {\n if (node.disabled || !selection || selection.mode === \"none\") return;\n if (selection.mode === \"single\") {\n const current = selection.selectedKey ?? selection.defaultSelectedKey ?? null;\n const next = current === node.id && selection.disallowEmptySelection !== true ? null : node.id;\n selection.onSelectionChange?.(next);\n return;\n }\n const next = new Set(selection.selectedKeys ?? selection.defaultSelectedKeys ?? []);\n if (next.has(node.id)) next.delete(node.id); else next.add(node.id);\n selection.onSelectionChange?.(next);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>, node: ResolvedTreeNodeDescriptor<Id>) => {\n const key = event.key as WebKeyboardKey;\n const arrow = getTreeArrowKeyIntent(key, direction);\n if (arrow) {\n const result = getTreeArrowResult(visible, node.id, arrow);\n if (result.action === \"none\") return;\n event.preventDefault();\n if (result.action === \"moveFocus\") focusNode(result.targetId);\n else setExpanded(node.id, result.action === \"expand\");\n return;\n }\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Home\" || key === \"End\") {\n const intent = key === \"ArrowDown\" ? \"next\" : key === \"ArrowUp\" ? \"previous\" : key === \"Home\" ? \"first\" : \"last\";\n const target = getVisibleTreeNavigationTarget(visible, node.id, intent);\n if (target === undefined) return;\n event.preventDefault();\n focusNode(target);\n return;\n }\n if (key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); }\n else toggleSelection(node);\n return;\n }\n if (event.key.length !== 1 || event.metaKey || event.ctrlKey || event.altKey) return;\n const now = Date.now();\n const query = now - typeahead.current.at > typeaheadResetMs ? event.key : typeahead.current.query + event.key;\n typeahead.current = { query, at: now };\n const match = getVisibleTreeTypeaheadMatch(visible, query, { startsAfterKey: node.id });\n if (match !== undefined) { event.preventDefault(); focusNode(match); }\n };\n\n return (\n <div\n ref={composeRefs(rootRef, forwardedRef)}\n role=\"tree\"\n aria-label={label}\n aria-busy={asyncState.status === \"loading\" || asyncState.status === \"loadingMore\" || undefined}\n aria-multiselectable={selection?.mode === \"multiple\" || undefined}\n className={classNames(\"hjm-tree\", className)}\n style={{ \"--hjm-tree-indent\": `${treeRecipe.indentPerLevel}px` } as CSSProperties}\n >\n {asyncState.status === \"empty\" || asyncState.status === \"error\" || asyncState.status === \"loading\" ? (\n <p className=\"hjm-tree__state\" role={asyncState.status === \"error\" ? \"alert\" : \"status\"}>{asyncState.message}</p>\n ) : null}\n {visible.map((node) => (\n <div\n key={node.id}\n role=\"treeitem\"\n data-hjm-tree-node={node.id}\n aria-level={node.depth}\n aria-posinset={node.position}\n aria-setsize={node.siblingCount}\n aria-expanded={node.hasChildren ? node.expanded : undefined}\n aria-selected={selection && selection.mode !== \"none\" ? selected.has(node.id) : undefined}\n aria-checked={checkedStates ? (checkedStates.get(node.id) === \"mixed\" ? \"mixed\" : String(checkedStates.get(node.id) === true)) as \"true\" | \"false\" | \"mixed\" : undefined}\n aria-disabled={node.disabled || undefined}\n aria-label={node.accessibleName}\n // One tab stop for the whole tree; the glyph is decorative, not a\n // nested control, so a node is the only focusable thing in a row.\n tabIndex={node.id === activeId ? 0 : -1}\n className=\"hjm-tree__node\"\n style={{ \"--hjm-tree-depth\": node.depth - 1 } as CSSProperties}\n onClick={() => {\n setFocusedId(node.id);\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); }\n else toggleSelection(node);\n }}\n onFocus={() => setFocusedId(node.id)}\n onKeyDown={(event) => onKeyDown(event, node)}\n >\n <span aria-hidden=\"true\" className=\"hjm-tree__indent\" />\n {checkedStates ? (\n <span aria-hidden=\"true\" className=\"hjm-tree__check\" data-state={String(checkedStates.get(node.id) ?? false)}>\n {checkedStates.get(node.id) === true ? \"✓\" : checkedStates.get(node.id) === \"mixed\" ? \"–\" : \"\"}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"hjm-tree__toggle\" data-visible={node.hasChildren || undefined}>\n {node.hasChildren ? renderToggle?.({ expanded: node.expanded }) ?? (node.expanded ? \"▾\" : \"▸\") : null}\n </span>\n <span aria-hidden=\"true\" className=\"hjm-tree__label\">\n {node.label}\n {node.description ? <span className=\"hjm-tree__description\">{node.description}</span> : null}\n </span>\n </div>\n ))}\n {asyncState.status === \"loadingMore\" ? <p className=\"hjm-tree__state\" role=\"status\">{asyncState.message}</p> : null}\n </div>\n );\n}) as <Id extends string = string>(\n props: TreeProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../src/tree.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n getTreeArrowKeyIntent,\n getTreeArrowResult,\n getVisibleTreeNavigationTarget,\n getVisibleTreeTypeaheadMatch,\n reconcileTreeExpansion,\n resolveTreeDescriptor,\n treeRecipe,\n type ComposeTreeAccessibleName,\n type ResolvedTreeNodeDescriptor,\n type TreeAsyncState,\n type TreeNodeDescriptor,\n type TreeSelectionModel,\n} from \"@hjmds/design-contracts/components/tree\";\nimport type { CheckboxState, WebKeyboardKey } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactNode,\n} from \"react\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport { useOptionalHjmTheme } from \"./provider.js\";\n\nexport type TreeProps<Id extends string = string> = Readonly<{\n label: string;\n nodes: readonly TreeNodeDescriptor<Id>[];\n composeAccessibleName: ComposeTreeAccessibleName;\n expandedKeys?: ReadonlySet<Id>;\n defaultExpandedKeys?: ReadonlySet<Id>;\n onExpandedKeysChange?: (keys: ReadonlySet<Id>) => void;\n selection?: TreeSelectionModel<Id>;\n /**\n * Tri-state check marks per node, as `resolveTreeCheckedStates` derives them.\n * Checkboxes are not nested controls in a tree: the state rides on the node\n * itself as `aria-checked`, keeping the contract's one-tab-stop-per-node rule.\n */\n checkedStates?: ReadonlyMap<Id, CheckboxState>;\n onCheckedToggle?: (id: Id) => void;\n asyncState?: TreeAsyncState;\n /** Product-owned glyph for the expand/collapse affordance; decorative by contract. */\n renderToggle?: (state: Readonly<{ expanded: boolean }>) => ReactNode;\n className?: string;\n}>;\n\nconst typeaheadResetMs = 500;\n\nfunction selectedIds<Id extends string>(selection: TreeSelectionModel<Id> | undefined): ReadonlySet<Id> {\n if (!selection || selection.mode === \"none\") return new Set<Id>();\n if (selection.mode === \"single\") {\n const key = selection.selectedKey ?? selection.defaultSelectedKey ?? null;\n return key === null ? new Set<Id>() : new Set<Id>([key]);\n }\n return selection.selectedKeys ?? selection.defaultSelectedKeys ?? new Set<Id>();\n}\n\nexport const Tree = forwardRef(function Tree<Id extends string = string>(\n {\n label,\n nodes,\n composeAccessibleName,\n expandedKeys: controlledExpanded,\n defaultExpandedKeys,\n onExpandedKeysChange,\n selection,\n checkedStates,\n onCheckedToggle,\n asyncState = { status: \"idle\" },\n renderToggle,\n className,\n }: TreeProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [expandedKeys, setExpandedKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledExpanded === undefined ? {} : { value: controlledExpanded }),\n defaultValue: defaultExpandedKeys ?? new Set<Id>(),\n ...(onExpandedKeysChange === undefined ? {} : { onChange: onExpandedKeysChange }),\n });\n const direction = useOptionalHjmTheme()?.environment.direction ?? \"ltr\";\n // Expansion keys survive a node list change only where the node still exists\n // and can still hold children; the contract owns that reconciliation.\n const liveExpanded = useMemo(() => reconcileTreeExpansion(nodes, expandedKeys), [nodes, expandedKeys]);\n const resolved = useMemo(\n () => resolveTreeDescriptor(nodes, liveExpanded, { composeAccessibleName }),\n [nodes, liveExpanded, composeAccessibleName],\n );\n const visible = useMemo(() => resolved.filter((node) => node.visible), [resolved]);\n const selected = selectedIds(selection);\n const [focusedId, setFocusedId] = useState<Id | null>(null);\n // Roving tab stop: one node is tabbable, and it must stay a real visible node\n // after a collapse removed the previously focused descendant.\n const activeId = visible.some((node) => node.id === focusedId) ? focusedId : visible[0]?.id ?? null;\n const rootRef = useRef<HTMLDivElement>(null);\n const typeahead = useRef<Readonly<{ query: string; at: number }>>({ query: \"\", at: 0 });\n\n const focusNode = (id: Id) => {\n setFocusedId(id);\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-tree-node=\"${CSS.escape(id)}\"]`)?.focus();\n };\n const setExpanded = (id: Id, expanded: boolean) => {\n const next = new Set(liveExpanded);\n if (expanded) next.add(id); else next.delete(id);\n setExpandedKeys(next);\n };\n const toggleSelection = (node: ResolvedTreeNodeDescriptor<Id>) => {\n if (node.disabled || !selection || selection.mode === \"none\") return;\n if (selection.mode === \"single\") {\n const current = selection.selectedKey ?? selection.defaultSelectedKey ?? null;\n const next = current === node.id && selection.disallowEmptySelection !== true ? null : node.id;\n selection.onSelectionChange?.(next);\n return;\n }\n const next = new Set(selection.selectedKeys ?? selection.defaultSelectedKeys ?? []);\n if (next.has(node.id)) next.delete(node.id); else next.add(node.id);\n selection.onSelectionChange?.(next);\n };\n\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>, node: ResolvedTreeNodeDescriptor<Id>) => {\n const key = event.key as WebKeyboardKey;\n const arrow = getTreeArrowKeyIntent(key, direction);\n if (arrow) {\n const result = getTreeArrowResult(visible, node.id, arrow);\n if (result.action === \"none\") return;\n event.preventDefault();\n if (result.action === \"moveFocus\") focusNode(result.targetId);\n else setExpanded(node.id, result.action === \"expand\");\n return;\n }\n if (key === \"ArrowDown\" || key === \"ArrowUp\" || key === \"Home\" || key === \"End\") {\n const intent = key === \"ArrowDown\" ? \"next\" : key === \"ArrowUp\" ? \"previous\" : key === \"Home\" ? \"first\" : \"last\";\n const target = getVisibleTreeNavigationTarget(visible, node.id, intent);\n if (target === undefined) return;\n event.preventDefault();\n focusNode(target);\n return;\n }\n if (key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); }\n else toggleSelection(node);\n return;\n }\n if (event.key.length !== 1 || event.metaKey || event.ctrlKey || event.altKey) return;\n const now = Date.now();\n const query = now - typeahead.current.at > typeaheadResetMs ? event.key : typeahead.current.query + event.key;\n typeahead.current = { query, at: now };\n const match = getVisibleTreeTypeaheadMatch(visible, query, { startsAfterKey: node.id });\n if (match !== undefined) { event.preventDefault(); focusNode(match); }\n };\n\n return (\n <div\n ref={composeRefs(rootRef, forwardedRef)}\n role=\"tree\"\n aria-label={label}\n aria-busy={asyncState.status === \"loading\" || asyncState.status === \"loadingMore\" || undefined}\n aria-multiselectable={selection?.mode === \"multiple\" || undefined}\n className={classNames(\"hjm-tree\", className)}\n style={{ \"--hjm-tree-indent\": `${treeRecipe.indentPerLevel}px` } as CSSProperties}\n >\n {asyncState.status === \"empty\" || asyncState.status === \"error\" || asyncState.status === \"loading\" ? (\n <p className=\"hjm-tree__state\" role={asyncState.status === \"error\" ? \"alert\" : \"status\"}>{asyncState.message}</p>\n ) : null}\n {visible.map((node) => (\n <div\n key={node.id}\n role=\"treeitem\"\n data-hjm-tree-node={node.id}\n aria-level={node.depth}\n aria-posinset={node.position}\n aria-setsize={node.siblingCount}\n aria-expanded={node.hasChildren ? node.expanded : undefined}\n aria-selected={selection && selection.mode !== \"none\" ? selected.has(node.id) : undefined}\n aria-checked={checkedStates ? (checkedStates.get(node.id) === \"mixed\" ? \"mixed\" : String(checkedStates.get(node.id) === true)) as \"true\" | \"false\" | \"mixed\" : undefined}\n aria-disabled={node.disabled || undefined}\n aria-label={node.accessibleName}\n // One tab stop for the whole tree; the glyph is decorative, not a\n // nested control, so a node is the only focusable thing in a row.\n tabIndex={node.id === activeId ? 0 : -1}\n className=\"hjm-tree__node\"\n style={{ \"--hjm-tree-depth\": node.depth - 1 } as CSSProperties}\n onClick={(event) => {\n setFocusedId(node.id);\n // Pointer users had no way to expand: only arrow keys toggled (2026-09-30\n // audit WR-0930-2). The contract lets a row click change expansion; the\n // glyph toggles only, and in check mode the row click stays the check.\n const onGlyph = node.hasChildren &&\n (event.target as Element).closest?.(\".hjm-tree__toggle\") !== null;\n if (onGlyph) { setExpanded(node.id, !node.expanded); return; }\n if (checkedStates && onCheckedToggle) { if (!node.disabled) onCheckedToggle(node.id); return; }\n if (node.hasChildren) setExpanded(node.id, !node.expanded);\n toggleSelection(node);\n }}\n onFocus={() => setFocusedId(node.id)}\n onKeyDown={(event) => onKeyDown(event, node)}\n >\n <span aria-hidden=\"true\" className=\"hjm-tree__indent\" />\n {checkedStates ? (\n <span aria-hidden=\"true\" className=\"hjm-tree__check\" data-state={String(checkedStates.get(node.id) ?? false)}>\n {checkedStates.get(node.id) === true ? \"✓\" : checkedStates.get(node.id) === \"mixed\" ? \"–\" : \"\"}\n </span>\n ) : null}\n <span aria-hidden=\"true\" className=\"hjm-tree__toggle\" data-visible={node.hasChildren || undefined}>\n {node.hasChildren ? renderToggle?.({ expanded: node.expanded }) ?? (node.expanded ? \"▾\" : \"▸\") : null}\n </span>\n <span aria-hidden=\"true\" className=\"hjm-tree__label\">\n {node.label}\n {node.description ? <span className=\"hjm-tree__description\">{node.description}</span> : null}\n </span>\n </div>\n ))}\n {asyncState.status === \"loadingMore\" ? <p className=\"hjm-tree__state\" role=\"status\">{asyncState.message}</p> : null}\n </div>\n );\n}) as <Id extends string = string>(\n props: TreeProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
@@ -0,0 +1,12 @@
1
+ import { type ReactNode } from "react";
2
+ export type WatermarkProps = Readonly<{
3
+ text: string | readonly string[];
4
+ children: ReactNode;
5
+ tileWidth?: number;
6
+ tileHeight?: number;
7
+ rotate?: number;
8
+ opacity?: number;
9
+ }>;
10
+ /** React escapes all text; an inline SVG pattern avoids canvas, external requests and markup interpolation. */
11
+ export declare function Watermark({ text, children, tileWidth, tileHeight, rotate, opacity }: WatermarkProps): import("react").JSX.Element;
12
+ //# sourceMappingURL=watermark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watermark.d.ts","sourceRoot":"","sources":["../src/watermark.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9C,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAC7K,+GAA+G;AAC/G,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,+BAanG"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId } from "react";
3
+ import { resolveWatermark } from "@hjmds/design-contracts/components/watermark";
4
+ /** React escapes all text; an inline SVG pattern avoids canvas, external requests and markup interpolation. */
5
+ export function Watermark({ text, children, tileWidth, tileHeight, rotate, opacity }) {
6
+ const config = resolveWatermark(text, tileWidth, tileHeight, rotate, opacity);
7
+ const id = `hjm-watermark-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
8
+ return _jsxs("div", { className: "hjm-watermark", "data-hjm-watermark": true, children: [_jsx("div", { className: "hjm-watermark__content", children: children }), _jsxs("svg", { className: "hjm-watermark__overlay", "aria-hidden": "true", focusable: "false", width: "100%", height: "100%", children: [_jsx("defs", { children: _jsx("pattern", { id: id, width: config.width, height: config.height, patternUnits: "userSpaceOnUse", children: _jsx("g", { transform: `rotate(${config.rotate} ${config.width / 2} ${config.height / 2})`, opacity: config.opacity, fill: "currentColor", children: config.lines.map((line, index) => _jsx("text", { x: config.width / 2, y: config.height / 2, dy: `${(index - (config.lines.length - 1) / 2) * 1.2}em`, textAnchor: "middle", dominantBaseline: "middle", children: line }, index)) }) }) }), _jsx("rect", { width: "100%", height: "100%", fill: `url(#${id})` })] })] });
9
+ }
10
+ //# sourceMappingURL=watermark.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watermark.js","sourceRoot":"","sources":["../src/watermark.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAkB,MAAM,OAAO,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8CAA8C,CAAC;AAEhF,+GAA+G;AAC/G,MAAM,UAAU,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAkB;IAClG,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9E,MAAM,EAAE,GAAG,iBAAiB,KAAK,EAAE,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,EAAE,CAAC;IACrE,OAAO,eAAK,SAAS,EAAC,eAAe,yCACnC,cAAK,SAAS,EAAC,wBAAwB,YAAE,QAAQ,GAAO,EACxD,eAAK,SAAS,EAAC,wBAAwB,iBAAa,MAAM,EAAC,SAAS,EAAC,OAAO,EAAC,KAAK,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,aACrG,yBAAM,kBAAS,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,EAAC,gBAAgB,YAC9F,YAAG,SAAS,EAAE,UAAU,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAC,cAAc,YAC5H,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,eAAkB,CAAC,EAAE,MAAM,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,IAAI,EAAE,UAAU,EAAC,QAAQ,EAAC,gBAAgB,EAAC,QAAQ,YAAE,IAAI,IAA/J,KAAK,CAAkK,CAAC,GACpN,GACI,GAAO,EAAA,eAAM,KAAK,EAAC,MAAM,EAAC,MAAM,EAAC,MAAM,EAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,GAAI,IACrE,IACF,CAAC;AACT,CAAC","sourcesContent":["import { useId, type ReactNode } from \"react\";\nimport { resolveWatermark } from \"@hjmds/design-contracts/components/watermark\";\nexport type WatermarkProps = Readonly<{ text: string | readonly string[]; children: ReactNode; tileWidth?: number; tileHeight?: number; rotate?: number; opacity?: number }>;\n/** React escapes all text; an inline SVG pattern avoids canvas, external requests and markup interpolation. */\nexport function Watermark({ text, children, tileWidth, tileHeight, rotate, opacity }: WatermarkProps) {\n const config = resolveWatermark(text, tileWidth, tileHeight, rotate, opacity);\n const id = `hjm-watermark-${useId().replace(/[^a-zA-Z0-9_-]/g, \"\")}`;\n return <div className=\"hjm-watermark\" data-hjm-watermark>\n <div className=\"hjm-watermark__content\">{children}</div>\n <svg className=\"hjm-watermark__overlay\" aria-hidden=\"true\" focusable=\"false\" width=\"100%\" height=\"100%\">\n <defs><pattern id={id} width={config.width} height={config.height} patternUnits=\"userSpaceOnUse\">\n <g transform={`rotate(${config.rotate} ${config.width / 2} ${config.height / 2})`} opacity={config.opacity} fill=\"currentColor\">\n {config.lines.map((line, index) => <text key={index} x={config.width / 2} y={config.height / 2} dy={`${(index - (config.lines.length - 1) / 2) * 1.2}em`} textAnchor=\"middle\" dominantBaseline=\"middle\">{line}</text>)}\n </g>\n </pattern></defs><rect width=\"100%\" height=\"100%\" fill={`url(#${id})`} />\n </svg>\n </div>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hjmds/react",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Accessible React 19 renderers for HJM design contracts.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -308,6 +308,21 @@
308
308
  "import": "./dist/menu-morph.js",
309
309
  "default": "./dist/menu-morph.js"
310
310
  },
311
+ "./affix": {
312
+ "types": "./dist/affix.d.ts",
313
+ "import": "./dist/affix.js",
314
+ "default": "./dist/affix.js"
315
+ },
316
+ "./color-picker": {
317
+ "types": "./dist/color-picker.d.ts",
318
+ "import": "./dist/color-picker.js",
319
+ "default": "./dist/color-picker.js"
320
+ },
321
+ "./watermark": {
322
+ "types": "./dist/watermark.d.ts",
323
+ "import": "./dist/watermark.js",
324
+ "default": "./dist/watermark.js"
325
+ },
311
326
  "./masonry": {
312
327
  "types": "./dist/masonry.d.ts",
313
328
  "react-native": "./dist/masonry.js",
@@ -326,6 +341,31 @@
326
341
  "import": "./dist/qr-code.js",
327
342
  "default": "./dist/qr-code.js"
328
343
  },
344
+ "./sortable": {
345
+ "types": "./dist/sortable.d.ts",
346
+ "import": "./dist/sortable.js",
347
+ "default": "./dist/sortable.js"
348
+ },
349
+ "./swipe-actions": {
350
+ "types": "./dist/swipe-actions.d.ts",
351
+ "import": "./dist/swipe-actions.js",
352
+ "default": "./dist/swipe-actions.js"
353
+ },
354
+ "./content-transition": {
355
+ "types": "./dist/content-transition.d.ts",
356
+ "import": "./dist/content-transition.js",
357
+ "default": "./dist/content-transition.js"
358
+ },
359
+ "./carousel-motion": {
360
+ "types": "./dist/carousel-motion.d.ts",
361
+ "import": "./dist/carousel-motion.js",
362
+ "default": "./dist/carousel-motion.js"
363
+ },
364
+ "./celebration": {
365
+ "types": "./dist/celebration.d.ts",
366
+ "import": "./dist/celebration.js",
367
+ "default": "./dist/celebration.js"
368
+ },
329
369
  "./styles.css": "./dist/styles.css",
330
370
  "./styles.layered.css": "./dist/styles.layered.css"
331
371
  },
@@ -336,13 +376,17 @@
336
376
  "THIRD_PARTY_NOTICES.md"
337
377
  ],
338
378
  "peerDependencies": {
339
- "@hjmds/design-contracts": ">=1.8.0 <1.9.0",
379
+ "@hjmds/design-contracts": ">=1.9.0 <1.10.0",
340
380
  "react": ">=19.0.0",
341
381
  "react-dom": ">=19.0.0",
342
382
  "@number-flow/react": "0.6.2",
343
383
  "bloom-menu": "0.1.0",
344
384
  "framer-motion": "13.4.4",
345
- "qrcode-generator": "2.0.4"
385
+ "qrcode-generator": "2.0.4",
386
+ "@dnd-kit/react": "0.5.0",
387
+ "embla-carousel-react": "8.6.0",
388
+ "canvas-confetti": "1.9.4",
389
+ "@dnd-kit/dom": "0.5.0"
346
390
  },
347
391
  "devDependencies": {
348
392
  "@types/node": "^24.13.3",
@@ -358,7 +402,12 @@
358
402
  "bloom-menu": "0.1.0",
359
403
  "framer-motion": "13.4.4",
360
404
  "qrcode-generator": "2.0.4",
361
- "@hjmds/design-contracts": "1.8.0"
405
+ "@dnd-kit/react": "0.5.0",
406
+ "embla-carousel-react": "8.6.0",
407
+ "canvas-confetti": "1.9.4",
408
+ "@types/canvas-confetti": "1.9.0",
409
+ "@dnd-kit/dom": "0.5.0",
410
+ "@hjmds/design-contracts": "1.9.0"
362
411
  },
363
412
  "peerDependenciesMeta": {
364
413
  "@number-flow/react": {
@@ -372,6 +421,18 @@
372
421
  },
373
422
  "qrcode-generator": {
374
423
  "optional": true
424
+ },
425
+ "@dnd-kit/react": {
426
+ "optional": true
427
+ },
428
+ "embla-carousel-react": {
429
+ "optional": true
430
+ },
431
+ "canvas-confetti": {
432
+ "optional": true
433
+ },
434
+ "@dnd-kit/dom": {
435
+ "optional": true
375
436
  }
376
437
  },
377
438
  "scripts": {