@harbortouch/skytab-analytics-report-utils 0.41.11 → 0.41.12

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.
@@ -1,5 +1,5 @@
1
- import { a as ExportSection } from '../../types-JAEAu4KX.cjs';
2
- export { E as XlsxFilter, b as XlsxTable, c as XlsxTableColumn } from '../../types-JAEAu4KX.cjs';
1
+ import { a as ExportSection } from '../../types-D3IyO__4.cjs';
2
+ export { E as XlsxFilter, b as XlsxTable, c as XlsxTableColumn } from '../../types-D3IyO__4.cjs';
3
3
 
4
4
  declare class ExcelExporter {
5
5
  generate(content: {
@@ -1,5 +1,5 @@
1
- import { a as ExportSection } from '../../types-JAEAu4KX.js';
2
- export { E as XlsxFilter, b as XlsxTable, c as XlsxTableColumn } from '../../types-JAEAu4KX.js';
1
+ import { a as ExportSection } from '../../types-D3IyO__4.js';
2
+ export { E as XlsxFilter, b as XlsxTable, c as XlsxTableColumn } from '../../types-D3IyO__4.js';
3
3
 
4
4
  declare class ExcelExporter {
5
5
  generate(content: {
@@ -4146,7 +4146,10 @@ var ExportBuilder = class {
4146
4146
  renderTableContent(config, reportData, parameters) {
4147
4147
  const data = this.getDataForConfig(reportData, config);
4148
4148
  const { columns, headers, columnTypes } = this.resolveColumnsAndHeaders(parameters, config);
4149
- const tableColumns = columns.map((col, i) => ({ header: headers[i], key: col }));
4149
+ const tableColumns = columns.map((col, i) => {
4150
+ const size = config.fields[col]?.size;
4151
+ return { header: headers[i], key: col, ...size !== void 0 ? { size } : {} };
4152
+ });
4150
4153
  const rows = data.filter((row) => typeof row === "object" && row !== null).map((row) => this.formatRow(row, columns, columnTypes, row));
4151
4154
  if (config.hasFooter) {
4152
4155
  const totalsRow = this.buildTotalsRow(data, config);
@@ -1,5 +1,5 @@
1
1
  import { R as ReportType, A as AnalyticsField } from '../report-Dj8UU5d_.cjs';
2
- import { E as ExportFilter, R as ReportMetadata, a as ExportSection } from '../types-JAEAu4KX.cjs';
2
+ import { E as ExportFilter, R as ReportMetadata, a as ExportSection } from '../types-D3IyO__4.cjs';
3
3
 
4
4
  declare function resolveRawFiltersToExportFilters(rawFilters: Record<string, (string | number)[] | undefined>, locale: string, reportType?: ReportType): ExportFilter[];
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { R as ReportType, A as AnalyticsField } from '../report-Dj8UU5d_.js';
2
- import { E as ExportFilter, R as ReportMetadata, a as ExportSection } from '../types-JAEAu4KX.js';
2
+ import { E as ExportFilter, R as ReportMetadata, a as ExportSection } from '../types-D3IyO__4.js';
3
3
 
4
4
  declare function resolveRawFiltersToExportFilters(rawFilters: Record<string, (string | number)[] | undefined>, locale: string, reportType?: ReportType): ExportFilter[];
5
5
 
@@ -197,7 +197,10 @@ var ExportBuilder = class {
197
197
  renderTableContent(config, reportData, parameters) {
198
198
  const data = this.getDataForConfig(reportData, config);
199
199
  const { columns, headers, columnTypes } = this.resolveColumnsAndHeaders(parameters, config);
200
- const tableColumns = columns.map((col, i) => ({ header: headers[i], key: col }));
200
+ const tableColumns = columns.map((col, i) => {
201
+ const size = config.fields[col]?.size;
202
+ return { header: headers[i], key: col, ...size !== void 0 ? { size } : {} };
203
+ });
201
204
  const rows = data.filter((row) => typeof row === "object" && row !== null).map((row) => this.formatRow(row, columns, columnTypes, row));
202
205
  if (config.hasFooter) {
203
206
  const totalsRow = this.buildTotalsRow(data, config);
@@ -207,7 +207,14 @@ var PdfExporter = class {
207
207
  const dataFontSize = columnCount > 13 ? 5 : columnCount > 10 ? 6 : columnCount > 7 ? 7 : 8;
208
208
  const headerWidthSlack = 2;
209
209
  const headerMinWidths = /* @__PURE__ */ new Map();
210
+ const cssPixelsToPoints = 0.75;
211
+ const boundedColumns = /* @__PURE__ */ new Set();
210
212
  columns.forEach((col) => {
213
+ if (col.size !== void 0 && Number.isFinite(col.size) && col.size > 0) {
214
+ col.width = col.size * cssPixelsToPoints;
215
+ boundedColumns.add(col);
216
+ return;
217
+ }
211
218
  doc.font(FONT_BOLD).fontSize(headerFontSize);
212
219
  const headerTextWidth = doc.widthOfString(col.header);
213
220
  const headerMinWidth = Math.max(...col.header.split(" ").map((w) => doc.widthOfString(w)));
@@ -226,49 +233,50 @@ var PdfExporter = class {
226
233
  headerMinWidths.set(col, headerMinWidth + TABLE_CELL_PADDING * 2 + headerWidthSlack);
227
234
  });
228
235
  const usableWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
236
+ const minimumColumnWidth = Math.min(MIN_COLUMN_WIDTH, usableWidth / columnCount);
229
237
  const totalColumnWidth = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0);
230
238
  if (totalColumnWidth > usableWidth) {
231
239
  const scale = usableWidth / totalColumnWidth;
232
240
  columns.forEach((c) => {
233
- const headerMin = headerMinWidths.get(c) ?? MIN_COLUMN_WIDTH;
234
- c.width = Math.max(Math.floor((c.width ?? MIN_COLUMN_WIDTH) * scale), headerMin, MIN_COLUMN_WIDTH);
241
+ const width = c.width ?? MIN_COLUMN_WIDTH;
242
+ const headerMin = headerMinWidths.get(c) ?? minimumColumnWidth;
243
+ const minimumWidth = Math.min(width, Math.max(headerMin, minimumColumnWidth));
244
+ c.width = Math.max(Math.floor(width * scale), minimumWidth);
235
245
  });
236
- let excess = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0) - usableWidth;
237
- for (let i = columns.length - 1; i >= 0 && excess > 0; i--) {
238
- const col = columns[i];
239
- const available = (col.width ?? MIN_COLUMN_WIDTH) - MIN_COLUMN_WIDTH;
240
- const trim = Math.min(excess, available);
241
- if (trim > 0) {
242
- col.width = (col.width ?? MIN_COLUMN_WIDTH) - trim;
243
- excess -= trim;
246
+ const excess = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0) - usableWidth;
247
+ if (excess > 0) {
248
+ const reducibleWidth = columns.reduce(
249
+ (sum, c) => sum + Math.max((c.width ?? MIN_COLUMN_WIDTH) - minimumColumnWidth, 0),
250
+ 0
251
+ );
252
+ if (reducibleWidth > 0) {
253
+ columns.forEach((c) => {
254
+ const width = c.width ?? MIN_COLUMN_WIDTH;
255
+ const available = Math.max(width - minimumColumnWidth, 0);
256
+ c.width = width - excess * (available / reducibleWidth);
257
+ });
244
258
  }
245
259
  }
246
260
  } else if (totalColumnWidth < usableWidth) {
247
- const scale = usableWidth / totalColumnWidth;
248
- columns.forEach((c) => {
249
- c.width = Math.floor((c.width ?? MIN_COLUMN_WIDTH) * scale);
250
- });
251
- const expanded = columns.reduce((sum, c) => sum + (c.width ?? 0), 0);
252
- columns[columns.length - 1].width = (columns[columns.length - 1].width ?? 0) + (usableWidth - expanded);
261
+ const expandableColumns = columns.filter((c) => !boundedColumns.has(c));
262
+ const expandableWidth = expandableColumns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0);
263
+ if (expandableWidth > 0) {
264
+ const scale = (expandableWidth + usableWidth - totalColumnWidth) / expandableWidth;
265
+ expandableColumns.forEach((c) => {
266
+ c.width = Math.floor((c.width ?? MIN_COLUMN_WIDTH) * scale);
267
+ });
268
+ const expanded = columns.reduce((sum, c) => sum + (c.width ?? 0), 0);
269
+ const lastExpandableColumn = expandableColumns[expandableColumns.length - 1];
270
+ lastExpandableColumn.width = (lastExpandableColumn.width ?? 0) + (usableWidth - expanded);
271
+ }
253
272
  }
254
273
  const tableWidth = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0);
255
274
  const pageBottom = doc.page.height - doc.page.margins.bottom;
256
- const calculateRowHeight = (row, isHeader = false) => {
257
- const fontSize = isHeader ? headerFontSize : dataFontSize;
258
- doc.font(isHeader ? FONT_BOLD : FONT_REGULAR).fontSize(fontSize);
259
- let maxLines = 1;
260
- columns.forEach((col) => {
261
- const cellValue = isHeader ? col.header : row?.[col.key] !== void 0 ? String(row[col.key]) : "";
262
- const textWidth = doc.widthOfString(cellValue);
263
- const availableWidth = (col.width ?? MIN_COLUMN_WIDTH) - TABLE_CELL_PADDING * 2;
264
- const lines = Math.ceil(textWidth / availableWidth);
265
- if (lines > maxLines) {
266
- maxLines = lines;
267
- }
268
- });
269
- return Math.max(maxLines * (fontSize + 2) + TABLE_CELL_PADDING * 2, DEFAULT_ROW_HEIGHT);
275
+ const calculateRowHeight = (isHeader = false) => {
276
+ doc.font(isHeader ? FONT_BOLD : FONT_REGULAR).fontSize(isHeader ? headerFontSize : dataFontSize);
277
+ return Math.max(doc.currentLineHeight(true) + TABLE_CELL_PADDING * 2, DEFAULT_ROW_HEIGHT);
270
278
  };
271
- const headerHeight = calculateRowHeight(null, true);
279
+ const headerHeight = calculateRowHeight(true);
272
280
  const drawTableHeader = (y) => this.drawHeader(doc, columns, y, tableWidth, headerHeight, TABLE_CELL_PADDING, headerFontSize, MIN_COLUMN_WIDTH);
273
281
  let tableStartY = startY;
274
282
  if (tableStartY + headerHeight > pageBottom) {
@@ -278,7 +286,7 @@ var PdfExporter = class {
278
286
  let currentY = drawTableHeader(tableStartY);
279
287
  doc.font(FONT_REGULAR).fontSize(dataFontSize);
280
288
  rows.forEach((row, rowIndex) => {
281
- const currentRowHeight = calculateRowHeight(row, false);
289
+ const currentRowHeight = calculateRowHeight();
282
290
  if (currentY + currentRowHeight > pageBottom) {
283
291
  doc.addPage();
284
292
  currentY = drawTableHeader(doc.page.margins.top);
@@ -301,6 +309,28 @@ var PdfExporter = class {
301
309
  doc.strokeColor(HEADER_RULE_COLOR).lineWidth(HEADER_RULE_WIDTH).moveTo(left, closingRuleY).lineTo(left + tableWidth, closingRuleY).stroke();
302
310
  return closingRuleY;
303
311
  }
312
+ truncateTextToWidth(doc, text, availableWidth) {
313
+ if (doc.widthOfString(text) <= availableWidth) {
314
+ return text;
315
+ }
316
+ const ellipsis = "\u2026";
317
+ if (doc.widthOfString(ellipsis) > availableWidth) {
318
+ return "";
319
+ }
320
+ const characters = Array.from(text);
321
+ let low = 0;
322
+ let high = characters.length;
323
+ while (low < high) {
324
+ const middle = Math.ceil((low + high) / 2);
325
+ const candidate = `${characters.slice(0, middle).join("").trimEnd()}${ellipsis}`;
326
+ if (doc.widthOfString(candidate) <= availableWidth) {
327
+ low = middle;
328
+ } else {
329
+ high = middle - 1;
330
+ }
331
+ }
332
+ return `${characters.slice(0, low).join("").trimEnd()}${ellipsis}`;
333
+ }
304
334
  drawHeader(doc, columns, startY, tableWidth, headerHeight, cellPadding, headerFontSize, minColumnWidth) {
305
335
  const left = doc.page.margins.left;
306
336
  doc.rect(left, startY, tableWidth, headerHeight).fill(HEADER_BACKGROUND_COLOR);
@@ -310,12 +340,15 @@ var PdfExporter = class {
310
340
  const headerLineHeight = doc.currentLineHeight(true);
311
341
  const headerTextY = startY + Math.max((headerHeight - headerLineHeight) / 2, 0);
312
342
  columns.forEach((col) => {
313
- doc.text(col.header, xPosition + cellPadding, headerTextY, {
314
- width: (col.width ?? minColumnWidth) - cellPadding * 2,
343
+ const innerWidth = (col.width ?? minColumnWidth) - cellPadding * 2;
344
+ const bounded = col.size !== void 0 && Number.isFinite(col.size) && col.size > 0;
345
+ const headerText = bounded ? this.truncateTextToWidth(doc, col.header, innerWidth) : col.header;
346
+ doc.text(headerText, xPosition + cellPadding, headerTextY, {
347
+ width: innerWidth,
315
348
  height: headerLineHeight,
316
349
  align: "left",
317
350
  lineBreak: false,
318
- ellipsis: true
351
+ ellipsis: !bounded
319
352
  });
320
353
  xPosition += col.width ?? minColumnWidth;
321
354
  });
@@ -336,12 +369,15 @@ var PdfExporter = class {
336
369
  const rawValue = row[col.key];
337
370
  const cellValue = typeof rawValue === "string" || typeof rawValue === "number" || typeof rawValue === "boolean" ? String(rawValue) : "";
338
371
  const isNumeric = typeof rawValue === "number" || typeof rawValue === "string" && rawValue !== "" && !isNaN(Number(rawValue));
339
- doc.text(cellValue, xPosition + cellPadding, dataTextY, {
340
- width: (col.width ?? minColumnWidth) - cellPadding * 2,
372
+ const innerWidth = (col.width ?? minColumnWidth) - cellPadding * 2;
373
+ const bounded = col.size !== void 0 && Number.isFinite(col.size) && col.size > 0;
374
+ const rowText = bounded ? this.truncateTextToWidth(doc, cellValue, innerWidth) : cellValue;
375
+ doc.text(rowText, xPosition + cellPadding, dataTextY, {
376
+ width: innerWidth,
341
377
  height: dataLineHeight,
342
378
  align: isNumeric ? "right" : "left",
343
379
  lineBreak: false,
344
- ellipsis: true
380
+ ellipsis: !bounded
345
381
  });
346
382
  xPosition += col.width ?? minColumnWidth;
347
383
  if (i < columns.length - 1) {
@@ -1,4 +1,4 @@
1
- import { a as ExportSection, E as ExportFilter, b as ExportTable, c as ExportTableColumn } from '../../types-JAEAu4KX.cjs';
1
+ import { a as ExportSection, E as ExportFilter, b as ExportTable, c as ExportTableColumn } from '../../types-D3IyO__4.cjs';
2
2
 
3
3
  type MetaDataValue = ExportFilter[] | string;
4
4
  declare class PdfExporter {
@@ -10,6 +10,7 @@ declare class PdfExporter {
10
10
  drawSectionTitle(doc: PDFKit.PDFDocument, title: string, startY: number): number;
11
11
  drawMetaData(doc: PDFKit.PDFDocument, metaData: Record<string, MetaDataValue>, startY: number): number;
12
12
  drawTable(doc: PDFKit.PDFDocument, table: ExportTable, startY: number): number;
13
+ private truncateTextToWidth;
13
14
  drawHeader(doc: PDFKit.PDFDocument, columns: ExportTableColumn[], startY: number, tableWidth: number, headerHeight: number, cellPadding: number, headerFontSize: number, minColumnWidth: number): number;
14
15
  drawRow(doc: PDFKit.PDFDocument, row: Record<string, unknown>, columns: ExportTableColumn[], currentY: number, rowHeight: number, cellPadding: number, dataFontSize: number, minColumnWidth: number, rowIndex?: number): number;
15
16
  }
@@ -1,4 +1,4 @@
1
- import { a as ExportSection, E as ExportFilter, b as ExportTable, c as ExportTableColumn } from '../../types-JAEAu4KX.js';
1
+ import { a as ExportSection, E as ExportFilter, b as ExportTable, c as ExportTableColumn } from '../../types-D3IyO__4.js';
2
2
 
3
3
  type MetaDataValue = ExportFilter[] | string;
4
4
  declare class PdfExporter {
@@ -10,6 +10,7 @@ declare class PdfExporter {
10
10
  drawSectionTitle(doc: PDFKit.PDFDocument, title: string, startY: number): number;
11
11
  drawMetaData(doc: PDFKit.PDFDocument, metaData: Record<string, MetaDataValue>, startY: number): number;
12
12
  drawTable(doc: PDFKit.PDFDocument, table: ExportTable, startY: number): number;
13
+ private truncateTextToWidth;
13
14
  drawHeader(doc: PDFKit.PDFDocument, columns: ExportTableColumn[], startY: number, tableWidth: number, headerHeight: number, cellPadding: number, headerFontSize: number, minColumnWidth: number): number;
14
15
  drawRow(doc: PDFKit.PDFDocument, row: Record<string, unknown>, columns: ExportTableColumn[], currentY: number, rowHeight: number, cellPadding: number, dataFontSize: number, minColumnWidth: number, rowIndex?: number): number;
15
16
  }
@@ -167,7 +167,14 @@ var PdfExporter = class {
167
167
  const dataFontSize = columnCount > 13 ? 5 : columnCount > 10 ? 6 : columnCount > 7 ? 7 : 8;
168
168
  const headerWidthSlack = 2;
169
169
  const headerMinWidths = /* @__PURE__ */ new Map();
170
+ const cssPixelsToPoints = 0.75;
171
+ const boundedColumns = /* @__PURE__ */ new Set();
170
172
  columns.forEach((col) => {
173
+ if (col.size !== void 0 && Number.isFinite(col.size) && col.size > 0) {
174
+ col.width = col.size * cssPixelsToPoints;
175
+ boundedColumns.add(col);
176
+ return;
177
+ }
171
178
  doc.font(FONT_BOLD).fontSize(headerFontSize);
172
179
  const headerTextWidth = doc.widthOfString(col.header);
173
180
  const headerMinWidth = Math.max(...col.header.split(" ").map((w) => doc.widthOfString(w)));
@@ -186,49 +193,50 @@ var PdfExporter = class {
186
193
  headerMinWidths.set(col, headerMinWidth + TABLE_CELL_PADDING * 2 + headerWidthSlack);
187
194
  });
188
195
  const usableWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
196
+ const minimumColumnWidth = Math.min(MIN_COLUMN_WIDTH, usableWidth / columnCount);
189
197
  const totalColumnWidth = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0);
190
198
  if (totalColumnWidth > usableWidth) {
191
199
  const scale = usableWidth / totalColumnWidth;
192
200
  columns.forEach((c) => {
193
- const headerMin = headerMinWidths.get(c) ?? MIN_COLUMN_WIDTH;
194
- c.width = Math.max(Math.floor((c.width ?? MIN_COLUMN_WIDTH) * scale), headerMin, MIN_COLUMN_WIDTH);
201
+ const width = c.width ?? MIN_COLUMN_WIDTH;
202
+ const headerMin = headerMinWidths.get(c) ?? minimumColumnWidth;
203
+ const minimumWidth = Math.min(width, Math.max(headerMin, minimumColumnWidth));
204
+ c.width = Math.max(Math.floor(width * scale), minimumWidth);
195
205
  });
196
- let excess = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0) - usableWidth;
197
- for (let i = columns.length - 1; i >= 0 && excess > 0; i--) {
198
- const col = columns[i];
199
- const available = (col.width ?? MIN_COLUMN_WIDTH) - MIN_COLUMN_WIDTH;
200
- const trim = Math.min(excess, available);
201
- if (trim > 0) {
202
- col.width = (col.width ?? MIN_COLUMN_WIDTH) - trim;
203
- excess -= trim;
206
+ const excess = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0) - usableWidth;
207
+ if (excess > 0) {
208
+ const reducibleWidth = columns.reduce(
209
+ (sum, c) => sum + Math.max((c.width ?? MIN_COLUMN_WIDTH) - minimumColumnWidth, 0),
210
+ 0
211
+ );
212
+ if (reducibleWidth > 0) {
213
+ columns.forEach((c) => {
214
+ const width = c.width ?? MIN_COLUMN_WIDTH;
215
+ const available = Math.max(width - minimumColumnWidth, 0);
216
+ c.width = width - excess * (available / reducibleWidth);
217
+ });
204
218
  }
205
219
  }
206
220
  } else if (totalColumnWidth < usableWidth) {
207
- const scale = usableWidth / totalColumnWidth;
208
- columns.forEach((c) => {
209
- c.width = Math.floor((c.width ?? MIN_COLUMN_WIDTH) * scale);
210
- });
211
- const expanded = columns.reduce((sum, c) => sum + (c.width ?? 0), 0);
212
- columns[columns.length - 1].width = (columns[columns.length - 1].width ?? 0) + (usableWidth - expanded);
221
+ const expandableColumns = columns.filter((c) => !boundedColumns.has(c));
222
+ const expandableWidth = expandableColumns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0);
223
+ if (expandableWidth > 0) {
224
+ const scale = (expandableWidth + usableWidth - totalColumnWidth) / expandableWidth;
225
+ expandableColumns.forEach((c) => {
226
+ c.width = Math.floor((c.width ?? MIN_COLUMN_WIDTH) * scale);
227
+ });
228
+ const expanded = columns.reduce((sum, c) => sum + (c.width ?? 0), 0);
229
+ const lastExpandableColumn = expandableColumns[expandableColumns.length - 1];
230
+ lastExpandableColumn.width = (lastExpandableColumn.width ?? 0) + (usableWidth - expanded);
231
+ }
213
232
  }
214
233
  const tableWidth = columns.reduce((sum, c) => sum + (c.width ?? MIN_COLUMN_WIDTH), 0);
215
234
  const pageBottom = doc.page.height - doc.page.margins.bottom;
216
- const calculateRowHeight = (row, isHeader = false) => {
217
- const fontSize = isHeader ? headerFontSize : dataFontSize;
218
- doc.font(isHeader ? FONT_BOLD : FONT_REGULAR).fontSize(fontSize);
219
- let maxLines = 1;
220
- columns.forEach((col) => {
221
- const cellValue = isHeader ? col.header : row?.[col.key] !== void 0 ? String(row[col.key]) : "";
222
- const textWidth = doc.widthOfString(cellValue);
223
- const availableWidth = (col.width ?? MIN_COLUMN_WIDTH) - TABLE_CELL_PADDING * 2;
224
- const lines = Math.ceil(textWidth / availableWidth);
225
- if (lines > maxLines) {
226
- maxLines = lines;
227
- }
228
- });
229
- return Math.max(maxLines * (fontSize + 2) + TABLE_CELL_PADDING * 2, DEFAULT_ROW_HEIGHT);
235
+ const calculateRowHeight = (isHeader = false) => {
236
+ doc.font(isHeader ? FONT_BOLD : FONT_REGULAR).fontSize(isHeader ? headerFontSize : dataFontSize);
237
+ return Math.max(doc.currentLineHeight(true) + TABLE_CELL_PADDING * 2, DEFAULT_ROW_HEIGHT);
230
238
  };
231
- const headerHeight = calculateRowHeight(null, true);
239
+ const headerHeight = calculateRowHeight(true);
232
240
  const drawTableHeader = (y) => this.drawHeader(doc, columns, y, tableWidth, headerHeight, TABLE_CELL_PADDING, headerFontSize, MIN_COLUMN_WIDTH);
233
241
  let tableStartY = startY;
234
242
  if (tableStartY + headerHeight > pageBottom) {
@@ -238,7 +246,7 @@ var PdfExporter = class {
238
246
  let currentY = drawTableHeader(tableStartY);
239
247
  doc.font(FONT_REGULAR).fontSize(dataFontSize);
240
248
  rows.forEach((row, rowIndex) => {
241
- const currentRowHeight = calculateRowHeight(row, false);
249
+ const currentRowHeight = calculateRowHeight();
242
250
  if (currentY + currentRowHeight > pageBottom) {
243
251
  doc.addPage();
244
252
  currentY = drawTableHeader(doc.page.margins.top);
@@ -261,6 +269,28 @@ var PdfExporter = class {
261
269
  doc.strokeColor(HEADER_RULE_COLOR).lineWidth(HEADER_RULE_WIDTH).moveTo(left, closingRuleY).lineTo(left + tableWidth, closingRuleY).stroke();
262
270
  return closingRuleY;
263
271
  }
272
+ truncateTextToWidth(doc, text, availableWidth) {
273
+ if (doc.widthOfString(text) <= availableWidth) {
274
+ return text;
275
+ }
276
+ const ellipsis = "\u2026";
277
+ if (doc.widthOfString(ellipsis) > availableWidth) {
278
+ return "";
279
+ }
280
+ const characters = Array.from(text);
281
+ let low = 0;
282
+ let high = characters.length;
283
+ while (low < high) {
284
+ const middle = Math.ceil((low + high) / 2);
285
+ const candidate = `${characters.slice(0, middle).join("").trimEnd()}${ellipsis}`;
286
+ if (doc.widthOfString(candidate) <= availableWidth) {
287
+ low = middle;
288
+ } else {
289
+ high = middle - 1;
290
+ }
291
+ }
292
+ return `${characters.slice(0, low).join("").trimEnd()}${ellipsis}`;
293
+ }
264
294
  drawHeader(doc, columns, startY, tableWidth, headerHeight, cellPadding, headerFontSize, minColumnWidth) {
265
295
  const left = doc.page.margins.left;
266
296
  doc.rect(left, startY, tableWidth, headerHeight).fill(HEADER_BACKGROUND_COLOR);
@@ -270,12 +300,15 @@ var PdfExporter = class {
270
300
  const headerLineHeight = doc.currentLineHeight(true);
271
301
  const headerTextY = startY + Math.max((headerHeight - headerLineHeight) / 2, 0);
272
302
  columns.forEach((col) => {
273
- doc.text(col.header, xPosition + cellPadding, headerTextY, {
274
- width: (col.width ?? minColumnWidth) - cellPadding * 2,
303
+ const innerWidth = (col.width ?? minColumnWidth) - cellPadding * 2;
304
+ const bounded = col.size !== void 0 && Number.isFinite(col.size) && col.size > 0;
305
+ const headerText = bounded ? this.truncateTextToWidth(doc, col.header, innerWidth) : col.header;
306
+ doc.text(headerText, xPosition + cellPadding, headerTextY, {
307
+ width: innerWidth,
275
308
  height: headerLineHeight,
276
309
  align: "left",
277
310
  lineBreak: false,
278
- ellipsis: true
311
+ ellipsis: !bounded
279
312
  });
280
313
  xPosition += col.width ?? minColumnWidth;
281
314
  });
@@ -296,12 +329,15 @@ var PdfExporter = class {
296
329
  const rawValue = row[col.key];
297
330
  const cellValue = typeof rawValue === "string" || typeof rawValue === "number" || typeof rawValue === "boolean" ? String(rawValue) : "";
298
331
  const isNumeric = typeof rawValue === "number" || typeof rawValue === "string" && rawValue !== "" && !isNaN(Number(rawValue));
299
- doc.text(cellValue, xPosition + cellPadding, dataTextY, {
300
- width: (col.width ?? minColumnWidth) - cellPadding * 2,
332
+ const innerWidth = (col.width ?? minColumnWidth) - cellPadding * 2;
333
+ const bounded = col.size !== void 0 && Number.isFinite(col.size) && col.size > 0;
334
+ const rowText = bounded ? this.truncateTextToWidth(doc, cellValue, innerWidth) : cellValue;
335
+ doc.text(rowText, xPosition + cellPadding, dataTextY, {
336
+ width: innerWidth,
301
337
  height: dataLineHeight,
302
338
  align: isNumeric ? "right" : "left",
303
339
  lineBreak: false,
304
- ellipsis: true
340
+ ellipsis: !bounded
305
341
  });
306
342
  xPosition += col.width ?? minColumnWidth;
307
343
  if (i < columns.length - 1) {
@@ -2,6 +2,7 @@ interface ExportTableColumn {
2
2
  header: string;
3
3
  key: string;
4
4
  width?: number;
5
+ size?: number;
5
6
  }
6
7
  interface ExportTable {
7
8
  columns: ExportTableColumn[];
@@ -2,6 +2,7 @@ interface ExportTableColumn {
2
2
  header: string;
3
3
  key: string;
4
4
  width?: number;
5
+ size?: number;
5
6
  }
6
7
  interface ExportTable {
7
8
  columns: ExportTableColumn[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harbortouch/skytab-analytics-report-utils",
3
- "version": "0.41.11",
3
+ "version": "0.41.12",
4
4
  "description": "Centralized report column presentation configuration for SkyTab Analytics",
5
5
  "engines": {
6
6
  "node": ">=22.6.0",