@svgrid/enterprise 2.7.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -95
- package/dist/ExpressionEditorHarness.svelte.d.ts +2 -2
- package/dist/SvAdvancedFilter.svelte.d.ts +2 -2
- package/dist/SvAlertRuleEditor.svelte.d.ts +2 -2
- package/dist/SvAlertsManager.svelte.d.ts +3 -3
- package/dist/SvAlertsPanel.svelte.d.ts +1 -1
- package/dist/SvAuthGate.svelte +115 -115
- package/dist/SvAuthGate.svelte.d.ts +1 -1
- package/dist/SvBoard.svelte.d.ts +1 -1
- package/dist/SvExportMenu.svelte +553 -553
- package/dist/SvExportMenu.svelte.d.ts +2 -2
- package/dist/SvExpressionEditor.svelte.d.ts +2 -2
- package/dist/SvFileInput.svelte +113 -113
- package/dist/SvGridAlerts.svelte.d.ts +2 -2
- package/dist/SvGridBoard.svelte +2358 -2358
- package/dist/SvGridBulkEditDrawer.svelte.d.ts +1 -1
- package/dist/SvGridEditPanel.svelte +1157 -1157
- package/dist/SvGridEditPanel.svelte.d.ts +3 -3
- package/dist/SvGridMasterDetail.svelte +116 -116
- package/dist/SvGridMasterDetail.svelte.d.ts +1 -1
- package/dist/SvGridScheduler.svelte +5358 -5358
- package/dist/SvImportDialog.svelte +1020 -1020
- package/dist/SvImportDialog.svelte.d.ts +2 -2
- package/dist/SvLookupInput.svelte +180 -180
- package/dist/SvLookupInput.svelte.d.ts +1 -1
- package/dist/SvPivotDesigner.svelte +1990 -1990
- package/dist/SvPivotDesigner.svelte.d.ts +2 -2
- package/dist/SvRecordDetail.svelte.d.ts +1 -1
- package/dist/SvSchedule.svelte.d.ts +1 -1
- package/dist/SvSchemaChart.svelte +234 -234
- package/dist/SvSchemaChart.svelte.d.ts +2 -2
- package/dist/SvSchemaDashboard.svelte +130 -130
- package/dist/SvSchemaDashboard.svelte.d.ts +3 -3
- package/dist/advanced-filter/expr-columns-from-grid.d.ts +1 -1
- package/dist/advanced-filter-enable.js +3 -3
- package/dist/alerts/alert-engine-attach.d.ts +2 -2
- package/dist/alerts/alert-engine-attach.js +3 -3
- package/dist/alerts/alert-engine.d.ts +1 -1
- package/dist/alerts/alert-engine.js +2 -2
- package/dist/alerts/alert-formats.d.ts +1 -1
- package/dist/alerts/alert-formats.js +1 -1
- package/dist/alerts/alert-observer.d.ts +3 -3
- package/dist/alerts/alert-observer.js +1 -1
- package/dist/alerts/alert-storage.d.ts +1 -1
- package/dist/alerts/alert-store.svelte.d.ts +1 -1
- package/dist/alerts/alert-types.d.ts +2 -2
- package/dist/alerts.js +2 -2
- package/dist/board.js +2 -2
- package/dist/cdn/svgrid-enterprise.svelte-external.js +1162 -1145
- package/dist/designer/assets/GridMenus-B1JtIN21.js +8 -0
- package/dist/designer/assets/SvDateRangeInput-C0iCCZ8o.css +1 -0
- package/dist/designer/assets/{SvDateRangeInput-CVn-yqrQ.js → SvDateRangeInput-CjO7nn_x.js} +1 -1
- package/dist/designer/assets/SvDateTimePicker-ByBD_ov6.js +1 -0
- package/dist/designer/assets/SvDateTimePicker-DPv09DIT.css +1 -0
- package/dist/designer/assets/SvField-YDLipR6O.css +1 -0
- package/dist/designer/assets/{SvField-BsqiA5me.js → SvField-cj1PFNgZ.js} +1 -1
- package/dist/designer/assets/{preload-helper-CG0n22O7.js → SvGrid.helpers-BgT-gnCo.js} +1 -1
- package/dist/designer/assets/SvGridCellEditor-0W1L2hJ1.js +2 -0
- package/dist/designer/assets/SvGridChart-DBOR4xcz.js +2 -0
- package/dist/designer/assets/SvGridChart-fkMWeh1J.css +1 -0
- package/dist/designer/assets/SvGridChartPanel-symrhNnR.js +11 -0
- package/dist/designer/assets/SvGridChartView-BmRxq6mU.js +1 -0
- package/dist/designer/assets/SvGridChartView-DUc-1RUf.css +1 -0
- package/dist/designer/assets/SvGridDropdown-Bb3HH2Aj.js +1 -0
- package/dist/designer/assets/SvGridDropdown-CcidB3rm.css +1 -0
- package/dist/designer/assets/SvListBox-1oUF6Iav.js +110 -0
- package/dist/designer/assets/SvListBox-BXfyqy4a.css +1 -0
- package/dist/designer/assets/cell-formatting-wr5wm7US.js +1 -0
- package/dist/designer/assets/chart-DXFganU8.js +1 -0
- package/dist/designer/assets/date-format-DGagAqRc.css +1 -0
- package/dist/designer/assets/{date-format-4DJvNtG2.js → date-format-onlYDsrY.js} +1 -1
- package/dist/designer/assets/export-format-D-eP4b2l.js +6 -0
- package/dist/designer/assets/{index-BvaLmQSd.js → index-C_a9Cj8Y.js} +2984 -141
- package/dist/designer/assets/index-k2stW6L8.css +1 -0
- package/dist/designer/assets/preload-helper-HclGiUj8.js +1 -0
- package/dist/designer/assets/{row-drag-touch-IuJPEt0z.js → row-drag-touch-BdknxBNL.js} +1 -1
- package/dist/designer/assets/src-D9p27OGC.js +58 -0
- package/dist/designer/index.html +22 -22
- package/dist/export-conditional.d.ts +1 -1
- package/dist/export-print.js +19 -19
- package/dist/export-xls.d.ts +2 -2
- package/dist/export.d.ts +2 -2
- package/dist/export.js +7 -7
- package/dist/expressions/builder-tree.d.ts +2 -2
- package/dist/expressions/builder-tree.js +1 -1
- package/dist/expressions/compile.d.ts +1 -1
- package/dist/import.js +1 -1
- package/dist/index.d.ts +53 -53
- package/dist/index.js +50 -50
- package/dist/install.d.ts +4 -4
- package/dist/install.js +11 -11
- package/dist/license.d.ts +3 -2
- package/dist/license.js +9 -4
- package/dist/node/studio.js +124 -115
- package/dist/pivot-chart.d.ts +1 -1
- package/dist/pivot-designer.d.ts +1 -1
- package/dist/pivot-enable.js +3 -3
- package/dist/print.d.ts +1 -1
- package/dist/print.js +4 -4
- package/dist/scheduler-config.d.ts +5 -5
- package/dist/scheduler-freebusy.d.ts +1 -1
- package/dist/scheduler-freebusy.js +1 -1
- package/dist/scheduler.js +2 -2
- package/dist/schema-designer.d.ts +1 -1
- package/dist/schema-designer.js +2 -2
- package/dist/selection-bar.js +2 -2
- package/dist/sources/aggregate.d.ts +1 -1
- package/dist/sources/auth-supabase.js +1 -1
- package/dist/sources/dashboard.d.ts +2 -2
- package/dist/sources/dashboard.js +1 -1
- package/dist/sources/index.d.ts +13 -13
- package/dist/sources/index.js +13 -13
- package/dist/sources/realtime-supabase.js +1 -1
- package/dist/sources/relation-lookup.d.ts +1 -1
- package/dist/sources/relation-lookup.js +1 -1
- package/dist/sources/rest-adapters.d.ts +1 -1
- package/dist/sources/rest-adapters.js +1 -1
- package/dist/sources/rest.d.ts +2 -2
- package/dist/sources/rest.js +3 -3
- package/dist/sources/supabase.d.ts +2 -2
- package/dist/sources/supabase.js +3 -3
- package/dist/sources/with-entity-rules.d.ts +1 -1
- package/dist/sources/with-entity-rules.js +1 -1
- package/dist/sources/with-relation-labels.d.ts +1 -1
- package/dist/studio/emit-project.js +2161 -2127
- package/dist/studio/emit-schema.js +369 -369
- package/dist/studio/index.d.ts +1 -1
- package/dist/studio/index.js +1 -1
- package/dist/studio/introspect-db.js +45 -45
- package/dist/studio/project.d.ts +10 -0
- package/dist/studio/samples/crm.js +6 -6
- package/dist/studio/samples/fleet.js +7 -7
- package/dist/studio/samples/insurance.js +9 -9
- package/dist/studio/samples/inventory.js +12 -12
- package/dist/studio/samples/projects.js +5 -5
- package/dist/studio/samples/support.js +8 -8
- package/dist/studio/scaffold-app.js +104 -104
- package/dist/studio/scaffold.js +151 -151
- package/dist/studio/ui-components.d.ts +7 -0
- package/dist/studio/ui-components.js +35 -0
- package/dist/sveltekit/in-memory.d.ts +3 -3
- package/dist/sveltekit/in-memory.js +4 -4
- package/dist/sveltekit/index.d.ts +6 -6
- package/dist/sveltekit/index.js +5 -5
- package/dist/sveltekit/query-plan.d.ts +1 -1
- package/dist/sveltekit/sql-source.d.ts +4 -4
- package/dist/sveltekit/sql-source.js +4 -4
- package/dist/sveltekit/sql.d.ts +1 -1
- package/dist/sveltekit/transport.d.ts +2 -2
- package/dist/sveltekit/transport.js +1 -1
- package/dist/upgrade-prompt.js +25 -25
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -3
- package/dist/designer/assets/GridMenus-Cgilexpk.js +0 -8
- package/dist/designer/assets/SvDateRangeInput-BV5OXYQ2.css +0 -1
- package/dist/designer/assets/SvDateTimePicker-BdioTrJQ.js +0 -1
- package/dist/designer/assets/SvDateTimePicker-CY8TK4BT.css +0 -1
- package/dist/designer/assets/SvField-BF-9TJSd.css +0 -1
- package/dist/designer/assets/SvGridCellEditor-dLY7_kk9.js +0 -2
- package/dist/designer/assets/SvGridChart-BEDNTdc5.js +0 -2
- package/dist/designer/assets/SvGridChart-Vu4Nvjiv.css +0 -1
- package/dist/designer/assets/SvGridChartPanel-x3lzSxuf.js +0 -10
- package/dist/designer/assets/SvGridChartView-CAKhPELi.js +0 -1
- package/dist/designer/assets/SvGridChartView-DX9HfkBR.css +0 -1
- package/dist/designer/assets/SvGridDropdown-CAYG7Y-O.js +0 -1
- package/dist/designer/assets/SvGridDropdown-Crwb2P3Q.css +0 -1
- package/dist/designer/assets/SvListBox-CVYQcLX9.js +0 -1
- package/dist/designer/assets/SvListBox-DgN8kvjt.css +0 -1
- package/dist/designer/assets/chart-DHEgt4lN.js +0 -1
- package/dist/designer/assets/date-format-Dzy4mTGh.css +0 -1
- package/dist/designer/assets/export-format-DwjuTfrQ.js +0 -6
- package/dist/designer/assets/index-Cp7fDJ3b.css +0 -1
- package/dist/designer/assets/js-scroller.svelte-Bhxqb3-c.js +0 -110
- package/dist/designer/assets/src-7qOYsBug.js +0 -2896
- package/dist/designer/assets/src-B7DNcoQK.css +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/_svelte_metadata.json +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/_metadata.json +0 -178
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js +0 -3875
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js +0 -21
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js +0 -1938
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js +0 -205
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js +0 -18
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js +0 -264
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js +0 -697
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/package.json +0 -3
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js +0 -134
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js +0 -55
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_attachments.js +0 -2
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js +0 -46843
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js +0 -237
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_events.js +0 -2
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js +0 -4
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_client.js +0 -7
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js +0 -4
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js +0 -5
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js +0 -5
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js +0 -5
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_server.js +0 -6
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js +0 -83
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js +0 -559
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js +0 -24
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js +0 -122
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_server.js +0 -2
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js +0 -89
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js +0 -241
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js +0 -4938
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js +0 -495
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js +0 -190
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js +0 -8
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js.map +0 -1
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js +0 -138
- package/dist/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js.map +0 -1
|
@@ -17,8 +17,8 @@ type PivotInputRow = Record<string, unknown>;
|
|
|
17
17
|
* SvPivotDesigner {data} {fields} bind:layout
|
|
18
18
|
*/
|
|
19
19
|
import { type ColumnDef } from '@svgrid/grid';
|
|
20
|
-
import { type PivotRow, type PivotAggregatorId } from './pivot';
|
|
21
|
-
import { type PivotField, type PivotLayout, type PivotPreset } from './pivot-designer';
|
|
20
|
+
import { type PivotRow, type PivotAggregatorId } from './pivot.js';
|
|
21
|
+
import { type PivotField, type PivotLayout, type PivotPreset } from './pivot-designer.js';
|
|
22
22
|
declare function $$render<T extends PivotInputRow>(): {
|
|
23
23
|
props: {
|
|
24
24
|
/** Flat input rows. */
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* identically. When `row` is omitted it self-drives off `rows` (defaults to the
|
|
13
13
|
* first, switchable via the header dropdown).
|
|
14
14
|
*/
|
|
15
|
-
import type { EntitySchema } from './schema';
|
|
15
|
+
import type { EntitySchema } from './schema.js';
|
|
16
16
|
type Row = Record<string, unknown>;
|
|
17
17
|
type Section = {
|
|
18
18
|
label: string;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Pure presentation over plain rows + an EntitySchema (same inputs as SvBoard /
|
|
9
9
|
* SvSchemaChart), so the generated app and the designer preview match.
|
|
10
10
|
*/
|
|
11
|
-
import type { EntitySchema } from './schema';
|
|
11
|
+
import type { EntitySchema } from './schema.js';
|
|
12
12
|
type Row = Record<string, unknown>;
|
|
13
13
|
type $$ComponentProps = {
|
|
14
14
|
schema: EntitySchema<any>;
|
|
@@ -1,234 +1,234 @@
|
|
|
1
|
-
<script lang="ts" module>
|
|
2
|
-
type ChartRow = Record<string, unknown>
|
|
3
|
-
</script>
|
|
4
|
-
|
|
5
|
-
<script lang="ts" generics="TData extends ChartRow">
|
|
6
|
-
/**
|
|
7
|
-
* SvSchemaChart - a reusable chart bound to an `EntitySchema`. It picks
|
|
8
|
-
* chart-able fields from the schema (dimensions to group by, measures to
|
|
9
|
-
* reduce), renders built-in controls, and draws with `SvGridChart`.
|
|
10
|
-
*
|
|
11
|
-
* Aggregation is either client-side (pass `rows`) or server-side (pass
|
|
12
|
-
* `getAggregate`, e.g. a SQL / Supabase source's `GROUP BY`), so it scales
|
|
13
|
-
* past what a page holds. Click a category to drill (`onDrill`).
|
|
14
|
-
*/
|
|
15
|
-
import { SvGridChart, SvGridDropdown, type ChartSpec, type ChartType, type ChartSelection } from '@svgrid/grid'
|
|
16
|
-
import { titleCase, type EntitySchema } from './schema'
|
|
17
|
-
import {
|
|
18
|
-
aggregateRows,
|
|
19
|
-
chartFieldsFromSchema,
|
|
20
|
-
type AggregateBucket,
|
|
21
|
-
type AggregateReduce,
|
|
22
|
-
type AggregateRequest,
|
|
23
|
-
} from './sources/aggregate'
|
|
24
|
-
|
|
25
|
-
type FilterModel = AggregateRequest['filterModel']
|
|
26
|
-
|
|
27
|
-
type Props = {
|
|
28
|
-
schema: EntitySchema<TData>
|
|
29
|
-
/** Client-side data to aggregate. Ignored when `getAggregate` is set. */
|
|
30
|
-
rows?: ReadonlyArray<TData>
|
|
31
|
-
/** Server-side aggregation (a data source's `getAggregate`). Takes precedence. */
|
|
32
|
-
getAggregate?: (request: AggregateRequest) => Promise<AggregateBucket[]>
|
|
33
|
-
/** Filter passed to `getAggregate`. */
|
|
34
|
-
filterModel?: FilterModel
|
|
35
|
-
dimension?: string
|
|
36
|
-
measure?: string
|
|
37
|
-
reduce?: AggregateReduce
|
|
38
|
-
type?: ChartType
|
|
39
|
-
/** Show the built-in dimension / measure / type controls. Default true. */
|
|
40
|
-
controls?: boolean
|
|
41
|
-
height?: number
|
|
42
|
-
width?: number
|
|
43
|
-
dataLabels?: boolean
|
|
44
|
-
formatValue?: (v: number) => string
|
|
45
|
-
onDrill?: (category: string, dimension: string) => void
|
|
46
|
-
/** Bump to re-fetch server aggregates (e.g. after a mutation). */
|
|
47
|
-
refreshKey?: unknown
|
|
48
|
-
/** Brand color. Bars / lines use it; pie slices grouped by an enum use the
|
|
49
|
-
* enum's own option colors (matching status badges). */
|
|
50
|
-
accent?: string
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let {
|
|
54
|
-
schema,
|
|
55
|
-
rows,
|
|
56
|
-
getAggregate,
|
|
57
|
-
filterModel,
|
|
58
|
-
dimension,
|
|
59
|
-
measure,
|
|
60
|
-
reduce,
|
|
61
|
-
type,
|
|
62
|
-
controls = true,
|
|
63
|
-
height = 300,
|
|
64
|
-
width = 460,
|
|
65
|
-
dataLabels = true,
|
|
66
|
-
formatValue,
|
|
67
|
-
onDrill,
|
|
68
|
-
refreshKey,
|
|
69
|
-
accent,
|
|
70
|
-
}: Props = $props()
|
|
71
|
-
|
|
72
|
-
const fields = $derived(chartFieldsFromSchema(schema as EntitySchema))
|
|
73
|
-
|
|
74
|
-
// Render the chart at its ACTUAL pixel width so the SVG viewBox maps 1:1 to
|
|
75
|
-
// screen pixels (scale factor = 1). Otherwise the SVG stretches to fill a wide
|
|
76
|
-
// block and its text (font-size in user units) scales up with it - "fonts that
|
|
77
|
-
// grow with the chart". Measured width keeps labels at a fixed px size, like
|
|
78
|
-
// the grid. Falls back to the `width` prop until the first measurement.
|
|
79
|
-
let measuredW = $state(0)
|
|
80
|
-
let plotEl: HTMLDivElement | undefined = $state()
|
|
81
|
-
$effect(() => {
|
|
82
|
-
const el = plotEl
|
|
83
|
-
if (!el || typeof ResizeObserver === 'undefined') return
|
|
84
|
-
const ro = new ResizeObserver((entries) => {
|
|
85
|
-
const w = Math.round(entries[0]?.contentRect.width ?? 0)
|
|
86
|
-
if (w > 0 && w !== measuredW) measuredW = w
|
|
87
|
-
})
|
|
88
|
-
ro.observe(el)
|
|
89
|
-
return () => ro.disconnect()
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
let dim = $state('')
|
|
93
|
-
let msr = $state('')
|
|
94
|
-
let red = $state<AggregateReduce>('sum')
|
|
95
|
-
let ctype = $state<ChartType>('bar')
|
|
96
|
-
|
|
97
|
-
// Follow the props reactively, so a controlled parent (e.g. the Studio preview,
|
|
98
|
-
// which hides the dropdowns) updates the chart whenever its dimension / measure /
|
|
99
|
-
// reduce / type change. One effect per prop so a change to one doesn't reset the
|
|
100
|
-
// others; the built-in dropdowns still write to this state and persist, because a
|
|
101
|
-
// dropdown change doesn't feed back into the props (so these effects don't re-run).
|
|
102
|
-
$effect(() => { if (type !== undefined) ctype = type })
|
|
103
|
-
$effect(() => { if (dimension !== undefined) dim = dimension })
|
|
104
|
-
$effect(() => { if (measure !== undefined) msr = measure })
|
|
105
|
-
$effect(() => { if (reduce !== undefined) red = reduce })
|
|
106
|
-
// Keep the dimension / measure valid for the schema.
|
|
107
|
-
$effect(() => {
|
|
108
|
-
if (!dim || !fields.dimensions.includes(dim)) dim = fields.defaultDimension ?? fields.dimensions[0] ?? ''
|
|
109
|
-
if ((!msr || !fields.measures.includes(msr)) && fields.measures.length) msr = fields.defaultMeasure ?? fields.measures[0] ?? ''
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
const labelOf = (field: string) => schema.fields.find((f) => f.field === field)?.label ?? titleCase(field)
|
|
113
|
-
const measureLabel = $derived(
|
|
114
|
-
red === 'count' ? 'Count' : `${red[0]!.toUpperCase() + red.slice(1)} ${labelOf(msr)}`,
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
let serverBuckets = $state<AggregateBucket[]>([])
|
|
118
|
-
let fetchToken = 0
|
|
119
|
-
$effect(() => {
|
|
120
|
-
if (!getAggregate || !dim) return
|
|
121
|
-
void refreshKey // re-run after a mutation
|
|
122
|
-
const req: AggregateRequest = { dimension: dim, measure: msr || undefined, reduce: red, filterModel }
|
|
123
|
-
const token = ++fetchToken
|
|
124
|
-
getAggregate(req).then((bs) => {
|
|
125
|
-
if (token === fetchToken) serverBuckets = bs
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
const buckets = $derived(
|
|
130
|
-
getAggregate
|
|
131
|
-
? serverBuckets
|
|
132
|
-
: dim
|
|
133
|
-
? aggregateRows((rows ?? []) as ReadonlyArray<ChartRow>, { dimension: dim, measure: msr || undefined, reduce: red })
|
|
134
|
-
: [],
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
// Options for the custom dropdown controls.
|
|
138
|
-
const dimOptions = $derived(fields.dimensions.map((d) => ({ value: d, label: labelOf(d) })))
|
|
139
|
-
const measureOptions = $derived(fields.measures.map((m) => ({ value: m, label: labelOf(m) })))
|
|
140
|
-
const reduceOptions = [
|
|
141
|
-
{ value: 'count', label: 'Count' }, { value: 'sum', label: 'Sum' }, { value: 'avg', label: 'Average' },
|
|
142
|
-
{ value: 'min', label: 'Min' }, { value: 'max', label: 'Max' },
|
|
143
|
-
]
|
|
144
|
-
const typeOptions = [
|
|
145
|
-
{ value: 'bar', label: 'Bar' }, { value: 'pie', label: 'Pie' }, { value: 'line', label: 'Line' }, { value: 'area', label: 'Area' },
|
|
146
|
-
{ value: 'radar', label: 'Radar' }, { value: 'funnel', label: 'Funnel' }, { value: 'waterfall', label: 'Waterfall' }, { value: 'treemap', label: 'Treemap' },
|
|
147
|
-
]
|
|
148
|
-
// Cartesian types get x/y axis titles; radial / area-partition types don't.
|
|
149
|
-
const CARTESIAN_TYPES = new Set<ChartType>(['bar', 'line', 'area', 'waterfall'])
|
|
150
|
-
|
|
151
|
-
// Per-category colors from the dimension's enum options (status/stage/etc), in
|
|
152
|
-
// bucket order, so a pie's slices match the grid's badge colors.
|
|
153
|
-
const categoryColors = $derived.by<string[] | undefined>(() => {
|
|
154
|
-
const opts = schema.fields.find((f) => f.field === dim)?.options
|
|
155
|
-
if (!opts?.length) return undefined
|
|
156
|
-
return buckets.map((b) => {
|
|
157
|
-
const opt = opts.find((o) => String(o.value) === String(b.category))
|
|
158
|
-
return opt?.color || accent || '#6366f1'
|
|
159
|
-
})
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
const spec = $derived.by<ChartSpec>(() => {
|
|
163
|
-
const s: ChartSpec = {
|
|
164
|
-
type: ctype,
|
|
165
|
-
categories: buckets.map((b) => b.category),
|
|
166
|
-
// Bars / lines / areas take the brand accent; pie ignores series color and
|
|
167
|
-
// uses the palette (semantic enum colors when available).
|
|
168
|
-
series: [{ label: measureLabel, values: buckets.map((b) => b.value), ...(accent ? { color: accent } : {}) }],
|
|
169
|
-
width: measuredW || width,
|
|
170
|
-
height,
|
|
171
|
-
...(categoryColors ? { palette: categoryColors } : {}),
|
|
172
|
-
}
|
|
173
|
-
if (ctype === 'pie') s.innerRadius = 0.6
|
|
174
|
-
// Treemap reads a tree, not a category/series pair - build a flat one from
|
|
175
|
-
// the same buckets (each dimension value is a leaf sized by the measure).
|
|
176
|
-
if (ctype === 'treemap') s.treemap = { name: measureLabel, children: buckets.map((b) => ({ name: b.category, value: b.value })) }
|
|
177
|
-
if (CARTESIAN_TYPES.has(ctype)) {
|
|
178
|
-
s.xAxisTitle = labelOf(dim)
|
|
179
|
-
s.yAxisTitle = measureLabel
|
|
180
|
-
}
|
|
181
|
-
return s
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
function onSelect(sel: ChartSelection) {
|
|
185
|
-
onDrill?.(String(sel.category), dim)
|
|
186
|
-
}
|
|
187
|
-
</script>
|
|
188
|
-
|
|
189
|
-
<div class="sv-chart">
|
|
190
|
-
{#if controls}
|
|
191
|
-
<div class="sv-chart__controls">
|
|
192
|
-
<div class="sv-chart__ctrl">
|
|
193
|
-
<span>Group by</span>
|
|
194
|
-
<div class="sv-chart__dd"><SvGridDropdown options={dimOptions} value={dim} autoOpen={false} onChange={(v) => (dim = String(v))} /></div>
|
|
195
|
-
</div>
|
|
196
|
-
<div class="sv-chart__ctrl">
|
|
197
|
-
<span>Measure</span>
|
|
198
|
-
<div class="sv-chart__dd"><SvGridDropdown options={reduceOptions} value={red} autoOpen={false} onChange={(v) => (red = v as AggregateReduce)} /></div>
|
|
199
|
-
</div>
|
|
200
|
-
{#if red !== 'count' && fields.measures.length}
|
|
201
|
-
<div class="sv-chart__ctrl">
|
|
202
|
-
<span>of</span>
|
|
203
|
-
<div class="sv-chart__dd"><SvGridDropdown options={measureOptions} value={msr} autoOpen={false} onChange={(v) => (msr = String(v))} /></div>
|
|
204
|
-
</div>
|
|
205
|
-
{/if}
|
|
206
|
-
<div class="sv-chart__ctrl">
|
|
207
|
-
<span>Type</span>
|
|
208
|
-
<div class="sv-chart__dd"><SvGridDropdown options={typeOptions} value={ctype} autoOpen={false} onChange={(v) => (ctype = v as ChartType)} /></div>
|
|
209
|
-
</div>
|
|
210
|
-
</div>
|
|
211
|
-
{/if}
|
|
212
|
-
|
|
213
|
-
{#if dim}
|
|
214
|
-
<div class="sv-chart__plot" bind:this={plotEl}>
|
|
215
|
-
<SvGridChart {spec} {dataLabels} {formatValue} {onSelect} />
|
|
216
|
-
</div>
|
|
217
|
-
{:else}
|
|
218
|
-
<p class="sv-chart__empty">No chart-able fields in this schema.</p>
|
|
219
|
-
{/if}
|
|
220
|
-
</div>
|
|
221
|
-
|
|
222
|
-
<style>
|
|
223
|
-
.sv-chart { display: flex; flex-direction: column; gap: 10px; }
|
|
224
|
-
.sv-chart__plot { width: 100%; min-width: 0; }
|
|
225
|
-
.sv-chart__controls { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
226
|
-
.sv-chart__ctrl { display: flex; flex-direction: column; gap: 3px; font-size: 11px; font-weight: 600; color: var(--sg-muted, #666); }
|
|
227
|
-
.sv-chart__dd {
|
|
228
|
-
position: relative; height: 32px; min-width: 130px;
|
|
229
|
-
border: 1px solid var(--sg-border, #ccc); border-radius: 8px; background: var(--sg-input-bg, #fff);
|
|
230
|
-
}
|
|
231
|
-
.sv-chart__dd :global(.sv-grid-dropdown),
|
|
232
|
-
.sv-chart__dd :global(.sv-grid-dropdown-trigger) { background: transparent; border-radius: 8px; }
|
|
233
|
-
.sv-chart__empty { color: var(--sg-muted, #888); font-size: 13px; padding: 24px; text-align: center; }
|
|
234
|
-
</style>
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
type ChartRow = Record<string, unknown>
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts" generics="TData extends ChartRow">
|
|
6
|
+
/**
|
|
7
|
+
* SvSchemaChart - a reusable chart bound to an `EntitySchema`. It picks
|
|
8
|
+
* chart-able fields from the schema (dimensions to group by, measures to
|
|
9
|
+
* reduce), renders built-in controls, and draws with `SvGridChart`.
|
|
10
|
+
*
|
|
11
|
+
* Aggregation is either client-side (pass `rows`) or server-side (pass
|
|
12
|
+
* `getAggregate`, e.g. a SQL / Supabase source's `GROUP BY`), so it scales
|
|
13
|
+
* past what a page holds. Click a category to drill (`onDrill`).
|
|
14
|
+
*/
|
|
15
|
+
import { SvGridChart, SvGridDropdown, type ChartSpec, type ChartType, type ChartSelection } from '@svgrid/grid'
|
|
16
|
+
import { titleCase, type EntitySchema } from './schema'
|
|
17
|
+
import {
|
|
18
|
+
aggregateRows,
|
|
19
|
+
chartFieldsFromSchema,
|
|
20
|
+
type AggregateBucket,
|
|
21
|
+
type AggregateReduce,
|
|
22
|
+
type AggregateRequest,
|
|
23
|
+
} from './sources/aggregate'
|
|
24
|
+
|
|
25
|
+
type FilterModel = AggregateRequest['filterModel']
|
|
26
|
+
|
|
27
|
+
type Props = {
|
|
28
|
+
schema: EntitySchema<TData>
|
|
29
|
+
/** Client-side data to aggregate. Ignored when `getAggregate` is set. */
|
|
30
|
+
rows?: ReadonlyArray<TData>
|
|
31
|
+
/** Server-side aggregation (a data source's `getAggregate`). Takes precedence. */
|
|
32
|
+
getAggregate?: (request: AggregateRequest) => Promise<AggregateBucket[]>
|
|
33
|
+
/** Filter passed to `getAggregate`. */
|
|
34
|
+
filterModel?: FilterModel
|
|
35
|
+
dimension?: string
|
|
36
|
+
measure?: string
|
|
37
|
+
reduce?: AggregateReduce
|
|
38
|
+
type?: ChartType
|
|
39
|
+
/** Show the built-in dimension / measure / type controls. Default true. */
|
|
40
|
+
controls?: boolean
|
|
41
|
+
height?: number
|
|
42
|
+
width?: number
|
|
43
|
+
dataLabels?: boolean
|
|
44
|
+
formatValue?: (v: number) => string
|
|
45
|
+
onDrill?: (category: string, dimension: string) => void
|
|
46
|
+
/** Bump to re-fetch server aggregates (e.g. after a mutation). */
|
|
47
|
+
refreshKey?: unknown
|
|
48
|
+
/** Brand color. Bars / lines use it; pie slices grouped by an enum use the
|
|
49
|
+
* enum's own option colors (matching status badges). */
|
|
50
|
+
accent?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let {
|
|
54
|
+
schema,
|
|
55
|
+
rows,
|
|
56
|
+
getAggregate,
|
|
57
|
+
filterModel,
|
|
58
|
+
dimension,
|
|
59
|
+
measure,
|
|
60
|
+
reduce,
|
|
61
|
+
type,
|
|
62
|
+
controls = true,
|
|
63
|
+
height = 300,
|
|
64
|
+
width = 460,
|
|
65
|
+
dataLabels = true,
|
|
66
|
+
formatValue,
|
|
67
|
+
onDrill,
|
|
68
|
+
refreshKey,
|
|
69
|
+
accent,
|
|
70
|
+
}: Props = $props()
|
|
71
|
+
|
|
72
|
+
const fields = $derived(chartFieldsFromSchema(schema as EntitySchema))
|
|
73
|
+
|
|
74
|
+
// Render the chart at its ACTUAL pixel width so the SVG viewBox maps 1:1 to
|
|
75
|
+
// screen pixels (scale factor = 1). Otherwise the SVG stretches to fill a wide
|
|
76
|
+
// block and its text (font-size in user units) scales up with it - "fonts that
|
|
77
|
+
// grow with the chart". Measured width keeps labels at a fixed px size, like
|
|
78
|
+
// the grid. Falls back to the `width` prop until the first measurement.
|
|
79
|
+
let measuredW = $state(0)
|
|
80
|
+
let plotEl: HTMLDivElement | undefined = $state()
|
|
81
|
+
$effect(() => {
|
|
82
|
+
const el = plotEl
|
|
83
|
+
if (!el || typeof ResizeObserver === 'undefined') return
|
|
84
|
+
const ro = new ResizeObserver((entries) => {
|
|
85
|
+
const w = Math.round(entries[0]?.contentRect.width ?? 0)
|
|
86
|
+
if (w > 0 && w !== measuredW) measuredW = w
|
|
87
|
+
})
|
|
88
|
+
ro.observe(el)
|
|
89
|
+
return () => ro.disconnect()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
let dim = $state('')
|
|
93
|
+
let msr = $state('')
|
|
94
|
+
let red = $state<AggregateReduce>('sum')
|
|
95
|
+
let ctype = $state<ChartType>('bar')
|
|
96
|
+
|
|
97
|
+
// Follow the props reactively, so a controlled parent (e.g. the Studio preview,
|
|
98
|
+
// which hides the dropdowns) updates the chart whenever its dimension / measure /
|
|
99
|
+
// reduce / type change. One effect per prop so a change to one doesn't reset the
|
|
100
|
+
// others; the built-in dropdowns still write to this state and persist, because a
|
|
101
|
+
// dropdown change doesn't feed back into the props (so these effects don't re-run).
|
|
102
|
+
$effect(() => { if (type !== undefined) ctype = type })
|
|
103
|
+
$effect(() => { if (dimension !== undefined) dim = dimension })
|
|
104
|
+
$effect(() => { if (measure !== undefined) msr = measure })
|
|
105
|
+
$effect(() => { if (reduce !== undefined) red = reduce })
|
|
106
|
+
// Keep the dimension / measure valid for the schema.
|
|
107
|
+
$effect(() => {
|
|
108
|
+
if (!dim || !fields.dimensions.includes(dim)) dim = fields.defaultDimension ?? fields.dimensions[0] ?? ''
|
|
109
|
+
if ((!msr || !fields.measures.includes(msr)) && fields.measures.length) msr = fields.defaultMeasure ?? fields.measures[0] ?? ''
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
const labelOf = (field: string) => schema.fields.find((f) => f.field === field)?.label ?? titleCase(field)
|
|
113
|
+
const measureLabel = $derived(
|
|
114
|
+
red === 'count' ? 'Count' : `${red[0]!.toUpperCase() + red.slice(1)} ${labelOf(msr)}`,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
let serverBuckets = $state<AggregateBucket[]>([])
|
|
118
|
+
let fetchToken = 0
|
|
119
|
+
$effect(() => {
|
|
120
|
+
if (!getAggregate || !dim) return
|
|
121
|
+
void refreshKey // re-run after a mutation
|
|
122
|
+
const req: AggregateRequest = { dimension: dim, measure: msr || undefined, reduce: red, filterModel }
|
|
123
|
+
const token = ++fetchToken
|
|
124
|
+
getAggregate(req).then((bs) => {
|
|
125
|
+
if (token === fetchToken) serverBuckets = bs
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const buckets = $derived(
|
|
130
|
+
getAggregate
|
|
131
|
+
? serverBuckets
|
|
132
|
+
: dim
|
|
133
|
+
? aggregateRows((rows ?? []) as ReadonlyArray<ChartRow>, { dimension: dim, measure: msr || undefined, reduce: red })
|
|
134
|
+
: [],
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
// Options for the custom dropdown controls.
|
|
138
|
+
const dimOptions = $derived(fields.dimensions.map((d) => ({ value: d, label: labelOf(d) })))
|
|
139
|
+
const measureOptions = $derived(fields.measures.map((m) => ({ value: m, label: labelOf(m) })))
|
|
140
|
+
const reduceOptions = [
|
|
141
|
+
{ value: 'count', label: 'Count' }, { value: 'sum', label: 'Sum' }, { value: 'avg', label: 'Average' },
|
|
142
|
+
{ value: 'min', label: 'Min' }, { value: 'max', label: 'Max' },
|
|
143
|
+
]
|
|
144
|
+
const typeOptions = [
|
|
145
|
+
{ value: 'bar', label: 'Bar' }, { value: 'pie', label: 'Pie' }, { value: 'line', label: 'Line' }, { value: 'area', label: 'Area' },
|
|
146
|
+
{ value: 'radar', label: 'Radar' }, { value: 'funnel', label: 'Funnel' }, { value: 'waterfall', label: 'Waterfall' }, { value: 'treemap', label: 'Treemap' },
|
|
147
|
+
]
|
|
148
|
+
// Cartesian types get x/y axis titles; radial / area-partition types don't.
|
|
149
|
+
const CARTESIAN_TYPES = new Set<ChartType>(['bar', 'line', 'area', 'waterfall'])
|
|
150
|
+
|
|
151
|
+
// Per-category colors from the dimension's enum options (status/stage/etc), in
|
|
152
|
+
// bucket order, so a pie's slices match the grid's badge colors.
|
|
153
|
+
const categoryColors = $derived.by<string[] | undefined>(() => {
|
|
154
|
+
const opts = schema.fields.find((f) => f.field === dim)?.options
|
|
155
|
+
if (!opts?.length) return undefined
|
|
156
|
+
return buckets.map((b) => {
|
|
157
|
+
const opt = opts.find((o) => String(o.value) === String(b.category))
|
|
158
|
+
return opt?.color || accent || '#6366f1'
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const spec = $derived.by<ChartSpec>(() => {
|
|
163
|
+
const s: ChartSpec = {
|
|
164
|
+
type: ctype,
|
|
165
|
+
categories: buckets.map((b) => b.category),
|
|
166
|
+
// Bars / lines / areas take the brand accent; pie ignores series color and
|
|
167
|
+
// uses the palette (semantic enum colors when available).
|
|
168
|
+
series: [{ label: measureLabel, values: buckets.map((b) => b.value), ...(accent ? { color: accent } : {}) }],
|
|
169
|
+
width: measuredW || width,
|
|
170
|
+
height,
|
|
171
|
+
...(categoryColors ? { palette: categoryColors } : {}),
|
|
172
|
+
}
|
|
173
|
+
if (ctype === 'pie') s.innerRadius = 0.6
|
|
174
|
+
// Treemap reads a tree, not a category/series pair - build a flat one from
|
|
175
|
+
// the same buckets (each dimension value is a leaf sized by the measure).
|
|
176
|
+
if (ctype === 'treemap') s.treemap = { name: measureLabel, children: buckets.map((b) => ({ name: b.category, value: b.value })) }
|
|
177
|
+
if (CARTESIAN_TYPES.has(ctype)) {
|
|
178
|
+
s.xAxisTitle = labelOf(dim)
|
|
179
|
+
s.yAxisTitle = measureLabel
|
|
180
|
+
}
|
|
181
|
+
return s
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
function onSelect(sel: ChartSelection) {
|
|
185
|
+
onDrill?.(String(sel.category), dim)
|
|
186
|
+
}
|
|
187
|
+
</script>
|
|
188
|
+
|
|
189
|
+
<div class="sv-chart">
|
|
190
|
+
{#if controls}
|
|
191
|
+
<div class="sv-chart__controls">
|
|
192
|
+
<div class="sv-chart__ctrl">
|
|
193
|
+
<span>Group by</span>
|
|
194
|
+
<div class="sv-chart__dd"><SvGridDropdown options={dimOptions} value={dim} autoOpen={false} onChange={(v) => (dim = String(v))} /></div>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="sv-chart__ctrl">
|
|
197
|
+
<span>Measure</span>
|
|
198
|
+
<div class="sv-chart__dd"><SvGridDropdown options={reduceOptions} value={red} autoOpen={false} onChange={(v) => (red = v as AggregateReduce)} /></div>
|
|
199
|
+
</div>
|
|
200
|
+
{#if red !== 'count' && fields.measures.length}
|
|
201
|
+
<div class="sv-chart__ctrl">
|
|
202
|
+
<span>of</span>
|
|
203
|
+
<div class="sv-chart__dd"><SvGridDropdown options={measureOptions} value={msr} autoOpen={false} onChange={(v) => (msr = String(v))} /></div>
|
|
204
|
+
</div>
|
|
205
|
+
{/if}
|
|
206
|
+
<div class="sv-chart__ctrl">
|
|
207
|
+
<span>Type</span>
|
|
208
|
+
<div class="sv-chart__dd"><SvGridDropdown options={typeOptions} value={ctype} autoOpen={false} onChange={(v) => (ctype = v as ChartType)} /></div>
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
{/if}
|
|
212
|
+
|
|
213
|
+
{#if dim}
|
|
214
|
+
<div class="sv-chart__plot" bind:this={plotEl}>
|
|
215
|
+
<SvGridChart {spec} {dataLabels} {formatValue} {onSelect} />
|
|
216
|
+
</div>
|
|
217
|
+
{:else}
|
|
218
|
+
<p class="sv-chart__empty">No chart-able fields in this schema.</p>
|
|
219
|
+
{/if}
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<style>
|
|
223
|
+
.sv-chart { display: flex; flex-direction: column; gap: 10px; }
|
|
224
|
+
.sv-chart__plot { width: 100%; min-width: 0; }
|
|
225
|
+
.sv-chart__controls { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
226
|
+
.sv-chart__ctrl { display: flex; flex-direction: column; gap: 3px; font-size: 11px; font-weight: 600; color: var(--sg-muted, #666); }
|
|
227
|
+
.sv-chart__dd {
|
|
228
|
+
position: relative; height: 32px; min-width: 130px;
|
|
229
|
+
border: 1px solid var(--sg-border, #ccc); border-radius: 8px; background: var(--sg-input-bg, #fff);
|
|
230
|
+
}
|
|
231
|
+
.sv-chart__dd :global(.sv-grid-dropdown),
|
|
232
|
+
.sv-chart__dd :global(.sv-grid-dropdown-trigger) { background: transparent; border-radius: 8px; }
|
|
233
|
+
.sv-chart__empty { color: var(--sg-muted, #888); font-size: 13px; padding: 24px; text-align: center; }
|
|
234
|
+
</style>
|
|
@@ -9,8 +9,8 @@ type ChartRow = Record<string, unknown>;
|
|
|
9
9
|
* past what a page holds. Click a category to drill (`onDrill`).
|
|
10
10
|
*/
|
|
11
11
|
import { type ChartType } from '@svgrid/grid';
|
|
12
|
-
import { type EntitySchema } from './schema';
|
|
13
|
-
import { type AggregateBucket, type AggregateReduce, type AggregateRequest } from './sources/aggregate';
|
|
12
|
+
import { type EntitySchema } from './schema.js';
|
|
13
|
+
import { type AggregateBucket, type AggregateReduce, type AggregateRequest } from './sources/aggregate.js';
|
|
14
14
|
declare function $$render<TData extends ChartRow>(): {
|
|
15
15
|
props: {
|
|
16
16
|
schema: EntitySchema<TData>;
|