@particle-academy/fancy-slides 0.3.0 → 0.4.0
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.
- package/README.md +26 -0
- package/dist/index.cjs +460 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -9
- package/dist/index.d.ts +97 -9
- package/dist/index.js +455 -40
- package/dist/index.js.map +1 -1
- package/dist/registry.d.cts +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/{types-B2ecrEAz.d.cts → types-P-9MmnGU.d.cts} +32 -1
- package/dist/{types-B2ecrEAz.d.ts → types-P-9MmnGU.d.ts} +32 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -70,6 +70,79 @@ function resolveTheme(theme) {
|
|
|
70
70
|
function cn(...parts) {
|
|
71
71
|
return parts.filter(Boolean).join(" ");
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
// src/utils/builds.ts
|
|
75
|
+
var DEFAULT_BUILD_DURATION = 500;
|
|
76
|
+
function collectBuilds(slide) {
|
|
77
|
+
if (!slide) return [];
|
|
78
|
+
const builds = [];
|
|
79
|
+
slide.elements.forEach((element, index) => {
|
|
80
|
+
if (element.animation) {
|
|
81
|
+
builds.push({ element, animation: element.animation, index });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return builds.sort((a, b) => {
|
|
85
|
+
const ao = a.animation.order ?? 0;
|
|
86
|
+
const bo = b.animation.order ?? 0;
|
|
87
|
+
if (ao !== bo) return ao - bo;
|
|
88
|
+
return a.index - b.index;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function buildSteps(slide) {
|
|
92
|
+
const builds = collectBuilds(slide);
|
|
93
|
+
const steps = [];
|
|
94
|
+
for (const build of builds) {
|
|
95
|
+
const trigger = build.animation.trigger ?? "on-click";
|
|
96
|
+
if (steps.length === 0 || trigger === "on-click") {
|
|
97
|
+
steps.push({ builds: [build] });
|
|
98
|
+
} else {
|
|
99
|
+
steps[steps.length - 1].builds.push(build);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return steps;
|
|
103
|
+
}
|
|
104
|
+
function totalBuildSteps(slide) {
|
|
105
|
+
return buildSteps(slide).length;
|
|
106
|
+
}
|
|
107
|
+
function visibleElementIds(slide, buildStep) {
|
|
108
|
+
const visible = /* @__PURE__ */ new Set();
|
|
109
|
+
if (!slide) return visible;
|
|
110
|
+
const steps = buildSteps(slide);
|
|
111
|
+
const stepOfElement = /* @__PURE__ */ new Map();
|
|
112
|
+
steps.forEach((step, i) => {
|
|
113
|
+
for (const b of step.builds) stepOfElement.set(b.element.id, i + 1);
|
|
114
|
+
});
|
|
115
|
+
for (const element of slide.elements) {
|
|
116
|
+
const revealStep = stepOfElement.get(element.id);
|
|
117
|
+
if (revealStep === void 0) {
|
|
118
|
+
visible.add(element.id);
|
|
119
|
+
} else if (buildStep >= revealStep) {
|
|
120
|
+
visible.add(element.id);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return visible;
|
|
124
|
+
}
|
|
125
|
+
function buildsForStep(slide, buildStep) {
|
|
126
|
+
const steps = buildSteps(slide);
|
|
127
|
+
const step = steps[buildStep - 1];
|
|
128
|
+
return step ? step.builds : [];
|
|
129
|
+
}
|
|
130
|
+
function stepDelays(builds) {
|
|
131
|
+
const delays = /* @__PURE__ */ new Map();
|
|
132
|
+
const lead = builds[0];
|
|
133
|
+
if (!lead) return delays;
|
|
134
|
+
const leadDelay = lead.animation.delay ?? 0;
|
|
135
|
+
const leadDuration = lead.animation.duration ?? DEFAULT_BUILD_DURATION;
|
|
136
|
+
delays.set(lead.element.id, leadDelay);
|
|
137
|
+
for (let i = 1; i < builds.length; i++) {
|
|
138
|
+
const b = builds[i];
|
|
139
|
+
const own = b.animation.delay ?? 0;
|
|
140
|
+
const trigger = b.animation.trigger ?? "on-click";
|
|
141
|
+
const base = trigger === "after-prev" ? leadDelay + leadDuration : leadDelay;
|
|
142
|
+
delays.set(b.element.id, base + own);
|
|
143
|
+
}
|
|
144
|
+
return delays;
|
|
145
|
+
}
|
|
73
146
|
function TextElementRenderer({
|
|
74
147
|
element,
|
|
75
148
|
theme,
|
|
@@ -238,12 +311,94 @@ function renderShape(el, s) {
|
|
|
238
311
|
return null;
|
|
239
312
|
}
|
|
240
313
|
}
|
|
314
|
+
|
|
315
|
+
// src/components/Slide/builds-style.ts
|
|
316
|
+
var DEFAULT_BUILD_DURATION2 = 500;
|
|
317
|
+
var EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
318
|
+
function buildEnterStyle(animation, effectiveDelay) {
|
|
319
|
+
const duration = animation.duration ?? DEFAULT_BUILD_DURATION2;
|
|
320
|
+
const dir = animation.direction ?? "left";
|
|
321
|
+
let name;
|
|
322
|
+
switch (animation.effect) {
|
|
323
|
+
case "fade":
|
|
324
|
+
name = "fs-build-fade";
|
|
325
|
+
break;
|
|
326
|
+
case "zoom":
|
|
327
|
+
name = "fs-build-zoom";
|
|
328
|
+
break;
|
|
329
|
+
case "fly-in":
|
|
330
|
+
name = `fs-build-fly-${dir}`;
|
|
331
|
+
break;
|
|
332
|
+
case "wipe":
|
|
333
|
+
name = `fs-build-wipe-${dir}`;
|
|
334
|
+
break;
|
|
335
|
+
default:
|
|
336
|
+
name = "fs-build-fade";
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
animationName: name,
|
|
340
|
+
animationDuration: `${duration}ms`,
|
|
341
|
+
animationDelay: `${effectiveDelay}ms`,
|
|
342
|
+
animationTimingFunction: EASE,
|
|
343
|
+
animationFillMode: "both"
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
var BUILD_KEYFRAMES = `
|
|
347
|
+
@media (prefers-reduced-motion: reduce) {
|
|
348
|
+
.fs-build-enter { animation: none !important; }
|
|
349
|
+
}
|
|
350
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
351
|
+
@keyframes fs-build-fade {
|
|
352
|
+
from { opacity: 0; }
|
|
353
|
+
to { opacity: 1; }
|
|
354
|
+
}
|
|
355
|
+
@keyframes fs-build-zoom {
|
|
356
|
+
from { opacity: 0; transform: scale(0.8); }
|
|
357
|
+
to { opacity: 1; transform: scale(1); }
|
|
358
|
+
}
|
|
359
|
+
@keyframes fs-build-fly-left {
|
|
360
|
+
from { opacity: 0; transform: translateX(-24%); }
|
|
361
|
+
to { opacity: 1; transform: translateX(0); }
|
|
362
|
+
}
|
|
363
|
+
@keyframes fs-build-fly-right {
|
|
364
|
+
from { opacity: 0; transform: translateX(24%); }
|
|
365
|
+
to { opacity: 1; transform: translateX(0); }
|
|
366
|
+
}
|
|
367
|
+
@keyframes fs-build-fly-up {
|
|
368
|
+
from { opacity: 0; transform: translateY(24%); }
|
|
369
|
+
to { opacity: 1; transform: translateY(0); }
|
|
370
|
+
}
|
|
371
|
+
@keyframes fs-build-fly-down {
|
|
372
|
+
from { opacity: 0; transform: translateY(-24%); }
|
|
373
|
+
to { opacity: 1; transform: translateY(0); }
|
|
374
|
+
}
|
|
375
|
+
/* wipe: clip-path inset reveals from the named edge toward the opposite one.
|
|
376
|
+
inset(top right bottom left) \u2014 start fully clipped on the far side. */
|
|
377
|
+
@keyframes fs-build-wipe-left {
|
|
378
|
+
from { clip-path: inset(0 100% 0 0); }
|
|
379
|
+
to { clip-path: inset(0 0 0 0); }
|
|
380
|
+
}
|
|
381
|
+
@keyframes fs-build-wipe-right {
|
|
382
|
+
from { clip-path: inset(0 0 0 100%); }
|
|
383
|
+
to { clip-path: inset(0 0 0 0); }
|
|
384
|
+
}
|
|
385
|
+
@keyframes fs-build-wipe-up {
|
|
386
|
+
from { clip-path: inset(100% 0 0 0); }
|
|
387
|
+
to { clip-path: inset(0 0 0 0); }
|
|
388
|
+
}
|
|
389
|
+
@keyframes fs-build-wipe-down {
|
|
390
|
+
from { clip-path: inset(0 0 100% 0); }
|
|
391
|
+
to { clip-path: inset(0 0 0 0); }
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
`;
|
|
241
395
|
function Slide({
|
|
242
396
|
slide,
|
|
243
397
|
theme,
|
|
244
398
|
width,
|
|
245
399
|
aspectRatio,
|
|
246
400
|
editing = false,
|
|
401
|
+
buildStep,
|
|
247
402
|
onElementContentChange,
|
|
248
403
|
onElementSelect,
|
|
249
404
|
selectedElementId,
|
|
@@ -287,7 +442,21 @@ function Slide({
|
|
|
287
442
|
}),
|
|
288
443
|
[t, effectiveBg, slideWidthPx]
|
|
289
444
|
);
|
|
290
|
-
|
|
445
|
+
const buildInfo = useMemo(() => {
|
|
446
|
+
if (editing) return null;
|
|
447
|
+
const steps = buildSteps(slide);
|
|
448
|
+
if (steps.length === 0) return null;
|
|
449
|
+
const revealStep = /* @__PURE__ */ new Map();
|
|
450
|
+
steps.forEach((step, i) => {
|
|
451
|
+
for (const b of step.builds) revealStep.set(b.element.id, i + 1);
|
|
452
|
+
});
|
|
453
|
+
const driven = buildStep !== void 0;
|
|
454
|
+
const currentStep = driven ? buildStep : steps.length;
|
|
455
|
+
const firing = driven ? steps[currentStep - 1] : void 0;
|
|
456
|
+
const delays = firing ? stepDelays(firing.builds) : /* @__PURE__ */ new Map();
|
|
457
|
+
return { revealStep, currentStep, delays };
|
|
458
|
+
}, [editing, slide, buildStep]);
|
|
459
|
+
return /* @__PURE__ */ jsx(SlideContext.Provider, { value: slideContext, children: /* @__PURE__ */ jsxs(
|
|
291
460
|
"div",
|
|
292
461
|
{
|
|
293
462
|
ref,
|
|
@@ -305,23 +474,45 @@ function Slide({
|
|
|
305
474
|
onClick: (e) => {
|
|
306
475
|
if (e.target === e.currentTarget && onElementSelect) onElementSelect(null);
|
|
307
476
|
},
|
|
308
|
-
children:
|
|
309
|
-
|
|
310
|
-
{
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
477
|
+
children: [
|
|
478
|
+
buildInfo && /* @__PURE__ */ jsx("style", { children: BUILD_KEYFRAMES }),
|
|
479
|
+
orderedElements(slide.elements).map((element) => {
|
|
480
|
+
let buildHidden = false;
|
|
481
|
+
let buildAnimation;
|
|
482
|
+
let buildDelay = 0;
|
|
483
|
+
if (buildInfo) {
|
|
484
|
+
const step = buildInfo.revealStep.get(element.id);
|
|
485
|
+
if (step !== void 0) {
|
|
486
|
+
if (buildInfo.currentStep < step) {
|
|
487
|
+
buildHidden = true;
|
|
488
|
+
} else if (buildInfo.currentStep === step && element.animation) {
|
|
489
|
+
buildAnimation = element.animation;
|
|
490
|
+
buildDelay = buildInfo.delays.get(element.id) ?? 0;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (buildHidden) return null;
|
|
495
|
+
return /* @__PURE__ */ jsx(
|
|
496
|
+
SlideElementHost,
|
|
497
|
+
{
|
|
498
|
+
element,
|
|
499
|
+
theme: t,
|
|
500
|
+
slideWidthPx,
|
|
501
|
+
slideHeightPx,
|
|
502
|
+
editing,
|
|
503
|
+
selected: selectedElementId === element.id,
|
|
504
|
+
onContentChange: onElementContentChange,
|
|
505
|
+
onSelect: onElementSelect,
|
|
506
|
+
onMove: onElementMove,
|
|
507
|
+
onResize: onElementResize,
|
|
508
|
+
renderElement,
|
|
509
|
+
buildAnimation,
|
|
510
|
+
buildDelay
|
|
511
|
+
},
|
|
512
|
+
element.id
|
|
513
|
+
);
|
|
514
|
+
})
|
|
515
|
+
]
|
|
325
516
|
}
|
|
326
517
|
) });
|
|
327
518
|
}
|
|
@@ -338,7 +529,9 @@ function SlideElementHost({
|
|
|
338
529
|
onSelect,
|
|
339
530
|
onMove,
|
|
340
531
|
onResize,
|
|
341
|
-
renderElement
|
|
532
|
+
renderElement,
|
|
533
|
+
buildAnimation,
|
|
534
|
+
buildDelay = 0
|
|
342
535
|
}) {
|
|
343
536
|
const dragRef = useRef(null);
|
|
344
537
|
if (element.hidden) return null;
|
|
@@ -407,15 +600,18 @@ function SlideElementHost({
|
|
|
407
600
|
outline: selected ? "2px solid #8b5cf6" : void 0,
|
|
408
601
|
outlineOffset: selected ? 2 : void 0,
|
|
409
602
|
cursor: canMove ? "move" : interactive ? "pointer" : "default",
|
|
410
|
-
touchAction: canMove ? "none" : void 0
|
|
603
|
+
touchAction: canMove ? "none" : void 0,
|
|
604
|
+
...buildAnimation ? buildEnterStyle(buildAnimation, buildDelay) : null
|
|
411
605
|
};
|
|
412
606
|
const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange }) ?? renderElement?.(element, slideWidthPx);
|
|
413
607
|
return /* @__PURE__ */ jsxs(
|
|
414
608
|
"div",
|
|
415
609
|
{
|
|
610
|
+
className: buildAnimation ? "fs-build-enter" : void 0,
|
|
416
611
|
style: box,
|
|
417
612
|
"data-fancy-slides-element": element.id,
|
|
418
613
|
"data-fancy-slides-element-type": element.type,
|
|
614
|
+
"data-fancy-slides-build": buildAnimation ? "" : void 0,
|
|
419
615
|
onPointerDown: canMove ? startDrag("move") : void 0,
|
|
420
616
|
onPointerMove: canMove ? onPointerMove : void 0,
|
|
421
617
|
onPointerUp: canMove ? endDrag : void 0,
|
|
@@ -537,6 +733,8 @@ function useSlideKeyboard({
|
|
|
537
733
|
total,
|
|
538
734
|
index,
|
|
539
735
|
goTo,
|
|
736
|
+
onAdvance,
|
|
737
|
+
onRetreat,
|
|
540
738
|
onExit,
|
|
541
739
|
onBlank,
|
|
542
740
|
onFullscreen,
|
|
@@ -553,13 +751,15 @@ function useSlideKeyboard({
|
|
|
553
751
|
case "ArrowLeft":
|
|
554
752
|
case "PageUp":
|
|
555
753
|
e.preventDefault();
|
|
556
|
-
if (
|
|
754
|
+
if (onRetreat) onRetreat();
|
|
755
|
+
else if (index > 0) goTo(index - 1);
|
|
557
756
|
return;
|
|
558
757
|
case "ArrowRight":
|
|
559
758
|
case "PageDown":
|
|
560
759
|
case " ":
|
|
561
760
|
e.preventDefault();
|
|
562
|
-
if (
|
|
761
|
+
if (onAdvance) onAdvance();
|
|
762
|
+
else if (index < total - 1) goTo(index + 1);
|
|
563
763
|
return;
|
|
564
764
|
case "Home":
|
|
565
765
|
e.preventDefault();
|
|
@@ -601,7 +801,7 @@ function useSlideKeyboard({
|
|
|
601
801
|
};
|
|
602
802
|
window.addEventListener("keydown", handler);
|
|
603
803
|
return () => window.removeEventListener("keydown", handler);
|
|
604
|
-
}, [enabled, index, total, goTo, onExit, onBlank, onFullscreen]);
|
|
804
|
+
}, [enabled, index, total, goTo, onAdvance, onRetreat, onExit, onBlank, onFullscreen]);
|
|
605
805
|
}
|
|
606
806
|
function SlideViewer({
|
|
607
807
|
deck,
|
|
@@ -629,13 +829,34 @@ function SlideViewer({
|
|
|
629
829
|
const containerRef = useRef(null);
|
|
630
830
|
const prevIndexRef = useRef(index);
|
|
631
831
|
const forward = index >= prevIndexRef.current;
|
|
832
|
+
const slide = deck.slides[index];
|
|
833
|
+
const totalSteps = totalBuildSteps(slide);
|
|
834
|
+
const [buildStep, setBuildStep] = useState(0);
|
|
835
|
+
const nextFreshRef = useRef(false);
|
|
632
836
|
useEffect(() => {
|
|
837
|
+
if (index === prevIndexRef.current) return;
|
|
633
838
|
prevIndexRef.current = index;
|
|
634
|
-
|
|
839
|
+
const fresh = nextFreshRef.current;
|
|
840
|
+
nextFreshRef.current = false;
|
|
841
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
842
|
+
}, [index, deck.slides]);
|
|
843
|
+
const advance = useCallback(() => {
|
|
844
|
+
if (buildStep < totalSteps) {
|
|
845
|
+
setBuildStep((s) => s + 1);
|
|
846
|
+
} else if (index < deck.slides.length - 1) {
|
|
847
|
+
nextFreshRef.current = true;
|
|
848
|
+
goTo(index + 1);
|
|
849
|
+
}
|
|
850
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
851
|
+
const retreat = useCallback(() => {
|
|
852
|
+
if (index > 0) goTo(index - 1);
|
|
853
|
+
}, [index, goTo]);
|
|
635
854
|
useSlideKeyboard({
|
|
636
855
|
total: deck.slides.length,
|
|
637
856
|
index,
|
|
638
857
|
goTo,
|
|
858
|
+
onAdvance: advance,
|
|
859
|
+
onRetreat: retreat,
|
|
639
860
|
onExit,
|
|
640
861
|
onBlank: () => setBlanked((b) => !b),
|
|
641
862
|
onFullscreen: () => {
|
|
@@ -648,11 +869,15 @@ function SlideViewer({
|
|
|
648
869
|
useEffect(() => {
|
|
649
870
|
if (!autoAdvanceMs || deck.slides.length <= 1) return;
|
|
650
871
|
const t = setTimeout(() => {
|
|
651
|
-
|
|
872
|
+
if (buildStep < totalSteps) {
|
|
873
|
+
setBuildStep((s) => s + 1);
|
|
874
|
+
} else {
|
|
875
|
+
nextFreshRef.current = true;
|
|
876
|
+
goTo(index + 1 < deck.slides.length ? index + 1 : 0);
|
|
877
|
+
}
|
|
652
878
|
}, autoAdvanceMs);
|
|
653
879
|
return () => clearTimeout(t);
|
|
654
|
-
}, [autoAdvanceMs, index, deck.slides.length, goTo]);
|
|
655
|
-
const slide = deck.slides[index];
|
|
880
|
+
}, [autoAdvanceMs, index, deck.slides.length, goTo, buildStep, totalSteps]);
|
|
656
881
|
const theme = resolveTheme(deck.theme);
|
|
657
882
|
const aspectRatio = theme.aspectRatio ?? 16 / 9;
|
|
658
883
|
const transition = slide?.transition ?? theme.defaultTransition;
|
|
@@ -674,6 +899,11 @@ function SlideViewer({
|
|
|
674
899
|
},
|
|
675
900
|
tabIndex: 0,
|
|
676
901
|
"data-fancy-slides-viewer": deck.id,
|
|
902
|
+
"data-fancy-slides-build-step": buildStep,
|
|
903
|
+
onClick: () => {
|
|
904
|
+
if (blanked) return;
|
|
905
|
+
advance();
|
|
906
|
+
},
|
|
677
907
|
children: [
|
|
678
908
|
/* @__PURE__ */ jsx("style", { children: TRANSITION_KEYFRAMES }),
|
|
679
909
|
!blanked && slide && /* @__PURE__ */ jsx(
|
|
@@ -687,7 +917,7 @@ function SlideViewer({
|
|
|
687
917
|
["--fs-ratio"]: aspectRatio.toString(),
|
|
688
918
|
boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
|
|
689
919
|
},
|
|
690
|
-
children: /* @__PURE__ */ jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement }) }, index)
|
|
920
|
+
children: /* @__PURE__ */ jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsx(Slide, { slide, theme, buildStep, renderElement }) }, index)
|
|
691
921
|
}
|
|
692
922
|
),
|
|
693
923
|
!hideChrome && !blanked && /* @__PURE__ */ jsxs(
|
|
@@ -718,7 +948,7 @@ function SlideViewer({
|
|
|
718
948
|
);
|
|
719
949
|
}
|
|
720
950
|
var DEFAULT_DURATION = 400;
|
|
721
|
-
var
|
|
951
|
+
var EASE2 = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
722
952
|
function transitionEnterStyle(transition, forward) {
|
|
723
953
|
const kind = transition?.kind ?? "none";
|
|
724
954
|
if (kind === "none") return { width: "100%", height: "100%" };
|
|
@@ -744,7 +974,7 @@ function transitionEnterStyle(transition, forward) {
|
|
|
744
974
|
height: "100%",
|
|
745
975
|
animationName: name,
|
|
746
976
|
animationDuration: `${duration}ms`,
|
|
747
|
-
animationTimingFunction:
|
|
977
|
+
animationTimingFunction: EASE2,
|
|
748
978
|
animationFillMode: "both"
|
|
749
979
|
};
|
|
750
980
|
}
|
|
@@ -800,14 +1030,38 @@ function PresenterView({
|
|
|
800
1030
|
},
|
|
801
1031
|
[deck.slides.length, isControlled, onIndexChange]
|
|
802
1032
|
);
|
|
1033
|
+
const slide = deck.slides[index];
|
|
1034
|
+
const totalSteps = totalBuildSteps(slide);
|
|
1035
|
+
const [buildStep, setBuildStep] = useState(0);
|
|
1036
|
+
const prevIndexRef = useRef(index);
|
|
1037
|
+
const nextFreshRef = useRef(false);
|
|
1038
|
+
useEffect(() => {
|
|
1039
|
+
if (index === prevIndexRef.current) return;
|
|
1040
|
+
prevIndexRef.current = index;
|
|
1041
|
+
const fresh = nextFreshRef.current;
|
|
1042
|
+
nextFreshRef.current = false;
|
|
1043
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
1044
|
+
}, [index, deck.slides]);
|
|
1045
|
+
const advance = useCallback(() => {
|
|
1046
|
+
if (buildStep < totalSteps) {
|
|
1047
|
+
setBuildStep((s) => s + 1);
|
|
1048
|
+
} else if (index < deck.slides.length - 1) {
|
|
1049
|
+
nextFreshRef.current = true;
|
|
1050
|
+
goTo(index + 1);
|
|
1051
|
+
}
|
|
1052
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
1053
|
+
const retreat = useCallback(() => {
|
|
1054
|
+
if (index > 0) goTo(index - 1);
|
|
1055
|
+
}, [index, goTo]);
|
|
803
1056
|
useSlideKeyboard({
|
|
804
1057
|
total: deck.slides.length,
|
|
805
1058
|
index,
|
|
806
1059
|
goTo,
|
|
1060
|
+
onAdvance: advance,
|
|
1061
|
+
onRetreat: retreat,
|
|
807
1062
|
onExit
|
|
808
1063
|
});
|
|
809
1064
|
const theme = resolveTheme(deck.theme);
|
|
810
|
-
const slide = deck.slides[index];
|
|
811
1065
|
const nextSlide = deck.slides[index + 1];
|
|
812
1066
|
const [now, setNow] = useState(() => Date.now());
|
|
813
1067
|
useEffect(() => {
|
|
@@ -854,7 +1108,7 @@ function PresenterView({
|
|
|
854
1108
|
borderRadius: 8,
|
|
855
1109
|
overflow: "hidden"
|
|
856
1110
|
},
|
|
857
|
-
children: slide ? /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement }) : null
|
|
1111
|
+
children: slide ? /* @__PURE__ */ jsx(Slide, { slide, theme, buildStep, renderElement }) : null
|
|
858
1112
|
}
|
|
859
1113
|
)
|
|
860
1114
|
}
|
|
@@ -958,8 +1212,8 @@ function PresenterView({
|
|
|
958
1212
|
/* @__PURE__ */ jsx(StatusChip, { label: "Elapsed", children: formatElapsed(now - startedAtRef) }),
|
|
959
1213
|
/* @__PURE__ */ jsx(StatusChip, { label: "Clock", children: formatClock(now) }),
|
|
960
1214
|
/* @__PURE__ */ jsxs("div", { style: { marginLeft: "auto", display: "flex", gap: 8 }, children: [
|
|
961
|
-
/* @__PURE__ */ jsx(NavButton, { onClick:
|
|
962
|
-
/* @__PURE__ */ jsx(NavButton, { onClick:
|
|
1215
|
+
/* @__PURE__ */ jsx(NavButton, { onClick: retreat, disabled: index === 0, children: "\u2190 Prev" }),
|
|
1216
|
+
/* @__PURE__ */ jsx(NavButton, { onClick: advance, disabled: index >= deck.slides.length - 1 && buildStep >= totalSteps, children: "Next \u2192" })
|
|
963
1217
|
] })
|
|
964
1218
|
]
|
|
965
1219
|
}
|
|
@@ -1142,6 +1396,7 @@ function useDeckState({ value, onChange, onOp }) {
|
|
|
1142
1396
|
updateElement: (slideIdArg, elementIdArg, patch) => apply({ kind: "element_update", slideId: slideIdArg, elementId: elementIdArg, patch }),
|
|
1143
1397
|
moveElement: (slideIdArg, elementIdArg, x, y) => apply({ kind: "element_move", slideId: slideIdArg, elementId: elementIdArg, x, y }),
|
|
1144
1398
|
resizeElement: (slideIdArg, elementIdArg, w, h) => apply({ kind: "element_resize", slideId: slideIdArg, elementId: elementIdArg, w, h }),
|
|
1399
|
+
setAnimation: (slideIdArg, elementIdArg, animation) => apply({ kind: "element_set_animation", slideId: slideIdArg, elementId: elementIdArg, animation }),
|
|
1145
1400
|
getSlide: (id) => value.slides.find((s) => s.id === id),
|
|
1146
1401
|
getElement: (slideIdArg, elementIdArg) => value.slides.find((s) => s.id === slideIdArg)?.elements.find((e) => e.id === elementIdArg)
|
|
1147
1402
|
};
|
|
@@ -1209,6 +1464,23 @@ function reduce(deck, op) {
|
|
|
1209
1464
|
(s) => s.id === op.slideId ? { ...s, elements: s.elements.map((e) => e.id === op.elementId ? { ...e, w: op.w, h: op.h } : e) } : s
|
|
1210
1465
|
)
|
|
1211
1466
|
};
|
|
1467
|
+
case "element_set_animation":
|
|
1468
|
+
return {
|
|
1469
|
+
...deck,
|
|
1470
|
+
slides: deck.slides.map(
|
|
1471
|
+
(s) => s.id === op.slideId ? {
|
|
1472
|
+
...s,
|
|
1473
|
+
elements: s.elements.map((e) => {
|
|
1474
|
+
if (e.id !== op.elementId) return e;
|
|
1475
|
+
if (op.animation === void 0) {
|
|
1476
|
+
const { animation: _drop, ...rest } = e;
|
|
1477
|
+
return rest;
|
|
1478
|
+
}
|
|
1479
|
+
return { ...e, animation: op.animation };
|
|
1480
|
+
})
|
|
1481
|
+
} : s
|
|
1482
|
+
)
|
|
1483
|
+
};
|
|
1212
1484
|
}
|
|
1213
1485
|
}
|
|
1214
1486
|
|
|
@@ -1550,10 +1822,10 @@ function EditorToolbar({
|
|
|
1550
1822
|
/* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsx(Tooltip, { content: "Present (F)", children: /* @__PURE__ */ jsx(Action, { color: "violet", size: "sm", icon: "play", onClick: onPresent, children: "Present" }) }) })
|
|
1551
1823
|
] });
|
|
1552
1824
|
}
|
|
1553
|
-
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
|
|
1825
|
+
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }) {
|
|
1554
1826
|
if (!element) {
|
|
1555
1827
|
if (slide) {
|
|
1556
|
-
return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
|
|
1828
|
+
return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetElementAnimation });
|
|
1557
1829
|
}
|
|
1558
1830
|
return /* @__PURE__ */ jsxs("div", { className: "fs-inspector flex h-full flex-col border-l border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-900", children: [
|
|
1559
1831
|
/* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
|
|
@@ -1578,10 +1850,12 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1578
1850
|
/* @__PURE__ */ jsxs(Tabs.List, { children: [
|
|
1579
1851
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "style", children: "Style" }),
|
|
1580
1852
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "layout", children: "Layout" }),
|
|
1853
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "build", children: "Build" }),
|
|
1581
1854
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "advanced", children: "Advanced" })
|
|
1582
1855
|
] }),
|
|
1583
1856
|
/* @__PURE__ */ jsxs(Tabs.Panels, { children: [
|
|
1584
1857
|
/* @__PURE__ */ jsx(Tabs.Panel, { value: "style", children: /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(StyleSection, { element, onPatch }) }) }),
|
|
1858
|
+
/* @__PURE__ */ jsx(Tabs.Panel, { value: "build", children: /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(AnimateSection, { animation: element.animation, onSetAnimation }) }) }),
|
|
1585
1859
|
/* @__PURE__ */ jsx(Tabs.Panel, { value: "layout", children: /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(LayoutSection, { element, onPatch }) }) }),
|
|
1586
1860
|
/* @__PURE__ */ jsx(Tabs.Panel, { value: "advanced", children: /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(AdvancedSection, { element, onPatch }) }) })
|
|
1587
1861
|
] })
|
|
@@ -1591,7 +1865,8 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1591
1865
|
function SlideSettings({
|
|
1592
1866
|
slide,
|
|
1593
1867
|
onSetTransition,
|
|
1594
|
-
onSetBackground
|
|
1868
|
+
onSetBackground,
|
|
1869
|
+
onSetElementAnimation
|
|
1595
1870
|
}) {
|
|
1596
1871
|
const transition = slide.transition;
|
|
1597
1872
|
const kind = transition?.kind ?? "none";
|
|
@@ -1658,10 +1933,54 @@ function SlideSettings({
|
|
|
1658
1933
|
onChange: (c) => onSetBackground({ ...slide.background, color: c })
|
|
1659
1934
|
}
|
|
1660
1935
|
) })
|
|
1661
|
-
] }) })
|
|
1936
|
+
] }) }),
|
|
1937
|
+
onSetElementAnimation && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(BuildOrderList, { slide, onSetElementAnimation }) })
|
|
1662
1938
|
] })
|
|
1663
1939
|
] });
|
|
1664
1940
|
}
|
|
1941
|
+
function BuildOrderList({
|
|
1942
|
+
slide,
|
|
1943
|
+
onSetElementAnimation
|
|
1944
|
+
}) {
|
|
1945
|
+
const builds = collectBuilds(slide);
|
|
1946
|
+
const move = (from, to) => {
|
|
1947
|
+
if (to < 0 || to >= builds.length) return;
|
|
1948
|
+
const reordered = [...builds];
|
|
1949
|
+
const [item] = reordered.splice(from, 1);
|
|
1950
|
+
reordered.splice(to, 0, item);
|
|
1951
|
+
reordered.forEach((b, i) => {
|
|
1952
|
+
if ((b.animation.order ?? 0) !== i) {
|
|
1953
|
+
onSetElementAnimation(b.element.id, { ...b.animation, order: i });
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
};
|
|
1957
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1958
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Build order" }),
|
|
1959
|
+
builds.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", className: "!text-zinc-500", children: "No animated elements yet. Select an element and add a build under its Build tab." }) : builds.map((b, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1960
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", className: "!font-mono !text-zinc-400 w-5", children: [
|
|
1961
|
+
i + 1,
|
|
1962
|
+
"."
|
|
1963
|
+
] }),
|
|
1964
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1965
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", className: "truncate", children: buildLabel(b.element) }),
|
|
1966
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", className: "!font-mono !text-zinc-400", children: [
|
|
1967
|
+
b.animation.effect,
|
|
1968
|
+
" \xB7 ",
|
|
1969
|
+
b.animation.trigger ?? "on-click"
|
|
1970
|
+
] })
|
|
1971
|
+
] }),
|
|
1972
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "chevron-up", onClick: () => move(i, i - 1), disabled: i === 0, "aria-label": "Move earlier" }),
|
|
1973
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "chevron-down", onClick: () => move(i, i + 1), disabled: i === builds.length - 1, "aria-label": "Move later" })
|
|
1974
|
+
] }, b.element.id))
|
|
1975
|
+
] });
|
|
1976
|
+
}
|
|
1977
|
+
function buildLabel(element) {
|
|
1978
|
+
if (element.type === "text") {
|
|
1979
|
+
const text = element.content.replace(/\s+/g, " ").trim();
|
|
1980
|
+
return text ? text.length > 28 ? `${text.slice(0, 28)}\u2026` : text : "Text";
|
|
1981
|
+
}
|
|
1982
|
+
return `${element.type} #${element.id.slice(-6)}`;
|
|
1983
|
+
}
|
|
1665
1984
|
function LayoutSection({ element, onPatch }) {
|
|
1666
1985
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1667
1986
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
@@ -1683,6 +2002,100 @@ function AdvancedSection({ element, onPatch }) {
|
|
|
1683
2002
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(Action, { size: "sm", variant: element.hidden ? "default" : "ghost", onClick: () => onPatch({ hidden: !element.hidden }), children: element.hidden ? "Hidden \u2014 show" : "Hide on slide" }) })
|
|
1684
2003
|
] });
|
|
1685
2004
|
}
|
|
2005
|
+
var NO_ANIMATION = "none";
|
|
2006
|
+
function AnimateSection({
|
|
2007
|
+
animation,
|
|
2008
|
+
onSetAnimation
|
|
2009
|
+
}) {
|
|
2010
|
+
if (!onSetAnimation) {
|
|
2011
|
+
return /* @__PURE__ */ jsx(Text, { size: "sm", className: "!text-zinc-500", children: "Build animations aren't wired up in this editor." });
|
|
2012
|
+
}
|
|
2013
|
+
const effect = animation?.effect;
|
|
2014
|
+
const set = (next) => {
|
|
2015
|
+
const base = animation ?? { effect: "fade" };
|
|
2016
|
+
onSetAnimation({ ...base, ...next });
|
|
2017
|
+
};
|
|
2018
|
+
const showDirection = effect === "fly-in" || effect === "wipe";
|
|
2019
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2020
|
+
/* @__PURE__ */ jsx(
|
|
2021
|
+
Select,
|
|
2022
|
+
{
|
|
2023
|
+
label: "Effect",
|
|
2024
|
+
list: [
|
|
2025
|
+
{ value: NO_ANIMATION, label: "None" },
|
|
2026
|
+
{ value: "fade", label: "Fade" },
|
|
2027
|
+
{ value: "fly-in", label: "Fly in" },
|
|
2028
|
+
{ value: "zoom", label: "Zoom" },
|
|
2029
|
+
{ value: "wipe", label: "Wipe" }
|
|
2030
|
+
],
|
|
2031
|
+
value: effect ?? NO_ANIMATION,
|
|
2032
|
+
onValueChange: (v) => {
|
|
2033
|
+
if (v === NO_ANIMATION) onSetAnimation(void 0);
|
|
2034
|
+
else set({ effect: v });
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
),
|
|
2038
|
+
effect && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2039
|
+
/* @__PURE__ */ jsx(
|
|
2040
|
+
Select,
|
|
2041
|
+
{
|
|
2042
|
+
label: "Trigger",
|
|
2043
|
+
list: [
|
|
2044
|
+
{ value: "on-click", label: "On click" },
|
|
2045
|
+
{ value: "with-prev", label: "With previous" },
|
|
2046
|
+
{ value: "after-prev", label: "After previous" }
|
|
2047
|
+
],
|
|
2048
|
+
value: animation?.trigger ?? "on-click",
|
|
2049
|
+
onValueChange: (v) => set({ trigger: v })
|
|
2050
|
+
}
|
|
2051
|
+
),
|
|
2052
|
+
showDirection && /* @__PURE__ */ jsx(
|
|
2053
|
+
Select,
|
|
2054
|
+
{
|
|
2055
|
+
label: "Direction",
|
|
2056
|
+
list: [
|
|
2057
|
+
{ value: "left", label: "From left" },
|
|
2058
|
+
{ value: "right", label: "From right" },
|
|
2059
|
+
{ value: "up", label: "From bottom" },
|
|
2060
|
+
{ value: "down", label: "From top" }
|
|
2061
|
+
],
|
|
2062
|
+
value: animation?.direction ?? "left",
|
|
2063
|
+
onValueChange: (v) => set({ direction: v })
|
|
2064
|
+
}
|
|
2065
|
+
),
|
|
2066
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2067
|
+
/* @__PURE__ */ jsx(
|
|
2068
|
+
Input,
|
|
2069
|
+
{
|
|
2070
|
+
label: "Duration (ms)",
|
|
2071
|
+
type: "number",
|
|
2072
|
+
value: String(animation?.duration ?? 500),
|
|
2073
|
+
onChange: (e) => set({ duration: parseInt(e.target.value, 10) || 500 })
|
|
2074
|
+
}
|
|
2075
|
+
),
|
|
2076
|
+
/* @__PURE__ */ jsx(
|
|
2077
|
+
Input,
|
|
2078
|
+
{
|
|
2079
|
+
label: "Delay (ms)",
|
|
2080
|
+
type: "number",
|
|
2081
|
+
value: String(animation?.delay ?? 0),
|
|
2082
|
+
onChange: (e) => set({ delay: parseInt(e.target.value, 10) || 0 })
|
|
2083
|
+
}
|
|
2084
|
+
)
|
|
2085
|
+
] }),
|
|
2086
|
+
/* @__PURE__ */ jsx(
|
|
2087
|
+
Input,
|
|
2088
|
+
{
|
|
2089
|
+
label: "Order",
|
|
2090
|
+
type: "number",
|
|
2091
|
+
value: String(animation?.order ?? 0),
|
|
2092
|
+
onChange: (e) => set({ order: parseInt(e.target.value, 10) || 0 })
|
|
2093
|
+
}
|
|
2094
|
+
),
|
|
2095
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", className: "!text-zinc-500", children: `Builds reveal in ascending order. "On click" starts a new step; "with previous" plays alongside the step's lead; "after previous" follows it. Honors prefers-reduced-motion.` })
|
|
2096
|
+
] })
|
|
2097
|
+
] });
|
|
2098
|
+
}
|
|
1686
2099
|
function StyleSection({ element, onPatch }) {
|
|
1687
2100
|
switch (element.type) {
|
|
1688
2101
|
case "text":
|
|
@@ -2358,7 +2771,9 @@ function DeckEditor({
|
|
|
2358
2771
|
},
|
|
2359
2772
|
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
|
|
2360
2773
|
onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
|
|
2361
|
-
onSetBackground: (background) => slide && ops.setBackground(slide.id, background)
|
|
2774
|
+
onSetBackground: (background) => slide && ops.setBackground(slide.id, background),
|
|
2775
|
+
onSetAnimation: (animation) => slide && elementIdSelected && ops.setAnimation(slide.id, elementIdSelected, animation),
|
|
2776
|
+
onSetElementAnimation: (eid, animation) => slide && ops.setAnimation(slide.id, eid, animation)
|
|
2362
2777
|
}
|
|
2363
2778
|
) })
|
|
2364
2779
|
] }),
|
|
@@ -2368,6 +2783,6 @@ function DeckEditor({
|
|
|
2368
2783
|
);
|
|
2369
2784
|
}
|
|
2370
2785
|
|
|
2371
|
-
export { DeckEditor, EditorToolbar, ElementInspector, ImageElementRenderer, PresenterView, ShapeElementRenderer, Slide, SlideRail, SlideThumbnail, SlideViewer, SpeakerNotes, TextElementRenderer, builtinThemes, chartStarterOption, darkTheme, deckId, defaultTheme, defineTheme, elementId, nextId, reduce as reduceDeck, resolveTheme, slideId, useDeckState, useSlideKeyboard, vividTheme };
|
|
2786
|
+
export { DeckEditor, EditorToolbar, ElementInspector, ImageElementRenderer, PresenterView, ShapeElementRenderer, Slide, SlideRail, SlideThumbnail, SlideViewer, SpeakerNotes, TextElementRenderer, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, nextId, reduce as reduceDeck, resolveTheme, slideId, stepDelays, totalBuildSteps, useDeckState, useSlideKeyboard, visibleElementIds, vividTheme };
|
|
2372
2787
|
//# sourceMappingURL=index.js.map
|
|
2373
2788
|
//# sourceMappingURL=index.js.map
|