@pdfme/schemas 6.2.3 → 6.2.4-dev.2

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
@@ -1,18 +1,16 @@
1
1
  import { D as getBoxContentArea, E as createBoxDimension, L as CODE_BACKGROUND_COLOR, V as DEFAULT_FONT_COLOR, X as PLACEHOLDER_FONT_COLOR, Z as SYNTHETIC_BOLD_CSS_TEXT_SHADOW, a as createListItemSplitRange, c as getListItemRange, g as getFontKitFont, i as TEXT_LINE_SPLIT_UNIT, it as VERTICAL_ALIGN_MIDDLE, l as getTableBodyRange, n as LIST_ITEM_SPLIT_UNIT, o as createTableBodySplitRange, r as TABLE_BODY_SPLIT_UNIT, s as createTextLineSplitRange, t as BUILT_IN_DYNAMIC_LAYOUT_SPLIT_UNITS, u as getTextLineRange, z as DEFAULT_ALIGNMENT } from "./splitRange-B6311Noc.js";
2
2
  import { a as measureTextLines, f as resolveFontVariant, h as parseInlineMarkdown, u as isInlineMarkdownTextSchema } from "./measure-BarlRI1L.js";
3
- import { a as mapVerticalAlignToFlex, c as Formatter, d as embedAndGetFont, i as makeElementPlainTextContentEditable, l as getExtraFormatterSchema, n as textSchema, o as uiRender$4, r as buildStyledTextContainer, s as propPanel$3, t as builtInPlugins, u as pdfRender$4 } from "./builtins-BFhn3tdm.js";
4
- import { a as getCellPropPanelSchema, c as HEX_COLOR_PATTERN, i as getBodyWithSchemaRange, l as OPAQUE_HEX_COLOR_PATTERN, n as getDynamicLayoutForTable, o as getColumnStylesPropPanelSchema, r as getBody, s as getDefaultCellStyles, t as getDynamicHeightsForTable, u as createSingleTable } from "./dynamicTemplate-1cxsTQIG.js";
5
- import { a as createSvgStr, d as rgbColorToCmykColor, f as rotatePoint, g as visitVariables, h as getVariableNames, i as createErrorElm, l as isEditable, m as countUniqueVariableNames, n as applyAlphaToOpacity, p as splitHexAlpha, r as convertForPdfLayoutProps, s as hex2PrintingColor, t as addAlphaToHex, u as readFile } from "./utils-DL6a4DC2.js";
3
+ import { _ as embedAndGetFont, a as mapVerticalAlignToFlex, c as Formatter, d as TextAlignLeftIcon, f as TextAlignRightIcon, g as pdfRender$4, h as TextVerticalAlignTopIcon, i as makeElementPlainTextContentEditable, l as getExtraFormatterSchema, m as TextVerticalAlignMiddleIcon, n as textSchema, o as uiRender$4, p as TextVerticalAlignBottomIcon, r as buildStyledTextContainer, s as propPanel$3, t as builtInPlugins, u as TextAlignCenterIcon } from "./builtins-Ch7UBNGU.js";
4
+ import { C as hashImageDataUrl, S as normalizeImageOrientation, _ as TABLE_CELL_TYPES, a as getCellPropPanelSchema, b as TABLE_IMAGE_DATA_URL_PATTERN, c as HEX_COLOR_PATTERN, d as getTableImageObjectPosition, f as normalizeTableCellType, g as warnInvalidTableImageOnce, h as resolveImageDimension, i as getBodyWithSchemaRange, l as OPAQUE_HEX_COLOR_PATTERN, m as resolveFixedTableImageHeight, n as getDynamicLayoutForTable, o as getDefaultCellStyles, p as normalizeTableImageHeightMode, r as getBody, s as remapColumnStylesOnRemove, t as getDynamicHeightsForTable, u as createSingleTable, v as TABLE_IMAGE_HEIGHT_MODES, x as detectImageFormat, y as DEFAULT_TABLE_IMAGE_HEIGHT_MODE } from "./dynamicTemplate-Bnu4jVaz.js";
5
+ import { a as createSvgStr, d as rgbColorToCmykColor, f as rotatePoint, g as visitVariables, h as getVariableNames, i as createErrorElm, l as isEditable, m as countUniqueVariableNames, n as applyAlphaToOpacity, p as splitHexAlpha, r as convertForPdfLayoutProps, s as hex2PrintingColor, t as addAlphaToHex, u as readFile } from "./utils-BfzehhiZ.js";
6
6
  import { a as normalizeListItems, c as LIST_STYLE_BULLET, i as normalizeListItemEntries, l as LIST_STYLE_ORDERED, n as calculateListLayout, o as serializeListItems, s as DEFAULT_LIST_STYLE, t as getDynamicLayoutForList } from "./dynamicTemplate-CcnCXHfM.js";
7
7
  import { i as validateVariables, n as substituteVariables, r as substituteVariablesAsInlineMarkdownLiterals, t as resolveReadOnlyMultiVariableText } from "./helper-BR4_UZA5.js";
8
8
  import "./tables.js";
9
9
  import "./lists.js";
10
- import { DEFAULT_FONT_NAME, ZOOM, b64toUint8Array, getDefaultFont, getFallbackFontName, getInternalLinkTarget, isHexValid, mm2pt, normalizeLinkHref, px2mm } from "@pdfme/common";
10
+ import { DEFAULT_FONT_NAME, ZOOM, b64toUint8Array, cloneDeep, getDefaultFont, getFallbackFontName, getInternalLinkTarget, isHexValid, mm2pt, normalizeLinkHref, px2mm } from "@pdfme/common";
11
11
  import { Buffer as Buffer$1 } from "buffer";
12
12
  import { popGraphicsState, pushGraphicsState, rotateDegrees, translate } from "@pdfme/pdf-lib";
13
13
  import { Barcode, Calendar, CalendarClock, ChevronDown, Circle, CircleDot, Clock, Image, List, Minus, QrCode, Route, Square, SquareCheck, Table, Type } from "lucide";
14
- import jpegJsModule from "jpeg-js";
15
- import UPNGModule from "@pdf-lib/upng";
16
14
  import DOMPurify from "dompurify";
17
15
  import bwipjs from "bwip-js";
18
16
  import AirDatepicker from "air-datepicker";
@@ -1412,210 +1410,6 @@ var listSchema = {
1412
1410
  icon: createSvgStr(List)
1413
1411
  };
1414
1412
  //#endregion
