返回 reveal.js
demo-app.tsx
根目录 / react / demo / src / demo-app.tsx
1 import { Children, useEffect, useState } from 'react';
2 import type { SlideSyncEvent } from 'reveal.js';
3 import { Deck, Slide, Stack, Markdown, Fragment, Code, useReveal } from '@revealjs/react';
4 import 'reveal.js/reveal.css';
5 import 'reveal.js/theme/black.css';
6 import 'reveal.js/plugin/highlight/monokai.css';
7
8 import RevealHighlight from 'reveal.js/plugin/highlight';
9 import RevealNotes from 'reveal.js/plugin/notes';
10
11 const buttonStyle: React.CSSProperties = {
12 padding: '0.55em 0.95em',
13 fontSize: '0.7em',
14 fontWeight: 600,
15 lineHeight: 1.2,
16 color: '#ffffff',
17 background: 'rgba(8, 13, 24, 0.72)',
18 border: '1px solid rgba(255, 255, 255, 0.4)',
19 borderRadius: '0.35em',
20 cursor: 'pointer',
21 };
22
23 function NavigationControls() {
24 const deck = useReveal();
25
26 return (
27 <div style={{ marginTop: '1em' }}>
28 <button style={buttonStyle} onClick={() => deck?.prev()}>
29 Previous
30 </button>{' '}
31 <button style={buttonStyle} onClick={() => deck?.next()}>
32 Next
33 </button>
34 </div>
35 );
36 }
37
38 function Columns({ children }: { children: React.ReactNode }) {
39 return (
40 <div style={{ display: 'flex', flexDirection: 'row' }}>
41 {Children.map(children, (child, index) => (
42 <div key={index} style={{ flex: 1 }}>
43 {child}
44 </div>
45 ))}
46 </div>
47 );
48 }
49
50 function SlideSyncPlayground() {
51 const [count, setCount] = useState(0);
52 const [slideColor, setSlideColor] = useState('#1b1f2a');
53
54 const randomColor = () => {
55 const value = Math.floor(Math.random() * 0xffffff)
56 .toString(16)
57 .padStart(6, '0');
58 return `#${value}`;
59 };
60
61 return (
62 <Slide background={slideColor}>
63 <h2>Slide-local HTML updates</h2>
64 <p>
65 This slide updates only its own React-rendered HTML, without manually calling{' '}
66 <code>sync</code> or <code>syncSlide</code>.
67 </p>
68 <div>
69 <div style={{ marginBottom: '0.75em' }}>
70 <button style={buttonStyle} onClick={() => setCount((c) => c + 1)}>
71 Increase count
72 </button>{' '}
73 <button style={buttonStyle} onClick={() => setSlideColor(randomColor())}>
74 Randomize background
75 </button>
76 </div>
77
78 <p>
79 <strong>Current count:</strong> {count}
80 </p>
81 <p>
82 <strong>Slide color:</strong> {slideColor}
83 </p>
84 </div>
85 </Slide>
86 );
87 }
88
89 function Demo() {
90 const [showBonus, setShowBonus] = useState(false);
91 const [controls, setControls] = useState(true);
92
93 useEffect(() => {
94 const onKeyDown = (e: KeyboardEvent) => {
95 if (e.key === 'c' && !e.ctrlKey && !e.metaKey && !e.altKey) {
96 setControls((prev) => !prev);
97 }
98 };
99 window.addEventListener('keydown', onKeyDown);
100 return () => window.removeEventListener('keydown', onKeyDown);
101 }, []);
102
103 return (
104 <Deck
105 config={{
106 width: 1280,
107 height: 720,
108 transition: 'slide',
109 hash: true,
110 controls,
111 }}
112 plugins={[RevealHighlight, RevealNotes]}
113 onReady={(deck) => console.log('Deck ready!', deck)}
114 onSync={() => console.log('Deck synced')}
115 onSlideSync={(e) => {
116 const slide = (e as SlideSyncEvent).slide;
117 console.log('Slide synced', slide);
118 }}
119 onSlideChange={(e) => console.log('Slide changed')}
120 >
121 <Slide>
122 <h1>@revealjs/react</h1>
123 <p>React wrapper for reveal.js</p>
124 </Slide>
125
126 <Stack>
127 <Slide background="indigo">
128 <h2>Vertical Stack</h2>
129 <p>Press down to navigate</p>
130 <Code language="html" codeStyle={{ padding: '0.5em' }}>
131 {`
132 <Stack>
133 <Slide background="indigo">
134 <h2>Vertical Stack</h2>
135 <p>Press down to navigate</p>
136 </Slide>
137 <Slide background="indigo">
138 <h2>Stack Slide 2</h2>
139 <p>Vertical navigation works!</p>
140 </Slide>
141 </Stack>
142 `}
143 </Code>
144 </Slide>
145 <Slide background="indigo">
146 <h2>Stack Slide 2</h2>
147 <p>Vertical navigation works!</p>
148 </Slide>
149 </Stack>
150
151 <Slide>
152 <Columns>
153 <div style={{ textAlign: 'left' }}>
154 <h2>API Hook</h2>
155 <p>Components inside slides can access the reveal.js API via the useReveal() hook.</p>
156 <NavigationControls />
157 </div>
158 <div>
159 <Code language="javascript" lineNumbers="1|4">
160 {`
161 const deck = useReveal();
162
163 function nextSlide() {
164 deck?.next();
165 }
166 `}
167 </Code>
168 </div>
169 </Columns>
170 </Slide>
171
172 <SlideSyncPlayground />
173
174 <Slide>
175 <h2>Dynamic Slides</h2>
176 <p>Add slides at runtime — sync() handles it</p>
177 <button style={buttonStyle} onClick={() => setShowBonus((b) => !b)}>
178 {showBonus ? 'Remove' : 'Add'} bonus slide
179 </button>
180 </Slide>
181
182 {showBonus && (
183 <Slide data-background="#2d2d8c">
184 <h2>Bonus Slide!</h2>
185 <p>Dynamically added via React state</p>
186 </Slide>
187 )}
188
189 <Slide data-background="#000">
190 <h2>Fragments</h2>
191 <Columns>
192 <div>
193 <Fragment animation="fade-up">
194 <p>This appears first</p>
195 </Fragment>
196 <Fragment animation="fade-up">
197 <p>Then this</p>
198 </Fragment>
199 <Fragment animation="highlight-red" asChild>
200 <p>And this gets highlighted</p>
201 </Fragment>
202 </div>
203 <div>
204 <Code language="html" codeStyle={{ padding: '0.5em' }}>
205 {`
206 <Fragment animation="fade-up">
207 <p>This appears first</p>
208 </Fragment>
209 <Fragment animation="fade-up">
210 <p>Then this</p>
211 </Fragment>
212 <Fragment animation="highlight-red">
213 <p>And this gets highlighted</p>
214 </Fragment>
215 `}
216 </Code>
217 </div>
218 </Columns>
219 </Slide>
220
221 <Slide autoAnimate>
222 <h1>Auto-animate</h1>
223 <p>This slide is animated automatically</p>
224 </Slide>
225
226 <Slide autoAnimate>
227 <h1 style={{ opacity: 0.4 }}>Auto-animate</h1>
228 <Code
229 language="html"
230 code={`
231 <Slide autoAnimate>
232 <h1>Auto-animate</h1>
233 <p>This slide is animated automatically</p>
234 </Slide>
235 `}
236 />
237 <p style={{ opacity: 0.4 }}>This slide is animated automatically</p>
238 </Slide>
239
240 <Markdown
241 separator="^\n---\n$"
242 verticalSeparator="^\n--\n$"
243 options={{ smartypants: true, animateLists: true }}
244 >
245 {`
246 ## Markdown 1.1
247
248 - First point <!-- .element: class="fragment" -->
249 - Second point <!-- .element: class="fragment" -->
250
251 --
252
253 ## Markdown 1.2
254
255 Notes:
256 These are speaker notes parsed from markdown.
257
258 ---
259
260 <!-- .slide: data-background="#0f172a" -->
261 ## Markdown 2
262
263 \`\`\`js [1|2]
264 const a = 1;
265 const b = 2;
266 \`\`\`
267 `}
268 </Markdown>
269
270 <Markdown src="markdown.md" verticalSeparator="@@@" />
271
272 <Slide>
273 <h2>The End</h2>
274 <Fragment animation="fade-up">
275 <p>Thanks for watching!</p>
276 </Fragment>
277 </Slide>
278 </Deck>
279 );
280 }
281
282 export default Demo;
283
283 lines Plain Text