@flood-md/react 0.2.10 → 0.2.11
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/README.md +239 -1
- package/dist/app-shell.d.ts +3 -0
- package/dist/app-shell.js +66 -0
- package/dist/bulk-action-bar.d.ts +3 -0
- package/dist/bulk-action-bar.js +17 -0
- package/dist/collection-toolbar.d.ts +3 -0
- package/dist/collection-toolbar.js +26 -0
- package/dist/collection-view-switcher.d.ts +3 -0
- package/dist/collection-view-switcher.js +19 -0
- package/dist/combobox.d.ts +6 -1
- package/dist/combobox.js +77 -29
- package/dist/command-list.d.ts +5 -1
- package/dist/command-list.js +2 -2
- package/dist/data-display.js +1 -1
- package/dist/data-table.js +44 -9
- package/dist/date-picker.js +2 -2
- package/dist/date-range-picker.js +2 -2
- package/dist/date-time-picker.js +2 -2
- package/dist/date-time-range-picker.js +2 -2
- package/dist/disclosure.d.ts +2 -1
- package/dist/disclosure.js +4 -4
- package/dist/filter-condition.d.ts +3 -0
- package/dist/filter-condition.js +21 -0
- package/dist/filter-group.d.ts +2 -0
- package/dist/filter-group.js +62 -0
- package/dist/foundations.css +752 -76
- package/dist/index.d.ts +399 -3
- package/dist/index.js +61 -7
- package/dist/menu.js +5 -4
- package/dist/menubar.d.ts +2 -0
- package/dist/menubar.js +85 -0
- package/dist/navigation-group.d.ts +3 -0
- package/dist/navigation-group.js +13 -0
- package/dist/navigation-item.d.ts +3 -0
- package/dist/navigation-item.js +68 -0
- package/dist/navigation-layout.d.ts +2 -1
- package/dist/navigation-layout.js +12 -4
- package/dist/navigation-tree.js +51 -7
- package/dist/navigation.js +2 -2
- package/dist/overflow-actions.d.ts +2 -0
- package/dist/overflow-actions.js +75 -0
- package/dist/overlay-runtime.d.ts +3 -0
- package/dist/overlay-runtime.js +35 -1
- package/dist/overlays.js +32 -16
- package/dist/select.js +4 -1
- package/dist/sidebar.d.ts +2 -0
- package/dist/sidebar.js +58 -0
- package/dist/toolbar.js +2 -2
- package/package.json +2 -2
package/dist/combobox.js
CHANGED
|
@@ -1,39 +1,68 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
-
import { Input, InputGroup, IconButton, ScrollArea, Tag } from "./index.js";
|
|
3
|
+
import { Input, InputGroup, IconButton, ScrollArea, Tag, Button } from "./index.js";
|
|
4
4
|
import { mergeDescriptions, resolveFieldStatus } from "./field-state.js";
|
|
5
5
|
import { positionLayer, registerLayer } from "./overlay-runtime.js";
|
|
6
6
|
const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
7
7
|
const searchable = (text) => text.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase();
|
|
8
8
|
/** Shared search/layer model. Public single and multiple value contracts stay distinct. */
|
|
9
|
-
export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelection({ options, selectedValues: value, initialValues: defaultValue, onSelectionChange: onValueChange, multiple = false, removeItemLabel = "Remove", clearable = false, emptyMessage = "No results found.", listLabel = "Options", toggleLabel = "Show options", clearLabel = "Clear selection", invalidSelectionMessage = "Select an option.", controlSize = "md", readOnly, disabled, required, name, id: explicitId, className, onKeyDown, onBlur, onClick, onInvalid, autoComplete = "off", ...props }, forwardedRef) {
|
|
9
|
+
export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelection({ options, selectedOptions = [], query: controlledQuery, onQueryChange, filtering = "local", optionsQuery, loadState = "ready", loadingMessage = "Loading options…", errorMessage = "Could not load options.", onRetry, retryLabel = "Try again", freeText = false, textValue, initialText = "", onTextChange, onOptionSelect, selectedValues: value, initialValues: defaultValue, onSelectionChange: onValueChange, multiple = false, removeItemLabel = "Remove", clearable = false, emptyMessage = "No results found.", listLabel = "Options", toggleLabel = "Show options", clearLabel = "Clear selection", invalidSelectionMessage = "Select an option.", controlSize = "md", readOnly, disabled, required, name, id: explicitId, className, onKeyDown, onBlur, onClick, onInvalid, autoComplete = "off", ...props }, forwardedRef) {
|
|
10
10
|
if (new Set(options.map(option => option.value)).size !== options.length || options.some(option => !option.value || !option.label.trim()))
|
|
11
11
|
throw new Error("Flood Combobox options need unique nonempty values and labels");
|
|
12
|
-
if (![emptyMessage, listLabel, toggleLabel, clearLabel, invalidSelectionMessage].every(text => text.trim()))
|
|
12
|
+
if (![emptyMessage, listLabel, toggleLabel, clearLabel, invalidSelectionMessage, loadingMessage, errorMessage, retryLabel].every(text => text.trim()))
|
|
13
13
|
throw new Error("Flood Combobox messages and action labels must be nonempty");
|
|
14
|
+
if (new Set(selectedOptions.map(option => option.value)).size !== selectedOptions.length || selectedOptions.some(option => !option.value || !option.label.trim()))
|
|
15
|
+
throw new Error("Flood selectedOptions requires unique named records");
|
|
14
16
|
for (const values of [defaultValue, value])
|
|
15
17
|
if (values !== undefined && (!Array.isArray(values) || new Set(values).size !== values.length || values.some(item => typeof item !== "string" || !item)))
|
|
16
18
|
throw new Error("Flood selection values must be unique nonempty strings");
|
|
17
19
|
if (multiple && !removeItemLabel.trim())
|
|
18
20
|
throw new Error("Flood MultiSelect requires an accessible removal label");
|
|
21
|
+
if (filtering === "manual" && ((freeText ? textValue : controlledQuery) === undefined || optionsQuery === undefined))
|
|
22
|
+
throw new Error("Flood manual search requires controlled text/query and optionsQuery");
|
|
19
23
|
const generatedId = useId(), id = explicitId ?? `flood-combobox-${generatedId}`;
|
|
20
24
|
const menuId = `${id}-options`, validationId = `${id}-validation`;
|
|
21
25
|
const input = useRef(null), root = useRef(null), panel = useRef(null), viewport = useRef(null);
|
|
22
26
|
useImperativeHandle(forwardedRef, () => input.current, []);
|
|
23
|
-
const initial = useRef([...defaultValue]);
|
|
24
|
-
const [local, setLocal] = useState(() => [...defaultValue]), [
|
|
27
|
+
const initial = useRef([...defaultValue]), initialFreeText = useRef(initialText);
|
|
28
|
+
const [local, setLocal] = useState(() => [...defaultValue]), [draftQuery, setDraftQuery] = useState(null);
|
|
29
|
+
const [localText, setLocalText] = useState(initialText);
|
|
30
|
+
const text = textValue ?? localText;
|
|
31
|
+
const query = freeText ? text : draftQuery === null ? null : controlledQuery ?? draftQuery;
|
|
32
|
+
const setQuery = (next) => { setDraftQuery(next); if (!freeText && next !== null)
|
|
33
|
+
onQueryChange?.(next); };
|
|
34
|
+
const publishText = (next) => { if (textValue === undefined)
|
|
35
|
+
setLocalText(next); if (next !== text)
|
|
36
|
+
onTextChange?.(next); };
|
|
25
37
|
const [open, setOpen] = useState(false), [activeValue, setActiveValue] = useState();
|
|
26
38
|
const [validation, setValidation] = useState("");
|
|
27
|
-
const selections = (value ?? local).map(id => options.find(option => option.value === id)).filter((option) => Boolean(option));
|
|
39
|
+
const selections = (value ?? local).map(id => selectedOptions.find(option => option.value === id) ?? options.find(option => option.value === id)).filter((option) => Boolean(option));
|
|
28
40
|
const selected = selections[0], selectedIds = selections.map(option => option.value);
|
|
29
|
-
const displayed = query ?? (multiple ? "" : selected?.label ?? "");
|
|
30
|
-
const
|
|
41
|
+
const displayed = freeText ? text : query ?? (multiple ? "" : selected?.label ?? "");
|
|
42
|
+
const resultQuery = freeText ? text : controlledQuery ?? draftQuery ?? "";
|
|
43
|
+
const pending = loadState === "loading" || (filtering === "manual" && optionsQuery !== resultQuery);
|
|
44
|
+
const failed = loadState === "error" && !pending;
|
|
45
|
+
const resultMessage = pending ? loadingMessage : failed ? errorMessage : emptyMessage;
|
|
46
|
+
const filtered = pending || failed ? [] : filtering === "manual" || query === null ? options : options.filter(option => searchable([option.label, ...(option.keywords ?? [])].join(" ")).includes(searchable(query)));
|
|
31
47
|
const enabled = filtered.filter(option => !option.disabled);
|
|
48
|
+
const groups = [];
|
|
49
|
+
for (const option of filtered) {
|
|
50
|
+
const previous = groups.at(-1);
|
|
51
|
+
if (previous && previous.label === option.group)
|
|
52
|
+
previous.options.push(option);
|
|
53
|
+
else
|
|
54
|
+
groups.push({ label: option.group, options: [option] });
|
|
55
|
+
}
|
|
32
56
|
const active = enabled.find(option => option.value === activeValue) ?? enabled[0];
|
|
33
57
|
const optionId = (optionValue) => `${menuId}-${options.findIndex(option => option.value === optionValue)}`;
|
|
34
|
-
const current = useRef({ value, selected, readOnly, disabled });
|
|
35
|
-
current.current = { value, selected, readOnly, disabled };
|
|
36
|
-
const close = () => {
|
|
58
|
+
const current = useRef({ value, textValue, selected, readOnly, disabled });
|
|
59
|
+
current.current = { value, textValue, selected, readOnly, disabled };
|
|
60
|
+
const close = () => {
|
|
61
|
+
if (panel.current?.contains(panel.current.ownerDocument.activeElement))
|
|
62
|
+
input.current?.focus({ preventScroll: true });
|
|
63
|
+
setOpen(false);
|
|
64
|
+
setQuery(null);
|
|
65
|
+
};
|
|
37
66
|
const inert = () => Boolean(readOnly || disabled || input.current?.matches(":disabled"));
|
|
38
67
|
const publish = (next) => {
|
|
39
68
|
if (value === undefined)
|
|
@@ -43,14 +72,23 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
43
72
|
setValidation("");
|
|
44
73
|
};
|
|
45
74
|
const remove = (id) => {
|
|
46
|
-
if (inert() ||
|
|
75
|
+
if (inert() || selections.find(option => option.value === id)?.disabled)
|
|
47
76
|
return;
|
|
48
77
|
publish(selectedIds.filter(value => value !== id));
|
|
49
78
|
input.current?.focus({ preventScroll: true });
|
|
50
79
|
};
|
|
51
80
|
const select = (next) => {
|
|
52
|
-
if (inert() || (next !== "" && !
|
|
81
|
+
if (inert() || (next !== "" && !filtered.some(option => option.value === next && !option.disabled)))
|
|
53
82
|
return;
|
|
83
|
+
if (freeText) {
|
|
84
|
+
const option = filtered.find(option => option.value === next);
|
|
85
|
+
publishText(option?.label ?? "");
|
|
86
|
+
if (option)
|
|
87
|
+
onOptionSelect?.(option);
|
|
88
|
+
close();
|
|
89
|
+
input.current?.focus({ preventScroll: true });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
54
92
|
if (multiple) {
|
|
55
93
|
publish(next === "" ? selections.filter(option => option.disabled).map(option => option.value) : selectedIds.includes(next) ? selectedIds.filter(id => id !== next) : [...selectedIds, next]);
|
|
56
94
|
if (next) {
|
|
@@ -75,7 +113,7 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
75
113
|
};
|
|
76
114
|
useBrowserLayoutEffect(() => {
|
|
77
115
|
const control = input.current;
|
|
78
|
-
control?.setCustomValidity(required && !selections.length ? invalidSelectionMessage : "");
|
|
116
|
+
control?.setCustomValidity(!freeText && required && !selections.length ? invalidSelectionMessage : "");
|
|
79
117
|
if (validation && control?.validity.valid)
|
|
80
118
|
setValidation("");
|
|
81
119
|
});
|
|
@@ -92,6 +130,8 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
92
130
|
return;
|
|
93
131
|
if (current.current.value === undefined)
|
|
94
132
|
setLocal(initial.current);
|
|
133
|
+
if (current.current.textValue === undefined)
|
|
134
|
+
setLocalText(initialFreeText.current);
|
|
95
135
|
setQuery(null);
|
|
96
136
|
setOpen(false);
|
|
97
137
|
setValidation("");
|
|
@@ -167,7 +207,7 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
167
207
|
popup.style.visibility = "hidden";
|
|
168
208
|
popup.showPopover();
|
|
169
209
|
update();
|
|
170
|
-
unregister = registerLayer(document, { panel: popup, anchor: () => anchor, close
|
|
210
|
+
unregister = registerLayer(document, { panel: popup, anchor: () => anchor, close });
|
|
171
211
|
};
|
|
172
212
|
const toggle = (event) => { if (alive && event.newState === "closed" && !popup.matches(":popover-open")) {
|
|
173
213
|
setOpen(false);
|
|
@@ -192,7 +232,8 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
192
232
|
}, [open, active?.value, query, options]);
|
|
193
233
|
const glyph = (clear) => _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: clear ? _jsx("path", { d: "m6 6 12 12M18 6 6 18" }) : _jsx("path", { d: "m6 9 6 6 6-6" }) });
|
|
194
234
|
const inputProps = {
|
|
195
|
-
...props, id, type: "text", value: displayed,
|
|
235
|
+
...props, id, type: "text", value: displayed, placeholder: multiple && selections.length ? undefined : props.placeholder,
|
|
236
|
+
name: freeText ? name : undefined, disabled, readOnly, required: required && (!multiple || !selections.length), controlSize, autoComplete,
|
|
196
237
|
role: "combobox", "aria-autocomplete": "list", "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? menuId : undefined,
|
|
197
238
|
"aria-required": required || undefined, "aria-activedescendant": open && active ? optionId(active.value) : undefined,
|
|
198
239
|
"aria-describedby": mergeDescriptions(props["aria-describedby"], validation && validationId, multiple && Boolean(selections.length) && `${id}-selection`),
|
|
@@ -201,11 +242,14 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
201
242
|
if (inert())
|
|
202
243
|
return;
|
|
203
244
|
const text = event.currentTarget.value;
|
|
204
|
-
|
|
245
|
+
if (freeText)
|
|
246
|
+
publishText(text);
|
|
247
|
+
else
|
|
248
|
+
setQuery(text);
|
|
205
249
|
setActiveValue(undefined);
|
|
206
250
|
setOpen(true);
|
|
207
251
|
setValidation("");
|
|
208
|
-
if (!multiple && text === "" && selected)
|
|
252
|
+
if (!freeText && !multiple && text === "" && selected)
|
|
209
253
|
publish([]);
|
|
210
254
|
},
|
|
211
255
|
onClick: event => { onClick?.(event); if (!event.defaultPrevented)
|
|
@@ -255,25 +299,28 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
255
299
|
}
|
|
256
300
|
}
|
|
257
301
|
else if (event.key === "Enter" && open) {
|
|
258
|
-
|
|
302
|
+
if (!freeText || active)
|
|
303
|
+
event.preventDefault();
|
|
259
304
|
if (active)
|
|
260
305
|
select(active.value);
|
|
306
|
+
else
|
|
307
|
+
close();
|
|
261
308
|
}
|
|
262
309
|
else if (event.key === "Escape" && open) {
|
|
263
310
|
event.preventDefault();
|
|
264
311
|
event.stopPropagation();
|
|
265
312
|
close();
|
|
266
313
|
}
|
|
267
|
-
else if (event.key === "Tab")
|
|
314
|
+
else if (event.key === "Tab" && !(failed && onRetry && !event.shiftKey))
|
|
268
315
|
close();
|
|
269
316
|
},
|
|
270
317
|
};
|
|
271
318
|
const removable = selections.some(option => !option.disabled);
|
|
272
|
-
const actions = _jsxs("span", { className: "flood-combobox__actions", children: [clearable && _jsx(IconButton, { label: clearLabel, variant: "ghost", size: controlSize, className: !selected ? "flood-combobox__clear-empty" : undefined, disabled: disabled || readOnly || !selected || (multiple && !removable), "aria-hidden": !selected || undefined, tabIndex: -1, onPointerDown: event => event.preventDefault(), onClick: () => select(""), children: glyph(true) }), _jsx(IconButton, { label: toggleLabel, variant: "ghost", size: controlSize, disabled: disabled || readOnly, tabIndex: -1, "aria-expanded": open, "aria-controls": open ? menuId : undefined, onPointerDown: event => event.preventDefault(), onClick: () => { if (open)
|
|
319
|
+
const actions = _jsxs("span", { className: "flood-combobox__actions", children: [clearable && _jsx(IconButton, { label: clearLabel, variant: "ghost", size: controlSize, className: !(freeText ? text : selected) ? "flood-combobox__clear-empty" : undefined, disabled: disabled || readOnly || !(freeText ? text : selected) || (multiple && !removable), "aria-hidden": !(freeText ? text : selected) || undefined, tabIndex: -1, onPointerDown: event => event.preventDefault(), onClick: () => select(""), children: glyph(true) }), _jsx(IconButton, { label: toggleLabel, variant: "ghost", size: controlSize, disabled: disabled || readOnly, tabIndex: -1, "aria-expanded": open, "aria-controls": open ? menuId : undefined, onPointerDown: event => event.preventDefault(), onClick: () => { if (open)
|
|
273
320
|
close();
|
|
274
321
|
else
|
|
275
322
|
show(); input.current?.focus({ preventScroll: true }); }, children: glyph(false) })] });
|
|
276
|
-
return _jsxs("div", { className: multiple ? "flood-combobox flood-multi-select" : "flood-combobox", ref: root, hidden: props.hidden, dir: props.dir, lang: props.lang, children: [multiple ? _jsxs("div", { className: ["flood-input-group", "flood-multi-select__control", `flood-control--${controlSize}`, className].filter(Boolean).join(" "), style: props.style, "data-disabled": disabled || undefined, "data-readonly": readOnly || undefined, "data-status": resolveFieldStatus(props.status, props.invalid || Boolean(validation), props["aria-invalid"]), onClick: event => { if (event.target === event.currentTarget && !inert()) {
|
|
323
|
+
return _jsxs("div", { className: multiple ? "flood-combobox flood-multi-select" : "flood-combobox", ref: root, hidden: props.hidden, dir: props.dir, lang: props.lang, children: [multiple ? _jsxs("div", { className: ["flood-input-group", "flood-multi-select__control", `flood-control--${controlSize}`, className].filter(Boolean).join(" "), style: props.style, "data-disabled": disabled || undefined, "data-readonly": readOnly || undefined, "data-status": resolveFieldStatus(props.status, props.invalid || Boolean(validation), props["aria-invalid"]), "data-has-selection": Boolean(selections.length) || undefined, "data-query": Boolean(displayed) || undefined, "data-clearable": clearable || undefined, onClick: event => { if (event.target === event.currentTarget && !inert()) {
|
|
277
324
|
input.current?.focus();
|
|
278
325
|
show();
|
|
279
326
|
} }, children: [selections.map(option => _jsx(Tag, { size: controlSize === "sm" ? "sm" : controlSize === "md" ? "md" : "lg", disabled: disabled || option.disabled, onRemove: readOnly ? undefined : () => remove(option.value), removeLabel: `${removeItemLabel} ${option.label}`, removeButtonProps: { tabIndex: -1, onKeyDown: event => {
|
|
@@ -300,11 +347,12 @@ export const SearchSelection = /* @__PURE__ */ forwardRef(function SearchSelecti
|
|
|
300
347
|
event.preventDefault();
|
|
301
348
|
(buttons[Math.max(0, index - 1)])?.focus();
|
|
302
349
|
}
|
|
303
|
-
} }, children: option.label }, option.value)), _jsxs("div", { className: "flood-multi-select__editor", children: [_jsx(Input, { ...inputProps, ref: input, style: undefined }), actions] })] }) : _jsx(InputGroup, { ...inputProps, ref: input, className: ["flood-combobox__control", className].filter(Boolean).join(" "), end: actions }), (multiple ? selections : [selected]).map((option, index) => _jsx("input", { type: "hidden", name: name, value: option?.value ?? "", disabled: disabled, form: props.form }, option?.value ?? index)), multiple && Boolean(selections.length) && _jsx("span", { id: `${id}-selection`, className: "flood-visually-hidden", children: selections.map(option => option.label).join(", ") }), open &&
|
|
304
|
-
event.preventDefault(); }, children: _jsxs(ScrollArea, { ref: viewport, id: menuId, role: "listbox", "aria-label": listLabel, "aria-multiselectable": multiple || undefined, orientation: "vertical", tabIndex: -1, className: "flood-select__viewport", children: [!filtered.length && _jsx("div", { className: "flood-select__empty", role: "option", "aria-disabled": "true", "aria-selected": "false", children:
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
350
|
+
} }, children: option.label }, option.value)), _jsxs("div", { className: "flood-multi-select__editor", children: [_jsx(Input, { ...inputProps, ref: input, style: undefined }), actions] })] }) : _jsx(InputGroup, { ...inputProps, ref: input, className: ["flood-combobox__control", className].filter(Boolean).join(" "), end: actions }), !freeText && (multiple ? selections : [selected]).map((option, index) => _jsx("input", { type: "hidden", name: name, value: option?.value ?? "", disabled: disabled, form: props.form }, option?.value ?? index)), multiple && Boolean(selections.length) && _jsx("span", { id: `${id}-selection`, className: "flood-visually-hidden", children: selections.map(option => option.label).join(", ") }), open && _jsxs("div", { ref: panel, popover: "manual", className: "flood-select__menu flood-combobox__menu", onPointerDown: event => { if (event.pointerType === "mouse")
|
|
351
|
+
event.preventDefault(); }, children: [_jsxs(ScrollArea, { ref: viewport, id: menuId, role: "listbox", "aria-label": listLabel, "aria-busy": pending || undefined, "aria-multiselectable": multiple || undefined, orientation: "vertical", tabIndex: -1, className: "flood-select__viewport", children: [!filtered.length && _jsx("div", { className: "flood-select__empty", role: "option", "aria-disabled": "true", "aria-selected": "false", children: resultMessage }), groups.map((group, index) => _jsxs("div", { role: group.label ? "group" : "presentation", "aria-label": group.label, children: [group.label && _jsx("div", { className: "flood-select__group", "aria-hidden": "true", children: group.label }), group.options.map(option => _jsxs("div", { id: optionId(option.value), role: "option", "aria-selected": selectedIds.includes(option.value), "aria-disabled": option.disabled || undefined, className: "flood-select__option", "data-active": option.value === active?.value || undefined, onPointerMove: event => { if (event.pointerType === "mouse" && !option.disabled)
|
|
352
|
+
setActiveValue(option.value); }, onPointerDown: event => {
|
|
353
|
+
// Touch must retain its compatibility click and native scrolling.
|
|
354
|
+
if (event.pointerType === "mouse")
|
|
355
|
+
event.preventDefault();
|
|
356
|
+
}, onMouseDown: event => event.preventDefault(), onClick: () => select(option.value), children: [_jsxs("span", { className: "flood-combobox__option-text", children: [_jsx("span", { children: option.label }), option.description && _jsx("span", { className: "flood-combobox__description", children: option.description })] }), _jsx("span", { className: "flood-select__selected", "aria-hidden": "true", children: selectedIds.includes(option.value) ? "✓" : "" })] }, option.value))] }, index))] }), failed && onRetry && _jsx(Button, { variant: "subtle", size: "sm", onClick: () => { input.current?.focus({ preventScroll: true }); onRetry(); }, onBlur: event => { if (!root.current?.contains(event.relatedTarget))
|
|
357
|
+
close(); }, children: retryLabel })] }), open && !filtered.length && _jsx("span", { className: "flood-visually-hidden", role: "status", children: resultMessage }), validation && _jsx("span", { id: validationId, className: "flood-select__validation", role: "alert", children: validation })] });
|
|
310
358
|
});
|
package/dist/command-list.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import { type CommandListProps } from "./index.js";
|
|
2
|
+
interface SearchCommandListProps extends CommandListProps {
|
|
3
|
+
integrated?: boolean;
|
|
4
|
+
}
|
|
2
5
|
/** Searchable action suggestions. No selected form value or modal lifecycle. */
|
|
3
|
-
export declare const SearchCommandList: import("react").ForwardRefExoticComponent<
|
|
6
|
+
export declare const SearchCommandList: import("react").ForwardRefExoticComponent<SearchCommandListProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
7
|
+
export {};
|
package/dist/command-list.js
CHANGED
|
@@ -3,7 +3,7 @@ import { forwardRef, useEffect, useId, useImperativeHandle, useRef, useState } f
|
|
|
3
3
|
import { Input, Label, ScrollArea, Surface, Text, VisuallyHidden } from "./index.js";
|
|
4
4
|
const searchable = (value) => value.normalize("NFKD").replace(/\p{M}/gu, "").toLowerCase();
|
|
5
5
|
/** Searchable action suggestions. No selected form value or modal lifecycle. */
|
|
6
|
-
export const SearchCommandList = /* @__PURE__ */ forwardRef(function SearchCommandList({ label, hideLabel = false, items, query, defaultQuery = "", onQueryChange, onAction, disabled = false, loading = false, placeholder = "Search commands…", emptyMessage = "No commands found.", loadingMessage = "Loading commands…", controlSize = "md", id: explicitId, className, onKeyDown, ...props }, forwardedRef) {
|
|
6
|
+
export const SearchCommandList = /* @__PURE__ */ forwardRef(function SearchCommandList({ label, hideLabel = false, items, query, defaultQuery = "", onQueryChange, onAction, disabled = false, loading = false, placeholder = "Search commands…", emptyMessage = "No commands found.", loadingMessage = "Loading commands…", controlSize = "md", integrated = false, id: explicitId, className, onKeyDown, ...props }, forwardedRef) {
|
|
7
7
|
if (![label, emptyMessage, loadingMessage].every(value => value.trim()))
|
|
8
8
|
throw new Error("Flood CommandList requires nonempty accessible labels and state messages");
|
|
9
9
|
if (new Set(items.map(item => item.id)).size !== items.length || items.some(item => !item.id.trim() || !item.label.trim()))
|
|
@@ -84,7 +84,7 @@ export const SearchCommandList = /* @__PURE__ */ forwardRef(function SearchComma
|
|
|
84
84
|
setActiveId(enabled[next].id);
|
|
85
85
|
}, children: [_jsx(Label, { htmlFor: inputId, className: hideLabel ? "flood-visually-hidden" : undefined, children: label }), _jsx(Input, { ref: input, id: inputId, value: text, disabled: disabled, type: "text", controlSize: controlSize, placeholder: placeholder, autoComplete: "off", spellCheck: false, role: "combobox", "aria-autocomplete": "list", "aria-haspopup": "listbox", "aria-expanded": "true", "aria-controls": listId, "aria-activedescendant": active ? optionId(active) : undefined, onCompositionStart: () => { composing.current = true; }, onCompositionEnd: () => { composing.current = false; }, onChange: event => { const next = event.currentTarget.value; if (disabled || event.currentTarget.matches(":disabled"))
|
|
86
86
|
return; if (query === undefined)
|
|
87
|
-
setLocalQuery(next); onQueryChange?.(next); } }), _jsx(Surface, { padding: "1", className: "flood-command-list__surface", children: _jsx(ScrollArea, { ref: viewport, id: listId, role: "listbox", "aria-label": label, "aria-busy": loading || undefined, "aria-disabled": effectiveDisabled || undefined, tabIndex: -1, className: "flood-command-list__viewport",
|
|
87
|
+
setLocalQuery(next); onQueryChange?.(next); } }), _jsx(Surface, { elevation: integrated ? "overlay" : "default", padding: integrated ? "2" : "1", style: integrated ? { background: "transparent" } : undefined, className: "flood-command-list__surface", children: _jsx(ScrollArea, { ref: viewport, id: listId, role: "listbox", "aria-label": label, "aria-busy": loading || undefined, "aria-disabled": effectiveDisabled || undefined, tabIndex: -1, className: "flood-command-list__viewport", children: status ? _jsx("div", { role: "option", "aria-disabled": "true", "aria-selected": "false", className: "flood-command-list__empty", children: _jsx(Text, { size: "compact", tone: "subtle", children: status }) }) : [...groups].map(([group, entries], groupIndex) => {
|
|
88
88
|
const content = entries.map(item => _jsxs("div", { id: optionId(item), role: "option", "aria-disabled": item.disabled || effectiveDisabled || undefined, "aria-selected": item.id === active?.id, "data-active": item.id === active?.id || undefined, className: "flood-command-list__option", onPointerMove: event => { if (event.pointerType === "mouse" && !blocked() && !item.disabled)
|
|
89
89
|
setActiveId(item.id); }, onPointerDown: event => { if (event.pointerType === "mouse")
|
|
90
90
|
event.preventDefault(); }, onMouseDown: event => event.preventDefault(), onClick: () => invoke(item), children: [_jsx("span", { className: "flood-command-list__label", children: item.label }), item.description && _jsx("span", { className: "flood-command-list__description", children: item.description })] }, item.id));
|
package/dist/data-display.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef } from "react";
|
|
3
3
|
import { ScrollArea } from "./index.js";
|
|
4
4
|
export const DescribedData = /* @__PURE__ */ forwardRef(function DescribedData({ items, layout, density, dividers, className, ...props }, ref) {
|
|
5
|
-
if (items.some(item => !item.id.trim()) || new Set(items.map(item => item.id)).size !== items.length)
|
|
5
|
+
if (items.some(item => typeof item.id !== "string" || !item.id.trim()) || new Set(items.map(item => item.id)).size !== items.length)
|
|
6
6
|
throw new Error("Flood description items require unique nonempty IDs");
|
|
7
7
|
return _jsx("dl", { ...props, ref: ref, className: ["flood-description-list", className].filter(Boolean).join(" "), "data-layout": layout, "data-density": density, "data-dividers": dividers, children: items.map(item => _jsxs("div", { className: "flood-description-list__item", children: [_jsx("dt", { children: item.term }), _jsx("dd", { children: item.description })] }, item.id)) });
|
|
8
8
|
});
|
package/dist/data-table.js
CHANGED
|
@@ -1,24 +1,54 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useMemo, useState } from "react";
|
|
3
3
|
import { Table, Checkbox, Button, IconSlot, VisuallyHidden, Pagination, Text, Spinner, Alert, Stack } from "./index.js";
|
|
4
|
-
|
|
4
|
+
function activatePrimaryRowAction(event) {
|
|
5
|
+
const target = event.target;
|
|
6
|
+
const row = event.currentTarget;
|
|
7
|
+
if (!(target instanceof Element) || target.closest("tr") !== row || event.defaultPrevented ||
|
|
8
|
+
event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
|
|
9
|
+
return;
|
|
10
|
+
const control = target.closest('button,a,input,label,select,textarea,summary,[role],[tabindex],[contenteditable]:not([contenteditable="false"])');
|
|
11
|
+
if (control && row.contains(control))
|
|
12
|
+
return;
|
|
13
|
+
const selection = row.ownerDocument.getSelection();
|
|
14
|
+
if (selection && !selection.isCollapsed && selection.containsNode(row, true))
|
|
15
|
+
return;
|
|
16
|
+
const button = row.querySelector(":scope > td > .flood-stack > .flood-data-table__primary-action");
|
|
17
|
+
if (!button || button.disabled || button.getAttribute("aria-disabled") === "true")
|
|
18
|
+
return;
|
|
19
|
+
button.focus({ preventScroll: true });
|
|
20
|
+
button.click();
|
|
21
|
+
}
|
|
22
|
+
export const ManagedDataTable = /* @__PURE__ */ forwardRef(function ManagedDataTable({ caption, hideCaption = false, columns, rows, density = "comfortable", striped = false, minWidth = "40rem", toolbar, inset = "standard", sort: controlledSort, defaultSort = null, onSortChange, locale = "en", selectable = false, selectedIds, defaultSelectedIds = [], onSelectionChange, page: controlledPage, defaultPage = 1, pageSize = 10, onPageChange, showResults = true, paginationMode = "client", totalRows, selectionScope, state = "ready", onRetry, emptyContent, labels = {}, className, ...props }, ref) {
|
|
5
23
|
if (!caption.trim() || columns.length === 0 || columns.some(column => !column.id.trim() || !column.label.trim()) || new Set(columns.map(column => column.id)).size !== columns.length)
|
|
6
24
|
throw new Error("Flood DataTable requires a caption and unique named columns");
|
|
7
25
|
if (rows.some(row => !row.id.trim() || (row.label !== undefined && !row.label.trim())) || new Set(rows.map(row => row.id)).size !== rows.length)
|
|
8
26
|
throw new Error("Flood DataTable rows require unique nonempty IDs and labels");
|
|
27
|
+
if (rows.some(({ primaryAction }) => primaryAction && (typeof primaryAction.title !== "string" || !primaryAction.title.trim() ||
|
|
28
|
+
!columns.some(column => column.id === primaryAction.column))))
|
|
29
|
+
throw new Error("Flood DataTable primary actions require a nonempty title and an existing column");
|
|
9
30
|
if (!Number.isSafeInteger(pageSize) || pageSize < 1 || !Number.isSafeInteger(defaultPage) || defaultPage < 1 || (controlledPage !== undefined && (!Number.isSafeInteger(controlledPage) || controlledPage < 1)))
|
|
10
31
|
throw new Error("Flood DataTable page and pageSize must be positive safe integers");
|
|
32
|
+
const server = paginationMode === "server", scope = selectionScope ?? (server ? "page" : "dataset");
|
|
33
|
+
if (server && (controlledPage === undefined || !onPageChange || !Number.isSafeInteger(totalRows) || (totalRows ?? -1) < 0))
|
|
34
|
+
throw new Error("Flood server DataTable requires page, onPageChange and a nonnegative totalRows");
|
|
35
|
+
if (server && columns.some(column => column.sortable) && (controlledSort === undefined || !onSortChange))
|
|
36
|
+
throw new Error("Flood server sorting requires sort (or null) and onSortChange");
|
|
37
|
+
if (server && selectable && scope === "dataset" && (selectedIds === undefined || !onSelectionChange))
|
|
38
|
+
throw new Error("Flood server dataset selection must be controlled");
|
|
11
39
|
const [localSort, setSort] = useState(defaultSort), [localSelected, setSelected] = useState(defaultSelectedIds), [localPage, setPage] = useState(defaultPage);
|
|
12
40
|
const requestedSort = controlledSort === undefined ? localSort : controlledSort;
|
|
13
41
|
const sorting = requestedSort && columns.some(column => column.id === requestedSort.column && column.sortable) ? requestedSort : null;
|
|
14
42
|
const sorted = useMemo(() => {
|
|
15
|
-
if (!sorting)
|
|
43
|
+
if (server || !sorting)
|
|
16
44
|
return [...rows];
|
|
17
45
|
const collator = new Intl.Collator(locale, { numeric: true, sensitivity: "base" });
|
|
18
46
|
const value = (row) => {
|
|
19
47
|
const explicit = row.sortValues?.[sorting.column];
|
|
20
48
|
if (explicit !== undefined)
|
|
21
49
|
return explicit;
|
|
50
|
+
if (row.primaryAction?.column === sorting.column)
|
|
51
|
+
return row.primaryAction.title;
|
|
22
52
|
const cell = row.cells[sorting.column];
|
|
23
53
|
return typeof cell === "string" || typeof cell === "number" ? cell : null;
|
|
24
54
|
};
|
|
@@ -30,14 +60,17 @@ export const ManagedDataTable = /* @__PURE__ */ forwardRef(function ManagedDataT
|
|
|
30
60
|
const comparison = typeof left === "number" && typeof right === "number" ? left - right : collator.compare(String(left), String(right));
|
|
31
61
|
return comparison === 0 ? a.index - b.index : comparison * (sorting.direction === "ascending" ? 1 : -1);
|
|
32
62
|
}).map(entry => entry.row);
|
|
33
|
-
}, [rows, sorting?.column, sorting?.direction, locale]);
|
|
34
|
-
const pageCount = Math.max(1, Math.ceil(
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
}, [rows, sorting?.column, sorting?.direction, locale, server]);
|
|
64
|
+
const total = server ? totalRows : rows.length, pageCount = Math.max(1, Math.ceil(total / pageSize)), page = server ? controlledPage : Math.min(controlledPage ?? localPage, pageCount);
|
|
65
|
+
if (server && state === "ready" && (page > pageCount || rows.length > pageSize || (page - 1) * pageSize + rows.length > total))
|
|
66
|
+
throw new Error("Flood server rows and page must match totalRows/pageSize; reconcile the page after a query change");
|
|
67
|
+
const visible = server ? sorted : sorted.slice((page - 1) * pageSize, page * pageSize), eligible = new Set((scope === "page" ? visible : rows).filter(row => !row.selectionDisabled).map(row => row.id));
|
|
68
|
+
const accepts = (id) => server && scope === "dataset" ? !rows.some(row => row.id === id && row.selectionDisabled) : eligible.has(id);
|
|
69
|
+
const selection = [...new Set(selectedIds ?? localSelected)].filter(accepts), chosen = new Set(selection);
|
|
37
70
|
const pageChoices = visible.filter(row => !row.selectionDisabled), all = pageChoices.length > 0 && pageChoices.every(row => chosen.has(row.id)), some = pageChoices.some(row => chosen.has(row.id));
|
|
38
71
|
const blocked = state !== "ready";
|
|
39
72
|
const select = (next) => { if (blocked)
|
|
40
|
-
return; const accepted = [...new Set(next)].filter(
|
|
73
|
+
return; const accepted = [...new Set(next)].filter(accepts); if (selectedIds === undefined)
|
|
41
74
|
setSelected(accepted); onSelectionChange?.(accepted); };
|
|
42
75
|
const changePage = (next) => { if (blocked)
|
|
43
76
|
return; if (controlledPage === undefined)
|
|
@@ -53,6 +86,8 @@ export const ManagedDataTable = /* @__PURE__ */ forwardRef(function ManagedDataT
|
|
|
53
86
|
changePage(1);
|
|
54
87
|
};
|
|
55
88
|
const resultLabel = labels.results ?? ((from, to, total) => `${from}–${to} of ${total}`);
|
|
56
|
-
return _jsxs("div", { ...props, ref: ref, className: ["flood-data-table", className].filter(Boolean).join(" "), "aria-busy": state === "loading" || undefined, children: [_jsxs(Table, { caption: caption, hideCaption: hideCaption, density: density, striped: striped, style: { minWidth }, children: [_jsx("thead", { children: _jsxs("tr", { children: [selectable && _jsx("th", { scope: "col", className: "flood-data-table__choice", children: _jsx(Checkbox, { label: _jsx(VisuallyHidden, { children: labels.selectPage ?? "Select rows on this page" }), checked: all, indeterminate: some && !all, disabled: blocked || pageChoices.length === 0, onChange: event => { const ids = new Set(selection); for (const row of pageChoices)
|
|
57
|
-
event.currentTarget.checked ? ids.add(row.id) : ids.delete(row.id); select([...ids]); } }) }), columns.map(column => _jsx("th", { scope: "col", "data-sortable": column.sortable || undefined, "aria-sort": sorting?.column === column.id ? sorting.direction : undefined, style: {
|
|
89
|
+
return _jsxs("div", { ...props, ref: ref, className: ["flood-data-table", className].filter(Boolean).join(" "), "data-inset": inset, "aria-busy": state === "loading" || undefined, children: [toolbar != null && _jsx("div", { className: "flood-data-table__toolbar", children: toolbar }), _jsxs(Table, { caption: caption, hideCaption: hideCaption, density: density, striped: striped, style: { minWidth }, children: [_jsx("thead", { children: _jsxs("tr", { children: [selectable && _jsx("th", { scope: "col", className: "flood-data-table__choice", children: _jsx(Checkbox, { label: _jsx(VisuallyHidden, { children: labels.selectPage ?? "Select rows on this page" }), checked: all, indeterminate: some && !all, disabled: blocked || pageChoices.length === 0, onChange: event => { const ids = new Set(selection); for (const row of pageChoices)
|
|
90
|
+
event.currentTarget.checked ? ids.add(row.id) : ids.delete(row.id); select([...ids]); } }) }), columns.map(column => _jsx("th", { scope: "col", "data-numeric": column.numeric || undefined, "data-align": column.align, "data-sortable": column.sortable || undefined, "aria-sort": sorting?.column === column.id ? sorting.direction : undefined, style: { width: column.width }, children: column.sortable ? _jsxs(Button, { variant: "ghost", size: "sm", disabled: blocked, className: "flood-data-table__sort", "aria-label": (labels.sort ?? ((label) => `Sort by ${label}`))(column.label), onClick: () => changeSort(column.id), children: [column.label, _jsx(IconSlot, { size: "sm", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: sorting?.column === column.id ? _jsx("path", { d: "m7 14 5-5 5 5" }) : _jsx("path", { d: "m8 8 4-4 4 4M8 16l4 4 4-4" }) }) })] }) : column.label }, column.id))] }) }), _jsx("tbody", { children: state === "ready" && visible.length > 0 ? visible.map(row => _jsxs("tr", { "data-selected": selectable && chosen.has(row.id) || undefined, "data-primary-action": row.primaryAction && !row.primaryAction.buttonProps.disabled && row.primaryAction.buttonProps["aria-disabled"] !== "true" || undefined, onClick: row.primaryAction ? activatePrimaryRowAction : undefined, children: [selectable && _jsx("td", { className: "flood-data-table__choice", children: _jsx(Checkbox, { label: _jsx(VisuallyHidden, { children: (labels.selectRow ?? ((label) => `Select ${label}`))(row.label ?? row.id) }), checked: chosen.has(row.id), disabled: Boolean(row.selectionDisabled), onChange: event => select(event.currentTarget.checked ? [...selection, row.id] : selection.filter(id => id !== row.id)) }) }), columns.map(column => _jsx("td", { "data-numeric": column.numeric || undefined, "data-align": column.align, children: row.primaryAction?.column === column.id
|
|
91
|
+
? _jsxs(Stack, { gap: "1", children: [_jsx(Button, { ...row.primaryAction.buttonProps, type: "button", variant: "ghost", size: "sm", className: ["flood-data-table__primary-action", row.primaryAction.buttonProps.className].filter(Boolean).join(" "), children: row.primaryAction.title }), row.primaryAction.description != null && _jsx(Text, { as: "div", size: "helper", tone: "subtle", children: row.primaryAction.description })] })
|
|
92
|
+
: row.cells[column.id] ?? null }, column.id))] }, row.id)) : _jsx("tr", { children: _jsx("td", { colSpan: columns.length + (selectable ? 1 : 0), className: "flood-data-table__state", children: state === "loading" ? _jsx(Spinner, { label: labels.loading ?? "Loading rows…" }) : state === "error" ? _jsxs(Stack, { gap: "3", children: [_jsx(Alert, { tone: "error", announcement: "polite", children: labels.error ?? "Could not load the rows." }), onRetry && _jsx(Button, { variant: "subtle", size: "sm", onClick: onRetry, children: labels.retry ?? "Try again" })] }) : emptyContent ?? _jsx(Text, { as: "p", size: "compact", tone: "subtle", role: "status", children: labels.empty ?? "No results found." }) }) }) })] }), (showResults || selectable || pageCount > 1) && _jsxs("div", { className: "flood-data-table__footer", children: [showResults && _jsx(Text, { as: "p", size: "helper", tone: "subtle", children: state === "ready" ? resultLabel(visible.length ? (page - 1) * pageSize + 1 : 0, visible.length ? (page - 1) * pageSize + visible.length : 0, total) : "" }), selectable && _jsx(Text, { as: "p", size: "helper", tone: "subtle", role: "status", children: (labels.selection ?? ((count, total) => `${count} of ${total} selected`))(selection.length, server && scope === "dataset" ? total : eligible.size) }), pageCount > 1 && _jsx(Pagination, { page: Math.min(page, pageCount), pageCount: pageCount, onPageChange: changePage, disabled: blocked, label: labels.pagination, previousLabel: labels.previousPage, nextLabel: labels.nextPage, pageLabel: labels.page })] })] });
|
|
58
93
|
});
|
package/dist/date-picker.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Calendar, DateField, IconButton, Popover } from "./index.js";
|
|
|
4
4
|
const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
5
5
|
const triggerSizes = { sm: "xs", md: "sm", lg: "md", xl: "lg" };
|
|
6
6
|
/** Calendar selection and keyboard date entry share one DateField value/form bridge. */
|
|
7
|
-
export const CalendarDatePicker = /* @__PURE__ */ forwardRef(function CalendarDatePicker({ value, defaultValue = "", onValueChange, open: controlledOpen, defaultOpen = false, onOpenChange, calendarLabel, openCalendarLabel, locale, weekStartsOn, today, previousMonthLabel, nextMonthLabel, label, disabled = false, readOnly = false, controlSize = "md", min, max, form, className, onKeyDown, ...props }, forwardedRef) {
|
|
7
|
+
export const CalendarDatePicker = /* @__PURE__ */ forwardRef(function CalendarDatePicker({ value, defaultValue = "", onValueChange, open: controlledOpen, defaultOpen = false, onOpenChange, calendarLabel, openCalendarLabel, viewLabels, locale, weekStartsOn, today, previousMonthLabel, nextMonthLabel, label, disabled = false, readOnly = false, controlSize = "md", min, max, form, className, onKeyDown, ...props }, forwardedRef) {
|
|
8
8
|
const panelLabel = calendarLabel ?? `${label} calendar`;
|
|
9
9
|
const triggerLabel = openCalendarLabel ?? `Open calendar: ${label}`;
|
|
10
10
|
if (!panelLabel.trim() || !triggerLabel.trim())
|
|
@@ -100,7 +100,7 @@ export const CalendarDatePicker = /* @__PURE__ */ forwardRef(function CalendarDa
|
|
|
100
100
|
event.preventDefault();
|
|
101
101
|
requestOpen(true);
|
|
102
102
|
}
|
|
103
|
-
}, end: _jsx(Popover, { title: panelLabel, className: "flood-date-picker__panel", align: "end", positionReference: positionReference, style: { "--flood-date-picker-width": fieldWidth === undefined ? undefined : `${fieldWidth}px` }, open: !blocked && requestedOpen, onOpenChange: requestOpen, disabled: blocked, trigger: trigger => _jsx(IconButton, { ...trigger, label: triggerLabel, variant: "ghost", size: triggerSizes[controlSize], children: _jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "3", y: "5", width: "18", height: "16", rx: "2" }), _jsx("path", { d: "M16 3v4M8 3v4M3 11h18" })] }) }), children: _jsx(Calendar, { label: panelLabel, value: committed, min: min, max: max, locale: locale, weekStartsOn: weekStartsOn, today: today, previousMonthLabel: previousMonthLabel, nextMonthLabel: nextMonthLabel, autoFocus: true, onValueChange: changeDate, onDateSelect: () => {
|
|
103
|
+
}, end: _jsx(Popover, { title: panelLabel, chrome: "minimal", className: "flood-date-picker__panel", align: "end", positionReference: positionReference, style: { "--flood-date-picker-width": fieldWidth === undefined ? undefined : `${fieldWidth}px` }, open: !blocked && requestedOpen, onOpenChange: requestOpen, disabled: blocked, trigger: trigger => _jsx(IconButton, { ...trigger, label: triggerLabel, variant: "ghost", size: triggerSizes[controlSize], children: _jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "3", y: "5", width: "18", height: "16", rx: "2" }), _jsx("path", { d: "M16 3v4M8 3v4M3 11h18" })] }) }), children: _jsx(Calendar, { label: panelLabel, viewLabels: viewLabels, value: committed, min: min, max: max, locale: locale, weekStartsOn: weekStartsOn, today: today, previousMonthLabel: previousMonthLabel, nextMonthLabel: nextMonthLabel, autoFocus: true, onValueChange: changeDate, onDateSelect: () => {
|
|
104
104
|
if (blocked || nativeDisabled())
|
|
105
105
|
return;
|
|
106
106
|
requestOpen(false);
|
|
@@ -4,7 +4,7 @@ import { DateRangeField, IconButton, Popover, RangeCalendar } from "./index.js";
|
|
|
4
4
|
import { emptyDateRange } from "./date-range-value.js";
|
|
5
5
|
const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
6
6
|
const triggerSizes = { sm: "xs", md: "sm", lg: "md", xl: "lg" };
|
|
7
|
-
export const CalendarDateRangePicker = /* @__PURE__ */ forwardRef(function CalendarDateRangePicker({ value, defaultValue = emptyDateRange(), onValueChange, open: controlledOpen, defaultOpen = false, onOpenChange, label, calendarLabel, openCalendarLabel, locale, weekStartsOn, today, previousMonthLabel, nextMonthLabel, rangeLabels, disabled = false, readOnly = false, controlSize = "md", min, max, isDateDisabled, isRangeDisabled, form, className, onKeyDown, ...props }, forwardedRef) {
|
|
7
|
+
export const CalendarDateRangePicker = /* @__PURE__ */ forwardRef(function CalendarDateRangePicker({ value, defaultValue = emptyDateRange(), onValueChange, open: controlledOpen, defaultOpen = false, onOpenChange, label, calendarLabel, openCalendarLabel, viewLabels, locale, weekStartsOn, today, previousMonthLabel, nextMonthLabel, rangeLabels, disabled = false, readOnly = false, controlSize = "md", min, max, isDateDisabled, isRangeDisabled, form, className, onKeyDown, ...props }, forwardedRef) {
|
|
8
8
|
const panelLabel = calendarLabel ?? `${label} calendar`, triggerLabel = openCalendarLabel ?? `Open calendar: ${label}`;
|
|
9
9
|
if (!panelLabel.trim() || !triggerLabel.trim())
|
|
10
10
|
throw new Error("Flood DateRangePicker requires nonempty calendar and trigger labels");
|
|
@@ -76,6 +76,6 @@ export const CalendarDateRangePicker = /* @__PURE__ */ forwardRef(function Calen
|
|
|
76
76
|
event.preventDefault();
|
|
77
77
|
requestOpen(true);
|
|
78
78
|
}
|
|
79
|
-
}, end: _jsx(Popover, { title: panelLabel, className: "flood-date-range-picker__panel", align: "end", positionReference: positionReference, style: { "--flood-date-range-picker-width": fieldWidth ? `${fieldWidth}px` : undefined }, open: !blocked && requestedOpen, onOpenChange: requestOpen, disabled: blocked, trigger: trigger => _jsx(IconButton, { ...trigger, label: triggerLabel, variant: "ghost", size: triggerSizes[controlSize], children: _jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "3", y: "5", width: "18", height: "16", rx: "2" }), _jsx("path", { d: "M16 3v4M8 3v4M3 11h18" })] }) }), children: _jsx(RangeCalendar, { label: panelLabel, value: committed, min: min, max: max, isDateDisabled: isDateDisabled, isRangeDisabled: isRangeDisabled, locale: locale, weekStartsOn: weekStartsOn, today: today, previousMonthLabel: previousMonthLabel, nextMonthLabel: nextMonthLabel, rangeLabels: rangeLabels, autoFocus: true, onValueChange: change, onRangeSelect: () => { if (!blocked && !nativeDisabled())
|
|
79
|
+
}, end: _jsx(Popover, { title: panelLabel, chrome: "minimal", className: "flood-date-range-picker__panel", align: "end", positionReference: positionReference, style: { "--flood-date-range-picker-width": fieldWidth ? `${fieldWidth}px` : undefined }, open: !blocked && requestedOpen, onOpenChange: requestOpen, disabled: blocked, trigger: trigger => _jsx(IconButton, { ...trigger, label: triggerLabel, variant: "ghost", size: triggerSizes[controlSize], children: _jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("rect", { x: "3", y: "5", width: "18", height: "16", rx: "2" }), _jsx("path", { d: "M16 3v4M8 3v4M3 11h18" })] }) }), children: _jsx(RangeCalendar, { label: panelLabel, viewLabels: viewLabels, value: committed, min: min, max: max, isDateDisabled: isDateDisabled, isRangeDisabled: isRangeDisabled, locale: locale, weekStartsOn: weekStartsOn, today: today, previousMonthLabel: previousMonthLabel, nextMonthLabel: nextMonthLabel, rangeLabels: rangeLabels, autoFocus: true, onValueChange: change, onRangeSelect: () => { if (!blocked && !nativeDisabled())
|
|
80
80
|
requestOpen(false); } }) }) });
|
|
81
81
|
});
|
package/dist/date-time-picker.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
3
3
|
import { DatePicker, TimePicker, Fieldset } from "./index.js";
|
|
4
4
|
/** A civil date and wall-clock time. The host owns timezone conversion. */
|
|
5
|
-
export const CivilDateTimePicker = /* @__PURE__ */ forwardRef(function CivilDateTimePicker({ label, validationMessage, timeInterval, hideLabel = false, hideFieldLabels = false, value, defaultValue = { date: "", time: "" }, onValueChange, dateLabel = "Date", timeLabel = "Time", dateName, timeName, form, required = false, disabled = false, readOnly = false, controlSize = "md", precision = "minute", order = "month-day-year", locale = "en-US", minDate, maxDate, minTime, maxTime, step, status, description, className, ...props }, ref) {
|
|
5
|
+
export const CivilDateTimePicker = /* @__PURE__ */ forwardRef(function CivilDateTimePicker({ label, validationMessage, timeInterval, hideLabel = false, hideFieldLabels = false, value, defaultValue = { date: "", time: "" }, onValueChange, dateLabel = "Date", timeLabel = "Time", datePickerLabels, timePickerLabels, dateName, timeName, form, required = false, disabled = false, readOnly = false, controlSize = "md", precision = "minute", order = "month-day-year", locale = "en-US", minDate, maxDate, minTime, maxTime, step, status, description, className, ...props }, ref) {
|
|
6
6
|
const [local, setLocal] = useState(defaultValue);
|
|
7
7
|
const root = useRef(null), initial = useRef(defaultValue);
|
|
8
8
|
useImperativeHandle(ref, () => root.current, []);
|
|
@@ -27,5 +27,5 @@ export const CivilDateTimePicker = /* @__PURE__ */ forwardRef(function CivilDate
|
|
|
27
27
|
};
|
|
28
28
|
// Each field owns native validity, reset and submission. Values are deliberately
|
|
29
29
|
// separate: neither a timezone nor an instant is inferred from this pair.
|
|
30
|
-
return _jsx("div", { ...props, ref: root, className: ["flood-date-time-picker", className].filter(Boolean).join(" "), children: _jsx(Fieldset, { legend: label, hideLabel: hideLabel, hint: description, disabled: disabled, children: _jsxs("div", { className: "flood-date-time-picker__fields", children: [_jsx(DatePicker, { label: dateLabel, hideLabel: hideFieldLabels, value: value === undefined ? undefined : selected.date, defaultValue: defaultValue.date, onValueChange: next => change("date", next), name: dateName, form: form, required: required, disabled: disabled, readOnly: readOnly, controlSize: controlSize, order: order, locale: locale, min: minDate, max: maxDate, status: status, validationMessage: validationMessage }), _jsx(TimePicker, { label: timeLabel, hideLabel: hideFieldLabels, value: value === undefined ? undefined : selected.time, defaultValue: defaultValue.time, onValueChange: next => change("time", next), name: timeName, form: form, required: required, disabled: disabled, readOnly: readOnly, controlSize: controlSize, precision: precision, min: minTime, max: maxTime, step: step, interval: timeInterval, status: status })] }) }) });
|
|
30
|
+
return _jsx("div", { ...props, ref: root, className: ["flood-date-time-picker", className].filter(Boolean).join(" "), children: _jsx(Fieldset, { legend: label, hideLabel: hideLabel, hint: description, disabled: disabled, children: _jsxs("div", { className: "flood-date-time-picker__fields", children: [_jsx(DatePicker, { ...datePickerLabels, label: dateLabel, hideLabel: hideFieldLabels, value: value === undefined ? undefined : selected.date, defaultValue: defaultValue.date, onValueChange: next => change("date", next), name: dateName, form: form, required: required, disabled: disabled, readOnly: readOnly, controlSize: controlSize, order: order, locale: locale, min: minDate, max: maxDate, status: status, validationMessage: validationMessage }), _jsx(TimePicker, { ...timePickerLabels, label: timeLabel, hideLabel: hideFieldLabels, value: value === undefined ? undefined : selected.time, defaultValue: defaultValue.time, onValueChange: next => change("time", next), name: timeName, form: form, required: required, disabled: disabled, readOnly: readOnly, controlSize: controlSize, precision: precision, min: minTime, max: maxTime, step: step, interval: timeInterval, status: status })] }) }) });
|
|
31
31
|
});
|
|
@@ -6,7 +6,7 @@ import { parseTime, timeOnStep } from "./time-value.js";
|
|
|
6
6
|
const empty = () => ({ start: { date: "", time: "" }, end: { date: "", time: "" } });
|
|
7
7
|
const copy = (value) => ({ start: { ...value.start }, end: { ...value.end } });
|
|
8
8
|
const signature = (value) => [value.start.date, value.start.time, value.end.date, value.end.time].join("|");
|
|
9
|
-
export const CivilDateTimeRangePicker = /* @__PURE__ */ forwardRef(function CivilDateTimeRangePicker({ label, hideLabel = false, value, defaultValue = empty(), onValueChange, startDateName, startTimeName, endDateName, endTimeName, startLabel = "Start", endLabel = "End", startDateLabel = "Start date", startTimeLabel = "Start time", endDateLabel = "End date", endTimeLabel = "End time", form, required = false, disabled = false, readOnly = false, controlSize = "md", precision = "minute", order = "month-day-year", locale = "en-US", minDate, maxDate, step: explicitStep, interval, status, description, incompleteMessage = "Complete both dates and times.", reversedMessage = "End must not precede start.", className, onBlur, ...props }, ref) {
|
|
9
|
+
export const CivilDateTimeRangePicker = /* @__PURE__ */ forwardRef(function CivilDateTimeRangePicker({ label, hideLabel = false, value, defaultValue = empty(), onValueChange, startDateName, startTimeName, endDateName, endTimeName, startLabel = "Start", endLabel = "End", startDateLabel = "Start date", startTimeLabel = "Start time", endDateLabel = "End date", endTimeLabel = "End time", startDatePickerLabels, endDatePickerLabels, startTimePickerLabels, endTimePickerLabels, form, required = false, disabled = false, readOnly = false, controlSize = "md", precision = "minute", order = "month-day-year", locale = "en-US", minDate, maxDate, step: explicitStep, interval, status, description, incompleteMessage = "Complete both dates and times.", reversedMessage = "End must not precede start.", className, onBlur, ...props }, ref) {
|
|
10
10
|
if (![label, startLabel, endLabel, startDateLabel, startTimeLabel, endDateLabel, endTimeLabel, incompleteMessage, reversedMessage].every(text => text.trim()))
|
|
11
11
|
throw new Error("Flood DateTimeRangePicker requires nonempty labels and messages");
|
|
12
12
|
const initial = useRef(copy(defaultValue)), [local, setLocal] = useState(() => copy(defaultValue)), [proposal, setProposal] = useState();
|
|
@@ -47,5 +47,5 @@ export const CivilDateTimeRangePicker = /* @__PURE__ */ forwardRef(function Civi
|
|
|
47
47
|
onBlur?.(event);
|
|
48
48
|
if (!event.defaultPrevented && !event.currentTarget.contains(event.relatedTarget))
|
|
49
49
|
setProposal(undefined);
|
|
50
|
-
}, children: [_jsx(Fieldset, { legend: label, hideLabel: hideLabel, hint: description, disabled: disabled, children: _jsxs(Stack, { gap: "4", children: [_jsx(DateTimePicker, { ...shared, label: startLabel, dateLabel: startDateLabel, timeLabel: startTimeLabel, value: selected.start, defaultValue: initial.current.start, onValueChange: next => change("start", next), validationMessage: constraint }), _jsx(DateTimePicker, { ...shared, label: endLabel, dateLabel: endDateLabel, timeLabel: endTimeLabel, value: selected.end, defaultValue: initial.current.end, onValueChange: next => change("end", next) })] }) }), [[startDateName, selected.start.date], [startTimeName, selected.start.time], [endDateName, selected.end.date], [endTimeName, selected.end.time]].map(([name, part], index) => name && _jsx("input", { type: "hidden", name: name, form: form, value: accepted ? part : "", disabled: disabled }, index))] });
|
|
50
|
+
}, children: [_jsx(Fieldset, { legend: label, hideLabel: hideLabel, hint: description, disabled: disabled, children: _jsxs(Stack, { gap: "4", children: [_jsx(DateTimePicker, { ...shared, datePickerLabels: startDatePickerLabels, timePickerLabels: startTimePickerLabels, label: startLabel, dateLabel: startDateLabel, timeLabel: startTimeLabel, value: selected.start, defaultValue: initial.current.start, onValueChange: next => change("start", next), validationMessage: constraint }), _jsx(DateTimePicker, { ...shared, datePickerLabels: endDatePickerLabels, timePickerLabels: endTimePickerLabels, label: endLabel, dateLabel: endDateLabel, timeLabel: endTimeLabel, value: selected.end, defaultValue: initial.current.end, onValueChange: next => change("end", next) })] }) }), [[startDateName, selected.start.date], [startTimeName, selected.start.time], [endDateName, selected.end.date], [endTimeName, selected.end.time]].map(([name, part], index) => name && _jsx("input", { type: "hidden", name: name, form: form, value: accepted ? part : "", disabled: disabled }, index))] });
|
|
51
51
|
});
|
package/dist/disclosure.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type HTMLAttributes } from "react";
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
2
|
interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
3
3
|
title: string;
|
|
4
4
|
expanded: boolean;
|
|
@@ -6,6 +6,7 @@ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
controlSize: "sm" | "md" | "lg" | "xl";
|
|
8
8
|
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
9
|
+
headerAction?: ReactNode;
|
|
9
10
|
}
|
|
10
11
|
/** Shared original disclosure anatomy. The closed panel stays mounted but hidden. */
|
|
11
12
|
export declare const Disclosure: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<HTMLDivElement>>;
|
package/dist/disclosure.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { createElement, forwardRef, useEffect, useId, useLayoutEffect, useRef, useImperativeHandle } from "react";
|
|
3
|
-
import { Button } from "./index.js";
|
|
3
|
+
import { Button, IconSlot } from "./index.js";
|
|
4
4
|
const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
5
5
|
/** Shared original disclosure anatomy. The closed panel stays mounted but hidden. */
|
|
6
|
-
export const Disclosure = /* @__PURE__ */ forwardRef(function Disclosure({ title, expanded, onExpandedChange, disabled, controlSize, headingLevel, className, children, id: explicitId, ...props }, ref) {
|
|
6
|
+
export const Disclosure = /* @__PURE__ */ forwardRef(function Disclosure({ title, expanded, onExpandedChange, disabled, controlSize, headingLevel, headerAction, className, children, id: explicitId, ...props }, ref) {
|
|
7
7
|
const generatedId = useId(), id = explicitId ?? `flood-disclosure-${generatedId}`;
|
|
8
8
|
const root = useRef(null), panel = useRef(null), trigger = useRef(null);
|
|
9
9
|
const focusedInside = useRef(false);
|
|
@@ -15,7 +15,7 @@ export const Disclosure = /* @__PURE__ */ forwardRef(function Disclosure({ title
|
|
|
15
15
|
if (!expanded)
|
|
16
16
|
focusedInside.current = false;
|
|
17
17
|
}, [expanded, disabled]);
|
|
18
|
-
const button = _jsxs(Button, { ref: trigger, variant: "ghost", size: controlSize, id: `${id}-trigger`, type: "button", disabled: disabled, "aria-expanded": expanded, "aria-controls": `${id}-panel`, className: "flood-disclosure__trigger", onClick: () => onExpandedChange(!expanded), children: [_jsx("span", { className: "flood-disclosure__title", children: title }), _jsx("
|
|
19
|
-
return _jsxs("div", { ...props, id: id, ref: root, "data-open": expanded, className: ["flood-disclosure", `flood-control--${controlSize}`, className].filter(Boolean).join(" "), children: [createElement(headingLevel ? `h${headingLevel}` : "div", { className: "flood-disclosure__heading" }, button), _jsx("div", { ref: panel, id: `${id}-panel`, className: "flood-disclosure__panel", hidden: !expanded, onFocusCapture: () => { focusedInside.current = true; }, onBlurCapture: event => { if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget))
|
|
18
|
+
const button = _jsxs(Button, { ref: trigger, variant: "ghost", size: controlSize, id: `${id}-trigger`, type: "button", disabled: disabled, "aria-expanded": expanded, "aria-controls": `${id}-panel`, className: "flood-disclosure__trigger", onClick: () => onExpandedChange(!expanded), children: [_jsx("span", { className: "flood-disclosure__title", children: title }), _jsx(IconSlot, { size: "2xs", className: "flood-disclosure__indicator", children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("path", { d: "m3 8 9 8 9-8" }) }) })] });
|
|
19
|
+
return _jsxs("div", { ...props, id: id, ref: root, "data-open": expanded, className: ["flood-disclosure", `flood-control--${controlSize}`, className].filter(Boolean).join(" "), children: [createElement(headingLevel ? `h${headingLevel}` : "div", { className: "flood-disclosure__heading" }, button, headerAction != null && _jsx("div", { className: "flood-disclosure__action", children: headerAction })), _jsx("div", { ref: panel, id: `${id}-panel`, className: "flood-disclosure__panel", hidden: !expanded, onFocusCapture: () => { focusedInside.current = true; }, onBlurCapture: event => { if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget))
|
|
20
20
|
focusedInside.current = false; }, children: children })] });
|
|
21
21
|
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type FilterConditionProps } from './index.js';
|
|
2
|
+
/** One controlled finite-choice predicate. Query execution and draft/application belong to the host. */
|
|
3
|
+
export declare const PredicateRow: import("react").ForwardRefExoticComponent<FilterConditionProps & import("react").RefAttributes<HTMLFieldSetElement>>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { Field, Fieldset, Grid, IconButton, MultiSelect, Select } from './index.js';
|
|
4
|
+
/** One controlled finite-choice predicate. Query execution and draft/application belong to the host. */
|
|
5
|
+
export const PredicateRow = /* @__PURE__ */ forwardRef(function PredicateRow({ label, properties, value, onValueChange, onRemove, controlSize = 'sm', labels, disabled = false, hideLabel = true, className, ...props }, ref) {
|
|
6
|
+
const property = properties.find(property => property.id === value.property);
|
|
7
|
+
if (!label.trim() || !properties.length || new Set(properties.map(property => property.id)).size !== properties.length ||
|
|
8
|
+
properties.some(property => !property.id.trim() || !property.label.trim()) || !property ||
|
|
9
|
+
!['is', 'is-not'].includes(value.operator))
|
|
10
|
+
throw new Error('Flood FilterCondition requires named unique properties and a supported predicate');
|
|
11
|
+
const names = {
|
|
12
|
+
property: `${label} property`, operator: `${label} operator`, values: `${label} values`,
|
|
13
|
+
include: 'is any of', exclude: 'is none of', placeholder: 'Choose values…', remove: `Remove ${label.charAt(0).toLowerCase()}${label.slice(1)}`,
|
|
14
|
+
...labels,
|
|
15
|
+
};
|
|
16
|
+
return _jsx(Fieldset, { ...props, ref: ref, legend: label, hideLabel: hideLabel, disabled: disabled, className: ['flood-filter-condition', className].filter(Boolean).join(' '), "data-removable": Boolean(onRemove), children: _jsxs(Grid, { gap: "2", className: "flood-filter-condition__row", children: [_jsx(Field, { label: names.property, hideLabel: true, children: field => _jsx(Select, { ...field, controlSize: controlSize, value: value.property, disabled: disabled, onChange: event => { if (!disabled)
|
|
17
|
+
onValueChange({ ...value, property: event.currentTarget.value, values: [] }); }, children: properties.map(property => _jsx("option", { value: property.id, disabled: property.disabled, children: property.label }, property.id)) }) }), _jsx(Field, { label: names.operator, hideLabel: true, children: field => _jsxs(Select, { ...field, controlSize: controlSize, value: value.operator, disabled: disabled, onChange: event => { if (!disabled)
|
|
18
|
+
onValueChange({ ...value, operator: event.currentTarget.value }); }, children: [_jsx("option", { value: "is", children: names.include }), _jsx("option", { value: "is-not", children: names.exclude })] }) }), _jsx("div", { className: "flood-filter-condition__values", children: _jsx(Field, { label: names.values, hideLabel: true, children: field => _jsx(MultiSelect, { ...field, controlSize: controlSize, options: property.options, value: value.values, placeholder: names.placeholder, disabled: disabled, onValueChange: values => { if (!disabled)
|
|
19
|
+
onValueChange({ ...value, values }); } }) }) }), onRemove && _jsx(IconButton, { label: names.remove, size: controlSize, variant: "subtle", disabled: disabled, className: "flood-filter-condition__remove", onClick: () => { if (!disabled)
|
|
20
|
+
onRemove(); }, children: _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", "aria-hidden": "true", children: _jsx("path", { d: "m6 6 12 12M18 6 6 18" }) }) })] }) });
|
|
21
|
+
});
|