@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
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of thing a sheet tab is.
|
|
3
|
+
*
|
|
4
|
+
* A workbook's tabs have always been cell grids. A `grid` tab is the other
|
|
5
|
+
* thing a business workbook wants: a data-bound table, backed by records
|
|
6
|
+
* rather than by cells, sorted and filtered and edited as a grid rather
|
|
7
|
+
* than as a sheet.
|
|
8
|
+
*
|
|
9
|
+
* The trick that makes this cheap rather than a second engine: **a grid
|
|
10
|
+
* sheet PROJECTS its records into the workbook's cells.** The records are
|
|
11
|
+
* the truth, and after every change they are written into the same
|
|
12
|
+
* `string[][]` a cell sheet keeps, header row included. So
|
|
13
|
+
* `=SUM(Orders!D2:D99)` on a cell sheet reads a grid sheet with no new
|
|
14
|
+
* machinery at all: the formula engine, the dependency graph, the file
|
|
15
|
+
* writers and the printer never learn that this tab is different.
|
|
16
|
+
*
|
|
17
|
+
* The projection runs through `setRaw`, which ignores a write that changes
|
|
18
|
+
* nothing, so editing one field of one record rewrites one cell and
|
|
19
|
+
* recalculates what depended on it, not the sheet.
|
|
20
|
+
*
|
|
21
|
+
* What a grid sheet deliberately does NOT do: hold a formula of its own.
|
|
22
|
+
* Its cells are a rendering of its records, so anything typed into them
|
|
23
|
+
* would be overwritten by the next projection. A sheet that needs formulas
|
|
24
|
+
* beside the data is a cell sheet reading the grid one across.
|
|
25
|
+
*/
|
|
26
|
+
import { sheetCellsFromRows } from './from-rows.js';
|
|
27
|
+
export const emptyGridSheet = () => ({ fields: [], rows: [] });
|
|
28
|
+
/** The cells a grid sheet stands for: the header, the records, the totals. */
|
|
29
|
+
export function projectGridSheet(spec) {
|
|
30
|
+
return sheetCellsFromRows(spec.rows, spec.fields, spec.totals ? { totals: true } : {});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Where a record sits in the projection.
|
|
34
|
+
*
|
|
35
|
+
* Row 0 is the header, so record `i` is at row `i + 1`. Kept as a function
|
|
36
|
+
* rather than inlined so the offset is named in one place: getting it
|
|
37
|
+
* wrong reads the header as a record and is silent.
|
|
38
|
+
*/
|
|
39
|
+
export const rowOfRecord = (index) => index + 1;
|
|
40
|
+
export const recordOfRow = (row) => row - 1;
|
|
41
|
+
/** Is this row of the projection a record, rather than a header or total? */
|
|
42
|
+
export const isRecordRow = (spec, row) => row >= 1 && row <= spec.rows.length;
|
|
43
|
+
/**
|
|
44
|
+
* A field's column in the projection, or -1.
|
|
45
|
+
*
|
|
46
|
+
* Matched on the field name rather than the label, since two columns may
|
|
47
|
+
* share a label and the name is what the record is keyed by.
|
|
48
|
+
*/
|
|
49
|
+
export const columnOfField = (spec, field) => spec.fields.findIndex((f) => f.field === field);
|
|
50
|
+
/** A deep-enough copy for a document snapshot. */
|
|
51
|
+
export const copyGridSheet = (spec) => ({
|
|
52
|
+
fields: spec.fields.map((f) => ({ ...f })),
|
|
53
|
+
rows: spec.rows.map((r) => ({ ...r })),
|
|
54
|
+
...(spec.editable !== undefined ? { editable: spec.editable } : {}),
|
|
55
|
+
...(spec.totals !== undefined ? { totals: spec.totals } : {}),
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* A value typed into a grid sheet, coerced to the field's type.
|
|
59
|
+
*
|
|
60
|
+
* The grid hands back whatever its editor produced, which for a number
|
|
61
|
+
* column typed into by hand is a string. Storing that would make the
|
|
62
|
+
* record's shape depend on how it was edited, so it is narrowed here once.
|
|
63
|
+
*/
|
|
64
|
+
export function coerceFieldValue(field, value) {
|
|
65
|
+
if (value === null || value === undefined || value === '')
|
|
66
|
+
return value === '' ? '' : null;
|
|
67
|
+
switch (field.type) {
|
|
68
|
+
case 'number': {
|
|
69
|
+
const n = typeof value === 'number' ? value : Number(String(value).trim());
|
|
70
|
+
return Number.isFinite(n) ? n : value;
|
|
71
|
+
}
|
|
72
|
+
case 'boolean': {
|
|
73
|
+
if (typeof value === 'boolean')
|
|
74
|
+
return value;
|
|
75
|
+
const text = String(value).trim().toLowerCase();
|
|
76
|
+
if (text === 'true' || text === 'yes' || text === '1')
|
|
77
|
+
return true;
|
|
78
|
+
if (text === 'false' || text === 'no' || text === '0')
|
|
79
|
+
return false;
|
|
80
|
+
return value;
|
|
81
|
+
}
|
|
82
|
+
case 'date': {
|
|
83
|
+
if (value instanceof Date)
|
|
84
|
+
return value;
|
|
85
|
+
const text = String(value).trim();
|
|
86
|
+
return text === '' ? null : text;
|
|
87
|
+
}
|
|
88
|
+
default:
|
|
89
|
+
return typeof value === 'string' ? value : String(value);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -39,7 +39,7 @@ export declare function formatAllowed(target: SheetFormatTarget, rects: Readonly
|
|
|
39
39
|
/** Attach the store the formatting shortcuts write to. */
|
|
40
40
|
export declare function setFormatTarget(target: SheetFormatTarget | null): void;
|
|
41
41
|
export declare function getFormatTarget(): SheetFormatTarget | null;
|
|
42
|
-
export declare function setWorkbook(next: Workbook | null, onChange?: () => void): void;
|
|
42
|
+
export declare function setWorkbook(next: Workbook | null, onChange?: () => void, isHidden?: (name: string) => boolean): void;
|
|
43
43
|
export declare function getWorkbook(): Workbook | null;
|
|
44
44
|
/** Move `delta` sheets from the active one. Excel does NOT wrap at the ends,
|
|
45
45
|
* so neither does this: hitting the last sheet and pressing again should
|
|
@@ -57,7 +57,7 @@ export declare function setFindReplaceHandler(fn: ((cmd: GridCommandContext) =>
|
|
|
57
57
|
* answers them the way it answers the ribbon button; returning false means
|
|
58
58
|
* nothing is behind the action and the key falls through.
|
|
59
59
|
*/
|
|
60
|
-
export type RibbonKeyAction = 'recalculate' | 'toggle-formulas' | 'toggle-filter' | 'insert-function' | 'name-manager' | 'insert-table' | 'hide-rows' | 'hide-columns' | 'unhide-rows' | 'unhide-columns' | 'edit-comment' | 'open-list' | 'toggle-ribbon' | 'merge-center' | 'merge-across' | 'merge-cells' | 'unmerge-cells';
|
|
60
|
+
export type RibbonKeyAction = 'file-open' | 'file-save-xlsx' | 'file-print' | 'recalculate' | 'toggle-formulas' | 'toggle-filter' | 'insert-function' | 'name-manager' | 'insert-table' | 'hide-rows' | 'hide-columns' | 'unhide-rows' | 'unhide-columns' | 'edit-comment' | 'open-list' | 'toggle-ribbon' | 'insert-link' | 'merge-center' | 'merge-across' | 'merge-cells' | 'unmerge-cells' | 'cut';
|
|
61
61
|
export declare function setRibbonActionHandler(fn: ((action: RibbonKeyAction, cmd: GridCommandContext) => boolean) | null): void;
|
|
62
62
|
/** Raise a ribbon action from a button whose face is not an entry of its own. */
|
|
63
63
|
export declare function raiseRibbonAction(action: RibbonKeyAction, cmd: GridCommandContext): boolean;
|
|
@@ -70,6 +70,8 @@ export declare function activeEntry(cmd: GridCommandContext): CellFormatEntry |
|
|
|
70
70
|
/** Excel's Increase / Decrease Font Size: the next rung of the ladder. */
|
|
71
71
|
export declare function nudgeFontSize(direction: 1 | -1): SheetCommand;
|
|
72
72
|
export declare function setFormatDialogHandler(fn: ((cmd: GridCommandContext) => void) | null): void;
|
|
73
|
+
export declare function setShortcutOwner(token: object | null): void;
|
|
74
|
+
export declare function getShortcutOwner(): object | null;
|
|
73
75
|
export type SheetBinding = {
|
|
74
76
|
/** Matched case-insensitively against `event.key`. */
|
|
75
77
|
key: string;
|
|
@@ -147,9 +149,17 @@ export declare function applyBorders(cmd: GridCommandContext, preset: BorderPres
|
|
|
147
149
|
/** Excel's Clear Formats: drop every format on the selection, keep the values. */
|
|
148
150
|
export declare function clearFormats(cmd: GridCommandContext): boolean;
|
|
149
151
|
export declare function preset(name: FormatPresetName): SheetCommand;
|
|
150
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* Alt+=. Proposes =SUM(range) over the run Excel would guess. With one cell
|
|
154
|
+
* selected the formula is opened in the cell's editor rather than written,
|
|
155
|
+
* as Excel's AutoSum does: the guess is often a row short, and the editor is
|
|
156
|
+
* where it gets corrected before Enter takes it. A wider selection has the
|
|
157
|
+
* sum written into the active cell outright.
|
|
158
|
+
*/
|
|
151
159
|
export declare const autoSum: SheetCommand;
|
|
152
160
|
export declare const SHEET_BINDINGS: ReadonlyArray<SheetBinding>;
|
|
161
|
+
/** Shift+F11 and the ribbon's Insert Sheet: a new sheet after the last, made active. */
|
|
162
|
+
export declare function newSheet(): boolean;
|
|
153
163
|
export declare function structural(cmd: GridCommandContext, kind: 'insert' | 'delete'): boolean;
|
|
154
164
|
/**
|
|
155
165
|
* The handler registered with the grid. Returns true when a binding ran, which
|
package/dist/sheet/shortcuts.js
CHANGED
|
@@ -30,9 +30,12 @@ export function getFormatTarget() {
|
|
|
30
30
|
*/
|
|
31
31
|
let workbook = null;
|
|
32
32
|
let onWorkbookChange = null;
|
|
33
|
-
|
|
33
|
+
/** Which sheets are hidden, so Ctrl+PageUp and PageDown step over them. */
|
|
34
|
+
let sheetHidden = null;
|
|
35
|
+
export function setWorkbook(next, onChange, isHidden) {
|
|
34
36
|
workbook = next;
|
|
35
37
|
onWorkbookChange = onChange ?? null;
|
|
38
|
+
sheetHidden = isHidden ?? null;
|
|
36
39
|
}
|
|
37
40
|
export function getWorkbook() {
|
|
38
41
|
return workbook;
|
|
@@ -48,7 +51,10 @@ export function switchSheet(delta) {
|
|
|
48
51
|
const at = sheets.indexOf(wb.active);
|
|
49
52
|
if (at < 0)
|
|
50
53
|
return false;
|
|
51
|
-
|
|
54
|
+
// The next sheet that shows; a hidden one is stepped over, as in Excel.
|
|
55
|
+
let next = at + delta;
|
|
56
|
+
while (next >= 0 && next < sheets.length && sheetHidden?.(sheets[next]))
|
|
57
|
+
next += delta;
|
|
52
58
|
if (next < 0 || next >= sheets.length)
|
|
53
59
|
return false;
|
|
54
60
|
wb.setActive(sheets[next]);
|
|
@@ -108,6 +114,22 @@ let onFormatDialog = null;
|
|
|
108
114
|
export function setFormatDialogHandler(fn) {
|
|
109
115
|
onFormatDialog = fn;
|
|
110
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Which component the targets above belong to. Every one of them is a
|
|
119
|
+
* module-level singleton, so with two sheets on a page (a collaboration
|
|
120
|
+
* demo, a docs page with an example per section) the keys, the ribbon's
|
|
121
|
+
* buttons and its pressed states all went to whichever sheet mounted last:
|
|
122
|
+
* Ctrl+B bolded a cell three examples down. A sheet claims the targets when
|
|
123
|
+
* the pointer or the focus lands in it, and on unmount lets go only of what
|
|
124
|
+
* is still its own, so tearing one sheet down cannot disarm another.
|
|
125
|
+
*/
|
|
126
|
+
let owner = null;
|
|
127
|
+
export function setShortcutOwner(token) {
|
|
128
|
+
owner = token;
|
|
129
|
+
}
|
|
130
|
+
export function getShortcutOwner() {
|
|
131
|
+
return owner;
|
|
132
|
+
}
|
|
111
133
|
/** Read the grid through the command context as a blank-aware matrix. */
|
|
112
134
|
export function gridOf(cmd) {
|
|
113
135
|
return {
|
|
@@ -452,7 +474,13 @@ function rectOfActive(cmd) {
|
|
|
452
474
|
export function preset(name) {
|
|
453
475
|
return (cmd) => applyFormat(cmd, { numFmt: FORMAT_PRESETS[name] });
|
|
454
476
|
}
|
|
455
|
-
/**
|
|
477
|
+
/**
|
|
478
|
+
* Alt+=. Proposes =SUM(range) over the run Excel would guess. With one cell
|
|
479
|
+
* selected the formula is opened in the cell's editor rather than written,
|
|
480
|
+
* as Excel's AutoSum does: the guess is often a row short, and the editor is
|
|
481
|
+
* where it gets corrected before Enter takes it. A wider selection has the
|
|
482
|
+
* sum written into the active cell outright.
|
|
483
|
+
*/
|
|
456
484
|
export const autoSum = (cmd) => {
|
|
457
485
|
const active = cmd.activeCell;
|
|
458
486
|
if (!active)
|
|
@@ -464,7 +492,12 @@ export const autoSum = (cmd) => {
|
|
|
464
492
|
return false;
|
|
465
493
|
const [minRow, minCol, maxRow, maxCol] = range;
|
|
466
494
|
const ref = (r, c) => formatA1({ col: c, colAbs: false, row: r, rowAbs: false, sheet: null });
|
|
467
|
-
|
|
495
|
+
const formula = `=SUM(${ref(minRow, minCol)}:${ref(maxRow, maxCol)})`;
|
|
496
|
+
const last = cmd.ranges[cmd.ranges.length - 1];
|
|
497
|
+
const oneCell = !last || (last[0] === last[2] && last[1] === last[3]);
|
|
498
|
+
if (oneCell && cmd.startEditing(active.rowIndex, active.colIndex, formula))
|
|
499
|
+
return true;
|
|
500
|
+
cmd.setCellValue(active.rowIndex, active.colIndex, formula);
|
|
468
501
|
return true;
|
|
469
502
|
};
|
|
470
503
|
export const SHEET_BINDINGS = [
|
|
@@ -525,10 +558,16 @@ export const SHEET_BINDINGS = [
|
|
|
525
558
|
{ key: 'l', mod: true, shift: true, run: raise('toggle-filter'), label: 'Filter' },
|
|
526
559
|
{ key: 'F3', shift: true, run: raise('insert-function'), label: 'Insert Function' },
|
|
527
560
|
{ key: 'F2', shift: true, run: raise('edit-comment'), label: 'Insert or edit the comment on the active cell' },
|
|
561
|
+
{ key: 'k', mod: true, run: raise('insert-link'), label: 'Insert or edit the link on the active cell' },
|
|
528
562
|
{ key: 'ArrowDown', alt: true, run: raise('open-list'), label: 'Open the list a validated cell offers' },
|
|
529
563
|
{ key: 'F1', mod: true, run: raise('toggle-ribbon'), label: 'Collapse or expand the ribbon' },
|
|
530
564
|
{ key: 'F3', mod: true, run: raise('name-manager'), label: 'Name Manager' },
|
|
531
565
|
{ key: 't', mod: true, run: raise('insert-table'), label: 'Format as Table' },
|
|
566
|
+
// The File tab's keys. The browser's own Open and Save are what they
|
|
567
|
+
// replace, as in Excel; with no shell to answer them they fall through.
|
|
568
|
+
{ key: 'o', mod: true, run: raise('file-open'), label: 'Open' },
|
|
569
|
+
{ key: 's', mod: true, run: raise('file-save-xlsx'), label: 'Save As' },
|
|
570
|
+
{ key: 'p', mod: true, run: raise('file-print'), label: 'Print' },
|
|
532
571
|
// Structure. Like the format bindings, these decline when nothing is
|
|
533
572
|
// attached. Excel opens a dialog for an ambiguous selection; deciding what
|
|
534
573
|
// that looks like is the consumer's, so an ambiguous selection declines and
|
|
@@ -541,12 +580,22 @@ export const SHEET_BINDINGS = [
|
|
|
541
580
|
{ key: '0', code: 'Digit0', mod: true, run: raise('hide-columns'), label: 'Hide the selected columns' },
|
|
542
581
|
{ key: '(', code: 'Digit9', mod: true, shift: true, run: raise('unhide-rows'), label: 'Unhide rows in the selection' },
|
|
543
582
|
{ key: ')', code: 'Digit0', mod: true, shift: true, run: raise('unhide-columns'), label: 'Unhide columns in the selection' },
|
|
583
|
+
// Ctrl+H is Excel's Replace and Ctrl+F its Find; the shell has one dialog
|
|
584
|
+
// for both, so both open it. Ctrl+F is the browser's find otherwise, and a
|
|
585
|
+
// spreadsheet user who presses it wants the cells searched, as in Excel
|
|
586
|
+
// and Sheets; the browser's is a keystroke away with the sheet unfocused.
|
|
544
587
|
{ key: 'h', mod: true, run: (cmd) => {
|
|
545
588
|
if (!onFindReplace || !getFindTarget())
|
|
546
589
|
return false;
|
|
547
590
|
onFindReplace(cmd);
|
|
548
591
|
return true;
|
|
549
592
|
}, label: 'Find and Replace' },
|
|
593
|
+
{ key: 'f', mod: true, run: (cmd) => {
|
|
594
|
+
if (!onFindReplace || !getFindTarget())
|
|
595
|
+
return false;
|
|
596
|
+
onFindReplace(cmd);
|
|
597
|
+
return true;
|
|
598
|
+
}, label: 'Find' },
|
|
550
599
|
{ key: 'v', mod: true, shift: true, run: (cmd) => {
|
|
551
600
|
if (!onPasteSpecial)
|
|
552
601
|
return false;
|
|
@@ -557,15 +606,17 @@ export const SHEET_BINDINGS = [
|
|
|
557
606
|
// grid leaves Ctrl+PageDown to the browser.
|
|
558
607
|
{ key: 'PageDown', mod: true, run: () => switchSheet(1), label: 'Next sheet' },
|
|
559
608
|
{ key: 'PageUp', mod: true, run: () => switchSheet(-1), label: 'Previous sheet' },
|
|
560
|
-
{ key: 'F11', shift: true, run: () =>
|
|
561
|
-
const wb = workbook;
|
|
562
|
-
if (!wb)
|
|
563
|
-
return false;
|
|
564
|
-
wb.addSheet();
|
|
565
|
-
onWorkbookChange?.();
|
|
566
|
-
return true;
|
|
567
|
-
}, label: 'New sheet' },
|
|
609
|
+
{ key: 'F11', shift: true, run: () => newSheet(), label: 'New sheet' },
|
|
568
610
|
];
|
|
611
|
+
/** Shift+F11 and the ribbon's Insert Sheet: a new sheet after the last, made active. */
|
|
612
|
+
export function newSheet() {
|
|
613
|
+
const wb = workbook;
|
|
614
|
+
if (!wb)
|
|
615
|
+
return false;
|
|
616
|
+
wb.addSheet();
|
|
617
|
+
onWorkbookChange?.();
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
569
620
|
export function structural(cmd, kind) {
|
|
570
621
|
if (!getStructureTarget())
|
|
571
622
|
return false;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel's sort order over a block of rows, by one key or several.
|
|
3
|
+
*
|
|
4
|
+
* The rules that make a sheet's sort read as Excel's: numbers before text
|
|
5
|
+
* before booleans and errors, blanks last whichever way the sort runs,
|
|
6
|
+
* text without regard to case, a stable order so a tie keeps the rows as
|
|
7
|
+
* they were, and each key only consulted when the ones before it tie.
|
|
8
|
+
*/
|
|
9
|
+
import { type CellValue } from './ast.js';
|
|
10
|
+
export type SortDirection = 'asc' | 'desc';
|
|
11
|
+
/**
|
|
12
|
+
* What a key sorts on. Excel's "Sort On" list: the cell's value, or one of
|
|
13
|
+
* its colours.
|
|
14
|
+
*/
|
|
15
|
+
export type SortOn = 'value' | 'fill' | 'color';
|
|
16
|
+
export type SortKey = {
|
|
17
|
+
/** The column the key reads, as a sheet column index. */
|
|
18
|
+
col: number;
|
|
19
|
+
direction: SortDirection;
|
|
20
|
+
/** What the key reads in that column. Absent means the value. */
|
|
21
|
+
on?: SortOn;
|
|
22
|
+
/**
|
|
23
|
+
* The colour this key puts first, for a colour key: Excel sorts ONE
|
|
24
|
+
* colour to the top (ascending) or the bottom (descending) and leaves
|
|
25
|
+
* every other row where it was. Absent leaves the key doing nothing.
|
|
26
|
+
*/
|
|
27
|
+
colour?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* The rows in sorted order. `rows` are the row indexes of the block's data
|
|
31
|
+
* rows (the header left out), `valueAt` reads a computed cell.
|
|
32
|
+
*/
|
|
33
|
+
export declare function sortOrder(rows: ReadonlyArray<number>, keys: ReadonlyArray<SortKey>, valueAt: (row: number, col: number) => CellValue,
|
|
34
|
+
/**
|
|
35
|
+
* The colours a cell carries, for a key that sorts on one: its fill and
|
|
36
|
+
* its font colour, each as it is written, or null. Only a colour key
|
|
37
|
+
* calls it.
|
|
38
|
+
*/
|
|
39
|
+
colourAt?: (row: number, col: number, on: 'fill' | 'color') => string | null): number[];
|
|
40
|
+
/**
|
|
41
|
+
* Excel's guess at "my data has headers": the first row of the key column
|
|
42
|
+
* holds text and the row under it holds something that is not text. Given
|
|
43
|
+
* the block's columns it looks across them, as Excel does: a Name column
|
|
44
|
+
* over names says nothing, and the Pay column beside it, text over a
|
|
45
|
+
* number, is what says the first row is a header, so a sort by name does
|
|
46
|
+
* not carry "Name" down into the names.
|
|
47
|
+
*/
|
|
48
|
+
export declare function guessHeaderRow(valueAt: (row: number, col: number) => CellValue, top: number, col: number, columns?: {
|
|
49
|
+
left: number;
|
|
50
|
+
right: number;
|
|
51
|
+
}): boolean;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel's sort order over a block of rows, by one key or several.
|
|
3
|
+
*
|
|
4
|
+
* The rules that make a sheet's sort read as Excel's: numbers before text
|
|
5
|
+
* before booleans and errors, blanks last whichever way the sort runs,
|
|
6
|
+
* text without regard to case, a stable order so a tie keeps the rows as
|
|
7
|
+
* they were, and each key only consulted when the ones before it tie.
|
|
8
|
+
*/
|
|
9
|
+
import { isError } from './ast.js';
|
|
10
|
+
import { isBlankValue } from './navigate.js';
|
|
11
|
+
/** Excel's type order: numbers, then text, then booleans and errors. */
|
|
12
|
+
function rank(v) {
|
|
13
|
+
if (isBlankValue(v))
|
|
14
|
+
return [3, ''];
|
|
15
|
+
if (typeof v === 'number')
|
|
16
|
+
return [0, v];
|
|
17
|
+
if (typeof v === 'boolean')
|
|
18
|
+
return [2, v ? 1 : 0];
|
|
19
|
+
if (isError(v))
|
|
20
|
+
return [2, 0];
|
|
21
|
+
return [1, String(v).toLowerCase()];
|
|
22
|
+
}
|
|
23
|
+
function compareValues(a, b, direction) {
|
|
24
|
+
const [ka, va] = rank(a);
|
|
25
|
+
const [kb, vb] = rank(b);
|
|
26
|
+
// A blank sorts last both ways, as in Excel.
|
|
27
|
+
if (ka === 3 || kb === 3)
|
|
28
|
+
return ka === kb ? 0 : ka === 3 ? 1 : -1;
|
|
29
|
+
if (ka !== kb)
|
|
30
|
+
return direction === 'asc' ? ka - kb : kb - ka;
|
|
31
|
+
const cmp = typeof va === 'number' && typeof vb === 'number' ? va - vb : String(va).localeCompare(String(vb));
|
|
32
|
+
return direction === 'asc' ? cmp : -cmp;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The rows in sorted order. `rows` are the row indexes of the block's data
|
|
36
|
+
* rows (the header left out), `valueAt` reads a computed cell.
|
|
37
|
+
*/
|
|
38
|
+
export function sortOrder(rows, keys, valueAt,
|
|
39
|
+
/**
|
|
40
|
+
* The colours a cell carries, for a key that sorts on one: its fill and
|
|
41
|
+
* its font colour, each as it is written, or null. Only a colour key
|
|
42
|
+
* calls it.
|
|
43
|
+
*/
|
|
44
|
+
colourAt) {
|
|
45
|
+
if (!keys.length)
|
|
46
|
+
return [...rows];
|
|
47
|
+
return rows
|
|
48
|
+
.map((row, index) => ({ row, index }))
|
|
49
|
+
.sort((a, b) => {
|
|
50
|
+
for (const key of keys) {
|
|
51
|
+
const cmp = key.on === 'fill' || key.on === 'color'
|
|
52
|
+
? compareColours(a.row, b.row, key, colourAt)
|
|
53
|
+
: compareValues(valueAt(a.row, key.col), valueAt(b.row, key.col), key.direction);
|
|
54
|
+
if (cmp !== 0)
|
|
55
|
+
return cmp;
|
|
56
|
+
}
|
|
57
|
+
return a.index - b.index;
|
|
58
|
+
})
|
|
59
|
+
.map((entry) => entry.row);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A colour key: the rows wearing the chosen colour move to the top, or to
|
|
63
|
+
* the bottom when the key runs the other way, and everything else keeps the
|
|
64
|
+
* order it had.
|
|
65
|
+
*
|
|
66
|
+
* That is Excel's model and it is worth stating, because the obvious
|
|
67
|
+
* alternative, ordering colours among themselves, has no order to use: two
|
|
68
|
+
* colours are not greater or lesser than one another.
|
|
69
|
+
*/
|
|
70
|
+
function compareColours(rowA, rowB, key, colourAt) {
|
|
71
|
+
if (!key.colour || !colourAt || (key.on !== 'fill' && key.on !== 'color'))
|
|
72
|
+
return 0;
|
|
73
|
+
const wanted = key.colour.trim().toLowerCase();
|
|
74
|
+
const has = (row) => (colourAt(row, key.col, key.on) ?? '').trim().toLowerCase() === wanted;
|
|
75
|
+
const a = has(rowA);
|
|
76
|
+
const b = has(rowB);
|
|
77
|
+
if (a === b)
|
|
78
|
+
return 0;
|
|
79
|
+
return key.direction === 'asc' ? (a ? -1 : 1) : (a ? 1 : -1);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Excel's guess at "my data has headers": the first row of the key column
|
|
83
|
+
* holds text and the row under it holds something that is not text. Given
|
|
84
|
+
* the block's columns it looks across them, as Excel does: a Name column
|
|
85
|
+
* over names says nothing, and the Pay column beside it, text over a
|
|
86
|
+
* number, is what says the first row is a header, so a sort by name does
|
|
87
|
+
* not carry "Name" down into the names.
|
|
88
|
+
*/
|
|
89
|
+
export function guessHeaderRow(valueAt, top, col, columns) {
|
|
90
|
+
const isText = (v) => typeof v === 'string' && v !== '';
|
|
91
|
+
const headerOver = (c) => {
|
|
92
|
+
const below = valueAt(top + 1, c);
|
|
93
|
+
return isText(valueAt(top, c)) && !isText(below) && !isBlankValue(below);
|
|
94
|
+
};
|
|
95
|
+
if (!columns)
|
|
96
|
+
return headerOver(col);
|
|
97
|
+
let someHeader = false;
|
|
98
|
+
for (let c = columns.left; c <= columns.right; c += 1) {
|
|
99
|
+
const head = valueAt(top, c);
|
|
100
|
+
// A number or a blank in the first row is data, whatever is beside it.
|
|
101
|
+
if (!isBlankValue(head) && !isText(head))
|
|
102
|
+
return false;
|
|
103
|
+
if (headerOver(c))
|
|
104
|
+
someHeader = true;
|
|
105
|
+
}
|
|
106
|
+
return someHeader;
|
|
107
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sparklines: Excel's tiny chart drawn INSIDE a cell, one per row (or per
|
|
3
|
+
* column) of a block of numbers.
|
|
4
|
+
*
|
|
5
|
+
* Excel keeps them as a group rather than as one chart per cell: a data
|
|
6
|
+
* range, a location range of the same shape, and the settings the whole
|
|
7
|
+
* group shares (the kind, the colours, whether every line is drawn on one
|
|
8
|
+
* scale). That is what this keeps, because it is also what the user edits:
|
|
9
|
+
* changing the kind changes the group, and clearing one clears the group.
|
|
10
|
+
*
|
|
11
|
+
* A sparkline is not an object (`objects.ts`): it has no anchor and no size
|
|
12
|
+
* of its own, it IS the cell, so it moves and dies with the cells the way a
|
|
13
|
+
* conditional format does. Its data is read live from the range, so editing
|
|
14
|
+
* a number redraws it.
|
|
15
|
+
*/
|
|
16
|
+
import type { Rect } from './format-store.js';
|
|
17
|
+
import type { StructuralEdit } from './refs.js';
|
|
18
|
+
import type { CellValue } from './ast.js';
|
|
19
|
+
/** The three Excel offers: Line, Column and Win/Loss. */
|
|
20
|
+
export declare const SPARKLINE_TYPES: readonly ["line", "column", "winloss"];
|
|
21
|
+
export type SheetSparklineType = (typeof SPARKLINE_TYPES)[number];
|
|
22
|
+
export type SparklineGroup = {
|
|
23
|
+
id: string;
|
|
24
|
+
/** The cells the sparklines are drawn in: one row, or one column. */
|
|
25
|
+
location: Rect;
|
|
26
|
+
/** The numbers they read, one line of it per location cell. */
|
|
27
|
+
data: Rect;
|
|
28
|
+
type: SheetSparklineType;
|
|
29
|
+
/** The line or the positive bars. Defaults to the theme's accent. */
|
|
30
|
+
color?: string;
|
|
31
|
+
/** The negative bars of a Column or Win/Loss group. */
|
|
32
|
+
negativeColor?: string;
|
|
33
|
+
/** One value scale across the whole group, as Excel's "Same for All". */
|
|
34
|
+
sameScale?: boolean;
|
|
35
|
+
/** A dot on the last point of a Line group. */
|
|
36
|
+
markers?: boolean;
|
|
37
|
+
};
|
|
38
|
+
/** An id no other group of this session carries. */
|
|
39
|
+
export declare const sparklineId: () => string;
|
|
40
|
+
/** A copy safe to hand out or save. */
|
|
41
|
+
export declare function copySparkline(group: SparklineGroup): SparklineGroup;
|
|
42
|
+
/**
|
|
43
|
+
* The group after an insert or delete: both rectangles move with their
|
|
44
|
+
* cells. Null when the edit took every cell the group was drawn in, or
|
|
45
|
+
* every cell it read.
|
|
46
|
+
*/
|
|
47
|
+
export declare function shiftSparkline(group: SparklineGroup, edit: StructuralEdit): SparklineGroup | null;
|
|
48
|
+
/** The groups of a sheet after an insert or delete, the gone ones dropped. */
|
|
49
|
+
export declare function shiftSparklines(groups: ReadonlyArray<SparklineGroup>, edit: StructuralEdit): SparklineGroup[];
|
|
50
|
+
/** True when the group draws down a column of cells rather than along a row. */
|
|
51
|
+
export declare function isColumnLocation(group: SparklineGroup): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* The numbers one location cell draws, read through `valueAt`. The nth cell
|
|
54
|
+
* of the location reads the nth line of the data: the nth row when the
|
|
55
|
+
* sparklines run down a column, the nth column when they run along a row.
|
|
56
|
+
* A blank or a text cell inside the line is a gap, which Excel draws as a
|
|
57
|
+
* zero rather than breaking the line.
|
|
58
|
+
*/
|
|
59
|
+
export declare function sparklineSeries(group: SparklineGroup, row: number, col: number, valueAt: (row: number, col: number) => CellValue): number[] | null;
|
|
60
|
+
/** The group drawn in a cell, or null: the last one wins, as Excel's does. */
|
|
61
|
+
/**
|
|
62
|
+
* One line per cell of the group: the cell the sparkline is drawn in and
|
|
63
|
+
* the block of numbers it reads.
|
|
64
|
+
*
|
|
65
|
+
* The drawing works this out cell by cell as it paints; a writer needs the
|
|
66
|
+
* whole list, because Excel stores a sparkline group as one entry per cell
|
|
67
|
+
* with its own reference.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* A sparkline as standalone SVG markup, for a page the component cannot
|
|
71
|
+
* draw on: the printed sheet.
|
|
72
|
+
*
|
|
73
|
+
* The geometry is the grid's own `buildSparkline`, so the printed line is
|
|
74
|
+
* the line on screen rather than a second drawing that can disagree with
|
|
75
|
+
* it. The markup carries its colours inline, since the print document has
|
|
76
|
+
* no stylesheet of ours.
|
|
77
|
+
*/
|
|
78
|
+
export declare function sparklineSvg(values: ReadonlyArray<number>, options: {
|
|
79
|
+
type?: SheetSparklineType;
|
|
80
|
+
width: number;
|
|
81
|
+
height: number;
|
|
82
|
+
color?: string;
|
|
83
|
+
negativeColor?: string;
|
|
84
|
+
min?: number;
|
|
85
|
+
max?: number;
|
|
86
|
+
markers?: boolean;
|
|
87
|
+
}): string;
|
|
88
|
+
export declare function sparklineLines(group: SparklineGroup): Array<{
|
|
89
|
+
row: number;
|
|
90
|
+
col: number;
|
|
91
|
+
data: Rect;
|
|
92
|
+
}>;
|
|
93
|
+
export declare function sparklineAt(groups: ReadonlyArray<SparklineGroup>, row: number, col: number): SparklineGroup | null;
|
|
94
|
+
/**
|
|
95
|
+
* The scale a group is drawn on when every sparkline in it shares one, which
|
|
96
|
+
* is Excel's "Same for All": the smallest and largest number anywhere in the
|
|
97
|
+
* data range. Null when the group scales each sparkline on its own.
|
|
98
|
+
*/
|
|
99
|
+
export declare function sparklineScale(group: SparklineGroup, valueAt: (row: number, col: number) => CellValue): {
|
|
100
|
+
min: number;
|
|
101
|
+
max: number;
|
|
102
|
+
} | null;
|
|
103
|
+
/**
|
|
104
|
+
* Where Excel's Create Sparklines dialog opens: the data is the selected
|
|
105
|
+
* block, and the location the column just past it (or the row just under it
|
|
106
|
+
* when the block is a single row). A block one cell tall gets a location of
|
|
107
|
+
* one cell, which is the common "a row of months, a sparkline beside it".
|
|
108
|
+
*/
|
|
109
|
+
export declare function sparklinesFromRange(data: Rect, type?: SheetSparklineType): SparklineGroup;
|
|
110
|
+
/** The groups left after clearing every one the rectangles touch. */
|
|
111
|
+
export declare function clearSparklines(groups: ReadonlyArray<SparklineGroup>, rects: ReadonlyArray<Rect>): SparklineGroup[];
|