@hjmds/react 1.17.2 → 1.19.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 (201) hide show
  1. package/dist/activity-chip.d.ts +33 -0
  2. package/dist/activity-chip.d.ts.map +1 -0
  3. package/dist/activity-chip.js +24 -0
  4. package/dist/activity-chip.js.map +1 -0
  5. package/dist/advanced-display.d.ts.map +1 -1
  6. package/dist/advanced-display.js +41 -9
  7. package/dist/advanced-display.js.map +1 -1
  8. package/dist/advanced-forms.d.ts.map +1 -1
  9. package/dist/advanced-forms.js +22 -9
  10. package/dist/advanced-forms.js.map +1 -1
  11. package/dist/agreement.d.ts.map +1 -1
  12. package/dist/agreement.js +17 -11
  13. package/dist/agreement.js.map +1 -1
  14. package/dist/auth-screen.d.ts.map +1 -1
  15. package/dist/auth-screen.js +15 -2
  16. package/dist/auth-screen.js.map +1 -1
  17. package/dist/avatar-blobatar-motion.d.ts.map +1 -1
  18. package/dist/avatar-blobatar-motion.js +10 -2
  19. package/dist/avatar-blobatar-motion.js.map +1 -1
  20. package/dist/bottom-navigation.d.ts.map +1 -1
  21. package/dist/bottom-navigation.js +2 -2
  22. package/dist/bottom-navigation.js.map +1 -1
  23. package/dist/calendar.d.ts.map +1 -1
  24. package/dist/calendar.js +10 -2
  25. package/dist/calendar.js.map +1 -1
  26. package/dist/carousel-motion.d.ts +1 -0
  27. package/dist/carousel-motion.d.ts.map +1 -1
  28. package/dist/carousel-motion.js +4 -3
  29. package/dist/carousel-motion.js.map +1 -1
  30. package/dist/carousel.d.ts.map +1 -1
  31. package/dist/carousel.js +22 -4
  32. package/dist/carousel.js.map +1 -1
  33. package/dist/collapsible.d.ts.map +1 -1
  34. package/dist/collapsible.js +28 -4
  35. package/dist/collapsible.js.map +1 -1
  36. package/dist/collection-rail.d.ts.map +1 -1
  37. package/dist/collection-rail.js +19 -1
  38. package/dist/collection-rail.js.map +1 -1
  39. package/dist/color-picker.d.ts.map +1 -1
  40. package/dist/color-picker.js +12 -4
  41. package/dist/color-picker.js.map +1 -1
  42. package/dist/command-palette.d.ts.map +1 -1
  43. package/dist/command-palette.js +14 -2
  44. package/dist/command-palette.js.map +1 -1
  45. package/dist/content-transition.d.ts +2 -1
  46. package/dist/content-transition.d.ts.map +1 -1
  47. package/dist/content-transition.js +2 -2
  48. package/dist/content-transition.js.map +1 -1
  49. package/dist/context-menu.d.ts.map +1 -1
  50. package/dist/context-menu.js +31 -10
  51. package/dist/context-menu.js.map +1 -1
  52. package/dist/date-entry.d.ts +1 -1
  53. package/dist/date-entry.d.ts.map +1 -1
  54. package/dist/date-entry.js +20 -7
  55. package/dist/date-entry.js.map +1 -1
  56. package/dist/date-picker.d.ts.map +1 -1
  57. package/dist/date-picker.js +22 -6
  58. package/dist/date-picker.js.map +1 -1
  59. package/dist/display.d.ts +11 -1
  60. package/dist/display.d.ts.map +1 -1
  61. package/dist/display.js +13 -4
  62. package/dist/display.js.map +1 -1
  63. package/dist/duration-field.d.ts +2 -2
  64. package/dist/duration-field.d.ts.map +1 -1
  65. package/dist/duration-field.js +11 -3
  66. package/dist/duration-field.js.map +1 -1
  67. package/dist/evidence.d.ts +1 -1
  68. package/dist/evidence.js +1 -1
  69. package/dist/evidence.js.map +1 -1
  70. package/dist/feedback.d.ts +1 -0
  71. package/dist/feedback.d.ts.map +1 -1
  72. package/dist/feedback.js +7 -2
  73. package/dist/feedback.js.map +1 -1
  74. package/dist/field-group.d.ts +4 -2
  75. package/dist/field-group.d.ts.map +1 -1
  76. package/dist/field-group.js +4 -3
  77. package/dist/field-group.js.map +1 -1
  78. package/dist/file-picker.d.ts.map +1 -1
  79. package/dist/file-picker.js +15 -5
  80. package/dist/file-picker.js.map +1 -1
  81. package/dist/forms.d.ts +2 -2
  82. package/dist/forms.js +1 -1
  83. package/dist/forms.js.map +1 -1
  84. package/dist/gravity-letters.d.ts.map +1 -1
  85. package/dist/gravity-letters.js +9 -1
  86. package/dist/gravity-letters.js.map +1 -1
  87. package/dist/grid-reveal.d.ts.map +1 -1
  88. package/dist/grid-reveal.js +9 -1
  89. package/dist/grid-reveal.js.map +1 -1
  90. package/dist/index.d.ts +1 -0
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +1 -0
  93. package/dist/index.js.map +1 -1
  94. package/dist/inline-confirm.d.ts.map +1 -1
  95. package/dist/inline-confirm.js +25 -4
  96. package/dist/inline-confirm.js.map +1 -1
  97. package/dist/internal/document-resource.d.ts.map +1 -1
  98. package/dist/internal/document-resource.js +2 -2
  99. package/dist/internal/document-resource.js.map +1 -1
  100. package/dist/layout.d.ts +8 -2
  101. package/dist/layout.d.ts.map +1 -1
  102. package/dist/layout.js.map +1 -1
  103. package/dist/masonry.d.ts.map +1 -1
  104. package/dist/masonry.js +2 -1
  105. package/dist/masonry.js.map +1 -1
  106. package/dist/mentions.d.ts.map +1 -1
  107. package/dist/mentions.js +12 -4
  108. package/dist/mentions.js.map +1 -1
  109. package/dist/menu-morph.js +1 -1
  110. package/dist/menu-morph.js.map +1 -1
  111. package/dist/menubar.d.ts.map +1 -1
  112. package/dist/menubar.js +43 -19
  113. package/dist/menubar.js.map +1 -1
  114. package/dist/modal.d.ts.map +1 -1
  115. package/dist/modal.js +15 -4
  116. package/dist/modal.js.map +1 -1
  117. package/dist/navigation.d.ts.map +1 -1
  118. package/dist/navigation.js +8 -2
  119. package/dist/navigation.js.map +1 -1
  120. package/dist/number-field.d.ts.map +1 -1
  121. package/dist/number-field.js +3 -1
  122. package/dist/number-field.js.map +1 -1
  123. package/dist/overlay-stack.d.ts.map +1 -1
  124. package/dist/overlay-stack.js +23 -6
  125. package/dist/overlay-stack.js.map +1 -1
  126. package/dist/overlays.d.ts.map +1 -1
  127. package/dist/overlays.js +8 -0
  128. package/dist/overlays.js.map +1 -1
  129. package/dist/pagination.d.ts.map +1 -1
  130. package/dist/pagination.js +16 -4
  131. package/dist/pagination.js.map +1 -1
  132. package/dist/saved-items.d.ts.map +1 -1
  133. package/dist/saved-items.js +6 -1
  134. package/dist/saved-items.js.map +1 -1
  135. package/dist/screen-flows.d.ts +1 -2
  136. package/dist/screen-flows.d.ts.map +1 -1
  137. package/dist/screen-flows.js +120 -16
  138. package/dist/screen-flows.js.map +1 -1
  139. package/dist/screens.d.ts +1 -1
  140. package/dist/screens.d.ts.map +1 -1
  141. package/dist/screens.js +38 -10
  142. package/dist/screens.js.map +1 -1
  143. package/dist/select.d.ts.map +1 -1
  144. package/dist/select.js +6 -1
  145. package/dist/select.js.map +1 -1
  146. package/dist/side-panel.d.ts.map +1 -1
  147. package/dist/side-panel.js +11 -2
  148. package/dist/side-panel.js.map +1 -1
  149. package/dist/sidebar.d.ts.map +1 -1
  150. package/dist/sidebar.js +8 -1
  151. package/dist/sidebar.js.map +1 -1
  152. package/dist/skip-nav.d.ts.map +1 -1
  153. package/dist/skip-nav.js +16 -3
  154. package/dist/skip-nav.js.map +1 -1
  155. package/dist/sortable.d.ts.map +1 -1
  156. package/dist/sortable.js +2 -1
  157. package/dist/sortable.js.map +1 -1
  158. package/dist/splitter.d.ts.map +1 -1
  159. package/dist/splitter.js +23 -11
  160. package/dist/splitter.js.map +1 -1
  161. package/dist/statistic-motion.d.ts.map +1 -1
  162. package/dist/statistic-motion.js +30 -2
  163. package/dist/statistic-motion.js.map +1 -1
  164. package/dist/status-text.d.ts +11 -0
  165. package/dist/status-text.d.ts.map +1 -0
  166. package/dist/status-text.js +7 -0
  167. package/dist/status-text.js.map +1 -0
  168. package/dist/styles.css +53 -7
  169. package/dist/styles.layered.css +53 -7
  170. package/dist/supplemental-display.d.ts.map +1 -1
  171. package/dist/supplemental-display.js +19 -8
  172. package/dist/supplemental-display.js.map +1 -1
  173. package/dist/supplemental-navigation.d.ts.map +1 -1
  174. package/dist/supplemental-navigation.js +10 -1
  175. package/dist/supplemental-navigation.js.map +1 -1
  176. package/dist/tags-input.d.ts.map +1 -1
  177. package/dist/tags-input.js +18 -8
  178. package/dist/tags-input.js.map +1 -1
  179. package/dist/theme.d.ts.map +1 -1
  180. package/dist/theme.js +11 -0
  181. package/dist/theme.js.map +1 -1
  182. package/dist/toast.d.ts.map +1 -1
  183. package/dist/toast.js +41 -16
  184. package/dist/toast.js.map +1 -1
  185. package/dist/toggle-group.d.ts.map +1 -1
  186. package/dist/toggle-group.js +6 -1
  187. package/dist/toggle-group.js.map +1 -1
  188. package/dist/tour.d.ts.map +1 -1
  189. package/dist/tour.js +15 -2
  190. package/dist/tour.js.map +1 -1
  191. package/dist/transfer-list.d.ts +1 -1
  192. package/dist/transfer-list.d.ts.map +1 -1
  193. package/dist/transfer-list.js +52 -7
  194. package/dist/transfer-list.js.map +1 -1
  195. package/dist/tree.d.ts.map +1 -1
  196. package/dist/tree.js +41 -8
  197. package/dist/tree.js.map +1 -1
  198. package/dist/virtual-list.d.ts.map +1 -1
  199. package/dist/virtual-list.js +44 -7
  200. package/dist/virtual-list.js.map +1 -1
  201. package/package.json +13 -3
