@particle-academy/fancy-slides 0.3.0 → 0.5.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 +35 -0
- package/dist/index.cjs +570 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +139 -10
- package/dist/index.d.ts +139 -10
- package/dist/index.js +563 -65
- 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-9BbelJX1.d.cts} +42 -1
- package/dist/{types-B2ecrEAz.d.ts → types-9BbelJX1.d.ts} +42 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isDarkColor, SlideContext } from './chunk-WIUXPQAK.js';
|
|
2
2
|
export { useIsDarkSlide, useSlideContext, useSlideTheme } from './chunk-WIUXPQAK.js';
|
|
3
3
|
import { useId, useRef, useState, useEffect, useMemo, useCallback } from 'react';
|
|
4
|
-
import { ContentRenderer, Text, Action, ContextMenu, Separator, Tooltip, Dropdown, Badge, Heading, Tabs, Card, Select, Input, ColorPicker, Slider, Textarea } from '@particle-academy/react-fancy';
|
|
4
|
+
import { ContentRenderer, Text, Action, ContextMenu, Separator, Tooltip, Dropdown, Badge, Heading, Tabs, Card, Select, Input, ColorPicker, Slider, Switch, Textarea } from '@particle-academy/react-fancy';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
6
|
|
|
7
7
|
// src/theme/default-theme.ts
|
|
@@ -70,13 +70,211 @@ function resolveTheme(theme) {
|
|
|
70
70
|
function cn(...parts) {
|
|
71
71
|
return parts.filter(Boolean).join(" ");
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
// src/utils/builds.ts
|
|
75
|
+
function splitParagraphs(content) {
|
|
76
|
+
const lines = content.split("\n");
|
|
77
|
+
if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
|
|
78
|
+
return lines;
|
|
79
|
+
}
|
|
80
|
+
function isByParagraph(element, animation) {
|
|
81
|
+
if (!animation.byParagraph || element.type !== "text") return false;
|
|
82
|
+
return splitParagraphs(element.content).length > 1;
|
|
83
|
+
}
|
|
84
|
+
var DEFAULT_BUILD_DURATION = 500;
|
|
85
|
+
function collectBuilds(slide) {
|
|
86
|
+
if (!slide) return [];
|
|
87
|
+
const builds = [];
|
|
88
|
+
slide.elements.forEach((element, index) => {
|
|
89
|
+
if (element.animation) {
|
|
90
|
+
builds.push({ element, animation: element.animation, index });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const ordered = builds.sort((a, b) => {
|
|
94
|
+
const ao = a.animation.order ?? 0;
|
|
95
|
+
const bo = b.animation.order ?? 0;
|
|
96
|
+
if (ao !== bo) return ao - bo;
|
|
97
|
+
return a.index - b.index;
|
|
98
|
+
});
|
|
99
|
+
const expanded = [];
|
|
100
|
+
for (const build of ordered) {
|
|
101
|
+
if (isByParagraph(build.element, build.animation)) {
|
|
102
|
+
const paras = splitParagraphs(build.element.content);
|
|
103
|
+
paras.forEach((_, paraIndex) => {
|
|
104
|
+
const animation = paraIndex === 0 ? build.animation : { ...build.animation, trigger: "on-click" };
|
|
105
|
+
expanded.push({ element: build.element, animation, index: build.index, paraIndex });
|
|
106
|
+
});
|
|
107
|
+
} else {
|
|
108
|
+
expanded.push(build);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return expanded;
|
|
112
|
+
}
|
|
113
|
+
function buildSteps(slide) {
|
|
114
|
+
const builds = collectBuilds(slide);
|
|
115
|
+
const steps = [];
|
|
116
|
+
for (const build of builds) {
|
|
117
|
+
const trigger = build.animation.trigger ?? "on-click";
|
|
118
|
+
if (steps.length === 0 || trigger === "on-click") {
|
|
119
|
+
steps.push({ builds: [build] });
|
|
120
|
+
} else {
|
|
121
|
+
steps[steps.length - 1].builds.push(build);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return steps;
|
|
125
|
+
}
|
|
126
|
+
function totalBuildSteps(slide) {
|
|
127
|
+
return buildSteps(slide).length;
|
|
128
|
+
}
|
|
129
|
+
function visibleElementIds(slide, buildStep) {
|
|
130
|
+
const visible = /* @__PURE__ */ new Set();
|
|
131
|
+
if (!slide) return visible;
|
|
132
|
+
const steps = buildSteps(slide);
|
|
133
|
+
const stepOfElement = /* @__PURE__ */ new Map();
|
|
134
|
+
steps.forEach((step, i) => {
|
|
135
|
+
for (const b of step.builds) {
|
|
136
|
+
if (!stepOfElement.has(b.element.id)) stepOfElement.set(b.element.id, i + 1);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
for (const element of slide.elements) {
|
|
140
|
+
const revealStep = stepOfElement.get(element.id);
|
|
141
|
+
if (revealStep === void 0) {
|
|
142
|
+
visible.add(element.id);
|
|
143
|
+
} else if (buildStep >= revealStep) {
|
|
144
|
+
visible.add(element.id);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return visible;
|
|
148
|
+
}
|
|
149
|
+
function paragraphReveals(slide, buildStep) {
|
|
150
|
+
const out = /* @__PURE__ */ new Map();
|
|
151
|
+
if (!slide) return out;
|
|
152
|
+
const steps = buildSteps(slide);
|
|
153
|
+
steps.forEach((step, i) => {
|
|
154
|
+
const stepNum = i + 1;
|
|
155
|
+
for (const b of step.builds) {
|
|
156
|
+
if (b.paraIndex === void 0) continue;
|
|
157
|
+
const fired = buildStep >= stepNum;
|
|
158
|
+
const prev = out.get(b.element.id) ?? { revealed: 0 };
|
|
159
|
+
if (fired) {
|
|
160
|
+
prev.revealed = Math.max(prev.revealed, b.paraIndex + 1);
|
|
161
|
+
if (stepNum === buildStep) prev.firingParaIndex = b.paraIndex;
|
|
162
|
+
}
|
|
163
|
+
out.set(b.element.id, prev);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
function buildsForStep(slide, buildStep) {
|
|
169
|
+
const steps = buildSteps(slide);
|
|
170
|
+
const step = steps[buildStep - 1];
|
|
171
|
+
return step ? step.builds : [];
|
|
172
|
+
}
|
|
173
|
+
function stepDelays(builds) {
|
|
174
|
+
const delays = /* @__PURE__ */ new Map();
|
|
175
|
+
const lead = builds[0];
|
|
176
|
+
if (!lead) return delays;
|
|
177
|
+
const leadDelay = lead.animation.delay ?? 0;
|
|
178
|
+
const leadDuration = lead.animation.duration ?? DEFAULT_BUILD_DURATION;
|
|
179
|
+
delays.set(lead.element.id, leadDelay);
|
|
180
|
+
for (let i = 1; i < builds.length; i++) {
|
|
181
|
+
const b = builds[i];
|
|
182
|
+
const own = b.animation.delay ?? 0;
|
|
183
|
+
const trigger = b.animation.trigger ?? "on-click";
|
|
184
|
+
const base = trigger === "after-prev" ? leadDelay + leadDuration : leadDelay;
|
|
185
|
+
delays.set(b.element.id, base + own);
|
|
186
|
+
}
|
|
187
|
+
return delays;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/components/Slide/builds-style.ts
|
|
191
|
+
var DEFAULT_BUILD_DURATION2 = 500;
|
|
192
|
+
var EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
193
|
+
function buildEnterStyle(animation, effectiveDelay) {
|
|
194
|
+
const duration = animation.duration ?? DEFAULT_BUILD_DURATION2;
|
|
195
|
+
const dir = animation.direction ?? "left";
|
|
196
|
+
let name;
|
|
197
|
+
switch (animation.effect) {
|
|
198
|
+
case "fade":
|
|
199
|
+
name = "fs-build-fade";
|
|
200
|
+
break;
|
|
201
|
+
case "zoom":
|
|
202
|
+
name = "fs-build-zoom";
|
|
203
|
+
break;
|
|
204
|
+
case "fly-in":
|
|
205
|
+
name = `fs-build-fly-${dir}`;
|
|
206
|
+
break;
|
|
207
|
+
case "wipe":
|
|
208
|
+
name = `fs-build-wipe-${dir}`;
|
|
209
|
+
break;
|
|
210
|
+
default:
|
|
211
|
+
name = "fs-build-fade";
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
animationName: name,
|
|
215
|
+
animationDuration: `${duration}ms`,
|
|
216
|
+
animationDelay: `${effectiveDelay}ms`,
|
|
217
|
+
animationTimingFunction: EASE,
|
|
218
|
+
animationFillMode: "both"
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
var BUILD_KEYFRAMES = `
|
|
222
|
+
@media (prefers-reduced-motion: reduce) {
|
|
223
|
+
.fs-build-enter { animation: none !important; }
|
|
224
|
+
}
|
|
225
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
226
|
+
@keyframes fs-build-fade {
|
|
227
|
+
from { opacity: 0; }
|
|
228
|
+
to { opacity: 1; }
|
|
229
|
+
}
|
|
230
|
+
@keyframes fs-build-zoom {
|
|
231
|
+
from { opacity: 0; transform: scale(0.8); }
|
|
232
|
+
to { opacity: 1; transform: scale(1); }
|
|
233
|
+
}
|
|
234
|
+
@keyframes fs-build-fly-left {
|
|
235
|
+
from { opacity: 0; transform: translateX(-24%); }
|
|
236
|
+
to { opacity: 1; transform: translateX(0); }
|
|
237
|
+
}
|
|
238
|
+
@keyframes fs-build-fly-right {
|
|
239
|
+
from { opacity: 0; transform: translateX(24%); }
|
|
240
|
+
to { opacity: 1; transform: translateX(0); }
|
|
241
|
+
}
|
|
242
|
+
@keyframes fs-build-fly-up {
|
|
243
|
+
from { opacity: 0; transform: translateY(24%); }
|
|
244
|
+
to { opacity: 1; transform: translateY(0); }
|
|
245
|
+
}
|
|
246
|
+
@keyframes fs-build-fly-down {
|
|
247
|
+
from { opacity: 0; transform: translateY(-24%); }
|
|
248
|
+
to { opacity: 1; transform: translateY(0); }
|
|
249
|
+
}
|
|
250
|
+
/* wipe: clip-path inset reveals from the named edge toward the opposite one.
|
|
251
|
+
inset(top right bottom left) \u2014 start fully clipped on the far side. */
|
|
252
|
+
@keyframes fs-build-wipe-left {
|
|
253
|
+
from { clip-path: inset(0 100% 0 0); }
|
|
254
|
+
to { clip-path: inset(0 0 0 0); }
|
|
255
|
+
}
|
|
256
|
+
@keyframes fs-build-wipe-right {
|
|
257
|
+
from { clip-path: inset(0 0 0 100%); }
|
|
258
|
+
to { clip-path: inset(0 0 0 0); }
|
|
259
|
+
}
|
|
260
|
+
@keyframes fs-build-wipe-up {
|
|
261
|
+
from { clip-path: inset(100% 0 0 0); }
|
|
262
|
+
to { clip-path: inset(0 0 0 0); }
|
|
263
|
+
}
|
|
264
|
+
@keyframes fs-build-wipe-down {
|
|
265
|
+
from { clip-path: inset(0 0 100% 0); }
|
|
266
|
+
to { clip-path: inset(0 0 0 0); }
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
`;
|
|
73
270
|
function TextElementRenderer({
|
|
74
271
|
element,
|
|
75
272
|
theme,
|
|
76
273
|
slideWidthPx,
|
|
77
274
|
editing = false,
|
|
78
275
|
selected = false,
|
|
79
|
-
onContentChange
|
|
276
|
+
onContentChange,
|
|
277
|
+
paraReveal
|
|
80
278
|
}) {
|
|
81
279
|
const t = resolveTheme(theme);
|
|
82
280
|
const style = element.style ?? {};
|
|
@@ -123,30 +321,53 @@ function TextElementRenderer({
|
|
|
123
321
|
}
|
|
124
322
|
);
|
|
125
323
|
}
|
|
324
|
+
const proseScope = `[data-fs-text-scope="${scopeId}"]`;
|
|
325
|
+
const doubleScope = `${proseScope}${proseScope}`;
|
|
326
|
+
const proseStyle = /* @__PURE__ */ jsx("style", { children: `
|
|
327
|
+
${proseScope} > div { width: 100%; height: 100%; font-size: inherit; }
|
|
328
|
+
${doubleScope} :is(p, ul, ol, li, blockquote, h1, h2, h3, h4, h5, h6, pre, code, strong, em, a) {
|
|
329
|
+
font-size: inherit;
|
|
330
|
+
}
|
|
331
|
+
${doubleScope} h1 { font-size: 1.6em; font-weight: 700; }
|
|
332
|
+
${doubleScope} h2 { font-size: 1.35em; font-weight: 700; }
|
|
333
|
+
${doubleScope} h3 { font-size: 1.15em; font-weight: 600; }
|
|
334
|
+
${proseScope} :where(p, ul, ol, h1, h2, h3, h4, h5, h6, pre, blockquote) {
|
|
335
|
+
margin: 0;
|
|
336
|
+
padding: 0;
|
|
337
|
+
}
|
|
338
|
+
${proseScope} :where(p, li) + :where(p, li, ul, ol) { margin-top: 0.4em; }
|
|
339
|
+
${proseScope} :where(ul, ol) { padding-left: 1.4em; }
|
|
340
|
+
${proseScope} :where(strong) { font-weight: ${Math.max(700, weight(style.weight) ?? 400 + 200)}; }
|
|
341
|
+
${proseScope} :where(a) { color: inherit; text-decoration: underline; }
|
|
342
|
+
${proseScope} :where(code) { font-family: ${t.fonts?.mono ?? "monospace"}; }
|
|
343
|
+
` });
|
|
344
|
+
const renderChunk = (content) => format === "plain" ? content : /* @__PURE__ */ jsx(ContentRenderer, { value: content, format: format === "html" ? "html" : "markdown" });
|
|
345
|
+
if (paraReveal) {
|
|
346
|
+
const paras = splitParagraphs(element.content);
|
|
347
|
+
return /* @__PURE__ */ jsxs("div", { "data-fs-text-scope": scopeId, style: css, children: [
|
|
348
|
+
proseStyle,
|
|
349
|
+
paras.map((para, i) => {
|
|
350
|
+
if (i >= paraReveal.revealed) return null;
|
|
351
|
+
const firing = i === paraReveal.firingParaIndex && !!element.animation;
|
|
352
|
+
const enter = firing ? buildEnterStyle(element.animation, element.animation.delay ?? 0) : null;
|
|
353
|
+
return /* @__PURE__ */ jsx(
|
|
354
|
+
"div",
|
|
355
|
+
{
|
|
356
|
+
className: firing ? "fs-build-enter" : void 0,
|
|
357
|
+
style: { whiteSpace: format === "plain" ? "pre-wrap" : "normal", ...enter },
|
|
358
|
+
"data-fancy-slides-paragraph": i,
|
|
359
|
+
children: renderChunk(para)
|
|
360
|
+
},
|
|
361
|
+
i
|
|
362
|
+
);
|
|
363
|
+
})
|
|
364
|
+
] });
|
|
365
|
+
}
|
|
126
366
|
if (format === "plain") {
|
|
127
367
|
return /* @__PURE__ */ jsx("div", { style: css, children: element.content });
|
|
128
368
|
}
|
|
129
|
-
const proseScope = `[data-fs-text-scope="${scopeId}"]`;
|
|
130
|
-
const doubleScope = `${proseScope}${proseScope}`;
|
|
131
369
|
return /* @__PURE__ */ jsxs("div", { "data-fs-text-scope": scopeId, style: css, children: [
|
|
132
|
-
|
|
133
|
-
${proseScope} > div { width: 100%; height: 100%; font-size: inherit; }
|
|
134
|
-
${doubleScope} :is(p, ul, ol, li, blockquote, h1, h2, h3, h4, h5, h6, pre, code, strong, em, a) {
|
|
135
|
-
font-size: inherit;
|
|
136
|
-
}
|
|
137
|
-
${doubleScope} h1 { font-size: 1.6em; font-weight: 700; }
|
|
138
|
-
${doubleScope} h2 { font-size: 1.35em; font-weight: 700; }
|
|
139
|
-
${doubleScope} h3 { font-size: 1.15em; font-weight: 600; }
|
|
140
|
-
${proseScope} :where(p, ul, ol, h1, h2, h3, h4, h5, h6, pre, blockquote) {
|
|
141
|
-
margin: 0;
|
|
142
|
-
padding: 0;
|
|
143
|
-
}
|
|
144
|
-
${proseScope} :where(p, li) + :where(p, li, ul, ol) { margin-top: 0.4em; }
|
|
145
|
-
${proseScope} :where(ul, ol) { padding-left: 1.4em; }
|
|
146
|
-
${proseScope} :where(strong) { font-weight: ${Math.max(700, weight(style.weight) ?? 400 + 200)}; }
|
|
147
|
-
${proseScope} :where(a) { color: inherit; text-decoration: underline; }
|
|
148
|
-
${proseScope} :where(code) { font-family: ${t.fonts?.mono ?? "monospace"}; }
|
|
149
|
-
` }),
|
|
370
|
+
proseStyle,
|
|
150
371
|
/* @__PURE__ */ jsx(ContentRenderer, { value: element.content, format: format === "html" ? "html" : "markdown" })
|
|
151
372
|
] });
|
|
152
373
|
}
|
|
@@ -244,6 +465,7 @@ function Slide({
|
|
|
244
465
|
width,
|
|
245
466
|
aspectRatio,
|
|
246
467
|
editing = false,
|
|
468
|
+
buildStep,
|
|
247
469
|
onElementContentChange,
|
|
248
470
|
onElementSelect,
|
|
249
471
|
selectedElementId,
|
|
@@ -287,7 +509,24 @@ function Slide({
|
|
|
287
509
|
}),
|
|
288
510
|
[t, effectiveBg, slideWidthPx]
|
|
289
511
|
);
|
|
290
|
-
|
|
512
|
+
const buildInfo = useMemo(() => {
|
|
513
|
+
if (editing) return null;
|
|
514
|
+
const steps = buildSteps(slide);
|
|
515
|
+
if (steps.length === 0) return null;
|
|
516
|
+
const revealStep = /* @__PURE__ */ new Map();
|
|
517
|
+
steps.forEach((step, i) => {
|
|
518
|
+
for (const b of step.builds) {
|
|
519
|
+
if (!revealStep.has(b.element.id)) revealStep.set(b.element.id, i + 1);
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
const driven = buildStep !== void 0;
|
|
523
|
+
const currentStep = driven ? buildStep : steps.length;
|
|
524
|
+
const firing = driven ? steps[currentStep - 1] : void 0;
|
|
525
|
+
const delays = firing ? stepDelays(firing.builds) : /* @__PURE__ */ new Map();
|
|
526
|
+
const paraReveals = driven ? paragraphReveals(slide, currentStep) : /* @__PURE__ */ new Map();
|
|
527
|
+
return { revealStep, currentStep, delays, paraReveals, driven };
|
|
528
|
+
}, [editing, slide, buildStep]);
|
|
529
|
+
return /* @__PURE__ */ jsx(SlideContext.Provider, { value: slideContext, children: /* @__PURE__ */ jsxs(
|
|
291
530
|
"div",
|
|
292
531
|
{
|
|
293
532
|
ref,
|
|
@@ -305,23 +544,47 @@ function Slide({
|
|
|
305
544
|
onClick: (e) => {
|
|
306
545
|
if (e.target === e.currentTarget && onElementSelect) onElementSelect(null);
|
|
307
546
|
},
|
|
308
|
-
children:
|
|
309
|
-
|
|
310
|
-
{
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
547
|
+
children: [
|
|
548
|
+
buildInfo && /* @__PURE__ */ jsx("style", { children: BUILD_KEYFRAMES }),
|
|
549
|
+
orderedElements(slide.elements).map((element) => {
|
|
550
|
+
let buildHidden = false;
|
|
551
|
+
let buildAnimation;
|
|
552
|
+
let buildDelay = 0;
|
|
553
|
+
const paraReveal = buildInfo?.paraReveals.get(element.id);
|
|
554
|
+
if (buildInfo) {
|
|
555
|
+
const step = buildInfo.revealStep.get(element.id);
|
|
556
|
+
if (step !== void 0) {
|
|
557
|
+
if (buildInfo.currentStep < step) {
|
|
558
|
+
buildHidden = true;
|
|
559
|
+
} else if (paraReveal) ; else if (buildInfo.currentStep === step && element.animation) {
|
|
560
|
+
buildAnimation = element.animation;
|
|
561
|
+
buildDelay = buildInfo.delays.get(element.id) ?? 0;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (buildHidden) return null;
|
|
566
|
+
return /* @__PURE__ */ jsx(
|
|
567
|
+
SlideElementHost,
|
|
568
|
+
{
|
|
569
|
+
element,
|
|
570
|
+
theme: t,
|
|
571
|
+
slideWidthPx,
|
|
572
|
+
slideHeightPx,
|
|
573
|
+
editing,
|
|
574
|
+
selected: selectedElementId === element.id,
|
|
575
|
+
onContentChange: onElementContentChange,
|
|
576
|
+
onSelect: onElementSelect,
|
|
577
|
+
onMove: onElementMove,
|
|
578
|
+
onResize: onElementResize,
|
|
579
|
+
renderElement,
|
|
580
|
+
buildAnimation,
|
|
581
|
+
buildDelay,
|
|
582
|
+
paraReveal
|
|
583
|
+
},
|
|
584
|
+
element.id
|
|
585
|
+
);
|
|
586
|
+
})
|
|
587
|
+
]
|
|
325
588
|
}
|
|
326
589
|
) });
|
|
327
590
|
}
|
|
@@ -338,7 +601,10 @@ function SlideElementHost({
|
|
|
338
601
|
onSelect,
|
|
339
602
|
onMove,
|
|
340
603
|
onResize,
|
|
341
|
-
renderElement
|
|
604
|
+
renderElement,
|
|
605
|
+
buildAnimation,
|
|
606
|
+
buildDelay = 0,
|
|
607
|
+
paraReveal
|
|
342
608
|
}) {
|
|
343
609
|
const dragRef = useRef(null);
|
|
344
610
|
if (element.hidden) return null;
|
|
@@ -407,15 +673,18 @@ function SlideElementHost({
|
|
|
407
673
|
outline: selected ? "2px solid #8b5cf6" : void 0,
|
|
408
674
|
outlineOffset: selected ? 2 : void 0,
|
|
409
675
|
cursor: canMove ? "move" : interactive ? "pointer" : "default",
|
|
410
|
-
touchAction: canMove ? "none" : void 0
|
|
676
|
+
touchAction: canMove ? "none" : void 0,
|
|
677
|
+
...buildAnimation ? buildEnterStyle(buildAnimation, buildDelay) : null
|
|
411
678
|
};
|
|
412
|
-
const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange }) ?? renderElement?.(element, slideWidthPx);
|
|
679
|
+
const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) ?? renderElement?.(element, slideWidthPx);
|
|
413
680
|
return /* @__PURE__ */ jsxs(
|
|
414
681
|
"div",
|
|
415
682
|
{
|
|
683
|
+
className: buildAnimation ? "fs-build-enter" : void 0,
|
|
416
684
|
style: box,
|
|
417
685
|
"data-fancy-slides-element": element.id,
|
|
418
686
|
"data-fancy-slides-element-type": element.type,
|
|
687
|
+
"data-fancy-slides-build": buildAnimation ? "" : void 0,
|
|
419
688
|
onPointerDown: canMove ? startDrag("move") : void 0,
|
|
420
689
|
onPointerMove: canMove ? onPointerMove : void 0,
|
|
421
690
|
onPointerUp: canMove ? endDrag : void 0,
|
|
@@ -474,7 +743,7 @@ function ResizeHandles({ onStart, onMove, onEnd }) {
|
|
|
474
743
|
anchor
|
|
475
744
|
)) });
|
|
476
745
|
}
|
|
477
|
-
function renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange }) {
|
|
746
|
+
function renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) {
|
|
478
747
|
switch (element.type) {
|
|
479
748
|
case "text":
|
|
480
749
|
return /* @__PURE__ */ jsx(
|
|
@@ -485,7 +754,8 @@ function renderInner({ element, theme, slideWidthPx, editing, selected, onConten
|
|
|
485
754
|
slideWidthPx,
|
|
486
755
|
editing,
|
|
487
756
|
selected,
|
|
488
|
-
onContentChange: onContentChange ? (c) => onContentChange(element.id, c) : void 0
|
|
757
|
+
onContentChange: onContentChange ? (c) => onContentChange(element.id, c) : void 0,
|
|
758
|
+
paraReveal
|
|
489
759
|
}
|
|
490
760
|
);
|
|
491
761
|
case "image":
|
|
@@ -537,6 +807,8 @@ function useSlideKeyboard({
|
|
|
537
807
|
total,
|
|
538
808
|
index,
|
|
539
809
|
goTo,
|
|
810
|
+
onAdvance,
|
|
811
|
+
onRetreat,
|
|
540
812
|
onExit,
|
|
541
813
|
onBlank,
|
|
542
814
|
onFullscreen,
|
|
@@ -553,13 +825,15 @@ function useSlideKeyboard({
|
|
|
553
825
|
case "ArrowLeft":
|
|
554
826
|
case "PageUp":
|
|
555
827
|
e.preventDefault();
|
|
556
|
-
if (
|
|
828
|
+
if (onRetreat) onRetreat();
|
|
829
|
+
else if (index > 0) goTo(index - 1);
|
|
557
830
|
return;
|
|
558
831
|
case "ArrowRight":
|
|
559
832
|
case "PageDown":
|
|
560
833
|
case " ":
|
|
561
834
|
e.preventDefault();
|
|
562
|
-
if (
|
|
835
|
+
if (onAdvance) onAdvance();
|
|
836
|
+
else if (index < total - 1) goTo(index + 1);
|
|
563
837
|
return;
|
|
564
838
|
case "Home":
|
|
565
839
|
e.preventDefault();
|
|
@@ -601,7 +875,7 @@ function useSlideKeyboard({
|
|
|
601
875
|
};
|
|
602
876
|
window.addEventListener("keydown", handler);
|
|
603
877
|
return () => window.removeEventListener("keydown", handler);
|
|
604
|
-
}, [enabled, index, total, goTo, onExit, onBlank, onFullscreen]);
|
|
878
|
+
}, [enabled, index, total, goTo, onAdvance, onRetreat, onExit, onBlank, onFullscreen]);
|
|
605
879
|
}
|
|
606
880
|
function SlideViewer({
|
|
607
881
|
deck,
|
|
@@ -629,13 +903,34 @@ function SlideViewer({
|
|
|
629
903
|
const containerRef = useRef(null);
|
|
630
904
|
const prevIndexRef = useRef(index);
|
|
631
905
|
const forward = index >= prevIndexRef.current;
|
|
906
|
+
const slide = deck.slides[index];
|
|
907
|
+
const totalSteps = totalBuildSteps(slide);
|
|
908
|
+
const [buildStep, setBuildStep] = useState(0);
|
|
909
|
+
const nextFreshRef = useRef(false);
|
|
632
910
|
useEffect(() => {
|
|
911
|
+
if (index === prevIndexRef.current) return;
|
|
633
912
|
prevIndexRef.current = index;
|
|
634
|
-
|
|
913
|
+
const fresh = nextFreshRef.current;
|
|
914
|
+
nextFreshRef.current = false;
|
|
915
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
916
|
+
}, [index, deck.slides]);
|
|
917
|
+
const advance = useCallback(() => {
|
|
918
|
+
if (buildStep < totalSteps) {
|
|
919
|
+
setBuildStep((s) => s + 1);
|
|
920
|
+
} else if (index < deck.slides.length - 1) {
|
|
921
|
+
nextFreshRef.current = true;
|
|
922
|
+
goTo(index + 1);
|
|
923
|
+
}
|
|
924
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
925
|
+
const retreat = useCallback(() => {
|
|
926
|
+
if (index > 0) goTo(index - 1);
|
|
927
|
+
}, [index, goTo]);
|
|
635
928
|
useSlideKeyboard({
|
|
636
929
|
total: deck.slides.length,
|
|
637
930
|
index,
|
|
638
931
|
goTo,
|
|
932
|
+
onAdvance: advance,
|
|
933
|
+
onRetreat: retreat,
|
|
639
934
|
onExit,
|
|
640
935
|
onBlank: () => setBlanked((b) => !b),
|
|
641
936
|
onFullscreen: () => {
|
|
@@ -648,11 +943,15 @@ function SlideViewer({
|
|
|
648
943
|
useEffect(() => {
|
|
649
944
|
if (!autoAdvanceMs || deck.slides.length <= 1) return;
|
|
650
945
|
const t = setTimeout(() => {
|
|
651
|
-
|
|
946
|
+
if (buildStep < totalSteps) {
|
|
947
|
+
setBuildStep((s) => s + 1);
|
|
948
|
+
} else {
|
|
949
|
+
nextFreshRef.current = true;
|
|
950
|
+
goTo(index + 1 < deck.slides.length ? index + 1 : 0);
|
|
951
|
+
}
|
|
652
952
|
}, autoAdvanceMs);
|
|
653
953
|
return () => clearTimeout(t);
|
|
654
|
-
}, [autoAdvanceMs, index, deck.slides.length, goTo]);
|
|
655
|
-
const slide = deck.slides[index];
|
|
954
|
+
}, [autoAdvanceMs, index, deck.slides.length, goTo, buildStep, totalSteps]);
|
|
656
955
|
const theme = resolveTheme(deck.theme);
|
|
657
956
|
const aspectRatio = theme.aspectRatio ?? 16 / 9;
|
|
658
957
|
const transition = slide?.transition ?? theme.defaultTransition;
|
|
@@ -674,6 +973,11 @@ function SlideViewer({
|
|
|
674
973
|
},
|
|
675
974
|
tabIndex: 0,
|
|
676
975
|
"data-fancy-slides-viewer": deck.id,
|
|
976
|
+
"data-fancy-slides-build-step": buildStep,
|
|
977
|
+
onClick: () => {
|
|
978
|
+
if (blanked) return;
|
|
979
|
+
advance();
|
|
980
|
+
},
|
|
677
981
|
children: [
|
|
678
982
|
/* @__PURE__ */ jsx("style", { children: TRANSITION_KEYFRAMES }),
|
|
679
983
|
!blanked && slide && /* @__PURE__ */ jsx(
|
|
@@ -687,7 +991,7 @@ function SlideViewer({
|
|
|
687
991
|
["--fs-ratio"]: aspectRatio.toString(),
|
|
688
992
|
boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
|
|
689
993
|
},
|
|
690
|
-
children: /* @__PURE__ */ jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement }) }, index)
|
|
994
|
+
children: /* @__PURE__ */ jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsx(Slide, { slide, theme, buildStep, renderElement }) }, index)
|
|
691
995
|
}
|
|
692
996
|
),
|
|
693
997
|
!hideChrome && !blanked && /* @__PURE__ */ jsxs(
|
|
@@ -718,7 +1022,7 @@ function SlideViewer({
|
|
|
718
1022
|
);
|
|
719
1023
|
}
|
|
720
1024
|
var DEFAULT_DURATION = 400;
|
|
721
|
-
var
|
|
1025
|
+
var EASE2 = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
722
1026
|
function transitionEnterStyle(transition, forward) {
|
|
723
1027
|
const kind = transition?.kind ?? "none";
|
|
724
1028
|
if (kind === "none") return { width: "100%", height: "100%" };
|
|
@@ -744,7 +1048,7 @@ function transitionEnterStyle(transition, forward) {
|
|
|
744
1048
|
height: "100%",
|
|
745
1049
|
animationName: name,
|
|
746
1050
|
animationDuration: `${duration}ms`,
|
|
747
|
-
animationTimingFunction:
|
|
1051
|
+
animationTimingFunction: EASE2,
|
|
748
1052
|
animationFillMode: "both"
|
|
749
1053
|
};
|
|
750
1054
|
}
|
|
@@ -800,14 +1104,38 @@ function PresenterView({
|
|
|
800
1104
|
},
|
|
801
1105
|
[deck.slides.length, isControlled, onIndexChange]
|
|
802
1106
|
);
|
|
1107
|
+
const slide = deck.slides[index];
|
|
1108
|
+
const totalSteps = totalBuildSteps(slide);
|
|
1109
|
+
const [buildStep, setBuildStep] = useState(0);
|
|
1110
|
+
const prevIndexRef = useRef(index);
|
|
1111
|
+
const nextFreshRef = useRef(false);
|
|
1112
|
+
useEffect(() => {
|
|
1113
|
+
if (index === prevIndexRef.current) return;
|
|
1114
|
+
prevIndexRef.current = index;
|
|
1115
|
+
const fresh = nextFreshRef.current;
|
|
1116
|
+
nextFreshRef.current = false;
|
|
1117
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
1118
|
+
}, [index, deck.slides]);
|
|
1119
|
+
const advance = useCallback(() => {
|
|
1120
|
+
if (buildStep < totalSteps) {
|
|
1121
|
+
setBuildStep((s) => s + 1);
|
|
1122
|
+
} else if (index < deck.slides.length - 1) {
|
|
1123
|
+
nextFreshRef.current = true;
|
|
1124
|
+
goTo(index + 1);
|
|
1125
|
+
}
|
|
1126
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
1127
|
+
const retreat = useCallback(() => {
|
|
1128
|
+
if (index > 0) goTo(index - 1);
|
|
1129
|
+
}, [index, goTo]);
|
|
803
1130
|
useSlideKeyboard({
|
|
804
1131
|
total: deck.slides.length,
|
|
805
1132
|
index,
|
|
806
1133
|
goTo,
|
|
1134
|
+
onAdvance: advance,
|
|
1135
|
+
onRetreat: retreat,
|
|
807
1136
|
onExit
|
|
808
1137
|
});
|
|
809
1138
|
const theme = resolveTheme(deck.theme);
|
|
810
|
-
const slide = deck.slides[index];
|
|
811
1139
|
const nextSlide = deck.slides[index + 1];
|
|
812
1140
|
const [now, setNow] = useState(() => Date.now());
|
|
813
1141
|
useEffect(() => {
|
|
@@ -854,7 +1182,7 @@ function PresenterView({
|
|
|
854
1182
|
borderRadius: 8,
|
|
855
1183
|
overflow: "hidden"
|
|
856
1184
|
},
|
|
857
|
-
children: slide ? /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement }) : null
|
|
1185
|
+
children: slide ? /* @__PURE__ */ jsx(Slide, { slide, theme, buildStep, renderElement }) : null
|
|
858
1186
|
}
|
|
859
1187
|
)
|
|
860
1188
|
}
|
|
@@ -958,8 +1286,8 @@ function PresenterView({
|
|
|
958
1286
|
/* @__PURE__ */ jsx(StatusChip, { label: "Elapsed", children: formatElapsed(now - startedAtRef) }),
|
|
959
1287
|
/* @__PURE__ */ jsx(StatusChip, { label: "Clock", children: formatClock(now) }),
|
|
960
1288
|
/* @__PURE__ */ jsxs("div", { style: { marginLeft: "auto", display: "flex", gap: 8 }, children: [
|
|
961
|
-
/* @__PURE__ */ jsx(NavButton, { onClick:
|
|
962
|
-
/* @__PURE__ */ jsx(NavButton, { onClick:
|
|
1289
|
+
/* @__PURE__ */ jsx(NavButton, { onClick: retreat, disabled: index === 0, children: "\u2190 Prev" }),
|
|
1290
|
+
/* @__PURE__ */ jsx(NavButton, { onClick: advance, disabled: index >= deck.slides.length - 1 && buildStep >= totalSteps, children: "Next \u2192" })
|
|
963
1291
|
] })
|
|
964
1292
|
]
|
|
965
1293
|
}
|
|
@@ -1142,6 +1470,7 @@ function useDeckState({ value, onChange, onOp }) {
|
|
|
1142
1470
|
updateElement: (slideIdArg, elementIdArg, patch) => apply({ kind: "element_update", slideId: slideIdArg, elementId: elementIdArg, patch }),
|
|
1143
1471
|
moveElement: (slideIdArg, elementIdArg, x, y) => apply({ kind: "element_move", slideId: slideIdArg, elementId: elementIdArg, x, y }),
|
|
1144
1472
|
resizeElement: (slideIdArg, elementIdArg, w, h) => apply({ kind: "element_resize", slideId: slideIdArg, elementId: elementIdArg, w, h }),
|
|
1473
|
+
setAnimation: (slideIdArg, elementIdArg, animation) => apply({ kind: "element_set_animation", slideId: slideIdArg, elementId: elementIdArg, animation }),
|
|
1145
1474
|
getSlide: (id) => value.slides.find((s) => s.id === id),
|
|
1146
1475
|
getElement: (slideIdArg, elementIdArg) => value.slides.find((s) => s.id === slideIdArg)?.elements.find((e) => e.id === elementIdArg)
|
|
1147
1476
|
};
|
|
@@ -1209,6 +1538,23 @@ function reduce(deck, op) {
|
|
|
1209
1538
|
(s) => s.id === op.slideId ? { ...s, elements: s.elements.map((e) => e.id === op.elementId ? { ...e, w: op.w, h: op.h } : e) } : s
|
|
1210
1539
|
)
|
|
1211
1540
|
};
|
|
1541
|
+
case "element_set_animation":
|
|
1542
|
+
return {
|
|
1543
|
+
...deck,
|
|
1544
|
+
slides: deck.slides.map(
|
|
1545
|
+
(s) => s.id === op.slideId ? {
|
|
1546
|
+
...s,
|
|
1547
|
+
elements: s.elements.map((e) => {
|
|
1548
|
+
if (e.id !== op.elementId) return e;
|
|
1549
|
+
if (op.animation === void 0) {
|
|
1550
|
+
const { animation: _drop, ...rest } = e;
|
|
1551
|
+
return rest;
|
|
1552
|
+
}
|
|
1553
|
+
return { ...e, animation: op.animation };
|
|
1554
|
+
})
|
|
1555
|
+
} : s
|
|
1556
|
+
)
|
|
1557
|
+
};
|
|
1212
1558
|
}
|
|
1213
1559
|
}
|
|
1214
1560
|
|
|
@@ -1550,10 +1896,10 @@ function EditorToolbar({
|
|
|
1550
1896
|
/* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsx(Tooltip, { content: "Present (F)", children: /* @__PURE__ */ jsx(Action, { color: "violet", size: "sm", icon: "play", onClick: onPresent, children: "Present" }) }) })
|
|
1551
1897
|
] });
|
|
1552
1898
|
}
|
|
1553
|
-
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
|
|
1899
|
+
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }) {
|
|
1554
1900
|
if (!element) {
|
|
1555
1901
|
if (slide) {
|
|
1556
|
-
return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
|
|
1902
|
+
return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetElementAnimation });
|
|
1557
1903
|
}
|
|
1558
1904
|
return /* @__PURE__ */ jsxs("div", { className: "fs-inspector flex h-full flex-col border-l border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-900", children: [
|
|
1559
1905
|
/* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
|
|
@@ -1578,10 +1924,12 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1578
1924
|
/* @__PURE__ */ jsxs(Tabs.List, { children: [
|
|
1579
1925
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "style", children: "Style" }),
|
|
1580
1926
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "layout", children: "Layout" }),
|
|
1927
|
+
/* @__PURE__ */ jsx(Tabs.Tab, { value: "build", children: "Build" }),
|
|
1581
1928
|
/* @__PURE__ */ jsx(Tabs.Tab, { value: "advanced", children: "Advanced" })
|
|
1582
1929
|
] }),
|
|
1583
1930
|
/* @__PURE__ */ jsxs(Tabs.Panels, { children: [
|
|
1584
1931
|
/* @__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 }) }) }),
|
|
1932
|
+
/* @__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, isText: element.type === "text" }) }) }),
|
|
1585
1933
|
/* @__PURE__ */ jsx(Tabs.Panel, { value: "layout", children: /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(LayoutSection, { element, onPatch }) }) }),
|
|
1586
1934
|
/* @__PURE__ */ jsx(Tabs.Panel, { value: "advanced", children: /* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(AdvancedSection, { element, onPatch }) }) })
|
|
1587
1935
|
] })
|
|
@@ -1591,7 +1939,8 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1591
1939
|
function SlideSettings({
|
|
1592
1940
|
slide,
|
|
1593
1941
|
onSetTransition,
|
|
1594
|
-
onSetBackground
|
|
1942
|
+
onSetBackground,
|
|
1943
|
+
onSetElementAnimation
|
|
1595
1944
|
}) {
|
|
1596
1945
|
const transition = slide.transition;
|
|
1597
1946
|
const kind = transition?.kind ?? "none";
|
|
@@ -1658,10 +2007,54 @@ function SlideSettings({
|
|
|
1658
2007
|
onChange: (c) => onSetBackground({ ...slide.background, color: c })
|
|
1659
2008
|
}
|
|
1660
2009
|
) })
|
|
1661
|
-
] }) })
|
|
2010
|
+
] }) }),
|
|
2011
|
+
onSetElementAnimation && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsx(BuildOrderList, { slide, onSetElementAnimation }) })
|
|
1662
2012
|
] })
|
|
1663
2013
|
] });
|
|
1664
2014
|
}
|
|
2015
|
+
function BuildOrderList({
|
|
2016
|
+
slide,
|
|
2017
|
+
onSetElementAnimation
|
|
2018
|
+
}) {
|
|
2019
|
+
const builds = collectBuilds(slide);
|
|
2020
|
+
const move = (from, to) => {
|
|
2021
|
+
if (to < 0 || to >= builds.length) return;
|
|
2022
|
+
const reordered = [...builds];
|
|
2023
|
+
const [item] = reordered.splice(from, 1);
|
|
2024
|
+
reordered.splice(to, 0, item);
|
|
2025
|
+
reordered.forEach((b, i) => {
|
|
2026
|
+
if ((b.animation.order ?? 0) !== i) {
|
|
2027
|
+
onSetElementAnimation(b.element.id, { ...b.animation, order: i });
|
|
2028
|
+
}
|
|
2029
|
+
});
|
|
2030
|
+
};
|
|
2031
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2032
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Build order" }),
|
|
2033
|
+
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: [
|
|
2034
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", className: "!font-mono !text-zinc-400 w-5", children: [
|
|
2035
|
+
i + 1,
|
|
2036
|
+
"."
|
|
2037
|
+
] }),
|
|
2038
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2039
|
+
/* @__PURE__ */ jsx(Text, { size: "sm", className: "truncate", children: buildLabel(b.element) }),
|
|
2040
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", className: "!font-mono !text-zinc-400", children: [
|
|
2041
|
+
b.animation.effect,
|
|
2042
|
+
" \xB7 ",
|
|
2043
|
+
b.animation.trigger ?? "on-click"
|
|
2044
|
+
] })
|
|
2045
|
+
] }),
|
|
2046
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "chevron-up", onClick: () => move(i, i - 1), disabled: i === 0, "aria-label": "Move earlier" }),
|
|
2047
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "chevron-down", onClick: () => move(i, i + 1), disabled: i === builds.length - 1, "aria-label": "Move later" })
|
|
2048
|
+
] }, b.element.id))
|
|
2049
|
+
] });
|
|
2050
|
+
}
|
|
2051
|
+
function buildLabel(element) {
|
|
2052
|
+
if (element.type === "text") {
|
|
2053
|
+
const text = element.content.replace(/\s+/g, " ").trim();
|
|
2054
|
+
return text ? text.length > 28 ? `${text.slice(0, 28)}\u2026` : text : "Text";
|
|
2055
|
+
}
|
|
2056
|
+
return `${element.type} #${element.id.slice(-6)}`;
|
|
2057
|
+
}
|
|
1665
2058
|
function LayoutSection({ element, onPatch }) {
|
|
1666
2059
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1667
2060
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
@@ -1683,6 +2076,109 @@ function AdvancedSection({ element, onPatch }) {
|
|
|
1683
2076
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(Action, { size: "sm", variant: element.hidden ? "default" : "ghost", onClick: () => onPatch({ hidden: !element.hidden }), children: element.hidden ? "Hidden \u2014 show" : "Hide on slide" }) })
|
|
1684
2077
|
] });
|
|
1685
2078
|
}
|
|
2079
|
+
var NO_ANIMATION = "none";
|
|
2080
|
+
function AnimateSection({
|
|
2081
|
+
animation,
|
|
2082
|
+
onSetAnimation,
|
|
2083
|
+
isText
|
|
2084
|
+
}) {
|
|
2085
|
+
if (!onSetAnimation) {
|
|
2086
|
+
return /* @__PURE__ */ jsx(Text, { size: "sm", className: "!text-zinc-500", children: "Build animations aren't wired up in this editor." });
|
|
2087
|
+
}
|
|
2088
|
+
const effect = animation?.effect;
|
|
2089
|
+
const set = (next) => {
|
|
2090
|
+
const base = animation ?? { effect: "fade" };
|
|
2091
|
+
onSetAnimation({ ...base, ...next });
|
|
2092
|
+
};
|
|
2093
|
+
const showDirection = effect === "fly-in" || effect === "wipe";
|
|
2094
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2095
|
+
/* @__PURE__ */ jsx(
|
|
2096
|
+
Select,
|
|
2097
|
+
{
|
|
2098
|
+
label: "Effect",
|
|
2099
|
+
list: [
|
|
2100
|
+
{ value: NO_ANIMATION, label: "None" },
|
|
2101
|
+
{ value: "fade", label: "Fade" },
|
|
2102
|
+
{ value: "fly-in", label: "Fly in" },
|
|
2103
|
+
{ value: "zoom", label: "Zoom" },
|
|
2104
|
+
{ value: "wipe", label: "Wipe" }
|
|
2105
|
+
],
|
|
2106
|
+
value: effect ?? NO_ANIMATION,
|
|
2107
|
+
onValueChange: (v) => {
|
|
2108
|
+
if (v === NO_ANIMATION) onSetAnimation(void 0);
|
|
2109
|
+
else set({ effect: v });
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
),
|
|
2113
|
+
effect && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2114
|
+
/* @__PURE__ */ jsx(
|
|
2115
|
+
Select,
|
|
2116
|
+
{
|
|
2117
|
+
label: "Trigger",
|
|
2118
|
+
list: [
|
|
2119
|
+
{ value: "on-click", label: "On click" },
|
|
2120
|
+
{ value: "with-prev", label: "With previous" },
|
|
2121
|
+
{ value: "after-prev", label: "After previous" }
|
|
2122
|
+
],
|
|
2123
|
+
value: animation?.trigger ?? "on-click",
|
|
2124
|
+
onValueChange: (v) => set({ trigger: v })
|
|
2125
|
+
}
|
|
2126
|
+
),
|
|
2127
|
+
showDirection && /* @__PURE__ */ jsx(
|
|
2128
|
+
Select,
|
|
2129
|
+
{
|
|
2130
|
+
label: "Direction",
|
|
2131
|
+
list: [
|
|
2132
|
+
{ value: "left", label: "From left" },
|
|
2133
|
+
{ value: "right", label: "From right" },
|
|
2134
|
+
{ value: "up", label: "From bottom" },
|
|
2135
|
+
{ value: "down", label: "From top" }
|
|
2136
|
+
],
|
|
2137
|
+
value: animation?.direction ?? "left",
|
|
2138
|
+
onValueChange: (v) => set({ direction: v })
|
|
2139
|
+
}
|
|
2140
|
+
),
|
|
2141
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2142
|
+
/* @__PURE__ */ jsx(
|
|
2143
|
+
Input,
|
|
2144
|
+
{
|
|
2145
|
+
label: "Duration (ms)",
|
|
2146
|
+
type: "number",
|
|
2147
|
+
value: String(animation?.duration ?? 500),
|
|
2148
|
+
onChange: (e) => set({ duration: parseInt(e.target.value, 10) || 500 })
|
|
2149
|
+
}
|
|
2150
|
+
),
|
|
2151
|
+
/* @__PURE__ */ jsx(
|
|
2152
|
+
Input,
|
|
2153
|
+
{
|
|
2154
|
+
label: "Delay (ms)",
|
|
2155
|
+
type: "number",
|
|
2156
|
+
value: String(animation?.delay ?? 0),
|
|
2157
|
+
onChange: (e) => set({ delay: parseInt(e.target.value, 10) || 0 })
|
|
2158
|
+
}
|
|
2159
|
+
)
|
|
2160
|
+
] }),
|
|
2161
|
+
/* @__PURE__ */ jsx(
|
|
2162
|
+
Input,
|
|
2163
|
+
{
|
|
2164
|
+
label: "Order",
|
|
2165
|
+
type: "number",
|
|
2166
|
+
value: String(animation?.order ?? 0),
|
|
2167
|
+
onChange: (e) => set({ order: parseInt(e.target.value, 10) || 0 })
|
|
2168
|
+
}
|
|
2169
|
+
),
|
|
2170
|
+
isText && /* @__PURE__ */ jsx(
|
|
2171
|
+
Switch,
|
|
2172
|
+
{
|
|
2173
|
+
label: "Animate by paragraph (one line per click)",
|
|
2174
|
+
checked: !!animation?.byParagraph,
|
|
2175
|
+
onCheckedChange: (v) => set({ byParagraph: v })
|
|
2176
|
+
}
|
|
2177
|
+
),
|
|
2178
|
+
/* @__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.` })
|
|
2179
|
+
] })
|
|
2180
|
+
] });
|
|
2181
|
+
}
|
|
1686
2182
|
function StyleSection({ element, onPatch }) {
|
|
1687
2183
|
switch (element.type) {
|
|
1688
2184
|
case "text":
|
|
@@ -2358,7 +2854,9 @@ function DeckEditor({
|
|
|
2358
2854
|
},
|
|
2359
2855
|
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
|
|
2360
2856
|
onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
|
|
2361
|
-
onSetBackground: (background) => slide && ops.setBackground(slide.id, background)
|
|
2857
|
+
onSetBackground: (background) => slide && ops.setBackground(slide.id, background),
|
|
2858
|
+
onSetAnimation: (animation) => slide && elementIdSelected && ops.setAnimation(slide.id, elementIdSelected, animation),
|
|
2859
|
+
onSetElementAnimation: (eid, animation) => slide && ops.setAnimation(slide.id, eid, animation)
|
|
2362
2860
|
}
|
|
2363
2861
|
) })
|
|
2364
2862
|
] }),
|
|
@@ -2368,6 +2866,6 @@ function DeckEditor({
|
|
|
2368
2866
|
);
|
|
2369
2867
|
}
|
|
2370
2868
|
|
|
2371
|
-
export { DeckEditor, EditorToolbar, ElementInspector, ImageElementRenderer, PresenterView, ShapeElementRenderer, Slide, SlideRail, SlideThumbnail, SlideViewer, SpeakerNotes, TextElementRenderer, builtinThemes, chartStarterOption, darkTheme, deckId, defaultTheme, defineTheme, elementId, nextId, reduce as reduceDeck, resolveTheme, slideId, useDeckState, useSlideKeyboard, vividTheme };
|
|
2869
|
+
export { DeckEditor, EditorToolbar, ElementInspector, ImageElementRenderer, PresenterView, ShapeElementRenderer, Slide, SlideRail, SlideThumbnail, SlideViewer, SpeakerNotes, TextElementRenderer, buildSteps, buildsForStep, builtinThemes, chartStarterOption, collectBuilds, darkTheme, deckId, defaultTheme, defineTheme, elementId, isByParagraph, nextId, paragraphReveals, reduce as reduceDeck, resolveTheme, slideId, splitParagraphs, stepDelays, totalBuildSteps, useDeckState, useSlideKeyboard, visibleElementIds, vividTheme };
|
|
2372
2870
|
//# sourceMappingURL=index.js.map
|
|
2373
2871
|
//# sourceMappingURL=index.js.map
|