@tensor_1/react 2.30.0 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +430 -0
- package/dist/attachment-labels.d.ts +16 -0
- package/dist/attachment-labels.js +9 -0
- package/dist/calendar-model.d.ts +141 -1
- package/dist/calendar-model.js +200 -11
- package/dist/chart-preset.cjs +127 -7
- package/dist/chart-preset.d.cts +2 -2
- package/dist/chart-preset.d.ts +1 -0
- package/dist/chart-preset.js +6 -1
- package/dist/chart-theme-Cl0IrkKQ.d.cts +112 -0
- package/dist/chart-theme.d.ts +51 -0
- package/dist/chart-theme.js +99 -0
- package/dist/chart.cjs +127 -7
- package/dist/chart.d.cts +2 -2
- package/dist/chart.d.ts +1 -0
- package/dist/chart.js +6 -2
- package/dist/components/Alert.d.ts +17 -1
- package/dist/components/Alert.js +9 -4
- package/dist/components/AlertDismiss.d.ts +14 -0
- package/dist/components/AlertDismiss.js +11 -0
- package/dist/components/Attachment.d.ts +58 -0
- package/dist/components/Attachment.js +69 -0
- package/dist/components/AttachmentParts.d.ts +24 -0
- package/dist/components/AttachmentParts.js +20 -0
- package/dist/components/Chart.d.ts +1 -0
- package/dist/components/Chart.js +6 -1
- package/dist/components/ChartCore.d.ts +6 -1
- package/dist/components/ChartCore.js +23 -5
- package/dist/components/Chip.d.ts +17 -2
- package/dist/components/Chip.js +35 -11
- package/dist/components/ChipRemove.d.ts +17 -0
- package/dist/components/ChipRemove.js +13 -0
- package/dist/components/CollapsibleParts.d.ts +29 -0
- package/dist/components/CollapsibleParts.js +20 -0
- package/dist/components/CollapsibleSection.d.ts +30 -3
- package/dist/components/CollapsibleSection.js +7 -11
- package/dist/components/CollapsibleSectionState.d.ts +1 -1
- package/dist/components/CollapsibleSectionState.js +54 -27
- package/dist/components/DashboardGrid.d.ts +60 -0
- package/dist/components/DashboardGrid.js +40 -0
- package/dist/components/DateRangePicker.d.ts +7 -1
- package/dist/components/DateRangePicker.js +5 -1
- package/dist/components/DescriptionList.d.ts +12 -1
- package/dist/components/DescriptionList.js +5 -5
- package/dist/components/Dialogs.d.ts +24 -3
- package/dist/components/Dialogs.js +74 -16
- package/dist/components/EntityPicker.d.ts +56 -0
- package/dist/components/EntityPicker.js +168 -0
- package/dist/components/FileIcon.d.ts +9 -0
- package/dist/components/FileIcon.js +17 -0
- package/dist/components/FileUpload.d.ts +95 -0
- package/dist/components/FileUpload.js +145 -0
- package/dist/components/FilterEditor.d.ts +55 -0
- package/dist/components/FilterEditor.js +186 -0
- package/dist/components/Heartbeat.d.ts +31 -0
- package/dist/components/Heartbeat.js +28 -0
- package/dist/components/Lightbox.d.ts +45 -0
- package/dist/components/Lightbox.js +102 -0
- package/dist/components/Menu.d.ts +53 -5
- package/dist/components/Menu.js +104 -4
- package/dist/components/Meter.d.ts +7 -3
- package/dist/components/Meter.js +2 -1
- package/dist/components/Popover.d.ts +21 -2
- package/dist/components/Popover.js +94 -17
- package/dist/components/Prose.d.ts +6 -2
- package/dist/components/Prose.js +1 -1
- package/dist/components/RuleBuilder.d.ts +52 -4
- package/dist/components/RuleBuilder.js +109 -24
- package/dist/components/SchedulingCalendar.d.ts +142 -11
- package/dist/components/SchedulingCalendar.js +234 -57
- package/dist/components/SchedulingTimeline.d.ts +1 -0
- package/dist/components/SearchSuggest.d.ts +6 -2
- package/dist/components/SearchSuggest.js +4 -0
- package/dist/components/Segmented.d.ts +17 -3
- package/dist/components/Segmented.js +59 -5
- package/dist/components/Select.d.ts +66 -2
- package/dist/components/Select.js +800 -10
- package/dist/components/Spinner.d.ts +2 -1
- package/dist/components/Spinner.js +1 -1
- package/dist/components/StatusPill.d.ts +8 -2
- package/dist/components/StatusPill.js +2 -2
- package/dist/components/Steps.d.ts +28 -4
- package/dist/components/Steps.js +46 -16
- package/dist/components/TableControls.js +4 -3
- package/dist/components/Tabs.d.ts +7 -2
- package/dist/components/Tabs.js +14 -1
- package/dist/components/TextDiff.d.ts +49 -0
- package/dist/components/TextDiff.js +124 -0
- package/dist/{emoji-image-6mPZ15Zy.d.cts → emoji-image-DjCKpdwE.d.cts} +1 -1
- package/dist/emoji.cjs +169 -27
- package/dist/emoji.d.cts +3 -3
- package/dist/file-model.d.ts +56 -0
- package/dist/file-model.js +52 -0
- package/dist/filter-model.d.ts +112 -0
- package/dist/filter-model.js +103 -0
- package/dist/hooks/useAnchoredPosition.d.ts +24 -1
- package/dist/hooks/useAnchoredPosition.js +58 -8
- package/dist/hooks/useVirtualRows.d.ts +17 -2
- package/dist/hooks/useVirtualRows.js +6 -5
- package/dist/icons/Icon.d.ts +14 -14
- package/dist/index.cjs +4938 -1631
- package/dist/index.d.cts +1622 -178
- package/dist/index.d.ts +20 -6
- package/dist/index.js +88 -4
- package/dist/labels-context.d.ts +28 -0
- package/dist/{overlay-position-Cjv5E7mn.d.cts → overlay-position-2Afke51l.d.cts} +20 -0
- package/dist/overlay-position.d.ts +20 -0
- package/dist/overlay-position.js +17 -2
- package/dist/rich-text.cjs +169 -27
- package/dist/rich-text.d.cts +3 -3
- package/dist/rule-builder-model.d.ts +34 -2
- package/dist/rule-builder-model.js +48 -12
- package/dist/scheduling-calendar-model.d.ts +66 -0
- package/dist/scheduling-calendar-model.js +82 -0
- package/dist/select-model.d.ts +80 -0
- package/dist/select-model.js +109 -0
- package/dist/table-engine.d.ts +7 -0
- package/dist/table-engine.js +2 -1
- package/dist/text-diff-model.d.ts +71 -0
- package/dist/text-diff-model.js +261 -0
- package/package.json +1 -1
- package/dist/ChartCore-DzWe10bN.d.cts +0 -59
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useEffect, useId, useRef } from "react";
|
|
4
4
|
import { cx } from "../cx.js";
|
|
5
|
+
import { mergeChartTheme, uixChartTheme } from "../chart-theme.js";
|
|
5
6
|
function readToken(name) {
|
|
6
7
|
if (typeof getComputedStyle === "undefined") return "";
|
|
7
8
|
return getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
@@ -9,6 +10,9 @@ function readToken(name) {
|
|
|
9
10
|
function uixChartPalette() {
|
|
10
11
|
return [1, 2, 3, 4, 5, 6, 7, 8].map((i) => readToken(`--uix-chart-${i}`));
|
|
11
12
|
}
|
|
13
|
+
function themed(option, theme, element) {
|
|
14
|
+
return theme === "none" ? option : mergeChartTheme(uixChartTheme(element), option);
|
|
15
|
+
}
|
|
12
16
|
function motionSafe(option) {
|
|
13
17
|
if (typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
14
18
|
return { ...option, animation: false };
|
|
@@ -37,7 +41,8 @@ function ChartCore({
|
|
|
37
41
|
footer,
|
|
38
42
|
loading = false,
|
|
39
43
|
empty,
|
|
40
|
-
labels: labelOverrides
|
|
44
|
+
labels: labelOverrides,
|
|
45
|
+
theme = "uix"
|
|
41
46
|
}) {
|
|
42
47
|
const labels = { ...DEFAULT_CHART_LABELS, ...labelOverrides };
|
|
43
48
|
const containerRef = useRef(null);
|
|
@@ -49,7 +54,9 @@ function ChartCore({
|
|
|
49
54
|
const hasEmpty = empty != null;
|
|
50
55
|
const hasTextAlternative = Boolean(ariaLabel || title || tableData?.length);
|
|
51
56
|
const hasTextAlternativeRef = useRef(hasTextAlternative);
|
|
57
|
+
const themeRef = useRef(theme);
|
|
52
58
|
latestOptionRef.current = option;
|
|
59
|
+
themeRef.current = theme;
|
|
53
60
|
onReadyRef.current = onReady;
|
|
54
61
|
hasTextAlternativeRef.current = hasTextAlternative;
|
|
55
62
|
useEffect(() => {
|
|
@@ -57,9 +64,17 @@ function ChartCore({
|
|
|
57
64
|
if (!element) return;
|
|
58
65
|
const chart = engine.init(element, null, { renderer: "svg" });
|
|
59
66
|
chartRef.current = chart;
|
|
60
|
-
chart.setOption(motionSafe(latestOptionRef.current));
|
|
67
|
+
chart.setOption(motionSafe(themed(latestOptionRef.current, themeRef.current, element)));
|
|
61
68
|
appliedOptionRef.current = latestOptionRef.current;
|
|
62
69
|
onReadyRef.current?.(chart);
|
|
70
|
+
const retheme = () => {
|
|
71
|
+
if (themeRef.current === "none") return;
|
|
72
|
+
chart.setOption(motionSafe(themed(latestOptionRef.current, themeRef.current, element)));
|
|
73
|
+
};
|
|
74
|
+
const themeObserver = typeof MutationObserver === "undefined" ? null : new MutationObserver(retheme);
|
|
75
|
+
themeObserver?.observe(document.documentElement, { attributes: true, attributeFilter: ["class", "data-theme"] });
|
|
76
|
+
const scheme = typeof matchMedia === "undefined" ? null : matchMedia("(prefers-color-scheme: dark)");
|
|
77
|
+
scheme?.addEventListener?.("change", retheme);
|
|
63
78
|
if (!hasTextAlternativeRef.current) {
|
|
64
79
|
console.warn("[uix] Chart has no text alternative \u2014 provide ariaLabel, title, or tableData.");
|
|
65
80
|
}
|
|
@@ -81,6 +96,8 @@ function ChartCore({
|
|
|
81
96
|
observer.observe(element);
|
|
82
97
|
return () => {
|
|
83
98
|
observer.disconnect();
|
|
99
|
+
themeObserver?.disconnect();
|
|
100
|
+
scheme?.removeEventListener?.("change", retheme);
|
|
84
101
|
if (frame) cancelAnimationFrame(frame);
|
|
85
102
|
chart.dispose();
|
|
86
103
|
chartRef.current = null;
|
|
@@ -89,10 +106,11 @@ function ChartCore({
|
|
|
89
106
|
}, [engine, hasEmpty]);
|
|
90
107
|
useEffect(() => {
|
|
91
108
|
const chart = chartRef.current;
|
|
92
|
-
|
|
93
|
-
chart
|
|
109
|
+
const element = containerRef.current;
|
|
110
|
+
if (!chart || !element || appliedOptionRef.current === option) return;
|
|
111
|
+
chart.setOption(motionSafe(themed(option, theme, element)), { notMerge: false });
|
|
94
112
|
appliedOptionRef.current = option;
|
|
95
|
-
}, [option]);
|
|
113
|
+
}, [option, theme]);
|
|
96
114
|
const heightValue = typeof height === "number" ? `${height}px` : height;
|
|
97
115
|
const effectiveAriaLabel = ariaLabel ?? title ?? labels.chart;
|
|
98
116
|
const hasTable = Boolean(tableData?.length && tableHeaders?.length);
|
|
@@ -16,13 +16,26 @@ interface ChipProps extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
|
|
|
16
16
|
/** A link chip. */
|
|
17
17
|
href?: string;
|
|
18
18
|
renderLink?: UixRenderLink;
|
|
19
|
+
/**
|
|
20
|
+
* A link chip for the page that is open: `aria-current="page"` and the filled look
|
|
21
|
+
* (HAR-1632). `pressed` gives the same look but says "toggle button", which a link is not.
|
|
22
|
+
*/
|
|
23
|
+
current?: boolean;
|
|
19
24
|
/** Adds a × button that removes the chip. */
|
|
20
25
|
onRemove?: () => void;
|
|
21
26
|
/**
|
|
22
27
|
* Accessible name of the × button. `{label}` is replaced by the chip text when it is a
|
|
23
|
-
* string. Default "Remove {label}"; pass a full name
|
|
28
|
+
* string. Default: `UixLabelsProvider` `chip.remove`, then "Remove {label}"; pass a full name
|
|
29
|
+
* when the text is not a string.
|
|
24
30
|
*/
|
|
25
31
|
removeLabel?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Attributes for the body: the element that carries the chip's own action. On a plain chip
|
|
34
|
+
* that is the chip itself, where the other props already go; on a removable chip the other
|
|
35
|
+
* props go to the wrapper, so this is how `aria-haspopup`, `aria-expanded`, `aria-controls`
|
|
36
|
+
* or an `id` reach the button that opens the filter editor (HAR-1632).
|
|
37
|
+
*/
|
|
38
|
+
bodyProps?: HTMLAttributes<HTMLElement>;
|
|
26
39
|
/** `add` is the dashed "+ Add filter" chip. */
|
|
27
40
|
variant?: 'default' | 'add';
|
|
28
41
|
disabled?: boolean;
|
|
@@ -30,8 +43,10 @@ interface ChipProps extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
|
|
|
30
43
|
/**
|
|
31
44
|
* Filter, tag and token chip over `.uix-chip` (HAR-1360; TENSOR C7/B34, MOTUS C-6/B-A18).
|
|
32
45
|
* Plain text, a toggle, a link or a button, optionally with a × that removes it.
|
|
46
|
+
* The ref goes to the body (the button, link or span that carries the chip's action), also on
|
|
47
|
+
* a removable chip: anchor a popover to it and return focus to it without a DOM lookup.
|
|
33
48
|
*/
|
|
34
|
-
declare
|
|
49
|
+
declare const Chip: react.ForwardRefExoticComponent<ChipProps & react.RefAttributes<HTMLElement>>;
|
|
35
50
|
interface ChipGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
36
51
|
/** Names the group, e.g. "Active filters". */
|
|
37
52
|
label?: string;
|
package/dist/components/Chip.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
2
3
|
import { cx } from "../cx.js";
|
|
3
|
-
import { fillLabel } from "../fill-label.js";
|
|
4
4
|
import { renderUixLink } from "../link.js";
|
|
5
|
-
|
|
5
|
+
import { ChipRemoveButton } from "./ChipRemove.js";
|
|
6
|
+
const Chip = forwardRef(function Chip2({
|
|
6
7
|
children,
|
|
7
8
|
icon,
|
|
8
9
|
count,
|
|
@@ -11,15 +12,19 @@ function Chip({
|
|
|
11
12
|
onClick,
|
|
12
13
|
href,
|
|
13
14
|
renderLink,
|
|
15
|
+
current,
|
|
14
16
|
onRemove,
|
|
15
|
-
removeLabel
|
|
17
|
+
removeLabel,
|
|
18
|
+
bodyProps,
|
|
16
19
|
variant = "default",
|
|
17
20
|
disabled,
|
|
18
21
|
className,
|
|
19
22
|
...props
|
|
20
|
-
}) {
|
|
23
|
+
}, ref) {
|
|
21
24
|
const toggle = pressed !== void 0;
|
|
22
25
|
const interactive = toggle || onClick != null || href != null;
|
|
26
|
+
const link = href != null && !disabled;
|
|
27
|
+
const on = pressed || link && current || void 0;
|
|
23
28
|
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
24
29
|
icon != null && /* @__PURE__ */ jsx("span", { className: "uix-chip__icon", "aria-hidden": "true", children: icon }),
|
|
25
30
|
/* @__PURE__ */ jsx("span", { className: "uix-chip__label", children }),
|
|
@@ -38,7 +43,17 @@ function Chip({
|
|
|
38
43
|
className
|
|
39
44
|
);
|
|
40
45
|
const main = (classes, extra) => {
|
|
41
|
-
if (
|
|
46
|
+
if (link) {
|
|
47
|
+
return renderUixLink(renderLink, {
|
|
48
|
+
href,
|
|
49
|
+
className: classes,
|
|
50
|
+
children: body,
|
|
51
|
+
"aria-current": current ? "page" : void 0,
|
|
52
|
+
...extra,
|
|
53
|
+
...bodyProps,
|
|
54
|
+
ref
|
|
55
|
+
});
|
|
56
|
+
}
|
|
42
57
|
if (interactive) {
|
|
43
58
|
return /* @__PURE__ */ jsx(
|
|
44
59
|
"button",
|
|
@@ -49,21 +64,30 @@ function Chip({
|
|
|
49
64
|
disabled,
|
|
50
65
|
onClick: handleClick,
|
|
51
66
|
...extra,
|
|
67
|
+
...bodyProps,
|
|
68
|
+
ref,
|
|
52
69
|
children: body
|
|
53
70
|
}
|
|
54
71
|
);
|
|
55
72
|
}
|
|
56
|
-
return /* @__PURE__ */ jsx("span", { className: classes, ...extra, children: body });
|
|
73
|
+
return /* @__PURE__ */ jsx("span", { className: classes, ...extra, ...bodyProps, ref, children: body });
|
|
57
74
|
};
|
|
58
75
|
if (!onRemove) {
|
|
59
|
-
return /* @__PURE__ */ jsx(Fragment, { children: main(outer, { ...props, "data-on":
|
|
76
|
+
return /* @__PURE__ */ jsx(Fragment, { children: main(outer, { ...props, "data-on": on }) });
|
|
60
77
|
}
|
|
61
|
-
|
|
62
|
-
return /* @__PURE__ */ jsxs("span", { className: outer, "data-on": pressed || void 0, ...props, children: [
|
|
78
|
+
return /* @__PURE__ */ jsxs("span", { className: outer, "data-on": on, ...props, children: [
|
|
63
79
|
main("uix-chip__main"),
|
|
64
|
-
/* @__PURE__ */ jsx(
|
|
80
|
+
/* @__PURE__ */ jsx(
|
|
81
|
+
ChipRemoveButton,
|
|
82
|
+
{
|
|
83
|
+
label: removeLabel,
|
|
84
|
+
text: typeof children === "string" || typeof children === "number" ? String(children) : "",
|
|
85
|
+
disabled,
|
|
86
|
+
onRemove
|
|
87
|
+
}
|
|
88
|
+
)
|
|
65
89
|
] });
|
|
66
|
-
}
|
|
90
|
+
});
|
|
67
91
|
function ChipGroup({ label, children, className, ...props }) {
|
|
68
92
|
return /* @__PURE__ */ jsx("div", { role: "group", "aria-label": label, className: cx("uix-chip-group", className), ...props, children });
|
|
69
93
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
|
|
3
|
+
interface ChipRemoveButtonProps {
|
|
4
|
+
/** The chip's `removeLabel`, when one was passed. */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** The chip text for `{label}`; empty when the chip's children are not text. */
|
|
7
|
+
text: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
onRemove: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The × of a removable `Chip`. Internal. Its own client component so its name can come from
|
|
13
|
+
* `UixLabelsProvider` (`chip.remove`) while `Chip` itself stays server-renderable (HAR-1632).
|
|
14
|
+
*/
|
|
15
|
+
declare function ChipRemoveButton({ label, text, disabled, onRemove }: ChipRemoveButtonProps): react.JSX.Element;
|
|
16
|
+
|
|
17
|
+
export { ChipRemoveButton };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { XIcon } from "../icons/components.js";
|
|
4
|
+
import { fillLabel } from "../fill-label.js";
|
|
5
|
+
import { useUixLabels } from "../labels-context.js";
|
|
6
|
+
function ChipRemoveButton({ label, text, disabled, onRemove }) {
|
|
7
|
+
const provided = useUixLabels().chip?.remove;
|
|
8
|
+
const template = label ?? provided ?? "Remove {label}";
|
|
9
|
+
return /* @__PURE__ */ jsx("button", { type: "button", className: "uix-chip__remove", "aria-label": fillLabel(template, { label: text }).trim(), disabled, onClick: onRemove, children: /* @__PURE__ */ jsx(XIcon, {}) });
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
ChipRemoveButton
|
|
13
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, Ref } from 'react';
|
|
3
|
+
|
|
4
|
+
type CollapsibleHeadingLevel = 2 | 3 | 4 | 5 | 6;
|
|
5
|
+
/** Root classes shared by the plain and the stateful `CollapsibleSection`. */
|
|
6
|
+
declare const collapsibleClass: (compact: boolean | undefined, className: string | undefined) => string;
|
|
7
|
+
/** With a heading level the body is a region named by the title. */
|
|
8
|
+
declare const collapsibleBodyProps: (headingLevel: CollapsibleHeadingLevel | undefined, titleId: string) => {
|
|
9
|
+
role: string;
|
|
10
|
+
'aria-labelledby': string;
|
|
11
|
+
} | {
|
|
12
|
+
role?: undefined;
|
|
13
|
+
'aria-labelledby'?: undefined;
|
|
14
|
+
};
|
|
15
|
+
interface CollapsibleSummaryProps {
|
|
16
|
+
title: ReactNode;
|
|
17
|
+
summary?: ReactNode;
|
|
18
|
+
headingLevel?: CollapsibleHeadingLevel;
|
|
19
|
+
titleId: string;
|
|
20
|
+
summaryRef?: Ref<HTMLElement>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The `<summary>` row of a `CollapsibleSection`. Internal. With `headingLevel` the title sits
|
|
24
|
+
* in a real heading (a heading is the one block element a `<summary>` may hold), so the section
|
|
25
|
+
* shows up in the page outline; without it the markup is what 2.31.0 rendered.
|
|
26
|
+
*/
|
|
27
|
+
declare function CollapsibleSummary({ title, summary, headingLevel, titleId, summaryRef }: CollapsibleSummaryProps): react.JSX.Element;
|
|
28
|
+
|
|
29
|
+
export { type CollapsibleHeadingLevel, CollapsibleSummary, collapsibleBodyProps, collapsibleClass };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from "../cx.js";
|
|
3
|
+
import { ChevronDownIcon } from "../icons/components.js";
|
|
4
|
+
const collapsibleClass = (compact, className) => cx("uix-collapsible", compact && "uix-collapsible--compact", className);
|
|
5
|
+
const collapsibleBodyProps = (headingLevel, titleId) => headingLevel ? { role: "region", "aria-labelledby": titleId } : {};
|
|
6
|
+
function CollapsibleSummary({ title, summary, headingLevel, titleId, summaryRef }) {
|
|
7
|
+
const Heading = headingLevel ? `h${headingLevel}` : "span";
|
|
8
|
+
return /* @__PURE__ */ jsxs("summary", { ref: summaryRef, className: "uix-collapsible__summary", children: [
|
|
9
|
+
/* @__PURE__ */ jsxs(Heading, { className: headingLevel ? "uix-collapsible__heading" : void 0, children: [
|
|
10
|
+
/* @__PURE__ */ jsx("span", { id: headingLevel ? titleId : void 0, className: "uix-collapsible__title", children: title }),
|
|
11
|
+
summary != null && /* @__PURE__ */ jsx("span", { className: "uix-collapsible__meta", children: summary })
|
|
12
|
+
] }),
|
|
13
|
+
/* @__PURE__ */ jsx("span", { className: "uix-collapsible__chevron", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronDownIcon, { size: "sm" }) })
|
|
14
|
+
] });
|
|
15
|
+
}
|
|
16
|
+
export {
|
|
17
|
+
CollapsibleSummary,
|
|
18
|
+
collapsibleBodyProps,
|
|
19
|
+
collapsibleClass
|
|
20
|
+
};
|
|
@@ -5,6 +5,12 @@ interface CollapsibleSectionProps extends Omit<DetailsHTMLAttributes<HTMLDetails
|
|
|
5
5
|
title: ReactNode;
|
|
6
6
|
summary?: ReactNode;
|
|
7
7
|
children?: ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Open when first rendered. With `persistKey`, what the person last chose wins over it:
|
|
10
|
+
* "open by default, but remember" (HAR-1628). `open` is the same starting state and also
|
|
11
|
+
* re-applies whenever the parent changes it.
|
|
12
|
+
*/
|
|
13
|
+
defaultOpen?: boolean;
|
|
8
14
|
/**
|
|
9
15
|
* Mount the body only while it is needed (HAR-1352; TENSOR B32): `true` mounts it while
|
|
10
16
|
* open and unmounts it on close; `'keep'` mounts it on first open and keeps it. For
|
|
@@ -12,12 +18,33 @@ interface CollapsibleSectionProps extends Omit<DetailsHTMLAttributes<HTMLDetails
|
|
|
12
18
|
*/
|
|
13
19
|
lazy?: boolean | 'keep';
|
|
14
20
|
/**
|
|
15
|
-
* Remember the open state
|
|
16
|
-
*
|
|
21
|
+
* Remember the open state under this key (`uix:collapsible:<key>`), in `persistStorage`.
|
|
22
|
+
* Applied after mount, so the server HTML uses `open` / `defaultOpen`; a remembered state
|
|
23
|
+
* then replaces either.
|
|
17
24
|
*/
|
|
18
25
|
persistKey?: string;
|
|
19
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Where `persistKey` keeps the state: `'session'` (`sessionStorage`, this tab until it
|
|
28
|
+
* closes) or `'local'` (`localStorage`, across sessions). Default `'session'`.
|
|
29
|
+
*/
|
|
30
|
+
persistStorage?: 'session' | 'local';
|
|
31
|
+
/**
|
|
32
|
+
* Each new value opens the section, scrolls it into view and moves focus to its summary
|
|
33
|
+
* ("jump to SLA"). A value already set when the section mounts is honoured too (a deep link
|
|
34
|
+
* that lands on a closed section); `undefined` and `0` mean "no request yet", so a counter
|
|
35
|
+
* that starts at 0 does not open anything.
|
|
36
|
+
*/
|
|
20
37
|
openRequest?: number;
|
|
38
|
+
/**
|
|
39
|
+
* A quiet row for a section inside a card: no border or background of its own, tighter
|
|
40
|
+
* padding, a rule between neighbouring compact sections.
|
|
41
|
+
*/
|
|
42
|
+
compact?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Render the title as a heading of this level, and make the body a `role="region"` named by
|
|
45
|
+
* it. Leave unset for a section that should stay out of the page outline.
|
|
46
|
+
*/
|
|
47
|
+
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
21
48
|
}
|
|
22
49
|
/**
|
|
23
50
|
* A `<details>` disclosure. Without `lazy`, `persistKey` or `openRequest` it is a plain,
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { CollapsibleSummary, collapsibleBodyProps, collapsibleClass } from "./CollapsibleParts.js";
|
|
3
4
|
import { CollapsibleSectionState } from "./CollapsibleSectionState.js";
|
|
4
5
|
function CollapsibleSection(props) {
|
|
5
|
-
const { title, summary, children, className, lazy, persistKey, openRequest, ...rest } = props;
|
|
6
|
+
const { title, summary, children, className, lazy, persistKey, persistStorage, openRequest, open, defaultOpen, compact, headingLevel, ...rest } = props;
|
|
7
|
+
const titleId = useId();
|
|
6
8
|
if (lazy || persistKey !== void 0 || openRequest !== void 0) return /* @__PURE__ */ jsx(CollapsibleSectionState, { ...props });
|
|
7
|
-
return /* @__PURE__ */ jsxs("details", { className:
|
|
8
|
-
/* @__PURE__ */
|
|
9
|
-
|
|
10
|
-
/* @__PURE__ */ jsx("span", { className: "uix-collapsible__title", children: title }),
|
|
11
|
-
summary != null && /* @__PURE__ */ jsx("span", { className: "uix-collapsible__meta", children: summary })
|
|
12
|
-
] }),
|
|
13
|
-
/* @__PURE__ */ jsx("span", { className: "uix-collapsible__chevron", "aria-hidden": "true", children: "\u2304" })
|
|
14
|
-
] }),
|
|
15
|
-
/* @__PURE__ */ jsx("div", { className: "uix-collapsible__body", children })
|
|
9
|
+
return /* @__PURE__ */ jsxs("details", { className: collapsibleClass(compact, className), ...rest, open: open ?? defaultOpen, children: [
|
|
10
|
+
/* @__PURE__ */ jsx(CollapsibleSummary, { title, summary, headingLevel, titleId }),
|
|
11
|
+
/* @__PURE__ */ jsx("div", { className: "uix-collapsible__body", ...collapsibleBodyProps(headingLevel, titleId), children })
|
|
16
12
|
] });
|
|
17
13
|
}
|
|
18
14
|
export {
|
|
@@ -6,6 +6,6 @@ import { CollapsibleSectionProps } from './CollapsibleSection.js';
|
|
|
6
6
|
* state and `openRequest`. Internal — `CollapsibleSection` delegates here only when one
|
|
7
7
|
* of those props is set, so the plain section stays server-renderable and JS-free.
|
|
8
8
|
*/
|
|
9
|
-
declare function CollapsibleSectionState({ title, summary, children, className, lazy, persistKey, openRequest, open, onToggle, ...props }: CollapsibleSectionProps): react.JSX.Element;
|
|
9
|
+
declare function CollapsibleSectionState({ title, summary, children, className, lazy, persistKey, persistStorage, openRequest, open, defaultOpen, compact, headingLevel, onToggle, ...props }: CollapsibleSectionProps): react.JSX.Element;
|
|
10
10
|
|
|
11
11
|
export { CollapsibleSectionState };
|
|
@@ -1,36 +1,59 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useRef, useState } from "react";
|
|
4
|
-
import {
|
|
3
|
+
import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
4
|
+
import { CollapsibleSummary, collapsibleBodyProps, collapsibleClass } from "./CollapsibleParts.js";
|
|
5
|
+
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
5
6
|
const STORAGE_PREFIX = "uix:collapsible:";
|
|
6
|
-
const
|
|
7
|
+
const storageOf = (kind) => kind === "local" ? window.localStorage : window.sessionStorage;
|
|
8
|
+
const readStored = (kind, key) => {
|
|
7
9
|
try {
|
|
8
|
-
const value =
|
|
10
|
+
const value = storageOf(kind).getItem(STORAGE_PREFIX + key);
|
|
9
11
|
return value === "1" ? true : value === "0" ? false : void 0;
|
|
10
12
|
} catch {
|
|
11
13
|
return void 0;
|
|
12
14
|
}
|
|
13
15
|
};
|
|
14
|
-
const writeStored = (key, open) => {
|
|
16
|
+
const writeStored = (kind, key, open) => {
|
|
15
17
|
try {
|
|
16
|
-
|
|
18
|
+
storageOf(kind).setItem(STORAGE_PREFIX + key, open ? "1" : "0");
|
|
17
19
|
} catch {
|
|
18
20
|
}
|
|
19
21
|
};
|
|
20
|
-
function CollapsibleSectionState({
|
|
22
|
+
function CollapsibleSectionState({
|
|
23
|
+
title,
|
|
24
|
+
summary,
|
|
25
|
+
children,
|
|
26
|
+
className,
|
|
27
|
+
lazy,
|
|
28
|
+
persistKey,
|
|
29
|
+
persistStorage = "session",
|
|
30
|
+
openRequest,
|
|
31
|
+
open,
|
|
32
|
+
defaultOpen,
|
|
33
|
+
compact,
|
|
34
|
+
headingLevel,
|
|
35
|
+
onToggle,
|
|
36
|
+
...props
|
|
37
|
+
}) {
|
|
21
38
|
const ref = useRef(null);
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
39
|
+
const summaryRef = useRef(null);
|
|
40
|
+
const titleId = useId();
|
|
41
|
+
const initial = open ?? defaultOpen ?? false;
|
|
42
|
+
const [isOpen, setIsOpen] = useState(initial);
|
|
43
|
+
const [hasOpened, setHasOpened] = useState(initial);
|
|
44
|
+
const lastOpen = useRef(open);
|
|
45
|
+
const lastRequest = useRef(openRequest ? void 0 : openRequest);
|
|
25
46
|
useEffect(() => {
|
|
26
47
|
if (!persistKey) return;
|
|
27
|
-
const stored = readStored(persistKey);
|
|
48
|
+
const stored = readStored(persistStorage, persistKey);
|
|
28
49
|
if (stored !== void 0) {
|
|
29
50
|
setIsOpen(stored);
|
|
30
51
|
if (stored) setHasOpened(true);
|
|
31
52
|
}
|
|
32
|
-
}, [persistKey]);
|
|
53
|
+
}, [persistKey, persistStorage]);
|
|
33
54
|
useEffect(() => {
|
|
55
|
+
if (open === lastOpen.current) return;
|
|
56
|
+
lastOpen.current = open;
|
|
34
57
|
if (open === void 0) return;
|
|
35
58
|
setIsOpen(open);
|
|
36
59
|
if (open) setHasOpened(true);
|
|
@@ -40,26 +63,30 @@ function CollapsibleSectionState({ title, summary, children, className, lazy, pe
|
|
|
40
63
|
lastRequest.current = openRequest;
|
|
41
64
|
setIsOpen(true);
|
|
42
65
|
setHasOpened(true);
|
|
43
|
-
if (persistKey) writeStored(persistKey, true);
|
|
44
|
-
requestAnimationFrame(() =>
|
|
45
|
-
|
|
66
|
+
if (persistKey) writeStored(persistStorage, persistKey, true);
|
|
67
|
+
requestAnimationFrame(() => {
|
|
68
|
+
ref.current?.scrollIntoView?.({ block: "nearest" });
|
|
69
|
+
summaryRef.current?.focus({ preventScroll: true });
|
|
70
|
+
});
|
|
71
|
+
}, [openRequest, persistKey, persistStorage]);
|
|
72
|
+
const renderedOpen = useRef(isOpen);
|
|
73
|
+
useIsomorphicLayoutEffect(() => {
|
|
74
|
+
renderedOpen.current = isOpen;
|
|
75
|
+
}, [isOpen]);
|
|
46
76
|
const handleToggle = (event) => {
|
|
47
77
|
const next = event.currentTarget.open;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
78
|
+
if (next !== renderedOpen.current) {
|
|
79
|
+
renderedOpen.current = next;
|
|
80
|
+
setIsOpen(next);
|
|
81
|
+
if (next) setHasOpened(true);
|
|
82
|
+
if (persistKey) writeStored(persistStorage, persistKey, next);
|
|
83
|
+
}
|
|
51
84
|
onToggle?.(event);
|
|
52
85
|
};
|
|
53
86
|
const mountBody = !lazy || (lazy === "keep" ? hasOpened : isOpen);
|
|
54
|
-
return /* @__PURE__ */ jsxs("details", { ref, className:
|
|
55
|
-
/* @__PURE__ */
|
|
56
|
-
|
|
57
|
-
/* @__PURE__ */ jsx("span", { className: "uix-collapsible__title", children: title }),
|
|
58
|
-
summary != null && /* @__PURE__ */ jsx("span", { className: "uix-collapsible__meta", children: summary })
|
|
59
|
-
] }),
|
|
60
|
-
/* @__PURE__ */ jsx("span", { className: "uix-collapsible__chevron", "aria-hidden": "true", children: "\u2304" })
|
|
61
|
-
] }),
|
|
62
|
-
/* @__PURE__ */ jsx("div", { className: "uix-collapsible__body", children: mountBody ? children : null })
|
|
87
|
+
return /* @__PURE__ */ jsxs("details", { ref, className: collapsibleClass(compact, className), ...props, open: isOpen, onToggle: handleToggle, children: [
|
|
88
|
+
/* @__PURE__ */ jsx(CollapsibleSummary, { title, summary, headingLevel, titleId, summaryRef }),
|
|
89
|
+
/* @__PURE__ */ jsx("div", { className: "uix-collapsible__body", ...collapsibleBodyProps(headingLevel, titleId), children: mountBody ? children : null })
|
|
63
90
|
] });
|
|
64
91
|
}
|
|
65
92
|
export {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HTMLAttributes } from 'react';
|
|
3
|
+
|
|
4
|
+
/** Columns at one width. */
|
|
5
|
+
type DashboardGridColumnCount = 1 | 2 | 3 | 4;
|
|
6
|
+
/**
|
|
7
|
+
* A width of the GRID itself (a container query), not of the viewport:
|
|
8
|
+
* `base` any width, `sm` ≥ 36rem, `md` ≥ 48rem, `lg` ≥ 60rem, `xl` ≥ 80rem.
|
|
9
|
+
*/
|
|
10
|
+
type DashboardGridBreakpoint = 'base' | 'sm' | 'md' | 'lg' | 'xl';
|
|
11
|
+
/**
|
|
12
|
+
* One column count for every width, or a count per breakpoint. A breakpoint you leave out keeps
|
|
13
|
+
* the count of the next smaller one, and a missing `base` is 1.
|
|
14
|
+
*/
|
|
15
|
+
type DashboardGridColumns = DashboardGridColumnCount | Partial<Record<DashboardGridBreakpoint, DashboardGridColumnCount>>;
|
|
16
|
+
/** How much of the row an item takes. `2` clamps to the columns there are; `full` is always the whole row. */
|
|
17
|
+
type DashboardGridSpan = 1 | 2 | 'full';
|
|
18
|
+
/** Space between items: `sm` --uix-space-3, `md` --uix-space-4, `lg` --uix-space-6. */
|
|
19
|
+
type DashboardGridGap = 'sm' | 'md' | 'lg';
|
|
20
|
+
interface DashboardGridProps extends HTMLAttributes<HTMLDivElement> {
|
|
21
|
+
/** Default: 1 column, 2 from `sm`, 3 from `lg` (of the grid's own width). */
|
|
22
|
+
columns?: DashboardGridColumns;
|
|
23
|
+
/** Default `md`. */
|
|
24
|
+
gap?: DashboardGridGap;
|
|
25
|
+
}
|
|
26
|
+
interface DashboardGridItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
27
|
+
/** Default `1`. */
|
|
28
|
+
span?: DashboardGridSpan;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The class list of a dashboard grid, for markup you render yourself (a server template, or a
|
|
32
|
+
* drag-and-drop library's own element). Same output as `<DashboardGrid>`.
|
|
33
|
+
*/
|
|
34
|
+
declare function dashboardGridClassName(columns?: DashboardGridColumns, gap?: DashboardGridGap): string;
|
|
35
|
+
/** The class list of a dashboard grid item, for markup you render yourself. */
|
|
36
|
+
declare function dashboardGridItemClassName(span?: DashboardGridSpan): string;
|
|
37
|
+
/**
|
|
38
|
+
* A cell of a {@link DashboardGrid}: one column, two, or the full row. It is an inline-size
|
|
39
|
+
* container (container name: uix-dashboard-grid-item), so its content can size from it, e.g. a Chart with
|
|
40
|
+
* `height="clamp(160px, 40cqi, 360px)"`. Widgets in one row share a height.
|
|
41
|
+
*/
|
|
42
|
+
declare const DashboardGridItem: react.ForwardRefExoticComponent<DashboardGridItemProps & react.RefAttributes<HTMLDivElement>>;
|
|
43
|
+
/**
|
|
44
|
+
* The layout grid for dashboard widgets (HAR-1555). The column count follows the grid's own
|
|
45
|
+
* width, so it adapts to a sidebar as well as to the screen. Pure layout: drag-and-drop,
|
|
46
|
+
* persistence and widget chrome stay in the product. Server-renderable.
|
|
47
|
+
*
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <DashboardGrid columns={{ base: 1, sm: 2, lg: 3 }}>
|
|
50
|
+
* <DashboardGrid.Item><Stat … /></DashboardGrid.Item>
|
|
51
|
+
* <DashboardGrid.Item span={2}>…</DashboardGrid.Item>
|
|
52
|
+
* <DashboardGrid.Item span="full"><Chart … /></DashboardGrid.Item>
|
|
53
|
+
* </DashboardGrid>
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
declare const DashboardGrid: react.ForwardRefExoticComponent<DashboardGridProps & react.RefAttributes<HTMLDivElement>> & {
|
|
57
|
+
Item: react.ForwardRefExoticComponent<DashboardGridItemProps & react.RefAttributes<HTMLDivElement>>;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export { DashboardGrid, type DashboardGridBreakpoint, type DashboardGridColumnCount, type DashboardGridColumns, type DashboardGridGap, DashboardGridItem, type DashboardGridItemProps, type DashboardGridProps, type DashboardGridSpan, dashboardGridClassName, dashboardGridItemClassName };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cx } from "../cx.js";
|
|
4
|
+
const BREAKPOINTS = ["sm", "md", "lg", "xl"];
|
|
5
|
+
const count = (value, fallback) => {
|
|
6
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
7
|
+
return Math.min(4, Math.max(1, Math.round(value)));
|
|
8
|
+
};
|
|
9
|
+
function dashboardGridClassName(columns, gap = "md") {
|
|
10
|
+
const classes = ["uix-dashboard-grid"];
|
|
11
|
+
if (gap === "sm" || gap === "lg") classes.push(`uix-dashboard-grid--gap-${gap}`);
|
|
12
|
+
if (columns == null) return classes.join(" ");
|
|
13
|
+
if (typeof columns === "number") {
|
|
14
|
+
classes.push(`uix-dashboard-grid--cols-${count(columns, 1)}`);
|
|
15
|
+
return classes.join(" ");
|
|
16
|
+
}
|
|
17
|
+
classes.push(`uix-dashboard-grid--cols-${count(columns.base, 1)}`);
|
|
18
|
+
for (const breakpoint of BREAKPOINTS) {
|
|
19
|
+
if (columns[breakpoint] != null) classes.push(`uix-dashboard-grid--${breakpoint}-cols-${count(columns[breakpoint], 1)}`);
|
|
20
|
+
}
|
|
21
|
+
return classes.join(" ");
|
|
22
|
+
}
|
|
23
|
+
function dashboardGridItemClassName(span = 1) {
|
|
24
|
+
if (span === "full") return "uix-dashboard-grid__item uix-dashboard-grid__item--full";
|
|
25
|
+
if (span === 2) return "uix-dashboard-grid__item uix-dashboard-grid__item--span-2";
|
|
26
|
+
return "uix-dashboard-grid__item";
|
|
27
|
+
}
|
|
28
|
+
const DashboardGridItem = forwardRef(function DashboardGridItem2({ span = 1, className, ...props }, ref) {
|
|
29
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cx(dashboardGridItemClassName(span), className), ...props });
|
|
30
|
+
});
|
|
31
|
+
const DashboardGridRoot = forwardRef(function DashboardGrid({ columns, gap = "md", className, ...props }, ref) {
|
|
32
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cx(dashboardGridClassName(columns, gap), className), ...props });
|
|
33
|
+
});
|
|
34
|
+
const DashboardGrid2 = Object.assign(DashboardGridRoot, { Item: DashboardGridItem });
|
|
35
|
+
export {
|
|
36
|
+
DashboardGrid2 as DashboardGrid,
|
|
37
|
+
DashboardGridItem,
|
|
38
|
+
dashboardGridClassName,
|
|
39
|
+
dashboardGridItemClassName
|
|
40
|
+
};
|
|
@@ -35,8 +35,14 @@ interface DateRangePickerProps {
|
|
|
35
35
|
className?: string;
|
|
36
36
|
/** Translatable words; English defaults in {@link DEFAULT_DATE_RANGE_PICKER_LABELS}. */
|
|
37
37
|
labels?: Partial<DateRangePickerLabels>;
|
|
38
|
+
/**
|
|
39
|
+
* The ISO date (`YYYY-MM-DD`) that gets the today ring and `aria-current="date"`.
|
|
40
|
+
* Defaults to the viewer's local date; pass it when rendering on a server (whose
|
|
41
|
+
* clock and zone may differ from the viewer's), or `null` for no today marker.
|
|
42
|
+
*/
|
|
43
|
+
today?: string | null;
|
|
38
44
|
}
|
|
39
45
|
/** Controlled, two-month range picker using the UIx calendar language. */
|
|
40
|
-
declare function DateRangePicker({ value, onChange, visibleMonth: controlledMonth, onVisibleMonthChange, months, min, max, isDateDisabled, locale, label, invalid, invalidMessage, disabled, className, labels: labelOverrides, }: DateRangePickerProps): react.JSX.Element;
|
|
46
|
+
declare function DateRangePicker({ value, onChange, visibleMonth: controlledMonth, onVisibleMonthChange, months, min, max, isDateDisabled, locale, label, invalid, invalidMessage, disabled, className, labels: labelOverrides, today, }: DateRangePickerProps): react.JSX.Element;
|
|
41
47
|
|
|
42
48
|
export { DEFAULT_DATE_RANGE_PICKER_LABELS, DateRangePicker, type DateRangePickerLabels, type DateRangePickerProps };
|