@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.cjs
CHANGED
|
@@ -161,6 +161,22 @@ function weight(w) {
|
|
|
161
161
|
return void 0;
|
|
162
162
|
}
|
|
163
163
|
function ImageElementRenderer({ element }) {
|
|
164
|
+
const crop = element.crop;
|
|
165
|
+
const fit = element.fit ?? "contain";
|
|
166
|
+
if (crop && crop.w > 0 && crop.h > 0) {
|
|
167
|
+
const inner = {
|
|
168
|
+
position: "absolute",
|
|
169
|
+
left: 0,
|
|
170
|
+
top: 0,
|
|
171
|
+
width: `${1 / crop.w * 100}%`,
|
|
172
|
+
height: `${1 / crop.h * 100}%`,
|
|
173
|
+
transform: `translate(${-crop.x / crop.w * 100}%, ${-crop.y / crop.h * 100}%)`,
|
|
174
|
+
transformOrigin: "top left",
|
|
175
|
+
objectFit: fit,
|
|
176
|
+
display: "block"
|
|
177
|
+
};
|
|
178
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "relative", width: "100%", height: "100%", overflow: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: element.src, alt: element.alt ?? "", style: inner, draggable: false }) });
|
|
179
|
+
}
|
|
164
180
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
165
181
|
"img",
|
|
166
182
|
{
|
|
@@ -169,7 +185,7 @@ function ImageElementRenderer({ element }) {
|
|
|
169
185
|
style: {
|
|
170
186
|
width: "100%",
|
|
171
187
|
height: "100%",
|
|
172
|
-
objectFit:
|
|
188
|
+
objectFit: fit,
|
|
173
189
|
display: "block"
|
|
174
190
|
},
|
|
175
191
|
draggable: false
|
|
@@ -653,6 +669,11 @@ function SlideViewer({
|
|
|
653
669
|
);
|
|
654
670
|
const [blanked, setBlanked] = react.useState(false);
|
|
655
671
|
const containerRef = react.useRef(null);
|
|
672
|
+
const prevIndexRef = react.useRef(index);
|
|
673
|
+
const forward = index >= prevIndexRef.current;
|
|
674
|
+
react.useEffect(() => {
|
|
675
|
+
prevIndexRef.current = index;
|
|
676
|
+
}, [index]);
|
|
656
677
|
useSlideKeyboard({
|
|
657
678
|
total: deck.slides.length,
|
|
658
679
|
index,
|
|
@@ -676,6 +697,8 @@ function SlideViewer({
|
|
|
676
697
|
const slide = deck.slides[index];
|
|
677
698
|
const theme = resolveTheme(deck.theme);
|
|
678
699
|
const aspectRatio = theme.aspectRatio ?? 16 / 9;
|
|
700
|
+
const transition = slide?.transition ?? theme.defaultTransition;
|
|
701
|
+
const enterStyle = transitionEnterStyle(transition, forward);
|
|
679
702
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
680
703
|
"div",
|
|
681
704
|
{
|
|
@@ -694,6 +717,7 @@ function SlideViewer({
|
|
|
694
717
|
tabIndex: 0,
|
|
695
718
|
"data-fancy-slides-viewer": deck.id,
|
|
696
719
|
children: [
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsx("style", { children: TRANSITION_KEYFRAMES }),
|
|
697
721
|
!blanked && slide && /* @__PURE__ */ jsxRuntime.jsx(
|
|
698
722
|
"div",
|
|
699
723
|
{
|
|
@@ -705,7 +729,7 @@ function SlideViewer({
|
|
|
705
729
|
["--fs-ratio"]: aspectRatio.toString(),
|
|
706
730
|
boxShadow: "0 8px 30px rgba(0,0,0,0.35)"
|
|
707
731
|
},
|
|
708
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, renderElement })
|
|
732
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fs-slide-enter", style: enterStyle, children: /* @__PURE__ */ jsxRuntime.jsx(Slide, { slide, theme, renderElement }) }, index)
|
|
709
733
|
}
|
|
710
734
|
),
|
|
711
735
|
!hideChrome && !blanked && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -735,6 +759,68 @@ function SlideViewer({
|
|
|
735
759
|
}
|
|
736
760
|
);
|
|
737
761
|
}
|
|
762
|
+
var DEFAULT_DURATION = 400;
|
|
763
|
+
var EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
764
|
+
function transitionEnterStyle(transition, forward) {
|
|
765
|
+
const kind = transition?.kind ?? "none";
|
|
766
|
+
if (kind === "none") return { width: "100%", height: "100%" };
|
|
767
|
+
const duration = transition?.duration ?? DEFAULT_DURATION;
|
|
768
|
+
let name;
|
|
769
|
+
switch (kind) {
|
|
770
|
+
case "fade":
|
|
771
|
+
name = "fs-fade-in";
|
|
772
|
+
break;
|
|
773
|
+
case "zoom":
|
|
774
|
+
name = "fs-zoom-in";
|
|
775
|
+
break;
|
|
776
|
+
case "slide": {
|
|
777
|
+
const dir = transition?.direction ?? (forward ? "right" : "left");
|
|
778
|
+
name = `fs-slide-in-${dir}`;
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
default:
|
|
782
|
+
return { width: "100%", height: "100%" };
|
|
783
|
+
}
|
|
784
|
+
return {
|
|
785
|
+
width: "100%",
|
|
786
|
+
height: "100%",
|
|
787
|
+
animationName: name,
|
|
788
|
+
animationDuration: `${duration}ms`,
|
|
789
|
+
animationTimingFunction: EASE,
|
|
790
|
+
animationFillMode: "both"
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
var TRANSITION_KEYFRAMES = `
|
|
794
|
+
@media (prefers-reduced-motion: reduce) {
|
|
795
|
+
.fs-slide-enter { animation: none !important; }
|
|
796
|
+
}
|
|
797
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
798
|
+
@keyframes fs-fade-in {
|
|
799
|
+
from { opacity: 0; }
|
|
800
|
+
to { opacity: 1; }
|
|
801
|
+
}
|
|
802
|
+
@keyframes fs-zoom-in {
|
|
803
|
+
from { opacity: 0; transform: scale(0.92); }
|
|
804
|
+
to { opacity: 1; transform: scale(1); }
|
|
805
|
+
}
|
|
806
|
+
@keyframes fs-slide-in-right {
|
|
807
|
+
from { opacity: 0; transform: translateX(8%); }
|
|
808
|
+
to { opacity: 1; transform: translateX(0); }
|
|
809
|
+
}
|
|
810
|
+
@keyframes fs-slide-in-left {
|
|
811
|
+
from { opacity: 0; transform: translateX(-8%); }
|
|
812
|
+
to { opacity: 1; transform: translateX(0); }
|
|
813
|
+
}
|
|
814
|
+
@keyframes fs-slide-in-up {
|
|
815
|
+
from { opacity: 0; transform: translateY(8%); }
|
|
816
|
+
to { opacity: 1; transform: translateY(0); }
|
|
817
|
+
}
|
|
818
|
+
@keyframes fs-slide-in-down {
|
|
819
|
+
from { opacity: 0; transform: translateY(-8%); }
|
|
820
|
+
to { opacity: 1; transform: translateY(0); }
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
`;
|
|
738
824
|
function PresenterView({
|
|
739
825
|
deck,
|
|
740
826
|
index: controlledIndex,
|
|
@@ -1088,6 +1174,7 @@ function useDeckState({ value, onChange, onOp }) {
|
|
|
1088
1174
|
setLayout: (id, layout) => apply({ kind: "slide_set_layout", id, layout }),
|
|
1089
1175
|
setNotes: (id, notes) => apply({ kind: "slide_set_notes", id, notes }),
|
|
1090
1176
|
setBackground: (id, background) => apply({ kind: "slide_set_background", id, background }),
|
|
1177
|
+
setTransition: (id, transition) => apply({ kind: "slide_set_transition", id, transition }),
|
|
1091
1178
|
addElement: (slideId2, element) => {
|
|
1092
1179
|
const id = element.id ?? elementId();
|
|
1093
1180
|
apply({ kind: "element_add", slideId: slideId2, element: { ...element, id } });
|
|
@@ -1129,6 +1216,8 @@ function reduce(deck, op) {
|
|
|
1129
1216
|
return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, notes: op.notes } : s) };
|
|
1130
1217
|
case "slide_set_background":
|
|
1131
1218
|
return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, background: op.background } : s) };
|
|
1219
|
+
case "slide_set_transition":
|
|
1220
|
+
return { ...deck, slides: deck.slides.map((s) => s.id === op.id ? { ...s, transition: op.transition } : s) };
|
|
1132
1221
|
case "element_add":
|
|
1133
1222
|
return {
|
|
1134
1223
|
...deck,
|
|
@@ -1248,6 +1337,113 @@ function chartStarterOption(kind) {
|
|
|
1248
1337
|
};
|
|
1249
1338
|
}
|
|
1250
1339
|
}
|
|
1340
|
+
var CHART_PALETTE = [
|
|
1341
|
+
"#8b5cf6",
|
|
1342
|
+
"#3b82f6",
|
|
1343
|
+
"#10b981",
|
|
1344
|
+
"#f59e0b",
|
|
1345
|
+
"#ef4444",
|
|
1346
|
+
"#ec4899",
|
|
1347
|
+
"#14b8a6",
|
|
1348
|
+
"#6366f1"
|
|
1349
|
+
];
|
|
1350
|
+
function chartColorAt(index) {
|
|
1351
|
+
return CHART_PALETTE[index % CHART_PALETTE.length];
|
|
1352
|
+
}
|
|
1353
|
+
function isPlainObject(v) {
|
|
1354
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1355
|
+
}
|
|
1356
|
+
function toNumber(v) {
|
|
1357
|
+
const n = typeof v === "number" ? v : parseFloat(String(v));
|
|
1358
|
+
return Number.isFinite(n) ? n : 0;
|
|
1359
|
+
}
|
|
1360
|
+
function chartModelFromOption(option) {
|
|
1361
|
+
if (!isPlainObject(option)) return null;
|
|
1362
|
+
const seriesRaw = option.series;
|
|
1363
|
+
if (!Array.isArray(seriesRaw) || seriesRaw.length === 0) return null;
|
|
1364
|
+
if (!seriesRaw.every(isPlainObject)) return null;
|
|
1365
|
+
const types = seriesRaw.map((s) => String(s.type ?? ""));
|
|
1366
|
+
if (types[0] === "pie") {
|
|
1367
|
+
if (seriesRaw.length !== 1) return null;
|
|
1368
|
+
const data = seriesRaw[0].data;
|
|
1369
|
+
if (!Array.isArray(data)) return null;
|
|
1370
|
+
const slices = [];
|
|
1371
|
+
for (const d of data) {
|
|
1372
|
+
if (!isPlainObject(d)) return null;
|
|
1373
|
+
slices.push({ name: String(d.name ?? ""), value: toNumber(d.value) });
|
|
1374
|
+
}
|
|
1375
|
+
return { kind: "pie", categories: [], series: [], slices };
|
|
1376
|
+
}
|
|
1377
|
+
const cartesian = /* @__PURE__ */ new Set(["bar", "line", "scatter"]);
|
|
1378
|
+
if (!types.every((t) => cartesian.has(t))) return null;
|
|
1379
|
+
if (new Set(types).size !== 1) return null;
|
|
1380
|
+
const baseType = types[0];
|
|
1381
|
+
const isArea = baseType === "line" && seriesRaw.every((s) => isPlainObject(s.areaStyle) || s.areaStyle != null);
|
|
1382
|
+
const kind = baseType === "line" ? isArea ? "area" : "line" : baseType;
|
|
1383
|
+
const xAxis = option.xAxis;
|
|
1384
|
+
const axisData = isPlainObject(xAxis) ? xAxis.data : void 0;
|
|
1385
|
+
const categories = Array.isArray(axisData) ? axisData.map((c) => String(c)) : [];
|
|
1386
|
+
const firstData = seriesRaw[0].data;
|
|
1387
|
+
const valueCount = Array.isArray(firstData) ? firstData.length : 0;
|
|
1388
|
+
const cats = categories.length > 0 ? categories : Array.from({ length: valueCount }, (_, i) => String(i + 1));
|
|
1389
|
+
const series = [];
|
|
1390
|
+
for (const s of seriesRaw) {
|
|
1391
|
+
const data = s.data;
|
|
1392
|
+
if (!Array.isArray(data)) return null;
|
|
1393
|
+
const values = data.map((d) => {
|
|
1394
|
+
if (Array.isArray(d)) return toNumber(d[1]);
|
|
1395
|
+
if (isPlainObject(d)) return toNumber(d.value);
|
|
1396
|
+
return toNumber(d);
|
|
1397
|
+
});
|
|
1398
|
+
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 });
|
|
1399
|
+
}
|
|
1400
|
+
return { kind, categories: cats, series, slices: [] };
|
|
1401
|
+
}
|
|
1402
|
+
function chartOptionFromModel(model) {
|
|
1403
|
+
if (model.kind === "pie") {
|
|
1404
|
+
return {
|
|
1405
|
+
tooltip: { trigger: "item" },
|
|
1406
|
+
legend: { bottom: 0 },
|
|
1407
|
+
color: model.slices.map((_, i) => chartColorAt(i)),
|
|
1408
|
+
series: [
|
|
1409
|
+
{
|
|
1410
|
+
type: "pie",
|
|
1411
|
+
radius: ["40%", "70%"],
|
|
1412
|
+
name: "Segment",
|
|
1413
|
+
data: model.slices.map((s) => ({ name: s.name, value: s.value }))
|
|
1414
|
+
}
|
|
1415
|
+
]
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
const isScatter = model.kind === "scatter";
|
|
1419
|
+
const isArea = model.kind === "area";
|
|
1420
|
+
const seriesType = model.kind === "bar" ? "bar" : model.kind === "scatter" ? "scatter" : "line";
|
|
1421
|
+
const series = model.series.map((s, i) => {
|
|
1422
|
+
const color = s.color ?? chartColorAt(i);
|
|
1423
|
+
const base = {
|
|
1424
|
+
type: seriesType,
|
|
1425
|
+
name: s.name,
|
|
1426
|
+
itemStyle: { color }
|
|
1427
|
+
};
|
|
1428
|
+
if (isScatter) {
|
|
1429
|
+
base.symbolSize = 12;
|
|
1430
|
+
base.data = s.values.map((v, idx) => [idx, v]);
|
|
1431
|
+
} else {
|
|
1432
|
+
base.data = s.values;
|
|
1433
|
+
}
|
|
1434
|
+
if (seriesType === "line") base.smooth = true;
|
|
1435
|
+
if (isArea) base.areaStyle = { color };
|
|
1436
|
+
return base;
|
|
1437
|
+
});
|
|
1438
|
+
return {
|
|
1439
|
+
grid: { top: 24, left: 56, right: 16, bottom: isScatter ? 32 : 40 },
|
|
1440
|
+
tooltip: { trigger: isScatter ? "item" : "axis" },
|
|
1441
|
+
legend: model.series.length > 1 ? { bottom: 0 } : void 0,
|
|
1442
|
+
xAxis: isScatter ? { type: "value" } : { type: "category", data: [...model.categories] },
|
|
1443
|
+
yAxis: { type: "value" },
|
|
1444
|
+
series
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1251
1447
|
function SlideRail({
|
|
1252
1448
|
slides,
|
|
1253
1449
|
selectedId,
|
|
@@ -1396,8 +1592,11 @@ function EditorToolbar({
|
|
|
1396
1592
|
/* @__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" }) }) })
|
|
1397
1593
|
] });
|
|
1398
1594
|
}
|
|
1399
|
-
function ElementInspector({ element, onPatch, onDelete, onLockToggle }) {
|
|
1595
|
+
function ElementInspector({ element, onPatch, onDelete, onLockToggle, slide, onSetTransition, onSetBackground }) {
|
|
1400
1596
|
if (!element) {
|
|
1597
|
+
if (slide) {
|
|
1598
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SlideSettings, { slide, onSetTransition, onSetBackground });
|
|
1599
|
+
}
|
|
1401
1600
|
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: [
|
|
1402
1601
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h3", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Inspector" }),
|
|
1403
1602
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "mt-2 !text-zinc-500", children: "Select an element to edit its properties." })
|
|
@@ -1431,6 +1630,80 @@ function ElementInspector({ element, onPatch, onDelete, onLockToggle }) {
|
|
|
1431
1630
|
] }) })
|
|
1432
1631
|
] });
|
|
1433
1632
|
}
|
|
1633
|
+
function SlideSettings({
|
|
1634
|
+
slide,
|
|
1635
|
+
onSetTransition,
|
|
1636
|
+
onSetBackground
|
|
1637
|
+
}) {
|
|
1638
|
+
const transition = slide.transition;
|
|
1639
|
+
const kind = transition?.kind ?? "none";
|
|
1640
|
+
const setTransition = (next) => {
|
|
1641
|
+
const merged = { kind, duration: transition?.duration, direction: transition?.direction, ...next };
|
|
1642
|
+
onSetTransition?.(merged.kind === "none" ? { kind: "none" } : merged);
|
|
1643
|
+
};
|
|
1644
|
+
return /* @__PURE__ */ jsxRuntime.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: [
|
|
1645
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between border-b border-zinc-200 px-3 py-2 dark:border-zinc-800", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1646
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h3", size: "xs", className: "!font-mono !uppercase !tracking-wider !text-zinc-500", children: "slide" }),
|
|
1647
|
+
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.Text, { size: "xs", className: "!font-mono !text-zinc-400", children: [
|
|
1648
|
+
"#",
|
|
1649
|
+
slide.id.slice(-6)
|
|
1650
|
+
] })
|
|
1651
|
+
] }) }),
|
|
1652
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto p-3", children: [
|
|
1653
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "!bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1654
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Transition" }),
|
|
1655
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1656
|
+
reactFancy.Select,
|
|
1657
|
+
{
|
|
1658
|
+
label: "Kind",
|
|
1659
|
+
list: [
|
|
1660
|
+
{ value: "none", label: "None" },
|
|
1661
|
+
{ value: "fade", label: "Fade" },
|
|
1662
|
+
{ value: "slide", label: "Slide" },
|
|
1663
|
+
{ value: "zoom", label: "Zoom" }
|
|
1664
|
+
],
|
|
1665
|
+
value: kind,
|
|
1666
|
+
onValueChange: (v) => setTransition({ kind: v })
|
|
1667
|
+
}
|
|
1668
|
+
),
|
|
1669
|
+
kind === "slide" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1670
|
+
reactFancy.Select,
|
|
1671
|
+
{
|
|
1672
|
+
label: "Direction",
|
|
1673
|
+
list: [
|
|
1674
|
+
{ value: "left", label: "From left" },
|
|
1675
|
+
{ value: "right", label: "From right" },
|
|
1676
|
+
{ value: "up", label: "From bottom" },
|
|
1677
|
+
{ value: "down", label: "From top" }
|
|
1678
|
+
],
|
|
1679
|
+
value: transition?.direction ?? "right",
|
|
1680
|
+
onValueChange: (v) => setTransition({ direction: v })
|
|
1681
|
+
}
|
|
1682
|
+
),
|
|
1683
|
+
kind !== "none" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1684
|
+
reactFancy.Input,
|
|
1685
|
+
{
|
|
1686
|
+
label: "Duration (ms)",
|
|
1687
|
+
type: "number",
|
|
1688
|
+
value: String(transition?.duration ?? 400),
|
|
1689
|
+
onChange: (e) => setTransition({ duration: parseInt(e.target.value, 10) || 400 })
|
|
1690
|
+
}
|
|
1691
|
+
),
|
|
1692
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.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." })
|
|
1693
|
+
] }) }),
|
|
1694
|
+
onSetBackground && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Card, { padding: "md", className: "mt-3 !bg-white dark:!bg-zinc-950", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1695
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Background" }),
|
|
1696
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1697
|
+
reactFancy.ColorPicker,
|
|
1698
|
+
{
|
|
1699
|
+
value: slide.background?.color ?? "#ffffff",
|
|
1700
|
+
onChange: (c) => onSetBackground({ ...slide.background, color: c })
|
|
1701
|
+
}
|
|
1702
|
+
) })
|
|
1703
|
+
] }) })
|
|
1704
|
+
] })
|
|
1705
|
+
] });
|
|
1706
|
+
}
|
|
1434
1707
|
function LayoutSection({ element, onPatch }) {
|
|
1435
1708
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1436
1709
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
@@ -1524,7 +1797,35 @@ function TextStyleControls({ element, onPatch }) {
|
|
|
1524
1797
|
] });
|
|
1525
1798
|
}
|
|
1526
1799
|
function ImageStyleControls({ element, onPatch }) {
|
|
1800
|
+
const fileRef = react.useRef(null);
|
|
1801
|
+
const crop = element.crop;
|
|
1802
|
+
const onFile = (file) => {
|
|
1803
|
+
if (!file) return;
|
|
1804
|
+
const reader = new FileReader();
|
|
1805
|
+
reader.onload = () => {
|
|
1806
|
+
if (typeof reader.result === "string") onPatch({ src: reader.result });
|
|
1807
|
+
};
|
|
1808
|
+
reader.readAsDataURL(file);
|
|
1809
|
+
};
|
|
1810
|
+
const setCrop = (next) => {
|
|
1811
|
+
const base = crop ?? { x: 0, y: 0, w: 1, h: 1 };
|
|
1812
|
+
onPatch({ crop: { ...base, ...next } });
|
|
1813
|
+
};
|
|
1527
1814
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1815
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1816
|
+
"input",
|
|
1817
|
+
{
|
|
1818
|
+
ref: fileRef,
|
|
1819
|
+
type: "file",
|
|
1820
|
+
accept: "image/*",
|
|
1821
|
+
className: "hidden",
|
|
1822
|
+
onChange: (e) => {
|
|
1823
|
+
onFile(e.target.files?.[0]);
|
|
1824
|
+
e.target.value = "";
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
),
|
|
1828
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "sm", variant: "ghost", icon: "upload", onClick: () => fileRef.current?.click(), children: "Upload image" }),
|
|
1528
1829
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Textarea, { label: "Image URL", value: element.src, onValueChange: (v) => onPatch({ src: v }), rows: 2 }),
|
|
1529
1830
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Alt text", value: element.alt ?? "", onChange: (e) => onPatch({ alt: e.target.value }) }),
|
|
1530
1831
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1540,7 +1841,17 @@ function ImageStyleControls({ element, onPatch }) {
|
|
|
1540
1841
|
value: element.fit ?? "contain",
|
|
1541
1842
|
onValueChange: (v) => onPatch({ fit: v })
|
|
1542
1843
|
}
|
|
1543
|
-
)
|
|
1844
|
+
),
|
|
1845
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
|
|
1846
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1847
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Crop" }),
|
|
1848
|
+
crop && /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", onClick: () => onPatch({ crop: void 0 }), children: "Clear crop" })
|
|
1849
|
+
] }),
|
|
1850
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "X", value: crop?.x ?? 0, onValueChange: (v) => setCrop({ x: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
1851
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Y", value: crop?.y ?? 0, onValueChange: (v) => setCrop({ y: Number(v) }), min: 0, max: 1, step: 0.01, showValue: true }),
|
|
1852
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Width", value: crop?.w ?? 1, onValueChange: (v) => setCrop({ w: Number(v) }), min: 0.01, max: 1, step: 0.01, showValue: true }),
|
|
1853
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Slider, { label: "Height", value: crop?.h ?? 1, onValueChange: (v) => setCrop({ h: Number(v) }), min: 0.01, max: 1, step: 0.01, showValue: true }),
|
|
1854
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: "Crop is a window into the source image (0..1). Width/height shrink the visible region; X/Y pan it." })
|
|
1544
1855
|
] });
|
|
1545
1856
|
}
|
|
1546
1857
|
function ShapeStyleControls({ element, onPatch }) {
|
|
@@ -1587,54 +1898,254 @@ function CodeStyleControls({ element, onPatch }) {
|
|
|
1587
1898
|
] });
|
|
1588
1899
|
}
|
|
1589
1900
|
function ChartStyleControls({ element, onPatch }) {
|
|
1901
|
+
const model = chartModelFromOption(element.option);
|
|
1902
|
+
const writeModel = (m) => onPatch({ option: chartOptionFromModel(m) });
|
|
1903
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1904
|
+
model ? /* @__PURE__ */ jsxRuntime.jsx(ChartModelEditor, { model, onChange: writeModel }) : /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "rounded-md bg-amber-50 p-2 !text-amber-700 dark:bg-amber-950/40 dark:!text-amber-400", children: "This chart's option is too custom for the visual editor. Edit it as JSON below." }),
|
|
1905
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
1906
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer select-none px-2 py-1.5 text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Advanced \u2014 edit option JSON" }),
|
|
1907
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 pt-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1908
|
+
reactFancy.Textarea,
|
|
1909
|
+
{
|
|
1910
|
+
label: "ECharts option (JSON)",
|
|
1911
|
+
value: JSON.stringify(element.option, null, 2),
|
|
1912
|
+
onValueChange: (v) => {
|
|
1913
|
+
try {
|
|
1914
|
+
onPatch({ option: JSON.parse(v) });
|
|
1915
|
+
} catch {
|
|
1916
|
+
}
|
|
1917
|
+
},
|
|
1918
|
+
rows: 10
|
|
1919
|
+
}
|
|
1920
|
+
) })
|
|
1921
|
+
] })
|
|
1922
|
+
] });
|
|
1923
|
+
}
|
|
1924
|
+
var CHART_TYPE_OPTIONS = [
|
|
1925
|
+
{ value: "bar", label: "Bar" },
|
|
1926
|
+
{ value: "line", label: "Line" },
|
|
1927
|
+
{ value: "area", label: "Area" },
|
|
1928
|
+
{ value: "pie", label: "Pie" },
|
|
1929
|
+
{ value: "scatter", label: "Scatter" }
|
|
1930
|
+
];
|
|
1931
|
+
function ChartModelEditor({ model, onChange }) {
|
|
1932
|
+
const setKind = (kind) => {
|
|
1933
|
+
if (kind === model.kind) return;
|
|
1934
|
+
if (kind === "pie") {
|
|
1935
|
+
const first = model.series[0];
|
|
1936
|
+
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 }];
|
|
1937
|
+
onChange({ ...model, kind, slices });
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
if (model.kind === "pie") {
|
|
1941
|
+
const categories = model.slices.length ? model.slices.map((s) => s.name) : ["A", "B", "C"];
|
|
1942
|
+
const values = model.slices.length ? model.slices.map((s) => s.value) : [1, 2, 3];
|
|
1943
|
+
onChange({ ...model, kind, categories, series: [{ name: "Series 1", color: chartColorAt(0), values }] });
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
onChange({ ...model, kind });
|
|
1947
|
+
};
|
|
1590
1948
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1591
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "!text-zinc-500", children: "Chart option is JSON \u2014 paste any ECharts option here." }),
|
|
1592
1949
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1593
|
-
reactFancy.
|
|
1950
|
+
reactFancy.Select,
|
|
1594
1951
|
{
|
|
1595
|
-
label: "
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
onPatch({ option: JSON.parse(v) });
|
|
1600
|
-
} catch {
|
|
1601
|
-
}
|
|
1602
|
-
},
|
|
1603
|
-
rows: 10
|
|
1952
|
+
label: "Chart type",
|
|
1953
|
+
list: CHART_TYPE_OPTIONS,
|
|
1954
|
+
value: model.kind,
|
|
1955
|
+
onValueChange: (v) => setKind(v)
|
|
1604
1956
|
}
|
|
1605
|
-
)
|
|
1957
|
+
),
|
|
1958
|
+
model.kind === "pie" ? /* @__PURE__ */ jsxRuntime.jsx(PieSliceEditor, { model, onChange }) : /* @__PURE__ */ jsxRuntime.jsx(CartesianChartEditor, { model, onChange })
|
|
1959
|
+
] });
|
|
1960
|
+
}
|
|
1961
|
+
function PieSliceEditor({ model, onChange }) {
|
|
1962
|
+
const slices = model.slices;
|
|
1963
|
+
const update = (i, next) => {
|
|
1964
|
+
const copy = slices.map((s, idx) => idx === i ? { ...s, ...next } : s);
|
|
1965
|
+
onChange({ ...model, slices: copy });
|
|
1966
|
+
};
|
|
1967
|
+
const remove = (i) => onChange({ ...model, slices: slices.filter((_, idx) => idx !== i) });
|
|
1968
|
+
const add = () => onChange({ ...model, slices: [...slices, { name: `Slice ${slices.length + 1}`, value: 0 }] });
|
|
1969
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1970
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Slices" }),
|
|
1971
|
+
slices.map((s, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
|
|
1972
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: i === 0 ? "Name" : void 0, value: s.name, onChange: (e) => update(i, { name: e.target.value }) }) }),
|
|
1973
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-20", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: i === 0 ? "Value" : void 0, type: "number", value: String(s.value), onChange: (e) => update(i, { value: parseFloat(e.target.value) || 0 }) }) }),
|
|
1974
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => remove(i), "aria-label": "Remove slice" })
|
|
1975
|
+
] }, i)),
|
|
1976
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: add, children: "Add slice" })
|
|
1977
|
+
] });
|
|
1978
|
+
}
|
|
1979
|
+
function CartesianChartEditor({ model, onChange }) {
|
|
1980
|
+
const { categories, series } = model;
|
|
1981
|
+
const updateCategory = (i, label) => {
|
|
1982
|
+
onChange({ ...model, categories: categories.map((c, idx) => idx === i ? label : c) });
|
|
1983
|
+
};
|
|
1984
|
+
const removeCategory = (i) => {
|
|
1985
|
+
onChange({
|
|
1986
|
+
...model,
|
|
1987
|
+
categories: categories.filter((_, idx) => idx !== i),
|
|
1988
|
+
series: series.map((s) => ({ ...s, values: s.values.filter((_, idx) => idx !== i) }))
|
|
1989
|
+
});
|
|
1990
|
+
};
|
|
1991
|
+
const addCategory = () => {
|
|
1992
|
+
onChange({
|
|
1993
|
+
...model,
|
|
1994
|
+
categories: [...categories, `Cat ${categories.length + 1}`],
|
|
1995
|
+
series: series.map((s) => ({ ...s, values: [...s.values, 0] }))
|
|
1996
|
+
});
|
|
1997
|
+
};
|
|
1998
|
+
const updateSeries = (si, next) => {
|
|
1999
|
+
onChange({ ...model, series: series.map((s, idx) => idx === si ? { ...s, ...next } : s) });
|
|
2000
|
+
};
|
|
2001
|
+
const updateValue = (si, ci, value) => {
|
|
2002
|
+
onChange({
|
|
2003
|
+
...model,
|
|
2004
|
+
series: series.map(
|
|
2005
|
+
(s, idx) => idx === si ? { ...s, values: s.values.map((v, vi) => vi === ci ? value : v) } : s
|
|
2006
|
+
)
|
|
2007
|
+
});
|
|
2008
|
+
};
|
|
2009
|
+
const removeSeries = (si) => onChange({ ...model, series: series.filter((_, idx) => idx !== si) });
|
|
2010
|
+
const addSeries = () => onChange({
|
|
2011
|
+
...model,
|
|
2012
|
+
series: [
|
|
2013
|
+
...series,
|
|
2014
|
+
{ name: `Series ${series.length + 1}`, color: chartColorAt(series.length), values: categories.map(() => 0) }
|
|
2015
|
+
]
|
|
2016
|
+
});
|
|
2017
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2018
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2019
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Categories" }),
|
|
2020
|
+
categories.map((c, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2021
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { value: c, onChange: (e) => updateCategory(i, e.target.value) }) }),
|
|
2022
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeCategory(i), "aria-label": "Remove category" })
|
|
2023
|
+
] }, i)),
|
|
2024
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addCategory, children: "Add category" })
|
|
2025
|
+
] }),
|
|
2026
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
|
|
2027
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
2028
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Series" }),
|
|
2029
|
+
series.map((s, si) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 rounded-md border border-zinc-200 p-2 dark:border-zinc-800", children: [
|
|
2030
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end gap-2", children: [
|
|
2031
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Name", value: s.name, onChange: (e) => updateSeries(si, { name: e.target.value }) }) }),
|
|
2032
|
+
/* @__PURE__ */ jsxRuntime.jsx(FieldLabel, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ColorPicker, { value: s.color ?? chartColorAt(si), onChange: (c) => updateSeries(si, { color: c }) }) }),
|
|
2033
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeSeries(si), "aria-label": "Remove series" })
|
|
2034
|
+
] }),
|
|
2035
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: categories.map((c, ci) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2036
|
+
reactFancy.Input,
|
|
2037
|
+
{
|
|
2038
|
+
label: c,
|
|
2039
|
+
type: "number",
|
|
2040
|
+
value: String(s.values[ci] ?? 0),
|
|
2041
|
+
onChange: (e) => updateValue(si, ci, parseFloat(e.target.value) || 0)
|
|
2042
|
+
},
|
|
2043
|
+
ci
|
|
2044
|
+
)) })
|
|
2045
|
+
] }, si)),
|
|
2046
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addSeries, children: "Add series" })
|
|
2047
|
+
] })
|
|
1606
2048
|
] });
|
|
1607
2049
|
}
|
|
1608
2050
|
function TableStyleControls({ element, onPatch }) {
|
|
2051
|
+
const columns = element.columns;
|
|
2052
|
+
const rows = element.rows;
|
|
2053
|
+
const nextColKey = () => {
|
|
2054
|
+
const existing = new Set(columns.map((c) => c.key));
|
|
2055
|
+
let n = columns.length + 1;
|
|
2056
|
+
while (existing.has(`col${n}`)) n++;
|
|
2057
|
+
return `col${n}`;
|
|
2058
|
+
};
|
|
2059
|
+
const setColumnLabel = (i, label) => {
|
|
2060
|
+
onPatch({ columns: columns.map((c, idx) => idx === i ? { ...c, label } : c) });
|
|
2061
|
+
};
|
|
2062
|
+
const removeColumn = (i) => {
|
|
2063
|
+
const key = columns[i]?.key;
|
|
2064
|
+
const nextCols = columns.filter((_, idx) => idx !== i);
|
|
2065
|
+
const nextRows = key ? rows.map((r) => {
|
|
2066
|
+
const { [key]: _drop, ...rest } = r;
|
|
2067
|
+
return rest;
|
|
2068
|
+
}) : rows;
|
|
2069
|
+
onPatch({ columns: nextCols, rows: nextRows });
|
|
2070
|
+
};
|
|
2071
|
+
const addColumn = () => {
|
|
2072
|
+
const key = nextColKey();
|
|
2073
|
+
onPatch({
|
|
2074
|
+
columns: [...columns, { key, label: `Column ${columns.length + 1}` }],
|
|
2075
|
+
rows: rows.map((r) => ({ ...r, [key]: "" }))
|
|
2076
|
+
});
|
|
2077
|
+
};
|
|
2078
|
+
const setCell = (rowIdx, key, value) => {
|
|
2079
|
+
onPatch({ rows: rows.map((r, idx) => idx === rowIdx ? { ...r, [key]: value } : r) });
|
|
2080
|
+
};
|
|
2081
|
+
const removeRow = (rowIdx) => onPatch({ rows: rows.filter((_, idx) => idx !== rowIdx) });
|
|
2082
|
+
const addRow = () => {
|
|
2083
|
+
const blank = {};
|
|
2084
|
+
for (const c of columns) blank[c.key] = "";
|
|
2085
|
+
onPatch({ rows: [...rows, blank] });
|
|
2086
|
+
};
|
|
1609
2087
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1610
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1611
|
-
reactFancy.
|
|
1612
|
-
{
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
2088
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2089
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Columns" }),
|
|
2090
|
+
columns.map((c, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2091
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { value: c.label, onChange: (e) => setColumnLabel(i, e.target.value), "aria-label": `Column ${i + 1} label` }) }),
|
|
2092
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!font-mono !text-zinc-400", children: c.key }),
|
|
2093
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeColumn(i), "aria-label": "Remove column" })
|
|
2094
|
+
] }, c.key)),
|
|
2095
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addColumn, children: "Add column" })
|
|
2096
|
+
] }),
|
|
2097
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Separator, {}),
|
|
2098
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
2099
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Heading, { as: "h4", size: "xs", className: "!uppercase !tracking-wider !text-zinc-500", children: "Rows" }),
|
|
2100
|
+
columns.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "xs", className: "!text-zinc-500", children: "Add a column to start adding rows." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2101
|
+
rows.map((r, rowIdx) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 border-b border-zinc-100 pb-2 dark:border-zinc-800", children: [
|
|
2102
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid flex-1 grid-cols-1 gap-1", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2103
|
+
reactFancy.Input,
|
|
2104
|
+
{
|
|
2105
|
+
label: c.label,
|
|
2106
|
+
value: r[c.key] == null ? "" : String(r[c.key]),
|
|
2107
|
+
onChange: (e) => setCell(rowIdx, c.key, e.target.value)
|
|
2108
|
+
},
|
|
2109
|
+
c.key
|
|
2110
|
+
)) }),
|
|
2111
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", color: "red", icon: "x", onClick: () => removeRow(rowIdx), "aria-label": "Remove row" })
|
|
2112
|
+
] }, rowIdx)),
|
|
2113
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Action, { size: "xs", variant: "ghost", icon: "plus", onClick: addRow, children: "Add row" })
|
|
2114
|
+
] })
|
|
2115
|
+
] }),
|
|
2116
|
+
/* @__PURE__ */ jsxRuntime.jsxs("details", { className: "rounded-md border border-zinc-200 dark:border-zinc-800", children: [
|
|
2117
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { className: "cursor-pointer select-none px-2 py-1.5 text-xs font-medium text-zinc-600 dark:text-zinc-400", children: "Edit as JSON" }),
|
|
2118
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 p-2 pt-0", children: [
|
|
2119
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2120
|
+
reactFancy.Textarea,
|
|
2121
|
+
{
|
|
2122
|
+
label: "Columns (JSON)",
|
|
2123
|
+
value: JSON.stringify(columns, null, 2),
|
|
2124
|
+
onValueChange: (v) => {
|
|
2125
|
+
try {
|
|
2126
|
+
onPatch({ columns: JSON.parse(v) });
|
|
2127
|
+
} catch {
|
|
2128
|
+
}
|
|
2129
|
+
},
|
|
2130
|
+
rows: 5
|
|
1619
2131
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
2132
|
+
),
|
|
2133
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2134
|
+
reactFancy.Textarea,
|
|
2135
|
+
{
|
|
2136
|
+
label: "Rows (JSON)",
|
|
2137
|
+
value: JSON.stringify(rows, null, 2),
|
|
2138
|
+
onValueChange: (v) => {
|
|
2139
|
+
try {
|
|
2140
|
+
onPatch({ rows: JSON.parse(v) });
|
|
2141
|
+
} catch {
|
|
2142
|
+
}
|
|
2143
|
+
},
|
|
2144
|
+
rows: 8
|
|
1633
2145
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
)
|
|
2146
|
+
)
|
|
2147
|
+
] })
|
|
2148
|
+
] })
|
|
1638
2149
|
] });
|
|
1639
2150
|
}
|
|
1640
2151
|
function EmbedStyleControls({ element, onPatch }) {
|
|
@@ -1880,13 +2391,16 @@ function DeckEditor({
|
|
|
1880
2391
|
ElementInspector,
|
|
1881
2392
|
{
|
|
1882
2393
|
element: selectedElement,
|
|
2394
|
+
slide: slide ?? null,
|
|
1883
2395
|
onPatch: (patch) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, patch),
|
|
1884
2396
|
onDelete: () => {
|
|
1885
2397
|
if (!slide || !elementIdSelected) return;
|
|
1886
2398
|
ops.removeElement(slide.id, elementIdSelected);
|
|
1887
2399
|
setElementIdSelected(null);
|
|
1888
2400
|
},
|
|
1889
|
-
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked })
|
|
2401
|
+
onLockToggle: (locked) => slide && elementIdSelected && ops.updateElement(slide.id, elementIdSelected, { locked }),
|
|
2402
|
+
onSetTransition: (transition) => slide && ops.setTransition(slide.id, transition),
|
|
2403
|
+
onSetBackground: (background) => slide && ops.setBackground(slide.id, background)
|
|
1890
2404
|
}
|
|
1891
2405
|
) })
|
|
1892
2406
|
] }),
|