@@ -1 +1 @@
1
- {"version":3,"file":"transfer-list.js","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n moveTransferListSelection,\n reconcileTransferListSelection,\n resolveTransferListFocusAfterMove,\n resolveTransferListPanels,\n resolveTransferListSelectAllState,\n toggleTransferListSelectAll,\n toggleTransferListSelection,\n type TransferListMoveDirection,\n type TransferListPanel,\n type TransferListSelection,\n} from \"@hjmds/design-contracts/components/transfer-list\";\nimport type { SelectItemDescriptor } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TransferListLabels = Readonly<{\n source: string;\n target: string;\n toTarget: string;\n toSource: string;\n selectAll: string;\n empty: string;\n}>;\n\nexport type TransferListProps<Id extends string = string> = Readonly<{\n items: readonly SelectItemDescriptor<Id>[];\n labels: TransferListLabels;\n targetKeys?: ReadonlySet<Id>;\n defaultTargetKeys?: ReadonlySet<Id>;\n onTargetKeysChange?: (keys: ReadonlySet<Id>) => void;\n /** Receives which ids moved, in origin-panel order, so the product announces it. */\n onMove?: (movedIds: readonly Id[], direction: TransferListMoveDirection) => void;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst emptySelection = <Id extends string>(): TransferListSelection<Id> => ({\n source: new Set<Id>(),\n target: new Set<Id>(),\n});\n\nexport const TransferList = forwardRef(function TransferList<Id extends string = string>(\n {\n items,\n labels,\n targetKeys: controlledTargetKeys,\n defaultTargetKeys,\n onTargetKeysChange,\n onMove,\n className,\n layoutStyle,\n }: TransferListProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [targetKeys, setTargetKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),\n defaultValue: defaultTargetKeys ?? new Set<Id>(),\n ...(onTargetKeysChange === undefined ? {} : { onChange: onTargetKeysChange }),\n });\n const [rawSelection, setSelection] = useState<TransferListSelection<Id>>(emptySelection<Id>);\n const descriptor = useMemo(() => ({ items, targetKeys }), [items, targetKeys]);\n // A row that left the list entirely must not keep a pending check mark.\n const selection = useMemo(\n () => reconcileTransferListSelection(descriptor, rawSelection),\n [descriptor, rawSelection],\n );\n const panels = resolveTransferListPanels(descriptor);\n const rootRef = useRef<HTMLDivElement>(null);\n const [focused, setFocused] = useState<Readonly<Record<TransferListPanel, Id | null>>>({ source: null, target: null });\n\n const focusRow = (panel: TransferListPanel, id: Id | null) => {\n setFocused((previous) => ({ ...previous, [panel]: id }));\n if (id === null) return;\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)?.focus();\n };\n const move = (direction: TransferListMoveDirection) => {\n const from: TransferListPanel = direction === \"toTarget\" ? \"source\" : \"target\";\n const before = panels[from];\n const result = moveTransferListSelection(descriptor, selection, direction);\n if (result.movedIds.length === 0) return;\n const removedIndex = before.findIndex((item) => item.id === result.movedIds[0]);\n const remaining = before.filter((item) => !result.movedIds.includes(item.id));\n setTargetKeys(result.targetKeys);\n setSelection(result.selection);\n onMove?.(result.movedIds, direction);\n // Focus lands on the row that slid into the first removed position, or on\n // the panel's empty state — never on the document body.\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(from, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${from}\"]`)?.focus();\n });\n };\n const moveSingle = (panel: TransferListPanel, id: Id) => {\n // A focused row moves on its own: a single move must not require first\n // building a multi-selection.\n const single = { ...emptySelection<Id>(), [panel]: new Set<Id>([id]) } as TransferListSelection<Id>;\n const direction: TransferListMoveDirection = panel === \"source\" ? \"toTarget\" : \"toSource\";\n const result = moveTransferListSelection(descriptor, single, direction);\n if (result.movedIds.length === 0) return;\n const before = panels[panel];\n const removedIndex = before.findIndex((item) => item.id === id);\n const remaining = before.filter((item) => item.id !== id);\n setTargetKeys(result.targetKeys);\n setSelection((current) => ({ ...current, [panel]: new Set([...current[panel]].filter((key) => key !== id)) }));\n onMove?.(result.movedIds, direction);\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n });\n };\n\n const renderPanel = (panel: TransferListPanel) => {\n const panelItems = panels[panel];\n const selected = selection[panel];\n const selectAllState = resolveTransferListSelectAllState(descriptor, selection, panel);\n const activeId = panelItems.some((item) => item.id === focused[panel])\n ? focused[panel]\n : panelItems.find((item) => !item.disabled)?.id ?? panelItems[0]?.id ?? null;\n const onKeyDown = (event: KeyboardEvent<HTMLElement>, item: SelectItemDescriptor<Id>) => {\n const index = panelItems.findIndex((candidate) => candidate.id === item.id);\n const goto = (nextIndex: number) => {\n const target = panelItems[Math.min(Math.max(nextIndex, 0), panelItems.length - 1)];\n if (target) { event.preventDefault(); focusRow(panel, target.id); }\n };\n if (event.key === \"ArrowDown\") goto(index + 1);\n else if (event.key === \"ArrowUp\") goto(index - 1);\n else if (event.key === \"Home\") goto(0);\n else if (event.key === \"End\") goto(panelItems.length - 1);\n else if (event.key === \" \") {\n event.preventDefault();\n setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n moveSingle(panel, item.id);\n }\n };\n return (\n <div className=\"hjm-transfer-list__panel\" role=\"group\" aria-label={labels[panel]}>\n <div className=\"hjm-transfer-list__header\">\n <span className=\"hjm-transfer-list__title\">{labels[panel]}</span>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={selectAllState === \"mixed\" ? \"mixed\" : String(selectAllState === true) as \"true\" | \"false\"}\n aria-label={`${labels[panel]} ${labels.selectAll}`}\n className=\"hjm-transfer-list__select-all\"\n onClick={() => setSelection(toggleTransferListSelectAll(descriptor, selection, panel))}\n >\n {selectAllState === true ? \"✓\" : selectAllState === \"mixed\" ? \"–\" : \"\"}\n </button>\n </div>\n {panelItems.length === 0 ? (\n <p className=\"hjm-transfer-list__empty\" data-hjm-transfer-empty={panel} tabIndex={-1}>{labels.empty}</p>\n ) : (\n <div role=\"listbox\" aria-multiselectable=\"true\" aria-label={labels[panel]} className=\"hjm-transfer-list__options\">\n {panelItems.map((item) => (\n <div\n key={item.id}\n role=\"option\"\n data-hjm-transfer-row={`${panel}:${item.id}`}\n aria-selected={selected.has(item.id)}\n aria-disabled={item.disabled || undefined}\n tabIndex={item.id === activeId ? 0 : -1}\n className=\"hjm-transfer-list__option\"\n onFocus={() => setFocused((previous) => ({ ...previous, [panel]: item.id }))}\n onClick={() => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id))}\n onKeyDown={(event) => onKeyDown(event, item)}\n >\n <span aria-hidden=\"true\" className=\"hjm-transfer-list__mark\" data-checked={selected.has(item.id) || undefined}>\n {selected.has(item.id) ? \"✓\" : \"\"}\n </span>\n <span>{item.label}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={composeRefs(rootRef, forwardedRef)} style={layoutStyle} className={classNames(\"hjm-transfer-list\", className)}>\n {renderPanel(\"source\")}\n <div className=\"hjm-transfer-list__actions\">\n <Button tone=\"secondary\" disabled={selection.source.size === 0} onClick={() => move(\"toTarget\")}>\n {labels.toTarget}\n </Button>\n <Button tone=\"secondary\" disabled={selection.target.size === 0} onClick={() => move(\"toSource\")}>\n {labels.toSource}\n </Button>\n </div>\n {renderPanel(\"target\")}\n </div>\n );\n}) as <Id extends string = string>(\n props: TransferListProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\n"]}
1
+ {"version":3,"file":"transfer-list.js","sourceRoot":"","sources":["../src/transfer-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n moveTransferListSelection,\n reconcileTransferListSelection,\n resolveTransferListFocusAfterMove,\n resolveTransferListPanels,\n resolveTransferListSelectAllState,\n toggleTransferListSelectAll,\n toggleTransferListSelection,\n validateTransferListDescriptor,\n type TransferListMoveDirection,\n type TransferListPanel,\n type TransferListSelection,\n} from \"@hjmds/design-contracts/components/transfer-list\";\nimport { reconcileCheckboxSelection, type SelectItemDescriptor } from \"@hjmds/design-contracts/behaviors\";\nimport {\n forwardRef,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport { Button } from \"./actions.js\";\nimport { classNames, composeRefs, useControllableState } from \"./internal.js\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\n\nexport type TransferListLabels = Readonly<{\n source: string;\n target: string;\n toTarget: string;\n toSource: string;\n selectAll: string;\n empty: string;\n}>;\n\nexport type TransferListProps<Id extends string = string> = Readonly<{\n items: readonly SelectItemDescriptor<Id>[];\n labels: TransferListLabels;\n targetKeys?: ReadonlySet<Id>;\n defaultTargetKeys?: ReadonlySet<Id>;\n onTargetKeysChange?: (keys: ReadonlySet<Id>) => void;\n /** Receives which ids moved, in origin-panel order, so the product announces it. */\n onMove?: (movedIds: readonly Id[], direction: TransferListMoveDirection) => void;\n className?: string;\n /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\n}>;\n\nconst emptySelection = <Id extends string>(): TransferListSelection<Id> => ({\n source: new Set<Id>(),\n target: new Set<Id>(),\n});\n\nexport const TransferList = forwardRef(function TransferList<Id extends string = string>(\n {\n items,\n labels,\n targetKeys: controlledTargetKeys,\n defaultTargetKeys,\n onTargetKeysChange,\n onMove,\n className,\n layoutStyle,\n }: TransferListProps<Id>,\n forwardedRef: React.Ref<HTMLDivElement>,\n) {\n const [storedTargetKeys, setTargetKeys] = useControllableState<ReadonlySet<Id>>({\n ...(controlledTargetKeys === undefined ? {} : { value: controlledTargetKeys }),\n defaultValue: defaultTargetKeys ?? new Set<Id>(),\n ...(onTargetKeysChange === undefined ? {} : { onChange: onTargetKeysChange }),\n });\n const validatedInitialTargets = useRef(false);\n if (!validatedInitialTargets.current) {\n validateTransferListDescriptor({ items, targetKeys: storedTargetKeys });\n validatedInitialTargets.current = true;\n }\n const targetKeys = useMemo(() => controlledTargetKeys === undefined\n ? reconcileCheckboxSelection(items, storedTargetKeys) : storedTargetKeys,\n [controlledTargetKeys, items, storedTargetKeys]);\n useEffect(() => {\n if (controlledTargetKeys === undefined && targetKeys !== storedTargetKeys) setTargetKeys(targetKeys);\n }, [controlledTargetKeys, setTargetKeys, storedTargetKeys, targetKeys]);\n const [rawSelection, setSelection] = useState<TransferListSelection<Id>>(emptySelection<Id>);\n const descriptor = useMemo(() => ({ items, targetKeys }), [items, targetKeys]);\n // A row that left the list entirely must not keep a pending check mark.\n const selection = useMemo(\n () => reconcileTransferListSelection(descriptor, rawSelection),\n [descriptor, rawSelection],\n );\n useEffect(() => {\n if (selection !== rawSelection) setSelection(selection);\n }, [rawSelection, selection]);\n const panels = resolveTransferListPanels(descriptor);\n const canMove = (panel: TransferListPanel) => panels[panel].some(item => !item.disabled && selection[panel].has(item.id));\n const rootRef = useRef<HTMLDivElement>(null);\n const [focused, setFocused] = useState<Readonly<Record<TransferListPanel, Id | null>>>({ source: null, target: null });\n const focusWithin = useRef(false);\n const focusedPanel = useRef<TransferListPanel | null>(null);\n const previousPanels = useRef(panels);\n useLayoutEffect(() => {\n const before = previousPanels.current;\n previousPanels.current = panels;\n const panel = focusedPanel.current;\n if (!focusWithin.current || panel === null || document.activeElement !== document.body) return;\n const id = focused[panel];\n if (id === null || panels[panel].some(item => item.id === id)) return;\n // External removals bypass the move handler, but must not drop row focus onto body.\n const next = resolveTransferListFocusAfterMove(panels[panel], before[panel].findIndex(item => item.id === id));\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n }, [focused, panels]);\n\n const focusRow = (panel: TransferListPanel, id: Id | null) => {\n setFocused((previous) => ({ ...previous, [panel]: id }));\n if (id === null) return;\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)?.focus();\n };\n const move = (direction: TransferListMoveDirection) => {\n const from: TransferListPanel = direction === \"toTarget\" ? \"source\" : \"target\";\n const before = panels[from];\n const result = moveTransferListSelection(descriptor, selection, direction);\n if (result.movedIds.length === 0) return;\n const removedIndex = before.findIndex((item) => item.id === result.movedIds[0]);\n const remaining = before.filter((item) => !result.movedIds.includes(item.id));\n setTargetKeys(result.targetKeys);\n // Controlled owners may refuse or defer the move; keep checks until the rows actually leave.\n if (controlledTargetKeys === undefined) setSelection(result.selection);\n onMove?.(result.movedIds, direction);\n // Focus lands on the row that slid into the first removed position, or on\n // the panel's empty state — never on the document body.\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (result.movedIds.some(id => rootRef.current?.querySelector(`[data-hjm-transfer-row=\"${from}:${CSS.escape(id)}\"]`))) return;\n if (next !== null) focusRow(from, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${from}\"]`)?.focus();\n });\n };\n const moveSingle = (panel: TransferListPanel, id: Id) => {\n // A focused row moves on its own: a single move must not require first\n // building a multi-selection.\n const single = { ...emptySelection<Id>(), [panel]: new Set<Id>([id]) } as TransferListSelection<Id>;\n const direction: TransferListMoveDirection = panel === \"source\" ? \"toTarget\" : \"toSource\";\n const result = moveTransferListSelection(descriptor, single, direction);\n if (result.movedIds.length === 0) return;\n const before = panels[panel];\n const removedIndex = before.findIndex((item) => item.id === id);\n const remaining = before.filter((item) => item.id !== id);\n setTargetKeys(result.targetKeys);\n if (controlledTargetKeys === undefined) {\n setSelection((current) => ({ ...current, [panel]: new Set([...current[panel]].filter((key) => key !== id)) }));\n }\n onMove?.(result.movedIds, direction);\n const next = resolveTransferListFocusAfterMove(remaining, removedIndex);\n queueMicrotask(() => {\n if (rootRef.current?.querySelector(`[data-hjm-transfer-row=\"${panel}:${CSS.escape(id)}\"]`)) return;\n if (next !== null) focusRow(panel, next);\n else rootRef.current?.querySelector<HTMLElement>(`[data-hjm-transfer-empty=\"${panel}\"]`)?.focus();\n });\n };\n\n const renderPanel = (panel: TransferListPanel) => {\n const panelItems = panels[panel];\n const selected = selection[panel];\n const selectAllState = resolveTransferListSelectAllState(descriptor, selection, panel);\n const activeId = panelItems.some((item) => item.id === focused[panel])\n ? focused[panel]\n : panelItems.find((item) => !item.disabled)?.id ?? panelItems[0]?.id ?? null;\n const onKeyDown = (event: KeyboardEvent<HTMLElement>, item: SelectItemDescriptor<Id>) => {\n const index = panelItems.findIndex((candidate) => candidate.id === item.id);\n const goto = (nextIndex: number) => {\n const target = panelItems[Math.min(Math.max(nextIndex, 0), panelItems.length - 1)];\n if (target) { event.preventDefault(); focusRow(panel, target.id); }\n };\n if (event.key === \"ArrowDown\") goto(index + 1);\n else if (event.key === \"ArrowUp\") goto(index - 1);\n else if (event.key === \"Home\") goto(0);\n else if (event.key === \"End\") goto(panelItems.length - 1);\n else if (event.key === \" \") {\n event.preventDefault();\n setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id));\n } else if (event.key === \"Enter\") {\n event.preventDefault();\n moveSingle(panel, item.id);\n }\n };\n return (\n <div className=\"hjm-transfer-list__panel\" role=\"group\" aria-label={labels[panel]}>\n <div className=\"hjm-transfer-list__header\">\n <span className=\"hjm-transfer-list__title\">{labels[panel]}</span>\n <button\n type=\"button\"\n role=\"checkbox\"\n aria-checked={selectAllState === \"mixed\" ? \"mixed\" : String(selectAllState === true) as \"true\" | \"false\"}\n aria-label={`${labels[panel]} ${labels.selectAll}`}\n className=\"hjm-transfer-list__select-all\"\n disabled={!panelItems.some(item => !item.disabled)}\n onClick={() => setSelection(toggleTransferListSelectAll(descriptor, selection, panel))}\n >\n {selectAllState === true ? \"✓\" : selectAllState === \"mixed\" ? \"–\" : \"\"}\n </button>\n </div>\n {panelItems.length === 0 ? (\n <p className=\"hjm-transfer-list__empty\" data-hjm-transfer-empty={panel} tabIndex={-1}>{labels.empty}</p>\n ) : (\n <div role=\"listbox\" aria-multiselectable=\"true\" aria-label={labels[panel]} className=\"hjm-transfer-list__options\">\n {panelItems.map((item) => (\n <div\n key={item.id}\n role=\"option\"\n data-hjm-transfer-row={`${panel}:${item.id}`}\n aria-selected={selected.has(item.id)}\n aria-disabled={item.disabled || undefined}\n tabIndex={item.id === activeId ? 0 : -1}\n className=\"hjm-transfer-list__option\"\n onFocus={() => { focusedPanel.current = panel; setFocused((previous) => ({ ...previous, [panel]: item.id })); }}\n onClick={() => setSelection(toggleTransferListSelection(descriptor, selection, panel, item.id))}\n onKeyDown={(event) => onKeyDown(event, item)}\n >\n <span aria-hidden=\"true\" className=\"hjm-transfer-list__mark\" data-checked={selected.has(item.id) || undefined}>\n {selected.has(item.id) ? \"✓\" : \"\"}\n </span>\n <span>{item.label}</span>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n };\n\n return (\n <div ref={composeRefs(rootRef, forwardedRef)} style={layoutStyle} className={classNames(\"hjm-transfer-list\", className)}\n onFocusCapture={() => { focusWithin.current = true; }}\n onBlurCapture={event => { if (!event.currentTarget.contains(event.relatedTarget as Node | null)) focusWithin.current = false; }}>\n {renderPanel(\"source\")}\n <div className=\"hjm-transfer-list__actions\">\n <Button tone=\"secondary\" disabled={!canMove(\"source\")} onClick={() => move(\"toTarget\")}>\n {labels.toTarget}\n </Button>\n <Button tone=\"secondary\" disabled={!canMove(\"target\")} onClick={() => move(\"toSource\")}>\n {labels.toSource}\n </Button>\n </div>\n {renderPanel(\"target\")}\n </div>\n );\n}) as <Id extends string = string>(\n props: TransferListProps<Id> & { ref?: React.Ref<HTMLDivElement> },\n) => React.ReactElement | null;\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;AAGf,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,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;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAaH,eAAO,MAAM,IAAI,EAyKX,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,EASL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEtE,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;IACnB,gGAAgG;IAChG,WAAW,CAAC,EAAE,uBAAuB,CAAC;CACvC,CAAC,CAAC;AAaH,eAAO,MAAM,IAAI,EAwMX,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
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { getTreeArrowKeyIntent, getTreeArrowResult, getVisibleTreeNavigationTarget, getVisibleTreeTypeaheadMatch, reconcileTreeExpansion, resolveTreeDescriptor, treeRecipe, } from "@hjmds/design-contracts/components/tree";
3
- import { forwardRef, useMemo, useRef, useState, } from "react";
3
+ import { forwardRef, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
4
4
  import { classNames, composeRefs, useControllableState } from "./internal.js";
5
5
  import { useOptionalHjmTheme } from "./provider.js";
6
6
  const typeaheadResetMs = 500;
@@ -33,13 +33,46 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
33
33
  ? selection.selectedKey !== undefined
34
34
  : selection?.mode === "multiple" ? selection.selectedKeys !== undefined : true;
35
35
  const [uncontrolledSelected, setUncontrolledSelected] = useState(() => selectedIds(selection));
36
- const selected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;
36
+ const rawSelected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;
37
+ const selected = useMemo(() => {
38
+ const liveIds = new Set(resolved.map(node => node.id));
39
+ const keys = [...rawSelected].filter(id => liveIds.has(id));
40
+ return keys.length === rawSelected.size ? rawSelected : new Set(keys);
41
+ }, [rawSelected, resolved]);
42
+ useEffect(() => {
43
+ if (selectionControlled || selected === uncontrolledSelected || !selection || selection.mode === "none")
44
+ return;
45
+ // A removed node must not leak into the next callback or revive when reinserted.
46
+ setUncontrolledSelected(selected);
47
+ if (selection.mode === "single")
48
+ selection.onSelectionChange?.([...selected][0] ?? null);
49
+ else
50
+ selection.onSelectionChange?.(selected);
51
+ }, [selected, selection, selectionControlled, uncontrolledSelected]);
37
52
  const [focusedId, setFocusedId] = useState(null);
38
53
  // Roving tab stop: one node is tabbable, and it must stay a real visible node
39
- // after a collapse removed the previously focused descendant.
40
- const activeId = visible.some((node) => node.id === focusedId) ? focusedId : visible[0]?.id ?? null;
54
+ // after a collapse removed the previously focused descendant. The decorative
55
+ // glyph is not a nested control, so each row has only one focus target.
56
+ const previousResolved = useRef(resolved);
57
+ let fallbackId = focusedId;
58
+ while (fallbackId !== null && !visible.some(node => node.id === fallbackId)) {
59
+ fallbackId = previousResolved.current.find(node => node.id === fallbackId)?.parentId ?? null;
60
+ }
61
+ const activeId = fallbackId ?? visible[0]?.id ?? null;
62
+ const focusWithin = useRef(false);
41
63
  const rootRef = useRef(null);
42
64
  const typeahead = useRef({ query: "", at: 0 });
65
+ useLayoutEffect(() => {
66
+ previousResolved.current = resolved;
67
+ if (focusedId === null || activeId === null || focusedId === activeId)
68
+ return;
69
+ setFocusedId(activeId);
70
+ // External collapse/removal can unmount the focused row without firing blur.
71
+ // Restore its surviving ancestor only while focus still belongs to this tree.
72
+ if (focusWithin.current) {
73
+ rootRef.current?.querySelector(`[data-hjm-tree-node="${CSS.escape(activeId)}"]`)?.focus();
74
+ }
75
+ }, [activeId, focusedId, resolved]);
43
76
  const focusNode = (id) => {
44
77
  setFocusedId(id);
45
78
  rootRef.current?.querySelector(`[data-hjm-tree-node="${CSS.escape(id)}"]`)?.focus();
@@ -116,10 +149,10 @@ export const Tree = forwardRef(function Tree({ label, nodes, composeAccessibleNa
116
149
  focusNode(match);
117
150
  }
118
151
  };
119
- 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: { ...layoutStyle, "--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,
120
- // One tab stop for the whole tree; the glyph is decorative, not a
121
- // nested control, so a node is the only focusable thing in a row.
122
- tabIndex: node.id === activeId ? 0 : -1, className: "hjm-tree__node", style: { "--hjm-tree-depth": node.depth - 1 }, onClick: (event) => {
152
+ return (_jsxs("div", { ref: composeRefs(rootRef, forwardedRef), role: "tree", onFocusCapture: () => { focusWithin.current = true; }, onBlurCapture: (event) => {
153
+ if (!event.currentTarget.contains(event.relatedTarget))
154
+ focusWithin.current = false;
155
+ }, "aria-label": label, "aria-busy": asyncState.status === "loading" || asyncState.status === "loadingMore" || undefined, "aria-multiselectable": selection?.mode === "multiple" || undefined, className: classNames("hjm-tree", className), style: { ...layoutStyle, "--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, tabIndex: node.id === activeId ? 0 : -1, className: "hjm-tree__node", style: { "--hjm-tree-depth": node.depth - 1 }, onClick: (event) => {
123
156
  setFocusedId(node.id);
124
157
  // Pointer users had no way to expand: only arrow keys toggled (2026-09-30
125
158
  // audit WR-0930-2). The contract lets a row click change expansion; the
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\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.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 /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\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 layoutStyle,\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 // `defaultSelectedKey(s)` used to be re-read on every render, so an\n // uncontrolled tree reported the click to `onSelectionChange` but kept showing\n // the default. Keep the uncontrolled value in state like `useControllableState`\n // does for expansion; a controlled `selectedKey(s)` (including `null`) still wins.\n const selectionControlled = selection?.mode === \"single\"\n ? selection.selectedKey !== undefined\n : selection?.mode === \"multiple\" ? selection.selectedKeys !== undefined : true;\n const [uncontrolledSelected, setUncontrolledSelected] = useState<ReadonlySet<Id>>(() => selectedIds(selection));\n const selected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;\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 = selected.has(node.id) ? node.id : null;\n const next = current === node.id && selection.disallowEmptySelection !== true ? null : node.id;\n if (!selectionControlled) setUncontrolledSelected(next === null ? new Set<Id>() : new Set<Id>([next]));\n selection.onSelectionChange?.(next);\n return;\n }\n const next = new Set(selected);\n if (next.has(node.id)) next.delete(node.id); else next.add(node.id);\n if (!selectionControlled) setUncontrolledSelected(next);\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={{ ...layoutStyle, \"--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"]}
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 useEffect,\n useLayoutEffect,\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\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.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 /** Canonical layout-only placement on the root element. Controlled visual keys are excluded. */\n layoutStyle?: HjmCompositionStyleProp;\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 layoutStyle,\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 // `defaultSelectedKey(s)` used to be re-read on every render, so an\n // uncontrolled tree reported the click to `onSelectionChange` but kept showing\n // the default. Keep the uncontrolled value in state like `useControllableState`\n // does for expansion; a controlled `selectedKey(s)` (including `null`) still wins.\n const selectionControlled = selection?.mode === \"single\"\n ? selection.selectedKey !== undefined\n : selection?.mode === \"multiple\" ? selection.selectedKeys !== undefined : true;\n const [uncontrolledSelected, setUncontrolledSelected] = useState<ReadonlySet<Id>>(() => selectedIds(selection));\n const rawSelected = selectionControlled ? selectedIds(selection) : uncontrolledSelected;\n const selected = useMemo(() => {\n const liveIds = new Set(resolved.map(node => node.id));\n const keys = [...rawSelected].filter(id => liveIds.has(id));\n return keys.length === rawSelected.size ? rawSelected : new Set(keys);\n }, [rawSelected, resolved]);\n useEffect(() => {\n if (selectionControlled || selected === uncontrolledSelected || !selection || selection.mode === \"none\") return;\n // A removed node must not leak into the next callback or revive when reinserted.\n setUncontrolledSelected(selected);\n if (selection.mode === \"single\") selection.onSelectionChange?.([...selected][0] ?? null);\n else selection.onSelectionChange?.(selected);\n }, [selected, selection, selectionControlled, uncontrolledSelected]);\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. The decorative\n // glyph is not a nested control, so each row has only one focus target.\n const previousResolved = useRef(resolved);\n let fallbackId = focusedId;\n while (fallbackId !== null && !visible.some(node => node.id === fallbackId)) {\n fallbackId = previousResolved.current.find(node => node.id === fallbackId)?.parentId ?? null;\n }\n const activeId = fallbackId ?? visible[0]?.id ?? null;\n const focusWithin = useRef(false);\n const rootRef = useRef<HTMLDivElement>(null);\n const typeahead = useRef<Readonly<{ query: string; at: number }>>({ query: \"\", at: 0 });\n useLayoutEffect(() => {\n previousResolved.current = resolved;\n if (focusedId === null || activeId === null || focusedId === activeId) return;\n setFocusedId(activeId);\n // External collapse/removal can unmount the focused row without firing blur.\n // Restore its surviving ancestor only while focus still belongs to this tree.\n if (focusWithin.current) {\n rootRef.current?.querySelector<HTMLElement>(`[data-hjm-tree-node=\"${CSS.escape(activeId)}\"]`)?.focus();\n }\n }, [activeId, focusedId, resolved]);\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 = selected.has(node.id) ? node.id : null;\n const next = current === node.id && selection.disallowEmptySelection !== true ? null : node.id;\n if (!selectionControlled) setUncontrolledSelected(next === null ? new Set<Id>() : new Set<Id>([next]));\n selection.onSelectionChange?.(next);\n return;\n }\n const next = new Set(selected);\n if (next.has(node.id)) next.delete(node.id); else next.add(node.id);\n if (!selectionControlled) setUncontrolledSelected(next);\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 onFocusCapture={() => { focusWithin.current = true; }}\n onBlurCapture={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) focusWithin.current = false;\n }}\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={{ ...layoutStyle, \"--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 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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-list.d.ts","sourceRoot":"","sources":["../src/virtual-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAAC,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAAC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IACpO,mHAAmH;IACnH,WAAW,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAC1C,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAY,EAAE,WAAW,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,+BA+BlJ"}
1
+ {"version":3,"file":"virtual-list.d.ts","sourceRoot":"","sources":["../src/virtual-list.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAE1F,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACtE,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI;IAAE,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IAAC,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAAC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IACpO,mHAAmH;IACnH,WAAW,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAAC;AAC1C,gGAAgG;AAChG,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAAY,EAAE,WAAW,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,+BAyDlJ"}
@@ -6,21 +6,58 @@ export function VirtualList({ items, keyExtractor, renderItem, rowHeight, height
6
6
  const keys = useMemo(() => items.map(keyExtractor), [items, keyExtractor]);
7
7
  validateListKeys(keys, label);
8
8
  const [scrollTop, setScrollTop] = useState(0);
9
- const [active, setActive] = useState(0);
10
- const [focused, setFocused] = useState(null);
9
+ const [active, setActive] = useState({ key: null, index: 0 });
10
+ const [focusedKey, setFocusedKey] = useState(null);
11
11
  const host = useRef(null);
12
12
  const moveFocus = useRef(false);
13
13
  const id = useId();
14
14
  const range = resolveVirtualWindow(items.length, rowHeight, height, scrollTop, overscan);
15
- const selected = Math.min(active, Math.max(0, items.length - 1));
15
+ // Stable keys preserve the focused item when filtering/reordering moves it outside the window.
16
+ const activeIndex = active.key === null ? -1 : keys.indexOf(active.key);
17
+ const selected = activeIndex >= 0 ? activeIndex : Math.min(active.index, Math.max(0, items.length - 1));
18
+ const focused = focusedKey === null ? -1 : keys.indexOf(focusedKey);
16
19
  useLayoutEffect(() => { if (host.current && host.current.scrollTop !== range.offset)
17
20
  host.current.scrollTop = range.offset; }, [range.offset]);
18
21
  useLayoutEffect(() => { if (moveFocus.current) {
19
22
  document.getElementById(`${id}-${selected}`)?.focus({ preventScroll: true });
20
23
  moveFocus.current = false;
21
24
  } }, [selected, id]);
25
+ const previousKeys = useRef(keys);
26
+ useLayoutEffect(() => {
27
+ const previous = previousKeys.current;
28
+ previousKeys.current = keys;
29
+ if (focusedKey === null || keys.includes(focusedKey) || document.activeElement !== document.body)
30
+ return;
31
+ // Removing a focused DOM subtree sends focus to body without a blur event.
32
+ // Continue at its former position (or the previous last row), never steal
33
+ // focus from a control the user deliberately reached outside this list.
34
+ const index = Math.min(Math.max(0, previous.indexOf(focusedKey)), Math.max(0, keys.length - 1));
35
+ setFocusedKey(null);
36
+ setActive({ key: keys[index] ?? null, index });
37
+ const viewport = host.current;
38
+ if (!viewport)
39
+ return;
40
+ if (!keys.length) {
41
+ moveFocus.current = false;
42
+ viewport.focus({ preventScroll: true });
43
+ return;
44
+ }
45
+ const top = index * rowHeight;
46
+ if (top < viewport.scrollTop)
47
+ viewport.scrollTop = top;
48
+ else if (top + rowHeight > viewport.scrollTop + height)
49
+ viewport.scrollTop = top + rowHeight - height;
50
+ setScrollTop(viewport.scrollTop);
51
+ moveFocus.current = true;
52
+ const row = document.getElementById(`${id}-${index}`);
53
+ if (row) {
54
+ row.focus({ preventScroll: true });
55
+ moveFocus.current = false;
56
+ }
57
+ }, [keys, focusedKey, id, rowHeight, height]);
22
58
  // Keep keyboard/descendant focus mounted when wheel scrolling moves it outside the window.
23
- const indices = [...new Set([...Array.from({ length: range.end - range.start }, (_, i) => i + range.start), ...(items.length ? [selected] : []), ...(focused !== null && focused < items.length ? [focused] : [])])].sort((a, b) => a - b);
59
+ const indices = [...new Set([...Array.from({ length: range.end - range.start }, (_, i) => i + range.start), ...(items.length ? [selected] : []), ...(focused >= 0 ? [focused] : [])])].sort((a, b) => a - b);
60
+ // Pointer or programmatic focus must become the keyboard origin too.
24
61
  return _jsx("div", { "data-hjm-virtual-list": true, className: "hjm-virtual-list", ref: host, role: "list", "aria-label": label, tabIndex: 0, style: { ...layoutStyle, overflowY: "auto", height, position: "relative" }, onScroll: event => setScrollTop(event.currentTarget.scrollTop), onKeyDown: event => {
25
62
  if (!items.length || !(event.target instanceof HTMLElement) || (event.target !== event.currentTarget && !event.target.hasAttribute("data-virtual-row")))
26
63
  return;
@@ -29,7 +66,7 @@ export function VirtualList({ items, keyExtractor, renderItem, rowHeight, height
29
66
  return;
30
67
  event.preventDefault();
31
68
  moveFocus.current = true;
32
- setActive(next);
69
+ setActive({ key: keys[next], index: next });
33
70
  const top = next * rowHeight;
34
71
  const viewport = event.currentTarget;
35
72
  if (top < viewport.scrollTop)
@@ -42,7 +79,7 @@ export function VirtualList({ items, keyExtractor, renderItem, rowHeight, height
42
79
  rendered.focus({ preventScroll: true });
43
80
  moveFocus.current = false;
44
81
  }
45
- }, children: !items.length ? empty : _jsx("div", { style: { position: "relative", height: range.totalHeight }, children: indices.map(index => _jsx("div", { id: `${id}-${index}`, role: "listitem", "data-virtual-row": true, className: "hjm-virtual-list__row", tabIndex: index === selected ? 0 : -1, "aria-setsize": items.length, "aria-posinset": index + 1, onFocusCapture: () => setFocused(index), onBlurCapture: event => { if (!event.currentTarget.contains(event.relatedTarget))
46
- setFocused(null); }, style: { position: "absolute", top: index * rowHeight, height: rowHeight, width: "100%" }, children: renderItem(items[index], index) }, keys[index])) }) });
82
+ }, children: !items.length ? _jsx("div", { style: { minHeight: "100%", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", textAlign: "center" }, children: empty }) : _jsx("div", { style: { position: "relative", height: range.totalHeight }, children: indices.map(index => _jsx("div", { id: `${id}-${index}`, role: "listitem", "data-virtual-row": true, className: "hjm-virtual-list__row", tabIndex: index === selected ? 0 : -1, "aria-setsize": items.length, "aria-posinset": index + 1, onFocusCapture: () => { setFocusedKey(keys[index]); setActive({ key: keys[index], index }); }, onBlurCapture: event => { if (!event.currentTarget.contains(event.relatedTarget))
83
+ setFocusedKey(null); }, style: { position: "absolute", top: index * rowHeight, height: rowHeight, width: "100%" }, children: renderItem(items[index], index) }, keys[index])) }) });
47
84
  }
48
85
  //# sourceMappingURL=virtual-list.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtual-list.js","sourceRoot":"","sources":["../src/virtual-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useId, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { resolveVirtualWindow, validateListKeys } from \"@hjmds/design-contracts/components/virtual-list\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\nexport type VirtualListProps<T> = { items: readonly T[]; keyExtractor: (item: T) => string; renderItem: (item: T, index: number) => ReactNode; rowHeight: number; height: number; label: string; empty?: ReactNode; overscan?: number;\n /** Canonical layout-only placement on the scroll viewport. `height` stays the prop's: the window math reads it. */\n layoutStyle?: HjmCompositionStyleProp };\n/** Fixed-height rows are an explicit host contract; use List for unconstrained flowing copy. */\nexport function VirtualList<T>({ items, keyExtractor, renderItem, rowHeight, height, label, empty, overscan = 3, layoutStyle }: VirtualListProps<T>) {\n const keys = useMemo(() => items.map(keyExtractor), [items, keyExtractor]); validateListKeys(keys, label);\n const [scrollTop, setScrollTop] = useState(0);\n const [active, setActive] = useState(0);\n const [focused, setFocused] = useState<number | null>(null);\n const host = useRef<HTMLDivElement>(null); const moveFocus = useRef(false); const id = useId();\n const range = resolveVirtualWindow(items.length, rowHeight, height, scrollTop, overscan);\n const selected = Math.min(active, Math.max(0, items.length - 1));\n useLayoutEffect(() => { if (host.current && host.current.scrollTop !== range.offset) host.current.scrollTop = range.offset; }, [range.offset]);\n useLayoutEffect(() => { if (moveFocus.current) { document.getElementById(`${id}-${selected}`)?.focus({ preventScroll: true }); moveFocus.current = false; } }, [selected, id]);\n // Keep keyboard/descendant focus mounted when wheel scrolling moves it outside the window.\n const indices = [...new Set([...Array.from({ length: range.end - range.start }, (_, i) => i + range.start), ...(items.length ? [selected] : []), ...(focused !== null && focused < items.length ? [focused] : [])])].sort((a,b) => a-b);\n return <div data-hjm-virtual-list className=\"hjm-virtual-list\" ref={host} role=\"list\" aria-label={label} tabIndex={0}\n style={{ ...layoutStyle, overflowY: \"auto\", height, position: \"relative\" }} onScroll={event => setScrollTop(event.currentTarget.scrollTop)}\n onKeyDown={event => {\n if (!items.length || !(event.target instanceof HTMLElement) || (event.target !== event.currentTarget && !event.target.hasAttribute(\"data-virtual-row\"))) return;\n const next = event.key === \"ArrowDown\" ? Math.min(selected + 1, items.length - 1) : event.key === \"ArrowUp\" ? Math.max(0, selected - 1) : event.key === \"Home\" ? 0 : event.key === \"End\" ? items.length - 1 : null;\n if (next === null) return; event.preventDefault(); moveFocus.current = true; setActive(next);\n const top = next * rowHeight; const viewport = event.currentTarget;\n if (top < viewport.scrollTop) viewport.scrollTop = top;\n else if (top + rowHeight > viewport.scrollTop + height) viewport.scrollTop = top + rowHeight - height;\n setScrollTop(viewport.scrollTop);\n const rendered = document.getElementById(`${id}-${next}`);\n if (rendered) { rendered.focus({ preventScroll: true }); moveFocus.current = false; }\n }}>\n {!items.length ? empty : <div style={{ position: \"relative\", height: range.totalHeight }}>\n {indices.map(index => <div key={keys[index]} id={`${id}-${index}`} role=\"listitem\" data-virtual-row className=\"hjm-virtual-list__row\" tabIndex={index === selected ? 0 : -1} aria-setsize={items.length} aria-posinset={index + 1}\n onFocusCapture={() => setFocused(index)} onBlurCapture={event => { if (!event.currentTarget.contains(event.relatedTarget)) setFocused(null); }}\n style={{ position: \"absolute\", top: index * rowHeight, height: rowHeight, width: \"100%\" }}>{renderItem(items[index]!, index)}</div>)}\n </div>}\n </div>;\n}\n"]}
1
+ {"version":3,"file":"virtual-list.js","sourceRoot":"","sources":["../src/virtual-list.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useId, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { resolveVirtualWindow, validateListKeys } from \"@hjmds/design-contracts/components/virtual-list\";\nimport type { HjmCompositionStyleProp } from \"./composition-style.js\";\nexport type VirtualListProps<T> = { items: readonly T[]; keyExtractor: (item: T) => string; renderItem: (item: T, index: number) => ReactNode; rowHeight: number; height: number; label: string; empty?: ReactNode; overscan?: number;\n /** Canonical layout-only placement on the scroll viewport. `height` stays the prop's: the window math reads it. */\n layoutStyle?: HjmCompositionStyleProp };\n/** Fixed-height rows are an explicit host contract; use List for unconstrained flowing copy. */\nexport function VirtualList<T>({ items, keyExtractor, renderItem, rowHeight, height, label, empty, overscan = 3, layoutStyle }: VirtualListProps<T>) {\n const keys = useMemo(() => items.map(keyExtractor), [items, keyExtractor]); validateListKeys(keys, label);\n const [scrollTop, setScrollTop] = useState(0);\n const [active, setActive] = useState<{ key: string | null; index: number }>({ key: null, index: 0 });\n const [focusedKey, setFocusedKey] = useState<string | null>(null);\n const host = useRef<HTMLDivElement>(null); const moveFocus = useRef(false); const id = useId();\n const range = resolveVirtualWindow(items.length, rowHeight, height, scrollTop, overscan);\n // Stable keys preserve the focused item when filtering/reordering moves it outside the window.\n const activeIndex = active.key === null ? -1 : keys.indexOf(active.key);\n const selected = activeIndex >= 0 ? activeIndex : Math.min(active.index, Math.max(0, items.length - 1));\n const focused = focusedKey === null ? -1 : keys.indexOf(focusedKey);\n useLayoutEffect(() => { if (host.current && host.current.scrollTop !== range.offset) host.current.scrollTop = range.offset; }, [range.offset]);\n useLayoutEffect(() => { if (moveFocus.current) { document.getElementById(`${id}-${selected}`)?.focus({ preventScroll: true }); moveFocus.current = false; } }, [selected, id]);\n const previousKeys = useRef(keys);\n useLayoutEffect(() => {\n const previous = previousKeys.current;\n previousKeys.current = keys;\n if (focusedKey === null || keys.includes(focusedKey) || document.activeElement !== document.body) return;\n // Removing a focused DOM subtree sends focus to body without a blur event.\n // Continue at its former position (or the previous last row), never steal\n // focus from a control the user deliberately reached outside this list.\n const index = Math.min(Math.max(0, previous.indexOf(focusedKey)), Math.max(0, keys.length - 1));\n setFocusedKey(null);\n setActive({ key: keys[index] ?? null, index });\n const viewport = host.current;\n if (!viewport) return;\n if (!keys.length) { moveFocus.current = false; viewport.focus({ preventScroll: true }); return; }\n const top = index * rowHeight;\n if (top < viewport.scrollTop) viewport.scrollTop = top;\n else if (top + rowHeight > viewport.scrollTop + height) viewport.scrollTop = top + rowHeight - height;\n setScrollTop(viewport.scrollTop);\n moveFocus.current = true;\n const row = document.getElementById(`${id}-${index}`);\n if (row) { row.focus({ preventScroll: true }); moveFocus.current = false; }\n }, [keys, focusedKey, id, rowHeight, height]);\n // Keep keyboard/descendant focus mounted when wheel scrolling moves it outside the window.\n const indices = [...new Set([...Array.from({ length: range.end - range.start }, (_, i) => i + range.start), ...(items.length ? [selected] : []), ...(focused >= 0 ? [focused] : [])])].sort((a,b) => a-b);\n // Pointer or programmatic focus must become the keyboard origin too.\n return <div data-hjm-virtual-list className=\"hjm-virtual-list\" ref={host} role=\"list\" aria-label={label} tabIndex={0}\n style={{ ...layoutStyle, overflowY: \"auto\", height, position: \"relative\" }} onScroll={event => setScrollTop(event.currentTarget.scrollTop)}\n onKeyDown={event => {\n if (!items.length || !(event.target instanceof HTMLElement) || (event.target !== event.currentTarget && !event.target.hasAttribute(\"data-virtual-row\"))) return;\n const next = event.key === \"ArrowDown\" ? Math.min(selected + 1, items.length - 1) : event.key === \"ArrowUp\" ? Math.max(0, selected - 1) : event.key === \"Home\" ? 0 : event.key === \"End\" ? items.length - 1 : null;\n if (next === null) return; event.preventDefault(); moveFocus.current = true; setActive({ key: keys[next]!, index: next });\n const top = next * rowHeight; const viewport = event.currentTarget;\n if (top < viewport.scrollTop) viewport.scrollTop = top;\n else if (top + rowHeight > viewport.scrollTop + height) viewport.scrollTop = top + rowHeight - height;\n setScrollTop(viewport.scrollTop);\n const rendered = document.getElementById(`${id}-${next}`);\n if (rendered) { rendered.focus({ preventScroll: true }); moveFocus.current = false; }\n }}>\n {!items.length ? <div style={{ minHeight: \"100%\", display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", textAlign: \"center\" }}>{empty}</div> : <div style={{ position: \"relative\", height: range.totalHeight }}>\n {indices.map(index => <div key={keys[index]} id={`${id}-${index}`} role=\"listitem\" data-virtual-row className=\"hjm-virtual-list__row\" tabIndex={index === selected ? 0 : -1} aria-setsize={items.length} aria-posinset={index + 1}\n onFocusCapture={() => { setFocusedKey(keys[index]!); setActive({ key: keys[index]!, index }); }} onBlurCapture={event => { if (!event.currentTarget.contains(event.relatedTarget)) setFocusedKey(null); }}\n style={{ position: \"absolute\", top: index * rowHeight, height: rowHeight, width: \"100%\" }}>{renderItem(items[index]!, index)}</div>)}\n </div>}\n </div>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hjmds/react",
3
- "version": "1.17.2",
3
+ "version": "1.19.0",
4
4
  "description": "Accessible React 19 renderers for HJM design contracts.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -23,6 +23,11 @@
23
23
  "import": "./dist/index.js",
24
24
  "default": "./dist/index.js"
25
25
  },
26
+ "./status-text": {
27
+ "types": "./dist/status-text.d.ts",
28
+ "import": "./dist/status-text.js",
29
+ "default": "./dist/status-text.js"
30
+ },
26
31
  "./provider": {
27
32
  "types": "./dist/provider.d.ts",
28
33
  "import": "./dist/provider.js",
@@ -512,6 +517,11 @@
512
517
  "types": "./dist/collection-rail.d.ts",
513
518
  "import": "./dist/collection-rail.js",
514
519
  "default": "./dist/collection-rail.js"
520
+ },
521
+ "./activity-chip": {
522
+ "types": "./dist/activity-chip.d.ts",
523
+ "import": "./dist/activity-chip.js",
524
+ "default": "./dist/activity-chip.js"
515
525
  }
516
526
  },
517
527
  "files": [
@@ -521,7 +531,7 @@
521
531
  "THIRD_PARTY_NOTICES.md"
522
532
  ],
523
533
  "peerDependencies": {
524
- "@hjmds/design-contracts": ">=1.17.0 <1.18.0",
534
+ "@hjmds/design-contracts": ">=1.19.0 <1.20.0",
525
535
  "react": ">=19.0.0",
526
536
  "react-dom": ">=19.0.0",
527
537
  "@number-flow/react": "0.6.2",
@@ -558,7 +568,7 @@
558
568
  "blobatar": "2.7.0",
559
569
  "@blobatar/react": "2.7.0",
560
570
  "lucide-react": "1.49.0",
561
- "@hjmds/design-contracts": "1.17.2"
571
+ "@hjmds/design-contracts": "1.19.0"
562
572
  },
563
573
  "peerDependenciesMeta": {
564
574
  "@number-flow/react": {