@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.cjs
CHANGED
|
@@ -72,13 +72,211 @@ function resolveTheme(theme) {
|
|
|
72
72
|
function cn(...parts) {
|
|
73
73
|
return parts.filter(Boolean).join(" ");
|
|
74
74
|
}
|
|
75
|
+
|
|
76
|
+
// src/utils/builds.ts
|
|
77
|
+
function splitParagraphs(content) {
|
|
78
|
+
const lines = content.split("\n");
|
|
79
|
+
if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
|
|
80
|
+
return lines;
|
|
81
|
+
}
|
|
82
|
+
function isByParagraph(element, animation) {
|
|
83
|
+
if (!animation.byParagraph || element.type !== "text") return false;
|
|
84
|
+
return splitParagraphs(element.content).length > 1;
|
|
85
|
+
}
|
|
86
|
+
var DEFAULT_BUILD_DURATION = 500;
|
|
87
|
+
function collectBuilds(slide) {
|
|
88
|
+
if (!slide) return [];
|
|
89
|
+
const builds = [];
|
|
90
|
+
slide.elements.forEach((element, index) => {
|
|
91
|
+
if (element.animation) {
|
|
92
|
+
builds.push({ element, animation: element.animation, index });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const ordered = builds.sort((a, b) => {
|
|
96
|
+
const ao = a.animation.order ?? 0;
|
|
97
|
+
const bo = b.animation.order ?? 0;
|
|
98
|
+
if (ao !== bo) return ao - bo;
|
|
99
|
+
return a.index - b.index;
|
|
100
|
+
});
|
|
101
|
+
const expanded = [];
|
|
102
|
+
for (const build of ordered) {
|
|
103
|
+
if (isByParagraph(build.element, build.animation)) {
|
|
104
|
+
const paras = splitParagraphs(build.element.content);
|
|
105
|
+
paras.forEach((_, paraIndex) => {
|
|
106
|
+
const animation = paraIndex === 0 ? build.animation : { ...build.animation, trigger: "on-click" };
|
|
107
|
+
expanded.push({ element: build.element, animation, index: build.index, paraIndex });
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
expanded.push(build);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return expanded;
|
|
114
|
+
}
|
|
115
|
+
function buildSteps(slide) {
|
|
116
|
+
const builds = collectBuilds(slide);
|
|
117
|
+
const steps = [];
|
|
118
|
+
for (const build of builds) {
|
|
119
|
+
const trigger = build.animation.trigger ?? "on-click";
|
|
120
|
+
if (steps.length === 0 || trigger === "on-click") {
|
|
121
|
+
steps.push({ builds: [build] });
|
|
122
|
+
} else {
|
|
123
|
+
steps[steps.length - 1].builds.push(build);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return steps;
|
|
127
|
+
}
|
|
128
|
+
function totalBuildSteps(slide) {
|
|
129
|
+
return buildSteps(slide).length;
|
|
130
|
+
}
|
|
131
|
+
function visibleElementIds(slide, buildStep) {
|
|
132
|
+
const visible = /* @__PURE__ */ new Set();
|
|
133
|
+
if (!slide) return visible;
|
|
134
|
+
const steps = buildSteps(slide);
|
|
135
|
+
const stepOfElement = /* @__PURE__ */ new Map();
|
|
136
|
+
steps.forEach((step, i) => {
|
|
137
|
+
for (const b of step.builds) {
|
|
138
|
+
if (!stepOfElement.has(b.element.id)) stepOfElement.set(b.element.id, i + 1);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
for (const element of slide.elements) {
|
|
142
|
+
const revealStep = stepOfElement.get(element.id);
|
|
143
|
+
if (revealStep === void 0) {
|
|
144
|
+
visible.add(element.id);
|
|
145
|
+
} else if (buildStep >= revealStep) {
|
|
146
|
+
visible.add(element.id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return visible;
|
|
150
|
+
}
|
|
151
|
+
function paragraphReveals(slide, buildStep) {
|
|
152
|
+
const out = /* @__PURE__ */ new Map();
|
|
153
|
+
if (!slide) return out;
|
|
154
|
+
const steps = buildSteps(slide);
|
|
155
|
+
steps.forEach((step, i) => {
|
|
156
|
+
const stepNum = i + 1;
|
|
157
|
+
for (const b of step.builds) {
|
|
158
|
+
if (b.paraIndex === void 0) continue;
|
|
159
|
+
const fired = buildStep >= stepNum;
|
|
160
|
+
const prev = out.get(b.element.id) ?? { revealed: 0 };
|
|
161
|
+
if (fired) {
|
|
162
|
+
prev.revealed = Math.max(prev.revealed, b.paraIndex + 1);
|
|
163
|
+
if (stepNum === buildStep) prev.firingParaIndex = b.paraIndex;
|
|
164
|
+
}
|
|
165
|
+
out.set(b.element.id, prev);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return out;
|
|
169
|
+
}
|
|
170
|
+
function buildsForStep(slide, buildStep) {
|
|
171
|
+
const steps = buildSteps(slide);
|
|
172
|
+
const step = steps[buildStep - 1];
|
|
173
|
+
return step ? step.builds : [];
|
|
174
|
+
}
|
|
175
|
+
function stepDelays(builds) {
|
|
176
|
+
const delays = /* @__PURE__ */ new Map();
|
|
177
|
+
const lead = builds[0];
|
|
178
|
+
if (!lead) return delays;
|
|
179
|
+
const leadDelay = lead.animation.delay ?? 0;
|
|
180
|
+
const leadDuration = lead.animation.duration ?? DEFAULT_BUILD_DURATION;
|
|
181
|
+
delays.set(lead.element.id, leadDelay);
|
|
182
|
+
for (let i = 1; i < builds.length; i++) {
|
|
183
|
+
const b = builds[i];
|
|
184
|
+
const own = b.animation.delay ?? 0;
|
|
185
|
+
const trigger = b.animation.trigger ?? "on-click";
|
|
186
|
+
const base = trigger === "after-prev" ? leadDelay + leadDuration : leadDelay;
|
|
187
|
+
delays.set(b.element.id, base + own);
|
|
188
|
+
}
|
|
189
|
+
return delays;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// src/components/Slide/builds-style.ts
|
|
193
|
+
var DEFAULT_BUILD_DURATION2 = 500;
|
|
194
|
+
var EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
195
|
+
function buildEnterStyle(animation, effectiveDelay) {
|
|
196
|
+
const duration = animation.duration ?? DEFAULT_BUILD_DURATION2;
|
|
197
|
+
const dir = animation.direction ?? "left";
|
|
198
|
+
let name;
|
|
199
|
+
switch (animation.effect) {
|
|
200
|
+
case "fade":
|
|
201
|
+
name = "fs-build-fade";
|
|
202
|
+
break;
|
|
203
|
+
case "zoom":
|
|
204
|
+
name = "fs-build-zoom";
|
|
205
|
+
break;
|
|
206
|
+
case "fly-in":
|
|
207
|
+
name = `fs-build-fly-${dir}`;
|
|
208
|
+
break;
|
|
209
|
+
case "wipe":
|
|
210
|
+
name = `fs-build-wipe-${dir}`;
|
|
211
|
+
break;
|
|
212
|
+
default:
|
|
213
|
+
name = "fs-build-fade";
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
animationName: name,
|
|
217
|
+
animationDuration: `${duration}ms`,
|
|
218
|
+
animationDelay: `${effectiveDelay}ms`,
|
|
219
|
+
animationTimingFunction: EASE,
|
|
220
|
+
animationFillMode: "both"
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
var BUILD_KEYFRAMES = `
|
|
224
|
+
@media (prefers-reduced-motion: reduce) {
|
|
225
|
+
.fs-build-enter { animation: none !important; }
|
|
226
|
+
}
|
|
227
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
228
|
+
@keyframes fs-build-fade {
|
|
229
|
+
from { opacity: 0; }
|
|
230
|
+
to { opacity: 1; }
|
|
231
|
+
}
|
|
232
|
+
@keyframes fs-build-zoom {
|
|
233
|
+
from { opacity: 0; transform: scale(0.8); }
|
|
234
|
+
to { opacity: 1; transform: scale(1); }
|
|
235
|
+
}
|
|
236
|
+
@keyframes fs-build-fly-left {
|
|
237
|
+
from { opacity: 0; transform: translateX(-24%); }
|
|
238
|
+
to { opacity: 1; transform: translateX(0); }
|
|
239
|
+
}
|
|
240
|
+
@keyframes fs-build-fly-right {
|
|
241
|
+
from { opacity: 0; transform: translateX(24%); }
|
|
242
|
+
to { opacity: 1; transform: translateX(0); }
|
|
243
|
+
}
|
|
244
|
+
@keyframes fs-build-fly-up {
|
|
245
|
+
from { opacity: 0; transform: translateY(24%); }
|
|
246
|
+
to { opacity: 1; transform: translateY(0); }
|
|
247
|
+
}
|
|
248
|
+
@keyframes fs-build-fly-down {
|
|
249
|
+
from { opacity: 0; transform: translateY(-24%); }
|
|
250
|
+
to { opacity: 1; transform: translateY(0); }
|
|
251
|
+
}
|
|
252
|
+
/* wipe: clip-path inset reveals from the named edge toward the opposite one.
|
|
253
|
+
inset(top right bottom left) \u2014 start fully clipped on the far side. */
|
|
254
|
+
@keyframes fs-build-wipe-left {
|
|
255
|
+
from { clip-path: inset(0 100% 0 0); }
|
|
256
|
+
to { clip-path: inset(0 0 0 0); }
|
|
257
|
+
}
|
|
258
|
+
@keyframes fs-build-wipe-right {
|
|
259
|
+
from { clip-path: inset(0 0 0 100%); }
|
|
260
|
+
to { clip-path: inset(0 0 0 0); }
|
|
261
|
+
}
|
|
262
|
+
@keyframes fs-build-wipe-up {
|
|
263
|
+
from { clip-path: inset(100% 0 0 0); }
|
|
264
|
+
to { clip-path: inset(0 0 0 0); }
|
|
265
|
+
}
|
|
266
|
+
@keyframes fs-build-wipe-down {
|
|
267
|
+
from { clip-path: inset(0 0 100% 0); }
|
|
268
|
+
to { clip-path: inset(0 0 0 0); }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
`;
|
|
75
272
|
function TextElementRenderer({
|
|
76
273
|
element,
|
|
77
274
|
theme,
|
|
78
275
|
slideWidthPx,
|
|
79
276
|
editing = false,
|
|
80
277
|
selected = false,
|
|
81
|
-
onContentChange
|
|
278
|
+
onContentChange,
|
|
279
|
+
paraReveal
|
|
82
280
|
}) {
|
|
83
281
|
const t = resolveTheme(theme);
|
|
84
282
|
const style = element.style ?? {};
|
|
@@ -125,30 +323,53 @@ function TextElementRenderer({
|
|
|
125
323
|
}
|
|
126
324
|
);
|
|
127
325
|
}
|
|
326
|
+
const proseScope = `[data-fs-text-scope="${scopeId}"]`;
|
|
327
|
+
const doubleScope = `${proseScope}${proseScope}`;
|
|
328
|
+
const proseStyle = /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
|
|
329
|
+
${proseScope} > div { width: 100%; height: 100%; font-size: inherit; }
|
|
330
|
+
${doubleScope} :is(p, ul, ol, li, blockquote, h1, h2, h3, h4, h5, h6, pre, code, strong, em, a) {
|
|
331
|
+
font-size: inherit;
|
|
332
|
+
}
|
|
333
|
+
${doubleScope} h1 { font-size: 1.6em; font-weight: 700; }
|
|
334
|
+
${doubleScope} h2 { font-size: 1.35em; font-weight: 700; }
|
|
335
|
+
${doubleScope} h3 { font-size: 1.15em; font-weight: 600; }
|
|
336
|
+
${proseScope} :where(p, ul, ol, h1, h2, h3, h4, h5, h6, pre, blockquote) {
|
|
337
|
+
margin: 0;
|
|
338
|
+
padding: 0;
|
|
339
|
+
}
|
|
340
|
+
${proseScope} :where(p, li) + :where(p, li, ul, ol) { margin-top: 0.4em; }
|
|
341
|
+
${proseScope} :where(ul, ol) { padding-left: 1.4em; }
|
|
342
|
+
${proseScope} :where(strong) { font-weight: ${Math.max(700, weight(style.weight) ?? 400 + 200)}; }
|
|
343
|
+
${proseScope} :where(a) { color: inherit; text-decoration: underline; }
|
|
344
|
+
${proseScope} :where(code) { font-family: ${t.fonts?.mono ?? "monospace"}; }
|
|
345
|
+
` });
|
|
346
|
+
const renderChunk = (content) => format === "plain" ? content : /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContentRenderer, { value: content, format: format === "html" ? "html" : "markdown" });
|
|
347
|
+
if (paraReveal) {
|
|
348
|
+
const paras = splitParagraphs(element.content);
|
|
349
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-fs-text-scope": scopeId, style: css, children: [
|
|
350
|
+
proseStyle,
|
|
351
|
+
paras.map((para, i) => {
|
|
352
|
+
if (i >= paraReveal.revealed) return null;
|
|
353
|
+
const firing = i === paraReveal.firingParaIndex && !!element.animation;
|
|
354
|
+
const enter = firing ? buildEnterStyle(element.animation, element.animation.delay ?? 0) : null;
|
|
355
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
356
|
+
"div",
|
|
357
|
+
{
|
|
358
|
+
className: firing ? "fs-build-enter" : void 0,
|
|
359
|
+
style: { whiteSpace: format === "plain" ? "pre-wrap" : "normal", ...enter },
|
|
360
|
+
"data-fancy-slides-paragraph": i,
|
|
361
|
+
children: renderChunk(para)
|
|
362
|
+
},
|
|
363
|
+
i
|
|
364
|
+
);
|
|
365
|
+
})
|
|
366
|
+
] });
|
|
367
|
+
}
|
|
128
368
|
if (format === "plain") {
|
|
129
369
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: css, children: element.content });
|
|
130
370
|
}
|
|
131
|
-
const proseScope = `[data-fs-text-scope="${scopeId}"]`;
|
|
132
|
-
const doubleScope = `${proseScope}${proseScope}`;
|
|
133
371
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-fs-text-scope": scopeId, style: css, children: [
|
|
134
|
-
|
|
135
|
-
${proseScope} > div { width: 100%; height: 100%; font-size: inherit; }
|
|
136
|
-
${doubleScope} :is(p, ul, ol, li, blockquote, h1, h2, h3, h4, h5, h6, pre, code, strong, em, a) {
|
|
137
|
-
font-size: inherit;
|
|
138
|
-
}
|
|
139
|
-
${doubleScope} h1 { font-size: 1.6em; font-weight: 700; }
|
|
140
|
-
${doubleScope} h2 { font-size: 1.35em; font-weight: 700; }
|
|
141
|
-
${doubleScope} h3 { font-size: 1.15em; font-weight: 600; }
|
|
142
|
-
${proseScope} :where(p, ul, ol, h1, h2, h3, h4, h5, h6, pre, blockquote) {
|
|
143
|
-
margin: 0;
|
|
144
|
-
padding: 0;
|
|
145
|
-
}
|
|
146
|
-
${proseScope} :where(p, li) + :where(p, li, ul, ol) { margin-top: 0.4em; }
|
|
147
|
-
${proseScope} :where(ul, ol) { padding-left: 1.4em; }
|
|
148
|
-
${proseScope} :where(strong) { font-weight: ${Math.max(700, weight(style.weight) ?? 400 + 200)}; }
|
|
149
|
-
${proseScope} :where(a) { color: inherit; text-decoration: underline; }
|
|
150
|
-
${proseScope} :where(code) { font-family: ${t.fonts?.mono ?? "monospace"}; }
|
|
151
|
-
` }),
|
|
372
|
+
proseStyle,
|
|
152
373
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContentRenderer, { value: element.content, format: format === "html" ? "html" : "markdown" })
|
|
153
374
|
] });
|
|
154
375
|
}
|
|
@@ -286,6 +507,7 @@ function Slide({
|
|
|
286
507
|
width,
|
|
287
508
|
aspectRatio,
|
|
288
509
|
editing = false,
|
|
510
|
+
buildStep,
|
|
289
511
|
onElementContentChange,
|
|
290
512
|
onElementSelect,
|
|
291
513
|
selectedElementId,
|
|
@@ -329,7 +551,24 @@ function Slide({
|
|
|
329
551
|
}),
|
|
330
552
|
[t, effectiveBg, slideWidthPx]
|
|
331
553
|
);
|
|
332
|
-
|
|
554
|
+
const buildInfo = react.useMemo(() => {
|
|
555
|
+
if (editing) return null;
|
|
556
|
+
const steps = buildSteps(slide);
|
|
557
|
+
if (steps.length === 0) return null;
|
|
558
|
+
const revealStep = /* @__PURE__ */ new Map();
|
|
559
|
+
steps.forEach((step, i) => {
|
|
560
|
+
for (const b of step.builds) {
|
|
561
|
+
if (!revealStep.has(b.element.id)) revealStep.set(b.element.id, i + 1);
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
const driven = buildStep !== void 0;
|
|
565
|
+
const currentStep = driven ? buildStep : steps.length;
|
|
566
|
+
const firing = driven ? steps[currentStep - 1] : void 0;
|
|
567
|
+
const delays = firing ? stepDelays(firing.builds) : /* @__PURE__ */ new Map();
|
|
568
|
+
const paraReveals = driven ? paragraphReveals(slide, currentStep) : /* @__PURE__ */ new Map();
|
|
569
|
+
return { revealStep, currentStep, delays, paraReveals, driven };
|
|
570
|
+
}, [editing, slide, buildStep]);
|
|
571
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SlideContext.Provider, { value: slideContext, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
333
572
|
"div",
|
|
334
573
|
{
|
|
335
574
|
ref,
|
|
@@ -347,23 +586,47 @@ function Slide({
|
|
|
347
586
|
onClick: (e) => {
|
|
348
587
|
if (e.target === e.currentTarget && onElementSelect) onElementSelect(null);
|
|
349
588
|
},
|
|
350
|
-
children:
|
|
351
|
-
|
|
352
|
-
{
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
589
|
+
children: [
|
|
590
|
+
buildInfo && /* @__PURE__ */ jsxRuntime.jsx("style", { children: BUILD_KEYFRAMES }),
|
|
591
|
+
orderedElements(slide.elements).map((element) => {
|
|
592
|
+
let buildHidden = false;
|
|
593
|
+
let buildAnimation;
|
|
594
|
+
let buildDelay = 0;
|
|
595
|
+
const paraReveal = buildInfo?.paraReveals.get(element.id);
|
|
596
|
+
if (buildInfo) {
|
|
597
|
+
const step = buildInfo.revealStep.get(element.id);
|
|
598
|
+
if (step !== void 0) {
|
|
599
|
+
if (buildInfo.currentStep < step) {
|
|
600
|
+
buildHidden = true;
|
|
601
|
+
} else if (paraReveal) ; else if (buildInfo.currentStep === step && element.animation) {
|
|
602
|
+
buildAnimation = element.animation;
|
|
603
|
+
buildDelay = buildInfo.delays.get(element.id) ?? 0;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
if (buildHidden) return null;
|
|
608
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
609
|
+
SlideElementHost,
|
|
610
|
+
{
|
|
611
|
+
element,
|
|
612
|
+
theme: t,
|
|
613
|
+
slideWidthPx,
|
|
614
|
+
slideHeightPx,
|
|
615
|
+
editing,
|
|
616
|
+
selected: selectedElementId === element.id,
|
|
617
|
+
onContentChange: onElementContentChange,
|
|
618
|
+
onSelect: onElementSelect,
|
|
619
|
+
onMove: onElementMove,
|
|
620
|
+
onResize: onElementResize,
|
|
621
|
+
renderElement,
|
|
622
|
+
buildAnimation,
|
|
623
|
+
buildDelay,
|
|
624
|
+
paraReveal
|
|
625
|
+
},
|
|
626
|
+
element.id
|
|
627
|
+
);
|
|
628
|
+
})
|
|
629
|
+
]
|
|
367
630
|
}
|
|
368
631
|
) });
|
|
369
632
|
}
|
|
@@ -380,7 +643,10 @@ function SlideElementHost({
|
|
|
380
643
|
onSelect,
|
|
381
644
|
onMove,
|
|
382
645
|
onResize,
|
|
383
|
-
renderElement
|
|
646
|
+
renderElement,
|
|
647
|
+
buildAnimation,
|
|
648
|
+
buildDelay = 0,
|
|
649
|
+
paraReveal
|
|
384
650
|
}) {
|
|
385
651
|
const dragRef = react.useRef(null);
|
|
386
652
|
if (element.hidden) return null;
|
|
@@ -449,15 +715,18 @@ function SlideElementHost({
|
|
|
449
715
|
outline: selected ? "2px solid #8b5cf6" : void 0,
|
|
450
716
|
outlineOffset: selected ? 2 : void 0,
|
|
451
717
|
cursor: canMove ? "move" : interactive ? "pointer" : "default",
|
|
452
|
-
touchAction: canMove ? "none" : void 0
|
|
718
|
+
touchAction: canMove ? "none" : void 0,
|
|
719
|
+
...buildAnimation ? buildEnterStyle(buildAnimation, buildDelay) : null
|
|
453
720
|
};
|
|
454
|
-
const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange }) ?? renderElement?.(element, slideWidthPx);
|
|
721
|
+
const inner = renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) ?? renderElement?.(element, slideWidthPx);
|
|
455
722
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
456
723
|
"div",
|
|
457
724
|
{
|
|
725
|
+
className: buildAnimation ? "fs-build-enter" : void 0,
|
|
458
726
|
style: box,
|
|
459
727
|
"data-fancy-slides-element": element.id,
|
|
460
728
|
"data-fancy-slides-element-type": element.type,
|
|
729
|
+
"data-fancy-slides-build": buildAnimation ? "" : void 0,
|
|
461
730
|
onPointerDown: canMove ? startDrag("move") : void 0,
|
|
462
731
|
onPointerMove: canMove ? onPointerMove : void 0,
|
|
463
732
|
onPointerUp: canMove ? endDrag : void 0,
|
|
@@ -516,7 +785,7 @@ function ResizeHandles({ onStart, onMove, onEnd }) {
|
|
|
516
785
|
anchor
|
|
517
786
|
)) });
|
|
518
787
|
}
|
|
519
|
-
function renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange }) {
|
|
788
|
+
function renderInner({ element, theme, slideWidthPx, editing, selected, onContentChange, paraReveal }) {
|
|
520
789
|
switch (element.type) {
|
|
521
790
|
case "text":
|
|
522
791
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -527,7 +796,8 @@ function renderInner({ element, theme, slideWidthPx, editing, selected, onConten
|
|
|
527
796
|
slideWidthPx,
|
|
528
797
|
editing,
|
|
529
798
|
selected,
|
|
530
|
-
onContentChange: onContentChange ? (c) => onContentChange(element.id, c) : void 0
|
|
799
|
+
onContentChange: onContentChange ? (c) => onContentChange(element.id, c) : void 0,
|
|
800
|
+
paraReveal
|
|
531
801
|
}
|
|
532
802
|
);
|
|
533
803
|
case "image":
|
|
@@ -579,6 +849,8 @@ function useSlideKeyboard({
|
|
|
579
849
|
total,
|
|
580
850
|
index,
|
|
581
851
|
goTo,
|
|
852
|
+
onAdvance,
|
|
853
|
+
onRetreat,
|
|
582
854
|
onExit,
|
|
583
855
|
onBlank,
|
|
584
856
|
onFullscreen,
|
|
@@ -595,13 +867,15 @@ function useSlideKeyboard({
|
|
|
595
867
|
case "ArrowLeft":
|
|
596
868
|
case "PageUp":
|
|
597
869
|
e.preventDefault();
|
|
598
|
-
if (
|
|
870
|
+
if (onRetreat) onRetreat();
|
|
871
|
+
else if (index > 0) goTo(index - 1);
|
|
599
872
|
return;
|
|
600
873
|
case "ArrowRight":
|
|
601
874
|
case "PageDown":
|
|
602
875
|
case " ":
|
|
603
876
|
e.preventDefault();
|
|
604
|
-
if (
|
|
877
|
+
if (onAdvance) onAdvance();
|
|
878
|
+
else if (index < total - 1) goTo(index + 1);
|
|
605
879
|
return;
|
|
606
880
|
case "Home":
|
|
607
881
|
e.preventDefault();
|
|
@@ -643,7 +917,7 @@ function useSlideKeyboard({
|
|
|
643
917
|
};
|
|
644
918
|
window.addEventListener("keydown", handler);
|
|
645
919
|
return () => window.removeEventListener("keydown", handler);
|
|
646
|
-
}, [enabled, index, total, goTo, onExit, onBlank, onFullscreen]);
|
|
920
|
+
}, [enabled, index, total, goTo, onAdvance, onRetreat, onExit, onBlank, onFullscreen]);
|
|
647
921
|
}
|
|
648
922
|
function SlideViewer({
|
|
649
923
|
deck,
|
|
@@ -671,13 +945,34 @@ function SlideViewer({
|
|
|
671
945
|
const containerRef = react.useRef(null);
|
|
672
946
|
const prevIndexRef = react.useRef(index);
|
|
673
947
|
const forward = index >= prevIndexRef.current;
|
|
948
|
+
const slide = deck.slides[index];
|
|
949
|
+
const totalSteps = totalBuildSteps(slide);
|
|
950
|
+
const [buildStep, setBuildStep] = react.useState(0);
|
|
951
|
+
const nextFreshRef = react.useRef(false);
|
|
674
952
|
react.useEffect(() => {
|
|
953
|
+
if (index === prevIndexRef.current) return;
|
|
675
954
|
prevIndexRef.current = index;
|
|
676
|
-
|
|
955
|
+
const fresh = nextFreshRef.current;
|
|
956
|
+
nextFreshRef.current = false;
|
|
957
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
958
|
+
}, [index, deck.slides]);
|
|
959
|
+
const advance = react.useCallback(() => {
|
|
960
|
+
if (buildStep < totalSteps) {
|
|
961
|
+
setBuildStep((s) => s + 1);
|
|
962
|
+
} else if (index < deck.slides.length - 1) {
|
|
963
|
+
nextFreshRef.current = true;
|
|
964
|
+
goTo(index + 1);
|
|
965
|
+
}
|
|
966
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
967
|
+
const retreat = react.useCallback(() => {
|
|
968
|
+
if (index > 0) goTo(index - 1);
|
|
969
|
+
}, [index, goTo]);
|
|
677
970
|
useSlideKeyboard({
|
|
678
971
|
total: deck.slides.length,
|
|
679
972
|
index,
|
|
680
973
|
goTo,
|
|
974
|
+
onAdvance: advance,
|
|
975
|
+
onRetreat: retreat,
|
|
681
976
|
onExit,
|
|
682
977
|
onBlank: () => setBlanked((b) => !b),
|
|
683
978
|
onFullscreen: () => {
|
|
@@ -690,11 +985,15 @@ function SlideViewer({
|
|
|
690
985
|
react.useEffect(() => {
|
|
691
986
|
if (!autoAdvanceMs || deck.slides.length <= 1) return;
|
|
692
987
|
const t = setTimeout(() => {
|
|
693
|
-
|
|
988
|
+
if (buildStep < totalSteps) {
|
|
989
|
+
setBuildStep((s) => s + 1);
|
|
990
|
+
} else {
|
|
991
|
+
nextFreshRef.current = true;
|
|
992
|
+
goTo(index + 1 < deck.slides.length ? index + 1 : 0);
|
|
993
|
+
}
|
|
694
994
|
}, autoAdvanceMs);
|
|
695
995
|
return () => clearTimeout(t);
|
|
696
|
-
}, [autoAdvanceMs, index, deck.slides.length, goTo]);
|
|
697
|
-
const slide = deck.slides[index];
|
|
996
|
+
}, [autoAdvanceMs, index, deck.slides.length, goTo, buildStep, totalSteps]);
|
|
698
997
|
const theme = resolveTheme(deck.theme);
|
|
699
998
|
const aspectRatio = theme.aspectRatio ?? 16 / 9;
|
|
700
999
|
const transition = slide?.transition ?? theme.defaultTransition;
|
|
@@ -716,6 +1015,11 @@ function SlideViewer({
|
|
|
716
1015
|
},
|
|
717
1016
|
tabIndex: 0,
|
|
718
1017
|
"data-fancy-slides-viewer": deck.id,
|
|
1018
|
+
"data-fancy-slides-build-step": buildStep,
|
|
1019
|
+
onClick: () => {
|
|
1020
|
+
if (blanked) return;
|
|
1021
|
+
advance();
|
|
1022
|
+
},
|
|
719
1023
|
children: [
|
|
720
1024
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: TRANSITION_KEYFRAMES }),
|
|
721
1025
|
!blanked && slide && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -729,7 +1033,7 @@ function SlideViewer({
|
|
|
729
1033
|
["--fs-ratio"]: aspectRatio.toString(),
|
|
730
1034
|
boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
|
|
731
1035
|
},
|
|
732
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, renderElement }) }, index)
|
|
1036
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, buildStep, renderElement }) }, index)
|
|
733
1037
|
}
|
|
734
1038
|
),
|
|
735
1039
|
!hideChrome && !blanked && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -760,7 +1064,7 @@ function SlideViewer({
|
|
|
760
1064
|
);
|
|
761
1065
|
}
|
|
762
1066
|
var DEFAULT_DURATION = 400;
|
|
763
|
-
var
|
|
1067
|
+
var EASE2 = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
764
1068
|
function transitionEnterStyle(transition, forward) {
|
|
765
1069
|
const kind = transition?.kind ?? "none";
|
|
766
1070
|
if (kind === "none") return { width: "100%", height: "100%" };
|
|
@@ -786,7 +1090,7 @@ function transitionEnterStyle(transition, forward) {
|
|
|
786
1090
|
height: "100%",
|
|
787
1091
|
animationName: name,
|
|
788
1092
|
animationDuration: `${duration}ms`,
|
|
789
|
-
animationTimingFunction:
|
|
1093
|
+
animationTimingFunction: EASE2,
|
|
790
1094
|
animationFillMode: "both"
|
|
791
1095
|
};
|
|
792
1096
|
}
|
|
@@ -842,14 +1146,38 @@ function PresenterView({
|
|
|
842
1146
|
},
|
|
843
1147
|
[deck.slides.length, isControlled, onIndexChange]
|
|
844
1148
|
);
|
|
1149
|
+
const slide = deck.slides[index];
|
|
1150
|
+
const totalSteps = totalBuildSteps(slide);
|
|
1151
|
+
const [buildStep, setBuildStep] = react.useState(0);
|
|
1152
|
+
const prevIndexRef = react.useRef(index);
|
|
1153
|
+
const nextFreshRef = react.useRef(false);
|
|
1154
|
+
react.useEffect(() => {
|
|
1155
|
+
if (index === prevIndexRef.current) return;
|
|
1156
|
+
prevIndexRef.current = index;
|
|
1157
|
+
const fresh = nextFreshRef.current;
|
|
1158
|
+
nextFreshRef.current = false;
|
|
1159
|
+
setBuildStep(fresh ? 0 : totalBuildSteps(deck.slides[index]));
|
|
1160
|
+
}, [index, deck.slides]);
|
|
1161
|
+
const advance = react.useCallback(() => {
|
|
1162
|
+
if (buildStep < totalSteps) {
|
|
1163
|
+
setBuildStep((s) => s + 1);
|
|
1164
|
+
} else if (index < deck.slides.length - 1) {
|
|
1165
|
+
nextFreshRef.current = true;
|
|
1166
|
+
goTo(index + 1);
|
|
1167
|
+
}
|
|
1168
|
+
}, [buildStep, totalSteps, index, deck.slides.length, goTo]);
|
|
1169
|
+
const retreat = react.useCallback(() => {
|
|
1170
|
+
if (index > 0) goTo(index - 1);
|
|
1171
|
+
}, [index, goTo]);
|
|
845
1172
|
useSlideKeyboard({
|
|
846
1173
|
total: deck.slides.length,
|
|
847
1174
|
index,
|
|
848
1175
|
goTo,
|
|
1176
|
+
onAdvance: advance,
|
|
1177
|
+
onRetreat: retreat,
|
|
849
1178
|
onExit
|
|
850
1179
|
});
|
|
851
1180
|
const theme = resolveTheme(deck.theme);
|
|
852
|
-
const slide = deck.slides[index];
|
|
853
1181
|
const nextSlide = deck.slides[index + 1];
|
|
854
1182
|
const [now, setNow] = react.useState(() => Date.now());
|
|
855
1183
|
react.useEffect(() => {
|
|
@@ -896,7 +1224,7 @@ function PresenterView({
|
|
|
896
1224
|
borderRadius: 8,
|
|
897
1225
|
overflow: "hidden"
|
|
898
1226
|
},
|
|
899
|
-
children: slide ? /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, renderElement }) : null
|
|
1227
|
+
children: slide ? /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, buildStep, renderElement }) : null
|
|
900
1228
|
}
|
|
901
1229
|
)
|
|
902
1230
|
}
|
|
@@ -1000,8 +1328,8 @@ function PresenterView({
|
|
|
1000
1328
|
/* @__PURE__ */ jsxRuntime.jsx(StatusChip, { label: "Elapsed", children: formatElapsed(now - startedAtRef) }),
|
|
1001
1329
|
/* @__PURE__ */ jsxRuntime.jsx(StatusChip, { label: "Clock", children: formatClock(now) }),
|
|
1002
1330
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginLeft: "auto", display: "flex", gap: 8 }, children: [
|
|
1003
|
-
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick:
|
|
1004
|
-
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick:
|
|
1331
|
+
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick: retreat, disabled: index === 0, children: "\u2190 Prev" }),
|
|
1332
|
+
/* @__PURE__ */ jsxRuntime.jsx(NavButton, { onClick: advance, disabled: index >= deck.slides.length - 1 && buildStep >= totalSteps, children: "Next \u2192" })
|
|
1005
1333
|
] })
|
|
1006
1334
|
]
|
|
1007
1335
|
}
|
|
@@ -1184,6 +1512,7 @@ function useDeckState({ value, onChange, onOp }) {
|
|
|
1184
1512
|
updateElement: (slideIdArg, elementIdArg, patch) => apply({ kind: "element_update", slideId: slideIdArg, elementId: elementIdArg, patch }),
|
|
1185
1513
|
moveElement: (slideIdArg, elementIdArg, x, y) => apply({ kind: "element_move", slideId: slideIdArg, elementId: elementIdArg, x, y }),
|
|
1186
1514
|
resizeElement: (slideIdArg, elementIdArg, w, h) => apply({ kind: "element_resize", slideId: slideIdArg, elementId: elementIdArg, w, h }),
|
|
1515
|
+
setAnimation: (slideIdArg, elementIdArg, animation) => apply({ kind: "element_set_animation", slideId: slideIdArg, elementId: elementIdArg, animation }),
|
|
1187
1516
|
getSlide: (id) => value.slides.find((s) => s.id === id),
|
|
1188
1517
|
getElement: (slideIdArg, elementIdArg) => value.slides.find((s) => s.id === slideIdArg)?.elements.find((e) => e.id === elementIdArg)
|
|
1189
1518
|
};
|
|
@@ -1251,6 +1580,23 @@ function reduce(deck, op) {
|
|
|
1251
1580
|
(s) => s.id === op.slideId ? { ...s, elements: s.elements.map((e) => e.id === op.elementId ? { ...e, w: op.w, h: op.h } : e) } : s
|
|
1252
1581
|
)
|
|
1253
1582
|
};
|
|
1583
|
+
case "element_set_animation":
|
|
1584
|
+
return {
|
|
1585
|
+
...deck,
|
|
1586
|
+
slides: deck.slides.map(
|
|
1587
|
+
(s) => s.id === op.slideId ? {
|
|
1588
|
+
...s,
|
|
1589
|
+
elements: s.elements.map((e) => {
|
|
1590
|
+
if (e.id !== op.elementId) return e;
|
|
1591
|
+
if (op.animation === void 0) {
|
|
1592
|
+
const { animation: _drop, ...rest } = e;
|
|
1593
|
+
return rest;
|
|
1594
|
+
}
|
|
1595
|
+
return { ...e, animation: op.animation };
|
|
1596
|
+
})
|
|
1597
|
+
} : s
|
|
1598
|
+
)
|
|
1599
|
+
};
|
|
1254
1600
|
}
|
|
1255
1601
|
}
|
|
1256
1602
|
|
|
@@ -1592,10 +1938,10 @@ function EditorToolbar({
|
|
|
1592
1938
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tooltip, { content: "Present (F)", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { color: "violet", size: "sm", icon: "play", onClick: onPresent, children: "Present" }) }) })
|
|
1593
1939
|
] });
|
|
1594
1940
|
}
|
|
1595
|
-
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
|
|
1941
|
+
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground, onSetAnimation, onSetElementAnimation }) {
|
|
1596
1942
|
if (!element) {
|
|
1597
1943
|
if (slide) {
|
|
1598
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
|
|
1944
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SlideSettings, { slide, onSetTransition, onSetBackground, onSetElementAnimation });
|
|
1599
1945
|
}
|
|
1600
1946
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fs-inspector flex h-full flex-col border-l border-zinc-200 bg-zinc-50 p-4 dark:border-zinc-800 dark:bg-zinc-900", children: [
|
|
1601
1947
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
|
|
@@ -1620,10 +1966,12 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1620
1966
|
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Tabs.List, { children: [
|
|
1621
1967
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "style", children: "Style" }),
|
|
1622
1968
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "layout", children: "Layout" }),
|
|
1969
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "build", children: "Build" }),
|
|
1623
1970
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Tab, { value: "advanced", children: "Advanced" })
|
|
1624
1971
|
] }),
|
|
1625
1972
|
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Tabs.Panels, { children: [
|
|
1626
1973
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "style", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(StyleSection, { element, onPatch }) }) }),
|
|
1974
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "build", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(AnimateSection, { animation: element.animation, onSetAnimation, isText: element.type === "text" }) }) }),
|
|
1627
1975
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "layout", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(LayoutSection, { element, onPatch }) }) }),
|
|
1628
1976
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Tabs.Panel, { value: "advanced", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(AdvancedSection, { element, onPatch }) }) })
|
|
1629
1977
|
] })
|
|
@@ -1633,7 +1981,8 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onS
|
|
|
1633
1981
|
function SlideSettings({
|
|
1634
1982
|
slide,
|
|
1635
1983
|
onSetTransition,
|
|
1636
|
-
onSetBackground
|
|
1984
|
+
onSetBackground,
|
|
1985
|
+
onSetElementAnimation
|
|
1637
1986
|
}) {
|
|
1638
1987
|
const transition = slide.transition;
|
|
1639
1988
|
const kind = transition?.kind ?? "none";
|
|
@@ -1700,10 +2049,54 @@ function SlideSettings({
|
|
|
1700
2049
|
onChange: (c) => onSetBackground({ ...slide.background, color: c })
|
|
1701
2050
|
}
|
|
1702
2051
|
) })
|
|
1703
|
-
] }) })
|
|
2052
|
+
] }) }),
|
|
2053
|
+
onSetElementAnimation && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsx(BuildOrderList, { slide, onSetElementAnimation }) })
|
|
1704
2054
|
] })
|
|
1705
2055
|
] });
|
|
1706
2056
|
}
|
|
2057
|
+
function BuildOrderList({
|
|
2058
|
+
slide,
|
|
2059
|
+
onSetElementAnimation
|
|
2060
|
+
}) {
|
|
2061
|
+
const builds = collectBuilds(slide);
|
|
2062
|
+
const move = (from, to) => {
|
|
2063
|
+
if (to < 0 || to >= builds.length) return;
|
|
2064
|
+
const reordered = [...builds];
|
|
2065
|
+
const [item] = reordered.splice(from, 1);
|
|
2066
|
+
reordered.splice(to, 0, item);
|
|
2067
|
+
reordered.forEach((b, i) => {
|
|
2068
|
+
if ((b.animation.order ?? 0) !== i) {
|
|
2069
|
+
onSetElementAnimation(b.element.id, { ...b.animation, order: i });
|
|
2070
|
+
}
|
|
2071
|
+
});
|
|
2072
|
+
};
|
|
2073
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2074
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Build order" }),
|
|
2075
|
+
builds.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: "No animated elements yet. Select an element and add a build under its Build tab." }) : builds.map((b, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2076
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Text, { size: "xs", className: "!font-mono !text-zinc-400 w-5", children: [
|
|
2077
|
+
i + 1,
|
|
2078
|
+
"."
|
|
2079
|
+
] }),
|
|
2080
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2081
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "truncate", children: buildLabel(b.element) }),
|
|
2082
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Text, { size: "xs", className: "!font-mono !text-zinc-400", children: [
|
|
2083
|
+
b.animation.effect,
|
|
2084
|
+
" \xB7 ",
|
|
2085
|
+
b.animation.trigger ?? "on-click"
|
|
2086
|
+
] })
|
|
2087
|
+
] }),
|
|
2088
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "chevron-up", onClick: () => move(i, i - 1), disabled: i === 0, "aria-label": "Move earlier" }),
|
|
2089
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "chevron-down", onClick: () => move(i, i + 1), disabled: i === builds.length - 1, "aria-label": "Move later" })
|
|
2090
|
+
] }, b.element.id))
|
|
2091
|
+
] });
|
|
2092
|
+
}
|
|
2093
|
+
function buildLabel(element) {
|
|
2094
|
+
if (element.type === "text") {
|
|
2095
|
+
const text = element.content.replace(/\s+/g, " ").trim();
|
|
2096
|
+
return text ? text.length > 28 ? `${text.slice(0, 28)}\u2026` : text : "Text";
|
|
2097
|
+
}
|
|
2098
|
+
return `${element.type} #${element.id.slice(-6)}`;
|
|
2099
|
+
}
|
|
1707
2100
|
function LayoutSection({ element, onPatch }) {
|
|
1708
2101
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1709
2102
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
@@ -1725,6 +2118,109 @@ function AdvancedSection({ element, onPatch }) {
|
|
|
1725
2118
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "sm", variant: element.hidden ? "default" : "ghost", onClick: () => onPatch({ hidden: !element.hidden }), children: element.hidden ? "Hidden \u2014 show" : "Hide on slide" }) })
|
|
1726
2119
|
] });
|
|
1727
2120
|
}
|
|
2121
|
+
var NO_ANIMATION = "none";
|
|
2122
|
+
function AnimateSection({
|
|
2123
|
+
animation,
|
|
2124
|
+
onSetAnimation,
|
|
2125
|
+
isText
|
|
2126
|
+
}) {
|
|
2127
|
+
if (!onSetAnimation) {
|
|
2128
|
+
return /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "!text-zinc-500", children: "Build animations aren't wired up in this editor." });
|
|
2129
|
+
}
|
|
2130
|
+
const effect = animation?.effect;
|
|
2131
|
+
const set = (next) => {
|
|
2132
|
+
const base = animation ?? { effect: "fade" };
|
|
2133
|
+
onSetAnimation({ ...base, ...next });
|
|
2134
|
+
};
|
|
2135
|
+
const showDirection = effect === "fly-in" || effect === "wipe";
|
|
2136
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2137
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2138
|
+
reactFancy.Select,
|
|
2139
|
+
{
|
|
2140
|
+
label: "Effect",
|
|
2141
|
+
list: [
|
|
2142
|
+
{ value: NO_ANIMATION, label: "None" },
|
|
2143
|
+
{ value: "fade", label: "Fade" },
|
|
2144
|
+
{ value: "fly-in", label: "Fly in" },
|
|
2145
|
+
{ value: "zoom", label: "Zoom" },
|
|
2146
|
+
{ value: "wipe", label: "Wipe" }
|
|
2147
|
+
],
|
|
2148
|
+
value: effect ?? NO_ANIMATION,
|
|
2149
|
+
onValueChange: (v) => {
|
|
2150
|
+
if (v === NO_ANIMATION) onSetAnimation(void 0);
|
|
2151
|
+
else set({ effect: v });
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
),
|
|
2155
|
+
effect && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2156
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2157
|
+
reactFancy.Select,
|
|
2158
|
+
{
|
|
2159
|
+
label: "Trigger",
|
|
2160
|
+
list: [
|
|
2161
|
+
{ value: "on-click", label: "On click" },
|
|
2162
|
+
{ value: "with-prev", label: "With previous" },
|
|
2163
|
+
{ value: "after-prev", label: "After previous" }
|
|
2164
|
+
],
|
|
2165
|
+
value: animation?.trigger ?? "on-click",
|
|
2166
|
+
onValueChange: (v) => set({ trigger: v })
|
|
2167
|
+
}
|
|
2168
|
+
),
|
|
2169
|
+
showDirection && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2170
|
+
reactFancy.Select,
|
|
2171
|
+
{
|
|
2172
|
+
label: "Direction",
|
|
2173
|
+
list: [
|
|
2174
|
+
{ value: "left", label: "From left" },
|
|
2175
|
+
{ value: "right", label: "From right" },
|
|
2176
|
+
{ value: "up", label: "From bottom" },
|
|
2177
|
+
{ value: "down", label: "From top" }
|
|
2178
|
+
],
|
|
2179
|
+
value: animation?.direction ?? "left",
|
|
2180
|
+
onValueChange: (v) => set({ direction: v })
|
|
2181
|
+
}
|
|
2182
|
+
),
|
|
2183
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
2184
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2185
|
+
reactFancy.Input,
|
|
2186
|
+
{
|
|
2187
|
+
label: "Duration (ms)",
|
|
2188
|
+
type: "number",
|
|
2189
|
+
value: String(animation?.duration ?? 500),
|
|
2190
|
+
onChange: (e) => set({ duration: parseInt(e.target.value, 10) || 500 })
|
|
2191
|
+
}
|
|
2192
|
+
),
|
|
2193
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2194
|
+
reactFancy.Input,
|
|
2195
|
+
{
|
|
2196
|
+
label: "Delay (ms)",
|
|
2197
|
+
type: "number",
|
|
2198
|
+
value: String(animation?.delay ?? 0),
|
|
2199
|
+
onChange: (e) => set({ delay: parseInt(e.target.value, 10) || 0 })
|
|
2200
|
+
}
|
|
2201
|
+
)
|
|
2202
|
+
] }),
|
|
2203
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2204
|
+
reactFancy.Input,
|
|
2205
|
+
{
|
|
2206
|
+
label: "Order",
|
|
2207
|
+
type: "number",
|
|
2208
|
+
value: String(animation?.order ?? 0),
|
|
2209
|
+
onChange: (e) => set({ order: parseInt(e.target.value, 10) || 0 })
|
|
2210
|
+
}
|
|
2211
|
+
),
|
|
2212
|
+
isText && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2213
|
+
reactFancy.Switch,
|
|
2214
|
+
{
|
|
2215
|
+
label: "Animate by paragraph (one line per click)",
|
|
2216
|
+
checked: !!animation?.byParagraph,
|
|
2217
|
+
onCheckedChange: (v) => set({ byParagraph: v })
|
|
2218
|
+
}
|
|
2219
|
+
),
|
|
2220
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: `Builds reveal in ascending order. "On click" starts a new step; "with previous" plays alongside the step's lead; "after previous" follows it. Honors prefers-reduced-motion.` })
|
|
2221
|
+
] })
|
|
2222
|
+
] });
|
|
2223
|
+
}
|
|
1728
2224
|
function StyleSection({ element, onPatch }) {
|
|
1729
2225
|
switch (element.type) {
|
|
1730
2226
|
case "text":
|
|
@@ -2400,7 +2896,9 @@ function DeckEditor({
|
|
|
2400
2896
|
},
|
|
2401
2897
|
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
|
|
2402
2898
|
onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
|
|
2403
|
-
onSetBackground: (background) => slide && ops.setBackground(slide.id, background)
|
|
2899
|
+
onSetBackground: (background) => slide && ops.setBackground(slide.id, background),
|
|
2900
|
+
onSetAnimation: (animation) => slide && elementIdSelected && ops.setAnimation(slide.id, elementIdSelected, animation),
|
|
2901
|
+
onSetElementAnimation: (eid, animation) => slide && ops.setAnimation(slide.id, eid, animation)
|
|
2404
2902
|
}
|
|
2405
2903
|
) })
|
|
2406
2904
|
] }),
|
|
@@ -2422,22 +2920,31 @@ exports.SlideThumbnail = SlideThumbnail;
|
|
|
2422
2920
|
exports.SlideViewer = SlideViewer;
|
|
2423
2921
|
exports.SpeakerNotes = SpeakerNotes;
|
|
2424
2922
|
exports.TextElementRenderer = TextElementRenderer;
|
|
2923
|
+
exports.buildSteps = buildSteps;
|
|
2924
|
+
exports.buildsForStep = buildsForStep;
|
|
2425
2925
|
exports.builtinThemes = builtinThemes;
|
|
2426
2926
|
exports.chartStarterOption = chartStarterOption;
|
|
2927
|
+
exports.collectBuilds = collectBuilds;
|
|
2427
2928
|
exports.darkTheme = darkTheme;
|
|
2428
2929
|
exports.deckId = deckId;
|
|
2429
2930
|
exports.defaultTheme = defaultTheme;
|
|
2430
2931
|
exports.defineTheme = defineTheme;
|
|
2431
2932
|
exports.elementId = elementId;
|
|
2933
|
+
exports.isByParagraph = isByParagraph;
|
|
2432
2934
|
exports.nextId = nextId;
|
|
2935
|
+
exports.paragraphReveals = paragraphReveals;
|
|
2433
2936
|
exports.reduceDeck = reduce;
|
|
2434
2937
|
exports.resolveTheme = resolveTheme;
|
|
2435
2938
|
exports.slideId = slideId;
|
|
2939
|
+
exports.splitParagraphs = splitParagraphs;
|
|
2940
|
+
exports.stepDelays = stepDelays;
|
|
2941
|
+
exports.totalBuildSteps = totalBuildSteps;
|
|
2436
2942
|
exports.useDeckState = useDeckState;
|
|
2437
2943
|
exports.useIsDarkSlide = useIsDarkSlide;
|
|
2438
2944
|
exports.useSlideContext = useSlideContext;
|
|
2439
2945
|
exports.useSlideKeyboard = useSlideKeyboard;
|
|
2440
2946
|
exports.useSlideTheme = useSlideTheme;
|
|
2947
|
+
exports.visibleElementIds = visibleElementIds;
|
|
2441
2948
|
exports.vividTheme = vividTheme;
|
|
2442
2949
|
//# sourceMappingURL=index.cjs.map
|
|
2443
2950
|
//# sourceMappingURL=index.cjs.map
|