@ttt-productions/media-viewer 0.2.9 → 0.2.11

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.
@@ -1,3 +1,3 @@
1
- import type { MediaViewerProps } from "./types";
2
- export declare function AudioViewer(props: MediaViewerProps): import("react/jsx-runtime").JSX.Element;
1
+ import type { AudioViewerProps } from "./types.js";
2
+ export declare function AudioViewer(props: AudioViewerProps): import("react/jsx-runtime").JSX.Element;
3
3
  //# sourceMappingURL=audio-viewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"audio-viewer.d.ts","sourceRoot":"","sources":["../src/audio-viewer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAmDlD"}
1
+ {"version":3,"file":"audio-viewer.d.ts","sourceRoot":"","sources":["../src/audio-viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CA6IlD"}
@@ -1,9 +1,57 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { useInView } from "react-intersection-observer";
4
+ import { Skeleton } from "@ttt-productions/ui-core";
3
5
  export function AudioViewer(props) {
4
- const { url, className, mediaClassName, controls = true, autoPlay, loop, } = props;
6
+ const { url, className, mediaClassName, controls = true, autoPlay, loop = false, lazy = true, skeleton: showSkeleton = true, priority = false, preload = "metadata", onLoad, onLoadChange, onError, fallback, } = props;
7
+ const [isLoaded, setIsLoaded] = React.useState(false);
8
+ const [hasError, setHasError] = React.useState(false);
9
+ const [shouldLoad, setShouldLoad] = React.useState(priority || !lazy);
5
10
  const audioRef = React.useRef(null);
6
- // Only add "tap-to-toggle" when controls are OFF (prevents jank)
11
+ const { ref: inViewRef, inView } = useInView({
12
+ triggerOnce: false,
13
+ threshold: 0.01,
14
+ skip: priority || shouldLoad,
15
+ rootMargin: "100px",
16
+ });
17
+ // Load when in view
18
+ React.useEffect(() => {
19
+ if (inView && !shouldLoad) {
20
+ setShouldLoad(true);
21
+ }
22
+ }, [inView, shouldLoad]);
23
+ // Accordion compatibility: delayed auto-load on mount
24
+ React.useEffect(() => {
25
+ if (priority || shouldLoad)
26
+ return;
27
+ const timer = setTimeout(() => {
28
+ if (!shouldLoad)
29
+ setShouldLoad(true);
30
+ }, 100);
31
+ return () => clearTimeout(timer);
32
+ }, [url, priority, shouldLoad]);
33
+ // Report loading state
34
+ React.useEffect(() => {
35
+ onLoadChange?.(!isLoaded);
36
+ }, [isLoaded, onLoadChange]);
37
+ // Reset on URL change
38
+ React.useEffect(() => {
39
+ setHasError(false);
40
+ setIsLoaded(false);
41
+ }, [url]);
42
+ const handleLoadedData = React.useCallback(() => {
43
+ setIsLoaded(true);
44
+ onLoad?.();
45
+ }, [onLoad]);
46
+ const handleCanPlay = React.useCallback(() => {
47
+ setIsLoaded(true);
48
+ }, []);
49
+ const handleError = React.useCallback(() => {
50
+ setHasError(true);
51
+ setIsLoaded(true); // Stop skeleton on error
52
+ onError?.();
53
+ }, [onError]);
54
+ // Tap-to-toggle when controls are off
7
55
  const togglePlay = React.useCallback(() => {
8
56
  const el = audioRef.current;
9
57
  if (!el)
@@ -21,6 +69,15 @@ export function AudioViewer(props) {
21
69
  togglePlay();
22
70
  }
23
71
  }, [controls, togglePlay]);
24
- return (_jsx("div", { className: className, role: controls ? undefined : "button", tabIndex: controls ? undefined : 0, "aria-label": controls ? undefined : "Toggle audio playback", onClick: controls ? undefined : togglePlay, onKeyDown: controls ? undefined : onKeyDown, style: { outline: "none" }, children: _jsx("audio", { ref: audioRef, src: url, className: mediaClassName, controls: controls, autoPlay: autoPlay, loop: loop }) }));
72
+ if (hasError) {
73
+ if (fallback)
74
+ return _jsx(_Fragment, { children: fallback });
75
+ return (_jsx("div", { className: className, children: _jsx("div", { className: "mv-audio-error", children: _jsx("p", { children: "Failed to load audio" }) }) }));
76
+ }
77
+ return (_jsx("div", { ref: inViewRef, className: className, role: controls ? undefined : "button", tabIndex: controls ? undefined : 0, "aria-label": controls ? undefined : "Toggle audio playback", onClick: controls ? undefined : togglePlay, onKeyDown: controls ? undefined : onKeyDown, style: { position: "relative", width: "100%", outline: "none" }, children: !shouldLoad ? (showSkeleton ? _jsx(Skeleton, { style: { height: "3.5rem", width: "100%" } }) : null) : (_jsxs(_Fragment, { children: [showSkeleton && !isLoaded && (_jsx(Skeleton, { style: { position: "absolute", inset: 0, height: "3.5rem", width: "100%" } })), _jsx("audio", { ref: audioRef, src: url, className: mediaClassName, controls: controls, autoPlay: autoPlay, loop: loop, preload: preload, onLoadedData: handleLoadedData, onCanPlay: handleCanPlay, onError: handleError, style: {
78
+ width: "100%",
79
+ opacity: isLoaded ? 1 : 0,
80
+ transition: "opacity 300ms ease",
81
+ } })] })) }));
25
82
  }
26
83
  //# sourceMappingURL=audio-viewer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audio-viewer.js","sourceRoot":"","sources":["../src/audio-viewer.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EACJ,GAAG,EACH,SAAS,EACT,cAAc,EACd,QAAQ,GAAG,IAAI,EACf,QAAQ,EACR,IAAI,GACL,GAAG,KAAK,CAAC;IAEV,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAA0B,IAAI,CAAC,CAAC;IAE7D,iEAAiE;IACjE,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACxC,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,EAAE,CAAC,MAAM;YAAE,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;;YACzC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CACjC,CAAC,CAAsB,EAAE,EAAE;QACzB,IAAI,QAAQ;YAAE,OAAO;QACrB,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvC,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,UAAU,EAAE,CAAC;QACf,CAAC;IACH,CAAC,EACD,CAAC,QAAQ,EAAE,UAAU,CAAC,CACvB,CAAC;IAEF,OAAO,CACL,cACE,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EACrC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,gBACtB,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,uBAAuB,EAC1D,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAC1C,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAC3C,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,YAE1B,gBACE,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,GACV,GACE,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"audio-viewer.js","sourceRoot":"","sources":["../src/audio-viewer.tsx"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { FallbackMode } from "./types";
1
+ import type { FallbackMode } from "./types.js";
2
2
  export declare function MediaFallbackLink(props: {
3
3
  url: string;
4
4
  filename?: string;
@@ -6,4 +6,14 @@ export declare function MediaFallbackLink(props: {
6
6
  label?: string;
7
7
  }): import("react/jsx-runtime").JSX.Element;
8
8
  export declare function shouldShowFallback(mode: FallbackMode | undefined): boolean;
9
+ /** Empty state: no URL provided */
10
+ export declare function EmptyFallback(props: {
11
+ isCircular?: boolean;
12
+ className?: string;
13
+ }): import("react/jsx-runtime").JSX.Element;
14
+ /** Error state: failed to load */
15
+ export declare function ErrorFallback(props: {
16
+ isCircular?: boolean;
17
+ className?: string;
18
+ }): import("react/jsx-runtime").JSX.Element;
9
19
  //# sourceMappingURL=fallback.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,2CAcA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAE1E"}
1
+ {"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../src/fallback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,2CAcA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,GAAG,OAAO,CAE1E;AAED,mCAAmC;AACnC,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAehF;AAED,kCAAkC;AAClC,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAchF"}
package/dist/fallback.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  export function MediaFallbackLink(props) {
3
3
  const { url, filename, className, label } = props;
4
4
  return (_jsx("a", { href: url, target: "_blank", rel: "noreferrer", download: filename || undefined, className: className, children: label ?? "Download" }));
@@ -6,4 +6,17 @@ export function MediaFallbackLink(props) {
6
6
  export function shouldShowFallback(mode) {
7
7
  return mode !== "none";
8
8
  }
9
+ /** Empty state: no URL provided */
10
+ export function EmptyFallback(props) {
11
+ const { isCircular, className } = props;
12
+ if (isCircular) {
13
+ return (_jsx("div", { className: className ?? "mv-avatar-fallback", children: "?" }));
14
+ }
15
+ return (_jsx("div", { className: className ?? "mv-empty-fallback", children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }), _jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" })] }) }));
16
+ }
17
+ /** Error state: failed to load */
18
+ export function ErrorFallback(props) {
19
+ const { isCircular, className } = props;
20
+ return (_jsx("div", { className: className ?? "mv-error-fallback", style: isCircular ? { borderRadius: "50%" } : undefined, children: _jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }), _jsx("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }), _jsx("line", { x1: "9", x2: "15", y1: "15", y2: "9" })] }) }));
21
+ }
9
22
  //# sourceMappingURL=fallback.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../src/fallback.tsx"],"names":[],"mappings":";AAEA,MAAM,UAAU,iBAAiB,CAAC,KAKjC;IACC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAElD,OAAO,CACL,YACE,IAAI,EAAE,GAAG,EACT,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,YAAY,EAChB,QAAQ,EAAE,QAAQ,IAAI,SAAS,EAC/B,SAAS,EAAE,SAAS,YAEnB,KAAK,IAAI,UAAU,GAClB,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAA8B;IAC/D,OAAO,IAAI,KAAK,MAAM,CAAC;AACzB,CAAC"}
1
+ {"version":3,"file":"fallback.js","sourceRoot":"","sources":["../src/fallback.tsx"],"names":[],"mappings":";AAEA,MAAM,UAAU,iBAAiB,CAAC,KAKjC;IACC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAElD,OAAO,CACL,YACE,IAAI,EAAE,GAAG,EACT,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,YAAY,EAChB,QAAQ,EAAE,QAAQ,IAAI,SAAS,EAC/B,SAAS,EAAE,SAAS,YAEnB,KAAK,IAAI,UAAU,GAClB,CACL,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAA8B;IAC/D,OAAO,IAAI,KAAK,MAAM,CAAC;AACzB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,aAAa,CAAC,KAAmD;IAC/E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,oBAAoB,kBAAS,CAC3D,CAAC;IACJ,CAAC;IACD,OAAO,CACL,cAAK,SAAS,EAAE,SAAS,IAAI,mBAAmB,YAC9C,eAAK,KAAK,EAAC,4BAA4B,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,aAC/K,eAAM,CAAC,EAAC,4DAA4D,GAAG,EACvE,eAAM,CAAC,EAAC,yBAAyB,GAAG,IAChC,GACF,CACP,CAAC;AACJ,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,aAAa,CAAC,KAAmD;IAC/E,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxC,OAAO,CACL,cACE,SAAS,EAAE,SAAS,IAAI,mBAAmB,EAC3C,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,YAEvD,eAAK,KAAK,EAAC,4BAA4B,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,OAAO,EAAC,WAAW,EAAC,IAAI,EAAC,MAAM,EAAC,MAAM,EAAC,cAAc,EAAC,WAAW,EAAC,GAAG,EAAC,aAAa,EAAC,OAAO,EAAC,cAAc,EAAC,OAAO,aAC/K,eAAM,CAAC,EAAC,4DAA4D,GAAG,EACvE,eAAM,CAAC,EAAC,yBAAyB,GAAG,EACpC,eAAM,EAAE,EAAC,GAAG,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,IAAI,EAAC,EAAE,EAAC,GAAG,GAAG,IAClC,GACF,CACP,CAAC;AACJ,CAAC"}
@@ -1,3 +1,3 @@
1
- import type { MediaViewerProps } from "./types";
2
- export declare function ImageViewer(props: MediaViewerProps): import("react/jsx-runtime").JSX.Element;
1
+ import type { ImageViewerProps } from "./types.js";
2
+ export declare function ImageViewer(props: ImageViewerProps): import("react/jsx-runtime").JSX.Element | null;
3
3
  //# sourceMappingURL=image-viewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../src/image-viewer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CA+ClD"}
1
+ {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../src/image-viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDA8IlD"}
@@ -1,8 +1,58 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { useInView } from "react-intersection-observer";
4
+ import { Skeleton } from "@ttt-productions/ui-core";
3
5
  export function ImageViewer(props) {
4
- const { url, alt, className, mediaClassName, enableZoom = true } = props;
6
+ const { url, alt, className, mediaClassName, enableZoom = true, lazy = true, unloadOnExit = false, priority = false, skeleton: showSkeleton = true, isCircular = false, preventGestures = true, onLoad, onError, fallback, } = props;
7
+ const [isLoaded, setIsLoaded] = React.useState(false);
8
+ const [hasError, setHasError] = React.useState(false);
9
+ const [shouldLoad, setShouldLoad] = React.useState(priority || !lazy);
5
10
  const [zoomed, setZoomed] = React.useState(false);
11
+ const imgRef = React.useRef(null);
12
+ const { ref: inViewRef, inView } = useInView({
13
+ triggerOnce: !unloadOnExit,
14
+ threshold: 0.01,
15
+ rootMargin: "50px",
16
+ skip: priority || !lazy,
17
+ });
18
+ // Load when entering viewport
19
+ React.useEffect(() => {
20
+ if (inView && !shouldLoad) {
21
+ setShouldLoad(true);
22
+ }
23
+ // Unload when leaving viewport (if enabled)
24
+ if (!inView && unloadOnExit && shouldLoad && !priority) {
25
+ setShouldLoad(false);
26
+ setIsLoaded(false);
27
+ }
28
+ }, [inView, shouldLoad, unloadOnExit, priority]);
29
+ // Reset error state when URL changes
30
+ React.useEffect(() => {
31
+ setHasError(false);
32
+ setIsLoaded(false);
33
+ }, [url]);
34
+ const handleLoad = React.useCallback(() => {
35
+ setIsLoaded(true);
36
+ onLoad?.();
37
+ }, [onLoad]);
38
+ const handleError = React.useCallback(() => {
39
+ setHasError(true);
40
+ onError?.();
41
+ }, [onError]);
42
+ // iOS: prevent multi-touch zoom gestures on the image
43
+ React.useEffect(() => {
44
+ const img = imgRef.current;
45
+ if (!img || !preventGestures)
46
+ return;
47
+ const prevent = (e) => {
48
+ if (e.touches.length > 1)
49
+ e.preventDefault();
50
+ };
51
+ img.addEventListener("touchstart", prevent, { passive: false });
52
+ return () => {
53
+ img.removeEventListener("touchstart", prevent);
54
+ };
55
+ }, [preventGestures, shouldLoad]);
6
56
  const toggleZoom = React.useCallback(() => {
7
57
  if (!enableZoom)
8
58
  return;
@@ -16,12 +66,25 @@ export function ImageViewer(props) {
16
66
  toggleZoom();
17
67
  }
18
68
  }, [enableZoom, toggleZoom]);
19
- return (_jsx("div", { className: className, role: enableZoom ? "button" : undefined, tabIndex: enableZoom ? 0 : undefined, "aria-pressed": enableZoom ? zoomed : undefined, "aria-label": enableZoom ? (zoomed ? "Zoom out image" : "Zoom in image") : undefined, onClick: toggleZoom, onKeyDown: onKeyDown, style: { outline: "none" }, children: _jsx("img", { src: url, alt: alt ?? "", draggable: false, className: mediaClassName, style: {
20
- maxWidth: "100%",
21
- maxHeight: "100%",
22
- transition: "transform 0.15s ease",
23
- transform: zoomed ? "scale(1.5)" : "scale(1)",
24
- cursor: enableZoom ? (zoomed ? "zoom-out" : "zoom-in") : undefined,
25
- } }) }));
69
+ if (hasError) {
70
+ return fallback ? _jsx(_Fragment, { children: fallback }) : null;
71
+ }
72
+ const wrapperStyle = isCircular
73
+ ? { borderRadius: "50%", overflow: "hidden" }
74
+ : {};
75
+ return (_jsxs("div", { ref: inViewRef, className: className, role: enableZoom ? "button" : undefined, tabIndex: enableZoom ? 0 : undefined, "aria-pressed": enableZoom ? zoomed : undefined, "aria-label": enableZoom ? (zoomed ? "Zoom out image" : "Zoom in image") : undefined, onClick: toggleZoom, onKeyDown: onKeyDown, style: { position: "relative", width: "100%", height: "100%", outline: "none", ...wrapperStyle }, children: [showSkeleton && !isLoaded && shouldLoad && (_jsx(Skeleton, { style: { position: "absolute", inset: 0, width: "100%", height: "100%" } })), shouldLoad && (_jsx("img", { ref: imgRef, src: url, alt: alt ?? "", draggable: false, className: mediaClassName, onLoad: handleLoad, onError: handleError, loading: priority ? "eager" : "lazy", decoding: "async", style: {
76
+ maxWidth: "100%",
77
+ maxHeight: "100%",
78
+ width: "100%",
79
+ height: "100%",
80
+ objectFit: "cover",
81
+ opacity: isLoaded ? 1 : 0,
82
+ transition: "opacity 300ms ease, transform 150ms ease",
83
+ transform: zoomed ? "scale(1.5)" : "scale(1)",
84
+ cursor: enableZoom ? (zoomed ? "zoom-out" : "zoom-in") : undefined,
85
+ WebkitUserSelect: "none",
86
+ WebkitTouchCallout: "none",
87
+ WebkitTapHighlightColor: "transparent",
88
+ } }))] }));
26
89
  }
27
90
  //# sourceMappingURL=image-viewer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-viewer.js","sourceRoot":"","sources":["../src/image-viewer.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IAEzE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QACxC,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CACjC,CAAC,CAAsB,EAAE,EAAE;QACzB,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvC,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,UAAU,EAAE,CAAC;QACf,CAAC;IACH,CAAC,EACD,CAAC,UAAU,EAAE,UAAU,CAAC,CACzB,CAAC;IAEF,OAAO,CACL,cACE,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EACvC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,kBACtB,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,gBACjC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,SAAS,EAClF,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,YAE1B,cACE,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,IAAI,EAAE,EACd,SAAS,EAAE,KAAK,EAChB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE;gBACL,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,MAAM;gBACjB,UAAU,EAAE,sBAAsB;gBAClC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU;gBAC7C,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS;aACnE,GACD,GACE,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"image-viewer.js","sourceRoot":"","sources":["../src/image-viewer.tsx"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { MediaViewer, MediaPreview } from "./media-viewer";
2
- export type { MediaViewerProps, MediaViewerType, FallbackMode } from "./types";
3
- export { ImageViewer } from "./image-viewer";
4
- export { VideoViewer } from "./video-viewer";
5
- export { AudioViewer } from "./audio-viewer";
6
- export { MediaFallbackLink, shouldShowFallback } from "./fallback";
1
+ export { MediaViewer, MediaPreview } from "./media-viewer.js";
2
+ export { ImageViewer } from "./image-viewer.js";
3
+ export { VideoViewer } from "./video-viewer.js";
4
+ export { AudioViewer } from "./audio-viewer.js";
5
+ export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
6
+ export type { MediaViewerType, FallbackMode, BaseMediaProps, ImageViewerProps, VideoViewerProps, AudioViewerProps, MediaPreviewProps, MediaViewerProps, } from "./types.js";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE3D,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEpG,YAAY,EACV,eAAe,EACf,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- export { MediaViewer, MediaPreview } from "./media-viewer";
2
- export { ImageViewer } from "./image-viewer";
3
- export { VideoViewer } from "./video-viewer";
4
- export { AudioViewer } from "./audio-viewer";
5
- export { MediaFallbackLink, shouldShowFallback } from "./fallback";
1
+ export { MediaViewer, MediaPreview } from "./media-viewer.js";
2
+ export { ImageViewer } from "./image-viewer.js";
3
+ export { VideoViewer } from "./video-viewer.js";
4
+ export { AudioViewer } from "./audio-viewer.js";
5
+ export { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
6
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI3D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC"}
@@ -1,5 +1,5 @@
1
- import type { MediaViewerProps } from "./types";
2
- export declare function MediaViewer(props: MediaViewerProps): import("react/jsx-runtime").JSX.Element | null;
1
+ import type { MediaPreviewProps } from "./types.js";
2
+ export declare function MediaViewer(props: MediaPreviewProps): import("react/jsx-runtime").JSX.Element | null;
3
3
  /** Back-compat alias */
4
4
  export declare const MediaPreview: typeof MediaViewer;
5
5
  //# sourceMappingURL=media-viewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"media-viewer.d.ts","sourceRoot":"","sources":["../src/media-viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAmB,MAAM,SAAS,CAAC;AAcjE,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,kDAclD;AAED,wBAAwB;AACxB,eAAO,MAAM,YAAY,oBAAc,CAAC"}
1
+ {"version":3,"file":"media-viewer.d.ts","sourceRoot":"","sources":["../src/media-viewer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAiC,MAAM,YAAY,CAAC;AAoBnF,wBAAgB,WAAW,CAAC,KAAK,EAAE,iBAAiB,kDAwJnD;AAED,wBAAwB;AACxB,eAAO,MAAM,YAAY,oBAAc,CAAC"}
@@ -1,26 +1,81 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
2
3
  import { getSimplifiedMediaType } from "@ttt-productions/media-contracts";
3
- import { MediaFallbackLink, shouldShowFallback } from "./fallback";
4
- import { ImageViewer } from "./image-viewer";
5
- import { VideoViewer } from "./video-viewer";
6
- import { AudioViewer } from "./audio-viewer";
4
+ import { MediaFallbackLink, shouldShowFallback, EmptyFallback, ErrorFallback } from "./fallback.js";
5
+ import { ImageViewer } from "./image-viewer.js";
6
+ import { VideoViewer } from "./video-viewer.js";
7
+ import { AudioViewer } from "./audio-viewer.js";
7
8
  function inferType(props) {
8
- if (props.type)
9
- return props.type;
9
+ if (props.type) {
10
+ // Handle MIME strings like "image/png" -> "image"
11
+ const primary = typeof props.type === "string" ? props.type.split("/")[0] : props.type;
12
+ if (primary === "image" || primary === "video" || primary === "audio")
13
+ return primary;
14
+ if (primary === "other")
15
+ return "other";
16
+ }
10
17
  const hint = props.mime ?? props.name ?? props.url;
18
+ if (!hint)
19
+ return "other";
11
20
  const simplified = getSimplifiedMediaType(hint);
12
21
  return simplified;
13
22
  }
14
23
  export function MediaViewer(props) {
15
- const { url, className, filename, fallbackMode = "link", fallbackLabel } = props;
16
- const t = inferType(props);
17
- if (t === "image")
18
- return _jsx(ImageViewer, { ...props, type: "image" });
19
- if (t === "video")
20
- return _jsx(VideoViewer, { ...props, type: "video" });
21
- if (t === "audio")
22
- return _jsx(AudioViewer, { ...props, type: "audio" });
23
- return shouldShowFallback(fallbackMode) ? (_jsx("div", { className: className, children: _jsx(MediaFallbackLink, { url: url, filename: filename, label: fallbackLabel ?? "Download" }) })) : null;
24
+ const { url, alt, type, className, unloadOnExit, isCircular = false, priority, skeleton, lazy, controls, autoPlay, muted, loop, posterUrl, preload, onLoad, onError, onLoadChange, fallbackMode = "link", fallbackLabel, filename, mime, name, } = props;
25
+ const [hasError, setHasError] = React.useState(false);
26
+ const [sourceUrl, setSourceUrl] = React.useState(null);
27
+ // Handle string / Blob / File / null URLs
28
+ React.useEffect(() => {
29
+ setHasError(false);
30
+ let objectUrl = null;
31
+ if (typeof url === "string") {
32
+ setSourceUrl(url);
33
+ }
34
+ else if (url instanceof Blob) {
35
+ objectUrl = URL.createObjectURL(url);
36
+ setSourceUrl(objectUrl);
37
+ }
38
+ else {
39
+ setSourceUrl(null);
40
+ }
41
+ return () => {
42
+ if (objectUrl)
43
+ URL.revokeObjectURL(objectUrl);
44
+ };
45
+ }, [url]);
46
+ const handleError = React.useCallback(() => {
47
+ setHasError(true);
48
+ onError?.();
49
+ }, [onError]);
50
+ const circularStyle = isCircular
51
+ ? { borderRadius: "50%", overflow: "hidden" }
52
+ : {};
53
+ const wrapperStyle = {
54
+ position: "relative",
55
+ width: "100%",
56
+ height: "100%",
57
+ ...circularStyle,
58
+ };
59
+ // No URL -> empty fallback
60
+ if (!sourceUrl) {
61
+ return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(EmptyFallback, { isCircular: isCircular }) }));
62
+ }
63
+ // Error -> error fallback
64
+ if (hasError) {
65
+ return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(ErrorFallback, { isCircular: isCircular }) }));
66
+ }
67
+ const t = inferType({ type, mime, name, url: sourceUrl });
68
+ if (t === "image") {
69
+ return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(ImageViewer, { url: sourceUrl, alt: alt, lazy: lazy, priority: priority, skeleton: skeleton, unloadOnExit: unloadOnExit, isCircular: isCircular, onLoad: onLoad, onError: handleError, fallback: _jsx(ErrorFallback, { isCircular: isCircular }) }) }));
70
+ }
71
+ if (t === "video") {
72
+ return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(VideoViewer, { url: sourceUrl, lazy: lazy, priority: priority, skeleton: skeleton, unloadOnExit: unloadOnExit, controls: controls, autoPlay: autoPlay, muted: muted, loop: loop, posterUrl: posterUrl, preload: preload, onLoad: onLoad, onError: handleError }) }));
73
+ }
74
+ if (t === "audio") {
75
+ return (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(AudioViewer, { url: sourceUrl, lazy: lazy, priority: priority, skeleton: skeleton, controls: controls, autoPlay: autoPlay, loop: loop, preload: preload, onLoad: onLoad, onLoadChange: onLoadChange, onError: handleError }) }));
76
+ }
77
+ // Unknown type -> fallback link
78
+ return shouldShowFallback(fallbackMode) ? (_jsx("div", { className: className, style: wrapperStyle, children: _jsx(MediaFallbackLink, { url: sourceUrl, filename: filename, label: fallbackLabel ?? "Download" }) })) : null;
24
79
  }
