@particle-academy/fancy-slides 0.2.0 → 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 +401 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +401 -40
- package/dist/index.js.map +1 -1
- 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
|
|
@@ -1321,6 +1337,113 @@ function chartStarterOption(kind) {
|
|
|
1321
1337
|
};
|
|
1322
1338
|
}
|
|
1323
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
|
+
}
|
|
1324
1447
|
function SlideRail({
|
|
1325
1448
|
slides,
|
|
1326
1449
|
selectedId,
|
|
@@ -1674,7 +1797,35 @@ function TextStyleControls({ element, onPatch }) {
|
|
|
1674
1797
|
] });
|
|
1675
1798
|
}
|
|
1676
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
|
+
};
|
|
1677
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" }),
|
|
1678
1829
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Textarea, { label: "Image URL", value: element.src, onValueChange: (v) => onPatch({ src: v }), rows: 2 }),
|
|
1679
1830
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Input, { label: "Alt text", value: element.alt ?? "", onChange: (e) => onPatch({ alt: e.target.value }) }),
|
|
1680
1831
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1690,7 +1841,17 @@ function ImageStyleControls({ element, onPatch }) {
|
|
|
1690
1841
|
value: element.fit ?? "contain",
|
|
1691
1842
|
onValueChange: (v) => onPatch({ fit: v })
|
|
1692
1843
|
}
|
|
1693
|
-
)
|
|
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." })
|
|
1694
1855
|
] });
|
|
1695
1856
|
}
|
|
1696
1857
|
function ShapeStyleControls({ element, onPatch }) {
|
|
@@ -1737,54 +1898,254 @@ function CodeStyleControls({ element, onPatch }) {
|
|
|
1737
1898
|
] });
|
|
1738
1899
|
}
|
|
1739
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
|
+
};
|
|
1740
1948
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1741
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.Text, { size: "sm", className: "!text-zinc-500", children: "Chart option is JSON \u2014 paste any ECharts option here." }),
|
|
1742
1949
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1743
|
-
reactFancy.
|
|
1950
|
+
reactFancy.Select,
|
|
1744
1951
|
{
|
|
1745
|
-
label: "
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
onPatch({ option: JSON.parse(v) });
|
|
1750
|
-
} catch {
|
|
1751
|
-
}
|
|
1752
|
-
},
|
|
1753
|
-
rows: 10
|
|
1952
|
+
label: "Chart type",
|
|
1953
|
+
list: CHART_TYPE_OPTIONS,
|
|
1954
|
+
value: model.kind,
|
|
1955
|
+
onValueChange: (v) => setKind(v)
|
|
1754
1956
|
}
|
|
1755
|
-
)
|
|
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
|
+
] })
|
|
1756
2048
|
] });
|
|
1757
2049
|
}
|
|
1758
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
|
+
};
|
|
1759
2087
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1760
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1761
|
-
reactFancy.
|
|
1762
|
-
{
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
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
|
|
1769
2131
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
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
|
|
1783
2145
|
}
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
)
|
|
2146
|
+
)
|
|
2147
|
+
] })
|
|
2148
|
+
] })
|
|
1788
2149
|
] });
|
|
1789
2150
|
}
|
|
1790
2151
|
function EmbedStyleControls({ element, onPatch }) {
|