@oprt/react 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +105 -2
- package/dist/ActionStatus/ActionStatus.js +104 -0
- package/dist/ActionStatus/ActionStatus.manifest.js +64 -0
- package/dist/ActionStatus/index.js +7 -0
- package/dist/Alert/Alert.js +86 -0
- package/dist/Alert/Alert.manifest.js +36 -0
- package/dist/Alert/index.js +7 -0
- package/dist/AppShell/AppShell.js +103 -0
- package/dist/AppShell/AppShell.manifest.js +66 -0
- package/dist/AppShell/index.js +9 -0
- package/dist/AttachmentPreview/AttachmentPreview.js +145 -0
- package/dist/AttachmentPreview/AttachmentPreview.manifest.js +42 -0
- package/dist/AttachmentPreview/index.js +9 -0
- package/dist/Avatar/Avatar.js +87 -0
- package/dist/Avatar/Avatar.manifest.js +25 -0
- package/dist/Avatar/index.js +7 -0
- package/dist/Badge/Badge.js +124 -0
- package/dist/Badge/Badge.manifest.js +41 -0
- package/dist/Badge/index.js +7 -0
- package/dist/Breadcrumb/Breadcrumb.js +64 -0
- package/dist/Breadcrumb/Breadcrumb.manifest.js +28 -0
- package/dist/Breadcrumb/index.js +7 -0
- package/dist/BulkActionBar/BulkActionBar.js +145 -0
- package/dist/BulkActionBar/BulkActionBar.manifest.js +46 -0
- package/dist/BulkActionBar/index.js +9 -0
- package/dist/BulkActionBar/selection.js +44 -0
- package/dist/BulkResults/BulkResults.js +269 -0
- package/dist/BulkResults/BulkResults.manifest.js +45 -0
- package/dist/BulkResults/index.js +7 -0
- package/dist/Button/Button.js +170 -0
- package/dist/Button/Button.manifest.js +36 -0
- package/dist/Button/index.js +7 -0
- package/dist/ButtonGroup/ButtonGroup.js +60 -0
- package/dist/ButtonGroup/ButtonGroup.manifest.js +22 -0
- package/dist/ButtonGroup/index.js +7 -0
- package/dist/Calendar/Calendar.js +106 -0
- package/dist/Calendar/Calendar.manifest.js +32 -0
- package/dist/Calendar/index.js +7 -0
- package/dist/Card/Card.js +70 -0
- package/dist/Card/Card.manifest.js +57 -0
- package/dist/Card/index.js +16 -0
- package/dist/Chart/Chart.js +1200 -0
- package/dist/Chart/Chart.manifest.js +110 -0
- package/dist/Chart/ChartControls.js +190 -0
- package/dist/Chart/geometry.js +217 -0
- package/dist/Chart/index.js +9 -0
- package/dist/Checkbox/Checkbox.js +166 -0
- package/dist/Checkbox/Checkbox.manifest.js +38 -0
- package/dist/Checkbox/index.js +7 -0
- package/dist/Collapsible/Collapsible.js +177 -0
- package/dist/Collapsible/Collapsible.manifest.js +54 -0
- package/dist/Collapsible/index.js +12 -0
- package/dist/CollectionState/CollectionState.js +68 -0
- package/dist/CollectionState/CollectionState.manifest.js +44 -0
- package/dist/CollectionState/index.js +7 -0
- package/dist/ComboBox/ComboBox.js +221 -0
- package/dist/ComboBox/ComboBox.manifest.js +87 -0
- package/dist/ComboBox/index.js +7 -0
- package/dist/CommandPalette/CommandPalette.js +276 -0
- package/dist/CommandPalette/CommandPalette.manifest.js +24 -0
- package/dist/CommandPalette/index.js +7 -0
- package/dist/ConfirmDialog/ConfirmDialog.js +84 -0
- package/dist/ConfirmDialog/ConfirmDialog.manifest.js +32 -0
- package/dist/ConfirmDialog/index.js +7 -0
- package/dist/ContextMenu/ContextMenu.js +192 -0
- package/dist/ContextMenu/ContextMenu.manifest.js +41 -0
- package/dist/ContextMenu/index.js +7 -0
- package/dist/CoordinateInput/CoordinateInput.js +141 -0
- package/dist/CoordinateInput/CoordinateInput.manifest.js +36 -0
- package/dist/CoordinateInput/index.js +9 -0
- package/dist/CopyButton/CopyButton.js +129 -0
- package/dist/CopyButton/CopyButton.manifest.js +35 -0
- package/dist/CopyButton/index.js +7 -0
- package/dist/DataGrid/DataGrid.js +459 -0
- package/dist/DataGrid/DataGrid.manifest.js +135 -0
- package/dist/DataGrid/PageControls.js +41 -0
- package/dist/DataGrid/index.js +9 -0
- package/dist/DataTable/DataTable.js +375 -0
- package/dist/DataTable/DataTable.manifest.js +86 -0
- package/dist/DataTable/index.js +9 -0
- package/dist/DataTable/sort.js +45 -0
- package/dist/DataTable/useScrollsSideways.js +61 -0
- package/dist/DataTable/useTableWindow.js +185 -0
- package/dist/DatePicker/DatePicker.js +187 -0
- package/dist/DatePicker/DatePicker.manifest.js +40 -0
- package/dist/DatePicker/index.js +7 -0
- package/dist/DateRangePicker/DateRangePicker.js +156 -0
- package/dist/DateRangePicker/DateRangePicker.manifest.js +41 -0
- package/dist/DateRangePicker/index.js +7 -0
- package/dist/DateTimePicker/DateTimePicker.js +213 -0
- package/dist/DateTimePicker/DateTimePicker.manifest.js +44 -0
- package/dist/DateTimePicker/index.js +7 -0
- package/dist/DescriptionList/DescriptionList.js +93 -0
- package/dist/DescriptionList/DescriptionList.manifest.js +40 -0
- package/dist/DescriptionList/index.js +9 -0
- package/dist/Dialog/Dialog.js +214 -0
- package/dist/Dialog/Dialog.manifest.js +31 -0
- package/dist/Dialog/index.js +7 -0
- package/dist/Divider/Divider.js +64 -0
- package/dist/Divider/Divider.manifest.js +21 -0
- package/dist/Divider/index.js +7 -0
- package/dist/DropdownMenu/DropdownMenu.js +170 -0
- package/dist/DropdownMenu/DropdownMenu.manifest.js +82 -0
- package/dist/DropdownMenu/index.js +16 -0
- package/dist/EntityPreview/EntityPreview.js +82 -0
- package/dist/EntityPreview/EntityPreview.manifest.js +43 -0
- package/dist/EntityPreview/index.js +7 -0
- package/dist/EventTimeline/EventTimeline.js +249 -0
- package/dist/EventTimeline/EventTimeline.manifest.js +57 -0
- package/dist/EventTimeline/index.js +7 -0
- package/dist/Field/Field.js +81 -0
- package/dist/Field/Field.manifest.js +32 -0
- package/dist/Field/index.js +9 -0
- package/dist/FileUpload/FileUpload.js +215 -0
- package/dist/FileUpload/FileUpload.manifest.js +48 -0
- package/dist/FileUpload/index.js +7 -0
- package/dist/FilterBar/FilterBar.js +173 -0
- package/dist/FilterBar/FilterBar.manifest.js +49 -0
- package/dist/FilterBar/index.js +7 -0
- package/dist/FormErrorSummary/FormErrorSummary.js +70 -0
- package/dist/FormErrorSummary/FormErrorSummary.manifest.js +29 -0
- package/dist/FormErrorSummary/index.js +9 -0
- package/dist/FormSection/FormSection.js +51 -0
- package/dist/FormSection/FormSection.manifest.js +30 -0
- package/dist/FormSection/index.js +7 -0
- package/dist/InlineEdit/InlineEdit.js +261 -0
- package/dist/InlineEdit/InlineEdit.manifest.js +40 -0
- package/dist/InlineEdit/index.js +7 -0
- package/dist/Input/Input.js +126 -0
- package/dist/Input/Input.manifest.js +49 -0
- package/dist/Input/index.js +7 -0
- package/dist/Inspector/Inspector.js +63 -0
- package/dist/Inspector/Inspector.manifest.js +35 -0
- package/dist/Inspector/index.js +7 -0
- package/dist/JobStatus/JobStatus.js +118 -0
- package/dist/JobStatus/JobStatus.manifest.js +53 -0
- package/dist/JobStatus/index.js +7 -0
- package/dist/LayerControl/LayerControl.js +199 -0
- package/dist/LayerControl/LayerControl.manifest.js +34 -0
- package/dist/LayerControl/index.js +7 -0
- package/dist/Link/Link.js +83 -0
- package/dist/Link/Link.manifest.js +37 -0
- package/dist/Link/index.js +7 -0
- package/dist/MapControls/MapControls.js +122 -0
- package/dist/MapControls/MapControls.manifest.js +31 -0
- package/dist/MapControls/index.js +7 -0
- package/dist/MapLegend/MapLegend.js +89 -0
- package/dist/MapLegend/MapLegend.manifest.js +29 -0
- package/dist/MapLegend/index.js +7 -0
- package/dist/MapScale/MapScale.js +96 -0
- package/dist/MapScale/MapScale.manifest.js +31 -0
- package/dist/MapScale/index.js +7 -0
- package/dist/MediaFrame/MediaFrame.js +113 -0
- package/dist/MediaFrame/MediaFrame.manifest.js +57 -0
- package/dist/MediaFrame/index.js +7 -0
- package/dist/MultiSelect/MultiSelect.js +113 -0
- package/dist/MultiSelect/MultiSelect.manifest.js +51 -0
- package/dist/MultiSelect/index.js +7 -0
- package/dist/NumberInput/NumberInput.js +210 -0
- package/dist/NumberInput/NumberInput.manifest.js +56 -0
- package/dist/NumberInput/index.js +7 -0
- package/dist/OperationalStatus/OperationalStatus.js +89 -0
- package/dist/OperationalStatus/OperationalStatus.manifest.js +39 -0
- package/dist/OperationalStatus/index.js +9 -0
- package/dist/PlaybackControls/PlaybackControls.js +289 -0
- package/dist/PlaybackControls/PlaybackControls.manifest.js +85 -0
- package/dist/PlaybackControls/index.js +7 -0
- package/dist/Popover/Popover.js +302 -0
- package/dist/Popover/Popover.manifest.js +50 -0
- package/dist/Popover/index.js +7 -0
- package/dist/Progress/Progress.js +132 -0
- package/dist/Progress/Progress.manifest.js +30 -0
- package/dist/Progress/index.js +7 -0
- package/dist/PropertyFilter/PropertyFilter.js +306 -0
- package/dist/PropertyFilter/PropertyFilter.manifest.js +32 -0
- package/dist/PropertyFilter/index.js +11 -0
- package/dist/PropertyFilter/tokenError.js +17 -0
- package/dist/RadioGroup/RadioGroup.js +143 -0
- package/dist/RadioGroup/RadioGroup.manifest.js +44 -0
- package/dist/RadioGroup/index.js +9 -0
- package/dist/Select/Select.js +219 -0
- package/dist/Select/Select.manifest.js +77 -0
- package/dist/Select/index.js +9 -0
- package/dist/Sidebar/Sidebar.js +146 -0
- package/dist/Sidebar/Sidebar.manifest.js +72 -0
- package/dist/Sidebar/index.js +18 -0
- package/dist/Skeleton/Skeleton.js +76 -0
- package/dist/Skeleton/Skeleton.manifest.js +30 -0
- package/dist/Skeleton/index.js +7 -0
- package/dist/Slider/Slider.js +246 -0
- package/dist/Slider/Slider.manifest.js +56 -0
- package/dist/Slider/index.js +7 -0
- package/dist/Sparkline/Sparkline.js +135 -0
- package/dist/Sparkline/Sparkline.manifest.js +37 -0
- package/dist/Sparkline/index.js +7 -0
- package/dist/SplitPane/SplitPane.js +316 -0
- package/dist/SplitPane/SplitPane.manifest.js +79 -0
- package/dist/SplitPane/index.js +7 -0
- package/dist/StatCard/StatCard.js +163 -0
- package/dist/StatCard/StatCard.manifest.js +45 -0
- package/dist/StatCard/index.js +7 -0
- package/dist/Tabs/Tabs.js +181 -0
- package/dist/Tabs/Tabs.manifest.js +72 -0
- package/dist/Tabs/index.js +16 -0
- package/dist/Tabs/useTabsIndicator.js +71 -0
- package/dist/Tabs/useTabsScroll.js +123 -0
- package/dist/TagGroup/TagGroup.js +142 -0
- package/dist/TagGroup/TagGroup.manifest.js +35 -0
- package/dist/TagGroup/index.js +7 -0
- package/dist/Textarea/Textarea.js +68 -0
- package/dist/Textarea/Textarea.manifest.js +25 -0
- package/dist/Textarea/index.js +7 -0
- package/dist/TimeField/TimeField.js +118 -0
- package/dist/TimeField/TimeField.manifest.js +42 -0
- package/dist/TimeField/index.js +7 -0
- package/dist/TimeRange/TimeRange.js +184 -0
- package/dist/TimeRange/TimeRange.manifest.js +48 -0
- package/dist/TimeRange/index.js +9 -0
- package/dist/Toast/Toast.js +188 -0
- package/dist/Toast/Toast.manifest.js +77 -0
- package/dist/Toast/index.js +9 -0
- package/dist/Toggle/Toggle.js +154 -0
- package/dist/Toggle/Toggle.manifest.js +54 -0
- package/dist/Toggle/index.js +7 -0
- package/dist/Toolbar/Toolbar.js +86 -0
- package/dist/Toolbar/Toolbar.manifest.js +70 -0
- package/dist/Toolbar/index.js +12 -0
- package/dist/Tooltip/Tooltip.js +123 -0
- package/dist/Tooltip/Tooltip.manifest.js +27 -0
- package/dist/Tooltip/index.js +7 -0
- package/dist/TreeView/TreeView.js +129 -0
- package/dist/TreeView/TreeView.manifest.js +56 -0
- package/dist/TreeView/index.js +7 -0
- package/dist/UploadQueue/UploadQueue.js +296 -0
- package/dist/UploadQueue/UploadQueue.manifest.js +34 -0
- package/dist/UploadQueue/index.js +7 -0
- package/dist/_internal/MotionPopover.js +113 -0
- package/dist/_internal/aria.js +40 -0
- package/dist/_internal/bytes.js +18 -0
- package/dist/_internal/controlStyles.js +26 -0
- package/dist/_internal/date.js +179 -0
- package/dist/_internal/devValidate.js +51 -0
- package/dist/_internal/fieldIds.js +50 -0
- package/dist/_internal/hydration.js +74 -0
- package/dist/_internal/manifest-entry.js +0 -0
- package/dist/_internal/mergeRefs.js +68 -0
- package/dist/_internal/motion.js +138 -0
- package/dist/_internal/navigationLandmark.js +13 -0
- package/dist/_internal/numberText.js +176 -0
- package/dist/_internal/overlay.js +53 -0
- package/dist/_internal/segmentField.js +125 -0
- package/dist/_internal/suggestions.js +20 -0
- package/dist/_internal/timestamp.js +25 -0
- package/dist/_internal/unavailable.js +13 -0
- package/dist/_internal/useActionFocus.js +27 -0
- package/dist/_internal/useElementWidth.js +37 -0
- package/dist/_internal/useIsMounted.js +18 -0
- package/dist/_internal/useIsomorphicLayoutEffect.js +8 -0
- package/dist/_internal/useRestoreFocusOnRemoval.js +57 -0
- package/dist/index.cjs +17831 -0
- package/dist/index.d.cts +2330 -0
- package/dist/index.d.ts +2330 -0
- package/dist/index.js +407 -0
- package/dist/version.js +8 -0
- package/package.json +80 -3
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// src/Sparkline/Sparkline.manifest.ts
|
|
2
|
+
var SparklineManifest = {
|
|
3
|
+
Sparkline: {
|
|
4
|
+
description: "Compact SVG line or area trend with preserved missing-data gaps and a complete accessible sample description.",
|
|
5
|
+
props: {
|
|
6
|
+
label: {
|
|
7
|
+
type: "string",
|
|
8
|
+
required: true,
|
|
9
|
+
description: "Accessible graphic name. Provide the metric and sampling context."
|
|
10
|
+
},
|
|
11
|
+
data: {
|
|
12
|
+
type: "array",
|
|
13
|
+
required: true,
|
|
14
|
+
description: "Readonly (number | null)[] in sample order. Null and non-finite values create gaps and are announced as Missing; zero remains zero."
|
|
15
|
+
},
|
|
16
|
+
unit: {
|
|
17
|
+
type: "string",
|
|
18
|
+
description: "Unit appended to every finite sample in the accessible description."
|
|
19
|
+
},
|
|
20
|
+
seriesId: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Optional Chart series ID. Reuses the series' automatic categorical color so a detail trend matches its source plot; omit for the first categorical color. When the series sets its own color, pass the same color too."
|
|
23
|
+
},
|
|
24
|
+
color: {
|
|
25
|
+
type: "number",
|
|
26
|
+
description: "Optional categorical token 1\u20138, as a Chart series' color; it takes precedence over seriesId."
|
|
27
|
+
},
|
|
28
|
+
variant: { type: "enum", values: ["line", "area"], default: "line" }
|
|
29
|
+
},
|
|
30
|
+
composable_with: ["StatCard", "DataTable", "DataGrid", "Card"],
|
|
31
|
+
never_inside: ["Sparkline", "Button"],
|
|
32
|
+
example: '<Sparkline label="Latency, last seven samples" data={[22, 24, null, 21, 23, 20, 22]} unit="ms" />'
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export {
|
|
36
|
+
SparklineManifest
|
|
37
|
+
};
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/SplitPane/SplitPane.tsx
|
|
4
|
+
import { ChevronDown, ChevronLeft, ChevronRight, ChevronUp } from "@oprt/icons";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import {
|
|
8
|
+
forwardRef,
|
|
9
|
+
useCallback,
|
|
10
|
+
useEffect,
|
|
11
|
+
useRef,
|
|
12
|
+
useState
|
|
13
|
+
} from "react";
|
|
14
|
+
import { devValidate } from "../_internal/devValidate.js";
|
|
15
|
+
import { useHydratedId } from "../_internal/hydration.js";
|
|
16
|
+
import { useMergedRefs } from "../_internal/mergeRefs.js";
|
|
17
|
+
import { createResizeObserver, useElementWidth } from "../_internal/useElementWidth.js";
|
|
18
|
+
import { useRestoreFocusOnRemoval } from "../_internal/useRestoreFocusOnRemoval.js";
|
|
19
|
+
import { SplitPaneManifest } from "./SplitPane.manifest.js";
|
|
20
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
21
|
+
var rootStyles = cva("flex min-w-0 min-h-0 font-sans text-oprt-text", {
|
|
22
|
+
variants: { orientation: { horizontal: "flex-row", vertical: "flex-col" } },
|
|
23
|
+
defaultVariants: { orientation: "horizontal" }
|
|
24
|
+
});
|
|
25
|
+
var dividerStyles = cva("relative flex shrink-0 items-center justify-center", {
|
|
26
|
+
variants: {
|
|
27
|
+
orientation: { horizontal: "w-3u min-h-3u flex-col", vertical: "h-3u min-w-3u flex-row" }
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: { orientation: "horizontal" }
|
|
30
|
+
});
|
|
31
|
+
var separatorStyles = cva(
|
|
32
|
+
"absolute inset-0 flex touch-none select-none items-center justify-center border border-transparent bg-oprt-surface-1 focus-visible:outline-hidden focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus",
|
|
33
|
+
{
|
|
34
|
+
variants: {
|
|
35
|
+
orientation: { horizontal: "cursor-col-resize", vertical: "cursor-row-resize" },
|
|
36
|
+
disabled: { true: "cursor-default opacity-50", false: "hover:bg-oprt-surface-2" }
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: { orientation: "horizontal", disabled: false }
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
var stepButtonStyles = "flex h-oprt-sm w-oprt-sm shrink-0 items-center justify-center border border-oprt-border-control bg-oprt-surface-1 text-oprt-text hover:border-oprt-accent focus-visible:outline-hidden focus-visible:border-oprt-border-focus focus-visible:shadow-oprt-focus disabled:cursor-not-allowed disabled:bg-oprt-surface-3 disabled:text-oprt-text-dim disabled:hover:border-oprt-border-control";
|
|
42
|
+
var paneStyles = "min-w-0 min-h-0 overflow-auto focus-visible:outline-hidden focus-visible:shadow-oprt-focus forced-colors:focus-visible:outline-solid forced-colors:focus-visible:outline-2 forced-colors:focus-visible:-outline-offset-2 forced-colors:focus-visible:outline-[CanvasText]";
|
|
43
|
+
var BUTTON_STEP = 10;
|
|
44
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
45
|
+
function usePaneScrolls(pane) {
|
|
46
|
+
const [scrolls, setScrolls] = useState(false);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
const node = pane.current;
|
|
49
|
+
if (!node) return;
|
|
50
|
+
const measure = () => setScrolls(
|
|
51
|
+
node.scrollWidth > node.clientWidth + 1 || node.scrollHeight > node.clientHeight + 1
|
|
52
|
+
);
|
|
53
|
+
const resize = createResizeObserver(measure);
|
|
54
|
+
const watched = /* @__PURE__ */ new Set();
|
|
55
|
+
const observe = () => {
|
|
56
|
+
if (!resize) return;
|
|
57
|
+
const current = /* @__PURE__ */ new Set([node, ...node.children]);
|
|
58
|
+
for (const element of watched)
|
|
59
|
+
if (!current.has(element)) {
|
|
60
|
+
resize.unobserve(element);
|
|
61
|
+
watched.delete(element);
|
|
62
|
+
}
|
|
63
|
+
for (const element of current)
|
|
64
|
+
if (!watched.has(element)) {
|
|
65
|
+
resize.observe(element);
|
|
66
|
+
watched.add(element);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
observe();
|
|
70
|
+
measure();
|
|
71
|
+
const children = new MutationObserver(() => {
|
|
72
|
+
observe();
|
|
73
|
+
measure();
|
|
74
|
+
});
|
|
75
|
+
children.observe(node, { childList: true });
|
|
76
|
+
return () => {
|
|
77
|
+
resize?.disconnect();
|
|
78
|
+
children.disconnect();
|
|
79
|
+
};
|
|
80
|
+
}, [pane]);
|
|
81
|
+
return scrolls;
|
|
82
|
+
}
|
|
83
|
+
var SplitPane = forwardRef(
|
|
84
|
+
({
|
|
85
|
+
primary,
|
|
86
|
+
secondary,
|
|
87
|
+
size,
|
|
88
|
+
defaultSize = 50,
|
|
89
|
+
onSizeChange,
|
|
90
|
+
minSize = 20,
|
|
91
|
+
maxSize = 80,
|
|
92
|
+
step = 1,
|
|
93
|
+
orientation = "horizontal",
|
|
94
|
+
disabled = false,
|
|
95
|
+
label = "Resize panels",
|
|
96
|
+
stackBelow = 640,
|
|
97
|
+
primaryLabel = "Primary panel",
|
|
98
|
+
secondaryLabel = "Secondary panel",
|
|
99
|
+
className,
|
|
100
|
+
onFocusCapture,
|
|
101
|
+
onBlurCapture,
|
|
102
|
+
...props
|
|
103
|
+
}, forwardedRef) => {
|
|
104
|
+
if (process.env.NODE_ENV !== "production") {
|
|
105
|
+
devValidate("SplitPane", SplitPaneManifest.SplitPane.props, { orientation });
|
|
106
|
+
}
|
|
107
|
+
const vertical = orientation === "vertical";
|
|
108
|
+
const axis = vertical ? "vertical" : "horizontal";
|
|
109
|
+
const rootRef = useRef(null);
|
|
110
|
+
const primaryRef = useRef(null);
|
|
111
|
+
const secondaryRef = useRef(null);
|
|
112
|
+
const separatorRef = useRef(null);
|
|
113
|
+
const ownChild = (selector) => () => Array.from(rootRef.current?.children ?? []).find((child) => child.matches(selector))?.id;
|
|
114
|
+
const primaryId = useHydratedId(ownChild('[data-pane="primary"]'));
|
|
115
|
+
const helpId = useHydratedId(ownChild("[data-oprt-split-help]"));
|
|
116
|
+
const [internalSize, setInternalSize] = useState(defaultSize);
|
|
117
|
+
const drag = useRef(null);
|
|
118
|
+
const lower = clamp(Number.isFinite(minSize) ? minSize : 20, 0, 100);
|
|
119
|
+
const upper = clamp(Number.isFinite(maxSize) ? maxSize : 80, lower, 100);
|
|
120
|
+
const rawSize = size ?? internalSize;
|
|
121
|
+
const currentSize = clamp(Number.isFinite(rawSize) ? rawSize : 50, lower, upper);
|
|
122
|
+
const increment = Number.isFinite(step) && step > 0 ? step : 1;
|
|
123
|
+
const buttonStep = Math.max(BUTTON_STEP, increment);
|
|
124
|
+
const threshold = Number.isFinite(stackBelow) ? Math.max(0, stackBelow) : 640;
|
|
125
|
+
const setRef = useMergedRefs(rootRef, forwardedRef);
|
|
126
|
+
const isNarrow = useCallback((width) => width < threshold, [threshold]);
|
|
127
|
+
const narrow = useElementWidth(rootRef, {
|
|
128
|
+
initial: false,
|
|
129
|
+
select: isNarrow
|
|
130
|
+
});
|
|
131
|
+
const stacked = !vertical && narrow;
|
|
132
|
+
const primaryScrolls = usePaneScrolls(primaryRef);
|
|
133
|
+
const secondaryScrolls = usePaneScrolls(secondaryRef);
|
|
134
|
+
const restoreFocus = useRestoreFocusOnRemoval(rootRef, {
|
|
135
|
+
track: (element) => element.closest("[data-oprt-split-divider]")?.parentElement === rootRef.current ? true : null,
|
|
136
|
+
findTarget: () => separatorRef.current?.isConnected ? separatorRef.current : primaryRef.current
|
|
137
|
+
});
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
drag.current = null;
|
|
140
|
+
}, [disabled, stacked, orientation]);
|
|
141
|
+
const resize = (next) => {
|
|
142
|
+
if (disabled || stacked || !Number.isFinite(next)) return;
|
|
143
|
+
const bounded = Math.round(clamp(next, lower, upper) * 1e3) / 1e3;
|
|
144
|
+
if (bounded === currentSize) return;
|
|
145
|
+
if (size === void 0) setInternalSize(bounded);
|
|
146
|
+
onSizeChange?.(bounded);
|
|
147
|
+
};
|
|
148
|
+
const keyDown = (event) => {
|
|
149
|
+
if (disabled || stacked) return;
|
|
150
|
+
const decrease = vertical ? "ArrowUp" : "ArrowLeft";
|
|
151
|
+
const increase = vertical ? "ArrowDown" : "ArrowRight";
|
|
152
|
+
let next;
|
|
153
|
+
switch (event.key) {
|
|
154
|
+
case decrease:
|
|
155
|
+
next = currentSize - increment;
|
|
156
|
+
break;
|
|
157
|
+
case increase:
|
|
158
|
+
next = currentSize + increment;
|
|
159
|
+
break;
|
|
160
|
+
case "Home":
|
|
161
|
+
next = lower;
|
|
162
|
+
break;
|
|
163
|
+
case "End":
|
|
164
|
+
next = upper;
|
|
165
|
+
break;
|
|
166
|
+
default:
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
resize(next);
|
|
171
|
+
};
|
|
172
|
+
const pointerDown = (event) => {
|
|
173
|
+
if (disabled || stacked || event.button !== 0 || !rootRef.current) return;
|
|
174
|
+
const rootBounds = rootRef.current.getBoundingClientRect();
|
|
175
|
+
const handleBounds = event.currentTarget.getBoundingClientRect();
|
|
176
|
+
const extent = vertical ? rootBounds.height - handleBounds.height : rootBounds.width - handleBounds.width;
|
|
177
|
+
if (extent <= 0) return;
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
event.currentTarget.focus();
|
|
180
|
+
event.currentTarget.setPointerCapture?.(event.pointerId);
|
|
181
|
+
drag.current = {
|
|
182
|
+
pointerId: event.pointerId,
|
|
183
|
+
coordinate: vertical ? event.clientY : event.clientX,
|
|
184
|
+
size: currentSize,
|
|
185
|
+
extent
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
const pointerMove = (event) => {
|
|
189
|
+
if (!drag.current || drag.current.pointerId !== event.pointerId) return;
|
|
190
|
+
const coordinate = vertical ? event.clientY : event.clientX;
|
|
191
|
+
const next = drag.current.size + (coordinate - drag.current.coordinate) / drag.current.extent * 100;
|
|
192
|
+
resize(lower + Math.round((next - lower) / increment) * increment);
|
|
193
|
+
};
|
|
194
|
+
const pointerEnd = (event) => {
|
|
195
|
+
if (drag.current?.pointerId !== event.pointerId) return;
|
|
196
|
+
drag.current = null;
|
|
197
|
+
if (event.currentTarget.hasPointerCapture?.(event.pointerId))
|
|
198
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
199
|
+
};
|
|
200
|
+
const [Decrease, Increase] = vertical ? [ChevronUp, ChevronDown] : [ChevronLeft, ChevronRight];
|
|
201
|
+
const [decreaseLabel, increaseLabel] = vertical ? ["Move divider up", "Move divider down"] : ["Move divider left", "Move divider right"];
|
|
202
|
+
const region = (scrolls, name) => scrolls ? { role: "region", "aria-label": name, tabIndex: 0 } : {};
|
|
203
|
+
return /* @__PURE__ */ jsxs(
|
|
204
|
+
"div",
|
|
205
|
+
{
|
|
206
|
+
...props,
|
|
207
|
+
ref: setRef,
|
|
208
|
+
"data-stacked": stacked || void 0,
|
|
209
|
+
className: clsx(rootStyles({ orientation: stacked ? "vertical" : axis }), className),
|
|
210
|
+
onFocusCapture: (event) => {
|
|
211
|
+
onFocusCapture?.(event);
|
|
212
|
+
restoreFocus.onFocusCapture(event);
|
|
213
|
+
},
|
|
214
|
+
onBlurCapture: (event) => {
|
|
215
|
+
onBlurCapture?.(event);
|
|
216
|
+
restoreFocus.onBlurCapture(event);
|
|
217
|
+
},
|
|
218
|
+
children: [
|
|
219
|
+
/* @__PURE__ */ jsx(
|
|
220
|
+
"div",
|
|
221
|
+
{
|
|
222
|
+
ref: primaryRef,
|
|
223
|
+
id: primaryId,
|
|
224
|
+
"data-pane": "primary",
|
|
225
|
+
className: paneStyles,
|
|
226
|
+
style: { flexBasis: "0%", flexGrow: currentSize },
|
|
227
|
+
...primaryScrolls ? region(true, primaryLabel) : stacked ? { tabIndex: -1 } : {},
|
|
228
|
+
children: primary
|
|
229
|
+
}
|
|
230
|
+
),
|
|
231
|
+
!stacked && /* @__PURE__ */ jsxs("div", { "data-oprt-split-divider": "", className: dividerStyles({ orientation: axis }), children: [
|
|
232
|
+
/* @__PURE__ */ jsx(
|
|
233
|
+
"div",
|
|
234
|
+
{
|
|
235
|
+
ref: separatorRef,
|
|
236
|
+
role: "separator",
|
|
237
|
+
tabIndex: disabled ? -1 : 0,
|
|
238
|
+
"aria-label": label,
|
|
239
|
+
"aria-controls": primaryId,
|
|
240
|
+
"aria-describedby": helpId,
|
|
241
|
+
"aria-orientation": vertical ? "horizontal" : "vertical",
|
|
242
|
+
"aria-valuemin": lower,
|
|
243
|
+
"aria-valuemax": upper,
|
|
244
|
+
"aria-valuenow": currentSize,
|
|
245
|
+
"aria-valuetext": `${currentSize}% primary panel`,
|
|
246
|
+
"aria-disabled": disabled || void 0,
|
|
247
|
+
className: separatorStyles({ orientation: axis, disabled }),
|
|
248
|
+
onKeyDown: keyDown,
|
|
249
|
+
onPointerDown: pointerDown,
|
|
250
|
+
onPointerMove: pointerMove,
|
|
251
|
+
onPointerUp: pointerEnd,
|
|
252
|
+
onPointerCancel: pointerEnd,
|
|
253
|
+
onLostPointerCapture: () => {
|
|
254
|
+
drag.current = null;
|
|
255
|
+
},
|
|
256
|
+
children: disabled && /* @__PURE__ */ jsx(
|
|
257
|
+
"span",
|
|
258
|
+
{
|
|
259
|
+
"aria-hidden": "true",
|
|
260
|
+
className: vertical ? "w-4u border-t border-oprt-border-strong" : "h-4u border-l border-oprt-border-strong"
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
!disabled && /* @__PURE__ */ jsxs("div", { className: clsx("relative flex gap-0.5u", vertical ? "flex-row" : "flex-col"), children: [
|
|
266
|
+
/* @__PURE__ */ jsx(
|
|
267
|
+
"button",
|
|
268
|
+
{
|
|
269
|
+
type: "button",
|
|
270
|
+
tabIndex: -1,
|
|
271
|
+
"aria-label": decreaseLabel,
|
|
272
|
+
disabled: currentSize <= lower,
|
|
273
|
+
className: stepButtonStyles,
|
|
274
|
+
onClick: () => resize(currentSize - buttonStep),
|
|
275
|
+
children: /* @__PURE__ */ jsx(Decrease, { size: 16, "aria-hidden": "true" })
|
|
276
|
+
}
|
|
277
|
+
),
|
|
278
|
+
/* @__PURE__ */ jsx(
|
|
279
|
+
"button",
|
|
280
|
+
{
|
|
281
|
+
type: "button",
|
|
282
|
+
tabIndex: -1,
|
|
283
|
+
"aria-label": increaseLabel,
|
|
284
|
+
disabled: currentSize >= upper,
|
|
285
|
+
className: stepButtonStyles,
|
|
286
|
+
onClick: () => resize(currentSize + buttonStep),
|
|
287
|
+
children: /* @__PURE__ */ jsx(Increase, { size: 16, "aria-hidden": "true" })
|
|
288
|
+
}
|
|
289
|
+
)
|
|
290
|
+
] })
|
|
291
|
+
] }),
|
|
292
|
+
/* @__PURE__ */ jsx(
|
|
293
|
+
"div",
|
|
294
|
+
{
|
|
295
|
+
ref: secondaryRef,
|
|
296
|
+
"data-pane": "secondary",
|
|
297
|
+
className: paneStyles,
|
|
298
|
+
style: { flexBasis: "0%", flexGrow: 100 - currentSize },
|
|
299
|
+
...region(secondaryScrolls, secondaryLabel),
|
|
300
|
+
children: secondary
|
|
301
|
+
}
|
|
302
|
+
),
|
|
303
|
+
/* @__PURE__ */ jsxs("span", { id: helpId, "data-oprt-split-help": "", hidden: true, children: [
|
|
304
|
+
"Use ",
|
|
305
|
+
vertical ? "Up and Down" : "Left and Right",
|
|
306
|
+
" arrow keys to resize. Home and End move to the limits."
|
|
307
|
+
] })
|
|
308
|
+
]
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
);
|
|
313
|
+
SplitPane.displayName = "SplitPane";
|
|
314
|
+
export {
|
|
315
|
+
SplitPane
|
|
316
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/SplitPane/SplitPane.manifest.ts
|
|
2
|
+
var SplitPaneManifest = {
|
|
3
|
+
SplitPane: {
|
|
4
|
+
description: "Two panels with a keyboard and pointer resize separator and two step buttons on the divider; a horizontal split stacks below a container-width threshold.",
|
|
5
|
+
props: {
|
|
6
|
+
primary: {
|
|
7
|
+
type: "node",
|
|
8
|
+
required: true,
|
|
9
|
+
description: "Primary panel content, always first in reading order."
|
|
10
|
+
},
|
|
11
|
+
secondary: { type: "node", required: true, description: "Secondary panel content." },
|
|
12
|
+
size: {
|
|
13
|
+
type: "number",
|
|
14
|
+
description: "Controlled primary-panel percentage of available space, excluding the handle. Clamped to minSize/maxSize. Stacked panels share a bounded height by the same split."
|
|
15
|
+
},
|
|
16
|
+
defaultSize: {
|
|
17
|
+
type: "number",
|
|
18
|
+
default: 50,
|
|
19
|
+
description: "Initial uncontrolled primary-panel percentage."
|
|
20
|
+
},
|
|
21
|
+
onSizeChange: {
|
|
22
|
+
type: "function",
|
|
23
|
+
description: "(percent: number) => void. Called for valid keyboard, pointer or step-button adjustments."
|
|
24
|
+
},
|
|
25
|
+
minSize: {
|
|
26
|
+
type: "number",
|
|
27
|
+
default: 20,
|
|
28
|
+
description: "Minimum primary-panel percentage. Finite values clamp to 0\u2013100; non-finite values use 20."
|
|
29
|
+
},
|
|
30
|
+
maxSize: {
|
|
31
|
+
type: "number",
|
|
32
|
+
default: 80,
|
|
33
|
+
description: "Maximum primary-panel percentage. Finite values clamp between minSize and 100; non-finite values use 80."
|
|
34
|
+
},
|
|
35
|
+
step: {
|
|
36
|
+
type: "number",
|
|
37
|
+
default: 1,
|
|
38
|
+
description: "Positive percentage increment for the arrow keys and dragging. Invalid values use 1. The step buttons move 10 points, or step when it is larger."
|
|
39
|
+
},
|
|
40
|
+
orientation: {
|
|
41
|
+
type: "enum",
|
|
42
|
+
values: ["horizontal", "vertical"],
|
|
43
|
+
default: "horizontal",
|
|
44
|
+
description: "Horizontal puts panels side by side; vertical puts them above and below. Give vertical layouts a bounded height."
|
|
45
|
+
},
|
|
46
|
+
disabled: {
|
|
47
|
+
type: "boolean",
|
|
48
|
+
default: false,
|
|
49
|
+
description: "Turns resizing off: the separator leaves the focus order and the step buttons are hidden."
|
|
50
|
+
},
|
|
51
|
+
label: {
|
|
52
|
+
type: "string",
|
|
53
|
+
default: "Resize panels",
|
|
54
|
+
description: "Accessible name of the separator."
|
|
55
|
+
},
|
|
56
|
+
stackBelow: {
|
|
57
|
+
type: "number",
|
|
58
|
+
default: 640,
|
|
59
|
+
description: "Container width in CSS pixels below which a horizontal split stacks its panels and hides resizing. A vertical split never stacks. Use 0 to disable responsive stacking."
|
|
60
|
+
},
|
|
61
|
+
primaryLabel: {
|
|
62
|
+
type: "string",
|
|
63
|
+
default: "Primary panel",
|
|
64
|
+
description: "Accessible name of the primary panel while its content scrolls; it is then a focusable region."
|
|
65
|
+
},
|
|
66
|
+
secondaryLabel: {
|
|
67
|
+
type: "string",
|
|
68
|
+
default: "Secondary panel",
|
|
69
|
+
description: "Accessible name of the secondary panel while its content scrolls; it is then a focusable region."
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
composable_with: ["Card", "DataGrid", "Chart", "EventTimeline"],
|
|
73
|
+
never_inside: ["Button"],
|
|
74
|
+
example: '<SplitPane primary={<section>Telemetry</section>} secondary={<section>Inspector</section>} defaultSize={60} label="Resize telemetry and inspector" />'
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
export {
|
|
78
|
+
SplitPaneManifest
|
|
79
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/StatCard/StatCard.tsx
|
|
4
|
+
import { TrendingUp, TrendingDown, TrendFlat } from "@oprt/icons";
|
|
5
|
+
import { Fragment, forwardRef, useId } from "react";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { StatCardManifest } from "./StatCard.manifest.js";
|
|
8
|
+
import { devValidate } from "../_internal/devValidate.js";
|
|
9
|
+
import { numberParts, splitLast, unitWords } from "../_internal/numberText.js";
|
|
10
|
+
import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
function deriveTrend(delta) {
|
|
12
|
+
if (delta > 0) return "up";
|
|
13
|
+
if (delta < 0) return "down";
|
|
14
|
+
return "flat";
|
|
15
|
+
}
|
|
16
|
+
var TREND_COLOR = {
|
|
17
|
+
up: "text-oprt-success",
|
|
18
|
+
down: "text-oprt-error",
|
|
19
|
+
flat: "text-oprt-text-dim"
|
|
20
|
+
};
|
|
21
|
+
var INTENT_COLOR = {
|
|
22
|
+
positive: "text-oprt-success",
|
|
23
|
+
negative: "text-oprt-error",
|
|
24
|
+
neutral: "text-oprt-text-dim"
|
|
25
|
+
};
|
|
26
|
+
var TREND_ICONS = {
|
|
27
|
+
up: TrendingUp,
|
|
28
|
+
down: TrendingDown,
|
|
29
|
+
flat: TrendFlat
|
|
30
|
+
};
|
|
31
|
+
var has = (table, key) => typeof key === "string" && Object.prototype.hasOwnProperty.call(table, key);
|
|
32
|
+
var VALUE_ADVANCE = 0.67;
|
|
33
|
+
var UNIT_ADVANCE = 0.63;
|
|
34
|
+
function glyphWeight(code) {
|
|
35
|
+
if (code < 592 || code >= 1536 && code < 1792 || code >= 2304 && code < 2560 || code >= 8192 && code < 8448)
|
|
36
|
+
return 1;
|
|
37
|
+
return code >= 65280 && code < 65520 ? 1.7 : 1.3;
|
|
38
|
+
}
|
|
39
|
+
var FORMAT_CHARACTER = /\p{Cf}/u;
|
|
40
|
+
var lengthOf = (text, advance) => {
|
|
41
|
+
let characters = 0;
|
|
42
|
+
for (const character of text)
|
|
43
|
+
if (!FORMAT_CHARACTER.test(character)) characters += glyphWeight(character.codePointAt(0));
|
|
44
|
+
return Number((characters * advance).toFixed(3));
|
|
45
|
+
};
|
|
46
|
+
var STEP_DOWN = "[font-size:max(var(--oprt-type-base),min(var(--oprt-type-2xl),calc((100cqi_-_var(--oprt-stat-lead)_*_0.8px)_/_var(--oprt-stat-lead)),max(calc((100cqi_-_var(--oprt-type-md)_*_var(--oprt-stat-unit)_-_(var(--oprt-stat-run)_+_var(--oprt-stat-unit))_*_0.8px)_/_var(--oprt-stat-run)),calc((100cqi_-_(var(--oprt-stat-run)_+_var(--oprt-stat-unit))_*_0.8px)_/_(var(--oprt-stat-run)_+_var(--oprt-stat-unit))))))]";
|
|
47
|
+
var VALUE_TEXT = "m-0 block min-w-0 normal-case font-mono text-oprt-2xl text-oprt-text";
|
|
48
|
+
var VALUE_NUMBER = `m-0 block min-w-0 normal-case font-mono text-oprt-text tracking-oprt-2xl leading-[1.4286] [font-weight:var(--oprt-weight-2xl)] ${STEP_DOWN}`;
|
|
49
|
+
var UNIT = "font-mono tracking-oprt-md leading-none text-oprt-text-dim [font-size:min(var(--oprt-type-md),1em)] [font-weight:var(--oprt-weight-md)]";
|
|
50
|
+
function StatValue({ value, unit }) {
|
|
51
|
+
const text = String(value);
|
|
52
|
+
const words = unitWords(unit);
|
|
53
|
+
const number = numberParts(text.trim());
|
|
54
|
+
const unitRun = words && /* @__PURE__ */ jsx("span", { "data-oprt-stat-unit": "", className: UNIT, children: `\xA0${words.first}` });
|
|
55
|
+
const unitRest = words?.rest ? /* @__PURE__ */ jsx("span", { className: clsx(UNIT, "oprt-wrap-anywhere"), children: ` ${words.rest}` }) : null;
|
|
56
|
+
if (number) {
|
|
57
|
+
const last2 = lengthOf(number.tail, VALUE_ADVANCE);
|
|
58
|
+
const lead = number.lead.length > 0 ? Math.max(...number.lead.map((piece) => lengthOf(piece, VALUE_ADVANCE))) : last2;
|
|
59
|
+
const style = {
|
|
60
|
+
"--oprt-stat-lead": lead,
|
|
61
|
+
"--oprt-stat-run": last2,
|
|
62
|
+
"--oprt-stat-unit": words ? lengthOf(` ${words.first}`, UNIT_ADVANCE) : 0
|
|
63
|
+
};
|
|
64
|
+
return /* @__PURE__ */ jsx("span", { "data-oprt-stat-value": "", className: VALUE_NUMBER, style, children: /* @__PURE__ */ jsxs("span", { "data-oprt-stat-value-text": "", children: [
|
|
65
|
+
number.lead.length > 0 && // The groups before the last, each a run that cannot wrap, with a break after each.
|
|
66
|
+
/* @__PURE__ */ jsx("span", { children: number.lead.map((piece, index) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
67
|
+
/* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: piece }),
|
|
68
|
+
/* @__PURE__ */ jsx("wbr", {})
|
|
69
|
+
] }, index)) }),
|
|
70
|
+
/* @__PURE__ */ jsxs("span", { "data-oprt-stat-run": "", className: "inline-block", children: [
|
|
71
|
+
/* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: number.tail }),
|
|
72
|
+
words && /* @__PURE__ */ jsx("span", { "data-oprt-stat-unit": "", className: UNIT, children: ` ${words.first}` })
|
|
73
|
+
] }),
|
|
74
|
+
unitRest
|
|
75
|
+
] }) });
|
|
76
|
+
}
|
|
77
|
+
const [head, last] = splitLast(text);
|
|
78
|
+
return /* @__PURE__ */ jsx("span", { "data-oprt-stat-value": "", className: VALUE_TEXT, children: /* @__PURE__ */ jsx("span", { "data-oprt-stat-value-text": "", className: "oprt-wrap-anywhere", children: words ? /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
79
|
+
head,
|
|
80
|
+
/* @__PURE__ */ jsxs("span", { "data-oprt-stat-run": "", className: "whitespace-nowrap", children: [
|
|
81
|
+
last,
|
|
82
|
+
unitRun
|
|
83
|
+
] }),
|
|
84
|
+
unitRest
|
|
85
|
+
] }) : text }) });
|
|
86
|
+
}
|
|
87
|
+
var StatCard = forwardRef((allProps, ref) => {
|
|
88
|
+
const {
|
|
89
|
+
className,
|
|
90
|
+
label,
|
|
91
|
+
value,
|
|
92
|
+
unit,
|
|
93
|
+
delta,
|
|
94
|
+
trend,
|
|
95
|
+
trendIntent,
|
|
96
|
+
deltaLabel,
|
|
97
|
+
caption,
|
|
98
|
+
"aria-label": ariaLabel,
|
|
99
|
+
"aria-labelledby": ariaLabelledBy,
|
|
100
|
+
...props
|
|
101
|
+
} = allProps;
|
|
102
|
+
const labelId = useId();
|
|
103
|
+
const showTrend = delta !== void 0 || trend !== void 0;
|
|
104
|
+
const explicitTrend = has(TREND_ICONS, trend) ? trend : void 0;
|
|
105
|
+
const resolvedTrend = explicitTrend ?? (delta !== void 0 ? deriveTrend(delta) : "flat");
|
|
106
|
+
const TrendIcon = TREND_ICONS[resolvedTrend];
|
|
107
|
+
const trendColor = has(INTENT_COLOR, trendIntent) ? INTENT_COLOR[trendIntent] : TREND_COLOR[resolvedTrend];
|
|
108
|
+
if (process.env.NODE_ENV !== "production") {
|
|
109
|
+
devValidate("StatCard", StatCardManifest.StatCard.props, allProps);
|
|
110
|
+
}
|
|
111
|
+
return /* @__PURE__ */ jsxs(
|
|
112
|
+
"div",
|
|
113
|
+
{
|
|
114
|
+
ref,
|
|
115
|
+
role: "group",
|
|
116
|
+
"aria-label": ariaLabel,
|
|
117
|
+
"aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : labelId),
|
|
118
|
+
"data-oprt-stat-card": "",
|
|
119
|
+
className: clsx(
|
|
120
|
+
// A minimum footprint (180 wide, 169 tall) that grows with content as needed, so a grid of
|
|
121
|
+
// StatCards stays uniform. The width gives way inside a container narrower than 180 px
|
|
122
|
+
// (two cards side by side on a phone); the height does not. Min dimensions are a deliberate
|
|
123
|
+
// component spec, not layout-grid spacing. The card is a size container, so a large value
|
|
124
|
+
// can step its type down to the card's width: it takes the width its cell gives it (R3), and
|
|
125
|
+
// in a shrink-wrapped flex row that is its 180 px minimum. Containment gives a card no
|
|
126
|
+
// intrinsic width of its own, so one in a shrink-wrapped grid would collapse to its padding
|
|
127
|
+
// (measured: 50 px wide, a 0 px value). `contain-intrinsic-inline-size` is the natural width
|
|
128
|
+
// of its content box at that minimum: 180 px less the 2 x 24 px padding and the 2 x 1 px border.
|
|
129
|
+
"@container [contain-intrinsic-inline-size:130px] flex min-h-[169px] min-w-[min(180px,100%)] flex-col gap-1u bg-oprt-surface-1 border border-oprt-border px-3u py-3u font-sans",
|
|
130
|
+
className
|
|
131
|
+
),
|
|
132
|
+
...props,
|
|
133
|
+
children: [
|
|
134
|
+
/* @__PURE__ */ jsx(
|
|
135
|
+
"span",
|
|
136
|
+
{
|
|
137
|
+
id: labelId,
|
|
138
|
+
className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-sm uppercase tracking-oprt-sm text-oprt-text-dim",
|
|
139
|
+
children: label
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
/* @__PURE__ */ jsx(StatValue, { value, unit }),
|
|
143
|
+
showTrend && /* @__PURE__ */ jsxs(
|
|
144
|
+
"span",
|
|
145
|
+
{
|
|
146
|
+
role: "img",
|
|
147
|
+
className: clsx("m-0 inline-flex min-w-0 max-w-full items-center gap-1u", trendColor),
|
|
148
|
+
"aria-label": `trend ${resolvedTrend}${deltaLabel ? " " + deltaLabel : ""}`,
|
|
149
|
+
children: [
|
|
150
|
+
/* @__PURE__ */ jsx(TrendIcon, { size: 16 }),
|
|
151
|
+
deltaLabel !== void 0 && /* @__PURE__ */ jsx("span", { className: "min-w-0 oprt-wrap-anywhere font-mono text-oprt-base normal-case", children: deltaLabel })
|
|
152
|
+
]
|
|
153
|
+
}
|
|
154
|
+
),
|
|
155
|
+
caption !== void 0 && /* @__PURE__ */ jsx("span", { className: "m-0 min-w-0 oprt-wrap-anywhere text-oprt-base normal-case text-oprt-text-dim", children: caption })
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
StatCard.displayName = "StatCard";
|
|
161
|
+
export {
|
|
162
|
+
StatCard
|
|
163
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/StatCard/StatCard.manifest.ts
|
|
2
|
+
var StatCardManifest = {
|
|
3
|
+
StatCard: {
|
|
4
|
+
description: "Single-metric display: label, mono numeric value, and an optional trend indicator paired with an arrow glyph (non-color cue per WCAG 1.4.1). It keeps a minimum footprint (169 px tall, up to 180 px wide, narrower inside a narrower container) and its text wraps. It measures its own width, so a large number steps down in size to fit it (never below the body text size), and in a row that sizes its items to their content it is 180 px wide.",
|
|
5
|
+
props: {
|
|
6
|
+
label: {
|
|
7
|
+
type: "string",
|
|
8
|
+
required: true,
|
|
9
|
+
description: "Short metric heading displayed uppercase and used as the default accessible name for the metric group. Values, units, and captions retain their casing. Override with aria-label or aria-labelledby when more context is needed."
|
|
10
|
+
},
|
|
11
|
+
value: {
|
|
12
|
+
type: "union",
|
|
13
|
+
types: ["string", "number"],
|
|
14
|
+
required: true,
|
|
15
|
+
description: "The metric value (string or number). Rendered in mono for digit alignment. A number is never split between its digits: one with digit groups (1,234,567.890) wraps only after a group separator, and its last group stays with the unit. When they do not fit the card the value steps down in size, never below the body text size (--oprt-type-base); at that size, if they still do not fit together, the unit moves to its own line under the value. Any other text breaks anywhere. Nothing is truncated or abbreviated."
|
|
16
|
+
},
|
|
17
|
+
unit: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description: "Unit suffix shown beside the value, e.g. 'ms'. Its first word stays beside the last digits at every size above the body text size; only when they cannot fit together even at that size does the unit move to its own line under the value. The rest of a long unit wraps."
|
|
20
|
+
},
|
|
21
|
+
delta: {
|
|
22
|
+
type: "number",
|
|
23
|
+
description: "Signed change. Trend is derived from its sign when 'trend' is omitted."
|
|
24
|
+
},
|
|
25
|
+
trend: {
|
|
26
|
+
type: "enum",
|
|
27
|
+
values: ["up", "down", "flat"],
|
|
28
|
+
description: "Explicit trend direction. Overrides the sign of 'delta' when provided."
|
|
29
|
+
},
|
|
30
|
+
trendIntent: {
|
|
31
|
+
type: "enum",
|
|
32
|
+
values: ["positive", "negative", "neutral"],
|
|
33
|
+
description: "Whether the change is good, bad or neither for the application, which sets the trend color independently of its direction: falling errors or latency are 'positive', rising critical alarms are 'negative'. Without it the color follows the direction (up is success, down is error). The arrow and the change text stay as the non-color cue."
|
|
34
|
+
},
|
|
35
|
+
deltaLabel: { type: "string", description: "Human-readable change text, e.g. '+12%'." },
|
|
36
|
+
caption: { type: "string", description: "Secondary supporting line below the metric." }
|
|
37
|
+
},
|
|
38
|
+
composable_with: ["Card", "DataTable", "Sidebar", "Toolbar"],
|
|
39
|
+
never_inside: ["StatCard", "Badge", "Button"],
|
|
40
|
+
example: '<StatCard label="LATENCY" value={42} unit="ms" delta={-12} deltaLabel="-12%" trendIntent="positive" />'
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
export {
|
|
44
|
+
StatCardManifest
|
|
45
|
+
};
|