@pixldocs/canvas-renderer 0.5.179 → 0.5.180

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/README.md CHANGED
@@ -206,6 +206,48 @@ const result = await renderer.renderPdf(templateConfig, { title: 'My Doc' });
206
206
 
207
207
  ## API Reference
208
208
 
209
+ ### Form schema shape (V2 `sectionState`)
210
+
211
+ `sectionState` accepts entries for both **repeatable sections** and
212
+ **repeatable pages**. Both look the same on the wire — an array of entry
213
+ objects keyed by the section/page `id`:
214
+
215
+ ```ts
216
+ sectionState = {
217
+ // single section
218
+ personal: { name: 'Jane' },
219
+
220
+ // repeatable section (e.g. Experience)
221
+ experience: [
222
+ { title: 'Designer', company: 'Acme' },
223
+ { title: 'Lead', company: 'Beta' },
224
+ ],
225
+
226
+ // repeatable PAGE (e.g. Photo Page) — same shape as above, but at render
227
+ // time the bound template page is cloned once per entry.
228
+ photo_page: [
229
+ { caption: 'Cover' },
230
+ { caption: 'Back' },
231
+ ],
232
+ }
233
+ ```
234
+
235
+ Fetch the schema from the Pixldocs form API to discover what keys/fields are
236
+ expected. The response exposes repeatable pages as a dedicated
237
+ `schema.repeatablePages[]` array (each entry mirrors a repeatable section:
238
+ `id`, `label`, `order`, `templateKeyPrefix`, `minEntries`, `maxEntries`,
239
+ `entryFields`).
240
+
241
+ ```ts
242
+ const res = await fetch(
243
+ `${SUPABASE_URL}/functions/v1/form-api?form_schema_id=${SCHEMA_ID}&action=schema`,
244
+ { headers: { apikey: SUPABASE_ANON_KEY } },
245
+ ).then((r) => r.json());
246
+
247
+ res.schema.sections // single + repeatable sections
248
+ res.schema.repeatablePages // repeatable pages (clone-per-entry)
249
+ ```
250
+
209
251
  ### `PixldocsPreview` (React Component)
210
252
 
211
253
  | Prop | Type | Default | Description |