25
80
  /** Back-compat alias */
26
81
  export const MediaPreview = MediaViewer;
@@ -1 +1 @@
1
- {"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../src/media-viewer.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAI1E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,SAAS,SAAS,CAAC,KAA+D;IAChF,IAAI,KAAK,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IAElC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC;IACnD,MAAM,UAAU,GAAwB,sBAAsB,CAAC,IAAI,CAAC,CAAC;IACrE,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,GAAG,MAAM,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC;IAEjF,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAE3B,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,KAAC,WAAW,OAAK,KAAK,EAAE,IAAI,EAAC,OAAO,GAAG,CAAC;IAClE,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,KAAC,WAAW,OAAK,KAAK,EAAE,IAAI,EAAC,OAAO,GAAG,CAAC;IAClE,IAAI,CAAC,KAAK,OAAO;QAAE,OAAO,KAAC,WAAW,OAAK,KAAK,EAAE,IAAI,EAAC,OAAO,GAAG,CAAC;IAElE,OAAO,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CACxC,cAAK,SAAS,EAAE,SAAS,YACvB,KAAC,iBAAiB,IAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,IAAI,UAAU,GAAI,GACnF,CACP,CAAC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,wBAAwB;AACxB,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC"}
1
+ {"version":3,"file":"media-viewer.js","sourceRoot":"","sources":["../src/media-viewer.tsx"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,32 +1,67 @@
1
+ import type * as React from "react";
1
2
  export type MediaViewerType = "image" | "video" | "audio" | "other";
2
3
  export type FallbackMode = "none" | "link";
3
- export type MediaViewerProps = {
4
- /** If omitted, inferred from mime/name/url. */
5
- type?: MediaViewerType;
6
- /** Required media URL */
4
+ export type BaseMediaProps = {
7
5
  url: string;
8
- /** Optional hints for inference */
9
- mime?: string;
10
- name?: string;
11
- /** Optional download filename (fallback link) */
12
- filename?: string;
13
- /** Wrapper class */
14
6
  className?: string;
15
- /** Media element class */
16
7
  mediaClassName?: string;
17
- /** Accessibility for images */
8
+ lazy?: boolean;
9
+ priority?: boolean;
10
+ skeleton?: boolean;
11
+ onLoad?: () => void;
12
+ onError?: () => void;
13
+ fallback?: React.ReactNode;
14
+ };
15
+ export type ImageViewerProps = BaseMediaProps & {
18
16
  alt?: string;
19
- /** Fallback rendering */
20
- fallbackMode?: FallbackMode;
21
- fallbackLabel?: string;
22
- /** Image */
23
17
  enableZoom?: boolean;
24
- /** Video */
18
+ unloadOnExit?: boolean;
19
+ isCircular?: boolean;
20
+ preventGestures?: boolean;
21
+ };
22
+ export type VideoViewerProps = BaseMediaProps & {
25
23
  controls?: boolean;
26
24
  autoPlay?: boolean;
27
25
  muted?: boolean;
28
26
  loop?: boolean;
29
27
  posterUrl?: string;
30
28
  enableFullscreen?: boolean;
29
+ unloadOnExit?: boolean;
30
+ preload?: "auto" | "metadata" | "none";
31
+ autoPlayOnVisible?: boolean;
32
+ };
33
+ export type AudioViewerProps = BaseMediaProps & {
34
+ controls?: boolean;
35
+ autoPlay?: boolean;
36
+ loop?: boolean;
37
+ preload?: "auto" | "metadata" | "none";
38
+ onLoadChange?: (isLoading: boolean) => void;
39
+ };
40
+ export type MediaPreviewProps = {
41
+ url?: string | File | Blob | null;
42
+ alt?: string;
43
+ type?: MediaViewerType | string;
44
+ className?: string;
45
+ unloadOnExit?: boolean;
46
+ isCircular?: boolean;
47
+ priority?: boolean;
48
+ skeleton?: boolean;
49
+ lazy?: boolean;
50
+ controls?: boolean;
51
+ autoPlay?: boolean;
52
+ muted?: boolean;
53
+ loop?: boolean;
54
+ posterUrl?: string;
55
+ preload?: "auto" | "metadata" | "none";
56
+ onLoad?: () => void;
57
+ onError?: () => void;
58
+ onLoadChange?: (isLoading: boolean) => void;
59
+ fallbackMode?: FallbackMode;
60
+ fallbackLabel?: string;
61
+ filename?: string;
62
+ mime?: string;
63
+ name?: string;
31
64
  };
65
+ /** Back-compat alias */
66
+ export type MediaViewerProps = MediaPreviewProps;
32
67
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AACpE,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+CAA+C;IAC/C,IAAI,CAAC,EAAE,eAAe,CAAC;IAEvB,yBAAyB;IACzB,GAAG,EAAE,MAAM,CAAC;IAEZ,mCAAmC;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,oBAAoB;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,0BAA0B;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,+BAA+B;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,yBAAyB;IACzB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,YAAY;IACZ,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,YAAY;IACZ,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAI5B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import type { MediaViewerProps } from "./types";
2
- export declare function VideoViewer(props: MediaViewerProps): import("react/jsx-runtime").JSX.Element;
1
+ import type { VideoViewerProps } from "./types.js";
2
+ export declare function VideoViewer(props: VideoViewerProps): import("react/jsx-runtime").JSX.Element;
3
3
  //# sourceMappingURL=video-viewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"video-viewer.d.ts","sourceRoot":"","sources":["../src/video-viewer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAsElD"}
1
+ {"version":3,"file":"video-viewer.d.ts","sourceRoot":"","sources":["../src/video-viewer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAgLlD"}
@@ -1,8 +1,70 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { useInView } from "react-intersection-observer";
4
+ import { Skeleton } from "@ttt-productions/ui-core";
3
5
  export function VideoViewer(props) {
4
- const { url, className, mediaClassName, controls = true, autoPlay, muted, loop, posterUrl, enableFullscreen = true, } = props;
6
+ const { url, className, mediaClassName, controls = true, autoPlay = false, muted, loop = false, posterUrl, enableFullscreen = true, lazy = true, unloadOnExit = true, priority = false, skeleton: showSkeleton = true, preload = "metadata", autoPlayOnVisible = false, onLoad, onError, fallback, } = props;
7
+ const resolvedMuted = muted ?? autoPlay;
8
+ const [isLoaded, setIsLoaded] = React.useState(false);
9
+ const [hasError, setHasError] = React.useState(false);
10
+ const [shouldLoad, setShouldLoad] = React.useState(priority || !lazy);
5
11
  const videoRef = React.useRef(null);
12
+ const { ref: inViewRef, inView } = useInView({
13
+ triggerOnce: !unloadOnExit,
14
+ threshold: 0.01,
15
+ rootMargin: "200px",
16
+ skip: priority || !lazy,
17
+ });
18
+ // Load/unload based on viewport
19
+ React.useEffect(() => {
20
+ if (inView && !shouldLoad) {
21
+ setShouldLoad(true);
22
+ }
23
+ if (!inView && unloadOnExit && shouldLoad && !priority) {
24
+ const video = videoRef.current;
25
+ if (video) {
26
+ video.pause();
27
+ video.removeAttribute("src");
28
+ video.load();
29
+ }
30
+ setIsLoaded(false);
31
+ setShouldLoad(false);
32
+ }
33
+ }, [inView, shouldLoad, unloadOnExit, priority]);
34
+ // Reset state when URL changes
35
+ React.useEffect(() => {
36
+ setHasError(false);
37
+ setIsLoaded(false);
38
+ }, [url]);
39
+ const handleLoadedData = React.useCallback(() => {
40
+ setIsLoaded(true);
41
+ onLoad?.();
42
+ }, [onLoad]);
43
+ const handleError = React.useCallback(() => {
44
+ setHasError(true);
45
+ onError?.();
46
+ }, [onError]);
47
+ // Autoplay on visible / pause on exit
48
+ React.useEffect(() => {
49
+ const video = videoRef.current;
50
+ if (!video || !shouldLoad || (!autoPlayOnVisible && !autoPlay))
51
+ return;
52
+ const observer = new IntersectionObserver((entries) => {
53
+ for (const entry of entries) {
54
+ if (entry.isIntersecting) {
55
+ if (video.paused) {
56
+ video.play().catch(() => { });
57
+ }
58
+ }
59
+ else if (!video.paused) {
60
+ video.pause();
61
+ }
62
+ }
63
+ }, { threshold: 0.5 });
64
+ observer.observe(video);
65
+ return () => observer.disconnect();
66
+ }, [autoPlayOnVisible, autoPlay, shouldLoad]);
67
+ // Fullscreen
6
68
  const requestFullscreen = React.useCallback(() => {
7
69
  if (!enableFullscreen)
8
70
  return;
@@ -17,9 +79,7 @@ export function VideoViewer(props) {
17
79
  try {
18
80
  fn.call(el);
19
81
  }
20
- catch {
21
- // ignore
22
- }
82
+ catch { /* ignore */ }
23
83
  }
24
84
  }, [enableFullscreen]);
25
85
  const onKeyDown = React.useCallback((e) => {
@@ -30,6 +90,20 @@ export function VideoViewer(props) {
30
90
  requestFullscreen();
31
91
  }
32
92
  }, [enableFullscreen, requestFullscreen]);
33
- return (_jsx("div", { className: className, role: enableFullscreen ? "button" : undefined, tabIndex: enableFullscreen ? 0 : undefined, "aria-label": enableFullscreen ? "Open video fullscreen" : undefined, onDoubleClick: requestFullscreen, onKeyDown: onKeyDown, style: { outline: "none" }, children: _jsx("video", { ref: videoRef, src: url, className: mediaClassName, controls: controls, autoPlay: autoPlay, muted: muted, loop: loop, playsInline: true, poster: posterUrl, style: { width: "100%", height: "100%" } }) }));
93
+ if (hasError) {
94
+ if (fallback)
95
+ return _jsx(_Fragment, { children: fallback });
96
+ return (_jsx("div", { className: className, children: _jsx("div", { className: "mv-video-error", children: _jsx("p", { children: "Failed to load video" }) }) }));
97
+ }
98
+ return (_jsxs("div", { ref: inViewRef, className: className, role: enableFullscreen ? "button" : undefined, tabIndex: enableFullscreen ? 0 : undefined, "aria-label": enableFullscreen ? "Open video fullscreen" : undefined, onDoubleClick: requestFullscreen, onKeyDown: onKeyDown, style: { position: "relative", width: "100%", height: "100%", overflow: "hidden", outline: "none" }, children: [showSkeleton && !isLoaded && shouldLoad && (_jsx(Skeleton, { style: { position: "absolute", inset: 0, width: "100%", height: "100%" } })), shouldLoad && (_jsx("video", { ref: videoRef, src: url, className: mediaClassName, controls: controls, autoPlay: autoPlay, muted: resolvedMuted, loop: loop, poster: posterUrl, onLoadedData: handleLoadedData, onError: handleError, preload: priority ? "auto" : preload, playsInline: true, style: {
99
+ width: "100%",
100
+ height: "100%",
101
+ objectFit: "cover",
102
+ opacity: isLoaded ? 1 : 0,
103
+ transition: "opacity 300ms ease",
104
+ WebkitUserSelect: "none",
105
+ WebkitTouchCallout: "none",
106
+ WebkitTapHighlightColor: "transparent",
107
+ } }))] }));
34
108
  }
35
109
  //# sourceMappingURL=video-viewer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"video-viewer.js","sourceRoot":"","sources":["../src/video-viewer.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EACJ,GAAG,EACH,SAAS,EACT,cAAc,EACd,QAAQ,GAAG,IAAI,EACf,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,SAAS,EACT,gBAAgB,GAAG,IAAI,GACxB,GAAG,KAAK,CAAC;IAEV,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAA0B,IAAI,CAAC,CAAC;IAE7D,MAAM,iBAAiB,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE;QAC/C,IAAI,CAAC,gBAAgB;YAAE,OAAO;QAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAc,CAAC;QACnC,IAAI,CAAC,EAAE;YAAE,OAAO;QAEhB,MAAM,EAAE,GACN,EAAE,CAAC,iBAAiB;YACpB,EAAE,CAAC,uBAAuB;YAC1B,EAAE,CAAC,oBAAoB;YACvB,EAAE,CAAC,mBAAmB,CAAC;QAEzB,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACd,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAEvB,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CACjC,CAAC,CAAsB,EAAE,EAAE;QACzB,IAAI,CAAC,gBAAgB;YAAE,OAAO;QAC9B,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;YACvC,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,iBAAiB,EAAE,CAAC;QACtB,CAAC;IACH,CAAC,EACD,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CACtC,CAAC;IAEF,OAAO,CACL,cACE,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAC7C,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,gBAC9B,gBAAgB,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,SAAS,EAClE,aAAa,EAAE,iBAAiB,EAChC,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,YAE1B,gBACE,GAAG,EAAE,QAAQ,EACb,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,IAAI,EACV,WAAW,QACX,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GACxC,GACE,CACP,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"video-viewer.js","sourceRoot":"","sources":["../src/video-viewer.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/media-viewer",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ttt-productions/ttt-packages.git",
@@ -12,13 +12,15 @@
12
12
  "types": "dist/index.d.ts",
13
13
  "sideEffects": false,
14
14
  "files": [
15
- "dist"
15
+ "dist",
16
+ "src/styles"
16
17
  ],
17
18
  "exports": {
18
19
  ".": {
19
20
  "types": "./dist/index.d.ts",
20
21
  "default": "./dist/index.js"
21
- }
22
+ },
23
+ "./styles": "./src/styles/media-viewer.css"
22
24
  },
23
25
  "scripts": {
24
26
  "build": "tsc",
@@ -33,7 +35,8 @@
33
35
  "peerDependencies": {
34
36
  "react": ">=18.0.0",
35
37
  "react-dom": ">=18.0.0",
36
- "lucide-react": ">=0.0.0"
38
+ "lucide-react": ">=0.0.0",
39
+ "react-intersection-observer": ">=9.0.0"
37
40
  },
38
41
  "devDependencies": {
39
42
  "typescript": "^5.8.3"
@@ -0,0 +1,95 @@
1
+ /* @ttt-productions/media-viewer styles */
2
+
3
+ /* Skeleton fallback (CSS-only, for environments without ui-core) */
4
+ .mv-skeleton {
5
+ background: hsl(var(--muted, 220 14% 96%));
6
+ animation: mv-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
7
+ border-radius: var(--radius, 0.375rem);
8
+ }
9
+
10
+ @keyframes mv-pulse {
11
+ 0%, 100% { opacity: 1; }
12
+ 50% { opacity: 0.5; }
13
+ }
14
+
15
+ /* Error fallback */
16
+ .mv-error-fallback {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ width: 100%;
21
+ height: 100%;
22
+ background: hsl(var(--destructive, 0 84% 60%) / 0.1);
23
+ }
24
+
25
+ .mv-error-fallback svg {
26
+ width: 50%;
27
+ height: 50%;
28
+ max-width: 4rem;
29
+ max-height: 4rem;
30
+ color: hsl(var(--destructive, 0 84% 60%) / 0.8);
31
+ }
32
+
33
+ /* Empty fallback */
34
+ .mv-empty-fallback {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ width: 100%;
39
+ height: 100%;
40
+ background: hsl(var(--muted, 220 14% 96%));
41
+ }
42
+
43
+ .mv-empty-fallback svg {
44
+ width: 50%;
45
+ height: 50%;
46
+ max-width: 4rem;
47
+ max-height: 4rem;
48
+ color: hsl(var(--muted-foreground, 220 9% 46%));
49
+ }
50
+
51
+ /* Avatar/circular empty fallback */
52
+ .mv-avatar-fallback {
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: center;
56
+ width: 100%;
57
+ height: 100%;
58
+ border-radius: 50%;
59
+ background: hsl(var(--muted, 220 14% 96%));
60
+ font-size: 1.125rem;
61
+ color: hsl(var(--muted-foreground, 220 9% 46%));
62
+ }
63
+
64
+ /* Video error state */
65
+ .mv-video-error {
66
+ display: flex;
67
+ flex-direction: column;
68
+ align-items: center;
69
+ justify-content: center;
70
+ width: 100%;
71
+ height: 100%;
72
+ background: hsl(var(--muted, 220 14% 96%));
73
+ border-radius: var(--radius, 0.375rem);
74
+ }
75
+
76
+ .mv-video-error p {
77
+ color: hsl(var(--muted-foreground, 220 9% 46%));
78
+ font-size: 0.875rem;
79
+ }
80
+
81
+ /* Audio error state */
82
+ .mv-audio-error {
83
+ display: flex;
84
+ align-items: center;
85
+ justify-content: center;
86
+ background: hsl(var(--muted, 220 14% 96%));
87
+ border-radius: var(--radius, 0.375rem);
88
+ padding: 1rem;
89
+ height: 3.5rem;
90
+ }
91
+
92
+ .mv-audio-error p {
93
+ color: hsl(var(--destructive, 0 84% 60%));
94
+ font-size: 0.75rem;
95
+ }