Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 6a3d56e

Browse files
committed
feat(client): introduce fragments
1 parent 586d2b9 commit 6a3d56e

File tree

7 files changed

+98
-41
lines changed

7 files changed

+98
-41
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
1-
import React, { memo, useEffect } from 'react';
1+
import React, { memo, useEffect, useRef } from 'react';
22
import { useSlides, updateCurrentIndex } from '../../context/slides';
33
import { SlideCore } from '../SlideCore';
44

55
export const Base = memo(() => {
66
const {
7-
state: { currentIndex },
7+
state: { currentIndex, timeline },
88
dispatch,
99
} = useSlides();
10+
const currentIndexRef = useRef(currentIndex);
1011

1112
useEffect(() => {
1213
// TODO: swiper should be gone to context
1314
const { swiper } = document.querySelector('.swiper-container');
1415
swiper?.slideTo(currentIndex);
1516
}, [currentIndex]);
1617

17-
useEffect(() => {
18-
const keyboardListener = ({ key }) => {
19-
if (key === 'ArrowRight') {
20-
dispatch(updateCurrentIndex('+'));
21-
} else if (key === 'ArrowLeft') {
22-
dispatch(updateCurrentIndex('-'));
23-
}
24-
};
18+
const keyboardListener = ({ key }) => {
19+
if (Array.isArray(timeline[currentIndexRef.current])) {
20+
return;
21+
}
2522

23+
if (key === 'ArrowRight') {
24+
dispatch(updateCurrentIndex('+'));
25+
} else if (key === 'ArrowLeft') {
26+
dispatch(updateCurrentIndex('-'));
27+
}
28+
};
29+
30+
useEffect(() => {
2631
document.addEventListener('keydown', keyboardListener);
2732

2833
return () => {
2934
document.removeEventListener('keydown', keyboardListener);
3035
};
3136
}, []);
3237

38+
useEffect(() => {
39+
currentIndexRef.current = currentIndex;
40+
}, [currentIndex]);
41+
3342
return <SlideCore />;
3443
});

packages/client/src/components/Fragments.js

+39-17
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,57 @@
11
import React, { useEffect, useState, useRef } from 'react';
2-
import { useSlides } from '../context/slides';
2+
import { useSlides, updateCurrentIndex } from '../context/slides';
33

4-
export const Fragments = ({ children }) => {
4+
export const Fragments = ({ children, id }) => {
55
const [currentStep, setCurrentStep] = useState(0);
6-
const ref = useRef(currentStep);
6+
const [steps, setSteps] = useState(0);
7+
const currentStepRef = useRef(currentStep);
8+
const stepsRef = useRef(steps);
79
const {
8-
state: { currentIndex },
10+
state: { currentIndex, timeline },
11+
dispatch,
912
} = useSlides();
1013

1114
const listener = (e) => {
1215
e.stopPropagation();
13-
e.preventDefault();
14-
console.log('aaaa');
16+
1517
if (e.key === 'ArrowRight') {
16-
setCurrentStep(ref.current + 1);
18+
const next = currentStepRef.current + 1;
19+
20+
if (next > stepsRef.current) {
21+
dispatch(updateCurrentIndex('+'));
22+
document.removeEventListener('keydown', listener);
23+
} else {
24+
setCurrentStep(next);
25+
}
26+
} else if (e.key === 'ArrowLeft') {
27+
const next = currentStepRef.current - 1;
28+
29+
if (next < 0) {
30+
dispatch(updateCurrentIndex('-'));
31+
document.removeEventListener('keydown', listener);
32+
} else {
33+
setCurrentStep(next);
34+
}
1735
}
1836
};
1937

2038
useEffect(() => {
21-
ref.current = currentStep;
22-
}, [currentStep]);
39+
currentStepRef.current = currentStep;
40+
stepsRef.current = steps;
41+
}, [currentStep, steps]);
2342

2443
useEffect(() => {
25-
// document.addEventListener('keydown', listener, {
26-
// capture: false,
27-
// passive: false,
28-
// });
29-
// return () => {
30-
// document.removeEventListener('keydown', listener);
31-
// };
32-
}, []);
44+
const current = timeline[currentIndex];
45+
46+
if (Array.isArray(current) && current[0] === id) {
47+
setSteps(current.length);
48+
49+
document.addEventListener('keydown', listener, {
50+
capture: false,
51+
passive: false,
52+
});
53+
}
54+
}, [currentIndex]);
3355

