-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDeckShell.tsx
More file actions
97 lines (91 loc) · 2.83 KB
/
Copy pathDeckShell.tsx
File metadata and controls
97 lines (91 loc) · 2.83 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
'use client';
import { useEffect, useState, type ReactNode } from 'react';
import DeckChrome from './DeckChrome';
import DriftingLeaves from './DriftingLeaves';
export default function DeckShell({
pages,
pageMeta,
}: {
pages: ReactNode[];
pageMeta: Array<{
act: string;
counter: string;
dark?: boolean;
leaves?: boolean;
}>;
}) {
const [current, setCurrent] = useState(1);
useEffect(() => {
const handler = (e: KeyboardEvent) => {
if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return;
const target = e.target as HTMLElement | null;
if (
target &&
(target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' ||
target.tagName === 'SELECT' ||
target.isContentEditable)
) {
return;
}
e.preventDefault();
const next = Math.min(
Math.max(current + (e.key === 'ArrowDown' ? 1 : -1), 1),
pages.length
);
document
.getElementById(`page-${next}`)
?.scrollIntoView({ behavior: 'smooth' });
setCurrent(next);
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [current, pages.length]);
useEffect(() => {
const container = document.querySelector('.deck');
if (!container) return;
const sections = container.querySelectorAll('section[id^="page-"]');
if (sections.length === 0) return;
const observer = new IntersectionObserver(
entries => {
let best: { page: number; ratio: number } | null = null;
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const id = entry.target.id;
const page = Number(id.replace('page-', ''));
if (Number.isNaN(page)) continue;
if (!best || entry.intersectionRatio > best.ratio) {
best = { page, ratio: entry.intersectionRatio };
}
}
if (best) setCurrent(best.page);
},
{ threshold: 0.6 }
);
sections.forEach(section => observer.observe(section));
return () => observer.disconnect();
}, [pages.length]);
return (
<div className="deck">
<div className="deck-ambient" aria-hidden="true">
<div
className={`deck-leaves${pageMeta[current - 1]?.leaves ? '' : ' deck-leaves-hidden'}${pageMeta[current - 1]?.dark ? ' deck-leaves-dark' : ''}`}
>
<DriftingLeaves />
</div>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/opportunity/cover-decoration.png"
alt=""
className={`deck-tree${current > 1 ? ' deck-tree-hidden' : ''}`}
/>
</div>
<DeckChrome
meta={pageMeta[current - 1]}
hidden={current === 1}
dark={pageMeta[current - 1]?.dark === true}
/>
{pages}
</div>
);
}