@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,208 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
'use client';
|
|
3
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
4
|
+
import { useId, useRef, useState, useEffect } from 'react';
|
|
5
|
+
import { usePrefixedClassNames, classNames } from '../../helpers/classNames.js';
|
|
6
|
+
import { useBulmaClasses } from '../../helpers/useBulmaClasses.js';
|
|
7
|
+
import { useLatestRef } from '../../helpers/useLatestRef.js';
|
|
8
|
+
import { Delete } from '../../elements/Delete.js';
|
|
9
|
+
import { Icon } from '../../elements/Icon.js';
|
|
10
|
+
import { Control } from '../Control.js';
|
|
11
|
+
import { Field } from '../Field.js';
|
|
12
|
+
import { useInsideField, useInsideControl, rendersOwnField } from '../FormContext.js';
|
|
13
|
+
import { useAutoLabelId } from '../useAutoLabelId.js';
|
|
14
|
+
import { PickerPopover } from '../_pickerInternals/PickerPopover.js';
|
|
15
|
+
import { isPopoverToggleKey } from '../_pickerInternals/pickerTypes.js';
|
|
16
|
+
import { usePickerLocaleScope } from '../_pickerInternals/useLocaleDefaults.js';
|
|
17
|
+
import { HiddenValues } from './ValueTags.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A field laid over the real one, unseen and letting presses through, so the
|
|
21
|
+
* browser's `required` check reads it and its message points at the field.
|
|
22
|
+
*/
|
|
23
|
+
const requiredProxyStyle = {
|
|
24
|
+
position: 'absolute',
|
|
25
|
+
inset: 0,
|
|
26
|
+
width: '100%',
|
|
27
|
+
height: '100%',
|
|
28
|
+
margin: 0,
|
|
29
|
+
padding: 0,
|
|
30
|
+
border: 0,
|
|
31
|
+
opacity: 0,
|
|
32
|
+
pointerEvents: 'none',
|
|
33
|
+
// Focused, it opens no on-screen keyboard (`inputMode`), and iOS zooms
|
|
34
|
+
// in on a field set under 16px. It's unseen, so the size shows nowhere.
|
|
35
|
+
fontSize: '16px',
|
|
36
|
+
};
|
|
37
|
+
/** Keys pressed on their own that don't act, such as Ctrl to stop speech. */
|
|
38
|
+
const MODIFIER_KEYS = new Set([
|
|
39
|
+
'Alt',
|
|
40
|
+
'AltGraph',
|
|
41
|
+
'CapsLock',
|
|
42
|
+
'Control',
|
|
43
|
+
'Fn',
|
|
44
|
+
'Meta',
|
|
45
|
+
'NumLock',
|
|
46
|
+
'ScrollLock',
|
|
47
|
+
'Shift',
|
|
48
|
+
]);
|
|
49
|
+
const ignoreChange = () => { };
|
|
50
|
+
/**
|
|
51
|
+
* A read-only `role="combobox"` field, wired as the date pickers' are, that
|
|
52
|
+
* opens a `PickerPopover` dialog panel: in a `Field` of its own unless one is
|
|
53
|
+
* around it, with a chevron at its inline end, a clear button, the hidden
|
|
54
|
+
* inputs for `name` and what the browser's `required` check reads. A click,
|
|
55
|
+
* Enter, Space, Down or Alt+Down opens the panel, which holds focus, as the
|
|
56
|
+
* date pickers' does: Tab and Shift+Tab wrap inside it. Escape and Alt+Up
|
|
57
|
+
* close it and put focus back on the field, and a press outside closes it
|
|
58
|
+
* where the press put focus.
|
|
59
|
+
*/
|
|
60
|
+
function DialogSelect({ component, rootClass, panelClass, text, values, hasValue, open, onOpenChange, onFormReset, onPrintableKey, onClear, clearable, clearLabel, chooseLabel, placeholder, name, form, required = false, disabled, color, size, isRounded, position = 'bottom-left', appendToBody = false, inputRef, fieldRef, describedBy, below, initialFocusSelector, children, label, labelSize, labelProps, horizontal, message, messageColor, fieldClassName, className, id, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, 'aria-describedby': ariaDescribedBy, ...props }) {
|
|
61
|
+
const insideField = useInsideField();
|
|
62
|
+
const insideControl = useInsideControl();
|
|
63
|
+
const ownField = rendersOwnField(component, {
|
|
64
|
+
insideField,
|
|
65
|
+
insideControl,
|
|
66
|
+
label,
|
|
67
|
+
message,
|
|
68
|
+
horizontal,
|
|
69
|
+
fieldClassName,
|
|
70
|
+
});
|
|
71
|
+
const { controlId, fieldLabelProps, labelId } = useAutoLabelId({
|
|
72
|
+
label,
|
|
73
|
+
id,
|
|
74
|
+
labelProps,
|
|
75
|
+
rendersLabel: ownField,
|
|
76
|
+
needsLabelId: true,
|
|
77
|
+
});
|
|
78
|
+
const reactId = useId();
|
|
79
|
+
const panelId = `${controlId ?? reactId}-panel`;
|
|
80
|
+
const localeScope = usePickerLocaleScope(undefined, props.dir);
|
|
81
|
+
const { bulmaHelperClasses, rest } = useBulmaClasses(props);
|
|
82
|
+
const rootRef = useRef(null);
|
|
83
|
+
// The field's name, which the panel takes too unless it's given its own.
|
|
84
|
+
const fieldName = ariaLabel
|
|
85
|
+
? { 'aria-label': ariaLabel }
|
|
86
|
+
: ariaLabelledBy
|
|
87
|
+
? { 'aria-labelledby': ariaLabelledBy }
|
|
88
|
+
: labelId
|
|
89
|
+
? { 'aria-labelledby': labelId }
|
|
90
|
+
: {};
|
|
91
|
+
const panelName = chooseLabel
|
|
92
|
+
? { 'aria-label': chooseLabel }
|
|
93
|
+
: fieldName;
|
|
94
|
+
// Whether the field `required` hangs on has focus, which the browser gives
|
|
95
|
+
// it when it stops a submit. Taking it away or disabling it while it has
|
|
96
|
+
// focus fires no blur, so that clears it too.
|
|
97
|
+
const [proxyFocused, setProxyFocused] = useState(false);
|
|
98
|
+
if (proxyFocused && (!required || disabled))
|
|
99
|
+
setProxyFocused(false);
|
|
100
|
+
const showClear = !!clearable && hasValue && !disabled;
|
|
101
|
+
const rootClasses = usePrefixedClassNames(rootClass, {
|
|
102
|
+
'is-disabled': disabled,
|
|
103
|
+
'has-clear': showClear,
|
|
104
|
+
});
|
|
105
|
+
const inputClass = usePrefixedClassNames('input', {
|
|
106
|
+
[`is-${color}`]: !!color,
|
|
107
|
+
[`is-${size}`]: !!size,
|
|
108
|
+
'is-rounded': !!isRounded,
|
|
109
|
+
'is-focused': proxyFocused,
|
|
110
|
+
});
|
|
111
|
+
const chevronClass = usePrefixedClassNames(`${rootClass}-chevron`);
|
|
112
|
+
const clearClass = usePrefixedClassNames(`${rootClass}-clear`);
|
|
113
|
+
const panelClasses = usePrefixedClassNames(panelClass, {
|
|
114
|
+
[`is-${size}`]: !!size,
|
|
115
|
+
});
|
|
116
|
+
const helpClass = usePrefixedClassNames('help', {
|
|
117
|
+
[`is-${messageColor}`]: !!messageColor,
|
|
118
|
+
});
|
|
119
|
+
const isOpen = open && !disabled;
|
|
120
|
+
// A reset of the field's form puts the value back, as it does a native
|
|
121
|
+
// field's. The reset event fires before the form resets, and a listener
|
|
122
|
+
// after this one can still cancel it, so the value goes back a task later,
|
|
123
|
+
// and only if the reset went ahead, as File's and Dropzone's do. It's
|
|
124
|
+
// heard on the field's document, or the shadow root it's in, and matched
|
|
125
|
+
// to the field's form as it is then, so a form its `form` names that
|
|
126
|
+
// mounts later, or a `form` that changes, is followed, and a reset
|
|
127
|
+
// already under way still lands.
|
|
128
|
+
const onFormResetRef = useLatestRef(onFormReset);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
const root = inputRef.current.getRootNode();
|
|
131
|
+
let timer;
|
|
132
|
+
const onReset = (event) => {
|
|
133
|
+
if (event.target !== inputRef.current?.form)
|
|
134
|
+
return;
|
|
135
|
+
timer = setTimeout(() => {
|
|
136
|
+
if (!event.defaultPrevented)
|
|
137
|
+
onFormResetRef.current();
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
root.addEventListener('reset', onReset, true);
|
|
141
|
+
return () => {
|
|
142
|
+
root.removeEventListener('reset', onReset, true);
|
|
143
|
+
clearTimeout(timer);
|
|
144
|
+
};
|
|
145
|
+
}, [inputRef, onFormResetRef]);
|
|
146
|
+
const handleKeyDown = (event) => {
|
|
147
|
+
if (disabled)
|
|
148
|
+
return;
|
|
149
|
+
if (isPopoverToggleKey(event)) {
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
if (event.key === 'ArrowDown')
|
|
152
|
+
onOpenChange(true);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (event.altKey || event.ctrlKey || event.metaKey)
|
|
156
|
+
return;
|
|
157
|
+
if (event.key === 'Enter' ||
|
|
158
|
+
event.key === ' ' ||
|
|
159
|
+
event.key === 'ArrowDown') {
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
onOpenChange(true);
|
|
162
|
+
}
|
|
163
|
+
else if (event.key.length === 1 &&
|
|
164
|
+
!event.nativeEvent.isComposing &&
|
|
165
|
+
onPrintableKey?.(event.key)) {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const fieldDescription = [ariaDescribedBy, describedBy].filter(Boolean).join(' ') || undefined;
|
|
170
|
+
const field = (jsxs(Fragment, { children: [jsx("input", { ref: fieldRef, type: "text", className: inputClass, id: controlId, readOnly: true, role: "combobox", "aria-haspopup": "dialog", "aria-expanded": isOpen, "aria-controls": isOpen ? panelId : undefined, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": fieldDescription, "aria-required": required || undefined, autoComplete: "off", value: text, placeholder: placeholder, disabled: disabled, form: form,
|
|
171
|
+
// A disabled field takes no clicks.
|
|
172
|
+
onClick: () => onOpenChange(!isOpen), onKeyDown: handleKeyDown }), showClear && (jsx(Delete, { className: clearClass, size: size, ariaLabel: clearLabel, onClick: () => {
|
|
173
|
+
onClear();
|
|
174
|
+
onOpenChange(false);
|
|
175
|
+
inputRef.current?.focus();
|
|
176
|
+
} })), jsx("span", { className: chevronClass, "aria-hidden": "true", children: jsx(Icon, { name: "chevron-down", size: size }) })] }));
|
|
177
|
+
const root = (jsxs("div", { ...localeScope.rootAttrs, ...rest, ref: rootRef, className: classNames(rootClasses, bulmaHelperClasses, className), children: [insideControl ? field : jsx(Control, { size: size, children: field }), required && (jsx("input", { ...(proxyFocused
|
|
178
|
+
? {
|
|
179
|
+
...fieldName,
|
|
180
|
+
role: 'combobox',
|
|
181
|
+
'aria-haspopup': 'dialog',
|
|
182
|
+
'aria-expanded': false,
|
|
183
|
+
'aria-describedby': fieldDescription,
|
|
184
|
+
}
|
|
185
|
+
: { 'aria-hidden': true }), tabIndex: -1, inputMode: "none", required: true, value: hasValue ? 'picked' : '', onChange: ignoreChange, form: form, disabled: disabled,
|
|
186
|
+
// When the browser stops a submit, it focuses this and shows its
|
|
187
|
+
// message beside it, and every browser takes the message down as
|
|
188
|
+
// this loses focus. So focus stays here, over the field, which
|
|
189
|
+
// looks focused, and a press goes where it lands, as from any
|
|
190
|
+
// field. The next key hands focus to the field and does there
|
|
191
|
+
// what it would have. Firefox also stops taking clicks if focus
|
|
192
|
+
// leaves while it's still focusing this.
|
|
193
|
+
onFocus: () => setProxyFocused(true), onBlur: () => setProxyFocused(false), onKeyDown: event => {
|
|
194
|
+
if (MODIFIER_KEYS.has(event.key))
|
|
195
|
+
return;
|
|
196
|
+
inputRef.current?.focus();
|
|
197
|
+
handleKeyDown(event);
|
|
198
|
+
}, style: requiredProxyStyle })), jsx(HiddenValues, { name: name, values: values, form: form, disabled: disabled }), jsx(PickerPopover, { localeScope: localeScope.panel, isOpen: isOpen, onClose: () => onOpenChange(false), anchorRef: rootRef, position: position, appendToBody: appendToBody, className: panelClasses, ariaLabel: panelName['aria-label'], ariaLabelledBy: panelName['aria-labelledby'], id: panelId, restoreFocusRef: inputRef, initialFocusSelector: initialFocusSelector, matchWidth: true, followContent: true, children: children(panelName) })] }));
|
|
199
|
+
const fieldElement = below === undefined ? (root) : (jsxs("div", { children: [root, below] }));
|
|
200
|
+
const messageEl = message ? jsx("p", { className: helpClass, children: message }) : null;
|
|
201
|
+
if (ownField) {
|
|
202
|
+
return (jsxs(Field, { label: label, labelSize: labelSize, labelProps: fieldLabelProps, horizontal: horizontal, className: fieldClassName, children: [fieldElement, messageEl] }));
|
|
203
|
+
}
|
|
204
|
+
return (jsxs(Fragment, { children: [fieldElement, messageEl] }));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export { DialogSelect };
|
|
208
|
+
//# sourceMappingURL=DialogSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DialogSelect.js","sources":["../../../../src/form/_selectInternals/DialogSelect.tsx"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts. The field a picker\n// of tree data opens its panel from: TreeSelect's, and Cascader's after it.\n\nimport React, { useEffect, useId, useRef, useState } from 'react';\nimport { classNames, usePrefixedClassNames } from '../../helpers/classNames';\nimport {\n useBulmaClasses,\n type BulmaClassesProps,\n} from '../../helpers/useBulmaClasses';\nimport { useLatestRef } from '../../helpers/useLatestRef';\nimport { Delete } from '../../elements/Delete';\nimport { Icon } from '../../elements/Icon';\nimport { Control } from '../Control';\nimport { Field } from '../Field';\nimport type { FormFieldProps } from '../fieldProps';\nimport {\n rendersOwnField,\n useInsideControl,\n useInsideField,\n} from '../FormContext';\nimport { useAutoLabelId } from '../useAutoLabelId';\nimport { PickerPopover } from '../_pickerInternals/PickerPopover';\nimport {\n isPopoverToggleKey,\n type PickerPosition,\n} from '../_pickerInternals/pickerTypes';\nimport { usePickerLocaleScope } from '../_pickerInternals/useLocaleDefaults';\nimport { HiddenValues } from './ValueTags';\n\n/** What names the panel, for what's in it to take as well. */\nexport interface PanelName {\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n}\n\n/** Props for the field a tree picker opens its panel from. */\nexport interface DialogSelectProps\n extends\n Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'color' | 'onChange' | 'defaultValue' | 'children'\n >,\n Omit<BulmaClassesProps, 'color'>,\n FormFieldProps {\n /** The component's name, for development warnings. */\n component: string;\n /** The root's own class, before any prefix, such as `tree-select`. */\n rootClass: string;\n /** The panel's own class, beside `picker-popover`, before any prefix. */\n panelClass: string;\n /** What the field shows. */\n text: string;\n /** What `name` posts, a hidden input each. */\n values: string[];\n /** Something is picked: `required` is met, and the clear button shows. */\n hasValue: boolean;\n /** Whether the panel is open. */\n open: boolean;\n /**\n * Asked to open or close the panel: by a click on the field, its keys,\n * Escape, Alt+Up, a press outside and the clear button.\n */\n onOpenChange: (open: boolean) => void;\n /**\n * Called once a reset of the field's form has gone ahead, so the picker\n * can put its value back.\n */\n onFormReset: () => void;\n /**\n * A printable key on the field. Returns whether it used the key, as a\n * search box opening with it does.\n */\n onPrintableKey?: (key: string) => boolean;\n /** Empties the value, from the clear button. */\n onClear: () => void;\n /** Shows the clear button while something is picked. */\n clearable?: boolean;\n /** Names the clear button. */\n clearLabel: string;\n /** Names the panel in place of the field's label. */\n chooseLabel?: string;\n placeholder?: string;\n name?: string;\n form?: string;\n required?: boolean;\n /** Already including a disabled `Fieldset` around it. */\n disabled: boolean;\n color?: 'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger';\n size?: 'small' | 'medium' | 'large';\n isRounded?: boolean;\n position?: PickerPosition;\n appendToBody?: boolean;\n /** Holds the field, which focus goes back to. */\n inputRef: React.RefObject<HTMLInputElement | null>;\n /**\n * Set on the field: the picker's callback that fills `inputRef` and the\n * ref its caller gave.\n */\n fieldRef: React.RefCallback<HTMLInputElement>;\n /** The ids of the tags that describe the field. */\n describedBy?: string;\n /** Shown under the field, inside its Field: the tags. */\n below?: React.ReactNode;\n /** Selects what takes focus as the panel opens. */\n initialFocusSelector?: string;\n /** The panel's content, handed the panel's name. */\n children: (name: PanelName) => React.ReactNode;\n}\n\n/**\n * A field laid over the real one, unseen and letting presses through, so the\n * browser's `required` check reads it and its message points at the field.\n */\nconst requiredProxyStyle: React.CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n margin: 0,\n padding: 0,\n border: 0,\n opacity: 0,\n pointerEvents: 'none',\n // Focused, it opens no on-screen keyboard (`inputMode`), and iOS zooms\n // in on a field set under 16px. It's unseen, so the size shows nowhere.\n fontSize: '16px',\n};\n\n/** Keys pressed on their own that don't act, such as Ctrl to stop speech. */\nconst MODIFIER_KEYS = new Set([\n 'Alt',\n 'AltGraph',\n 'CapsLock',\n 'Control',\n 'Fn',\n 'Meta',\n 'NumLock',\n 'ScrollLock',\n 'Shift',\n]);\n\nconst ignoreChange = () => {};\n\n/**\n * A read-only `role=\"combobox\"` field, wired as the date pickers' are, that\n * opens a `PickerPopover` dialog panel: in a `Field` of its own unless one is\n * around it, with a chevron at its inline end, a clear button, the hidden\n * inputs for `name` and what the browser's `required` check reads. A click,\n * Enter, Space, Down or Alt+Down opens the panel, which holds focus, as the\n * date pickers' does: Tab and Shift+Tab wrap inside it. Escape and Alt+Up\n * close it and put focus back on the field, and a press outside closes it\n * where the press put focus.\n */\nexport function DialogSelect({\n component,\n rootClass,\n panelClass,\n text,\n values,\n hasValue,\n open,\n onOpenChange,\n onFormReset,\n onPrintableKey,\n onClear,\n clearable,\n clearLabel,\n chooseLabel,\n placeholder,\n name,\n form,\n required = false,\n disabled,\n color,\n size,\n isRounded,\n position = 'bottom-left',\n appendToBody = false,\n inputRef,\n fieldRef,\n describedBy,\n below,\n initialFocusSelector,\n children,\n label,\n labelSize,\n labelProps,\n horizontal,\n message,\n messageColor,\n fieldClassName,\n className,\n id,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n 'aria-describedby': ariaDescribedBy,\n ...props\n}: DialogSelectProps) {\n const insideField = useInsideField();\n const insideControl = useInsideControl();\n const ownField = rendersOwnField(component, {\n insideField,\n insideControl,\n label,\n message,\n horizontal,\n fieldClassName,\n });\n const { controlId, fieldLabelProps, labelId } = useAutoLabelId({\n label,\n id,\n labelProps,\n rendersLabel: ownField,\n needsLabelId: true,\n });\n const reactId = useId();\n const panelId = `${controlId ?? reactId}-panel`;\n const localeScope = usePickerLocaleScope(undefined, props.dir);\n const { bulmaHelperClasses, rest } = useBulmaClasses(props);\n const rootRef = useRef<HTMLDivElement>(null);\n\n // The field's name, which the panel takes too unless it's given its own.\n const fieldName: PanelName = ariaLabel\n ? { 'aria-label': ariaLabel }\n : ariaLabelledBy\n ? { 'aria-labelledby': ariaLabelledBy }\n : labelId\n ? { 'aria-labelledby': labelId }\n : {};\n const panelName: PanelName = chooseLabel\n ? { 'aria-label': chooseLabel }\n : fieldName;\n\n // Whether the field `required` hangs on has focus, which the browser gives\n // it when it stops a submit. Taking it away or disabling it while it has\n // focus fires no blur, so that clears it too.\n const [proxyFocused, setProxyFocused] = useState(false);\n if (proxyFocused && (!required || disabled)) setProxyFocused(false);\n\n const showClear = !!clearable && hasValue && !disabled;\n const rootClasses = usePrefixedClassNames(rootClass, {\n 'is-disabled': disabled,\n 'has-clear': showClear,\n });\n const inputClass = usePrefixedClassNames('input', {\n [`is-${color}`]: !!color,\n [`is-${size}`]: !!size,\n 'is-rounded': !!isRounded,\n 'is-focused': proxyFocused,\n });\n const chevronClass = usePrefixedClassNames(`${rootClass}-chevron`);\n const clearClass = usePrefixedClassNames(`${rootClass}-clear`);\n const panelClasses = usePrefixedClassNames(panelClass, {\n [`is-${size}`]: !!size,\n });\n const helpClass = usePrefixedClassNames('help', {\n [`is-${messageColor}`]: !!messageColor,\n });\n const isOpen = open && !disabled;\n\n // A reset of the field's form puts the value back, as it does a native\n // field's. The reset event fires before the form resets, and a listener\n // after this one can still cancel it, so the value goes back a task later,\n // and only if the reset went ahead, as File's and Dropzone's do. It's\n // heard on the field's document, or the shadow root it's in, and matched\n // to the field's form as it is then, so a form its `form` names that\n // mounts later, or a `form` that changes, is followed, and a reset\n // already under way still lands.\n const onFormResetRef = useLatestRef(onFormReset);\n useEffect(() => {\n const root = inputRef.current!.getRootNode();\n let timer: ReturnType<typeof setTimeout> | undefined;\n const onReset = (event: Event) => {\n if (event.target !== inputRef.current?.form) return;\n timer = setTimeout(() => {\n if (!event.defaultPrevented) onFormResetRef.current();\n });\n };\n root.addEventListener('reset', onReset, true);\n return () => {\n root.removeEventListener('reset', onReset, true);\n clearTimeout(timer);\n };\n }, [inputRef, onFormResetRef]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return;\n if (isPopoverToggleKey(event)) {\n event.preventDefault();\n if (event.key === 'ArrowDown') onOpenChange(true);\n return;\n }\n if (event.altKey || event.ctrlKey || event.metaKey) return;\n if (\n event.key === 'Enter' ||\n event.key === ' ' ||\n event.key === 'ArrowDown'\n ) {\n event.preventDefault();\n onOpenChange(true);\n } else if (\n event.key.length === 1 &&\n !event.nativeEvent.isComposing &&\n onPrintableKey?.(event.key)\n ) {\n event.preventDefault();\n }\n };\n\n const fieldDescription =\n [ariaDescribedBy, describedBy].filter(Boolean).join(' ') || undefined;\n\n const field = (\n <>\n <input\n ref={fieldRef}\n type=\"text\"\n className={inputClass}\n id={controlId}\n readOnly\n role=\"combobox\"\n aria-haspopup=\"dialog\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? panelId : undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledBy}\n aria-describedby={fieldDescription}\n aria-required={required || undefined}\n autoComplete=\"off\"\n value={text}\n placeholder={placeholder}\n disabled={disabled}\n form={form}\n // A disabled field takes no clicks.\n onClick={() => onOpenChange(!isOpen)}\n onKeyDown={handleKeyDown}\n />\n {showClear && (\n <Delete\n className={clearClass}\n size={size}\n ariaLabel={clearLabel}\n onClick={() => {\n onClear();\n onOpenChange(false);\n inputRef.current?.focus();\n }}\n />\n )}\n <span className={chevronClass} aria-hidden=\"true\">\n <Icon name=\"chevron-down\" size={size} />\n </span>\n </>\n );\n\n const root = (\n <div\n {...localeScope.rootAttrs}\n {...rest}\n ref={rootRef}\n className={classNames(rootClasses, bulmaHelperClasses, className)}\n >\n {insideControl ? field : <Control size={size}>{field}</Control>}\n {required && (\n <input\n // Focus puts it in the accessibility tree, and Chromium won't\n // leave focus hidden, so it's hidden only until it's focused, and\n // then reads as the field, shut.\n {...(proxyFocused\n ? {\n ...fieldName,\n role: 'combobox',\n 'aria-haspopup': 'dialog' as const,\n 'aria-expanded': false,\n 'aria-describedby': fieldDescription,\n }\n : { 'aria-hidden': true })}\n tabIndex={-1}\n inputMode=\"none\"\n required\n value={hasValue ? 'picked' : ''}\n onChange={ignoreChange}\n form={form}\n disabled={disabled}\n // When the browser stops a submit, it focuses this and shows its\n // message beside it, and every browser takes the message down as\n // this loses focus. So focus stays here, over the field, which\n // looks focused, and a press goes where it lands, as from any\n // field. The next key hands focus to the field and does there\n // what it would have. Firefox also stops taking clicks if focus\n // leaves while it's still focusing this.\n onFocus={() => setProxyFocused(true)}\n onBlur={() => setProxyFocused(false)}\n onKeyDown={event => {\n if (MODIFIER_KEYS.has(event.key)) return;\n inputRef.current?.focus();\n handleKeyDown(event);\n }}\n style={requiredProxyStyle}\n />\n )}\n <HiddenValues\n name={name}\n values={values}\n form={form}\n disabled={disabled}\n />\n <PickerPopover\n localeScope={localeScope.panel}\n isOpen={isOpen}\n onClose={() => onOpenChange(false)}\n anchorRef={rootRef}\n position={position}\n appendToBody={appendToBody}\n className={panelClasses}\n ariaLabel={panelName['aria-label']}\n ariaLabelledBy={panelName['aria-labelledby']}\n id={panelId}\n restoreFocusRef={inputRef}\n initialFocusSelector={initialFocusSelector}\n matchWidth\n followContent\n >\n {children(panelName)}\n </PickerPopover>\n </div>\n );\n\n const fieldElement =\n below === undefined ? (\n root\n ) : (\n <div>\n {root}\n {below}\n </div>\n );\n const messageEl = message ? <p className={helpClass}>{message}</p> : null;\n\n if (ownField) {\n return (\n <Field\n label={label}\n labelSize={labelSize}\n labelProps={fieldLabelProps}\n horizontal={horizontal}\n className={fieldClassName}\n >\n {fieldElement}\n {messageEl}\n </Field>\n );\n }\n return (\n <>\n {fieldElement}\n {messageEl}\n </>\n );\n}\n"],"names":["_jsxs","_Fragment","_jsx"],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
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, Fragment, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
import { Delete } from '../../elements/Delete.js';
|
|
5
|
+
import { Tag } from '../../elements/Tag.js';
|
|
6
|
+
import { Tags } from '../../elements/Tags.js';
|
|
7
|
+
|
|
8
|
+
const everyValue = () => true;
|
|
9
|
+
/**
|
|
10
|
+
* The values of a field holding several, as Bulma tags under it, each with a
|
|
11
|
+
* `Delete` that takes its value off. Nothing while there are none.
|
|
12
|
+
*/
|
|
13
|
+
function ValueTags({ values, ids, labelFor, disabled, ariaCloseLabel, onRemove, removable = everyValue, }) {
|
|
14
|
+
if (values.length === 0)
|
|
15
|
+
return null;
|
|
16
|
+
return (jsx(Tags, { mt: "2", children: values.map((value, index) => (jsxs(Tag, { children: [jsx("span", { id: ids[index], children: labelFor(value) }), !disabled && removable(value) && (jsx(Delete, { size: "small", ariaLabel: ariaCloseLabel || `Remove ${labelFor(value)}`, "aria-describedby": ariaCloseLabel ? ids[index] : undefined, onClick: () => onRemove(value) }))] }, value))) }));
|
|
17
|
+
}
|
|
18
|
+
/** The ids `ValueTags` gives its labels, built from one id of the field's. */
|
|
19
|
+
const tagIds = (base, values) => values.map((_, index) => `${base}-${index}`);
|
|
20
|
+
/** A hidden input for each value, so a form posts them under `name`. */
|
|
21
|
+
function HiddenValues({ name, values, form, disabled, }) {
|
|
22
|
+
if (!name)
|
|
23
|
+
return null;
|
|
24
|
+
return (jsx(Fragment, { children: values.map(value => (jsx("input", { type: "hidden", name: name, value: value, form: form, disabled: disabled }, value))) }));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { HiddenValues, ValueTags, tagIds };
|
|
28
|
+
//# sourceMappingURL=ValueTags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ValueTags.js","sources":["../../../../src/form/_selectInternals/ValueTags.tsx"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts. The parts a field\n// holding values shows and posts them with, shared by Combobox and TreeSelect.\n\nimport { Delete } from '../../elements/Delete';\nimport { Tag } from '../../elements/Tag';\nimport { Tags } from '../../elements/Tags';\n\n/** Props for the tags of a field holding several values. */\nexport interface ValueTagsProps {\n /** The values, in the order they show. */\n values: string[];\n /** The id of each tag's label, from `tagIds`, which the field's description lists. */\n ids: string[];\n /** What a value shows. */\n labelFor: (value: string) => string;\n /** The field can't be used, so the tags have no buttons. */\n disabled?: boolean;\n /**\n * Names each button in place of \"Remove\" and the value's label. The\n * button is then described by its tag's label, so it still says which.\n */\n ariaCloseLabel?: string;\n /** Called with a value when its tag's button is pressed. */\n onRemove: (value: string) => void;\n /** Whether a value's tag has a button. Every tag has one without it. */\n removable?: (value: string) => boolean;\n}\n\nconst everyValue = () => true;\n\n/**\n * The values of a field holding several, as Bulma tags under it, each with a\n * `Delete` that takes its value off. Nothing while there are none.\n */\nexport function ValueTags({\n values,\n ids,\n labelFor,\n disabled,\n ariaCloseLabel,\n onRemove,\n removable = everyValue,\n}: ValueTagsProps) {\n if (values.length === 0) return null;\n return (\n <Tags mt=\"2\">\n {values.map((value, index) => (\n <Tag key={value}>\n <span id={ids[index]}>{labelFor(value)}</span>\n {!disabled && removable(value) && (\n <Delete\n size=\"small\"\n ariaLabel={ariaCloseLabel || `Remove ${labelFor(value)}`}\n aria-describedby={ariaCloseLabel ? ids[index] : undefined}\n onClick={() => onRemove(value)}\n />\n )}\n </Tag>\n ))}\n </Tags>\n );\n}\n\n/** The ids `ValueTags` gives its labels, built from one id of the field's. */\nexport const tagIds = (base: string, values: string[]): string[] =>\n values.map((_, index) => `${base}-${index}`);\n\n/** Props for the hidden inputs that post a field's values. */\nexport interface HiddenValuesProps {\n /** The form field name. Nothing renders without one. */\n name?: string;\n /** One hidden input each. */\n values: string[];\n /** The id of the form they post with, for a field outside it. */\n form?: string;\n /** They don't post, as a disabled field's value doesn't. */\n disabled?: boolean;\n}\n\n/** A hidden input for each value, so a form posts them under `name`. */\nexport function HiddenValues({\n name,\n values,\n form,\n disabled,\n}: HiddenValuesProps) {\n if (!name) return null;\n return (\n <>\n {values.map(value => (\n <input\n key={value}\n type=\"hidden\"\n name={name}\n value={value}\n form={form}\n disabled={disabled}\n />\n ))}\n </>\n );\n}\n"],"names":["_jsx","_jsxs","_Fragment"],"mappings":";;;;;;;AA4BA,MAAM,UAAU,GAAG,MAAM,IAAI;AAE7B;;;AAGG;SACa,SAAS,CAAC,EACxB,MAAM,EACN,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,SAAS,GAAG,UAAU,GACP,EAAA;AACf,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;AAAE,QAAA,OAAO,IAAI;IACpC,QACEA,IAAC,IAAI,EAAA,EAAC,EAAE,EAAC,GAAG,YACT,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,MACvBC,IAAA,CAAC,GAAG,eACFD,GAAA,CAAA,MAAA,EAAA,EAAM,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,EAAA,QAAA,EAAG,QAAQ,CAAC,KAAK,CAAC,EAAA,CAAQ,EAC7C,CAAC,QAAQ,IAAI,SAAS,CAAC,KAAK,CAAC,KAC5BA,GAAA,CAAC,MAAM,EAAA,EACL,IAAI,EAAC,OAAO,EACZ,SAAS,EAAE,cAAc,IAAI,CAAA,OAAA,EAAU,QAAQ,CAAC,KAAK,CAAC,EAAE,EAAA,kBAAA,EACtC,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,SAAS,EACzD,OAAO,EAAE,MAAM,QAAQ,CAAC,KAAK,CAAC,EAAA,CAC9B,CACH,CAAA,EAAA,EATO,KAAK,CAUT,CACP,CAAC,EAAA,CACG;AAEX;AAEA;AACO,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,MAAgB,KACnD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,KAAK,CAAA,CAAE;AAc7C;AACM,SAAU,YAAY,CAAC,EAC3B,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,QAAQ,GACU,EAAA;AAClB,IAAA,IAAI,CAAC,IAAI;AAAE,QAAA,OAAO,IAAI;AACtB,IAAA,QACEA,GAAA,CAAAE,QAAA,EAAA,EAAA,QAAA,EACG,MAAM,CAAC,GAAG,CAAC,KAAK,KACfF,GAAA,CAAA,OAAA,EAAA,EAEE,IAAI,EAAC,QAAQ,EACb,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,IALb,KAAK,CAMV,CACH,CAAC,EAAA,CACD;AAEP;;"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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 } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// INTERNAL: deliberately not exported from src/index.ts.
|
|
6
|
+
/**
|
|
7
|
+
* A check mark, 1em square and drawn in `currentColor`, so it takes the text
|
|
8
|
+
* color around it, the system's in forced colors included. Combobox draws it
|
|
9
|
+
* beside a chosen item, and CopyButton after a copy. It's decorative: put it
|
|
10
|
+
* inside something `aria-hidden`.
|
|
11
|
+
*
|
|
12
|
+
* @function CheckIcon
|
|
13
|
+
* @returns The check mark's `<svg>`.
|
|
14
|
+
*/
|
|
15
|
+
function CheckIcon$1() {
|
|
16
|
+
return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "1em", height: "1em", focusable: "false", children: jsx("polyline", { points: "3 8.5 6.5 12 13 4.5" }) }));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { CheckIcon$1 as CheckIcon };
|
|
20
|
+
//# sourceMappingURL=checkIcon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkIcon.js","sources":["../../../src/helpers/checkIcon.tsx"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts.\n\n/**\n * A check mark, 1em square and drawn in `currentColor`, so it takes the text\n * color around it, the system's in forced colors included. Combobox draws it\n * beside a chosen item, and CopyButton after a copy. It's decorative: put it\n * inside something `aria-hidden`.\n *\n * @function CheckIcon\n * @returns The check mark's `<svg>`.\n */\nexport function CheckIcon() {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n width=\"1em\"\n height=\"1em\"\n focusable=\"false\"\n >\n <polyline points=\"3 8.5 6.5 12 13 4.5\" />\n </svg>\n );\n}\n"],"names":["CheckIcon","_jsx"],"mappings":";;;;AAAA;AAEA;;;;;;;;AAQG;SACaA,WAAS,GAAA;IACvB,QACEC,aACE,KAAK,EAAC,4BAA4B,EAClC,OAAO,EAAC,WAAW,EACnB,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,cAAc,EACrB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,cAAc,EAAC,OAAO,EACtB,KAAK,EAAC,KAAK,EACX,MAAM,EAAC,KAAK,EACZ,SAAS,EAAC,OAAO,EAAA,QAAA,EAEjBA,GAAA,CAAA,UAAA,EAAA,EAAU,MAAM,EAAC,qBAAqB,EAAA,CAAG,EAAA,CACrC;AAEV;;"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
/**
|
|
3
|
+
* INTERNAL, deliberately not exported from `src/index.ts`. Whether a prop
|
|
4
|
+
* was given something to render: `undefined`, `null`, a boolean and `''`
|
|
5
|
+
* weren't, and `0` was, since React renders it.
|
|
6
|
+
*/
|
|
7
|
+
const isGiven = (node) => node !== undefined &&
|
|
8
|
+
node !== null &&
|
|
9
|
+
typeof node !== 'boolean' &&
|
|
10
|
+
node !== '';
|
|
11
|
+
|
|
12
|
+
export { isGiven };
|
|
13
|
+
//# sourceMappingURL=isGiven.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isGiven.js","sources":["../../../src/helpers/isGiven.ts"],"sourcesContent":["import type { ReactNode } from 'react';\n\n/**\n * INTERNAL, deliberately not exported from `src/index.ts`. Whether a prop\n * was given something to render: `undefined`, `null`, a boolean and `''`\n * weren't, and `0` was, since React renders it.\n */\nexport const isGiven = (node: ReactNode) =>\n node !== undefined &&\n node !== null &&\n typeof node !== 'boolean' &&\n node !== '';\n"],"names":[],"mappings":";AAEA;;;;AAIG;AACI,MAAM,OAAO,GAAG,CAAC,IAAe,KACrC,IAAI,KAAK,SAAS;AAClB,IAAA,IAAI,KAAK,IAAI;IACb,OAAO,IAAI,KAAK,SAAS;IACzB,IAAI,KAAK;;"}
|
|
@@ -17,11 +17,11 @@ const announceDelay = 100;
|
|
|
17
17
|
* again.
|
|
18
18
|
*/
|
|
19
19
|
const announcementLifetime = 1000;
|
|
20
|
-
const noAnnouncements$
|
|
20
|
+
const noAnnouncements$2 = [];
|
|
21
21
|
const nothingWritten = [];
|
|
22
22
|
const noAnnouncementsYet = {
|
|
23
23
|
key: 0,
|
|
24
|
-
waiting: noAnnouncements$
|
|
24
|
+
waiting: noAnnouncements$2,
|
|
25
25
|
written: nothingWritten,
|
|
26
26
|
};
|
|
27
27
|
// Hides the region with inline styles rather than a class, so it needs no
|
|
@@ -123,7 +123,7 @@ function useAnnouncements(items, describe) {
|
|
|
123
123
|
setAnnouncements(current => current.key === key
|
|
124
124
|
? {
|
|
125
125
|
...current,
|
|
126
|
-
waiting: noAnnouncements$
|
|
126
|
+
waiting: noAnnouncements$2,
|
|
127
127
|
written: [
|
|
128
128
|
...current.written,
|
|
129
129
|
...current.waiting
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* @allxsmith/bestax-bulma — AI agents: see AGENTS.md in the package root, or https://bestax.io/llms.txt */
|
|
2
|
+
import { readChecks } from './useTreeView.js';
|
|
3
|
+
|
|
4
|
+
// INTERNAL: deliberately not exported from src/index.ts. Walking the nodes a
|
|
5
|
+
// TreeSelect takes as `data`, so a picker can name, check and search a tree
|
|
6
|
+
// while it isn't on the page.
|
|
7
|
+
/**
|
|
8
|
+
* Every node by its value, in the order the tree reads from top to bottom.
|
|
9
|
+
* Values are meant to be unique, and the first node with a value wins.
|
|
10
|
+
*/
|
|
11
|
+
function indexTree(data) {
|
|
12
|
+
const index = new Map();
|
|
13
|
+
const walk = (nodes, path) => {
|
|
14
|
+
for (const node of nodes) {
|
|
15
|
+
if (!index.has(node.value))
|
|
16
|
+
index.set(node.value, { node, path });
|
|
17
|
+
if (node.children)
|
|
18
|
+
walk(node.children, [...path, node.value]);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
walk(data, []);
|
|
22
|
+
return index;
|
|
23
|
+
}
|
|
24
|
+
/** What a node is called: its `label`, or its `value` when that's empty. */
|
|
25
|
+
const nodeLabel = (node) => node.label || node.value;
|
|
26
|
+
/** The nodes under a node, none when it has no `children`. */
|
|
27
|
+
const childNodes = (node) => node.children ?? [];
|
|
28
|
+
/** Whether a node is a parent: it has children, or `hasChildren` says it will. */
|
|
29
|
+
const isParentNode = (node) => childNodes(node).length > 0 || !!node.hasChildren;
|
|
30
|
+
/**
|
|
31
|
+
* The records TreeView's check rules read, built from the nodes rather than
|
|
32
|
+
* from the items on the page, so the checked list can be worked out while
|
|
33
|
+
* the tree isn't there.
|
|
34
|
+
*/
|
|
35
|
+
function checkRecords(index) {
|
|
36
|
+
const records = new Map();
|
|
37
|
+
index.forEach(({ node, path }, value) => records.set(value, {
|
|
38
|
+
parent: path[path.length - 1] ?? null,
|
|
39
|
+
disabled: !!node.disabled,
|
|
40
|
+
isParent: isParentNode(node),
|
|
41
|
+
}));
|
|
42
|
+
return records;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The checked list a `checkedStrategy` reports for the items `listed` checks
|
|
46
|
+
* by TreeView's rules, in the tree's order. `all` is every checked item;
|
|
47
|
+
* `child` the checked items with no enabled item under them; `parent` the
|
|
48
|
+
* checked items whose parent isn't. A disabled item is checked by its own
|
|
49
|
+
* value only, so a checked one is in every list. Values no node has are
|
|
50
|
+
* kept, at the end. Passed back in, each list checks the same items.
|
|
51
|
+
*/
|
|
52
|
+
function checkedByStrategy(records, listed, strategy) {
|
|
53
|
+
const { states, ends } = readChecks(records, new Set(listed));
|
|
54
|
+
const isEnd = new Set(ends);
|
|
55
|
+
const on = (value) => value !== null && states.get(value) === true;
|
|
56
|
+
const kept = [];
|
|
57
|
+
records.forEach(({ parent, disabled }, value) => {
|
|
58
|
+
if (!on(value))
|
|
59
|
+
return;
|
|
60
|
+
if (strategy === 'all' ||
|
|
61
|
+
disabled ||
|
|
62
|
+
(strategy === 'child' ? isEnd.has(value) : !on(parent))) {
|
|
63
|
+
kept.push(value);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const unknown = new Set(listed.filter(value => !records.has(value)));
|
|
67
|
+
return [...kept, ...unknown];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* What a search shows of a tree: the nodes that match, the nodes above them,
|
|
71
|
+
* so the results stay a tree, and the nodes under them, so a matching branch
|
|
72
|
+
* can still be opened.
|
|
73
|
+
*/
|
|
74
|
+
function searchTree(data, matches) {
|
|
75
|
+
const hidden = new Set();
|
|
76
|
+
const open = [];
|
|
77
|
+
// Whether the nodes, or any node under them, match. `above` is the path
|
|
78
|
+
// down to them.
|
|
79
|
+
const walk = (nodes, underMatch, above) => {
|
|
80
|
+
let found = false;
|
|
81
|
+
for (const node of nodes) {
|
|
82
|
+
const path = [...above, node];
|
|
83
|
+
const self = matches(node, path);
|
|
84
|
+
const below = walk(childNodes(node), underMatch || self, path);
|
|
85
|
+
if (below)
|
|
86
|
+
open.push(node.value);
|
|
87
|
+
if (!underMatch && !self && !below)
|
|
88
|
+
hidden.add(node.value);
|
|
89
|
+
found = found || self || below;
|
|
90
|
+
}
|
|
91
|
+
return found;
|
|
92
|
+
};
|
|
93
|
+
return { hidden, open, found: walk(data, false, []) };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { checkRecords, checkedByStrategy, childNodes, indexTree, isParentNode, nodeLabel, searchTree };
|
|
97
|
+
//# sourceMappingURL=treeData.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"treeData.js","sources":["../../../src/helpers/treeData.ts"],"sourcesContent":["// INTERNAL: deliberately not exported from src/index.ts. Walking the nodes a\n// TreeSelect takes as `data`, so a picker can name, check and search a tree\n// while it isn't on the page.\n\nimport type {\n TreeItemData,\n TreeSelectCheckedStrategy,\n} from '../form/TreeSelect';\nimport { readChecks, type TreeViewItemRecord } from './useTreeView';\n\n/** A node, and the values of the nodes above it, from the top. */\nexport interface TreeNodeEntry {\n node: TreeItemData;\n path: string[];\n}\n\n/**\n * Every node by its value, in the order the tree reads from top to bottom.\n * Values are meant to be unique, and the first node with a value wins.\n */\nexport function indexTree(data: TreeItemData[]): Map<string, TreeNodeEntry> {\n const index = new Map<string, TreeNodeEntry>();\n const walk = (nodes: TreeItemData[], path: string[]) => {\n for (const node of nodes) {\n if (!index.has(node.value)) index.set(node.value, { node, path });\n if (node.children) walk(node.children, [...path, node.value]);\n }\n };\n walk(data, []);\n return index;\n}\n\n/** What a node is called: its `label`, or its `value` when that's empty. */\nexport const nodeLabel = (node: TreeItemData): string =>\n node.label || node.value;\n\n/** The nodes under a node, none when it has no `children`. */\nexport const childNodes = (node: TreeItemData): TreeItemData[] =>\n node.children ?? [];\n\n/** Whether a node is a parent: it has children, or `hasChildren` says it will. */\nexport const isParentNode = (node: TreeItemData): boolean =>\n childNodes(node).length > 0 || !!node.hasChildren;\n\n/**\n * The records TreeView's check rules read, built from the nodes rather than\n * from the items on the page, so the checked list can be worked out while\n * the tree isn't there.\n */\nexport function checkRecords(\n index: Map<string, TreeNodeEntry>\n): Map<string, TreeViewItemRecord> {\n const records = new Map<string, TreeViewItemRecord>();\n index.forEach(({ node, path }, value) =>\n records.set(value, {\n parent: path[path.length - 1] ?? null,\n disabled: !!node.disabled,\n isParent: isParentNode(node),\n })\n );\n return records;\n}\n\n/**\n * The checked list a `checkedStrategy` reports for the items `listed` checks\n * by TreeView's rules, in the tree's order. `all` is every checked item;\n * `child` the checked items with no enabled item under them; `parent` the\n * checked items whose parent isn't. A disabled item is checked by its own\n * value only, so a checked one is in every list. Values no node has are\n * kept, at the end. Passed back in, each list checks the same items.\n */\nexport function checkedByStrategy(\n records: Map<string, TreeViewItemRecord>,\n listed: string[],\n strategy: TreeSelectCheckedStrategy\n): string[] {\n const { states, ends } = readChecks(records, new Set(listed));\n const isEnd = new Set(ends);\n const on = (value: string | null) =>\n value !== null && states.get(value) === true;\n const kept: string[] = [];\n records.forEach(({ parent, disabled }, value) => {\n if (!on(value)) return;\n if (\n strategy === 'all' ||\n disabled ||\n (strategy === 'child' ? isEnd.has(value) : !on(parent))\n ) {\n kept.push(value);\n }\n });\n const unknown = new Set(listed.filter(value => !records.has(value)));\n return [...kept, ...unknown];\n}\n\n/** What a search leaves of a tree. */\nexport interface TreeSearch {\n /** The values of the nodes it hides. */\n hidden: Set<string>;\n /** The values of the nodes with a match under them, which open. */\n open: string[];\n /** Whether anything matched. */\n found: boolean;\n}\n\n/**\n * What a search shows of a tree: the nodes that match, the nodes above them,\n * so the results stay a tree, and the nodes under them, so a matching branch\n * can still be opened.\n */\nexport function searchTree(\n data: TreeItemData[],\n matches: (node: TreeItemData, path: TreeItemData[]) => boolean\n): TreeSearch {\n const hidden = new Set<string>();\n const open: string[] = [];\n // Whether the nodes, or any node under them, match. `above` is the path\n // down to them.\n const walk = (\n nodes: TreeItemData[],\n underMatch: boolean,\n above: TreeItemData[]\n ): boolean => {\n let found = false;\n for (const node of nodes) {\n const path = [...above, node];\n const self = matches(node, path);\n const below = walk(childNodes(node), underMatch || self, path);\n if (below) open.push(node.value);\n if (!underMatch && !self && !below) hidden.add(node.value);\n found = found || self || below;\n }\n return found;\n };\n return { hidden, open, found: walk(data, false, []) };\n}\n"],"names":[],"mappings":"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"}
|