1415
- //#region src/graphics/orientation.ts
1416
- var isJpegJsApi = (value) => !!value && typeof value === "object" && typeof value.decode === "function" && typeof value.encode === "function";
1417
- var isUPNGApi = (value) => !!value && typeof value === "object" && typeof value.decode === "function" && typeof value.toRGBA8 === "function" && typeof value.encode === "function";
1418
- var resolveExport = (value, guard, label) => {
1419
- let current = value;
1420
- while (current && typeof current === "object") {
1421
- if (guard(current)) return current;
1422
- current = current.default;
1423
- }
1424
- throw new TypeError(`Failed to resolve ${label} exports`);
1425
- };
1426
- var jpegJs = resolveExport(jpegJsModule, isJpegJsApi, "jpeg-js");
1427
- var UPNG = resolveExport(UPNGModule, isUPNGApi, "@pdf-lib/upng");
1428
- var JPEG_ENCODE_QUALITY = 90;
1429
- var JPEG_MAX_MEMORY_MB = 512;
1430
- var JPEG_SOI_0 = 255;
1431
- var JPEG_SOI_1 = 216;
1432
- var PNG_SIGNATURE = new Uint8Array([
1433
- 137,
1434
- 80,
1435
- 78,
1436
- 71,
1437
- 13,
1438
- 10,
1439
- 26,
1440
- 10
1441
- ]);
1442
- var toArrayBuffer = (bytes) => {
1443
- const copy = new Uint8Array(bytes.byteLength);
1444
- copy.set(bytes);
1445
- return copy.buffer;
1446
- };
1447
- var isPngSignature = (bytes) => {
1448
- if (bytes.length < PNG_SIGNATURE.length) return false;
1449
- for (let i = 0; i < PNG_SIGNATURE.length; i++) if (bytes[i] !== PNG_SIGNATURE[i]) return false;
1450
- return true;
1451
- };
1452
- var detectImageFormat = (bytes) => {
1453
- if (bytes.length >= 2 && bytes[0] === JPEG_SOI_0 && bytes[1] === JPEG_SOI_1) return "jpeg";
1454
- if (isPngSignature(bytes)) return "png";
1455
- return "other";
1456
- };
1457
- /** TIFF/IFD0: returns 1..8 or undefined. Never throws. */
1458
- var parseTiffOrientation = (tiff) => {
1459
- try {
1460
- if (tiff.length < 8) return void 0;
1461
- const le = tiff[0] === 73 && tiff[1] === 73;
1462
- if (!le && !(tiff[0] === 77 && tiff[1] === 77)) return void 0;
1463
- const view = new DataView(tiff.buffer, tiff.byteOffset, tiff.byteLength);
1464
- if (view.getUint16(2, le) !== 42) return void 0;
1465
- const ifd = view.getUint32(4, le);
1466
- if (ifd + 2 > tiff.length) return void 0;
1467
- const n = view.getUint16(ifd, le);
1468
- for (let i = 0; i < n; i++) {
1469
- const entry = ifd + 2 + i * 12;
1470
- if (entry + 12 > tiff.length) return void 0;
1471
- if (view.getUint16(entry, le) !== 274) continue;
1472
- if (view.getUint16(entry + 2, le) !== 3) return void 0;
1473
- if (view.getUint32(entry + 4, le) !== 1) return void 0;
1474
- const val = view.getUint16(entry + 8, le);
1475
- return val >= 1 && val <= 8 ? val : void 0;
1476
- }
1477
- return;
1478
- } catch {
1479
- return;
1480
- }
1481
- };
1482
- var isExifApp1Payload = (segment) => segment.length >= 6 && segment[0] === 69 && segment[1] === 120 && segment[2] === 105 && segment[3] === 102 && segment[4] === 0 && segment[5] === 0;
1483
- /** Standalone JPEG markers have no length field (TEM, RST0–7, SOI). */
1484
- var isStandaloneJpegMarker = (marker) => marker === 1 || marker >= 208 && marker <= 216;
1485
- /**
1486
- * JPEG: scan APP1 'Exif\0\0' segments; stop at SOS/EOI.
1487
- * Skips 0xFF fill/padding and standalone markers. If an Exif APP1 has no
1488
- * valid Orientation, keep scanning for a later one.
1489
- */
1490
- var getJpegOrientation = (bytes) => {
1491
- if (bytes.length < 2 || bytes[0] !== JPEG_SOI_0 || bytes[1] !== JPEG_SOI_1) return void 0;
1492
- let pos = 2;
1493
- while (pos < bytes.length) {
1494
- if (bytes[pos] !== 255) return void 0;
1495
- while (pos < bytes.length && bytes[pos] === 255) pos++;
1496
- if (pos >= bytes.length) return void 0;
1497
- const marker = bytes[pos++];
1498
- if (isStandaloneJpegMarker(marker)) continue;
1499
- if (marker === 218 || marker === 217) return void 0;
1500
- if (pos + 2 > bytes.length) return void 0;
1501
- const size = bytes[pos] << 8 | bytes[pos + 1];
1502
- if (size < 2 || pos + size > bytes.length) return void 0;
1503
- if (marker === 225 && size >= 8) {
1504
- const segment = bytes.subarray(pos + 2, pos + size);
1505
- if (isExifApp1Payload(segment)) {
1506
- const orientation = parseTiffOrientation(segment.subarray(6));
1507
- if (orientation !== void 0) return orientation;
1508
- }
1509
- }
1510
- pos += size;
1511
- }
1512
- };
1513
- /**
1514
- * PNG: scan chunks for eXIf (raw TIFF payload). Skip IDAT and other
1515
- * ancillary chunks; eXIf may appear after image data. Stop at IEND.
1516
- */
1517
- var getPngOrientation = (bytes) => {
1518
- if (!isPngSignature(bytes)) return void 0;
1519
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
1520
- let pos = 8;
1521
- while (pos + 12 <= bytes.length) {
1522
- const length = view.getUint32(pos);
1523
- if (pos + 12 + length > bytes.length) return void 0;
1524
- const type = String.fromCharCode(bytes[pos + 4], bytes[pos + 5], bytes[pos + 6], bytes[pos + 7]);
1525
- if (type === "eXIf") return parseTiffOrientation(bytes.subarray(pos + 8, pos + 8 + length));
1526
- if (type === "IEND") return void 0;
1527
- pos += 12 + length;
1528
- }
1529
- };
1530
- var orientationSwapsDimensions = (orientation) => orientation >= 5;
1531
- /**
1532
- * Map source (x, y) into destination (dx, dy) for EXIF orientations 2–8.
1533
- * Orientations 5–8 swap width and height.
1534
- */
1535
- var transformRgba = (src, width, height, orientation) => {
1536
- const swap = orientationSwapsDimensions(orientation);
1537
- const destWidth = swap ? height : width;
1538
- const destHeight = swap ? width : height;
1539
- const dest = new Uint8Array(destWidth * destHeight * 4);
1540
- for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
1541
- let dx = x;
1542
- let dy = y;
1543
- switch (orientation) {
1544
- case 2:
1545
- dx = width - 1 - x;
1546
- dy = y;
1547
- break;
1548
- case 3:
1549
- dx = width - 1 - x;
1550
- dy = height - 1 - y;
1551
- break;
1552
- case 4:
1553
- dx = x;
1554
- dy = height - 1 - y;
1555
- break;
1556
- case 5:
1557
- dx = y;
1558
- dy = x;
1559
- break;
1560
- case 6:
1561
- dx = height - 1 - y;
1562
- dy = x;
1563
- break;
1564
- case 7:
1565
- dx = height - 1 - y;
1566
- dy = width - 1 - x;
1567
- break;
1568
- case 8:
1569
- dx = y;
1570
- dy = width - 1 - x;
1571
- }
1572
- const srcIndex = (y * width + x) * 4;
1573
- const destIndex = (dy * destWidth + dx) * 4;
1574
- dest[destIndex] = src[srcIndex];
1575
- dest[destIndex + 1] = src[srcIndex + 1];
1576
- dest[destIndex + 2] = src[srcIndex + 2];
1577
- dest[destIndex + 3] = src[srcIndex + 3];
1578
- }
1579
- return {
1580
- data: dest,
1581
- width: destWidth,
1582
- height: destHeight
1583
- };
1584
- };
1585
- var bakeJpegOrientation = (bytes) => {
1586
- const orientation = getJpegOrientation(bytes);
1587
- if (!orientation || orientation === 1) return bytes;
1588
- const raw = jpegJs.decode(bytes, {
1589
- useTArray: true,
1590
- formatAsRGBA: true,
1591
- maxMemoryUsageInMB: JPEG_MAX_MEMORY_MB
1592
- });
1593
- const transformed = transformRgba(raw.data, raw.width, raw.height, orientation);
1594
- return new Uint8Array(jpegJs.encode({
1595
- data: transformed.data,
1596
- width: transformed.width,
1597
- height: transformed.height
1598
- }, JPEG_ENCODE_QUALITY).data);
1599
- };
1600
- var bakePngOrientation = (bytes) => {
1601
- const orientation = getPngOrientation(bytes);
1602
- if (!orientation || orientation === 1) return bytes;
1603
- const img = UPNG.decode(toArrayBuffer(bytes));
1604
- const transformed = transformRgba(new Uint8Array(UPNG.toRGBA8(img)[0]), img.width, img.height, orientation);
1605
- return new Uint8Array(UPNG.encode([toArrayBuffer(transformed.data)], transformed.width, transformed.height, 0));
1606
- };
1607
- /**
1608
- * One normalize path for JPEG and PNG. Tag absent/1 is a byte-identical
1609
- * passthrough. Tag 2–8 decodes, transforms pixels, and re-encodes without
1610
- * an orientation tag. May throw on decode/encode failure — callers fall back.
1611
- */
1612
- var normalizeImageOrientation = (bytes) => {
1613
- const kind = detectImageFormat(bytes);
1614
- if (kind === "jpeg") return bakeJpegOrientation(bytes);
1615
- if (kind === "png") return bakePngOrientation(bytes);
1616
- return bytes;
1617
- };
1618
- //#endregion
1619
1413
  //#region src/graphics/image.ts
1620
1414
  /**
1621
1415
  * Build a short fingerprint for a potentially-large base64 image string.
@@ -1632,15 +1426,7 @@ var normalizeImageOrientation = (bytes) => {
1632
1426
  * cost, without retaining any slice of the input as a Map key. Keys stay
1633
1427
  * well under ~40 chars regardless of input size.
1634
1428
  */
