@wistia/vhs 6.0.2-beta.3abc3475.31abda7 → 6.0.2-beta.3c90e751.554d424
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/index.js +126 -42
- package/dist/index.js.map +1 -1
- package/package.json +12 -16
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
|
-
* @license @wistia/vhs v6.0.2-beta.
|
|
3
|
+
* @license @wistia/vhs v6.0.2-beta.3c90e751.554d424
|
|
4
4
|
*
|
|
5
5
|
* Copyright (c) 2021-2026, Wistia, Inc. and its affiliates.
|
|
6
6
|
*
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { AnimatePresence, MotionConfig, motion } from "motion/react";
|
|
11
|
-
import { LiveAnnouncer, LiveMessage } from "react-aria-live";
|
|
12
11
|
import { hasKey, isArray, isBoolean, isEmptyString, isFunction, isNil, isNonEmptyArray, isNonEmptyString, isNotNil, isNotUndefined, isNumber, isString, isUndefined } from "@wistia/type-guards";
|
|
13
|
-
import { Children, Fragment, cloneElement, createContext, forwardRef, isValidElement, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
12
|
+
import { Children, Fragment, cloneElement, createContext, forwardRef, isValidElement, memo, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
14
13
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
15
14
|
import { createPortal } from "react-dom";
|
|
16
15
|
import { ThemeProvider, createGlobalStyle, css, keyframes, styled, useTheme as useTheme$1 } from "styled-components";
|
|
@@ -110,6 +109,123 @@ const useThemeColor = (colorName = null) => {
|
|
|
110
109
|
return getColor(colors, colorName);
|
|
111
110
|
};
|
|
112
111
|
//#endregion
|
|
112
|
+
//#region src/providers/AriaLiveProvider/AriaLiveContext.tsx
|
|
113
|
+
const warnMissingProvider = () => {
|
|
114
|
+
console.warn("Announcement failed: render VHSProvider (or AriaLiveProvider) above this component");
|
|
115
|
+
};
|
|
116
|
+
const AriaLiveContext = createContext({ announce: warnMissingProvider });
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/components/ScreenReaderOnly/ScreenReaderOnly.tsx
|
|
119
|
+
const screenReaderOnlyStyle = css`
|
|
120
|
+
border: 0;
|
|
121
|
+
clip-path: inset(50%);
|
|
122
|
+
height: 1px;
|
|
123
|
+
overflow: hidden;
|
|
124
|
+
padding: 0;
|
|
125
|
+
position: absolute;
|
|
126
|
+
white-space: nowrap;
|
|
127
|
+
width: 1px;
|
|
128
|
+
`;
|
|
129
|
+
const VisuallyHidden = styled.div`
|
|
130
|
+
${screenReaderOnlyStyle}
|
|
131
|
+
`;
|
|
132
|
+
const VisuallyHiddenButFocusable = styled.div`
|
|
133
|
+
&:not(:focus-within) {
|
|
134
|
+
${screenReaderOnlyStyle}
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
const ScreenReaderOnly = ({ text, children, focusable = false, ...otherProps }) => {
|
|
138
|
+
const accessibleText = isNotNil(text) ? text : children;
|
|
139
|
+
if (focusable) return /* @__PURE__ */ jsx(VisuallyHiddenButFocusable, {
|
|
140
|
+
...otherProps,
|
|
141
|
+
children: accessibleText
|
|
142
|
+
});
|
|
143
|
+
return /* @__PURE__ */ jsx(VisuallyHidden, {
|
|
144
|
+
...otherProps,
|
|
145
|
+
children: accessibleText
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
ScreenReaderOnly.displayName = "ScreenReaderOnly_VHS";
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/providers/AriaLiveProvider/AriaLiveProvider.tsx
|
|
151
|
+
const ANNOUNCEMENT_CLEAR_DELAY_MS = 5e3;
|
|
152
|
+
const HiddenRegion = styled.div`
|
|
153
|
+
${screenReaderOnlyStyle}
|
|
154
|
+
margin: -1px;
|
|
155
|
+
`;
|
|
156
|
+
const LiveRegion = ({ announcements, politeness }) => /* @__PURE__ */ jsx(HiddenRegion, {
|
|
157
|
+
"aria-live": politeness,
|
|
158
|
+
role: "log",
|
|
159
|
+
children: announcements.filter((announcement) => announcement.politeness === politeness).map((announcement) => /* @__PURE__ */ jsx("div", { children: announcement.message }, announcement.id))
|
|
160
|
+
});
|
|
161
|
+
const AriaLiveProvider = ({ children }) => {
|
|
162
|
+
const [announcements, setAnnouncements] = useState([]);
|
|
163
|
+
const nextId = useRef(0);
|
|
164
|
+
const pendingKeys = useRef(/* @__PURE__ */ new Set());
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
pendingKeys.current.clear();
|
|
167
|
+
});
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
if (announcements.length === 0) return;
|
|
170
|
+
const soonest = Math.min(...announcements.map((announcement) => announcement.expiresAt));
|
|
171
|
+
const timer = setTimeout(() => {
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
setAnnouncements((current) => current.filter((announcement) => announcement.expiresAt > now));
|
|
174
|
+
}, Math.max(soonest - Date.now(), 0));
|
|
175
|
+
return () => {
|
|
176
|
+
clearTimeout(timer);
|
|
177
|
+
};
|
|
178
|
+
}, [announcements]);
|
|
179
|
+
const announce = useCallback((message, politeness = "polite") => {
|
|
180
|
+
if (!isNonEmptyString(message)) return;
|
|
181
|
+
const key = `${politeness}:${message}`;
|
|
182
|
+
if (pendingKeys.current.has(key)) return;
|
|
183
|
+
pendingKeys.current.add(key);
|
|
184
|
+
const id = nextId.current;
|
|
185
|
+
nextId.current += 1;
|
|
186
|
+
const expiresAt = Date.now() + ANNOUNCEMENT_CLEAR_DELAY_MS;
|
|
187
|
+
setAnnouncements((current) => [...current, {
|
|
188
|
+
expiresAt,
|
|
189
|
+
id,
|
|
190
|
+
message,
|
|
191
|
+
politeness
|
|
192
|
+
}]);
|
|
193
|
+
}, []);
|
|
194
|
+
const value = useMemo(() => ({ announce }), [announce]);
|
|
195
|
+
return /* @__PURE__ */ jsxs(AriaLiveContext, {
|
|
196
|
+
value,
|
|
197
|
+
children: [children, /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(LiveRegion, {
|
|
198
|
+
announcements,
|
|
199
|
+
politeness: "assertive"
|
|
200
|
+
}), /* @__PURE__ */ jsx(LiveRegion, {
|
|
201
|
+
announcements,
|
|
202
|
+
politeness: "polite"
|
|
203
|
+
})] })]
|
|
204
|
+
});
|
|
205
|
+
};
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/private/hooks/useAriaLive/useAriaLive.ts
|
|
208
|
+
const useAriaLive = () => use(AriaLiveContext);
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/private/components/LiveAnnouncement/LiveAnnouncement.tsx
|
|
211
|
+
/**
|
|
212
|
+
* Announces `message` to screen readers when it mounts and each time `message` changes.
|
|
213
|
+
*/
|
|
214
|
+
const LiveAnnouncement = ({ message, politeness = "polite" }) => {
|
|
215
|
+
const { announce } = useAriaLive();
|
|
216
|
+
const announcedMessage = useRef(null);
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
if (announcedMessage.current === message) return;
|
|
219
|
+
announcedMessage.current = message;
|
|
220
|
+
announce(message, politeness);
|
|
221
|
+
}, [
|
|
222
|
+
announce,
|
|
223
|
+
message,
|
|
224
|
+
politeness
|
|
225
|
+
]);
|
|
226
|
+
return null;
|
|
227
|
+
};
|
|
228
|
+
//#endregion
|
|
113
229
|
//#region src/private/helpers/coerceToBoolean/coerceToBoolean.ts
|
|
114
230
|
/**
|
|
115
231
|
* Converts a boolean string value `value` into a boolean. If passed
|
|
@@ -1618,9 +1734,9 @@ const ButtonContent = ({ children, icon, iconName, iconPosition, isLoading, size
|
|
|
1618
1734
|
return /* @__PURE__ */ jsxs(Fragment$1, { children: [isLoading ? /* @__PURE__ */ jsxs(ButtonLoadingContainer, {
|
|
1619
1735
|
$textAlign: textAlign,
|
|
1620
1736
|
"data-testid": "button-content-loading",
|
|
1621
|
-
children: [/* @__PURE__ */ jsx(
|
|
1622
|
-
|
|
1623
|
-
|
|
1737
|
+
children: [/* @__PURE__ */ jsx(LiveAnnouncement, {
|
|
1738
|
+
message: "Button has been disabled while action is in progress",
|
|
1739
|
+
politeness: "assertive"
|
|
1624
1740
|
}), /* @__PURE__ */ jsx(Icon, {
|
|
1625
1741
|
size: iconSizeMap$2[size],
|
|
1626
1742
|
type: "spinner"
|
|
@@ -2090,9 +2206,9 @@ const Toast = memo(({ actionInterceptor, actionLabel = defaultActionLabel, color
|
|
|
2090
2206
|
...otherProps,
|
|
2091
2207
|
children: [/* @__PURE__ */ jsxs(ToastContent, {
|
|
2092
2208
|
"data-testid": "toast-message",
|
|
2093
|
-
children: [/* @__PURE__ */ jsx(
|
|
2094
|
-
|
|
2095
|
-
|
|
2209
|
+
children: [/* @__PURE__ */ jsx(LiveAnnouncement, {
|
|
2210
|
+
message,
|
|
2211
|
+
politeness: ariaLive
|
|
2096
2212
|
}), message]
|
|
2097
2213
|
}), /* @__PURE__ */ jsx(Button, {
|
|
2098
2214
|
colorOverride: accentColor,
|
|
@@ -2644,7 +2760,7 @@ const DEFAULT_THEME = {};
|
|
|
2644
2760
|
const VHSProvider = ({ children, theme = DEFAULT_THEME, globalStylesIncluded = true, skipAnimations = false, Link }) => {
|
|
2645
2761
|
return /* @__PURE__ */ jsxs(VHSThemeProvider, {
|
|
2646
2762
|
theme,
|
|
2647
|
-
children: [globalStylesIncluded ? /* @__PURE__ */ jsx(GlobalStyle$1, {}) : null, /* @__PURE__ */ jsx(
|
|
2763
|
+
children: [globalStylesIncluded ? /* @__PURE__ */ jsx(GlobalStyle$1, {}) : null, /* @__PURE__ */ jsx(AriaLiveProvider, { children: /* @__PURE__ */ jsx(MotionConfig, {
|
|
2648
2764
|
reducedMotion: skipAnimations ? "always" : "user",
|
|
2649
2765
|
children: /* @__PURE__ */ jsx(WrapperProvider, {
|
|
2650
2766
|
value: { Link },
|
|
@@ -2654,38 +2770,6 @@ const VHSProvider = ({ children, theme = DEFAULT_THEME, globalStylesIncluded = t
|
|
|
2654
2770
|
});
|
|
2655
2771
|
};
|
|
2656
2772
|
//#endregion
|
|
2657
|
-
//#region src/components/ScreenReaderOnly/ScreenReaderOnly.tsx
|
|
2658
|
-
const screenReaderOnlyStyle = css`
|
|
2659
|
-
border: 0;
|
|
2660
|
-
clip-path: inset(50%);
|
|
2661
|
-
height: 1px;
|
|
2662
|
-
overflow: hidden;
|
|
2663
|
-
padding: 0;
|
|
2664
|
-
position: absolute;
|
|
2665
|
-
white-space: nowrap;
|
|
2666
|
-
width: 1px;
|
|
2667
|
-
`;
|
|
2668
|
-
const VisuallyHidden = styled.div`
|
|
2669
|
-
${screenReaderOnlyStyle}
|
|
2670
|
-
`;
|
|
2671
|
-
const VisuallyHiddenButFocusable = styled.div`
|
|
2672
|
-
&:not(:focus-within) {
|
|
2673
|
-
${screenReaderOnlyStyle}
|
|
2674
|
-
}
|
|
2675
|
-
`;
|
|
2676
|
-
const ScreenReaderOnly = ({ text, children, focusable = false, ...otherProps }) => {
|
|
2677
|
-
const accessibleText = isNotNil(text) ? text : children;
|
|
2678
|
-
if (focusable) return /* @__PURE__ */ jsx(VisuallyHiddenButFocusable, {
|
|
2679
|
-
...otherProps,
|
|
2680
|
-
children: accessibleText
|
|
2681
|
-
});
|
|
2682
|
-
return /* @__PURE__ */ jsx(VisuallyHidden, {
|
|
2683
|
-
...otherProps,
|
|
2684
|
-
children: accessibleText
|
|
2685
|
-
});
|
|
2686
|
-
};
|
|
2687
|
-
ScreenReaderOnly.displayName = "ScreenReaderOnly_VHS";
|
|
2688
|
-
//#endregion
|
|
2689
2773
|
//#region src/components/Truncate/Truncate.tsx
|
|
2690
2774
|
const Truncate = ({ children, maxChars, terminator = "...", breakOnWords = false }) => {
|
|
2691
2775
|
if (isNil(children)) return null;
|