@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/sheet/ast.d.ts
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* shapes without importing each other.
|
|
5
5
|
*/
|
|
6
6
|
import type { CellRef } from './address.js';
|
|
7
|
-
export type SheetError = '#REF!' | '#CYCLE!' | '#DIV/0!' | '#VALUE!' | '#NAME?' | '#NUM!' | '#N/A' | '#PARSE!'
|
|
7
|
+
export type SheetError = '#REF!' | '#CYCLE!' | '#DIV/0!' | '#VALUE!' | '#NAME?' | '#NUM!' | '#N/A' | '#PARSE!' | '#SPILL!' | '#CALC!'
|
|
8
|
+
/** Excel's empty intersection. Nothing here produces it; a file can
|
|
9
|
+
* carry it, and a formula can name it, so it is one of the values. */
|
|
10
|
+
| '#NULL!';
|
|
8
11
|
/** A computed cell value. Errors are values, not exceptions, once evaluation
|
|
9
12
|
* finishes - that is what lets `=IFERROR(A1, 0)` see one. */
|
|
10
13
|
export type CellValue = string | number | boolean | {
|
|
@@ -16,6 +19,10 @@ export declare function isError(v: CellValue): v is {
|
|
|
16
19
|
export declare function err(code: SheetError): {
|
|
17
20
|
error: SheetError;
|
|
18
21
|
};
|
|
22
|
+
/** The error a typed text stands for, or null when it is ordinary text. */
|
|
23
|
+
export declare function typedError(text: string): {
|
|
24
|
+
error: SheetError;
|
|
25
|
+
} | null;
|
|
19
26
|
/** Thrown during evaluation and caught at the boundary, where it becomes a
|
|
20
27
|
* value. Carrying the code on a real Error keeps stack traces useful in dev
|
|
21
28
|
* without the evaluator having to thread a result type through every branch. */
|
|
@@ -33,12 +40,30 @@ export type Node = {
|
|
|
33
40
|
k: 'bool';
|
|
34
41
|
v: boolean;
|
|
35
42
|
}
|
|
43
|
+
/** An error written INTO a formula: `=#N/A`, `=IFERROR(A1, #N/A)`. Excel
|
|
44
|
+
* takes one as a value, and a file from Excel, Google Sheets or
|
|
45
|
+
* LibreOffice can carry an error cell as a formula that is only this. */
|
|
46
|
+
| {
|
|
47
|
+
k: 'err';
|
|
48
|
+
v: SheetError;
|
|
49
|
+
}
|
|
36
50
|
/** A single cell. `ref.row` is never null here; a column-only reference
|
|
37
51
|
* parses as a range instead. */
|
|
38
52
|
| {
|
|
39
53
|
k: 'ref';
|
|
40
54
|
ref: CellRef;
|
|
41
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The spilled-range operator, `A1#`: the rectangle a dynamic array
|
|
58
|
+
* anchored at `ref` currently spills into. Like a table reference, it is
|
|
59
|
+
* NOT resolved to a range here - the rectangle depends on how far the
|
|
60
|
+
* array spills right now, which changes as the array grows, so it is
|
|
61
|
+
* resolved against the workbook at evaluation. `ref.row` is never null.
|
|
62
|
+
*/
|
|
63
|
+
| {
|
|
64
|
+
k: 'spill';
|
|
65
|
+
ref: CellRef;
|
|
66
|
+
}
|
|
42
67
|
/** A rectangle. Either end's row may be null, meaning a whole-column ref
|
|
43
68
|
* that runs to the last used row of the sheet. */
|
|
44
69
|
| {
|
|
@@ -46,6 +71,19 @@ export type Node = {
|
|
|
46
71
|
from: CellRef;
|
|
47
72
|
to: CellRef;
|
|
48
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* A 3D reference: `Sheet1:Sheet3!A1` or `Jan:Dec!B5:B10`. The same cell or
|
|
76
|
+
* rectangle on every sheet from `sheetFrom` to `sheetTo` in tab order,
|
|
77
|
+
* inclusive. `from`/`to` carry no sheet of their own (the sheets are the
|
|
78
|
+
* range); resolved against the workbook's sheet order at evaluation, so a
|
|
79
|
+
* tab inserted between the two joins the sum, as Excel's does. */
|
|
80
|
+
| {
|
|
81
|
+
k: 'ref3d';
|
|
82
|
+
sheetFrom: string;
|
|
83
|
+
sheetTo: string;
|
|
84
|
+
from: CellRef;
|
|
85
|
+
to: CellRef;
|
|
86
|
+
}
|
|
49
87
|
/** A defined name (`=Tax`), resolved against the workbook at evaluation. */
|
|
50
88
|
| {
|
|
51
89
|
k: 'name';
|
|
@@ -83,6 +121,12 @@ export type Node = {
|
|
|
83
121
|
k: 'fn';
|
|
84
122
|
name: string;
|
|
85
123
|
args: Node[];
|
|
124
|
+
}
|
|
125
|
+
/** An omitted argument: the gap in `PMT(rate, nper, pv, , 1)`. Reads as a
|
|
126
|
+
* blank, so a function sees its default the way it does when the
|
|
127
|
+
* argument is simply left off the end. */
|
|
128
|
+
| {
|
|
129
|
+
k: 'empty';
|
|
86
130
|
};
|
|
87
131
|
export type BinaryOp = '+' | '-' | '*' | '/' | '^' | '&' | '=' | '<>' | '<' | '>' | '<=' | '>=';
|
|
88
132
|
/** Binding power. Comparison loosest, then concat, then arithmetic. */
|
package/dist/sheet/ast.js
CHANGED
|
@@ -4,6 +4,20 @@ export function isError(v) {
|
|
|
4
4
|
export function err(code) {
|
|
5
5
|
return { error: code };
|
|
6
6
|
}
|
|
7
|
+
/** The error codes a user can type into a cell. Excel stores what is typed
|
|
8
|
+
* as the error itself, so =ISNA(A1) over a cell holding #N/A is TRUE.
|
|
9
|
+
* #PARSE! is ours, not Excel's, and no one types it. */
|
|
10
|
+
const TYPED_ERRORS = [
|
|
11
|
+
'#REF!', '#DIV/0!', '#VALUE!', '#NAME?', '#NUM!', '#N/A', '#SPILL!', '#CALC!', '#CYCLE!', '#NULL!',
|
|
12
|
+
];
|
|
13
|
+
/** The error a typed text stands for, or null when it is ordinary text. */
|
|
14
|
+
export function typedError(text) {
|
|
15
|
+
if (!text.startsWith('#'))
|
|
16
|
+
return null;
|
|
17
|
+
const upper = text.toUpperCase();
|
|
18
|
+
const found = TYPED_ERRORS.find((code) => code === upper);
|
|
19
|
+
return found ? { error: found } : null;
|
|
20
|
+
}
|
|
7
21
|
/** Thrown during evaluation and caught at the boundary, where it becomes a
|
|
8
22
|
* value. Carrying the code on a real Error keeps stack traces useful in dev
|
|
9
23
|
* without the evaluator having to thread a result type through every branch. */
|
|
@@ -20,14 +20,47 @@ export type FilterCondition = {
|
|
|
20
20
|
value?: string;
|
|
21
21
|
valueTo?: string;
|
|
22
22
|
};
|
|
23
|
+
/** Excel's Date Filters: a period around today, or a bound typed as a date. */
|
|
24
|
+
export type DatePeriod = 'equals' | 'before' | 'after' | 'between' | 'tomorrow' | 'today' | 'yesterday' | 'nextWeek' | 'thisWeek' | 'lastWeek' | 'nextMonth' | 'thisMonth' | 'lastMonth' | 'nextQuarter' | 'thisQuarter' | 'lastQuarter' | 'nextYear' | 'thisYear' | 'lastYear' | 'yearToDate';
|
|
23
25
|
export type ColumnFilter = {
|
|
24
|
-
kind: 'values';
|
|
26
|
+
kind: 'values';
|
|
27
|
+
/** Display texts left unticked; '' stands for the blanks. */
|
|
25
28
|
excluded: string[];
|
|
29
|
+
/** The texts ticked, from a file that lists what is IN (Excel and ODF
|
|
30
|
+
* both do): wins over `excluded` while set. The menu writes the
|
|
31
|
+
* exclusion form back the next time the filter is applied. */
|
|
32
|
+
included?: string[];
|
|
26
33
|
} | {
|
|
27
34
|
kind: 'condition';
|
|
28
35
|
first: FilterCondition;
|
|
29
36
|
join?: 'and' | 'or';
|
|
30
37
|
second?: FilterCondition;
|
|
38
|
+
}
|
|
39
|
+
/** A date period; `value` and `valueTo` are `yyyy-mm-dd` for the typed bounds. */
|
|
40
|
+
| {
|
|
41
|
+
kind: 'date';
|
|
42
|
+
period: DatePeriod;
|
|
43
|
+
value?: string;
|
|
44
|
+
valueTo?: string;
|
|
45
|
+
}
|
|
46
|
+
/** Excel's Filter by Color: the cell's fill, or null for the cells with none. */
|
|
47
|
+
| {
|
|
48
|
+
kind: 'color';
|
|
49
|
+
fill: string | null;
|
|
50
|
+
}
|
|
51
|
+
/** Excel's Top 10: the `count` largest (or smallest) values, or that percent of the rows. */
|
|
52
|
+
| {
|
|
53
|
+
kind: 'top';
|
|
54
|
+
top: boolean;
|
|
55
|
+
count: number;
|
|
56
|
+
percent?: boolean;
|
|
57
|
+
};
|
|
58
|
+
/** What a filter reads beside the cell's value and text. */
|
|
59
|
+
export type FilterContext = {
|
|
60
|
+
/** The cell's fill, its own or a conditional format's; null when none. */
|
|
61
|
+
fillAt?: (r: number, c: number) => string | null;
|
|
62
|
+
/** The day the date periods count from. */
|
|
63
|
+
today?: Date;
|
|
31
64
|
};
|
|
32
65
|
export type AutoFilterState = {
|
|
33
66
|
/** The region, header row first. */
|
|
@@ -46,14 +79,35 @@ export type FilterValue = {
|
|
|
46
79
|
* rows other columns' filters hide, as Excel's list does.
|
|
47
80
|
*/
|
|
48
81
|
export declare function distinctValues(state: AutoFilterState, col: number, valueAt: (r: number, c: number) => CellValue, displayAt: (r: number, c: number) => string, rowVisible?: (r: number) => boolean): FilterValue[];
|
|
49
|
-
/**
|
|
50
|
-
|
|
82
|
+
/**
|
|
83
|
+
* The first and last day of a period, as UTC day-start milliseconds,
|
|
84
|
+
* counted from `today`. Weeks run Sunday to Saturday; quarters are the
|
|
85
|
+
* calendar's. The typed periods read `value` and `valueTo`.
|
|
86
|
+
*/
|
|
87
|
+
export declare function datePeriodBounds(filter: Extract<ColumnFilter, {
|
|
88
|
+
kind: 'date';
|
|
89
|
+
}>, today?: Date): [from: number, to: number] | null;
|
|
90
|
+
/**
|
|
91
|
+
* Whether a row passes one column's filter. A Top 10 filter needs the
|
|
92
|
+
* whole column to know its threshold, which `hiddenRowsFor` works out and
|
|
93
|
+
* passes as `topPasses`; on its own the filter passes everything.
|
|
94
|
+
*/
|
|
95
|
+
export declare function passesFilter(filter: ColumnFilter, value: CellValue, display: string, extra?: {
|
|
96
|
+
fill?: string | null;
|
|
97
|
+
today?: Date;
|
|
98
|
+
topPasses?: (value: CellValue) => boolean;
|
|
99
|
+
}): boolean;
|
|
51
100
|
/**
|
|
52
101
|
* The rows the state hides: every row of the region under the header
|
|
53
102
|
* that fails a column's filter. Blanks past the region's edge are not in
|
|
54
103
|
* it, as Excel's region is what was filtered.
|
|
55
104
|
*/
|
|
56
|
-
export declare function hiddenRowsFor(state: AutoFilterState | null, valueAt: (r: number, c: number) => CellValue, displayAt: (r: number, c: number) => string): Set<number>;
|
|
105
|
+
export declare function hiddenRowsFor(state: AutoFilterState | null, valueAt: (r: number, c: number) => CellValue, displayAt: (r: number, c: number) => string, ctx?: FilterContext): Set<number>;
|
|
106
|
+
/** The fills a column's cells carry, each once, first seen first; null
|
|
107
|
+
* stands for the cells with none. Excel's Filter by Color list. */
|
|
108
|
+
export declare function distinctFills(state: AutoFilterState, col: number, fillAt: (r: number, c: number) => string | null): Array<string | null>;
|
|
109
|
+
/** Whether a column reads as dates: every cell with something in it holds one. */
|
|
110
|
+
export declare function isDateColumn(values: ReadonlyArray<FilterValue>): boolean;
|
|
57
111
|
/** Whether any column is filtered. */
|
|
58
112
|
export declare function isFiltering(state: AutoFilterState | null): boolean;
|
|
59
113
|
/** The state with one column's filter set, or cleared with null. */
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { compileExcelFilter } from '@svgrid/grid/filtering';
|
|
15
15
|
import { isError } from './ast.js';
|
|
16
16
|
import { shiftRect, lineShift } from './rects.js';
|
|
17
|
+
import { dateValue } from './validation.js';
|
|
17
18
|
/** The operators that compare as numbers; the rest compare the display text. */
|
|
18
19
|
const NUMERIC_OPS = new Set(['greaterThan', 'lessThan', 'between']);
|
|
19
20
|
/**
|
|
@@ -60,31 +61,139 @@ function passesCondition(cond, value, display) {
|
|
|
60
61
|
return test(display === '' ? null : display);
|
|
61
62
|
return test(isError(value) ? value.error : display);
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
const DAY_MS = 86400000;
|
|
65
|
+
const dayStart = (d) => Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());
|
|
66
|
+
/**
|
|
67
|
+
* The first and last day of a period, as UTC day-start milliseconds,
|
|
68
|
+
* counted from `today`. Weeks run Sunday to Saturday; quarters are the
|
|
69
|
+
* calendar's. The typed periods read `value` and `valueTo`.
|
|
70
|
+
*/
|
|
71
|
+
export function datePeriodBounds(filter, today = new Date()) {
|
|
72
|
+
const t = dayStart(today);
|
|
73
|
+
const y = today.getUTCFullYear();
|
|
74
|
+
const m = today.getUTCMonth();
|
|
75
|
+
const q = Math.floor(m / 3);
|
|
76
|
+
const sunday = t - today.getUTCDay() * DAY_MS;
|
|
77
|
+
const month = (yy, mm) => [Date.UTC(yy, mm, 1), Date.UTC(yy, mm + 1, 0)];
|
|
78
|
+
const quarter = (yy, qq) => [Date.UTC(yy, qq * 3, 1), Date.UTC(yy, qq * 3 + 3, 0)];
|
|
79
|
+
const typed = (text) => {
|
|
80
|
+
if (!text)
|
|
81
|
+
return null;
|
|
82
|
+
const ms = dateValue(text.trim());
|
|
83
|
+
return ms === null ? null : dayStart(new Date(ms));
|
|
84
|
+
};
|
|
85
|
+
switch (filter.period) {
|
|
86
|
+
case 'today': return [t, t];
|
|
87
|
+
case 'yesterday': return [t - DAY_MS, t - DAY_MS];
|
|
88
|
+
case 'tomorrow': return [t + DAY_MS, t + DAY_MS];
|
|
89
|
+
case 'thisWeek': return [sunday, sunday + 6 * DAY_MS];
|
|
90
|
+
case 'lastWeek': return [sunday - 7 * DAY_MS, sunday - DAY_MS];
|
|
91
|
+
case 'nextWeek': return [sunday + 7 * DAY_MS, sunday + 13 * DAY_MS];
|
|
92
|
+
case 'thisMonth': return month(y, m);
|
|
93
|
+
case 'lastMonth': return month(y, m - 1);
|
|
94
|
+
case 'nextMonth': return month(y, m + 1);
|
|
95
|
+
case 'thisQuarter': return quarter(y, q);
|
|
96
|
+
case 'lastQuarter': return q === 0 ? quarter(y - 1, 3) : quarter(y, q - 1);
|
|
97
|
+
case 'nextQuarter': return q === 3 ? quarter(y + 1, 0) : quarter(y, q + 1);
|
|
98
|
+
case 'thisYear': return [Date.UTC(y, 0, 1), Date.UTC(y, 11, 31)];
|
|
99
|
+
case 'lastYear': return [Date.UTC(y - 1, 0, 1), Date.UTC(y - 1, 11, 31)];
|
|
100
|
+
case 'nextYear': return [Date.UTC(y + 1, 0, 1), Date.UTC(y + 1, 11, 31)];
|
|
101
|
+
case 'yearToDate': return [Date.UTC(y, 0, 1), t];
|
|
102
|
+
case 'equals': {
|
|
103
|
+
const d = typed(filter.value);
|
|
104
|
+
return d === null ? null : [d, d];
|
|
105
|
+
}
|
|
106
|
+
case 'before': {
|
|
107
|
+
const d = typed(filter.value);
|
|
108
|
+
return d === null ? null : [-Infinity, d - DAY_MS];
|
|
109
|
+
}
|
|
110
|
+
case 'after': {
|
|
111
|
+
const d = typed(filter.value);
|
|
112
|
+
return d === null ? null : [d + DAY_MS, Infinity];
|
|
113
|
+
}
|
|
114
|
+
case 'between': {
|
|
115
|
+
const a = typed(filter.value);
|
|
116
|
+
const b = typed(filter.valueTo);
|
|
117
|
+
return a === null || b === null ? null : [Math.min(a, b), Math.max(a, b)];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const sameFill = (a, b) => (a ?? '').trim().toLowerCase() === (b ?? '').trim().toLowerCase();
|
|
122
|
+
/**
|
|
123
|
+
* Whether a row passes one column's filter. A Top 10 filter needs the
|
|
124
|
+
* whole column to know its threshold, which `hiddenRowsFor` works out and
|
|
125
|
+
* passes as `topPasses`; on its own the filter passes everything.
|
|
126
|
+
*/
|
|
127
|
+
export function passesFilter(filter, value, display, extra = {}) {
|
|
128
|
+
switch (filter.kind) {
|
|
129
|
+
case 'values': return filter.included ? filter.included.includes(display) : !filter.excluded.includes(display);
|
|
130
|
+
case 'date': {
|
|
131
|
+
const bounds = datePeriodBounds(filter, extra.today);
|
|
132
|
+
if (!bounds)
|
|
133
|
+
return true;
|
|
134
|
+
const ms = dateValue(value);
|
|
135
|
+
if (ms === null)
|
|
136
|
+
return false;
|
|
137
|
+
const day = dayStart(new Date(ms));
|
|
138
|
+
return day >= bounds[0] && day <= bounds[1];
|
|
139
|
+
}
|
|
140
|
+
case 'color': return sameFill(extra.fill ?? null, filter.fill);
|
|
141
|
+
case 'top': return extra.topPasses ? extra.topPasses(value) : true;
|
|
142
|
+
default: {
|
|
143
|
+
const first = passesCondition(filter.first, value, display);
|
|
144
|
+
if (!filter.second)
|
|
145
|
+
return first;
|
|
146
|
+
const second = passesCondition(filter.second, value, display);
|
|
147
|
+
return filter.join === 'or' ? first || second : first && second;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** The test a Top 10 filter applies once the column's numbers are known:
|
|
152
|
+
* the values at or past the threshold, counted over the whole region as
|
|
153
|
+
* Excel counts them, whatever the other columns hide. */
|
|
154
|
+
function topPassesFor(filter, numbers) {
|
|
155
|
+
if (!numbers.length)
|
|
156
|
+
return () => false;
|
|
157
|
+
const sorted = numbers.slice().sort((a, b) => (filter.top ? b - a : a - b));
|
|
158
|
+
const count = filter.percent ? Math.round((filter.count / 100) * sorted.length) : filter.count;
|
|
159
|
+
const keep = Math.max(0, Math.min(sorted.length, Math.trunc(count)));
|
|
160
|
+
if (keep === 0)
|
|
161
|
+
return () => false;
|
|
162
|
+
const threshold = sorted[keep - 1];
|
|
163
|
+
return (value) => typeof value === 'number' && Number.isFinite(value) && (filter.top ? value >= threshold : value <= threshold);
|
|
72
164
|
}
|
|
73
165
|
/**
|
|
74
166
|
* The rows the state hides: every row of the region under the header
|
|
75
167
|
* that fails a column's filter. Blanks past the region's edge are not in
|
|
76
168
|
* it, as Excel's region is what was filtered.
|
|
77
169
|
*/
|
|
78
|
-
export function hiddenRowsFor(state, valueAt, displayAt) {
|
|
170
|
+
export function hiddenRowsFor(state, valueAt, displayAt, ctx = {}) {
|
|
79
171
|
const out = new Set();
|
|
80
172
|
if (!state)
|
|
81
173
|
return out;
|
|
82
174
|
const columns = Object.entries(state.filters).map(([c, f]) => [Number(c), f]);
|
|
83
175
|
if (!columns.length)
|
|
84
176
|
return out;
|
|
177
|
+
const tops = new Map();
|
|
178
|
+
for (const [c, filter] of columns) {
|
|
179
|
+
if (filter.kind !== 'top')
|
|
180
|
+
continue;
|
|
181
|
+
const numbers = [];
|
|
182
|
+
for (let r = state.range[0] + 1; r <= state.range[2]; r += 1) {
|
|
183
|
+
const v = valueAt(r, c);
|
|
184
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
185
|
+
numbers.push(v);
|
|
186
|
+
}
|
|
187
|
+
tops.set(c, topPassesFor(filter, numbers));
|
|
188
|
+
}
|
|
85
189
|
for (let r = state.range[0] + 1; r <= state.range[2]; r += 1) {
|
|
86
190
|
for (const [c, filter] of columns) {
|
|
87
|
-
|
|
191
|
+
const extra = {
|
|
192
|
+
today: ctx.today,
|
|
193
|
+
fill: filter.kind === 'color' ? ctx.fillAt?.(r, c) ?? null : undefined,
|
|
194
|
+
topPasses: tops.get(c),
|
|
195
|
+
};
|
|
196
|
+
if (!passesFilter(filter, valueAt(r, c), displayAt(r, c), extra)) {
|
|
88
197
|
out.add(r);
|
|
89
198
|
break;
|
|
90
199
|
}
|
|
@@ -92,6 +201,23 @@ export function hiddenRowsFor(state, valueAt, displayAt) {
|
|
|
92
201
|
}
|
|
93
202
|
return out;
|
|
94
203
|
}
|
|
204
|
+
/** The fills a column's cells carry, each once, first seen first; null
|
|
205
|
+
* stands for the cells with none. Excel's Filter by Color list. */
|
|
206
|
+
export function distinctFills(state, col, fillAt) {
|
|
207
|
+
const seen = [];
|
|
208
|
+
for (let r = state.range[0] + 1; r <= state.range[2]; r += 1) {
|
|
209
|
+
const fill = fillAt(r, col);
|
|
210
|
+
const key = fill ? fill.trim().toLowerCase() : null;
|
|
211
|
+
if (!seen.some((f) => sameFill(f, key)))
|
|
212
|
+
seen.push(key);
|
|
213
|
+
}
|
|
214
|
+
return seen;
|
|
215
|
+
}
|
|
216
|
+
/** Whether a column reads as dates: every cell with something in it holds one. */
|
|
217
|
+
export function isDateColumn(values) {
|
|
218
|
+
const filled = values.filter((v) => v.text !== '');
|
|
219
|
+
return filled.length > 0 && filled.every((v) => v.numeric === null && dateValue(v.text) !== null);
|
|
220
|
+
}
|
|
95
221
|
/** Whether any column is filtered. */
|
|
96
222
|
export function isFiltering(state) {
|
|
97
223
|
return !!state && Object.keys(state.filters).length > 0;
|
|
@@ -133,10 +259,32 @@ const OP_WORDS = {
|
|
|
133
259
|
between: 'is between', contains: 'contains', notContains: 'does not contain', startsWith: 'begins with',
|
|
134
260
|
endsWith: 'ends with', isBlank: 'is blank', isNotBlank: 'is not blank',
|
|
135
261
|
};
|
|
262
|
+
const PERIOD_WORDS = {
|
|
263
|
+
equals: 'equals', before: 'is before', after: 'is after', between: 'is between',
|
|
264
|
+
tomorrow: 'tomorrow', today: 'today', yesterday: 'yesterday',
|
|
265
|
+
nextWeek: 'next week', thisWeek: 'this week', lastWeek: 'last week',
|
|
266
|
+
nextMonth: 'next month', thisMonth: 'this month', lastMonth: 'last month',
|
|
267
|
+
nextQuarter: 'next quarter', thisQuarter: 'this quarter', lastQuarter: 'last quarter',
|
|
268
|
+
nextYear: 'next year', thisYear: 'this year', lastYear: 'last year', yearToDate: 'year to date',
|
|
269
|
+
};
|
|
136
270
|
/** A filter in words, for the menu's heading. */
|
|
137
271
|
export function describeFilter(filter) {
|
|
272
|
+
if (filter.kind === 'values' && filter.included)
|
|
273
|
+
return filter.included.length === 1 ? '1 value shown' : `${filter.included.length} values shown`;
|
|
138
274
|
if (filter.kind === 'values')
|
|
139
275
|
return filter.excluded.length === 1 ? '1 value hidden' : `${filter.excluded.length} values hidden`;
|
|
276
|
+
if (filter.kind === 'date') {
|
|
277
|
+
const word = PERIOD_WORDS[filter.period];
|
|
278
|
+
if (filter.period === 'between')
|
|
279
|
+
return `${word} ${filter.value ?? ''} and ${filter.valueTo ?? ''}`;
|
|
280
|
+
if (filter.period === 'equals' || filter.period === 'before' || filter.period === 'after')
|
|
281
|
+
return `${word} ${filter.value ?? ''}`;
|
|
282
|
+
return word;
|
|
283
|
+
}
|
|
284
|
+
if (filter.kind === 'color')
|
|
285
|
+
return filter.fill ? `filled ${filter.fill}` : 'no fill';
|
|
286
|
+
if (filter.kind === 'top')
|
|
287
|
+
return `${filter.top ? 'top' : 'bottom'} ${filter.count}${filter.percent ? ' percent' : ''}`;
|
|
140
288
|
const one = (c) => {
|
|
141
289
|
const word = OP_WORDS[c.op] ?? c.op;
|
|
142
290
|
if (c.op === 'isBlank' || c.op === 'isNotBlank')
|
|
@@ -5,6 +5,15 @@ export type FunctionSuggestion = {
|
|
|
5
5
|
/** Where the caret is, i.e. where the partial name ends. */
|
|
6
6
|
end: number;
|
|
7
7
|
};
|
|
8
|
+
/**
|
|
9
|
+
* The evaluator dispatches these itself, so they are not in the table.
|
|
10
|
+
*
|
|
11
|
+
* Exported because `function-catalog.ts` needs the same list: it kept a
|
|
12
|
+
* copy of its own, the copy fell behind this one, and Insert Function
|
|
13
|
+
* quietly stopped listing LET, LAMBDA, the lambda helpers and SUBTOTAL
|
|
14
|
+
* even though it carried written descriptions for them. One list now.
|
|
15
|
+
*/
|
|
16
|
+
export declare const EVALUATOR_NAMES: string[];
|
|
8
17
|
/** Signature hints, for the ones whose argument order is not obvious. */
|
|
9
18
|
export declare const SIGNATURES: Record<string, string>;
|
|
10
19
|
/**
|
|
@@ -8,16 +8,40 @@
|
|
|
8
8
|
* with an index, which the component owns.
|
|
9
9
|
*/
|
|
10
10
|
import { FUNCTIONS } from './functions.js';
|
|
11
|
-
|
|
11
|
+
import { ARRAY_FUNCTIONS } from './packs/array.js';
|
|
12
|
+
/**
|
|
13
|
+
* The evaluator dispatches these itself, so they are not in the table.
|
|
14
|
+
*
|
|
15
|
+
* Exported because `function-catalog.ts` needs the same list: it kept a
|
|
16
|
+
* copy of its own, the copy fell behind this one, and Insert Function
|
|
17
|
+
* quietly stopped listing LET, LAMBDA, the lambda helpers and SUBTOTAL
|
|
18
|
+
* even though it carried written descriptions for them. One list now.
|
|
19
|
+
*/
|
|
20
|
+
export const EVALUATOR_NAMES = ['IF', 'IFS', 'IFERROR', 'IFNA', 'SWITCH', 'ISERROR', 'ISERR', 'ISNA', 'ROW', 'COLUMN', 'ADDRESS', 'OFFSET', 'INDIRECT', 'SUBTOTAL',
|
|
21
|
+
// LET, LAMBDA and the helpers that take one: the evaluator answers these
|
|
22
|
+
// itself, since a lambda is an AST rather than a value.
|
|
23
|
+
'LET', 'LAMBDA', 'MAP', 'BYROW', 'BYCOL', 'REDUCE', 'SCAN', 'MAKEARRAY'];
|
|
24
|
+
const NAMES = [...new Set([...Object.keys(FUNCTIONS), ...Object.keys(ARRAY_FUNCTIONS), ...EVALUATOR_NAMES])].sort();
|
|
12
25
|
/** Signature hints, for the ones whose argument order is not obvious. */
|
|
13
26
|
export const SIGNATURES = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
LET: 'LET(name, value, ..., calculation)',
|
|
28
|
+
LAMBDA: 'LAMBDA(parameter, ..., calculation)',
|
|
29
|
+
MAP: 'MAP(array, ..., lambda)',
|
|
30
|
+
BYROW: 'BYROW(array, lambda)',
|
|
31
|
+
BYCOL: 'BYCOL(array, lambda)',
|
|
32
|
+
REDUCE: 'REDUCE(initial, array, lambda)',
|
|
33
|
+
SCAN: 'SCAN(initial, array, lambda)',
|
|
34
|
+
MAKEARRAY: 'MAKEARRAY(rows, columns, lambda)',
|
|
35
|
+
VLOOKUP: 'VLOOKUP(lookup, table, colIndex, [rangeLookup])',
|
|
36
|
+
HLOOKUP: 'HLOOKUP(lookup, table, rowIndex, [rangeLookup])',
|
|
37
|
+
XLOOKUP: 'XLOOKUP(lookup, haystack, results, [ifMissing], [matchMode], [searchMode])',
|
|
17
38
|
INDEX: 'INDEX(range, row, [col])',
|
|
18
39
|
MATCH: 'MATCH(lookup, range, [mode])',
|
|
19
40
|
SUMIF: 'SUMIF(range, criterion, [sumRange])',
|
|
20
41
|
SUMIFS: 'SUMIFS(sumRange, range, criterion, ...)',
|
|
42
|
+
SUBTOTAL: 'SUBTOTAL(code, range, ...)',
|
|
43
|
+
TRUE: 'TRUE()',
|
|
44
|
+
FALSE: 'FALSE()',
|
|
21
45
|
COUNTIF: 'COUNTIF(range, criterion)',
|
|
22
46
|
COUNTIFS: 'COUNTIFS(range, criterion, ...)',
|
|
23
47
|
AVERAGEIF: 'AVERAGEIF(range, criterion, [avgRange])',
|
|
@@ -32,6 +56,68 @@ export const SIGNATURES = {
|
|
|
32
56
|
MID: 'MID(text, start, length)',
|
|
33
57
|
DATE: 'DATE(year, month, day)',
|
|
34
58
|
EOMONTH: 'EOMONTH(date, months)',
|
|
59
|
+
PMT: 'PMT(rate, nper, pv, [fv], [type])',
|
|
60
|
+
IPMT: 'IPMT(rate, per, nper, pv, [fv], [type])',
|
|
61
|
+
PPMT: 'PPMT(rate, per, nper, pv, [fv], [type])',
|
|
62
|
+
PV: 'PV(rate, nper, pmt, [fv], [type])',
|
|
63
|
+
FV: 'FV(rate, nper, pmt, [pv], [type])',
|
|
64
|
+
NPER: 'NPER(rate, pmt, pv, [fv], [type])',
|
|
65
|
+
RATE: 'RATE(nper, pmt, pv, [fv], [type], [guess])',
|
|
66
|
+
NPV: 'NPV(rate, value1, ...)',
|
|
67
|
+
IRR: 'IRR(values, [guess])',
|
|
68
|
+
SLN: 'SLN(cost, salvage, life)',
|
|
69
|
+
SUMPRODUCT: 'SUMPRODUCT(array1, [array2], ...)',
|
|
70
|
+
CEILING: 'CEILING(number, significance)',
|
|
71
|
+
'CEILING.MATH': 'CEILING.MATH(number, [significance], [mode])',
|
|
72
|
+
FLOOR: 'FLOOR(number, significance)',
|
|
73
|
+
'FLOOR.MATH': 'FLOOR.MATH(number, [significance], [mode])',
|
|
74
|
+
MROUND: 'MROUND(number, multiple)',
|
|
75
|
+
TRUNC: 'TRUNC(number, [digits])',
|
|
76
|
+
LOG: 'LOG(number, [base])',
|
|
77
|
+
RANDBETWEEN: 'RANDBETWEEN(bottom, top)',
|
|
78
|
+
QUOTIENT: 'QUOTIENT(numerator, denominator)',
|
|
79
|
+
LARGE: 'LARGE(range, k)',
|
|
80
|
+
SMALL: 'SMALL(range, k)',
|
|
81
|
+
PERCENTILE: 'PERCENTILE(range, k)',
|
|
82
|
+
'PERCENTILE.INC': 'PERCENTILE.INC(range, k)',
|
|
83
|
+
'PERCENTILE.EXC': 'PERCENTILE.EXC(range, k)',
|
|
84
|
+
QUARTILE: 'QUARTILE(range, quart)',
|
|
85
|
+
'QUARTILE.INC': 'QUARTILE.INC(range, quart)',
|
|
86
|
+
'QUARTILE.EXC': 'QUARTILE.EXC(range, quart)',
|
|
87
|
+
AVERAGEIFS: 'AVERAGEIFS(avgRange, range, criterion, ...)',
|
|
88
|
+
MAXIFS: 'MAXIFS(maxRange, range, criterion, ...)',
|
|
89
|
+
MINIFS: 'MINIFS(minRange, range, criterion, ...)',
|
|
90
|
+
CORREL: 'CORREL(range1, range2)',
|
|
91
|
+
SLOPE: 'SLOPE(known_ys, known_xs)',
|
|
92
|
+
INTERCEPT: 'INTERCEPT(known_ys, known_xs)',
|
|
93
|
+
FORECAST: 'FORECAST(x, known_ys, known_xs)',
|
|
94
|
+
'FORECAST.LINEAR': 'FORECAST.LINEAR(x, known_ys, known_xs)',
|
|
95
|
+
REPT: 'REPT(text, count)',
|
|
96
|
+
IMAGE: 'IMAGE(source, [alt_text])',
|
|
97
|
+
NUMBERVALUE: 'NUMBERVALUE(text, [decimal_separator], [group_separator])',
|
|
98
|
+
REPLACE: 'REPLACE(old_text, start, count, new_text)',
|
|
99
|
+
EXACT: 'EXACT(text1, text2)',
|
|
100
|
+
WEEKDAY: 'WEEKDAY(date, [type])',
|
|
101
|
+
EDATE: 'EDATE(date, months)',
|
|
102
|
+
NETWORKDAYS: 'NETWORKDAYS(start_date, end_date, [holidays])',
|
|
103
|
+
WORKDAY: 'WORKDAY(start_date, days, [holidays])',
|
|
104
|
+
WEEKNUM: 'WEEKNUM(date, [type])',
|
|
105
|
+
TIME: 'TIME(hour, minute, second)',
|
|
106
|
+
YEARFRAC: 'YEARFRAC(start_date, end_date, [basis])',
|
|
107
|
+
NA: 'NA()',
|
|
108
|
+
CHOOSE: 'CHOOSE(index, value1, ...)',
|
|
109
|
+
ROW: 'ROW([reference])',
|
|
110
|
+
COLUMN: 'COLUMN([reference])',
|
|
111
|
+
ADDRESS: 'ADDRESS(row, column, [abs], [a1], [sheet])',
|
|
112
|
+
OFFSET: 'OFFSET(reference, rows, cols, [height], [width])',
|
|
113
|
+
INDIRECT: 'INDIRECT(text, [a1])',
|
|
114
|
+
FILTER: 'FILTER(array, include, [if_empty])',
|
|
115
|
+
UNIQUE: 'UNIQUE(array, [by_col], [exactly_once])',
|
|
116
|
+
SORT: 'SORT(array, [sort_index], [sort_order], [by_col])',
|
|
117
|
+
SORTBY: 'SORTBY(array, by_array1, [sort_order1], ...)',
|
|
118
|
+
SEQUENCE: 'SEQUENCE(rows, [columns], [start], [step])',
|
|
119
|
+
TRANSPOSE: 'TRANSPOSE(array)',
|
|
120
|
+
TEXTSPLIT: 'TEXTSPLIT(text, col_delimiter, [row_delimiter], [ignore_empty])',
|
|
35
121
|
};
|
|
36
122
|
/**
|
|
37
123
|
* The word being typed immediately before the caret, if it could be a function
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel's `IMAGE` function: a picture IN a cell rather than floating over
|
|
3
|
+
* it.
|
|
4
|
+
*
|
|
5
|
+
* The difference is the whole point. A picture object hangs from a cell and
|
|
6
|
+
* covers whatever is under it; an `IMAGE` cell IS the picture, so it sorts
|
|
7
|
+
* with its row, filters with it, copies as a formula and moves when the
|
|
8
|
+
* cells move, with nothing to keep in step. A catalogue with a thumbnail
|
|
9
|
+
* column wants this one.
|
|
10
|
+
*
|
|
11
|
+
* The function answers with the source, which is what makes `=IMAGE(A2)`
|
|
12
|
+
* read like any other formula: the value is text, `ISTEXT` says so, and a
|
|
13
|
+
* cell that reads it gets the address rather than a picture it cannot use.
|
|
14
|
+
* Drawing it is the shell's part, and this is what the shell reads to know
|
|
15
|
+
* a cell is one.
|
|
16
|
+
*/
|
|
17
|
+
/** The arguments of a top-level `=IMAGE(...)`, as written. */
|
|
18
|
+
export type ImageCall = {
|
|
19
|
+
/** The source expression: a quoted URL, a reference, a formula. */
|
|
20
|
+
source: string;
|
|
21
|
+
/** The alt text expression, when the call gives one. */
|
|
22
|
+
alt?: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The call's arguments when a formula IS one `IMAGE(...)` call, or null.
|
|
26
|
+
*
|
|
27
|
+
* Read off the text rather than the parsed tree on purpose: this runs for
|
|
28
|
+
* every cell that paints, and a scan is cheaper than a parse. A formula
|
|
29
|
+
* that merely CONTAINS an IMAGE call (`=IF(A1, IMAGE(B1), "")`) is not one,
|
|
30
|
+
* because its value is not a picture in every case.
|
|
31
|
+
*/
|
|
32
|
+
export declare function imageCall(text: string): ImageCall | null;
|
|
33
|
+
/**
|
|
34
|
+
* Whether a source is one a browser will load. A `data:` URL and an
|
|
35
|
+
* ordinary web address are; anything else (a file path, a `javascript:`
|
|
36
|
+
* scheme) is not, and a cell showing it as text says more than a broken
|
|
37
|
+
* image would.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isDrawableImageSource(source: string): boolean;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excel's `IMAGE` function: a picture IN a cell rather than floating over
|
|
3
|
+
* it.
|
|
4
|
+
*
|
|
5
|
+
* The difference is the whole point. A picture object hangs from a cell and
|
|
6
|
+
* covers whatever is under it; an `IMAGE` cell IS the picture, so it sorts
|
|
7
|
+
* with its row, filters with it, copies as a formula and moves when the
|
|
8
|
+
* cells move, with nothing to keep in step. A catalogue with a thumbnail
|
|
9
|
+
* column wants this one.
|
|
10
|
+
*
|
|
11
|
+
* The function answers with the source, which is what makes `=IMAGE(A2)`
|
|
12
|
+
* read like any other formula: the value is text, `ISTEXT` says so, and a
|
|
13
|
+
* cell that reads it gets the address rather than a picture it cannot use.
|
|
14
|
+
* Drawing it is the shell's part, and this is what the shell reads to know
|
|
15
|
+
* a cell is one.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The call's arguments when a formula IS one `IMAGE(...)` call, or null.
|
|
19
|
+
*
|
|
20
|
+
* Read off the text rather than the parsed tree on purpose: this runs for
|
|
21
|
+
* every cell that paints, and a scan is cheaper than a parse. A formula
|
|
22
|
+
* that merely CONTAINS an IMAGE call (`=IF(A1, IMAGE(B1), "")`) is not one,
|
|
23
|
+
* because its value is not a picture in every case.
|
|
24
|
+
*/
|
|
25
|
+
export function imageCall(text) {
|
|
26
|
+
const trimmed = text.trim();
|
|
27
|
+
const head = /^=\s*IMAGE\s*\(/i.exec(trimmed);
|
|
28
|
+
if (!head)
|
|
29
|
+
return null;
|
|
30
|
+
const inside = trimmed.slice(head[0].length);
|
|
31
|
+
const args = [];
|
|
32
|
+
let depth = 0;
|
|
33
|
+
let quoted = false;
|
|
34
|
+
let start = 0;
|
|
35
|
+
let closed = -1;
|
|
36
|
+
for (let i = 0; i < inside.length; i += 1) {
|
|
37
|
+
const ch = inside[i];
|
|
38
|
+
if (quoted) {
|
|
39
|
+
if (ch === '"') {
|
|
40
|
+
if (inside[i + 1] === '"')
|
|
41
|
+
i += 1;
|
|
42
|
+
else
|
|
43
|
+
quoted = false;
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (ch === '"') {
|
|
48
|
+
quoted = true;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (ch === '(') {
|
|
52
|
+
depth += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (ch === ')') {
|
|
56
|
+
if (depth === 0) {
|
|
57
|
+
args.push(inside.slice(start, i));
|
|
58
|
+
closed = i;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
depth -= 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (ch === ',' && depth === 0) {
|
|
65
|
+
args.push(inside.slice(start, i));
|
|
66
|
+
start = i + 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Anything after the closing bracket means the call is part of a larger
|
|
70
|
+
// formula, so the cell is not simply a picture.
|
|
71
|
+
if (closed < 0 || inside.slice(closed + 1).trim() !== '')
|
|
72
|
+
return null;
|
|
73
|
+
const source = (args[0] ?? '').trim();
|
|
74
|
+
if (!source)
|
|
75
|
+
return null;
|
|
76
|
+
const alt = (args[1] ?? '').trim();
|
|
77
|
+
return { source, ...(alt ? { alt } : {}) };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Whether a source is one a browser will load. A `data:` URL and an
|
|
81
|
+
* ordinary web address are; anything else (a file path, a `javascript:`
|
|
82
|
+
* scheme) is not, and a cell showing it as text says more than a broken
|
|
83
|
+
* image would.
|
|
84
|
+
*/
|
|
85
|
+
export function isDrawableImageSource(source) {
|
|
86
|
+
const value = source.trim();
|
|
87
|
+
if (/^data:image\//i.test(value))
|
|
88
|
+
return true;
|
|
89
|
+
return /^https?:\/\/\S+$/i.test(value);
|
|
90
|
+
}
|