@allxsmith/bestax-bulma 5.46.0 → 5.48.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 (33) hide show
  1. package/dist/bestax.css +1 -1
  2. package/dist/bestax.css.map +1 -1
  3. package/dist/esm/components/ScrollArea.js +225 -0
  4. package/dist/esm/components/ScrollArea.js.map +1 -0
  5. package/dist/esm/components/TreeView.js +5 -2
  6. package/dist/esm/components/TreeView.js.map +1 -1
  7. package/dist/esm/helpers/useTreeView.js +64 -9
  8. package/dist/esm/helpers/useTreeView.js.map +1 -1
  9. package/dist/extras.css +1 -1
  10. package/dist/extras.css.map +1 -1
  11. package/dist/index.cjs +283 -10
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.esm.js +1 -0
  14. package/dist/index.esm.js.map +1 -1
  15. package/dist/types/components/ScrollArea.d.ts +105 -0
  16. package/dist/types/components/TreeView.d.ts +2 -0
  17. package/dist/types/helpers/useTreeView.d.ts +19 -3
  18. package/dist/types/index.d.ts +1 -0
  19. package/dist/types-cjs/components/ScrollArea.d.ts +105 -0
  20. package/dist/types-cjs/components/TreeView.d.ts +2 -0
  21. package/dist/types-cjs/helpers/useTreeView.d.ts +19 -3
  22. package/dist/types-cjs/index.d.ts +1 -0
  23. package/dist/versions/bestax-no-dark-mode.css +1 -1
  24. package/dist/versions/bestax-no-dark-mode.css.map +1 -1
  25. package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
  26. package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
  27. package/dist/versions/bestax-no-helpers.css +1 -1
  28. package/dist/versions/bestax-no-helpers.css.map +1 -1
  29. package/dist/versions/bestax-prefixed.css +1 -1
  30. package/dist/versions/bestax-prefixed.css.map +1 -1
  31. package/package.json +2 -1
  32. package/src/scss/components/_index.scss +1 -0
  33. package/src/scss/components/_scrollarea.scss +248 -0
