@dataimago/ui 0.1.0-alpha.35 → 0.1.0-alpha.42
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 +12 -0
- package/dist/index.cjs +591 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +155 -1
- package/dist/index.d.ts +155 -1
- package/dist/index.js +579 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -614,9 +614,580 @@ import { jsx as jsx10 } from "react/jsx-runtime";
|
|
|
614
614
|
function ThemeScript({ nonce }) {
|
|
615
615
|
return /* @__PURE__ */ jsx10("script", { nonce, dangerouslySetInnerHTML: { __html: themeInitScript } });
|
|
616
616
|
}
|
|
617
|
+
|
|
618
|
+
// src/video/CaptionedVideo.tsx
|
|
619
|
+
import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
|
|
620
|
+
|
|
621
|
+
// src/video/CaptionLayer.tsx
|
|
622
|
+
import { useLayoutEffect, useRef } from "react";
|
|
623
|
+
|
|
624
|
+
// src/video/captions.ts
|
|
625
|
+
var CAPTIONS_SCHEMA = "dataimago.video_captions.v1";
|
|
626
|
+
function pageAt(pages, t) {
|
|
627
|
+
let lo = 0, hi = pages.length - 1;
|
|
628
|
+
while (lo <= hi) {
|
|
629
|
+
const mid = lo + hi >> 1, p = pages[mid];
|
|
630
|
+
if (t < p.start) hi = mid - 1;
|
|
631
|
+
else if (t >= p.end) lo = mid + 1;
|
|
632
|
+
else return p;
|
|
633
|
+
}
|
|
634
|
+
return void 0;
|
|
635
|
+
}
|
|
636
|
+
function wordTone(at, t, span) {
|
|
637
|
+
if (span <= 0) return t >= at ? 1 : 0;
|
|
638
|
+
return Math.min(1, Math.max(0, (t - at) / span));
|
|
639
|
+
}
|
|
640
|
+
function toneColor(tone, ink, inkMute) {
|
|
641
|
+
if (tone >= 1) return ink;
|
|
642
|
+
if (tone <= 0) return inkMute;
|
|
643
|
+
return `color-mix(in srgb, ${ink} ${Math.round(tone * 100)}%, ${inkMute})`;
|
|
644
|
+
}
|
|
645
|
+
function splitMath(text) {
|
|
646
|
+
const m = text.match(/^(.*?)\$([^$\s]+)\$(.*)$/);
|
|
647
|
+
return m ? { before: m[1], tex: m[2], after: m[3] } : null;
|
|
648
|
+
}
|
|
649
|
+
function paletteVar(token) {
|
|
650
|
+
return /^color\.palette\.[a-z]+\.\d{2,3}$/.test(token) ? `var(--${token.replace(/\./g, "-")})` : null;
|
|
651
|
+
}
|
|
652
|
+
function captionLook(treatment, o) {
|
|
653
|
+
const { size, scale, ink, paper } = o;
|
|
654
|
+
const halo = [2, 6, 14, 24].map((r) => `0 0 ${r * scale}px ${paper}`).join(", ");
|
|
655
|
+
const panel = treatment === "boxed" ? { background: ink, padding: "0.35em 0.7em", borderRadius: 4 * scale } : treatment === "frosted" ? {
|
|
656
|
+
background: `color-mix(in srgb, ${paper} 82%, transparent)`,
|
|
657
|
+
backdropFilter: `blur(${14 * scale}px)`,
|
|
658
|
+
WebkitBackdropFilter: `blur(${14 * scale}px)`,
|
|
659
|
+
padding: "0.5em 0.95em",
|
|
660
|
+
borderRadius: size * 0.6,
|
|
661
|
+
boxShadow: `0 ${size * 0.15}px ${size * 0.8}px color-mix(in srgb, ${ink} 10%, transparent)`,
|
|
662
|
+
border: `${Math.max(1, scale)}px solid color-mix(in srgb, ${ink} 8%, transparent)`
|
|
663
|
+
} : { padding: "0.2em 0.4em", textShadow: halo };
|
|
664
|
+
const text = {
|
|
665
|
+
fontFamily: o.family,
|
|
666
|
+
fontWeight: treatment === "boxed" ? 400 : o.weight,
|
|
667
|
+
fontSize: size,
|
|
668
|
+
lineHeight: o.lineHeight,
|
|
669
|
+
textAlign: "center",
|
|
670
|
+
letterSpacing: treatment === "boxed" ? 0 : "0.005em",
|
|
671
|
+
...treatment === "boxed" ? { color: paper } : {}
|
|
672
|
+
};
|
|
673
|
+
return { panel, text, lift: treatment === "frosted" ? Math.round(size * 0.95) : 0, toned: treatment !== "boxed" };
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/video/mathml.ts
|
|
677
|
+
var MATHML_NS = "http://www.w3.org/1998/Math/MathML";
|
|
678
|
+
var ELEMENTS = /* @__PURE__ */ new Set([
|
|
679
|
+
"math",
|
|
680
|
+
"mrow",
|
|
681
|
+
"mi",
|
|
682
|
+
"mn",
|
|
683
|
+
"mo",
|
|
684
|
+
"ms",
|
|
685
|
+
"mtext",
|
|
686
|
+
"mspace",
|
|
687
|
+
"msub",
|
|
688
|
+
"msup",
|
|
689
|
+
"msubsup",
|
|
690
|
+
"mfrac",
|
|
691
|
+
"msqrt",
|
|
692
|
+
"mroot",
|
|
693
|
+
"mover",
|
|
694
|
+
"munder",
|
|
695
|
+
"munderover",
|
|
696
|
+
"mtable",
|
|
697
|
+
"mtr",
|
|
698
|
+
"mtd",
|
|
699
|
+
"mpadded",
|
|
700
|
+
"mphantom",
|
|
701
|
+
"mstyle",
|
|
702
|
+
"menclose",
|
|
703
|
+
"semantics",
|
|
704
|
+
"annotation"
|
|
705
|
+
]);
|
|
706
|
+
var ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
707
|
+
"display",
|
|
708
|
+
"mathvariant",
|
|
709
|
+
"stretchy",
|
|
710
|
+
"fence",
|
|
711
|
+
"separator",
|
|
712
|
+
"form",
|
|
713
|
+
"lspace",
|
|
714
|
+
"rspace",
|
|
715
|
+
"accent",
|
|
716
|
+
"accentunder",
|
|
717
|
+
"movablelimits",
|
|
718
|
+
"largeop",
|
|
719
|
+
"symmetric",
|
|
720
|
+
"minsize",
|
|
721
|
+
"maxsize",
|
|
722
|
+
"width",
|
|
723
|
+
"height",
|
|
724
|
+
"depth",
|
|
725
|
+
"voffset",
|
|
726
|
+
"linethickness",
|
|
727
|
+
"columnalign",
|
|
728
|
+
"rowalign",
|
|
729
|
+
"columnspacing",
|
|
730
|
+
"rowspacing",
|
|
731
|
+
"scriptlevel",
|
|
732
|
+
"displaystyle",
|
|
733
|
+
"notation",
|
|
734
|
+
"encoding"
|
|
735
|
+
]);
|
|
736
|
+
function rebuild(node, doc) {
|
|
737
|
+
if (node.nodeType === Node.TEXT_NODE) return doc.createTextNode(node.textContent ?? "");
|
|
738
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return null;
|
|
739
|
+
const el = node;
|
|
740
|
+
const tag = el.localName;
|
|
741
|
+
if (el.namespaceURI !== MATHML_NS || !ELEMENTS.has(tag)) return null;
|
|
742
|
+
if (tag === "annotation") return null;
|
|
743
|
+
const out = doc.createElementNS(MATHML_NS, tag);
|
|
744
|
+
for (const a of Array.from(el.attributes)) {
|
|
745
|
+
if (!ATTRIBUTES.has(a.name)) continue;
|
|
746
|
+
out.setAttribute(a.name, a.value);
|
|
747
|
+
}
|
|
748
|
+
for (const child of Array.from(el.childNodes)) {
|
|
749
|
+
const c = rebuild(child, doc);
|
|
750
|
+
if (c) out.appendChild(c);
|
|
751
|
+
}
|
|
752
|
+
return out;
|
|
753
|
+
}
|
|
754
|
+
var cache = /* @__PURE__ */ new Map();
|
|
755
|
+
function safeMathML(markup, doc = document) {
|
|
756
|
+
if (!cache.has(markup)) {
|
|
757
|
+
const parsed = new DOMParser().parseFromString(`<!doctype html><body>${markup}`, "text/html");
|
|
758
|
+
const root = parsed.body.firstElementChild;
|
|
759
|
+
const math = root && root.localName === "math" ? rebuild(root, doc) : null;
|
|
760
|
+
cache.set(markup, math);
|
|
761
|
+
}
|
|
762
|
+
const hit = cache.get(markup);
|
|
763
|
+
return hit ? hit.cloneNode(true) : null;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// src/video/CaptionLayer.tsx
|
|
767
|
+
import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
768
|
+
var INK = "var(--color-palette-ink-900)";
|
|
769
|
+
var INK_MUTE = "var(--color-palette-ink-500)";
|
|
770
|
+
var PAPER = "var(--color-palette-stone-50)";
|
|
771
|
+
var MathSpan = ({ markup, tex }) => {
|
|
772
|
+
const ref = useRef(null);
|
|
773
|
+
useLayoutEffect(() => {
|
|
774
|
+
const host = ref.current;
|
|
775
|
+
if (!host) return;
|
|
776
|
+
const math = safeMathML(markup, host.ownerDocument);
|
|
777
|
+
if (!math) {
|
|
778
|
+
host.textContent = tex;
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
math.style.fontFamily = "var(--font-family-math)";
|
|
782
|
+
math.style.fontStyle = "normal";
|
|
783
|
+
host.replaceChildren(math);
|
|
784
|
+
}, [markup, tex]);
|
|
785
|
+
return /* @__PURE__ */ jsx11("span", { ref, style: { whiteSpace: "nowrap" } });
|
|
786
|
+
};
|
|
787
|
+
var CaptionLayer = ({ track, time, width, reducedMotion = true, clearance = 0, page: given }) => {
|
|
788
|
+
const page = given === void 0 ? pageAt(track.pages, time) : given;
|
|
789
|
+
const scale = width / track.frame.width;
|
|
790
|
+
const size = track.frame.fontSize * scale;
|
|
791
|
+
const { safe } = track.frame;
|
|
792
|
+
const c = track.style.colors;
|
|
793
|
+
const ink = (c && paletteVar(c.ink)) ?? INK, inkMute = (c && paletteVar(c.inkMute)) ?? INK_MUTE, paper = (c && paletteVar(c.paper)) ?? PAPER;
|
|
794
|
+
const look = captionLook(track.style.treatment, {
|
|
795
|
+
size,
|
|
796
|
+
scale,
|
|
797
|
+
weight: track.style.weight,
|
|
798
|
+
lineHeight: track.style.lineHeight,
|
|
799
|
+
family: "var(--font-family-sans)",
|
|
800
|
+
ink,
|
|
801
|
+
paper
|
|
802
|
+
});
|
|
803
|
+
const toned = look.toned && !reducedMotion && given === void 0;
|
|
804
|
+
const lines = page ? page.lines.map((_, li) => page.words.filter((w) => w.line === li)) : [];
|
|
805
|
+
const plain = page ? page.lines.map((l) => l.replace(/\$([^$\s]+)\$/g, "$1")).join(" ") : "";
|
|
806
|
+
return /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
807
|
+
/* @__PURE__ */ jsx11("span", { "data-caption-text": "", lang: track.lang || void 0, style: { position: "absolute", width: 1, height: 1, margin: -1, padding: 0, overflow: "hidden", clip: "rect(0 0 0 0)", clipPath: "inset(50%)", whiteSpace: "nowrap", border: 0 }, children: plain }),
|
|
808
|
+
/* @__PURE__ */ jsx11("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0, pointerEvents: "none", overflow: "hidden" }, children: page ? /* @__PURE__ */ jsx11("div", { style: {
|
|
809
|
+
position: "absolute",
|
|
810
|
+
left: safe.left * scale,
|
|
811
|
+
right: safe.right * scale,
|
|
812
|
+
bottom: Math.max(safe.bottom * scale + look.lift, clearance),
|
|
813
|
+
display: "flex",
|
|
814
|
+
justifyContent: "center"
|
|
815
|
+
}, children: /* @__PURE__ */ jsx11("div", { style: { ...look.panel, ...look.text }, children: lines.map((ws, li) => /* @__PURE__ */ jsx11("div", { children: ws.map((w, wi) => {
|
|
816
|
+
const m = splitMath(w.text);
|
|
817
|
+
return /* @__PURE__ */ jsxs6("span", { style: look.toned ? { color: toned ? toneColor(wordTone(w.at, time, track.style.toneSeconds), ink, inkMute) : ink } : void 0, children: [
|
|
818
|
+
m && w.math ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
819
|
+
m.before,
|
|
820
|
+
/* @__PURE__ */ jsx11(MathSpan, { markup: w.math, tex: m.tex }),
|
|
821
|
+
m.after
|
|
822
|
+
] }) : w.text,
|
|
823
|
+
wi < ws.length - 1 ? " " : ""
|
|
824
|
+
] }, wi);
|
|
825
|
+
}) }, li)) }) }) : null })
|
|
826
|
+
] });
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
// src/video/CaptionedVideo.tsx
|
|
830
|
+
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
831
|
+
var CAPTIONS_STORAGE_KEY = "dataimago:video-captions";
|
|
832
|
+
var readCaptionsChoice = () => {
|
|
833
|
+
try {
|
|
834
|
+
return window.localStorage.getItem(CAPTIONS_STORAGE_KEY) !== "off";
|
|
835
|
+
} catch {
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
var writeCaptionsChoice = (on) => {
|
|
840
|
+
try {
|
|
841
|
+
window.localStorage.setItem(CAPTIONS_STORAGE_KEY, on ? "on" : "off");
|
|
842
|
+
} catch {
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
var num = (x) => typeof x === "number" && Number.isFinite(x);
|
|
846
|
+
var strings = (x) => Array.isArray(x) && x.every((y) => typeof y === "string");
|
|
847
|
+
function isCaptionTrack(x) {
|
|
848
|
+
const t = x;
|
|
849
|
+
if (!t || typeof t !== "object" || t.schema !== CAPTIONS_SCHEMA) return false;
|
|
850
|
+
const f = t.frame, st = t.style;
|
|
851
|
+
if (!f || !num(f.width) || f.width <= 0 || !num(f.height) || f.height <= 0 || !num(f.fontSize) || f.fontSize <= 0 || !f.safe || !["top", "right", "bottom", "left"].every((k) => num(f.safe[k]))) return false;
|
|
852
|
+
if (!st || !["editorial", "frosted", "boxed"].includes(st.treatment) || !num(st.weight) || !num(st.toneSeconds) || !num(st.lineHeight)) return false;
|
|
853
|
+
if (st.colors !== void 0 && !(st.colors && [st.colors.paper, st.colors.ink, st.colors.inkMute].every((x2) => typeof x2 === "string" && paletteVar(x2)))) return false;
|
|
854
|
+
return Array.isArray(t.pages) && t.pages.length > 0 && t.pages.every((p, i) => p && num(p.start) && num(p.end) && p.start < p.end && (i === 0 || p.start >= t.pages[i - 1].end) && strings(p.lines) && p.lines.length > 0 && Array.isArray(p.words) && p.words.length > 0 && p.words.every((w) => w && typeof w.text === "string" && num(w.at) && Number.isInteger(w.line) && w.line >= 0 && w.line < p.lines.length && (w.math === void 0 || typeof w.math === "string")));
|
|
855
|
+
}
|
|
856
|
+
function cueLines(cue) {
|
|
857
|
+
let text = "";
|
|
858
|
+
const walk = (n) => {
|
|
859
|
+
if (n.nodeType === Node.TEXT_NODE) text += n.textContent ?? "";
|
|
860
|
+
else if (n.localName === "br") text += "\n";
|
|
861
|
+
else n.childNodes.forEach(walk);
|
|
862
|
+
};
|
|
863
|
+
walk(cue.getCueAsHTML());
|
|
864
|
+
return text.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
865
|
+
}
|
|
866
|
+
var CONTROLS_HOLD_MS = 2500;
|
|
867
|
+
var CONTROLS_HEIGHT = 56;
|
|
868
|
+
var usePrefersReducedMotion3 = () => {
|
|
869
|
+
const [reduced, setReduced] = useState3(true);
|
|
870
|
+
useEffect3(() => {
|
|
871
|
+
const q = window.matchMedia?.("(prefers-reduced-motion: reduce)");
|
|
872
|
+
if (!q) {
|
|
873
|
+
setReduced(false);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
setReduced(q.matches);
|
|
877
|
+
const on = () => setReduced(q.matches);
|
|
878
|
+
q.addEventListener("change", on);
|
|
879
|
+
return () => q.removeEventListener("change", on);
|
|
880
|
+
}, []);
|
|
881
|
+
return reduced;
|
|
882
|
+
};
|
|
883
|
+
var buttonStyle = {
|
|
884
|
+
font: "inherit",
|
|
885
|
+
fontFamily: "var(--font-family-sans)",
|
|
886
|
+
fontWeight: 600,
|
|
887
|
+
fontSize: 13,
|
|
888
|
+
lineHeight: 1,
|
|
889
|
+
color: "var(--color-palette-paper-50)",
|
|
890
|
+
background: "color-mix(in srgb, var(--color-palette-ink-900) 72%, transparent)",
|
|
891
|
+
border: "1px solid color-mix(in srgb, var(--color-palette-paper-50) 35%, transparent)",
|
|
892
|
+
borderRadius: 4,
|
|
893
|
+
padding: "6px 9px",
|
|
894
|
+
cursor: "pointer",
|
|
895
|
+
pointerEvents: "auto",
|
|
896
|
+
// the house floor for touch targets ([[accessibility-beyond-compliance]]), as ThemeToggle
|
|
897
|
+
minWidth: 48,
|
|
898
|
+
minHeight: 48
|
|
899
|
+
};
|
|
900
|
+
var NATIVE_FULLSCREEN_RULE = [
|
|
901
|
+
"video[data-dataimago-captioned-video]::-webkit-media-controls-fullscreen-button { display: none; }",
|
|
902
|
+
"video[data-dataimago-captioned-video]::-webkit-media-controls-toggle-closed-captions-button { display: none; }"
|
|
903
|
+
].join("\n");
|
|
904
|
+
var nativeRuleAdded = false;
|
|
905
|
+
function hideNativeFullscreenButton() {
|
|
906
|
+
if (nativeRuleAdded || typeof document === "undefined" || !("adoptedStyleSheets" in document)) return;
|
|
907
|
+
try {
|
|
908
|
+
const sheet = new CSSStyleSheet();
|
|
909
|
+
sheet.replaceSync(NATIVE_FULLSCREEN_RULE);
|
|
910
|
+
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
|
|
911
|
+
nativeRuleAdded = true;
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
var CaptionedVideo = ({
|
|
916
|
+
src,
|
|
917
|
+
poster,
|
|
918
|
+
captions,
|
|
919
|
+
fallbackCaptions,
|
|
920
|
+
chapters,
|
|
921
|
+
title,
|
|
922
|
+
lang = "en",
|
|
923
|
+
className,
|
|
924
|
+
style
|
|
925
|
+
}) => {
|
|
926
|
+
const wrapRef = useRef2(null);
|
|
927
|
+
const stageRef = useRef2(null);
|
|
928
|
+
const videoRef = useRef2(null);
|
|
929
|
+
const trackRef = useRef2(null);
|
|
930
|
+
const chaptersRef = useRef2(null);
|
|
931
|
+
const [track, setTrack] = useState3(null);
|
|
932
|
+
const [failed, setFailed] = useState3(false);
|
|
933
|
+
const [cue, setCue] = useState3(null);
|
|
934
|
+
const [time, setTime] = useState3(0);
|
|
935
|
+
const [width, setWidth] = useState3(0);
|
|
936
|
+
const [on, setOn] = useState3(true);
|
|
937
|
+
const [full, setFull] = useState3(false);
|
|
938
|
+
const [paused, setPaused] = useState3(true);
|
|
939
|
+
const [active, setActive] = useState3(false);
|
|
940
|
+
const [focusWithin, setFocusWithin] = useState3(false);
|
|
941
|
+
const reducedMotion = usePrefersReducedMotion3();
|
|
942
|
+
useEffect3(() => setOn(readCaptionsChoice()), []);
|
|
943
|
+
useEffect3(() => {
|
|
944
|
+
let live = true;
|
|
945
|
+
setTrack(null);
|
|
946
|
+
setFailed(false);
|
|
947
|
+
fetch(captions, { credentials: "same-origin" }).then((r) => r.ok ? r.json() : Promise.reject(new Error(String(r.status)))).then((j) => {
|
|
948
|
+
if (!live) return;
|
|
949
|
+
if (isCaptionTrack(j)) setTrack(j);
|
|
950
|
+
else setFailed(true);
|
|
951
|
+
}).catch(() => {
|
|
952
|
+
if (live) setFailed(true);
|
|
953
|
+
});
|
|
954
|
+
return () => {
|
|
955
|
+
live = false;
|
|
956
|
+
};
|
|
957
|
+
}, [captions]);
|
|
958
|
+
useEffect3(() => {
|
|
959
|
+
const el = trackRef.current, t = el?.track;
|
|
960
|
+
if (!el || !t) return;
|
|
961
|
+
const change = () => {
|
|
962
|
+
const c = t.activeCues?.[0];
|
|
963
|
+
const lines = c ? cueLines(c) : [];
|
|
964
|
+
setCue(c ? { start: c.startTime, end: c.endTime, lines, words: lines.flatMap((l, line) => l.split(" ").map((w) => ({ text: w, at: c.startTime, line }))) } : null);
|
|
965
|
+
};
|
|
966
|
+
t.addEventListener("cuechange", change);
|
|
967
|
+
el.addEventListener("load", change);
|
|
968
|
+
t.mode = nativeViewRef.current && onRef.current ? "showing" : "hidden";
|
|
969
|
+
change();
|
|
970
|
+
return () => {
|
|
971
|
+
t.removeEventListener("cuechange", change);
|
|
972
|
+
el.removeEventListener("load", change);
|
|
973
|
+
};
|
|
974
|
+
}, [fallbackCaptions]);
|
|
975
|
+
useEffect3(() => {
|
|
976
|
+
const v = videoRef.current;
|
|
977
|
+
if (!v) return;
|
|
978
|
+
let raf = 0;
|
|
979
|
+
const tick = () => {
|
|
980
|
+
setTime(v.currentTime);
|
|
981
|
+
raf = requestAnimationFrame(tick);
|
|
982
|
+
};
|
|
983
|
+
const sync = () => {
|
|
984
|
+
setPaused(v.paused);
|
|
985
|
+
setTime(v.currentTime);
|
|
986
|
+
cancelAnimationFrame(raf);
|
|
987
|
+
if (!v.paused && !v.ended) raf = requestAnimationFrame(tick);
|
|
988
|
+
};
|
|
989
|
+
const events = ["play", "playing", "pause", "seeking", "seeked", "timeupdate", "ended", "emptied", "loadstart", "loadedmetadata"];
|
|
990
|
+
for (const e of events) v.addEventListener(e, sync);
|
|
991
|
+
sync();
|
|
992
|
+
return () => {
|
|
993
|
+
cancelAnimationFrame(raf);
|
|
994
|
+
for (const e of events) v.removeEventListener(e, sync);
|
|
995
|
+
};
|
|
996
|
+
}, [src]);
|
|
997
|
+
useEffect3(() => {
|
|
998
|
+
const el = stageRef.current;
|
|
999
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
1000
|
+
const ro = new ResizeObserver(([e]) => setWidth(e.contentRect.width));
|
|
1001
|
+
ro.observe(el);
|
|
1002
|
+
return () => ro.disconnect();
|
|
1003
|
+
}, []);
|
|
1004
|
+
const [nativeView, setNativeView] = useState3(false);
|
|
1005
|
+
const nativeViewRef = useRef2(false), onRef = useRef2(true);
|
|
1006
|
+
nativeViewRef.current = nativeView;
|
|
1007
|
+
onRef.current = on;
|
|
1008
|
+
useEffect3(() => {
|
|
1009
|
+
const v = videoRef.current;
|
|
1010
|
+
if (!v) return;
|
|
1011
|
+
let fs = false, pip = false;
|
|
1012
|
+
const update = () => setNativeView(fs || pip);
|
|
1013
|
+
const change = () => {
|
|
1014
|
+
setFull(document.fullscreenElement === wrapRef.current);
|
|
1015
|
+
fs = document.fullscreenElement === v;
|
|
1016
|
+
update();
|
|
1017
|
+
};
|
|
1018
|
+
const flag = (set, x) => () => {
|
|
1019
|
+
set(x);
|
|
1020
|
+
update();
|
|
1021
|
+
};
|
|
1022
|
+
const handlers = [
|
|
1023
|
+
[document, "fullscreenchange", change],
|
|
1024
|
+
[v, "webkitbeginfullscreen", flag((x) => {
|
|
1025
|
+
fs = x;
|
|
1026
|
+
}, true)],
|
|
1027
|
+
[v, "webkitendfullscreen", flag((x) => {
|
|
1028
|
+
fs = x;
|
|
1029
|
+
}, false)],
|
|
1030
|
+
[v, "enterpictureinpicture", flag((x) => {
|
|
1031
|
+
pip = x;
|
|
1032
|
+
}, true)],
|
|
1033
|
+
[v, "leavepictureinpicture", flag((x) => {
|
|
1034
|
+
pip = x;
|
|
1035
|
+
}, false)]
|
|
1036
|
+
];
|
|
1037
|
+
for (const [t, e, h] of handlers) t.addEventListener(e, h);
|
|
1038
|
+
return () => {
|
|
1039
|
+
for (const [t, e, h] of handlers) t.removeEventListener(e, h);
|
|
1040
|
+
};
|
|
1041
|
+
}, []);
|
|
1042
|
+
useEffect3(() => {
|
|
1043
|
+
const v = videoRef.current;
|
|
1044
|
+
if (!v) return;
|
|
1045
|
+
const want = nativeView && on ? "showing" : "hidden";
|
|
1046
|
+
const hold = () => {
|
|
1047
|
+
const t = trackRef.current?.track;
|
|
1048
|
+
if (t && t.mode !== want) t.mode = want;
|
|
1049
|
+
};
|
|
1050
|
+
hold();
|
|
1051
|
+
v.textTracks.addEventListener("change", hold);
|
|
1052
|
+
trackRef.current?.addEventListener("load", hold);
|
|
1053
|
+
const el = trackRef.current;
|
|
1054
|
+
return () => {
|
|
1055
|
+
v.textTracks.removeEventListener("change", hold);
|
|
1056
|
+
el?.removeEventListener("load", hold);
|
|
1057
|
+
};
|
|
1058
|
+
}, [nativeView, on, fallbackCaptions]);
|
|
1059
|
+
useEffect3(hideNativeFullscreenButton, []);
|
|
1060
|
+
useEffect3(() => {
|
|
1061
|
+
const t = chaptersRef.current?.track;
|
|
1062
|
+
if (t) t.mode = "hidden";
|
|
1063
|
+
}, [chapters]);
|
|
1064
|
+
const holdRef = useRef2(0);
|
|
1065
|
+
const wake = useCallback(() => {
|
|
1066
|
+
setActive(true);
|
|
1067
|
+
window.clearTimeout(holdRef.current);
|
|
1068
|
+
holdRef.current = window.setTimeout(() => setActive(false), CONTROLS_HOLD_MS);
|
|
1069
|
+
}, []);
|
|
1070
|
+
useEffect3(() => () => window.clearTimeout(holdRef.current), []);
|
|
1071
|
+
const toggleCaptions = useCallback(() => setOn((x) => {
|
|
1072
|
+
writeCaptionsChoice(!x);
|
|
1073
|
+
return !x;
|
|
1074
|
+
}), []);
|
|
1075
|
+
const toggleFullscreen = useCallback(() => {
|
|
1076
|
+
const wrap = wrapRef.current, v = videoRef.current;
|
|
1077
|
+
if (document.fullscreenElement === wrap) {
|
|
1078
|
+
void document.exitFullscreen();
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
if (wrap?.requestFullscreen) {
|
|
1082
|
+
wrap.requestFullscreen().catch(() => v?.webkitEnterFullscreen?.());
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
v?.webkitEnterFullscreen?.();
|
|
1086
|
+
}, []);
|
|
1087
|
+
const onKey = (e) => {
|
|
1088
|
+
const t = e.target;
|
|
1089
|
+
if (e.altKey || e.ctrlKey || e.metaKey || t.isContentEditable || t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement) return;
|
|
1090
|
+
if (e.key === "c" || e.key === "C") {
|
|
1091
|
+
e.preventDefault();
|
|
1092
|
+
toggleCaptions();
|
|
1093
|
+
}
|
|
1094
|
+
if (e.key === "f" || e.key === "F") {
|
|
1095
|
+
e.preventDefault();
|
|
1096
|
+
toggleFullscreen();
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
const controlsUp = paused || active || focusWithin;
|
|
1100
|
+
const showLayer = on && width > 0 && !nativeView && (track !== null || cue !== null || failed);
|
|
1101
|
+
const layerTrack = track ?? { ...FALLBACK_FRAME, lang };
|
|
1102
|
+
const frame = layerTrack.frame;
|
|
1103
|
+
return /* @__PURE__ */ jsx12(
|
|
1104
|
+
"div",
|
|
1105
|
+
{
|
|
1106
|
+
ref: wrapRef,
|
|
1107
|
+
className,
|
|
1108
|
+
onPointerMove: wake,
|
|
1109
|
+
onFocus: () => {
|
|
1110
|
+
setFocusWithin(true);
|
|
1111
|
+
wake();
|
|
1112
|
+
},
|
|
1113
|
+
onBlur: (e) => {
|
|
1114
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setFocusWithin(false);
|
|
1115
|
+
},
|
|
1116
|
+
onKeyDown: onKey,
|
|
1117
|
+
style: {
|
|
1118
|
+
position: "relative",
|
|
1119
|
+
display: full ? "flex" : "block",
|
|
1120
|
+
alignItems: "center",
|
|
1121
|
+
justifyContent: "center",
|
|
1122
|
+
background: full ? "var(--color-palette-ink-900)" : void 0,
|
|
1123
|
+
...style
|
|
1124
|
+
},
|
|
1125
|
+
children: /* @__PURE__ */ jsxs7("div", { ref: stageRef, style: { position: "relative", width: full ? `min(100vw, calc(100vh * ${frame.width / frame.height}))` : "100%", aspectRatio: `${frame.width} / ${frame.height}` }, children: [
|
|
1126
|
+
/* @__PURE__ */ jsxs7(
|
|
1127
|
+
"video",
|
|
1128
|
+
{
|
|
1129
|
+
ref: videoRef,
|
|
1130
|
+
src,
|
|
1131
|
+
poster,
|
|
1132
|
+
controls: true,
|
|
1133
|
+
controlsList: "nofullscreen",
|
|
1134
|
+
"data-dataimago-captioned-video": "",
|
|
1135
|
+
playsInline: true,
|
|
1136
|
+
preload: "metadata",
|
|
1137
|
+
"aria-label": title,
|
|
1138
|
+
style: { display: "block", width: "100%", height: "100%" },
|
|
1139
|
+
children: [
|
|
1140
|
+
/* @__PURE__ */ jsx12("track", { ref: trackRef, kind: "captions", src: fallbackCaptions, srcLang: lang, label: "Captions", default: true }),
|
|
1141
|
+
chapters ? /* @__PURE__ */ jsx12("track", { ref: chaptersRef, kind: "chapters", src: chapters, srcLang: lang, label: "Chapters" }) : null
|
|
1142
|
+
]
|
|
1143
|
+
}
|
|
1144
|
+
),
|
|
1145
|
+
showLayer ? /* @__PURE__ */ jsx12(
|
|
1146
|
+
CaptionLayer,
|
|
1147
|
+
{
|
|
1148
|
+
track: layerTrack,
|
|
1149
|
+
time,
|
|
1150
|
+
width,
|
|
1151
|
+
reducedMotion,
|
|
1152
|
+
clearance: controlsUp ? CONTROLS_HEIGHT : 0,
|
|
1153
|
+
page: track ? void 0 : cue
|
|
1154
|
+
}
|
|
1155
|
+
) : null,
|
|
1156
|
+
/* @__PURE__ */ jsxs7("div", { style: {
|
|
1157
|
+
position: "absolute",
|
|
1158
|
+
top: 8,
|
|
1159
|
+
right: 8,
|
|
1160
|
+
display: "flex",
|
|
1161
|
+
gap: 6,
|
|
1162
|
+
pointerEvents: "none",
|
|
1163
|
+
// hidden once faded: an invisible button must not take a click or a Tab stop
|
|
1164
|
+
opacity: controlsUp ? 1 : 0,
|
|
1165
|
+
visibility: controlsUp ? "visible" : "hidden",
|
|
1166
|
+
transition: reducedMotion ? void 0 : controlsUp ? "opacity 200ms" : "opacity 200ms, visibility 0s linear 200ms"
|
|
1167
|
+
}, children: [
|
|
1168
|
+
/* @__PURE__ */ jsx12("button", { type: "button", style: buttonStyle, "aria-pressed": on, "aria-label": "CC (captions)", title: "Captions (c)", onClick: toggleCaptions, onFocus: wake, children: "CC" }),
|
|
1169
|
+
/* @__PURE__ */ jsx12("button", { type: "button", style: buttonStyle, "aria-pressed": full, "aria-label": full ? "Exit full screen" : "Full screen", title: "Full screen (f)", onClick: toggleFullscreen, onFocus: wake, children: full ? "Exit" : "Full" })
|
|
1170
|
+
] })
|
|
1171
|
+
] })
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
};
|
|
1175
|
+
var FALLBACK_FRAME = {
|
|
1176
|
+
schema: CAPTIONS_SCHEMA,
|
|
1177
|
+
work: "",
|
|
1178
|
+
version: "",
|
|
1179
|
+
lang: "en",
|
|
1180
|
+
pages: [],
|
|
1181
|
+
style: { profile: "dataimago-v1", treatment: "frosted", weight: 500, toneSeconds: 0, lineHeight: 1.28 },
|
|
1182
|
+
frame: { width: 1920, height: 1080, fontSize: 44, safe: { top: 72, right: 96, bottom: 72, left: 96 } }
|
|
1183
|
+
};
|
|
617
1184
|
export {
|
|
618
1185
|
BrandAnimation,
|
|
619
1186
|
Button,
|
|
1187
|
+
CAPTIONS_SCHEMA,
|
|
1188
|
+
CAPTIONS_STORAGE_KEY,
|
|
1189
|
+
CaptionLayer,
|
|
1190
|
+
CaptionedVideo,
|
|
620
1191
|
CopulaLockup,
|
|
621
1192
|
Logo,
|
|
622
1193
|
LogoMonogram,
|
|
@@ -627,12 +1198,19 @@ export {
|
|
|
627
1198
|
THEME_STORAGE_KEY,
|
|
628
1199
|
ThemeScript,
|
|
629
1200
|
ThemeToggle,
|
|
1201
|
+
captionLook,
|
|
1202
|
+
isCaptionTrack,
|
|
630
1203
|
nextThemePreference,
|
|
1204
|
+
pageAt,
|
|
1205
|
+
paletteVar,
|
|
631
1206
|
readThemePreference,
|
|
632
1207
|
resolveTheme,
|
|
633
1208
|
setThemePreference,
|
|
1209
|
+
splitMath,
|
|
634
1210
|
themeInitScript,
|
|
1211
|
+
toneColor,
|
|
635
1212
|
useResolvedTheme,
|
|
636
|
-
useThemePreference
|
|
1213
|
+
useThemePreference,
|
|
1214
|
+
wordTone
|
|
637
1215
|
};
|
|
638
1216
|
//# sourceMappingURL=index.js.map
|