@allxsmith/bestax-bulma 5.61.0 → 5.63.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.
- package/dist/bestax.css +1 -1
- package/dist/bestax.css.map +1 -1
- package/dist/esm/components/EmptyState.js +2 -6
- package/dist/esm/components/EmptyState.js.map +1 -1
- package/dist/esm/components/Timeline.js +5 -9
- package/dist/esm/components/Timeline.js.map +1 -1
- package/dist/esm/components/TreeView.js +16 -16
- package/dist/esm/components/TreeView.js.map +1 -1
- package/dist/esm/elements/Buttons.js +2 -0
- package/dist/esm/elements/Buttons.js.map +1 -1
- package/dist/esm/elements/CopyButton.js +132 -0
- package/dist/esm/elements/CopyButton.js.map +1 -0
- package/dist/esm/elements/DescriptionList.js +1 -5
- package/dist/esm/elements/DescriptionList.js.map +1 -1
- package/dist/esm/elements/Stat.js +196 -0
- package/dist/esm/elements/Stat.js.map +1 -0
- package/dist/esm/elements/Stats.js +48 -0
- package/dist/esm/elements/Stats.js.map +1 -0
- package/dist/esm/elements/statContext.js +10 -0
- package/dist/esm/elements/statContext.js.map +1 -0
- package/dist/esm/form/Combobox.js +10 -14
- package/dist/esm/form/Combobox.js.map +1 -1
- package/dist/esm/form/Field.js.map +1 -1
- package/dist/esm/form/TreeSelect.js +395 -0
- package/dist/esm/form/TreeSelect.js.map +1 -0
- package/dist/esm/form/_pickerInternals/PickerPopover.js +5 -1
- package/dist/esm/form/_pickerInternals/PickerPopover.js.map +1 -1
- package/dist/esm/form/_selectInternals/DialogSelect.js +208 -0
- package/dist/esm/form/_selectInternals/DialogSelect.js.map +1 -0
- package/dist/esm/form/_selectInternals/ValueTags.js +28 -0
- package/dist/esm/form/_selectInternals/ValueTags.js.map +1 -0
- package/dist/esm/helpers/checkIcon.js +20 -0
- package/dist/esm/helpers/checkIcon.js.map +1 -0
- package/dist/esm/helpers/isGiven.js +13 -0
- package/dist/esm/helpers/isGiven.js.map +1 -0
- package/dist/esm/helpers/statusRegion.js +3 -3
- package/dist/esm/helpers/treeData.js +97 -0
- package/dist/esm/helpers/treeData.js.map +1 -0
- package/dist/esm/helpers/useClipboard.js +176 -0
- package/dist/esm/helpers/useClipboard.js.map +1 -0
- package/dist/esm/helpers/useComboboxEngine.js +2 -2
- package/dist/esm/helpers/useComboboxEngine.js.map +1 -1
- package/dist/esm/helpers/useControlled.js +30 -0
- package/dist/esm/helpers/useControlled.js.map +1 -0
- package/dist/esm/helpers/useTreeView.js +15 -13
- package/dist/esm/helpers/useTreeView.js.map +1 -1
- package/dist/extras.css +1 -1
- package/dist/extras.css.map +1 -1
- package/dist/index.cjs +10605 -9380
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +5 -0
- package/dist/index.esm.js.map +1 -1
- package/dist/types/elements/Buttons.d.ts +1 -0
- package/dist/types/elements/CopyButton.d.ts +105 -0
- package/dist/types/elements/Stat.d.ts +242 -0
- package/dist/types/elements/Stats.d.ts +40 -0
- package/dist/types/elements/statContext.d.ts +18 -0
- package/dist/types/form/Field.d.ts +1 -1
- package/dist/types/form/TreeSelect.d.ts +308 -0
- package/dist/types/form/_pickerInternals/PickerPopover.d.ts +8 -0
- package/dist/types/form/_selectInternals/DialogSelect.d.ts +86 -0
- package/dist/types/form/_selectInternals/ValueTags.d.ts +40 -0
- package/dist/types/helpers/checkIcon.d.ts +10 -0
- package/dist/types/helpers/isGiven.d.ts +7 -0
- package/dist/types/helpers/treeData.d.ts +48 -0
- package/dist/types/helpers/useClipboard.d.ts +65 -0
- package/dist/types/helpers/useControlled.d.ts +14 -0
- package/dist/types/helpers/useTreeView.d.ts +30 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types-cjs/elements/Buttons.d.ts +1 -0
- package/dist/types-cjs/elements/CopyButton.d.ts +105 -0
- package/dist/types-cjs/elements/Stat.d.ts +242 -0
- package/dist/types-cjs/elements/Stats.d.ts +40 -0
- package/dist/types-cjs/elements/statContext.d.ts +18 -0
- package/dist/types-cjs/form/Field.d.ts +1 -1
- package/dist/types-cjs/form/TreeSelect.d.ts +308 -0
- package/dist/types-cjs/form/_pickerInternals/PickerPopover.d.ts +8 -0
- package/dist/types-cjs/form/_selectInternals/DialogSelect.d.ts +86 -0
- package/dist/types-cjs/form/_selectInternals/ValueTags.d.ts +40 -0
- package/dist/types-cjs/helpers/checkIcon.d.ts +10 -0
- package/dist/types-cjs/helpers/isGiven.d.ts +7 -0
- package/dist/types-cjs/helpers/treeData.d.ts +48 -0
- package/dist/types-cjs/helpers/useClipboard.d.ts +65 -0
- package/dist/types-cjs/helpers/useControlled.d.ts +14 -0
- package/dist/types-cjs/helpers/useTreeView.d.ts +30 -0
- package/dist/types-cjs/index.d.ts +6 -0
- package/dist/versions/bestax-no-dark-mode.css +1 -1
- package/dist/versions/bestax-no-dark-mode.css.map +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css +1 -1
- package/dist/versions/bestax-no-helpers-prefixed.css.map +1 -1
- package/dist/versions/bestax-no-helpers.css +1 -1
- package/dist/versions/bestax-no-helpers.css.map +1 -1
- package/dist/versions/bestax-prefixed.css +1 -1
- package/dist/versions/bestax-prefixed.css.map +1 -1
- package/package.json +1 -1
- package/src/scss/elements/_index.scss +1 -0
- package/src/scss/elements/_stat.scss +153 -0
- package/src/scss/form/_index.scss +1 -0
- package/src/scss/form/_picker-popover.scss +6 -0
- package/src/scss/form/_treeselect.scss +146 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
3
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
import { forwardRef, useMemo, useEffect, useState, useRef, useCallback, useId } from 'react';
|
|
5
|
+
import { usePrefixedClassNames } from '../helpers/classNames.js';
|
|
6
|
+
import { warnOnce } from '../helpers/devWarnings.js';
|
|
7
|
+
import { useLocaleMatcher } from '../helpers/localeFormat.js';
|
|
8
|
+
import { spokenText, StatusRegion } from '../helpers/statusRegion.js';
|
|
9
|
+
import { readChecks, TreeViewHiddenContext, toggledList } from '../helpers/useTreeView.js';
|
|
10
|
+
import { useControlled } from '../helpers/useControlled.js';
|
|
11
|
+
import { indexTree, checkRecords, searchTree, checkedByStrategy, childNodes, nodeLabel, isParentNode } from '../helpers/treeData.js';
|
|
12
|
+
import { TreeView } from '../components/TreeView.js';
|
|
13
|
+
import { useFieldsetDisabled } from './FormContext.js';
|
|
14
|
+
import { DialogSelect } from './_selectInternals/DialogSelect.js';
|
|
15
|
+
import { tagIds, ValueTags } from './_selectInternals/ValueTags.js';
|
|
16
|
+
|
|
17
|
+
const NOTHING_HIDDEN = new Set();
|
|
18
|
+
const noMatches = [{ id: 'empty' }];
|
|
19
|
+
const nothingToSay = [];
|
|
20
|
+
/** What takes focus as the panel opens. */
|
|
21
|
+
const INITIAL_FOCUS = '[data-initial-focus]';
|
|
22
|
+
/** One value or several, as a list. */
|
|
23
|
+
const asList = (value) => value == null ? [] : Array.isArray(value) ? value : [value];
|
|
24
|
+
/** Whether two lists hold the same values, whatever their order and repeats. */
|
|
25
|
+
const sameValues = (a, b) => {
|
|
26
|
+
const inA = new Set(a);
|
|
27
|
+
const inB = new Set(b);
|
|
28
|
+
return inA.size === inB.size && [...inA].every(value => inB.has(value));
|
|
29
|
+
};
|
|
30
|
+
/** The list with every value of `more` it doesn't have added at the end. */
|
|
31
|
+
const withAll = (list, more) => [
|
|
32
|
+
...list,
|
|
33
|
+
...more.filter(value => !list.includes(value)),
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* The `TreeSelect` component is a form field whose options are a tree, such as categories, places or teams: it opens a panel holding a real `TreeView`, picks one item or checks a branch at a time, and searches labels.
|
|
37
|
+
*
|
|
38
|
+
* The field is a read-only `role="combobox"` that opens a dialog, wired as
|
|
39
|
+
* DateInput's is, so focus moves into the tree for real and the tree keeps
|
|
40
|
+
* TreeView's keys, type-ahead and spoken output. The tree is given as `data`,
|
|
41
|
+
* so the field and the tags can name what's picked while the panel is shut,
|
|
42
|
+
* and on the server.
|
|
43
|
+
*
|
|
44
|
+
* `selectionMode` picks the kind. `'single'` picks one item and closes. With
|
|
45
|
+
* `'checkbox'`, a check reaches every enabled item under it, closed or hidden
|
|
46
|
+
* by a search, and `checkedStrategy` decides what the value holds: the
|
|
47
|
+
* leaves, the topmost checked items or every one. With
|
|
48
|
+
* `'checkbox-independent'`, each box is its own item's, so a parent means
|
|
49
|
+
* itself alone (Mantine's and Ant Design's `checkStrictly`). The value shows
|
|
50
|
+
* as tags under the field.
|
|
51
|
+
*
|
|
52
|
+
* A value no node has is kept and shown as it is, with a warning in
|
|
53
|
+
* development, so a value under a branch that hasn't loaded survives until
|
|
54
|
+
* it does.
|
|
55
|
+
*
|
|
56
|
+
* @function
|
|
57
|
+
* @param {TreeSelectProps} props - Props for the TreeSelect component.
|
|
58
|
+
* @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the field.
|
|
59
|
+
* @returns {JSX.Element} The field, its tags and its panel, in a `Field` with the label unless one is around it.
|
|
60
|
+
* @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/combobox/ | APG Combobox Pattern}
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* <TreeSelect
|
|
64
|
+
* label="Save to"
|
|
65
|
+
* data={[
|
|
66
|
+
* { value: 'docs', label: 'Documents', children: [{ value: 'invoices', label: 'Invoices' }] },
|
|
67
|
+
* { value: 'photos', label: 'Photos' },
|
|
68
|
+
* ]}
|
|
69
|
+
* searchable
|
|
70
|
+
* />
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* <TreeSelect label="Regions" selectionMode="checkbox" checkedStrategy="parent" data={regions} />
|
|
74
|
+
*/
|
|
75
|
+
const TreeSelect = forwardRef((props, ref) => {
|
|
76
|
+
const { data, expanded: expandedProp, defaultExpanded = [], onExpandedChange, open: openProp, defaultOpen = false, onOpenChange, searchable = false, query: queryProp, defaultQuery = '', onQueryChange, filter, empty, itemTemplate, disabled: disabledProp, clearable = false, size, labels, selectionMode = 'single', value: valueProp, defaultValue,
|
|
77
|
+
// Called below through `props`, which knows which value it takes.
|
|
78
|
+
onChange: _onChange, parentsSelectable = true, closeOnSelect = true, checkedStrategy = 'child', ariaCloseLabel, ...rest } = props;
|
|
79
|
+
const disabled = useFieldsetDisabled(disabledProp);
|
|
80
|
+
const single = selectionMode === 'single';
|
|
81
|
+
const cascade = selectionMode === 'checkbox';
|
|
82
|
+
const index = useMemo(() => indexTree(data), [data]);
|
|
83
|
+
const records = useMemo(() => checkRecords(index), [index]);
|
|
84
|
+
const labelFor = (value) => {
|
|
85
|
+
const entry = index.get(value);
|
|
86
|
+
return entry ? nodeLabel(entry.node) : value;
|
|
87
|
+
};
|
|
88
|
+
// The value, as a list: none or one in `single`.
|
|
89
|
+
const [held, setHeld] = useControlled(valueProp, defaultValue ?? null);
|
|
90
|
+
const listed = asList(held);
|
|
91
|
+
const change = (next) => {
|
|
92
|
+
const value = single ? (next[0] ?? null) : next;
|
|
93
|
+
setHeld(value);
|
|
94
|
+
// The mode decides which of the two `onChange` takes.
|
|
95
|
+
const report = props.onChange;
|
|
96
|
+
report?.(value);
|
|
97
|
+
};
|
|
98
|
+
// What the field, the tags and the hidden inputs show: the value as it's
|
|
99
|
+
// held. `checkedStrategy` shapes a new value as a check makes it, so a
|
|
100
|
+
// branch that loads later changes nothing the form posts.
|
|
101
|
+
const picked = single ? listed.slice(0, 1) : Array.from(new Set(listed));
|
|
102
|
+
const unknown = picked.find(value => !index.has(value));
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (unknown !== undefined) {
|
|
105
|
+
warnOnce('TreeSelect:unknown-value', `[bestax-bulma] <TreeSelect> was given the value "${unknown}", ` +
|
|
106
|
+
'which no node in its data has. It is kept and shown as it is, ' +
|
|
107
|
+
'as it should be for a node under a branch that has not loaded ' +
|
|
108
|
+
'yet. Otherwise, check the value against the data.');
|
|
109
|
+
}
|
|
110
|
+
}, [unknown]);
|
|
111
|
+
const [expanded, setExpanded] = useControlled(expandedProp, defaultExpanded);
|
|
112
|
+
const changeExpanded = (next) => {
|
|
113
|
+
setExpanded(next);
|
|
114
|
+
onExpandedChange?.(next);
|
|
115
|
+
};
|
|
116
|
+
// The picked item focus goes to: the first in the tree's order.
|
|
117
|
+
const pickedSet = new Set(picked);
|
|
118
|
+
const target = Array.from(index.keys()).find(value => pickedSet.has(value));
|
|
119
|
+
const [query, setQueryState] = useControlled(queryProp, defaultQuery);
|
|
120
|
+
const setQuery = (next) => {
|
|
121
|
+
if (next === query)
|
|
122
|
+
return;
|
|
123
|
+
setQueryState(next);
|
|
124
|
+
onQueryChange?.(next);
|
|
125
|
+
};
|
|
126
|
+
const [isOpen, setOpenState] = useControlled(openProp, defaultOpen);
|
|
127
|
+
const setOpen = (next) => {
|
|
128
|
+
if (next === isOpen)
|
|
129
|
+
return;
|
|
130
|
+
if (next) {
|
|
131
|
+
// Opening shows the picked item, its branch open.
|
|
132
|
+
const path = (target !== undefined && index.get(target)?.path) || [];
|
|
133
|
+
if (path.some(value => !expanded.includes(value))) {
|
|
134
|
+
changeExpanded(withAll(expanded, path));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
setQuery('');
|
|
139
|
+
}
|
|
140
|
+
setOpenState(next);
|
|
141
|
+
onOpenChange?.(next);
|
|
142
|
+
};
|
|
143
|
+
const matchText = useLocaleMatcher();
|
|
144
|
+
const typed = searchable ? query.trim() : '';
|
|
145
|
+
// Every item reads the hidden set, so it's only built again when the
|
|
146
|
+
// search text or the data changes.
|
|
147
|
+
const search = useMemo(() => typed
|
|
148
|
+
? searchTree(data, (node, path) => {
|
|
149
|
+
if (filter)
|
|
150
|
+
return filter(typed, node, path);
|
|
151
|
+
const text = nodeLabel(node);
|
|
152
|
+
return matchText
|
|
153
|
+
? matchText(text, typed)
|
|
154
|
+
: text.toLowerCase().includes(typed.toLowerCase());
|
|
155
|
+
})
|
|
156
|
+
: null,
|
|
157
|
+
// `filter` is read as it is when the text or the data changes: one
|
|
158
|
+
// written inline is a new function every render, and listing it would
|
|
159
|
+
// search again on each.
|
|
160
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
161
|
+
[data, typed, matchText]);
|
|
162
|
+
// The branches a search opened that were closed during it, for the
|
|
163
|
+
// search text they were closed under: any new text, from the box or
|
|
164
|
+
// from a held `query`, starts over.
|
|
165
|
+
const [searchClosed, setSearchClosed] = useState({
|
|
166
|
+
query: typed,
|
|
167
|
+
values: [],
|
|
168
|
+
});
|
|
169
|
+
if (searchClosed.query !== typed) {
|
|
170
|
+
setSearchClosed({ query: typed, values: [] });
|
|
171
|
+
}
|
|
172
|
+
const closedHere = searchClosed.query === typed ? searchClosed.values : [];
|
|
173
|
+
// The branches a search opens show open over the ones you opened, and
|
|
174
|
+
// close again once it's cleared.
|
|
175
|
+
const forced = search
|
|
176
|
+
? search.open.filter(value => !closedHere.includes(value))
|
|
177
|
+
: [];
|
|
178
|
+
const shownExpanded = withAll(expanded, forced);
|
|
179
|
+
// TreeView's click on a row selects it and opens it. A pick by click, as
|
|
180
|
+
// a pick by Enter, only picks, so the open the tree asks for right after
|
|
181
|
+
// one on a parent is dropped, and a lazy parent isn't fetched to be
|
|
182
|
+
// picked.
|
|
183
|
+
const clickPickedRef = useRef(false);
|
|
184
|
+
const onTreeExpandedChange = (next) => {
|
|
185
|
+
if (clickPickedRef.current) {
|
|
186
|
+
clickPickedRef.current = false;
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const closed = shownExpanded.filter(value => !next.includes(value));
|
|
190
|
+
const opened = next.filter(value => !shownExpanded.includes(value));
|
|
191
|
+
const closedForced = closed.filter(value => forced.includes(value));
|
|
192
|
+
if (closedForced.length > 0) {
|
|
193
|
+
setSearchClosed({
|
|
194
|
+
query: typed,
|
|
195
|
+
values: [...closedHere, ...closedForced],
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
if (opened.length > 0 || closed.some(value => expanded.includes(value))) {
|
|
199
|
+
changeExpanded([
|
|
200
|
+
...expanded.filter(value => !closed.includes(value)),
|
|
201
|
+
...opened,
|
|
202
|
+
]);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
// Whether an item is on the page with the panel open: every branch
|
|
206
|
+
// above it open, and the search not hiding it.
|
|
207
|
+
const visible = (value) => !search?.hidden.has(value) &&
|
|
208
|
+
index.get(value).path.every(above => shownExpanded.includes(above));
|
|
209
|
+
const focusTarget = target !== undefined && visible(target)
|
|
210
|
+
? target
|
|
211
|
+
: data.find(node => visible(node.value))?.value;
|
|
212
|
+
// Picking in `single`. TreeView shows `selected`, held here, and the
|
|
213
|
+
// picks are made from the rows' own keys and clicks, so a parent can
|
|
214
|
+
// open instead and a pick of the picked item still closes.
|
|
215
|
+
const pickable = (node) => !node.disabled && (parentsSelectable || !isParentNode(node));
|
|
216
|
+
const pick = (value) => {
|
|
217
|
+
if (listed.length !== 1 || listed[0] !== value)
|
|
218
|
+
change([value]);
|
|
219
|
+
if (closeOnSelect)
|
|
220
|
+
setOpen(false);
|
|
221
|
+
};
|
|
222
|
+
const toggleClass = usePrefixedClassNames('tree-view-toggle');
|
|
223
|
+
const rowHandlers = (node) => single
|
|
224
|
+
? {
|
|
225
|
+
onKeyDown: (event) => {
|
|
226
|
+
if (event.target !== event.currentTarget ||
|
|
227
|
+
event.repeat ||
|
|
228
|
+
event.altKey ||
|
|
229
|
+
event.ctrlKey ||
|
|
230
|
+
event.metaKey ||
|
|
231
|
+
(event.key !== 'Enter' && event.key !== ' ')) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (pickable(node)) {
|
|
235
|
+
pick(node.value);
|
|
236
|
+
}
|
|
237
|
+
else if (!node.disabled && isParentNode(node)) {
|
|
238
|
+
// TreeView leaves a key that's been used alone.
|
|
239
|
+
event.preventDefault();
|
|
240
|
+
onTreeExpandedChange(shownExpanded.includes(node.value)
|
|
241
|
+
? shownExpanded.filter(value => value !== node.value)
|
|
242
|
+
: [...shownExpanded, node.value]);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
onClick: (event) => {
|
|
246
|
+
const item = event.currentTarget;
|
|
247
|
+
const target = event.target;
|
|
248
|
+
// A click in this item's own row, where TreeView reads one,
|
|
249
|
+
// not one in the group of items inside it, and not on its
|
|
250
|
+
// chevron, which only opens it.
|
|
251
|
+
const inRow = target === item || item.firstElementChild.contains(target);
|
|
252
|
+
const onChevron = isParentNode(node) && !!target.closest(`.${toggleClass}`);
|
|
253
|
+
if (inRow && !onChevron && pickable(node)) {
|
|
254
|
+
// TreeView opens a parent it's handed this click on, in
|
|
255
|
+
// this same click: the flag goes once the click is over.
|
|
256
|
+
clickPickedRef.current = isParentNode(node);
|
|
257
|
+
queueMicrotask(() => {
|
|
258
|
+
clickPickedRef.current = false;
|
|
259
|
+
});
|
|
260
|
+
pick(node.value);
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
: undefined;
|
|
265
|
+
const renderNode = (node) => (jsx(TreeView.Item, { value: node.value, label: itemTemplate ? itemTemplate(node) : nodeLabel(node), icon: node.icon, hasChildren: node.hasChildren, isLoading: node.isLoading, loadError: node.loadError, onRetry: node.onRetry, disabled: node.disabled, "data-initial-focus": !searchable && node.value === focusTarget ? '' : undefined, ...rowHandlers(node), children: childNodes(node).map(renderNode) }, node.value));
|
|
266
|
+
// In `checkbox`, TreeView reports every checked item, read here into
|
|
267
|
+
// the list `checkedStrategy` gives.
|
|
268
|
+
const shaped = (next) => cascade ? checkedByStrategy(records, next, checkedStrategy) : next;
|
|
269
|
+
const onCheckedChange = (next) => change(shaped(next));
|
|
270
|
+
// Every item's check state, read once a render, which the clear button
|
|
271
|
+
// and the tags' buttons decide from. Only `checkbox` reads TreeView's
|
|
272
|
+
// rules: in `checkbox-independent` a listed item is a checked one.
|
|
273
|
+
const listedKey = JSON.stringify(listed);
|
|
274
|
+
const checks = useMemo(() => (cascade ? readChecks(records, new Set(listed)) : null),
|
|
275
|
+
// `listed` by what it holds, since a held list is a new array each
|
|
276
|
+
// render.
|
|
277
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
278
|
+
[cascade, records, listedKey]);
|
|
279
|
+
const isDisabled = (value) => !!index.get(value)?.node.disabled;
|
|
280
|
+
// Read in `checkbox` only, where there are states to read.
|
|
281
|
+
const isChecked = (value) => checks.states.get(value) === true;
|
|
282
|
+
// What a control can uncheck. In `checkbox`, that's each checked end no
|
|
283
|
+
// checked disabled item above it keeps checked, and every item above it,
|
|
284
|
+
// which takes it along; elsewhere it's each listed item that isn't
|
|
285
|
+
// disabled. A disabled item is checked only by its own value, so nothing
|
|
286
|
+
// unchecks it, or what it keeps checked.
|
|
287
|
+
const uncheckable = new Set();
|
|
288
|
+
if (checks) {
|
|
289
|
+
for (const end of checks.ends) {
|
|
290
|
+
const { path } = index.get(end);
|
|
291
|
+
if (isChecked(end) &&
|
|
292
|
+
!path.some(above => isDisabled(above) && isChecked(above))) {
|
|
293
|
+
uncheckable.add(end);
|
|
294
|
+
path.forEach(above => uncheckable.add(above));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
else if (!single) {
|
|
299
|
+
picked.forEach(value => !isDisabled(value) && uncheckable.add(value));
|
|
300
|
+
}
|
|
301
|
+
// A tag's button takes its value off where that changes what's checked,
|
|
302
|
+
// the way its box would. A value no node has can always go.
|
|
303
|
+
const removable = (value) => !index.has(value) || (!isDisabled(value) && uncheckable.has(value));
|
|
304
|
+
const without = (value) => cascade && records.has(value)
|
|
305
|
+
? shaped(toggledList(records, listed, value))
|
|
306
|
+
: listed.filter(item => item !== value);
|
|
307
|
+
// What the clear button leaves: the checked disabled items, and in
|
|
308
|
+
// `checkbox` what they keep checked, shaped as a check's value is.
|
|
309
|
+
const cleared = single
|
|
310
|
+
? []
|
|
311
|
+
: shaped(checks
|
|
312
|
+
? Array.from(records.keys()).filter(value => isDisabled(value) && isChecked(value))
|
|
313
|
+
: picked.filter(isDisabled));
|
|
314
|
+
const canClear = single
|
|
315
|
+
? picked.length > 0
|
|
316
|
+
: uncheckable.size > 0 || picked.some(value => !index.has(value));
|
|
317
|
+
const inputRef = useRef(null);
|
|
318
|
+
// A forwarded callback's cleanup, which React 19 lets it return, run on
|
|
319
|
+
// detach in place of a call with `null`, on React 18 as well.
|
|
320
|
+
const refCleanupRef = useRef(null);
|
|
321
|
+
const fieldRef = useCallback((node) => {
|
|
322
|
+
inputRef.current = node;
|
|
323
|
+
if (typeof ref === 'function') {
|
|
324
|
+
if (node === null) {
|
|
325
|
+
const cleanup = refCleanupRef.current;
|
|
326
|
+
refCleanupRef.current = null;
|
|
327
|
+
if (cleanup)
|
|
328
|
+
cleanup();
|
|
329
|
+
else
|
|
330
|
+
ref(null);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const cleanup = ref(node);
|
|
334
|
+
refCleanupRef.current =
|
|
335
|
+
typeof cleanup === 'function' ? cleanup : null;
|
|
336
|
+
}
|
|
337
|
+
else if (ref) {
|
|
338
|
+
ref.current = node;
|
|
339
|
+
}
|
|
340
|
+
}, [ref]);
|
|
341
|
+
const tagsId = useId();
|
|
342
|
+
const ids = tagIds(tagsId, picked);
|
|
343
|
+
const searchClass = usePrefixedClassNames('input', 'tree-select-search', {
|
|
344
|
+
[`is-${size}`]: !!size,
|
|
345
|
+
});
|
|
346
|
+
const emptyClass = usePrefixedClassNames('tree-select-empty');
|
|
347
|
+
const treeRef = useRef(null);
|
|
348
|
+
const emptyRef = useRef(null);
|
|
349
|
+
const nothingFound = !!search && !search.found;
|
|
350
|
+
// The region reads the text on screen, whatever `empty` renders.
|
|
351
|
+
const describeEmpty = useCallback(() => {
|
|
352
|
+
const node = emptyRef.current;
|
|
353
|
+
/* istanbul ignore if: it's only announced while it's on screen */
|
|
354
|
+
if (!node)
|
|
355
|
+
return null;
|
|
356
|
+
return spokenText(node);
|
|
357
|
+
}, []);
|
|
358
|
+
return (jsx(DialogSelect, { ...rest, component: "TreeSelect", rootClass: "tree-select", panelClass: "tree-select-panel", text: single && picked.length > 0 ? labelFor(picked[0]) : '', values: single ? [picked[0] ?? ''] : picked, hasValue: picked.length > 0, open: isOpen, onOpenChange: setOpen, onPrintableKey: searchable
|
|
359
|
+
? key => {
|
|
360
|
+
setQuery(key);
|
|
361
|
+
setOpen(true);
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
: undefined, onClear: () => change(cleared), clearable: clearable && canClear, onFormReset: () => {
|
|
365
|
+
// Back to `defaultValue`, or to nothing for a held value, which
|
|
366
|
+
// its owner sets from `onChange`.
|
|
367
|
+
const back = valueProp === undefined ? asList(defaultValue) : [];
|
|
368
|
+
if (!sameValues(back, listed))
|
|
369
|
+
change(back);
|
|
370
|
+
}, clearLabel: labels?.clear || 'Clear', chooseLabel: labels?.choose, disabled: disabled, size: size, inputRef: inputRef, fieldRef: fieldRef, describedBy: single ? undefined : ids.join(' '), below: single ? undefined : (jsx(ValueTags, { values: picked, ids: ids, labelFor: labelFor, disabled: disabled, ariaCloseLabel: ariaCloseLabel, removable: removable, onRemove: value => {
|
|
371
|
+
change(without(value));
|
|
372
|
+
inputRef.current?.focus();
|
|
373
|
+
} })), initialFocusSelector: INITIAL_FOCUS, children: panelName => (jsxs(Fragment, { children: [searchable && (jsx("input", { className: searchClass, type: "search", "aria-label": labels?.search || 'Search', autoComplete: "off", value: query, "data-initial-focus": "", onChange: event => setQuery(event.target.value), onFocus: event => {
|
|
374
|
+
// A key that opened the panel is in the box: type after it.
|
|
375
|
+
const { length } = event.currentTarget.value;
|
|
376
|
+
event.currentTarget.setSelectionRange(length, length);
|
|
377
|
+
}, onKeyDown: event => {
|
|
378
|
+
if (event.key === 'Enter') {
|
|
379
|
+
// The search box's Enter would submit the form.
|
|
380
|
+
event.preventDefault();
|
|
381
|
+
}
|
|
382
|
+
else if (event.key === 'ArrowDown' && !event.altKey) {
|
|
383
|
+
event.preventDefault();
|
|
384
|
+
const tree = treeRef.current;
|
|
385
|
+
(tree?.querySelector('[role="treeitem"][tabindex="0"]') ?? tree?.querySelector('[role="treeitem"]'))?.focus();
|
|
386
|
+
}
|
|
387
|
+
} })), nothingFound ? (empty && (jsx("div", { className: emptyClass, ref: emptyRef, children: empty }))) : (jsx(TreeViewHiddenContext.Provider, { value: search?.hidden ?? NOTHING_HIDDEN, children: jsx(TreeView, { ...panelName, ref: treeRef,
|
|
388
|
+
// It scrolls, and Firefox makes a scroller a Tab stop of
|
|
389
|
+
// its own: the roving one on its items is the tree's.
|
|
390
|
+
tabIndex: -1, selectionMode: selectionMode, size: size, selected: single ? (picked[0] ?? null) : undefined, checked: single ? undefined : listed, onCheckedChange: onCheckedChange, expanded: shownExpanded, onExpandedChange: onTreeExpandedChange, loadingLabel: labels?.loading, retryLabel: labels?.retry, loadingAnnouncement: labels?.loadingAnnouncement, loadedAnnouncement: labels?.loadedAnnouncement, children: data.map(renderNode) }) })), jsx(StatusRegion, { items: nothingFound && empty ? noMatches : nothingToSay, describe: describeEmpty })] })) }));
|
|
391
|
+
});
|
|
392
|
+
TreeSelect.displayName = 'TreeSelect';
|
|
393
|
+
|
|
394
|
+
export { TreeSelect };
|
|
395
|
+
//# sourceMappingURL=TreeSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TreeSelect.js","sources":["../../../src/form/TreeSelect.tsx"],"sourcesContent":["import React, {\n forwardRef,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { usePrefixedClassNames } from '../helpers/classNames';\nimport type { BulmaClassesProps } from '../helpers/useBulmaClasses';\nimport { warnOnce } from '../helpers/devWarnings';\nimport { useLocaleMatcher } from '../helpers/localeFormat';\nimport { StatusRegion, spokenText } from '../helpers/statusRegion';\nimport {\n TreeViewHiddenContext,\n readChecks,\n toggledList,\n} from '../helpers/useTreeView';\nimport { useControlled } from '../helpers/useControlled';\nimport {\n checkRecords,\n checkedByStrategy,\n childNodes,\n indexTree,\n isParentNode,\n nodeLabel,\n searchTree,\n} from '../helpers/treeData';\nimport { TreeView } from '../components/TreeView';\nimport type { FormFieldProps } from './fieldProps';\nimport { useFieldsetDisabled } from './FormContext';\nimport type { PickerPosition } from './_pickerInternals/pickerTypes';\nimport { DialogSelect } from './_selectInternals/DialogSelect';\nimport { ValueTags, tagIds } from './_selectInternals/ValueTags';\n\n/**\n * One node in a TreeSelect's `data`: Combobox's item fields plus\n * TreeView.Item's, with `children` for the nodes under it.\n */\nexport interface TreeItemData {\n /** **Required.** What the value holds and `name` posts. Unique in the whole tree. */\n value: string;\n /** What the row, the field and the tags show, and what search matches. The `value` when it's left out. */\n label?: string;\n /** Reachable, but never picked, checked, opened or closed. */\n disabled?: boolean;\n /** A picture before the label: an `Icon` name, or a node. Hidden from assistive tech. */\n icon?: React.ReactNode;\n /** The nodes under this one. */\n children?: TreeItemData[];\n /** Shows the node as a parent before its children are known, for a branch that loads when it opens. */\n hasChildren?: boolean;\n /** Its children are on their way: an open branch ends in a row that says so, and the live region says it too. */\n isLoading?: boolean;\n /** Its children didn't load: an open branch ends in this message, with a Retry button when there's an `onRetry`. */\n loadError?: React.ReactNode;\n /** Called by the Retry button on a failed load's row. Set `isLoading` while the retry runs. */\n onRetry?: () => void;\n}\n\n/** What a checked branch puts in the value a check makes. */\nexport type TreeSelectCheckedStrategy = 'child' | 'parent' | 'all';\n\n/** The visible and accessible strings, in your app's language. */\nexport interface TreeSelectLabels {\n /**\n * Names the panel and the tree in it.\n * @defaultValue the field's label\n */\n choose?: string;\n /**\n * Names the search box.\n * @defaultValue 'Search'\n */\n search?: string;\n /**\n * Names the clear button.\n * @defaultValue 'Clear'\n */\n clear?: string;\n /**\n * What a loading branch's row says, as on TreeView.\n * @defaultValue 'Loading…'\n */\n loading?: string;\n /**\n * The Retry button's text on a failed load's row, as on TreeView.\n * @defaultValue 'Retry'\n */\n retry?: string;\n /**\n * What the live region says when an open branch starts loading, built\n * from its name, as on TreeView.\n * @defaultValue name => `Loading ${name}`\n */\n loadingAnnouncement?: (name: string) => string;\n /**\n * What the live region says when that load finishes, as on TreeView.\n * @defaultValue name => `${name} loaded`\n */\n loadedAnnouncement?: (name: string) => string;\n}\n\n/**\n * What every TreeSelect takes. The helper props and the other attributes\n * land on the root `<div>`, as Combobox's do, and the `ref` on the field.\n */\ninterface TreeSelectBaseProps\n extends\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'color' | 'onChange' | 'defaultValue' | 'children'\n >,\n Omit<BulmaClassesProps, 'color'>,\n FormFieldProps {\n /** **Required.** The tree, as nodes nested through `children`. */\n data: TreeItemData[];\n /**\n * The field's label, which names the field through `htmlFor`, and the\n * panel and the tree in it through `aria-labelledby`. Dropped inside an\n * outer `Field`, whose label names them instead.\n */\n label?: React.ReactNode;\n /** The field's `id`, which the label then points at. The panel's is this one with `-panel` after it. */\n id?: string;\n /** Names the field, and the panel and the tree, in place of a `label`. */\n 'aria-label'?: string;\n /** The ids of what names the field, and the panel and the tree, in place of a `label`. */\n 'aria-labelledby'?: string;\n /** The ids of what describes the field, ahead of the tags. */\n 'aria-describedby'?: string;\n /** The open branches' values when you hold the state, as TreeView's `expanded`. */\n expanded?: string[];\n /** The branches open to begin with, when TreeSelect holds its own state. */\n defaultExpanded?: string[];\n /**\n * Called with the new list whenever a branch opens or closes, opening the\n * panel on the picked item's branch included. Fetch a lazy branch's\n * children from here. The branches a search opens aren't in it.\n */\n onExpandedChange?: (expanded: string[]) => void;\n /** Whether the panel is open, controlled: opening and closing then only call `onOpenChange`. */\n open?: boolean;\n /** The panel is open to begin with. */\n defaultOpen?: boolean;\n /** Called with the new state when the panel opens or closes. */\n onOpenChange?: (open: boolean) => void;\n /**\n * Puts a search box at the top of the panel. It matches labels, keeps a\n * match's ancestors and the nodes under it, and opens every branch with a\n * match. A printable key on the field opens the panel with it in the box,\n * and the search empties when the panel closes.\n */\n searchable?: boolean;\n /** The search text when you hold the state. */\n query?: string;\n /** The search text to begin with. */\n defaultQuery?: string;\n /** Called with the search text as it changes. */\n onQueryChange?: (query: string) => void;\n /**\n * Whether a node matches the search, in place of the locale-aware match on\n * its label. It's handed the search text trimmed, the node, and its path\n * from the top node down, the node last, as Cascader's `filter` is. A\n * match's ancestors stay either way. The search runs again when the search\n * text or `data` changes, not when only this does, so one written inline\n * costs nothing.\n */\n filter?: (query: string, item: TreeItemData, path: TreeItemData[]) => boolean;\n /** What the panel shows when the search matches nothing, such as \"No folders match\". Announced once. */\n empty?: React.ReactNode;\n /** Renders a row's content in place of its label. The field and the tags still show the label. */\n itemTemplate?: (item: TreeItemData) => React.ReactNode;\n /** What the field shows while nothing is picked. */\n placeholder?: string;\n /** A form field name. A hidden input posts the value, or one per value in the checkbox modes. */\n name?: string;\n /** The id of the form the hidden inputs belong to, when it isn't an ancestor. */\n form?: string;\n /**\n * A value is needed: the field is `aria-required`, and the form won't\n * submit while nothing is picked. The browser's message points at the\n * field, which shows focus, and the next key goes to it.\n */\n required?: boolean;\n /** The field can't be used: it can't be focused or opened, its tags lose their buttons, and its hidden inputs don't post. */\n disabled?: boolean;\n /**\n * Shows a clear button at the field's inline end while something it can\n * take off is picked. It leaves checked disabled items, which no control\n * unchecks.\n */\n clearable?: boolean;\n /** Bulma color modifier for the field. */\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n /** The field's size, which the tree in the panel follows. */\n size?: 'small' | 'medium' | 'large';\n /** Rounds the field's corners (Bulma's `is-rounded`). */\n isRounded?: boolean;\n /**\n * Where the panel opens, relative to the field, as on the pickers.\n * @defaultValue 'bottom-left'\n */\n position?: PickerPosition;\n /** Renders the panel through a portal, into a `ConfigProvider`'s `portalContainer`, `document.body` by default. */\n appendToBody?: boolean;\n /** The visible and accessible strings, in your app's language. */\n labels?: TreeSelectLabels;\n}\n\n/** Props for a TreeSelect that picks one item. */\nexport interface TreeSelectSingleProps extends TreeSelectBaseProps {\n /**\n * `'single'`: one item, picked with Enter, Space or a click on its row.\n * `'checkbox'`: any number, with a box on every item. A check reaches every\n * enabled item under it, and a parent with some of them checked is mixed.\n * `'checkbox-independent'`: any number, each box checking its own item, so\n * a parent means the parent alone (Mantine's and Ant Design's\n * `checkStrictly`). The checkbox modes show the value as tags.\n */\n selectionMode?: 'single';\n /**\n * The value when you hold the state: in `'single'` the picked item's\n * value, or `null` for none, and in the checkbox modes the checked values.\n * In `'checkbox'` a parent's value checks its whole branch, so the list any\n * `checkedStrategy` gives checks the same boxes.\n */\n value?: string | null;\n /** The value to begin with, when TreeSelect holds its own state: a value or `null`, or a list in the checkbox modes. */\n defaultValue?: string | null;\n /**\n * Called with the new value: in `'single'` the picked item's value, or\n * `null` when it's cleared, and in the checkbox modes the new list, after a\n * check, an uncheck, a tag's button or the clear button, each shaped by\n * `checkedStrategy`. A reset of the field's form calls it too, with\n * `defaultValue` as it was given, or with `null` or `[]` when you hold\n * the value, for you to set.\n */\n onChange?: (value: string | null) => void;\n /**\n * A parent's row picks the parent. `false` makes the row, Enter and Space\n * open it instead, so only leaves are picked.\n * @defaultValue true\n */\n parentsSelectable?: boolean;\n /**\n * Closes the panel after a pick and puts focus back on the field.\n * @defaultValue true\n */\n closeOnSelect?: boolean;\n /** @internal Read with the checkbox modes only. */\n checkedStrategy?: never;\n /** @internal Read with the checkbox modes only. */\n ariaCloseLabel?: never;\n}\n\n/** Props for a TreeSelect with a box on every item. */\nexport interface TreeSelectCheckboxProps extends TreeSelectBaseProps {\n /**\n * `'checkbox'`: a check reaches every enabled item under it, and a parent\n * with some of them checked is mixed. `'checkbox-independent'`: each box\n * checks its own item, so a parent means the parent alone.\n */\n selectionMode: 'checkbox' | 'checkbox-independent';\n /**\n * The checked values when you hold the state. In `'checkbox'`, a parent's\n * value checks its whole branch, so the list any `checkedStrategy` gives\n * checks the same boxes.\n */\n value?: string[];\n /** The values checked to begin with, when TreeSelect holds its own state. */\n defaultValue?: string[];\n /**\n * Called with the new list after a check, an uncheck, a tag's button or\n * the clear button, shaped by `checkedStrategy`, and after a reset of the\n * field's form, with `defaultValue` as given, or `[]` when you hold the\n * value.\n */\n onChange?: (value: string[]) => void;\n /**\n * What a checked branch puts in the value, in `'checkbox'`: `'child'` its\n * leaves, `'parent'` its topmost checked item, `'all'` every checked item,\n * parents included. A checked disabled item is in all three, since only\n * its own value checks it. It shapes the value a check, an uncheck, a\n * tag's button or the clear button makes, which `onChange` reports, and\n * not `defaultValue`, a value you pass or the one a form reset puts back.\n * The tags and the hidden\n * inputs show the value as it's held, so a value you pass shows as you\n * pass it, and a branch that loads later changes nothing the form posts.\n * `'checkbox-independent'` always reports exactly the checked items.\n * @defaultValue 'child'\n */\n checkedStrategy?: TreeSelectCheckedStrategy;\n /** Names each tag's button, in place of \"Remove\" and the item's label, as on Combobox. */\n ariaCloseLabel?: string;\n /** @internal Read with `'single'` only. */\n parentsSelectable?: never;\n /** @internal Read with `'single'` only: the panel stays open while boxes are checked. */\n closeOnSelect?: never;\n}\n\n/**\n * Props for the TreeSelect component. The helper props and the other\n * attributes land on the `div.tree-select` around the field, and the `ref`\n * on the field. It renders the `Field` and the label itself, unless an outer\n * `Field` already does.\n *\n * @extraProp {React.Ref<HTMLInputElement>} [ref] - Ref forwarded to the field, the read-only `<input role=\"combobox\">`.\n */\nexport type TreeSelectProps = TreeSelectSingleProps | TreeSelectCheckboxProps;\n\nconst NOTHING_HIDDEN: ReadonlySet<string> = new Set();\nconst noMatches = [{ id: 'empty' }];\nconst nothingToSay: Array<{ id: string }> = [];\n/** What takes focus as the panel opens. */\nconst INITIAL_FOCUS = '[data-initial-focus]';\n\n/** One value or several, as a list. */\nconst asList = (value: string | string[] | null | undefined): string[] =>\n value == null ? [] : Array.isArray(value) ? value : [value];\n\n/** Whether two lists hold the same values, whatever their order and repeats. */\nconst sameValues = (a: string[], b: string[]): boolean => {\n const inA = new Set(a);\n const inB = new Set(b);\n return inA.size === inB.size && [...inA].every(value => inB.has(value));\n};\n\n/** The list with every value of `more` it doesn't have added at the end. */\nconst withAll = (list: string[], more: string[]): string[] => [\n ...list,\n ...more.filter(value => !list.includes(value)),\n];\n\n/**\n * The `TreeSelect` component is a form field whose options are a tree, such as categories, places or teams: it opens a panel holding a real `TreeView`, picks one item or checks a branch at a time, and searches labels.\n *\n * The field is a read-only `role=\"combobox\"` that opens a dialog, wired as\n * DateInput's is, so focus moves into the tree for real and the tree keeps\n * TreeView's keys, type-ahead and spoken output. The tree is given as `data`,\n * so the field and the tags can name what's picked while the panel is shut,\n * and on the server.\n *\n * `selectionMode` picks the kind. `'single'` picks one item and closes. With\n * `'checkbox'`, a check reaches every enabled item under it, closed or hidden\n * by a search, and `checkedStrategy` decides what the value holds: the\n * leaves, the topmost checked items or every one. With\n * `'checkbox-independent'`, each box is its own item's, so a parent means\n * itself alone (Mantine's and Ant Design's `checkStrictly`). The value shows\n * as tags under the field.\n *\n * A value no node has is kept and shown as it is, with a warning in\n * development, so a value under a branch that hasn't loaded survives until\n * it does.\n *\n * @function\n * @param {TreeSelectProps} props - Props for the TreeSelect component.\n * @param {React.Ref<HTMLInputElement>} ref - Forwarded ref to the field.\n * @returns {JSX.Element} The field, its tags and its panel, in a `Field` with the label unless one is around it.\n * @see {@link https://www.w3.org/WAI/ARIA/apg/patterns/combobox/ | APG Combobox Pattern}\n *\n * @example\n * <TreeSelect\n * label=\"Save to\"\n * data={[\n * { value: 'docs', label: 'Documents', children: [{ value: 'invoices', label: 'Invoices' }] },\n * { value: 'photos', label: 'Photos' },\n * ]}\n * searchable\n * />\n *\n * @example\n * <TreeSelect label=\"Regions\" selectionMode=\"checkbox\" checkedStrategy=\"parent\" data={regions} />\n */\nexport const TreeSelect = forwardRef<HTMLInputElement, TreeSelectProps>(\n (props, ref) => {\n const {\n data,\n expanded: expandedProp,\n defaultExpanded = [],\n onExpandedChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n searchable = false,\n query: queryProp,\n defaultQuery = '',\n onQueryChange,\n filter,\n empty,\n itemTemplate,\n disabled: disabledProp,\n clearable = false,\n size,\n labels,\n selectionMode = 'single',\n value: valueProp,\n defaultValue,\n // Called below through `props`, which knows which value it takes.\n onChange: _onChange,\n parentsSelectable = true,\n closeOnSelect = true,\n checkedStrategy = 'child',\n ariaCloseLabel,\n ...rest\n } = props;\n const disabled = useFieldsetDisabled(disabledProp);\n const single = selectionMode === 'single';\n const cascade = selectionMode === 'checkbox';\n\n const index = useMemo(() => indexTree(data), [data]);\n const records = useMemo(() => checkRecords(index), [index]);\n const labelFor = (value: string) => {\n const entry = index.get(value);\n return entry ? nodeLabel(entry.node) : value;\n };\n\n // The value, as a list: none or one in `single`.\n const [held, setHeld] = useControlled<string | string[] | null>(\n valueProp,\n defaultValue ?? null\n );\n const listed = asList(held);\n const change = (next: string[]) => {\n const value = single ? (next[0] ?? null) : next;\n setHeld(value);\n // The mode decides which of the two `onChange` takes.\n const report = props.onChange as\n ((value: string | string[] | null) => void) | undefined;\n report?.(value);\n };\n // What the field, the tags and the hidden inputs show: the value as it's\n // held. `checkedStrategy` shapes a new value as a check makes it, so a\n // branch that loads later changes nothing the form posts.\n const picked = single ? listed.slice(0, 1) : Array.from(new Set(listed));\n\n const unknown = picked.find(value => !index.has(value));\n useEffect(() => {\n if (unknown !== undefined) {\n warnOnce(\n 'TreeSelect:unknown-value',\n `[bestax-bulma] <TreeSelect> was given the value \"${unknown}\", ` +\n 'which no node in its data has. It is kept and shown as it is, ' +\n 'as it should be for a node under a branch that has not loaded ' +\n 'yet. Otherwise, check the value against the data.'\n );\n }\n }, [unknown]);\n\n const [expanded, setExpanded] = useControlled(\n expandedProp,\n defaultExpanded\n );\n const changeExpanded = (next: string[]) => {\n setExpanded(next);\n onExpandedChange?.(next);\n };\n\n // The picked item focus goes to: the first in the tree's order.\n const pickedSet = new Set(picked);\n const target = Array.from(index.keys()).find(value => pickedSet.has(value));\n\n const [query, setQueryState] = useControlled(queryProp, defaultQuery);\n const setQuery = (next: string) => {\n if (next === query) return;\n setQueryState(next);\n onQueryChange?.(next);\n };\n\n const [isOpen, setOpenState] = useControlled(openProp, defaultOpen);\n const setOpen = (next: boolean) => {\n if (next === isOpen) return;\n if (next) {\n // Opening shows the picked item, its branch open.\n const path = (target !== undefined && index.get(target)?.path) || [];\n if (path.some(value => !expanded.includes(value))) {\n changeExpanded(withAll(expanded, path));\n }\n } else {\n setQuery('');\n }\n setOpenState(next);\n onOpenChange?.(next);\n };\n\n const matchText = useLocaleMatcher();\n const typed = searchable ? query.trim() : '';\n // Every item reads the hidden set, so it's only built again when the\n // search text or the data changes.\n const search = useMemo(\n () =>\n typed\n ? searchTree(data, (node, path) => {\n if (filter) return filter(typed, node, path);\n const text = nodeLabel(node);\n return matchText\n ? matchText(text, typed)\n : text.toLowerCase().includes(typed.toLowerCase());\n })\n : null,\n // `filter` is read as it is when the text or the data changes: one\n // written inline is a new function every render, and listing it would\n // search again on each.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [data, typed, matchText]\n );\n // The branches a search opened that were closed during it, for the\n // search text they were closed under: any new text, from the box or\n // from a held `query`, starts over.\n const [searchClosed, setSearchClosed] = useState({\n query: typed,\n values: [] as string[],\n });\n if (searchClosed.query !== typed) {\n setSearchClosed({ query: typed, values: [] });\n }\n const closedHere = searchClosed.query === typed ? searchClosed.values : [];\n // The branches a search opens show open over the ones you opened, and\n // close again once it's cleared.\n const forced = search\n ? search.open.filter(value => !closedHere.includes(value))\n : [];\n const shownExpanded = withAll(expanded, forced);\n // TreeView's click on a row selects it and opens it. A pick by click, as\n // a pick by Enter, only picks, so the open the tree asks for right after\n // one on a parent is dropped, and a lazy parent isn't fetched to be\n // picked.\n const clickPickedRef = useRef(false);\n const onTreeExpandedChange = (next: string[]) => {\n if (clickPickedRef.current) {\n clickPickedRef.current = false;\n return;\n }\n const closed = shownExpanded.filter(value => !next.includes(value));\n const opened = next.filter(value => !shownExpanded.includes(value));\n const closedForced = closed.filter(value => forced.includes(value));\n if (closedForced.length > 0) {\n setSearchClosed({\n query: typed,\n values: [...closedHere, ...closedForced],\n });\n }\n if (opened.length > 0 || closed.some(value => expanded.includes(value))) {\n changeExpanded([\n ...expanded.filter(value => !closed.includes(value)),\n ...opened,\n ]);\n }\n };\n\n // Whether an item is on the page with the panel open: every branch\n // above it open, and the search not hiding it.\n const visible = (value: string) =>\n !search?.hidden.has(value) &&\n index.get(value)!.path.every(above => shownExpanded.includes(above));\n const focusTarget =\n target !== undefined && visible(target)\n ? target\n : data.find(node => visible(node.value))?.value;\n\n // Picking in `single`. TreeView shows `selected`, held here, and the\n // picks are made from the rows' own keys and clicks, so a parent can\n // open instead and a pick of the picked item still closes.\n const pickable = (node: TreeItemData) =>\n !node.disabled && (parentsSelectable || !isParentNode(node));\n const pick = (value: string) => {\n if (listed.length !== 1 || listed[0] !== value) change([value]);\n if (closeOnSelect) setOpen(false);\n };\n const toggleClass = usePrefixedClassNames('tree-view-toggle');\n const rowHandlers = (node: TreeItemData) =>\n single\n ? {\n onKeyDown: (event: React.KeyboardEvent<HTMLLIElement>) => {\n if (\n event.target !== event.currentTarget ||\n event.repeat ||\n event.altKey ||\n event.ctrlKey ||\n event.metaKey ||\n (event.key !== 'Enter' && event.key !== ' ')\n ) {\n return;\n }\n if (pickable(node)) {\n pick(node.value);\n } else if (!node.disabled && isParentNode(node)) {\n // TreeView leaves a key that's been used alone.\n event.preventDefault();\n onTreeExpandedChange(\n shownExpanded.includes(node.value)\n ? shownExpanded.filter(value => value !== node.value)\n : [...shownExpanded, node.value]\n );\n }\n },\n onClick: (event: React.MouseEvent<HTMLLIElement>) => {\n const item = event.currentTarget;\n const target = event.target as Element;\n // A click in this item's own row, where TreeView reads one,\n // not one in the group of items inside it, and not on its\n // chevron, which only opens it.\n const inRow =\n target === item || item.firstElementChild!.contains(target);\n const onChevron =\n isParentNode(node) && !!target.closest(`.${toggleClass}`);\n if (inRow && !onChevron && pickable(node)) {\n // TreeView opens a parent it's handed this click on, in\n // this same click: the flag goes once the click is over.\n clickPickedRef.current = isParentNode(node);\n queueMicrotask(() => {\n clickPickedRef.current = false;\n });\n pick(node.value);\n }\n },\n }\n : undefined;\n\n const renderNode = (node: TreeItemData): React.ReactNode => (\n <TreeView.Item\n key={node.value}\n value={node.value}\n label={itemTemplate ? itemTemplate(node) : nodeLabel(node)}\n icon={node.icon}\n hasChildren={node.hasChildren}\n isLoading={node.isLoading}\n loadError={node.loadError}\n onRetry={node.onRetry}\n disabled={node.disabled}\n data-initial-focus={\n !searchable && node.value === focusTarget ? '' : undefined\n }\n {...rowHandlers(node)}\n >\n {childNodes(node).map(renderNode)}\n </TreeView.Item>\n );\n\n // In `checkbox`, TreeView reports every checked item, read here into\n // the list `checkedStrategy` gives.\n const shaped = (next: string[]) =>\n cascade ? checkedByStrategy(records, next, checkedStrategy) : next;\n const onCheckedChange = (next: string[]) => change(shaped(next));\n // Every item's check state, read once a render, which the clear button\n // and the tags' buttons decide from. Only `checkbox` reads TreeView's\n // rules: in `checkbox-independent` a listed item is a checked one.\n const listedKey = JSON.stringify(listed);\n const checks = useMemo(\n () => (cascade ? readChecks(records, new Set(listed)) : null),\n // `listed` by what it holds, since a held list is a new array each\n // render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [cascade, records, listedKey]\n );\n const isDisabled = (value: string) => !!index.get(value)?.node.disabled;\n // Read in `checkbox` only, where there are states to read.\n const isChecked = (value: string) => checks!.states.get(value) === true;\n // What a control can uncheck. In `checkbox`, that's each checked end no\n // checked disabled item above it keeps checked, and every item above it,\n // which takes it along; elsewhere it's each listed item that isn't\n // disabled. A disabled item is checked only by its own value, so nothing\n // unchecks it, or what it keeps checked.\n const uncheckable = new Set<string>();\n if (checks) {\n for (const end of checks.ends) {\n const { path } = index.get(end)!;\n if (\n isChecked(end) &&\n !path.some(above => isDisabled(above) && isChecked(above))\n ) {\n uncheckable.add(end);\n path.forEach(above => uncheckable.add(above));\n }\n }\n } else if (!single) {\n picked.forEach(value => !isDisabled(value) && uncheckable.add(value));\n }\n // A tag's button takes its value off where that changes what's checked,\n // the way its box would. A value no node has can always go.\n const removable = (value: string) =>\n !index.has(value) || (!isDisabled(value) && uncheckable.has(value));\n const without = (value: string) =>\n cascade && records.has(value)\n ? shaped(toggledList(records, listed, value))\n : listed.filter(item => item !== value);\n // What the clear button leaves: the checked disabled items, and in\n // `checkbox` what they keep checked, shaped as a check's value is.\n const cleared = single\n ? []\n : shaped(\n checks\n ? Array.from(records.keys()).filter(\n value => isDisabled(value) && isChecked(value)\n )\n : picked.filter(isDisabled)\n );\n const canClear = single\n ? picked.length > 0\n : uncheckable.size > 0 || picked.some(value => !index.has(value));\n const inputRef = useRef<HTMLInputElement | null>(null);\n // A forwarded callback's cleanup, which React 19 lets it return, run on\n // detach in place of a call with `null`, on React 18 as well.\n const refCleanupRef = useRef<(() => void) | null>(null);\n const fieldRef = useCallback(\n (node: HTMLInputElement | null) => {\n inputRef.current = node;\n if (typeof ref === 'function') {\n if (node === null) {\n const cleanup = refCleanupRef.current;\n refCleanupRef.current = null;\n if (cleanup) cleanup();\n else ref(null);\n return;\n }\n const cleanup: unknown = ref(node);\n refCleanupRef.current =\n typeof cleanup === 'function' ? (cleanup as () => void) : null;\n } else if (ref) {\n ref.current = node;\n }\n },\n [ref]\n );\n const tagsId = useId();\n const ids = tagIds(tagsId, picked);\n\n const searchClass = usePrefixedClassNames('input', 'tree-select-search', {\n [`is-${size}`]: !!size,\n });\n const emptyClass = usePrefixedClassNames('tree-select-empty');\n const treeRef = useRef<HTMLUListElement>(null);\n const emptyRef = useRef<HTMLDivElement>(null);\n const nothingFound = !!search && !search.found;\n // The region reads the text on screen, whatever `empty` renders.\n const describeEmpty = useCallback(() => {\n const node = emptyRef.current;\n /* istanbul ignore if: it's only announced while it's on screen */\n if (!node) return null;\n return spokenText(node);\n }, []);\n\n return (\n <DialogSelect\n {...rest}\n component=\"TreeSelect\"\n rootClass=\"tree-select\"\n panelClass=\"tree-select-panel\"\n text={single && picked.length > 0 ? labelFor(picked[0]) : ''}\n values={single ? [picked[0] ?? ''] : picked}\n hasValue={picked.length > 0}\n open={isOpen}\n onOpenChange={setOpen}\n onPrintableKey={\n searchable\n ? key => {\n setQuery(key);\n setOpen(true);\n return true;\n }\n : undefined\n }\n onClear={() => change(cleared)}\n clearable={clearable && canClear}\n onFormReset={() => {\n // Back to `defaultValue`, or to nothing for a held value, which\n // its owner sets from `onChange`.\n const back = valueProp === undefined ? asList(defaultValue) : [];\n if (!sameValues(back, listed)) change(back);\n }}\n clearLabel={labels?.clear || 'Clear'}\n chooseLabel={labels?.choose}\n disabled={disabled}\n size={size}\n inputRef={inputRef}\n fieldRef={fieldRef}\n describedBy={single ? undefined : ids.join(' ')}\n below={\n single ? undefined : (\n <ValueTags\n values={picked}\n ids={ids}\n labelFor={labelFor}\n disabled={disabled}\n ariaCloseLabel={ariaCloseLabel}\n removable={removable}\n onRemove={value => {\n change(without(value));\n inputRef.current?.focus();\n }}\n />\n )\n }\n initialFocusSelector={INITIAL_FOCUS}\n >\n {panelName => (\n <>\n {searchable && (\n <input\n className={searchClass}\n type=\"search\"\n aria-label={labels?.search || 'Search'}\n autoComplete=\"off\"\n value={query}\n data-initial-focus=\"\"\n onChange={event => setQuery(event.target.value)}\n onFocus={event => {\n // A key that opened the panel is in the box: type after it.\n const { length } = event.currentTarget.value;\n event.currentTarget.setSelectionRange(length, length);\n }}\n onKeyDown={event => {\n if (event.key === 'Enter') {\n // The search box's Enter would submit the form.\n event.preventDefault();\n } else if (event.key === 'ArrowDown' && !event.altKey) {\n event.preventDefault();\n const tree = treeRef.current;\n (\n tree?.querySelector<HTMLElement>(\n '[role=\"treeitem\"][tabindex=\"0\"]'\n ) ?? tree?.querySelector<HTMLElement>('[role=\"treeitem\"]')\n )?.focus();\n }\n }}\n />\n )}\n {nothingFound ? (\n empty && (\n <div className={emptyClass} ref={emptyRef}>\n {empty}\n </div>\n )\n ) : (\n <TreeViewHiddenContext.Provider\n value={search?.hidden ?? NOTHING_HIDDEN}\n >\n <TreeView\n {...panelName}\n ref={treeRef}\n // It scrolls, and Firefox makes a scroller a Tab stop of\n // its own: the roving one on its items is the tree's.\n tabIndex={-1}\n selectionMode={selectionMode}\n size={size}\n selected={single ? (picked[0] ?? null) : undefined}\n checked={single ? undefined : listed}\n onCheckedChange={onCheckedChange}\n expanded={shownExpanded}\n onExpandedChange={onTreeExpandedChange}\n loadingLabel={labels?.loading}\n retryLabel={labels?.retry}\n loadingAnnouncement={labels?.loadingAnnouncement}\n loadedAnnouncement={labels?.loadedAnnouncement}\n >\n {data.map(renderNode)}\n </TreeView>\n </TreeViewHiddenContext.Provider>\n )}\n <StatusRegion\n items={nothingFound && empty ? noMatches : nothingToSay}\n describe={describeEmpty}\n />\n </>\n )}\n </DialogSelect>\n );\n }\n);\n\nTreeSelect.displayName = 'TreeSelect';\n\nexport default TreeSelect;\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ import { useLatestRef } from '../../helpers/useLatestRef.js';
|
|
|
12
12
|
import { useAnchoredPosition, marginGap } from '../../helpers/useAnchoredPosition.js';
|
|
13
13
|
|
|
14
14
|
const isBrowser = typeof window !== 'undefined';
|
|
15
|
-
const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'bottom-left', appendToBody = false, className, trapFocus = true, closeOnClickOutside = true, closeOnEscape = true, ariaLabel, ariaLabelledBy, role = 'dialog', id, restoreFocusRef, initialFocusSelector, localeScope, }) => {
|
|
15
|
+
const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'bottom-left', appendToBody = false, className, trapFocus = true, closeOnClickOutside = true, closeOnEscape = true, ariaLabel, ariaLabelledBy, role = 'dialog', id, restoreFocusRef, initialFocusSelector, localeScope, matchWidth = false, followContent = false, }) => {
|
|
16
16
|
const panelRef = useRef(null);
|
|
17
17
|
const resolveContainer = usePortalContainer();
|
|
18
18
|
// Read as the trap turns on, once the panel's content has rendered.
|
|
@@ -32,6 +32,8 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
32
32
|
// the panel.
|
|
33
33
|
offset: 0,
|
|
34
34
|
styledGap: marginGap,
|
|
35
|
+
matchWidth,
|
|
36
|
+
followContent,
|
|
35
37
|
});
|
|
36
38
|
// Read when a press lands outside or a key arrives, so both listeners stay
|
|
37
39
|
// put while `onClose` changes identity, as an inline one does on every
|
|
@@ -90,6 +92,7 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
90
92
|
'is-active': isOpen,
|
|
91
93
|
[`is-${resolved.position}`]: true,
|
|
92
94
|
'is-portal': appendToBody,
|
|
95
|
+
'has-anchor-width': matchWidth,
|
|
93
96
|
});
|
|
94
97
|
if (!isOpen)
|
|
95
98
|
return null;
|
|
@@ -98,6 +101,7 @@ const PickerPopover = ({ isOpen, onClose, anchorRef, children, position = 'botto
|
|
|
98
101
|
position: 'fixed',
|
|
99
102
|
top: resolved.top,
|
|
100
103
|
left: resolved.left,
|
|
104
|
+
minWidth: resolved.minWidth,
|
|
101
105
|
}
|
|
102
106
|
: {};
|
|
103
107
|
const panel = (jsx("div", { ref: panelRef, id: id, role: role, "aria-modal": role === 'dialog' ? 'false' : undefined, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, tabIndex: -1, className: classNames(panelClass, className), style: style,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PickerPopover.js","sources":["../../../../src/form/_pickerInternals/PickerPopover.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../../helpers/classNames';\nimport { useFocusTrap } from '../../helpers/useFocusTrap';\nimport { usePortalContainer } from '../../helpers/portal';\nimport {\n LocalePortalScope,\n type PortalLocale,\n} from '../../helpers/LocaleProvider';\nimport { isEventInside } from '../../helpers/shadowDom';\nimport { useLatestRef } from '../../helpers/useLatestRef';\nimport {\n marginGap,\n useAnchoredPosition,\n} from '../../helpers/useAnchoredPosition';\nimport { PickerPosition } from './pickerTypes';\n\nexport interface PickerPopoverProps {\n isOpen: boolean;\n onClose: () => void;\n anchorRef: React.RefObject<HTMLElement | null>;\n children: React.ReactNode;\n position?: PickerPosition;\n appendToBody?: boolean;\n className?: string;\n trapFocus?: boolean;\n closeOnClickOutside?: boolean;\n /** Close on Escape and on Alt+ArrowUp. Default `true`. */\n closeOnEscape?: boolean;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n role?: 'dialog' | 'group';\n id?: string;\n /**\n * Where focus goes when the popover closes. Without it, focus goes back to\n * whatever had it when the focus trap turned on, which is read after the\n * panel's content mounts: a calendar that focuses a cell as it mounts\n * would be recorded instead, and that cell leaves with the panel.\n */\n restoreFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * Selects the element in the panel that takes focus as it opens, such as a\n * calendar's focused date rather than the first button in its header. When\n * nothing matches, or the match can't take focus, the first tab stop does.\n */\n initialFocusSelector?: string;\n /**\n * The `dir` and `lang` the panel carries when it portals, in place of a\n * `LocaleProvider`'s: the picker's own, from `usePickerLocaleScope`.\n */\n localeScope?: PortalLocale;\n}\n\nconst isBrowser = typeof window !== 'undefined';\n\nexport const PickerPopover: React.FC<PickerPopoverProps> = ({\n isOpen,\n onClose,\n anchorRef,\n children,\n position = 'bottom-left',\n appendToBody = false,\n className,\n trapFocus = true,\n closeOnClickOutside = true,\n closeOnEscape = true,\n ariaLabel,\n ariaLabelledBy,\n role = 'dialog',\n id,\n restoreFocusRef,\n initialFocusSelector,\n localeScope,\n}) => {\n const panelRef = useRef<HTMLDivElement>(null);\n const resolveContainer = usePortalContainer();\n // Read as the trap turns on, once the panel's content has rendered.\n const initialFocusRef = useMemo(\n () =>\n initialFocusSelector === undefined\n ? undefined\n : {\n get current() {\n return (\n panelRef.current?.querySelector<HTMLElement>(\n initialFocusSelector\n ) ?? null\n );\n },\n },\n [initialFocusSelector]\n );\n const resolved = useAnchoredPosition(anchorRef, panelRef, {\n active: isOpen,\n position,\n fixed: appendToBody,\n // The gap comes from `--bulma-picker-popover-offset` as a margin, in place\n // or portaled, so the coordinates leave it out and `auto` reads it off\n // the panel.\n offset: 0,\n styledGap: marginGap,\n });\n\n // Read when a press lands outside or a key arrives, so both listeners stay\n // put while `onClose` changes identity, as an inline one does on every\n // render of the field.\n const onCloseRef = useLatestRef(onClose);\n\n useEffect(() => {\n if (!isOpen || !closeOnClickOutside || !isBrowser) return undefined;\n // Listen on `pointerdown` (rather than `mousedown`) so touch starts\n // outside the popover dismiss it on mobile — including swipe-from-outside\n // gestures, where the synthetic mouse event would otherwise lag a few\n // hundred ms behind the actual touch.\n const handler = (e: PointerEvent) => {\n const path = e.composedPath();\n if (panelRef.current && path.includes(panelRef.current)) return;\n if (anchorRef.current && path.includes(anchorRef.current)) return;\n onCloseRef.current();\n };\n document.addEventListener('pointerdown', handler);\n return () => document.removeEventListener('pointerdown', handler);\n }, [isOpen, closeOnClickOutside, anchorRef, onCloseRef]);\n\n // Escape closes it, and so does Alt+ArrowUp, the combobox's key for it,\n // which the calendar and the time wheels inside leave to it. Escape closes\n // it from anywhere, but the key is only the picker's when it was pressed in\n // the panel or the field: then it's marked used, so a Modal or Sidebar\n // around the picker stays open. From anywhere else, such as a dialog opened\n // over the picker, the key goes on to whatever has focus.\n useEffect(() => {\n if (!isOpen || !closeOnEscape || !isBrowser) return undefined;\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (\n isEventInside(e, panelRef.current) ||\n isEventInside(e, anchorRef.current)\n ) {\n e.preventDefault();\n }\n onCloseRef.current();\n } else if (e.altKey && e.key === 'ArrowUp') {\n e.preventDefault();\n e.stopPropagation();\n onCloseRef.current();\n }\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [isOpen, closeOnEscape, anchorRef, onCloseRef]);\n\n useFocusTrap(panelRef, {\n active: isOpen && trapFocus,\n initialFocusRef,\n restoreFocus: restoreFocusRef ?? true,\n });\n\n const panelClass = usePrefixedClassNames('picker-popover', {\n 'is-active': isOpen,\n [`is-${resolved.position}`]: true,\n 'is-portal': appendToBody,\n });\n\n if (!isOpen) return null;\n\n const style: React.CSSProperties = appendToBody\n ? {\n position: 'fixed',\n top: resolved.top,\n left: resolved.left,\n }\n : {};\n\n const panel = (\n <div\n ref={panelRef}\n id={id}\n role={role}\n aria-modal={role === 'dialog' ? 'false' : undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n tabIndex={-1}\n className={classNames(panelClass, className)}\n style={style}\n // An Escape pressed in the panel is the picker's, and the listener\n // above claims it. A Dropdown menu around the picker hears Escape in a\n // React handler, which runs before that listener, so the panel claims\n // it here too, where React gets to it first.\n onKeyDown={e => {\n if (closeOnEscape && e.key === 'Escape') e.preventDefault();\n }}\n >\n {children}\n </div>\n );\n\n if (appendToBody && isBrowser) {\n const target = resolveContainer();\n return createPortal(\n <LocalePortalScope target={target} scope={localeScope}>\n {panel}\n </LocalePortalScope>,\n target\n );\n }\n return panel;\n};\n\nPickerPopover.displayName = 'PickerPopover';\n\nexport default PickerPopover;\n"],"names":["_jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PickerPopover.js","sources":["../../../../src/form/_pickerInternals/PickerPopover.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { classNames, usePrefixedClassNames } from '../../helpers/classNames';\nimport { useFocusTrap } from '../../helpers/useFocusTrap';\nimport { usePortalContainer } from '../../helpers/portal';\nimport {\n LocalePortalScope,\n type PortalLocale,\n} from '../../helpers/LocaleProvider';\nimport { isEventInside } from '../../helpers/shadowDom';\nimport { useLatestRef } from '../../helpers/useLatestRef';\nimport {\n marginGap,\n useAnchoredPosition,\n} from '../../helpers/useAnchoredPosition';\nimport { PickerPosition } from './pickerTypes';\n\nexport interface PickerPopoverProps {\n isOpen: boolean;\n onClose: () => void;\n anchorRef: React.RefObject<HTMLElement | null>;\n children: React.ReactNode;\n position?: PickerPosition;\n appendToBody?: boolean;\n className?: string;\n trapFocus?: boolean;\n closeOnClickOutside?: boolean;\n /** Close on Escape and on Alt+ArrowUp. Default `true`. */\n closeOnEscape?: boolean;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n role?: 'dialog' | 'group';\n id?: string;\n /**\n * Where focus goes when the popover closes. Without it, focus goes back to\n * whatever had it when the focus trap turned on, which is read after the\n * panel's content mounts: a calendar that focuses a cell as it mounts\n * would be recorded instead, and that cell leaves with the panel.\n */\n restoreFocusRef?: React.RefObject<HTMLElement | null>;\n /**\n * Selects the element in the panel that takes focus as it opens, such as a\n * calendar's focused date rather than the first button in its header. When\n * nothing matches, or the match can't take focus, the first tab stop does.\n */\n initialFocusSelector?: string;\n /**\n * The `dir` and `lang` the panel carries when it portals, in place of a\n * `LocaleProvider`'s: the picker's own, from `usePickerLocaleScope`.\n */\n localeScope?: PortalLocale;\n /**\n * At least as wide as the anchor: in place through its partial's\n * `has-anchor-width` rule, which needs the anchor to be the element it's\n * placed against, and portaled through the anchor's width set inline.\n */\n matchWidth?: boolean;\n /** Placed again after each render, for content that changes size while it's open. */\n followContent?: boolean;\n}\n\nconst isBrowser = typeof window !== 'undefined';\n\nexport const PickerPopover: React.FC<PickerPopoverProps> = ({\n isOpen,\n onClose,\n anchorRef,\n children,\n position = 'bottom-left',\n appendToBody = false,\n className,\n trapFocus = true,\n closeOnClickOutside = true,\n closeOnEscape = true,\n ariaLabel,\n ariaLabelledBy,\n role = 'dialog',\n id,\n restoreFocusRef,\n initialFocusSelector,\n localeScope,\n matchWidth = false,\n followContent = false,\n}) => {\n const panelRef = useRef<HTMLDivElement>(null);\n const resolveContainer = usePortalContainer();\n // Read as the trap turns on, once the panel's content has rendered.\n const initialFocusRef = useMemo(\n () =>\n initialFocusSelector === undefined\n ? undefined\n : {\n get current() {\n return (\n panelRef.current?.querySelector<HTMLElement>(\n initialFocusSelector\n ) ?? null\n );\n },\n },\n [initialFocusSelector]\n );\n const resolved = useAnchoredPosition(anchorRef, panelRef, {\n active: isOpen,\n position,\n fixed: appendToBody,\n // The gap comes from `--bulma-picker-popover-offset` as a margin, in place\n // or portaled, so the coordinates leave it out and `auto` reads it off\n // the panel.\n offset: 0,\n styledGap: marginGap,\n matchWidth,\n followContent,\n });\n\n // Read when a press lands outside or a key arrives, so both listeners stay\n // put while `onClose` changes identity, as an inline one does on every\n // render of the field.\n const onCloseRef = useLatestRef(onClose);\n\n useEffect(() => {\n if (!isOpen || !closeOnClickOutside || !isBrowser) return undefined;\n // Listen on `pointerdown` (rather than `mousedown`) so touch starts\n // outside the popover dismiss it on mobile — including swipe-from-outside\n // gestures, where the synthetic mouse event would otherwise lag a few\n // hundred ms behind the actual touch.\n const handler = (e: PointerEvent) => {\n const path = e.composedPath();\n if (panelRef.current && path.includes(panelRef.current)) return;\n if (anchorRef.current && path.includes(anchorRef.current)) return;\n onCloseRef.current();\n };\n document.addEventListener('pointerdown', handler);\n return () => document.removeEventListener('pointerdown', handler);\n }, [isOpen, closeOnClickOutside, anchorRef, onCloseRef]);\n\n // Escape closes it, and so does Alt+ArrowUp, the combobox's key for it,\n // which the calendar and the time wheels inside leave to it. Escape closes\n // it from anywhere, but the key is only the picker's when it was pressed in\n // the panel or the field: then it's marked used, so a Modal or Sidebar\n // around the picker stays open. From anywhere else, such as a dialog opened\n // over the picker, the key goes on to whatever has focus.\n useEffect(() => {\n if (!isOpen || !closeOnEscape || !isBrowser) return undefined;\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (\n isEventInside(e, panelRef.current) ||\n isEventInside(e, anchorRef.current)\n ) {\n e.preventDefault();\n }\n onCloseRef.current();\n } else if (e.altKey && e.key === 'ArrowUp') {\n e.preventDefault();\n e.stopPropagation();\n onCloseRef.current();\n }\n };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [isOpen, closeOnEscape, anchorRef, onCloseRef]);\n\n useFocusTrap(panelRef, {\n active: isOpen && trapFocus,\n initialFocusRef,\n restoreFocus: restoreFocusRef ?? true,\n });\n\n const panelClass = usePrefixedClassNames('picker-popover', {\n 'is-active': isOpen,\n [`is-${resolved.position}`]: true,\n 'is-portal': appendToBody,\n 'has-anchor-width': matchWidth,\n });\n\n if (!isOpen) return null;\n\n const style: React.CSSProperties = appendToBody\n ? {\n position: 'fixed',\n top: resolved.top,\n left: resolved.left,\n minWidth: resolved.minWidth,\n }\n : {};\n\n const panel = (\n <div\n ref={panelRef}\n id={id}\n role={role}\n aria-modal={role === 'dialog' ? 'false' : undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n tabIndex={-1}\n className={classNames(panelClass, className)}\n style={style}\n // An Escape pressed in the panel is the picker's, and the listener\n // above claims it. A Dropdown menu around the picker hears Escape in a\n // React handler, which runs before that listener, so the panel claims\n // it here too, where React gets to it first.\n onKeyDown={e => {\n if (closeOnEscape && e.key === 'Escape') e.preventDefault();\n }}\n >\n {children}\n </div>\n );\n\n if (appendToBody && isBrowser) {\n const target = resolveContainer();\n return createPortal(\n <LocalePortalScope target={target} scope={localeScope}>\n {panel}\n </LocalePortalScope>,\n target\n );\n }\n return panel;\n};\n\nPickerPopover.displayName = 'PickerPopover';\n\nexport default PickerPopover;\n"],"names":["_jsx"],"mappings":"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"}
|