@@ -0,0 +1,225 @@
1
+ /* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { forwardRef, useEffect, useRef, useCallback, useState } from 'react';
4
+ import { usePrefixedClassNames, classNames } from '../helpers/classNames.js';
5
+ import { useBulmaClasses } from '../helpers/useBulmaClasses.js';
6
+ import { useClientLayoutEffect } from '../helpers/useClientLayoutEffect.js';
7
+ import { useLatestRef } from '../helpers/useLatestRef.js';
8
+ import { warnOnce } from '../helpers/devWarnings.js';
9
+
10
+ const EDGES = ['top', 'bottom', 'start', 'end'];
11
+ // Layout can land a fraction of a pixel short of an edge, so anything closer
12
+ // than this counts as there.
13
+ const SLACK = 1;
14
+ const sameMeasure = (a, b) => !!a &&
15
+ a.overflowing === b.overflowing &&
16
+ a.barInline === b.barInline &&
17
+ a.barBlock === b.barBlock &&
18
+ EDGES.every(edge => a.edges[edge] === b.edges[edge]);
19
+ /**
20
+ * The `ScrollArea` component renders a box whose content scrolls inside it, on the browser's own scrollbars in Bulma's colors.
21
+ *
22
+ * The bars are the system's, made thin and colored from Bulma's tokens, so
23
+ * they follow dark mode and keep the system's own settings. While the
24
+ * content overflows, the viewport is a region you can tab to and scroll by
25
+ * keyboard, Safari included, named by `aria-label` or `aria-labelledby`.
26
+ * While it fits, it's a plain box. Setting the bars' colors turns off any
27
+ * `::-webkit-scrollbar` styles inside it.
28
+ *
29
+ * @function
30
+ * @param {ScrollAreaProps} props - Props for the ScrollArea component.
31
+ * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the viewport.
32
+ * @returns {JSX.Element} The rendered scroll area.
33
+ *
34
+ * @example
35
+ * <ScrollArea height={240} aria-label="Release notes">
36
+ * <Content>…</Content>
37
+ * </ScrollArea>
38
+ *
39
+ * @example
40
+ * <ScrollArea maxHeight="20rem" aria-label="Messages" hasFade>
41
+ * …
42
+ * </ScrollArea>
43
+ */
44
+ const ScrollArea = forwardRef(({ className, style, textColor, bgColor, height, maxHeight, orientation = 'both', showScrollbars = 'auto', hasStableGutter, hasFade, onEdgeReach, onScroll, onFocus, onBlur, children, ...props }, ref) => {
45
+ const { bulmaHelperClasses, rest } = useBulmaClasses({
46
+ color: textColor,
47
+ backgroundColor: bgColor,
48
+ ...props,
49
+ });
50
+ const frameClass = usePrefixedClassNames('scroll-area', {
51
+ 'is-vertical': orientation === 'vertical',
52
+ 'is-horizontal': orientation === 'horizontal',
53
+ 'has-hover-scrollbars': showScrollbars === 'hover',
54
+ 'has-visible-scrollbars': showScrollbars === 'always',
55
+ 'has-stable-gutter': !!hasStableGutter,
56
+ 'has-fade': !!hasFade,
57
+ });
58
+ const viewportClass = usePrefixedClassNames('scroll-area-viewport');
59
+ const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);
60
+ useEffect(() => {
61
+ if (!named) {
62
+ warnOnce('ScrollArea:unnamed', '[bestax-bulma] <ScrollArea> has no accessible name. Pass ' +
63
+ 'aria-label or aria-labelledby, so a screen reader can say what ' +
64
+ 'took focus when the area overflows.');
65
+ }
66
+ }, [named]);
67
+ const viewportRef = useRef(null);
68
+ const setRefs = useCallback((node) => {
69
+ viewportRef.current = node;
70
+ if (typeof ref === 'function')
71
+ return ref(node);
72
+ if (ref)
73
+ ref.current = node;
74
+ }, [ref]);
75
+ // Nothing is known about the overflow until the client measures it, so
76
+ // the server renders a plain scroller and hydrates as one.
77
+ const [measured, setMeasured] = useState(null);
78
+ const [focused, setFocused] = useState(false);
79
+ // The last measurement, read by the scroll handler to see which edges
80
+ // it just reached, and the direction and bar sizes, which only change
81
+ // with layout, so a scroll doesn't read computed styles.
82
+ const lastRef = useRef(null);
83
+ const layoutRef = useRef({ rtl: false, barInline: 0, barBlock: 0 });
84
+ const onEdgeReachRef = useLatestRef(onEdgeReach);
85
+ const measure = useCallback((fromScroll) => {
86
+ const el = viewportRef.current;
87
+ /* istanbul ignore next: measuring only runs while it's mounted */
88
+ if (!el)
89
+ return;
90
+ const scrollsY = orientation !== 'horizontal';
91
+ const scrollsX = orientation !== 'vertical';
92
+ const maxTop = el.scrollHeight - el.clientHeight;
93
+ const maxLeft = el.scrollWidth - el.clientWidth;
94
+ const overflowY = scrollsY && maxTop > SLACK;
95
+ const overflowX = scrollsX && maxLeft > SLACK;
96
+ // Right to left, scrollLeft runs from 0 at the start to negative.
97
+ const fromStart = layoutRef.current.rtl
98
+ ? -el.scrollLeft
99
+ : el.scrollLeft;
100
+ const next = {
101
+ overflowing: overflowY || overflowX,
102
+ edges: {
103
+ top: overflowY && el.scrollTop > SLACK,
104
+ bottom: overflowY && maxTop - el.scrollTop > SLACK,
105
+ start: overflowX && fromStart > SLACK,
106
+ end: overflowX && maxLeft - fromStart > SLACK,
107
+ },
108
+ barInline: layoutRef.current.barInline,
109
+ barBlock: layoutRef.current.barBlock,
110
+ };
111
+ const last = lastRef.current;
112
+ lastRef.current = next;
113
+ if (fromScroll && last) {
114
+ for (const edge of EDGES) {
115
+ if (last.edges[edge] && !next.edges[edge]) {
116
+ onEdgeReachRef.current?.(edge);
117
+ }
118
+ }
119
+ }
120
+ setMeasured(prev => (sameMeasure(prev, next) ? prev : next));
121
+ }, [orientation, onEdgeReachRef]);
122
+ // Layout: the direction and the bars' sizes, then the overflow. It runs
123
+ // before paint, and again whenever the viewport or anything in it
124
+ // changes size, or the children change.
125
+ useClientLayoutEffect(() => {
126
+ const el = viewportRef.current;
127
+ /* istanbul ignore next: the effect only runs once it's mounted */
128
+ if (!el)
129
+ return;
130
+ const relayout = () => {
131
+ const cs = getComputedStyle(el);
132
+ const px = (value) => parseFloat(value) || 0;
133
+ layoutRef.current = {
134
+ rtl: cs.direction === 'rtl',
135
+ barInline: Math.max(0, el.offsetWidth -
136
+ el.clientWidth -
137
+ px(cs.borderLeftWidth) -
138
+ px(cs.borderRightWidth)),
139
+ barBlock: Math.max(0, el.offsetHeight -
140
+ el.clientHeight -
141
+ px(cs.borderTopWidth) -
142
+ px(cs.borderBottomWidth)),
143
+ };
144
+ measure(false);
145
+ };
146
+ relayout();
147
+ if (typeof ResizeObserver === 'undefined')
148
+ return;
149
+ const resizes = new ResizeObserver(relayout);
150
+ const observeAll = () => {
151
+ resizes.disconnect();
152
+ resizes.observe(el);
153
+ for (const child of Array.from(el.children))
154
+ resizes.observe(child);
155
+ };
156
+ observeAll();
157
+ // Content that changes can overflow before any size is reported: a
158
+ // child added or taken away, or text React rewrites in place, which
159
+ // resizes no element when the area's height is fixed, and inline
160
+ // children, which a ResizeObserver doesn't report at all.
161
+ const mutations = typeof MutationObserver === 'undefined'
162
+ ? null
163
+ : new MutationObserver(records => {
164
+ if (records.some(record => record.target === el && record.type === 'childList')) {
165
+ observeAll();
166
+ }
167
+ relayout();
168
+ });
169
+ mutations?.observe(el, {
170
+ childList: true,
171
+ characterData: true,
172
+ subtree: true,
173
+ });
174
+ return () => {
175
+ resizes.disconnect();
176
+ mutations?.disconnect();
177
+ };
178
+ }, [measure]);
179
+ const handleScroll = (event) => {
180
+ onScroll?.(event);
181
+ measure(true);
182
+ };
183
+ const handleFocus = (event) => {
184
+ onFocus?.(event);
185
+ if (event.target === event.currentTarget)
186
+ setFocused(true);
187
+ };
188
+ const handleBlur = (event) => {
189
+ onBlur?.(event);
190
+ if (event.target === event.currentTarget)
191
+ setFocused(false);
192
+ };
193
+ // A tab stop while it overflows. Taking tabIndex off the focused element
194
+ // would drop focus to the page, so a viewport that stops overflowing
195
+ // keeps it until focus leaves.
196
+ const isRegion = !!measured?.overflowing || focused;
197
+ // The name goes with the role: ARIA prohibits naming a plain div.
198
+ const { 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ...viewportRest } = rest;
199
+ const edgeAttributes = {};
200
+ if (measured) {
201
+ for (const edge of EDGES) {
202
+ if (measured.edges[edge])
203
+ edgeAttributes[`data-overflow-${edge}`] = '';
204
+ }
205
+ }
206
+ const viewportStyle = measured
207
+ ? {
208
+ '--scroll-area-bar-inline-size': `${measured.barInline}px`,
209
+ '--scroll-area-bar-block-size': `${measured.barBlock}px`,
210
+ }
211
+ : undefined;
212
+ return (jsx("div", { className: classNames(frameClass, bulmaHelperClasses, className), style: height !== undefined || maxHeight !== undefined
213
+ ? {
214
+ ...style,
215
+ // Only what's given, so `maxHeight` alone keeps a height
216
+ // the style sets.
217
+ ...(height !== undefined && { height }),
218
+ ...(maxHeight !== undefined && { maxHeight }),
219
+ }
220
+ : style, children: jsx("div", { ref: setRefs, className: viewportClass, style: viewportStyle, ...viewportRest, ...edgeAttributes, role: isRegion ? 'region' : undefined, "aria-label": isRegion ? ariaLabel : undefined, "aria-labelledby": isRegion ? ariaLabelledby : undefined, tabIndex: isRegion ? 0 : undefined, onScroll: handleScroll, onFocus: handleFocus, onBlur: handleBlur, children: children }) }));
221
+ });
222
+ ScrollArea.displayName = 'ScrollArea';
223
+
224
+ export { ScrollArea };
225
+ //# sourceMappingURL=ScrollArea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScrollArea.js","sources":["../../../src/components/ScrollArea.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport {\n useBulmaClasses,\n BulmaClassesProps,\n validColors,\n} from '../helpers/useBulmaClasses';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport { useLatestRef } from '../helpers/useLatestRef';\nimport { warnOnce } from '../helpers/devWarnings';\n\n/** Which way the content scrolls. */\nexport type ScrollAreaOrientation = 'vertical' | 'horizontal' | 'both';\n\n/** When the scrollbars show. */\nexport type ScrollAreaScrollbars = 'auto' | 'hover' | 'always';\n\n/** An edge of the content: the block's top and bottom, the line's start and end. */\nexport type ScrollAreaEdge = 'top' | 'bottom' | 'start' | 'end';\n\nconst EDGES: readonly ScrollAreaEdge[] = ['top', 'bottom', 'start', 'end'];\n\n// Layout can land a fraction of a pixel short of an edge, so anything closer\n// than this counts as there.\nconst SLACK = 1;\n\n/**\n * Props for the ScrollArea component.\n *\n * @helperProps The helper props go on the outer `<div>`, the `.scroll-area`\n * frame, with `className` and `style`. The `ref`, the name, `onScroll` and\n * the other attributes go on the `<div>` inside it, the viewport that\n * scrolls.\n *\n * @extraProp {React.Ref<HTMLDivElement>} [ref] - Ref forwarded to the viewport, the element that scrolls, for `scrollTo` and the like.\n * @extraProp {string} [aria-label] - Names the region, such as \"Release notes\". It needs a name, so pass this or `aria-labelledby`, and a development build warns when it has neither. The name is rendered with the region's role, while the content overflows, since ARIA doesn't allow naming a plain `div`.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading that names the region, in place of `aria-label`.\n * @extraProp {React.UIEventHandler<HTMLDivElement>} [onScroll] - Called as the viewport scrolls.\n */\nexport interface ScrollAreaProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'role' | 'tabIndex'>,\n Omit<BulmaClassesProps, 'color' | 'backgroundColor'> {\n /** Additional CSS classes for the outer frame. */\n className?: string;\n /** Inline styles for the outer frame. */\n style?: React.CSSProperties;\n /** What scrolls. */\n children?: React.ReactNode;\n /**\n * The area's height. A number is pixels, and a string is any CSS length\n * (`\"20rem\"`, `\"50vh\"`, `\"100%\"`).\n */\n height?: number | string;\n /**\n * The height it grows to with its content, then scrolls past. A number is\n * pixels. Use it in place of `height` for an area that's only as tall as\n * it needs to be.\n */\n maxHeight?: number | string;\n /**\n * Which way it scrolls. `vertical` and `horizontal` clip the other axis,\n * and `both` scrolls whichever overflows.\n */\n orientation?: ScrollAreaOrientation;\n /**\n * When the scrollbars show. `auto`: whenever the content overflows, the\n * way the system shows them. `hover`: colorless until the pointer is over\n * the area or focus is in it, which hides that the area scrolls until\n * then, so keep it for places where the layout makes that plain.\n * `always`: on every axis it scrolls, even when the content fits. Where the\n * system draws its bars over the content (macOS by default, phones and\n * tablets), they still show only while you scroll, and in forced colors\n * they always show.\n */\n showScrollbars?: ScrollAreaScrollbars;\n /**\n * Keeps room for the vertical scrollbar whether or not it shows, so the\n * content doesn't shift when it starts to overflow. It does nothing where\n * the system draws its bars over the content.\n */\n hasStableGutter?: boolean;\n /**\n * Fades the content out toward each edge that has more past it, without\n * fading the scrollbar. It's off in forced colors and when the user asks\n * for more contrast.\n */\n hasFade?: boolean;\n /**\n * Called with the edge when scrolling reaches it, once each time it\n * arrives, such as to load older messages at the top. It isn't called for\n * the edge the area starts at, or when the content changes size.\n */\n onEdgeReach?: (edge: ScrollAreaEdge) => void;\n /** Text color helper. */\n textColor?: (typeof validColors)[number] | 'inherit' | 'current';\n /** Background color helper. */\n bgColor?: (typeof validColors)[number] | 'inherit' | 'current';\n}\n\n/** What a measurement of the viewport found. */\ninterface Measured {\n /** Overflows on an axis it scrolls. */\n overflowing: boolean;\n /** Each edge that has more content past it. */\n edges: Record<ScrollAreaEdge, boolean>;\n /** The vertical scrollbar's width, and the horizontal one's height. */\n barInline: number;\n barBlock: number;\n}\n\nconst sameMeasure = (a: Measured | null, b: Measured): boolean =>\n !!a &&\n a.overflowing === b.overflowing &&\n a.barInline === b.barInline &&\n a.barBlock === b.barBlock &&\n EDGES.every(edge => a.edges[edge] === b.edges[edge]);\n\n/**\n * The `ScrollArea` component renders a box whose content scrolls inside it, on the browser's own scrollbars in Bulma's colors.\n *\n * The bars are the system's, made thin and colored from Bulma's tokens, so\n * they follow dark mode and keep the system's own settings. While the\n * content overflows, the viewport is a region you can tab to and scroll by\n * keyboard, Safari included, named by `aria-label` or `aria-labelledby`.\n * While it fits, it's a plain box. Setting the bars' colors turns off any\n * `::-webkit-scrollbar` styles inside it.\n *\n * @function\n * @param {ScrollAreaProps} props - Props for the ScrollArea component.\n * @param {React.Ref<HTMLDivElement>} ref - Forwarded ref to the viewport.\n * @returns {JSX.Element} The rendered scroll area.\n *\n * @example\n * <ScrollArea height={240} aria-label=\"Release notes\">\n * <Content>…</Content>\n * </ScrollArea>\n *\n * @example\n * <ScrollArea maxHeight=\"20rem\" aria-label=\"Messages\" hasFade>\n * …\n * </ScrollArea>\n */\nexport const ScrollArea = forwardRef<HTMLDivElement, ScrollAreaProps>(\n (\n {\n className,\n style,\n textColor,\n bgColor,\n height,\n maxHeight,\n orientation = 'both',\n showScrollbars = 'auto',\n hasStableGutter,\n hasFade,\n onEdgeReach,\n onScroll,\n onFocus,\n onBlur,\n children,\n ...props\n },\n ref\n ) => {\n const { bulmaHelperClasses, rest } = useBulmaClasses({\n color: textColor,\n backgroundColor: bgColor,\n ...props,\n });\n\n const frameClass = usePrefixedClassNames('scroll-area', {\n 'is-vertical': orientation === 'vertical',\n 'is-horizontal': orientation === 'horizontal',\n 'has-hover-scrollbars': showScrollbars === 'hover',\n 'has-visible-scrollbars': showScrollbars === 'always',\n 'has-stable-gutter': !!hasStableGutter,\n 'has-fade': !!hasFade,\n });\n const viewportClass = usePrefixedClassNames('scroll-area-viewport');\n\n const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'ScrollArea:unnamed',\n '[bestax-bulma] <ScrollArea> has no accessible name. Pass ' +\n 'aria-label or aria-labelledby, so a screen reader can say what ' +\n 'took focus when the area overflows.'\n );\n }\n }, [named]);\n\n const viewportRef = useRef<HTMLDivElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLDivElement | null) => {\n viewportRef.current = node;\n if (typeof ref === 'function') return ref(node);\n if (ref) ref.current = node;\n },\n [ref]\n );\n\n // Nothing is known about the overflow until the client measures it, so\n // the server renders a plain scroller and hydrates as one.\n const [measured, setMeasured] = useState<Measured | null>(null);\n const [focused, setFocused] = useState(false);\n // The last measurement, read by the scroll handler to see which edges\n // it just reached, and the direction and bar sizes, which only change\n // with layout, so a scroll doesn't read computed styles.\n const lastRef = useRef<Measured | null>(null);\n const layoutRef = useRef({ rtl: false, barInline: 0, barBlock: 0 });\n const onEdgeReachRef = useLatestRef(onEdgeReach);\n\n const measure = useCallback(\n (fromScroll: boolean) => {\n const el = viewportRef.current;\n /* istanbul ignore next: measuring only runs while it's mounted */\n if (!el) return;\n const scrollsY = orientation !== 'horizontal';\n const scrollsX = orientation !== 'vertical';\n const maxTop = el.scrollHeight - el.clientHeight;\n const maxLeft = el.scrollWidth - el.clientWidth;\n const overflowY = scrollsY && maxTop > SLACK;\n const overflowX = scrollsX && maxLeft > SLACK;\n // Right to left, scrollLeft runs from 0 at the start to negative.\n const fromStart = layoutRef.current.rtl\n ? -el.scrollLeft\n : el.scrollLeft;\n const next: Measured = {\n overflowing: overflowY || overflowX,\n edges: {\n top: overflowY && el.scrollTop > SLACK,\n bottom: overflowY && maxTop - el.scrollTop > SLACK,\n start: overflowX && fromStart > SLACK,\n end: overflowX && maxLeft - fromStart > SLACK,\n },\n barInline: layoutRef.current.barInline,\n barBlock: layoutRef.current.barBlock,\n };\n const last = lastRef.current;\n lastRef.current = next;\n if (fromScroll && last) {\n for (const edge of EDGES) {\n if (last.edges[edge] && !next.edges[edge]) {\n onEdgeReachRef.current?.(edge);\n }\n }\n }\n setMeasured(prev => (sameMeasure(prev, next) ? prev : next));\n },\n [orientation, onEdgeReachRef]\n );\n\n // Layout: the direction and the bars' sizes, then the overflow. It runs\n // before paint, and again whenever the viewport or anything in it\n // changes size, or the children change.\n useClientLayoutEffect(() => {\n const el = viewportRef.current;\n /* istanbul ignore next: the effect only runs once it's mounted */\n if (!el) return;\n const relayout = () => {\n const cs = getComputedStyle(el);\n const px = (value: string) => parseFloat(value) || 0;\n layoutRef.current = {\n rtl: cs.direction === 'rtl',\n barInline: Math.max(\n 0,\n el.offsetWidth -\n el.clientWidth -\n px(cs.borderLeftWidth) -\n px(cs.borderRightWidth)\n ),\n barBlock: Math.max(\n 0,\n el.offsetHeight -\n el.clientHeight -\n px(cs.borderTopWidth) -\n px(cs.borderBottomWidth)\n ),\n };\n measure(false);\n };\n relayout();\n if (typeof ResizeObserver === 'undefined') return;\n const resizes = new ResizeObserver(relayout);\n const observeAll = () => {\n resizes.disconnect();\n resizes.observe(el);\n for (const child of Array.from(el.children)) resizes.observe(child);\n };\n observeAll();\n // Content that changes can overflow before any size is reported: a\n // child added or taken away, or text React rewrites in place, which\n // resizes no element when the area's height is fixed, and inline\n // children, which a ResizeObserver doesn't report at all.\n const mutations =\n typeof MutationObserver === 'undefined'\n ? null\n : new MutationObserver(records => {\n if (\n records.some(\n record => record.target === el && record.type === 'childList'\n )\n ) {\n observeAll();\n }\n relayout();\n });\n mutations?.observe(el, {\n childList: true,\n characterData: true,\n subtree: true,\n });\n return () => {\n resizes.disconnect();\n mutations?.disconnect();\n };\n }, [measure]);\n\n const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {\n onScroll?.(event);\n measure(true);\n };\n const handleFocus = (event: React.FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n if (event.target === event.currentTarget) setFocused(true);\n };\n const handleBlur = (event: React.FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n if (event.target === event.currentTarget) setFocused(false);\n };\n\n // A tab stop while it overflows. Taking tabIndex off the focused element\n // would drop focus to the page, so a viewport that stops overflowing\n // keeps it until focus leaves.\n const isRegion = !!measured?.overflowing || focused;\n // The name goes with the role: ARIA prohibits naming a plain div.\n const {\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n ...viewportRest\n } = rest;\n const edgeAttributes: Record<string, string> = {};\n if (measured) {\n for (const edge of EDGES) {\n if (measured.edges[edge]) edgeAttributes[`data-overflow-${edge}`] = '';\n }\n }\n const viewportStyle = measured\n ? ({\n '--scroll-area-bar-inline-size': `${measured.barInline}px`,\n '--scroll-area-bar-block-size': `${measured.barBlock}px`,\n } as React.CSSProperties)\n : undefined;\n\n return (\n <div\n className={classNames(frameClass, bulmaHelperClasses, className)}\n style={\n height !== undefined || maxHeight !== undefined\n ? {\n ...style,\n // Only what's given, so `maxHeight` alone keeps a height\n // the style sets.\n ...(height !== undefined && { height }),\n ...(maxHeight !== undefined && { maxHeight }),\n }\n : style\n }\n >\n <div\n ref={setRefs}\n className={viewportClass}\n style={viewportStyle}\n {...viewportRest}\n {...edgeAttributes}\n role={isRegion ? 'region' : undefined}\n aria-label={isRegion ? ariaLabel : undefined}\n aria-labelledby={isRegion ? ariaLabelledby : undefined}\n tabIndex={isRegion ? 0 : undefined}\n onScroll={handleScroll}\n onFocus={handleFocus}\n onBlur={handleBlur}\n >\n {children}\n </div>\n </div>\n );\n }\n);\n\nScrollArea.displayName = 'ScrollArea';\n\nexport default ScrollArea;\n"],"names":["_jsx"],"mappings":";;;;;;;;;AA0BA,MAAM,KAAK,GAA8B,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC;AAE1E;AACA;AACA,MAAM,KAAK,GAAG,CAAC;AAuFf,MAAM,WAAW,GAAG,CAAC,CAAkB,EAAE,CAAW,KAClD,CAAC,CAAC,CAAC;AACH,IAAA,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;AAC/B,IAAA,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS;AAC3B,IAAA,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;IACzB,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;AAwBG;MACU,UAAU,GAAG,UAAU,CAClC,CACE,EACE,SAAS,EACT,KAAK,EACL,SAAS,EACT,OAAO,EACP,MAAM,EACN,SAAS,EACT,WAAW,GAAG,MAAM,EACpB,cAAc,GAAG,MAAM,EACvB,eAAe,EACf,OAAO,EACP,WAAW,EACX,QAAQ,EACR,OAAO,EACP,MAAM,EACN,QAAQ,EACR,GAAG,KAAK,EACT,EACD,GAAG,KACD;AACF,IAAA,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,eAAe,CAAC;AACnD,QAAA,KAAK,EAAE,SAAS;AAChB,QAAA,eAAe,EAAE,OAAO;AACxB,QAAA,GAAG,KAAK;AACT,KAAA,CAAC;AAEF,IAAA,MAAM,UAAU,GAAG,qBAAqB,CAAC,aAAa,EAAE;QACtD,aAAa,EAAE,WAAW,KAAK,UAAU;QACzC,eAAe,EAAE,WAAW,KAAK,YAAY;QAC7C,sBAAsB,EAAE,cAAc,KAAK,OAAO;QAClD,wBAAwB,EAAE,cAAc,KAAK,QAAQ;QACrD,mBAAmB,EAAE,CAAC,CAAC,eAAe;QACtC,UAAU,EAAE,CAAC,CAAC,OAAO;AACtB,KAAA,CAAC;AACF,IAAA,MAAM,aAAa,GAAG,qBAAqB,CAAC,sBAAsB,CAAC;AAEnE,IAAA,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACpE,SAAS,CAAC,MAAK;QACb,IAAI,CAAC,KAAK,EAAE;YACV,QAAQ,CACN,oBAAoB,EACpB,2DAA2D;gBACzD,iEAAiE;AACjE,gBAAA,qCAAqC,CACxC;QACH;AACF,IAAA,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAEX,IAAA,MAAM,WAAW,GAAG,MAAM,CAAwB,IAAI,CAAC;AACvD,IAAA,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,IAA2B,KAAI;AAC9B,QAAA,WAAW,CAAC,OAAO,GAAG,IAAI;QAC1B,IAAI,OAAO,GAAG,KAAK,UAAU;AAAE,YAAA,OAAO,GAAG,CAAC,IAAI,CAAC;AAC/C,QAAA,IAAI,GAAG;AAAE,YAAA,GAAG,CAAC,OAAO,GAAG,IAAI;AAC7B,IAAA,CAAC,EACD,CAAC,GAAG,CAAC,CACN;;;IAID,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC;IAC/D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;;;;AAI7C,IAAA,MAAM,OAAO,GAAG,MAAM,CAAkB,IAAI,CAAC;AAC7C,IAAA,MAAM,SAAS,GAAG,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AACnE,IAAA,MAAM,cAAc,GAAG,YAAY,CAAC,WAAW,CAAC;AAEhD,IAAA,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,UAAmB,KAAI;AACtB,QAAA,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO;;AAE9B,QAAA,IAAI,CAAC,EAAE;YAAE;AACT,QAAA,MAAM,QAAQ,GAAG,WAAW,KAAK,YAAY;AAC7C,QAAA,MAAM,QAAQ,GAAG,WAAW,KAAK,UAAU;QAC3C,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY;QAChD,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW;AAC/C,QAAA,MAAM,SAAS,GAAG,QAAQ,IAAI,MAAM,GAAG,KAAK;AAC5C,QAAA,MAAM,SAAS,GAAG,QAAQ,IAAI,OAAO,GAAG,KAAK;;AAE7C,QAAA,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC;AAClC,cAAE,CAAC,EAAE,CAAC;AACN,cAAE,EAAE,CAAC,UAAU;AACjB,QAAA,MAAM,IAAI,GAAa;YACrB,WAAW,EAAE,SAAS,IAAI,SAAS;AACnC,YAAA,KAAK,EAAE;AACL,gBAAA,GAAG,EAAE,SAAS,IAAI,EAAE,CAAC,SAAS,GAAG,KAAK;gBACtC,MAAM,EAAE,SAAS,IAAI,MAAM,GAAG,EAAE,CAAC,SAAS,GAAG,KAAK;AAClD,gBAAA,KAAK,EAAE,SAAS,IAAI,SAAS,GAAG,KAAK;AACrC,gBAAA,GAAG,EAAE,SAAS,IAAI,OAAO,GAAG,SAAS,GAAG,KAAK;AAC9C,aAAA;AACD,YAAA,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,SAAS;AACtC,YAAA,QAAQ,EAAE,SAAS,CAAC,OAAO,CAAC,QAAQ;SACrC;AACD,QAAA,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO;AAC5B,QAAA,OAAO,CAAC,OAAO,GAAG,IAAI;AACtB,QAAA,IAAI,UAAU,IAAI,IAAI,EAAE;AACtB,YAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,gBAAA,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;AACzC,oBAAA,cAAc,CAAC,OAAO,GAAG,IAAI,CAAC;gBAChC;YACF;QACF;QACA,WAAW,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC;AAC9D,IAAA,CAAC,EACD,CAAC,WAAW,EAAE,cAAc,CAAC,CAC9B;;;;IAKD,qBAAqB,CAAC,MAAK;AACzB,QAAA,MAAM,EAAE,GAAG,WAAW,CAAC,OAAO;;AAE9B,QAAA,IAAI,CAAC,EAAE;YAAE;QACT,MAAM,QAAQ,GAAG,MAAK;AACpB,YAAA,MAAM,EAAE,GAAG,gBAAgB,CAAC,EAAE,CAAC;AAC/B,YAAA,MAAM,EAAE,GAAG,CAAC,KAAa,KAAK,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC;YACpD,SAAS,CAAC,OAAO,GAAG;AAClB,gBAAA,GAAG,EAAE,EAAE,CAAC,SAAS,KAAK,KAAK;gBAC3B,SAAS,EAAE,IAAI,CAAC,GAAG,CACjB,CAAC,EACD,EAAE,CAAC,WAAW;AACZ,oBAAA,EAAE,CAAC,WAAW;AACd,oBAAA,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC;AACtB,oBAAA,EAAE,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAC1B;gBACD,QAAQ,EAAE,IAAI,CAAC,GAAG,CAChB,CAAC,EACD,EAAE,CAAC,YAAY;AACb,oBAAA,EAAE,CAAC,YAAY;AACf,oBAAA,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC;AACrB,oBAAA,EAAE,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAC3B;aACF;YACD,OAAO,CAAC,KAAK,CAAC;AAChB,QAAA,CAAC;AACD,QAAA,QAAQ,EAAE;QACV,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE;AAC3C,QAAA,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC;QAC5C,MAAM,UAAU,GAAG,MAAK;YACtB,OAAO,CAAC,UAAU,EAAE;AACpB,YAAA,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACnB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC;AAAE,gBAAA,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;AACrE,QAAA,CAAC;AACD,QAAA,UAAU,EAAE;;;;;AAKZ,QAAA,MAAM,SAAS,GACb,OAAO,gBAAgB,KAAK;AAC1B,cAAE;AACF,cAAE,IAAI,gBAAgB,CAAC,OAAO,IAAG;gBAC7B,IACE,OAAO,CAAC,IAAI,CACV,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,CAC9D,EACD;AACA,oBAAA,UAAU,EAAE;gBACd;AACA,gBAAA,QAAQ,EAAE;AACZ,YAAA,CAAC,CAAC;AACR,QAAA,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE;AACrB,YAAA,SAAS,EAAE,IAAI;AACf,YAAA,aAAa,EAAE,IAAI;AACnB,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC;AACF,QAAA,OAAO,MAAK;YACV,OAAO,CAAC,UAAU,EAAE;YACpB,SAAS,EAAE,UAAU,EAAE;AACzB,QAAA,CAAC;AACH,IAAA,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AAEb,IAAA,MAAM,YAAY,GAAG,CAAC,KAAoC,KAAI;AAC5D,QAAA,QAAQ,GAAG,KAAK,CAAC;QACjB,OAAO,CAAC,IAAI,CAAC;AACf,IAAA,CAAC;AACD,IAAA,MAAM,WAAW,GAAG,CAAC,KAAuC,KAAI;AAC9D,QAAA,OAAO,GAAG,KAAK,CAAC;AAChB,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa;YAAE,UAAU,CAAC,IAAI,CAAC;AAC5D,IAAA,CAAC;AACD,IAAA,MAAM,UAAU,GAAG,CAAC,KAAuC,KAAI;AAC7D,QAAA,MAAM,GAAG,KAAK,CAAC;AACf,QAAA,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,aAAa;YAAE,UAAU,CAAC,KAAK,CAAC;AAC7D,IAAA,CAAC;;;;IAKD,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,EAAE,WAAW,IAAI,OAAO;;AAEnD,IAAA,MAAM,EACJ,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EACjC,GAAG,YAAY,EAChB,GAAG,IAAI;IACR,MAAM,cAAc,GAA2B,EAAE;IACjD,IAAI,QAAQ,EAAE;AACZ,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACxB,YAAA,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;AAAE,gBAAA,cAAc,CAAC,CAAA,cAAA,EAAiB,IAAI,EAAE,CAAC,GAAG,EAAE;QACxE;IACF;IACA,MAAM,aAAa,GAAG;AACpB,UAAG;AACC,YAAA,+BAA+B,EAAE,CAAA,EAAG,QAAQ,CAAC,SAAS,CAAA,EAAA,CAAI;AAC1D,YAAA,8BAA8B,EAAE,CAAA,EAAG,QAAQ,CAAC,QAAQ,CAAA,EAAA,CAAI;AACjC;UACzB,SAAS;IAEb,QACEA,aACE,SAAS,EAAE,UAAU,CAAC,UAAU,EAAE,kBAAkB,EAAE,SAAS,CAAC,EAChE,KAAK,EACH,MAAM,KAAK,SAAS,IAAI,SAAS,KAAK;AACpC,cAAE;AACE,gBAAA,GAAG,KAAK;;;gBAGR,IAAI,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;gBACvC,IAAI,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC;AAC9C;AACH,cAAE,KAAK,EAAA,QAAA,EAGXA,GAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,aAAa,EACxB,KAAK,EAAE,aAAa,EAAA,GAChB,YAAY,EAAA,GACZ,cAAc,EAClB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,gBACzB,QAAQ,GAAG,SAAS,GAAG,SAAS,EAAA,iBAAA,EAC3B,QAAQ,GAAG,cAAc,GAAG,SAAS,EACtD,QAAQ,EAAE,QAAQ,GAAG,CAAC,GAAG,SAAS,EAClC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,UAAU,EAAA,QAAA,EAEjB,QAAQ,EAAA,CACL,EAAA,CACF;AAEV,CAAC;AAGH,UAAU,CAAC,WAAW,GAAG,YAAY;;"}
@@ -213,7 +213,9 @@ const TreeViewItem = forwardRef(function TreeViewItem({ value, label, icon, chil
213
213
  if (branch.hidden)
214
214
  return jsx(Fragment, { children: items });
215
215
  const info = { value, parent, disabled, isParent, open };
216
- return (jsxs("li", { ...props, ref: setRefs, className: classNames(itemClass, className), role: "treeitem", "aria-labelledby": labelId, "aria-level": branch.path.length + 1, "aria-expanded": isParent ? open : undefined, "aria-disabled": disabled || undefined, "aria-selected": selected, "aria-checked": checked, tabIndex: tree.tabStop === value ? 0 : -1, onKeyDown: event => {
216
+ // A position the caller gives wins, for a set the page holds only part of.
217
+ const position = tree.positionOf(value);
218
+ return (jsxs("li", { ...props, ref: setRefs, className: classNames(itemClass, className), role: "treeitem", "aria-labelledby": labelId, "aria-level": branch.path.length + 1, "aria-posinset": props['aria-posinset'] ?? position?.posinset, "aria-setsize": props['aria-setsize'] ?? position?.setsize, "aria-expanded": isParent ? open : undefined, "aria-disabled": disabled || undefined, "aria-selected": selected, "aria-checked": checked, tabIndex: tree.tabStop === value ? 0 : -1, onKeyDown: event => {
217
219
  onKeyDown?.(event);
218
220
  tree.onKeyDown(event, info);
219
221
  }, onFocus: event => {
@@ -283,7 +285,8 @@ function StatusRow({ kind, parent, level, message, onRetry, labelRef, }) {
283
285
  open: false,
284
286
  retry,
285
287
  };
286
- return (jsx("li", { ref: setRef, className: itemClass, role: "treeitem", "aria-labelledby": retry ? `${labelId} ${retryId}` : labelId, "aria-level": level, "aria-disabled": !retry || undefined, tabIndex: tree.tabStop === labelId ? 0 : -1, onKeyDown: event => tree.onKeyDown(event, info), onFocus: event => {
288
+ const position = tree.positionOf(labelId);
289
+ return (jsx("li", { ref: setRef, className: itemClass, role: "treeitem", "aria-labelledby": retry ? `${labelId} ${retryId}` : labelId, "aria-level": level, "aria-posinset": position?.posinset, "aria-setsize": position?.setsize, "aria-disabled": !retry || undefined, tabIndex: tree.tabStop === labelId ? 0 : -1, onKeyDown: event => tree.onKeyDown(event, info), onFocus: event => {
287
290
  // Focus in something the message holds isn't the row's.
288
291
  if (event.target === event.currentTarget) {
289
292
  tree.onFocus(labelId, event.currentTarget);
@@ -1 +1 @@
1
- {"version":3,"file":"TreeView.js","sources":["../../../src/components/TreeView.tsx"],"sourcesContent":["import React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { warnOnce } from '../helpers/devWarnings';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport { useTreeView, type TreeViewEngine } from '../helpers/useTreeView';\nimport { StatusRegion, spokenText } from '../helpers/statusRegion';\nimport { Checkbox } from '../form/Checkbox';\nimport { Icon } from '../elements/Icon';\n\n/**\n * How items are picked: `none`, nothing is; `single`, one item at a time;\n * `checkbox`, any number, with a box on every item and mixed parents.\n */\nexport type TreeViewSelectionMode = 'none' | 'single' | 'checkbox';\n\nconst validTreeViewSizes = ['small', 'medium', 'large'] as const;\n\n/** What an open item's load says in the tree's live region. */\ninterface LoadAnnouncement {\n /** The item's key, so what it says next replaces this. */\n key: string;\n /** The key and the kind: the region says it once each time it appears. */\n id: string;\n kind: 'loading' | 'loaded' | 'error';\n /** The item's name, read off its label. */\n name: string;\n /** A failed load's message, read off its row. */\n message: string;\n}\n\n/** What the root hands its items: the engine, the live region, and the rows' words. */\ninterface TreeContextValue extends TreeViewEngine {\n /** Puts what an item's load says in the live region, or takes it out with `null`. */\n announce: (key: string, announcement: LoadAnnouncement | null) => void;\n loadingLabel: string;\n retryLabel: string;\n}\n\n// Not exported, so they stay out of the public API that `index.ts` re-exports.\nconst TreeViewContext = createContext<TreeContextValue | null>(null);\n\nconst sayLoading = (name: string) => `Loading ${name}`;\nconst sayLoaded = (name: string) => `${name} loaded`;\n\n/** Where an item sits: the values of the items above it, and whether one of them is closed. */\ninterface Branch {\n path: string[];\n hidden: boolean;\n}\n\nconst TOP: Branch = { path: [], hidden: false };\nconst BranchContext = createContext<Branch>(TOP);\n\n/** The box's input lets the row's click toggle it, so a change on its own does nothing. */\nconst ignoreChange = () => {};\n\n/** Whether an `icon` was given something to render. */\nconst isGiven = (node: React.ReactNode) =>\n node !== undefined &&\n node !== null &&\n typeof node !== 'boolean' &&\n node !== '';\n\n/**\n * Props for the TreeView component. The helper props, the other attributes\n * and the `ref` land on the `<ul role=\"tree\">`.\n *\n * @extraProp {string} [aria-label] - Names the tree, such as \"Files\". A tree needs a name, so pass this or `aria-labelledby`, and a development build warns when it has neither.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading or label that names the tree, in place of `aria-label`.\n * @extraProp {React.Ref<HTMLUListElement>} [ref] - Ref forwarded to the `<ul>`.\n */\nexport interface TreeViewProps\n extends\n Omit<\n React.HTMLAttributes<HTMLUListElement>,\n | keyof BulmaClassesProps\n | 'role'\n | 'onSelect'\n | 'defaultValue'\n | 'defaultChecked'\n >,\n BulmaClassesProps {\n /**\n * `TreeView.Item`s, nested inside each other for their children. Render\n * an item's children whenever you have them, open or not: the tree hides\n * them while it's closed.\n */\n children?: React.ReactNode;\n /**\n * `none`: nothing is picked, and Enter and Space open and close a parent.\n * `single`: one item, picked with Enter, Space or a click.\n * `checkbox`: any number, checked with Space or a click. A check reaches\n * every enabled item under it, and a parent with some of them checked is mixed.\n */\n selectionMode?: TreeViewSelectionMode;\n /**\n * The selected item's value when you hold the state, or `null` for none.\n * Read with `single` only. Picking an item then only calls\n * `onSelectedChange`, and nothing changes until you pass the new value.\n */\n selected?: string | null;\n /** The item selected to begin with, when the tree holds its own state. Read with `single` only. */\n defaultSelected?: string | null;\n /**\n * Called with the value of the item picked. Not called for the one\n * already selected, and moving focus never calls it.\n */\n onSelectedChange?: (value: string) => void;\n /**\n * The checked items' values when you hold the state. Read with `checkbox`\n * only. A value checks its item, and an enabled item also counts as checked\n * when any item above it is in the list, so a parent's value stands for its\n * whole branch, loaded or not. A disabled item is checked only by its own value.\n */\n checked?: string[];\n /** The values checked to begin with, when the tree holds its own state. Read with `checkbox` only. */\n defaultChecked?: string[];\n /**\n * Called after a check or an uncheck with every item that now shows\n * checked, parents included, plus any earlier value that matches no item\n * the tree knows, left as it was. The order means nothing. Passed back as\n * `checked`, the list shows the same boxes.\n */\n onCheckedChange?: (checked: string[]) => void;\n /**\n * The open items' values when you hold the state. A value stays in the\n * list while its item's parent is closed, so the item reopens as it was.\n */\n expanded?: string[];\n /** The items open to begin with, when the tree holds its own state. */\n defaultExpanded?: string[];\n /** Called with the new list whenever an item opens or closes, `*` included. */\n onExpandedChange?: (expanded: string[]) => void;\n /** The text size, as Bulma's menu sizes it. The rows, the indent and the chevron scale with it. */\n size?: 'small' | 'medium' | 'large';\n /** What the row in a loading item's group says. In your app's language. */\n loadingLabel?: string;\n /** The Retry button's text on a failed load's row. In your app's language. */\n retryLabel?: string;\n /**\n * What the live region says when an open item starts loading, built from\n * the item's name. In your app's language. Default: `` `Loading ${name}` ``.\n */\n loadingAnnouncement?: (name: string) => string;\n /**\n * What the live region says when that load finishes. In your app's\n * language. Default: `` `${name} loaded` ``.\n */\n loadedAnnouncement?: (name: string) => string;\n}\n\n/**\n * Props for TreeView.Item, one row and, nested in it, its children. The other\n * attributes and the `ref` land on its `<li role=\"treeitem\">`.\n *\n * @extraProp {React.Ref<HTMLLIElement>} [ref] - Ref forwarded to the `<li>`.\n */\nexport interface TreeViewItemProps extends Omit<\n React.LiHTMLAttributes<HTMLLIElement>,\n 'role' | 'value' | 'onSelect' | 'children'\n> {\n /** **Required.** Identifies the item in `selected`, `checked` and `expanded`. Unique in the tree. */\n value: string;\n /**\n * **Required.** What the row shows, and the item's name for assistive\n * tech. Nothing in it should be a link or a button: a tree item does one\n * thing, and nothing else in its row takes focus.\n */\n label: React.ReactNode;\n /** A picture before the label: an `Icon` name, or a node. Hidden from assistive tech. */\n icon?: React.ReactNode;\n /**\n * `TreeView.Item`s under this one. Render them whenever you have them,\n * open or not: the tree hides them while it's closed, and a checkbox tree\n * needs them to know what a check reaches.\n */\n children?: React.ReactNode;\n /** Shows the item as a parent before its children are known, such as a folder that loads when it opens. */\n hasChildren?: boolean;\n /**\n * Its children are on their way. While it's open, its group ends in a row\n * that says so, and the tree's live region says it's loading, then that\n * it's loaded once this goes back to `false`. A parent only: pair it with\n * `hasChildren`, or with the children it already has.\n */\n isLoading?: boolean;\n /**\n * Its children didn't load. While it's open, its group ends in a row with\n * this message, and a Retry button when there's an `onRetry`, and the live\n * region reads the message out, so name the item in it (\"Couldn't load\n * src\"), then says it's loaded once this goes with the item still open. A\n * parent only, like `isLoading`. Nothing in it should be a link or a\n * button: the row is what's pressed. `isLoading` wins while both are given.\n */\n loadError?: React.ReactNode;\n /**\n * Called by the Retry button on a failed load's row, and by Enter or Space\n * there. Set `isLoading` while the retry runs, so the live region says it's\n * loading, and reads the message out again if it fails again. The same\n * message passed a second time with no loading between is no change the\n * tree can see.\n */\n onRetry?: () => void;\n /**\n * Reachable with the arrow keys, Home, End and type-ahead, so a screen\n * reader finds it, but never selected, checked, opened or closed. One\n * that's already open stays open.\n */\n disabled?: boolean;\n}\n\n/**\n * The `TreeView` component renders a tree of items you browse and pick from, the way a file explorer works: one tab stop, the arrow keys to move, and Right and Left to open and close.\n *\n * It's the APG Tree View pattern, drawn to look like Bulma's menu. It isn't\n * for site navigation: items aren't links, and a sidebar whose sections open\n * and close is `Menu`'s job. The root owns which items are open, through\n * `expanded`, `defaultExpanded` and `onExpandedChange`.\n *\n * `selectionMode` picks one of three trees. With `none`, nothing is picked.\n * With `single`, one item is, and `selected` says which. With `checkbox`,\n * each item has a box, a check reaches every enabled item under it, closed\n * branches included, and a parent with some of its items checked is mixed.\n * `selected` is read in a `single` tree only, and `checked` in a `checkbox`\n * tree only. Selection never follows focus.\n *\n * Disabled items stay reachable from the keys, so a screen reader finds\n * them. The tree needs a name, from `aria-label` or `aria-labelledby`, and a\n * development build warns when it has neither.\n *\n * @function\n * @param {TreeViewProps} props - Props for the TreeView component.\n * @param {React.Ref<HTMLUListElement>} ref - Forwarded ref to the `<ul>`.\n * @returns {JSX.Element} The rendered tree.\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ | APG Tree View Pattern}\n *\n * @example\n * <TreeView aria-label=\"Files\" selectionMode=\"single\" defaultExpanded={['src']}>\n * <TreeView.Item value=\"src\" label=\"src\" icon=\"folder\">\n * <TreeView.Item value=\"src/index.ts\" label=\"index.ts\" icon=\"file\" />\n * </TreeView.Item>\n * <TreeView.Item value=\"package.json\" label=\"package.json\" icon=\"file\" />\n * </TreeView>\n */\nconst TreeViewRoot = forwardRef<HTMLUListElement, TreeViewProps>(\n function TreeView(\n {\n children,\n selectionMode = 'none',\n selected,\n defaultSelected = null,\n onSelectedChange,\n checked,\n defaultChecked = [],\n onCheckedChange,\n expanded,\n defaultExpanded = [],\n onExpandedChange,\n size,\n loadingLabel = 'Loading…',\n retryLabel = 'Retry',\n loadingAnnouncement,\n loadedAnnouncement,\n className,\n ...props\n },\n ref\n ) {\n const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });\n const engine = useTreeView({\n mode: selectionMode,\n selected,\n defaultSelected,\n onSelectedChange,\n checked,\n defaultChecked,\n onCheckedChange,\n expanded,\n defaultExpanded,\n onExpandedChange,\n });\n\n const { treeRef } = engine;\n const setRefs = useCallback(\n (node: HTMLUListElement | null) => {\n treeRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [ref, treeRef]\n );\n\n const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'TreeView:unnamed',\n '[bestax-bulma] <TreeView> has no accessible name. Pass ' +\n 'aria-label or aria-labelledby, so a screen reader can say what ' +\n 'the tree holds.'\n );\n }\n }, [named]);\n\n const rootClass = usePrefixedClassNames('tree-view', {\n [`is-${size}`]:\n !!size && (validTreeViewSizes as readonly string[]).includes(size),\n });\n\n // What each open item's load says, in the order they started saying it.\n const [announcements, setAnnouncements] = useState<LoadAnnouncement[]>([]);\n const announce = useCallback(\n (key: string, announcement: LoadAnnouncement | null) => {\n setAnnouncements(current => {\n const others = current.filter(said => said.key !== key);\n return announcement ? [...others, announcement] : others;\n });\n },\n []\n );\n const describe = useCallback(\n ({ kind, name, message }: LoadAnnouncement) => {\n if (kind === 'error') return message;\n const say =\n kind === 'loading'\n ? (loadingAnnouncement ?? sayLoading)\n : (loadedAnnouncement ?? sayLoaded);\n return say(name);\n },\n [loadingAnnouncement, loadedAnnouncement]\n );\n\n // The props are spread first, so the role and the attributes the tree\n // owns win over anything a caller passes along. The live region sits\n // beside the tree, since a tree holds items and groups only, and it's\n // there from the start, so a screen reader is listening to it before\n // anything is written into it.\n return (\n <TreeViewContext.Provider\n value={{ ...engine, announce, loadingLabel, retryLabel }}\n >\n <BranchContext.Provider value={TOP}>\n <ul\n {...rest}\n ref={setRefs}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n role=\"tree\"\n aria-multiselectable={engine.rendersClosed || undefined}\n >\n {children}\n </ul>\n </BranchContext.Provider>\n <StatusRegion items={announcements} describe={describe} />\n </TreeViewContext.Provider>\n );\n }\n);\n\n/**\n * One item in the tree: a row with its chevron, its box, its icon and its label, and, while it's open, a group of the items under it.\n *\n * @function\n * @param {TreeViewItemProps} props - Props for TreeView.Item.\n * @param {React.Ref<HTMLLIElement>} ref - Forwarded ref to the `<li>`.\n * @returns {JSX.Element | null} The rendered item, or nothing under a closed one.\n */\nexport const TreeViewItem = forwardRef<HTMLLIElement, TreeViewItemProps>(\n function TreeViewItem(\n {\n value,\n label,\n icon,\n children,\n hasChildren = false,\n isLoading = false,\n loadError,\n onRetry,\n disabled = false,\n className,\n onClick,\n onKeyDown,\n onFocus,\n onBlur,\n ...props\n },\n ref\n ) {\n const tree = useContext(TreeViewContext);\n const branch = useContext(BranchContext);\n // Also the item's key in the live region.\n const labelId = useId();\n const hasItems = React.Children.toArray(children).length > 0;\n const isParent = hasItems || hasChildren;\n const open = isParent && !!tree?.isOpen(value);\n const parent = branch.path[branch.path.length - 1] ?? null;\n\n // The row at the end of its group, while it's open and on the page.\n const shown = open && !branch.hidden;\n const status = !shown\n ? null\n : isLoading\n ? 'loading'\n : isGiven(loadError)\n ? 'error'\n : null;\n const labelRef = useRef<HTMLSpanElement | null>(null);\n const statusLabelRef = useRef<HTMLSpanElement | null>(null);\n\n // What the live region says follows the row: it's loading, or it failed,\n // while the row says so, and it's loaded once the row goes with the item\n // still open, whichever row it was, until the item closes or loads again.\n // A closed item says nothing, loading or not.\n const phase = shown ? (status ?? 'open') : 'closed';\n const lastPhaseRef = useRef(phase);\n const announce = tree?.announce;\n useEffect(() => {\n const was = lastPhaseRef.current;\n lastPhaseRef.current = phase;\n const kind =\n phase === 'loading' || phase === 'error'\n ? phase\n : phase === 'open' && (was === 'loading' || was === 'error')\n ? 'loaded'\n : null;\n if (!kind) return undefined;\n // Only an item inside a tree can be open.\n announce!(labelId, {\n key: labelId,\n id: `${labelId}:${kind}`,\n kind,\n name: spokenText(labelRef.current!),\n message: kind === 'error' ? spokenText(statusLabelRef.current!) : '',\n });\n return () => announce!(labelId, null);\n }, [phase, announce, labelId]);\n\n const itemClass = usePrefixedClassNames('tree-view-item');\n const groupClass = usePrefixedClassNames('tree-view-group');\n const toggleClass = usePrefixedClassNames('tree-view-toggle');\n const checkClass = usePrefixedClassNames('tree-view-check');\n const iconClass = usePrefixedClassNames('tree-view-icon');\n const labelClass = usePrefixedClassNames('tree-view-label');\n\n const register = tree?.register;\n useClientLayoutEffect(() => {\n if (!register) {\n warnOnce(\n 'TreeView.Item:outside',\n '[bestax-bulma] <TreeView.Item> is outside a <TreeView>, so it ' +\n 'renders nothing. Put it inside one.'\n );\n return undefined;\n }\n return register(value, { parent, disabled, isParent });\n }, [register, value, parent, disabled, isParent]);\n\n const setElement = tree?.setElement;\n const setRefs = useCallback(\n (node: HTMLLIElement | null) => {\n // Only an item inside a tree renders an element to hand over.\n setElement!(value, node);\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [setElement, value, ref]\n );\n const rowRef = useRef<HTMLDivElement | null>(null);\n const toggleRef = useRef<HTMLSpanElement | null>(null);\n\n const childBranch = useMemo(\n () => ({\n path: [...branch.path, value],\n hidden: branch.hidden || !open,\n }),\n [branch, value, open]\n );\n\n const { selected, checked } =\n tree?.stateOf(value, branch.path, disabled) ?? {};\n const rowClass = usePrefixedClassNames('tree-view-row', {\n 'is-selected': selected,\n 'is-disabled': disabled,\n });\n\n if (!tree) return null;\n\n // While it's open the children render in its group. In a checkbox tree\n // they render while it's closed too, drawing nothing, so a check knows\n // what's under it.\n const items = (open || tree.rendersClosed) && (\n <BranchContext.Provider value={childBranch}>\n {children}\n </BranchContext.Provider>\n );\n if (branch.hidden) return <>{items}</>;\n\n const info = { value, parent, disabled, isParent, open };\n\n return (\n <li\n {...props}\n ref={setRefs}\n className={classNames(itemClass, className)}\n role=\"treeitem\"\n aria-labelledby={labelId}\n aria-level={branch.path.length + 1}\n aria-expanded={isParent ? open : undefined}\n aria-disabled={disabled || undefined}\n aria-selected={selected}\n aria-checked={checked}\n tabIndex={tree.tabStop === value ? 0 : -1}\n onKeyDown={event => {\n onKeyDown?.(event);\n tree.onKeyDown(event, info);\n }}\n onFocus={event => {\n onFocus?.(event);\n if (event.target === event.currentTarget) {\n tree.onFocus(value, event.currentTarget);\n }\n }}\n onBlur={event => {\n onBlur?.(event);\n if (event.target === event.currentTarget) tree.onBlur();\n }}\n onClick={event => {\n onClick?.(event);\n // A click in its row is its own, and so is one on the item itself,\n // as a test's click on the treeitem is. One in its group belongs\n // to the item inside it, or to nothing. The chevron's box is on\n // every row, but it's a chevron only on a parent: on a leaf it's\n // more of the row.\n const target = event.target as Node;\n if (\n target === event.currentTarget ||\n rowRef.current!.contains(target)\n ) {\n tree.onClick(\n event.currentTarget,\n info,\n isParent && toggleRef.current!.contains(target)\n );\n }\n }}\n >\n <div className={rowClass} ref={rowRef}>\n <span className={toggleClass} aria-hidden=\"true\" ref={toggleRef} />\n {checked !== undefined && (\n <span\n className={checkClass}\n aria-hidden=\"true\"\n onClick={event => {\n // A click on the box's label is followed by one on its\n // input, and both reach the item. The label's is cancelled,\n // so the item toggles once.\n if (!(event.target instanceof HTMLInputElement)) {\n event.preventDefault();\n }\n }}\n >\n <Checkbox\n tabIndex={-1}\n checked={checked === true}\n indeterminate={checked === 'mixed'}\n disabled={disabled}\n onChange={ignoreChange}\n />\n </span>\n )}\n {isGiven(icon) && (\n <span className={iconClass} aria-hidden=\"true\">\n {typeof icon === 'string' ? <Icon name={icon} /> : icon}\n </span>\n )}\n <span className={labelClass} id={labelId} ref={labelRef}>\n {label}\n </span>\n </div>\n {open && (hasItems || status) ? (\n <ul className={groupClass} role=\"group\">\n {items}\n {status && (\n <StatusRow\n kind={status}\n parent={value}\n level={branch.path.length + 2}\n message={loadError}\n onRetry={onRetry}\n labelRef={statusLabelRef}\n />\n )}\n </ul>\n ) : (\n items\n )}\n </li>\n );\n }\n);\n\nTreeViewItem.displayName = 'TreeView.Item';\n\n/** Props for the row at the end of a loading item's group, or a failed load's. */\ninterface StatusRowProps {\n /** `loading`, or `error` for a load that failed. */\n kind: 'loading' | 'error';\n /** The value of the item it's in. */\n parent: string;\n /** Its `aria-level`, one under the item it's in. */\n level: number;\n /** A failed load's message. */\n message: React.ReactNode;\n /** Retries a failed load. */\n onRetry?: () => void;\n /** For its label, which the item reads a failed load's message off. */\n labelRef: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * The row at the end of a loading item's group, or of one whose load failed.\n * It's a `treeitem` the keys reach, the way they reach a disabled item, so a\n * screen reader finds it. It has no value and no record, so it's never\n * selected or checked, and a check never counts it. A failed load's row with\n * `onRetry` retries with Enter, Space or a click, and any other is disabled.\n * The Retry button is drawn, hidden from assistive tech and out of the tab\n * order, the way an item's box is: the row is what's pressed, and its name\n * says so. Rendered in one place, a failed load's row that starts loading\n * again is the same element, so focus stays on it.\n */\nfunction StatusRow({\n kind,\n parent,\n level,\n message,\n onRetry,\n labelRef,\n}: StatusRowProps) {\n // Only an item inside a tree renders a group.\n const tree = useContext(TreeViewContext)!;\n // Also its key in the registry and for the tab stop, where an item has its value.\n const labelId = useId();\n const retryId = useId();\n const retry = kind === 'error' ? onRetry : undefined;\n\n const itemClass = usePrefixedClassNames('tree-view-item');\n const rowClass = usePrefixedClassNames('tree-view-row', {\n 'is-disabled': !retry,\n });\n const toggleClass = usePrefixedClassNames('tree-view-toggle');\n const labelClass = usePrefixedClassNames('tree-view-label');\n const loaderClass = usePrefixedClassNames('loader');\n const retryClass = usePrefixedClassNames('button', { 'is-small': true });\n\n const { setElement } = tree;\n const setRef = useCallback(\n (node: HTMLLIElement | null) => setElement(labelId, node),\n [setElement, labelId]\n );\n const info = {\n value: labelId,\n parent,\n disabled: !retry,\n isParent: false,\n open: false,\n retry,\n };\n\n return (\n <li\n ref={setRef}\n className={itemClass}\n role=\"treeitem\"\n aria-labelledby={retry ? `${labelId} ${retryId}` : labelId}\n aria-level={level}\n aria-disabled={!retry || undefined}\n tabIndex={tree.tabStop === labelId ? 0 : -1}\n onKeyDown={event => tree.onKeyDown(event, info)}\n onFocus={event => {\n // Focus in something the message holds isn't the row's.\n if (event.target === event.currentTarget) {\n tree.onFocus(labelId, event.currentTarget);\n }\n }}\n onBlur={event => {\n if (event.target === event.currentTarget) tree.onBlur();\n }}\n onClick={event => tree.onClick(event.currentTarget, info, false)}\n >\n <div className={rowClass}>\n <span className={toggleClass} aria-hidden=\"true\">\n {kind === 'loading' && <span className={loaderClass} />}\n </span>\n <span className={labelClass} id={labelId} ref={labelRef}>\n {kind === 'loading' ? tree.loadingLabel : message}\n </span>\n {retry && (\n <span className={retryClass} id={retryId} aria-hidden=\"true\">\n {tree.retryLabel}\n </span>\n )}\n </div>\n </li>\n );\n}\n\nexport const TreeView = withSubComponents(\n TreeViewRoot,\n { Item: TreeViewItem },\n 'TreeView'\n);\n\nexport default TreeView;\n"],"names":["_jsxs","_jsx","_Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"TreeView.js","sources":["../../../src/components/TreeView.tsx"],"sourcesContent":["import React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { classNames, usePrefixedClassNames } from '../helpers/classNames';\nimport { useBulmaClasses, BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { withSubComponents } from '../helpers/withSubComponents';\nimport { warnOnce } from '../helpers/devWarnings';\nimport { useClientLayoutEffect } from '../helpers/useClientLayoutEffect';\nimport { useTreeView, type TreeViewEngine } from '../helpers/useTreeView';\nimport { StatusRegion, spokenText } from '../helpers/statusRegion';\nimport { Checkbox } from '../form/Checkbox';\nimport { Icon } from '../elements/Icon';\n\n/**\n * How items are picked: `none`, nothing is; `single`, one item at a time;\n * `checkbox`, any number, with a box on every item and mixed parents.\n */\nexport type TreeViewSelectionMode = 'none' | 'single' | 'checkbox';\n\nconst validTreeViewSizes = ['small', 'medium', 'large'] as const;\n\n/** What an open item's load says in the tree's live region. */\ninterface LoadAnnouncement {\n /** The item's key, so what it says next replaces this. */\n key: string;\n /** The key and the kind: the region says it once each time it appears. */\n id: string;\n kind: 'loading' | 'loaded' | 'error';\n /** The item's name, read off its label. */\n name: string;\n /** A failed load's message, read off its row. */\n message: string;\n}\n\n/** What the root hands its items: the engine, the live region, and the rows' words. */\ninterface TreeContextValue extends TreeViewEngine {\n /** Puts what an item's load says in the live region, or takes it out with `null`. */\n announce: (key: string, announcement: LoadAnnouncement | null) => void;\n loadingLabel: string;\n retryLabel: string;\n}\n\n// Not exported, so they stay out of the public API that `index.ts` re-exports.\nconst TreeViewContext = createContext<TreeContextValue | null>(null);\n\nconst sayLoading = (name: string) => `Loading ${name}`;\nconst sayLoaded = (name: string) => `${name} loaded`;\n\n/** Where an item sits: the values of the items above it, and whether one of them is closed. */\ninterface Branch {\n path: string[];\n hidden: boolean;\n}\n\nconst TOP: Branch = { path: [], hidden: false };\nconst BranchContext = createContext<Branch>(TOP);\n\n/** The box's input lets the row's click toggle it, so a change on its own does nothing. */\nconst ignoreChange = () => {};\n\n/** Whether an `icon` was given something to render. */\nconst isGiven = (node: React.ReactNode) =>\n node !== undefined &&\n node !== null &&\n typeof node !== 'boolean' &&\n node !== '';\n\n/**\n * Props for the TreeView component. The helper props, the other attributes\n * and the `ref` land on the `<ul role=\"tree\">`.\n *\n * @extraProp {string} [aria-label] - Names the tree, such as \"Files\". A tree needs a name, so pass this or `aria-labelledby`, and a development build warns when it has neither.\n * @extraProp {string} [aria-labelledby] - The id of a visible heading or label that names the tree, in place of `aria-label`.\n * @extraProp {React.Ref<HTMLUListElement>} [ref] - Ref forwarded to the `<ul>`.\n */\nexport interface TreeViewProps\n extends\n Omit<\n React.HTMLAttributes<HTMLUListElement>,\n | keyof BulmaClassesProps\n | 'role'\n | 'onSelect'\n | 'defaultValue'\n | 'defaultChecked'\n >,\n BulmaClassesProps {\n /**\n * `TreeView.Item`s, nested inside each other for their children. Render\n * an item's children whenever you have them, open or not: the tree hides\n * them while it's closed.\n */\n children?: React.ReactNode;\n /**\n * `none`: nothing is picked, and Enter and Space open and close a parent.\n * `single`: one item, picked with Enter, Space or a click.\n * `checkbox`: any number, checked with Space or a click. A check reaches\n * every enabled item under it, and a parent with some of them checked is mixed.\n */\n selectionMode?: TreeViewSelectionMode;\n /**\n * The selected item's value when you hold the state, or `null` for none.\n * Read with `single` only. Picking an item then only calls\n * `onSelectedChange`, and nothing changes until you pass the new value.\n */\n selected?: string | null;\n /** The item selected to begin with, when the tree holds its own state. Read with `single` only. */\n defaultSelected?: string | null;\n /**\n * Called with the value of the item picked. Not called for the one\n * already selected, and moving focus never calls it.\n */\n onSelectedChange?: (value: string) => void;\n /**\n * The checked items' values when you hold the state. Read with `checkbox`\n * only. A value checks its item, and an enabled item also counts as checked\n * when any item above it is in the list, so a parent's value stands for its\n * whole branch, loaded or not. A disabled item is checked only by its own value.\n */\n checked?: string[];\n /** The values checked to begin with, when the tree holds its own state. Read with `checkbox` only. */\n defaultChecked?: string[];\n /**\n * Called after a check or an uncheck with every item that now shows\n * checked, parents included, plus any earlier value that matches no item\n * the tree knows, left as it was. The order means nothing. Passed back as\n * `checked`, the list shows the same boxes.\n */\n onCheckedChange?: (checked: string[]) => void;\n /**\n * The open items' values when you hold the state. A value stays in the\n * list while its item's parent is closed, so the item reopens as it was.\n */\n expanded?: string[];\n /** The items open to begin with, when the tree holds its own state. */\n defaultExpanded?: string[];\n /** Called with the new list whenever an item opens or closes, `*` included. */\n onExpandedChange?: (expanded: string[]) => void;\n /** The text size, as Bulma's menu sizes it. The rows, the indent and the chevron scale with it. */\n size?: 'small' | 'medium' | 'large';\n /** What the row in a loading item's group says. In your app's language. */\n loadingLabel?: string;\n /** The Retry button's text on a failed load's row. In your app's language. */\n retryLabel?: string;\n /**\n * What the live region says when an open item starts loading, built from\n * the item's name. In your app's language. Default: `` `Loading ${name}` ``.\n */\n loadingAnnouncement?: (name: string) => string;\n /**\n * What the live region says when that load finishes. In your app's\n * language. Default: `` `${name} loaded` ``.\n */\n loadedAnnouncement?: (name: string) => string;\n}\n\n/**\n * Props for TreeView.Item, one row and, nested in it, its children. The other\n * attributes and the `ref` land on its `<li role=\"treeitem\">`.\n *\n * @extraProp {number} [aria-posinset] - The item's number among the items beside it. The tree declares it, and `aria-setsize`, from the items on the page once it mounts. When the page holds only part of a set, such as the first page of a long folder, pass both on every item of that set: yours win, and the tree's own stay on any item without them.\n * @extraProp {number} [aria-setsize] - How many items sit beside it, itself included. Declared by the tree like `aria-posinset`, and `-1` says the count isn't known.\n * @extraProp {React.Ref<HTMLLIElement>} [ref] - Ref forwarded to the `<li>`.\n */\nexport interface TreeViewItemProps extends Omit<\n React.LiHTMLAttributes<HTMLLIElement>,\n 'role' | 'value' | 'onSelect' | 'children'\n> {\n /** **Required.** Identifies the item in `selected`, `checked` and `expanded`. Unique in the tree. */\n value: string;\n /**\n * **Required.** What the row shows, and the item's name for assistive\n * tech. Nothing in it should be a link or a button: a tree item does one\n * thing, and nothing else in its row takes focus.\n */\n label: React.ReactNode;\n /** A picture before the label: an `Icon` name, or a node. Hidden from assistive tech. */\n icon?: React.ReactNode;\n /**\n * `TreeView.Item`s under this one. Render them whenever you have them,\n * open or not: the tree hides them while it's closed, and a checkbox tree\n * needs them to know what a check reaches.\n */\n children?: React.ReactNode;\n /** Shows the item as a parent before its children are known, such as a folder that loads when it opens. */\n hasChildren?: boolean;\n /**\n * Its children are on their way. While it's open, its group ends in a row\n * that says so, and the tree's live region says it's loading, then that\n * it's loaded once this goes back to `false`. A parent only: pair it with\n * `hasChildren`, or with the children it already has.\n */\n isLoading?: boolean;\n /**\n * Its children didn't load. While it's open, its group ends in a row with\n * this message, and a Retry button when there's an `onRetry`, and the live\n * region reads the message out, so name the item in it (\"Couldn't load\n * src\"), then says it's loaded once this goes with the item still open. A\n * parent only, like `isLoading`. Nothing in it should be a link or a\n * button: the row is what's pressed. `isLoading` wins while both are given.\n */\n loadError?: React.ReactNode;\n /**\n * Called by the Retry button on a failed load's row, and by Enter or Space\n * there. Set `isLoading` while the retry runs, so the live region says it's\n * loading, and reads the message out again if it fails again. The same\n * message passed a second time with no loading between is no change the\n * tree can see.\n */\n onRetry?: () => void;\n /**\n * Reachable with the arrow keys, Home, End and type-ahead, so a screen\n * reader finds it, but never selected, checked, opened or closed. One\n * that's already open stays open.\n */\n disabled?: boolean;\n}\n\n/**\n * The `TreeView` component renders a tree of items you browse and pick from, the way a file explorer works: one tab stop, the arrow keys to move, and Right and Left to open and close.\n *\n * It's the APG Tree View pattern, drawn to look like Bulma's menu. It isn't\n * for site navigation: items aren't links, and a sidebar whose sections open\n * and close is `Menu`'s job. The root owns which items are open, through\n * `expanded`, `defaultExpanded` and `onExpandedChange`.\n *\n * `selectionMode` picks one of three trees. With `none`, nothing is picked.\n * With `single`, one item is, and `selected` says which. With `checkbox`,\n * each item has a box, a check reaches every enabled item under it, closed\n * branches included, and a parent with some of its items checked is mixed.\n * `selected` is read in a `single` tree only, and `checked` in a `checkbox`\n * tree only. Selection never follows focus.\n *\n * Disabled items stay reachable from the keys, so a screen reader finds\n * them. The tree needs a name, from `aria-label` or `aria-labelledby`, and a\n * development build warns when it has neither.\n *\n * @function\n * @param {TreeViewProps} props - Props for the TreeView component.\n * @param {React.Ref<HTMLUListElement>} ref - Forwarded ref to the `<ul>`.\n * @returns {JSX.Element} The rendered tree.\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ | APG Tree View Pattern}\n *\n * @example\n * <TreeView aria-label=\"Files\" selectionMode=\"single\" defaultExpanded={['src']}>\n * <TreeView.Item value=\"src\" label=\"src\" icon=\"folder\">\n * <TreeView.Item value=\"src/index.ts\" label=\"index.ts\" icon=\"file\" />\n * </TreeView.Item>\n * <TreeView.Item value=\"package.json\" label=\"package.json\" icon=\"file\" />\n * </TreeView>\n */\nconst TreeViewRoot = forwardRef<HTMLUListElement, TreeViewProps>(\n function TreeView(\n {\n children,\n selectionMode = 'none',\n selected,\n defaultSelected = null,\n onSelectedChange,\n checked,\n defaultChecked = [],\n onCheckedChange,\n expanded,\n defaultExpanded = [],\n onExpandedChange,\n size,\n loadingLabel = 'Loading…',\n retryLabel = 'Retry',\n loadingAnnouncement,\n loadedAnnouncement,\n className,\n ...props\n },\n ref\n ) {\n const { bulmaHelperClasses, rest } = useBulmaClasses({ ...props });\n const engine = useTreeView({\n mode: selectionMode,\n selected,\n defaultSelected,\n onSelectedChange,\n checked,\n defaultChecked,\n onCheckedChange,\n expanded,\n defaultExpanded,\n onExpandedChange,\n });\n\n const { treeRef } = engine;\n const setRefs = useCallback(\n (node: HTMLUListElement | null) => {\n treeRef.current = node;\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [ref, treeRef]\n );\n\n const named = Boolean(rest['aria-label'] || rest['aria-labelledby']);\n useEffect(() => {\n if (!named) {\n warnOnce(\n 'TreeView:unnamed',\n '[bestax-bulma] <TreeView> has no accessible name. Pass ' +\n 'aria-label or aria-labelledby, so a screen reader can say what ' +\n 'the tree holds.'\n );\n }\n }, [named]);\n\n const rootClass = usePrefixedClassNames('tree-view', {\n [`is-${size}`]:\n !!size && (validTreeViewSizes as readonly string[]).includes(size),\n });\n\n // What each open item's load says, in the order they started saying it.\n const [announcements, setAnnouncements] = useState<LoadAnnouncement[]>([]);\n const announce = useCallback(\n (key: string, announcement: LoadAnnouncement | null) => {\n setAnnouncements(current => {\n const others = current.filter(said => said.key !== key);\n return announcement ? [...others, announcement] : others;\n });\n },\n []\n );\n const describe = useCallback(\n ({ kind, name, message }: LoadAnnouncement) => {\n if (kind === 'error') return message;\n const say =\n kind === 'loading'\n ? (loadingAnnouncement ?? sayLoading)\n : (loadedAnnouncement ?? sayLoaded);\n return say(name);\n },\n [loadingAnnouncement, loadedAnnouncement]\n );\n\n // The props are spread first, so the role and the attributes the tree\n // owns win over anything a caller passes along. The live region sits\n // beside the tree, since a tree holds items and groups only, and it's\n // there from the start, so a screen reader is listening to it before\n // anything is written into it.\n return (\n <TreeViewContext.Provider\n value={{ ...engine, announce, loadingLabel, retryLabel }}\n >\n <BranchContext.Provider value={TOP}>\n <ul\n {...rest}\n ref={setRefs}\n className={classNames(rootClass, bulmaHelperClasses, className)}\n role=\"tree\"\n aria-multiselectable={engine.rendersClosed || undefined}\n >\n {children}\n </ul>\n </BranchContext.Provider>\n <StatusRegion items={announcements} describe={describe} />\n </TreeViewContext.Provider>\n );\n }\n);\n\n/**\n * One item in the tree: a row with its chevron, its box, its icon and its label, and, while it's open, a group of the items under it.\n *\n * @function\n * @param {TreeViewItemProps} props - Props for TreeView.Item.\n * @param {React.Ref<HTMLLIElement>} ref - Forwarded ref to the `<li>`.\n * @returns {JSX.Element | null} The rendered item, or nothing under a closed one.\n */\nexport const TreeViewItem = forwardRef<HTMLLIElement, TreeViewItemProps>(\n function TreeViewItem(\n {\n value,\n label,\n icon,\n children,\n hasChildren = false,\n isLoading = false,\n loadError,\n onRetry,\n disabled = false,\n className,\n onClick,\n onKeyDown,\n onFocus,\n onBlur,\n ...props\n },\n ref\n ) {\n const tree = useContext(TreeViewContext);\n const branch = useContext(BranchContext);\n // Also the item's key in the live region.\n const labelId = useId();\n const hasItems = React.Children.toArray(children).length > 0;\n const isParent = hasItems || hasChildren;\n const open = isParent && !!tree?.isOpen(value);\n const parent = branch.path[branch.path.length - 1] ?? null;\n\n // The row at the end of its group, while it's open and on the page.\n const shown = open && !branch.hidden;\n const status = !shown\n ? null\n : isLoading\n ? 'loading'\n : isGiven(loadError)\n ? 'error'\n : null;\n const labelRef = useRef<HTMLSpanElement | null>(null);\n const statusLabelRef = useRef<HTMLSpanElement | null>(null);\n\n // What the live region says follows the row: it's loading, or it failed,\n // while the row says so, and it's loaded once the row goes with the item\n // still open, whichever row it was, until the item closes or loads again.\n // A closed item says nothing, loading or not.\n const phase = shown ? (status ?? 'open') : 'closed';\n const lastPhaseRef = useRef(phase);\n const announce = tree?.announce;\n useEffect(() => {\n const was = lastPhaseRef.current;\n lastPhaseRef.current = phase;\n const kind =\n phase === 'loading' || phase === 'error'\n ? phase\n : phase === 'open' && (was === 'loading' || was === 'error')\n ? 'loaded'\n : null;\n if (!kind) return undefined;\n // Only an item inside a tree can be open.\n announce!(labelId, {\n key: labelId,\n id: `${labelId}:${kind}`,\n kind,\n name: spokenText(labelRef.current!),\n message: kind === 'error' ? spokenText(statusLabelRef.current!) : '',\n });\n return () => announce!(labelId, null);\n }, [phase, announce, labelId]);\n\n const itemClass = usePrefixedClassNames('tree-view-item');\n const groupClass = usePrefixedClassNames('tree-view-group');\n const toggleClass = usePrefixedClassNames('tree-view-toggle');\n const checkClass = usePrefixedClassNames('tree-view-check');\n const iconClass = usePrefixedClassNames('tree-view-icon');\n const labelClass = usePrefixedClassNames('tree-view-label');\n\n const register = tree?.register;\n useClientLayoutEffect(() => {\n if (!register) {\n warnOnce(\n 'TreeView.Item:outside',\n '[bestax-bulma] <TreeView.Item> is outside a <TreeView>, so it ' +\n 'renders nothing. Put it inside one.'\n );\n return undefined;\n }\n return register(value, { parent, disabled, isParent });\n }, [register, value, parent, disabled, isParent]);\n\n const setElement = tree?.setElement;\n const setRefs = useCallback(\n (node: HTMLLIElement | null) => {\n // Only an item inside a tree renders an element to hand over.\n setElement!(value, node);\n if (typeof ref === 'function') ref(node);\n else if (ref) ref.current = node;\n },\n [setElement, value, ref]\n );\n const rowRef = useRef<HTMLDivElement | null>(null);\n const toggleRef = useRef<HTMLSpanElement | null>(null);\n\n const childBranch = useMemo(\n () => ({\n path: [...branch.path, value],\n hidden: branch.hidden || !open,\n }),\n [branch, value, open]\n );\n\n const { selected, checked } =\n tree?.stateOf(value, branch.path, disabled) ?? {};\n const rowClass = usePrefixedClassNames('tree-view-row', {\n 'is-selected': selected,\n 'is-disabled': disabled,\n });\n\n if (!tree) return null;\n\n // While it's open the children render in its group. In a checkbox tree\n // they render while it's closed too, drawing nothing, so a check knows\n // what's under it.\n const items = (open || tree.rendersClosed) && (\n <BranchContext.Provider value={childBranch}>\n {children}\n </BranchContext.Provider>\n );\n if (branch.hidden) return <>{items}</>;\n\n const info = { value, parent, disabled, isParent, open };\n // A position the caller gives wins, for a set the page holds only part of.\n const position = tree.positionOf(value);\n\n return (\n <li\n {...props}\n ref={setRefs}\n className={classNames(itemClass, className)}\n role=\"treeitem\"\n aria-labelledby={labelId}\n aria-level={branch.path.length + 1}\n aria-posinset={props['aria-posinset'] ?? position?.posinset}\n aria-setsize={props['aria-setsize'] ?? position?.setsize}\n aria-expanded={isParent ? open : undefined}\n aria-disabled={disabled || undefined}\n aria-selected={selected}\n aria-checked={checked}\n tabIndex={tree.tabStop === value ? 0 : -1}\n onKeyDown={event => {\n onKeyDown?.(event);\n tree.onKeyDown(event, info);\n }}\n onFocus={event => {\n onFocus?.(event);\n if (event.target === event.currentTarget) {\n tree.onFocus(value, event.currentTarget);\n }\n }}\n onBlur={event => {\n onBlur?.(event);\n if (event.target === event.currentTarget) tree.onBlur();\n }}\n onClick={event => {\n onClick?.(event);\n // A click in its row is its own, and so is one on the item itself,\n // as a test's click on the treeitem is. One in its group belongs\n // to the item inside it, or to nothing. The chevron's box is on\n // every row, but it's a chevron only on a parent: on a leaf it's\n // more of the row.\n const target = event.target as Node;\n if (\n target === event.currentTarget ||\n rowRef.current!.contains(target)\n ) {\n tree.onClick(\n event.currentTarget,\n info,\n isParent && toggleRef.current!.contains(target)\n );\n }\n }}\n >\n <div className={rowClass} ref={rowRef}>\n <span className={toggleClass} aria-hidden=\"true\" ref={toggleRef} />\n {checked !== undefined && (\n <span\n className={checkClass}\n aria-hidden=\"true\"\n onClick={event => {\n // A click on the box's label is followed by one on its\n // input, and both reach the item. The label's is cancelled,\n // so the item toggles once.\n if (!(event.target instanceof HTMLInputElement)) {\n event.preventDefault();\n }\n }}\n >\n <Checkbox\n tabIndex={-1}\n checked={checked === true}\n indeterminate={checked === 'mixed'}\n disabled={disabled}\n onChange={ignoreChange}\n />\n </span>\n )}\n {isGiven(icon) && (\n <span className={iconClass} aria-hidden=\"true\">\n {typeof icon === 'string' ? <Icon name={icon} /> : icon}\n </span>\n )}\n <span className={labelClass} id={labelId} ref={labelRef}>\n {label}\n </span>\n </div>\n {open && (hasItems || status) ? (\n <ul className={groupClass} role=\"group\">\n {items}\n {status && (\n <StatusRow\n kind={status}\n parent={value}\n level={branch.path.length + 2}\n message={loadError}\n onRetry={onRetry}\n labelRef={statusLabelRef}\n />\n )}\n </ul>\n ) : (\n items\n )}\n </li>\n );\n }\n);\n\nTreeViewItem.displayName = 'TreeView.Item';\n\n/** Props for the row at the end of a loading item's group, or a failed load's. */\ninterface StatusRowProps {\n /** `loading`, or `error` for a load that failed. */\n kind: 'loading' | 'error';\n /** The value of the item it's in. */\n parent: string;\n /** Its `aria-level`, one under the item it's in. */\n level: number;\n /** A failed load's message. */\n message: React.ReactNode;\n /** Retries a failed load. */\n onRetry?: () => void;\n /** For its label, which the item reads a failed load's message off. */\n labelRef: React.Ref<HTMLSpanElement>;\n}\n\n/**\n * The row at the end of a loading item's group, or of one whose load failed.\n * It's a `treeitem` the keys reach, the way they reach a disabled item, so a\n * screen reader finds it. It has no value and no record, so it's never\n * selected or checked, and a check never counts it. A failed load's row with\n * `onRetry` retries with Enter, Space or a click, and any other is disabled.\n * The Retry button is drawn, hidden from assistive tech and out of the tab\n * order, the way an item's box is: the row is what's pressed, and its name\n * says so. Rendered in one place, a failed load's row that starts loading\n * again is the same element, so focus stays on it.\n */\nfunction StatusRow({\n kind,\n parent,\n level,\n message,\n onRetry,\n labelRef,\n}: StatusRowProps) {\n // Only an item inside a tree renders a group.\n const tree = useContext(TreeViewContext)!;\n // Also its key in the registry and for the tab stop, where an item has its value.\n const labelId = useId();\n const retryId = useId();\n const retry = kind === 'error' ? onRetry : undefined;\n\n const itemClass = usePrefixedClassNames('tree-view-item');\n const rowClass = usePrefixedClassNames('tree-view-row', {\n 'is-disabled': !retry,\n });\n const toggleClass = usePrefixedClassNames('tree-view-toggle');\n const labelClass = usePrefixedClassNames('tree-view-label');\n const loaderClass = usePrefixedClassNames('loader');\n const retryClass = usePrefixedClassNames('button', { 'is-small': true });\n\n const { setElement } = tree;\n const setRef = useCallback(\n (node: HTMLLIElement | null) => setElement(labelId, node),\n [setElement, labelId]\n );\n const info = {\n value: labelId,\n parent,\n disabled: !retry,\n isParent: false,\n open: false,\n retry,\n };\n const position = tree.positionOf(labelId);\n\n return (\n <li\n ref={setRef}\n className={itemClass}\n role=\"treeitem\"\n aria-labelledby={retry ? `${labelId} ${retryId}` : labelId}\n aria-level={level}\n aria-posinset={position?.posinset}\n aria-setsize={position?.setsize}\n aria-disabled={!retry || undefined}\n tabIndex={tree.tabStop === labelId ? 0 : -1}\n onKeyDown={event => tree.onKeyDown(event, info)}\n onFocus={event => {\n // Focus in something the message holds isn't the row's.\n if (event.target === event.currentTarget) {\n tree.onFocus(labelId, event.currentTarget);\n }\n }}\n onBlur={event => {\n if (event.target === event.currentTarget) tree.onBlur();\n }}\n onClick={event => tree.onClick(event.currentTarget, info, false)}\n >\n <div className={rowClass}>\n <span className={toggleClass} aria-hidden=\"true\">\n {kind === 'loading' && <span className={loaderClass} />}\n </span>\n <span className={labelClass} id={labelId} ref={labelRef}>\n {kind === 'loading' ? tree.loadingLabel : message}\n </span>\n {retry && (\n <span className={retryClass} id={retryId} aria-hidden=\"true\">\n {tree.retryLabel}\n </span>\n )}\n </div>\n </li>\n );\n}\n\nexport const TreeView = withSubComponents(\n TreeViewRoot,\n { Item: TreeViewItem },\n 'TreeView'\n);\n\nexport default TreeView;\n"],"names":["_jsxs","_jsx","_Fragment"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  /* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
2
- import { useRef, useReducer, useState, useCallback } from 'react';
2
+ import { useRef, useReducer, useState, useEffect, useCallback } from 'react';
3
3
  import { useClientLayoutEffect } from './useClientLayoutEffect.js';
4
4
  import { getActiveElementInTree } from './shadowDom.js';
5
5
  import { useTypeahead, typeaheadMatch, typeaheadText } from './typeahead.js';
@@ -29,6 +29,38 @@ function parentItemOf(element) {
29
29
  const group = element.parentElement;
30
30
  return group.getAttribute('role') === 'group' ? group.parentElement : null;
31
31
  }
32
+ /** No positions: on the server, and until the tree has mounted. */
33
+ const NO_POSITIONS = new Map();
34
+ /**
35
+ * Each item's place among the items beside it, by its key, read off the
36
+ * items on the page: the ones in the same group, or at the top. A loading or
37
+ * failed load's row counts, as it does for a browser that works positions
38
+ * out for itself, so the declared ones say what a browser would.
39
+ */
40
+ function positionsOf(shown, { values }) {
41
+ const sets = new Map();
42
+ shown.forEach(element => {
43
+ const set = sets.get(element.parentElement);
44
+ if (set)
45
+ set.push(element);
46
+ else
47
+ sets.set(element.parentElement, [element]);
48
+ });
49
+ const positions = new Map();
50
+ sets.forEach(set => set.forEach((element, at) => positions.set(values.get(element), {
51
+ posinset: at + 1,
52
+ setsize: set.length,
53
+ })));
54
+ return positions;
55
+ }
56
+ /** Whether two sets of positions say the same for every item. */
57
+ function samePositions(a, b) {
58
+ return (a.size === b.size &&
59
+ Array.from(a).every(([key, { posinset, setsize }]) => {
60
+ const other = b.get(key);
61
+ return other?.posinset === posinset && other.setsize === setsize;
62
+ }));
63
+ }
32
64
  /** The values of the items an item on the page is in, from the top. */
33
65
  function pathOf(element, { values }) {
34
66
  const path = [];
@@ -123,9 +155,11 @@ function useControlled(controlled, initial) {
123
155
  return [isControlled ? controlled : own, set];
124
156
  }
125
157
  /**
126
- * The open state, the selection, the keys, the tab stop and the registry of
127
- * a TreeView (#1096). Focus moves for real, with a roving tabindex, rather
128
- * than through `aria-activedescendant`.
158
+ * The open state, the selection, the keys, the tab stop, the positions and
159
+ * the registry of a TreeView (#1096). Focus moves for real, with a roving
160
+ * tabindex, rather than through `aria-activedescendant`. Each item's
161
+ * position among the items beside it is declared rather than left to the
162
+ * browser (#1171), read off the page once the tree has mounted.
129
163
  *
130
164
  * Every item registers its value, its parent and whether it's disabled from
131
165
  * a layout effect. In a checkbox tree a closed item's children still render
@@ -167,6 +201,8 @@ function useTreeView(options) {
167
201
  const restingPathRef = useRef([]);
168
202
  // The values above an item that left the page with focus in it.
169
203
  const lostRef = useRef(null);
204
+ // Each item's place among the items beside it, by its key.
205
+ const [positions, setPositions] = useState(NO_POSITIONS);
170
206
  const { records } = registryRef.current;
171
207
  const checks = mode === 'checkbox'
172
208
  ? // eslint-disable-next-line react-hooks/refs -- read in render on purpose: items write the registry from their layout effects and then ask for this render with `refresh`
@@ -220,11 +256,12 @@ function useTreeView(options) {
220
256
  else
221
257
  act(item, list);
222
258
  };
223
- // After every render, put the stop where it belongs. While focus is in
224
- // the tree it's the focused item. Otherwise it's the selected item, or the
225
- // first checked one on the page, and failing that it stays where it was.
226
- // An item that goes away hands it to its nearest ancestor on the page, or
227
- // to the first item, and focus goes with it if it had focus.
259
+ // After every render, read each item's position off the page, and put the
260
+ // stop where it belongs. While focus is in the tree it's the focused item.
261
+ // Otherwise it's the selected item, or the first checked one on the page,
262
+ // and failing that it stays where it was. An item that goes away hands it
263
+ // to its nearest ancestor on the page, or to the first item, and focus goes
264
+ // with it if it had focus.
228
265
  // eslint-disable-next-line react-hooks/exhaustive-deps -- after every render on purpose: it reads the page, and sets state only when what it reads has changed
229
266
  useClientLayoutEffect(() => {
230
267
  const tree = treeRef.current;
@@ -234,6 +271,9 @@ function useTreeView(options) {
234
271
  // sharing a value, which they shouldn't, can leave it without one the
235
272
  // page still has.
236
273
  const shown = shownIn(tree, registry);
274
+ const placed = positionsOf(shown, registry);
275
+ if (!samePositions(positions, placed))
276
+ setPositions(placed);
237
277
  const onPage = shown.map(element => registry.values.get(element));
238
278
  const elementOf = (value) => shown[onPage.indexOf(value)];
239
279
  const isShown = (value) => value != null && onPage.includes(value);
@@ -255,6 +295,20 @@ function useTreeView(options) {
255
295
  restingPathRef.current = next ? pathOf(elementOf(next), registry) : [];
256
296
  }
257
297
  });
298
+ // Items can also move without the tree rendering, such as children that a
299
+ // component of the app sorts with state of its own. That changes no record,
300
+ // so the page is watched for items coming, going and moving, and the
301
+ // positions are read again.
302
+ useEffect(() => {
303
+ const tree = treeRef.current;
304
+ const observer = new MutationObserver(() => {
305
+ const registry = registryRef.current;
306
+ const placed = positionsOf(shownIn(tree, registry), registry);
307
+ setPositions(current => samePositions(current, placed) ? current : placed);
308
+ });
309
+ observer.observe(tree, { childList: true, subtree: true });
310
+ return () => observer.disconnect();
311
+ }, []);
258
312
  const register = useCallback((value, record) => {
259
313
  const { records: known } = registryRef.current;
260
314
  known.set(value, record);
@@ -377,6 +431,7 @@ function useTreeView(options) {
377
431
  tabStop,
378
432
  isOpen,
379
433
  stateOf,
434
+ positionOf: key => positions.get(key),
380
435
  register,
381
436
  setElement,
382
437
  onFocus,