@oprt/react 0.1.0 → 0.2.1
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/dist/ActionStatus/ActionStatus.js +15 -4
- package/dist/Alert/Alert.js +21 -12
- package/dist/AttachmentPreview/AttachmentPreview.js +15 -12
- package/dist/Chart/Chart.js +10 -2
- package/dist/CopyButton/CopyButton.js +19 -0
- package/dist/CopyButton/CopyButton.manifest.js +1 -1
- package/dist/EventTimeline/EventTimeline.js +9 -1
- package/dist/LayerControl/LayerControl.js +2 -2
- package/dist/Toast/Toast.js +1 -1
- package/dist/Tooltip/Tooltip.js +2 -1
- package/dist/Tooltip/Tooltip.manifest.js +1 -1
- package/dist/UploadQueue/UploadQueue.js +2 -2
- package/dist/_internal/liveRegion.js +27 -0
- package/dist/index.cjs +746 -667
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -8,6 +8,7 @@ import { Badge } from "../Badge/Badge.js";
|
|
|
8
8
|
import { Button } from "../Button/Button.js";
|
|
9
9
|
import { useActionFocus } from "../_internal/useActionFocus.js";
|
|
10
10
|
import { devValidate } from "../_internal/devValidate.js";
|
|
11
|
+
import { useHoldBackNewText } from "../_internal/liveRegion.js";
|
|
11
12
|
import { ActionStatusManifest } from "./ActionStatus.manifest.js";
|
|
12
13
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
var statusStyles = cva(
|
|
@@ -34,6 +35,8 @@ function ActionStatusLine({
|
|
|
34
35
|
requestId,
|
|
35
36
|
children
|
|
36
37
|
}) {
|
|
38
|
+
const spoken = [`${label}, ${state}.`, requestId && `Request: ${requestId}.`, message].filter(Boolean).join(" ");
|
|
39
|
+
const holdBack = useHoldBackNewText(true);
|
|
37
40
|
return /* @__PURE__ */ jsxs(
|
|
38
41
|
"div",
|
|
39
42
|
{
|
|
@@ -42,13 +45,21 @@ function ActionStatusLine({
|
|
|
42
45
|
"aria-atomic": "true",
|
|
43
46
|
className: "flex min-w-0 flex-col gap-1u",
|
|
44
47
|
children: [
|
|
45
|
-
/* @__PURE__ */ jsx("span", { className: "
|
|
46
|
-
/* @__PURE__ */ jsx(
|
|
47
|
-
|
|
48
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: holdBack ? null : spoken }),
|
|
49
|
+
/* @__PURE__ */ jsx(
|
|
50
|
+
"span",
|
|
51
|
+
{
|
|
52
|
+
"aria-hidden": "true",
|
|
53
|
+
className: "min-w-0 oprt-wrap-anywhere font-medium uppercase tracking-oprt-caps",
|
|
54
|
+
children: label
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", children: /* @__PURE__ */ jsx(Badge, { variant: variants[state], indicator: "dot", children: state }) }),
|
|
58
|
+
requestId && /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", className: "min-w-0 oprt-wrap-anywhere font-mono", children: [
|
|
48
59
|
"Request: ",
|
|
49
60
|
requestId
|
|
50
61
|
] }),
|
|
51
|
-
message && /* @__PURE__ */ jsx("p", { className: "m-0 min-w-0 oprt-wrap-anywhere", children: message }),
|
|
62
|
+
message && /* @__PURE__ */ jsx("p", { "aria-hidden": "true", className: "m-0 min-w-0 oprt-wrap-anywhere", children: message }),
|
|
52
63
|
children
|
|
53
64
|
]
|
|
54
65
|
}
|
package/dist/Alert/Alert.js
CHANGED
|
@@ -7,6 +7,7 @@ import { forwardRef } from "react";
|
|
|
7
7
|
import clsx from "clsx";
|
|
8
8
|
import { AlertManifest } from "./Alert.manifest.js";
|
|
9
9
|
import { devValidate } from "../_internal/devValidate.js";
|
|
10
|
+
import { useHoldBackNewText } from "../_internal/liveRegion.js";
|
|
10
11
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
12
|
var ICONS = {
|
|
12
13
|
info: Info,
|
|
@@ -48,6 +49,7 @@ var Alert = forwardRef((allProps, ref) => {
|
|
|
48
49
|
...props
|
|
49
50
|
} = allProps;
|
|
50
51
|
const role = variant === "error" || variant === "warning" ? "alert" : "status";
|
|
52
|
+
const holdBack = useHoldBackNewText(role === "status");
|
|
51
53
|
const Icon = ICONS[variant];
|
|
52
54
|
const hasBody = children != null && typeof children !== "boolean" && children !== "";
|
|
53
55
|
if (process.env.NODE_ENV !== "production") {
|
|
@@ -55,19 +57,26 @@ var Alert = forwardRef((allProps, ref) => {
|
|
|
55
57
|
}
|
|
56
58
|
return /* @__PURE__ */ jsxs("div", { ref, role, className: clsx(containerStyles({ variant }), className), ...props, children: [
|
|
57
59
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: clsx("shrink-0", accentStyles({ variant })), children: /* @__PURE__ */ jsx(Icon, { size: 16 }) }),
|
|
58
|
-
/* @__PURE__ */ jsxs(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
/* @__PURE__ */ jsxs(
|
|
61
|
+
"div",
|
|
62
|
+
{
|
|
63
|
+
className: "flex min-w-0 flex-1 flex-col gap-1u oprt-wrap-anywhere",
|
|
64
|
+
...holdBack ? { "aria-hidden": true } : {},
|
|
65
|
+
children: [
|
|
66
|
+
title && /* @__PURE__ */ jsx(
|
|
67
|
+
"p",
|
|
68
|
+
{
|
|
69
|
+
className: clsx(
|
|
70
|
+
"m-0 text-oprt-sm uppercase tracking-oprt-sm",
|
|
71
|
+
accentStyles({ variant })
|
|
72
|
+
),
|
|
73
|
+
children: title
|
|
74
|
+
}
|
|
65
75
|
),
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
] }),
|
|
76
|
+
hasBody && /* @__PURE__ */ jsx("div", { className: "text-oprt-base normal-case text-oprt-text", children })
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
),
|
|
71
80
|
dismissible && /* @__PURE__ */ jsx(
|
|
72
81
|
"button",
|
|
73
82
|
{
|
|
@@ -77,18 +77,21 @@ var AttachmentPreview = forwardRef(
|
|
|
77
77
|
/* @__PURE__ */ jsx("span", { className: "oprt-wrap-anywhere", children: attachment.mediaType })
|
|
78
78
|
] })
|
|
79
79
|
] }),
|
|
80
|
-
preview.kind === "image" &&
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
preview.kind === "image" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
81
|
+
/* @__PURE__ */ jsx("p", { role: "status", className: imageError ? "m-0 text-oprt-error" : "sr-only", children: imageError }),
|
|
82
|
+
!imageError && /* @__PURE__ */ jsx(
|
|
83
|
+
"img",
|
|
84
|
+
{
|
|
85
|
+
src: preview.src,
|
|
86
|
+
alt: preview.alt,
|
|
87
|
+
onError: () => setImageState(
|
|
88
|
+
(current) => current.key === imageKey ? { key: imageKey, failed: true } : current
|
|
89
|
+
),
|
|
90
|
+
className: clsx(PREVIEW_HEIGHT, "max-w-full self-start object-contain")
|
|
91
|
+
},
|
|
92
|
+
imageKey
|
|
93
|
+
)
|
|
94
|
+
] }),
|
|
92
95
|
preview.kind === "text" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
93
96
|
/* @__PURE__ */ jsx(
|
|
94
97
|
"pre",
|
package/dist/Chart/Chart.js
CHANGED
|
@@ -874,7 +874,15 @@ var Chart = forwardRef((allProps, ref) => {
|
|
|
874
874
|
onBlur: () => setInspectionFocused(false)
|
|
875
875
|
}
|
|
876
876
|
),
|
|
877
|
-
|
|
877
|
+
/* @__PURE__ */ jsx(
|
|
878
|
+
"p",
|
|
879
|
+
{
|
|
880
|
+
role: "status",
|
|
881
|
+
className: empty ? "m-0 py-6u text-oprt-base text-oprt-text-dim" : "sr-only",
|
|
882
|
+
children: empty
|
|
883
|
+
}
|
|
884
|
+
),
|
|
885
|
+
empty ? null : /* @__PURE__ */ jsxs(
|
|
878
886
|
"svg",
|
|
879
887
|
{
|
|
880
888
|
role: "img",
|
|
@@ -1092,7 +1100,7 @@ var Chart = forwardRef((allProps, ref) => {
|
|
|
1092
1100
|
variant: "ghost",
|
|
1093
1101
|
size: "sm",
|
|
1094
1102
|
className: "min-w-0 max-w-full oprt-wrap-anywhere",
|
|
1095
|
-
"aria-label": variant === "heatmap" ? `${seriesNumber.get(item.id)} ${item.label}` : item.label
|
|
1103
|
+
"aria-label": `${variant === "heatmap" ? `${seriesNumber.get(item.id)} ${item.label}` : item.label}${hidden.has(item.id) ? " Hidden" : ""}`,
|
|
1096
1104
|
"aria-pressed": !hidden.has(item.id),
|
|
1097
1105
|
disabled,
|
|
1098
1106
|
onClick: () => {
|
|
@@ -21,9 +21,28 @@ var feedbackStyles = cva("min-w-0 break-words font-sans text-oprt-base normal-ca
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
|
+
var reportedLabels = /* @__PURE__ */ new Set();
|
|
25
|
+
function reportLabelWithoutVisibleText(label) {
|
|
26
|
+
if (process.env.NODE_ENV === "production" || reportedLabels.has(label)) return;
|
|
27
|
+
reportedLabels.add(label);
|
|
28
|
+
console.error(
|
|
29
|
+
JSON.stringify(
|
|
30
|
+
{
|
|
31
|
+
component: "CopyButton",
|
|
32
|
+
error: "LABEL_OMITS_VISIBLE_TEXT",
|
|
33
|
+
prop: "label",
|
|
34
|
+
received: label,
|
|
35
|
+
suggestion: 'The button shows the word "Copy", so its label must contain it (WCAG 2.5.3, Label in Name). Start the label with it: "Copy equipment ID".'
|
|
36
|
+
},
|
|
37
|
+
null,
|
|
38
|
+
2
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
24
42
|
var CopyButton = forwardRef((allProps, ref) => {
|
|
25
43
|
const { value, label, onCopy, onCopyError, disabled = false, size = "md", className } = allProps;
|
|
26
44
|
const [pending, setPending] = useState(false);
|
|
45
|
+
if (!/copy/i.test(label)) reportLabelWithoutVisibleText(label);
|
|
27
46
|
const [feedback, setFeedback] = useState(null);
|
|
28
47
|
const pendingRef = useRef(false);
|
|
29
48
|
const isMounted = useIsMounted();
|
|
@@ -11,7 +11,7 @@ var CopyButtonManifest = {
|
|
|
11
11
|
label: {
|
|
12
12
|
type: "string",
|
|
13
13
|
required: true,
|
|
14
|
-
description: 'Accessible action name identifying the value. Feedback reads this label, then the outcome: "Copy equipment ID: copied".'
|
|
14
|
+
description: 'Accessible action name identifying the value. It must contain "Copy", the word the button shows. Feedback reads this label, then the outcome: "Copy equipment ID: copied".'
|
|
15
15
|
},
|
|
16
16
|
size: { type: "enum", values: ["sm", "md", "lg"], default: "md" },
|
|
17
17
|
disabled: { type: "boolean", default: false },
|
|
@@ -172,7 +172,15 @@ var EventTimeline = forwardRef(
|
|
|
172
172
|
"aria-label": label,
|
|
173
173
|
className: clsx(timelineStyles(), className),
|
|
174
174
|
children: [
|
|
175
|
-
|
|
175
|
+
/* @__PURE__ */ jsx(
|
|
176
|
+
"p",
|
|
177
|
+
{
|
|
178
|
+
role: "status",
|
|
179
|
+
className: !validRange ? "m-0 text-oprt-base text-oprt-text-dim" : "sr-only",
|
|
180
|
+
children: !validRange ? "Choose a valid time range with start before end." : ""
|
|
181
|
+
}
|
|
182
|
+
),
|
|
183
|
+
!validRange ? null : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
176
184
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-between gap-1u font-mono text-oprt-base text-oprt-text-dim", children: [
|
|
177
185
|
/* @__PURE__ */ jsx("time", { dateTime: new Date(start).toISOString(), children: utc(start) }),
|
|
178
186
|
/* @__PURE__ */ jsx("time", { dateTime: new Date(end).toISOString(), children: utc(end) })
|
|
@@ -165,7 +165,7 @@ var LayerControl = forwardRef(
|
|
|
165
165
|
{
|
|
166
166
|
size: "sm",
|
|
167
167
|
variant: "tertiary",
|
|
168
|
-
"aria-label": `Move ${item.label}
|
|
168
|
+
"aria-label": `Move up ${item.label}`,
|
|
169
169
|
disabled: !canMove(index, -1),
|
|
170
170
|
onClick: () => move(index, -1),
|
|
171
171
|
children: "Move up"
|
|
@@ -176,7 +176,7 @@ var LayerControl = forwardRef(
|
|
|
176
176
|
{
|
|
177
177
|
size: "sm",
|
|
178
178
|
variant: "tertiary",
|
|
179
|
-
"aria-label": `Move ${item.label}
|
|
179
|
+
"aria-label": `Move down ${item.label}`,
|
|
180
180
|
disabled: !canMove(index, 1),
|
|
181
181
|
onClick: () => move(index, 1),
|
|
182
182
|
children: "Move down"
|
package/dist/Toast/Toast.js
CHANGED
|
@@ -165,7 +165,7 @@ var Toast = forwardRef((allProps, ref) => {
|
|
|
165
165
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 select-text flex-col gap-1u oprt-wrap-anywhere", children: [
|
|
166
166
|
/* @__PURE__ */ jsx(RadixToast.Title, { className: titleStyles({ variant: severity }), children: title }),
|
|
167
167
|
description && /* @__PURE__ */ jsxs(RadixToast.Description, { className: "m-0 text-oprt-base normal-case text-oprt-text", children: [
|
|
168
|
-
/* @__PURE__ */ jsx("span", {
|
|
168
|
+
/* @__PURE__ */ jsx("span", { "data-radix-toast-announce-exclude": "", "data-radix-toast-announce-alt": ". " }),
|
|
169
169
|
description
|
|
170
170
|
] })
|
|
171
171
|
] }),
|
package/dist/Tooltip/Tooltip.js
CHANGED
|
@@ -23,6 +23,7 @@ import { isSingleElement, SCREEN_MARGIN, toPlacement, TOOLTIP_WIDTH } from "../_
|
|
|
23
23
|
import { FocusableWhenUnavailableProvider } from "../_internal/unavailable.js";
|
|
24
24
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
25
25
|
var PLAIN_TRIGGER = "inline-flex min-h-3u items-center border-0 bg-transparent p-0 [color:inherit] [font:inherit] [letter-spacing:inherit] underline decoration-dotted underline-offset-2 cursor-help outline-hidden focus-visible:shadow-oprt-focus";
|
|
26
|
+
var CLOSE_DELAY = 150;
|
|
26
27
|
var Tooltip = forwardRef(
|
|
27
28
|
({
|
|
28
29
|
content,
|
|
@@ -78,7 +79,7 @@ var Tooltip = forwardRef(
|
|
|
78
79
|
isOpen,
|
|
79
80
|
onOpenChange: setOpen,
|
|
80
81
|
delay: delayDuration,
|
|
81
|
-
closeDelay:
|
|
82
|
+
closeDelay: CLOSE_DELAY,
|
|
82
83
|
shouldCloseOnPress: !unavailable,
|
|
83
84
|
children: [
|
|
84
85
|
unavailable ? /* @__PURE__ */ jsx(FocusableWhenUnavailableProvider, { value: true, children: focusable }) : focusable,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/Tooltip/Tooltip.manifest.ts
|
|
2
2
|
var TooltipManifest = {
|
|
3
3
|
Tooltip: {
|
|
4
|
-
description: "Supplementary text on hover or keyboard focus of a trigger, wrapping at about 20rem. A disabled Button trigger stays focusable and announced unavailable, so focus or a tap shows the explanation. Built on React Aria Components (TooltipTrigger + Tooltip), portal'd, with a bordered surface.",
|
|
4
|
+
description: "Supplementary text on hover or keyboard focus of a trigger, wrapping at about 20rem. The pointer can move onto the tooltip without closing it; it closes 150 ms after the pointer has left both. A disabled Button trigger stays focusable and announced unavailable, so focus or a tap shows the explanation. Built on React Aria Components (TooltipTrigger + Tooltip), portal'd, with a bordered surface.",
|
|
5
5
|
props: {
|
|
6
6
|
side: { type: "enum", values: ["top", "right", "bottom", "left"], default: "top" },
|
|
7
7
|
align: { type: "enum", values: ["start", "center", "end"], default: "center" },
|
|
@@ -223,7 +223,7 @@ var UploadQueue = forwardRef(
|
|
|
223
223
|
}
|
|
224
224
|
)
|
|
225
225
|
] }),
|
|
226
|
-
/* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-text-dim", children: /* @__PURE__ */ jsx(ByteCountLabel, { value: item.sizeBytes }) }),
|
|
226
|
+
/* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-text-dim oprt-wrap-anywhere", children: /* @__PURE__ */ jsx(ByteCountLabel, { value: item.sizeBytes }) }),
|
|
227
227
|
item.message && /* @__PURE__ */ jsx("p", { className: "m-0 oprt-wrap-anywhere", children: item.message }),
|
|
228
228
|
transferring && (invalidProgress ? /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-error", children: "Invalid progress counts." }) : item.totalBytes === 0 ? /* @__PURE__ */ jsx("p", { className: "m-0 text-oprt-text-dim", children: `0${NUMBER_UNIT_SPACE}bytes total; no percentage available.` }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
229
229
|
/* @__PURE__ */ jsx(
|
|
@@ -233,7 +233,7 @@ var UploadQueue = forwardRef(
|
|
|
233
233
|
...item.totalBytes !== void 0 && validLoaded ? { value: item.loadedBytes, max: item.totalBytes } : {}
|
|
234
234
|
}
|
|
235
235
|
),
|
|
236
|
-
/* @__PURE__ */ jsxs("p", { className: "m-0 font-mono tabular-nums text-oprt-text-dim", children: [
|
|
236
|
+
/* @__PURE__ */ jsxs("p", { className: "m-0 font-mono tabular-nums text-oprt-text-dim oprt-wrap-anywhere", children: [
|
|
237
237
|
validLoaded ? `${item.loadedBytes}${NUMBER_UNIT_SPACE}bytes transferred` : "Transferred byte count unavailable",
|
|
238
238
|
item.totalBytes !== void 0 ? ` of ${item.totalBytes}${NUMBER_UNIT_SPACE}bytes` : "; total unknown",
|
|
239
239
|
"."
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/_internal/liveRegion.ts
|
|
4
|
+
import { useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
5
|
+
var neverChanges = () => () => {
|
|
6
|
+
};
|
|
7
|
+
var LIVE_REGION_LEAD_MS = 100;
|
|
8
|
+
function useHoldBackNewText(enabled) {
|
|
9
|
+
const hydrating = useSyncExternalStore(
|
|
10
|
+
neverChanges,
|
|
11
|
+
() => false,
|
|
12
|
+
() => true
|
|
13
|
+
);
|
|
14
|
+
const lateArrival = useRef(null);
|
|
15
|
+
if (lateArrival.current === null) lateArrival.current = !hydrating;
|
|
16
|
+
const [held, setHeld] = useState(Boolean(lateArrival.current));
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!held) return;
|
|
19
|
+
const timer = window.setTimeout(() => setHeld(false), LIVE_REGION_LEAD_MS);
|
|
20
|
+
return () => window.clearTimeout(timer);
|
|
21
|
+
}, [held]);
|
|
22
|
+
return enabled && held;
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
LIVE_REGION_LEAD_MS,
|
|
26
|
+
useHoldBackNewText
|
|
27
|
+
};
|