@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/dist/index.js CHANGED
@@ -616,7 +616,7 @@ function ThemeScript({ nonce }) {
616
616
  }
617
617
 
618
618
  // src/video/CaptionedVideo.tsx
619
- import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
619
+ import { useCallback, useEffect as useEffect3, useId, useRef as useRef2, useState as useState3 } from "react";
620
620
 
621
621
  // src/video/CaptionLayer.tsx
622
622
  import { useLayoutEffect, useRef } from "react";
@@ -866,7 +866,7 @@ function cueLines(cue) {
866
866
  var CONTROLS_HOLD_MS = 2500;
867
867
  var CONTROLS_HEIGHT = 56;
868
868
  var usePrefersReducedMotion3 = () => {
869
- const [reduced, setReduced] = useState3(true);
869
+ const [reduced, setReduced] = useState3(null);
870
870
  useEffect3(() => {
871
871
  const q = window.matchMedia?.("(prefers-reduced-motion: reduce)");
872
872
  if (!q) {
@@ -880,6 +880,37 @@ var usePrefersReducedMotion3 = () => {
880
880
  }, []);
881
881
  return reduced;
882
882
  };
883
+ var linkStyle = {
884
+ display: "inline-flex",
885
+ alignItems: "center",
886
+ justifyContent: "center",
887
+ minWidth: 48,
888
+ minHeight: 48,
889
+ padding: "0 8px",
890
+ color: "var(--color-palette-ink-900)",
891
+ textUnderlineOffset: "0.2em"
892
+ };
893
+ var videoOnlyFullscreen = () => typeof navigator !== "undefined" && /iPhone|iPod/.test(navigator.userAgent);
894
+ var gateStyle = {
895
+ position: "absolute",
896
+ left: "50%",
897
+ top: "50%",
898
+ transform: "translate(-50%, -50%)",
899
+ maxWidth: "min(90%, 26rem)",
900
+ display: "flex",
901
+ flexDirection: "column",
902
+ alignItems: "center",
903
+ gap: 10,
904
+ padding: "14px 18px",
905
+ textAlign: "center",
906
+ fontFamily: "var(--font-family-sans)",
907
+ fontSize: 15,
908
+ lineHeight: 1.4,
909
+ color: "var(--color-palette-ink-900)",
910
+ background: "var(--color-palette-stone-50)",
911
+ border: "1px solid var(--color-palette-ink-200)",
912
+ borderRadius: 6
913
+ };
883
914
  var buttonStyle = {
884
915
  font: "inherit",
885
916
  fontFamily: "var(--font-family-sans)",
@@ -918,6 +949,8 @@ var CaptionedVideo = ({
918
949
  captions,
919
950
  fallbackCaptions,
920
951
  chapters,
952
+ reducedSrc,
953
+ storyboard,
921
954
  title,
922
955
  lang = "en",
923
956
  className,
@@ -938,7 +971,12 @@ var CaptionedVideo = ({
938
971
  const [paused, setPaused] = useState3(true);
939
972
  const [active, setActive] = useState3(false);
940
973
  const [focusWithin, setFocusWithin] = useState3(false);
941
- const reducedMotion = usePrefersReducedMotion3();
974
+ const prefersReduced = usePrefersReducedMotion3();
975
+ const reducedMotion = prefersReduced !== false;
976
+ const [chosen, setChosen] = useState3(false);
977
+ const noticeId = useId();
978
+ const gated = prefersReduced === true && !reducedSrc && !chosen;
979
+ const videoSrc = prefersReduced && reducedSrc ? reducedSrc : prefersReduced === null && reducedSrc ? void 0 : src;
942
980
  useEffect3(() => setOn(readCaptionsChoice()), []);
943
981
  useEffect3(() => {
944
982
  let live = true;
@@ -993,7 +1031,7 @@ var CaptionedVideo = ({
993
1031
  cancelAnimationFrame(raf);
994
1032
  for (const e of events) v.removeEventListener(e, sync);
995
1033
  };
996
- }, [src]);
1034
+ }, [videoSrc]);
997
1035
  useEffect3(() => {
998
1036
  const el = stageRef.current;
999
1037
  if (!el || typeof ResizeObserver === "undefined") return;
@@ -1068,21 +1106,32 @@ var CaptionedVideo = ({
1068
1106
  holdRef.current = window.setTimeout(() => setActive(false), CONTROLS_HOLD_MS);
1069
1107
  }, []);
1070
1108
  useEffect3(() => () => window.clearTimeout(holdRef.current), []);
1109
+ useEffect3(() => {
1110
+ const v = videoRef.current;
1111
+ if (!v) return;
1112
+ v.addEventListener("play", wake);
1113
+ return () => v.removeEventListener("play", wake);
1114
+ }, [wake]);
1071
1115
  const toggleCaptions = useCallback(() => setOn((x) => {
1072
1116
  writeCaptionsChoice(!x);
1073
1117
  return !x;
1074
1118
  }), []);
1075
1119
  const toggleFullscreen = useCallback(() => {
1076
- const wrap = wrapRef.current, v = videoRef.current;
1120
+ const wrap = wrapRef.current;
1121
+ const v = videoRef.current;
1077
1122
  if (document.fullscreenElement === wrap) {
1078
1123
  void document.exitFullscreen();
1079
1124
  return;
1080
1125
  }
1081
- if (wrap?.requestFullscreen) {
1082
- wrap.requestFullscreen().catch(() => v?.webkitEnterFullscreen?.());
1126
+ if (v?.webkitDisplayingFullscreen) {
1127
+ v.webkitExitFullscreen?.();
1128
+ return;
1129
+ }
1130
+ if (!wrap?.requestFullscreen || !document.fullscreenEnabled || videoOnlyFullscreen()) {
1131
+ v?.webkitEnterFullscreen?.();
1083
1132
  return;
1084
1133
  }
1085
- v?.webkitEnterFullscreen?.();
1134
+ wrap.requestFullscreen().catch(() => v?.webkitEnterFullscreen?.());
1086
1135
  }, []);
1087
1136
  const onKey = (e) => {
1088
1137
  const t = e.target;
@@ -1106,6 +1155,7 @@ var CaptionedVideo = ({
1106
1155
  ref: wrapRef,
1107
1156
  className,
1108
1157
  onPointerMove: wake,
1158
+ onPointerDown: wake,
1109
1159
  onFocus: () => {
1110
1160
  setFocusWithin(true);
1111
1161
  wake();
@@ -1127,14 +1177,15 @@ var CaptionedVideo = ({
1127
1177
  "video",
1128
1178
  {
1129
1179
  ref: videoRef,
1130
- src,
1180
+ src: videoSrc,
1131
1181
  poster,
1132
- controls: true,
1182
+ controls: !gated,
1133
1183
  controlsList: "nofullscreen",
1134
1184
  "data-dataimago-captioned-video": "",
1135
1185
  playsInline: true,
1136
1186
  preload: "metadata",
1137
1187
  "aria-label": title,
1188
+ "aria-hidden": gated || void 0,
1138
1189
  style: { display: "block", width: "100%", height: "100%" },
1139
1190
  children: [
1140
1191
  /* @__PURE__ */ jsx12("track", { ref: trackRef, kind: "captions", src: fallbackCaptions, srcLang: lang, label: "Captions", default: true }),
@@ -1153,11 +1204,28 @@ var CaptionedVideo = ({
1153
1204
  page: track ? void 0 : cue
1154
1205
  }
1155
1206
  ) : null,
1207
+ gated ? /* @__PURE__ */ jsxs7("div", { role: "group", "aria-labelledby": noticeId, style: gateStyle, children: [
1208
+ /* @__PURE__ */ jsx12("p", { id: noticeId, style: { margin: 0 }, children: "This video is animated throughout." }),
1209
+ /* @__PURE__ */ jsx12(
1210
+ "button",
1211
+ {
1212
+ type: "button",
1213
+ style: { ...buttonStyle, pointerEvents: "auto" },
1214
+ onClick: () => {
1215
+ setChosen(true);
1216
+ void videoRef.current?.play().catch(() => {
1217
+ });
1218
+ },
1219
+ children: "Play the video"
1220
+ }
1221
+ ),
1222
+ storyboard ? /* @__PURE__ */ jsx12("a", { href: storyboard, style: linkStyle, children: "View the storyboard instead" }) : null
1223
+ ] }) : null,
1156
1224
  /* @__PURE__ */ jsxs7("div", { style: {
1157
1225
  position: "absolute",
1158
1226
  top: 8,
1159
1227
  right: 8,
1160
- display: "flex",
1228
+ display: gated ? "none" : "flex",
1161
1229
  gap: 6,
1162
1230
  pointerEvents: "none",
1163
1231
  // hidden once faded: an invisible button must not take a click or a Tab stop