@particle-academy/fancy-slides 0.2.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 +861 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -9
- package/dist/index.d.ts +105 -9
- package/dist/index.js +856 -80
- 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.cjs
CHANGED
|
@@ -72,6 +72,79 @@ function resolveTheme(theme) {
|
|
|
72
72
|
function cn(...parts) {
|
|
73
73
|
return parts.filter(Boolean).join(" ");
|
|
74
74
|
}
|
|
75
|
+
|
|
76
|
+
// src/utils/builds.ts
|
|
77
|
+
var DEFAULT_BUILD_DURATION = 500;
|
|
78
|
+
function collectBuilds(slide) {
|
|
79
|
+
if (!slide) return [];
|
|
80
|
+
const builds = [];
|
|
81
|
+
slide.elements.forEach((element, index) => {
|
|
82
|
+
if (element.animation) {
|
|
83
|
+
builds.push({ element, animation: element.animation, index });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
return builds.sort((a, b) => {
|
|
87
|
+
const ao = a.animation.order ?? 0;
|
|
88
|
+
const bo = b.animation.order ?? 0;
|
|
89
|
+
if (ao !== bo) return ao - bo;
|
|
90
|
+
return a.index - b.index;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function buildSteps(slide) {
|
|
94
|
+
const builds = collectBuilds(slide);
|
|
95
|
+
const steps = [];
|
|
96
|
+
for (const build of builds) {
|
|
97
|
+
const trigger = build.animation.trigger ?? "on-click";
|
|
98
|
+
if (steps.length === 0 || trigger === "on-click") {
|
|
99
|
+
steps.push({ builds: [build] });
|
|
100
|
+
} else {
|
|
101
|
+
steps[steps.length - 1].builds.push(build);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return steps;
|
|
105
|
+
}
|
|
106
|
+
function totalBuildSteps(slide) {
|
|
107
|
+
return buildSteps(slide).length;
|
|
108
|
+
}
|
|
109
|
+
function visibleElementIds(slide, buildStep) {
|
|
110
|
+
const visible = /* @__PURE__ */ new Set();
|
|
111
|
+
if (!slide) return visible;
|
|
112
|
+
const steps = buildSteps(slide);
|
|
113
|
+
const stepOfElement = /* @__PURE__ */ new Map();
|
|
114
|
+
steps.forEach((step, i) => {
|
|
115
|
+
for (const b of step.builds) stepOfElement.set(b.element.id, i + 1);
|
|
116
|
+
});
|
|
117
|
+
for (const element of slide.elements) {
|
|
118
|
+
const revealStep = stepOfElement.get(element.id);
|
|
119
|
+
if (revealStep === void 0) {
|
|
120
|
+
visible.add(element.id);
|
|
121
|
+
} else if (buildStep >= revealStep) {
|
|
122
|
+
visible.add(element.id);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return visible;
|
|
126
|
+
}
|
|
127
|
+
function buildsForStep(slide, buildStep) {
|
|
128
|
+
const steps = buildSteps(slide);
|
|
129
|
+
const step = steps[buildStep - 1];
|
|
130
|
+
return step ? step.builds : [];
|
|
131
|
+
}
|
|
132
|
+
function stepDelays(builds) {
|
|
133
|
+
const delays = /* @__PURE__ */ new Map();
|
|
134
|
+
const lead = builds[0];
|
|
135
|
+
if (!lead) return delays;
|
|
136
|
+
const leadDelay = lead.animation.delay ?? 0;
|
|
137
|
+
const leadDuration = lead.animation.duration ?? DEFAULT_BUILD_DURATION;
|
|
138
|
+
delays.set(lead.element.id, leadDelay);
|
|
139
|
+
for (let i = 1; i < builds.length; i++) {
|
|
140
|
+
const b = builds[i];
|
|
141
|
+
const own = b.animation.delay ?? 0;
|
|
142
|
+
const trigger = b.animation.trigger ?? "on-click";
|
|
143
|
+
const base = trigger === "after-prev" ? leadDelay + leadDuration : leadDelay;
|
|
144
|
+
delays.set(b.element.id, base + own);
|
|
145
|
+
}
|
|
146
|
+
return delays;
|
|
147
|
+
}
|
|
75
148
|
function TextElementRenderer({
|
|
76
149
|
element,
|
|
77
150
|
theme,
|
|
@@ -161,6 +234,22 @@ function weight(w) {
|
|
|
161
234
|
return void 0;
|
|
162
235
|
}
|
|
163
236
|
function ImageElementRenderer({ element }) {
|
|
237
|
+
const crop = element.crop;
|
|
238
|
+
const fit = element.fit ?? "contain";
|
|
239
|
+
if (crop && crop.w > 0 && crop.h > 0) {
|
|
240
|
+
const inner = {
|
|
241
|
+
position: "absolute",
|
|
242
|
+
left: 0,
|
|
243
|
+
top: 0,
|
|
244
|
+
width: `${1 / crop.w * 100}%`,
|
|
245
|
+
height: `${1 / crop.h * 100}%`,
|
|
246
|
+
transform: `translate(${-crop.x / crop.w * 100}%, ${-crop.y / crop.h * 100}%)`,
|
|
247
|
+
transformOrigin: "top left",
|
|
248
|
+
objectFit: fit,
|
|
249
|
+
display: "block"
|
|
250
|
+
};
|
|
251
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", width: "100%", height: "100%", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: element.src, alt: element.alt ?? "", style: inner, draggable: false }) });
|
|
252
|
+
}
|
|
164
253
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
165
254
|
"img",
|
|
166
255
|
{
|
|
@@ -169,7 +258,7 @@ function ImageElementRenderer({ element }) {
|
|
|
169
258
|
style: {
|
|
170
259
|
width: "100%",
|
|
171
260
|
height: "100%",
|
|
172
|
-
objectFit:
|
|
261
|
+
objectFit: fit,
|
|
173
262
|
display: "block"
|
|
174
263
|
},
|
|
175
264
|
draggable: false
|
|
@@ -264,12 +353,94 @@ function relativeLuminance(r, g, b) {
|
|
|
264
353
|
};
|
|
265
354
|
return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
266
355
|
}
|
|
356
|
+
|
|
357
|
+
// src/components/Slide/builds-style.ts
|
|
358
|
+
var DEFAULT_BUILD_DURATION2 = 500;
|
|
359
|
+
var EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
360
|
+
function buildEnterStyle(animation, effectiveDelay) {
|
|
361
|
+
const duration = animation.duration ?? DEFAULT_BUILD_DURATION2;
|
|
362
|
+
const dir = animation.direction ?? "left";
|
|
363
|
+
let name;
|
|
364
|
+
switch (animation.effect) {
|
|
365
|
+
case "fade":
|
|
366
|
+
name = "fs-build-fade";
|
|
367
|
+
break;
|
|
368
|
+
case "zoom":
|
|
369
|
+
name = "fs-build-zoom";
|
|
370
|
+
break;
|
|
371
|
+
case "fly-in":
|
|
372
|
+
name = `fs-build-fly-${dir}`;
|
|
373
|
+
break;
|
|
374
|
+
case "wipe":
|
|
375
|
+
name = `fs-build-wipe-${dir}`;
|
|
376
|
+
break;
|
|
377
|
+
default:
|
|
378
|
+
name = "fs-build-fade";
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
animationName: name,
|
|
382
|
+
animationDuration: `${duration}ms`,
|
|
383
|
+
animationDelay: `${effectiveDelay}ms`,
|
|
384
|
+
animationTimingFunction: EASE,
|
|
385
|
+
animationFillMode: "both"
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
var BUILD_KEYFRAMES = `
|
|
389
|
+
@media (prefers-reduced-motion: reduce) {
|
|
390
|
+
.fs-build-enter { animation: none !important; }
|
|
391
|
+
}
|
|
392
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
393
|
+
@keyframes fs-build-fade {
|
|
394
|
+
from { opacity: 0; }
|
|
395
|
+
to { opacity: 1; }
|
|
396
|
+
}
|
|
397
|
+
@keyframes fs-build-zoom {
|
|
398
|
+
from { opacity: 0; transform: scale(0.8); }
|
|
399
|
+
to { opacity: 1; transform: scale(1); }
|
|
400
|
+
}
|
|
401
|
+
@keyframes fs-build-fly-left {
|
|
402
|
+
from { opacity: 0; transform: translateX(-24%); }
|
|
403
|
+
to { opacity: 1; transform: translateX(0); }
|
|
404
|
+
}
|
|
405
|
+
@keyframes fs-build-fly-right {
|
|
406
|
+
from { opacity: 0; transform: translateX(24%); }
|
|
407
|
+
to { opacity: 1; transform: translateX(0); }
|
|
408
|
+
}
|
|
409
|
+
@keyframes fs-build-fly-up {
|
|
410
|
+
from { opacity: 0; transform: translateY(24%); }
|
|
411
|
+
to { opacity: 1; transform: translateY(0); }
|
|
412
|
+
}
|
|
413
|
+
@keyframes fs-build-fly-down {
|
|
414
|
+
from { opacity: 0; transform: translateY(-24%); }
|
|
415
|
+
to { opacity: 1; transform: translateY(0); }
|
|
416
|
+
}
|
|
417
|
+
/* wipe: clip-path inset reveals from the named edge toward the opposite one.
|
|
418
|
+
inset(top right bottom left) \u2014 start fully clipped on the far side. */
|
|
419
|
+
@keyframes fs-build-wipe-left {
|
|
420
|
+
from { clip-path: inset(0 100% 0 0); }
|
|
421
|
+
to { clip-path: inset(0 0 0 0); }
|
|
422
|
+
}
|
|
423
|
+
@keyframes fs-build-wipe-right {
|
|
424
|
+
from { clip-path: inset(0 0 0 100%); }
|
|
425
|
+
to { clip-path: inset(0 0 0 0); }
|
|
426
|
+
}
|
|
427
|
+
@keyframes fs-build-wipe-up {
|
|
428
|
+
from { clip-path: inset(100% 0 0 0); }
|
|
429
|
+
to { clip-path: inset(0 0 0 0); }
|
|
430
|
+
}
|
|
431
|
+
@keyframes fs-build-wipe-down {
|
|
432
|
+
from { clip-path: inset(0 0 100% 0); }
|
|
433
|
+
to { clip-path: inset(0 0 0 0); }
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
`;
|
|
267
437
|
function Slide({
|
|
268
438
|
slide,
|
|
269
439
|
theme,
|
|
270
440
|
width,
|
|
271
441
|
aspectRatio,
|
|
272
442
|
editing = false,
|
|
443
|
+
buildStep,
|
|
273
444
|
onElementContentChange,
|
|
274
445
|
onElementSelect,
|
|
275
446
|
selectedElementId,
|
|
@@ -313,7 +484,21 @@ function Slide({
|
|
|
313
484
|
}),
|
|
314
485
|
[t, effectiveBg, slideWidthPx]
|
|
315
486
|
);
|
|
316
|
-
|
|
487
|
+
const buildInfo = react.useMemo(() => {
|
|
488
|
+
if (editing) return null;
|
|
489
|
+
const steps = buildSteps(slide);
|
|
490
|
+
if (steps.length === 0) return null;
|
|
491
|
+
const revealStep = /* @__PURE__ */ new Map();
|
|
492
|
+
steps.forEach((step, i) => {
|
|
493
|
+
for (const b of step.builds) revealStep.set(b.element.id, i + 1);
|
|
494
|
+
});
|
|
495
|
+
const driven = buildStep !== void 0;
|
|
496
|
+
const currentStep = driven ? buildStep : steps.length;
|
|
497
|
+
const firing = driven ? steps[currentStep - 1] : void 0;
|
|
498
|
+
const delays = firing ? stepDelays(firing.builds) : /* @__PURE__ */ new Map();
|
|
499
|
+
return { revealStep, currentStep, delays };
|
|
500
|
+
}, [editing, slide, buildStep]);
|
|
501
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SlideContext.Provider, { value: slideContext, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
317
502
|
"div",
|
|
318
503
|
{
|
|
319
504
|
ref,
|
|
@@ -331,23 +516,45 @@ function Slide({
|
|
|
331
516
|
onClick: (e) => {
|
|
332
517
|
if (e.target === e.currentTarget && onElementSelect) onElementSelect(null);
|
|
333
518
|
},
|
|
334
|
-
children:
|
|
335
|
-
|
|
336
|
-
{
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
519
|
+
children: [
|
|
520
|
+
buildInfo && /* @__PURE__ */ jsxRuntime.jsx("style", { children: BUILD_KEYFRAMES }),
|
|
521
|
+
orderedElements(slide.elements).map((element) => {
|
|
522
|
+
let buildHidden = false;
|
|
523
|
+
let buildAnimation;
|
|
524
|
+
let buildDelay = 0;
|
|
525
|
+
if (buildInfo) {
|
|
526
|
+
const step = buildInfo.revealStep.get(element.id);
|
|
527
|
+
if (step !== void 0) {
|
|
528
|
+
if (buildInfo.currentStep < step) {
|
|
529
|
+
buildHidden = true;
|
|
530
|
+
} else if (buildInfo.currentStep === step && element.animation) {
|
|
531
|
+
buildAnimation = element.animation;
|
|
532
|
+
buildDelay = buildInfo.delays.get(element.id) ?? 0;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (buildHidden) return null;
|
|
537
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
538
|
+
SlideElementHost,
|
|
539
|
+
{
|
|
540
|
+
element,
|
|
541
|
+
theme: t,
|
|
542
|
+
slideWidthPx,
|
|
543
|
+
slideHeightPx,
|
|
544
|
+
editing,
|
|
545
|
+
selected: selectedElementId === element.id,
|
|
546
|
+
onContentChange: onElementContentChange,
|
|
547
|
+
onSelect: onElementSelect,
|
|
548
|
+
onMove: onElementMove,
|
|
549
|
+
onResize: onElementResize,
|
|
550
|
+
renderElement,
|
|
551
|
+
buildAnimation,
|
|
552
|
+
buildDelay
|
|
553
|
+
},
|
|
554
|
+
element.id
|
|
555
|
+
);
|
|
556
|
+
})
|
|
557
|
+
]
|
|
351
558
|
}
|
|
352
559
|
) });
|
|
353
560
|
}
|
|
@@ -364,7 +571,9 @@ function SlideElementHost({
|
|
|
364
571
|
onSelect,
|
|
365
572
|
onMove,
|
|
366
573
|
onResize,
|
|
367
|
-
renderElement
|
|
574
|
+
renderElement,
|
|
575
|
+
buildAnimation,
|
|
576
|
+
buildDelay = 0
|
|
368
577
|
}) {
|
|
369
578
|
const dragRef = react.useRef(null);
|
|
370
579
|
if (element.hidden) return null;
|
|
@@ -433,15 +642,18 @@ function SlideElementHost({
|
|
|
433
642
|
outline: selected ? "2px solid #8b5cf6" : void 0,
|
|
434
643
|
outlineOffset: selected ? 2 : void 0,
|
|
435
644
|
cursor: canMove ? "move" : interactive ? "pointer" : "default",
|
|
436
|
-
touchAction: canMove ? "none" : void 0
|
|
645
|
+
touchAction: canMove ? "none" : void 0,
|
|
646
|
+
...buildAnimation ? buildEnterStyle(buildAnimation, buildDelay) : null
|
|
437
647
|
};
|
|
438
648
|
const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange }) ?? renderElement?.(element, slideWidthPx);
|
|
439
649
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
440
650
|
"div",
|
|
441
651
|
{
|
|
652
|
+
className: buildAnimation ? "fs-build-enter" : void 0,
|
|
442
653
|
style: box,
|
|
443
654
|
"data-fancy-slides-element": element.id,
|
|
444
655
|
"data-fancy-slides-element-type": element.type,
|
|
656
|
+
"data-fancy-slides-build": buildAnimation ? "" : void 0,
|
|
445
657
|
onPointerDown: canMove ? startDrag("move") : void 0,
|
|
446
658
|
onPointerMove: canMove ? onPointerMove : void 0,
|
|
447
659
|
onPointerUp: canMove ? endDrag : void 0,
|
|
@@ -563,6 +775,8 @@ function useSlideKeyboard({
|
|
|
563
775
|
total,
|
|
564
776
|
index,
|
|
565
777
|
goTo,
|
|
778
|
+
onAdvance,
|
|
779
|
+
onRetreat,
|
|
566
780
|
onExit,
|
|
567
781
|
onBlank,
|
|
568
782
|
onFullscreen,
|
|
@@ -579,13 +793,15 @@ function useSlideKeyboard({
|
|
|
579
793
|
case "ArrowLeft":
|
|
580
794
|
case "PageUp":
|
|
581
795
|
e.preventDefault();
|
|
582
|
-
if (
|
|
796
|
+
if (onRetreat) onRetreat();
|
|
797
|
+
else if (index > 0) goTo(index - 1);
|
|
583
798
|
return;
|
|
584
799
|
case "ArrowRight":
|
|
585
800
|
case "PageDown":
|
|
586
801
|
case " ":
|
|
587
802
|
e.preventDefault();
|
|
588
|
-
if (
|
|
803
|
+
if (onAdvance) onAdvance();
|
|
804
|
+
else if (index < total - 1) goTo(index + 1);
|
|
589
805
|
return;
|
|
590
806
|
case "Home":
|
|
591
807
|
e.preventDefault();
|
|
@@ -627,7 +843,7 @@ function useSlideKeyboard({
|
|
|
627
843
|
};
|
|
628
844
|
window.addEventListener("keydown", handler);
|
|
629
845
|
return () => window.removeEventListener("keydown", handler);
|
|
630
|
-
}, [enabled, index, total, goTo, onExit, onBlank, onFullscreen]);
|
|
846
|
+
}, [enabled, index, total, goTo, onAdvance, onRetreat, onExit, onBlank, onFullscreen]);
|
|
631
847
|
}
|
|
632
848
|
function SlideViewer({
|
|
633
849
|
deck,
|
|
@@ -655,13 +871,34 @@ function SlideViewer({
|
|
|
655
871
|
const containerRef = react.useRef(null);
|
|
656
872
|
const prevIndexRef = react.useRef(index);
|
|
657
873
|
const forward = index >= prevIndexRef.current;
|
|
874
|
+
const slide = deck.slides[index];
|
|
875
|
+
const totalSteps = totalBuildSteps(slide);
|
|
876
|
+
const [buildStep, setBuildStep] = react.useState(0);
|
|
877
|
+
const nextFreshRef = react.useRef(false);
|
|
658
878
|
react.useEffect(() => {
|
|
879
|
+
if (index === prevIndexRef.current) return;
|
|
659
880
|
prevIndexRef.current = index;
|
|
660
|
-
|
|
881
|
+
const fresh = nextFreshRef.current;
|
|
882
|
+
nextFreshRef.current = false;
|
|
883
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
884
|
+
}, [index, deck.slides]);
|
|
885
|
+
const advance = react.useCallback(() => {
|
|
886
|
+
if (buildStep < totalSteps) {
|
|
887
|
+
setBuildStep((s) => s + 1);
|
|
888
|
+
} else if (index < deck.slides.length - 1) {
|
|
889
|
+
nextFreshRef.current = true;
|
|
890
|
+
goTo(index + 1);
|
|
891
|
+
}
|
|
892
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
893
|
+
const retreat = react.useCallback(() => {
|
|
894
|
+
if (index > 0) goTo(index - 1);
|
|
895
|
+
}, [index, goTo]);
|
|
661
896
|
useSlideKeyboard({
|
|
662
897
|
total: deck.slides.length,
|
|
663
898
|
index,
|
|
664
899
|
goTo,
|
|
900
|
+
onAdvance: advance,
|
|
901
|
+
onRetreat: retreat,
|
|
665
902
|
onExit,
|
|
666
903
|
onBlank: () => setBlanked((b) => !b),
|
|
667
904
|
onFullscreen: () => {
|
|
@@ -674,11 +911,15 @@ function SlideViewer({
|
|
|
674
911
|
react.useEffect(() => {
|
|
675
912
|
if (!autoAdvanceMs || deck.slides.length <= 1) return;
|
|
676
913
|
const t = setTimeout(() => {
|
|
677
|
-
|
|
914
|
+
if (buildStep < totalSteps) {
|
|
915
|
+
setBuildStep((s) => s + 1);
|
|
916
|
+
} else {
|
|
917
|
+
nextFreshRef.current = true;
|
|
918
|
+
goTo(index + 1 < deck.slides.length ? index + 1 : 0);
|
|
919
|
+
}
|
|
678
920
|
}, autoAdvanceMs);
|
|
679
921
|
return () => clearTimeout(t);
|
|
680
|
-
}, [autoAdvanceMs, index, deck.slides.length, goTo]);
|
|
681
|
-
const slide = deck.slides[index];
|
|
922
|
+
}, [autoAdvanceMs, index, deck.slides.length, goTo, buildStep, totalSteps]);
|
|
682
923
|
const theme = resolveTheme(deck.theme);
|
|
683
924
|
const aspectRatio = theme.aspectRatio ?? 16 / 9;
|
|
684
925
|
const transition = slide?.transition ?? theme.defaultTransition;
|
|
@@ -700,6 +941,11 @@ function SlideViewer({
|
|
|
700
941
|
},
|
|
701
942
|
tabIndex: 0,
|
|
702
943
|
"data-fancy-slides-viewer": deck.id,
|
|
944
|
+
"data-fancy-slides-build-step": buildStep,
|
|
945
|
+
onClick: () => {
|
|
946
|
+
if (blanked) return;
|
|
947
|
+
advance();
|
|
948
|
+
},
|
|
703
949
|
children: [
|
|
704
950
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: TRANSITION_KEYFRAMES }),
|
|
705
951
|
!blanked && slide && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -713,7 +959,7 @@ function SlideViewer({
|
|
|
713
959
|
["--fs-ratio"]: aspectRatio.toString(),
|
|
714
960
|
boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
|
|
715
961
|
},
|
|
716
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, renderElement }) }, index)
|
|
962
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, buildStep, renderElement }) }, index)
|
|
717
963
|
}
|
|
718
964
|
),
|
|
719
965
|
!hideChrome && !blanked && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -744,7 +990,7 @@ function SlideViewer({
|
|
|
744
990
|
);
|
|
745
991
|
}
|
|
746
992
|
var DEFAULT_DURATION = 400;
|
|
747
|
-
var
|
|
993
|
+
var EASE2 = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
748
994
|
function transitionEnterStyle(transition, forward) {
|
|
749
995
|
const kind = transition?.kind ?? "none";
|
|
750
996
|
if (kind === "none") return { width: "100%", height: "100%" };
|
|
@@ -770,7 +1016,7 @@ function transitionEnterStyle(transition, forward) {
|
|
|
770
1016
|
height: "100%",
|
|
771
1017
|
animationName: name,
|
|
772
1018
|
animationDuration: `${duration}ms`,
|
|
773
|
-
animationTimingFunction:
|
|
1019
|
+
animationTimingFunction: EASE2,
|
|
774
1020
|
animationFillMode: "both"
|
|
775
1021
|
};
|
|
776
1022
|
}
|
|
@@ -826,14 +1072,38 @@ function PresenterView({
|
|
|
826
1072
|
},
|
|
827
1073
|
[deck.slides.length, isControlled, onIndexChange]
|
|
828
1074
|
);
|
|
1075
|
+
const slide = deck.slides[index];
|
|
1076
|
+
const totalSteps = totalBuildSteps(slide);
|
|
1077
|
+
const [buildStep, setBuildStep] = react.useState(0);
|
|
1078
|
+
const prevIndexRef = react.useRef(index);
|
|
1079
|
+
const nextFreshRef = react.useRef(false);
|
|
1080
|
+
react.useEffect(() => {
|
|
1081
|
+
if (index === prevIndexRef.current) return;
|
|
1082
|
+
prevIndexRef.current = index;
|
|
1083
|
+
const fresh = nextFreshRef.current;
|
|
1084
|
+
nextFreshRef.current = false;
|
|
1085
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
1086
|
+
}, [index, deck.slides]);
|
|
1087
|
+
const advance = react.useCallback(() => {
|
|
1088
|
+
if (buildStep < totalSteps) {
|
|
1089
|
+
setBuildStep((s) => s + 1);
|
|
1090
|
+
} else if (index < deck.slides.length - 1) {
|
|
1091
|
+
nextFreshRef.current = true;
|
|
1092
|
+
goTo(index + 1);
|
|
1093
|
+
}
|
|
1094
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
1095
|
+
const retreat = react.useCallback(() => {
|
|
1096
|
+
if (index > 0) goTo(index - 1);
|
|
1097
|
+
}, [index, goTo]);
|
|
829
1098
|
useSlideKeyboard({
|
|
830
1099
|
total: deck.slides.length,
|
|
831
1100
|
index,
|
|
832
1101
|
goTo,
|
|
1102
|
+
onAdvance: advance,
|
|
1103
|
+
onRetreat: retreat,
|
|
833
1104
|
onExit
|
|
834
1105
|
});
|
|
835
1106
|
const theme = resolveTheme(deck.theme);
|
|
836
|
-
const slide = deck.slides[index];
|
|
837
1107
|
const nextSlide = deck.slides[index + 1];
|
|
838
1108
|
const [now, setNow] = react.useState(() => Date.now());
|
|
839
1109
|
react.useEffect(() => {
|
|
@@ -880,7 +1150,7 @@ function PresenterView({
|
|
|
880
1150
|
borderRadius: 8,
|
|
881
1151
|
overflow: "hidden"
|
|
882
1152
|
},
|
|
883
|
-
children: slide ? /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, renderElement }) : null
|
|
1153
|
+
children: slide ? /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, buildStep, renderElement }) : null
|
|
884
1154
|
}
|
|
885
1155
|
)
|
|
886
1156
|
}
|
|
@@ -984,8 +1254,8 @@ function PresenterView({
|
|
|
984
1254
|
/* @__PURE__ */ jsxRuntime.jsx(StatusChip, { label: "Elapsed", children: formatElapsed(now - startedAtRef) }),
|
|
985
1255
|
/* @__PURE__ */ jsxRuntime.jsx(StatusChip, { label: "Clock", children: formatClock(now) }),
|
|
986
1256
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginLeft: "auto", display: "flex", gap: 8 }, children: [
|
|
987
|
-
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick:
|
|
988
|
-
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick:
|
|
1257
|
+
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick: retreat, disabled: index === 0, children: "\u2190 Prev" }),
|
|
1258
|
+
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick: advance, disabled: index >= deck.slides.length - 1 && buildStep >= totalSteps, children: "Next \u2192" })
|
|
989
1259
|
] })
|
|
990
1260
|
]
|
|
991
1261
|
}
|
|
@@ -1168,6 +1438,7 @@ function useDeckState({ value, onChange, onOp }) {
|
|
|
1168
1438
|
updateElement: (slideIdArg, elementIdArg, patch) => apply({ kind: "element_update", slideId: slideIdArg, elementId: elementIdArg, patch }),
|
|
1169
1439
|
moveElement: (slideIdArg, elementIdArg, x, y) => apply({ kind: "element_move", slideId: slideIdArg, elementId: elementIdArg, x, y }),
|
|
1170
1440
|
resizeElement: (slideIdArg, elementIdArg, w, h) => apply({ kind: "element_resize", slideId: slideIdArg, elementId: elementIdArg, w, h }),
|
|
1441
|
+
setAnimation: (slideIdArg, elementIdArg, animation) => apply({ kind: "element_set_animation", slideId: slideIdArg, elementId: elementIdArg, animation }),
|
|
1171
1442
|
getSlide: (id) => value.slides.find((s) => s.id === id),
|
|
1172
1443
|
getElement: (slideIdArg, elementIdArg) => value.slides.find((s) => s.id === slideIdArg)?.elements.find((e) => e.id === elementIdArg)
|
|
1173
1444
|
};
|
|
@@ -1235,6 +1506,23 @@ function reduce(deck, op) {
|
|
|
1235
1506
|
(s) => s.id === op.slideId ? { ...s, elements: s.elements.map((e) => e.id === op.elementId ? { ...e, w: op.w, h: op.h } : e) } : s
|
|
1236
1507
|
)
|
|
1237
1508
|
};
|
|
1509
|
+
case "element_set_animation":
|
|
1510
|
+
return {
|
|
1511
|
+
...deck,
|
|
1512
|
+
slides: deck.slides.map(
|
|
1513
|
+
(s) => s.id === op.slideId ? {
|
|
1514
|
+
...s,
|
|
1515
|
+
elements: s.elements.map((e) => {
|
|
1516
|
+
if (e.id !== op.elementId) return e;
|
|
1517
|
+
if (op.animation === void 0) {
|
|
1518
|
+
const { animation: _drop, ...rest } = e;
|
|
1519
|
+
return rest;
|
|
1520
|
+
}
|
|
1521
|
+
return { ...e, animation: op.animation };
|
|
1522
|
+
})
|
|
1523
|
+
} : s
|
|
1524
|
+
)
|
|
1525
|
+
};
|
|
1238
1526
|
}
|
|
1239
1527
|
}
|
|
1240
1528
|
|
|
@@ -1321,6 +1609,113 @@ function chartStarterOption(kind) {
|
|
|
1321
1609
|
};
|
|
1322
1610
|
}
|
|
1323
1611
|
}
|
|
1612
|
+
var CHART_PALETTE = [
|
|
1613
|
+
"#8b5cf6",
|
|
1614
|
+
"#3b82f6",
|
|
1615
|
+
"#10b981",
|
|
1616
|
+
"#f59e0b",
|
|
1617
|
+
"#ef4444",
|
|
1618
|
+
"#ec4899",
|
|
1619
|
+
"#14b8a6",
|
|
1620
|
+
"#6366f1"
|
|
1621
|
+
];
|
|
1622
|
+
function chartColorAt(index) {
|
|
1623
|
+
return CHART_PALETTE[index % CHART_PALETTE.length];
|
|
1624
|
+
}
|
|
1625
|
+
function isPlainObject(v) {
|
|
1626
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1627
|
+
}
|
|
1628
|
+
function toNumber(v) {
|
|
1629
|
+
const n = typeof v === "number" ? v : parseFloat(String(v));
|
|
1630
|
+
return Number.isFinite(n) ? n : 0;
|
|
1631
|
+
}
|
|
1632
|
+
function chartModelFromOption(option) {
|
|
1633
|
+
if (!isPlainObject(option)) return null;
|
|
1634
|
+
const seriesRaw = option.series;
|
|
1635
|
+
if (!Array.isArray(seriesRaw) || seriesRaw.length === 0) return null;
|
|
1636
|
+
if (!seriesRaw.every(isPlainObject)) return null;
|
|
1637
|
+
const types = seriesRaw.map((s) => String(s.type ?? ""));
|
|
1638
|
+
if (types[0] === "pie") {
|
|
1639
|
+
if (seriesRaw.length !== 1) return null;
|
|
1640
|
+
const data = seriesRaw[0].data;
|
|
1641
|
+
if (!Array.isArray(data)) return null;
|
|
1642
|
+
const slices = [];
|
|
1643
|
+
for (const d of data) {
|
|
1644
|
+
if (!isPlainObject(d)) return null;
|
|
1645
|
+
slices.push({ name: String(d.name ?? ""), value: toNumber(d.value) });
|
|
1646
|
+
}
|
|
1647
|
+
return { kind: "pie", categories: [], series: [], slices };
|
|
1648
|
+
}
|
|
1649
|
+
const cartesian = /* @__PURE__ */ new Set(["bar", "line", "scatter"]);
|
|
1650
|
+
if (!types.every((t) => cartesian.has(t))) return null;
|
|
1651
|
+
if (new Set(types).size !== 1) return null;
|
|
1652
|
+
const baseType = types[0];
|
|
1653
|
+
const isArea = baseType === "line" && seriesRaw.every((s) => isPlainObject(s.areaStyle) || s.areaStyle != null);
|
|
1654
|
+
const kind = baseType === "line" ? isArea ? "area" : "line" : baseType;
|
|
1655
|
+
const xAxis = option.xAxis;
|
|
1656
|
+
const axisData = isPlainObject(xAxis) ? xAxis.data : void 0;
|
|
1657
|
+
const categories = Array.isArray(axisData) ? axisData.map((c) => String(c)) : [];
|
|
1658
|
+
const firstData = seriesRaw[0].data;
|
|
1659
|
+
const valueCount = Array.isArray(firstData) ? firstData.length : 0;
|
|
1660
|
+
const cats = categories.length > 0 ? categories : Array.from({ length: valueCount }, (_, i) => String(i + 1));
|
|
1661
|
+
const series = [];
|
|
1662
|
+
for (const s of seriesRaw) {
|
|
1663
|
+
const data = s.data;
|
|
1664
|
+
if (!Array.isArray(data)) return null;
|
|
1665
|
+
const values = data.map((d) => {
|
|
1666
|
+
if (Array.isArray(d)) return toNumber(d[1]);
|
|
1667
|
+
if (isPlainObject(d)) return toNumber(d.value);
|
|
1668
|
+
return toNumber(d);
|
|
1669
|
+
});
|
|
1670
|
+
series.push({ name: String(s.name ?? "Series"), color: typeof s.itemStyle === "object" && s.itemStyle && isPlainObject(s.itemStyle) ? typeof s.itemStyle.color === "string" ? s.itemStyle.color : void 0 : typeof s.color === "string" ? s.color : void 0, values });
|
|
1671
|
+
}
|
|
1672
|
+
return { kind, categories: cats, series, slices: [] };
|
|
1673
|
+
}
|
|
1674
|
+
function chartOptionFromModel(model) {
|
|
1675
|
+
if (model.kind === "pie") {
|
|
1676
|
+
return {
|
|
1677
|
+
tooltip: { trigger: "item" },
|
|
1678
|
+
legend: { bottom: 0 },
|
|
1679
|
+
color: model.slices.map((_, i) => chartColorAt(i)),
|
|
1680
|
+
series: [
|
|
1681
|
+
{
|
|
1682
|
+
type: "pie",
|
|
1683
|
+
radius: ["40%", "70%"],
|
|
1684
|
+
name: "Segment",
|
|
1685
|
+
data: model.slices.map((s) => ({ name: s.name, value: s.value }))
|
|
1686
|
+
}
|
|
1687
|
+
]
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
const isScatter = model.kind === "scatter";
|
|
1691
|
+
const isArea = model.kind === "area";
|
|
1692
|
+
const seriesType = model.kind === "bar" ? "bar" : model.kind === "scatter" ? "scatter" : "line";
|
|
1693
|
+
const series = model.series.map((s, i) => {
|
|
1694
|
+
const color = s.color ?? chartColorAt(i);
|
|
1695
|
+
const base = {
|
|
1696
|
+
type: seriesType,
|
|
1697
|
+
name: s.name,
|
|
1698
|
+
itemStyle: { color }
|
|
1699
|
+
};
|
|
1700
|
+
if (isScatter) {
|
|
1701
|
+
base.symbolSize = 12;
|
|
1702
|
+
base.data = s.values.map((v, idx) => [idx, v]);
|
|
1703
|
+
} else {
|
|
1704
|
+
base.data = s.values;
|
|
1705
|
+
}
|
|
1706
|
+
if (seriesType === "line") base.smooth = true;
|
|
1707
|
+
if (isArea) base.areaStyle = { color };
|
|
1708
|
+
return base;
|
|
1709
|
+
});
|
|
1710
|
+
return {
|
|
1711
|
+
grid: { top: 24, left: 56, right: 16, bottom: isScatter ? 32 : 40 },
|
|
1712
|
+
tooltip: { trigger: isScatter ? "item" : "axis" },
|
|
1713
|
+
legend: model.series.length > 1 ? { bottom: 0 } : void 0,
|
|
1714
|
+
xAxis: isScatter ? { type: "value" } : { type: "category", data: [...model.categories] },
|
|
1715
|
+
yAxis: { type: "value" },
|
|
1716
|
+
series
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1324
1719
|
function SlideRail({
|
|
1325
1720
|
slides,
|
|
1326
1721
|
selectedId,
|
|
@@ -1469,10 +1864,10 @@ function EditorToolbar({
|
|
|
1469
1864
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tooltip, { content: "Present (F)", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { color: "violet", size: "sm", icon: "play", onClick: onPresent, children: "Present" }) }) })
|
|
1470
1865
|
] });
|
|
1471
1866
|
}
|
|
1472
|
-
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
|
|
1867
|
+
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }) {
|
|
1473
1868
|
if (!element) {
|
|
1474
1869
|
if (slide) {
|
|
1475
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
|
|
1870
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetElementAnimation });
|
|
1476
1871
|
}
|
|
1477
1872
|
return /* @__PURE__ */ jsxRuntime.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: [
|
|
1478
1873
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
|
|
@@ -1497,10 +1892,12 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1497
1892
|
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Tabs.List, { children: [
|
|
1498
1893
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "style", children: "Style" }),
|
|
1499
1894
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "layout", children: "Layout" }),
|
|
1895
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "build", children: "Build" }),
|
|
1500
1896
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "advanced", children: "Advanced" })
|
|
1501
1897
|
] }),
|
|
1502
1898
|
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Tabs.Panels, { children: [
|
|
1503
1899
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "style", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(StyleSection, { element, onPatch }) }) }),
|
|
1900
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "build", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(AnimateSection, { animation: element.animation, onSetAnimation }) }) }),
|
|
1504
1901
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "layout", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(LayoutSection, { element, onPatch }) }) }),
|
|
1505
1902
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "advanced", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(AdvancedSection, { element, onPatch }) }) })
|
|
1506
1903
|
] })
|
|
@@ -1510,7 +1907,8 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1510
1907
|
function SlideSettings({
|
|
1511
1908
|
slide,
|
|
1512
1909
|
onSetTransition,
|
|
1513
|
-
onSetBackground
|
|
1910
|
+
onSetBackground,
|
|
1911
|
+
onSetElementAnimation
|
|
1514
1912
|
}) {
|
|
1515
1913
|
const transition = slide.transition;
|
|
1516
1914
|
const kind = transition?.kind ?? "none";
|
|
@@ -1577,10 +1975,54 @@ function SlideSettings({
|
|
|
1577
1975
|
onChange: (c) => onSetBackground({ ...slide.background, color: c })
|
|
1578
1976
|
}
|
|
1579
1977
|
) })
|
|
1580
|
-
] }) })
|
|
1978
|
+
] }) }),
|
|
1979
|
+
onSetElementAnimation && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(BuildOrderList, { slide, onSetElementAnimation }) })
|
|
1581
1980
|
] })
|
|
1582
1981
|
] });
|
|
1583
1982
|
}
|
|
1983
|
+
function BuildOrderList({
|
|
1984
|
+
slide,
|
|
1985
|
+
onSetElementAnimation
|
|
1986
|
+
}) {
|
|
1987
|
+
const builds = collectBuilds(slide);
|
|
1988
|
+
const move = (from, to) => {
|
|
1989
|
+
if (to < 0 || to >= builds.length) return;
|
|
1990
|
+
const reordered = [...builds];
|
|
1991
|
+
const [item] = reordered.splice(from, 1);
|
|
1992
|
+
reordered.splice(to, 0, item);
|
|
1993
|
+
reordered.forEach((b, i) => {
|
|
1994
|
+
if ((b.animation.order ?? 0) !== i) {
|
|
1995
|
+
onSetElementAnimation(b.element.id, { ...b.animation, order: i });
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
1998
|
+
};
|
|
1999
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2000
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Build order" }),
|
|
2001
|
+
builds.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactFancy.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2002
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Text, { size: "xs", className: "!font-mono !text-zinc-400 w-5", children: [
|
|
2003
|
+
i + 1,
|
|
2004
|
+
"."
|
|
2005
|
+
] }),
|
|
2006
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2007
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "truncate", children: buildLabel(b.element) }),
|
|
2008
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Text, { size: "xs", className: "!font-mono !text-zinc-400", children: [
|
|
2009
|
+
b.animation.effect,
|
|
2010
|
+
" \xB7 ",
|
|
2011
|
+
b.animation.trigger ?? "on-click"
|
|
2012
|
+
] })
|
|
2013
|
+
] }),
|
|
2014
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "chevron-up", onClick: () => move(i, i - 1), disabled: i === 0, "aria-label": "Move earlier" }),
|
|
2015
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "chevron-down", onClick: () => move(i, i + 1), disabled: i === builds.length - 1, "aria-label": "Move later" })
|
|
2016
|
+
] }, b.element.id))
|
|
2017
|
+
] });
|
|
2018
|
+
}
|
|
2019
|
+
function buildLabel(element) {
|
|
2020
|
+
if (element.type === "text") {
|
|
2021
|
+
const text = element.content.replace(/\s+/g, " ").trim();
|
|
2022
|
+
return text ? text.length > 28 ? `${text.slice(0, 28)}\u2026` : text : "Text";
|
|
2023
|
+
}
|
|
2024
|
+
return `${element.type} #${element.id.slice(-6)}`;
|
|
2025
|
+
}
|
|
1584
2026
|
function LayoutSection({ element, onPatch }) {
|
|
1585
2027
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1586
2028
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
@@ -1602,6 +2044,100 @@ function AdvancedSection({ element, onPatch }) {
|
|
|
1602
2044
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "sm", variant: element.hidden ? "default" : "ghost", onClick: () => onPatch({ hidden: !element.hidden }), children: element.hidden ? "Hidden \u2014 show" : "Hide on slide" }) })
|
|
1603
2045
|
] });
|
|
1604
2046
|
}
|
|
2047
|
+
var NO_ANIMATION = "none";
|
|
2048
|
+
function AnimateSection({
|
|
2049
|
+
animation,
|
|
2050
|
+
onSetAnimation
|
|
2051
|
+
}) {
|
|
2052
|
+
if (!onSetAnimation) {
|
|
2053
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "!text-zinc-500", children: "Build animations aren't wired up in this editor." });
|
|
2054
|
+
}
|
|
2055
|
+
const effect = animation?.effect;
|
|
2056
|
+
const set = (next) => {
|
|
2057
|
+
const base = animation ?? { effect: "fade" };
|
|
2058
|
+
onSetAnimation({ ...base, ...next });
|
|
2059
|
+
};
|
|
2060
|
+
const showDirection = effect === "fly-in" || effect === "wipe";
|
|
2061
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2062
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2063
|
+
reactFancy.Select,
|
|
2064
|
+
{
|
|
2065
|
+
label: "Effect",
|
|
2066
|
+
list: [
|
|
2067
|
+
{ value: NO_ANIMATION, label: "None" },
|
|
2068
|
+
{ value: "fade", label: "Fade" },
|
|
2069
|
+
{ value: "fly-in", label: "Fly in" },
|
|
2070
|
+
{ value: "zoom", label: "Zoom" },
|
|
2071
|
+
{ value: "wipe", label: "Wipe" }
|
|
2072
|
+
],
|
|
2073
|
+
value: effect ?? NO_ANIMATION,
|
|
2074
|
+
onValueChange: (v) => {
|
|
2075
|
+
if (v === NO_ANIMATION) onSetAnimation(void 0);
|
|
2076
|
+
else set({ effect: v });
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
),
|
|
2080
|
+
effect && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2081
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2082
|
+
reactFancy.Select,
|
|
2083
|
+
{
|
|
2084
|
+
label: "Trigger",
|
|
2085
|
+
list: [
|
|
2086
|
+
{ value: "on-click", label: "On click" },
|
|
2087
|
+
{ value: "with-prev", label: "With previous" },
|
|
2088
|
+
{ value: "after-prev", label: "After previous" }
|
|
2089
|
+
],
|
|
2090
|
+
value: animation?.trigger ?? "on-click",
|
|
2091
|
+
onValueChange: (v) => set({ trigger: v })
|
|
2092
|
+
}
|
|
2093
|
+
),
|
|
2094
|
+
showDirection && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2095
|
+
reactFancy.Select,
|
|
2096
|
+
{
|
|
2097
|
+
label: "Direction",
|
|
2098
|
+
list: [
|
|
2099
|
+
{ value: "left", label: "From left" },
|
|
2100
|
+
{ value: "right", label: "From right" },
|
|
2101
|
+
{ value: "up", label: "From bottom" },
|
|
2102
|
+
{ value: "down", label: "From top" }
|
|
2103
|
+
],
|
|
2104
|
+
value: animation?.direction ?? "left",
|
|
2105
|
+
onValueChange: (v) => set({ direction: v })
|
|
2106
|
+
}
|
|
2107
|
+
),
|
|
2108
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2109
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2110
|
+
reactFancy.Input,
|
|
2111
|
+
{
|
|
2112
|
+
label: "Duration (ms)",
|
|
2113
|
+
type: "number",
|
|
2114
|
+
value: String(animation?.duration ?? 500),
|
|
2115
|
+
onChange: (e) => set({ duration: parseInt(e.target.value, 10) || 500 })
|
|
2116
|
+
}
|
|
2117
|
+
),
|
|
2118
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2119
|
+
reactFancy.Input,
|
|
2120
|
+
{
|
|
2121
|
+
label: "Delay (ms)",
|
|
2122
|
+
type: "number",
|
|
2123
|
+
value: String(animation?.delay ?? 0),
|
|
2124
|
+
onChange: (e) => set({ delay: parseInt(e.target.value, 10) || 0 })
|
|
2125
|
+
}
|
|
2126
|
+
)
|
|
2127
|
+
] }),
|
|
2128
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2129
|
+
reactFancy.Input,
|
|
2130
|
+
{
|
|
2131
|
+
label: "Order",
|
|
2132
|
+
type: "number",
|
|
2133
|
+
value: String(animation?.order ?? 0),
|
|
2134
|
+
onChange: (e) => set({ order: parseInt(e.target.value, 10) || 0 })
|
|
2135
|
+
}
|
|
2136
|
+
),
|
|
2137
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.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.` })
|
|
2138
|
+
] })
|
|
2139
|
+
] });
|
|
2140
|
+
}
|
|
1605
2141
|
function StyleSection({ element, onPatch }) {
|
|
1606
2142
|
switch (element.type) {
|
|
1607
2143
|
case "text":
|
|
@@ -1674,7 +2210,35 @@ function TextStyleControls({ element, onPatch }) {
|
|
|
1674
2210
|
] });
|
|
1675
2211
|
}
|
|
1676
2212
|
function ImageStyleControls({ element, onPatch }) {
|
|
2213
|
+
const fileRef = react.useRef(null);
|
|
2214
|
+
const crop = element.crop;
|
|
2215
|
+
const onFile = (file) => {
|
|
2216
|
+
if (!file) return;
|
|
2217
|
+
const reader = new FileReader();
|
|
2218
|
+
reader.onload = () => {
|
|
2219
|
+
if (typeof reader.result === "string") onPatch({ src: reader.result });
|
|
2220
|
+
};
|
|
2221
|
+
reader.readAsDataURL(file);
|
|
2222
|
+
};
|
|
2223
|
+
const setCrop = (next) => {
|
|
2224
|
+
const base = crop ?? { x: 0, y: 0, w: 1, h: 1 };
|
|
2225
|
+
onPatch({ crop: { ...base, ...next } });
|
|
2226
|
+
};
|
|
1677
2227
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2228
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2229
|
+
"input",
|
|
2230
|
+
{
|
|
2231
|
+
ref: fileRef,
|
|
2232
|
+
type: "file",
|
|
2233
|
+
accept: "image/*",
|
|
2234
|
+
className: "hidden",
|
|
2235
|
+
onChange: (e) => {
|
|
2236
|
+
onFile(e.target.files?.[0]);
|
|
2237
|
+
e.target.value = "";
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
),
|
|
2241
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "sm", variant: "ghost", icon: "upload", onClick: () => fileRef.current?.click(), children: "Upload image" }),
|
|
1678
2242
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Textarea, { label: "Image URL", value: element.src, onValueChange: (v) => onPatch({ src: v }), rows: 2 }),
|
|
1679
2243
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Alt text", value: element.alt ?? "", onChange: (e) => onPatch({ alt: e.target.value }) }),
|
|
1680
2244
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1690,7 +2254,17 @@ function ImageStyleControls({ element, onPatch }) {
|
|
|
1690
2254
|
value: element.fit ?? "contain",
|
|
1691
2255
|
onValueChange: (v) => onPatch({ fit: v })
|
|
1692
2256
|
}
|
|
1693
|
-
)
|
|
2257
|
+
),
|
|
2258
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
|
|
2259
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2260
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Crop" }),
|
|
2261
|
+
crop && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", onClick: () => onPatch({ crop: void 0 }), children: "Clear crop" })
|
|
2262
|
+
] }),
|
|
2263
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "X", value: crop?.x ?? 0, onValueChange: (v) => setCrop({ x: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
2264
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Y", value: crop?.y ?? 0, onValueChange: (v) => setCrop({ y: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
2265
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Width", value: crop?.w ?? 1, onValueChange: (v) => setCrop({ w: Number(v) }), min: 0.01, max: 1, step: 0.01, showValue: true }),
|
|
2266
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Height", value: crop?.h ?? 1, onValueChange: (v) => setCrop({ h: Number(v) }), min: 0.01, max: 1, step: 0.01, showValue: true }),
|
|
2267
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: "Crop is a window into the source image (0..1). Width/height shrink the visible region; X/Y pan it." })
|
|
1694
2268
|
] });
|
|
1695
2269
|
}
|
|
1696
2270
|
function ShapeStyleControls({ element, onPatch }) {
|
|
@@ -1737,54 +2311,254 @@ function CodeStyleControls({ element, onPatch }) {
|
|
|
1737
2311
|
] });
|
|
1738
2312
|
}
|
|
1739
2313
|
function ChartStyleControls({ element, onPatch }) {
|
|
2314
|
+
const model = chartModelFromOption(element.option);
|
|
2315
|
+
const writeModel = (m) => onPatch({ option: chartOptionFromModel(m) });
|
|
2316
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2317
|
+
model ? /* @__PURE__ */ jsxRuntime.jsx(ChartModelEditor, { model, onChange: writeModel }) : /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "rounded-md bg-amber-50 p-2 !text-amber-700 dark:bg-amber-950/40 dark:!text-amber-400", children: "This chart's option is too custom for the visual editor. Edit it as JSON below." }),
|
|
2318
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
2319
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer select-none px-2 py-1.5 text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Advanced \u2014 edit option JSON" }),
|
|
2320
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 pt-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2321
|
+
reactFancy.Textarea,
|
|
2322
|
+
{
|
|
2323
|
+
label: "ECharts option (JSON)",
|
|
2324
|
+
value: JSON.stringify(element.option, null, 2),
|
|
2325
|
+
onValueChange: (v) => {
|
|
2326
|
+
try {
|
|
2327
|
+
onPatch({ option: JSON.parse(v) });
|
|
2328
|
+
} catch {
|
|
2329
|
+
}
|
|
2330
|
+
},
|
|
2331
|
+
rows: 10
|
|
2332
|
+
}
|
|
2333
|
+
) })
|
|
2334
|
+
] })
|
|
2335
|
+
] });
|
|
2336
|
+
}
|
|
2337
|
+
var CHART_TYPE_OPTIONS = [
|
|
2338
|
+
{ value: "bar", label: "Bar" },
|
|
2339
|
+
{ value: "line", label: "Line" },
|
|
2340
|
+
{ value: "area", label: "Area" },
|
|
2341
|
+
{ value: "pie", label: "Pie" },
|
|
2342
|
+
{ value: "scatter", label: "Scatter" }
|
|
2343
|
+
];
|
|
2344
|
+
function ChartModelEditor({ model, onChange }) {
|
|
2345
|
+
const setKind = (kind) => {
|
|
2346
|
+
if (kind === model.kind) return;
|
|
2347
|
+
if (kind === "pie") {
|
|
2348
|
+
const first = model.series[0];
|
|
2349
|
+
const slices = model.slices.length ? model.slices : model.categories.length ? model.categories.map((name, i) => ({ name, value: first?.values[i] ?? 0 })) : [{ name: "Slice 1", value: 1 }];
|
|
2350
|
+
onChange({ ...model, kind, slices });
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
if (model.kind === "pie") {
|
|
2354
|
+
const categories = model.slices.length ? model.slices.map((s) => s.name) : ["A", "B", "C"];
|
|
2355
|
+
const values = model.slices.length ? model.slices.map((s) => s.value) : [1, 2, 3];
|
|
2356
|
+
onChange({ ...model, kind, categories, series: [{ name: "Series 1", color: chartColorAt(0), values }] });
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
onChange({ ...model, kind });
|
|
2360
|
+
};
|
|
1740
2361
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1741
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "!text-zinc-500", children: "Chart option is JSON \u2014 paste any ECharts option here." }),
|
|
1742
2362
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1743
|
-
reactFancy.
|
|
2363
|
+
reactFancy.Select,
|
|
1744
2364
|
{
|
|
1745
|
-
label: "
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
onPatch({ option: JSON.parse(v) });
|
|
1750
|
-
} catch {
|
|
1751
|
-
}
|
|
1752
|
-
},
|
|
1753
|
-
rows: 10
|
|
2365
|
+
label: "Chart type",
|
|
2366
|
+
list: CHART_TYPE_OPTIONS,
|
|
2367
|
+
value: model.kind,
|
|
2368
|
+
onValueChange: (v) => setKind(v)
|
|
1754
2369
|
}
|
|
1755
|
-
)
|
|
2370
|
+
),
|
|
2371
|
+
model.kind === "pie" ? /* @__PURE__ */ jsxRuntime.jsx(PieSliceEditor, { model, onChange }) : /* @__PURE__ */ jsxRuntime.jsx(CartesianChartEditor, { model, onChange })
|
|
2372
|
+
] });
|
|
2373
|
+
}
|
|
2374
|
+
function PieSliceEditor({ model, onChange }) {
|
|
2375
|
+
const slices = model.slices;
|
|
2376
|
+
const update = (i, next) => {
|
|
2377
|
+
const copy = slices.map((s, idx) => idx === i ? { ...s, ...next } : s);
|
|
2378
|
+
onChange({ ...model, slices: copy });
|
|
2379
|
+
};
|
|
2380
|
+
const remove = (i) => onChange({ ...model, slices: slices.filter((_, idx) => idx !== i) });
|
|
2381
|
+
const add = () => onChange({ ...model, slices: [...slices, { name: `Slice ${slices.length + 1}`, value: 0 }] });
|
|
2382
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2383
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Slices" }),
|
|
2384
|
+
slices.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
|
|
2385
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: i === 0 ? "Name" : void 0, value: s.name, onChange: (e) => update(i, { name: e.target.value }) }) }),
|
|
2386
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-20", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: i === 0 ? "Value" : void 0, type: "number", value: String(s.value), onChange: (e) => update(i, { value: parseFloat(e.target.value) || 0 }) }) }),
|
|
2387
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => remove(i), "aria-label": "Remove slice" })
|
|
2388
|
+
] }, i)),
|
|
2389
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: add, children: "Add slice" })
|
|
2390
|
+
] });
|
|
2391
|
+
}
|
|
2392
|
+
function CartesianChartEditor({ model, onChange }) {
|
|
2393
|
+
const { categories, series } = model;
|
|
2394
|
+
const updateCategory = (i, label) => {
|
|
2395
|
+
onChange({ ...model, categories: categories.map((c, idx) => idx === i ? label : c) });
|
|
2396
|
+
};
|
|
2397
|
+
const removeCategory = (i) => {
|
|
2398
|
+
onChange({
|
|
2399
|
+
...model,
|
|
2400
|
+
categories: categories.filter((_, idx) => idx !== i),
|
|
2401
|
+
series: series.map((s) => ({ ...s, values: s.values.filter((_, idx) => idx !== i) }))
|
|
2402
|
+
});
|
|
2403
|
+
};
|
|
2404
|
+
const addCategory = () => {
|
|
2405
|
+
onChange({
|
|
2406
|
+
...model,
|
|
2407
|
+
categories: [...categories, `Cat ${categories.length + 1}`],
|
|
2408
|
+
series: series.map((s) => ({ ...s, values: [...s.values, 0] }))
|
|
2409
|
+
});
|
|
2410
|
+
};
|
|
2411
|
+
const updateSeries = (si, next) => {
|
|
2412
|
+
onChange({ ...model, series: series.map((s, idx) => idx === si ? { ...s, ...next } : s) });
|
|
2413
|
+
};
|
|
2414
|
+
const updateValue = (si, ci, value) => {
|
|
2415
|
+
onChange({
|
|
2416
|
+
...model,
|
|
2417
|
+
series: series.map(
|
|
2418
|
+
(s, idx) => idx === si ? { ...s, values: s.values.map((v, vi) => vi === ci ? value : v) } : s
|
|
2419
|
+
)
|
|
2420
|
+
});
|
|
2421
|
+
};
|
|
2422
|
+
const removeSeries = (si) => onChange({ ...model, series: series.filter((_, idx) => idx !== si) });
|
|
2423
|
+
const addSeries = () => onChange({
|
|
2424
|
+
...model,
|
|
2425
|
+
series: [
|
|
2426
|
+
...series,
|
|
2427
|
+
{ name: `Series ${series.length + 1}`, color: chartColorAt(series.length), values: categories.map(() => 0) }
|
|
2428
|
+
]
|
|
2429
|
+
});
|
|
2430
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2431
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2432
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Categories" }),
|
|
2433
|
+
categories.map((c, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2434
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { value: c, onChange: (e) => updateCategory(i, e.target.value) }) }),
|
|
2435
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeCategory(i), "aria-label": "Remove category" })
|
|
2436
|
+
] }, i)),
|
|
2437
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addCategory, children: "Add category" })
|
|
2438
|
+
] }),
|
|
2439
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
|
|
2440
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2441
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Series" }),
|
|
2442
|
+
series.map((s, si) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 rounded-md border border-zinc-200 p-2 dark:border-zinc-800", children: [
|
|
2443
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
|
|
2444
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Name", value: s.name, onChange: (e) => updateSeries(si, { name: e.target.value }) }) }),
|
|
2445
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ColorPicker, { value: s.color ?? chartColorAt(si), onChange: (c) => updateSeries(si, { color: c }) }) }),
|
|
2446
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeSeries(si), "aria-label": "Remove series" })
|
|
2447
|
+
] }),
|
|
2448
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: categories.map((c, ci) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2449
|
+
reactFancy.Input,
|
|
2450
|
+
{
|
|
2451
|
+
label: c,
|
|
2452
|
+
type: "number",
|
|
2453
|
+
value: String(s.values[ci] ?? 0),
|
|
2454
|
+
onChange: (e) => updateValue(si, ci, parseFloat(e.target.value) || 0)
|
|
2455
|
+
},
|
|
2456
|
+
ci
|
|
2457
|
+
)) })
|
|
2458
|
+
] }, si)),
|
|
2459
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addSeries, children: "Add series" })
|
|
2460
|
+
] })
|
|
1756
2461
|
] });
|
|
1757
2462
|
}
|
|
1758
2463
|
function TableStyleControls({ element, onPatch }) {
|
|
2464
|
+
const columns = element.columns;
|
|
2465
|
+
const rows = element.rows;
|
|
2466
|
+
const nextColKey = () => {
|
|
2467
|
+
const existing = new Set(columns.map((c) => c.key));
|
|
2468
|
+
let n = columns.length + 1;
|
|
2469
|
+
while (existing.has(`col${n}`)) n++;
|
|
2470
|
+
return `col${n}`;
|
|
2471
|
+
};
|
|
2472
|
+
const setColumnLabel = (i, label) => {
|
|
2473
|
+
onPatch({ columns: columns.map((c, idx) => idx === i ? { ...c, label } : c) });
|
|
2474
|
+
};
|
|
2475
|
+
const removeColumn = (i) => {
|
|
2476
|
+
const key = columns[i]?.key;
|
|
2477
|
+
const nextCols = columns.filter((_, idx) => idx !== i);
|
|
2478
|
+
const nextRows = key ? rows.map((r) => {
|
|
2479
|
+
const { [key]: _drop, ...rest } = r;
|
|
2480
|
+
return rest;
|
|
2481
|
+
}) : rows;
|
|
2482
|
+
onPatch({ columns: nextCols, rows: nextRows });
|
|
2483
|
+
};
|
|
2484
|
+
const addColumn = () => {
|
|
2485
|
+
const key = nextColKey();
|
|
2486
|
+
onPatch({
|
|
2487
|
+
columns: [...columns, { key, label: `Column ${columns.length + 1}` }],
|
|
2488
|
+
rows: rows.map((r) => ({ ...r, [key]: "" }))
|
|
2489
|
+
});
|
|
2490
|
+
};
|
|
2491
|
+
const setCell = (rowIdx, key, value) => {
|
|
2492
|
+
onPatch({ rows: rows.map((r, idx) => idx === rowIdx ? { ...r, [key]: value } : r) });
|
|
2493
|
+
};
|
|
2494
|
+
const removeRow = (rowIdx) => onPatch({ rows: rows.filter((_, idx) => idx !== rowIdx) });
|
|
2495
|
+
const addRow = () => {
|
|
2496
|
+
const blank = {};
|
|
2497
|
+
for (const c of columns) blank[c.key] = "";
|
|
2498
|
+
onPatch({ rows: [...rows, blank] });
|
|
2499
|
+
};
|
|
1759
2500
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1760
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1761
|
-
reactFancy.
|
|
1762
|
-
{
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
2501
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2502
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Columns" }),
|
|
2503
|
+
columns.map((c, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2504
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { value: c.label, onChange: (e) => setColumnLabel(i, e.target.value), "aria-label": `Column ${i + 1} label` }) }),
|
|
2505
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!font-mono !text-zinc-400", children: c.key }),
|
|
2506
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeColumn(i), "aria-label": "Remove column" })
|
|
2507
|
+
] }, c.key)),
|
|
2508
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addColumn, children: "Add column" })
|
|
2509
|
+
] }),
|
|
2510
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
|
|
2511
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2512
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Rows" }),
|
|
2513
|
+
columns.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: "Add a column to start adding rows." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2514
|
+
rows.map((r, rowIdx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 border-b border-zinc-100 pb-2 dark:border-zinc-800", children: [
|
|
2515
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid flex-1 grid-cols-1 gap-1", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2516
|
+
reactFancy.Input,
|
|
2517
|
+
{
|
|
2518
|
+
label: c.label,
|
|
2519
|
+
value: r[c.key] == null ? "" : String(r[c.key]),
|
|
2520
|
+
onChange: (e) => setCell(rowIdx, c.key, e.target.value)
|
|
2521
|
+
},
|
|
2522
|
+
c.key
|
|
2523
|
+
)) }),
|
|
2524
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeRow(rowIdx), "aria-label": "Remove row" })
|
|
2525
|
+
] }, rowIdx)),
|
|
2526
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addRow, children: "Add row" })
|
|
2527
|
+
] })
|
|
2528
|
+
] }),
|
|
2529
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
2530
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer select-none px-2 py-1.5 text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Edit as JSON" }),
|
|
2531
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 p-2 pt-0", children: [
|
|
2532
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2533
|
+
reactFancy.Textarea,
|
|
2534
|
+
{
|
|
2535
|
+
label: "Columns (JSON)",
|
|
2536
|
+
value: JSON.stringify(columns, null, 2),
|
|
2537
|
+
onValueChange: (v) => {
|
|
2538
|
+
try {
|
|
2539
|
+
onPatch({ columns: JSON.parse(v) });
|
|
2540
|
+
} catch {
|
|
2541
|
+
}
|
|
2542
|
+
},
|
|
2543
|
+
rows: 5
|
|
1769
2544
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
2545
|
+
),
|
|
2546
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2547
|
+
reactFancy.Textarea,
|
|
2548
|
+
{
|
|
2549
|
+
label: "Rows (JSON)",
|
|
2550
|
+
value: JSON.stringify(rows, null, 2),
|
|
2551
|
+
onValueChange: (v) => {
|
|
2552
|
+
try {
|
|
2553
|
+
onPatch({ rows: JSON.parse(v) });
|
|
2554
|
+
} catch {
|
|
2555
|
+
}
|
|
2556
|
+
},
|
|
2557
|
+
rows: 8
|
|
1783
2558
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
)
|
|
2559
|
+
)
|
|
2560
|
+
] })
|
|
2561
|
+
] })
|
|
1788
2562
|
] });
|
|
1789
2563
|
}
|
|
1790
2564
|
function EmbedStyleControls({ element, onPatch }) {
|
|
@@ -2039,7 +2813,9 @@ function DeckEditor({
|
|
|
2039
2813
|
},
|
|
2040
2814
|
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
|
|
2041
2815
|
onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
|
|
2042
|
-
onSetBackground: (background) => slide && ops.setBackground(slide.id, background)
|
|
2816
|
+
onSetBackground: (background) => slide && ops.setBackground(slide.id, background),
|
|
2817
|
+
onSetAnimation: (animation) => slide && elementIdSelected && ops.setAnimation(slide.id, elementIdSelected, animation),
|
|
2818
|
+
onSetElementAnimation: (eid, animation) => slide && ops.setAnimation(slide.id, eid, animation)
|
|
2043
2819
|
}
|
|
2044
2820
|
) })
|
|
2045
2821
|
] }),
|
|
@@ -2061,8 +2837,11 @@ exports.SlideThumbnail = SlideThumbnail;
|
|
|
2061
2837
|
exports.SlideViewer = SlideViewer;
|
|
2062
2838
|
exports.SpeakerNotes = SpeakerNotes;
|
|
2063
2839
|
exports.TextElementRenderer = TextElementRenderer;
|
|
2840
|
+
exports.buildSteps = buildSteps;
|
|
2841
|
+
exports.buildsForStep = buildsForStep;
|
|
2064
2842
|
exports.builtinThemes = builtinThemes;
|
|
2065
2843
|
exports.chartStarterOption = chartStarterOption;
|
|
2844
|
+
exports.collectBuilds = collectBuilds;
|
|
2066
2845
|
exports.darkTheme = darkTheme;
|
|
2067
2846
|
exports.deckId = deckId;
|
|
2068
2847
|
exports.defaultTheme = defaultTheme;
|
|
@@ -2072,11 +2851,14 @@ exports.nextId = nextId;
|
|
|
2072
2851
|
exports.reduceDeck = reduce;
|
|
2073
2852
|
exports.resolveTheme = resolveTheme;
|
|
2074
2853
|
exports.slideId = slideId;
|
|
2854
|
+
exports.stepDelays = stepDelays;
|
|
2855
|
+
exports.totalBuildSteps = totalBuildSteps;
|
|
2075
2856
|
exports.useDeckState = useDeckState;
|
|
2076
2857
|
exports.useIsDarkSlide = useIsDarkSlide;
|
|
2077
2858
|
exports.useSlideContext = useSlideContext;
|
|
2078
2859
|
exports.useSlideKeyboard = useSlideKeyboard;
|
|
2079
2860
|
exports.useSlideTheme = useSlideTheme;
|
|
2861
|
+
exports.visibleElementIds = visibleElementIds;
|
|
2080
2862
|
exports.vividTheme = vividTheme;
|
|
2081
2863
|
//# sourceMappingURL=index.cjs.map
|
|
2082
2864
|
//# sourceMappingURL=index.cjs.map
|