@svgrid/enterprise 3.0.2 → 3.1.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/LICENSE +94 -24
- package/README.md +36 -4
- package/dist/SvAdvancedFilter.svelte +1 -1
- package/dist/SvAlertsManager.svelte +2 -1
- package/dist/SvBoard.svelte +2 -1
- package/dist/SvExportMenu.svelte +4 -1
- package/dist/SvExportMenu.svelte.d.ts +0 -13
- package/dist/SvExpressionEditor.svelte +3 -3
- package/dist/SvFormulaBar.svelte +94 -20
- package/dist/SvFormulaBar.svelte.d.ts +7 -0
- package/dist/SvGridAlerts.svelte +49 -9
- package/dist/SvGridBoard.svelte +13 -8
- package/dist/SvGridBulkEditDrawer.svelte +26 -2
- package/dist/SvGridEditPanel.svelte +3 -0
- package/dist/SvGridScheduler.svelte +69 -40
- package/dist/SvGridSelectionBar.svelte +10 -3
- package/dist/SvPivotDesigner.svelte +198 -40
- package/dist/SvPivotDesigner.svelte.d.ts +27 -20
- package/dist/SvSheet.svelte +4043 -243
- package/dist/SvSheet.svelte.d.ts +76 -3
- package/dist/SvSheetChartSetup.svelte +151 -0
- package/dist/SvSheetChartSetup.svelte.d.ts +18 -0
- package/dist/SvSheetComment.svelte +216 -31
- package/dist/SvSheetComment.svelte.d.ts +11 -11
- package/dist/SvSheetConditionalFormat.svelte +28 -31
- package/dist/SvSheetDataValidation.svelte +66 -42
- package/dist/SvSheetEditRanges.svelte +217 -0
- package/dist/SvSheetEditRanges.svelte.d.ts +15 -0
- package/dist/SvSheetErrors.svelte +125 -0
- package/dist/SvSheetErrors.svelte.d.ts +20 -0
- package/dist/SvSheetEvaluate.svelte +133 -0
- package/dist/SvSheetEvaluate.svelte.d.ts +15 -0
- package/dist/SvSheetFilterMenu.svelte +137 -34
- package/dist/SvSheetFilterMenu.svelte.d.ts +5 -0
- package/dist/SvSheetFindReplace.svelte +19 -19
- package/dist/SvSheetFormatCells.svelte +125 -81
- package/dist/SvSheetGoalSeek.svelte +17 -16
- package/dist/SvSheetInsertFunction.svelte +15 -13
- package/dist/SvSheetIteration.svelte +81 -0
- package/dist/SvSheetIteration.svelte.d.ts +10 -0
- package/dist/SvSheetLink.svelte +99 -0
- package/dist/SvSheetLink.svelte.d.ts +17 -0
- package/dist/SvSheetListPicker.svelte +4 -2
- package/dist/SvSheetListPicker.svelte.d.ts +0 -6
- package/dist/SvSheetManageRules.svelte +20 -18
- package/dist/SvSheetNameManager.svelte +17 -15
- package/dist/SvSheetPageSetup.svelte +159 -0
- package/dist/SvSheetPageSetup.svelte.d.ts +13 -0
- package/dist/SvSheetPasteSpecial.svelte +22 -20
- package/dist/SvSheetPivot.svelte +220 -0
- package/dist/SvSheetPivot.svelte.d.ts +21 -0
- package/dist/SvSheetProtectSheet.svelte +81 -0
- package/dist/SvSheetProtectSheet.svelte.d.ts +11 -0
- package/dist/SvSheetRemoveDuplicates.svelte +13 -10
- package/dist/SvSheetRibbon.svelte +104 -51
- package/dist/SvSheetSizeDialog.svelte +4 -2
- package/dist/SvSheetSort.svelte +218 -0
- package/dist/SvSheetSort.svelte.d.ts +28 -0
- package/dist/SvSheetSparklines.svelte +135 -0
- package/dist/SvSheetSparklines.svelte.d.ts +14 -0
- package/dist/SvSheetTable.svelte +160 -0
- package/dist/SvSheetTable.svelte.d.ts +24 -0
- package/dist/SvSheetTabs.svelte +161 -31
- package/dist/SvSheetTabs.svelte.d.ts +22 -8
- package/dist/SvSheetTextToColumns.svelte +20 -20
- package/dist/SvSheetValidationAlert.svelte +8 -6
- package/dist/advanced-filter-enable.js +1 -1
- package/dist/alerts/alert-engine.d.ts +6 -0
- package/dist/alerts/alert-engine.js +21 -4
- package/dist/alerts/alert-formats.js +2 -0
- package/dist/alerts/alert-types.d.ts +1 -1
- package/dist/alerts.d.ts +1 -1
- package/dist/alerts.js +2 -2
- package/dist/board.js +2 -2
- package/dist/bulk-edit.d.ts +3 -1
- package/dist/bulk-edit.js +1 -1
- package/dist/cdn/svgrid-enterprise.svelte-external.js +39827 -16429
- package/dist/designer/assets/GridMenus-8gTNssgV.js +8 -0
- package/dist/designer/assets/{SvChartMenu-D9hEeHar.js → SvChartMenu-BDoeqGz6.js} +1 -1
- package/dist/designer/assets/{SvDateTimePicker-DARvKUL2.js → SvDateTimePicker-DCPg7WGN.js} +1 -1
- package/dist/designer/assets/SvGridCellEditor-DMoWJjEI.js +4 -0
- package/dist/designer/assets/{SvGridChart-B8j1SSy3.js → SvGridChart-Cy1ATrWh.js} +2 -2
- package/dist/designer/assets/SvGridChartBuilder-eQ3mn0L9.js +1 -0
- package/dist/designer/assets/{SvGridChartPanel-pT6ITc2P.js → SvGridChartPanel-CATdVc9X.js} +3 -3
- package/dist/designer/assets/{SvGridChartView-bg281UA1.js → SvGridChartView-CmsZtCED.js} +1 -1
- package/dist/designer/assets/SvGridDropdown-Cjz0H-fI.js +1 -0
- package/dist/designer/assets/{SvTabs-UFCMTHXS.js → SvTabs-2mHC6oaz.js} +1 -1
- package/dist/designer/assets/cell-formatting-BbubJTVz.js +1 -0
- package/dist/designer/assets/{chart-cIPUsbM5.js → chart-Cn7MnNqA.js} +1 -1
- package/dist/designer/assets/{chart-panel-messages-DtTxpBUU.js → chart-panel-messages-Dz7PTIqT.js} +1 -1
- package/dist/designer/assets/{chart-summary-Czu_5epb.js → chart-summary-BD1qYB2R.js} +1 -1
- package/dist/designer/assets/{chart-trend-D5I5VEe8.js → chart-trend-B8xr5LuD.js} +1 -1
- package/dist/designer/assets/{dismissable-BO547MH9.js → dismissable-ChWRPkoa.js} +1 -1
- package/dist/designer/assets/{export-format-D-eP4b2l.js → export-format-DGckmgKr.js} +1 -1
- package/dist/designer/assets/{focus-trap-CAreAhlU.js → focus-trap-V2-6Fy9I.js} +1 -1
- package/dist/designer/assets/{index-DwU7CAMm.js → index-BSjv6Qec.js} +317 -315
- package/dist/designer/assets/index-_JVFxt02.css +1 -0
- package/dist/designer/assets/row-resize-BInFWCpr.js +1 -0
- package/dist/designer/assets/server-block-cache-CXJKkJpy.js +1 -0
- package/dist/designer/assets/src-DUQ4xE3x.js +58 -0
- package/dist/designer/assets/validate-C3B8QKsM.js +1 -0
- package/dist/designer/index.html +17 -16
- package/dist/export-pdf.d.ts +75 -0
- package/dist/export-pdf.js +216 -15
- package/dist/export.js +10 -2
- package/dist/gantt/SvGridGantt.svelte +2890 -0
- package/dist/gantt/SvGridGantt.svelte.d.ts +30 -0
- package/dist/gantt/gantt-config.d.ts +73 -0
- package/dist/gantt/gantt-config.js +1 -0
- package/dist/gantt/gantt-critical-path.d.ts +67 -0
- package/dist/gantt/gantt-critical-path.js +207 -0
- package/dist/gantt/gantt-model.d.ts +257 -0
- package/dist/gantt/gantt-model.js +651 -0
- package/dist/gantt/gantt-resources.d.ts +59 -0
- package/dist/gantt/gantt-resources.js +71 -0
- package/dist/gantt/gantt.d.ts +7 -0
- package/dist/gantt/gantt.js +36 -0
- package/dist/gantt/index.d.ts +15 -0
- package/dist/gantt/index.js +22 -0
- package/dist/gantt/timeline-arrows.d.ts +102 -0
- package/dist/gantt/timeline-arrows.js +181 -0
- package/dist/import.d.ts +2 -2
- package/dist/import.js +1 -1
- package/dist/index.d.ts +34 -7
- package/dist/index.js +44 -7
- package/dist/install.js +8 -1
- package/dist/license-core.d.ts +17 -1
- package/dist/license-core.js +16 -5
- package/dist/license.d.ts +18 -2
- package/dist/license.js +71 -6
- package/dist/node/studio.js +140 -8
- package/dist/pivot-enable.js +2 -2
- package/dist/scheduler-dependencies.d.ts +35 -0
- package/dist/scheduler-dependencies.js +36 -1
- package/dist/scheduler.js +2 -2
- package/dist/selection-bar.js +2 -2
- package/dist/server/SvGroupCell.svelte +213 -0
- package/dist/server/SvGroupCell.svelte.d.ts +62 -0
- package/dist/server/SvRowGroupPanel.svelte +322 -0
- package/dist/server/SvRowGroupPanel.svelte.d.ts +37 -0
- package/dist/server/enable.d.ts +17 -0
- package/dist/server/enable.js +49 -0
- package/dist/server/index.d.ts +22 -0
- package/dist/server/index.js +24 -0
- package/dist/server/messages.d.ts +45 -0
- package/dist/server/messages.js +35 -0
- package/dist/server/server-group-model.d.ts +104 -0
- package/dist/server/server-group-model.js +272 -0
- package/dist/server/server-pivot.d.ts +46 -0
- package/dist/server/server-pivot.js +57 -0
- package/dist/server/server-row-model.d.ts +547 -0
- package/dist/server/server-row-model.js +1551 -0
- package/dist/server/server-selection.d.ts +99 -0
- package/dist/server/server-selection.js +215 -0
- package/dist/server/svgrid-adapter.d.ts +155 -0
- package/dist/server/svgrid-adapter.js +202 -0
- package/dist/sheet/ast.d.ts +45 -1
- package/dist/sheet/ast.js +14 -0
- package/dist/sheet/auto-filter.d.ts +58 -4
- package/dist/sheet/auto-filter.js +159 -11
- package/dist/sheet/autocomplete.d.ts +9 -0
- package/dist/sheet/autocomplete.js +90 -4
- package/dist/sheet/cell-images.d.ts +39 -0
- package/dist/sheet/cell-images.js +90 -0
- package/dist/sheet/cell-types.d.ts +58 -0
- package/dist/sheet/cell-types.js +96 -0
- package/dist/sheet/coerce.d.ts +40 -0
- package/dist/sheet/coerce.js +170 -3
- package/dist/sheet/comments.d.ts +43 -9
- package/dist/sheet/comments.js +85 -27
- package/dist/sheet/conditional-formats.d.ts +29 -6
- package/dist/sheet/conditional-formats.js +37 -12
- package/dist/sheet/csv.d.ts +32 -0
- package/dist/sheet/csv.js +158 -0
- package/dist/sheet/culture.d.ts +87 -0
- package/dist/sheet/culture.js +247 -0
- package/dist/sheet/delta.d.ts +102 -0
- package/dist/sheet/delta.js +203 -0
- package/dist/sheet/deps.d.ts +45 -1
- package/dist/sheet/deps.js +77 -2
- package/dist/sheet/document.d.ts +106 -4
- package/dist/sheet/document.js +204 -3
- package/dist/sheet/engine.d.ts +67 -0
- package/dist/sheet/engine.js +19 -0
- package/dist/sheet/entry.d.ts +8 -4
- package/dist/sheet/entry.js +171 -21
- package/dist/sheet/error-check.d.ts +49 -0
- package/dist/sheet/error-check.js +83 -0
- package/dist/sheet/evaluate-steps.d.ts +48 -0
- package/dist/sheet/evaluate-steps.js +196 -0
- package/dist/sheet/evaluate.d.ts +67 -0
- package/dist/sheet/evaluate.js +725 -13
- package/dist/sheet/filter-files.d.ts +37 -0
- package/dist/sheet/filter-files.js +465 -0
- package/dist/sheet/format-store.d.ts +15 -0
- package/dist/sheet/format-store.js +11 -1
- package/dist/sheet/from-rows.d.ts +14 -0
- package/dist/sheet/from-rows.js +50 -0
- package/dist/sheet/function-catalog.d.ts +1 -1
- package/dist/sheet/function-catalog.js +296 -5
- package/dist/sheet/functions.js +363 -91
- package/dist/sheet/index.d.ts +35 -8
- package/dist/sheet/index.js +35 -8
- package/dist/sheet/links.d.ts +57 -0
- package/dist/sheet/links.js +174 -0
- package/dist/sheet/messages.d.ts +778 -0
- package/dist/sheet/messages.js +806 -0
- package/dist/sheet/number-format.d.ts +52 -5
- package/dist/sheet/number-format.js +465 -68
- package/dist/sheet/objects.d.ts +106 -0
- package/dist/sheet/objects.js +149 -0
- package/dist/sheet/ods-document.d.ts +44 -0
- package/dist/sheet/ods-document.js +1275 -0
- package/dist/sheet/open-file.d.ts +26 -0
- package/dist/sheet/open-file.js +56 -0
- package/dist/sheet/outline.d.ts +82 -0
- package/dist/sheet/outline.js +228 -0
- package/dist/sheet/packs/array.d.ts +16 -0
- package/dist/sheet/packs/array.js +172 -0
- package/dist/sheet/packs/database.d.ts +42 -0
- package/dist/sheet/packs/database.js +142 -0
- package/dist/sheet/packs/engineering.d.ts +2 -0
- package/dist/sheet/packs/engineering.js +296 -0
- package/dist/sheet/packs/financial.d.ts +5 -0
- package/dist/sheet/packs/financial.js +220 -0
- package/dist/sheet/packs/math-stats.d.ts +2 -0
- package/dist/sheet/packs/math-stats.js +372 -0
- package/dist/sheet/packs/special.d.ts +44 -0
- package/dist/sheet/packs/special.js +187 -0
- package/dist/sheet/packs/stats-dist.d.ts +2 -0
- package/dist/sheet/packs/stats-dist.js +785 -0
- package/dist/sheet/packs/text-date.d.ts +2 -0
- package/dist/sheet/packs/text-date.js +332 -0
- package/dist/sheet/packs/trig.d.ts +2 -0
- package/dist/sheet/packs/trig.js +73 -0
- package/dist/sheet/page-setup.d.ts +73 -0
- package/dist/sheet/page-setup.js +73 -0
- package/dist/sheet/parse.js +42 -10
- package/dist/sheet/paste-special.d.ts +22 -0
- package/dist/sheet/paste-special.js +14 -5
- package/dist/sheet/pivot-range.d.ts +160 -0
- package/dist/sheet/pivot-range.js +333 -0
- package/dist/sheet/presence.d.ts +73 -0
- package/dist/sheet/presence.js +79 -0
- package/dist/sheet/print.d.ts +104 -0
- package/dist/sheet/print.js +169 -0
- package/dist/sheet/protection.d.ts +55 -1
- package/dist/sheet/protection.js +55 -2
- package/dist/sheet/rects.d.ts +1 -1
- package/dist/sheet/refs.d.ts +48 -0
- package/dist/sheet/refs.js +155 -8
- package/dist/sheet/ribbon-icons.d.ts +1 -1
- package/dist/sheet/ribbon-icons.js +186 -0
- package/dist/sheet/ribbon.d.ts +1 -1
- package/dist/sheet/ribbon.js +324 -23
- package/dist/sheet/sheet-kinds.d.ts +74 -0
- package/dist/sheet/sheet-kinds.js +91 -0
- package/dist/sheet/shortcuts.d.ts +13 -3
- package/dist/sheet/shortcuts.js +63 -12
- package/dist/sheet/sort.d.ts +51 -0
- package/dist/sheet/sort.js +107 -0
- package/dist/sheet/sparklines.d.ts +111 -0
- package/dist/sheet/sparklines.js +204 -0
- package/dist/sheet/table-styles.d.ts +74 -0
- package/dist/sheet/table-styles.js +152 -0
- package/dist/sheet/tables.d.ts +19 -0
- package/dist/sheet/tables.js +31 -0
- package/dist/sheet/tokenize.d.ts +18 -1
- package/dist/sheet/tokenize.js +60 -9
- package/dist/sheet/validation.d.ts +16 -0
- package/dist/sheet/validation.js +37 -0
- package/dist/sheet/workbook.d.ts +148 -3
- package/dist/sheet/workbook.js +742 -31
- package/dist/sheet/xls-cfb.d.ts +32 -0
- package/dist/sheet/xls-cfb.js +290 -0
- package/dist/sheet/xls-document.d.ts +10 -0
- package/dist/sheet/xls-document.js +1243 -0
- package/dist/sheet/xls-formula.d.ts +40 -0
- package/dist/sheet/xls-formula.js +569 -0
- package/dist/sheet/xlsx-document.d.ts +108 -0
- package/dist/sheet/xlsx-document.js +1849 -0
- package/dist/sheet/xlsx-drawing.d.ts +99 -0
- package/dist/sheet/xlsx-drawing.js +420 -0
- package/dist/sheet-enable.js +6 -7
- package/dist/sheet-text.d.ts +7 -0
- package/dist/sheet-text.js +29 -0
- package/dist/sources/auth-supabase.js +2 -2
- package/dist/sources/realtime-supabase.js +2 -2
- package/dist/sources/rest.d.ts +6 -2
- package/dist/sources/rest.js +42 -7
- package/dist/sources/supabase.d.ts +10 -1
- package/dist/sources/supabase.js +73 -40
- package/dist/studio/emit-project.js +14 -1
- package/dist/studio/index.js +1 -1
- package/dist/studio/project.d.ts +11 -2
- package/dist/studio/project.js +5 -2
- package/dist/studio/ui-components.generated.js +107 -2
- package/dist/sveltekit/in-memory.d.ts +2 -2
- package/dist/sveltekit/in-memory.js +125 -4
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/index.js +1 -1
- package/dist/sveltekit/query-plan.d.ts +50 -4
- package/dist/sveltekit/query-plan.js +25 -3
- package/dist/sveltekit/sql-source.d.ts +6 -0
- package/dist/sveltekit/sql-source.js +34 -6
- package/dist/sveltekit/sql.d.ts +31 -0
- package/dist/sveltekit/sql.js +135 -25
- package/dist/sveltekit/transport.d.ts +10 -2
- package/dist/sveltekit/transport.js +30 -6
- package/dist/upgrade-prompt.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/watermark.js +35 -2
- package/dist/wc/GridMenus-By9vPuq0.js +1088 -0
- package/dist/wc/SvChartMenu-CQNrQGT2.js +308 -0
- package/dist/wc/SvDateRangeInput-DR6m7bSM.js +199 -0
- package/dist/wc/SvDateTimePicker-D-vRnaMo.js +771 -0
- package/dist/wc/SvField-B_kurx_P.js +149 -0
- package/dist/wc/SvGrid.helpers-BdVDtjUm.js +135 -0
- package/dist/wc/SvGridCellEditor-Bnf1Kr5C.js +560 -0
- package/dist/wc/SvGridChart-BqUJh2pG.js +3572 -0
- package/dist/wc/SvGridChartBuilder-CGycC5cn.js +988 -0
- package/dist/wc/SvGridChartPanel-CRnizsHD.js +1746 -0
- package/dist/wc/SvGridChartView-D1pa_xMy.js +47 -0
- package/dist/wc/SvGridDropdown-45WsObLL.js +259 -0
- package/dist/wc/SvModal-DrfOYYyX.js +189 -0
- package/dist/wc/cell-formatting-CBfgahv1.js +86 -0
- package/dist/wc/chart-6OXlybVT.js +4341 -0
- package/dist/wc/chart-export-CeSADGfi.js +135 -0
- package/dist/wc/chart-export-pdf-BGko7MER.js +104 -0
- package/dist/wc/chart-summary-2m3ia1-n.js +137 -0
- package/dist/wc/chart-trend-CQOQtTMl.js +50 -0
- package/dist/wc/chart-validate-CD6ptiwg.js +180 -0
- package/dist/wc/client-DQIzjDBG.js +3064 -0
- package/dist/wc/column-resize-DUPgynvc.js +101 -0
- package/dist/wc/date-format-DS-z_h9v.js +1130 -0
- package/dist/wc/dismissable-CQ6XFXsk.js +45 -0
- package/dist/wc/editor-contract-peBDxir_.js +22 -0
- package/dist/wc/editor-registry-DxGKqcvx.js +24 -0
- package/dist/wc/focus-trap-Dbb2tJxb.js +65 -0
- package/dist/wc/list-option-DrRyivWp.js +82 -0
- package/dist/wc/popover-WsGlvtQv.js +69 -0
- package/dist/wc/positioning-BiKLfu1E.js +112 -0
- package/dist/wc/react.d.ts +41 -0
- package/dist/wc/react.js +90 -0
- package/dist/wc/rolldown-runtime-Dy4uBu1J.js +11 -0
- package/dist/wc/row-drag-touch-CycRR65n.js +93 -0
- package/dist/wc/row-resize-limG_N1z.js +111 -0
- package/dist/wc/sv-sheet-element.d.ts +97 -0
- package/dist/wc/sv-sheet-element.js +44213 -0
- package/dist/wc/svelte-virtualizer.svelte-CXtQy2TQ.js +530 -0
- package/dist/wc/validate-BqDthkNr.js +88 -0
- package/dist/wc/vue.d.ts +31 -0
- package/dist/wc/vue.js +75 -0
- package/package.json +30 -5
- package/dist/designer/assets/GridMenus-BlsvL_aU.js +0 -8
- package/dist/designer/assets/SvGridCellEditor-C7uQXYds.js +0 -4
- package/dist/designer/assets/SvGridChartBuilder-kvyIU_Rl.js +0 -1
- package/dist/designer/assets/SvGridDropdown-Bb3HH2Aj.js +0 -1
- package/dist/designer/assets/cell-formatting-wr5wm7US.js +0 -1
- package/dist/designer/assets/index-CMFgAoVV.css +0 -1
- package/dist/designer/assets/row-resize-DLktcqUK.js +0 -1
- package/dist/designer/assets/src-EdI3Xqds.js +0 -58
- package/dist/designer/assets/validate-h-aslRV9.js +0 -1
package/dist/sources/rest.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveIdField } from '../schema.js';
|
|
2
|
-
import {
|
|
2
|
+
import { nudgeEnterpriseFor } from '../license.js';
|
|
3
3
|
import { normalizeFilters } from './filters.js';
|
|
4
4
|
const encodePredicate = (p) => {
|
|
5
5
|
if (p.op === 'in')
|
|
@@ -48,22 +48,57 @@ function defaultBuildQuery(request) {
|
|
|
48
48
|
if (aggs.length)
|
|
49
49
|
params.aggregate = aggs.map((a) => `${a.fn}:${a.col}`).join(',');
|
|
50
50
|
}
|
|
51
|
+
// The grid wants a grand-total row it does not have yet. An endpoint that
|
|
52
|
+
// supports it answers with `grandTotal` in the body (the aggregates over
|
|
53
|
+
// the filtered set, path excluded); one that does not simply ignores the
|
|
54
|
+
// flag and the grid shows no total. The aggregate list is sent either
|
|
55
|
+
// way, so a leaf-level request still says what to total.
|
|
56
|
+
if (request.needsGrandTotal) {
|
|
57
|
+
params.grandTotal = '1';
|
|
58
|
+
const aggs = request.aggregations ?? [];
|
|
59
|
+
if (aggs.length && !params.aggregate)
|
|
60
|
+
params.aggregate = aggs.map((a) => `${a.fn}:${a.col}`).join(',');
|
|
61
|
+
}
|
|
62
|
+
// Pivot: the columns whose values become fields, on a grouped request.
|
|
63
|
+
// The endpoint answers with the pivoted fields on each group row and
|
|
64
|
+
// lists them in `pivotResultFields`; see the SQL source for the shape.
|
|
65
|
+
//
|
|
66
|
+
// ?groupBy=region&aggregate=sum:amount&pivot=year,quarter
|
|
67
|
+
if (request.pivotMode && request.pivotBy?.length && params.groupBy) {
|
|
68
|
+
params.pivot = request.pivotBy.join(',');
|
|
69
|
+
}
|
|
51
70
|
return params;
|
|
52
71
|
}
|
|
53
|
-
|
|
72
|
+
/**
|
|
73
|
+
* The count when the API sent none: a short block is the last one, so the
|
|
74
|
+
* end is known; a full block says nothing, so the count is unknown (`-1`)
|
|
75
|
+
* and an infinite-scrolling grid discovers the end from a later short
|
|
76
|
+
* block. Claiming `rows.length` would tell a pager there is one page.
|
|
77
|
+
*/
|
|
78
|
+
function countWithoutTotal(rows, request) {
|
|
79
|
+
const asked = Math.max(1, request.endRow - request.startRow);
|
|
80
|
+
return rows.length < asked ? request.startRow + rows.length : -1;
|
|
81
|
+
}
|
|
82
|
+
function defaultParse(body, response, request) {
|
|
54
83
|
if (Array.isArray(body)) {
|
|
55
84
|
// Total from a Content-Range header (`items 0-9/240`) when present.
|
|
56
85
|
const range = response.headers.get('content-range');
|
|
57
86
|
const total = range && /\/(\d+)\s*$/.exec(range);
|
|
58
|
-
return { rows: body, rowCount: total ? Number(total[1]) : body
|
|
87
|
+
return { rows: body, rowCount: total ? Number(total[1]) : countWithoutTotal(body, request) };
|
|
59
88
|
}
|
|
60
89
|
const b = (body ?? {});
|
|
90
|
+
// `grandTotal` rides along in an envelope body when the endpoint answered
|
|
91
|
+
// the request for one. Absent means "keep what you have", `null` means
|
|
92
|
+
// "there is none" - the same three-way contract as ServerResult.
|
|
93
|
+
const grandTotal = 'grandTotal' in b ? { grandTotal: (b.grandTotal ?? null) } : {};
|
|
61
94
|
const rows = (b.rows ?? b.data ?? b.items ?? []);
|
|
62
|
-
const
|
|
63
|
-
|
|
95
|
+
const declared = b.rowCount ?? b.total ?? b.count;
|
|
96
|
+
const rowCount = declared == null ? countWithoutTotal(rows, request) : Number(declared);
|
|
97
|
+
const pivot = Array.isArray(b.pivotResultFields) ? { pivotResultFields: b.pivotResultFields } : {};
|
|
98
|
+
return { rows, rowCount, ...grandTotal, ...pivot };
|
|
64
99
|
}
|
|
65
100
|
export function createRestDataSource(config) {
|
|
66
|
-
|
|
101
|
+
nudgeEnterpriseFor('studio', 'Studio'); // evaluation nudge; never blocks
|
|
67
102
|
const doFetch = config.fetch ?? globalThis.fetch;
|
|
68
103
|
const idField = config.idField ?? (config.schema ? resolveIdField(config.schema) : 'id');
|
|
69
104
|
const base = config.url.replace(/\/+$/, '');
|
|
@@ -86,7 +121,7 @@ export function createRestDataSource(config) {
|
|
|
86
121
|
const qs = new URLSearchParams({ ...config.query, ...buildQuery(request) }).toString();
|
|
87
122
|
const url = qs ? `${base}?${qs}` : base;
|
|
88
123
|
const { body, res } = (await send(url, { headers: headers() }));
|
|
89
|
-
return parse(body, res);
|
|
124
|
+
return parse(body, res, request);
|
|
90
125
|
},
|
|
91
126
|
async createRow(input) {
|
|
92
127
|
const { body } = (await send(base, {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* with `searchColumns`. The primary key comes from the schema (`idField`) and is
|
|
13
13
|
* stripped from inserts (it's database-generated).
|
|
14
14
|
*/
|
|
15
|
-
import type { RowData } from '@svgrid/grid';
|
|
15
|
+
import type { RowData, ServerRequest, ServerResult } from '@svgrid/grid';
|
|
16
16
|
import { type EntitySchema } from '../schema.js';
|
|
17
17
|
import type { WritableDataSource } from '../sveltekit/types.js';
|
|
18
18
|
/** The slice of a `supabase-js` client this adapter needs. */
|
|
@@ -29,5 +29,14 @@ export type SupabaseDataSourceConfig<TData extends RowData> = {
|
|
|
29
29
|
* schema's text + enum fields.
|
|
30
30
|
*/
|
|
31
31
|
searchColumns?: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Server-side pivot. PostgREST has no way to turn values into columns, so
|
|
34
|
+
* a pivoted request (`pivotMode` with `pivotBy`) goes to this function
|
|
35
|
+
* instead - typically an RPC (`client.rpc(...)`) that returns the group
|
|
36
|
+
* rows with one field per (pivot key x aggregation) and lists them in
|
|
37
|
+
* `pivotResultFields`; the SQL source documents the shape. Without it a
|
|
38
|
+
* pivoted request is answered as plain grouping, with a warning once.
|
|
39
|
+
*/
|
|
40
|
+
pivot?: (request: ServerRequest) => Promise<ServerResult<TData>>;
|
|
32
41
|
};
|
|
33
42
|
export declare function createSupabaseDataSource<TData extends RowData>(config: SupabaseDataSourceConfig<TData>): WritableDataSource<TData>;
|
package/dist/sources/supabase.js
CHANGED
|
@@ -1,15 +1,27 @@
|
|
|
1
1
|
import { resolveIdField } from '../schema.js';
|
|
2
|
-
import {
|
|
2
|
+
import { nudgeEnterpriseFor } from '../license.js';
|
|
3
3
|
import { normalizeFilters } from './filters.js';
|
|
4
4
|
const sanitize = (term) => term.replace(/[%,()]/g, '');
|
|
5
5
|
export function createSupabaseDataSource(config) {
|
|
6
|
-
|
|
6
|
+
nudgeEnterpriseFor('studio', 'Studio'); // evaluation nudge; never blocks
|
|
7
7
|
const { client, table, schema } = config;
|
|
8
8
|
const idField = resolveIdField(schema);
|
|
9
9
|
const searchColumns = config.searchColumns ??
|
|
10
10
|
schema.fields.filter((f) => f.type === 'text' || f.type === 'enum').map((f) => String(f.field));
|
|
11
|
+
let warnedPivot = false;
|
|
11
12
|
return {
|
|
12
13
|
async getRows(request) {
|
|
14
|
+
// ---- Server-side pivot ----------------------------------------------
|
|
15
|
+
// Only a grouped level pivots; the leaves under a path do not.
|
|
16
|
+
const grouped = (request.groupKeys?.length ?? 0) < (request.groupBy?.length ?? 0);
|
|
17
|
+
if (grouped && request.pivotMode && request.pivotBy?.length) {
|
|
18
|
+
if (config.pivot)
|
|
19
|
+
return config.pivot(request);
|
|
20
|
+
if (!warnedPivot) {
|
|
21
|
+
warnedPivot = true;
|
|
22
|
+
console.warn('[svgrid] createSupabaseDataSource: a pivoted request needs the `pivot` option (an RPC that pivots); answering it as plain grouping.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
13
25
|
// ---- Server-side grouping -------------------------------------------
|
|
14
26
|
// The grid asks one level at a time. The already-chosen path becomes
|
|
15
27
|
// ordinary `.eq()` filters below; at this level we ask PostgREST for the
|
|
@@ -26,50 +38,61 @@ export function createSupabaseDataSource(config) {
|
|
|
26
38
|
const groupCols = request.groupBy ?? [];
|
|
27
39
|
const groupKeys = request.groupKeys ?? [];
|
|
28
40
|
const groupField = groupKeys.length < groupCols.length ? groupCols[groupKeys.length] : undefined;
|
|
41
|
+
const aggregateSelects = (request.aggregations ?? []).map((a) => a.fn === 'count' ? `${a.col}:count()` : `${a.col}:${a.col}.${a.fn}()`);
|
|
42
|
+
// The innermost group level can say how many rows each group holds
|
|
43
|
+
// (a plain count). Higher levels would need COUNT(DISTINCT next),
|
|
44
|
+
// which PostgREST does not expose, so those group rows carry no
|
|
45
|
+
// childCount and the grid shows no badge for them.
|
|
46
|
+
const innermost = groupField != null && groupKeys.length === groupCols.length - 1;
|
|
29
47
|
const selectExpr = groupField
|
|
30
|
-
? [
|
|
31
|
-
groupField,
|
|
32
|
-
...(request.aggregations ?? []).map((a) => a.fn === 'count' ? `${a.col}:count()` : `${a.col}:${a.col}.${a.fn}()`),
|
|
33
|
-
].join(',')
|
|
48
|
+
? [groupField, ...aggregateSelects, ...(innermost ? ['childCount:count()'] : [])].join(',')
|
|
34
49
|
: '*';
|
|
35
50
|
let q = client.from(table).select(selectExpr, { count: 'exact' });
|
|
51
|
+
// The filters are applied to two queries when a grand total was asked
|
|
52
|
+
// for: the level itself (below, with the group path) and the total
|
|
53
|
+
// (the filters alone). Build them once.
|
|
54
|
+
const applyFilters = (query) => {
|
|
55
|
+
let out = query;
|
|
56
|
+
const { predicates, search } = normalizeFilters(request.filterModel);
|
|
57
|
+
for (const p of predicates) {
|
|
58
|
+
switch (p.op) {
|
|
59
|
+
case 'in':
|
|
60
|
+
out = out.in(p.column, p.values);
|
|
61
|
+
break;
|
|
62
|
+
case 'contains':
|
|
63
|
+
out = out.ilike(p.column, `%${p.value}%`);
|
|
64
|
+
break;
|
|
65
|
+
case 'startsWith':
|
|
66
|
+
out = out.ilike(p.column, `${p.value}%`);
|
|
67
|
+
break;
|
|
68
|
+
case 'eq':
|
|
69
|
+
out = out.eq(p.column, p.value);
|
|
70
|
+
break;
|
|
71
|
+
case 'gt':
|
|
72
|
+
out = out.gt(p.column, p.value);
|
|
73
|
+
break;
|
|
74
|
+
case 'lt':
|
|
75
|
+
out = out.lt(p.column, p.value);
|
|
76
|
+
break;
|
|
77
|
+
case 'between':
|
|
78
|
+
out = out.gte(p.column, p.value).lte(p.column, p.valueTo);
|
|
79
|
+
break;
|
|
80
|
+
case 'isNull':
|
|
81
|
+
out = out.is(p.column, null);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (search && searchColumns.length) {
|
|
86
|
+
const term = sanitize(search);
|
|
87
|
+
out = out.or(searchColumns.map((c) => `${c}.ilike.%${term}%`).join(','));
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
};
|
|
36
91
|
// The path constrains the rows before grouping.
|
|
37
92
|
for (let i = 0; i < groupKeys.length && i < groupCols.length; i += 1) {
|
|
38
93
|
q = q.eq(groupCols[i], groupKeys[i]);
|
|
39
94
|
}
|
|
40
|
-
|
|
41
|
-
for (const p of predicates) {
|
|
42
|
-
switch (p.op) {
|
|
43
|
-
case 'in':
|
|
44
|
-
q = q.in(p.column, p.values);
|
|
45
|
-
break;
|
|
46
|
-
case 'contains':
|
|
47
|
-
q = q.ilike(p.column, `%${p.value}%`);
|
|
48
|
-
break;
|
|
49
|
-
case 'startsWith':
|
|
50
|
-
q = q.ilike(p.column, `${p.value}%`);
|
|
51
|
-
break;
|
|
52
|
-
case 'eq':
|
|
53
|
-
q = q.eq(p.column, p.value);
|
|
54
|
-
break;
|
|
55
|
-
case 'gt':
|
|
56
|
-
q = q.gt(p.column, p.value);
|
|
57
|
-
break;
|
|
58
|
-
case 'lt':
|
|
59
|
-
q = q.lt(p.column, p.value);
|
|
60
|
-
break;
|
|
61
|
-
case 'between':
|
|
62
|
-
q = q.gte(p.column, p.value).lte(p.column, p.valueTo);
|
|
63
|
-
break;
|
|
64
|
-
case 'isNull':
|
|
65
|
-
q = q.is(p.column, null);
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (search && searchColumns.length) {
|
|
70
|
-
const term = sanitize(search);
|
|
71
|
-
q = q.or(searchColumns.map((c) => `${c}.ilike.%${term}%`).join(','));
|
|
72
|
-
}
|
|
95
|
+
q = applyFilters(q);
|
|
73
96
|
// When grouping, only columns the grouped select actually produces can be
|
|
74
97
|
// ordered on - PostgREST rejects an ORDER BY over a column that is
|
|
75
98
|
// neither grouped nor aggregated. Fall back to the group key so the
|
|
@@ -95,7 +118,17 @@ export function createSupabaseDataSource(config) {
|
|
|
95
118
|
const { data, count, error } = await q.range(request.startRow, request.endRow - 1);
|
|
96
119
|
if (error)
|
|
97
120
|
throw new Error(error.message);
|
|
98
|
-
|
|
121
|
+
const result = { rows: (data ?? []), rowCount: count ?? 0 };
|
|
122
|
+
// The grand total: one aggregate-only select over the filtered set,
|
|
123
|
+
// with no group column (so PostgREST returns a single row) and no path.
|
|
124
|
+
if (request.needsGrandTotal && aggregateSelects.length) {
|
|
125
|
+
const total = applyFilters(client.from(table).select(aggregateSelects.join(',')));
|
|
126
|
+
const { data: totalRows, error: totalError } = await total;
|
|
127
|
+
if (totalError)
|
|
128
|
+
throw new Error(totalError.message);
|
|
129
|
+
result.grandTotal = (totalRows?.[0] ?? null);
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
99
132
|
},
|
|
100
133
|
async createRow(input) {
|
|
101
134
|
const rest = { ...input };
|
|
@@ -478,6 +478,17 @@ ${panels}
|
|
|
478
478
|
// Dragging a card updates its groupBy value in the local row state (optimistic).
|
|
479
479
|
return ` <div ${wrapperStyle(block)}${cls}>
|
|
480
480
|
<SvBoard schema={${schemaVar}} rows={allRows} loading={!allRowsReady} groupBy=${JSON.stringify(cfg.groupBy)} titleField=${JSON.stringify(asText(cfg.titleField))}${badge}${sub}${onOpen} height={${h}} onMove={(id, value) => { allRows = allRows.map((r) => String((r as Record<string, unknown>)[idField]) === String(id) ? ({ ...r, ['${cfg.groupBy}']: value }) : r) }} />
|
|
481
|
+
</div>`;
|
|
482
|
+
}
|
|
483
|
+
case 'sheet': {
|
|
484
|
+
const h = block.height ?? 480;
|
|
485
|
+
// The shell reads `data` once at mount, so it mounts once the rows are in.
|
|
486
|
+
const chosen = cfg.fields?.length ? cfg.fields : entity.fields.map((f) => f.field);
|
|
487
|
+
const specs = chosen.map((field) => ({ field, label: entity.fields.find((f) => f.field === field)?.label ?? field }));
|
|
488
|
+
// Excel's sheet-name rules: none of []:*?/\ and at most 31 characters.
|
|
489
|
+
const sheetName = (entity.label ?? entity.name).replace(/[[\]:*?/\\]/g, ' ').trim().slice(0, 31) || 'Sheet1';
|
|
490
|
+
return ` <div ${wrapperStyle(block, `min-height: ${h}px`)}${cls}>
|
|
491
|
+
{#if allRowsReady}<SvSheet data={[{ name: ${JSON.stringify(sheetName)}, cells: sheetCellsFromRows(${rowsExpr}, ${JSON.stringify(specs)}, { totals: ${cfg.totals ? 'true' : 'false'} }) }]} height={${h}} />{/if}
|
|
481
492
|
</div>`;
|
|
482
493
|
}
|
|
483
494
|
case 'calendar': {
|
|
@@ -1981,7 +1992,7 @@ function screenPage(schema, rawSchema, screen, resolve, rawResolve, accessEnable
|
|
|
1981
1992
|
const exportApiVars = blocks.filter((b) => b.config.kind === 'grid' && gridHasExport(b.config) && !(codeGrid && b.id === codeGridBlockId)).map((b) => `gridApi_${b.id.replace(/-/g, '_')}`);
|
|
1982
1993
|
const needsGridApiType = codeGrid || exportApiVars.length > 0;
|
|
1983
1994
|
// The pivot reads the whole table (like charts / dashboards).
|
|
1984
|
-
const needsAllRows = hasAgg || hasMD || hasPivot || hasBoundComponent || hasGroupedGrid || hasTreeGrid || hasSchedulerGrid || has(allBlocks, 'board') || has(allBlocks, 'calendar') || has(allBlocks, 'detail');
|
|
1995
|
+
const needsAllRows = hasAgg || hasMD || hasPivot || hasBoundComponent || hasGroupedGrid || hasTreeGrid || hasSchedulerGrid || has(allBlocks, 'board') || has(allBlocks, 'calendar') || has(allBlocks, 'detail') || has(allBlocks, 'sheet');
|
|
1985
1996
|
// --- imports ---
|
|
1986
1997
|
const gridSpecs = [];
|
|
1987
1998
|
if (needsController)
|
|
@@ -2030,6 +2041,8 @@ function screenPage(schema, rawSchema, screen, resolve, rawResolve, accessEnable
|
|
|
2030
2041
|
entImports.push('schemaToColumns');
|
|
2031
2042
|
if (has(allBlocks, 'detail'))
|
|
2032
2043
|
entImports.push('SvRecordDetail');
|
|
2044
|
+
if (has(allBlocks, 'sheet'))
|
|
2045
|
+
entImports.push('SvSheet', 'sheetCellsFromRows');
|
|
2033
2046
|
if (has(allBlocks, 'kpi') || has(allBlocks, 'gauge') || hasAggregateBinding)
|
|
2034
2047
|
entImports.push('reduceValue');
|
|
2035
2048
|
const kpiCfgs = allBlocks.filter((b) => b.config.kind === 'kpi').map((b) => b.config);
|
package/dist/studio/index.js
CHANGED
|
@@ -36,6 +36,6 @@ export { resolveDeployTarget, deployCommands, missingEnvKeys, } from './deploy-c
|
|
|
36
36
|
export { runStudioAdd, runStudioAddApp, resolveSchema, resolveSchemas, } from './cli.js';
|
|
37
37
|
// Re-export the schema surface so consumers get everything from one entry.
|
|
38
38
|
export { resolveIdField, schemaToColumns, schemaToFormFields, isFieldHidden, linkRelationLabels, pickLabelField, } from '../schema.js';
|
|
39
|
-
// Pure license classification, so the Node/MCP generator can
|
|
39
|
+
// Pure license classification, so the Node/MCP generator can nudge with the
|
|
40
40
|
// same rules as the browser (see license-core.ts). No DOM, safe in Node.
|
|
41
41
|
export { checkLicenseKey } from '../license-core.js';
|
package/dist/studio/project.d.ts
CHANGED
|
@@ -95,7 +95,7 @@ export type EntityDataSource = MemorySource | RestSource | SqlSource | SupabaseS
|
|
|
95
95
|
/** The kinds of block a screen can hold: data-bound (entity-derived) or the
|
|
96
96
|
* entity-agnostic `'component'` (a UI-kit component from `UI_COMPONENT_REGISTRY`,
|
|
97
97
|
* usable on any screen, including a freestanding one with no entity). */
|
|
98
|
-
export type BlockKind = 'grid' | 'form' | 'chart' | 'dashboard' | 'kpi' | 'gauge' | 'tree' | 'tabs' | 'accordion' | 'master-detail' | 'lookup' | 'pivot' | 'filter' | 'record' | 'board' | 'calendar' | 'detail' | 'component';
|
|
98
|
+
export type BlockKind = 'grid' | 'form' | 'chart' | 'dashboard' | 'kpi' | 'gauge' | 'tree' | 'tabs' | 'accordion' | 'master-detail' | 'lookup' | 'pivot' | 'filter' | 'record' | 'board' | 'calendar' | 'detail' | 'sheet' | 'component';
|
|
99
99
|
export type GridAlign = 'left' | 'center' | 'right';
|
|
100
100
|
/** No-code per-column value formatting. Compiles to the grid's `format` (CellFormatConfig):
|
|
101
101
|
* a raw `1234.5` becomes `$1,234.50` / `42%` / `Jun 27, 2026` without a code renderer. */
|
|
@@ -516,6 +516,15 @@ export type DetailConfig = {
|
|
|
516
516
|
}[];
|
|
517
517
|
related?: DetailRelated[];
|
|
518
518
|
};
|
|
519
|
+
/** The entity's rows on the spreadsheet shell (SvSheet): a header row of field
|
|
520
|
+
* labels, a row per record, and, with `totals`, a SUM row under every numeric
|
|
521
|
+
* column. `fields` narrows and orders the columns (empty = every field). The
|
|
522
|
+
* sheet is Excel over the dataset: formulas, formats, filters and an xlsx out. */
|
|
523
|
+
export type SheetConfig = {
|
|
524
|
+
kind: 'sheet';
|
|
525
|
+
fields?: string[];
|
|
526
|
+
totals?: boolean;
|
|
527
|
+
};
|
|
519
528
|
/** A UI-kit component (from `UI_COMPONENT_REGISTRY`, keyed by `component`) dropped
|
|
520
529
|
* onto a screen. Entity-agnostic - works on a freestanding page or mixed onto an
|
|
521
530
|
* entity-bound screen alike. `props` holds its configured "chrome" values, keyed
|
|
@@ -543,7 +552,7 @@ export type ComponentConfig = {
|
|
|
543
552
|
name?: string;
|
|
544
553
|
bindings?: Record<string, ComponentBinding>;
|
|
545
554
|
};
|
|
546
|
-
export type BlockConfig = GridConfig | FormConfig | ChartConfig | KpiConfig | GaugeConfig | TreeConfig | TabsConfig | AccordionConfig | DashboardConfig | MasterDetailConfig | LookupConfig | PivotConfig | FilterPanelConfig | RecordConfig | BoardConfig | CalendarConfig | DetailConfig | ComponentConfig;
|
|
555
|
+
export type BlockConfig = GridConfig | FormConfig | ChartConfig | KpiConfig | GaugeConfig | TreeConfig | TabsConfig | AccordionConfig | DashboardConfig | MasterDetailConfig | LookupConfig | PivotConfig | FilterPanelConfig | RecordConfig | BoardConfig | CalendarConfig | DetailConfig | SheetConfig | ComponentConfig;
|
|
547
556
|
/** Block kinds allowed inside a Tabs / Accordion container: the controller-free,
|
|
548
557
|
* `allRows`-driven display blocks (no grid / form / master-detail, which need the
|
|
549
558
|
* screen controller). `component` covers any UI-kit component added by key. */
|
package/dist/studio/project.js
CHANGED
|
@@ -432,6 +432,7 @@ export const blockPalette = [
|
|
|
432
432
|
{ kind: 'board', label: 'Board' },
|
|
433
433
|
{ kind: 'calendar', label: 'Calendar' },
|
|
434
434
|
{ kind: 'detail', label: 'Detail page' },
|
|
435
|
+
{ kind: 'sheet', label: 'Spreadsheet' },
|
|
435
436
|
{ kind: 'filter', label: 'Filter panel' },
|
|
436
437
|
{ kind: 'record', label: 'Record panel' },
|
|
437
438
|
{ kind: 'lookup', label: 'Lookup' },
|
|
@@ -538,6 +539,8 @@ export function defaultBlockConfig(kind, entity) {
|
|
|
538
539
|
const metrics = nonKey.filter((f) => f.type === 'number').slice(0, 3).map((f) => f.field);
|
|
539
540
|
return { kind, titleField: titleF, ...(subF ? { subtitleField: subF } : {}), ...(statusF ? { statusField: statusF } : {}), ...(metrics.length ? { metricFields: metrics } : {}) };
|
|
540
541
|
}
|
|
542
|
+
case 'sheet':
|
|
543
|
+
return { kind, totals: entity.fields.some((f) => f.type === 'number' && !f.primaryKey) };
|
|
541
544
|
case 'component':
|
|
542
545
|
// Entity-agnostic - built directly by `addComponentBlock`, never through here.
|
|
543
546
|
return { kind, component: '', props: {} };
|
|
@@ -554,7 +557,7 @@ export function pickFacetFields(entity) {
|
|
|
554
557
|
}
|
|
555
558
|
const facetRank = (t) => (t === 'enum' ? 0 : t === 'boolean' ? 1 : 2);
|
|
556
559
|
const DEFAULT_SPAN = {
|
|
557
|
-
grid: 3, form: 1, chart: 2, dashboard: 3, kpi: 1, gauge: 1, tree: 2, tabs: 3, accordion: 3, 'master-detail': 3, lookup: 1, pivot: 3, filter: 1, record: 1, board: 3, calendar: 3, detail: 3, component: 1,
|
|
560
|
+
grid: 3, form: 1, chart: 2, dashboard: 3, kpi: 1, gauge: 1, tree: 2, tabs: 3, accordion: 3, 'master-detail': 3, lookup: 1, pivot: 3, filter: 1, record: 1, board: 3, calendar: 3, detail: 3, sheet: 3, component: 1,
|
|
558
561
|
};
|
|
559
562
|
function makeBlock(kind, entity, taken) {
|
|
560
563
|
return { id: uid(`${kind}-`, taken), span: DEFAULT_SPAN[kind], config: defaultBlockConfig(kind, entity) };
|
|
@@ -1249,7 +1252,7 @@ function dockPaneTitle(block) {
|
|
|
1249
1252
|
grid: 'Grid', form: 'Form', chart: 'Chart', dashboard: 'Dashboard', tree: 'Tree', pivot: 'Pivot',
|
|
1250
1253
|
filter: 'Filters', record: 'Record', detail: 'Detail', board: 'Board', calendar: 'Calendar',
|
|
1251
1254
|
'master-detail': 'Master / detail', lookup: 'Lookup', tabs: 'Tabs', accordion: 'Accordion',
|
|
1252
|
-
kpi: 'KPI', gauge: 'Gauge', component: 'Component',
|
|
1255
|
+
kpi: 'KPI', gauge: 'Gauge', sheet: 'Spreadsheet', component: 'Component',
|
|
1253
1256
|
};
|
|
1254
1257
|
return c.label || c.title || c.name || c.component || byKind[c.kind] || 'Panel';
|
|
1255
1258
|
}
|
|
@@ -6583,6 +6583,7 @@ export const GENERATED_UI_SURFACE = {
|
|
|
6583
6583
|
"key": "data",
|
|
6584
6584
|
"label": "Data",
|
|
6585
6585
|
"type": "json",
|
|
6586
|
+
"description": "The rows to render. Optional only because `rowModel` can supply them instead; a grid with neither renders empty. When both are present `data` wins.",
|
|
6586
6587
|
"group": "common"
|
|
6587
6588
|
},
|
|
6588
6589
|
{
|
|
@@ -6605,6 +6606,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
6605
6606
|
"description": "Scheduler / calendar mode. When set, the grid renders its rows as events on a Month / Week / Day / Agenda calendar instead of a table. See .",
|
|
6606
6607
|
"group": "common"
|
|
6607
6608
|
},
|
|
6609
|
+
{
|
|
6610
|
+
"key": "gantt",
|
|
6611
|
+
"label": "Gantt",
|
|
6612
|
+
"type": "json",
|
|
6613
|
+
"description": "Gantt mode. When set, the grid renders its rows as a task table beside a time chart: one bar per row by start / end, a work-breakdown tree from `parentField`, and dependency arrows. See . A view of the grid like the board and scheduler; the renderer ships in `@svgrid/enterprise` (call `enableGanttView()`).",
|
|
6614
|
+
"group": "common"
|
|
6615
|
+
},
|
|
6608
6616
|
{
|
|
6609
6617
|
"key": "chart",
|
|
6610
6618
|
"label": "Chart",
|
|
@@ -7036,6 +7044,14 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7036
7044
|
"code": true,
|
|
7037
7045
|
"group": "advanced"
|
|
7038
7046
|
},
|
|
7047
|
+
{
|
|
7048
|
+
"key": "includeCollapsedRows",
|
|
7049
|
+
"label": "Include Collapsed Rows",
|
|
7050
|
+
"type": "json",
|
|
7051
|
+
"description": "Whether a collapsed row still takes part in a copy or a fill that spans it. Left out, both skip every collapsed row, which is what \"filter, copy, paste\" needs and what a fill dragged over a filtered list expects: the series runs over the rows that show. A spreadsheet answers per row: a row hidden by hand is copied and filled, as Excel does, while a row a filter folded away is not.",
|
|
7052
|
+
"code": true,
|
|
7053
|
+
"group": "advanced"
|
|
7054
|
+
},
|
|
7039
7055
|
{
|
|
7040
7056
|
"key": "clipboardHtml",
|
|
7041
7057
|
"label": "Clipboard Html",
|
|
@@ -7244,6 +7260,34 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7244
7260
|
"description": "Which rows feed the min/max range that `colorScale` / `dataBar` formats scale against. - `filtered` (default): every row that survives the filters, ignoring the page slice. The scale follows what you filtered to but does not shift when you page, so the same value keeps the same colour on page 1 and page 2. - `visible`: only the rows currently on screen. Rescales per page - use it when you want each page's heat map normalized to that page. - `all`: the full unfiltered dataset, for a scale that stays put as you filter.",
|
|
7245
7261
|
"group": "common"
|
|
7246
7262
|
},
|
|
7263
|
+
{
|
|
7264
|
+
"key": "rowPlaceholder",
|
|
7265
|
+
"label": "Row Placeholder",
|
|
7266
|
+
"type": "json",
|
|
7267
|
+
"description": "Marks a row as one whose data has not arrived: `\"loading\"` draws a shimmer in every cell, `\"failed\"` draws a full-width \"could not load\" row with a Retry button, and `null` (the default for every row) renders normally. Placeholder rows are inert - not selectable, not editable, and skipped by cell navigation - because there is nothing there to act on yet. `createServerDataSource` in `infinite` mode fills the gaps with rows this recognises, so the usual wiring is `rowPlaceholder={rowPlaceholderState}` (or nothing at all, via `rowModel`).",
|
|
7268
|
+
"code": true,
|
|
7269
|
+
"group": "advanced"
|
|
7270
|
+
},
|
|
7271
|
+
{
|
|
7272
|
+
"key": "rowModel",
|
|
7273
|
+
"label": "Row Model",
|
|
7274
|
+
"type": "json",
|
|
7275
|
+
"description": "Drive the grid from a row model instead of wiring a dozen props. `createServerDataSource` returns one, and so does the Enterprise server-side row model, so server-backed grids become: ```svelte <SvGrid rowModel={ctl} {columns} /> ``` The model supplies `data`, `loading`, `getRowId`, the external sort and filter wiring, the visible range, placeholder rows, group accessors, selection and paging - each one only if it implements that part. A prop written explicitly on the grid always wins, so you can adopt it and still override one piece.",
|
|
7276
|
+
"group": "common"
|
|
7277
|
+
},
|
|
7278
|
+
{
|
|
7279
|
+
"key": "pivotResultColumns",
|
|
7280
|
+
"label": "Pivot Result Columns",
|
|
7281
|
+
"type": "json",
|
|
7282
|
+
"description": "Columns that REPLACE `columns` while set. The server-side row model supplies them in pivot mode - one column per pivoted value, grouped under a header per pivot key - and clears them when pivot mode ends, so `columns` stays the app's own list. Rarely set by hand.",
|
|
7283
|
+
"group": "common"
|
|
7284
|
+
},
|
|
7285
|
+
{
|
|
7286
|
+
"key": "rowSelectionModel",
|
|
7287
|
+
"label": "Row Selection Model",
|
|
7288
|
+
"type": "json",
|
|
7289
|
+
"group": "common"
|
|
7290
|
+
},
|
|
7247
7291
|
{
|
|
7248
7292
|
"key": "isDetailRow",
|
|
7249
7293
|
"label": "Is Detail Row",
|
|
@@ -7252,11 +7296,41 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7252
7296
|
"code": true,
|
|
7253
7297
|
"group": "advanced"
|
|
7254
7298
|
},
|
|
7299
|
+
{
|
|
7300
|
+
"key": "detailRowHeight",
|
|
7301
|
+
"label": "Detail Row Height",
|
|
7302
|
+
"type": "number",
|
|
7303
|
+
"description": "The height of a detail row, in px, or a function of its data. With it the virtualizer can size detail rows, so master-detail works with `virtualization` on (the panel scrolls inside its cell when taller). Without it a detail row is auto height, which needs `virtualization={false}`.",
|
|
7304
|
+
"group": "common"
|
|
7305
|
+
},
|
|
7306
|
+
{
|
|
7307
|
+
"key": "showDetailToggle",
|
|
7308
|
+
"label": "Show Detail Toggle",
|
|
7309
|
+
"type": "boolean",
|
|
7310
|
+
"description": "A row-header column of chevrons that open and close detail rows. It sits with the row-number and selection columns: sticky at the left, no column menu, not resizable, not reorderable, outside the active cell and the selection. The chevron calls `onDetailToggle` (or, on a server row model, the model's own toggle) and reads `isDetailOpen` for its direction; `hasDetail` hides it on rows that have nothing to open. Detail rows themselves get an empty cell.",
|
|
7311
|
+
"group": "common"
|
|
7312
|
+
},
|
|
7313
|
+
{
|
|
7314
|
+
"key": "isDetailOpen",
|
|
7315
|
+
"label": "Is Detail Open",
|
|
7316
|
+
"type": "json",
|
|
7317
|
+
"description": "Whether the detail under a row is open: the chevron's direction and `aria-expanded`.",
|
|
7318
|
+
"code": true,
|
|
7319
|
+
"group": "advanced"
|
|
7320
|
+
},
|
|
7321
|
+
{
|
|
7322
|
+
"key": "hasDetail",
|
|
7323
|
+
"label": "Has Detail",
|
|
7324
|
+
"type": "json",
|
|
7325
|
+
"description": "Whether a row has a detail to open; the chevron is left out where this returns false.",
|
|
7326
|
+
"code": true,
|
|
7327
|
+
"group": "advanced"
|
|
7328
|
+
},
|
|
7255
7329
|
{
|
|
7256
7330
|
"key": "serverGroup",
|
|
7257
7331
|
"label": "Server Group",
|
|
7258
7332
|
"type": "json",
|
|
7259
|
-
"description": "Server-side group / tree keyboard + accessibility, built into the grid. When set, the grid uses the treegrid role and marks matching rows with `aria-level` / `aria-expanded`, and ArrowRight / ArrowLeft expand / collapse the focused group row (no app-level key handling). Pair with `serverGroupRows` + `SvGroupCell` for the visual expander. Every accessor receives the row data.",
|
|
7333
|
+
"description": "Server-side group / tree keyboard + accessibility, built into the grid. When set, the grid uses the treegrid role and marks matching rows with `aria-level` / `aria-expanded`, and ArrowRight / ArrowLeft expand / collapse the focused group row (no app-level key handling). Pair with `serverGroupRows` + `SvGroupCell` from `@svgrid/enterprise` for the visual expander, or drive it from your own tree state. Every accessor receives the row data.",
|
|
7260
7334
|
"group": "common"
|
|
7261
7335
|
},
|
|
7262
7336
|
{
|
|
@@ -7281,6 +7355,13 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7281
7355
|
"description": "Freeze the first N rows: they stay under the header while the body scrolls, and unlike `pinnedTopRows` they are the grid's own rows, still editable, selectable and numbered as rows 1..N. Excel's Freeze Panes, for the row half; the column half is `columnPinning`. Under `virtualization` the frozen rows are always rendered and the window skips them.",
|
|
7282
7356
|
"group": "common"
|
|
7283
7357
|
},
|
|
7358
|
+
{
|
|
7359
|
+
"key": "stickyGroupRows",
|
|
7360
|
+
"label": "Sticky Group Rows",
|
|
7361
|
+
"type": "boolean",
|
|
7362
|
+
"description": "Keep the group a row belongs to in view: while the rows of an open group scroll past, the group's row (and its parents' rows) stay under the header, the way a section heading stays put in a long list, so a screen deep inside a 60,000-row group still says which group it is. Works for server-side groups (`rowModel` / `serverGroup`), client grouping and tree data. Under `virtualization` the band shows a copy of each ancestor row; without it the rows themselves stick. Off by default.",
|
|
7363
|
+
"group": "common"
|
|
7364
|
+
},
|
|
7284
7365
|
{
|
|
7285
7366
|
"key": "mergedCells",
|
|
7286
7367
|
"label": "Merged Cells",
|
|
@@ -7398,7 +7479,7 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7398
7479
|
"key": "cellSelectionChange",
|
|
7399
7480
|
"label": "Cell Selection Change",
|
|
7400
7481
|
"prop": "onCellSelectionChange",
|
|
7401
|
-
"description": "Fires whenever the cell
|
|
7482
|
+
"description": "Fires whenever the cell selection changes (mouse, keyboard, or `api.selectCells()`). `ranges` is every selected rectangle, matching `api.getSelected()`: the ones a Ctrl+click committed and the active one last, each `[rowStart, colStart, rowEnd, colEnd]` in grid coords. Empty array when the user clears the selection."
|
|
7402
7483
|
},
|
|
7403
7484
|
{
|
|
7404
7485
|
"key": "sortingChange",
|
|
@@ -7466,6 +7547,24 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7466
7547
|
"prop": "onScrollBottomReached",
|
|
7467
7548
|
"description": "Fires once each time the body is scrolled to (within ~32px of) the bottom. Re-arms after the user scrolls back up. The canonical hook for infinite / lazy loading - append more rows to `data` when it fires."
|
|
7468
7549
|
},
|
|
7550
|
+
{
|
|
7551
|
+
"key": "visibleRangeChange",
|
|
7552
|
+
"label": "Visible Range Change",
|
|
7553
|
+
"prop": "onVisibleRangeChange",
|
|
7554
|
+
"description": "Fires when the range of rows on screen changes, with the first and last row INDEX (not pixels). Coalesced to one call per frame, so it is cheap to wire to something that fetches. This is the hook a block-loading data source needs and `onScrollBottomReached` cannot give it: \"the user is looking at rows 4,000-4,020\" answers which block to fetch and which to keep, while \"they hit the bottom\" only ever means \"append more\". Both are free; use this one with `createServerDataSource({ mode: 'infinite' })`, or let `rowModel` wire it for you. Reports `0, data.length - 1` when virtualization is off, since every row really is rendered."
|
|
7555
|
+
},
|
|
7556
|
+
{
|
|
7557
|
+
"key": "retryRow",
|
|
7558
|
+
"label": "Retry Row",
|
|
7559
|
+
"prop": "onRetryRow",
|
|
7560
|
+
"description": "Called by the Retry button on a `\"failed\"` placeholder row."
|
|
7561
|
+
},
|
|
7562
|
+
{
|
|
7563
|
+
"key": "detailToggle",
|
|
7564
|
+
"label": "Detail Toggle",
|
|
7565
|
+
"prop": "onDetailToggle",
|
|
7566
|
+
"description": "Open or close the detail under a row. The chevron of `showDetailToggle` and Ctrl+Enter on a row call it; you keep the open set and insert or remove the detail row in `data`. A server row model supplies its own (the `rowModel` prop wires `toggleDetail`), so this is the client side."
|
|
7567
|
+
},
|
|
7469
7568
|
{
|
|
7470
7569
|
"key": "columnOrderChange",
|
|
7471
7570
|
"label": "Column Order Change",
|
|
@@ -7477,6 +7576,12 @@ export const GENERATED_UI_SURFACE = {
|
|
|
7477
7576
|
"label": "Row Drag End",
|
|
7478
7577
|
"prop": "onRowDragEnd",
|
|
7479
7578
|
"description": "Fires on the TARGET grid after a managed row drag settles, with the moved row, its landing index, whether it stayed in the same grid, and the source / target grid ids. Use it to mirror the move into your own state (persistence, server sync). The grid has already applied the change to its internal data by the time this fires."
|
|
7579
|
+
},
|
|
7580
|
+
{
|
|
7581
|
+
"key": "rowDrop",
|
|
7582
|
+
"label": "Row Drop",
|
|
7583
|
+
"prop": "onRowDrop",
|
|
7584
|
+
"description": "Take the drop yourself. With this set the grid keeps the drag affordance and the drop indicator but does not touch its data: the handler gets the dragged row, the row it landed on (null for the empty space below the rows) and the side - `before` / `after`, or `into` when it landed on the middle of a group or tree row, which makes it that row's child. The path for a `rowModel`, whose rows the grid does not own: hand the move to the model (`moveRow`) or to your server. Only fires within one grid; a drop from another grid stays managed."
|
|
7480
7585
|
}
|
|
7481
7586
|
]
|
|
7482
7587
|
}
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* do, and is genuinely useful on its own for small datasets, tests, demos,
|
|
6
6
|
* and prototyping a schema before a database exists behind it.
|
|
7
7
|
*/
|
|
8
|
-
import type { RowData } from '@svgrid/grid';
|
|
8
|
+
import type { RowData, ServerDataSource } from '@svgrid/grid';
|
|
9
9
|
import { type EntitySchema } from '../schema.js';
|
|
10
10
|
import type { WritableDataSource } from './types.js';
|
|
11
11
|
import { type AggregateSource } from '../sources/aggregate.js';
|
|
12
|
-
export declare function createInMemoryDataSource<TData extends RowData>(initial: ReadonlyArray<TData>, schema: EntitySchema<TData>): WritableDataSource<TData> & AggregateSource & {
|
|
12
|
+
export declare function createInMemoryDataSource<TData extends RowData>(initial: ReadonlyArray<TData>, schema: EntitySchema<TData>): WritableDataSource<TData> & Required<Pick<ServerDataSource<TData>, 'updateWhere'>> & AggregateSource & {
|
|
13
13
|
rows(): ReadonlyArray<TData>;
|
|
14
14
|
};
|