@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.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,
|
|
@@ -159,6 +232,22 @@ function weight(w) {
|
|
|
159
232
|
return void 0;
|
|
160
233
|
}
|
|
161
234
|
function ImageElementRenderer({ element }) {
|
|
235
|
+
const crop = element.crop;
|
|
236
|
+
const fit = element.fit ?? "contain";
|
|
237
|
+
if (crop && crop.w > 0 && crop.h > 0) {
|
|
238
|
+
const inner = {
|
|
239
|
+
position: "absolute",
|
|
240
|
+
left: 0,
|
|
241
|
+
top: 0,
|
|
242
|
+
width: `${1 / crop.w * 100}%`,
|
|
243
|
+
height: `${1 / crop.h * 100}%`,
|
|
244
|
+
transform: `translate(${-crop.x / crop.w * 100}%, ${-crop.y / crop.h * 100}%)`,
|
|
245
|
+
transformOrigin: "top left",
|
|
246
|
+
objectFit: fit,
|
|
247
|
+
display: "block"
|
|
248
|
+
};
|
|
249
|
+
return /* @__PURE__ */ jsx("div", { style: { position: "relative", width: "100%", height: "100%", overflow: "hidden" }, children: /* @__PURE__ */ jsx("img", { src: element.src, alt: element.alt ?? "", style: inner, draggable: false }) });
|
|
250
|
+
}
|
|
162
251
|
return /* @__PURE__ */ jsx(
|
|
163
252
|
"img",
|
|
164
253
|
{
|
|
@@ -167,7 +256,7 @@ function ImageElementRenderer({ element }) {
|
|
|
167
256
|
style: {
|
|
168
257
|
width: "100%",
|
|
169
258
|
height: "100%",
|
|
170
|
-
objectFit:
|
|
259
|
+
objectFit: fit,
|
|
171
260
|
display: "block"
|
|
172
261
|
},
|
|
173
262
|
draggable: false
|
|
@@ -222,12 +311,94 @@ function renderShape(el, s) {
|
|
|
222
311
|
return null;
|
|
223
312
|
}
|
|
224
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
|
+
`;
|
|
225
395
|
function Slide({
|
|
226
396
|
slide,
|
|
227
397
|
theme,
|
|
228
398
|
width,
|
|
229
399
|
aspectRatio,
|
|
230
400
|
editing = false,
|
|
401
|
+
buildStep,
|
|
231
402
|
onElementContentChange,
|
|
232
403
|
onElementSelect,
|
|
233
404
|
selectedElementId,
|
|
@@ -271,7 +442,21 @@ function Slide({
|
|
|
271
442
|
}),
|
|
272
443
|
[t, effectiveBg, slideWidthPx]
|
|
273
444
|
);
|
|
274
|
-
|
|
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(
|
|
275
460
|
"div",
|
|
276
461
|
{
|
|
277
462
|
ref,
|
|
@@ -289,23 +474,45 @@ function Slide({
|
|
|
289
474
|
onClick: (e) => {
|
|
290
475
|
if (e.target === e.currentTarget && onElementSelect) onElementSelect(null);
|
|
291
476
|
},
|
|
292
|
-
children:
|
|
293
|
-
|
|
294
|
-
{
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
+
]
|
|
309
516
|
}
|
|
310
517
|
) });
|
|
311
518
|
}
|
|
@@ -322,7 +529,9 @@ function SlideElementHost({
|
|
|
322
529
|
onSelect,
|
|
323
530
|
onMove,
|
|
324
531
|
onResize,
|
|
325
|
-
renderElement
|
|
532
|
+
renderElement,
|
|
533
|
+
buildAnimation,
|
|
534
|
+
buildDelay = 0
|
|
326
535
|
}) {
|
|
327
536
|
const dragRef = useRef(null);
|
|
328
537
|
if (element.hidden) return null;
|
|
@@ -391,15 +600,18 @@ function SlideElementHost({
|
|
|
391
600
|
outline: selected ? "2px solid #8b5cf6" : void 0,
|
|
392
601
|
outlineOffset: selected ? 2 : void 0,
|
|
393
602
|
cursor: canMove ? "move" : interactive ? "pointer" : "default",
|
|
394
|
-
touchAction: canMove ? "none" : void 0
|
|
603
|
+
touchAction: canMove ? "none" : void 0,
|
|
604
|
+
...buildAnimation ? buildEnterStyle(buildAnimation, buildDelay) : null
|
|
395
605
|
};
|
|
396
606
|
const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange }) ?? renderElement?.(element, slideWidthPx);
|
|
397
607
|
return /* @__PURE__ */ jsxs(
|
|
398
608
|
"div",
|
|
399
609
|
{
|
|
610
|
+
className: buildAnimation ? "fs-build-enter" : void 0,
|
|
400
611
|
style: box,
|
|
401
612
|
"data-fancy-slides-element": element.id,
|
|
402
613
|
"data-fancy-slides-element-type": element.type,
|
|
614
|
+
"data-fancy-slides-build": buildAnimation ? "" : void 0,
|
|
403
615
|
onPointerDown: canMove ? startDrag("move") : void 0,
|
|
404
616
|
onPointerMove: canMove ? onPointerMove : void 0,
|
|
405
617
|
onPointerUp: canMove ? endDrag : void 0,
|
|
@@ -521,6 +733,8 @@ function useSlideKeyboard({
|
|
|
521
733
|
total,
|
|
522
734
|
index,
|
|
523
735
|
goTo,
|
|
736
|
+
onAdvance,
|
|
737
|
+
onRetreat,
|
|
524
738
|
onExit,
|
|
525
739
|
onBlank,
|
|
526
740
|
onFullscreen,
|
|
@@ -537,13 +751,15 @@ function useSlideKeyboard({
|
|
|
537
751
|
case "ArrowLeft":
|
|
538
752
|
case "PageUp":
|
|
539
753
|
e.preventDefault();
|
|
540
|
-
if (
|
|
754
|
+
if (onRetreat) onRetreat();
|
|
755
|
+
else if (index > 0) goTo(index - 1);
|
|
541
756
|
return;
|
|
542
757
|
case "ArrowRight":
|
|
543
758
|
case "PageDown":
|
|
544
759
|
case " ":
|
|
545
760
|
e.preventDefault();
|
|
546
|
-
if (
|
|
761
|
+
if (onAdvance) onAdvance();
|
|
762
|
+
else if (index < total - 1) goTo(index + 1);
|
|
547
763
|
return;
|
|
548
764
|
case "Home":
|
|
549
765
|
e.preventDefault();
|
|
@@ -585,7 +801,7 @@ function useSlideKeyboard({
|
|
|
585
801
|
};
|
|
586
802
|
window.addEventListener("keydown", handler);
|
|
587
803
|
return () => window.removeEventListener("keydown", handler);
|
|
588
|
-
}, [enabled, index, total, goTo, onExit, onBlank, onFullscreen]);
|
|
804
|
+
}, [enabled, index, total, goTo, onAdvance, onRetreat, onExit, onBlank, onFullscreen]);
|
|
589
805
|
}
|
|
590
806
|
function SlideViewer({
|
|
591
807
|
deck,
|
|
@@ -613,13 +829,34 @@ function SlideViewer({
|
|
|
613
829
|
const containerRef = useRef(null);
|
|
614
830
|
const prevIndexRef = useRef(index);
|
|
615
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);
|
|
616
836
|
useEffect(() => {
|
|
837
|
+
if (index === prevIndexRef.current) return;
|
|
617
838
|
prevIndexRef.current = index;
|
|
618
|
-
|
|
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]);
|
|
619
854
|
useSlideKeyboard({
|
|
620
855
|
total: deck.slides.length,
|
|
621
856
|
index,
|
|
622
857
|
goTo,
|
|
858
|
+
onAdvance: advance,
|
|
859
|
+
onRetreat: retreat,
|
|
623
860
|
onExit,
|
|
624
861
|
onBlank: () => setBlanked((b) => !b),
|
|
625
862
|
onFullscreen: () => {
|
|
@@ -632,11 +869,15 @@ function SlideViewer({
|
|
|
632
869
|
useEffect(() => {
|
|
633
870
|
if (!autoAdvanceMs || deck.slides.length <= 1) return;
|
|
634
871
|
const t = setTimeout(() => {
|
|
635
|
-
|
|
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
|
+
}
|
|
636
878
|
}, autoAdvanceMs);
|
|
637
879
|
return () => clearTimeout(t);
|
|
638
|
-
}, [autoAdvanceMs, index, deck.slides.length, goTo]);
|
|
639
|
-
const slide = deck.slides[index];
|
|
880
|
+
}, [autoAdvanceMs, index, deck.slides.length, goTo, buildStep, totalSteps]);
|
|
640
881
|
const theme = resolveTheme(deck.theme);
|
|
641
882
|
const aspectRatio = theme.aspectRatio ?? 16 / 9;
|
|
642
883
|
const transition = slide?.transition ?? theme.defaultTransition;
|
|
@@ -658,6 +899,11 @@ function SlideViewer({
|
|
|
658
899
|
},
|
|
659
900
|
tabIndex: 0,
|
|
660
901
|
"data-fancy-slides-viewer": deck.id,
|
|
902
|
+
"data-fancy-slides-build-step": buildStep,
|
|
903
|
+
onClick: () => {
|
|
904
|
+
if (blanked) return;
|
|
905
|
+
advance();
|
|
906
|
+
},
|
|
661
907
|
children: [
|
|
662
908
|
/* @__PURE__ */ jsx("style", { children: TRANSITION_KEYFRAMES }),
|
|
663
909
|
!blanked && slide && /* @__PURE__ */ jsx(
|
|
@@ -671,7 +917,7 @@ function SlideViewer({
|
|
|
671
917
|
["--fs-ratio"]: aspectRatio.toString(),
|
|
672
918
|
boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
|
|
673
919
|
},
|
|
674
|
-
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)
|
|
675
921
|
}
|
|
676
922
|
),
|
|
677
923
|
!hideChrome && !blanked && /* @__PURE__ */ jsxs(
|
|
@@ -702,7 +948,7 @@ function SlideViewer({
|
|
|
702
948
|
);
|
|
703
949
|
}
|
|
704
950
|
var DEFAULT_DURATION = 400;
|
|
705
|
-
var
|
|
951
|
+
var EASE2 = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
706
952
|
function transitionEnterStyle(transition, forward) {
|
|
707
953
|
const kind = transition?.kind ?? "none";
|
|
708
954
|
if (kind === "none") return { width: "100%", height: "100%" };
|
|
@@ -728,7 +974,7 @@ function transitionEnterStyle(transition, forward) {
|
|
|
728
974
|
height: "100%",
|
|
729
975
|
animationName: name,
|
|
730
976
|
animationDuration: `${duration}ms`,
|
|
731
|
-
animationTimingFunction:
|
|
977
|
+
animationTimingFunction: EASE2,
|
|
732
978
|
animationFillMode: "both"
|
|
733
979
|
};
|
|
734
980
|
}
|
|
@@ -784,14 +1030,38 @@ function PresenterView({
|
|
|
784
1030
|
},
|
|
785
1031
|
[deck.slides.length, isControlled, onIndexChange]
|
|
786
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]);
|
|
787
1056
|
useSlideKeyboard({
|
|
788
1057
|
total: deck.slides.length,
|
|
789
1058
|
index,
|
|
790
1059
|
goTo,
|
|
1060
|
+
onAdvance: advance,
|
|
1061
|
+
onRetreat: retreat,
|
|
791
1062
|
onExit
|
|
792
1063
|
});
|
|
793
1064
|
const theme = resolveTheme(deck.theme);
|
|
794
|
-
const slide = deck.slides[index];
|
|
795
1065
|
const nextSlide = deck.slides[index + 1];
|
|
796
1066
|
const [now, setNow] = useState(() => Date.now());
|
|
797
1067
|
useEffect(() => {
|
|
@@ -838,7 +1108,7 @@ function PresenterView({
|
|
|
838
1108
|
borderRadius: 8,
|
|
839
1109
|
overflow: "hidden"
|
|
840
1110
|
},
|
|
841
|
-
children: slide ? /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement }) : null
|
|
1111
|
+
children: slide ? /* @__PURE__ */ jsx(Slide, { slide, theme, buildStep, renderElement }) : null
|
|
842
1112
|
}
|
|
843
1113
|
)
|
|
844
1114
|
}
|
|
@@ -942,8 +1212,8 @@ function PresenterView({
|
|
|
942
1212
|
/* @__PURE__ */ jsx(StatusChip, { label: "Elapsed", children: formatElapsed(now - startedAtRef) }),
|
|
943
1213
|
/* @__PURE__ */ jsx(StatusChip, { label: "Clock", children: formatClock(now) }),
|
|
944
1214
|
/* @__PURE__ */ jsxs("div", { style: { marginLeft: "auto", display: "flex", gap: 8 }, children: [
|
|
945
|
-
/* @__PURE__ */ jsx(NavButton, { onClick:
|
|
946
|
-
/* @__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" })
|
|
947
1217
|
] })
|
|
948
1218
|
]
|
|
949
1219
|
}
|
|
@@ -1126,6 +1396,7 @@ function useDeckState({ value, onChange, onOp }) {
|
|
|
1126
1396
|
updateElement: (slideIdArg, elementIdArg, patch) => apply({ kind: "element_update", slideId: slideIdArg, elementId: elementIdArg, patch }),
|
|
1127
1397
|
moveElement: (slideIdArg, elementIdArg, x, y) => apply({ kind: "element_move", slideId: slideIdArg, elementId: elementIdArg, x, y }),
|
|
1128
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 }),
|
|
1129
1400
|
getSlide: (id) => value.slides.find((s) => s.id === id),
|
|
1130
1401
|
getElement: (slideIdArg, elementIdArg) => value.slides.find((s) => s.id === slideIdArg)?.elements.find((e) => e.id === elementIdArg)
|
|
1131
1402
|
};
|
|
@@ -1193,6 +1464,23 @@ function reduce(deck, op) {
|
|
|
1193
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
|
|
1194
1465
|
)
|
|
1195
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
|
+
};
|
|
1196
1484
|
}
|
|
1197
1485
|
}
|
|
1198
1486
|
|
|
@@ -1279,6 +1567,113 @@ function chartStarterOption(kind) {
|
|
|
1279
1567
|
};
|
|
1280
1568
|
}
|
|
1281
1569
|
}
|
|
1570
|
+
var CHART_PALETTE = [
|
|
1571
|
+
"#8b5cf6",
|
|
1572
|
+
"#3b82f6",
|
|
1573
|
+
"#10b981",
|
|
1574
|
+
"#f59e0b",
|
|
1575
|
+
"#ef4444",
|
|
1576
|
+
"#ec4899",
|
|
1577
|
+
"#14b8a6",
|
|
1578
|
+
"#6366f1"
|
|
1579
|
+
];
|
|
1580
|
+
function chartColorAt(index) {
|
|
1581
|
+
return CHART_PALETTE[index % CHART_PALETTE.length];
|
|
1582
|
+
}
|
|
1583
|
+
function isPlainObject(v) {
|
|
1584
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1585
|
+
}
|
|
1586
|
+
function toNumber(v) {
|
|
1587
|
+
const n = typeof v === "number" ? v : parseFloat(String(v));
|
|
1588
|
+
return Number.isFinite(n) ? n : 0;
|
|
1589
|
+
}
|
|
1590
|
+
function chartModelFromOption(option) {
|
|
1591
|
+
if (!isPlainObject(option)) return null;
|
|
1592
|
+
const seriesRaw = option.series;
|
|
1593
|
+
if (!Array.isArray(seriesRaw) || seriesRaw.length === 0) return null;
|
|
1594
|
+
if (!seriesRaw.every(isPlainObject)) return null;
|
|
1595
|
+
const types = seriesRaw.map((s) => String(s.type ?? ""));
|
|
1596
|
+
if (types[0] === "pie") {
|
|
1597
|
+
if (seriesRaw.length !== 1) return null;
|
|
1598
|
+
const data = seriesRaw[0].data;
|
|
1599
|
+
if (!Array.isArray(data)) return null;
|
|
1600
|
+
const slices = [];
|
|
1601
|
+
for (const d of data) {
|
|
1602
|
+
if (!isPlainObject(d)) return null;
|
|
1603
|
+
slices.push({ name: String(d.name ?? ""), value: toNumber(d.value) });
|
|
1604
|
+
}
|
|
1605
|
+
return { kind: "pie", categories: [], series: [], slices };
|
|
1606
|
+
}
|
|
1607
|
+
const cartesian = /* @__PURE__ */ new Set(["bar", "line", "scatter"]);
|
|
1608
|
+
if (!types.every((t) => cartesian.has(t))) return null;
|
|
1609
|
+
if (new Set(types).size !== 1) return null;
|
|
1610
|
+
const baseType = types[0];
|
|
1611
|
+
const isArea = baseType === "line" && seriesRaw.every((s) => isPlainObject(s.areaStyle) || s.areaStyle != null);
|
|
1612
|
+
const kind = baseType === "line" ? isArea ? "area" : "line" : baseType;
|
|
1613
|
+
const xAxis = option.xAxis;
|
|
1614
|
+
const axisData = isPlainObject(xAxis) ? xAxis.data : void 0;
|
|
1615
|
+
const categories = Array.isArray(axisData) ? axisData.map((c) => String(c)) : [];
|
|
1616
|
+
const firstData = seriesRaw[0].data;
|
|
1617
|
+
const valueCount = Array.isArray(firstData) ? firstData.length : 0;
|
|
1618
|
+
const cats = categories.length > 0 ? categories : Array.from({ length: valueCount }, (_, i) => String(i + 1));
|
|
1619
|
+
const series = [];
|
|
1620
|
+
for (const s of seriesRaw) {
|
|
1621
|
+
const data = s.data;
|
|
1622
|
+
if (!Array.isArray(data)) return null;
|
|
1623
|
+
const values = data.map((d) => {
|
|
1624
|
+
if (Array.isArray(d)) return toNumber(d[1]);
|
|
1625
|
+
if (isPlainObject(d)) return toNumber(d.value);
|
|
1626
|
+
return toNumber(d);
|
|
1627
|
+
});
|
|
1628
|
+
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 });
|
|
1629
|
+
}
|
|
1630
|
+
return { kind, categories: cats, series, slices: [] };
|
|
1631
|
+
}
|
|
1632
|
+
function chartOptionFromModel(model) {
|
|
1633
|
+
if (model.kind === "pie") {
|
|
1634
|
+
return {
|
|
1635
|
+
tooltip: { trigger: "item" },
|
|
1636
|
+
legend: { bottom: 0 },
|
|
1637
|
+
color: model.slices.map((_, i) => chartColorAt(i)),
|
|
1638
|
+
series: [
|
|
1639
|
+
{
|
|
1640
|
+
type: "pie",
|
|
1641
|
+
radius: ["40%", "70%"],
|
|
1642
|
+
name: "Segment",
|
|
1643
|
+
data: model.slices.map((s) => ({ name: s.name, value: s.value }))
|
|
1644
|
+
}
|
|
1645
|
+
]
|
|
1646
|
+
};
|
|
1647
|
+
}
|
|
1648
|
+
const isScatter = model.kind === "scatter";
|
|
1649
|
+
const isArea = model.kind === "area";
|
|
1650
|
+
const seriesType = model.kind === "bar" ? "bar" : model.kind === "scatter" ? "scatter" : "line";
|
|
1651
|
+
const series = model.series.map((s, i) => {
|
|
1652
|
+
const color = s.color ?? chartColorAt(i);
|
|
1653
|
+
const base = {
|
|
1654
|
+
type: seriesType,
|
|
1655
|
+
name: s.name,
|
|
1656
|
+
itemStyle: { color }
|
|
1657
|
+
};
|
|
1658
|
+
if (isScatter) {
|
|
1659
|
+
base.symbolSize = 12;
|
|
1660
|
+
base.data = s.values.map((v, idx) => [idx, v]);
|
|
1661
|
+
} else {
|
|
1662
|
+
base.data = s.values;
|
|
1663
|
+
}
|
|
1664
|
+
if (seriesType === "line") base.smooth = true;
|
|
1665
|
+
if (isArea) base.areaStyle = { color };
|
|
1666
|
+
return base;
|
|
1667
|
+
});
|
|
1668
|
+
return {
|
|
1669
|
+
grid: { top: 24, left: 56, right: 16, bottom: isScatter ? 32 : 40 },
|
|
1670
|
+
tooltip: { trigger: isScatter ? "item" : "axis" },
|
|
1671
|
+
legend: model.series.length > 1 ? { bottom: 0 } : void 0,
|
|
1672
|
+
xAxis: isScatter ? { type: "value" } : { type: "category", data: [...model.categories] },
|
|
1673
|
+
yAxis: { type: "value" },
|
|
1674
|
+
series
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1282
1677
|
function SlideRail({
|
|
1283
1678
|
slides,
|
|
1284
1679
|
selectedId,
|
|
@@ -1427,10 +1822,10 @@ function EditorToolbar({
|
|
|
1427
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" }) }) })
|
|
1428
1823
|
] });
|
|
1429
1824
|
}
|
|
1430
|
-
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
|
|
1825
|
+
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }) {
|
|
1431
1826
|
if (!element) {
|
|
1432
1827
|
if (slide) {
|
|
1433
|
-
return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
|
|
1828
|
+
return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetElementAnimation });
|
|
1434
1829
|
}
|
|
1435
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: [
|
|
1436
1831
|
/* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
|
|
@@ -1455,10 +1850,12 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1455
1850
|
/* @__PURE__ */ jsxs(Tabs.List, { children: [
|
|
1456
1851
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "style", children: "Style" }),
|
|
1457
1852
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "layout", children: "Layout" }),
|
|
1853
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "build", children: "Build" }),
|
|
1458
1854
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "advanced", children: "Advanced" })
|
|
1459
1855
|
] }),
|
|
1460
1856
|
/* @__PURE__ */ jsxs(Tabs.Panels, { children: [
|
|
1461
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 }) }) }),
|
|
1462
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 }) }) }),
|
|
1463
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 }) }) })
|
|
1464
1861
|
] })
|
|
@@ -1468,7 +1865,8 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1468
1865
|
function SlideSettings({
|
|
1469
1866
|
slide,
|
|
1470
1867
|
onSetTransition,
|
|
1471
|
-
onSetBackground
|
|
1868
|
+
onSetBackground,
|
|
1869
|
+
onSetElementAnimation
|
|
1472
1870
|
}) {
|
|
1473
1871
|
const transition = slide.transition;
|
|
1474
1872
|
const kind = transition?.kind ?? "none";
|
|
@@ -1535,10 +1933,54 @@ function SlideSettings({
|
|
|
1535
1933
|
onChange: (c) => onSetBackground({ ...slide.background, color: c })
|
|
1536
1934
|
}
|
|
1537
1935
|
) })
|
|
1538
|
-
] }) })
|
|
1936
|
+
] }) }),
|
|
1937
|
+
onSetElementAnimation && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(BuildOrderList, { slide, onSetElementAnimation }) })
|
|
1539
1938
|
] })
|
|
1540
1939
|
] });
|
|
1541
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
|
+
}
|
|
1542
1984
|
function LayoutSection({ element, onPatch }) {
|
|
1543
1985
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1544
1986
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
@@ -1560,6 +2002,100 @@ function AdvancedSection({ element, onPatch }) {
|
|
|
1560
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" }) })
|
|
1561
2003
|
] });
|
|
1562
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
|
+
}
|
|
1563
2099
|
function StyleSection({ element, onPatch }) {
|
|
1564
2100
|
switch (element.type) {
|
|
1565
2101
|
case "text":
|
|
@@ -1632,7 +2168,35 @@ function TextStyleControls({ element, onPatch }) {
|
|
|
1632
2168
|
] });
|
|
1633
2169
|
}
|
|
1634
2170
|
function ImageStyleControls({ element, onPatch }) {
|
|
2171
|
+
const fileRef = useRef(null);
|
|
2172
|
+
const crop = element.crop;
|
|
2173
|
+
const onFile = (file) => {
|
|
2174
|
+
if (!file) return;
|
|
2175
|
+
const reader = new FileReader();
|
|
2176
|
+
reader.onload = () => {
|
|
2177
|
+
if (typeof reader.result === "string") onPatch({ src: reader.result });
|
|
2178
|
+
};
|
|
2179
|
+
reader.readAsDataURL(file);
|
|
2180
|
+
};
|
|
2181
|
+
const setCrop = (next) => {
|
|
2182
|
+
const base = crop ?? { x: 0, y: 0, w: 1, h: 1 };
|
|
2183
|
+
onPatch({ crop: { ...base, ...next } });
|
|
2184
|
+
};
|
|
1635
2185
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2186
|
+
/* @__PURE__ */ jsx(
|
|
2187
|
+
"input",
|
|
2188
|
+
{
|
|
2189
|
+
ref: fileRef,
|
|
2190
|
+
type: "file",
|
|
2191
|
+
accept: "image/*",
|
|
2192
|
+
className: "hidden",
|
|
2193
|
+
onChange: (e) => {
|
|
2194
|
+
onFile(e.target.files?.[0]);
|
|
2195
|
+
e.target.value = "";
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
),
|
|
2199
|
+
/* @__PURE__ */ jsx(Action, { size: "sm", variant: "ghost", icon: "upload", onClick: () => fileRef.current?.click(), children: "Upload image" }),
|
|
1636
2200
|
/* @__PURE__ */ jsx(Textarea, { label: "Image URL", value: element.src, onValueChange: (v) => onPatch({ src: v }), rows: 2 }),
|
|
1637
2201
|
/* @__PURE__ */ jsx(Input, { label: "Alt text", value: element.alt ?? "", onChange: (e) => onPatch({ alt: e.target.value }) }),
|
|
1638
2202
|
/* @__PURE__ */ jsx(
|
|
@@ -1648,7 +2212,17 @@ function ImageStyleControls({ element, onPatch }) {
|
|
|
1648
2212
|
value: element.fit ?? "contain",
|
|
1649
2213
|
onValueChange: (v) => onPatch({ fit: v })
|
|
1650
2214
|
}
|
|
1651
|
-
)
|
|
2215
|
+
),
|
|
2216
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
2217
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
2218
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Crop" }),
|
|
2219
|
+
crop && /* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", onClick: () => onPatch({ crop: void 0 }), children: "Clear crop" })
|
|
2220
|
+
] }),
|
|
2221
|
+
/* @__PURE__ */ jsx(Slider, { label: "X", value: crop?.x ?? 0, onValueChange: (v) => setCrop({ x: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
2222
|
+
/* @__PURE__ */ jsx(Slider, { label: "Y", value: crop?.y ?? 0, onValueChange: (v) => setCrop({ y: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
2223
|
+
/* @__PURE__ */ jsx(Slider, { label: "Width", value: crop?.w ?? 1, onValueChange: (v) => setCrop({ w: Number(v) }), min: 0.01, max: 1, step: 0.01, showValue: true }),
|
|
2224
|
+
/* @__PURE__ */ jsx(Slider, { label: "Height", value: crop?.h ?? 1, onValueChange: (v) => setCrop({ h: Number(v) }), min: 0.01, max: 1, step: 0.01, showValue: true }),
|
|
2225
|
+
/* @__PURE__ */ jsx(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." })
|
|
1652
2226
|
] });
|
|
1653
2227
|
}
|
|
1654
2228
|
function ShapeStyleControls({ element, onPatch }) {
|
|
@@ -1695,54 +2269,254 @@ function CodeStyleControls({ element, onPatch }) {
|
|
|
1695
2269
|
] });
|
|
1696
2270
|
}
|
|
1697
2271
|
function ChartStyleControls({ element, onPatch }) {
|
|
2272
|
+
const model = chartModelFromOption(element.option);
|
|
2273
|
+
const writeModel = (m) => onPatch({ option: chartOptionFromModel(m) });
|
|
2274
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2275
|
+
model ? /* @__PURE__ */ jsx(ChartModelEditor, { model, onChange: writeModel }) : /* @__PURE__ */ jsx(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." }),
|
|
2276
|
+
/* @__PURE__ */ jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
2277
|
+
/* @__PURE__ */ 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" }),
|
|
2278
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 pt-0", children: /* @__PURE__ */ jsx(
|
|
2279
|
+
Textarea,
|
|
2280
|
+
{
|
|
2281
|
+
label: "ECharts option (JSON)",
|
|
2282
|
+
value: JSON.stringify(element.option, null, 2),
|
|
2283
|
+
onValueChange: (v) => {
|
|
2284
|
+
try {
|
|
2285
|
+
onPatch({ option: JSON.parse(v) });
|
|
2286
|
+
} catch {
|
|
2287
|
+
}
|
|
2288
|
+
},
|
|
2289
|
+
rows: 10
|
|
2290
|
+
}
|
|
2291
|
+
) })
|
|
2292
|
+
] })
|
|
2293
|
+
] });
|
|
2294
|
+
}
|
|
2295
|
+
var CHART_TYPE_OPTIONS = [
|
|
2296
|
+
{ value: "bar", label: "Bar" },
|
|
2297
|
+
{ value: "line", label: "Line" },
|
|
2298
|
+
{ value: "area", label: "Area" },
|
|
2299
|
+
{ value: "pie", label: "Pie" },
|
|
2300
|
+
{ value: "scatter", label: "Scatter" }
|
|
2301
|
+
];
|
|
2302
|
+
function ChartModelEditor({ model, onChange }) {
|
|
2303
|
+
const setKind = (kind) => {
|
|
2304
|
+
if (kind === model.kind) return;
|
|
2305
|
+
if (kind === "pie") {
|
|
2306
|
+
const first = model.series[0];
|
|
2307
|
+
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 }];
|
|
2308
|
+
onChange({ ...model, kind, slices });
|
|
2309
|
+
return;
|
|
2310
|
+
}
|
|
2311
|
+
if (model.kind === "pie") {
|
|
2312
|
+
const categories = model.slices.length ? model.slices.map((s) => s.name) : ["A", "B", "C"];
|
|
2313
|
+
const values = model.slices.length ? model.slices.map((s) => s.value) : [1, 2, 3];
|
|
2314
|
+
onChange({ ...model, kind, categories, series: [{ name: "Series 1", color: chartColorAt(0), values }] });
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
onChange({ ...model, kind });
|
|
2318
|
+
};
|
|
1698
2319
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1699
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", className: "!text-zinc-500", children: "Chart option is JSON \u2014 paste any ECharts option here." }),
|
|
1700
2320
|
/* @__PURE__ */ jsx(
|
|
1701
|
-
|
|
2321
|
+
Select,
|
|
1702
2322
|
{
|
|
1703
|
-
label: "
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
onPatch({ option: JSON.parse(v) });
|
|
1708
|
-
} catch {
|
|
1709
|
-
}
|
|
1710
|
-
},
|
|
1711
|
-
rows: 10
|
|
2323
|
+
label: "Chart type",
|
|
2324
|
+
list: CHART_TYPE_OPTIONS,
|
|
2325
|
+
value: model.kind,
|
|
2326
|
+
onValueChange: (v) => setKind(v)
|
|
1712
2327
|
}
|
|
1713
|
-
)
|
|
2328
|
+
),
|
|
2329
|
+
model.kind === "pie" ? /* @__PURE__ */ jsx(PieSliceEditor, { model, onChange }) : /* @__PURE__ */ jsx(CartesianChartEditor, { model, onChange })
|
|
2330
|
+
] });
|
|
2331
|
+
}
|
|
2332
|
+
function PieSliceEditor({ model, onChange }) {
|
|
2333
|
+
const slices = model.slices;
|
|
2334
|
+
const update = (i, next) => {
|
|
2335
|
+
const copy = slices.map((s, idx) => idx === i ? { ...s, ...next } : s);
|
|
2336
|
+
onChange({ ...model, slices: copy });
|
|
2337
|
+
};
|
|
2338
|
+
const remove = (i) => onChange({ ...model, slices: slices.filter((_, idx) => idx !== i) });
|
|
2339
|
+
const add = () => onChange({ ...model, slices: [...slices, { name: `Slice ${slices.length + 1}`, value: 0 }] });
|
|
2340
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2341
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Slices" }),
|
|
2342
|
+
slices.map((s, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
2343
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Input, { label: i === 0 ? "Name" : void 0, value: s.name, onChange: (e) => update(i, { name: e.target.value }) }) }),
|
|
2344
|
+
/* @__PURE__ */ jsx("div", { className: "w-20", children: /* @__PURE__ */ jsx(Input, { label: i === 0 ? "Value" : void 0, type: "number", value: String(s.value), onChange: (e) => update(i, { value: parseFloat(e.target.value) || 0 }) }) }),
|
|
2345
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => remove(i), "aria-label": "Remove slice" })
|
|
2346
|
+
] }, i)),
|
|
2347
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: add, children: "Add slice" })
|
|
2348
|
+
] });
|
|
2349
|
+
}
|
|
2350
|
+
function CartesianChartEditor({ model, onChange }) {
|
|
2351
|
+
const { categories, series } = model;
|
|
2352
|
+
const updateCategory = (i, label) => {
|
|
2353
|
+
onChange({ ...model, categories: categories.map((c, idx) => idx === i ? label : c) });
|
|
2354
|
+
};
|
|
2355
|
+
const removeCategory = (i) => {
|
|
2356
|
+
onChange({
|
|
2357
|
+
...model,
|
|
2358
|
+
categories: categories.filter((_, idx) => idx !== i),
|
|
2359
|
+
series: series.map((s) => ({ ...s, values: s.values.filter((_, idx) => idx !== i) }))
|
|
2360
|
+
});
|
|
2361
|
+
};
|
|
2362
|
+
const addCategory = () => {
|
|
2363
|
+
onChange({
|
|
2364
|
+
...model,
|
|
2365
|
+
categories: [...categories, `Cat ${categories.length + 1}`],
|
|
2366
|
+
series: series.map((s) => ({ ...s, values: [...s.values, 0] }))
|
|
2367
|
+
});
|
|
2368
|
+
};
|
|
2369
|
+
const updateSeries = (si, next) => {
|
|
2370
|
+
onChange({ ...model, series: series.map((s, idx) => idx === si ? { ...s, ...next } : s) });
|
|
2371
|
+
};
|
|
2372
|
+
const updateValue = (si, ci, value) => {
|
|
2373
|
+
onChange({
|
|
2374
|
+
...model,
|
|
2375
|
+
series: series.map(
|
|
2376
|
+
(s, idx) => idx === si ? { ...s, values: s.values.map((v, vi) => vi === ci ? value : v) } : s
|
|
2377
|
+
)
|
|
2378
|
+
});
|
|
2379
|
+
};
|
|
2380
|
+
const removeSeries = (si) => onChange({ ...model, series: series.filter((_, idx) => idx !== si) });
|
|
2381
|
+
const addSeries = () => onChange({
|
|
2382
|
+
...model,
|
|
2383
|
+
series: [
|
|
2384
|
+
...series,
|
|
2385
|
+
{ name: `Series ${series.length + 1}`, color: chartColorAt(series.length), values: categories.map(() => 0) }
|
|
2386
|
+
]
|
|
2387
|
+
});
|
|
2388
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2389
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2390
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Categories" }),
|
|
2391
|
+
categories.map((c, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2392
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Input, { value: c, onChange: (e) => updateCategory(i, e.target.value) }) }),
|
|
2393
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeCategory(i), "aria-label": "Remove category" })
|
|
2394
|
+
] }, i)),
|
|
2395
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addCategory, children: "Add category" })
|
|
2396
|
+
] }),
|
|
2397
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
2398
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2399
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Series" }),
|
|
2400
|
+
series.map((s, si) => /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-zinc-200 p-2 dark:border-zinc-800", children: [
|
|
2401
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
2402
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Input, { label: "Name", value: s.name, onChange: (e) => updateSeries(si, { name: e.target.value }) }) }),
|
|
2403
|
+
/* @__PURE__ */ jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsx(ColorPicker, { value: s.color ?? chartColorAt(si), onChange: (c) => updateSeries(si, { color: c }) }) }),
|
|
2404
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeSeries(si), "aria-label": "Remove series" })
|
|
2405
|
+
] }),
|
|
2406
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: categories.map((c, ci) => /* @__PURE__ */ jsx(
|
|
2407
|
+
Input,
|
|
2408
|
+
{
|
|
2409
|
+
label: c,
|
|
2410
|
+
type: "number",
|
|
2411
|
+
value: String(s.values[ci] ?? 0),
|
|
2412
|
+
onChange: (e) => updateValue(si, ci, parseFloat(e.target.value) || 0)
|
|
2413
|
+
},
|
|
2414
|
+
ci
|
|
2415
|
+
)) })
|
|
2416
|
+
] }, si)),
|
|
2417
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addSeries, children: "Add series" })
|
|
2418
|
+
] })
|
|
1714
2419
|
] });
|
|
1715
2420
|
}
|
|
1716
2421
|
function TableStyleControls({ element, onPatch }) {
|
|
2422
|
+
const columns = element.columns;
|
|
2423
|
+
const rows = element.rows;
|
|
2424
|
+
const nextColKey = () => {
|
|
2425
|
+
const existing = new Set(columns.map((c) => c.key));
|
|
2426
|
+
let n = columns.length + 1;
|
|
2427
|
+
while (existing.has(`col${n}`)) n++;
|
|
2428
|
+
return `col${n}`;
|
|
2429
|
+
};
|
|
2430
|
+
const setColumnLabel = (i, label) => {
|
|
2431
|
+
onPatch({ columns: columns.map((c, idx) => idx === i ? { ...c, label } : c) });
|
|
2432
|
+
};
|
|
2433
|
+
const removeColumn = (i) => {
|
|
2434
|
+
const key = columns[i]?.key;
|
|
2435
|
+
const nextCols = columns.filter((_, idx) => idx !== i);
|
|
2436
|
+
const nextRows = key ? rows.map((r) => {
|
|
2437
|
+
const { [key]: _drop, ...rest } = r;
|
|
2438
|
+
return rest;
|
|
2439
|
+
}) : rows;
|
|
2440
|
+
onPatch({ columns: nextCols, rows: nextRows });
|
|
2441
|
+
};
|
|
2442
|
+
const addColumn = () => {
|
|
2443
|
+
const key = nextColKey();
|
|
2444
|
+
onPatch({
|
|
2445
|
+
columns: [...columns, { key, label: `Column ${columns.length + 1}` }],
|
|
2446
|
+
rows: rows.map((r) => ({ ...r, [key]: "" }))
|
|
2447
|
+
});
|
|
2448
|
+
};
|
|
2449
|
+
const setCell = (rowIdx, key, value) => {
|
|
2450
|
+
onPatch({ rows: rows.map((r, idx) => idx === rowIdx ? { ...r, [key]: value } : r) });
|
|
2451
|
+
};
|
|
2452
|
+
const removeRow = (rowIdx) => onPatch({ rows: rows.filter((_, idx) => idx !== rowIdx) });
|
|
2453
|
+
const addRow = () => {
|
|
2454
|
+
const blank = {};
|
|
2455
|
+
for (const c of columns) blank[c.key] = "";
|
|
2456
|
+
onPatch({ rows: [...rows, blank] });
|
|
2457
|
+
};
|
|
1717
2458
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1718
|
-
/* @__PURE__ */
|
|
1719
|
-
|
|
1720
|
-
{
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
2459
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2460
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Columns" }),
|
|
2461
|
+
columns.map((c, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2462
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Input, { value: c.label, onChange: (e) => setColumnLabel(i, e.target.value), "aria-label": `Column ${i + 1} label` }) }),
|
|
2463
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", className: "!font-mono !text-zinc-400", children: c.key }),
|
|
2464
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeColumn(i), "aria-label": "Remove column" })
|
|
2465
|
+
] }, c.key)),
|
|
2466
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addColumn, children: "Add column" })
|
|
2467
|
+
] }),
|
|
2468
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
2469
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2470
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Rows" }),
|
|
2471
|
+
columns.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", className: "!text-zinc-500", children: "Add a column to start adding rows." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2472
|
+
rows.map((r, rowIdx) => /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 border-b border-zinc-100 pb-2 dark:border-zinc-800", children: [
|
|
2473
|
+
/* @__PURE__ */ jsx("div", { className: "grid flex-1 grid-cols-1 gap-1", children: columns.map((c) => /* @__PURE__ */ jsx(
|
|
2474
|
+
Input,
|
|
2475
|
+
{
|
|
2476
|
+
label: c.label,
|
|
2477
|
+
value: r[c.key] == null ? "" : String(r[c.key]),
|
|
2478
|
+
onChange: (e) => setCell(rowIdx, c.key, e.target.value)
|
|
2479
|
+
},
|
|
2480
|
+
c.key
|
|
2481
|
+
)) }),
|
|
2482
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeRow(rowIdx), "aria-label": "Remove row" })
|
|
2483
|
+
] }, rowIdx)),
|
|
2484
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addRow, children: "Add row" })
|
|
2485
|
+
] })
|
|
2486
|
+
] }),
|
|
2487
|
+
/* @__PURE__ */ jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
2488
|
+
/* @__PURE__ */ 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" }),
|
|
2489
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3 p-2 pt-0", children: [
|
|
2490
|
+
/* @__PURE__ */ jsx(
|
|
2491
|
+
Textarea,
|
|
2492
|
+
{
|
|
2493
|
+
label: "Columns (JSON)",
|
|
2494
|
+
value: JSON.stringify(columns, null, 2),
|
|
2495
|
+
onValueChange: (v) => {
|
|
2496
|
+
try {
|
|
2497
|
+
onPatch({ columns: JSON.parse(v) });
|
|
2498
|
+
} catch {
|
|
2499
|
+
}
|
|
2500
|
+
},
|
|
2501
|
+
rows: 5
|
|
1727
2502
|
}
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
2503
|
+
),
|
|
2504
|
+
/* @__PURE__ */ jsx(
|
|
2505
|
+
Textarea,
|
|
2506
|
+
{
|
|
2507
|
+
label: "Rows (JSON)",
|
|
2508
|
+
value: JSON.stringify(rows, null, 2),
|
|
2509
|
+
onValueChange: (v) => {
|
|
2510
|
+
try {
|
|
2511
|
+
onPatch({ rows: JSON.parse(v) });
|
|
2512
|
+
} catch {
|
|
2513
|
+
}
|
|
2514
|
+
},
|
|
2515
|
+
rows: 8
|
|
1741
2516
|
}
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
)
|
|
2517
|
+
)
|
|
2518
|
+
] })
|
|
2519
|
+
] })
|
|
1746
2520
|
] });
|
|
1747
2521
|
}
|
|
1748
2522
|
function EmbedStyleControls({ element, onPatch }) {
|
|
@@ -1997,7 +2771,9 @@ function DeckEditor({
|
|
|
1997
2771
|
},
|
|
1998
2772
|
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
|
|
1999
2773
|
onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
|
|
2000
|
-
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)
|
|
2001
2777
|
}
|
|
2002
2778
|
) })
|
|
2003
2779
|
] }),
|
|
@@ -2007,6 +2783,6 @@ function DeckEditor({
|
|
|
2007
2783
|
);
|
|
2008
2784
|
}
|
|
2009
2785
|
|
|
2010
|
-
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 };
|
|
2011
2787
|
//# sourceMappingURL=index.js.map
|
|
2012
2788
|
//# sourceMappingURL=index.js.map
|