@@ -484,9 +484,19 @@ function resolveStackGroupEffectivePositions(group, pageChildren, options) {
484
484
  const gap = group.stackSpacing ?? 8;
485
485
  const padTop = group.paddingTop ?? 0;
486
486
  const padLeft = group.paddingLeft ?? 0;
487
+ const padRight = group.paddingRight ?? 0;
488
+ const padBottom = group.paddingBottom ?? 0;
489
+ const justify = group.justifyContent ?? "start";
490
+ const align = group.alignItems ?? "start";
491
+ const isVertical = isVerticalStackLayoutMode(mode);
487
492
  const kids = group.children ?? [];
488
493
  const out = /* @__PURE__ */ new Map();
489
- if (isVerticalStackLayoutMode(mode)) {
494
+ const sizes = /* @__PURE__ */ new Map();
495
+ for (const c of kids) {
496
+ const b = getNodeBounds(c, pageChildren);
497
+ sizes.set(c.id, { width: b.width, height: b.height });
498
+ }
499
+ if (isVertical) {
490
500
  let prevBottom = padTop;
491
501
  let firstSeen = false;
492
502
  for (let i = 0; i < kids.length; i++) {
@@ -498,7 +508,7 @@ function resolveStackGroupEffectivePositions(group, pageChildren, options) {
498
508
  const effectiveTop = !firstSeen ? padTop + storedTop + mTop : prevBottom + gap + storedTop + mTop;
499
509
  firstSeen = true;
500
510
  out.set(child.id, { top: effectiveTop, left: padLeft + storedLeft + mLeft });
501
- const h = getNodeBounds(child, pageChildren).height;
511
+ const h = sizes.get(child.id).height;
502
512
  prevBottom = effectiveTop + h + (child.marginBottom ?? 0);
503
513
  }
504
514
  } else {
@@ -513,10 +523,99 @@ function resolveStackGroupEffectivePositions(group, pageChildren, options) {
513
523
  const effectiveLeft = !firstSeen ? padLeft + storedLeft + mLeft : prevRight + gap + storedLeft + mLeft;
514
524
  firstSeen = true;
515
525
  out.set(child.id, { top: padTop + storedTop + mTop, left: effectiveLeft });
516
- const w = getNodeBounds(child, pageChildren).width;
526
+ const w = sizes.get(child.id).width;
517
527
  prevRight = effectiveLeft + w + (child.marginRight ?? 0);
518
528
  }
519
529
  }
530
+ const containerW = typeof group.width === "number" ? group.width : void 0;
531
+ const containerH = typeof group.height === "number" ? group.height : void 0;
532
+ const mainContainer = isVertical ? containerH : containerW;
533
+ const crossContainer = isVertical ? containerW : containerH;
534
+ if (align !== "start" && crossContainer != null && kids.length > 0) {
535
+ const crossPad0 = isVertical ? padLeft : padTop;
536
+ const crossPadEnd = isVertical ? padRight : padBottom;
537
+ const innerCross = Math.max(0, crossContainer - crossPad0 - crossPadEnd);
538
+ for (const child of kids) {
539
+ const pos = out.get(child.id);
540
+ if (!pos) continue;
541
+ const sz = sizes.get(child.id);
542
+ const childCross = isVertical ? sz.width : sz.height;
543
+ let crossPos;
544
+ if (align === "stretch") {
545
+ crossPos = crossPad0;
546
+ } else if (align === "center") {
547
+ crossPos = crossPad0 + (innerCross - childCross) / 2;
548
+ } else {
549
+ crossPos = crossPad0 + (innerCross - childCross);
550
+ }
551
+ if (isVertical) {
552
+ out.set(child.id, { top: pos.top, left: crossPos });
553
+ } else {
554
+ out.set(child.id, { top: crossPos, left: pos.left });
555
+ }
556
+ }
557
+ }
558
+ if (justify !== "start" && mainContainer != null && kids.length > 0) {
559
+ const mainPad0 = isVertical ? padTop : padLeft;
560
+ const mainPadEnd = isVertical ? padBottom : padRight;
561
+ const innerMain = Math.max(0, mainContainer - mainPad0 - mainPadEnd);
562
+ let totalMain = 0;
563
+ for (let i = 0; i < kids.length; i++) {
564
+ const child = kids[i];
565
+ const sz = sizes.get(child.id);
566
+ totalMain += isVertical ? sz.height : sz.width;
567
+ const marginStart = isVertical ? child.marginTop ?? 0 : child.marginLeft ?? 0;
568
+ const marginEnd = isVertical ? child.marginBottom ?? 0 : child.marginRight ?? 0;
569
+ totalMain += marginStart + marginEnd;
570
+ }
571
+ const baseGapTotal = gap * Math.max(0, kids.length - 1);
572
+ const free = innerMain - totalMain - baseGapTotal;
573
+ let offset = 0;
574
+ let extraGap = 0;
575
+ if (free > 0) {
576
+ switch (justify) {
577
+ case "center":
578
+ offset = free / 2;
579
+ break;
580
+ case "end":
581
+ offset = free;
582
+ break;
583
+ case "space-between":
584
+ if (kids.length > 1) extraGap = free / (kids.length - 1);
585
+ else offset = free / 2;
586
+ break;
587
+ case "space-around":
588
+ if (kids.length > 0) {
589
+ extraGap = free / kids.length;
590
+ offset = extraGap / 2;
591
+ }
592
+ break;
593
+ case "space-evenly":
594
+ extraGap = free / (kids.length + 1);
595
+ offset = extraGap;
596
+ break;
597
+ }
598
+ }
599
+ if (offset !== 0 || extraGap !== 0) {
600
+ let cursor = mainPad0 + offset;
601
+ for (let i = 0; i < kids.length; i++) {
602
+ const child = kids[i];
603
+ const sz = sizes.get(child.id);
604
+ const marginStart = isVertical ? child.marginTop ?? 0 : child.marginLeft ?? 0;
605
+ const marginEnd = isVertical ? child.marginBottom ?? 0 : child.marginRight ?? 0;
606
+ const main = isVertical ? sz.height : sz.width;
607
+ const pos = out.get(child.id);
608
+ if (!pos) continue;
609
+ const startMain = cursor + marginStart;
610
+ if (isVertical) {
611
+ out.set(child.id, { top: startMain, left: pos.left });
612
+ } else {
613
+ out.set(child.id, { top: pos.top, left: startMain });
614
+ }
615
+ cursor = startMain + main + marginEnd + gap + extraGap;
616
+ }
617
+ }
618
+ }
520
619
  return out;
521
620
  }
522
621
  function groupBoundsFromChildren(group, pageChildren, options) {
@@ -7617,7 +7716,28 @@ const PageCanvas = react.forwardRef(
7617
7716
  const element = id ? elementById.get(id) : void 0;
7618
7717
  if (!element) return;
7619
7718
  if (element.overflowPolicy === "auto-shrink") {
7719
+ try {
7720
+ const measured = createText(element);
7721
+ const newFontSize = measured.fontSize;
7722
+ const newWidth = measured.width;
7723
+ const currentFontSize = obj.fontSize;
7724
+ if (typeof newFontSize === "number" && newFontSize > 0 && newFontSize !== currentFontSize) {
7725
+ obj.set({ fontSize: newFontSize });
7726
+ if (typeof newWidth === "number" && newWidth > 0) {
7727
+ obj.width = newWidth;
7728
+ }
7729
+ obj.initDimensions();
7730
+ obj.setCoords();
7731
+ didReflow = true;
7732
+ }
7733
+ } catch {
7734
+ }
7620
7735
  obj.dirty = true;
7736
+ try {
7737
+ obj._forceClearCache = true;
7738
+ if (typeof obj._clearCache === "function") obj._clearCache();
7739
+ } catch {
7740
+ }
7621
7741
  return;
7622
7742
  }
7623
7743
  const targetWidth = Math.max(1, Number(element.width) > 0 ? Number(element.width) : Number(obj.width ?? 200));
@@ -9230,9 +9350,26 @@ const PageCanvas = react.forwardRef(
9230
9350
  const needsCropGroupFadeUpdate = isCropGroup2 && fadeKeyChanged;
9231
9351
  const hadUrlBefore = storedImageUrl && String(storedImageUrl).trim() !== "";
9232
9352
  if (!hasUrl && hadUrlBefore) {
9233
- const placeholder = isCropGroup2 ? createImagePlaceholderForGroup(element) : createImagePlaceholder(element);
9353
+ const resolvedSizeImg = (pageChildren == null ? void 0 : pageChildren.length) ? getNodeBounds(element, pageChildren) : { width: typeof element.width === "number" ? element.width : 200, height: typeof element.height === "number" ? element.height : 50 };
9354
+ const hasExplicitSize = typeof element.width === "number" && Number.isFinite(element.width) && element.width > 0 && typeof element.height === "number" && Number.isFinite(element.height) && element.height > 0;
9355
+ const minVisiblePlaceholder = hasExplicitSize ? 1 : 20;
9356
+ const nextWidth = Math.max(minVisiblePlaceholder, Number(resolvedSizeImg.width) || 200);
9357
+ const nextHeight = Math.max(minVisiblePlaceholder, Number(resolvedSizeImg.height) || 50);
9358
+ const storePosImg = pageChildren ? (() => {
9359
+ const node = findNodeById(pageChildren, element.id);
9360
+ return node ? getAbsoluteBounds(node, pageChildren) : { left: element.left ?? 0, top: element.top ?? 0 };
9361
+ })() : { left: element.left ?? 0, top: element.top ?? 0 };
9362
+ const elementForPlaceholder = { ...element, width: nextWidth, height: nextHeight };
9363
+ const placeholder = isCropGroup2 ? createImagePlaceholderForGroup(elementForPlaceholder) : createImagePlaceholder(elementForPlaceholder);
9234
9364
  setObjectData(placeholder, element.id);
9235
9365
  placeholder.__imageSrc = "";
9366
+ placeholder.set({
9367
+ left: storePosImg.left + nextWidth / 2,
9368
+ top: storePosImg.top + nextHeight / 2,
9369
+ originX: "center",
9370
+ originY: "center"
9371
+ });
9372
+ placeholder.setCoords();
9236
9373
  const idx = fc.getObjects().indexOf(existingObj);
9237
9374
  fc.remove(existingObj);
9238
9375
  if (idx >= 0) {
@@ -11756,7 +11893,8 @@ function formDefSectionsToInferred(schemaSections, repeatableNodeMap) {
11756
11893
  // Honor minEntries: 0 — start with no entries, user adds via "+ Add" button.
11757
11894
  initialEntryCount: minEntries,
11758
11895
  parentId,
11759
- entryNameFieldKey: def.entryNameFieldKey
11896
+ entryNameFieldKey: def.entryNameFieldKey,
11897
+ isRepeatablePage: def.isRepeatablePage
11760
11898
  };
11761
11899
  if (treeNodeId) section.treeNodeId = treeNodeId;
11762
11900
  sections.push(section);
@@ -16360,9 +16498,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16360
16498
  }
16361
16499
  return svgString;
16362
16500
  }
16363
- const resolvedPackageVersion = "0.5.179";
16501
+ const resolvedPackageVersion = "0.5.180";
16364
16502
  const PACKAGE_VERSION = resolvedPackageVersion;
16365
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.179";
16503
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.180";
16366
16504
  const roundParityValue = (value) => {
16367
16505
  if (typeof value !== "number") return value;
16368
16506
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16864,7 +17002,7 @@ class PixldocsRenderer {
16864
17002
  await this.waitForCanvasScene(container, cloned, i);
16865
17003
  }
16866
17004
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
16867
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Be8MJ_2c.cjs"));
17005
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-B8qiTpn8.cjs"));
16868
17006
  const prepared = preparePagesForExport(
16869
17007
  cloned.pages,
16870
17008
  canvasWidth,
@@ -19009,7 +19147,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19009
19147
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19010
19148
  sanitizeSvgTreeForPdf(svgToDraw);
19011
19149
  try {
19012
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-Be8MJ_2c.cjs"));
19150
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-B8qiTpn8.cjs"));
19013
19151
  try {
19014
19152
  await logTextMeasurementDiagnostic(svgToDraw);
19015
19153
  } catch {
@@ -19402,4 +19540,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
19402
19540
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
19403
19541
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
19404
19542
  exports.warmTemplateFromForm = warmTemplateFromForm;
19405
- //# sourceMappingURL=index-BBOaToIA.cjs.map
19543
+ //# sourceMappingURL=index-B9NOXCTL.cjs.map