3456
return React.Children.map(children, (child, i) =>
3557
React.cloneElement(child, { style: { visibility: i >= currentStep ? 'hidden' : 'initial' } })

packages/client/src/components/SlideCore.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export const SlideCore = () => {
7272
direction={process.env.UI.VERTICAL === 'true' ? 'vertical' : 'horizontal'}
7373
loop={/*TODO: respect to params to generate pdf */ process.env.LOOP}
7474
speed={350}
75-
allowTouchMove={window.innerWidth <= 768}
75+
allowTouchMove={false}
7676
slidesPerView={1}
7777
hashNavigation={{
7878
watchState: true,

packages/client/src/context/slides.js

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const initialState = {
88
currentIndex: getCurrentIndexFromUrl(),
99
slides: [],
1010
contentsList: [],
11+
timeline: [],
1112
};
1213

1314
const SlidesContext = createContext(initialState);

packages/client/src/utils/createSlidesProps.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export function createSlidesProps(slides) {
1212
slidesArr.push(...slides);
1313
propsArr.push(...fusumaProps);
1414
backgroundsArr.push(...backgrounds);
15-
fragmentStepsArr.push(...fragmentSteps);
15+
fragmentStepsArr.push(fragmentSteps);
16+
slidesTimeline.push(...fragmentSteps);
1617
});
1718

1819
propsArr.reduce((acc, { sectionTitle }, i) => {
@@ -42,23 +43,17 @@ export function createSlidesProps(slides) {
4243
? props.classes[0].split(',') // for HMR
4344
: props.classes.split(',');
4445
}
45-
if (fragmentStepsArr[i] === 0) {
46-
slidesTimeline.push(0);
47-
} else {
48-
slidesTimeline.push(Array.from({ length: fragmentStepsArr[i] }, (_, i) => i + 1));
49-
}
5046

5147
return {
5248
slide: props.contents ? ToC({ list: res.contentsList }) : slide,
5349
fusumaProps: {
5450
...props,
5551
background,
5652
},
57-
fragmentSteps: fragmentStepsArr[i],
5853
};
5954
});
6055

61-
res.slidesTimeline = slidesTimeline;
56+
res.timeline = slidesTimeline;
6257

6358
return res;
6459
}

packages/mdx-loader/src/mdxPlugin.js

+33-5
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,35 @@ function mdxPlugin() {
2121
let mermaidId = 1;
2222
let isFragmentArea = false;
2323
let fragmentSteps = 0;
24+
let fragmentId = 0;
25+
let hasFragments = false;
26+
27+
function formatSlidesTimeline(fragmentSteps, fragmentId) {
28+
if (fragmentSteps === 0) {
29+
return [0];
30+
} else {
31+
hasFragments = true;
32+
return [[...Array(fragmentSteps)].fill(fragmentId)];
33+
}
34+
}
2435

2536
// TODO: refactor using visit
2637
tree.children.forEach((n, i) => {
2738
const { type, value, lang, meta } = n;
2839

2940
// move to a new slide
3041
if (type === 'thematicBreak') {
31-
slides.push({ slide, props, background, fragmentSteps });
42+
slides.push({
43+
slide,
44+
props,
45+
background,
46+
fragmentSteps: formatSlidesTimeline(fragmentSteps, fragmentId),
47+
});
3248
slide = [];
3349
props = {};
3450
background = 0; // why 0? because null, undefined, '' are omitted at client side
3551
fragmentSteps = 0;
52+
fragmentId = 0;
3653
isFragmentArea = false;
3754
return;
3855
}
@@ -69,10 +86,11 @@ function mdxPlugin() {
6986
return;
7087
}
7188
if (prefix === 'fragments-start') {
89+
fragmentId = Math.random();
7290
slide.push({
7391
...n,
7492
type: 'jsx',
75-
value: '<Client.Fragments>',
93+
value: `<Client.Fragments id={${fragmentId}}>`,
7694
});
7795
isFragmentArea = true;
7896
return;
@@ -180,7 +198,12 @@ function mdxPlugin() {
180198
});
181199

182200
// push last slide
183-
slides.push({ slide, props, background, fragmentSteps });
201+
slides.push({
202+
slide,
203+
props,
204+
background,
205+
fragmentSteps: formatSlidesTimeline(fragmentSteps, fragmentId),
206+
});
184207

185208
const res = {
186209
jsx: [],
@@ -212,7 +235,7 @@ function mdxPlugin() {
212235
res.jsx.push(template);
213236
res.fusumaProps.push(fusumaProps);
214237
res.background.push(background);
215-
res.fragmentSteps.push(fragmentSteps);
238+
res.fragmentSteps.push(...fragmentSteps);
216239
}
217240
});
218241

@@ -224,12 +247,17 @@ function mdxPlugin() {
224247
value: `
225248
import React from 'react';
226249
import { mdx } from '@mdx-js/react';
250+
${
251+
hasFragments &&
252+
`
227253
// don't import as named to avoid using makeShortcode by mdx
228254
import * as Client from '@fusuma/client';
255+
`
256+
}
229257
230258
export const slides = [${res.jsx.join(',\n')}];
231259
export const backgrounds = [${res.background.join(',\n')}];
232-
export const fragmentSteps = [${res.fragmentSteps.join(',\n')}];
260+
export const fragmentSteps = ${JSON.stringify(res.fragmentSteps)};
233261
export const fusumaProps = [${res.fusumaProps.join(',\n')}];`,
234262
});
235263
};

samples/debug/slides/06-blocks.md

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ outer
2222

2323
---
2424

25+
### Fragments
26+
2527
<!-- fragments-start -->
2628

2729
1

0 commit comments

Comments
 (0)