@pixldocs/canvas-renderer 0.4.5 → 0.4.7

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.d.ts CHANGED
@@ -482,6 +482,8 @@ export declare interface TemplateConfigPage {
482
482
  name: string;
483
483
  children: CanvasNode[];
484
484
  settings?: PageSettings;
485
+ /** When set, the page is bound to a `repeatablePage` in the form schema and is cloned per entry at render time. */
486
+ boundRepeatablePageId?: string;
485
487
  }
486
488
 
487
489
  export declare interface ThemeConfig {
package/dist/index.js CHANGED
@@ -599,6 +599,42 @@ function applyStackReflowToPageTree(children) {
599
599
  function constrainChildrenToGroupBounds(_group, _pageChildren) {
600
600
  return /* @__PURE__ */ new Map();
601
601
  }
602
+ function stripCrossTemplateMetadata(node) {
603
+ const cleaned = { ...node };
604
+ if (cleaned.repeatableSection && typeof cleaned.repeatableSection === "object") {
605
+ const { formDefId: _f, formDefSectionPrefix: _p, ...keep } = cleaned.repeatableSection;
606
+ cleaned.repeatableSection = keep;
607
+ }
608
+ return cleaned;
609
+ }
610
+ function reIdSubtree(nodes) {
611
+ const idMap = /* @__PURE__ */ new Map();
612
+ idMap.set(PAGE_BACKGROUND_ELEMENT_ID, PAGE_BACKGROUND_ELEMENT_ID);
613
+ const cloneOne = (node) => {
614
+ if (isGroup(node)) {
615
+ const g = node;
616
+ const newId2 = generateId("group");
617
+ idMap.set(g.id, newId2);
618
+ return {
619
+ ...stripCrossTemplateMetadata(g),
620
+ id: newId2,
621
+ children: (g.children ?? []).map(cloneOne)
622
+ };
623
+ }
624
+ const prefix = node.type === "text" ? "text" : node.type === "image" ? "img" : node.type === "shape" ? "shape" : node.type === "line" ? "line" : "el";
625
+ const newId = generateId(prefix);
626
+ idMap.set(node.id, newId);
627
+ return {
628
+ ...stripCrossTemplateMetadata(node),
629
+ id: newId
630
+ };
631
+ };
632
+ return { nodes: nodes.map(cloneOne), idMap };
633
+ }
634
+ function rewriteFieldMappings(mappings, idMap) {
635
+ if (!(mappings == null ? void 0 : mappings.length)) return [];
636
+ return mappings.filter((m) => idMap.has(m.elementId)).map((m) => ({ ...m, elementId: idMap.get(m.elementId) }));
637
+ }
602
638
  const defaultProjectSettings = {
603
639
  showGrid: false,
604
640
  snapToGrid: false,
@@ -1953,16 +1989,48 @@ const useEditorStore = create((set, get) => ({
1953
1989
  return { canvas: nextCanvas, ...committed };
1954
1990
  }),
1955
1991
  deletePage: (pageId) => set((state) => {
1992
+ var _a;
1956
1993
  if (state.canvas.pages.length <= 1) return {};
1994
+ const deletedPage = state.canvas.pages.find((p) => p.id === pageId);
1957
1995
  const newPages = state.canvas.pages.filter((p) => p.id !== pageId);
1958
1996
  const currentPageDeleted = state.canvas.currentPageId === pageId;
1959
1997
  const newCurrentPageId = currentPageDeleted ? newPages[0].id : state.canvas.currentPageId;
1960
- const nextCanvas = {
1998
+ const deletedElementIds = new Set(getAllElementIds((deletedPage == null ? void 0 : deletedPage.children) ?? []));
1999
+ const remainingElementIds = /* @__PURE__ */ new Set();
2000
+ for (const p of newPages) for (const id of getAllElementIds(p.children ?? [])) remainingElementIds.add(id);
2001
+ for (const id of remainingElementIds) deletedElementIds.delete(id);
2002
+ let nextCanvas = {
1961
2003
  ...state.canvas,
1962
2004
  pages: newPages,
1963
2005
  currentPageId: newCurrentPageId,
1964
2006
  selectedIds: currentPageDeleted ? [] : state.canvas.selectedIds
1965
2007
  };
2008
+ if (nextCanvas.themeConfig && deletedElementIds.size > 0) {
2009
+ const remainingProps = nextCanvas.themeConfig.properties.filter((p) => !deletedElementIds.has(p.elementId));
2010
+ if (remainingProps.length !== nextCanvas.themeConfig.properties.length) {
2011
+ const removedPropIds = new Set(
2012
+ nextCanvas.themeConfig.properties.filter((p) => deletedElementIds.has(p.elementId)).map((p) => p.id)
2013
+ );
2014
+ nextCanvas = {
2015
+ ...nextCanvas,
2016
+ themeConfig: {
2017
+ ...nextCanvas.themeConfig,
2018
+ properties: remainingProps,
2019
+ variants: nextCanvas.themeConfig.variants.map((v) => {
2020
+ const values = { ...v.values };
2021
+ for (const pid of removedPropIds) delete values[pid];
2022
+ return { ...v, values };
2023
+ })
2024
+ }
2025
+ };
2026
+ }
2027
+ }
2028
+ if (((_a = nextCanvas.dynamicFields) == null ? void 0 : _a.length) && deletedElementIds.size > 0) {
2029
+ nextCanvas = {
2030
+ ...nextCanvas,
2031
+ dynamicFields: nextCanvas.dynamicFields.map((f) => ({ ...f, mappings: (f.mappings ?? []).filter((m) => !deletedElementIds.has(m.elementId)) })).filter((f) => (f.mappings ?? []).length > 0)
2032
+ };
2033
+ }
1966
2034
  const committed = commitFromState(state, nextCanvas);
1967
2035
  return { canvas: nextCanvas, ...committed };
1968
2036
  }),
@@ -1989,6 +2057,64 @@ const useEditorStore = create((set, get) => ({
1989
2057
  const committed = commitFromState(state, nextCanvas);
1990
2058
  return { canvas: nextCanvas, ...committed };
1991
2059
  }),
2060
+ pastePage: (payload) => {
2061
+ let resultPageId = null;
2062
+ set((state) => {
2063
+ const { nodes: freshChildren, idMap } = reIdSubtree(payload.page.children ?? []);
2064
+ const freshPage = {
2065
+ id: generateId("page"),
2066
+ name: payload.page.name,
2067
+ children: freshChildren,
2068
+ settings: { ...defaultPageSettings, ...payload.page.settings ?? {} }
2069
+ };
2070
+ const carriedFields = (payload.dynamicFields ?? []).map((f) => ({ ...f, mappings: rewriteFieldMappings(f.mappings, idMap) })).filter((f) => f.mappings.length > 0);
2071
+ const carriedTheme = (payload.themeProperties ?? []).filter((t) => idMap.has(t.property.elementId)).map((t) => ({ ...t, property: { ...t.property, elementId: idMap.get(t.property.elementId) } }));
2072
+ const currentIndex = state.canvas.pages.findIndex((p) => p.id === state.canvas.currentPageId);
2073
+ const insertAt = currentIndex >= 0 ? currentIndex + 1 : state.canvas.pages.length;
2074
+ const newPages = [...state.canvas.pages];
2075
+ newPages.splice(insertAt, 0, freshPage);
2076
+ const existingFields = state.canvas.dynamicFields ?? [];
2077
+ const mergedFields = [...existingFields];
2078
+ const taken = new Set(mergedFields.map((f) => f.id));
2079
+ for (const incoming of carriedFields) {
2080
+ let candidate = incoming.id;
2081
+ let n = 2;
2082
+ while (taken.has(candidate)) {
2083
+ candidate = `${incoming.id}_${n++}`;
2084
+ }
2085
+ taken.add(candidate);
2086
+ mergedFields.push({ ...incoming, id: candidate });
2087
+ }
2088
+ let themeConfig = state.canvas.themeConfig;
2089
+ if (carriedTheme.length > 0) {
2090
+ themeConfig = themeConfig ? {
2091
+ properties: [...themeConfig.properties],
2092
+ variants: themeConfig.variants.map((v) => ({ ...v, values: { ...v.values } }))
2093
+ } : createEmptyThemeConfig();
2094
+ for (const carried of carriedTheme) {
2095
+ const newProp = { ...carried.property, id: generateId("themeprop") };
2096
+ themeConfig.properties.push(newProp);
2097
+ if (carried.defaultValue !== void 0) {
2098
+ for (const v of themeConfig.variants) {
2099
+ v.values[newProp.id] = carried.defaultValue;
2100
+ }
2101
+ }
2102
+ }
2103
+ }
2104
+ const nextCanvas = {
2105
+ ...state.canvas,
2106
+ pages: newPages,
2107
+ currentPageId: freshPage.id,
2108
+ selectedIds: [],
2109
+ dynamicFields: mergedFields,
2110
+ themeConfig
2111
+ };
2112
+ resultPageId = freshPage.id;
2113
+ const committed = commitFromState(state, nextCanvas);
2114
+ return { canvas: nextCanvas, ...committed };
2115
+ });
2116
+ return resultPageId;
2117
+ },
1992
2118
  renamePage: (pageId, name) => set((state) => {
1993
2119
  const trimmedName = name.trim();
1994
2120
  if (!trimmedName) return {};
@@ -9915,17 +10041,16 @@ function splitNestedForOverflow(flowStack, stayChildren, overflowChildren, overf
9915
10041
  }
9916
10042
  return { stayChildren: newStay, overflowChildren: newOverflow };
9917
10043
  }
9918
- function applyContentBoundsPagination(config) {
10044
+ function paginateSinglePage(sourcePage, pageOffsetIndex) {
9919
10045
  var _a, _b, _c, _d, _e, _f;
9920
- const pages = config.pages ?? [];
9921
- if (pages.length === 0) return config;
9922
- const firstPage = pages[0];
9923
- const contentTop = (_a = firstPage.settings) == null ? void 0 : _a.contentTop;
9924
- const contentBottom = (_b = firstPage.settings) == null ? void 0 : _b.contentBottom;
9925
- if (contentTop == null || contentBottom == null || contentBottom <= contentTop) return config;
9926
- const pageChildren = firstPage.children ?? [];
10046
+ const contentTop = (_a = sourcePage.settings) == null ? void 0 : _a.contentTop;
10047
+ const contentBottom = (_b = sourcePage.settings) == null ? void 0 : _b.contentBottom;
10048
+ if (contentTop == null || contentBottom == null || contentBottom <= contentTop) {
10049
+ return [sourcePage];
10050
+ }
10051
+ const pageChildren = sourcePage.children ?? [];
9927
10052
  const allFlowStacks = findAllFlowStacks(pageChildren);
9928
- if (allFlowStacks.length === 0) return config;
10053
+ if (allFlowStacks.length === 0) return [sourcePage];
9929
10054
  const stackResults = [];
9930
10055
  let anyOverflow = false;
9931
10056
  for (const flowStack of allFlowStacks) {
@@ -9942,25 +10067,26 @@ function applyContentBoundsPagination(config) {
9942
10067
  }
9943
10068
  stackResults.push({ flowStack, flowStackIndex, stayChildren, overflowChildren });
9944
10069
  }
9945
- if (!anyOverflow) return config;
10070
+ if (!anyOverflow) return [sourcePage];
9946
10071
  const flowStackIds = new Set(allFlowStacks.map((s) => s.id));
9947
- let newFirstRoot = pageChildren.map((node) => {
10072
+ let newSourceRoot = pageChildren.map((node) => {
9948
10073
  if (!flowStackIds.has(node.id)) return node;
9949
10074
  const result = stackResults.find((r) => r.flowStack.id === node.id);
9950
10075
  const newStack = { ...result.flowStack, children: result.stayChildren };
9951
10076
  return applyStackReflowToPageTree([newStack])[0];
9952
10077
  });
9953
- newFirstRoot = applyStackReflowToPageTree(newFirstRoot);
9954
- const newFirstPage = {
9955
- ...firstPage,
9956
- children: newFirstRoot
10078
+ newSourceRoot = applyStackReflowToPageTree(newSourceRoot);
10079
+ const newSourcePage = {
10080
+ ...sourcePage,
10081
+ children: newSourceRoot
9957
10082
  };
9958
- const resultPages = [newFirstPage];
10083
+ const resultPages = [newSourcePage];
9959
10084
  let currentOverflows = stackResults.filter((r) => r.overflowChildren.length > 0).map((r) => ({ flowStack: r.flowStack, overflowChildren: r.overflowChildren }));
9960
10085
  let currentPageIndex = 1;
9961
10086
  const MAX_CONTINUATION_PAGES = 50;
9962
10087
  while (currentOverflows.length > 0 && currentPageIndex < MAX_CONTINUATION_PAGES) {
9963
- const contPage = buildContinuationPage(firstPage, currentOverflows, contentTop, currentPageIndex);
10088
+ const continuationGlobalIndex = pageOffsetIndex * 1e3 + currentPageIndex;
10089
+ const contPage = buildContinuationPage(sourcePage, currentOverflows, contentTop, continuationGlobalIndex);
9964
10090
  resultPages.push(contPage);
9965
10091
  const contChildren = contPage.children ?? [];
9966
10092
  const contFlowStacks = findAllFlowStacks(contChildren);
@@ -10028,6 +10154,20 @@ function applyContentBoundsPagination(config) {
10028
10154
  if (flowChildCount === 0 && !hasContent) resultPages.pop();
10029
10155
  else break;
10030
10156
  }
10157
+ return resultPages;
10158
+ }
10159
+ function applyContentBoundsPagination(config) {
10160
+ const pages = config.pages ?? [];
10161
+ if (pages.length === 0) return config;
10162
+ const resultPages = [];
10163
+ let mutated = false;
10164
+ for (let i = 0; i < pages.length; i++) {
10165
+ const sourcePage = pages[i];
10166
+ const paginated = paginateSinglePage(sourcePage, i);
10167
+ if (paginated.length !== 1 || paginated[0] !== sourcePage) mutated = true;
10168
+ resultPages.push(...paginated);
10169
+ }
10170
+ if (!mutated) return config;
10031
10171
  return { ...config, pages: resultPages };
10032
10172
  }
10033
10173
  async function fetchRow(supabaseUrl, anonKey, table, id) {
@@ -10332,9 +10472,9 @@ function normalizeConfigForEC2Parity(config) {
10332
10472
  if (isStack && node.stackSpacing == null) node.stackSpacing = 8;
10333
10473
  if (node.type === "text") {
10334
10474
  const overflowPolicy = String(node.overflowPolicy ?? "grow-and-push");
10335
- const fontSize = typeof node.fontSize === "number" ? node.fontSize : 16;
10336
- if (overflowPolicy === "auto-shrink" && fontSize >= 24) node.overflowPolicy = "grow-and-push";
10337
- delete node.height;
10475
+ if (overflowPolicy !== "auto-shrink") {
10476
+ delete node.height;
10477
+ }
10338
10478
  }
10339
10479
  if (Array.isArray(node.children)) {
10340
10480
  delete node.height;