@angular-bootstrap/ngbootstrap 2.0.1 → 2.0.3
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.3 - 2026-07-05
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- Replaced the default PDF export implementation with a dependency-free browser PDF writer so Angular apps do not need jsPDF optional HTML/canvas dependencies for basic table export.
|
|
8
|
+
- Removed `jspdf` and `jspdf-autotable` from optional peer dependencies.
|
|
9
|
+
|
|
10
|
+
## 2.0.2 - 2026-07-05
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Fixed PDF export bundling by loading jsPDF from its browser UMD bundle, avoiding build-time resolution of jsPDF optional ESM dependencies.
|
|
15
|
+
|
|
3
16
|
## 2.0.1 - 2026-07-05
|
|
4
17
|
|
|
5
18
|
### Fixed
|
package/README.md
CHANGED
|
@@ -29,15 +29,21 @@ npm install @angular-bootstrap/ngbootstrap bootstrap bootstrap-icons
|
|
|
29
29
|
Optional integrations are installed only when you use those features:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
npm install chart.js
|
|
32
|
+
npm install chart.js
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
PDF and Excel export are dependency-free by default.
|
|
36
|
+
|
|
35
37
|
Excel export is dependency-free by default. The built-in `BrowserExcelExportAdapter`
|
|
36
38
|
generates an Excel-compatible workbook in the browser and avoids unmaintained
|
|
37
39
|
spreadsheet writer dependencies. It is intended for visible column values and
|
|
38
40
|
basic scalar cell types; use a custom `ExcelExportAdapter` for formulas, charts,
|
|
39
41
|
multiple sheets, workbook styling, or other advanced workbook features.
|
|
40
42
|
|
|
43
|
+
The built-in PDF adapter generates a simple table PDF in the browser. Provide a custom
|
|
44
|
+
`PdfExportAdapter` when your product needs branded PDFs, images, charts, advanced layout,
|
|
45
|
+
rich typography, headers, footers, or more precise pagination.
|
|
46
|
+
|
|
41
47
|
## Use
|
|
42
48
|
|
|
43
49
|
Import standalone components directly in your Angular component.
|
package/RELEASE_NOTES.md
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
# @angular-bootstrap/ngbootstrap 2.0.
|
|
1
|
+
# @angular-bootstrap/ngbootstrap 2.0.3
|
|
2
2
|
|
|
3
|
-
This patch
|
|
3
|
+
This patch makes the default PDF export path dependency-free.
|
|
4
4
|
|
|
5
5
|
## Fixed
|
|
6
6
|
|
|
7
|
-
- `JsPdfAdapter`
|
|
8
|
-
-
|
|
7
|
+
- `JsPdfAdapter` now generates a simple table PDF directly in the browser.
|
|
8
|
+
- Removed `jspdf` and `jspdf-autotable` from optional peer dependencies.
|
|
9
|
+
- Angular apps no longer need jsPDF optional HTML/canvas dependencies for basic DataGrid PDF export.
|
|
9
10
|
|
|
10
11
|
## Notes
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
PDF and Excel export are dependency-free by default. The built-in PDF adapter is intended for simple table export only.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
npm install jspdf jspdf-autotable
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Excel export remains dependency-free through `BrowserExcelExportAdapter`.
|
|
15
|
+
Use a custom `PdfExportAdapter` for branded layouts, images, charts, rich typography, or advanced pagination.
|
|
@@ -1249,13 +1249,104 @@ class NgbDatagridDefaultEditService {
|
|
|
1249
1249
|
}
|
|
1250
1250
|
}
|
|
1251
1251
|
|
|
1252
|
+
const PAGE_SIZES = {
|
|
1253
|
+
A4: [595.28, 841.89],
|
|
1254
|
+
Letter: [612, 792],
|
|
1255
|
+
};
|
|
1252
1256
|
class JsPdfAdapter {
|
|
1253
|
-
async export(
|
|
1254
|
-
const
|
|
1255
|
-
const
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1257
|
+
async export(payload) {
|
|
1258
|
+
const pdf = this.createPdf(payload);
|
|
1259
|
+
const blob = new Blob([pdf], { type: 'application/pdf' });
|
|
1260
|
+
const url = URL.createObjectURL(blob);
|
|
1261
|
+
const link = document.createElement('a');
|
|
1262
|
+
link.href = url;
|
|
1263
|
+
link.download = `${payload.fileName}.pdf`;
|
|
1264
|
+
link.click();
|
|
1265
|
+
URL.revokeObjectURL(url);
|
|
1266
|
+
}
|
|
1267
|
+
createPdf({ fileName, columns, rows, options }) {
|
|
1268
|
+
const requestedSize = String(options?.pageSize || 'A4');
|
|
1269
|
+
const baseSize = PAGE_SIZES[requestedSize] || PAGE_SIZES.A4;
|
|
1270
|
+
const landscape = Boolean(options?.landscape);
|
|
1271
|
+
const [pageWidth, pageHeight] = landscape ? [baseSize[1], baseSize[0]] : baseSize;
|
|
1272
|
+
const margin = this.resolveMargin(options?.margins);
|
|
1273
|
+
const lineHeight = 14;
|
|
1274
|
+
const maxLines = Math.max(1, Math.floor((pageHeight - margin.top - margin.bottom - 52) / lineHeight));
|
|
1275
|
+
const tableLines = this.toTableLines(columns, rows);
|
|
1276
|
+
const pages = this.chunk(tableLines, maxLines);
|
|
1277
|
+
const objects = [];
|
|
1278
|
+
const pageIds = [];
|
|
1279
|
+
objects.push('<< /Type /Catalog /Pages 2 0 R >>');
|
|
1280
|
+
objects.push('');
|
|
1281
|
+
for (const [index, lines] of pages.entries()) {
|
|
1282
|
+
const pageObjectId = objects.length + 1;
|
|
1283
|
+
const contentObjectId = pageObjectId + 1;
|
|
1284
|
+
pageIds.push(pageObjectId);
|
|
1285
|
+
objects.push(`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${pageWidth} ${pageHeight}] /Resources << /Font << /F1 ${pages.length * 2 + 3} 0 R >> >> /Contents ${contentObjectId} 0 R >>`);
|
|
1286
|
+
objects.push(this.contentStream(fileName, lines, index + 1, pages.length, pageHeight, margin, lineHeight));
|
|
1287
|
+
}
|
|
1288
|
+
objects[1] = `<< /Type /Pages /Kids [${pageIds.map(id => `${id} 0 R`).join(' ')}] /Count ${pageIds.length} >>`;
|
|
1289
|
+
objects.push('<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>');
|
|
1290
|
+
return this.serializePdf(objects);
|
|
1291
|
+
}
|
|
1292
|
+
resolveMargin(margins) {
|
|
1293
|
+
if (!Array.isArray(margins) || margins.length !== 4) {
|
|
1294
|
+
return { top: 40, right: 40, bottom: 40, left: 40 };
|
|
1295
|
+
}
|
|
1296
|
+
const [top, right, bottom, left] = margins.map(value => Math.max(16, Number(value) || 40));
|
|
1297
|
+
return { top, right, bottom, left };
|
|
1298
|
+
}
|
|
1299
|
+
toTableLines(columns, rows) {
|
|
1300
|
+
const headers = columns.map(column => this.printable(column));
|
|
1301
|
+
const body = rows.map(row => columns.map(column => this.printable(row?.[column])));
|
|
1302
|
+
const widths = headers.map((header, index) => Math.min(24, Math.max(header.length, ...body.map(values => values[index]?.length || 0))));
|
|
1303
|
+
const format = (values) => values.map((value, index) => value.padEnd(widths[index]).slice(0, widths[index])).join(' ');
|
|
1304
|
+
return [
|
|
1305
|
+
format(headers),
|
|
1306
|
+
widths.map(width => '-'.repeat(width)).join(' '),
|
|
1307
|
+
...body.map(format),
|
|
1308
|
+
];
|
|
1309
|
+
}
|
|
1310
|
+
contentStream(fileName, lines, page, totalPages, pageHeight, margin, lineHeight) {
|
|
1311
|
+
const commands = [
|
|
1312
|
+
'BT',
|
|
1313
|
+
`/F1 16 Tf ${margin.left} ${pageHeight - margin.top} Td (${this.pdfText(fileName)}) Tj`,
|
|
1314
|
+
`/F1 9 Tf 0 -24 Td (${this.pdfText(`Page ${page} of ${totalPages}`)}) Tj`,
|
|
1315
|
+
...lines.map(line => `0 -${lineHeight} Td (${this.pdfText(line)}) Tj`),
|
|
1316
|
+
'ET',
|
|
1317
|
+
].join('\n');
|
|
1318
|
+
return `<< /Length ${commands.length} >>\nstream\n${commands}\nendstream`;
|
|
1319
|
+
}
|
|
1320
|
+
chunk(items, size) {
|
|
1321
|
+
const pages = [];
|
|
1322
|
+
for (let index = 0; index < items.length; index += size) {
|
|
1323
|
+
pages.push(items.slice(index, index + size));
|
|
1324
|
+
}
|
|
1325
|
+
return pages.length ? pages : [[]];
|
|
1326
|
+
}
|
|
1327
|
+
printable(value) {
|
|
1328
|
+
if (value === null || value === undefined) {
|
|
1329
|
+
return '';
|
|
1330
|
+
}
|
|
1331
|
+
return String(value).replace(/[^\x20-\x7E]/g, '?').replace(/\s+/g, ' ').trim();
|
|
1332
|
+
}
|
|
1333
|
+
pdfText(value) {
|
|
1334
|
+
return value.replace(/\\/g, '\\\\').replace(/\(/g, '\\(').replace(/\)/g, '\\)');
|
|
1335
|
+
}
|
|
1336
|
+
serializePdf(objects) {
|
|
1337
|
+
let pdf = '%PDF-1.4\n';
|
|
1338
|
+
const offsets = [0];
|
|
1339
|
+
for (const [index, object] of objects.entries()) {
|
|
1340
|
+
offsets[index + 1] = pdf.length;
|
|
1341
|
+
pdf += `${index + 1} 0 obj\n${object}\nendobj\n`;
|
|
1342
|
+
}
|
|
1343
|
+
const xrefOffset = pdf.length;
|
|
1344
|
+
pdf += `xref\n0 ${objects.length + 1}\n0000000000 65535 f \n`;
|
|
1345
|
+
for (let index = 1; index <= objects.length; index++) {
|
|
1346
|
+
pdf += `${String(offsets[index]).padStart(10, '0')} 00000 n \n`;
|
|
1347
|
+
}
|
|
1348
|
+
pdf += `trailer\n<< /Size ${objects.length + 1} /Root 1 0 R >>\nstartxref\n${xrefOffset}\n%%EOF`;
|
|
1349
|
+
return pdf;
|
|
1259
1350
|
}
|
|
1260
1351
|
}
|
|
1261
1352
|
|
|
@@ -14254,8 +14345,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImpor
|
|
|
14254
14345
|
args: ['nodeBtn', { read: ElementRef }]
|
|
14255
14346
|
}] } });
|
|
14256
14347
|
|
|
14257
|
-
/// <reference path="./types/optional-peer-deps.d.ts" />
|
|
14258
|
-
|
|
14259
14348
|
/**
|
|
14260
14349
|
* Generated bundle index. Do not edit.
|
|
14261
14350
|
*/
|