@svgrid/enterprise 2.5.2 → 2.6.0
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/SvAlertRuleEditor.svelte.d.ts +13 -0
- package/dist/SvAlertsManager.svelte.d.ts +14 -0
- package/dist/SvAlertsPanel.svelte.d.ts +9 -0
- package/dist/SvAuthGate.svelte.d.ts +22 -0
- package/dist/SvBoard.svelte.d.ts +35 -0
- package/dist/SvExportMenu.svelte.d.ts +61 -0
- package/dist/SvExpressionEditor.svelte.d.ts +12 -0
- package/dist/SvFileInput.svelte.d.ts +20 -0
- package/dist/SvGridAlerts.svelte.d.ts +77 -0
- package/dist/SvGridBoard.svelte.d.ts +30 -0
- package/dist/SvGridEditPanel.svelte.d.ts +73 -0
- package/dist/SvGridMasterDetail.svelte.d.ts +49 -0
- package/dist/SvGridScheduler.svelte.d.ts +30 -0
- package/dist/SvImportDialog.svelte.d.ts +88 -0
- package/dist/SvLookupInput.svelte.d.ts +21 -0
- package/dist/SvPivotDesigner.svelte.d.ts +176 -0
- package/dist/SvRecordDetail.svelte.d.ts +58 -0
- package/dist/SvSchedule.svelte.d.ts +29 -0
- package/dist/SvSchemaChart.svelte.d.ts +61 -0
- package/dist/SvSchemaDashboard.svelte.d.ts +46 -0
- package/dist/alerts/alert-engine-attach.d.ts +79 -0
- package/dist/alerts/alert-engine-attach.js +106 -0
- package/dist/alerts/alert-engine.d.ts +42 -0
- package/dist/alerts/alert-engine.js +196 -0
- package/dist/alerts/alert-formats.d.ts +26 -0
- package/dist/alerts/alert-formats.js +58 -0
- package/dist/alerts/alert-observer.d.ts +54 -0
- package/dist/alerts/alert-observer.js +156 -0
- package/dist/alerts/alert-scheduler.d.ts +30 -0
- package/dist/alerts/alert-scheduler.js +54 -0
- package/dist/alerts/alert-storage.d.ts +31 -0
- package/dist/alerts/alert-storage.js +92 -0
- package/dist/alerts/alert-store.svelte.d.ts +24 -0
- package/dist/alerts/alert-store.svelte.js +69 -0
- package/dist/alerts/alert-types.d.ts +86 -0
- package/dist/alerts/alert-types.js +1 -0
- package/dist/alerts.d.ts +2 -0
- package/{src/alerts.ts → dist/alerts.js} +9 -9
- package/dist/board.d.ts +7 -0
- package/{src/board.ts → dist/board.js} +13 -14
- package/dist/cdn/svgrid-enterprise.svelte-external.js +2987 -2981
- package/dist/designer/assets/{index-CM5xvaOy.js → index-BF0UH638.js} +3 -3
- package/dist/designer/assets/{src-D-ET9Mmo.js → src-BPJruwB9.js} +184 -184
- package/dist/designer/index.html +2 -2
- package/dist/edit-panel.d.ts +105 -0
- package/dist/edit-panel.js +350 -0
- package/dist/export-conditional.d.ts +31 -0
- package/dist/export-conditional.js +65 -0
- package/dist/export-ooxml.d.ts +93 -0
- package/dist/export-ooxml.js +385 -0
- package/dist/export-pdf.d.ts +107 -0
- package/dist/export-pdf.js +144 -0
- package/dist/export-print.d.ts +55 -0
- package/dist/export-print.js +91 -0
- package/{src/export-serialize.ts → dist/export-serialize.d.ts} +11 -24
- package/dist/export-serialize.js +11 -0
- package/dist/export-xls.d.ts +26 -0
- package/dist/export-xls.js +157 -0
- package/dist/export.d.ts +323 -0
- package/dist/export.js +1021 -0
- package/dist/expressions/evaluate.d.ts +17 -0
- package/dist/expressions/evaluate.js +214 -0
- package/dist/expressions/expression-columns.d.ts +61 -0
- package/dist/expressions/expression-columns.js +72 -0
- package/dist/expressions/expression-types.d.ts +125 -0
- package/dist/expressions/expression-types.js +1 -0
- package/dist/expressions/parse.d.ts +25 -0
- package/dist/expressions/parse.js +537 -0
- package/dist/import.d.ts +232 -0
- package/dist/import.js +846 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +91 -0
- package/dist/install.d.ts +70 -0
- package/dist/install.js +61 -0
- package/dist/license-core.d.ts +37 -0
- package/dist/license-core.js +81 -0
- package/dist/license.d.ts +30 -0
- package/dist/license.js +139 -0
- package/dist/master-detail.d.ts +23 -0
- package/dist/master-detail.js +35 -0
- package/dist/node/studio.js +1 -1
- package/dist/pivot-chart.d.ts +28 -0
- package/dist/pivot-chart.js +71 -0
- package/dist/pivot-designer.d.ts +69 -0
- package/dist/pivot-designer.js +38 -0
- package/dist/pivot-enable.d.ts +6 -0
- package/{src/pivot-enable.ts → dist/pivot-enable.js} +13 -18
- package/dist/pivot.d.ts +111 -0
- package/dist/pivot.js +353 -0
- package/dist/print.d.ts +33 -0
- package/dist/print.js +61 -0
- package/dist/revoked.d.ts +1 -0
- package/{src/revoked.ts → dist/revoked.js} +5 -6
- package/dist/scheduler-assignments.d.ts +64 -0
- package/dist/scheduler-assignments.js +92 -0
- package/dist/scheduler-axis.d.ts +81 -0
- package/dist/scheduler-axis.js +180 -0
- package/{src/scheduler-booking.ts → dist/scheduler-booking.d.ts} +10 -43
- package/dist/scheduler-booking.js +50 -0
- package/dist/scheduler-config.d.ts +177 -0
- package/dist/scheduler-config.js +1 -0
- package/dist/scheduler-dependencies.d.ts +89 -0
- package/dist/scheduler-dependencies.js +179 -0
- package/dist/scheduler-freebusy.d.ts +19 -0
- package/{src/scheduler-freebusy.ts → dist/scheduler-freebusy.js} +9 -19
- package/{src/scheduler-heatmap.ts → dist/scheduler-heatmap.d.ts} +12 -35
- package/dist/scheduler-heatmap.js +41 -0
- package/dist/scheduler-resource-tree.d.ts +48 -0
- package/dist/scheduler-resource-tree.js +78 -0
- package/dist/scheduler-slots.d.ts +37 -0
- package/dist/scheduler-slots.js +80 -0
- package/dist/scheduler-summary.d.ts +41 -0
- package/dist/scheduler-summary.js +54 -0
- package/dist/scheduler.d.ts +7 -0
- package/{src/scheduler.ts → dist/scheduler.js} +13 -14
- package/dist/scheduling.d.ts +132 -0
- package/dist/scheduling.js +228 -0
- package/dist/schema-designer.d.ts +42 -0
- package/dist/schema-designer.js +117 -0
- package/dist/schema.d.ts +369 -0
- package/dist/schema.js +216 -0
- package/dist/smart-shim.d.ts +62 -0
- package/dist/smart-shim.js +34 -0
- package/dist/sources/aggregate.d.ts +43 -0
- package/dist/sources/aggregate.js +60 -0
- package/dist/sources/auth-supabase.d.ts +69 -0
- package/dist/sources/auth-supabase.js +39 -0
- package/dist/sources/dashboard.d.ts +65 -0
- package/dist/sources/dashboard.js +95 -0
- package/dist/sources/field-inference.d.ts +23 -0
- package/dist/sources/field-inference.js +52 -0
- package/dist/sources/filters.d.ts +35 -0
- package/dist/sources/filters.js +52 -0
- package/dist/sources/index.d.ts +18 -0
- package/dist/sources/index.js +18 -0
- package/dist/sources/introspect-supabase.d.ts +30 -0
- package/dist/sources/introspect-supabase.js +134 -0
- package/dist/sources/realtime-supabase.d.ts +37 -0
- package/dist/sources/realtime-supabase.js +52 -0
- package/dist/sources/relation-lookup.d.ts +50 -0
- package/dist/sources/relation-lookup.js +77 -0
- package/dist/sources/rest-adapters.d.ts +48 -0
- package/dist/sources/rest-adapters.js +85 -0
- package/dist/sources/rest.d.ts +38 -0
- package/dist/sources/rest.js +86 -0
- package/dist/sources/schema-from-columns.d.ts +37 -0
- package/dist/sources/schema-from-columns.js +48 -0
- package/dist/sources/supabase.d.ts +33 -0
- package/dist/sources/supabase.js +74 -0
- package/dist/sources/with-entity-rules.d.ts +25 -0
- package/dist/sources/with-entity-rules.js +47 -0
- package/dist/sources/with-relation-labels.d.ts +26 -0
- package/dist/sources/with-relation-labels.js +38 -0
- package/dist/staged-editing.d.ts +85 -0
- package/dist/staged-editing.js +0 -0
- package/dist/studio/bug-report.d.ts +61 -0
- package/dist/studio/bug-report.js +126 -0
- package/dist/studio/cli.d.ts +56 -0
- package/dist/studio/cli.js +70 -0
- package/dist/studio/copilot-core.d.ts +22 -0
- package/{src/studio/copilot-core.ts → dist/studio/copilot-core.js} +34 -38
- package/dist/studio/csv.d.ts +36 -0
- package/dist/studio/csv.js +173 -0
- package/dist/studio/db-connect-string.d.ts +45 -0
- package/dist/studio/db-connect-string.js +113 -0
- package/dist/studio/deploy-cli.d.ts +35 -0
- package/dist/studio/deploy-cli.js +81 -0
- package/dist/studio/emit-project.d.ts +77 -0
- package/dist/studio/emit-project.js +5389 -0
- package/dist/studio/emit-schema.d.ts +110 -0
- package/dist/studio/emit-schema.js +1128 -0
- package/dist/studio/index.d.ts +35 -0
- package/dist/studio/index.js +38 -0
- package/dist/studio/init-flow.d.ts +58 -0
- package/dist/studio/init-flow.js +451 -0
- package/dist/studio/introspect-db.d.ts +47 -0
- package/dist/studio/introspect-db.js +255 -0
- package/dist/studio/introspect-openapi.d.ts +19 -0
- package/dist/studio/introspect-openapi.js +244 -0
- package/dist/studio/introspect-prisma.d.ts +25 -0
- package/dist/studio/introspect-prisma.js +164 -0
- package/dist/studio/introspect.d.ts +35 -0
- package/dist/studio/introspect.js +306 -0
- package/dist/studio/project.d.ts +1327 -0
- package/dist/studio/project.js +1798 -0
- package/dist/studio/sample-data.d.ts +19 -0
- package/dist/studio/sample-data.js +207 -0
- package/dist/studio/samples/ats.d.ts +2 -0
- package/dist/studio/samples/ats.js +194 -0
- package/dist/studio/samples/clinic.d.ts +2 -0
- package/dist/studio/samples/clinic.js +171 -0
- package/dist/studio/samples/crm.d.ts +2 -0
- package/dist/studio/samples/crm.js +280 -0
- package/dist/studio/samples/datasets.d.ts +38 -0
- package/dist/studio/samples/datasets.js +296 -0
- package/dist/studio/samples/ecommerce.d.ts +2 -0
- package/dist/studio/samples/ecommerce.js +178 -0
- package/dist/studio/samples/events.d.ts +2 -0
- package/dist/studio/samples/events.js +192 -0
- package/dist/studio/samples/fleet.d.ts +2 -0
- package/dist/studio/samples/fleet.js +203 -0
- package/dist/studio/samples/gym.d.ts +2 -0
- package/dist/studio/samples/gym.js +205 -0
- package/dist/studio/samples/hr.d.ts +2 -0
- package/dist/studio/samples/hr.js +186 -0
- package/dist/studio/samples/index.d.ts +13 -0
- package/dist/studio/samples/index.js +44 -0
- package/dist/studio/samples/insurance.d.ts +2 -0
- package/dist/studio/samples/insurance.js +220 -0
- package/dist/studio/samples/inventory.d.ts +2 -0
- package/dist/studio/samples/inventory.js +204 -0
- package/dist/studio/samples/invoicing.d.ts +2 -0
- package/dist/studio/samples/invoicing.js +154 -0
- package/dist/studio/samples/library.d.ts +2 -0
- package/dist/studio/samples/library.js +171 -0
- package/dist/studio/samples/live-data.d.ts +5 -0
- package/dist/studio/samples/live-data.js +261 -0
- package/dist/studio/samples/projects.d.ts +2 -0
- package/dist/studio/samples/projects.js +203 -0
- package/dist/studio/samples/realestate.d.ts +2 -0
- package/dist/studio/samples/realestate.js +187 -0
- package/dist/studio/samples/restaurant.d.ts +2 -0
- package/dist/studio/samples/restaurant.js +180 -0
- package/dist/studio/samples/school.d.ts +2 -0
- package/dist/studio/samples/school.js +188 -0
- package/dist/studio/samples/shared.d.ts +142 -0
- package/dist/studio/samples/shared.js +164 -0
- package/dist/studio/samples/starter.d.ts +4 -0
- package/dist/studio/samples/starter.js +237 -0
- package/dist/studio/samples/subscriptions.d.ts +2 -0
- package/dist/studio/samples/subscriptions.js +175 -0
- package/dist/studio/samples/support.d.ts +2 -0
- package/dist/studio/samples/support.js +209 -0
- package/dist/studio/scaffold-app.d.ts +22 -0
- package/{src/studio/scaffold-app.ts → dist/studio/scaffold-app.js} +45 -63
- package/dist/studio/scaffold.d.ts +63 -0
- package/dist/studio/scaffold.js +346 -0
- package/dist/studio/screen-suites.d.ts +193 -0
- package/dist/studio/screen-suites.js +297 -0
- package/dist/studio/themes.d.ts +34 -0
- package/dist/studio/themes.js +41 -0
- package/dist/studio/ui-components.d.ts +89 -0
- package/dist/studio/ui-components.generated.d.ts +29 -0
- package/dist/studio/ui-components.generated.js +7139 -0
- package/dist/studio/ui-components.js +673 -0
- package/dist/studio/user-error.d.ts +23 -0
- package/{src/studio/user-error.ts → dist/studio/user-error.js} +10 -13
- package/dist/studio/verify.d.ts +34 -0
- package/dist/studio/verify.js +47 -0
- package/dist/sveltekit/in-memory.d.ts +14 -0
- package/dist/sveltekit/in-memory.js +113 -0
- package/dist/sveltekit/index.d.ts +6 -0
- package/dist/sveltekit/index.js +5 -0
- package/dist/sveltekit/query-plan.d.ts +44 -0
- package/dist/sveltekit/query-plan.js +76 -0
- package/dist/sveltekit/sql-source.d.ts +47 -0
- package/dist/sveltekit/sql-source.js +106 -0
- package/dist/sveltekit/sql.d.ts +48 -0
- package/dist/sveltekit/sql.js +73 -0
- package/dist/sveltekit/transport.d.ts +209 -0
- package/dist/sveltekit/transport.js +219 -0
- package/{src/sveltekit/types.ts → dist/sveltekit/types.d.ts} +8 -11
- package/dist/sveltekit/types.js +1 -0
- package/dist/upgrade-prompt.d.ts +16 -0
- package/dist/upgrade-prompt.js +141 -0
- package/dist/version.d.ts +13 -0
- package/{src/version.ts → dist/version.js} +1 -1
- package/dist/watermark.d.ts +2 -0
- package/dist/watermark.js +146 -0
- package/package.json +26 -24
- package/src/SvBoard.dom.test.ts +0 -67
- package/src/SvGridAlerts.dom.test.ts +0 -113
- package/src/SvGridEditPanel.dom.test.ts +0 -320
- package/src/SvRecordDetail.dom.test.ts +0 -137
- package/src/SvSchedule.dom.test.ts +0 -57
- package/src/ai-export-pdf.dom.test.ts +0 -77
- package/src/ai-export-xlsx.dom.test.ts +0 -90
- package/src/ai-export.dom.test.ts +0 -114
- package/src/alerts/alert-engine-attach.ts +0 -165
- package/src/alerts/alert-engine.test.ts +0 -135
- package/src/alerts/alert-engine.ts +0 -260
- package/src/alerts/alert-formats.test.ts +0 -87
- package/src/alerts/alert-formats.ts +0 -77
- package/src/alerts/alert-observer.test.ts +0 -189
- package/src/alerts/alert-observer.ts +0 -208
- package/src/alerts/alert-scheduler.ts +0 -96
- package/src/alerts/alert-storage.test.ts +0 -54
- package/src/alerts/alert-storage.ts +0 -116
- package/src/alerts/alert-store.svelte.ts +0 -80
- package/src/alerts/alert-types.ts +0 -94
- package/src/board.dom.test.ts +0 -941
- package/src/edit-panel.test.ts +0 -322
- package/src/edit-panel.ts +0 -379
- package/src/export-conditional.test.ts +0 -60
- package/src/export-conditional.ts +0 -94
- package/src/export-ooxml.test.ts +0 -152
- package/src/export-ooxml.ts +0 -489
- package/src/export-pdf.test.ts +0 -138
- package/src/export-pdf.ts +0 -245
- package/src/export-print.test.ts +0 -66
- package/src/export-print.ts +0 -132
- package/src/export-serialize.test.ts +0 -56
- package/src/export-xls.ts +0 -199
- package/src/export-xlsx-roundtrip.test.ts +0 -120
- package/src/export-xlsx.test.ts +0 -150
- package/src/export.test.ts +0 -349
- package/src/export.ts +0 -1464
- package/src/expressions/evaluate.test.ts +0 -111
- package/src/expressions/evaluate.ts +0 -237
- package/src/expressions/expression-columns.ts +0 -133
- package/src/expressions/expression-types.ts +0 -84
- package/src/expressions/parse.test.ts +0 -106
- package/src/expressions/parse.ts +0 -610
- package/src/import-automap.test.ts +0 -131
- package/src/import-hardening.test.ts +0 -158
- package/src/import.test.ts +0 -415
- package/src/import.ts +0 -1044
- package/src/index.ts +0 -569
- package/src/install.ts +0 -154
- package/src/license-core.test.ts +0 -23
- package/src/license-core.ts +0 -107
- package/src/license-expiry.test.ts +0 -127
- package/src/license.ts +0 -137
- package/src/master-detail.test.ts +0 -61
- package/src/master-detail.ts +0 -42
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/_metadata.json +0 -178
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-ZrPsKxob.js +0 -3875
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-ZrPsKxob.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-ACqvWGE2.js +0 -21
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-ACqvWGE2.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-D3w5jt92.js +0 -1938
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-D3w5jt92.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-CC2V3H0y.js +0 -205
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-CC2V3H0y.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-BXP37tAa.js +0 -18
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-BXP37tAa.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-DwubnU5b.js +0 -264
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-DwubnU5b.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-hD7f6PUz.js +0 -697
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-hD7f6PUz.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js +0 -134
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js +0 -55
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_attachments.js +0 -2
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js +0 -46843
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js +0 -237
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_events.js +0 -2
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js +0 -4
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_client.js +0 -7
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js +0 -4
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js +0 -5
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js +0 -5
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js +0 -5
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_server.js +0 -6
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js +0 -83
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js +0 -559
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js +0 -24
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js +0 -122
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_server.js +0 -2
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js +0 -89
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js +0 -241
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DBx1SCN6.js +0 -4938
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DBx1SCN6.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-gjfpSTFS.js +0 -495
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-gjfpSTFS.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-DKoOKg8S.js +0 -190
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-DKoOKg8S.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-C0MiR7eR.js +0 -8
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-C0MiR7eR.js.map +0 -1
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-B6Y7P0Q-.js +0 -138
- package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-B6Y7P0Q-.js.map +0 -1
- package/src/pivot-chart.test.ts +0 -86
- package/src/pivot-chart.ts +0 -112
- package/src/pivot-designer.ts +0 -101
- package/src/pivot.test.ts +0 -335
- package/src/pivot.ts +0 -578
- package/src/print.ts +0 -107
- package/src/scheduler-assignments.test.ts +0 -97
- package/src/scheduler-assignments.ts +0 -134
- package/src/scheduler-axis.test.ts +0 -108
- package/src/scheduler-axis.ts +0 -238
- package/src/scheduler-booking.test.ts +0 -57
- package/src/scheduler-config.ts +0 -179
- package/src/scheduler-dependencies.test.ts +0 -155
- package/src/scheduler-dependencies.ts +0 -223
- package/src/scheduler-freebusy.test.ts +0 -41
- package/src/scheduler-heatmap.test.ts +0 -39
- package/src/scheduler-resource-tree.test.ts +0 -84
- package/src/scheduler-resource-tree.ts +0 -107
- package/src/scheduler-slots.test.ts +0 -53
- package/src/scheduler-slots.ts +0 -94
- package/src/scheduler-summary.test.ts +0 -47
- package/src/scheduler-summary.ts +0 -81
- package/src/scheduling.test.ts +0 -194
- package/src/scheduling.ts +0 -293
- package/src/schema-designer.test.ts +0 -121
- package/src/schema-designer.ts +0 -142
- package/src/schema.test.ts +0 -268
- package/src/schema.ts +0 -562
- package/src/smart-shim.ts +0 -107
- package/src/sources/aggregate.test.ts +0 -79
- package/src/sources/aggregate.ts +0 -102
- package/src/sources/auth-supabase.test.ts +0 -80
- package/src/sources/auth-supabase.ts +0 -87
- package/src/sources/dashboard.kpi.test.ts +0 -50
- package/src/sources/dashboard.test.ts +0 -61
- package/src/sources/dashboard.ts +0 -136
- package/src/sources/field-inference.test.ts +0 -60
- package/src/sources/field-inference.ts +0 -63
- package/src/sources/filters.test.ts +0 -58
- package/src/sources/filters.ts +0 -70
- package/src/sources/index.ts +0 -67
- package/src/sources/introspect-supabase.test.ts +0 -112
- package/src/sources/introspect-supabase.ts +0 -139
- package/src/sources/realtime-supabase.test.ts +0 -93
- package/src/sources/realtime-supabase.ts +0 -99
- package/src/sources/relation-lookup.test.ts +0 -99
- package/src/sources/relation-lookup.ts +0 -128
- package/src/sources/rest-adapters.test.ts +0 -95
- package/src/sources/rest-adapters.ts +0 -120
- package/src/sources/rest.test.ts +0 -104
- package/src/sources/rest.ts +0 -129
- package/src/sources/schema-from-columns.test.ts +0 -106
- package/src/sources/schema-from-columns.ts +0 -88
- package/src/sources/supabase.test.ts +0 -110
- package/src/sources/supabase.ts +0 -99
- package/src/sources/with-entity-rules.test.ts +0 -104
- package/src/sources/with-entity-rules.ts +0 -78
- package/src/sources/with-relation-labels.test.ts +0 -75
- package/src/sources/with-relation-labels.ts +0 -73
- package/src/staged-editing.ts +0 -0
- package/src/studio/bug-report.test.ts +0 -101
- package/src/studio/bug-report.ts +0 -165
- package/src/studio/cli.test.ts +0 -187
- package/src/studio/cli.ts +0 -117
- package/src/studio/copilot-core.test.ts +0 -45
- package/src/studio/csv.test.ts +0 -90
- package/src/studio/csv.ts +0 -165
- package/src/studio/db-connect-string.test.ts +0 -94
- package/src/studio/db-connect-string.ts +0 -128
- package/src/studio/deploy-cli.test.ts +0 -56
- package/src/studio/deploy-cli.ts +0 -100
- package/src/studio/emit-project.test.ts +0 -3324
- package/src/studio/emit-project.ts +0 -5377
- package/src/studio/emit-schema.test.ts +0 -111
- package/src/studio/emit-schema.ts +0 -1181
- package/src/studio/index.ts +0 -380
- package/src/studio/init-flow.test.ts +0 -514
- package/src/studio/init-flow.ts +0 -556
- package/src/studio/introspect-db.test.ts +0 -186
- package/src/studio/introspect-db.ts +0 -312
- package/src/studio/introspect-openapi.test.ts +0 -84
- package/src/studio/introspect-openapi.ts +0 -252
- package/src/studio/introspect-prisma.test.ts +0 -99
- package/src/studio/introspect-prisma.ts +0 -175
- package/src/studio/introspect.test.ts +0 -172
- package/src/studio/introspect.ts +0 -310
- package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/_svelte_metadata.json +0 -1
- package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/package.json +0 -3
- package/src/studio/pipeline.test.ts +0 -42
- package/src/studio/project-robust.test.ts +0 -157
- package/src/studio/project.test.ts +0 -929
- package/src/studio/project.ts +0 -2394
- package/src/studio/sample-data.test.ts +0 -58
- package/src/studio/sample-data.ts +0 -203
- package/src/studio/samples/ats.ts +0 -202
- package/src/studio/samples/clinic.ts +0 -179
- package/src/studio/samples/crm.ts +0 -291
- package/src/studio/samples/datasets.test.ts +0 -84
- package/src/studio/samples/datasets.ts +0 -340
- package/src/studio/samples/ecommerce.ts +0 -187
- package/src/studio/samples/events.ts +0 -201
- package/src/studio/samples/fleet.ts +0 -213
- package/src/studio/samples/gym.ts +0 -215
- package/src/studio/samples/hr.ts +0 -195
- package/src/studio/samples/index.ts +0 -55
- package/src/studio/samples/insurance.ts +0 -229
- package/src/studio/samples/inventory.ts +0 -213
- package/src/studio/samples/invoicing.ts +0 -162
- package/src/studio/samples/library.ts +0 -180
- package/src/studio/samples/live-data.test.ts +0 -99
- package/src/studio/samples/live-data.ts +0 -306
- package/src/studio/samples/projects.ts +0 -212
- package/src/studio/samples/realestate.ts +0 -196
- package/src/studio/samples/restaurant.ts +0 -189
- package/src/studio/samples/samples.test.ts +0 -304
- package/src/studio/samples/school.ts +0 -197
- package/src/studio/samples/seed-floor.test.ts +0 -25
- package/src/studio/samples/shared.ts +0 -237
- package/src/studio/samples/starter.ts +0 -251
- package/src/studio/samples/subscriptions.ts +0 -183
- package/src/studio/samples/support.ts +0 -216
- package/src/studio/scaffold-app.test.ts +0 -91
- package/src/studio/scaffold.test.ts +0 -192
- package/src/studio/scaffold.ts +0 -390
- package/src/studio/screen-suites.test.ts +0 -269
- package/src/studio/screen-suites.ts +0 -448
- package/src/studio/themes.ts +0 -62
- package/src/studio/ui-components-surface.test.ts +0 -185
- package/src/studio/ui-components.generated.ts +0 -7154
- package/src/studio/ui-components.ts +0 -742
- package/src/studio/verify.test.ts +0 -47
- package/src/studio/verify.ts +0 -79
- package/src/sveltekit/in-memory.test.ts +0 -104
- package/src/sveltekit/in-memory.ts +0 -129
- package/src/sveltekit/index.ts +0 -24
- package/src/sveltekit/query-plan.test.ts +0 -109
- package/src/sveltekit/query-plan.ts +0 -125
- package/src/sveltekit/sql-source.test.ts +0 -198
- package/src/sveltekit/sql-source.ts +0 -171
- package/src/sveltekit/sql.test.ts +0 -80
- package/src/sveltekit/sql.ts +0 -131
- package/src/sveltekit/transport-scope.test.ts +0 -125
- package/src/sveltekit/transport.test.ts +0 -201
- package/src/sveltekit/transport.ts +0 -373
- package/src/upgrade-prompt.test.ts +0 -71
- package/src/upgrade-prompt.ts +0 -162
- package/src/version.test.ts +0 -17
- package/src/watermark.test.ts +0 -97
- package/src/watermark.ts +0 -156
- /package/{src → dist}/SvAlertRuleEditor.svelte +0 -0
- /package/{src → dist}/SvAlertsManager.svelte +0 -0
- /package/{src → dist}/SvAlertsPanel.svelte +0 -0
- /package/{src → dist}/SvAuthGate.svelte +0 -0
- /package/{src → dist}/SvBoard.svelte +0 -0
- /package/{src → dist}/SvExportMenu.svelte +0 -0
- /package/{src → dist}/SvExpressionEditor.svelte +0 -0
- /package/{src → dist}/SvFileInput.svelte +0 -0
- /package/{src → dist}/SvGridAlerts.svelte +0 -0
- /package/{src → dist}/SvGridBoard.svelte +0 -0
- /package/{src → dist}/SvGridEditPanel.svelte +0 -0
- /package/{src → dist}/SvGridMasterDetail.svelte +0 -0
- /package/{src → dist}/SvGridScheduler.svelte +0 -0
- /package/{src → dist}/SvImportDialog.svelte +0 -0
- /package/{src → dist}/SvLookupInput.svelte +0 -0
- /package/{src → dist}/SvPivotDesigner.svelte +0 -0
- /package/{src → dist}/SvRecordDetail.svelte +0 -0
- /package/{src → dist}/SvSchedule.svelte +0 -0
- /package/{src → dist}/SvSchemaChart.svelte +0 -0
- /package/{src → dist}/SvSchemaDashboard.svelte +0 -0
- /package/{src → dist}/pdfmake-shims.d.ts +0 -0
- /package/{src → dist}/smart.export.d.ts +0 -0
- /package/{src → dist}/smart.export.js +0 -0
- /package/{src → dist}/studio/HANDLERS-DESIGN.md +0 -0
- /package/{src → dist/studio}/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/_svelte_metadata.json +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/_metadata.json +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js.map +0 -0
- /package/{src → dist/studio}/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/package.json +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_attachments.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_events.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_client.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_server.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_server.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js.map +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js +0 -0
- /package/{src → dist}/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js.map +0 -0
package/src/export.ts
DELETED
|
@@ -1,1464 +0,0 @@
|
|
|
1
|
-
/// <reference path="./pdfmake-shims.d.ts" />
|
|
2
|
-
import type { CellFormatConfig, RowData, TableFeatures } from '@svgrid/grid'
|
|
3
|
-
import type { SvGridApi } from '@svgrid/grid'
|
|
4
|
-
// Import the pure formatters from the grid's Svelte-free `/format` subpath so
|
|
5
|
-
// this module (and the enterprise unit tests) don't drag in SvGrid.svelte.
|
|
6
|
-
import { formatValueForExport, toExcelNumFmt, valueForExcel } from '@svgrid/grid/format'
|
|
7
|
-
import { assertEnterpriseLicensed } from './license'
|
|
8
|
-
import { installSmartShim, type SmartDataExporterInstance } from './smart-shim'
|
|
9
|
-
import {
|
|
10
|
-
downloadBlobFile,
|
|
11
|
-
downloadTextFile,
|
|
12
|
-
serializeDelimited,
|
|
13
|
-
serializeHtml,
|
|
14
|
-
serializeJson,
|
|
15
|
-
serializeMarkdown,
|
|
16
|
-
serializeXml,
|
|
17
|
-
type CsvOptions,
|
|
18
|
-
type SerializeProgress,
|
|
19
|
-
} from './export-serialize'
|
|
20
|
-
import { serializeSpreadsheetML } from './export-xls'
|
|
21
|
-
import { buildXlsxParts, packageXlsx, type XlsxCell, type XlsxCondFormat, type XlsxTableSpec } from './export-ooxml'
|
|
22
|
-
import { buildPdfDocDefinition, type PdfBodyRow, type PdfExportOptions } from './export-pdf'
|
|
23
|
-
import { buildConditionalResolver, type ExportCellVisualFn } from './export-conditional'
|
|
24
|
-
import type { ConditionalFormat } from '@svgrid/grid/format'
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Export target formats.
|
|
28
|
-
* - `xlsx` modern OOXML workbook (needs `jszip`),
|
|
29
|
-
* - `xls` legacy Excel 2003 XML Spreadsheet (no peer dependency),
|
|
30
|
-
* - `pdf` paginated document (needs `pdfmake`),
|
|
31
|
-
* - `csv` / `tsv` / `html` / `json` / `xml` / `md` native, no peer deps.
|
|
32
|
-
*/
|
|
33
|
-
export type ExportFormat =
|
|
34
|
-
| 'xlsx'
|
|
35
|
-
| 'xls'
|
|
36
|
-
| 'pdf'
|
|
37
|
-
| 'csv'
|
|
38
|
-
| 'tsv'
|
|
39
|
-
| 'html'
|
|
40
|
-
| 'json'
|
|
41
|
-
| 'xml'
|
|
42
|
-
| 'md'
|
|
43
|
-
|
|
44
|
-
/** Formats served by the native, dependency-free, streaming serializers. */
|
|
45
|
-
const NATIVE_TEXT_FORMATS: ReadonlyArray<ExportFormat> = [
|
|
46
|
-
'csv',
|
|
47
|
-
'tsv',
|
|
48
|
-
'html',
|
|
49
|
-
'json',
|
|
50
|
-
'xml',
|
|
51
|
-
'md',
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
export type ExportColumn<TData = RowData> = {
|
|
55
|
-
/** Data field name on the row object. */
|
|
56
|
-
field: string
|
|
57
|
-
/** Header label to render in the exported file. Defaults to `field`. */
|
|
58
|
-
header?: string
|
|
59
|
-
/**
|
|
60
|
-
* Column format config. When set (and `rawValues` is off), the exporter
|
|
61
|
-
* renders the value the way the grid does on screen - currency symbol,
|
|
62
|
-
* date pattern, percent, etc. Auto-populated from the grid's own columns
|
|
63
|
-
* when you don't pass `columns` explicitly.
|
|
64
|
-
*/
|
|
65
|
-
format?: CellFormatConfig
|
|
66
|
-
/** Cell alignment, carried into html / pdf output. */
|
|
67
|
-
align?: 'left' | 'center' | 'right'
|
|
68
|
-
/** Explicit column width in pixels (xlsx / xls). Overrides auto-fit. */
|
|
69
|
-
width?: number
|
|
70
|
-
/**
|
|
71
|
-
* Turn this column's cells into hyperlinks. Return the URL for a row (or
|
|
72
|
-
* null for no link). Rendered as clickable links in xls / html / pdf /
|
|
73
|
-
* print. (xlsx via the vendored writer doesn't support cell links.)
|
|
74
|
-
*/
|
|
75
|
-
link?: (row: TData) => string | null | undefined
|
|
76
|
-
/**
|
|
77
|
-
* Escape hatch for columns whose on-screen value comes from a custom cell
|
|
78
|
-
* renderer (a snippet), a `fieldFn`, or a lookup the raw field can't
|
|
79
|
-
* express. Return the value to write for this row. Takes precedence over
|
|
80
|
-
* `format`. Return a number / Date to keep it typed in xlsx.
|
|
81
|
-
*/
|
|
82
|
-
exportValue?: (row: TData) => string | number | Date | null | undefined
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** Progress reported during an export. `phase` is 'project' while building
|
|
86
|
-
* rows, 'serialize' while writing text formats, 'write' around the xlsx/pdf
|
|
87
|
-
* writer. `ratio` is 0..1. */
|
|
88
|
-
export type ExportProgress =
|
|
89
|
-
| SerializeProgress
|
|
90
|
-
| { phase: 'project' | 'write'; ratio: number; row?: number; total?: number }
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* The built export file. Returned by `exportData` for the dependency-free
|
|
94
|
-
* paths (csv/tsv/html/json/xml/md, xls, pdf, single-sheet xlsx) so callers can
|
|
95
|
-
* preview / upload / email / attach it instead of (or as well as) downloading.
|
|
96
|
-
* With `download: false`, nothing is downloaded and this is returned.
|
|
97
|
-
*/
|
|
98
|
-
export type ExportResult = {
|
|
99
|
-
blob: Blob
|
|
100
|
-
filename: string
|
|
101
|
-
mime: string
|
|
102
|
-
/** Number of data rows written (excludes the header). */
|
|
103
|
-
rowCount: number
|
|
104
|
-
byteSize: number
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Per-cell style descriptor. Mirrors a subset of CSS; the keys the
|
|
109
|
-
* underlying exporter honours are font / colour / background / border /
|
|
110
|
-
* alignment. Anything else is ignored gracefully.
|
|
111
|
-
*/
|
|
112
|
-
export type ExportCellStyle = {
|
|
113
|
-
color?: string
|
|
114
|
-
backgroundColor?: string
|
|
115
|
-
fontWeight?: 'normal' | 'bold' | number
|
|
116
|
-
fontStyle?: 'normal' | 'italic'
|
|
117
|
-
fontSize?: number | string
|
|
118
|
-
fontFamily?: string
|
|
119
|
-
border?: string
|
|
120
|
-
textAlign?: 'left' | 'right' | 'center'
|
|
121
|
-
verticalAlign?: 'top' | 'middle' | 'bottom'
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Document-level style. Apply blanket styles to the header row, the
|
|
126
|
-
* value rows, or selectively per cell-reference (e.g. `'B2'`).
|
|
127
|
-
*/
|
|
128
|
-
export type ExportStyles = {
|
|
129
|
-
/** Style applied to every header cell. */
|
|
130
|
-
headerRow?: ExportCellStyle
|
|
131
|
-
/** Style applied to every data row. Even / odd zebra are derived from this if `rowAlternate` is set. */
|
|
132
|
-
rows?: ExportCellStyle
|
|
133
|
-
/** Optional zebra background for odd-indexed rows. */
|
|
134
|
-
rowAlternate?: ExportCellStyle
|
|
135
|
-
/** Per-cell overrides keyed by Excel-style reference (`'A1'`, `'C3'`). */
|
|
136
|
-
cells?: Record<string, ExportCellStyle>
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Header / footer entries for xlsx + pdf. Each line is rendered top to
|
|
141
|
-
* bottom on the page. Embed an image with `{ image: dataUrl }`; embed
|
|
142
|
-
* text with `{ text: '...', style?: ExportCellStyle }`.
|
|
143
|
-
*/
|
|
144
|
-
export type ExportHeaderFooterLine =
|
|
145
|
-
| { text: string; style?: ExportCellStyle }
|
|
146
|
-
| { image: string; width?: number; height?: number }
|
|
147
|
-
| { left?: string; center?: string; right?: string }
|
|
148
|
-
|
|
149
|
-
export type ExportSheet<TData> = {
|
|
150
|
-
/** Sheet/tab label. Required. */
|
|
151
|
-
label: string
|
|
152
|
-
/** Rows for this sheet. */
|
|
153
|
-
rows: ReadonlyArray<TData>
|
|
154
|
-
/** Per-sheet columns. Falls back to the top-level `columns` if omitted. */
|
|
155
|
-
columns?: ReadonlyArray<ExportColumn<TData>>
|
|
156
|
-
/** Per-sheet styles. */
|
|
157
|
-
styles?: ExportStyles
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Which rows to export:
|
|
162
|
-
* 'displayed' (default) - the current filtered / sorted / paginated view,
|
|
163
|
-
* 'selected' - only checked rows,
|
|
164
|
-
* 'all' - the full underlying dataset (pre-filter),
|
|
165
|
-
* an explicit array - export exactly these.
|
|
166
|
-
*/
|
|
167
|
-
export type ExportRowSource<TData> = ReadonlyArray<TData> | 'displayed' | 'selected' | 'all'
|
|
168
|
-
|
|
169
|
-
export type ExportOptions<TData> = {
|
|
170
|
-
format: ExportFormat
|
|
171
|
-
/** Base filename (extension is appended if missing). Defaults to "grid". */
|
|
172
|
-
filename?: string
|
|
173
|
-
/**
|
|
174
|
-
* Columns to include. If omitted, the grid's own visible columns are used
|
|
175
|
-
* (carrying their header labels + `format`), falling back to the keys of
|
|
176
|
-
* the first row.
|
|
177
|
-
*/
|
|
178
|
-
columns?: ReadonlyArray<ExportColumn<TData>>
|
|
179
|
-
/**
|
|
180
|
-
* Source rows. Defaults to `'displayed'` (the current view). Pass
|
|
181
|
-
* `'selected'` / `'all'`, or an explicit array to override.
|
|
182
|
-
*/
|
|
183
|
-
rows?: ExportRowSource<TData>
|
|
184
|
-
/**
|
|
185
|
-
* By default the exporter writes the FORMATTED display value (what the user
|
|
186
|
-
* sees) so the file matches the grid. Set `rawValues: true` to write the
|
|
187
|
-
* underlying raw values instead (numbers stay numeric in xlsx; useful for
|
|
188
|
-
* downstream data pipelines).
|
|
189
|
-
*/
|
|
190
|
-
rawValues?: boolean
|
|
191
|
-
/**
|
|
192
|
-
* Auto-carry the grid's active row grouping into the export (xlsx outline
|
|
193
|
-
* rows). On by default; set false to ignore grouping, or pass `groupBy`
|
|
194
|
-
* explicitly to override. Ignored when you pass `groupBy` / `hierarchical`
|
|
195
|
-
* / `sheets` yourself.
|
|
196
|
-
*/
|
|
197
|
-
autoGroup?: boolean
|
|
198
|
-
/** CSV / TSV tuning: delimiter, line ending, UTF-8 BOM. */
|
|
199
|
-
csv?: CsvOptions
|
|
200
|
-
/**
|
|
201
|
-
* Freeze the header row so it stays visible when scrolling (xlsx / xls).
|
|
202
|
-
* Default true.
|
|
203
|
-
*/
|
|
204
|
-
freezeHeader?: boolean
|
|
205
|
-
/** Freeze this many leading columns (xlsx / xls). Default 0. */
|
|
206
|
-
freezeColumns?: number
|
|
207
|
-
/**
|
|
208
|
-
* Size columns to their content (xlsx / xls). Default true. A column's
|
|
209
|
-
* explicit `width` always wins.
|
|
210
|
-
*/
|
|
211
|
-
autoFitColumns?: boolean
|
|
212
|
-
/**
|
|
213
|
-
* Export long integer IDs (> 15 significant digits) as TEXT so Excel's
|
|
214
|
-
* 15-digit float precision doesn't silently mangle them. Default false.
|
|
215
|
-
* (xlsx / xls.)
|
|
216
|
-
*/
|
|
217
|
-
precisionSafe?: boolean
|
|
218
|
-
/**
|
|
219
|
-
* Wrap the xlsx data in a native Excel Table (filter dropdowns, banded rows,
|
|
220
|
-
* structured refs). `{ totalsRow: true }` adds a totals row with live
|
|
221
|
-
* SUM/AVG formulas for numeric columns. Single-sheet xlsx only.
|
|
222
|
-
*/
|
|
223
|
-
excelTable?: boolean | { totalsRow?: boolean; style?: string }
|
|
224
|
-
/** Progress callback for large exports (row projection + serialization). */
|
|
225
|
-
onProgress?: (progress: ExportProgress) => void
|
|
226
|
-
/** Abort an in-flight export (checked between chunks). Throws `AbortError`. */
|
|
227
|
-
signal?: AbortSignal
|
|
228
|
-
/**
|
|
229
|
-
* When false, don't trigger a browser download - just build the file and
|
|
230
|
-
* return it as an {@link ExportResult} (`{ blob, filename, mime, rowCount,
|
|
231
|
-
* byteSize }`) so the app can preview / upload / email it. Default true.
|
|
232
|
-
* Not supported for the vendored-writer xlsx paths (grouped / multi-sheet /
|
|
233
|
-
* images / blanket styles), which download directly.
|
|
234
|
-
*/
|
|
235
|
-
download?: boolean
|
|
236
|
-
/** PDF only. Page orientation. Defaults to "portrait" (auto-landscape for
|
|
237
|
-
* wide grids). Shorthand for `pdf.pageOrientation`. */
|
|
238
|
-
pageOrientation?: 'portrait' | 'landscape'
|
|
239
|
-
/** PDF layout options: page size, margins, title / subtitle / logo, theme
|
|
240
|
-
* colors, column widths, repeated header, page numbers. */
|
|
241
|
-
pdf?: PdfExportOptions
|
|
242
|
-
/**
|
|
243
|
-
* Conditional formatting rules - the same array you pass to
|
|
244
|
-
* `<SvGrid conditionalFormats>`. Carried into the styled formats (pdf, xlsx,
|
|
245
|
-
* html): cell background / text color / bold, plus icon-set glyphs. Ignored
|
|
246
|
-
* by the data formats (csv / tsv / json / xml / md).
|
|
247
|
-
*/
|
|
248
|
-
conditionalFormats?: ReadonlyArray<ConditionalFormat<TData>>
|
|
249
|
-
/**
|
|
250
|
-
* Cell + row styles. Apply once to match a light/dark theme, or per-cell
|
|
251
|
-
* for conditional formatting. xlsx and pdf honour these; csv/tsv ignore
|
|
252
|
-
* them; html bakes them into inline `style=` attributes.
|
|
253
|
-
*/
|
|
254
|
-
styles?: ExportStyles
|
|
255
|
-
/** Page header lines (xlsx, pdf, html). Logos go here as `{ image: ... }`. */
|
|
256
|
-
header?: ReadonlyArray<ExportHeaderFooterLine>
|
|
257
|
-
/** Page footer lines. Common pattern: page number on the right. */
|
|
258
|
-
footer?: ReadonlyArray<ExportHeaderFooterLine>
|
|
259
|
-
/**
|
|
260
|
-
* Multi-sheet export (xlsx only). When set, each entry becomes one
|
|
261
|
-
* sheet/tab. The top-level `rows` / `columns` are ignored.
|
|
262
|
-
*/
|
|
263
|
-
sheets?: ReadonlyArray<ExportSheet<TData>>
|
|
264
|
-
/**
|
|
265
|
-
* If a row has a column whose value matches one of these field names
|
|
266
|
-
* AND the value looks like a URL or data URL, the cell is exported as
|
|
267
|
-
* an embedded image (xlsx). Defaults to `[]` (no auto-detection).
|
|
268
|
-
*/
|
|
269
|
-
imageFields?: ReadonlyArray<string>
|
|
270
|
-
/**
|
|
271
|
-
* Pixel dimensions used when embedding images. Smart's xlsx writer
|
|
272
|
-
* draws the image at this size relative to the cell origin. Defaults
|
|
273
|
-
* to `{ width: 32, height: 32 }`. Set larger for thumbnails, smaller
|
|
274
|
-
* for inline icons.
|
|
275
|
-
*/
|
|
276
|
-
imageSize?: { width: number; height: number }
|
|
277
|
-
/**
|
|
278
|
-
* Group flat rows by one or more field names. The exporter wraps each
|
|
279
|
-
* group in an Excel outline row (with the +/- expand button), and
|
|
280
|
-
* emits a `<value> <field>` group header above every cluster. Maps
|
|
281
|
-
* straight to Smart DataExporter's constructor `groupBy` arg. xlsx
|
|
282
|
-
* only; csv/tsv/html flatten the groups back out.
|
|
283
|
-
*/
|
|
284
|
-
groupBy?: ReadonlyArray<string>
|
|
285
|
-
/**
|
|
286
|
-
* Mark rows as a hierarchical (tree) data source. Each row should
|
|
287
|
-
* declare its own children either via the `subRows` convention or by
|
|
288
|
-
* matching the parent/child shape Smart expects on the input rows.
|
|
289
|
-
* Mutually exclusive with `groupBy`. xlsx only.
|
|
290
|
-
*/
|
|
291
|
-
hierarchical?: boolean
|
|
292
|
-
/**
|
|
293
|
-
* Merged cells to write into the sheet (xlsx / pdf). Each entry spans
|
|
294
|
-
* `colSpan` columns and `rowSpan` rows starting at the given zero-based
|
|
295
|
-
* **body** row / column index (the header row is not counted). Mirrors the
|
|
296
|
-
* grid's own `MergeSpec` shape closely, so grid merges map straight through -
|
|
297
|
-
* convert a `MergeSpec` (`{ rowIndex, columnId, rowspan, colspan }`) by
|
|
298
|
-
* resolving `columnId` to its column index. Mutually exclusive with
|
|
299
|
-
* `groupBy` / `hierarchical`.
|
|
300
|
-
*/
|
|
301
|
-
merges?: ReadonlyArray<ExportMerge>
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/**
|
|
305
|
-
* A merged cell region for {@link ExportOptions.merges}. `row` / `col` are
|
|
306
|
-
* zero-based indices into the exported body (header excluded). A cell that
|
|
307
|
-
* spans two columns to the right is `{ row, col, colSpan: 2 }`.
|
|
308
|
-
*/
|
|
309
|
-
export type ExportMerge = {
|
|
310
|
-
row: number
|
|
311
|
-
col: number
|
|
312
|
-
/** Columns to span (default 1). */
|
|
313
|
-
colSpan?: number
|
|
314
|
-
/** Rows to span (default 1). */
|
|
315
|
-
rowSpan?: number
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
let exporterCtorPromise: Promise<
|
|
319
|
-
new (
|
|
320
|
-
options: Record<string, unknown>,
|
|
321
|
-
groupBy?: ReadonlyArray<string>,
|
|
322
|
-
filterBy?: Record<string, unknown>,
|
|
323
|
-
conditionalFormatting?: unknown,
|
|
324
|
-
) => SmartDataExporterInstance
|
|
325
|
-
> | null = null
|
|
326
|
-
|
|
327
|
-
async function getDataExporter() {
|
|
328
|
-
if (typeof window === 'undefined') {
|
|
329
|
-
throw new Error('@svgrid/enterprise: export requires a browser environment')
|
|
330
|
-
}
|
|
331
|
-
if (!exporterCtorPromise) {
|
|
332
|
-
exporterCtorPromise = (async () => {
|
|
333
|
-
installSmartShim()
|
|
334
|
-
// Side-effect import: the IIFE registers Smart.Utilities.DataExporter
|
|
335
|
-
// on the global Smart namespace that installSmartShim() set up.
|
|
336
|
-
await import('./smart.export.js')
|
|
337
|
-
const Ctor = window.Smart?.Utilities?.DataExporter
|
|
338
|
-
if (!Ctor) {
|
|
339
|
-
throw new Error('@svgrid/enterprise: failed to load Smart.Utilities.DataExporter')
|
|
340
|
-
}
|
|
341
|
-
return Ctor
|
|
342
|
-
})()
|
|
343
|
-
}
|
|
344
|
-
return exporterCtorPromise
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
async function ensureGlobals(format: ExportFormat): Promise<void> {
|
|
348
|
-
const g = globalThis as unknown as { JSZip?: unknown; pdfMake?: unknown }
|
|
349
|
-
if (format === 'xlsx' && g.JSZip == null) {
|
|
350
|
-
let mod: unknown
|
|
351
|
-
try {
|
|
352
|
-
// Optional peer, loaded on demand. The `@ts-ignore` keeps a consumer's
|
|
353
|
-
// type-check clean when jszip isn't installed (no-op when it is).
|
|
354
|
-
// @ts-ignore - "jszip" is an optional peerDependency
|
|
355
|
-
mod = await import('jszip')
|
|
356
|
-
} catch {
|
|
357
|
-
throw new Error(
|
|
358
|
-
'@svgrid/enterprise: xlsx export requires the "jszip" peer dependency. ' +
|
|
359
|
-
'Install it with: pnpm add jszip',
|
|
360
|
-
)
|
|
361
|
-
}
|
|
362
|
-
g.JSZip = (mod as { default?: unknown }).default ?? mod
|
|
363
|
-
}
|
|
364
|
-
if (format === 'pdf' && g.pdfMake == null) {
|
|
365
|
-
let pdfMakeMod: unknown
|
|
366
|
-
let vfsMod: unknown
|
|
367
|
-
try {
|
|
368
|
-
// Optional peer, loaded on demand (keeps consumer type-checks clean when absent).
|
|
369
|
-
// @ts-ignore - "pdfmake" is an optional peerDependency
|
|
370
|
-
pdfMakeMod = await import('pdfmake/build/pdfmake')
|
|
371
|
-
// @ts-ignore - "pdfmake" is an optional peerDependency
|
|
372
|
-
vfsMod = await import('pdfmake/build/vfs_fonts')
|
|
373
|
-
} catch {
|
|
374
|
-
throw new Error(
|
|
375
|
-
'@svgrid/enterprise: pdf export requires the "pdfmake" peer dependency. ' +
|
|
376
|
-
'Install it with: pnpm add pdfmake',
|
|
377
|
-
)
|
|
378
|
-
}
|
|
379
|
-
const pdfMake = ((pdfMakeMod as { default?: unknown }).default ?? pdfMakeMod) as {
|
|
380
|
-
vfs?: Record<string, string>
|
|
381
|
-
createPdf: (def: unknown) => { download(name: string): void; getBlob(cb: (b: Blob) => void): void }
|
|
382
|
-
}
|
|
383
|
-
const vfsRoot = (vfsMod as { default?: unknown }).default ?? vfsMod
|
|
384
|
-
// pdfmake's vfs_fonts file historically exports either { pdfMake: { vfs } }
|
|
385
|
-
// or { default: { vfs } } or { vfs } depending on bundler. Try each shape.
|
|
386
|
-
const candidate =
|
|
387
|
-
(vfsRoot as { pdfMake?: { vfs?: Record<string, string> } }).pdfMake?.vfs ??
|
|
388
|
-
(vfsRoot as { vfs?: Record<string, string> }).vfs ??
|
|
389
|
-
(vfsRoot as { default?: { vfs?: Record<string, string> } }).default?.vfs
|
|
390
|
-
if (candidate) pdfMake.vfs = candidate
|
|
391
|
-
g.pdfMake = pdfMake
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
/** Resolve the {@link ExportRowSource} shorthand against the grid api. */
|
|
396
|
-
export function resolveRowSource<TFeatures extends TableFeatures, TData extends RowData>(
|
|
397
|
-
api: SvGridApi<TFeatures, TData>,
|
|
398
|
-
source: ExportRowSource<TData> | undefined,
|
|
399
|
-
): ReadonlyArray<TData> {
|
|
400
|
-
if (Array.isArray(source)) return source
|
|
401
|
-
switch (source) {
|
|
402
|
-
case 'selected':
|
|
403
|
-
return api.getSelectedRows()
|
|
404
|
-
case 'all':
|
|
405
|
-
return api.getData()
|
|
406
|
-
case 'displayed':
|
|
407
|
-
case undefined:
|
|
408
|
-
default:
|
|
409
|
-
return api.getDisplayedRows()
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/** Resolve the export columns, carrying grid header labels + `format`. */
|
|
414
|
-
export function resolveColumns<TFeatures extends TableFeatures, TData extends RowData>(
|
|
415
|
-
api: SvGridApi<TFeatures, TData>,
|
|
416
|
-
opts: Pick<ExportOptions<TData>, 'columns'>,
|
|
417
|
-
sourceRows: ReadonlyArray<TData>,
|
|
418
|
-
): ReadonlyArray<ExportColumn<TData>> {
|
|
419
|
-
if (opts.columns && opts.columns.length > 0) return opts.columns
|
|
420
|
-
const gridCols = api.getColumns().filter((c) => c.visible && c.field)
|
|
421
|
-
if (gridCols.length > 0) {
|
|
422
|
-
return gridCols.map((c) => ({
|
|
423
|
-
field: c.field!,
|
|
424
|
-
header: c.header,
|
|
425
|
-
format: c.format,
|
|
426
|
-
align: c.align,
|
|
427
|
-
}))
|
|
428
|
-
}
|
|
429
|
-
if (sourceRows.length > 0) {
|
|
430
|
-
return Object.keys(sourceRows[0] as Record<string, unknown>)
|
|
431
|
-
.filter((k) => !k.startsWith('_'))
|
|
432
|
-
.map((field) => ({ field }) as ExportColumn<TData>)
|
|
433
|
-
}
|
|
434
|
-
return []
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* The value written for one cell: an `exportValue` hook wins; otherwise the
|
|
439
|
-
* raw field value, formatted to the on-screen display string unless
|
|
440
|
-
* `rawValues` is set.
|
|
441
|
-
*/
|
|
442
|
-
function cellValueFor<TData extends RowData>(
|
|
443
|
-
col: ExportColumn<TData>,
|
|
444
|
-
rowData: TData,
|
|
445
|
-
rawValues: boolean,
|
|
446
|
-
): unknown {
|
|
447
|
-
if (col.exportValue) {
|
|
448
|
-
const v = col.exportValue(rowData)
|
|
449
|
-
return v ?? ''
|
|
450
|
-
}
|
|
451
|
-
const raw = (rowData as unknown as Record<string, unknown>)[col.field]
|
|
452
|
-
if (rawValues) return raw
|
|
453
|
-
return formatValueForExport(raw, col.format)
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Project source rows into the `[header, ...records]` shape the writers
|
|
459
|
-
* consume, applying per-column formatting / `exportValue`. Async so it can
|
|
460
|
-
* yield to the event loop (and honour an abort) on large datasets.
|
|
461
|
-
*/
|
|
462
|
-
export async function projectRows<TData extends RowData>(
|
|
463
|
-
sourceRows: ReadonlyArray<TData>,
|
|
464
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
465
|
-
opts: Pick<ExportOptions<TData>, 'rawValues' | 'onProgress' | 'signal'>,
|
|
466
|
-
): Promise<Array<Record<string, unknown>>> {
|
|
467
|
-
const rawValues = opts.rawValues === true
|
|
468
|
-
const headerRow: Record<string, unknown> = {}
|
|
469
|
-
for (const c of cols) headerRow[c.field] = c.header ?? c.field
|
|
470
|
-
|
|
471
|
-
const projected: Array<Record<string, unknown>> = [headerRow]
|
|
472
|
-
const total = sourceRows.length
|
|
473
|
-
const CHUNK = 5000
|
|
474
|
-
for (let i = 0; i < total; i++) {
|
|
475
|
-
if (opts.signal?.aborted) throw new DOMException('Export aborted', 'AbortError')
|
|
476
|
-
const r = sourceRows[i]!
|
|
477
|
-
const row: Record<string, unknown> = {}
|
|
478
|
-
for (const c of cols) row[c.field] = cellValueFor(c, r, rawValues)
|
|
479
|
-
projected.push(row)
|
|
480
|
-
if (i > 0 && i % CHUNK === 0) {
|
|
481
|
-
opts.onProgress?.({ phase: 'project', ratio: i / total, row: i, total })
|
|
482
|
-
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
opts.onProgress?.({ phase: 'project', ratio: 1, row: total, total })
|
|
486
|
-
return projected
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Translate the public {@link ExportStyles} shape into the keys the Smart
|
|
491
|
-
* exporter actually consumes:
|
|
492
|
-
* `style.header` → all header cells
|
|
493
|
-
* `style.rows` → all data rows. Cell-prop keys (color, backgroundColor,
|
|
494
|
-
* fontWeight, fontStyle, fontSize, fontFamily,
|
|
495
|
-
* textAlign, verticalAlign, textDecoration, numFmt)
|
|
496
|
-
* are recognised by Smart's `storeCellStyle`.
|
|
497
|
-
* `style.rows.alternationCount` + `alternationStart` +
|
|
498
|
-
* `alternationIndex1BackgroundColor` (etc.) → zebra striping
|
|
499
|
-
* `style.cells.<ref>` → HTML-only per-cell ref overrides
|
|
500
|
-
*/
|
|
501
|
-
function translateStyles(styles: ExportStyles | undefined): Record<string, unknown> | undefined {
|
|
502
|
-
if (!styles) return undefined
|
|
503
|
-
const out: Record<string, unknown> = {}
|
|
504
|
-
if (styles.headerRow) out.header = { ...styles.headerRow }
|
|
505
|
-
const rows: Record<string, unknown> = styles.rows ? { ...styles.rows } : {}
|
|
506
|
-
if (styles.rowAlternate) {
|
|
507
|
-
rows.alternationCount = 2
|
|
508
|
-
rows.alternationStart = 0
|
|
509
|
-
if (styles.rowAlternate.backgroundColor)
|
|
510
|
-
rows.alternationIndex1BackgroundColor = styles.rowAlternate.backgroundColor
|
|
511
|
-
if (styles.rowAlternate.color)
|
|
512
|
-
rows.alternationIndex1Color = styles.rowAlternate.color
|
|
513
|
-
}
|
|
514
|
-
if (Object.keys(rows).length > 0) out.rows = rows
|
|
515
|
-
if (styles.cells) out.cells = styles.cells
|
|
516
|
-
return out
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
let _imgIdCounter = 0
|
|
520
|
-
const _imgIdCache = new Map<string, string>()
|
|
521
|
-
function imageIdFor(dataUrl: string): string {
|
|
522
|
-
const cached = _imgIdCache.get(dataUrl)
|
|
523
|
-
if (cached) return cached
|
|
524
|
-
const id = `img${++_imgIdCounter}`
|
|
525
|
-
_imgIdCache.set(dataUrl, id)
|
|
526
|
-
return id
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Build the image object Smart's xlsx writer expects. The exporter pulls
|
|
531
|
-
* `{ id, base64, imageType, width, height }` off the return of
|
|
532
|
-
* `addImageToCell(...).image` and stores it under `this.images` →
|
|
533
|
-
* eventually written to the workbook as `xl/media/imageN.<imageType>`.
|
|
534
|
-
*
|
|
535
|
-
* `base64` may be a full data URL - Smart strips the prefix when writing.
|
|
536
|
-
*/
|
|
537
|
-
function makeSmartImage(dataUrl: string, width = 64, height = 64): {
|
|
538
|
-
id: string; base64: string; imageType: string; width: number; height: number
|
|
539
|
-
} {
|
|
540
|
-
// Extract MIME → imageType. Defaults to png for safety.
|
|
541
|
-
const mime = /^data:image\/([a-zA-Z0-9+]+);/i.exec(dataUrl)?.[1]?.toLowerCase() ?? 'png'
|
|
542
|
-
const imageType = mime === 'jpg' ? 'jpeg'
|
|
543
|
-
: mime === 'svg+xml' ? 'svg'
|
|
544
|
-
: mime
|
|
545
|
-
return {
|
|
546
|
-
id: imageIdFor(dataUrl),
|
|
547
|
-
base64: dataUrl,
|
|
548
|
-
imageType,
|
|
549
|
-
width,
|
|
550
|
-
height,
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Build the header / footer arrays the Smart exporter understands.
|
|
556
|
-
* Each input line becomes one full-width row in the spreadsheet with
|
|
557
|
-
* `style.mergeAcross` so the line spans every column.
|
|
558
|
-
*
|
|
559
|
-
* Image lines are written as a data-URL string into the first column and
|
|
560
|
-
* the wrapper's `addImageToCell` hook embeds them as real picture cells.
|
|
561
|
-
*/
|
|
562
|
-
function buildContentRows(
|
|
563
|
-
lines: ReadonlyArray<ExportHeaderFooterLine> | undefined,
|
|
564
|
-
datafields: ReadonlyArray<string>,
|
|
565
|
-
): Array<{ cells: Record<string, unknown>; style?: Record<string, unknown> }> | undefined {
|
|
566
|
-
if (!lines || lines.length === 0 || datafields.length === 0) return undefined
|
|
567
|
-
const first = datafields[0]!
|
|
568
|
-
const out: Array<{ cells: Record<string, unknown>; style?: Record<string, unknown> }> = []
|
|
569
|
-
for (const line of lines) {
|
|
570
|
-
const cells: Record<string, unknown> = {}
|
|
571
|
-
let style: Record<string, unknown> | undefined
|
|
572
|
-
if ('image' in line) {
|
|
573
|
-
cells[first] = line.image
|
|
574
|
-
style = { mergeAcross: true, textAlign: 'left' }
|
|
575
|
-
} else if ('text' in line) {
|
|
576
|
-
cells[first] = line.text
|
|
577
|
-
style = { mergeAcross: true, ...(line.style ?? {}) }
|
|
578
|
-
} else {
|
|
579
|
-
// 3-column { left, center, right }. Distribute across the first,
|
|
580
|
-
// middle, and last datafield.
|
|
581
|
-
const mid = datafields[Math.floor(datafields.length / 2)]!
|
|
582
|
-
const last = datafields[datafields.length - 1]!
|
|
583
|
-
if (line.left !== undefined) cells[first] = line.left
|
|
584
|
-
if (line.center !== undefined) cells[mid] = line.center
|
|
585
|
-
if (line.right !== undefined) cells[last] = line.right
|
|
586
|
-
style = { textAlign: 'left' }
|
|
587
|
-
}
|
|
588
|
-
out.push({ cells, style })
|
|
589
|
-
}
|
|
590
|
-
return out
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* Build an `addImageToCell` handler that embeds a value as a real image
|
|
595
|
-
* cell whenever the value is a data URL. Smart calls this for every body
|
|
596
|
-
* cell AND every header/footer cell, so the same handler covers both
|
|
597
|
-
* code paths.
|
|
598
|
-
*
|
|
599
|
-
* `bodyImageSize` controls the embedded pixel size for body cells (the
|
|
600
|
-
* thumbnails). Header/footer image lines use their own size hint
|
|
601
|
-
* already encoded in the `cells[first]` value's metadata - here we just
|
|
602
|
-
* embed with a slightly larger default so logos read well in the page
|
|
603
|
-
* banner.
|
|
604
|
-
*/
|
|
605
|
-
function buildImageHandler(
|
|
606
|
-
imageFields: ReadonlyArray<string> | undefined,
|
|
607
|
-
bodyImageSize: { width: number; height: number },
|
|
608
|
-
): NonNullable<SmartDataExporterInstance['addImageToCell']> {
|
|
609
|
-
const allowed = imageFields && imageFields.length > 0 ? new Set(imageFields) : null
|
|
610
|
-
return (_rowIndex, dataField, value) => {
|
|
611
|
-
if (typeof value !== 'string') return null
|
|
612
|
-
if (!/^data:image\//i.test(value)) return null
|
|
613
|
-
if (allowed && !allowed.has(dataField)) {
|
|
614
|
-
// Header/footer image lines come through the first column too -
|
|
615
|
-
// those cells aren't in `imageFields`, but the wrapper wrote the
|
|
616
|
-
// data URL there itself. Embed bigger (logo banner).
|
|
617
|
-
return { image: makeSmartImage(value, 96, 96), value: '' }
|
|
618
|
-
}
|
|
619
|
-
return { image: makeSmartImage(value, bodyImageSize.width, bodyImageSize.height), value: '' }
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
/**
|
|
624
|
-
* Build the constructor-options object passed to the Smart DataExporter.
|
|
625
|
-
* Constructor only consumes a small set of keys; instance-level options
|
|
626
|
-
* (`headerContent`, `footerContent`, `addImageToCell`) are applied
|
|
627
|
-
* separately in {@link exportGrid} after `new DataExporter(...)`.
|
|
628
|
-
*/
|
|
629
|
-
function buildExporterOptions<TData>(
|
|
630
|
-
opts: ExportOptions<TData>,
|
|
631
|
-
): Record<string, unknown> {
|
|
632
|
-
const out: Record<string, unknown> = {
|
|
633
|
-
exportHeader: true,
|
|
634
|
-
pageOrientation: opts.pageOrientation ?? 'portrait',
|
|
635
|
-
}
|
|
636
|
-
// Freeze the header row (and optional leading columns) so they stay in view
|
|
637
|
-
// when scrolling the xlsx. On by default.
|
|
638
|
-
if (opts.freezeHeader !== false) out.freezeHeader = true
|
|
639
|
-
if (opts.freezeColumns && opts.freezeColumns > 0) {
|
|
640
|
-
out.freezeColumnsCount = Math.floor(opts.freezeColumns)
|
|
641
|
-
}
|
|
642
|
-
const style = translateStyles(opts.styles)
|
|
643
|
-
if (style) out.style = style
|
|
644
|
-
// Smart's xlsx writer emits Excel-native row outlining when
|
|
645
|
-
// `hierarchical: true` is set on the constructor options OR when the
|
|
646
|
-
// second constructor arg is a non-empty groupBy array. We thread
|
|
647
|
-
// hierarchical through here and groupBy through the constructor call
|
|
648
|
-
// site in exportGrid.
|
|
649
|
-
if (opts.hierarchical) out.hierarchical = true
|
|
650
|
-
// Merged cells: translate the friendly {row, col, rowSpan, colSpan} shape to
|
|
651
|
-
// Smart's native {cell: [row, col], rowspan, colspan}. Skip 1x1 "merges"
|
|
652
|
-
// (nothing to span). Ignored when grouping/hierarchy owns the row layout.
|
|
653
|
-
if (opts.merges?.length && !opts.groupBy?.length && !opts.hierarchical) {
|
|
654
|
-
const mergedCells = opts.merges
|
|
655
|
-
.map((m) => ({
|
|
656
|
-
cell: [m.row, m.col],
|
|
657
|
-
rowspan: Math.max(1, Math.floor(m.rowSpan ?? 1)),
|
|
658
|
-
colspan: Math.max(1, Math.floor(m.colSpan ?? 1)),
|
|
659
|
-
}))
|
|
660
|
-
.filter((m) => m.rowspan > 1 || m.colspan > 1)
|
|
661
|
-
if (mergedCells.length) out.mergedCells = mergedCells
|
|
662
|
-
}
|
|
663
|
-
return out
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* Apply instance-level options the constructor does not consume.
|
|
668
|
-
* Mutates and returns the exporter for fluent use.
|
|
669
|
-
*/
|
|
670
|
-
function applyInstanceOptions<TData>(
|
|
671
|
-
exporter: SmartDataExporterInstance,
|
|
672
|
-
opts: ExportOptions<TData>,
|
|
673
|
-
datafields: ReadonlyArray<string>,
|
|
674
|
-
): SmartDataExporterInstance {
|
|
675
|
-
const headerRows = buildContentRows(opts.header, datafields)
|
|
676
|
-
const footerRows = buildContentRows(opts.footer, datafields)
|
|
677
|
-
if (headerRows) exporter.headerContent = headerRows
|
|
678
|
-
if (footerRows) exporter.footerContent = footerRows
|
|
679
|
-
|
|
680
|
-
// Wire image embedding when the caller opts in via imageFields OR the
|
|
681
|
-
// header/footer carries an image line. Both code paths funnel through
|
|
682
|
-
// the same handler.
|
|
683
|
-
const hasHeaderOrFooterImage =
|
|
684
|
-
(opts.header?.some((l) => 'image' in l) ?? false) ||
|
|
685
|
-
(opts.footer?.some((l) => 'image' in l) ?? false)
|
|
686
|
-
if (opts.imageFields?.length || hasHeaderOrFooterImage) {
|
|
687
|
-
const bodySize = opts.imageSize ?? { width: 32, height: 32 }
|
|
688
|
-
exporter.addImageToCell = buildImageHandler(opts.imageFields, bodySize)
|
|
689
|
-
}
|
|
690
|
-
return exporter
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
/** Append `.ext` to a filename that doesn't already carry it. */
|
|
694
|
-
function ensureExt(name: string, ext: string): string {
|
|
695
|
-
return name.toLowerCase().endsWith('.' + ext) ? name : `${name}.${ext}`
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/** Download the blob (unless `download === false`) and return the result. */
|
|
699
|
-
function deliver(
|
|
700
|
-
blob: Blob,
|
|
701
|
-
filename: string,
|
|
702
|
-
mime: string,
|
|
703
|
-
rowCount: number,
|
|
704
|
-
download: boolean | undefined,
|
|
705
|
-
): ExportResult {
|
|
706
|
-
if (download !== false) downloadBlobFile(blob, filename)
|
|
707
|
-
return { blob, filename, mime, rowCount, byteSize: blob.size }
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
/** Text-file variant: download via `downloadTextFile`, also return the blob. */
|
|
711
|
-
function deliverText(
|
|
712
|
-
text: string,
|
|
713
|
-
filename: string,
|
|
714
|
-
mime: string,
|
|
715
|
-
rowCount: number,
|
|
716
|
-
download: boolean | undefined,
|
|
717
|
-
): ExportResult {
|
|
718
|
-
if (download !== false) downloadTextFile(text, filename, mime)
|
|
719
|
-
const blob = new Blob([text], { type: mime })
|
|
720
|
-
return { blob, filename, mime, rowCount, byteSize: blob.size }
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
/**
|
|
724
|
-
* Estimate a per-column pixel width from the header + a sample of cell values,
|
|
725
|
-
* for xlsx / xls auto-fit. A column's explicit `width` always wins. ~7px per
|
|
726
|
-
* character, clamped to a sensible range.
|
|
727
|
-
*/
|
|
728
|
-
function autoFitWidths<TData extends RowData>(
|
|
729
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
730
|
-
rows: ReadonlyArray<TData>,
|
|
731
|
-
rawValues: boolean,
|
|
732
|
-
): number[] {
|
|
733
|
-
const SAMPLE = Math.min(rows.length, 200)
|
|
734
|
-
return cols.map((c) => {
|
|
735
|
-
if (typeof c.width === 'number') return c.width
|
|
736
|
-
let maxChars = String(c.header ?? c.field).length
|
|
737
|
-
for (let i = 0; i < SAMPLE; i++) {
|
|
738
|
-
const v = cellValueFor(c, rows[i]!, rawValues)
|
|
739
|
-
const len = v == null ? 0 : String(v).length
|
|
740
|
-
if (len > maxChars) maxChars = len
|
|
741
|
-
}
|
|
742
|
-
return Math.max(48, Math.min(400, maxChars * 7 + 14))
|
|
743
|
-
})
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/**
|
|
747
|
-
* Freeze-column count: explicit `freezeColumns` wins; otherwise derive it from
|
|
748
|
-
* the grid's left-pinned columns that lead the export column order. Silent
|
|
749
|
-
* (returns undefined) when neither applies.
|
|
750
|
-
*/
|
|
751
|
-
function resolveFreezeColumns<TFeatures extends TableFeatures, TData extends RowData>(
|
|
752
|
-
api: SvGridApi<TFeatures, TData>,
|
|
753
|
-
opts: Pick<ExportOptions<TData>, 'freezeColumns'>,
|
|
754
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
755
|
-
): number | undefined {
|
|
756
|
-
if (typeof opts.freezeColumns === 'number') return opts.freezeColumns
|
|
757
|
-
const left = api.getColumnPinning().left
|
|
758
|
-
if (!left || left.length === 0) return undefined
|
|
759
|
-
const pinnedSet = new Set(left)
|
|
760
|
-
// Count leading export columns that are pinned-left (by field id).
|
|
761
|
-
let n = 0
|
|
762
|
-
for (const c of cols) {
|
|
763
|
-
if (pinnedSet.has(c.field)) n++
|
|
764
|
-
else break
|
|
765
|
-
}
|
|
766
|
-
return n > 0 ? n : undefined
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
/** Excel column letter for a 0-based column index (0 -> A, 26 -> AA). */
|
|
770
|
-
function colLetter(index: number): string {
|
|
771
|
-
let n = index
|
|
772
|
-
let s = ''
|
|
773
|
-
do {
|
|
774
|
-
s = String.fromCharCode(65 + (n % 26)) + s
|
|
775
|
-
n = Math.floor(n / 26) - 1
|
|
776
|
-
} while (n >= 0)
|
|
777
|
-
return s
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
/** A `(rowIdx, colIdx) => url` resolver from column `link` hooks, or undefined
|
|
781
|
-
* when no column defines links. */
|
|
782
|
-
export function buildLinkResolver<TData extends RowData>(
|
|
783
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
784
|
-
rows: ReadonlyArray<TData>,
|
|
785
|
-
): ((rowIdx: number, colIdx: number) => string | undefined) | undefined {
|
|
786
|
-
if (!cols.some((c) => c.link)) return undefined
|
|
787
|
-
return (rowIdx, colIdx) => {
|
|
788
|
-
const col = cols[colIdx]
|
|
789
|
-
const row = rows[rowIdx]
|
|
790
|
-
if (!col?.link || !row) return undefined
|
|
791
|
-
return col.link(row) || undefined
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
/** Build a `field -> align` map from resolved columns (for html / md). */
|
|
796
|
-
function alignMap<TData extends RowData>(
|
|
797
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
798
|
-
): Record<string, 'left' | 'center' | 'right'> {
|
|
799
|
-
const align: Record<string, 'left' | 'center' | 'right'> = {}
|
|
800
|
-
for (const c of cols) if (c.align) align[c.field] = c.align
|
|
801
|
-
return align
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
/**
|
|
805
|
-
* Serialize projected rows to the requested native format string. Shared by
|
|
806
|
-
* file export and clipboard export. Returns `{ text, mime, ext }`.
|
|
807
|
-
*/
|
|
808
|
-
async function serializeNative<TData extends RowData>(
|
|
809
|
-
format: ExportFormat,
|
|
810
|
-
projected: ReadonlyArray<Record<string, unknown>>,
|
|
811
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
812
|
-
opts: Pick<ExportOptions<TData>, 'onProgress' | 'signal' | 'csv' | 'filename'>,
|
|
813
|
-
cellStyle?: ExportCellVisualFn,
|
|
814
|
-
cellLink?: (rowIdx: number, colIdx: number) => string | undefined,
|
|
815
|
-
): Promise<{ text: string; mime: string; ext: string }> {
|
|
816
|
-
const fields = cols.map((c) => c.field)
|
|
817
|
-
const p = { onProgress: opts.onProgress, signal: opts.signal }
|
|
818
|
-
switch (format) {
|
|
819
|
-
case 'html': {
|
|
820
|
-
const text = await serializeHtml(projected, fields, {
|
|
821
|
-
...p,
|
|
822
|
-
title: (opts.filename ?? 'grid').trim() || 'grid',
|
|
823
|
-
align: alignMap(cols),
|
|
824
|
-
cellStyle,
|
|
825
|
-
cellLink,
|
|
826
|
-
})
|
|
827
|
-
return { text, mime: 'text/html;charset=utf-8', ext: 'html' }
|
|
828
|
-
}
|
|
829
|
-
case 'json': {
|
|
830
|
-
const text = await serializeJson(projected, fields, p)
|
|
831
|
-
return { text, mime: 'application/json;charset=utf-8', ext: 'json' }
|
|
832
|
-
}
|
|
833
|
-
case 'xml': {
|
|
834
|
-
const text = await serializeXml(projected, fields, p)
|
|
835
|
-
return { text, mime: 'application/xml;charset=utf-8', ext: 'xml' }
|
|
836
|
-
}
|
|
837
|
-
case 'md': {
|
|
838
|
-
const text = await serializeMarkdown(projected, fields, { ...p, align: alignMap(cols) })
|
|
839
|
-
return { text, mime: 'text/markdown;charset=utf-8', ext: 'md' }
|
|
840
|
-
}
|
|
841
|
-
case 'tsv': {
|
|
842
|
-
const text = await serializeDelimited(projected, fields, { ...p, csv: { ...opts.csv, delimiter: '\t' } })
|
|
843
|
-
return { text, mime: 'text/tab-separated-values;charset=utf-8', ext: 'tsv' }
|
|
844
|
-
}
|
|
845
|
-
case 'csv':
|
|
846
|
-
default: {
|
|
847
|
-
const delimiter = opts.csv?.delimiter ?? ','
|
|
848
|
-
const text = await serializeDelimited(projected, fields, { ...p, csv: { ...opts.csv, delimiter } })
|
|
849
|
-
return { text, mime: 'text/csv;charset=utf-8', ext: 'csv' }
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
/**
|
|
855
|
-
* Native text export (csv / tsv / html / json / xml / md). No Smart exporter,
|
|
856
|
-
* no peer deps - pure streaming serialization with progress + abort. Values
|
|
857
|
-
* are formatted to match the on-screen display (json defaults to raw so it
|
|
858
|
-
* stays machine-readable).
|
|
859
|
-
*/
|
|
860
|
-
async function exportTextFormat<
|
|
861
|
-
TFeatures extends TableFeatures,
|
|
862
|
-
TData extends RowData,
|
|
863
|
-
>(api: SvGridApi<TFeatures, TData>, opts: ExportOptions<TData>): Promise<ExportResult> {
|
|
864
|
-
if (opts.sheets && opts.sheets.length > 0) {
|
|
865
|
-
throw new Error(
|
|
866
|
-
`@svgrid/enterprise: multi-sheet export requires format 'xlsx', got '${opts.format}'`,
|
|
867
|
-
)
|
|
868
|
-
}
|
|
869
|
-
const sourceRows = resolveRowSource(api, opts.rows)
|
|
870
|
-
if (sourceRows.length === 0) {
|
|
871
|
-
throw new Error('@svgrid/enterprise: nothing to export - the grid has no rows')
|
|
872
|
-
}
|
|
873
|
-
const cols = resolveColumns(api, opts, sourceRows)
|
|
874
|
-
// JSON is a data format: default to raw values so consumers get real
|
|
875
|
-
// numbers/dates, not "$19.95" strings. The other formats default formatted.
|
|
876
|
-
const projectOpts =
|
|
877
|
-
opts.format === 'json' ? { ...opts, rawValues: opts.rawValues ?? true } : opts
|
|
878
|
-
const projected = await projectRows(sourceRows, cols, projectOpts)
|
|
879
|
-
const filename = (opts.filename ?? 'grid').trim() || 'grid'
|
|
880
|
-
|
|
881
|
-
// HTML carries conditional formatting (fill / color / bold / icon); the other
|
|
882
|
-
// native formats are unstyled data.
|
|
883
|
-
const cellStyle =
|
|
884
|
-
opts.format === 'html'
|
|
885
|
-
? buildConditionalResolver(cols, sourceRows, opts.conditionalFormats)
|
|
886
|
-
: undefined
|
|
887
|
-
const cellLink = opts.format === 'html' ? buildLinkResolver(cols, sourceRows) : undefined
|
|
888
|
-
const { text, mime, ext } = await serializeNative(opts.format, projected, cols, opts, cellStyle, cellLink)
|
|
889
|
-
return deliverText(text, ensureExt(filename, ext), mime, sourceRows.length, opts.download)
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
/**
|
|
893
|
-
* Legacy `.xls` export (Excel 2003 XML Spreadsheet). Dependency-free and
|
|
894
|
-
* streaming, like the text path, but produces a real typed spreadsheet.
|
|
895
|
-
*/
|
|
896
|
-
async function exportXls<
|
|
897
|
-
TFeatures extends TableFeatures,
|
|
898
|
-
TData extends RowData,
|
|
899
|
-
>(api: SvGridApi<TFeatures, TData>, opts: ExportOptions<TData>): Promise<ExportResult> {
|
|
900
|
-
if (opts.sheets && opts.sheets.length > 0) {
|
|
901
|
-
throw new Error(
|
|
902
|
-
"@svgrid/enterprise: multi-sheet export requires format 'xlsx'. Use 'xls' for a single legacy sheet.",
|
|
903
|
-
)
|
|
904
|
-
}
|
|
905
|
-
const sourceRows = resolveRowSource(api, opts.rows)
|
|
906
|
-
if (sourceRows.length === 0) {
|
|
907
|
-
throw new Error('@svgrid/enterprise: nothing to export - the grid has no rows')
|
|
908
|
-
}
|
|
909
|
-
const cols = resolveColumns(api, opts, sourceRows)
|
|
910
|
-
const filename = (opts.filename ?? 'grid').trim() || 'grid'
|
|
911
|
-
const visual = buildConditionalResolver(cols, sourceRows, opts.conditionalFormats)
|
|
912
|
-
const xml = await serializeSpreadsheetML(filename, cols, sourceRows, {
|
|
913
|
-
rawValues: opts.rawValues,
|
|
914
|
-
onProgress: opts.onProgress,
|
|
915
|
-
signal: opts.signal,
|
|
916
|
-
freezeHeader: opts.freezeHeader !== false,
|
|
917
|
-
freezeColumns: resolveFreezeColumns(api, opts, cols),
|
|
918
|
-
widths: opts.autoFitColumns !== false ? autoFitWidths(cols, sourceRows, opts.rawValues === true) : undefined,
|
|
919
|
-
cellVisual: opts.conditionalFormats?.length ? visual : undefined,
|
|
920
|
-
precisionSafe: opts.precisionSafe,
|
|
921
|
-
})
|
|
922
|
-
const mime = 'application/vnd.ms-excel;charset=utf-8'
|
|
923
|
-
return deliverText(xml, ensureExt(filename, 'xls'), mime, sourceRows.length, opts.download)
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
/** Columns whose values can be summed for a PDF subtotal row. */
|
|
927
|
-
function isSummableColumn<TData extends RowData>(col: ExportColumn<TData>): boolean {
|
|
928
|
-
return col.format?.type === 'number' || col.format?.type === 'currency'
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
/**
|
|
932
|
-
* Build a grouped PDF body: a bold group header per cluster (nested for
|
|
933
|
-
* multi-level grouping), the cluster's data rows, then a subtotal row summing
|
|
934
|
-
* the numeric / currency columns. Values use the same formatting as the grid.
|
|
935
|
-
*/
|
|
936
|
-
export function buildGroupedPdfBody<TData extends RowData>(
|
|
937
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
938
|
-
rows: ReadonlyArray<TData>,
|
|
939
|
-
groupBy: ReadonlyArray<string>,
|
|
940
|
-
): PdfBodyRow[] {
|
|
941
|
-
const out: PdfBodyRow[] = []
|
|
942
|
-
const headerOf = (field: string) => cols.find((c) => c.field === field)?.header ?? field
|
|
943
|
-
// Put the "Subtotal" label in the first NON-numeric column; if every column
|
|
944
|
-
// is numeric, -1 means "don't overwrite a sum with the label".
|
|
945
|
-
const labelColIdx = cols.findIndex((c) => !isSummableColumn(c))
|
|
946
|
-
|
|
947
|
-
const recurse = (subset: ReadonlyArray<TData>, level: number) => {
|
|
948
|
-
if (level >= groupBy.length) {
|
|
949
|
-
for (const r of subset) {
|
|
950
|
-
out.push({ kind: 'data', cells: cols.map((c) => String(cellValueFor(c, r, false) ?? '')) })
|
|
951
|
-
}
|
|
952
|
-
return
|
|
953
|
-
}
|
|
954
|
-
const field = groupBy[level]!
|
|
955
|
-
// Preserve first-seen order of group keys.
|
|
956
|
-
const buckets = new Map<string, TData[]>()
|
|
957
|
-
for (const r of subset) {
|
|
958
|
-
const key = String((r as Record<string, unknown>)[field] ?? '')
|
|
959
|
-
const bucket = buckets.get(key)
|
|
960
|
-
if (bucket) bucket.push(r)
|
|
961
|
-
else buckets.set(key, [r])
|
|
962
|
-
}
|
|
963
|
-
for (const [key, groupRows] of buckets) {
|
|
964
|
-
out.push({ kind: 'group', label: `${headerOf(field)}: ${key} (${groupRows.length})`, level })
|
|
965
|
-
recurse(groupRows, level + 1)
|
|
966
|
-
// Subtotal row for the numeric columns of this cluster.
|
|
967
|
-
const hasSummable = cols.some(isSummableColumn)
|
|
968
|
-
if (hasSummable) {
|
|
969
|
-
const cells = cols.map((c) => {
|
|
970
|
-
if (!isSummableColumn(c)) return ''
|
|
971
|
-
const sum = groupRows.reduce(
|
|
972
|
-
(a, r) => a + (Number((r as Record<string, unknown>)[c.field]) || 0),
|
|
973
|
-
0,
|
|
974
|
-
)
|
|
975
|
-
return formatValueForExport(sum, c.format)
|
|
976
|
-
})
|
|
977
|
-
if (labelColIdx >= 0) cells[labelColIdx] = `Subtotal (${key})`
|
|
978
|
-
out.push({ kind: 'subtotal', cells })
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
recurse(rows, 0)
|
|
983
|
-
return out
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
/**
|
|
987
|
-
* PDF export via our own pdfmake docDefinition (page setup, repeated header,
|
|
988
|
-
* page numbers, per-column alignment/widths, title/logo, theme colors, and
|
|
989
|
-
* group + subtotal rows carried from the grid's grouping). Values are the
|
|
990
|
-
* formatted display strings, so the PDF matches the grid.
|
|
991
|
-
*/
|
|
992
|
-
async function exportPdf<
|
|
993
|
-
TFeatures extends TableFeatures,
|
|
994
|
-
TData extends RowData,
|
|
995
|
-
>(api: SvGridApi<TFeatures, TData>, opts: ExportOptions<TData>): Promise<ExportResult> {
|
|
996
|
-
await ensureGlobals('pdf')
|
|
997
|
-
const pdfMake = (globalThis as unknown as {
|
|
998
|
-
pdfMake?: {
|
|
999
|
-
createPdf: (def: unknown) => {
|
|
1000
|
-
download(name: string): void
|
|
1001
|
-
getBlob(cb: (b: Blob) => void): void
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
}).pdfMake
|
|
1005
|
-
if (!pdfMake) {
|
|
1006
|
-
throw new Error('@svgrid/enterprise: pdf export requires the "pdfmake" peer dependency.')
|
|
1007
|
-
}
|
|
1008
|
-
const sourceRows = resolveRowSource(api, opts.rows)
|
|
1009
|
-
if (sourceRows.length === 0) {
|
|
1010
|
-
throw new Error('@svgrid/enterprise: nothing to export - the grid has no rows')
|
|
1011
|
-
}
|
|
1012
|
-
const cols = resolveColumns(api, opts, sourceRows)
|
|
1013
|
-
const fields = cols.map((c) => c.field)
|
|
1014
|
-
const columns = cols.map((c) => ({ header: c.header ?? c.field, align: c.align }))
|
|
1015
|
-
|
|
1016
|
-
// Carry the grid's active grouping into group + subtotal rows (unless the
|
|
1017
|
-
// caller opted out or passed hierarchical), matching the xlsx outline path.
|
|
1018
|
-
const autoGrouping =
|
|
1019
|
-
opts.autoGroup !== false && !opts.groupBy?.length && !opts.hierarchical
|
|
1020
|
-
? (api.getState().grouping ?? []).filter((id) => !!id).map((id) => String(id))
|
|
1021
|
-
: []
|
|
1022
|
-
const effGroupBy = opts.groupBy?.length
|
|
1023
|
-
? opts.groupBy
|
|
1024
|
-
: autoGrouping.length > 0
|
|
1025
|
-
? autoGrouping
|
|
1026
|
-
: undefined
|
|
1027
|
-
|
|
1028
|
-
if (opts.signal?.aborted) throw new DOMException('Export aborted', 'AbortError')
|
|
1029
|
-
opts.onProgress?.({ phase: 'write', ratio: 0, total: sourceRows.length })
|
|
1030
|
-
|
|
1031
|
-
const pdfOpts: PdfExportOptions = {
|
|
1032
|
-
...opts.pdf,
|
|
1033
|
-
pageOrientation: opts.pdf?.pageOrientation ?? opts.pageOrientation,
|
|
1034
|
-
}
|
|
1035
|
-
const now = new Date()
|
|
1036
|
-
let def
|
|
1037
|
-
if (effGroupBy?.length) {
|
|
1038
|
-
// Conditional formatting is skipped for grouped PDFs (group / subtotal
|
|
1039
|
-
// rows own the layout); the grid colors still show on screen.
|
|
1040
|
-
const body = buildGroupedPdfBody(cols, sourceRows, effGroupBy)
|
|
1041
|
-
def = buildPdfDocDefinition({ columns, body, opts: pdfOpts, now })
|
|
1042
|
-
} else {
|
|
1043
|
-
const projected = await projectRows(sourceRows, cols, opts)
|
|
1044
|
-
const dataRows = projected.slice(1).map((r) => fields.map((f) => String(r[f] ?? '')))
|
|
1045
|
-
// Conditional formatting -> per-cell fill / color / bold + icon prefix.
|
|
1046
|
-
const visual = buildConditionalResolver(cols, sourceRows, opts.conditionalFormats)
|
|
1047
|
-
for (let r = 0; r < dataRows.length; r++) {
|
|
1048
|
-
for (let c = 0; c < cols.length; c++) {
|
|
1049
|
-
const icon = visual(r, c)?.icon
|
|
1050
|
-
if (icon) dataRows[r]![c] = `${icon} ${dataRows[r]![c] ?? ''}`
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
const link = buildLinkResolver(cols, sourceRows)
|
|
1054
|
-
def = buildPdfDocDefinition({
|
|
1055
|
-
columns,
|
|
1056
|
-
rows: dataRows,
|
|
1057
|
-
dataCellStyle: (r, c) => {
|
|
1058
|
-
const v = visual(r, c)
|
|
1059
|
-
return v ? { fill: v.fill, color: v.color, bold: v.bold } : undefined
|
|
1060
|
-
},
|
|
1061
|
-
dataCellLink: link,
|
|
1062
|
-
opts: pdfOpts,
|
|
1063
|
-
now,
|
|
1064
|
-
})
|
|
1065
|
-
}
|
|
1066
|
-
const filename = (opts.filename ?? 'grid').trim() || 'grid'
|
|
1067
|
-
const blob = await new Promise<Blob>((resolve) => pdfMake.createPdf(def).getBlob(resolve))
|
|
1068
|
-
opts.onProgress?.({ phase: 'write', ratio: 1, total: sourceRows.length })
|
|
1069
|
-
return deliver(blob, ensureExt(filename, 'pdf'), 'application/pdf', sourceRows.length, opts.download)
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
/**
|
|
1073
|
-
* Resolve one cell into the typed descriptor the native xlsx writer needs:
|
|
1074
|
-
* real numbers / dates with a number format (Excel sums + sorts), or a
|
|
1075
|
-
* formatted string, plus conditional-format fill / color / bold / icon and a
|
|
1076
|
-
* hyperlink.
|
|
1077
|
-
*/
|
|
1078
|
-
async function buildXlsxCellMatrix<TData extends RowData>(
|
|
1079
|
-
cols: ReadonlyArray<ExportColumn<TData>>,
|
|
1080
|
-
rows: ReadonlyArray<TData>,
|
|
1081
|
-
rawValues: boolean,
|
|
1082
|
-
visual: ExportCellVisualFn,
|
|
1083
|
-
link: ((rowIdx: number, colIdx: number) => string | undefined) | undefined,
|
|
1084
|
-
opts?: { onProgress?: (p: ExportProgress) => void; signal?: AbortSignal; precisionSafe?: boolean },
|
|
1085
|
-
): Promise<{ header: string[]; cells: XlsxCell[][] }> {
|
|
1086
|
-
const header = cols.map((c) => c.header ?? c.field)
|
|
1087
|
-
const precisionSafe = opts?.precisionSafe === true
|
|
1088
|
-
// Excel stores numbers as 64-bit floats (~15 significant digits); a longer
|
|
1089
|
-
// integer (an ID) would be silently rounded. In precision-safe mode we write
|
|
1090
|
-
// such values as text so they survive intact.
|
|
1091
|
-
const num = (value: number, base: object, numFmt?: string): XlsxCell =>
|
|
1092
|
-
precisionSafe && Math.abs(value) >= 1e15
|
|
1093
|
-
? { t: 's', value: String(value), ...base }
|
|
1094
|
-
: { t: 'n', value, numFmt, ...base }
|
|
1095
|
-
|
|
1096
|
-
const cellFor = (row: TData, r: number, c: number): XlsxCell => {
|
|
1097
|
-
const col = cols[c]!
|
|
1098
|
-
const v = visual(r, c)
|
|
1099
|
-
const base = { align: col.align, fill: v?.fill, color: v?.color, bold: v?.bold, link: link?.(r, c) }
|
|
1100
|
-
if (col.exportValue) {
|
|
1101
|
-
const ev = col.exportValue(row)
|
|
1102
|
-
if (typeof ev === 'number' && Number.isFinite(ev)) return num(ev, base, toExcelNumFmt(col.format))
|
|
1103
|
-
if (ev instanceof Date) return { t: 'd', value: ev, numFmt: toExcelNumFmt(col.format) ?? 'yyyy-mm-dd', ...base }
|
|
1104
|
-
return { t: 's', value: ev == null ? '' : String(ev), ...base }
|
|
1105
|
-
}
|
|
1106
|
-
const raw = (row as Record<string, unknown>)[col.field]
|
|
1107
|
-
// An icon-set glyph forces a string cell (icon + formatted value).
|
|
1108
|
-
if (v?.icon) {
|
|
1109
|
-
const disp = rawValues ? String(raw ?? '') : formatValueForExport(raw, col.format)
|
|
1110
|
-
return { t: 's', value: `${v.icon} ${disp}`, ...base }
|
|
1111
|
-
}
|
|
1112
|
-
if (rawValues) {
|
|
1113
|
-
if (typeof raw === 'number' && Number.isFinite(raw)) return num(raw, base)
|
|
1114
|
-
return { t: 's', value: raw == null ? '' : String(raw), ...base }
|
|
1115
|
-
}
|
|
1116
|
-
const typed = valueForExcel(raw, col.format)
|
|
1117
|
-
if (typed.ok) {
|
|
1118
|
-
if (typed.value instanceof Date) return { t: 'd', value: typed.value, numFmt: toExcelNumFmt(col.format) ?? 'yyyy-mm-dd', ...base }
|
|
1119
|
-
return num(typed.value, base, toExcelNumFmt(col.format))
|
|
1120
|
-
}
|
|
1121
|
-
return { t: 's', value: formatValueForExport(raw, col.format), ...base }
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// Build in chunks, yielding to the event loop so a huge export doesn't freeze
|
|
1125
|
-
// the tab (the per-cell Intl formatting is the expensive part).
|
|
1126
|
-
const cells: XlsxCell[][] = []
|
|
1127
|
-
const total = rows.length
|
|
1128
|
-
const CHUNK = 2000
|
|
1129
|
-
for (let r = 0; r < total; r++) {
|
|
1130
|
-
if (opts?.signal?.aborted) throw new DOMException('Export aborted', 'AbortError')
|
|
1131
|
-
const row = rows[r]!
|
|
1132
|
-
cells.push(cols.map((_, c) => cellFor(row, r, c)))
|
|
1133
|
-
if (r > 0 && r % CHUNK === 0) {
|
|
1134
|
-
opts?.onProgress?.({ phase: 'project', ratio: r / total, row: r, total })
|
|
1135
|
-
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
return { header, cells }
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
/**
|
|
1142
|
-
* Native single-sheet xlsx export (our own OOXML writer). Typed numbers +
|
|
1143
|
-
* number formats, true per-cell conditional-format fills, freeze header,
|
|
1144
|
-
* auto-fit widths, and real hyperlinks - none of which the vendored writer can
|
|
1145
|
-
* do. Needs only `jszip`.
|
|
1146
|
-
*/
|
|
1147
|
-
async function exportXlsxNative<
|
|
1148
|
-
TFeatures extends TableFeatures,
|
|
1149
|
-
TData extends RowData,
|
|
1150
|
-
>(api: SvGridApi<TFeatures, TData>, opts: ExportOptions<TData>): Promise<ExportResult> {
|
|
1151
|
-
await ensureGlobals('xlsx') // loads jszip onto globalThis
|
|
1152
|
-
const JSZip = (globalThis as unknown as { JSZip?: new () => never }).JSZip as unknown as
|
|
1153
|
-
| (new () => { file(p: string, d: string): void; generateAsync(o: { type: 'blob'; mimeType?: string }): Promise<Blob> })
|
|
1154
|
-
| undefined
|
|
1155
|
-
if (!JSZip) throw new Error('@svgrid/enterprise: xlsx export requires the "jszip" peer dependency.')
|
|
1156
|
-
|
|
1157
|
-
const src = resolveRowSource(api, opts.rows)
|
|
1158
|
-
if (src.length === 0) {
|
|
1159
|
-
throw new Error('@svgrid/enterprise: nothing to export - the grid has no rows')
|
|
1160
|
-
}
|
|
1161
|
-
const cols = resolveColumns(api, opts, src)
|
|
1162
|
-
const rawValues = opts.rawValues === true
|
|
1163
|
-
|
|
1164
|
-
// Color scales + data bars become NATIVE Excel conditional formatting (Excel
|
|
1165
|
-
// computes them, so they're interactive). Predicate rules + icon sets stay
|
|
1166
|
-
// client-computed per cell. So the per-cell resolver only sees rule/iconSet.
|
|
1167
|
-
const cf = opts.conditionalFormats ?? []
|
|
1168
|
-
const cellCf = cf.filter((f) => f.type === 'rule' || f.type === 'iconSet')
|
|
1169
|
-
const visual = buildConditionalResolver(cols, src, cellCf)
|
|
1170
|
-
const link = buildLinkResolver(cols, src)
|
|
1171
|
-
|
|
1172
|
-
const condFormats: XlsxCondFormat[] = []
|
|
1173
|
-
for (const f of cf) {
|
|
1174
|
-
if (f.type !== 'colorScale' && f.type !== 'dataBar') continue
|
|
1175
|
-
const targetIdxs =
|
|
1176
|
-
f.columns && f.columns.length > 0
|
|
1177
|
-
? cols.map((c, i) => (f.columns!.includes(c.field) ? i : -1)).filter((i) => i >= 0)
|
|
1178
|
-
: cols.map((_, i) => i)
|
|
1179
|
-
for (const ci of targetIdxs) {
|
|
1180
|
-
if (f.type === 'dataBar') condFormats.push({ kind: 'dataBar', colIdx: ci, color: f.color })
|
|
1181
|
-
else {
|
|
1182
|
-
// min/mid/max are optional on the format; fall back to the same defaults
|
|
1183
|
-
// the on-screen renderer uses (#ffffff -> #000000) so the export matches.
|
|
1184
|
-
const min = f.min ?? '#ffffff'
|
|
1185
|
-
const max = f.max ?? '#000000'
|
|
1186
|
-
condFormats.push({ kind: 'colorScale', colIdx: ci, colors: f.mid ? [min, f.mid, max] : [min, max] })
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
if (opts.signal?.aborted) throw new DOMException('Export aborted', 'AbortError')
|
|
1192
|
-
opts.onProgress?.({ phase: 'write', ratio: 0, total: src.length })
|
|
1193
|
-
const { header, cells } = await buildXlsxCellMatrix(cols, src, rawValues, visual, link, {
|
|
1194
|
-
onProgress: opts.onProgress,
|
|
1195
|
-
signal: opts.signal,
|
|
1196
|
-
precisionSafe: opts.precisionSafe,
|
|
1197
|
-
})
|
|
1198
|
-
const widths = opts.autoFitColumns !== false ? autoFitWidths(cols, src, rawValues) : undefined
|
|
1199
|
-
const filename = (opts.filename ?? 'grid').trim() || 'grid'
|
|
1200
|
-
|
|
1201
|
-
// Excel Table (ListObject): filter dropdowns + banded rows, and optionally a
|
|
1202
|
-
// totals row with SUBTOTAL formulas over the summable columns.
|
|
1203
|
-
let table: XlsxTableSpec | undefined
|
|
1204
|
-
if (opts.excelTable) {
|
|
1205
|
-
const totalsRow = typeof opts.excelTable === 'object' ? opts.excelTable.totalsRow === true : false
|
|
1206
|
-
const style = typeof opts.excelTable === 'object' ? opts.excelTable.style : undefined
|
|
1207
|
-
table = {
|
|
1208
|
-
name: 'Table1',
|
|
1209
|
-
totalsRow,
|
|
1210
|
-
style,
|
|
1211
|
-
totalsFns: cols.map((c) => (isSummableColumn(c) ? 'sum' : undefined)),
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
const parts = buildXlsxParts({
|
|
1216
|
-
sheetName: filename,
|
|
1217
|
-
header,
|
|
1218
|
-
rows: cells,
|
|
1219
|
-
widths,
|
|
1220
|
-
freezeHeader: opts.freezeHeader !== false,
|
|
1221
|
-
freezeColumns: resolveFreezeColumns(api, opts, cols),
|
|
1222
|
-
condFormats,
|
|
1223
|
-
table,
|
|
1224
|
-
})
|
|
1225
|
-
const blob = await packageXlsx(parts, JSZip)
|
|
1226
|
-
opts.onProgress?.({ phase: 'write', ratio: 1, total: src.length })
|
|
1227
|
-
return deliver(blob, ensureExt(filename, 'xlsx'), 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', src.length, opts.download)
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
export async function exportGrid<
|
|
1231
|
-
TFeatures extends TableFeatures,
|
|
1232
|
-
TData extends RowData,
|
|
1233
|
-
>(api: SvGridApi<TFeatures, TData>, opts: ExportOptions<TData>): Promise<ExportResult | undefined> {
|
|
1234
|
-
assertEnterpriseLicensed('Export')
|
|
1235
|
-
|
|
1236
|
-
// Native text formats have their own dependency-free, streaming path.
|
|
1237
|
-
if (NATIVE_TEXT_FORMATS.includes(opts.format)) {
|
|
1238
|
-
return exportTextFormat(api, opts)
|
|
1239
|
-
}
|
|
1240
|
-
// Legacy xls (SpreadsheetML) is also dependency-free.
|
|
1241
|
-
if (opts.format === 'xls') {
|
|
1242
|
-
return exportXls(api, opts)
|
|
1243
|
-
}
|
|
1244
|
-
// PDF via our own pdfmake docDefinition.
|
|
1245
|
-
if (opts.format === 'pdf') {
|
|
1246
|
-
return exportPdf(api, opts)
|
|
1247
|
-
}
|
|
1248
|
-
|
|
1249
|
-
// Native single-sheet xlsx (our OOXML writer): typed numbers + number
|
|
1250
|
-
// formats, real per-cell conditional-format fills, freeze, widths, and
|
|
1251
|
-
// hyperlinks. Grouping (Excel outline), multi-sheet, images, merges, and
|
|
1252
|
-
// header/footer lines keep the vendored writer below.
|
|
1253
|
-
if (opts.format === 'xlsx') {
|
|
1254
|
-
const gridGrouping =
|
|
1255
|
-
opts.autoGroup !== false ? (api.getState().grouping ?? []).filter((id) => !!id) : []
|
|
1256
|
-
const grouped = (opts.groupBy?.length ?? 0) > 0 || gridGrouping.length > 0
|
|
1257
|
-
// Blanket `styles` (header fill / zebra) are applied by the vendored
|
|
1258
|
-
// writer, not our OOXML one - so a styled export (without conditional
|
|
1259
|
-
// formatting, which the native writer does render) keeps the Smart path.
|
|
1260
|
-
const wantsBlanketStyles = !!opts.styles && !opts.conditionalFormats?.length
|
|
1261
|
-
if (
|
|
1262
|
-
!opts.sheets?.length &&
|
|
1263
|
-
!grouped &&
|
|
1264
|
-
!opts.hierarchical &&
|
|
1265
|
-
!opts.merges?.length &&
|
|
1266
|
-
!opts.header?.length &&
|
|
1267
|
-
!opts.footer?.length &&
|
|
1268
|
-
!opts.imageFields?.length &&
|
|
1269
|
-
!wantsBlanketStyles
|
|
1270
|
-
) {
|
|
1271
|
-
return exportXlsxNative(api, opts)
|
|
1272
|
-
}
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
await ensureGlobals(opts.format)
|
|
1276
|
-
const Ctor = await getDataExporter()
|
|
1277
|
-
|
|
1278
|
-
// Multi-sheet path. Smart's `spreadsheets` option takes an array of
|
|
1279
|
-
// `{ label, dataSource, columns, dataFields? }`. Each entry becomes one
|
|
1280
|
-
// tab; the exporter prepends its own header row built from `columns`.
|
|
1281
|
-
if (opts.sheets && opts.sheets.length > 0) {
|
|
1282
|
-
if (opts.format !== 'xlsx') {
|
|
1283
|
-
throw new Error(`@svgrid/enterprise: multi-sheet export requires format 'xlsx', got '${opts.format}'`)
|
|
1284
|
-
}
|
|
1285
|
-
const rawValues = opts.rawValues === true
|
|
1286
|
-
const sheets = opts.sheets.map((sheet) => {
|
|
1287
|
-
// Reuse the single-sheet column derivation (explicit → grid columns →
|
|
1288
|
-
// row keys), so per-sheet columns pick up `format` for formatting too.
|
|
1289
|
-
const cols = resolveColumns(
|
|
1290
|
-
api,
|
|
1291
|
-
{ columns: sheet.columns ?? opts.columns },
|
|
1292
|
-
sheet.rows,
|
|
1293
|
-
)
|
|
1294
|
-
// Project each row to only the included fields (in column order),
|
|
1295
|
-
// applying per-column formatting / exportValue.
|
|
1296
|
-
const dataSource = sheet.rows.map((r) => {
|
|
1297
|
-
const out: Record<string, unknown> = {}
|
|
1298
|
-
for (const c of cols) out[c.field] = cellValueFor(c, r, rawValues)
|
|
1299
|
-
return out
|
|
1300
|
-
})
|
|
1301
|
-
const widths = opts.autoFitColumns !== false ? autoFitWidths(cols, sheet.rows, rawValues) : null
|
|
1302
|
-
return {
|
|
1303
|
-
label: sheet.label,
|
|
1304
|
-
dataSource,
|
|
1305
|
-
columns: cols.map((c, i) =>
|
|
1306
|
-
widths
|
|
1307
|
-
? { dataField: c.field, label: c.header ?? c.field, width: widths[i] }
|
|
1308
|
-
: { dataField: c.field, label: c.header ?? c.field },
|
|
1309
|
-
),
|
|
1310
|
-
dataFields: cols.map((c) => c.field),
|
|
1311
|
-
style: translateStyles(sheet.styles ?? opts.styles),
|
|
1312
|
-
}
|
|
1313
|
-
})
|
|
1314
|
-
const filename = (opts.filename ?? 'grid').trim() || 'grid'
|
|
1315
|
-
const ctorOpts = buildExporterOptions(opts)
|
|
1316
|
-
// Smart needs at least one main sheet to operate. Use the first sheet
|
|
1317
|
-
// as the "main" exportData payload AND keep the full set on the
|
|
1318
|
-
// `spreadsheets` property so the workbook ends up with one tab per
|
|
1319
|
-
// entry.
|
|
1320
|
-
const firstSheet = sheets[0]!
|
|
1321
|
-
const exporter = new Ctor(ctorOpts)
|
|
1322
|
-
exporter.spreadsheets = sheets
|
|
1323
|
-
applyInstanceOptions(exporter, opts, firstSheet.dataFields)
|
|
1324
|
-
// First sheet's data goes through the standard exportData path with
|
|
1325
|
-
// its own header row.
|
|
1326
|
-
const headerRow: Record<string, unknown> = {}
|
|
1327
|
-
for (const c of firstSheet.columns) headerRow[c.dataField] = c.label
|
|
1328
|
-
const firstSheetRows = [headerRow, ...firstSheet.dataSource]
|
|
1329
|
-
if (opts.download === false) {
|
|
1330
|
-
throw new Error('@svgrid/enterprise: download:false is not supported for multi-sheet xlsx.')
|
|
1331
|
-
}
|
|
1332
|
-
exporter.exportData(firstSheetRows, opts.format, filename)
|
|
1333
|
-
return undefined
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
// Single-sheet xlsx (grouped / merges / header / footer) path.
|
|
1337
|
-
const sourceRows = resolveRowSource(api, opts.rows)
|
|
1338
|
-
if (sourceRows.length === 0) {
|
|
1339
|
-
throw new Error('@svgrid/enterprise: nothing to export - the grid has no rows')
|
|
1340
|
-
}
|
|
1341
|
-
const cols = resolveColumns(api, opts, sourceRows)
|
|
1342
|
-
const rows = await projectRows(sourceRows, cols, opts)
|
|
1343
|
-
const filename = (opts.filename ?? 'grid').trim() || 'grid'
|
|
1344
|
-
const datafields = cols.map((c) => c.field)
|
|
1345
|
-
|
|
1346
|
-
// Auto-carry the grid's active row grouping (unless the caller opted out,
|
|
1347
|
-
// passed their own groupBy, or is exporting a hierarchy). Grouping column
|
|
1348
|
-
// ids line up with field names, which are the projected row keys.
|
|
1349
|
-
const autoGrouping =
|
|
1350
|
-
opts.autoGroup !== false && !opts.groupBy?.length && !opts.hierarchical
|
|
1351
|
-
? (api.getState().grouping ?? []).filter((id) => !!id).map((id) => String(id))
|
|
1352
|
-
: []
|
|
1353
|
-
const effectiveGroupBy = opts.groupBy?.length
|
|
1354
|
-
? opts.groupBy
|
|
1355
|
-
: autoGrouping.length > 0
|
|
1356
|
-
? autoGrouping
|
|
1357
|
-
: undefined
|
|
1358
|
-
// The constructor's SECOND arg is `groupBy`: when non-empty AND
|
|
1359
|
-
// `hierarchical` is NOT set, Smart wraps each group of rows in an
|
|
1360
|
-
// Excel outline row with an expand/collapse button at the group key.
|
|
1361
|
-
const groupBy = !opts.hierarchical && effectiveGroupBy?.length ? effectiveGroupBy : undefined
|
|
1362
|
-
|
|
1363
|
-
// Conditional formatting -> a per-cell style map (A1 refs) that Smart's xlsx
|
|
1364
|
-
// writer applies, plus icon-set glyphs prepended to the cell text.
|
|
1365
|
-
const ctorOpts = buildExporterOptions(opts)
|
|
1366
|
-
if (opts.conditionalFormats?.length) {
|
|
1367
|
-
const visual = buildConditionalResolver(cols, sourceRows, opts.conditionalFormats)
|
|
1368
|
-
const cfCells: Record<string, ExportCellStyle> = {}
|
|
1369
|
-
for (let r = 0; r < sourceRows.length; r++) {
|
|
1370
|
-
for (let c = 0; c < cols.length; c++) {
|
|
1371
|
-
const v = visual(r, c)
|
|
1372
|
-
if (!v) continue
|
|
1373
|
-
const st: ExportCellStyle = {}
|
|
1374
|
-
if (v.fill) st.backgroundColor = v.fill
|
|
1375
|
-
if (v.color) st.color = v.color
|
|
1376
|
-
if (v.bold) st.fontWeight = 'bold'
|
|
1377
|
-
if (Object.keys(st).length) cfCells[colLetter(c) + (r + 2)] = st // header is row 1
|
|
1378
|
-
if (v.icon) {
|
|
1379
|
-
const dataRow = rows[r + 1] // header at index 0
|
|
1380
|
-
const field = cols[c]!.field
|
|
1381
|
-
if (dataRow) dataRow[field] = `${v.icon} ${dataRow[field] ?? ''}`
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
if (Object.keys(cfCells).length) {
|
|
1386
|
-
const style = (ctorOpts.style as Record<string, unknown> | undefined) ?? {}
|
|
1387
|
-
ctorOpts.style = {
|
|
1388
|
-
...style,
|
|
1389
|
-
cells: { ...((style.cells as Record<string, unknown> | undefined) ?? {}), ...cfCells },
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
if (opts.download === false) {
|
|
1395
|
-
throw new Error(
|
|
1396
|
-
'@svgrid/enterprise: download:false is not supported for this xlsx export (grouped / merges / header / footer / images). Use a plain xlsx, xls, pdf, or a text format.',
|
|
1397
|
-
)
|
|
1398
|
-
}
|
|
1399
|
-
if (opts.signal?.aborted) throw new DOMException('Export aborted', 'AbortError')
|
|
1400
|
-
opts.onProgress?.({ phase: 'write', ratio: 0, total: rows.length - 1 })
|
|
1401
|
-
const exporter = new Ctor(ctorOpts, groupBy)
|
|
1402
|
-
applyInstanceOptions(exporter, opts, datafields)
|
|
1403
|
-
exporter.exportData(rows, opts.format, filename)
|
|
1404
|
-
opts.onProgress?.({ phase: 'write', ratio: 1, total: rows.length - 1 })
|
|
1405
|
-
return undefined
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
/** Native formats that can be placed on the clipboard. */
|
|
1409
|
-
export type ClipboardFormat = 'csv' | 'tsv' | 'html' | 'json' | 'xml' | 'md'
|
|
1410
|
-
|
|
1411
|
-
export type ClipboardExportOptions<TData> = {
|
|
1412
|
-
/** Clipboard payload format. Default 'tsv' (pastes cleanly into Excel / Sheets). */
|
|
1413
|
-
format?: ClipboardFormat
|
|
1414
|
-
columns?: ReadonlyArray<ExportColumn<TData>>
|
|
1415
|
-
rows?: ExportRowSource<TData>
|
|
1416
|
-
rawValues?: boolean
|
|
1417
|
-
csv?: CsvOptions
|
|
1418
|
-
onProgress?: (progress: ExportProgress) => void
|
|
1419
|
-
signal?: AbortSignal
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
/** Write text (and, for html, rich text/html) to the clipboard. */
|
|
1423
|
-
async function writeClipboard(text: string, format: ClipboardFormat): Promise<void> {
|
|
1424
|
-
const nav = typeof navigator !== 'undefined' ? navigator : undefined
|
|
1425
|
-
if (!nav?.clipboard) {
|
|
1426
|
-
throw new Error('@svgrid/enterprise: clipboard is unavailable (needs a secure context + user gesture)')
|
|
1427
|
-
}
|
|
1428
|
-
// For html, offer BOTH text/html (rich paste into Excel / Sheets / Docs) and
|
|
1429
|
-
// a text/plain fallback. ClipboardItem may be missing in older browsers.
|
|
1430
|
-
const CI = (globalThis as { ClipboardItem?: typeof ClipboardItem }).ClipboardItem
|
|
1431
|
-
if (format === 'html' && CI && nav.clipboard.write) {
|
|
1432
|
-
const item = new CI({
|
|
1433
|
-
'text/html': new Blob([text], { type: 'text/html' }),
|
|
1434
|
-
'text/plain': new Blob([text], { type: 'text/plain' }),
|
|
1435
|
-
})
|
|
1436
|
-
await nav.clipboard.write([item])
|
|
1437
|
-
return
|
|
1438
|
-
}
|
|
1439
|
-
await nav.clipboard.writeText(text)
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
/**
|
|
1443
|
-
* Copy the grid (current view / selection / all) to the clipboard in a native
|
|
1444
|
-
* text format instead of downloading a file. Defaults to `tsv`, which pastes
|
|
1445
|
-
* straight into Excel / Google Sheets as real columns. `html` writes rich
|
|
1446
|
-
* `text/html` so pasted cells keep their table structure.
|
|
1447
|
-
*/
|
|
1448
|
-
export async function copyExportToClipboard<
|
|
1449
|
-
TFeatures extends TableFeatures,
|
|
1450
|
-
TData extends RowData,
|
|
1451
|
-
>(api: SvGridApi<TFeatures, TData>, opts: ClipboardExportOptions<TData> = {}): Promise<void> {
|
|
1452
|
-
assertEnterpriseLicensed('Export')
|
|
1453
|
-
const format: ClipboardFormat = opts.format ?? 'tsv'
|
|
1454
|
-
const sourceRows = resolveRowSource(api, opts.rows)
|
|
1455
|
-
if (sourceRows.length === 0) {
|
|
1456
|
-
throw new Error('@svgrid/enterprise: nothing to copy - the grid has no rows')
|
|
1457
|
-
}
|
|
1458
|
-
const cols = resolveColumns(api, opts, sourceRows)
|
|
1459
|
-
const projectOpts =
|
|
1460
|
-
format === 'json' ? { ...opts, rawValues: opts.rawValues ?? true } : opts
|
|
1461
|
-
const projected = await projectRows(sourceRows, cols, projectOpts)
|
|
1462
|
-
const { text } = await serializeNative(format, projected, cols, opts)
|
|
1463
|
-
await writeClipboard(text, format)
|
|
1464
|
-
}
|