@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.cjs CHANGED
@@ -2203,6 +2203,18 @@ const useEditorStore = zustand.create((set, get) => ({
2203
2203
  const committed = commitFromState(state, nextCanvas);
2204
2204
  return { canvas: nextCanvas, ...committed };
2205
2205
  }),
2206
+ setPageBoundRepeatablePage: (pageId, repeatablePageId) => set((state) => {
2207
+ const updatedPages = state.canvas.pages.map((p) => {
2208
+ if (p.id !== pageId) return p;
2209
+ const next = { ...p };
2210
+ if (repeatablePageId) next.boundRepeatablePageId = repeatablePageId;
2211
+ else delete next.boundRepeatablePageId;
2212
+ return next;
2213
+ });
2214
+ const nextCanvas = { ...state.canvas, pages: updatedPages };
2215
+ const committed = commitFromState(state, nextCanvas);
2216
+ return { canvas: nextCanvas, ...committed };
2217
+ }),
2206
2218
  moveElementsToPage: (elementIds, targetPageId) => set((state) => {
2207
2219
  const currentPage = getCurrentPageFromCanvas(state.canvas);
2208
2220
  const nodesToMove = [];
@@ -2347,7 +2359,8 @@ const useEditorStore = zustand.create((set, get) => ({
2347
2359
  id: p.id,
2348
2360
  name: p.name,
2349
2361
  children,
2350
- settings: { backgroundColor: "#ffffff", ...p.settings }
2362
+ settings: { backgroundColor: "#ffffff", ...p.settings },
2363
+ ...p.boundRepeatablePageId ? { boundRepeatablePageId: p.boundRepeatablePageId } : {}
2351
2364
  };
2352
2365
  });
2353
2366
  pagesWithPositions = pagesWithPositions.map((p) => {
@@ -8785,13 +8798,14 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8785
8798
  const labelPrefix = rawPrefix.startsWith("field_") ? rawPrefix : `field_${rawPrefix}`;
8786
8799
  const treeNodeId = repeatableNodeMap == null ? void 0 : repeatableNodeMap.get(def.label.trim().toLowerCase());
8787
8800
  const prefix = treeNodeId ? `field_${treeNodeId}` : labelPrefix;
8801
+ const minEntries = def.minEntries != null ? Math.max(0, def.minEntries) : 1;
8788
8802
  const section = {
8789
8803
  id: def.id,
8790
8804
  label: def.label,
8791
8805
  order: def.order ?? 0,
8792
8806
  type: "repeatable",
8793
8807
  templateKeyPrefix: prefix,
8794
- minEntries: def.minEntries ?? 1,
8808
+ minEntries,
8795
8809
  maxEntries: def.maxEntries,
8796
8810
  entryFields: def.fields.map((f, i) => ({
8797
8811
  key: f.key,
@@ -8801,7 +8815,8 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
8801
8815
  templateKey: f.key,
8802
8816
  placeholder: f.placeholder
8803
8817
  })),
8804
- initialEntryCount: def.minEntries ?? 1,
8818
+ // Honor minEntries: 0 — start with no entries, user adds via "+ Add" button.
8819
+ initialEntryCount: minEntries,
8805
8820
  parentId
8806
8821
  };
8807
8822
  if (treeNodeId) section.treeNodeId = treeNodeId;
@@ -9357,11 +9372,11 @@ function idPrefix(node) {
9357
9372
  if (t === "shape") return "shape";
9358
9373
  return "el";
9359
9374
  }
9360
- function cloneNodeWithNewIds$1(node) {
9375
+ function cloneNodeWithNewIds$1(node, cloneSuffix) {
9361
9376
  const oldToNew = /* @__PURE__ */ new Map();
9362
9377
  function clone(n) {
9363
9378
  var _a;
9364
- const newId = generateId(idPrefix(n));
9379
+ const newId = cloneSuffix ? `${baseId(n.id)}__c${cloneSuffix}` : generateId(idPrefix(n));
9365
9380
  oldToNew.set(n.id, newId);
9366
9381
  const base = baseId(n.id);
9367
9382
  const withMeta = { ...n, id: newId, __baseNodeId: base, __sourceId: n.id };
@@ -9527,12 +9542,12 @@ function getNestedRepeatableEntryCount(parentId, parentIndex, childId, formValue
9527
9542
  }
9528
9543
  return Math.max(1, maxIndex);
9529
9544
  }
9530
- function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap) {
9545
+ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsFromSchema, repeatableEntryCounts, repeatableNestedEntryCounts, displayFormatMap, repeatablePagesFromSchema) {
9531
9546
  var _a, _b, _c, _d, _e;
9532
9547
  const cloned = JSON.parse(JSON.stringify(config));
9533
9548
  if (!cloned.pages) return cloned;
9534
9549
  const dynamicFields = cloned.dynamicFields;
9535
- const pages = cloned.pages;
9550
+ let pages = cloned.pages;
9536
9551
  const repeatableList = (repeatableSectionsFromSchema == null ? void 0 : repeatableSectionsFromSchema.length) ? repeatableSectionsFromSchema : getRepeatableFromConfig(pages);
9537
9552
  const nodeIds = repeatableList.map((r) => r.nodeId);
9538
9553
  const entryCountFromList = (baseNodeId) => {
@@ -9575,7 +9590,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9575
9590
  const N = Math.max(1, countFromState ?? getRepeatableEntryCount(baseNodeId, formValues));
9576
9591
  const clones = [];
9577
9592
  for (let i = 1; i <= N; i++) {
9578
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9593
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${baseNodeId}_e${i}`);
9579
9594
  delete clone.repeatableSection;
9580
9595
  clones.push(clone);
9581
9596
  const newToOriginal = /* @__PURE__ */ new Map();
@@ -9611,7 +9626,7 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9611
9626
  const phase1Map = phase1NewToOriginal.get(`${parentBaseNodeId}_${parentIndex1Based}`);
9612
9627
  const clones = [];
9613
9628
  for (let i = 1; i <= N; i++) {
9614
- const [clone, oldToNew] = cloneNodeWithNewIds$1(node);
9629
+ const [clone, oldToNew] = cloneNodeWithNewIds$1(node, `${parentBaseNodeId}_p${parentIndex1Based}_${baseNodeId}_e${i}`);
9615
9630
  delete clone.repeatableSection;
9616
9631
  clones.push(clone);
9617
9632
  for (const [oldId, newId] of oldToNew) {
@@ -9825,9 +9840,20 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
9825
9840
  }
9826
9841
  }
9827
9842
  }
9843
+ const repeatablePagePrefixSet = new Set(
9844
+ [].map((p) => `field_${p.templateKeyPrefix}_`)
9845
+ );
9828
9846
  if (dynamicFields == null ? void 0 : dynamicFields.length) {
9829
9847
  for (const key of Object.keys(formValues)) {
9830
9848
  if (NESTED_REPEATABLE_KEY_REGEX.test(key)) continue;
9849
+ let isPageKey = false;
9850
+ for (const pfx of repeatablePagePrefixSet) {
9851
+ if (key.startsWith(pfx)) {
9852
+ isPageKey = true;
9853
+ break;
9854
+ }
9855
+ }
9856
+ if (isPageKey) continue;
9831
9857
  const match = key.match(REPEATABLE_KEY_REGEX);
9832
9858
  if (!match) continue;
9833
9859
  const [, nodeId, indexStr, fieldId] = match;
@@ -10734,6 +10760,7 @@ function PixldocsPreview(props) {
10734
10760
  const [resolvedConfig, setResolvedConfig] = react.useState(null);
10735
10761
  const [isLoading, setIsLoading] = react.useState(false);
10736
10762
  const [fontsReady, setFontsReady] = react.useState(false);
10763
+ const [fontsReadyVersion, setFontsReadyVersion] = react.useState(0);
10737
10764
  const isResolveMode = !("config" in props && props.config);
10738
10765
  react.useEffect(() => {
10739
10766
  if (!isResolveMode) {
@@ -10744,6 +10771,7 @@ function PixldocsPreview(props) {
10744
10771
  if (!p.templateId || !p.formSchemaId || !p.supabaseUrl || !p.supabaseAnonKey) return;
10745
10772
  let cancelled = false;
10746
10773
  setIsLoading(true);
10774
+ setFontsReady(false);
10747
10775
  resolveFromForm({
10748
10776
  templateId: p.templateId,
10749
10777
  formSchemaId: p.formSchemaId,
@@ -10785,7 +10813,28 @@ function PixldocsPreview(props) {
10785
10813
  ]);
10786
10814
  const config = isResolveMode ? resolvedConfig : props.config;
10787
10815
  react.useEffect(() => {
10788
- if (isResolveMode || !config) return;
10816
+ var _a, _b;
10817
+ if (!config) return;
10818
+ let cancelled = false;
10819
+ const bump = () => {
10820
+ if (cancelled) return;
10821
+ clearMeasurementCache();
10822
+ setFontsReadyVersion((v) => v + 1);
10823
+ };
10824
+ (_b = (_a = document.fonts) == null ? void 0 : _a.ready) == null ? void 0 : _b.then(bump);
10825
+ const timeoutId = window.setTimeout(bump, 350);
10826
+ return () => {
10827
+ cancelled = true;
10828
+ window.clearTimeout(timeoutId);
10829
+ };
10830
+ }, [config]);
10831
+ const previewKey = react.useMemo(() => `${pageIndex}-${fontsReadyVersion}`, [pageIndex, fontsReadyVersion]);
10832
+ react.useEffect(() => {
10833
+ if (isResolveMode) return;
10834
+ if (!config) {
10835
+ setFontsReady(false);
10836
+ return;
10837
+ }
10789
10838
  setFontsReady(false);
10790
10839
  ensureFontsForResolvedConfig(config).then(() => setFontsReady(true)).catch(() => setFontsReady(true));
10791
10840
  }, [isResolveMode, config]);
@@ -10793,6 +10842,9 @@ function PixldocsPreview(props) {
10793
10842
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10794
10843
  }
10795
10844
  if (!config) return null;
10845
+ if (!fontsReady) {
10846
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10847
+ }
10796
10848
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children: /* @__PURE__ */ jsxRuntime.jsx(
10797
10849
  PreviewCanvas,
10798
10850
  {
@@ -10802,7 +10854,8 @@ function PixldocsPreview(props) {
10802
10854
  absoluteZoom,
10803
10855
  onDynamicFieldClick,
10804
10856
  onReady
10805
- }
10857
+ },
10858
+ previewKey
10806
10859
  ) });
10807
10860
  }
10808
10861
  class PixldocsRenderer {