@flood-md/react 0.2.12 → 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 +1 -1
- 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 +2 -2
- package/dist/data-display.js +1 -1
- package/dist/dumbbell-chart.d.ts +2 -0
- package/dist/dumbbell-chart.js +80 -0
- package/dist/feedback-actions.js +18 -3
- package/dist/foundations.css +1616 -199
- package/dist/histogram.d.ts +2 -0
- package/dist/histogram.js +78 -0
- package/dist/index.d.ts +257 -0
- package/dist/index.js +46 -2
- package/dist/menu.d.ts +1 -0
- package/dist/menu.js +6 -6
- package/dist/overlay-runtime.d.ts +1 -1
- package/dist/overlay-runtime.js +25 -4
- package/dist/overlays.js +4 -2
- 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/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/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);
|