-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSlideFrame.tsx
More file actions
103 lines (94 loc) · 2.59 KB
/
SlideFrame.tsx
File metadata and controls
103 lines (94 loc) · 2.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
'use client';
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
export const CANVAS_W = 1920;
export const CANVAS_H = 1080;
type Props = {
src: string;
title?: string;
/** When false, defer src assignment until in view (for thumbnails). */
lazy?: boolean;
/** When false, do not run scripts inside the slide. */
interactive?: boolean;
/** Show a subtle loading bar while the iframe loads. */
showLoader?: boolean;
className?: string;
};
export function SlideFrame({
src,
title,
lazy = false,
interactive = true,
showLoader = false,
className,
}: Props) {
const wrapRef = useRef<HTMLDivElement>(null);
const iframeRef = useRef<HTMLIFrameElement>(null);
const [scale, setScale] = useState(1);
const [visible, setVisible] = useState(!lazy);
const [loading, setLoading] = useState(true);
useLayoutEffect(() => {
const el = wrapRef.current;
if (!el) return;
const update = () => {
const r = el.getBoundingClientRect();
const s = Math.min(r.width / CANVAS_W, r.height / CANVAS_H);
setScale(s > 0 ? s : 1);
};
update();
const ro = new ResizeObserver(update);
ro.observe(el);
return () => ro.disconnect();
}, []);
useEffect(() => {
if (!lazy) return;
const el = wrapRef.current;
if (!el) return;
const io = new IntersectionObserver(
(entries) => {
for (const e of entries) {
if (e.isIntersecting) {
setVisible(true);
io.disconnect();
break;
}
}
},
{ rootMargin: '200px' },
);
io.observe(el);
return () => io.disconnect();
}, [lazy]);
// Reset loading state when src changes
useEffect(() => {
setLoading(true);
}, [src]);
const sandbox = interactive ? 'allow-scripts allow-same-origin' : '';
const wrapClass = interactive ? 'slide-wrap' : 'slide-wrap slide-wrap-passive';
return (
<div ref={wrapRef} className={`${wrapClass} ${className ?? ''}`}>
{showLoader && loading ? <div className="slide-loader" aria-hidden /> : null}
<div
className="slide-canvas"
style={{
width: CANVAS_W,
height: CANVAS_H,
transform: `scale(${scale})`,
}}
>
{visible ? (
<iframe
ref={iframeRef}
src={src}
title={title ?? 'slide'}
width={CANVAS_W}
height={CANVAS_H}
sandbox={sandbox}
loading={lazy ? 'lazy' : 'eager'}
tabIndex={-1}
onLoad={() => setLoading(false)}
/>
) : null}
</div>
</div>
);
}