@flood-md/react 0.2.11 → 0.2.13
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 +39 -7
- package/dist/app-shell.js +2 -2
- package/dist/box-plot.d.ts +2 -0
- package/dist/box-plot.js +148 -0
- package/dist/bullet-chart.d.ts +2 -0
- package/dist/bullet-chart.js +27 -0
- package/dist/charts.d.ts +6 -0
- package/dist/charts.js +561 -0
- package/dist/collection-toolbar.js +4 -4
- package/dist/data-display.js +27 -3
- package/dist/data-table-columns.d.ts +25 -0
- package/dist/data-table-columns.js +271 -0
- package/dist/data-table-expansion.d.ts +21 -0
- package/dist/data-table-expansion.js +79 -0
- package/dist/data-table.js +100 -7
- package/dist/dumbbell-chart.d.ts +2 -0
- package/dist/dumbbell-chart.js +80 -0
- package/dist/editable.js +57 -18
- package/dist/feedback-actions.js +18 -3
- package/dist/foundations.css +2006 -227
- package/dist/histogram.d.ts +2 -0
- package/dist/histogram.js +78 -0
- package/dist/index.d.ts +328 -3
- package/dist/index.js +57 -4
- package/dist/menu.d.ts +1 -0
- package/dist/menu.js +6 -6
- package/dist/overlay-runtime.d.ts +5 -4
- package/dist/overlay-runtime.js +35 -12
- package/dist/overlays.d.ts +1 -0
- package/dist/overlays.js +5 -3
- package/dist/range-chart.d.ts +2 -0
- package/dist/range-chart.js +97 -0
- package/dist/scatter-chart.d.ts +2 -0
- package/dist/scatter-chart.js +170 -0
- package/dist/sparkline.d.ts +2 -0
- package/dist/sparkline.js +65 -0
- package/dist/waterfall-chart.d.ts +2 -0
- package/dist/waterfall-chart.js +89 -0
- package/package.json +2 -2
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useId, useState } from "react";
|
|
3
|
+
import { Inline, Surface, Text, VisuallyHidden } from "./index.js";
|
|
4
|
+
const number = new Intl.NumberFormat("en", { maximumSignificantDigits: 21 });
|
|
5
|
+
export const DumbbellPlot = /* @__PURE__ */ forwardRef(function DumbbellPlot({ label, items, domain, beforeLabel = "Before", afterLabel = "After", missingLabel = "No data", emptyLabel = "No observations", formatValue = number.format, navigationLabel = "Use arrow keys to explore groups, or Home and End to jump.", className, ...props }, ref) {
|
|
6
|
+
const instructions = useId();
|
|
7
|
+
const [cursor, setCursor] = useState(0);
|
|
8
|
+
const [hover, setHover] = useState();
|
|
9
|
+
const [tap, setTap] = useState();
|
|
10
|
+
const [focused, setFocused] = useState(false);
|
|
11
|
+
const ids = new Set();
|
|
12
|
+
let low = Infinity;
|
|
13
|
+
let high = -Infinity;
|
|
14
|
+
for (const item of items) {
|
|
15
|
+
if (!item.id.trim() || ids.has(item.id) || !item.label.trim())
|
|
16
|
+
throw new Error("Flood DumbbellChart needs unique IDs and nonempty labels");
|
|
17
|
+
ids.add(item.id);
|
|
18
|
+
for (const value of [item.before, item.after]) {
|
|
19
|
+
if (value === null)
|
|
20
|
+
continue;
|
|
21
|
+
if (!Number.isFinite(value))
|
|
22
|
+
throw new RangeError("Flood DumbbellChart endpoints must be finite");
|
|
23
|
+
low = Math.min(low, value);
|
|
24
|
+
high = Math.max(high, value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (domain && (!Number.isFinite(domain[0]) || !Number.isFinite(domain[1]) ||
|
|
28
|
+
domain[0] >= domain[1] || domain[0] > low || domain[1] < high)) {
|
|
29
|
+
throw new RangeError("Flood DumbbellChart domain must contain all observed endpoints and have finite increasing bounds");
|
|
30
|
+
}
|
|
31
|
+
const [min, max] = domain ?? [low, high];
|
|
32
|
+
const span = max - min;
|
|
33
|
+
const position = (value) => span === 0 ? 50 : 100 * (Number.isFinite(span)
|
|
34
|
+
? (value - min) / span : (value / 2 - min / 2) / (max / 2 - min / 2));
|
|
35
|
+
const last = items.length - 1;
|
|
36
|
+
const active = Math.min(last, hover ?? tap ?? cursor);
|
|
37
|
+
const item = items[active];
|
|
38
|
+
const inspecting = hover !== undefined || tap !== undefined || focused;
|
|
39
|
+
const values = item ? [item.before, item.after].map(value => value === null ? missingLabel : formatValue(value)) : [];
|
|
40
|
+
const valueText = item && `${item.label}, ${beforeLabel}: ${values[0]}, ${afterLabel}: ${values[1]}`;
|
|
41
|
+
const navigate = (event) => {
|
|
42
|
+
let next;
|
|
43
|
+
switch (event.key) {
|
|
44
|
+
case "ArrowDown":
|
|
45
|
+
case "ArrowRight":
|
|
46
|
+
next = active + 1;
|
|
47
|
+
break;
|
|
48
|
+
case "ArrowUp":
|
|
49
|
+
case "ArrowLeft":
|
|
50
|
+
next = active - 1;
|
|
51
|
+
break;
|
|
52
|
+
case "Home":
|
|
53
|
+
next = 0;
|
|
54
|
+
break;
|
|
55
|
+
case "End":
|
|
56
|
+
next = last;
|
|
57
|
+
break;
|
|
58
|
+
case "Escape":
|
|
59
|
+
if (inspecting)
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
setHover(undefined);
|
|
62
|
+
setTap(undefined);
|
|
63
|
+
setFocused(false);
|
|
64
|
+
return;
|
|
65
|
+
default: return;
|
|
66
|
+
}
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
setHover(undefined);
|
|
69
|
+
setTap(undefined);
|
|
70
|
+
setFocused(true);
|
|
71
|
+
setCursor(Math.max(0, Math.min(last, next)));
|
|
72
|
+
};
|
|
73
|
+
return _jsx("div", { ...props, ref: ref, className: ["flood-dumbbell-chart", className].filter(Boolean).join(" "), role: "group", "aria-label": label, children: low <= high ? _jsxs(_Fragment, { children: [last > 0 && _jsx(VisuallyHidden, { id: instructions, children: navigationLabel }), _jsxs("div", { className: "flood-dumbbell-chart__plot", dir: "ltr", role: last > 0 ? "slider" : "img", tabIndex: 0, "aria-label": last > 0 ? label : `${label}. ${valueText}`, "aria-describedby": last > 0 ? instructions : undefined, "aria-valuemin": last > 0 ? 1 : undefined, "aria-valuemax": last > 0 ? items.length : undefined, "aria-valuenow": last > 0 ? active + 1 : undefined, "aria-valuetext": last > 0 ? valueText : undefined, onFocus: event => setFocused(event.currentTarget.matches(":focus-visible")), onBlur: () => setFocused(false), onKeyDown: navigate, onPointerLeave: () => setHover(undefined), children: [_jsx("div", { className: "flood-dumbbell-chart__rows", "aria-hidden": "true", children: items.map(({ id, label: name, before, after }, index) => {
|
|
74
|
+
const start = before === null ? undefined : position(before);
|
|
75
|
+
const end = after === null ? undefined : position(after);
|
|
76
|
+
return _jsxs("div", { className: "flood-dumbbell-chart__row", "data-active": inspecting && index === active || undefined, onPointerEnter: event => { if (event.pointerType !== "touch")
|
|
77
|
+
setHover(index); }, onPointerUp: event => { if (event.pointerType === "touch")
|
|
78
|
+
setTap(previous => previous === index ? undefined : index); }, children: [_jsx(Text, { className: "flood-dumbbell-chart__name", size: "helper", tone: "subtle", dir: "auto", title: name, children: name }), _jsxs("div", { className: "flood-dumbbell-chart__track", children: [start !== undefined && end !== undefined && _jsx("span", { className: "flood-dumbbell-chart__connector", style: { left: `${Math.min(start, end)}%`, width: `${Math.abs(end - start)}%` } }), start !== undefined && _jsx("span", { className: "flood-dumbbell-chart__point", "data-point": "before", style: { left: `${start}%` } }), end !== undefined && _jsx("span", { className: "flood-dumbbell-chart__point", "data-point": "after", style: { left: `${end}%` } })] }), inspecting && index === active && _jsxs(Surface, { className: "flood-chart__tooltip flood-dumbbell-chart__tooltip", padding: "2", elevation: "raised", "data-side": index < items.length / 2 ? "below" : "above", style: { "--flood-dumbbell-anchor": (end ?? start ?? 50) / 100 }, children: [_jsx(Text, { size: "helper", dir: "auto", children: name }), _jsxs(Inline, { gap: "2", className: "flood-dumbbell-chart__reading", children: [_jsx(Text, { size: "helper", tone: "subtle", children: beforeLabel }), _jsx(Text, { size: "helper", children: values[0] })] }), _jsxs(Inline, { gap: "2", className: "flood-dumbbell-chart__reading", children: [_jsx(Text, { size: "helper", tone: "subtle", children: afterLabel }), _jsx(Text, { size: "helper", children: values[1] })] })] })] }, id);
|
|
79
|
+
}) }), _jsxs("div", { className: "flood-dumbbell-chart__axis", "aria-hidden": "true", children: [_jsx(Text, { size: "helper", tone: "subtle", children: formatValue(min) }), min !== max && _jsx(Text, { size: "helper", tone: "subtle", children: formatValue(max) })] })] }), _jsxs(Inline, { gap: "4", className: "flood-dumbbell-chart__legend", "aria-hidden": "true", children: [_jsxs(Inline, { gap: "2", align: "center", children: [_jsx("span", { className: "flood-dumbbell-chart__key", "data-point": "before" }), _jsx(Text, { size: "helper", tone: "subtle", children: beforeLabel })] }), _jsxs(Inline, { gap: "2", align: "center", children: [_jsx("span", { className: "flood-dumbbell-chart__key", "data-point": "after" }), _jsx(Text, { size: "helper", tone: "subtle", children: afterLabel })] })] })] }) : _jsx(Text, { size: "compact", tone: "subtle", children: emptyLabel }) });
|
|
80
|
+
});
|
package/dist/editable.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useEffect, useId, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
-
import { Button, IconButton, IconSlot, Input, InputGroup } from "./index.js";
|
|
3
|
+
import { Button, IconButton, IconSlot, Input, InputGroup, Text } from "./index.js";
|
|
4
|
+
import { mergeDescriptions } from "./field-state.js";
|
|
5
|
+
import { positionLayer, registerLayer } from "./overlay-runtime.js";
|
|
4
6
|
const useBrowserLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
5
7
|
const cx = (...values) => values.filter(Boolean).join(" ");
|
|
6
8
|
/** A draft is private until explicitly committed; native forms receive committed text. */
|
|
7
|
-
export const InlineEditable = /* @__PURE__ */ forwardRef(function InlineEditable({ label, hideLabel = false, value, defaultValue = "", onValueChange, editing, defaultEditing = false, onEditingChange, onCommit, onCancel, blurAction = "commit", readOnly = false, disabled = false, placeholder = "Empty", controlSize = "md", showActions = true, editLabel = "Edit", saveLabel = "Save", cancelLabel = "Cancel", inputRef, name, form, id: explicitId, className, onBlur, onKeyDown, ...props }, forwardedRef) {
|
|
9
|
+
export const InlineEditable = /* @__PURE__ */ forwardRef(function InlineEditable({ label, hideLabel = false, value, defaultValue = "", onValueChange, editing, defaultEditing = false, onEditingChange, onCommit, onCancel, validate, inputMode, previewVariant = "subtle", editorOverlayWidth, blurAction = "commit", readOnly = false, disabled = false, placeholder = "Empty", controlSize = "md", showActions = true, editLabel = "Edit", saveLabel = "Save", cancelLabel = "Cancel", inputRef, name, form, id: explicitId, className, onBlur, onKeyDown, ...props }, forwardedRef) {
|
|
8
10
|
if (![label, editLabel, saveLabel, cancelLabel].every(text => text.trim()))
|
|
9
11
|
throw new Error("Flood Editable requires nonempty control and action labels");
|
|
10
12
|
const initial = useRef(defaultValue), [local, setLocal] = useState(defaultValue);
|
|
@@ -12,9 +14,13 @@ export const InlineEditable = /* @__PURE__ */ forwardRef(function InlineEditable
|
|
|
12
14
|
const [localEditing, setLocalEditing] = useState(defaultEditing);
|
|
13
15
|
const requested = editing ?? localEditing, active = requested && !disabled && !readOnly;
|
|
14
16
|
const [draft, setDraft] = useState(committed);
|
|
17
|
+
const [validationError, setValidationError] = useState();
|
|
15
18
|
const root = useRef(null), editor = useRef(null), bridge = useRef(null), preview = useRef(null);
|
|
16
|
-
const
|
|
17
|
-
|
|
19
|
+
const layer = useRef(null);
|
|
20
|
+
const floating = editorOverlayWidth !== undefined;
|
|
21
|
+
const fitDraft = editorOverlayWidth === "fit-content";
|
|
22
|
+
const current = useRef({ committed, active, draft, value, editing, onValueChange, onEditingChange, onCommit, onCancel, validate });
|
|
23
|
+
current.current = { committed, active, draft, value, editing, onValueChange, onEditingChange, onCommit, onCancel, validate };
|
|
18
24
|
const composing = useRef(false), resolved = useRef(false), wasActive = useRef(false);
|
|
19
25
|
const generated = useId(), id = explicitId ?? `flood-editable-${generated}`, inputId = `${id}-input`, previewId = `${id}-preview`;
|
|
20
26
|
useImperativeHandle(forwardedRef, () => root.current, []);
|
|
@@ -34,8 +40,12 @@ export const InlineEditable = /* @__PURE__ */ forwardRef(function InlineEditable
|
|
|
34
40
|
const finish = (action, restoreFocus) => {
|
|
35
41
|
if (!current.current.active || resolved.current || (composing.current && action === "commit") || editor.current?.matches(":disabled"))
|
|
36
42
|
return;
|
|
37
|
-
resolved.current = true;
|
|
38
43
|
const next = current.current.draft;
|
|
44
|
+
const message = action === "commit" ? current.current.validate?.(next) : undefined;
|
|
45
|
+
setValidationError(message);
|
|
46
|
+
if (message)
|
|
47
|
+
return;
|
|
48
|
+
resolved.current = true;
|
|
39
49
|
if (action === "commit") {
|
|
40
50
|
if (next !== current.current.committed) {
|
|
41
51
|
if (current.current.value === undefined)
|
|
@@ -62,10 +72,35 @@ export const InlineEditable = /* @__PURE__ */ forwardRef(function InlineEditable
|
|
|
62
72
|
// A controlled host may refuse to close and allow a later deliberate attempt.
|
|
63
73
|
queueMicrotask(() => { resolved.current = false; });
|
|
64
74
|
};
|
|
75
|
+
useBrowserLayoutEffect(() => {
|
|
76
|
+
const panel = layer.current, anchor = preview.current;
|
|
77
|
+
if (!active || !floating || !panel || !anchor)
|
|
78
|
+
return;
|
|
79
|
+
const document = panel.ownerDocument, view = document.defaultView;
|
|
80
|
+
panel.showPopover();
|
|
81
|
+
let frame = 0;
|
|
82
|
+
const position = () => {
|
|
83
|
+
positionLayer(anchor, panel, "bottom", "end", true);
|
|
84
|
+
frame = view.requestAnimationFrame(position);
|
|
85
|
+
};
|
|
86
|
+
position();
|
|
87
|
+
const unregister = registerLayer(document, {
|
|
88
|
+
panel, anchor: () => anchor, close: () => finish("cancel", true), closeOnFocusOutside: false,
|
|
89
|
+
outside: () => { if (blurAction !== "none")
|
|
90
|
+
finish(blurAction, false); },
|
|
91
|
+
});
|
|
92
|
+
return () => {
|
|
93
|
+
unregister();
|
|
94
|
+
view.cancelAnimationFrame(frame);
|
|
95
|
+
if (panel.matches(":popover-open"))
|
|
96
|
+
panel.hidePopover();
|
|
97
|
+
};
|
|
98
|
+
}, [active, floating, blurAction]);
|
|
65
99
|
useBrowserLayoutEffect(() => {
|
|
66
100
|
let frame = 0;
|
|
67
101
|
if (active && !wasActive.current) {
|
|
68
102
|
setDraft(current.current.committed);
|
|
103
|
+
setValidationError(undefined);
|
|
69
104
|
resolved.current = false;
|
|
70
105
|
composing.current = false;
|
|
71
106
|
const view = editor.current?.ownerDocument.defaultView;
|
|
@@ -106,7 +141,20 @@ export const InlineEditable = /* @__PURE__ */ forwardRef(function InlineEditable
|
|
|
106
141
|
return () => { owner.removeEventListener("reset", reset); clearTimeout(timer); };
|
|
107
142
|
}, [form]);
|
|
108
143
|
const glyph = (kind) => _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: kind === "save" ? _jsx("path", { d: "m5 12 4 4L19 6" }) : kind === "cancel" ? _jsx("path", { d: "m6 6 12 12M18 6 6 18" }) : _jsx(_Fragment, { children: _jsx("path", { d: "m15 5 4 4M4 20l4-1L20 7a2.1 2.1 0 0 0-3-3L5 16l-1 4Z" }) }) });
|
|
109
|
-
const
|
|
144
|
+
const errorId = `${inputId}-error`;
|
|
145
|
+
const editorProps = { id: inputId, inputMode, "aria-describedby": mergeDescriptions(props["aria-describedby"], active && validationError ? errorId : undefined),
|
|
146
|
+
"aria-invalid": active && validationError ? true : props["aria-invalid"], controlSize, disabled };
|
|
147
|
+
const editButton = _jsxs(Button, { ref: preview, id: previewId, type: "button", variant: previewVariant, size: controlSize, disabled: disabled, className: "flood-editable__preview", "aria-describedby": props["aria-describedby"], "aria-label": `${editLabel}: ${label}. ${committed || placeholder}`, "aria-hidden": active && floating || undefined, tabIndex: active && floating ? -1 : undefined, onClick: start, children: [_jsx("span", { className: "flood-editable__value", "data-empty": !committed || undefined, children: committed || placeholder }), _jsx(IconSlot, { size: controlSize, children: glyph("edit") })] });
|
|
148
|
+
const editorContent = active && _jsxs(_Fragment, { children: [_jsx(InputGroup, { ...editorProps, ref: editor, value: draft, placeholder: placeholder, size: fitDraft ? Math.max(4, (draft || placeholder).length + 1) : undefined, onChange: event => { setValidationError(undefined); setDraft(event.currentTarget.value); current.current.draft = event.currentTarget.value; }, onCompositionStart: () => { composing.current = true; }, onCompositionEnd: event => {
|
|
149
|
+
composing.current = false;
|
|
150
|
+
setDraft(event.currentTarget.value);
|
|
151
|
+
current.current.draft = event.currentTarget.value;
|
|
152
|
+
const document = event.currentTarget.ownerDocument;
|
|
153
|
+
if (blurAction !== "none" && !root.current?.contains(document.activeElement))
|
|
154
|
+
queueMicrotask(() => finish(blurAction, false));
|
|
155
|
+
}, end: showActions ? _jsxs("div", { className: "flood-editable__actions", children: [_jsx(IconButton, { type: "button", label: saveLabel, size: controlSize, variant: "ghost", onPointerDown: event => { if (event.pointerType === "mouse")
|
|
156
|
+
event.preventDefault(); }, onMouseDown: event => event.preventDefault(), onClick: () => finish("commit", true), children: glyph("save") }), _jsx(IconButton, { type: "button", label: cancelLabel, size: controlSize, variant: "ghost", onPointerDown: event => { if (event.pointerType === "mouse")
|
|
157
|
+
event.preventDefault(); }, onMouseDown: event => event.preventDefault(), onClick: () => finish("cancel", true), children: glyph("cancel") })] }) : undefined }), validationError && _jsx(Text, { as: "p", id: errorId, role: "alert", size: "helper", tone: "error", className: "flood-editable__error", children: validationError })] });
|
|
110
158
|
return _jsxs("div", { ...props, ref: root, id: id, className: cx("flood-editable", className), "data-editing": active || undefined, "data-disabled": disabled || undefined, "data-readonly": readOnly || undefined, onKeyDown: event => {
|
|
111
159
|
onKeyDown?.(event);
|
|
112
160
|
if (event.defaultPrevented || !current.current.active || event.target !== editor.current || event.nativeEvent.isComposing || composing.current || event.keyCode === 229 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
|
|
@@ -120,16 +168,7 @@ export const InlineEditable = /* @__PURE__ */ forwardRef(function InlineEditable
|
|
|
120
168
|
if (event.defaultPrevented || !current.current.active || event.currentTarget.contains(event.relatedTarget) || blurAction === "none")
|
|
121
169
|
return;
|
|
122
170
|
finish(blurAction, false);
|
|
123
|
-
}, children: [_jsx("label", { htmlFor: active || readOnly ? inputId : previewId, className: cx("flood-label", hideLabel && "flood-visually-hidden"), children: label }), _jsx("input", { ref: bridge, type: "hidden", name: name, form: form, value: committed, disabled: disabled }),
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
current.current.draft = event.currentTarget.value;
|
|
127
|
-
const document = event.currentTarget.ownerDocument;
|
|
128
|
-
if (blurAction !== "none" && !root.current?.contains(document.activeElement))
|
|
129
|
-
queueMicrotask(() => finish(blurAction, false));
|
|
130
|
-
}, end: showActions ? _jsxs("div", { className: "flood-editable__actions", children: [_jsx(IconButton, { type: "button", label: saveLabel, size: controlSize, variant: "ghost", onPointerDown: event => { if (event.pointerType === "mouse")
|
|
131
|
-
event.preventDefault(); }, onMouseDown: event => event.preventDefault(), onClick: () => finish("commit", true), children: glyph("save") }), _jsx(IconButton, { type: "button", label: cancelLabel, size: controlSize, variant: "ghost", onPointerDown: event => { if (event.pointerType === "mouse")
|
|
132
|
-
event.preventDefault(); }, onMouseDown: event => event.preventDefault(), onClick: () => finish("cancel", true), children: glyph("cancel") })] }) : undefined })
|
|
133
|
-
: readOnly ? _jsx(Input, { ...editorProps, ref: editor, value: committed, readOnly: true })
|
|
134
|
-
: _jsxs(Button, { ref: preview, id: previewId, type: "button", variant: "subtle", size: controlSize, disabled: disabled, className: "flood-editable__preview", "aria-describedby": props["aria-describedby"], "aria-label": `${editLabel}: ${label}. ${committed || placeholder}`, onClick: start, children: [_jsx("span", { className: "flood-editable__value", "data-empty": !committed || undefined, children: committed || placeholder }), _jsx(IconSlot, { size: controlSize, children: glyph("edit") })] })] });
|
|
171
|
+
}, children: [_jsx("label", { htmlFor: active || readOnly ? inputId : previewId, className: cx("flood-label", hideLabel && "flood-visually-hidden"), children: label }), _jsx("input", { ref: bridge, type: "hidden", name: name, form: form, value: committed, disabled: disabled }), readOnly ? _jsx(Input, { ...editorProps, ref: editor, value: committed, readOnly: true }) : (!active || floating) && editButton, active && (floating
|
|
172
|
+
? _jsx("div", { ref: layer, popover: "manual", className: "flood-overlay-panel flood-editable__overlay", "data-fit-draft": fitDraft || undefined, style: { inlineSize: editorOverlayWidth }, children: editorContent })
|
|
173
|
+
: editorContent)] });
|
|
135
174
|
});
|
package/dist/feedback-actions.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
3
3
|
import { Button, IconButton, IconSlot, Spinner, VisuallyHidden } from "./index.js";
|
|
4
|
-
|
|
4
|
+
function ClipboardGlyph({ index, pending }) {
|
|
5
|
+
if (pending)
|
|
6
|
+
return _jsx(Spinner, { decorative: true });
|
|
7
|
+
return _jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round", strokeLinejoin: "round", children: index === 1 ? _jsx("path", { d: "m5 12 4 4L19 6" }) : index === 2 ? _jsx("path", { d: "m6 6 12 12M18 6 6 18" }) : _jsxs(_Fragment, { children: [_jsx("rect", { x: "8", y: "8", width: "12", height: "12", rx: "2" }), _jsx("path", { d: "M15 8V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h3" })] }) });
|
|
8
|
+
}
|
|
9
|
+
export const ClipboardAction = /* @__PURE__ */ forwardRef(function ClipboardAction({ value, iconOnly, copyLabel = "Copy", copiedLabel = "Copied", errorLabel = "Failed", resetDelay = 2000, onCopied, onCopyError, className, disabled, size = "md", variant = "subtle", "aria-label": accessibleLabel, ...props }, ref) {
|
|
5
10
|
if (!Number.isFinite(resetDelay) || resetDelay < 0)
|
|
6
11
|
throw new Error("Flood CopyButton resetDelay must be finite and nonnegative");
|
|
7
12
|
const [phase, setPhase] = useState("idle");
|
|
@@ -9,7 +14,9 @@ export const ClipboardAction = /* @__PURE__ */ forwardRef(function ClipboardActi
|
|
|
9
14
|
const timer = useRef(undefined);
|
|
10
15
|
const alive = useRef(true);
|
|
11
16
|
useEffect(() => { alive.current = true; return () => { alive.current = false; clearTimeout(timer.current); }; }, []);
|
|
12
|
-
const
|
|
17
|
+
const phaseIndex = phase === "copied" ? 1 : phase === "error" ? 2 : 0;
|
|
18
|
+
const labels = [copyLabel, copiedLabel, errorLabel];
|
|
19
|
+
const label = labels[phaseIndex];
|
|
13
20
|
const copy = async (node) => {
|
|
14
21
|
if (pending || node.matches(":disabled"))
|
|
15
22
|
return;
|
|
@@ -38,7 +45,15 @@ export const ClipboardAction = /* @__PURE__ */ forwardRef(function ClipboardActi
|
|
|
38
45
|
}
|
|
39
46
|
onCopied?.();
|
|
40
47
|
};
|
|
41
|
-
|
|
48
|
+
const buttonLabel = accessibleLabel ? (phase === "idle" ? accessibleLabel : `${label}: ${accessibleLabel}`) : label;
|
|
49
|
+
const buttonProps = {
|
|
50
|
+
...props, ref, type: "button", size, variant, disabled: disabled || pending,
|
|
51
|
+
className: ["flood-copy-button", className].filter(Boolean).join(" "),
|
|
52
|
+
onClick: (event) => { void copy(event.currentTarget); },
|
|
53
|
+
};
|
|
54
|
+
return _jsxs(_Fragment, { children: [iconOnly
|
|
55
|
+
? _jsx(IconButton, { ...buttonProps, label: buttonLabel, title: props.title ?? buttonLabel, children: _jsx(ClipboardGlyph, { index: phaseIndex, pending: pending }) })
|
|
56
|
+
: _jsx(Button, { ...buttonProps, "aria-label": buttonLabel, children: _jsx("span", { className: "flood-copy-button__labels", "aria-hidden": "true", children: labels.map((text, index) => _jsxs("span", { "data-visible": index === phaseIndex || undefined, children: [_jsx(IconSlot, { size: size, children: _jsx(ClipboardGlyph, { index: index, pending: pending }) }), _jsx("span", { children: text })] }, index)) }) }), _jsx(VisuallyHidden, { role: "status", children: phase === "idle" ? "" : label })] });
|
|
42
57
|
});
|
|
43
58
|
function TimedToast({ item, closeLabel, onDismiss }) {
|
|
44
59
|
const duration = item.duration ?? (item.action || item.status === "error" ? 0 : 5000);
|