@particle-academy/fancy-cms-ui 0.0.1-dev.11 → 0.0.1-dev.13
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/{chunk-ZV7CV54P.js → chunk-E5AAQX7P.js} +10 -2
- package/dist/chunk-E5AAQX7P.js.map +1 -0
- package/dist/editor.cjs +141 -9
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +134 -10
- package/dist/editor.js.map +1 -1
- package/dist/react.cjs +8 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-ZV7CV54P.js.map +0 -1
|
@@ -13,8 +13,16 @@ var defaultRegistry = {
|
|
|
13
13
|
grid: ({ children }) => children,
|
|
14
14
|
frame: ({ children }) => children,
|
|
15
15
|
shape: ({ children }) => children,
|
|
16
|
+
card: ({ children }) => children,
|
|
16
17
|
text: ({ node }) => literal(node.props.content),
|
|
17
18
|
heading: ({ node }) => literal(node.props.content) || "Heading",
|
|
19
|
+
link: ({ node }) => /* @__PURE__ */ jsx("a", { href: literal(node.props.href) || "#", style: { color: "inherit", textDecoration: "underline" }, children: literal(node.props.content) || "link" }),
|
|
20
|
+
divider: () => /* @__PURE__ */ jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
|
|
21
|
+
callout: ({ node }) => {
|
|
22
|
+
const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[literal(node.props.variant)] ?? "#3b82f6";
|
|
23
|
+
return /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 10, padding: "12px 14px", borderRadius: 10, background: `${tone}14`, borderLeft: `3px solid ${tone}` }, children: /* @__PURE__ */ jsx("span", { style: { fontSize: 13 }, children: literal(node.props.content) || "Callout" }) });
|
|
24
|
+
},
|
|
25
|
+
code: ({ node }) => /* @__PURE__ */ jsx("pre", { style: { margin: 0, padding: "12px 14px", borderRadius: 10, background: "#0b1220", color: "#e2e8f0", fontFamily: "ui-monospace, monospace", fontSize: 13, overflowX: "auto" }, children: /* @__PURE__ */ jsx("code", { children: literal(node.props.content) }) }),
|
|
18
26
|
button: ({ node }) => {
|
|
19
27
|
const label = literal(node.props.label) || literal(node.props.content) || "Button";
|
|
20
28
|
const variant = literal(node.props.variant) || "primary";
|
|
@@ -74,5 +82,5 @@ function CmsPage({
|
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
export { CmsPage, RenderNode, defaultRegistry };
|
|
77
|
-
//# sourceMappingURL=chunk-
|
|
78
|
-
//# sourceMappingURL=chunk-
|
|
85
|
+
//# sourceMappingURL=chunk-E5AAQX7P.js.map
|
|
86
|
+
//# sourceMappingURL=chunk-E5AAQX7P.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/registry.tsx","../src/react/RenderNode.tsx","../src/react/CmsPage.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-E5AAQX7P.js","sourcesContent":["import type { ReactNode } from \"react\";\nimport { isBinding, type Bound, type Json, type Node, type PageDoc } from \"../document/types\";\n\nexport interface ElementContext {\n node: Node;\n doc: PageDoc;\n /** Pre-rendered child nodes, in order. */\n children: ReactNode;\n}\n\n/**\n * Renders the INNER content of a node. The outer `[data-cms]` wrapper is added\n * by {@link RenderNode}, so a renderer only returns what goes inside.\n */\nexport type ElementRenderer = (ctx: ElementContext) => ReactNode;\n\nexport type ElementRegistry = Record<string, ElementRenderer>;\n\n/** Resolve a literal prop to a string (bindings resolve in Phase 3). */\nfunction literal(v: Bound<Json> | undefined): string {\n if (v === undefined || v === null) return \"\";\n if (isBinding(v)) return \"\";\n return String(v);\n}\n\n/** Built-in native elements. Containers render their children; leaves render content. */\nexport const defaultRegistry: ElementRegistry = {\n section: ({ children }) => children,\n stack: ({ children }) => children,\n grid: ({ children }) => children,\n frame: ({ children }) => children,\n shape: ({ children }) => children,\n card: ({ children }) => children,\n text: ({ node }) => literal(node.props.content),\n heading: ({ node }) => literal(node.props.content) || \"Heading\",\n link: ({ node }) => (\n <a href={literal(node.props.href) || \"#\"} style={{ color: \"inherit\", textDecoration: \"underline\" }}>\n {literal(node.props.content) || \"link\"}\n </a>\n ),\n divider: () => <hr style={{ border: \"none\", borderTop: \"1px solid #e2e8f0\", margin: 0 }} />,\n callout: ({ node }) => {\n const tone = ({ info: \"#3b82f6\", success: \"#10b981\", warning: \"#f59e0b\", danger: \"#ef4444\" } as Record<string, string>)[literal(node.props.variant)] ?? \"#3b82f6\";\n return (\n <div style={{ display: \"flex\", gap: 10, padding: \"12px 14px\", borderRadius: 10, background: `${tone}14`, borderLeft: `3px solid ${tone}` }}>\n <span style={{ fontSize: 13 }}>{literal(node.props.content) || \"Callout\"}</span>\n </div>\n );\n },\n code: ({ node }) => (\n <pre style={{ margin: 0, padding: \"12px 14px\", borderRadius: 10, background: \"#0b1220\", color: \"#e2e8f0\", fontFamily: \"ui-monospace, monospace\", fontSize: 13, overflowX: \"auto\" }}>\n <code>{literal(node.props.content)}</code>\n </pre>\n ),\n button: ({ node }) => {\n const label = literal(node.props.label) || literal(node.props.content) || \"Button\";\n const variant = literal(node.props.variant) || \"primary\";\n return <span data-cms-button={variant} style={buttonStyle(variant)}>{label}</span>;\n },\n image: ({ node }) => (\n <img\n src={literal(node.props.src)}\n alt={literal(node.props.alt)}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }}\n />\n ),\n};\n\n/**\n * A self-contained button look so the `button` primitive renders sensibly with\n * zero host CSS. Hosts that want their own button classes override the `button`\n * renderer in their registry (e.g. the sandbox maps it to `.btn .btn-primary`).\n */\nfunction buttonStyle(variant: string): import(\"react\").CSSProperties {\n const base: import(\"react\").CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: 6,\n padding: \"9px 16px\",\n borderRadius: 8,\n fontWeight: 600,\n fontSize: 14,\n lineHeight: 1,\n cursor: \"pointer\",\n userSelect: \"none\",\n };\n if (variant === \"ghost\") return { ...base, background: \"transparent\", color: \"inherit\", border: \"1px solid currentColor\" };\n if (variant === \"outline\") return { ...base, background: \"transparent\", color: \"#7c3aed\", border: \"1px solid #7c3aed\" };\n return { ...base, background: \"#7c3aed\", color: \"#fff\", border: \"1px solid #7c3aed\" };\n}\n","import type { ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { childrenOf } from \"../document/reduce\";\nimport { defaultRegistry, type ElementRegistry } from \"./registry\";\n\nexport interface RenderNodeProps {\n doc: PageDoc;\n id: string;\n registry?: ElementRegistry;\n}\n\n/**\n * Render one node (and its subtree) to a `[data-cms]`-tagged element. The CSS\n * emitted by `emitDocCss` targets these `data-cms` handles. Interactive /\n * 3rd-party **islands** with no registered renderer become an empty, sized\n * placeholder the host hydrates client-side — mirroring the PHP renderer.\n */\nexport function RenderNode({ doc, id, registry = defaultRegistry }: RenderNodeProps): ReactElement | null {\n const node = doc.nodes[id];\n if (!node) return null;\n\n const kids = childrenOf(doc, id).map((child) => (\n <RenderNode key={child.id} doc={doc} id={child.id} registry={registry} />\n ));\n\n const renderer = registry[node.type];\n const inner = renderer ? renderer({ node, doc, children: kids }) : node.island ? null : kids;\n\n return (\n <div\n data-cms={node.id}\n data-cms-island={node.island ? node.type : undefined}\n className={node.className}\n >\n {inner}\n </div>\n );\n}\n","import { Fragment, type ReactElement } from \"react\";\nimport type { PageDoc } from \"../document/types\";\nimport { emitDocCss } from \"../render/css\";\nimport { RenderNode } from \"./RenderNode\";\nimport { defaultRegistry, type ElementRegistry } from \"./registry\";\n\nexport interface CmsPageProps {\n doc: PageDoc;\n registry?: ElementRegistry;\n /** Inject the compiled stylesheet. Set false when the host emits CSS itself. */\n includeStyles?: boolean;\n}\n\n/** Render a full page document: the compiled stylesheet + every section. */\nexport function CmsPage({\n doc,\n registry = defaultRegistry,\n includeStyles = true,\n}: CmsPageProps): ReactElement {\n return (\n <Fragment>\n {includeStyles ? <style data-cms-styles=\"\">{emitDocCss(doc)}</style> : null}\n {doc.sections.map((id) => (\n <RenderNode key={id} doc={doc} id={id} registry={registry} />\n ))}\n </Fragment>\n );\n}\n"]}
|
package/dist/editor.cjs
CHANGED
|
@@ -372,8 +372,16 @@ var defaultRegistry = {
|
|
|
372
372
|
grid: ({ children }) => children,
|
|
373
373
|
frame: ({ children }) => children,
|
|
374
374
|
shape: ({ children }) => children,
|
|
375
|
+
card: ({ children }) => children,
|
|
375
376
|
text: ({ node }) => literal(node.props.content),
|
|
376
377
|
heading: ({ node }) => literal(node.props.content) || "Heading",
|
|
378
|
+
link: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx("a", { href: literal(node.props.href) || "#", style: { color: "inherit", textDecoration: "underline" }, children: literal(node.props.content) || "link" }),
|
|
379
|
+
divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
|
|
380
|
+
callout: ({ node }) => {
|
|
381
|
+
const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[literal(node.props.variant)] ?? "#3b82f6";
|
|
382
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: 10, padding: "12px 14px", borderRadius: 10, background: `${tone}14`, borderLeft: `3px solid ${tone}` }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 13 }, children: literal(node.props.content) || "Callout" }) });
|
|
383
|
+
},
|
|
384
|
+
code: ({ node }) => /* @__PURE__ */ jsxRuntime.jsx("pre", { style: { margin: 0, padding: "12px 14px", borderRadius: 10, background: "#0b1220", color: "#e2e8f0", fontFamily: "ui-monospace, monospace", fontSize: 13, overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: literal(node.props.content) }) }),
|
|
377
385
|
button: ({ node }) => {
|
|
378
386
|
const label2 = literal(node.props.label) || literal(node.props.content) || "Button";
|
|
379
387
|
const variant = literal(node.props.variant) || "primary";
|
|
@@ -802,7 +810,7 @@ function Toolbar({ ed }) {
|
|
|
802
810
|
}
|
|
803
811
|
);
|
|
804
812
|
}
|
|
805
|
-
var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape"]);
|
|
813
|
+
var CONTAINER_TYPES = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
|
|
806
814
|
function NodeInspector({
|
|
807
815
|
node,
|
|
808
816
|
transform,
|
|
@@ -1250,11 +1258,48 @@ function EditablePage({
|
|
|
1250
1258
|
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
|
|
1251
1259
|
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
1252
1260
|
const def = ADD_DEFAULTS[kind];
|
|
1253
|
-
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1261
|
+
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1254
1262
|
ed.select(id);
|
|
1255
1263
|
},
|
|
1256
1264
|
[ed, selNode]
|
|
1257
1265
|
);
|
|
1266
|
+
const addNodeAt = react.useCallback(
|
|
1267
|
+
(kind, targetId) => {
|
|
1268
|
+
const doc2 = ed.state.doc;
|
|
1269
|
+
const target = targetId ? doc2.nodes[targetId] : null;
|
|
1270
|
+
const parent = target ? CONTAINER_TYPES2.has(target.type) ? target.id : target.parent : doc2.sections[doc2.sections.length - 1] ?? null;
|
|
1271
|
+
const siblings = childrenOf(doc2, parent);
|
|
1272
|
+
const order = keyBetween(siblings.length ? siblings[siblings.length - 1].order : null, null);
|
|
1273
|
+
const id = `n${doc2.seq + 1}-${Math.floor(performance.now())}`;
|
|
1274
|
+
const def = ADD_DEFAULTS[kind];
|
|
1275
|
+
ed.apply({ t: "insert_node", node: { id, type: def.type, parent, order, layout: def.layout, props: { ...def.props }, style: { base: { ...def.style } } } });
|
|
1276
|
+
ed.select(id);
|
|
1277
|
+
},
|
|
1278
|
+
[ed]
|
|
1279
|
+
);
|
|
1280
|
+
const [paletteOpen, setPaletteOpen] = react.useState(false);
|
|
1281
|
+
const [draggingEl, setDraggingEl] = react.useState(false);
|
|
1282
|
+
react.useEffect(() => {
|
|
1283
|
+
if (!editing) {
|
|
1284
|
+
setPaletteOpen(false);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const onMove = (e) => {
|
|
1288
|
+
if (draggingEl) return;
|
|
1289
|
+
if (e.clientX >= window.innerWidth - 26) setPaletteOpen(true);
|
|
1290
|
+
else if (e.clientX < window.innerWidth - 236) setPaletteOpen(false);
|
|
1291
|
+
};
|
|
1292
|
+
window.addEventListener("mousemove", onMove);
|
|
1293
|
+
return () => window.removeEventListener("mousemove", onMove);
|
|
1294
|
+
}, [editing, draggingEl]);
|
|
1295
|
+
const onPageDrop = (e) => {
|
|
1296
|
+
const kind = e.dataTransfer.getData("application/x-cms-element");
|
|
1297
|
+
if (!kind) return;
|
|
1298
|
+
e.preventDefault();
|
|
1299
|
+
const el = document.elementFromPoint(e.clientX, e.clientY)?.closest("[data-cms]");
|
|
1300
|
+
addNodeAt(kind, el?.getAttribute("data-cms") ?? null);
|
|
1301
|
+
setDraggingEl(false);
|
|
1302
|
+
};
|
|
1258
1303
|
const setStyle = (patch) => selection && ed.apply({ t: "set_style", id: selection, breakpoint: "base", patch });
|
|
1259
1304
|
const setProps = (patch) => selection && ed.apply({ t: "set_props", id: selection, patch });
|
|
1260
1305
|
const setLayout = (layout) => selection && ed.apply({ t: "set_layout", id: selection, layout });
|
|
@@ -1322,7 +1367,17 @@ function EditablePage({
|
|
|
1322
1367
|
if (el) ed.select(el.getAttribute("data-cms"));
|
|
1323
1368
|
else ed.select(null);
|
|
1324
1369
|
};
|
|
1325
|
-
const page = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1370
|
+
const page = /* @__PURE__ */ jsxRuntime.jsx(
|
|
1371
|
+
"div",
|
|
1372
|
+
{
|
|
1373
|
+
ref: pageRef,
|
|
1374
|
+
onClickCapture,
|
|
1375
|
+
onContextMenu: onContextMenuSelect,
|
|
1376
|
+
onDragOver: editing ? (e) => e.preventDefault() : void 0,
|
|
1377
|
+
onDrop: editing ? onPageDrop : void 0,
|
|
1378
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(CmsPage, { doc: ed.state.doc, registry })
|
|
1379
|
+
}
|
|
1380
|
+
);
|
|
1326
1381
|
const pageShell = pinned ? /* @__PURE__ */ jsxRuntime.jsx("div", { ref: spacerRef, style: { height: `${Math.max(1, frames) * 100}vh` }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "sticky", top: 0, height: "100vh", overflow: "hidden" }, children: page }) }) : frames > 1 ? (
|
|
1327
1382
|
// Normal flow, but "expanded" — extra scroll length below the content so a
|
|
1328
1383
|
// page can be made longer than its content (the + Frame control).
|
|
@@ -1389,16 +1444,86 @@ function EditablePage({
|
|
|
1389
1444
|
onAdd: addNode
|
|
1390
1445
|
}
|
|
1391
1446
|
) : null,
|
|
1447
|
+
editing ? /* @__PURE__ */ jsxRuntime.jsx(ElementPalette, { open: paletteOpen, onDragChange: setDraggingEl }) : null,
|
|
1392
1448
|
editing && timelineDock ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "fixed", left: 0, right: 0, bottom: 0, zIndex: Z2 }, children: timelineDock }) : null
|
|
1393
1449
|
] });
|
|
1394
1450
|
}
|
|
1395
|
-
|
|
1451
|
+
function ElementPalette({ open, onDragChange }) {
|
|
1452
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1453
|
+
"div",
|
|
1454
|
+
{
|
|
1455
|
+
style: {
|
|
1456
|
+
position: "fixed",
|
|
1457
|
+
top: 64,
|
|
1458
|
+
bottom: 96,
|
|
1459
|
+
right: 0,
|
|
1460
|
+
width: 216,
|
|
1461
|
+
transform: open ? "translateX(0)" : "translateX(100%)",
|
|
1462
|
+
transition: "transform 0.18s ease",
|
|
1463
|
+
background: "#0b1220",
|
|
1464
|
+
color: "#e2e8f0",
|
|
1465
|
+
borderLeft: "1px solid #1e293b",
|
|
1466
|
+
borderTopLeftRadius: 12,
|
|
1467
|
+
borderBottomLeftRadius: 12,
|
|
1468
|
+
boxShadow: "-18px 0 48px -20px rgba(0,0,0,0.6)",
|
|
1469
|
+
zIndex: Z2 + 2,
|
|
1470
|
+
fontFamily: "system-ui, sans-serif",
|
|
1471
|
+
padding: 12,
|
|
1472
|
+
overflowY: "auto"
|
|
1473
|
+
},
|
|
1474
|
+
children: [
|
|
1475
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 11, letterSpacing: "0.06em", textTransform: "uppercase", opacity: 0.5, marginBottom: 10 }, children: "Elements" }),
|
|
1476
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }, children: ADD_MENU.map((m) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1477
|
+
"div",
|
|
1478
|
+
{
|
|
1479
|
+
draggable: true,
|
|
1480
|
+
onDragStart: (e) => {
|
|
1481
|
+
e.dataTransfer.setData("application/x-cms-element", m.kind);
|
|
1482
|
+
e.dataTransfer.effectAllowed = "copy";
|
|
1483
|
+
onDragChange(true);
|
|
1484
|
+
},
|
|
1485
|
+
onDragEnd: () => onDragChange(false),
|
|
1486
|
+
title: `Drag "${m.label}" onto the page`,
|
|
1487
|
+
style: {
|
|
1488
|
+
display: "flex",
|
|
1489
|
+
flexDirection: "column",
|
|
1490
|
+
alignItems: "center",
|
|
1491
|
+
justifyContent: "center",
|
|
1492
|
+
gap: 4,
|
|
1493
|
+
height: 56,
|
|
1494
|
+
borderRadius: 8,
|
|
1495
|
+
background: "#0f172a",
|
|
1496
|
+
border: "1px solid #1e293b",
|
|
1497
|
+
cursor: "grab",
|
|
1498
|
+
fontSize: 12,
|
|
1499
|
+
userSelect: "none"
|
|
1500
|
+
},
|
|
1501
|
+
children: [
|
|
1502
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 16, opacity: 0.7 }, children: "\uFF0B" }),
|
|
1503
|
+
m.label
|
|
1504
|
+
]
|
|
1505
|
+
},
|
|
1506
|
+
m.kind
|
|
1507
|
+
)) }),
|
|
1508
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: 10.5, opacity: 0.45, marginTop: 12, lineHeight: 1.4 }, children: "Drag an Element onto the page. It drops into the container you release over." })
|
|
1509
|
+
]
|
|
1510
|
+
}
|
|
1511
|
+
);
|
|
1512
|
+
}
|
|
1513
|
+
var CONTAINER_TYPES2 = /* @__PURE__ */ new Set(["section", "frame", "stack", "grid", "shape", "card", "device"]);
|
|
1396
1514
|
var ADD_DEFAULTS = {
|
|
1397
1515
|
text: { type: "text", props: { content: "New text" }, style: { color: "inherit" } },
|
|
1398
1516
|
heading: { type: "heading", props: { content: "New heading" }, style: { fontSize: { value: 28, unit: "px" }, fontWeight: 700 } },
|
|
1399
1517
|
button: { type: "button", props: { label: "Button", href: "#", variant: "primary" }, style: {} },
|
|
1518
|
+
link: { type: "link", props: { content: "link text", href: "#" }, style: { color: "#7c3aed" } },
|
|
1400
1519
|
image: { type: "image", props: { src: "", alt: "" }, style: {} },
|
|
1401
|
-
box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } }
|
|
1520
|
+
box: { type: "frame", props: {}, style: { padding: { value: 16, unit: "px" } } },
|
|
1521
|
+
stack: { type: "stack", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "stack" },
|
|
1522
|
+
grid: { type: "grid", props: {}, style: { gap: { value: 12, unit: "px" } }, layout: "grid" },
|
|
1523
|
+
card: { type: "card", props: {}, style: { padding: { value: 16, unit: "px" }, radius: { value: 12, unit: "px" }, border: "1px solid #e2e8f0" } },
|
|
1524
|
+
callout: { type: "callout", props: { content: "Heads up \u2014 this is a callout.", variant: "info" }, style: {} },
|
|
1525
|
+
divider: { type: "divider", props: {}, style: {} },
|
|
1526
|
+
code: { type: "code", props: { content: "npm install @particle-academy/react-fancy", lang: "bash" }, style: {} }
|
|
1402
1527
|
};
|
|
1403
1528
|
var GRIPS = [
|
|
1404
1529
|
{ key: "nw", north: true, west: true, cursor: "nwse-resize" },
|
|
@@ -1598,11 +1723,18 @@ function FloatingToolbar({
|
|
|
1598
1723
|
] });
|
|
1599
1724
|
}
|
|
1600
1725
|
var ADD_MENU = [
|
|
1601
|
-
{ kind: "text", label: "Text" },
|
|
1602
1726
|
{ kind: "heading", label: "Heading" },
|
|
1727
|
+
{ kind: "text", label: "Text" },
|
|
1603
1728
|
{ kind: "button", label: "Button" },
|
|
1729
|
+
{ kind: "link", label: "Link" },
|
|
1604
1730
|
{ kind: "image", label: "Image" },
|
|
1605
|
-
{ kind: "
|
|
1731
|
+
{ kind: "card", label: "Card" },
|
|
1732
|
+
{ kind: "callout", label: "Callout" },
|
|
1733
|
+
{ kind: "stack", label: "Stack" },
|
|
1734
|
+
{ kind: "grid", label: "Grid" },
|
|
1735
|
+
{ kind: "box", label: "Box" },
|
|
1736
|
+
{ kind: "divider", label: "Divider" },
|
|
1737
|
+
{ kind: "code", label: "Code" }
|
|
1606
1738
|
];
|
|
1607
1739
|
function EditBar({
|
|
1608
1740
|
editing,
|
|
@@ -1620,7 +1752,7 @@ function EditBar({
|
|
|
1620
1752
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: editing ? "#8b5cf6" : "transparent" }, onClick: onToggle, children: editing ? "\u25CF Editing" : "Edit" }),
|
|
1621
1753
|
editing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1622
1754
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
|
|
1623
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: open ? "#1e293b" : "transparent" }, onClick: () => setOpen((v) => !v), title: "Add an
|
|
1755
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, background: open ? "#1e293b" : "transparent" }, onClick: () => setOpen((v) => !v), title: "Add an Element (or drag from the right-edge palette)", children: "\uFF0B Element \u25BE" }),
|
|
1624
1756
|
open ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: "calc(100% + 6px)", right: 0, background: "#0f172a", border: "1px solid #1e293b", borderRadius: 10, padding: 4, display: "flex", flexDirection: "column", minWidth: 132, boxShadow: "0 12px 32px -10px rgba(0,0,0,0.6)" }, children: ADD_MENU.map((m) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1625
1757
|
"button",
|
|
1626
1758
|
{
|
|
@@ -1658,7 +1790,7 @@ function EditMenuItems({
|
|
|
1658
1790
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1659
1791
|
editable ? /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: onEditText, children: "Edit text" }) : null,
|
|
1660
1792
|
/* @__PURE__ */ jsxRuntime.jsxs(reactFancy.ContextMenu.Sub, { children: [
|
|
1661
|
-
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.SubTrigger, { children: isContainer ? "Add inside" : "Add" }),
|
|
1793
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.SubTrigger, { children: isContainer ? "Add Element inside" : "Add Element" }),
|
|
1662
1794
|
/* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.SubContent, { className: "!z-[2147483647]", children: ADD_MENU.map((m) => /* @__PURE__ */ jsxRuntime.jsx(reactFancy.ContextMenu.Item, { onClick: () => onAdd(m.kind), children: m.label }, m.kind)) })
|
|
1663
1795
|
] }),
|
|
1664
1796
|
node ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|