@svgrid/enterprise 3.0.2 → 3.0.3
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-D5bVbuzI.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,58 @@
|
|
|
1
|
+
import type { StructuralEdit } from './refs.js';
|
|
2
|
+
import { type Rect } from './rects.js';
|
|
3
|
+
/** Excel writes a ticked box as TRUE and an unticked one as FALSE. */
|
|
4
|
+
export declare const CHECKED = "TRUE";
|
|
5
|
+
export declare const UNCHECKED = "FALSE";
|
|
6
|
+
export type CellTypeKind = 'checkbox' | 'button' | 'radio';
|
|
7
|
+
export type CellTypeRegion = {
|
|
8
|
+
id: string;
|
|
9
|
+
rects: ReadonlyArray<Rect>;
|
|
10
|
+
kind: CellTypeKind;
|
|
11
|
+
/**
|
|
12
|
+
* A checkbox's two values, when the sheet wants something other than
|
|
13
|
+
* Excel's TRUE and FALSE (a Yes / No column, say). Compared as text,
|
|
14
|
+
* ignoring case, the way the engine compares a boolean typed by hand.
|
|
15
|
+
*/
|
|
16
|
+
checked?: string;
|
|
17
|
+
unchecked?: string;
|
|
18
|
+
/** A button's face. The cell's own text when this is absent. */
|
|
19
|
+
label?: string;
|
|
20
|
+
/** What a button press reports through `onCellAction`. */
|
|
21
|
+
action?: string;
|
|
22
|
+
/** A radio group's choices, in order. */
|
|
23
|
+
choices?: ReadonlyArray<string>;
|
|
24
|
+
};
|
|
25
|
+
export declare const newCellTypeId: () => string;
|
|
26
|
+
/** The region that applies at a cell, or null. The last one wins. */
|
|
27
|
+
export declare function cellTypeAt(regions: ReadonlyArray<CellTypeRegion>, row: number, col: number): CellTypeRegion | null;
|
|
28
|
+
/** The two texts a checkbox toggles between, Excel's by default. */
|
|
29
|
+
export declare const checkboxValues: (region: CellTypeRegion) => {
|
|
30
|
+
on: string;
|
|
31
|
+
off: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Is the cell ticked?
|
|
35
|
+
*
|
|
36
|
+
* Read off the cell's own text so a formula, a paste or a typed TRUE all
|
|
37
|
+
* show the same way. Anything that is not the ticked value reads as
|
|
38
|
+
* unticked, which is what Excel does with a stray entry.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isChecked(region: CellTypeRegion, raw: string): boolean;
|
|
41
|
+
/** What clicking a checkbox should write into its cell. */
|
|
42
|
+
export declare function toggledValue(region: CellTypeRegion, raw: string): string;
|
|
43
|
+
/** Which radio choice a cell holds, or -1. Compared ignoring case. */
|
|
44
|
+
export declare function radioIndex(region: CellTypeRegion, raw: string): number;
|
|
45
|
+
/** The regions after an insert or delete, moved the way the rules move. */
|
|
46
|
+
export declare function shiftCellTypes(regions: ReadonlyArray<CellTypeRegion>, edit: StructuralEdit): CellTypeRegion[];
|
|
47
|
+
/** The regions with the rectangles cut out: what Clear All removes. */
|
|
48
|
+
export declare function removeCellTypes(regions: ReadonlyArray<CellTypeRegion>, rects: ReadonlyArray<Rect>): CellTypeRegion[];
|
|
49
|
+
/**
|
|
50
|
+
* Lay a region over some cells, taking those cells out of any region that
|
|
51
|
+
* already claimed them.
|
|
52
|
+
*
|
|
53
|
+
* Without the cut, two regions would overlap and the later one would win
|
|
54
|
+
* silently; cutting makes the model say what the screen shows.
|
|
55
|
+
*/
|
|
56
|
+
export declare function applyCellType(regions: ReadonlyArray<CellTypeRegion>, region: CellTypeRegion): CellTypeRegion[];
|
|
57
|
+
/** A deep-enough copy for the document's snapshot. */
|
|
58
|
+
export declare const copyCellType: (region: CellTypeRegion) => CellTypeRegion;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a cell IS, rather than what it holds: a checkbox, a button or a
|
|
3
|
+
* group of radio choices drawn in the cell and driven from it.
|
|
4
|
+
*
|
|
5
|
+
* Excel has one of these, the checkbox its Insert tab puts in a cell, and
|
|
6
|
+
* it is deliberately thin: the cell's value is still `TRUE` or `FALSE`, so
|
|
7
|
+
* every formula over it keeps working and the file carries an ordinary
|
|
8
|
+
* boolean. That is the model here, widened to the two other controls a
|
|
9
|
+
* data-entry sheet actually asks for.
|
|
10
|
+
*
|
|
11
|
+
* The rule that keeps this honest: **a cell type is a rendering, never a
|
|
12
|
+
* second source of truth.** A checkbox is ticked because its cell reads
|
|
13
|
+
* TRUE, not because the checkbox remembers being clicked; a radio is on
|
|
14
|
+
* because the cell holds its choice. Clicking one writes the cell and
|
|
15
|
+
* nothing else, so undo, the formula engine, a paste and the file all keep
|
|
16
|
+
* working without knowing this module exists.
|
|
17
|
+
*
|
|
18
|
+
* Regions are kept and moved the way validation rules are, and the LAST
|
|
19
|
+
* region covering a cell wins, so a narrower one laid over a wider one
|
|
20
|
+
* overrides it.
|
|
21
|
+
*/
|
|
22
|
+
import { toText } from './coerce.js';
|
|
23
|
+
import { rectContains, shiftRects, subtractRect } from './rects.js';
|
|
24
|
+
/** Excel writes a ticked box as TRUE and an unticked one as FALSE. */
|
|
25
|
+
export const CHECKED = 'TRUE';
|
|
26
|
+
export const UNCHECKED = 'FALSE';
|
|
27
|
+
let counter = 0;
|
|
28
|
+
export const newCellTypeId = () => `ct${(counter += 1)}`;
|
|
29
|
+
/** The region that applies at a cell, or null. The last one wins. */
|
|
30
|
+
export function cellTypeAt(regions, row, col) {
|
|
31
|
+
for (let i = regions.length - 1; i >= 0; i -= 1) {
|
|
32
|
+
const region = regions[i];
|
|
33
|
+
if (region.rects.some((rect) => rectContains(rect, row, col)))
|
|
34
|
+
return region;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/** The two texts a checkbox toggles between, Excel's by default. */
|
|
39
|
+
export const checkboxValues = (region) => ({
|
|
40
|
+
on: region.checked ?? CHECKED,
|
|
41
|
+
off: region.unchecked ?? UNCHECKED,
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Is the cell ticked?
|
|
45
|
+
*
|
|
46
|
+
* Read off the cell's own text so a formula, a paste or a typed TRUE all
|
|
47
|
+
* show the same way. Anything that is not the ticked value reads as
|
|
48
|
+
* unticked, which is what Excel does with a stray entry.
|
|
49
|
+
*/
|
|
50
|
+
export function isChecked(region, raw) {
|
|
51
|
+
const { on } = checkboxValues(region);
|
|
52
|
+
return toText(raw).trim().toLowerCase() === on.trim().toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
/** What clicking a checkbox should write into its cell. */
|
|
55
|
+
export function toggledValue(region, raw) {
|
|
56
|
+
const { on, off } = checkboxValues(region);
|
|
57
|
+
return isChecked(region, raw) ? off : on;
|
|
58
|
+
}
|
|
59
|
+
/** Which radio choice a cell holds, or -1. Compared ignoring case. */
|
|
60
|
+
export function radioIndex(region, raw) {
|
|
61
|
+
const choices = region.choices ?? [];
|
|
62
|
+
const value = toText(raw).trim().toLowerCase();
|
|
63
|
+
return choices.findIndex((choice) => choice.trim().toLowerCase() === value);
|
|
64
|
+
}
|
|
65
|
+
/** The regions after an insert or delete, moved the way the rules move. */
|
|
66
|
+
export function shiftCellTypes(regions, edit) {
|
|
67
|
+
return shiftRects(regions, edit);
|
|
68
|
+
}
|
|
69
|
+
/** The regions with the rectangles cut out: what Clear All removes. */
|
|
70
|
+
export function removeCellTypes(regions, rects) {
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const region of regions) {
|
|
73
|
+
let kept = [...region.rects];
|
|
74
|
+
for (const hole of rects)
|
|
75
|
+
kept = kept.flatMap((rect) => subtractRect(rect, hole));
|
|
76
|
+
if (kept.length)
|
|
77
|
+
out.push({ ...region, rects: kept });
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Lay a region over some cells, taking those cells out of any region that
|
|
83
|
+
* already claimed them.
|
|
84
|
+
*
|
|
85
|
+
* Without the cut, two regions would overlap and the later one would win
|
|
86
|
+
* silently; cutting makes the model say what the screen shows.
|
|
87
|
+
*/
|
|
88
|
+
export function applyCellType(regions, region) {
|
|
89
|
+
return [...removeCellTypes(regions, region.rects), region];
|
|
90
|
+
}
|
|
91
|
+
/** A deep-enough copy for the document's snapshot. */
|
|
92
|
+
export const copyCellType = (region) => ({
|
|
93
|
+
...region,
|
|
94
|
+
rects: region.rects.map((rect) => [...rect]),
|
|
95
|
+
...(region.choices ? { choices: [...region.choices] } : {}),
|
|
96
|
+
});
|
package/dist/sheet/coerce.d.ts
CHANGED
|
@@ -10,12 +10,46 @@
|
|
|
10
10
|
* - Booleans are 1 and 0 in arithmetic but are skipped by numeric aggregates.
|
|
11
11
|
*/
|
|
12
12
|
import { type CellValue } from './ast.js';
|
|
13
|
+
/** A date from a cell: a serial number, or text `new Date` can read
|
|
14
|
+
* (`2024-03-09`, `2024-03-09 13:45`). Anything else is `#VALUE!`. */
|
|
15
|
+
export declare function toDate(v: CellValue): Date;
|
|
16
|
+
/** The `yyyy-mm-dd` the date functions hand back. */
|
|
17
|
+
export declare const isoDate: (d: Date) => string;
|
|
18
|
+
/** Excel's serial for a date, fractional when it carries a time. */
|
|
19
|
+
export declare function dateSerial(d: Date): number;
|
|
20
|
+
/**
|
|
21
|
+
* The serial a date or a time written as text is worth in arithmetic, or
|
|
22
|
+
* null when the text is neither. Dates are `yyyy-mm-dd` text in a sheet
|
|
23
|
+
* (see `toDate`), so `=A1+1` under one has to read it as Excel reads a
|
|
24
|
+
* date cell: as its day number, so the sum is the next day rather than
|
|
25
|
+
* `#VALUE!`. A clock time is the fraction of a day it is, `="10:30"*2` being
|
|
26
|
+
* 0.875, and `m/d/yyyy` is read the way `new Date` reads it, as toDate does.
|
|
27
|
+
*/
|
|
28
|
+
export declare function dateTextSerial(text: string): number | null;
|
|
13
29
|
export declare function toNumber(v: CellValue): number;
|
|
14
30
|
export declare function toBool(v: CellValue): boolean;
|
|
15
31
|
export declare function toText(v: CellValue): string;
|
|
16
32
|
/** Excel's `=` between two values. Text compares case-insensitively; a number
|
|
17
33
|
* never equals the text that looks like it. */
|
|
18
34
|
export declare function looseEquals(a: CellValue, b: CellValue): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Excel's wildcards, which every text criterion and exact lookup speaks:
|
|
37
|
+
* `*` stands for any run of characters, `?` for exactly one, and `~` asks
|
|
38
|
+
* for the next character itself, so `"~*"` is a literal asterisk.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hasWildcards(pattern: string): boolean;
|
|
41
|
+
/** The pattern as a regular expression, anchored at both ends and
|
|
42
|
+
* case-insensitive, which is how Excel compares text. */
|
|
43
|
+
export declare function wildcardRegExp(pattern: string): RegExp;
|
|
44
|
+
/** The pattern with its `~` escapes taken off, which is the text a plain
|
|
45
|
+
* comparison should look for: `"~*"` means a literal asterisk. */
|
|
46
|
+
export declare function unescapeWildcards(pattern: string): string;
|
|
47
|
+
/** Does this cell match a wildcard pattern? Only text takes part: Excel
|
|
48
|
+
* never turns a number into text to match `"1*"`. */
|
|
49
|
+
export declare function wildcardMatches(value: CellValue, pattern: string): boolean;
|
|
50
|
+
/** Equality as a criterion or an exact lookup reads it: a pattern carrying
|
|
51
|
+
* wildcards matches, anything else compares as `=` does. */
|
|
52
|
+
export declare function patternEquals(value: CellValue, operand: CellValue): boolean;
|
|
19
53
|
/** Ordering for `<` and friends. Excel orders number < text < boolean. */
|
|
20
54
|
export declare function compare(a: CellValue, b: CellValue): number;
|
|
21
55
|
/** The numeric members of a flattened argument list. Blanks, text and booleans
|
|
@@ -29,3 +63,9 @@ export declare function isBlank(v: CellValue): boolean;
|
|
|
29
63
|
* plural forms, which all share this grammar.
|
|
30
64
|
*/
|
|
31
65
|
export declare function matchesCriterion(value: CellValue, criterion: CellValue): boolean;
|
|
66
|
+
/** The indexes in `range` that meet `criterion`, for the IF family. */
|
|
67
|
+
export declare function criteriaHits(range: ReadonlyArray<CellValue>, criterion: CellValue): number[];
|
|
68
|
+
/** SUMIFS and its family: every (range, criterion) pair must match at an index. */
|
|
69
|
+
export declare function multiCriteriaHits(pairs: Array<[ReadonlyArray<CellValue>, CellValue]>): number[];
|
|
70
|
+
/** The (range, criterion) pairs of a *IFS call, starting at argument `from`. */
|
|
71
|
+
export declare function criteriaPairs(args: ReadonlyArray<ReadonlyArray<CellValue>>, from: number): Array<[ReadonlyArray<CellValue>, CellValue]>;
|
package/dist/sheet/coerce.js
CHANGED
|
@@ -10,6 +10,64 @@
|
|
|
10
10
|
* - Booleans are 1 and 0 in arithmetic but are skipped by numeric aggregates.
|
|
11
11
|
*/
|
|
12
12
|
import { FormulaError, isError } from './ast.js';
|
|
13
|
+
/** The epoch Excel's date serials count from: serial 1 is 1900-01-01. Day 60
|
|
14
|
+
* is Excel's phantom 1900-02-29, so every date from 1900-03-01 on is right
|
|
15
|
+
* and the two months before it are a day out, which no sheet has cared
|
|
16
|
+
* about. */
|
|
17
|
+
const EXCEL_EPOCH_MS = Date.UTC(1899, 11, 30);
|
|
18
|
+
/** A date from a cell: a serial number, or text `new Date` can read
|
|
19
|
+
* (`2024-03-09`, `2024-03-09 13:45`). Anything else is `#VALUE!`. */
|
|
20
|
+
export function toDate(v) {
|
|
21
|
+
if (typeof v === 'number')
|
|
22
|
+
return new Date(EXCEL_EPOCH_MS + v * 86400000);
|
|
23
|
+
const d = new Date(toText(v));
|
|
24
|
+
if (Number.isNaN(d.getTime()))
|
|
25
|
+
throw new FormulaError('#VALUE!');
|
|
26
|
+
return d;
|
|
27
|
+
}
|
|
28
|
+
/** The `yyyy-mm-dd` the date functions hand back. */
|
|
29
|
+
export const isoDate = (d) => d.toISOString().slice(0, 10);
|
|
30
|
+
/** Excel's serial for a date, fractional when it carries a time. */
|
|
31
|
+
export function dateSerial(d) {
|
|
32
|
+
return (d.getTime() - EXCEL_EPOCH_MS) / 86400000;
|
|
33
|
+
}
|
|
34
|
+
const ISO_DATE_TEXT = /^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{1,2}):(\d{2})(?::(\d{2}))?)?$/;
|
|
35
|
+
const SLASH_DATE_TEXT = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
|
|
36
|
+
const CLOCK_TEXT = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/;
|
|
37
|
+
/**
|
|
38
|
+
* The serial a date or a time written as text is worth in arithmetic, or
|
|
39
|
+
* null when the text is neither. Dates are `yyyy-mm-dd` text in a sheet
|
|
40
|
+
* (see `toDate`), so `=A1+1` under one has to read it as Excel reads a
|
|
41
|
+
* date cell: as its day number, so the sum is the next day rather than
|
|
42
|
+
* `#VALUE!`. A clock time is the fraction of a day it is, `="10:30"*2` being
|
|
43
|
+
* 0.875, and `m/d/yyyy` is read the way `new Date` reads it, as toDate does.
|
|
44
|
+
*/
|
|
45
|
+
export function dateTextSerial(text) {
|
|
46
|
+
const t = text.trim();
|
|
47
|
+
let m = ISO_DATE_TEXT.exec(t);
|
|
48
|
+
if (m) {
|
|
49
|
+
const month = Number(m[2]), day = Number(m[3]), hours = Number(m[4] ?? 0), minutes = Number(m[5] ?? 0);
|
|
50
|
+
if (month < 1 || month > 12 || day < 1 || day > 31 || hours > 23 || minutes > 59)
|
|
51
|
+
return null;
|
|
52
|
+
const ms = Date.UTC(Number(m[1]), month - 1, day, hours, minutes, Number(m[6] ?? 0));
|
|
53
|
+
return (ms - EXCEL_EPOCH_MS) / 86400000;
|
|
54
|
+
}
|
|
55
|
+
m = SLASH_DATE_TEXT.exec(t);
|
|
56
|
+
if (m) {
|
|
57
|
+
const month = Number(m[1]), day = Number(m[2]);
|
|
58
|
+
if (month < 1 || month > 12 || day < 1 || day > 31)
|
|
59
|
+
return null;
|
|
60
|
+
return (Date.UTC(Number(m[3]), month - 1, day) - EXCEL_EPOCH_MS) / 86400000;
|
|
61
|
+
}
|
|
62
|
+
m = CLOCK_TEXT.exec(t);
|
|
63
|
+
if (m) {
|
|
64
|
+
const hours = Number(m[1]), minutes = Number(m[2]), seconds = Number(m[3] ?? 0);
|
|
65
|
+
if (hours > 23 || minutes > 59 || seconds > 59)
|
|
66
|
+
return null;
|
|
67
|
+
return (hours * 3600 + minutes * 60 + seconds) / 86400;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
13
71
|
export function toNumber(v) {
|
|
14
72
|
if (typeof v === 'number')
|
|
15
73
|
return v;
|
|
@@ -21,6 +79,9 @@ export function toNumber(v) {
|
|
|
21
79
|
const n = Number(v);
|
|
22
80
|
if (Number.isFinite(n))
|
|
23
81
|
return n;
|
|
82
|
+
const serial = dateTextSerial(v);
|
|
83
|
+
if (serial !== null)
|
|
84
|
+
return serial;
|
|
24
85
|
throw new FormulaError('#VALUE!');
|
|
25
86
|
}
|
|
26
87
|
if (isError(v))
|
|
@@ -69,6 +130,80 @@ export function looseEquals(a, b) {
|
|
|
69
130
|
return false;
|
|
70
131
|
return false;
|
|
71
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Excel's wildcards, which every text criterion and exact lookup speaks:
|
|
135
|
+
* `*` stands for any run of characters, `?` for exactly one, and `~` asks
|
|
136
|
+
* for the next character itself, so `"~*"` is a literal asterisk.
|
|
137
|
+
*/
|
|
138
|
+
export function hasWildcards(pattern) {
|
|
139
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
140
|
+
const ch = pattern[i];
|
|
141
|
+
if (ch === '~') {
|
|
142
|
+
i += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (ch === '*' || ch === '?')
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
const LITERAL = /[\\^$.*+?()[\]{}|]/g;
|
|
151
|
+
/** The pattern as a regular expression, anchored at both ends and
|
|
152
|
+
* case-insensitive, which is how Excel compares text. */
|
|
153
|
+
export function wildcardRegExp(pattern) {
|
|
154
|
+
let out = '';
|
|
155
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
156
|
+
const ch = pattern[i];
|
|
157
|
+
if (ch === '~' && i + 1 < pattern.length) {
|
|
158
|
+
out += pattern[i + 1].replace(LITERAL, '\\$&');
|
|
159
|
+
i += 1;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (ch === '*') {
|
|
163
|
+
out += '[\\s\\S]*';
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (ch === '?') {
|
|
167
|
+
out += '[\\s\\S]';
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
out += ch.replace(LITERAL, '\\$&');
|
|
171
|
+
}
|
|
172
|
+
return new RegExp(`^${out}$`, 'i');
|
|
173
|
+
}
|
|
174
|
+
/** The pattern with its `~` escapes taken off, which is the text a plain
|
|
175
|
+
* comparison should look for: `"~*"` means a literal asterisk. */
|
|
176
|
+
export function unescapeWildcards(pattern) {
|
|
177
|
+
if (!pattern.includes('~'))
|
|
178
|
+
return pattern;
|
|
179
|
+
let out = '';
|
|
180
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
181
|
+
const ch = pattern[i];
|
|
182
|
+
if (ch === '~' && i + 1 < pattern.length) {
|
|
183
|
+
out += pattern[i + 1];
|
|
184
|
+
i += 1;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
out += ch;
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
/** Does this cell match a wildcard pattern? Only text takes part: Excel
|
|
192
|
+
* never turns a number into text to match `"1*"`. */
|
|
193
|
+
export function wildcardMatches(value, pattern) {
|
|
194
|
+
if (typeof value !== 'string')
|
|
195
|
+
return false;
|
|
196
|
+
return wildcardRegExp(pattern).test(value);
|
|
197
|
+
}
|
|
198
|
+
/** Equality as a criterion or an exact lookup reads it: a pattern carrying
|
|
199
|
+
* wildcards matches, anything else compares as `=` does. */
|
|
200
|
+
export function patternEquals(value, operand) {
|
|
201
|
+
if (typeof operand !== 'string')
|
|
202
|
+
return looseEquals(value, operand);
|
|
203
|
+
if (hasWildcards(operand))
|
|
204
|
+
return wildcardMatches(value, operand);
|
|
205
|
+
return looseEquals(value, unescapeWildcards(operand));
|
|
206
|
+
}
|
|
72
207
|
/** Ordering for `<` and friends. Excel orders number < text < boolean. */
|
|
73
208
|
export function compare(a, b) {
|
|
74
209
|
if (typeof a === 'number' && typeof b === 'number')
|
|
@@ -108,8 +243,8 @@ export function matchesCriterion(value, criterion) {
|
|
|
108
243
|
const n = Number(rest);
|
|
109
244
|
const operand = rest !== '' && Number.isFinite(n) ? n : rest;
|
|
110
245
|
switch (opText) {
|
|
111
|
-
case '=': return
|
|
112
|
-
case '<>': return !
|
|
246
|
+
case '=': return patternEquals(value, operand);
|
|
247
|
+
case '<>': return !patternEquals(value, operand);
|
|
113
248
|
case '<': return compare(value, operand) < 0;
|
|
114
249
|
case '>': return compare(value, operand) > 0;
|
|
115
250
|
case '<=': return compare(value, operand) <= 0;
|
|
@@ -118,5 +253,37 @@ export function matchesCriterion(value, criterion) {
|
|
|
118
253
|
}
|
|
119
254
|
}
|
|
120
255
|
}
|
|
121
|
-
return
|
|
256
|
+
return patternEquals(value, criterion);
|
|
257
|
+
}
|
|
258
|
+
/** The indexes in `range` that meet `criterion`, for the IF family. */
|
|
259
|
+
export function criteriaHits(range, criterion) {
|
|
260
|
+
const out = [];
|
|
261
|
+
for (let i = 0; i < range.length; i += 1) {
|
|
262
|
+
if (matchesCriterion(range[i], criterion))
|
|
263
|
+
out.push(i);
|
|
264
|
+
}
|
|
265
|
+
return out;
|
|
266
|
+
}
|
|
267
|
+
/** SUMIFS and its family: every (range, criterion) pair must match at an index. */
|
|
268
|
+
export function multiCriteriaHits(pairs) {
|
|
269
|
+
const firstPair = pairs[0];
|
|
270
|
+
if (!firstPair)
|
|
271
|
+
return [];
|
|
272
|
+
const out = [];
|
|
273
|
+
for (let i = 0; i < firstPair[0].length; i += 1) {
|
|
274
|
+
if (pairs.every(([range, crit]) => matchesCriterion(range[i] ?? '', crit)))
|
|
275
|
+
out.push(i);
|
|
276
|
+
}
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
/** The (range, criterion) pairs of a *IFS call, starting at argument `from`. */
|
|
280
|
+
export function criteriaPairs(args, from) {
|
|
281
|
+
const pairs = [];
|
|
282
|
+
for (let i = from; i < args.length; i += 2) {
|
|
283
|
+
const range = args[i];
|
|
284
|
+
if (!range)
|
|
285
|
+
break;
|
|
286
|
+
pairs.push([range, args[i + 1]?.[0] ?? '']);
|
|
287
|
+
}
|
|
288
|
+
return pairs;
|
|
122
289
|
}
|
package/dist/sheet/comments.d.ts
CHANGED
|
@@ -1,18 +1,52 @@
|
|
|
1
1
|
import type { NotesMap } from './rects.js';
|
|
2
2
|
export type { NotesMap } from './rects.js';
|
|
3
|
-
/**
|
|
4
|
-
export
|
|
3
|
+
/** One entry of a thread: its text, who wrote it and when (ISO 8601). */
|
|
4
|
+
export type CommentEntry = {
|
|
5
|
+
text: string;
|
|
6
|
+
author?: string;
|
|
7
|
+
at?: string;
|
|
8
|
+
};
|
|
9
|
+
/** A cell's comment: the first entry, its replies, and whether it is done. */
|
|
10
|
+
export type CommentThread = CommentEntry & {
|
|
11
|
+
replies?: CommentEntry[];
|
|
12
|
+
resolved?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** A note as saved before threads existed, or a thread. */
|
|
15
|
+
export type CommentValue = string | CommentThread;
|
|
16
|
+
/** `r4` -> `B` -> the comment; the document's own shape. */
|
|
17
|
+
export type CommentsMap = Record<string, Record<string, CommentValue>>;
|
|
18
|
+
/** Either shape as a thread; undefined for a blank note. */
|
|
19
|
+
export declare function threadOf(value: CommentValue | undefined): CommentThread | undefined;
|
|
20
|
+
/** The thread on (r, c), or undefined when there is none. */
|
|
21
|
+
export declare function threadAt(comments: CommentsMap, r: number, c: number): CommentThread | undefined;
|
|
22
|
+
/** The comment's first text on (r, c), or undefined when there is none. */
|
|
23
|
+
export declare function commentAt(comments: CommentsMap, r: number, c: number): string | undefined;
|
|
24
|
+
/** True when the thread carries more than a note's text: it is written as an object. */
|
|
25
|
+
export declare function isThreaded(thread: CommentThread): boolean;
|
|
26
|
+
/** The thread as the text a tooltip shows: each entry under its author. */
|
|
27
|
+
export declare function threadText(thread: CommentThread): string;
|
|
28
|
+
/**
|
|
29
|
+
* The map with (r, c) set to `thread`, or cleared when it is null or blank.
|
|
30
|
+
* A thread with nothing but a text is written as that text, so a document
|
|
31
|
+
* that never used threads reads as it always did. A new map, never the
|
|
32
|
+
* old one changed: the grid re-reads `notes` only when the object it is
|
|
33
|
+
* handed is a different one.
|
|
34
|
+
*/
|
|
35
|
+
export declare function withThread(comments: CommentsMap, r: number, c: number, thread: CommentThread | null): CommentsMap;
|
|
5
36
|
/**
|
|
6
|
-
* The map with (r, c) set to `text`, or
|
|
7
|
-
*
|
|
8
|
-
*
|
|
37
|
+
* The map with the first text of (r, c) set to `text`, or the comment
|
|
38
|
+
* removed when the text is blank. A thread keeps its author, replies and
|
|
39
|
+
* state; a note stays a note.
|
|
9
40
|
*/
|
|
10
|
-
export declare function withComment(
|
|
11
|
-
/**
|
|
12
|
-
export declare function
|
|
41
|
+
export declare function withComment(comments: CommentsMap, r: number, c: number, text: string): CommentsMap;
|
|
42
|
+
/** The grid's `notes`: the text of every comment, for the mark and the tooltip. */
|
|
43
|
+
export declare function notesOf(comments: CommentsMap): NotesMap;
|
|
44
|
+
/** Every comment, top to bottom then left to right, with its first text. */
|
|
45
|
+
export declare function listComments(comments: CommentsMap): Array<{
|
|
13
46
|
row: number;
|
|
14
47
|
col: number;
|
|
15
48
|
text: string;
|
|
49
|
+
thread: CommentThread;
|
|
16
50
|
}>;
|
|
17
51
|
/**
|
|
18
52
|
* The comment after (or before) `from`, reading the sheet row by row and
|
|
@@ -20,7 +54,7 @@ export declare function listComments(notes: NotesMap): Array<{
|
|
|
20
54
|
* cell itself is skipped, so pressing Next on a commented cell moves on;
|
|
21
55
|
* null when the sheet has no comments at all.
|
|
22
56
|
*/
|
|
23
|
-
export declare function nextComment(
|
|
57
|
+
export declare function nextComment(comments: CommentsMap, from: {
|
|
24
58
|
row: number;
|
|
25
59
|
col: number;
|
|
26
60
|
}, dir: 1 | -1): {
|
package/dist/sheet/comments.js
CHANGED
|
@@ -1,30 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cell comments
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (`
|
|
7
|
-
*
|
|
2
|
+
* Cell comments: Excel's notes and its threaded comments in one map. A
|
|
3
|
+
* note is a text on a cell, shown as a corner mark and read on hover; a
|
|
4
|
+
* thread is that text with an author and a time, replies under it, and a
|
|
5
|
+
* resolved flag. They live in the sheet document keyed the way the grid's
|
|
6
|
+
* `notes` are (`r4` -> `B` -> value), so the document moves them with an
|
|
7
|
+
* insert or delete (`remapNotes`) and the grid draws the mark and the
|
|
8
|
+
* tooltip from the text projection `notesOf` makes.
|
|
9
|
+
*
|
|
10
|
+
* A value is a plain string for a note (the shape every document saved so
|
|
11
|
+
* far carries) or a `CommentThread`; `threadOf` reads either. This module
|
|
12
|
+
* is the pure part: reading, writing, and walking them the way Review >
|
|
13
|
+
* Previous / Next do.
|
|
8
14
|
*/
|
|
9
15
|
import { colToLetters, lettersToCol } from './address.js';
|
|
10
|
-
/**
|
|
11
|
-
export function
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
/** Either shape as a thread; undefined for a blank note. */
|
|
17
|
+
export function threadOf(value) {
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
if (typeof value === 'string')
|
|
21
|
+
return value.trim() ? { text: value } : undefined;
|
|
22
|
+
return value.text.trim() || value.replies?.length ? value : undefined;
|
|
23
|
+
}
|
|
24
|
+
/** The thread on (r, c), or undefined when there is none. */
|
|
25
|
+
export function threadAt(comments, r, c) {
|
|
26
|
+
return threadOf(comments[`r${r}`]?.[colToLetters(c)]);
|
|
27
|
+
}
|
|
28
|
+
/** The comment's first text on (r, c), or undefined when there is none. */
|
|
29
|
+
export function commentAt(comments, r, c) {
|
|
30
|
+
return threadAt(comments, r, c)?.text;
|
|
31
|
+
}
|
|
32
|
+
/** True when the thread carries more than a note's text: it is written as an object. */
|
|
33
|
+
export function isThreaded(thread) {
|
|
34
|
+
return Boolean(thread.author || thread.at || thread.replies?.length || thread.resolved);
|
|
35
|
+
}
|
|
36
|
+
/** The thread as the text a tooltip shows: each entry under its author. */
|
|
37
|
+
export function threadText(thread) {
|
|
38
|
+
const line = (entry) => (entry.author ? `${entry.author}:\n${entry.text}` : entry.text);
|
|
39
|
+
return [line(thread), ...(thread.replies ?? []).map(line)].join('\n\n');
|
|
40
|
+
}
|
|
41
|
+
function copy(comments) {
|
|
42
|
+
const out = {};
|
|
43
|
+
for (const [id, line] of Object.entries(comments))
|
|
44
|
+
out[id] = { ...line };
|
|
45
|
+
return out;
|
|
14
46
|
}
|
|
15
47
|
/**
|
|
16
|
-
* The map with (r, c) set to `
|
|
17
|
-
* A
|
|
18
|
-
*
|
|
48
|
+
* The map with (r, c) set to `thread`, or cleared when it is null or blank.
|
|
49
|
+
* A thread with nothing but a text is written as that text, so a document
|
|
50
|
+
* that never used threads reads as it always did. A new map, never the
|
|
51
|
+
* old one changed: the grid re-reads `notes` only when the object it is
|
|
52
|
+
* handed is a different one.
|
|
19
53
|
*/
|
|
20
|
-
export function
|
|
54
|
+
export function withThread(comments, r, c, thread) {
|
|
21
55
|
const rowId = `r${r}`;
|
|
22
56
|
const columnId = colToLetters(c);
|
|
23
|
-
const out =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
out[rowId] = { ...(out[rowId] ?? {}), [columnId]: text };
|
|
57
|
+
const out = copy(comments);
|
|
58
|
+
const kept = thread ? threadOf(thread) : undefined;
|
|
59
|
+
if (kept) {
|
|
60
|
+
out[rowId] = { ...(out[rowId] ?? {}), [columnId]: isThreaded(kept) ? kept : kept.text };
|
|
28
61
|
}
|
|
29
62
|
else if (out[rowId]) {
|
|
30
63
|
delete out[rowId][columnId];
|
|
@@ -33,18 +66,43 @@ export function withComment(notes, r, c, text) {
|
|
|
33
66
|
}
|
|
34
67
|
return out;
|
|
35
68
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
69
|
+
/**
|
|
70
|
+
* The map with the first text of (r, c) set to `text`, or the comment
|
|
71
|
+
* removed when the text is blank. A thread keeps its author, replies and
|
|
72
|
+
* state; a note stays a note.
|
|
73
|
+
*/
|
|
74
|
+
export function withComment(comments, r, c, text) {
|
|
75
|
+
if (!text.trim())
|
|
76
|
+
return withThread(comments, r, c, null);
|
|
77
|
+
const before = threadAt(comments, r, c);
|
|
78
|
+
return withThread(comments, r, c, before ? { ...before, text } : { text });
|
|
79
|
+
}
|
|
80
|
+
/** The grid's `notes`: the text of every comment, for the mark and the tooltip. */
|
|
81
|
+
export function notesOf(comments) {
|
|
82
|
+
const out = {};
|
|
83
|
+
for (const [rowId, line] of Object.entries(comments)) {
|
|
84
|
+
for (const [columnId, value] of Object.entries(line)) {
|
|
85
|
+
const thread = threadOf(value);
|
|
86
|
+
if (!thread)
|
|
87
|
+
continue;
|
|
88
|
+
out[rowId] = { ...(out[rowId] ?? {}), [columnId]: threadText(thread) };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
/** Every comment, top to bottom then left to right, with its first text. */
|
|
94
|
+
export function listComments(comments) {
|
|
38
95
|
const out = [];
|
|
39
|
-
for (const [rowId, line] of Object.entries(
|
|
96
|
+
for (const [rowId, line] of Object.entries(comments)) {
|
|
40
97
|
const row = Number(rowId.slice(1));
|
|
41
98
|
if (!Number.isInteger(row) || row < 0)
|
|
42
99
|
continue;
|
|
43
|
-
for (const [columnId,
|
|
100
|
+
for (const [columnId, value] of Object.entries(line)) {
|
|
44
101
|
const col = lettersToCol(columnId);
|
|
45
|
-
|
|
102
|
+
const thread = threadOf(value);
|
|
103
|
+
if (col < 0 || !thread)
|
|
46
104
|
continue;
|
|
47
|
-
out.push({ row, col, text });
|
|
105
|
+
out.push({ row, col, text: thread.text, thread });
|
|
48
106
|
}
|
|
49
107
|
}
|
|
50
108
|
return out.sort((a, b) => a.row - b.row || a.col - b.col);
|
|
@@ -55,8 +113,8 @@ export function listComments(notes) {
|
|
|
55
113
|
* cell itself is skipped, so pressing Next on a commented cell moves on;
|
|
56
114
|
* null when the sheet has no comments at all.
|
|
57
115
|
*/
|
|
58
|
-
export function nextComment(
|
|
59
|
-
const all = listComments(
|
|
116
|
+
export function nextComment(comments, from, dir) {
|
|
117
|
+
const all = listComments(comments);
|
|
60
118
|
if (!all.length)
|
|
61
119
|
return null;
|
|
62
120
|
const after = (a, b) => a.row > b.row || (a.row === b.row && a.col > b.col);
|