@dataimago/ui 0.1.0-alpha.42 → 0.1.0-alpha.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +78 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +79 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Current public exports:
|
|
|
21
21
|
- `<Logo>` — manifest-driven mark renderer backed by `@dataimago/brand`.
|
|
22
22
|
- `<BrandAnimation>` — decorative brand animation with reduced-motion poster fallback.
|
|
23
23
|
- `<NavbarLogo>` — animated navbar mark with reduced-motion poster fallback.
|
|
24
|
-
- `<CaptionedVideo>` — a video with the video-foundry's caption treatment drawn over it (frosted panel, word tone, MathML), from a release's `captions.<lang>.json`; the WebVTT file stays as the hidden native track and the fallback text. Fullscreen takes the whole player so the captions come along. `<CaptionLayer>` draws one moment of it (pure presentation), and the caption functions (`pageAt`, `wordTone`, `captionLook`, …) match the foundry's burned-in treatment (the foundry imports them once this ships; #407). Needs `@dataimago/css` loaded for the palette, `--font-family-sans` and the self-hosted Noto Sans Math.
|
|
24
|
+
- `<CaptionedVideo>` — a video with the video-foundry's caption treatment drawn over it (frosted panel, word tone, MathML), from a release's `captions.<lang>.json`; the WebVTT file stays as the hidden native track and the fallback text. Fullscreen takes the whole player so the captions come along. `<CaptionLayer>` draws one moment of it (pure presentation), and the caption functions (`pageAt`, `wordTone`, `captionLook`, …) match the foundry's burned-in treatment (the foundry imports them once this ships; #407). Needs `@dataimago/css` loaded for the palette, `--font-family-sans` and the self-hosted Noto Sans Math. `poster` is required and nothing autoplays. Under `prefers-reduced-motion: reduce` the player plays `reducedSrc` if given; otherwise it says the video is animated throughout and offers `storyboard` (a link) first, and plays the full cut only if the reader chooses it.
|
|
25
25
|
|
|
26
26
|
```tsx
|
|
27
27
|
<CaptionedVideo
|
package/dist/index.cjs
CHANGED
|
@@ -924,7 +924,7 @@ function cueLines(cue) {
|
|
|
924
924
|
var CONTROLS_HOLD_MS = 2500;
|
|
925
925
|
var CONTROLS_HEIGHT = 56;
|
|
926
926
|
var usePrefersReducedMotion3 = () => {
|
|
927
|
-
const [reduced, setReduced] = (0, import_react2.useState)(
|
|
927
|
+
const [reduced, setReduced] = (0, import_react2.useState)(null);
|
|
928
928
|
(0, import_react2.useEffect)(() => {
|
|
929
929
|
const q = window.matchMedia?.("(prefers-reduced-motion: reduce)");
|
|
930
930
|
if (!q) {
|
|
@@ -938,6 +938,37 @@ var usePrefersReducedMotion3 = () => {
|
|
|
938
938
|
}, []);
|
|
939
939
|
return reduced;
|
|
940
940
|
};
|
|
941
|
+
var linkStyle = {
|
|
942
|
+
display: "inline-flex",
|
|
943
|
+
alignItems: "center",
|
|
944
|
+
justifyContent: "center",
|
|
945
|
+
minWidth: 48,
|
|
946
|
+
minHeight: 48,
|
|
947
|
+
padding: "0 8px",
|
|
948
|
+
color: "var(--color-palette-ink-900)",
|
|
949
|
+
textUnderlineOffset: "0.2em"
|
|
950
|
+
};
|
|
951
|
+
var videoOnlyFullscreen = () => typeof navigator !== "undefined" && /iPhone|iPod/.test(navigator.userAgent);
|
|
952
|
+
var gateStyle = {
|
|
953
|
+
position: "absolute",
|
|
954
|
+
left: "50%",
|
|
955
|
+
top: "50%",
|
|
956
|
+
transform: "translate(-50%, -50%)",
|
|
957
|
+
maxWidth: "min(90%, 26rem)",
|
|
958
|
+
display: "flex",
|
|
959
|
+
flexDirection: "column",
|
|
960
|
+
alignItems: "center",
|
|
961
|
+
gap: 10,
|
|
962
|
+
padding: "14px 18px",
|
|
963
|
+
textAlign: "center",
|
|
964
|
+
fontFamily: "var(--font-family-sans)",
|
|
965
|
+
fontSize: 15,
|
|
966
|
+
lineHeight: 1.4,
|
|
967
|
+
color: "var(--color-palette-ink-900)",
|
|
968
|
+
background: "var(--color-palette-stone-50)",
|
|
969
|
+
border: "1px solid var(--color-palette-ink-200)",
|
|
970
|
+
borderRadius: 6
|
|
971
|
+
};
|
|
941
972
|
var buttonStyle = {
|
|
942
973
|
font: "inherit",
|
|
943
974
|
fontFamily: "var(--font-family-sans)",
|
|
@@ -976,6 +1007,8 @@ var CaptionedVideo = ({
|
|
|
976
1007
|
captions,
|
|
977
1008
|
fallbackCaptions,
|
|
978
1009
|
chapters,
|
|
1010
|
+
reducedSrc,
|
|
1011
|
+
storyboard,
|
|
979
1012
|
title,
|
|
980
1013
|
lang = "en",
|
|
981
1014
|
className,
|
|
@@ -996,7 +1029,12 @@ var CaptionedVideo = ({
|
|
|
996
1029
|
const [paused, setPaused] = (0, import_react2.useState)(true);
|
|
997
1030
|
const [active, setActive] = (0, import_react2.useState)(false);
|
|
998
1031
|
const [focusWithin, setFocusWithin] = (0, import_react2.useState)(false);
|
|
999
|
-
const
|
|
1032
|
+
const prefersReduced = usePrefersReducedMotion3();
|
|
1033
|
+
const reducedMotion = prefersReduced !== false;
|
|
1034
|
+
const [chosen, setChosen] = (0, import_react2.useState)(false);
|
|
1035
|
+
const noticeId = (0, import_react2.useId)();
|
|
1036
|
+
const gated = prefersReduced === true && !reducedSrc && !chosen;
|
|
1037
|
+
const videoSrc = prefersReduced && reducedSrc ? reducedSrc : prefersReduced === null && reducedSrc ? void 0 : src;
|
|
1000
1038
|
(0, import_react2.useEffect)(() => setOn(readCaptionsChoice()), []);
|
|
1001
1039
|
(0, import_react2.useEffect)(() => {
|
|
1002
1040
|
let live = true;
|
|
@@ -1051,7 +1089,7 @@ var CaptionedVideo = ({
|
|
|
1051
1089
|
cancelAnimationFrame(raf);
|
|
1052
1090
|
for (const e of events) v.removeEventListener(e, sync);
|
|
1053
1091
|
};
|
|
1054
|
-
}, [
|
|
1092
|
+
}, [videoSrc]);
|
|
1055
1093
|
(0, import_react2.useEffect)(() => {
|
|
1056
1094
|
const el = stageRef.current;
|
|
1057
1095
|
if (!el || typeof ResizeObserver === "undefined") return;
|
|
@@ -1126,21 +1164,32 @@ var CaptionedVideo = ({
|
|
|
1126
1164
|
holdRef.current = window.setTimeout(() => setActive(false), CONTROLS_HOLD_MS);
|
|
1127
1165
|
}, []);
|
|
1128
1166
|
(0, import_react2.useEffect)(() => () => window.clearTimeout(holdRef.current), []);
|
|
1167
|
+
(0, import_react2.useEffect)(() => {
|
|
1168
|
+
const v = videoRef.current;
|
|
1169
|
+
if (!v) return;
|
|
1170
|
+
v.addEventListener("play", wake);
|
|
1171
|
+
return () => v.removeEventListener("play", wake);
|
|
1172
|
+
}, [wake]);
|
|
1129
1173
|
const toggleCaptions = (0, import_react2.useCallback)(() => setOn((x) => {
|
|
1130
1174
|
writeCaptionsChoice(!x);
|
|
1131
1175
|
return !x;
|
|
1132
1176
|
}), []);
|
|
1133
1177
|
const toggleFullscreen = (0, import_react2.useCallback)(() => {
|
|
1134
|
-
const wrap = wrapRef.current
|
|
1178
|
+
const wrap = wrapRef.current;
|
|
1179
|
+
const v = videoRef.current;
|
|
1135
1180
|
if (document.fullscreenElement === wrap) {
|
|
1136
1181
|
void document.exitFullscreen();
|
|
1137
1182
|
return;
|
|
1138
1183
|
}
|
|
1139
|
-
if (
|
|
1140
|
-
|
|
1184
|
+
if (v?.webkitDisplayingFullscreen) {
|
|
1185
|
+
v.webkitExitFullscreen?.();
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
if (!wrap?.requestFullscreen || !document.fullscreenEnabled || videoOnlyFullscreen()) {
|
|
1189
|
+
v?.webkitEnterFullscreen?.();
|
|
1141
1190
|
return;
|
|
1142
1191
|
}
|
|
1143
|
-
v?.webkitEnterFullscreen?.();
|
|
1192
|
+
wrap.requestFullscreen().catch(() => v?.webkitEnterFullscreen?.());
|
|
1144
1193
|
}, []);
|
|
1145
1194
|
const onKey = (e) => {
|
|
1146
1195
|
const t = e.target;
|
|
@@ -1164,6 +1213,7 @@ var CaptionedVideo = ({
|
|
|
1164
1213
|
ref: wrapRef,
|
|
1165
1214
|
className,
|
|
1166
1215
|
onPointerMove: wake,
|
|
1216
|
+
onPointerDown: wake,
|
|
1167
1217
|
onFocus: () => {
|
|
1168
1218
|
setFocusWithin(true);
|
|
1169
1219
|
wake();
|
|
@@ -1185,14 +1235,15 @@ var CaptionedVideo = ({
|
|
|
1185
1235
|
"video",
|
|
1186
1236
|
{
|
|
1187
1237
|
ref: videoRef,
|
|
1188
|
-
src,
|
|
1238
|
+
src: videoSrc,
|
|
1189
1239
|
poster,
|
|
1190
|
-
controls:
|
|
1240
|
+
controls: !gated,
|
|
1191
1241
|
controlsList: "nofullscreen",
|
|
1192
1242
|
"data-dataimago-captioned-video": "",
|
|
1193
1243
|
playsInline: true,
|
|
1194
1244
|
preload: "metadata",
|
|
1195
1245
|
"aria-label": title,
|
|
1246
|
+
"aria-hidden": gated || void 0,
|
|
1196
1247
|
style: { display: "block", width: "100%", height: "100%" },
|
|
1197
1248
|
children: [
|
|
1198
1249
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("track", { ref: trackRef, kind: "captions", src: fallbackCaptions, srcLang: lang, label: "Captions", default: true }),
|
|
@@ -1211,11 +1262,28 @@ var CaptionedVideo = ({
|
|
|
1211
1262
|
page: track ? void 0 : cue
|
|
1212
1263
|
}
|
|
1213
1264
|
) : null,
|
|
1265
|
+
gated ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { role: "group", "aria-labelledby": noticeId, style: gateStyle, children: [
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { id: noticeId, style: { margin: 0 }, children: "This video is animated throughout." }),
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1268
|
+
"button",
|
|
1269
|
+
{
|
|
1270
|
+
type: "button",
|
|
1271
|
+
style: { ...buttonStyle, pointerEvents: "auto" },
|
|
1272
|
+
onClick: () => {
|
|
1273
|
+
setChosen(true);
|
|
1274
|
+
void videoRef.current?.play().catch(() => {
|
|
1275
|
+
});
|
|
1276
|
+
},
|
|
1277
|
+
children: "Play the video"
|
|
1278
|
+
}
|
|
1279
|
+
),
|
|
1280
|
+
storyboard ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("a", { href: storyboard, style: linkStyle, children: "View the storyboard instead" }) : null
|
|
1281
|
+
] }) : null,
|
|
1214
1282
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: {
|
|
1215
1283
|
position: "absolute",
|
|
1216
1284
|
top: 8,
|
|
1217
1285
|
right: 8,
|
|
1218
|
-
display: "flex",
|
|
1286
|
+
display: gated ? "none" : "flex",
|
|
1219
1287
|
gap: 6,
|
|
1220
1288
|
pointerEvents: "none",
|
|
1221
1289
|
// hidden once faded: an invisible button must not take a click or a Tab stop
|