@particle-academy/fancy-cms-ui 0.8.0 → 0.9.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/editor.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- export { C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-CIRm2oi-.cjs';
1
+ export { C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-B2kVZJA0.cjs';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { P as PageDoc, h as ElementRegistry, g as DataContext, N as Node, q as StyleProps, L as LayoutMode } from './registry-B4FaQDip.cjs';
3
+ import { P as PageDoc, h as ElementRegistry, g as DataContext, N as Node, q as StyleProps, L as LayoutMode } from './registry-F2TAMjhx.cjs';
4
4
  import '@particle-academy/fancy-doc-commons';
5
5
 
6
6
  /** Animatable per-node transform (mirrors fancy-motion's NodeState, kept local to avoid a hard dep). */
package/dist/editor.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-DZDD9qpS.js';
1
+ export { C as Canvas, a as CanvasProps, E as Editor, b as EditorAction, c as EditorApi, d as EditorProps, e as EditorState, I as Inspector, f as InspectorProps, L as LayersPanel, g as LayersPanelProps, i as editorReduce, j as initEditor, u as useEditor } from './useEditor-BQSictH3.js';
2
2
  import { ReactNode, ReactElement } from 'react';
3
- import { P as PageDoc, h as ElementRegistry, g as DataContext, N as Node, q as StyleProps, L as LayoutMode } from './registry-B4FaQDip.js';
3
+ import { P as PageDoc, h as ElementRegistry, g as DataContext, N as Node, q as StyleProps, L as LayoutMode } from './registry-F2TAMjhx.js';
4
4
  import '@particle-academy/fancy-doc-commons';
5
5
 
6
6
  /** Animatable per-node transform (mirrors fancy-motion's NodeState, kept local to avoid a hard dep). */
package/dist/editor.js CHANGED
@@ -1,6 +1,6 @@
1
- import { useEditor, useLatestRef, CONTAINER_TYPES, ADD_DEFAULTS, ADD_MENU } from './chunk-IOMIYI45.js';
2
- export { Canvas, Editor, Inspector, LayersPanel, editorReduce, initEditor, useEditor } from './chunk-IOMIYI45.js';
3
- import { isBinding, lastRootId, childrenOf, CmsPage, defaultRegistry } from './chunk-SJYKJ6SG.js';
1
+ import { useEditor, useLatestRef, CONTAINER_TYPES, ADD_DEFAULTS, ADD_MENU } from './chunk-BVMSHUQF.js';
2
+ export { Canvas, Editor, Inspector, LayersPanel, editorReduce, initEditor, useEditor } from './chunk-BVMSHUQF.js';
3
+ import { isBinding, lastRootId, childrenOf, CmsPage, defaultRegistry } from './chunk-IRG5IKKO.js';
4
4
  import { useState, useRef, useEffect, useCallback, useLayoutEffect } from 'react';
5
5
  import { ContextMenu } from '@particle-academy/react-fancy';
6
6
  import { fractionalKey } from '@particle-academy/fancy-doc-commons';
package/dist/index.cjs CHANGED
@@ -13,6 +13,12 @@ function hasNodeMap(value) {
13
13
  const nodes = value.nodes;
14
14
  return typeof nodes === "object" && nodes !== null && !Array.isArray(nodes);
15
15
  }
16
+ function withNodeDefaults(node) {
17
+ const props = typeof node.props === "object" && node.props !== null ? node.props : {};
18
+ const style = typeof node.style === "object" && node.style !== null ? node.style : { base: {} };
19
+ const base = typeof style.base === "object" && style.base !== null ? style.base : {};
20
+ return props === node.props && style === node.style && base === style.base ? node : { ...node, props, style: { ...style, base } };
21
+ }
16
22
  var DEFAULT_BREAKPOINTS = {
17
23
  base: 0,
18
24
  md: 768,
@@ -332,6 +338,28 @@ function cssString(value) {
332
338
  return out;
333
339
  }
334
340
  var selectorFor = (id) => `[data-cms="${cssString(id)}"]`;
341
+ var BASE_CSS = [
342
+ "/* fancy-cms base */",
343
+ ":where([data-cms]) {\n box-sizing: border-box;\n}",
344
+ ":where(h1, h2, h3, h4, h5, h6, p, ul, ol)[data-cms] {\n margin: 0;\n}",
345
+ ":where(ul, ol)[data-cms] {\n padding-left: 1.25em;\n}",
346
+ ":where(a[data-cms]) {\n color: inherit;\n}",
347
+ ":where([data-cms] > img) {\n display: block;\n height: 100%;\n object-fit: cover;\n width: 100%;\n}",
348
+ ':where([data-cms] > img[data-cms-fit="contain"]) {\n object-fit: contain;\n}',
349
+ ':where([data-cms] > img[data-cms-fit="fill"]) {\n object-fit: fill;\n}',
350
+ ':where([data-cms] > img[data-cms-fit="none"]) {\n object-fit: none;\n}',
351
+ ':where([data-cms] > img[data-cms-fit="scale-down"]) {\n object-fit: scale-down;\n}',
352
+ ":where([data-cms-button]) {\n align-items: center;\n border: 1px solid var(--cms-color-primary, #7c3aed);\n border-radius: 8px;\n cursor: pointer;\n display: inline-flex;\n font-weight: 600;\n gap: 6px;\n justify-content: center;\n line-height: 1.2;\n padding: 10px 18px;\n text-decoration: none;\n}",
353
+ ':where([data-cms-button="primary"]) {\n background: var(--cms-color-primary, #7c3aed);\n color: #ffffff;\n}',
354
+ ':where([data-cms-button="outline"]) {\n background: transparent;\n color: var(--cms-color-primary, #7c3aed);\n}',
355
+ ':where([data-cms-button="ghost"]) {\n background: transparent;\n border-color: currentColor;\n color: inherit;\n}',
356
+ ":where([data-cms-callout]) {\n background: #3b82f614;\n border-left: 3px solid #3b82f6;\n border-radius: 10px;\n padding: 12px 14px;\n}",
357
+ ':where([data-cms-callout="success"]) {\n background: #10b98114;\n border-left-color: #10b981;\n}',
358
+ ':where([data-cms-callout="warning"]) {\n background: #f59e0b14;\n border-left-color: #f59e0b;\n}',
359
+ ':where([data-cms-callout="danger"]) {\n background: #ef444414;\n border-left-color: #ef4444;\n}',
360
+ ":where([data-cms] > hr) {\n border: none;\n border-top: 1px solid #e2e8f0;\n margin: 0;\n}",
361
+ ":where([data-cms] > pre) {\n background: #0b1220;\n border-radius: 10px;\n color: #e2e8f0;\n font-family: ui-monospace, monospace;\n font-size: 13px;\n margin: 0;\n overflow-x: auto;\n padding: 12px 14px;\n}"
362
+ ].join("\n\n");
335
363
  var UNITS = /* @__PURE__ */ new Set(["px", "%", "vw", "vh", "rem", "fr"]);
336
364
  function lengthToCss(len) {
337
365
  if (typeof len !== "object" || len === null) return null;
@@ -544,7 +572,7 @@ function emitDocCss(doc) {
544
572
  const themeRule = serializeRule(":root", themeDecls(doc.theme), "");
545
573
  if (themeRule) blocks.push(themeRule);
546
574
  for (const id of Object.keys(doc.nodes).sort()) {
547
- const node = doc.nodes[id];
575
+ const node = withNodeDefaults(doc.nodes[id]);
548
576
  const selector = selectorFor(id);
549
577
  const parentFree = isParentFree(doc, node);
550
578
  const base = {
@@ -557,12 +585,13 @@ function emitDocCss(doc) {
557
585
  if (baseRule) blocks.push(baseRule);
558
586
  for (const bp of doc.breakpoints) {
559
587
  if (bp === "base") continue;
560
- const px = breakpointPx[bp];
561
- if (px === void 0) continue;
562
- const bpStyle = node.style[bp];
588
+ const px = own(breakpointPx, bp);
589
+ if (typeof px !== "number") continue;
590
+ const bpStyle = own(node.style, bp);
591
+ const bpConstraints = node.constraints ? own(node.constraints, bp) : void 0;
563
592
  const decls = {
564
593
  ...bpStyle ? containerDecls(node, bpStyle, false) : {},
565
- ...node.constraints?.[bp] ? constraintDecls(node.constraints[bp], parentFree) : {},
594
+ ...bpConstraints ? constraintDecls(bpConstraints, parentFree) : {},
566
595
  ...bpStyle ? styleDecls(bpStyle) : {},
567
596
  ...bpStyle ? visibilityDecls(node, bpStyle, false) : {}
568
597
  };
@@ -572,7 +601,8 @@ ${inner}
572
601
  }`);
573
602
  }
574
603
  }
575
- return blocks.join("\n\n") + (blocks.length ? "\n" : "");
604
+ if (blocks.length === 0 && Object.keys(doc.nodes).length === 0) return "";
605
+ return [BASE_CSS, ...blocks].join("\n\n") + "\n";
576
606
  }
577
607
  function getPath(data, path) {
578
608
  return path.split(".").reduce((o, k) => o == null ? void 0 : o[k], data);
@@ -615,10 +645,17 @@ function elementFor(node, text) {
615
645
  case "button":
616
646
  case "link": {
617
647
  const href = safeHref(text(node.props.href));
618
- if (href === null) return { tag: node.type === "link" ? "span" : "div" };
619
- const attrs = { href };
620
- if (/^https?:\/\//i.test(href)) attrs.rel = "noopener noreferrer";
621
- return { tag: "a", attrs };
648
+ const attrs = {};
649
+ if (href !== null) {
650
+ attrs.href = href;
651
+ if (/^https?:\/\//i.test(href)) attrs.rel = "noopener noreferrer";
652
+ }
653
+ if (node.type === "button") attrs["data-cms-button"] = BUTTON_VARIANTS.has(text(node.props.variant)) ? text(node.props.variant) : "primary";
654
+ return { tag: href !== null ? "a" : node.type === "link" ? "span" : "div", attrs };
655
+ }
656
+ case "callout": {
657
+ const variant = text(node.props.variant);
658
+ return { tag: "div", attrs: { "data-cms-callout": CALLOUT_VARIANTS.has(variant) ? variant : "info" } };
622
659
  }
623
660
  default:
624
661
  return { tag: CONTAINERS.has(node.type) && LANDMARKS.has(as) ? as : "div" };
@@ -642,51 +679,32 @@ var defaultRegistry = {
642
679
  const items = resolve(node.props.items);
643
680
  return Array.isArray(items) ? items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item === null || item === void 0 ? "" : String(item) }, i)) : null;
644
681
  },
645
- divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { border: "none", borderTop: "1px solid #e2e8f0", margin: 0 } }),
646
- callout: ({ node, text }) => {
647
- const tone = { info: "#3b82f6", success: "#10b981", warning: "#f59e0b", danger: "#ef4444" }[text(node.props.variant)] ?? "#3b82f6";
648
- 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: text(node.props.content) || "Callout" }) });
649
- },
650
- code: ({ node, text }) => /* @__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: text(node.props.content) }) }),
651
- button: ({ node, text }) => {
652
- const label2 = text(node.props.label) || text(node.props.content) || "Button";
653
- const variant = text(node.props.variant) || "primary";
654
- return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cms-button": variant, style: buttonStyle(variant), children: label2 });
655
- },
682
+ // Looks come from the base stylesheet (render/css.ts BASE_CSS), never from
683
+ // inline style objects, so the PHP renderer emits the same markup.
684
+ divider: () => /* @__PURE__ */ jsxRuntime.jsx("hr", {}),
685
+ callout: ({ node, text }) => text(node.props.content) || "Callout",
686
+ code: ({ node, text }) => /* @__PURE__ */ jsxRuntime.jsx("pre", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: text(node.props.content) }) }),
687
+ button: ({ node, text }) => text(node.props.label) || text(node.props.content) || "Button",
656
688
  image: ({ node, text }) => {
657
689
  const fit = text(node.props.fit);
658
690
  return /* @__PURE__ */ jsxRuntime.jsx(
659
691
  "img",
660
692
  {
661
- src: text(node.props.src),
693
+ src: text(node.props.src) || void 0,
662
694
  alt: text(node.props.alt),
663
695
  loading: text(node.props.loading) === "eager" ? "eager" : "lazy",
664
- style: { width: "100%", height: "100%", objectFit: IMAGE_FITS.has(fit) ? fit : "cover", display: "block" }
696
+ "data-cms-fit": IMAGE_FITS.has(fit) && fit !== "cover" ? fit : void 0
665
697
  }
666
698
  );
667
699
  }
668
700
  };
669
701
  var IMAGE_FITS = /* @__PURE__ */ new Set(["cover", "contain", "fill", "none", "scale-down"]);
670
- function buttonStyle(variant) {
671
- const base = {
672
- display: "inline-flex",
673
- alignItems: "center",
674
- gap: 6,
675
- padding: "9px 16px",
676
- borderRadius: 8,
677
- fontWeight: 600,
678
- fontSize: 14,
679
- lineHeight: 1,
680
- cursor: "pointer",
681
- userSelect: "none"
682
- };
683
- if (variant === "ghost") return { ...base, background: "transparent", color: "inherit", border: "1px solid currentColor" };
684
- if (variant === "outline") return { ...base, background: "transparent", color: "#7c3aed", border: "1px solid #7c3aed" };
685
- return { ...base, background: "#7c3aed", color: "#fff", border: "1px solid #7c3aed" };
686
- }
702
+ var BUTTON_VARIANTS = /* @__PURE__ */ new Set(["primary", "outline", "ghost"]);
703
+ var CALLOUT_VARIANTS = /* @__PURE__ */ new Set(["info", "success", "warning", "danger"]);
687
704
  function RenderNode({ doc, id, registry = defaultRegistry, data }) {
688
- const node = own(doc.nodes, id);
689
- if (!node) return null;
705
+ const stored = own(doc.nodes, id);
706
+ if (!stored) return null;
707
+ const node = withNodeDefaults(stored);
690
708
  if (node.type === "repeater") {
691
709
  const arr = resolveValue(node.props.items, data);
692
710
  const items = Array.isArray(arr) ? arr : [];
@@ -1355,5 +1373,6 @@ exports.resolveInsertParent = resolveInsertParent;
1355
1373
  exports.resolveValue = resolveValue;
1356
1374
  exports.rootIds = rootIds;
1357
1375
  exports.useEditor = useEditor;
1376
+ exports.withNodeDefaults = withNodeDefaults;
1358
1377
  //# sourceMappingURL=index.cjs.map
1359
1378
  //# sourceMappingURL=index.cjs.map