1635
- var getCacheKey = (schema, input) => {
1636
- let hash = 2166136261;
1637
- for (let i = 0; i < input.length; i++) {
1638
- hash ^= input.charCodeAt(i);
1639
- hash = Math.imul(hash, 16777619);
1640
- }
1641
- const hex = (hash >>> 0).toString(16).padStart(8, "0");
1642
- return `${schema.type}:${input.length}:${hex}`;
1643
- };
1429
+ var getCacheKey = (schema, input) => `${schema.type}:${input.length}:${hashImageDataUrl(input)}`;
1644
1430
  var dataUrlToBytes = (value) => {
1645
1431
  const idx = value.indexOf(";base64,");
1646
1432
  const base64 = idx >= 0 ? value.slice(idx + 8) : value;
@@ -2408,6 +2194,11 @@ var BARCODE_TYPES = [
2408
2194
  ];
2409
2195
  var DEFAULT_BARCODE_BG_COLOR = "#ffffff";
2410
2196
  var DEFAULT_BARCODE_COLOR = "#000000";
2197
+ var ASPECT_RATIO_LOCKED_BARCODE_TYPES = [
2198
+ "qrcode",
2199
+ "gs1datamatrix",
2200
+ "pdf417"
2201
+ ];
2411
2202
  //#endregion
2412
2203
  //#region src/barcodes/helper.ts
2413
2204
  var validateCheckDigit = (input, checkDigitPos) => {
@@ -2510,17 +2301,67 @@ var renderBarcodeToNodeBuffer = async (options) => {
2510
2301
  if (typeof toBuffer !== "function") throw new Error("[@pdfme/schemas] bwip-js toBuffer() is not available in this environment.");
2511
2302
  return toBuffer(options);
2512
2303
  };
2304
+ var aspectRatioLockedTypes = ASPECT_RATIO_LOCKED_BARCODE_TYPES;
2305
+ var isAspectRatioLockedBarcodeType = (type) => aspectRatioLockedTypes.includes(type);
2306
+ var SQUARE_BARCODE_TYPES = ["qrcode", "gs1datamatrix"];
2307
+ var isSquareBarcodeType = (type) => SQUARE_BARCODE_TYPES.includes(type);
2308
+ var SVG_VIEW_BOX_PATTERN = /viewBox="\s*(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s*"/;
2309
+ var positiveSize = (value) => {
2310
+ const size = Number(value);
2311
+ return Number.isFinite(size) && size > 0 ? size : void 0;
2312
+ };
2313
+ /** Root `viewBox` width/height, then `width`/`height` attributes. `undefined` when neither parses. */
2314
+ var getSvgViewBoxSize = (svg) => {
2315
+ const viewBox = svg.match(SVG_VIEW_BOX_PATTERN);
2316
+ if (viewBox) {
2317
+ const width = positiveSize(viewBox[3]);
2318
+ const height = positiveSize(viewBox[4]);
2319
+ if (width !== void 0 && height !== void 0) return {
2320
+ width,
2321
+ height
2322
+ };
2323
+ }
2324
+ const openTag = svg.match(/<svg\b[^>]*>/)?.[0];
2325
+ if (!openTag) return void 0;
2326
+ const width = positiveSize(openTag.match(/\bwidth="([^"]+)"/)?.[1]);
2327
+ const height = positiveSize(openTag.match(/\bheight="([^"]+)"/)?.[1]);
2328
+ if (width === void 0 || height === void 0) return void 0;
2329
+ return {
2330
+ width,
2331
+ height
2332
+ };
2333
+ };
2334
+ /** Centered contain rect for a natural size; the full box when the natural size is unknown. */
2335
+ var getBarcodeFitLayout = ({ boxWidth, boxHeight, naturalWidth, naturalHeight }) => {
2336
+ const stretch = {
2337
+ width: boxWidth,
2338
+ height: boxHeight,
2339
+ offsetX: 0,
2340
+ offsetY: 0
2341
+ };
2342
+ if (naturalWidth === void 0 || naturalHeight === void 0 || naturalWidth <= 0 || naturalHeight <= 0) return stretch;
2343
+ const ratio = naturalWidth / naturalHeight;
2344
+ const widthLimited = boxWidth / boxHeight <= ratio;
2345
+ const width = widthLimited ? boxWidth : boxHeight * ratio;
2346
+ const height = widthLimited ? boxWidth / ratio : boxHeight;
2347
+ return {
2348
+ width,
2349
+ height,
2350
+ offsetX: (boxWidth - width) / 2,
2351
+ offsetY: (boxHeight - height) / 2
2352
+ };
2353
+ };
2513
2354
  var createBwipJsRenderOptions = (arg) => {
2514
2355
  const { type, input, width, height, backgroundColor, barColor, textColor, includetext = true } = arg;
2515
2356
  const bwipjsArg = {
2516
2357
  bcid: barCodeType2Bcid(type),
2517
2358
  text: input,
2518
- width,
2519
- height,
2520
2359
  scale: 5,
2521
2360
  includetext,
2522
2361
  textxalign: "center"
2523
2362
  };
2363
+ if (width !== void 0) bwipjsArg.width = width;
2364
+ if (height !== void 0) bwipjsArg.height = height;
2524
2365
  if (backgroundColor) bwipjsArg.backgroundcolor = mapHexColorForBwipJsLib(backgroundColor);
2525
2366
  if (barColor) bwipjsArg.barcolor = mapHexColorForBwipJsLib(barColor);
2526
2367
  if (textColor) bwipjsArg.textcolor = mapHexColorForBwipJsLib(textColor);
@@ -2534,6 +2375,39 @@ var createBarCodeSvg = (arg) => {
2534
2375
  if (/<image\b/i.test(svg)) throw new Error(`[@pdfme/schemas] bwip-js emitted an embedded image for ${arg.type}; vector barcode PDF rendering requires path-based SVG.`);
2535
2376
  return svg;
2536
2377
  };
2378
+ /** Natural (unscaled) symbol size from bwip-js, in SVG user units. */
2379
+ var getNaturalBarcodeSize = (schema, input) => getSvgViewBoxSize(createBarCodeSvg({
2380
+ ...schema,
2381
+ width: void 0,
2382
+ height: void 0,
2383
+ backgroundColor: void 0,
2384
+ input
2385
+ }));
2386
+ var contentAspectRatioMemo = /* @__PURE__ */ new Map();
2387
+ var CONTENT_ASPECT_RATIO_MEMO_LIMIT = 200;
2388
+ /**
2389
+ * Natural width / height of a locked symbology; undefined for 1D types. Square types
2390
+ * return 1 even for empty content. The Designer calls this on every width/height/content
2391
+ * commit, so content-dependent ratios are memoized.
2392
+ */
2393
+ var getBarcodeAspectRatio = (schema) => {
2394
+ if (!isAspectRatioLockedBarcodeType(schema.type)) return void 0;
2395
+ if (isSquareBarcodeType(schema.type)) return 1;
2396
+ const input = schema.content ?? "";
2397
+ if (!validateBarcodeInput(schema.type, input)) return void 0;
2398
+ const key = `${schema.type}:${input}`;
2399
+ if (contentAspectRatioMemo.has(key)) return contentAspectRatioMemo.get(key);
2400
+ let ratio;
2401
+ try {
2402
+ const size = getNaturalBarcodeSize(schema, input);
2403
+ ratio = size ? size.width / size.height : void 0;
2404
+ } catch {
2405
+ ratio = void 0;
2406
+ }
2407
+ if (contentAspectRatioMemo.size >= CONTENT_ASPECT_RATIO_MEMO_LIMIT) contentAspectRatioMemo.clear();
2408
+ contentAspectRatioMemo.set(key, ratio);
2409
+ return ratio;
2410
+ };
2537
2411
  var createBarCode = async (arg) => {
2538
2412
  const bwipjsArg = createBwipJsRenderOptions(arg);
2539
2413
  const runtime = resolveBarcodeRenderRuntime();
@@ -2543,8 +2417,20 @@ var createBarCode = async (arg) => {
2543
2417
  };
2544
2418
  //#endregion
2545
2419
  //#region src/barcodes/pdfRender.ts
2546
- var getBarcodeCacheKey = (schema, value) => {
2547
- return `svg:${schema.type}:${schema.width}:${schema.height}:${schema.barColor}:${schema.textColor}:${schema.includetext}:${value}`;
2420
+ var getBarcodeCacheKey = (schema, size, value) => {
2421
+ return `svg:${schema.type}:${size.width}:${size.height}:${schema.barColor}:${schema.textColor}:${schema.includetext}:${value}`;
2422
+ };
2423
+ var getNaturalSize = (schema, value, cache) => {
2424
+ if (!isAspectRatioLockedBarcodeType(schema.type)) return void 0;
2425
+ if (isSquareBarcodeType(schema.type)) return {
2426
+ width: 1,
2427
+ height: 1
2428
+ };
2429
+ const key = `svg-natural:${schema.type}:${value}`;
2430
+ if (cache.has(key)) return cache.get(key);
2431
+ const size = getNaturalBarcodeSize(schema, value);
2432
+ cache.set(key, size);
2433
+ return size;
2548
2434
  };
2549
2435
  var addSvgOpacity = (svg, opacity) => {
2550
2436
  return opacity === void 0 ? svg : svg.replace(/<svg\b/, `<svg opacity="${opacity}"`);
@@ -2558,11 +2444,20 @@ var getSvgColorMapper = (colorType = "") => {
2558
2444
  var pdfRender$1 = async (arg) => {
2559
2445
  const { value, schema, page, options, _cache } = arg;
2560
2446
  if (!validateBarcodeInput(schema.type, value)) return;
2561
- const inputBarcodeCacheKey = getBarcodeCacheKey(schema, value);
2447
+ const natural = getNaturalSize(schema, value, _cache);
2448
+ const fitted = getBarcodeFitLayout({
2449
+ boxWidth: schema.width,
2450
+ boxHeight: schema.height,
2451
+ naturalWidth: natural?.width,
2452
+ naturalHeight: natural?.height
2453
+ });
2454
+ const inputBarcodeCacheKey = getBarcodeCacheKey(schema, fitted, value);
2562
2455
  let svg = _cache.get(inputBarcodeCacheKey);
2563
2456
  if (!svg) {
2564
2457
  svg = createBarCodeSvg({
2565
2458
  ...schema,
2459
+ width: fitted.width,
2460
+ height: fitted.height,
2566
2461
  backgroundColor: void 0,
2567
2462
  type: schema.type,
2568
2463
  input: value
@@ -2596,11 +2491,17 @@ var pdfRender$1 = async (arg) => {
2596
2491
  opacity: applyAlphaToOpacity(opacity, alpha)
2597
2492
  });
2598
2493
  }
2494
+ const layout = getBarcodeFitLayout({
2495
+ boxWidth: width,
2496
+ boxHeight: height,
2497
+ naturalWidth: natural?.width,
2498
+ naturalHeight: natural?.height
2499
+ });
2599
2500
  await page.drawSvg(addSvgOpacity(svg, opacity), {
2600
- x,
2601
- y: y + height,
2602
- width,
2603
- height,
2501
+ x: x + layout.offsetX,
2502
+ y: y + height - layout.offsetY,
2503
+ width: layout.width,
2504
+ height: layout.height,
2604
2505
  mapColor: getSvgColorMapper(options.colorType)
2605
2506
  });
2606
2507
  } finally {
@@ -2764,7 +2665,7 @@ var barcodeDefaults = [
2764
2665
  position,
2765
2666
  ...defaultColors,
2766
2667
  width: 40,
2767
- height: 16,
2668
+ height: 11.65,
2768
2669
  rotate: 0,
2769
2670
  opacity: 1
2770
2671
  } }
@@ -2836,9 +2737,25 @@ var createBarcodeImage = async (schema, value) => {
2836
2737
  return await blobToDataURL(new Blob([new Uint8Array(imageBuf)], { type: "image/png" }));
2837
2738
  };
2838
2739
  var createBarcodeImageElm = async (schema, value) => {
2839
- const barcodeDataURL = await createBarcodeImage(schema, value);
2840
2740
  const img = document.createElement("img");
2841
- img.src = barcodeDataURL;
2741
+ if (isAspectRatioLockedBarcodeType(schema.type)) {
2742
+ const svg = createBarCodeSvg({
2743
+ ...schema,
2744
+ width: void 0,
2745
+ height: void 0,
2746
+ backgroundColor: void 0,
2747
+ input: value
2748
+ });
2749
+ img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
2750
+ const imgStyle = {
2751
+ ...fullSize,
2752
+ objectFit: "contain",
2753
+ borderRadius: 0
2754
+ };
2755
+ Object.assign(img.style, imgStyle);
2756
+ return img;
2757
+ }
2758
+ img.src = await createBarcodeImage(schema, value);
2842
2759
  const imgStyle = {
2843
2760
  ...fullSize,
2844
2761
  borderRadius: 0
@@ -2909,7 +2826,8 @@ var barcodes = BARCODE_TYPES.reduce((acc, type) => Object.assign(acc, { [type]:
2909
2826
  pdf: pdfRender$1,
2910
2827
  ui: uiRender$1,
2911
2828
  propPanel: getPropPanelByBarcodeType(type),
2912
- icon: createSvgStr(type == "qrcode" ? QrCode : Barcode)
2829
+ icon: createSvgStr(type == "qrcode" ? QrCode : Barcode),
2830
+ ...isAspectRatioLockedBarcodeType(type) ? { getAspectRatio: getBarcodeAspectRatio } : {}
2913
2831
  } }), {});
2914
2832
  //#endregion
2915
2833
  //#region src/shapes/line.ts
@@ -3000,7 +2918,154 @@ var lineSchema = {
3000
2918
  icon: createSvgStr(Minus)
3001
2919
  };
3002
2920
  //#endregion
2921
+ //#region src/tables/imageCellUi.ts
2922
+ var buttonSize$1 = 18;
2923
+ var pendingImagePicker = null;
2924
+ var tableSchemaKey = (schema) => typeof schema.id === "string" && schema.id !== "" ? schema.id : schema.name;
2925
+ var noteRenderingTable = (schema) => {
2926
+ const schemaKey = tableSchemaKey(schema);
2927
+ if (pendingImagePicker && pendingImagePicker.schemaKey !== schemaKey) pendingImagePicker = null;
2928
+ };
2929
+ var clearImagePickerRequest = () => {
2930
+ pendingImagePicker = null;
2931
+ };
2932
+ var requestImagePicker = (request) => {
2933
+ pendingImagePicker = request;
2934
+ };
2935
+ var consumePendingImagePicker = (request) => {
2936
+ const pending = pendingImagePicker;
2937
+ if (!pending || pending.rowIndex !== request.rowIndex || pending.colIndex !== request.colIndex || pending.schemaKey !== request.schemaKey) return false;
2938
+ pendingImagePicker = null;
2939
+ return true;
2940
+ };
2941
+ var createImageButton = (options) => {
2942
+ const button = document.createElement("button");
2943
+ button.type = "button";
2944
+ button.innerText = options.text;
2945
+ button.setAttribute("aria-label", options.ariaLabel);
2946
+ button.title = options.ariaLabel;
2947
+ button.style.width = `${buttonSize$1}px`;
2948
+ button.style.height = `${buttonSize$1}px`;
2949
+ button.style.position = "absolute";
2950
+ button.style.top = "0px";
2951
+ button.style.right = options.right;
2952
+ button.style.display = "inline-flex";
2953
+ button.style.alignItems = "center";
2954
+ button.style.justifyContent = "center";
2955
+ button.style.padding = "0";
2956
+ button.style.border = "1px solid #d9d9d9";
2957
+ button.style.borderRadius = "3px";
2958
+ button.style.background = "#ffffff";
2959
+ button.style.color = "#333333";
2960
+ button.style.fontSize = "11px";
2961
+ button.style.lineHeight = "1";
2962
+ button.style.cursor = "pointer";
2963
+ button.style.zIndex = "20";
2964
+ button.addEventListener("mousedown", (event) => {
2965
+ event.preventDefault();
2966
+ });
2967
+ button.addEventListener("click", (event) => {
2968
+ event.preventDefault();
2969
+ event.stopPropagation();
2970
+ options.onClick();
2971
+ });
2972
+ return button;
2973
+ };
2974
+ var appendFrame = (schema) => {
2975
+ const contentArea = getBoxContentArea(schema);
2976
+ const frame = document.createElement("div");
2977
+ frame.style.position = "absolute";
2978
+ frame.style.zIndex = "1";
2979
+ frame.style.width = `${contentArea.width}mm`;
2980
+ frame.style.height = `${contentArea.height}mm`;
2981
+ frame.style.top = `${contentArea.topInset}mm`;
2982
+ frame.style.left = `${contentArea.leftInset}mm`;
2983
+ frame.style.boxSizing = "border-box";
2984
+ return frame;
2985
+ };
2986
+ var warnUnsupportedImage = (columnIndex) => {
2987
+ console.warn(`[@pdfme/schemas/table] unsupported image in column ${columnIndex ?? 0}; only PNG/JPEG data URL is supported`);
2988
+ };
2989
+ var renderTableImageCellUi = (arg) => {
2990
+ const { schema, rootElement, value, onChange, i18n, _cache } = arg;
2991
+ const dimension = value ? resolveImageDimension(value, _cache) : void 0;
2992
+ const frame = appendFrame(schema);
2993
+ if (dimension && value) {
2994
+ const img = document.createElement("img");
2995
+ img.alt = "";
2996
+ img.src = value;
2997
+ img.style.width = "100%";
2998
+ img.style.height = "100%";
2999
+ img.style.objectFit = "contain";
3000
+ img.style.objectPosition = getTableImageObjectPosition(schema.alignment, schema.verticalAlignment);
3001
+ frame.appendChild(img);
3002
+ } else {
3003
+ frame.textContent = i18n("schemas.table.imageCell.placeholder");
3004
+ frame.style.border = "1px dotted #999";
3005
+ frame.style.color = "#999";
3006
+ frame.style.fontSize = "10px";
3007
+ frame.style.display = "flex";
3008
+ frame.style.alignItems = "center";
3009
+ frame.style.justifyContent = "center";
3010
+ frame.style.textAlign = "center";
3011
+ frame.style.overflow = "hidden";
3012
+ frame.style.padding = "1mm";
3013
+ }
3014
+ rootElement.appendChild(frame);
3015
+ const input = document.createElement("input");
3016
+ input.type = "file";
3017
+ input.accept = "image/png, image/jpeg";
3018
+ input.tabIndex = -1;
3019
+ input.style.display = "none";
3020
+ input.addEventListener("click", (event) => {
3021
+ event.stopPropagation();
3022
+ });
3023
+ input.addEventListener("change", () => {
3024
+ if (!input.files || input.files.length === 0) return;
3025
+ readFile(input.files).then((result) => {
3026
+ if (typeof result === "string" && TABLE_IMAGE_DATA_URL_PATTERN.test(result) && resolveImageDimension(result, _cache)) {
3027
+ onChange?.({
3028
+ key: "content",
3029
+ value: result
3030
+ });
3031
+ return;
3032
+ }
3033
+ warnUnsupportedImage(schema.columnIndex);
3034
+ }).catch(() => {
3035
+ warnUnsupportedImage(schema.columnIndex);
3036
+ });
3037
+ });
3038
+ rootElement.appendChild(input);
3039
+ const hasValue = typeof value === "string" && value !== "";
3040
+ rootElement.appendChild(createImageButton({
3041
+ text: "▣",
3042
+ ariaLabel: i18n("schemas.table.imageCell.select"),
3043
+ right: hasValue ? `20px` : "0px",
3044
+ onClick: () => {
3045
+ input.click();
3046
+ }
3047
+ }));
3048
+ if (hasValue) rootElement.appendChild(createImageButton({
3049
+ text: "×",
3050
+ ariaLabel: i18n("schemas.table.imageCell.remove"),
3051
+ right: "0px",
3052
+ onClick: () => {
3053
+ onChange?.({
3054
+ key: "content",
3055
+ value: ""
3056
+ });
3057
+ }
3058
+ }));
3059
+ const { rowIndex, columnIndex, pickerSchemaKey } = schema;
3060
+ if (typeof rowIndex === "number" && typeof columnIndex === "number" && typeof pickerSchemaKey === "string" && consumePendingImagePicker({
3061
+ rowIndex,
3062
+ colIndex: columnIndex,
3063
+ schemaKey: pickerSchemaKey
3064
+ }) && !hasValue) input.click();
3065
+ };
3066
+ //#endregion
3003
3067
  //#region src/tables/cell.ts
3068
+ var imagePdfRender = imageSchema.pdf;
3004
3069
  var linePdfRender = lineSchema.pdf;
3005
3070
  var rectanglePdfRender$1 = rectangle.pdf;
3006
3071
  var renderLine = async (arg, schema, position, width, height) => linePdfRender({
@@ -3072,6 +3137,32 @@ var cellSchema = {
3072
3137
  y: position.y
3073
3138
  }, borderWidth.left, height)
3074
3139
  ]);
3140
+ if (schema.cellType === "image") {
3141
+ if (arg.value && resolveImageDimension(arg.value, arg._cache)) {
3142
+ const imageSchema = {
3143
+ name: schema.name,
3144
+ type: "image",
3145
+ content: arg.value,
3146
+ position: contentArea.position,
3147
+ width: contentArea.width,
3148
+ height: contentArea.height,
3149
+ rotate: 0,
3150
+ opacity: 1,
3151
+ objectFit: "contain",
3152
+ objectPosition: getTableImageObjectPosition(schema.alignment, schema.verticalAlignment)
3153
+ };
3154
+ try {
3155
+ await imagePdfRender({
3156
+ ...arg,
3157
+ value: arg.value,
3158
+ schema: imageSchema
3159
+ });
3160
+ } catch {
3161
+ warnInvalidTableImageOnce(arg.value, schema.columnIndex ?? 0, arg._cache);
3162
+ }
3163
+ }
3164
+ return;
3165
+ }
3075
3166
  await pdfRender$4({
3076
3167
  ...arg,
3077
3168
  schema: {
@@ -3091,19 +3182,35 @@ var cellSchema = {
3091
3182
  const { schema, rootElement } = arg;
3092
3183
  const { borderWidth, width, height, borderColor, backgroundColor } = schema;
3093
3184
  rootElement.style.backgroundColor = backgroundColor;
3094
- const textDiv = createTextDiv(schema);
3095
- await uiRender$4({
3096
- ...arg,
3097
- schema: {
3098
- ...schema,
3099
- backgroundColor: "",
3100
- borderColor: "",
3101
- borderWidth: createBoxDimension(0),
3102
- padding: createBoxDimension(0)
3103
- },
3104
- rootElement: textDiv
3105
- });
3106
- rootElement.appendChild(textDiv);
3185
+ if (schema.cellType === "image") {
3186
+ if (arg.mode === "designer") renderTableImageCellUi(arg);
3187
+ else if (arg.value ? resolveImageDimension(arg.value, arg._cache) : void 0) {
3188
+ const imageFrame = createTextDiv(schema);
3189
+ const img = document.createElement("img");
3190
+ img.alt = "";
3191
+ img.src = arg.value;
3192
+ img.style.width = "100%";
3193
+ img.style.height = "100%";
3194
+ img.style.objectFit = "contain";
3195
+ img.style.objectPosition = getTableImageObjectPosition(schema.alignment, schema.verticalAlignment);
3196
+ imageFrame.appendChild(img);
3197
+ rootElement.appendChild(imageFrame);
3198
+ }
3199
+ } else {
3200
+ const textDiv = createTextDiv(schema);
3201
+ await uiRender$4({
3202
+ ...arg,
3203
+ schema: {
3204
+ ...schema,
3205
+ backgroundColor: "",
3206
+ borderColor: "",
3207
+ borderWidth: createBoxDimension(0),
3208
+ padding: createBoxDimension(0)
3209
+ },
3210
+ rootElement: textDiv
3211
+ });
3212
+ rootElement.appendChild(textDiv);
3213
+ }
3107
3214
  [
3108
3215
  createLineDiv(`${width}mm`, `${borderWidth.top}mm`, "0mm", null, null, "0mm", borderColor),
3109
3216
  createLineDiv(`${width}mm`, `${borderWidth.bottom}mm`, null, null, "0mm", "0mm", borderColor),
@@ -3143,7 +3250,7 @@ var cellSchema = {
3143
3250
  //#region src/tables/pdfRender.ts
3144
3251
  var rectanglePdfRender = rectangle.pdf;
3145
3252
  var cellPdfRender = cellSchema.pdf;
3146
- async function drawCell(arg, cell) {
3253
+ async function drawCell(arg, cell, columnIndex) {
3147
3254
  await cellPdfRender({
3148
3255
  ...arg,
3149
3256
  value: cell.raw,
@@ -3166,7 +3273,9 @@ async function drawCell(arg, cell) {
3166
3273
  fontColor: cell.styles.textColor,
3167
3274
  borderColor: cell.styles.lineColor,
3168
3275
  borderWidth: cell.styles.lineWidth,
3169
- padding: cell.styles.cellPadding
3276
+ padding: cell.styles.cellPadding,
3277
+ cellType: cell.isImage() ? "image" : "text",
3278
+ columnIndex
3170
3279
  }
3171
3280
  });
3172
3281
  }
@@ -3180,7 +3289,7 @@ async function drawRow(arg, table, row, cursor, columns) {
3180
3289
  }
3181
3290
  cell.x = cursor.x;
3182
3291
  cell.y = cursor.y;
3183
- await drawCell(arg, cell);
3292
+ await drawCell(arg, cell, column.index);
3184
3293
  cursor.x += column.width;
3185
3294
  }
3186
3295
  cursor.y += row.height;
@@ -3294,7 +3403,7 @@ var calcResizedHeadWidthPercentages = (arg) => {
3294
3403
  var setBorder = (div, borderPosition, arg) => {
3295
3404
  div.style[`border${borderPosition}`] = `${String(arg.schema.tableStyles.borderWidth)}mm solid ${arg.schema.tableStyles.borderColor}`;
3296
3405
  };
3297
- var drawBorder = (div, row, colIndex, rowIndex, rowsLength, arg) => {
3406
+ var drawBorder = (div, row, colIndex, rowIndex, rowsLength, arg, bodyLength) => {
3298
3407
  const isFirstColumn = colIndex === 0;
3299
3408
  const isLastColumn = colIndex === Object.values(row.cells).length - 1;
3300
3409
  const isLastRow = rowIndex === rowsLength - 1;
@@ -3302,7 +3411,7 @@ var drawBorder = (div, row, colIndex, rowIndex, rowsLength, arg) => {
3302
3411
  setBorder(div, "Top", arg);
3303
3412
  if (isFirstColumn) setBorder(div, "Left", arg);
3304
3413
  if (isLastColumn) setBorder(div, "Right", arg);
3305
- if (JSON.parse(arg.value || "[]").length === 0) setBorder(div, "Bottom", arg);
3414
+ if (bodyLength === 0) setBorder(div, "Bottom", arg);
3306
3415
  } else if (row.section === "body") {
3307
3416
  if (!arg.schema.showHead && rowIndex === 0) setBorder(div, "Top", arg);
3308
3417
  if (isFirstColumn) setBorder(div, "Left", arg);
@@ -3325,20 +3434,28 @@ var renderRowUi = (args) => {
3325
3434
  div.style.width = `${cell.width}mm`;
3326
3435
  div.style.height = `${cell.height}mm`;
3327
3436
  div.style.boxSizing = "border-box";
3328
- drawBorder(div, row, colIndex, rowIndex, rows.length, arg);
3329
- div.style.cursor = arg.mode === "designer" || arg.mode === "form" && section === "body" ? "text" : "default";
3437
+ drawBorder(div, row, colIndex, rowIndex, rows.length, arg, value.length);
3438
+ const imageCellEditable = arg.mode === "designer" || arg.mode === "form" && !arg.schema.readOnly;
3439
+ div.style.cursor = cell.isImage() ? imageCellEditable && section === "body" ? "pointer" : "default" : arg.mode === "designer" || arg.mode === "form" && section === "body" ? "text" : "default";
3330
3440
  div.addEventListener("click", () => {
3331
3441
  if (arg.mode === "viewer") return;
3442
+ const enteringEmptyImageCell = cell.isImage() && section === "body" && imageCellEditable && cell.raw === "" && (editingPosition.rowIndex !== rowIndex || editingPosition.colIndex !== colIndex);
3332
3443
  onChangeEditingPosition({
3333
3444
  rowIndex,
3334
3445
  colIndex
3335
3446
  });
3447
+ if (enteringEmptyImageCell) requestImagePicker({
3448
+ rowIndex,
3449
+ colIndex,
3450
+ schemaKey: tableSchemaKey(arg.schema)
3451
+ });
3336
3452
  });
3337
3453
  arg.rootElement.appendChild(div);
3338
3454
  const isEditing = editingPosition.rowIndex === rowIndex && editingPosition.colIndex === colIndex;
3339
3455
  let mode = "viewer";
3340
3456
  if (arg.mode === "form") mode = section === "body" && isEditing && !arg.schema.readOnly ? "designer" : "viewer";
3341
3457
  else if (arg.mode === "designer") mode = isEditing ? "designer" : "form";
3458
+ if (cell.isImage() && !isEditing) mode = "viewer";
3342
3459
  cellUiRender({
3343
3460
  ...arg,
3344
3461
  stopEditing: () => {
@@ -3377,7 +3494,11 @@ var renderRowUi = (args) => {
3377
3494
  },
3378
3495
  width: cell.width,
3379
3496
  height: cell.height,
3380
- ...convertToCellStyle(cell.styles)
3497
+ ...convertToCellStyle(cell.styles),
3498
+ cellType: cell.isImage() ? "image" : "text",
3499
+ columnIndex: colIndex,
3500
+ rowIndex,
3501
+ pickerSchemaKey: tableSchemaKey(arg.schema)
3381
3502
  }
3382
3503
  });
3383
3504
  colOffsetX += cell.width;
@@ -3398,9 +3519,11 @@ var resetEditingPosition = () => {
3398
3519
  headEditingPosition.colIndex = -1;
3399
3520
  bodyEditingPosition.rowIndex = -1;
3400
3521
  bodyEditingPosition.colIndex = -1;
3522
+ clearImagePickerRequest();
3401
3523
  };
3402
3524
  var uiRender = async (arg) => {
3403
3525
  const { rootElement, onChange, schema, value, mode, scale } = arg;
3526
+ noteRenderingTable(schema);
3404
3527
  const body = getBody(value);
3405
3528
  const bodyRange = getTableBodyRange(schema);
3406
3529
  const bodyWidthRange = getBodyWithSchemaRange(value, schema, bodyRange);
@@ -3526,6 +3649,10 @@ var uiRender = async (arg) => {
3526
3649
  {
3527
3650
  key: "content",
3528
3651
  value: JSON.stringify(bodyWidthRange.map((row) => row.filter((_, j) => j !== i)))
3652
+ },
3653
+ {
3654
+ key: "columnStyles",
3655
+ value: remapColumnStylesOnRemove(schema.columnStyles ?? {}, i)
3529
3656
  }
3530
3657
  ]);
3531
3658
  }
@@ -3604,15 +3731,678 @@ var uiRender = async (arg) => {
3604
3731
  });
3605
3732
  };
3606
3733
  //#endregion
3734
+ //#region src/tables/columnsWidget.ts
3735
+ var cellTypeWarnCache = /* @__PURE__ */ new Map();
3736
+ /** More than this many columns, and each block can collapse. */
3737
+ var COLLAPSE_WHEN_MORE_THAN = 6;
3738
+ var expandedColumnBlocks = /* @__PURE__ */ new Set();
3739
+ var columnBlockKey = (schemaId, columnIndex) => `${schemaId}:${columnIndex}`;
3740
+ var themeRecord = (theme) => theme ?? void 0;
3741
+ var themeToken = (theme, key, fallback) => {
3742
+ const value = themeRecord(theme)?.[key];
3743
+ return typeof value === "string" && value.length > 0 ? value : fallback;
3744
+ };
3745
+ var themeNumber = (theme, key, fallback) => {
3746
+ const value = themeRecord(theme)?.[key];
3747
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
3748
+ };
3749
+ /** antd outlined Select / InputNumber metrics, read from the Designer token when present. */
3750
+ var panelControlMetrics = (theme) => {
3751
+ const lineWidth = themeNumber(theme, "lineWidth", 1);
3752
+ const controlHeight = themeNumber(theme, "controlHeight", 32);
3753
+ const paddingSm = themeNumber(theme, "paddingSM", 12);
3754
+ const controlHeightSm = themeNumber(theme, "controlHeightSM", 24);
3755
+ const fontSize = themeNumber(theme, "fontSize", 14);
3756
+ const outlineWidth = themeNumber(theme, "controlOutlineWidth", 2);
3757
+ return {
3758
+ lineWidth,
3759
+ controlHeight,
3760
+ borderRadius: themeNumber(theme, "borderRadius", 6),
3761
+ fontSize,
3762
+ paddingInline: Math.max(0, paddingSm - lineWidth),
3763
+ handleWidth: Math.max(0, controlHeightSm - lineWidth * 2),
3764
+ handleFontSize: fontSize / 2,
3765
+ outlineWidth,
3766
+ colorBorder: themeToken(theme, "colorBorder", "#d9d9d9"),
3767
+ colorBg: themeToken(theme, "colorBgContainer", "#ffffff"),
3768
+ colorText: themeToken(theme, "colorText", "rgba(0, 0, 0, 0.88)"),
3769
+ colorArrow: themeToken(theme, "colorTextQuaternary", "rgba(0, 0, 0, 0.25)"),
3770
+ colorIcon: themeToken(theme, "colorIcon", "rgba(0, 0, 0, 0.45)"),
3771
+ colorPrimary: themeToken(theme, "colorPrimary", "#1677ff"),
3772
+ colorPrimaryHover: themeToken(theme, "colorPrimaryHover", "#4096ff"),
3773
+ controlOutline: themeToken(theme, "controlOutline", "rgba(5, 145, 255, 0.1)"),
3774
+ motion: themeToken(theme, "motionDurationMid", "0.2s")
3775
+ };
3776
+ };
3777
+ var ANT_DOWN_PATH = "M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z";
3778
+ var ANT_UP_PATH = "M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z";
3779
+ var appendSvgIcon = (parent, path, size) => {
3780
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3781
+ svg.setAttribute("viewBox", "64 64 896 896");
3782
+ svg.setAttribute("width", String(size));
3783
+ svg.setAttribute("height", String(size));
3784
+ svg.setAttribute("fill", "currentColor");
3785
+ svg.setAttribute("aria-hidden", "true");
3786
+ svg.setAttribute("focusable", "false");
3787
+ const shape = document.createElementNS("http://www.w3.org/2000/svg", "path");
3788
+ shape.setAttribute("d", path);
3789
+ svg.appendChild(shape);
3790
+ parent.appendChild(svg);
3791
+ };
3792
+ var ensurePanelControlStyles = (rootElement, metrics) => {
3793
+ rootElement.style.setProperty("--pdfme-border", metrics.colorBorder);
3794
+ rootElement.style.setProperty("--pdfme-hover-border", metrics.colorPrimaryHover);
3795
+ rootElement.style.setProperty("--pdfme-active-border", metrics.colorPrimary);
3796
+ rootElement.style.setProperty("--pdfme-outline", metrics.controlOutline);
3797
+ rootElement.style.setProperty("--pdfme-outline-width", `${metrics.outlineWidth}px`);
3798
+ rootElement.style.setProperty("--pdfme-handle-width", `${metrics.handleWidth}px`);
3799
+ rootElement.style.setProperty("--pdfme-handle-hover", metrics.colorPrimary);
3800
+ rootElement.style.setProperty("--pdfme-padding-inline", `${metrics.paddingInline}px`);
3801
+ if (rootElement.querySelector("style[data-pdfme-column-controls]")) return;
3802
+ const style = document.createElement("style");
3803
+ style.dataset.pdfmeColumnControls = "true";
3804
+ style.textContent = `
3805
+ .pdfme-column-select,
3806
+ .pdfme-column-number {
3807
+ border-color: var(--pdfme-border);
3808
+ }
3809
+ .pdfme-column-select:hover,
3810
+ .pdfme-column-number:hover {
3811
+ border-color: var(--pdfme-hover-border);
3812
+ }
3813
+ .pdfme-column-select:focus-within,
3814
+ .pdfme-column-number:focus-within {
3815
+ border-color: var(--pdfme-active-border);
3816
+ box-shadow: 0 0 0 var(--pdfme-outline-width) var(--pdfme-outline);
3817
+ outline: 0;
3818
+ }
3819
+ .pdfme-column-select-input,
3820
+ .pdfme-column-number-input {
3821
+ outline: none;
3822
+ }
3823
+ .pdfme-column-select-input {
3824
+ appearance: none;
3825
+ -webkit-appearance: none;
3826
+ }
3827
+ .pdfme-column-number-input {
3828
+ appearance: textfield;
3829
+ -moz-appearance: textfield;
3830
+ padding-left: var(--pdfme-padding-inline);
3831
+ padding-right: var(--pdfme-padding-inline);
3832
+ }
3833
+ .pdfme-column-number:hover .pdfme-column-number-input,
3834
+ .pdfme-column-number:focus-within .pdfme-column-number-input {
3835
+ padding-right: calc(var(--pdfme-handle-width) + var(--pdfme-padding-inline));
3836
+ }
3837
+ .pdfme-column-number-input::-webkit-outer-spin-button,
3838
+ .pdfme-column-number-input::-webkit-inner-spin-button {
3839
+ -webkit-appearance: none;
3840
+ margin: 0;
3841
+ }
3842
+ .pdfme-column-number-handlers {
3843
+ opacity: 0;
3844
+ width: 0;
3845
+ }
3846
+ .pdfme-column-number:hover .pdfme-column-number-handlers,
3847
+ .pdfme-column-number:focus-within .pdfme-column-number-handlers {
3848
+ opacity: 1;
3849
+ width: var(--pdfme-handle-width);
3850
+ }
3851
+ .pdfme-column-number-handler:hover {
3852
+ color: var(--pdfme-handle-hover);
3853
+ height: 60%;
3854
+ }
3855
+ `;
3856
+ rootElement.appendChild(style);
3857
+ };
3858
+ var HORIZONTAL_ALIGNMENTS = [
3859
+ {
3860
+ value: "left",
3861
+ icon: TextAlignLeftIcon,
3862
+ labelKey: "schemas.left"
3863
+ },
3864
+ {
3865
+ value: "center",
3866
+ icon: TextAlignCenterIcon,
3867
+ labelKey: "schemas.center"
3868
+ },
3869
+ {
3870
+ value: "right",
3871
+ icon: TextAlignRightIcon,
3872
+ labelKey: "schemas.right"
3873
+ }
3874
+ ];
3875
+ var VERTICAL_ALIGNMENTS = [
3876
+ {
3877
+ value: "top",
3878
+ icon: TextVerticalAlignTopIcon,
3879
+ labelKey: "schemas.top"
3880
+ },
3881
+ {
3882
+ value: "middle",
3883
+ icon: TextVerticalAlignMiddleIcon,
3884
+ labelKey: "schemas.middle"
3885
+ },
3886
+ {
3887
+ value: "bottom",
3888
+ icon: TextVerticalAlignBottomIcon,
3889
+ labelKey: "schemas.bottom"
3890
+ }
3891
+ ];
3892
+ var isUnset = (value) => value == null;
3893
+ var clearColumnContent = (content, columnIndex) => {
3894
+ let rows;
3895
+ try {
3896
+ rows = JSON.parse(content ?? "[]");
3897
+ } catch {
3898
+ return content ?? "[]";
3899
+ }
3900
+ if (!Array.isArray(rows)) return content ?? "[]";
3901
+ const next = rows.map((row) => {
3902
+ if (!Array.isArray(row)) return row;
3903
+ const copy = row.slice();
3904
+ if (columnIndex < copy.length) copy[columnIndex] = "";
3905
+ return copy;
3906
+ });
3907
+ return JSON.stringify(next);
3908
+ };
3909
+ var dropColumnKey = (map, columnIndex) => {
3910
+ if (!map) return map;
3911
+ const next = { ...map };
3912
+ delete next[columnIndex];
3913
+ return Object.keys(next).length === 0 ? void 0 : next;
3914
+ };
3915
+ var columnLabel = (head, index, i18n) => {
3916
+ const label = head[index];
3917
+ return label ? label : `${i18n("schemas.table.columnLabel")} ${index + 1}`;
3918
+ };
3919
+ var commitStyles = (props, columnStyles, content) => {
3920
+ const schemaId = props.activeSchema.id;
3921
+ const changes = [{
3922
+ key: "columnStyles",
3923
+ value: columnStyles,
3924
+ schemaId
3925
+ }];
3926
+ if (content !== void 0) changes.push({
3927
+ key: "content",
3928
+ value: content,
3929
+ schemaId
3930
+ });
3931
+ props.changeSchemas(changes);
3932
+ };
3933
+ var setCellType = (props, table, columnIndex, nextType) => {
3934
+ const next = cloneDeep(table.columnStyles ?? {});
3935
+ if (nextType === "image") {
3936
+ next.cellType = {
3937
+ ...next.cellType,
3938
+ [columnIndex]: "image"
3939
+ };
3940
+ if (isUnset(next.imageHeightMode?.[columnIndex])) next.imageHeightMode = {
3941
+ ...next.imageHeightMode,
3942
+ [columnIndex]: DEFAULT_TABLE_IMAGE_HEIGHT_MODE
3943
+ };
3944
+ if (isUnset(next.imageHeight?.[columnIndex])) next.imageHeight = {
3945
+ ...next.imageHeight,
3946
+ [columnIndex]: 20
3947
+ };
3948
+ if (isUnset(next.alignment?.[columnIndex])) next.alignment = {
3949
+ ...next.alignment,
3950
+ [columnIndex]: "center"
3951
+ };
3952
+ } else {
3953
+ const cellType = dropColumnKey(next.cellType, columnIndex);
3954
+ const imageHeightMode = dropColumnKey(next.imageHeightMode, columnIndex);
3955
+ const imageHeight = dropColumnKey(next.imageHeight, columnIndex);
3956
+ if (cellType) next.cellType = cellType;
3957
+ else delete next.cellType;
3958
+ if (imageHeightMode) next.imageHeightMode = imageHeightMode;
3959
+ else delete next.imageHeightMode;
3960
+ if (imageHeight) next.imageHeight = imageHeight;
3961
+ else delete next.imageHeight;
3962
+ }
3963
+ commitStyles(props, next, clearColumnContent(table.content, columnIndex));
3964
+ };
3965
+ var setImageHeightMode = (props, table, columnIndex, mode) => {
3966
+ const next = cloneDeep(table.columnStyles ?? {});
3967
+ next.imageHeightMode = {
3968
+ ...next.imageHeightMode,
3969
+ [columnIndex]: mode
3970
+ };
3971
+ commitStyles(props, next);
3972
+ };
3973
+ var setImageHeight = (props, table, columnIndex, height) => {
3974
+ const next = cloneDeep(table.columnStyles ?? {});
3975
+ next.imageHeight = {
3976
+ ...next.imageHeight,
3977
+ [columnIndex]: height
3978
+ };
3979
+ commitStyles(props, next);
3980
+ };
3981
+ var setAlignment = (props, table, columnIndex, value) => {
3982
+ const next = cloneDeep(table.columnStyles ?? {});
3983
+ if (next.alignment?.[columnIndex] === value) return;
3984
+ next.alignment = {
3985
+ ...next.alignment,
3986
+ [columnIndex]: value
3987
+ };
3988
+ commitStyles(props, next);
3989
+ };
3990
+ var setVerticalAlignment = (props, table, columnIndex, value) => {
3991
+ const next = cloneDeep(table.columnStyles ?? {});
3992
+ if (next.verticalAlignment?.[columnIndex] === value) return;
3993
+ next.verticalAlignment = {
3994
+ ...next.verticalAlignment,
3995
+ [columnIndex]: value
3996
+ };
3997
+ commitStyles(props, next);
3998
+ };
3999
+ var iconSrc = (svg, color) => `data:image/svg+xml;utf8,${encodeURIComponent(svg.replace(/currentColor/g, color))}`;
4000
+ var appendIcon = (parent, svg, color, size = 17) => {
4001
+ const img = document.createElement("img");
4002
+ img.alt = "";
4003
+ img.width = size;
4004
+ img.height = size;
4005
+ img.src = iconSrc(svg, color);
4006
+ parent.appendChild(img);
4007
+ return img;
4008
+ };
4009
+ var appendSelect = (parent, options, value, control, columnIndex, metrics, layout, onChange) => {
4010
+ const shell = document.createElement("div");
4011
+ shell.className = "pdfme-column-select";
4012
+ shell.style.position = "relative";
4013
+ shell.style.display = "inline-flex";
4014
+ shell.style.alignItems = "center";
4015
+ shell.style.boxSizing = "border-box";
4016
+ shell.style.height = `${metrics.controlHeight}px`;
4017
+ shell.style.borderWidth = `${metrics.lineWidth}px`;
4018
+ shell.style.borderStyle = "solid";
4019
+ shell.style.borderRadius = `${metrics.borderRadius}px`;
4020
+ shell.style.background = metrics.colorBg;
4021
+ shell.style.overflow = "hidden";
4022
+ shell.style.transition = `all ${metrics.motion}`;
4023
+ shell.style.flexShrink = layout.flex ? "1" : "0";
4024
+ if (layout.width) shell.style.width = layout.width;
4025
+ if (layout.flex) {
4026
+ shell.style.flex = layout.flex;
4027
+ shell.style.minWidth = "0";
4028
+ }
4029
+ const select = document.createElement("select");
4030
+ select.className = "pdfme-column-select-input";
4031
+ select.dataset.control = control;
4032
+ select.dataset.columnIndex = String(columnIndex);
4033
+ select.style.width = "100%";
4034
+ select.style.height = "100%";
4035
+ select.style.boxSizing = "border-box";
4036
+ select.style.margin = "0";
4037
+ select.style.border = "0";
4038
+ select.style.borderRadius = `${metrics.borderRadius}px`;
4039
+ select.style.background = "transparent";
4040
+ select.style.padding = `0 ${metrics.paddingInline + 18}px 0 ${metrics.paddingInline}px`;
4041
+ select.style.font = "inherit";
4042
+ select.style.fontSize = `${metrics.fontSize}px`;
4043
+ select.style.lineHeight = `${metrics.controlHeight - metrics.lineWidth * 2}px`;
4044
+ select.style.color = metrics.colorText;
4045
+ select.style.cursor = "pointer";
4046
+ options.forEach((option) => {
4047
+ const element = document.createElement("option");
4048
+ element.value = option.value;
4049
+ element.textContent = option.label;
4050
+ select.appendChild(element);
4051
+ });
4052
+ select.value = value;
4053
+ select.addEventListener("change", () => {
4054
+ onChange(select.value);
4055
+ });
4056
+ const arrow = document.createElement("span");
4057
+ arrow.className = "pdfme-column-select-arrow";
4058
+ arrow.setAttribute("aria-hidden", "true");
4059
+ arrow.style.position = "absolute";
4060
+ arrow.style.top = "50%";
4061
+ arrow.style.right = `${metrics.paddingInline}px`;
4062
+ arrow.style.transform = "translateY(-50%)";
4063
+ arrow.style.display = "flex";
4064
+ arrow.style.width = "12px";
4065
+ arrow.style.height = "12px";
4066
+ arrow.style.color = metrics.colorArrow;
4067
+ arrow.style.fontSize = "12px";
4068
+ arrow.style.lineHeight = "1";
4069
+ arrow.style.pointerEvents = "none";
4070
+ appendSvgIcon(arrow, ANT_DOWN_PATH, 12);
4071
+ shell.appendChild(select);
4072
+ shell.appendChild(arrow);
4073
+ parent.appendChild(shell);
4074
+ return select;
4075
+ };
4076
+ var appendNumberInput = (parent, value, columnIndex, ariaLabel, metrics, onCommit) => {
4077
+ const shell = document.createElement("div");
4078
+ shell.className = "pdfme-column-number";
4079
+ shell.style.position = "relative";
4080
+ shell.style.display = "inline-flex";
4081
+ shell.style.alignItems = "center";
4082
+ shell.style.boxSizing = "border-box";
4083
+ shell.style.width = "72px";
4084
+ shell.style.height = `${metrics.controlHeight}px`;
4085
+ shell.style.flexShrink = "0";
4086
+ shell.style.borderWidth = `${metrics.lineWidth}px`;
4087
+ shell.style.borderStyle = "solid";
4088
+ shell.style.borderRadius = `${metrics.borderRadius}px`;
4089
+ shell.style.background = metrics.colorBg;
4090
+ shell.style.transition = `all ${metrics.motion}`;
4091
+ shell.style.overflow = "hidden";
4092
+ const input = document.createElement("input");
4093
+ input.type = "number";
4094
+ input.min = "1";
4095
+ input.step = "1";
4096
+ input.className = "pdfme-column-number-input";
4097
+ input.dataset.control = "imageHeight";
4098
+ input.dataset.columnIndex = String(columnIndex);
4099
+ input.setAttribute("aria-label", ariaLabel);
4100
+ input.style.width = "100%";
4101
+ input.style.height = "100%";
4102
+ input.style.boxSizing = "border-box";
4103
+ input.style.margin = "0";
4104
+ input.style.border = "0";
4105
+ input.style.borderRadius = "0";
4106
+ input.style.background = "transparent";
4107
+ input.style.font = "inherit";
4108
+ input.style.fontSize = `${metrics.fontSize}px`;
4109
+ input.style.lineHeight = "22px";
4110
+ input.style.color = metrics.colorText;
4111
+ input.value = value;
4112
+ input.addEventListener("change", () => {
4113
+ onCommit(input);
4114
+ });
4115
+ const handlers = document.createElement("div");
4116
+ handlers.className = "pdfme-column-number-handlers";
4117
+ handlers.style.position = "absolute";
4118
+ handlers.style.top = "0";
4119
+ handlers.style.right = "0";
4120
+ handlers.style.bottom = "0";
4121
+ handlers.style.display = "flex";
4122
+ handlers.style.flexDirection = "column";
4123
+ handlers.style.overflow = "hidden";
4124
+ handlers.style.background = metrics.colorBg;
4125
+ handlers.style.borderRadius = `0 ${metrics.borderRadius}px ${metrics.borderRadius}px 0`;
4126
+ handlers.style.transition = `all ${metrics.motion}`;
4127
+ const step = (delta) => {
4128
+ const current = Number(input.value);
4129
+ const base = Number.isFinite(current) ? current : Number(value);
4130
+ const next = Math.max(1, base + delta);
4131
+ if (next === base) return;
4132
+ input.value = String(next);
4133
+ input.dispatchEvent(new Event("change"));
4134
+ };
4135
+ const addHandler = (direction, path, label) => {
4136
+ const button = document.createElement("button");
4137
+ button.type = "button";
4138
+ button.className = "pdfme-column-number-handler";
4139
+ button.dataset.control = direction === "up" ? "imageHeightUp" : "imageHeightDown";
4140
+ button.setAttribute("aria-label", label);
4141
+ button.style.flex = "1 1 50%";
4142
+ button.style.display = "flex";
4143
+ button.style.alignItems = "center";
4144
+ button.style.justifyContent = "center";
4145
+ button.style.margin = "0";
4146
+ button.style.padding = "0";
4147
+ button.style.border = "0";
4148
+ button.style.borderLeft = `${metrics.lineWidth}px solid ${metrics.colorBorder}`;
4149
+ button.style.background = "transparent";
4150
+ button.style.color = metrics.colorIcon;
4151
+ button.style.cursor = "pointer";
4152
+ button.style.lineHeight = "0";
4153
+ if (direction === "down") button.style.borderTop = `${metrics.lineWidth}px solid ${metrics.colorBorder}`;
4154
+ button.addEventListener("mousedown", (event) => {
4155
+ event.preventDefault();
4156
+ });
4157
+ button.addEventListener("click", () => {
4158
+ step(direction === "up" ? 1 : -1);
4159
+ });
4160
+ appendSvgIcon(button, path, metrics.handleFontSize);
4161
+ handlers.appendChild(button);
4162
+ };
4163
+ addHandler("up", ANT_UP_PATH, "Increase Value");
4164
+ addHandler("down", ANT_DOWN_PATH, "Decrease Value");
4165
+ shell.appendChild(input);
4166
+ shell.appendChild(handlers);
4167
+ parent.appendChild(shell);
4168
+ return input;
4169
+ };
4170
+ var appendButtonGroup = (parent, control, buttons, current, columnIndex, colors, i18n, onSelect) => {
4171
+ const group = document.createElement("div");
4172
+ group.dataset.control = control;
4173
+ group.dataset.columnIndex = String(columnIndex);
4174
+ group.setAttribute("role", "group");
4175
+ group.style.display = "inline-flex";
4176
+ buttons.forEach((button, index) => {
4177
+ const element = document.createElement("button");
4178
+ element.type = "button";
4179
+ element.dataset.value = button.value;
4180
+ const label = i18n(button.labelKey);
4181
+ element.setAttribute("aria-label", label);
4182
+ element.title = label;
4183
+ const active = current === button.value;
4184
+ element.setAttribute("aria-pressed", active ? "true" : "false");
4185
+ element.style.padding = "7px";
4186
+ element.style.lineHeight = "0";
4187
+ element.style.background = "transparent";
4188
+ element.style.cursor = "pointer";
4189
+ element.style.border = `1px solid ${active ? colors.primary : colors.border}`;
4190
+ element.style.marginLeft = index === 0 ? "0" : "-1px";
4191
+ element.style.borderRadius = index === 0 ? "6px 0 0 6px" : index === buttons.length - 1 ? "0 6px 6px 0" : "0";
4192
+ element.style.position = "relative";
4193
+ element.style.zIndex = active ? "1" : "0";
4194
+ appendIcon(element, button.icon, active ? colors.primary : colors.text);
4195
+ element.addEventListener("click", () => {
4196
+ if (current === button.value) return;
4197
+ onSelect(button.value);
4198
+ });
4199
+ group.appendChild(element);
4200
+ });
4201
+ parent.appendChild(group);
4202
+ return group;
4203
+ };
4204
+ var ellipsis = (element) => {
4205
+ element.style.overflow = "hidden";
4206
+ element.style.textOverflow = "ellipsis";
4207
+ element.style.whiteSpace = "nowrap";
4208
+ element.style.minWidth = "0";
4209
+ };
4210
+ var TableColumns = (props) => {
4211
+ const { rootElement, activeSchema, i18n } = props;
4212
+ const table = activeSchema;
4213
+ const head = table.head || [];
4214
+ const columnStyles = table.columnStyles ?? {};
4215
+ const metrics = panelControlMetrics(props.theme);
4216
+ ensurePanelControlStyles(rootElement, metrics);
4217
+ const colors = {
4218
+ primary: themeToken(props.theme, "colorPrimary", "#1677ff"),
4219
+ border: themeToken(props.theme, "colorBorder", "#d9d9d9"),
4220
+ text: themeToken(props.theme, "colorText", "#000000"),
4221
+ split: themeToken(props.theme, "colorSplit", "#f0f0f0")
4222
+ };
4223
+ const collapsible = head.length > COLLAPSE_WHEN_MORE_THAN;
4224
+ head.forEach((_, index) => {
4225
+ const storedType = columnStyles.cellType?.[index];
4226
+ const cellType = normalizeTableCellType(storedType, index, cellTypeWarnCache) ?? "text";
4227
+ const label = columnLabel(head, index, i18n);
4228
+ const alignment = columnStyles.alignment?.[index];
4229
+ const verticalAlignment = columnStyles.verticalAlignment?.[index];
4230
+ const mode = cellType === "image" ? normalizeTableImageHeightMode(columnStyles.imageHeightMode?.[index]) ?? "fixed" : void 0;
4231
+ const block = document.createElement("div");
4232
+ block.dataset.columnIndex = String(index);
4233
+ block.style.marginBottom = index === head.length - 1 ? "0" : "10px";
4234
+ block.style.paddingBottom = index === head.length - 1 ? "0" : "10px";
4235
+ if (index !== head.length - 1) block.style.borderBottom = `1px solid ${colors.split}`;
4236
+ const body = document.createElement("div");
4237
+ body.dataset.control = "column-body";
4238
+ const expanded = collapsible && expandedColumnBlocks.has(columnBlockKey(activeSchema.id, index));
4239
+ if (collapsible) {
4240
+ const summary = document.createElement("button");
4241
+ summary.type = "button";
4242
+ summary.dataset.control = "summary";
4243
+ summary.setAttribute("aria-expanded", expanded ? "true" : "false");
4244
+ summary.setAttribute("aria-label", `Expand ${label}`);
4245
+ summary.title = label;
4246
+ summary.style.display = "flex";
4247
+ summary.style.alignItems = "center";
4248
+ summary.style.gap = "6px";
4249
+ summary.style.width = "100%";
4250
+ summary.style.padding = "0";
4251
+ summary.style.border = "none";
4252
+ summary.style.background = "transparent";
4253
+ summary.style.cursor = "pointer";
4254
+ summary.style.font = "inherit";
4255
+ summary.style.textAlign = "left";
4256
+ const name = document.createElement("span");
4257
+ name.dataset.control = "summary-name";
4258
+ name.textContent = label;
4259
+ name.style.fontWeight = "600";
4260
+ name.style.fontSize = "12px";
4261
+ name.style.flex = "1";
4262
+ ellipsis(name);
4263
+ summary.appendChild(name);
4264
+ const typeLabel = document.createElement("span");
4265
+ typeLabel.dataset.control = "summary-type";
4266
+ typeLabel.textContent = i18n(`schemas.table.cellType.${cellType}`);
4267
+ typeLabel.style.fontSize = "12px";
4268
+ typeLabel.style.flexShrink = "0";
4269
+ summary.appendChild(typeLabel);
4270
+ const alignmentIcon = HORIZONTAL_ALIGNMENTS.find((button) => button.value === alignment);
4271
+ if (alignmentIcon) {
4272
+ const icon = document.createElement("span");
4273
+ icon.dataset.control = "summary-alignment";
4274
+ icon.dataset.value = alignmentIcon.value;
4275
+ icon.style.lineHeight = "0";
4276
+ icon.style.flexShrink = "0";
4277
+ appendIcon(icon, alignmentIcon.icon, colors.text, 14);
4278
+ summary.appendChild(icon);
4279
+ }
4280
+ const verticalIcon = VERTICAL_ALIGNMENTS.find((button) => button.value === verticalAlignment);
4281
+ if (verticalIcon) {
4282
+ const icon = document.createElement("span");
4283
+ icon.dataset.control = "summary-vertical-alignment";
4284
+ icon.dataset.value = verticalIcon.value;
4285
+ icon.style.lineHeight = "0";
4286
+ icon.style.flexShrink = "0";
4287
+ appendIcon(icon, verticalIcon.icon, colors.text, 14);
4288
+ summary.appendChild(icon);
4289
+ }
4290
+ if (cellType === "image" && mode === "fixed") {
4291
+ const height = document.createElement("span");
4292
+ height.dataset.control = "summary-height";
4293
+ height.textContent = `${resolveFixedTableImageHeight(columnStyles.imageHeight?.[index])}mm`;
4294
+ height.style.fontSize = "12px";
4295
+ height.style.flexShrink = "0";
4296
+ summary.appendChild(height);
4297
+ }
4298
+ summary.addEventListener("click", () => {
4299
+ expandedColumnBlocks.add(columnBlockKey(activeSchema.id, index));
4300
+ summary.hidden = true;
4301
+ summary.setAttribute("aria-expanded", "true");
4302
+ body.hidden = false;
4303
+ });
4304
+ summary.hidden = expanded;
4305
+ body.hidden = !expanded;
4306
+ block.appendChild(summary);
4307
+ }
4308
+ const header = document.createElement("div");
4309
+ header.style.display = "flex";
4310
+ header.style.alignItems = "center";
4311
+ header.style.gap = "8px";
4312
+ if (collapsible) {
4313
+ const collapse = document.createElement("button");
4314
+ collapse.type = "button";
4315
+ collapse.dataset.control = "collapse";
4316
+ collapse.setAttribute("aria-expanded", "true");
4317
+ collapse.setAttribute("aria-label", `Collapse ${label}`);
4318
+ collapse.textContent = "▾";
4319
+ collapse.style.border = "none";
4320
+ collapse.style.background = "transparent";
4321
+ collapse.style.cursor = "pointer";
4322
+ collapse.style.padding = "0 2px 0 0";
4323
+ collapse.style.lineHeight = "1";
4324
+ collapse.addEventListener("click", () => {
4325
+ expandedColumnBlocks.delete(columnBlockKey(activeSchema.id, index));
4326
+ const summary = block.querySelector("[data-control=\"summary\"]");
4327
+ if (summary) {
4328
+ summary.hidden = false;
4329
+ summary.setAttribute("aria-expanded", "false");
4330
+ }
4331
+ body.hidden = true;
4332
+ });
4333
+ header.appendChild(collapse);
4334
+ }
4335
+ const title = document.createElement("div");
4336
+ title.dataset.control = "label";
4337
+ title.textContent = label;
4338
+ title.title = label;
4339
+ title.style.fontSize = "12px";
4340
+ title.style.fontWeight = "600";
4341
+ title.style.flex = "1";
4342
+ ellipsis(title);
4343
+ header.appendChild(title);
4344
+ appendSelect(header, TABLE_CELL_TYPES.map((type) => ({
4345
+ value: type,
4346
+ label: i18n(`schemas.table.cellType.${type}`)
4347
+ })), cellType, "cellType", index, metrics, { width: "110px" }, (value) => {
4348
+ if (value !== "text" && value !== "image") return;
4349
+ if (value === cellType) return;
4350
+ setCellType(props, table, index, value);
4351
+ });
4352
+ body.appendChild(header);
4353
+ const alignments = document.createElement("div");
4354
+ alignments.style.display = "flex";
4355
+ alignments.style.justifyContent = "space-between";
4356
+ alignments.style.alignItems = "center";
4357
+ alignments.style.marginTop = "8px";
4358
+ appendButtonGroup(alignments, "alignment", HORIZONTAL_ALIGNMENTS, alignment, index, colors, i18n, (value) => {
4359
+ if (value !== "left" && value !== "center" && value !== "right") return;
4360
+ setAlignment(props, table, index, value);
4361
+ });
4362
+ appendButtonGroup(alignments, "verticalAlignment", VERTICAL_ALIGNMENTS, verticalAlignment, index, colors, i18n, (value) => {
4363
+ if (value !== "top" && value !== "middle" && value !== "bottom") return;
4364
+ setVerticalAlignment(props, table, index, value);
4365
+ });
4366
+ body.appendChild(alignments);
4367
+ if (cellType === "image" && mode) {
4368
+ const imageRow = document.createElement("div");
4369
+ imageRow.style.display = "flex";
4370
+ imageRow.style.alignItems = "center";
4371
+ imageRow.style.gap = "8px";
4372
+ imageRow.style.marginTop = "8px";
4373
+ appendSelect(imageRow, TABLE_IMAGE_HEIGHT_MODES.map((heightMode) => ({
4374
+ value: heightMode,
4375
+ label: i18n(`schemas.table.imageHeightMode.${heightMode}`)
4376
+ })), mode, "imageHeightMode", index, metrics, { flex: "1" }, (value) => {
4377
+ if (value !== "fixed" && value !== "auto") return;
4378
+ if (value === mode) return;
4379
+ setImageHeightMode(props, table, index, value);
4380
+ });
4381
+ if (mode === "fixed") {
4382
+ const displayHeight = resolveFixedTableImageHeight(columnStyles.imageHeight?.[index]);
4383
+ appendNumberInput(imageRow, String(displayHeight), index, i18n("schemas.table.imageHeightMode.fixed"), metrics, (input) => {
4384
+ const parsed = Number(input.value);
4385
+ if (!Number.isFinite(parsed) || parsed <= 0) {
4386
+ input.value = String(displayHeight);
4387
+ return;
4388
+ }
4389
+ setImageHeight(props, table, index, parsed);
4390
+ });
4391
+ }
4392
+ body.appendChild(imageRow);
4393
+ }
4394
+ block.appendChild(body);
4395
+ rootElement.appendChild(block);
4396
+ });
4397
+ };
4398
+ //#endregion
3607
4399
  //#region src/tables/index.ts
3608
4400
  var tableSchema = {
3609
4401
  pdf: pdfRender,
3610
4402
  ui: uiRender,
3611
4403
  propPanel: {
3612
4404
  schema: ({ activeSchema, options, i18n }) => {
3613
- const tableSchema = activeSchema;
3614
- const head = tableSchema.head || [];
3615
- const showHead = tableSchema.showHead || false;
4405
+ const showHead = activeSchema.showHead || false;
3616
4406
  const font = options.font || { [DEFAULT_FONT_NAME]: {
3617
4407
  data: "",
3618
4408
  fallback: true
@@ -3687,18 +4477,22 @@ var tableSchema = {
3687
4477
  isBody: true
3688
4478
  })
3689
4479
  },
3690
- columnStyles: {
4480
+ tableColumns: {
3691
4481
  title: i18n("schemas.table.columnStyle"),
3692
4482
  type: "object",
3693
4483
  widget: "Card",
4484
+ bind: false,
3694
4485
  span: 24,
3695
- properties: getColumnStylesPropPanelSchema({
3696
- head,
3697
- i18n
3698
- })
4486
+ properties: { columns: {
4487
+ type: "void",
4488
+ widget: "TableColumns",
4489
+ bind: false,
4490
+ span: 24
4491
+ } }
3699
4492
  }
3700
4493
  };
3701
4494
  },
4495
+ widgets: { TableColumns },
3702
4496
  defaultSchema: {
3703
4497
  name: "",
3704
4498
  type: "table",