@particle-academy/fancy-slides 0.1.7 → 0.3.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/dist/index.cjs +557 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -5
- package/dist/index.d.ts +20 -5
- package/dist/index.js +558 -44
- package/dist/index.js.map +1 -1
- package/dist/registry.d.cts +1 -1
- package/dist/registry.d.ts +1 -1
- package/dist/{types-Bc-psiRF.d.cts → types-B2ecrEAz.d.cts} +4 -0
- package/dist/{types-Bc-psiRF.d.ts → types-B2ecrEAz.d.ts} +4 -0
- 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,
|
|
4
|
+
import { ContentRenderer, Text, Action, ContextMenu, Separator, Tooltip, Dropdown, Badge, Heading, Tabs, Card, Select, Input, ColorPicker, Slider, 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
|
|
@@ -159,6 +159,22 @@ function weight(w) {
|
|
|
159
159
|
return void 0;
|
|
160
160
|
}
|
|
161
161
|
function ImageElementRenderer({ element }) {
|
|
162
|
+
const crop = element.crop;
|
|
163
|
+
const fit = element.fit ?? "contain";
|
|
164
|
+
if (crop && crop.w > 0 && crop.h > 0) {
|
|
165
|
+
const inner = {
|
|
166
|
+
position: "absolute",
|
|
167
|
+
left: 0,
|
|
168
|
+
top: 0,
|
|
169
|
+
width: `${1 / crop.w * 100}%`,
|
|
170
|
+
height: `${1 / crop.h * 100}%`,
|
|
171
|
+
transform: `translate(${-crop.x / crop.w * 100}%, ${-crop.y / crop.h * 100}%)`,
|
|
172
|
+
transformOrigin: "top left",
|
|
173
|
+
objectFit: fit,
|
|
174
|
+
display: "block"
|
|
175
|
+
};
|
|
176
|
+
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 }) });
|
|
177
|
+
}
|
|
162
178
|
return /* @__PURE__ */ jsx(
|
|
163
179
|
"img",
|
|
164
180
|
{
|
|
@@ -167,7 +183,7 @@ function ImageElementRenderer({ element }) {
|
|
|
167
183
|
style: {
|
|
168
184
|
width: "100%",
|
|
169
185
|
height: "100%",
|
|
170
|
-
objectFit:
|
|
186
|
+
objectFit: fit,
|
|
171
187
|
display: "block"
|
|
172
188
|
},
|
|
173
189
|
draggable: false
|
|
@@ -611,6 +627,11 @@ function SlideViewer({
|
|
|
611
627
|
);
|
|
612
628
|
const [blanked, setBlanked] = useState(false);
|
|
613
629
|
const containerRef = useRef(null);
|
|
630
|
+
const prevIndexRef = useRef(index);
|
|
631
|
+
const forward = index >= prevIndexRef.current;
|
|
632
|
+
useEffect(() => {
|
|
633
|
+
prevIndexRef.current = index;
|
|
634
|
+
}, [index]);
|
|
614
635
|
useSlideKeyboard({
|
|
615
636
|
total: deck.slides.length,
|
|
616
637
|
index,
|
|
@@ -634,6 +655,8 @@ function SlideViewer({
|
|
|
634
655
|
const slide = deck.slides[index];
|
|
635
656
|
const theme = resolveTheme(deck.theme);
|
|
636
657
|
const aspectRatio = theme.aspectRatio ?? 16 / 9;
|
|
658
|
+
const transition = slide?.transition ?? theme.defaultTransition;
|
|
659
|
+
const enterStyle = transitionEnterStyle(transition, forward);
|
|
637
660
|
return /* @__PURE__ */ jsxs(
|
|
638
661
|
"div",
|
|
639
662
|
{
|
|
@@ -652,6 +675,7 @@ function SlideViewer({
|
|
|
652
675
|
tabIndex: 0,
|
|
653
676
|
"data-fancy-slides-viewer": deck.id,
|
|
654
677
|
children: [
|
|
678
|
+
/* @__PURE__ */ jsx("style", { children: TRANSITION_KEYFRAMES }),
|
|
655
679
|
!blanked && slide && /* @__PURE__ */ jsx(
|
|
656
680
|
"div",
|
|
657
681
|
{
|
|
@@ -663,7 +687,7 @@ function SlideViewer({
|
|
|
663
687
|
["--fs-ratio"]: aspectRatio.toString(),
|
|
664
688
|
boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
|
|
665
689
|
},
|
|
666
|
-
children: /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement })
|
|
690
|
+
children: /* @__PURE__ */ jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsx(Slide, { slide, theme, renderElement }) }, index)
|
|
667
691
|
}
|
|
668
692
|
),
|
|
669
693
|
!hideChrome && !blanked && /* @__PURE__ */ jsxs(
|
|
@@ -693,6 +717,68 @@ function SlideViewer({
|
|
|
693
717
|
}
|
|
694
718
|
);
|
|
695
719
|
}
|
|
720
|
+
var DEFAULT_DURATION = 400;
|
|
721
|
+
var EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
722
|
+
function transitionEnterStyle(transition, forward) {
|
|
723
|
+
const kind = transition?.kind ?? "none";
|
|
724
|
+
if (kind === "none") return { width: "100%", height: "100%" };
|
|
725
|
+
const duration = transition?.duration ?? DEFAULT_DURATION;
|
|
726
|
+
let name;
|
|
727
|
+
switch (kind) {
|
|
728
|
+
case "fade":
|
|
729
|
+
name = "fs-fade-in";
|
|
730
|
+
break;
|
|
731
|
+
case "zoom":
|
|
732
|
+
name = "fs-zoom-in";
|
|
733
|
+
break;
|
|
734
|
+
case "slide": {
|
|
735
|
+
const dir = transition?.direction ?? (forward ? "right" : "left");
|
|
736
|
+
name = `fs-slide-in-${dir}`;
|
|
737
|
+
break;
|
|
738
|
+
}
|
|
739
|
+
default:
|
|
740
|
+
return { width: "100%", height: "100%" };
|
|
741
|
+
}
|
|
742
|
+
return {
|
|
743
|
+
width: "100%",
|
|
744
|
+
height: "100%",
|
|
745
|
+
animationName: name,
|
|
746
|
+
animationDuration: `${duration}ms`,
|
|
747
|
+
animationTimingFunction: EASE,
|
|
748
|
+
animationFillMode: "both"
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
var TRANSITION_KEYFRAMES = `
|
|
752
|
+
@media (prefers-reduced-motion: reduce) {
|
|
753
|
+
.fs-slide-enter { animation: none !important; }
|
|
754
|
+
}
|
|
755
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
756
|
+
@keyframes fs-fade-in {
|
|
757
|
+
from { opacity: 0; }
|
|
758
|
+
to { opacity: 1; }
|
|
759
|
+
}
|
|
760
|
+
@keyframes fs-zoom-in {
|
|
761
|
+
from { opacity: 0; transform: scale(0.92); }
|
|
762
|
+
to { opacity: 1; transform: scale(1); }
|
|
763
|
+
}
|
|
764
|
+
@keyframes fs-slide-in-right {
|
|
765
|
+
from { opacity: 0; transform: translateX(8%); }
|
|
766
|
+
to { opacity: 1; transform: translateX(0); }
|
|
767
|
+
}
|
|
768
|
+
@keyframes fs-slide-in-left {
|
|
769
|
+
from { opacity: 0; transform: translateX(-8%); }
|
|
770
|
+
to { opacity: 1; transform: translateX(0); }
|
|
771
|
+
}
|
|
772
|
+
@keyframes fs-slide-in-up {
|
|
773
|
+
from { opacity: 0; transform: translateY(8%); }
|
|
774
|
+
to { opacity: 1; transform: translateY(0); }
|
|
775
|
+
}
|
|
776
|
+
@keyframes fs-slide-in-down {
|
|
777
|
+
from { opacity: 0; transform: translateY(-8%); }
|
|
778
|
+
to { opacity: 1; transform: translateY(0); }
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
`;
|
|
696
782
|
function PresenterView({
|
|
697
783
|
deck,
|
|
698
784
|
index: controlledIndex,
|
|
@@ -1046,6 +1132,7 @@ function useDeckState({ value, onChange, onOp }) {
|
|
|
1046
1132
|
setLayout: (id, layout) => apply({ kind: "slide_set_layout", id, layout }),
|
|
1047
1133
|
setNotes: (id, notes) => apply({ kind: "slide_set_notes", id, notes }),
|
|
1048
1134
|
setBackground: (id, background) => apply({ kind: "slide_set_background", id, background }),
|
|
1135
|
+
setTransition: (id, transition) => apply({ kind: "slide_set_transition", id, transition }),
|
|
1049
1136
|
addElement: (slideId2, element) => {
|
|
1050
1137
|
const id = element.id ?? elementId();
|
|
1051
1138
|
apply({ kind: "element_add", slideId: slideId2, element: { ...element, id } });
|
|
@@ -1087,6 +1174,8 @@ function reduce(deck, op) {
|
|
|
1087
1174
|
return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, notes: op.notes } : s) };
|
|
1088
1175
|
case "slide_set_background":
|
|
1089
1176
|
return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, background: op.background } : s) };
|
|
1177
|
+
case "slide_set_transition":
|
|
1178
|
+
return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, transition: op.transition } : s) };
|
|
1090
1179
|
case "element_add":
|
|
1091
1180
|
return {
|
|
1092
1181
|
...deck,
|
|
@@ -1206,6 +1295,113 @@ function chartStarterOption(kind) {
|
|
|
1206
1295
|
};
|
|
1207
1296
|
}
|
|
1208
1297
|
}
|
|
1298
|
+
var CHART_PALETTE = [
|
|
1299
|
+
"#8b5cf6",
|
|
1300
|
+
"#3b82f6",
|
|
1301
|
+
"#10b981",
|
|
1302
|
+
"#f59e0b",
|
|
1303
|
+
"#ef4444",
|
|
1304
|
+
"#ec4899",
|
|
1305
|
+
"#14b8a6",
|
|
1306
|
+
"#6366f1"
|
|
1307
|
+
];
|
|
1308
|
+
function chartColorAt(index) {
|
|
1309
|
+
return CHART_PALETTE[index % CHART_PALETTE.length];
|
|
1310
|
+
}
|
|
1311
|
+
function isPlainObject(v) {
|
|
1312
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1313
|
+
}
|
|
1314
|
+
function toNumber(v) {
|
|
1315
|
+
const n = typeof v === "number" ? v : parseFloat(String(v));
|
|
1316
|
+
return Number.isFinite(n) ? n : 0;
|
|
1317
|
+
}
|
|
1318
|
+
function chartModelFromOption(option) {
|
|
1319
|
+
if (!isPlainObject(option)) return null;
|
|
1320
|
+
const seriesRaw = option.series;
|
|
1321
|
+
if (!Array.isArray(seriesRaw) || seriesRaw.length === 0) return null;
|
|
1322
|
+
if (!seriesRaw.every(isPlainObject)) return null;
|
|
1323
|
+
const types = seriesRaw.map((s) => String(s.type ?? ""));
|
|
1324
|
+
if (types[0] === "pie") {
|
|
1325
|
+
if (seriesRaw.length !== 1) return null;
|
|
1326
|
+
const data = seriesRaw[0].data;
|
|
1327
|
+
if (!Array.isArray(data)) return null;
|
|
1328
|
+
const slices = [];
|
|
1329
|
+
for (const d of data) {
|
|
1330
|
+
if (!isPlainObject(d)) return null;
|
|
1331
|
+
slices.push({ name: String(d.name ?? ""), value: toNumber(d.value) });
|
|
1332
|
+
}
|
|
1333
|
+
return { kind: "pie", categories: [], series: [], slices };
|
|
1334
|
+
}
|
|
1335
|
+
const cartesian = /* @__PURE__ */ new Set(["bar", "line", "scatter"]);
|
|
1336
|
+
if (!types.every((t) => cartesian.has(t))) return null;
|
|
1337
|
+
if (new Set(types).size !== 1) return null;
|
|
1338
|
+
const baseType = types[0];
|
|
1339
|
+
const isArea = baseType === "line" && seriesRaw.every((s) => isPlainObject(s.areaStyle) || s.areaStyle != null);
|
|
1340
|
+
const kind = baseType === "line" ? isArea ? "area" : "line" : baseType;
|
|
1341
|
+
const xAxis = option.xAxis;
|
|
1342
|
+
const axisData = isPlainObject(xAxis) ? xAxis.data : void 0;
|
|
1343
|
+
const categories = Array.isArray(axisData) ? axisData.map((c) => String(c)) : [];
|
|
1344
|
+
const firstData = seriesRaw[0].data;
|
|
1345
|
+
const valueCount = Array.isArray(firstData) ? firstData.length : 0;
|
|
1346
|
+
const cats = categories.length > 0 ? categories : Array.from({ length: valueCount }, (_, i) => String(i + 1));
|
|
1347
|
+
const series = [];
|
|
1348
|
+
for (const s of seriesRaw) {
|
|
1349
|
+
const data = s.data;
|
|
1350
|
+
if (!Array.isArray(data)) return null;
|
|
1351
|
+
const values = data.map((d) => {
|
|
1352
|
+
if (Array.isArray(d)) return toNumber(d[1]);
|
|
1353
|
+
if (isPlainObject(d)) return toNumber(d.value);
|
|
1354
|
+
return toNumber(d);
|
|
1355
|
+
});
|
|
1356
|
+
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 });
|
|
1357
|
+
}
|
|
1358
|
+
return { kind, categories: cats, series, slices: [] };
|
|
1359
|
+
}
|
|
1360
|
+
function chartOptionFromModel(model) {
|
|
1361
|
+
if (model.kind === "pie") {
|
|
1362
|
+
return {
|
|
1363
|
+
tooltip: { trigger: "item" },
|
|
1364
|
+
legend: { bottom: 0 },
|
|
1365
|
+
color: model.slices.map((_, i) => chartColorAt(i)),
|
|
1366
|
+
series: [
|
|
1367
|
+
{
|
|
1368
|
+
type: "pie",
|
|
1369
|
+
radius: ["40%", "70%"],
|
|
1370
|
+
name: "Segment",
|
|
1371
|
+
data: model.slices.map((s) => ({ name: s.name, value: s.value }))
|
|
1372
|
+
}
|
|
1373
|
+
]
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
const isScatter = model.kind === "scatter";
|
|
1377
|
+
const isArea = model.kind === "area";
|
|
1378
|
+
const seriesType = model.kind === "bar" ? "bar" : model.kind === "scatter" ? "scatter" : "line";
|
|
1379
|
+
const series = model.series.map((s, i) => {
|
|
1380
|
+
const color = s.color ?? chartColorAt(i);
|
|
1381
|
+
const base = {
|
|
1382
|
+
type: seriesType,
|
|
1383
|
+
name: s.name,
|
|
1384
|
+
itemStyle: { color }
|
|
1385
|
+
};
|
|
1386
|
+
if (isScatter) {
|
|
1387
|
+
base.symbolSize = 12;
|
|
1388
|
+
base.data = s.values.map((v, idx) => [idx, v]);
|
|
1389
|
+
} else {
|
|
1390
|
+
base.data = s.values;
|
|
1391
|
+
}
|
|
1392
|
+
if (seriesType === "line") base.smooth = true;
|
|
1393
|
+
if (isArea) base.areaStyle = { color };
|
|
1394
|
+
return base;
|
|
1395
|
+
});
|
|
1396
|
+
return {
|
|
1397
|
+
grid: { top: 24, left: 56, right: 16, bottom: isScatter ? 32 : 40 },
|
|
1398
|
+
tooltip: { trigger: isScatter ? "item" : "axis" },
|
|
1399
|
+
legend: model.series.length > 1 ? { bottom: 0 } : void 0,
|
|
1400
|
+
xAxis: isScatter ? { type: "value" } : { type: "category", data: [...model.categories] },
|
|
1401
|
+
yAxis: { type: "value" },
|
|
1402
|
+
series
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1209
1405
|
function SlideRail({
|
|
1210
1406
|
slides,
|
|
1211
1407
|
selectedId,
|
|
@@ -1354,8 +1550,11 @@ function EditorToolbar({
|
|
|
1354
1550
|
/* @__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" }) }) })
|
|
1355
1551
|
] });
|
|
1356
1552
|
}
|
|
1357
|
-
function ElementInspector({ element, onPatch, onDelete, onLockToggle }) {
|
|
1553
|
+
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
|
|
1358
1554
|
if (!element) {
|
|
1555
|
+
if (slide) {
|
|
1556
|
+
return /* @__PURE__ */ jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
|
|
1557
|
+
}
|
|
1359
1558
|
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: [
|
|
1360
1559
|
/* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
|
|
1361
1560
|
/* @__PURE__ */ jsx(Text, { size: "sm", className: "mt-2 !text-zinc-500", children: "Select an element to edit its properties." })
|
|
@@ -1389,6 +1588,80 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle }) {
|
|
|
1389
1588
|
] }) })
|
|
1390
1589
|
] });
|
|
1391
1590
|
}
|
|
1591
|
+
function SlideSettings({
|
|
1592
|
+
slide,
|
|
1593
|
+
onSetTransition,
|
|
1594
|
+
onSetBackground
|
|
1595
|
+
}) {
|
|
1596
|
+
const transition = slide.transition;
|
|
1597
|
+
const kind = transition?.kind ?? "none";
|
|
1598
|
+
const setTransition = (next) => {
|
|
1599
|
+
const merged = { kind, duration: transition?.duration, direction: transition?.direction, ...next };
|
|
1600
|
+
onSetTransition?.(merged.kind === "none" ? { kind: "none" } : merged);
|
|
1601
|
+
};
|
|
1602
|
+
return /* @__PURE__ */ jsxs("div", { className: "fs-inspector flex h-full w-full flex-col border-l border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-900", children: [
|
|
1603
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between border-b border-zinc-200 px-3 py-2 dark:border-zinc-800", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1604
|
+
/* @__PURE__ */ jsx(Heading, { as: "h3", size: "xs", className: "!font-mono !uppercase !tracking-wider !text-zinc-500", children: "slide" }),
|
|
1605
|
+
/* @__PURE__ */ jsxs(Text, { size: "xs", className: "!font-mono !text-zinc-400", children: [
|
|
1606
|
+
"#",
|
|
1607
|
+
slide.id.slice(-6)
|
|
1608
|
+
] })
|
|
1609
|
+
] }) }),
|
|
1610
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto p-3", children: [
|
|
1611
|
+
/* @__PURE__ */ jsx(Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1612
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Transition" }),
|
|
1613
|
+
/* @__PURE__ */ jsx(
|
|
1614
|
+
Select,
|
|
1615
|
+
{
|
|
1616
|
+
label: "Kind",
|
|
1617
|
+
list: [
|
|
1618
|
+
{ value: "none", label: "None" },
|
|
1619
|
+
{ value: "fade", label: "Fade" },
|
|
1620
|
+
{ value: "slide", label: "Slide" },
|
|
1621
|
+
{ value: "zoom", label: "Zoom" }
|
|
1622
|
+
],
|
|
1623
|
+
value: kind,
|
|
1624
|
+
onValueChange: (v) => setTransition({ kind: v })
|
|
1625
|
+
}
|
|
1626
|
+
),
|
|
1627
|
+
kind === "slide" && /* @__PURE__ */ jsx(
|
|
1628
|
+
Select,
|
|
1629
|
+
{
|
|
1630
|
+
label: "Direction",
|
|
1631
|
+
list: [
|
|
1632
|
+
{ value: "left", label: "From left" },
|
|
1633
|
+
{ value: "right", label: "From right" },
|
|
1634
|
+
{ value: "up", label: "From bottom" },
|
|
1635
|
+
{ value: "down", label: "From top" }
|
|
1636
|
+
],
|
|
1637
|
+
value: transition?.direction ?? "right",
|
|
1638
|
+
onValueChange: (v) => setTransition({ direction: v })
|
|
1639
|
+
}
|
|
1640
|
+
),
|
|
1641
|
+
kind !== "none" && /* @__PURE__ */ jsx(
|
|
1642
|
+
Input,
|
|
1643
|
+
{
|
|
1644
|
+
label: "Duration (ms)",
|
|
1645
|
+
type: "number",
|
|
1646
|
+
value: String(transition?.duration ?? 400),
|
|
1647
|
+
onChange: (e) => setTransition({ duration: parseInt(e.target.value, 10) || 400 })
|
|
1648
|
+
}
|
|
1649
|
+
),
|
|
1650
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", className: "!text-zinc-500", children: "Entrance animation played when this slide appears in the viewer. Falls back to the theme default. Honors prefers-reduced-motion." })
|
|
1651
|
+
] }) }),
|
|
1652
|
+
onSetBackground && /* @__PURE__ */ jsx(Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1653
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Background" }),
|
|
1654
|
+
/* @__PURE__ */ jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsx(
|
|
1655
|
+
ColorPicker,
|
|
1656
|
+
{
|
|
1657
|
+
value: slide.background?.color ?? "#ffffff",
|
|
1658
|
+
onChange: (c) => onSetBackground({ ...slide.background, color: c })
|
|
1659
|
+
}
|
|
1660
|
+
) })
|
|
1661
|
+
] }) })
|
|
1662
|
+
] })
|
|
1663
|
+
] });
|
|
1664
|
+
}
|
|
1392
1665
|
function LayoutSection({ element, onPatch }) {
|
|
1393
1666
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1394
1667
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
@@ -1482,7 +1755,35 @@ function TextStyleControls({ element, onPatch }) {
|
|
|
1482
1755
|
] });
|
|
1483
1756
|
}
|
|
1484
1757
|
function ImageStyleControls({ element, onPatch }) {
|
|
1758
|
+
const fileRef = useRef(null);
|
|
1759
|
+
const crop = element.crop;
|
|
1760
|
+
const onFile = (file) => {
|
|
1761
|
+
if (!file) return;
|
|
1762
|
+
const reader = new FileReader();
|
|
1763
|
+
reader.onload = () => {
|
|
1764
|
+
if (typeof reader.result === "string") onPatch({ src: reader.result });
|
|
1765
|
+
};
|
|
1766
|
+
reader.readAsDataURL(file);
|
|
1767
|
+
};
|
|
1768
|
+
const setCrop = (next) => {
|
|
1769
|
+
const base = crop ?? { x: 0, y: 0, w: 1, h: 1 };
|
|
1770
|
+
onPatch({ crop: { ...base, ...next } });
|
|
1771
|
+
};
|
|
1485
1772
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1773
|
+
/* @__PURE__ */ jsx(
|
|
1774
|
+
"input",
|
|
1775
|
+
{
|
|
1776
|
+
ref: fileRef,
|
|
1777
|
+
type: "file",
|
|
1778
|
+
accept: "image/*",
|
|
1779
|
+
className: "hidden",
|
|
1780
|
+
onChange: (e) => {
|
|
1781
|
+
onFile(e.target.files?.[0]);
|
|
1782
|
+
e.target.value = "";
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
),
|
|
1786
|
+
/* @__PURE__ */ jsx(Action, { size: "sm", variant: "ghost", icon: "upload", onClick: () => fileRef.current?.click(), children: "Upload image" }),
|
|
1486
1787
|
/* @__PURE__ */ jsx(Textarea, { label: "Image URL", value: element.src, onValueChange: (v) => onPatch({ src: v }), rows: 2 }),
|
|
1487
1788
|
/* @__PURE__ */ jsx(Input, { label: "Alt text", value: element.alt ?? "", onChange: (e) => onPatch({ alt: e.target.value }) }),
|
|
1488
1789
|
/* @__PURE__ */ jsx(
|
|
@@ -1498,7 +1799,17 @@ function ImageStyleControls({ element, onPatch }) {
|
|
|
1498
1799
|
value: element.fit ?? "contain",
|
|
1499
1800
|
onValueChange: (v) => onPatch({ fit: v })
|
|
1500
1801
|
}
|
|
1501
|
-
)
|
|
1802
|
+
),
|
|
1803
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
1804
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1805
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Crop" }),
|
|
1806
|
+
crop && /* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", onClick: () => onPatch({ crop: void 0 }), children: "Clear crop" })
|
|
1807
|
+
] }),
|
|
1808
|
+
/* @__PURE__ */ jsx(Slider, { label: "X", value: crop?.x ?? 0, onValueChange: (v) => setCrop({ x: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
1809
|
+
/* @__PURE__ */ jsx(Slider, { label: "Y", value: crop?.y ?? 0, onValueChange: (v) => setCrop({ y: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
1810
|
+
/* @__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 }),
|
|
1811
|
+
/* @__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 }),
|
|
1812
|
+
/* @__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." })
|
|
1502
1813
|
] });
|
|
1503
1814
|
}
|
|
1504
1815
|
function ShapeStyleControls({ element, onPatch }) {
|
|
@@ -1545,54 +1856,254 @@ function CodeStyleControls({ element, onPatch }) {
|
|
|
1545
1856
|
] });
|
|
1546
1857
|
}
|
|
1547
1858
|
function ChartStyleControls({ element, onPatch }) {
|
|
1859
|
+
const model = chartModelFromOption(element.option);
|
|
1860
|
+
const writeModel = (m) => onPatch({ option: chartOptionFromModel(m) });
|
|
1861
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1862
|
+
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." }),
|
|
1863
|
+
/* @__PURE__ */ jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
1864
|
+
/* @__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" }),
|
|
1865
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 pt-0", children: /* @__PURE__ */ jsx(
|
|
1866
|
+
Textarea,
|
|
1867
|
+
{
|
|
1868
|
+
label: "ECharts option (JSON)",
|
|
1869
|
+
value: JSON.stringify(element.option, null, 2),
|
|
1870
|
+
onValueChange: (v) => {
|
|
1871
|
+
try {
|
|
1872
|
+
onPatch({ option: JSON.parse(v) });
|
|
1873
|
+
} catch {
|
|
1874
|
+
}
|
|
1875
|
+
},
|
|
1876
|
+
rows: 10
|
|
1877
|
+
}
|
|
1878
|
+
) })
|
|
1879
|
+
] })
|
|
1880
|
+
] });
|
|
1881
|
+
}
|
|
1882
|
+
var CHART_TYPE_OPTIONS = [
|
|
1883
|
+
{ value: "bar", label: "Bar" },
|
|
1884
|
+
{ value: "line", label: "Line" },
|
|
1885
|
+
{ value: "area", label: "Area" },
|
|
1886
|
+
{ value: "pie", label: "Pie" },
|
|
1887
|
+
{ value: "scatter", label: "Scatter" }
|
|
1888
|
+
];
|
|
1889
|
+
function ChartModelEditor({ model, onChange }) {
|
|
1890
|
+
const setKind = (kind) => {
|
|
1891
|
+
if (kind === model.kind) return;
|
|
1892
|
+
if (kind === "pie") {
|
|
1893
|
+
const first = model.series[0];
|
|
1894
|
+
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 }];
|
|
1895
|
+
onChange({ ...model, kind, slices });
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
if (model.kind === "pie") {
|
|
1899
|
+
const categories = model.slices.length ? model.slices.map((s) => s.name) : ["A", "B", "C"];
|
|
1900
|
+
const values = model.slices.length ? model.slices.map((s) => s.value) : [1, 2, 3];
|
|
1901
|
+
onChange({ ...model, kind, categories, series: [{ name: "Series 1", color: chartColorAt(0), values }] });
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
onChange({ ...model, kind });
|
|
1905
|
+
};
|
|
1548
1906
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1549
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", className: "!text-zinc-500", children: "Chart option is JSON \u2014 paste any ECharts option here." }),
|
|
1550
1907
|
/* @__PURE__ */ jsx(
|
|
1551
|
-
|
|
1908
|
+
Select,
|
|
1552
1909
|
{
|
|
1553
|
-
label: "
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
onPatch({ option: JSON.parse(v) });
|
|
1558
|
-
} catch {
|
|
1559
|
-
}
|
|
1560
|
-
},
|
|
1561
|
-
rows: 10
|
|
1910
|
+
label: "Chart type",
|
|
1911
|
+
list: CHART_TYPE_OPTIONS,
|
|
1912
|
+
value: model.kind,
|
|
1913
|
+
onValueChange: (v) => setKind(v)
|
|
1562
1914
|
}
|
|
1563
|
-
)
|
|
1915
|
+
),
|
|
1916
|
+
model.kind === "pie" ? /* @__PURE__ */ jsx(PieSliceEditor, { model, onChange }) : /* @__PURE__ */ jsx(CartesianChartEditor, { model, onChange })
|
|
1917
|
+
] });
|
|
1918
|
+
}
|
|
1919
|
+
function PieSliceEditor({ model, onChange }) {
|
|
1920
|
+
const slices = model.slices;
|
|
1921
|
+
const update = (i, next) => {
|
|
1922
|
+
const copy = slices.map((s, idx) => idx === i ? { ...s, ...next } : s);
|
|
1923
|
+
onChange({ ...model, slices: copy });
|
|
1924
|
+
};
|
|
1925
|
+
const remove = (i) => onChange({ ...model, slices: slices.filter((_, idx) => idx !== i) });
|
|
1926
|
+
const add = () => onChange({ ...model, slices: [...slices, { name: `Slice ${slices.length + 1}`, value: 0 }] });
|
|
1927
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1928
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Slices" }),
|
|
1929
|
+
slices.map((s, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
1930
|
+
/* @__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 }) }) }),
|
|
1931
|
+
/* @__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 }) }) }),
|
|
1932
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => remove(i), "aria-label": "Remove slice" })
|
|
1933
|
+
] }, i)),
|
|
1934
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: add, children: "Add slice" })
|
|
1935
|
+
] });
|
|
1936
|
+
}
|
|
1937
|
+
function CartesianChartEditor({ model, onChange }) {
|
|
1938
|
+
const { categories, series } = model;
|
|
1939
|
+
const updateCategory = (i, label) => {
|
|
1940
|
+
onChange({ ...model, categories: categories.map((c, idx) => idx === i ? label : c) });
|
|
1941
|
+
};
|
|
1942
|
+
const removeCategory = (i) => {
|
|
1943
|
+
onChange({
|
|
1944
|
+
...model,
|
|
1945
|
+
categories: categories.filter((_, idx) => idx !== i),
|
|
1946
|
+
series: series.map((s) => ({ ...s, values: s.values.filter((_, idx) => idx !== i) }))
|
|
1947
|
+
});
|
|
1948
|
+
};
|
|
1949
|
+
const addCategory = () => {
|
|
1950
|
+
onChange({
|
|
1951
|
+
...model,
|
|
1952
|
+
categories: [...categories, `Cat ${categories.length + 1}`],
|
|
1953
|
+
series: series.map((s) => ({ ...s, values: [...s.values, 0] }))
|
|
1954
|
+
});
|
|
1955
|
+
};
|
|
1956
|
+
const updateSeries = (si, next) => {
|
|
1957
|
+
onChange({ ...model, series: series.map((s, idx) => idx === si ? { ...s, ...next } : s) });
|
|
1958
|
+
};
|
|
1959
|
+
const updateValue = (si, ci, value) => {
|
|
1960
|
+
onChange({
|
|
1961
|
+
...model,
|
|
1962
|
+
series: series.map(
|
|
1963
|
+
(s, idx) => idx === si ? { ...s, values: s.values.map((v, vi) => vi === ci ? value : v) } : s
|
|
1964
|
+
)
|
|
1965
|
+
});
|
|
1966
|
+
};
|
|
1967
|
+
const removeSeries = (si) => onChange({ ...model, series: series.filter((_, idx) => idx !== si) });
|
|
1968
|
+
const addSeries = () => onChange({
|
|
1969
|
+
...model,
|
|
1970
|
+
series: [
|
|
1971
|
+
...series,
|
|
1972
|
+
{ name: `Series ${series.length + 1}`, color: chartColorAt(series.length), values: categories.map(() => 0) }
|
|
1973
|
+
]
|
|
1974
|
+
});
|
|
1975
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1976
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1977
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Categories" }),
|
|
1978
|
+
categories.map((c, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1979
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Input, { value: c, onChange: (e) => updateCategory(i, e.target.value) }) }),
|
|
1980
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeCategory(i), "aria-label": "Remove category" })
|
|
1981
|
+
] }, i)),
|
|
1982
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addCategory, children: "Add category" })
|
|
1983
|
+
] }),
|
|
1984
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
1985
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1986
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Series" }),
|
|
1987
|
+
series.map((s, si) => /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-zinc-200 p-2 dark:border-zinc-800", children: [
|
|
1988
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
1989
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Input, { label: "Name", value: s.name, onChange: (e) => updateSeries(si, { name: e.target.value }) }) }),
|
|
1990
|
+
/* @__PURE__ */ jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsx(ColorPicker, { value: s.color ?? chartColorAt(si), onChange: (c) => updateSeries(si, { color: c }) }) }),
|
|
1991
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeSeries(si), "aria-label": "Remove series" })
|
|
1992
|
+
] }),
|
|
1993
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: categories.map((c, ci) => /* @__PURE__ */ jsx(
|
|
1994
|
+
Input,
|
|
1995
|
+
{
|
|
1996
|
+
label: c,
|
|
1997
|
+
type: "number",
|
|
1998
|
+
value: String(s.values[ci] ?? 0),
|
|
1999
|
+
onChange: (e) => updateValue(si, ci, parseFloat(e.target.value) || 0)
|
|
2000
|
+
},
|
|
2001
|
+
ci
|
|
2002
|
+
)) })
|
|
2003
|
+
] }, si)),
|
|
2004
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addSeries, children: "Add series" })
|
|
2005
|
+
] })
|
|
1564
2006
|
] });
|
|
1565
2007
|
}
|
|
1566
2008
|
function TableStyleControls({ element, onPatch }) {
|
|
2009
|
+
const columns = element.columns;
|
|
2010
|
+
const rows = element.rows;
|
|
2011
|
+
const nextColKey = () => {
|
|
2012
|
+
const existing = new Set(columns.map((c) => c.key));
|
|
2013
|
+
let n = columns.length + 1;
|
|
2014
|
+
while (existing.has(`col${n}`)) n++;
|
|
2015
|
+
return `col${n}`;
|
|
2016
|
+
};
|
|
2017
|
+
const setColumnLabel = (i, label) => {
|
|
2018
|
+
onPatch({ columns: columns.map((c, idx) => idx === i ? { ...c, label } : c) });
|
|
2019
|
+
};
|
|
2020
|
+
const removeColumn = (i) => {
|
|
2021
|
+
const key = columns[i]?.key;
|
|
2022
|
+
const nextCols = columns.filter((_, idx) => idx !== i);
|
|
2023
|
+
const nextRows = key ? rows.map((r) => {
|
|
2024
|
+
const { [key]: _drop, ...rest } = r;
|
|
2025
|
+
return rest;
|
|
2026
|
+
}) : rows;
|
|
2027
|
+
onPatch({ columns: nextCols, rows: nextRows });
|
|
2028
|
+
};
|
|
2029
|
+
const addColumn = () => {
|
|
2030
|
+
const key = nextColKey();
|
|
2031
|
+
onPatch({
|
|
2032
|
+
columns: [...columns, { key, label: `Column ${columns.length + 1}` }],
|
|
2033
|
+
rows: rows.map((r) => ({ ...r, [key]: "" }))
|
|
2034
|
+
});
|
|
2035
|
+
};
|
|
2036
|
+
const setCell = (rowIdx, key, value) => {
|
|
2037
|
+
onPatch({ rows: rows.map((r, idx) => idx === rowIdx ? { ...r, [key]: value } : r) });
|
|
2038
|
+
};
|
|
2039
|
+
const removeRow = (rowIdx) => onPatch({ rows: rows.filter((_, idx) => idx !== rowIdx) });
|
|
2040
|
+
const addRow = () => {
|
|
2041
|
+
const blank = {};
|
|
2042
|
+
for (const c of columns) blank[c.key] = "";
|
|
2043
|
+
onPatch({ rows: [...rows, blank] });
|
|
2044
|
+
};
|
|
1567
2045
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1568
|
-
/* @__PURE__ */
|
|
1569
|
-
|
|
1570
|
-
{
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
2046
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2047
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Columns" }),
|
|
2048
|
+
columns.map((c, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2049
|
+
/* @__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` }) }),
|
|
2050
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", className: "!font-mono !text-zinc-400", children: c.key }),
|
|
2051
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeColumn(i), "aria-label": "Remove column" })
|
|
2052
|
+
] }, c.key)),
|
|
2053
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addColumn, children: "Add column" })
|
|
2054
|
+
] }),
|
|
2055
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
2056
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2057
|
+
/* @__PURE__ */ jsx(Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Rows" }),
|
|
2058
|
+
columns.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "xs", className: "!text-zinc-500", children: "Add a column to start adding rows." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2059
|
+
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: [
|
|
2060
|
+
/* @__PURE__ */ jsx("div", { className: "grid flex-1 grid-cols-1 gap-1", children: columns.map((c) => /* @__PURE__ */ jsx(
|
|
2061
|
+
Input,
|
|
2062
|
+
{
|
|
2063
|
+
label: c.label,
|
|
2064
|
+
value: r[c.key] == null ? "" : String(r[c.key]),
|
|
2065
|
+
onChange: (e) => setCell(rowIdx, c.key, e.target.value)
|
|
2066
|
+
},
|
|
2067
|
+
c.key
|
|
2068
|
+
)) }),
|
|
2069
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeRow(rowIdx), "aria-label": "Remove row" })
|
|
2070
|
+
] }, rowIdx)),
|
|
2071
|
+
/* @__PURE__ */ jsx(Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addRow, children: "Add row" })
|
|
2072
|
+
] })
|
|
2073
|
+
] }),
|
|
2074
|
+
/* @__PURE__ */ jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
2075
|
+
/* @__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" }),
|
|
2076
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3 p-2 pt-0", children: [
|
|
2077
|
+
/* @__PURE__ */ jsx(
|
|
2078
|
+
Textarea,
|
|
2079
|
+
{
|
|
2080
|
+
label: "Columns (JSON)",
|
|
2081
|
+
value: JSON.stringify(columns, null, 2),
|
|
2082
|
+
onValueChange: (v) => {
|
|
2083
|
+
try {
|
|
2084
|
+
onPatch({ columns: JSON.parse(v) });
|
|
2085
|
+
} catch {
|
|
2086
|
+
}
|
|
2087
|
+
},
|
|
2088
|
+
rows: 5
|
|
1577
2089
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
2090
|
+
),
|
|
2091
|
+
/* @__PURE__ */ jsx(
|
|
2092
|
+
Textarea,
|
|
2093
|
+
{
|
|
2094
|
+
label: "Rows (JSON)",
|
|
2095
|
+
value: JSON.stringify(rows, null, 2),
|
|
2096
|
+
onValueChange: (v) => {
|
|
2097
|
+
try {
|
|
2098
|
+
onPatch({ rows: JSON.parse(v) });
|
|
2099
|
+
} catch {
|
|
2100
|
+
}
|
|
2101
|
+
},
|
|
2102
|
+
rows: 8
|
|
1591
2103
|
}
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
)
|
|
2104
|
+
)
|
|
2105
|
+
] })
|
|
2106
|
+
] })
|
|
1596
2107
|
] });
|
|
1597
2108
|
}
|
|
1598
2109
|
function EmbedStyleControls({ element, onPatch }) {
|
|
@@ -1838,13 +2349,16 @@ function DeckEditor({
|
|
|
1838
2349
|
ElementInspector,
|
|
1839
2350
|
{
|
|
1840
2351
|
element: selectedElement,
|
|
2352
|
+
slide: slide ?? null,
|
|
1841
2353
|
onPatch: (patch) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, patch),
|
|
1842
2354
|
onDelete: () => {
|
|
1843
2355
|
if (!slide || !elementIdSelected) return;
|
|
1844
2356
|
ops.removeElement(slide.id, elementIdSelected);
|
|
1845
2357
|
setElementIdSelected(null);
|
|
1846
2358
|
},
|
|
1847
|
-
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked })
|
|
2359
|
+
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
|
|
2360
|
+
onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
|
|
2361
|
+
onSetBackground: (background) => slide && ops.setBackground(slide.id, background)
|
|
1848
2362
|
}
|
|
1849
2363
|
) })
|
|
1850
2364
|
] }),
|