@pixldocs/canvas-renderer 0.4.9 → 0.5.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.js CHANGED
@@ -2184,6 +2184,18 @@ const useEditorStore = create((set, get) => ({
2184
2184
  const committed = commitFromState(state, nextCanvas);
2185
2185
  return { canvas: nextCanvas, ...committed };
2186
2186
  }),
2187
+ setPageBoundRepeatablePage: (pageId, repeatablePageId) => set((state) => {
2188
+ const updatedPages = state.canvas.pages.map((p) => {
2189
+ if (p.id !== pageId) return p;
2190
+ const next = { ...p };
2191
+ if (repeatablePageId) next.boundRepeatablePageId = repeatablePageId;
2192
+ else delete next.boundRepeatablePageId;
2193
+ return next;
2194
+ });
2195
+ const nextCanvas = { ...state.canvas, pages: updatedPages };
2196
+ const committed = commitFromState(state, nextCanvas);
2197
+ return { canvas: nextCanvas, ...committed };
2198
+ }),
2187
2199
  moveElementsToPage: (elementIds, targetPageId) => set((state) => {
2188
2200
  const currentPage = getCurrentPageFromCanvas(state.canvas);
2189
2201
  const nodesToMove = [];
@@ -2328,7 +2340,8 @@ const useEditorStore = create((set, get) => ({
2328
2340
  id: p.id,
2329
2341
  name: p.name,
2330
2342
  children,
2331
- settings: { backgroundColor: "#ffffff", ...p.settings }
2343
+ settings: { backgroundColor: "#ffffff", ...p.settings },
2344
+ ...p.boundRepeatablePageId ? { boundRepeatablePageId: p.boundRepeatablePageId } : {}
2332
2345
  };
2333
2346
  });
2334
2347
  pagesWithPositions = pagesWithPositions.map((p) => {
@@ -8766,13 +8779,14 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8766
8779
  const labelPrefix = rawPrefix.startsWith("field_") ? rawPrefix : `field_${rawPrefix}`;
8767
8780
  const treeNodeId = repeatableNodeMap == null ? void 0 : repeatableNodeMap.get(def.label.trim().toLowerCase());
8768
8781
  const prefix = treeNodeId ? `field_${treeNodeId}` : labelPrefix;
8782
+ const minEntries = def.minEntries != null ? Math.max(0, def.minEntries) : 1;
8769
8783
  const section = {
8770
8784
  id: def.id,
8771
8785
  label: def.label,
8772
8786
  order: def.order ?? 0,
8773
8787
  type: "repeatable",
8774
8788
  templateKeyPrefix: prefix,
8775
- minEntries: def.minEntries ?? 1,
8789
+ minEntries,
8776
8790
  maxEntries: def.maxEntries,
8777
8791
  entryFields: def.fields.map((f, i) => ({
8778
8792
  key: f.key,
@@ -8782,7 +8796,8 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8782
8796
  templateKey: f.key,
8783
8797
  placeholder: f.placeholder
8784
8798
  })),
8785
- initialEntryCount: def.minEntries ?? 1,
8799
+ // Honor minEntries: 0 — start with no entries, user adds via "+ Add" button.
8800
+ initialEntryCount: minEntries,
8786
8801
  parentId
8787
8802
  };
8788
8803
  if (treeNodeId) section.treeNodeId = treeNodeId;
@@ -9338,11 +9353,11 @@ function idPrefix(node) {
9338
9353
  if (t === "shape") return "shape";
9339
9354
  return "el";
9340
9355
  }
9341
- function cloneNodeWithNewIds$1(node) {
9356
+ function cloneNodeWithNewIds$1(node, cloneSuffix) {
9342
9357
  const oldToNew = /* @__PURE__ */ new Map();
9343
9358
  function clone(n) {
9344
9359
  var _a;
9345
- const newId = generateId(idPrefix(n));
9360
+ const newId = cloneSuffix ? `${baseId(n.id)}__c${cloneSuffix}` : generateId(idPrefix(n));
9346
9361
  oldToNew.set(n.id, newId);
9347
9362
  const base = baseId(n.id);
9348
9363
  const withMeta = { ...n, id: newId, __baseNodeId: base, __sourceId: n.id };
@@ -9508,12 +9523,12 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
9508
9523
  }
9509
9524
  return Math.max(1, maxIndex);
9510
9525
  }
9511
- function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap) {
9526
+ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
9512
9527
  var _a, _b, _c, _d, _e;
9513
9528
  const cloned = JSON.parse(JSON.stringify(config));
9514
9529
  if (!cloned.pages) return cloned;
9515
9530
  const dynamicFields = cloned.dynamicFields;
9516
- const pages = cloned.pages;
9531
+ let pages = cloned.pages;
9517
9532
  const repeatableList = (repeatableSectionsFromSchema == null ? void 0 : repeatableSectionsFromSchema.length) ? repeatableSectionsFromSchema : getRepeatableFromConfig(pages);
9518
9533
  const nodeIds = repeatableList.map((r) => r.nodeId);
9519
9534
  const entryCountFromList = (baseNodeId) => {
@@ -9556,7 +9571,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9556
9571
  const N = Math.max(1, countFromState ?? getRepeatableEntryCount(baseNodeId, formValues));
9557
9572
  const clones = [];
9558
9573
  for (let i = 1; i <= N; i++) {
9559
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9574
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${baseNodeId}_e${i}`);
9560
9575
  delete clone.repeatableSection;
9561
9576
  clones.push(clone);
9562
9577
  const newToOriginal = /* @__PURE__ */ new Map();
@@ -9592,7 +9607,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9592
9607
  const phase1Map = phase1NewToOriginal.get(`${parentBaseNodeId}_${parentIndex1Based}`);
9593
9608
  const clones = [];
9594
9609
  for (let i = 1; i <= N; i++) {
9595
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9610
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${parentBaseNodeId}_p${parentIndex1Based}_${baseNodeId}_e${i}`);
9596
9611
  delete clone.repeatableSection;
9597
9612
  clones.push(clone);
9598
9613
  for (const [oldId, newId] of oldToNew) {
@@ -9806,9 +9821,20 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9806
9821
  }
9807
9822
  }
9808
9823
  }
9824
+ const repeatablePagePrefixSet = new Set(
9825
+ [].map((p) => `field_${p.templateKeyPrefix}_`)
9826
+ );
9809
9827
  if (dynamicFields == null ? void 0 : dynamicFields.length) {
9810
9828
  for (const key of Object.keys(formValues)) {
9811
9829
  if (NESTED_REPEATABLE_KEY_REGEX.test(key)) continue;
9830
+ let isPageKey = false;
9831
+ for (const pfx of repeatablePagePrefixSet) {
9832
+ if (key.startsWith(pfx)) {
9833
+ isPageKey = true;
9834
+ break;
9835
+ }
9836
+ }
9837
+ if (isPageKey) continue;
9812
9838
  const match = key.match(REPEATABLE_KEY_REGEX);
9813
9839
  if (!match) continue;
9814
9840
  const [, nodeId, indexStr, fieldId] = match;
@@ -10715,6 +10741,7 @@ function PixldocsPreview(props) {
10715
10741
  const [resolvedConfig, setResolvedConfig] = useState(null);
10716
10742
  const [isLoading, setIsLoading] = useState(false);
10717
10743
  const [fontsReady, setFontsReady] = useState(false);
10744
+ const [fontsReadyVersion, setFontsReadyVersion] = useState(0);
10718
10745
  const isResolveMode = !("config" in props && props.config);
10719
10746
  useEffect(() => {
10720
10747
  if (!isResolveMode) {
@@ -10725,6 +10752,7 @@ function PixldocsPreview(props) {
10725
10752
  if (!p.templateId || !p.formSchemaId || !p.supabaseUrl || !p.supabaseAnonKey) return;
10726
10753
  let cancelled = false;
10727
10754
  setIsLoading(true);
10755
+ setFontsReady(false);
10728
10756
  resolveFromForm({
10729
10757
  templateId: p.templateId,
10730
10758
  formSchemaId: p.formSchemaId,
@@ -10766,7 +10794,28 @@ function PixldocsPreview(props) {
10766
10794
  ]);
10767
10795
  const config = isResolveMode ? resolvedConfig : props.config;
10768
10796
  useEffect(() => {
10769
- if (isResolveMode || !config) return;
10797
+ var _a, _b;
10798
+ if (!config) return;
10799
+ let cancelled = false;
10800
+ const bump = () => {
10801
+ if (cancelled) return;
10802
+ clearMeasurementCache();
10803
+ setFontsReadyVersion((v) => v + 1);
10804
+ };
10805
+ (_b = (_a = document.fonts) == null ? void 0 : _a.ready) == null ? void 0 : _b.then(bump);
10806
+ const timeoutId = window.setTimeout(bump, 350);
10807
+ return () => {
10808
+ cancelled = true;
10809
+ window.clearTimeout(timeoutId);
10810
+ };
10811
+ }, [config]);
10812
+ const previewKey = useMemo(() => `${pageIndex}-${fontsReadyVersion}`, [pageIndex, fontsReadyVersion]);
10813
+ useEffect(() => {
10814
+ if (isResolveMode) return;
10815
+ if (!config) {
10816
+ setFontsReady(false);
10817
+ return;
10818
+ }
10770
10819
  setFontsReady(false);
10771
10820
  ensureFontsForResolvedConfig(config).then(() => setFontsReady(true)).catch(() => setFontsReady(true));
10772
10821
  }, [isResolveMode, config]);
@@ -10774,6 +10823,9 @@ function PixldocsPreview(props) {
10774
10823
  return /* @__PURE__ */ jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10775
10824
  }
10776
10825
  if (!config) return null;
10826
+ if (!fontsReady) {
10827
+ return /* @__PURE__ */ jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10828
+ }
10777
10829
  return /* @__PURE__ */ jsx("div", { className, style, children: /* @__PURE__ */ jsx(
10778
10830
  PreviewCanvas,
10779
10831
  {
@@ -10783,7 +10835,8 @@ function PixldocsPreview(props) {
10783
10835
  absoluteZoom,
10784
10836
  onDynamicFieldClick,
10785
10837
  onReady
10786
- }
10838
+ },
10839
+ previewKey
10787
10840
  ) });
10788
10841
  }
10789
10842
  class PixldocsRenderer {