@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,204 @@
|
|
|
1
|
+
import { shiftRect, rectContains } from './rects.js';
|
|
2
|
+
// The geometry only, through the grid's own subpath: this module is read by
|
|
3
|
+
// the xlsx writer and by tests that run without a DOM, and the package root
|
|
4
|
+
// would drag every component in with it.
|
|
5
|
+
import { buildSparkline } from '@svgrid/grid/sparkline';
|
|
6
|
+
/** The three Excel offers: Line, Column and Win/Loss. */
|
|
7
|
+
export const SPARKLINE_TYPES = ['line', 'column', 'winloss'];
|
|
8
|
+
let nextSparklineId = 1;
|
|
9
|
+
/** An id no other group of this session carries. */
|
|
10
|
+
export const sparklineId = () => `spark-${Date.now().toString(36)}-${(nextSparklineId += 1).toString(36)}`;
|
|
11
|
+
/** A copy safe to hand out or save. */
|
|
12
|
+
export function copySparkline(group) {
|
|
13
|
+
return { ...group, location: [...group.location], data: [...group.data] };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The group after an insert or delete: both rectangles move with their
|
|
17
|
+
* cells. Null when the edit took every cell the group was drawn in, or
|
|
18
|
+
* every cell it read.
|
|
19
|
+
*/
|
|
20
|
+
export function shiftSparkline(group, edit) {
|
|
21
|
+
const location = shiftRect(group.location, edit);
|
|
22
|
+
if (!location)
|
|
23
|
+
return null;
|
|
24
|
+
const data = shiftRect(group.data, edit);
|
|
25
|
+
if (!data)
|
|
26
|
+
return null;
|
|
27
|
+
return { ...copySparkline(group), location: location, data: data };
|
|
28
|
+
}
|
|
29
|
+
/** The groups of a sheet after an insert or delete, the gone ones dropped. */
|
|
30
|
+
export function shiftSparklines(groups, edit) {
|
|
31
|
+
return groups.map((g) => shiftSparkline(g, edit)).filter((g) => g !== null);
|
|
32
|
+
}
|
|
33
|
+
/** True when the group draws down a column of cells rather than along a row. */
|
|
34
|
+
export function isColumnLocation(group) {
|
|
35
|
+
const [r1, c1, r2, c2] = group.location;
|
|
36
|
+
// A single cell counts as a column: its data is read as one line either way.
|
|
37
|
+
return r2 - r1 >= c2 - c1;
|
|
38
|
+
}
|
|
39
|
+
/** A cell's value as a number, or null when it is not one. */
|
|
40
|
+
function numberOf(value) {
|
|
41
|
+
if (typeof value === 'number')
|
|
42
|
+
return Number.isFinite(value) ? value : null;
|
|
43
|
+
if (typeof value === 'boolean')
|
|
44
|
+
return value ? 1 : 0;
|
|
45
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
46
|
+
const n = Number(value);
|
|
47
|
+
return Number.isFinite(n) ? n : null;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The numbers one location cell draws, read through `valueAt`. The nth cell
|
|
53
|
+
* of the location reads the nth line of the data: the nth row when the
|
|
54
|
+
* sparklines run down a column, the nth column when they run along a row.
|
|
55
|
+
* A blank or a text cell inside the line is a gap, which Excel draws as a
|
|
56
|
+
* zero rather than breaking the line.
|
|
57
|
+
*/
|
|
58
|
+
export function sparklineSeries(group, row, col, valueAt) {
|
|
59
|
+
if (!rectContains(group.location, row, col))
|
|
60
|
+
return null;
|
|
61
|
+
const [lr1, lc1] = group.location;
|
|
62
|
+
const [dr1, dc1, dr2, dc2] = group.data;
|
|
63
|
+
const down = isColumnLocation(group);
|
|
64
|
+
const index = down ? row - lr1 : col - lc1;
|
|
65
|
+
const values = [];
|
|
66
|
+
if (down) {
|
|
67
|
+
const r = dr1 + index;
|
|
68
|
+
if (r > dr2)
|
|
69
|
+
return null;
|
|
70
|
+
for (let c = dc1; c <= dc2; c += 1)
|
|
71
|
+
values.push(numberOf(valueAt(r, c)) ?? 0);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const c = dc1 + index;
|
|
75
|
+
if (c > dc2)
|
|
76
|
+
return null;
|
|
77
|
+
for (let r = dr1; r <= dr2; r += 1)
|
|
78
|
+
values.push(numberOf(valueAt(r, c)) ?? 0);
|
|
79
|
+
}
|
|
80
|
+
return values.length ? values : null;
|
|
81
|
+
}
|
|
82
|
+
/** The group drawn in a cell, or null: the last one wins, as Excel's does. */
|
|
83
|
+
/**
|
|
84
|
+
* One line per cell of the group: the cell the sparkline is drawn in and
|
|
85
|
+
* the block of numbers it reads.
|
|
86
|
+
*
|
|
87
|
+
* The drawing works this out cell by cell as it paints; a writer needs the
|
|
88
|
+
* whole list, because Excel stores a sparkline group as one entry per cell
|
|
89
|
+
* with its own reference.
|
|
90
|
+
*/
|
|
91
|
+
/**
|
|
92
|
+
* A sparkline as standalone SVG markup, for a page the component cannot
|
|
93
|
+
* draw on: the printed sheet.
|
|
94
|
+
*
|
|
95
|
+
* The geometry is the grid's own `buildSparkline`, so the printed line is
|
|
96
|
+
* the line on screen rather than a second drawing that can disagree with
|
|
97
|
+
* it. The markup carries its colours inline, since the print document has
|
|
98
|
+
* no stylesheet of ours.
|
|
99
|
+
*/
|
|
100
|
+
export function sparklineSvg(values, options) {
|
|
101
|
+
const geo = buildSparkline([...values], {
|
|
102
|
+
type: options.type === 'column' ? 'bar' : options.type === 'winloss' ? 'winloss' : 'line',
|
|
103
|
+
width: options.width,
|
|
104
|
+
height: options.height,
|
|
105
|
+
...(options.color ? { color: options.color } : {}),
|
|
106
|
+
...(options.negativeColor ? { negativeColor: options.negativeColor } : {}),
|
|
107
|
+
...(options.min !== undefined ? { min: options.min } : {}),
|
|
108
|
+
...(options.max !== undefined ? { max: options.max } : {}),
|
|
109
|
+
lastPoint: Boolean(options.markers),
|
|
110
|
+
});
|
|
111
|
+
if (!geo)
|
|
112
|
+
return '';
|
|
113
|
+
const parts = [];
|
|
114
|
+
if (geo.bars.length) {
|
|
115
|
+
for (const bar of geo.bars) {
|
|
116
|
+
const fill = bar.negative ? geo.negativeColor : geo.color;
|
|
117
|
+
parts.push(`<rect x="${round(bar.x)}" y="${round(bar.y)}" width="${round(bar.w)}" height="${round(bar.h)}" fill="${fill}"/>`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (geo.linePath) {
|
|
121
|
+
parts.push(`<path d="${geo.linePath}" fill="none" stroke="${geo.color}" stroke-width="${geo.lineWidth}" stroke-linejoin="round" stroke-linecap="round"/>`);
|
|
122
|
+
}
|
|
123
|
+
if (geo.lastPoint && options.markers) {
|
|
124
|
+
parts.push(`<circle cx="${round(geo.lastPoint.x)}" cy="${round(geo.lastPoint.y)}" r="${geo.lineWidth + 0.5}" fill="${geo.color}"/>`);
|
|
125
|
+
}
|
|
126
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${geo.width}" height="${geo.height}" viewBox="0 0 ${geo.width} ${geo.height}" role="img" aria-hidden="true">${parts.join('')}</svg>`;
|
|
127
|
+
}
|
|
128
|
+
const round = (n) => Math.round(n * 100) / 100;
|
|
129
|
+
export function sparklineLines(group) {
|
|
130
|
+
const [lr1, lc1, lr2, lc2] = group.location;
|
|
131
|
+
const [dr1, dc1, dr2, dc2] = group.data;
|
|
132
|
+
const down = isColumnLocation(group);
|
|
133
|
+
const out = [];
|
|
134
|
+
const count = down ? lr2 - lr1 + 1 : lc2 - lc1 + 1;
|
|
135
|
+
for (let i = 0; i < count; i += 1) {
|
|
136
|
+
if (down) {
|
|
137
|
+
const r = dr1 + i;
|
|
138
|
+
if (r > dr2)
|
|
139
|
+
break;
|
|
140
|
+
out.push({ row: lr1 + i, col: lc1, data: [r, dc1, r, dc2] });
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const c = dc1 + i;
|
|
144
|
+
if (c > dc2)
|
|
145
|
+
break;
|
|
146
|
+
out.push({ row: lr1, col: lc1 + i, data: [dr1, c, dr2, c] });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
export function sparklineAt(groups, row, col) {
|
|
152
|
+
for (let i = groups.length - 1; i >= 0; i -= 1) {
|
|
153
|
+
if (rectContains(groups[i].location, row, col))
|
|
154
|
+
return groups[i];
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The scale a group is drawn on when every sparkline in it shares one, which
|
|
160
|
+
* is Excel's "Same for All": the smallest and largest number anywhere in the
|
|
161
|
+
* data range. Null when the group scales each sparkline on its own.
|
|
162
|
+
*/
|
|
163
|
+
export function sparklineScale(group, valueAt) {
|
|
164
|
+
if (!group.sameScale)
|
|
165
|
+
return null;
|
|
166
|
+
const [r1, c1, r2, c2] = group.data;
|
|
167
|
+
let min = Infinity;
|
|
168
|
+
let max = -Infinity;
|
|
169
|
+
for (let r = r1; r <= r2; r += 1) {
|
|
170
|
+
for (let c = c1; c <= c2; c += 1) {
|
|
171
|
+
const n = numberOf(valueAt(r, c));
|
|
172
|
+
if (n === null)
|
|
173
|
+
continue;
|
|
174
|
+
if (n < min)
|
|
175
|
+
min = n;
|
|
176
|
+
if (n > max)
|
|
177
|
+
max = n;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (min === Infinity)
|
|
181
|
+
return null;
|
|
182
|
+
return min === max ? { min: min - 1, max: max + 1 } : { min, max };
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Where Excel's Create Sparklines dialog opens: the data is the selected
|
|
186
|
+
* block, and the location the column just past it (or the row just under it
|
|
187
|
+
* when the block is a single row). A block one cell tall gets a location of
|
|
188
|
+
* one cell, which is the common "a row of months, a sparkline beside it".
|
|
189
|
+
*/
|
|
190
|
+
export function sparklinesFromRange(data, type = 'line') {
|
|
191
|
+
const [r1, c1, r2, c2] = data;
|
|
192
|
+
const alongARow = r2 === r1 && c2 > c1;
|
|
193
|
+
const location = alongARow
|
|
194
|
+
? [r1, c2 + 1, r1, c2 + 1]
|
|
195
|
+
: [r1, c2 + 1, r2, c2 + 1];
|
|
196
|
+
return { id: sparklineId(), location, data: [r1, c1, r2, c2], type, markers: type === 'line' };
|
|
197
|
+
}
|
|
198
|
+
/** The groups left after clearing every one the rectangles touch. */
|
|
199
|
+
export function clearSparklines(groups, rects) {
|
|
200
|
+
return groups.filter((g) => !rects.some((rect) => overlaps(g.location, rect)));
|
|
201
|
+
}
|
|
202
|
+
function overlaps(a, b) {
|
|
203
|
+
return !(a[2] < b[0] || b[2] < a[0] || a[3] < b[1] || b[3] < a[1]);
|
|
204
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The table styles gallery: Excel's presets, by the names Excel stores.
|
|
3
|
+
*
|
|
4
|
+
* A table's look is drawn rather than written into the cells, which is what
|
|
5
|
+
* lets a row typed under the last one arrive already banded. That drawing
|
|
6
|
+
* needs colours, and "the theme's accent" is one look rather than a gallery,
|
|
7
|
+
* so this is the set to pick from.
|
|
8
|
+
*
|
|
9
|
+
* The ids are Excel's own (`TableStyleMedium2`), and the colours are the
|
|
10
|
+
* Office theme's accents, so a table saved here opens in Excel wearing the
|
|
11
|
+
* same style rather than something approximate. A style is three tones of
|
|
12
|
+
* one accent: Light tints the header, Medium fills it, Dark fills it with a
|
|
13
|
+
* deeper shade, exactly as Excel's three families do.
|
|
14
|
+
*/
|
|
15
|
+
export type TableStyleTone = 'light' | 'medium' | 'dark';
|
|
16
|
+
export type TableStyle = {
|
|
17
|
+
/** Excel's name for it, which is what goes in the file. */
|
|
18
|
+
id: string;
|
|
19
|
+
/** The colour's name, for the gallery. */
|
|
20
|
+
colour: string;
|
|
21
|
+
tone: TableStyleTone;
|
|
22
|
+
/** The Office theme accent it is built from. */
|
|
23
|
+
accent: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Eighteen styles: six accents in three tones. Excel numbers each family
|
|
27
|
+
* from 1, with 1 reserved for the black-and-white variant, so the accents
|
|
28
|
+
* start at 2 and this maps one for one onto the built-in names.
|
|
29
|
+
*/
|
|
30
|
+
export declare const TABLE_STYLES: ReadonlyArray<TableStyle>;
|
|
31
|
+
/** What a table wears unless it is given something else: Excel's own default. */
|
|
32
|
+
export declare const DEFAULT_TABLE_STYLE = "TableStyleMedium2";
|
|
33
|
+
/** No style at all: the cells keep whatever formats they carry. */
|
|
34
|
+
export declare const NO_TABLE_STYLE = "None";
|
|
35
|
+
export declare function findTableStyle(id: string | undefined): TableStyle | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Black or white over a fill, whichever can be read.
|
|
38
|
+
*
|
|
39
|
+
* Excel paints a header's text white whatever the fill, which on its gold
|
|
40
|
+
* and sky accents is about 2:1: legible to some people, not to others, and
|
|
41
|
+
* flagged by every checker. Picking the readable one keeps Excel's colours
|
|
42
|
+
* and drops the part of Excel's behaviour that fails.
|
|
43
|
+
*/
|
|
44
|
+
export declare function readableOn(fill: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* The fill a filled header takes: the accent, darkened only when neither
|
|
47
|
+
* black nor white can be read on it.
|
|
48
|
+
*
|
|
49
|
+
* Mid grey is the one accent where that happens: white is 4.0 against it
|
|
50
|
+
* and near-black 4.3, so both sit under the 4.5 a checker asks for. A few
|
|
51
|
+
* steps darker keeps it recognisably the same grey and lets the white text
|
|
52
|
+
* clear it, which is a smaller lie than a header nobody can read.
|
|
53
|
+
*/
|
|
54
|
+
export declare function headerFill(accent: string): string;
|
|
55
|
+
export type TableStyleColours = {
|
|
56
|
+
/** The header row's fill, and the colour its text needs to stay readable. */
|
|
57
|
+
header: string;
|
|
58
|
+
headerText: string;
|
|
59
|
+
/** Every other data row. */
|
|
60
|
+
band: string;
|
|
61
|
+
/** The totals row, and the line over it. */
|
|
62
|
+
totals: string;
|
|
63
|
+
border: string;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* A style as CSS colours.
|
|
67
|
+
*
|
|
68
|
+
* `color-mix` rather than fixed hexes for the tints, so a table keeps its
|
|
69
|
+
* colour over a dark sheet as well as a light one: the accent is mixed with
|
|
70
|
+
* the surface underneath rather than painted over it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function tableStyleColours(style: TableStyle | string | undefined): TableStyleColours | null;
|
|
73
|
+
/** "Blue, Medium", for the gallery's tooltip and for a screen reader. */
|
|
74
|
+
export declare function tableStyleLabel(style: TableStyle): string;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The table styles gallery: Excel's presets, by the names Excel stores.
|
|
3
|
+
*
|
|
4
|
+
* A table's look is drawn rather than written into the cells, which is what
|
|
5
|
+
* lets a row typed under the last one arrive already banded. That drawing
|
|
6
|
+
* needs colours, and "the theme's accent" is one look rather than a gallery,
|
|
7
|
+
* so this is the set to pick from.
|
|
8
|
+
*
|
|
9
|
+
* The ids are Excel's own (`TableStyleMedium2`), and the colours are the
|
|
10
|
+
* Office theme's accents, so a table saved here opens in Excel wearing the
|
|
11
|
+
* same style rather than something approximate. A style is three tones of
|
|
12
|
+
* one accent: Light tints the header, Medium fills it, Dark fills it with a
|
|
13
|
+
* deeper shade, exactly as Excel's three families do.
|
|
14
|
+
*/
|
|
15
|
+
/** The Office theme's six accents, in Excel's order. */
|
|
16
|
+
const ACCENTS = [
|
|
17
|
+
{ name: 'Blue', hex: '#4472c4' },
|
|
18
|
+
{ name: 'Orange', hex: '#ed7d31' },
|
|
19
|
+
{ name: 'Grey', hex: '#7f7f7f' },
|
|
20
|
+
{ name: 'Gold', hex: '#ffc000' },
|
|
21
|
+
{ name: 'Sky', hex: '#5b9bd5' },
|
|
22
|
+
{ name: 'Green', hex: '#70ad47' },
|
|
23
|
+
];
|
|
24
|
+
const TONES = [
|
|
25
|
+
{ tone: 'light', family: 'Light' },
|
|
26
|
+
{ tone: 'medium', family: 'Medium' },
|
|
27
|
+
{ tone: 'dark', family: 'Dark' },
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Eighteen styles: six accents in three tones. Excel numbers each family
|
|
31
|
+
* from 1, with 1 reserved for the black-and-white variant, so the accents
|
|
32
|
+
* start at 2 and this maps one for one onto the built-in names.
|
|
33
|
+
*/
|
|
34
|
+
export const TABLE_STYLES = TONES.flatMap(({ tone, family }) => ACCENTS.map((accent, i) => ({
|
|
35
|
+
id: `TableStyle${family}${i + 2}`,
|
|
36
|
+
colour: accent.name,
|
|
37
|
+
tone,
|
|
38
|
+
accent: accent.hex,
|
|
39
|
+
})));
|
|
40
|
+
/** What a table wears unless it is given something else: Excel's own default. */
|
|
41
|
+
export const DEFAULT_TABLE_STYLE = 'TableStyleMedium2';
|
|
42
|
+
/** No style at all: the cells keep whatever formats they carry. */
|
|
43
|
+
export const NO_TABLE_STYLE = 'None';
|
|
44
|
+
export function findTableStyle(id) {
|
|
45
|
+
if (!id || id === NO_TABLE_STYLE)
|
|
46
|
+
return undefined;
|
|
47
|
+
return TABLE_STYLES.find((s) => s.id.toLowerCase() === id.toLowerCase());
|
|
48
|
+
}
|
|
49
|
+
/** A hex colour as its three channels, 0 to 255. */
|
|
50
|
+
function channels(hex) {
|
|
51
|
+
const value = hex.trim().replace('#', '');
|
|
52
|
+
const full = value.length === 3 ? value.split('').map((c) => c + c).join('') : value;
|
|
53
|
+
const n = Number.parseInt(full.slice(0, 6), 16);
|
|
54
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
55
|
+
}
|
|
56
|
+
/** Relative luminance, the way WCAG defines it. */
|
|
57
|
+
function luminance(hex) {
|
|
58
|
+
const [r, g, b] = channels(hex).map((c) => {
|
|
59
|
+
const s = c / 255;
|
|
60
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
61
|
+
});
|
|
62
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
63
|
+
}
|
|
64
|
+
function contrast(a, b) {
|
|
65
|
+
const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
|
|
66
|
+
return (x + 0.05) / (y + 0.05);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Black or white over a fill, whichever can be read.
|
|
70
|
+
*
|
|
71
|
+
* Excel paints a header's text white whatever the fill, which on its gold
|
|
72
|
+
* and sky accents is about 2:1: legible to some people, not to others, and
|
|
73
|
+
* flagged by every checker. Picking the readable one keeps Excel's colours
|
|
74
|
+
* and drops the part of Excel's behaviour that fails.
|
|
75
|
+
*/
|
|
76
|
+
export function readableOn(fill) {
|
|
77
|
+
const dark = '#0f172a';
|
|
78
|
+
const light = '#ffffff';
|
|
79
|
+
return contrast(fill, light) >= contrast(fill, dark) ? light : dark;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The fill a filled header takes: the accent, darkened only when neither
|
|
83
|
+
* black nor white can be read on it.
|
|
84
|
+
*
|
|
85
|
+
* Mid grey is the one accent where that happens: white is 4.0 against it
|
|
86
|
+
* and near-black 4.3, so both sit under the 4.5 a checker asks for. A few
|
|
87
|
+
* steps darker keeps it recognisably the same grey and lets the white text
|
|
88
|
+
* clear it, which is a smaller lie than a header nobody can read.
|
|
89
|
+
*/
|
|
90
|
+
export function headerFill(accent) {
|
|
91
|
+
const best = Math.max(contrast(accent, '#ffffff'), contrast(accent, '#0f172a'));
|
|
92
|
+
if (best >= 4.5)
|
|
93
|
+
return accent;
|
|
94
|
+
for (let percent = 92; percent >= 60; percent -= 8) {
|
|
95
|
+
const darker = mixWithBlack(accent, percent);
|
|
96
|
+
if (contrast(darker, '#ffffff') >= 4.5)
|
|
97
|
+
return darker;
|
|
98
|
+
}
|
|
99
|
+
return mixWithBlack(accent, 60);
|
|
100
|
+
}
|
|
101
|
+
/** The colour a mix of an accent with black comes out as, for the dark tone. */
|
|
102
|
+
function mixWithBlack(hex, percent) {
|
|
103
|
+
const mixed = channels(hex).map((c) => Math.round((c * percent) / 100));
|
|
104
|
+
return `#${mixed.map((c) => c.toString(16).padStart(2, '0')).join('')}`;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* A style as CSS colours.
|
|
108
|
+
*
|
|
109
|
+
* `color-mix` rather than fixed hexes for the tints, so a table keeps its
|
|
110
|
+
* colour over a dark sheet as well as a light one: the accent is mixed with
|
|
111
|
+
* the surface underneath rather than painted over it.
|
|
112
|
+
*/
|
|
113
|
+
export function tableStyleColours(style) {
|
|
114
|
+
const found = typeof style === 'string' || style === undefined ? findTableStyle(style) : style;
|
|
115
|
+
if (!found)
|
|
116
|
+
return null;
|
|
117
|
+
const accent = found.accent;
|
|
118
|
+
const mix = (percent, into = 'transparent') => `color-mix(in srgb, ${accent} ${percent}%, ${into})`;
|
|
119
|
+
if (found.tone === 'light') {
|
|
120
|
+
return {
|
|
121
|
+
header: mix(18),
|
|
122
|
+
headerText: 'inherit',
|
|
123
|
+
band: mix(8),
|
|
124
|
+
totals: mix(14),
|
|
125
|
+
border: mix(50),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (found.tone === 'dark') {
|
|
129
|
+
return {
|
|
130
|
+
header: `color-mix(in srgb, ${accent} 65%, #000)`,
|
|
131
|
+
headerText: readableOn(mixWithBlack(accent, 65)),
|
|
132
|
+
band: mix(22),
|
|
133
|
+
totals: mix(30),
|
|
134
|
+
border: mix(70),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const fill = headerFill(accent);
|
|
138
|
+
return {
|
|
139
|
+
header: fill,
|
|
140
|
+
// White where white can be read, near-black where it cannot: Excel's
|
|
141
|
+
// gold and sky headers are about 2:1 against white.
|
|
142
|
+
headerText: readableOn(fill),
|
|
143
|
+
band: mix(13),
|
|
144
|
+
totals: mix(20),
|
|
145
|
+
border: mix(55),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/** "Blue, Medium", for the gallery's tooltip and for a screen reader. */
|
|
149
|
+
export function tableStyleLabel(style) {
|
|
150
|
+
const tone = style.tone === 'light' ? 'Light' : style.tone === 'dark' ? 'Dark' : 'Medium';
|
|
151
|
+
return `${style.colour}, ${tone}`;
|
|
152
|
+
}
|
package/dist/sheet/tables.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* the range depends on the table's size at that moment rather than at parse
|
|
15
15
|
* time.
|
|
16
16
|
*/
|
|
17
|
+
import type { StructuralEdit } from './refs.js';
|
|
17
18
|
export type TableRegion = {
|
|
18
19
|
name: string;
|
|
19
20
|
sheet: string;
|
|
@@ -26,6 +27,12 @@ export type TableRegion = {
|
|
|
26
27
|
lastRow: number;
|
|
27
28
|
/** Whether the row after `lastRow` is a totals row. */
|
|
28
29
|
hasTotals: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The look, by Excel's name for it (`TableStyleMedium2`), or `'None'` for
|
|
32
|
+
* cells that keep whatever formats they carry. Absent means the default,
|
|
33
|
+
* which is Excel's default too.
|
|
34
|
+
*/
|
|
35
|
+
style?: string;
|
|
29
36
|
};
|
|
30
37
|
/** Which part of a table a structured reference asks for. */
|
|
31
38
|
export type TableSpecifier = '#All' | '#Data' | '#Headers' | '#Totals' | '#ThisRow';
|
|
@@ -68,3 +75,15 @@ export declare function resolveTableRange(table: TableRegion, specifier: TableSp
|
|
|
68
75
|
} | null, currentRow: number | null): TableRange | null;
|
|
69
76
|
/** How many data rows a table holds. */
|
|
70
77
|
export declare function rowCountOf(table: TableRegion): number;
|
|
78
|
+
/**
|
|
79
|
+
* A table after an insert or delete on its own sheet: its header row, its
|
|
80
|
+
* columns and its last row move with the cells. Null when the edit took the
|
|
81
|
+
* header row, or every column, which is what deleting a table looks like.
|
|
82
|
+
*
|
|
83
|
+
* A totals row sits on `lastRow + 1`, so it moves with the rest; an insert
|
|
84
|
+
* INSIDE the table grows it, which is what Excel does when a row is added
|
|
85
|
+
* in the middle of one.
|
|
86
|
+
*/
|
|
87
|
+
export declare function shiftTable(table: TableRegion, sheet: string, edit: StructuralEdit): TableRegion | null;
|
|
88
|
+
/** Every table after an edit, the gone ones dropped. */
|
|
89
|
+
export declare function shiftTables(tables: ReadonlyArray<TableRegion>, sheet: string, edit: StructuralEdit): TableRegion[];
|
package/dist/sheet/tables.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* the range depends on the table's size at that moment rather than at parse
|
|
15
15
|
* time.
|
|
16
16
|
*/
|
|
17
|
+
import { shiftRect } from './rects.js';
|
|
17
18
|
/** Excel's rules for a table name are the same as for a defined name. */
|
|
18
19
|
export function isValidTableName(name) {
|
|
19
20
|
if (!/^[A-Za-z_][A-Za-z0-9_.]*$/.test(name))
|
|
@@ -145,3 +146,33 @@ export function resolveTableRange(table, specifier, columns, currentRow) {
|
|
|
145
146
|
export function rowCountOf(table) {
|
|
146
147
|
return Math.max(0, table.lastRow - table.headerRow);
|
|
147
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* A table after an insert or delete on its own sheet: its header row, its
|
|
151
|
+
* columns and its last row move with the cells. Null when the edit took the
|
|
152
|
+
* header row, or every column, which is what deleting a table looks like.
|
|
153
|
+
*
|
|
154
|
+
* A totals row sits on `lastRow + 1`, so it moves with the rest; an insert
|
|
155
|
+
* INSIDE the table grows it, which is what Excel does when a row is added
|
|
156
|
+
* in the middle of one.
|
|
157
|
+
*/
|
|
158
|
+
export function shiftTable(table, sheet, edit) {
|
|
159
|
+
if (table.sheet.toLowerCase() !== sheet.toLowerCase())
|
|
160
|
+
return table;
|
|
161
|
+
const rows = edit.kind === 'insertRows' || edit.kind === 'deleteRows';
|
|
162
|
+
const rect = rows
|
|
163
|
+
? [table.headerRow, table.firstCol, table.lastRow + (table.hasTotals ? 1 : 0), table.lastCol]
|
|
164
|
+
: [table.headerRow, table.firstCol, table.lastRow + (table.hasTotals ? 1 : 0), table.lastCol];
|
|
165
|
+
const moved = shiftRect(rect, edit);
|
|
166
|
+
if (!moved)
|
|
167
|
+
return null;
|
|
168
|
+
const [r1, c1, r2, c2] = moved;
|
|
169
|
+
// A table with no data rows left is gone, as is one whose header went.
|
|
170
|
+
const lastRow = r2 - (table.hasTotals ? 1 : 0);
|
|
171
|
+
if (lastRow < r1 + 1)
|
|
172
|
+
return null;
|
|
173
|
+
return { ...table, headerRow: r1, firstCol: c1, lastCol: c2, lastRow };
|
|
174
|
+
}
|
|
175
|
+
/** Every table after an edit, the gone ones dropped. */
|
|
176
|
+
export function shiftTables(tables, sheet, edit) {
|
|
177
|
+
return tables.map((t) => shiftTable(t, sheet, edit)).filter((t) => t !== null);
|
|
178
|
+
}
|
package/dist/sheet/tokenize.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* is what keeps absolute references absolute.
|
|
12
12
|
*/
|
|
13
13
|
import { type CellRef } from './address.js';
|
|
14
|
-
import { type BinaryOp } from './ast.js';
|
|
14
|
+
import { type BinaryOp, type SheetError } from './ast.js';
|
|
15
15
|
export type TableRefToken = {
|
|
16
16
|
t: 'table';
|
|
17
17
|
table: string | null;
|
|
@@ -28,13 +28,30 @@ export type Token = TableRefToken | {
|
|
|
28
28
|
} | {
|
|
29
29
|
t: 'bool';
|
|
30
30
|
v: boolean;
|
|
31
|
+
} | {
|
|
32
|
+
t: 'err';
|
|
33
|
+
v: SheetError;
|
|
31
34
|
} | {
|
|
32
35
|
t: 'ref';
|
|
33
36
|
ref: CellRef;
|
|
37
|
+
}
|
|
38
|
+
/** The spilled-range operator `A1#`: the anchor a spill is read from. */
|
|
39
|
+
| {
|
|
40
|
+
t: 'spill';
|
|
41
|
+
ref: CellRef;
|
|
34
42
|
} | {
|
|
35
43
|
t: 'range';
|
|
36
44
|
from: CellRef;
|
|
37
45
|
to: CellRef;
|
|
46
|
+
}
|
|
47
|
+
/** A 3D reference `Sheet1:Sheet3!A1`: the cell or rectangle on every sheet
|
|
48
|
+
* in the tab range. `from`/`to` carry no sheet of their own. */
|
|
49
|
+
| {
|
|
50
|
+
t: 'range3d';
|
|
51
|
+
sheetFrom: string;
|
|
52
|
+
sheetTo: string;
|
|
53
|
+
from: CellRef;
|
|
54
|
+
to: CellRef;
|
|
38
55
|
} | {
|
|
39
56
|
t: 'name';
|
|
40
57
|
v: string;
|
package/dist/sheet/tokenize.js
CHANGED
|
@@ -20,6 +20,9 @@ const SPECIFIERS = {
|
|
|
20
20
|
'#totals': '#Totals',
|
|
21
21
|
'#this row': '#ThisRow',
|
|
22
22
|
};
|
|
23
|
+
/** The error values a formula can name, longest first so #NUM! cannot win
|
|
24
|
+
* the prefix of a longer code. */
|
|
25
|
+
const ERROR_LITERAL = /^(#NULL!|#DIV\/0!|#VALUE!|#NAME\?|#NUM!|#N\/A|#REF!|#SPILL!|#CALC!|#CYCLE!)/i;
|
|
23
26
|
const TWO_CHAR_OPS = new Set(['<=', '>=', '<>']);
|
|
24
27
|
const ONE_CHAR_OPS = '+-*/^%&=<>';
|
|
25
28
|
export function tokenize(src) {
|
|
@@ -161,6 +164,15 @@ export function tokenize(src) {
|
|
|
161
164
|
out.push({ t: 'name', v: word });
|
|
162
165
|
return;
|
|
163
166
|
}
|
|
167
|
+
// `A1#` is the spilled-range operator: the `#` binds to the reference
|
|
168
|
+
// right before it, with no space between, and names the whole array the
|
|
169
|
+
// cell anchors. A `#` that starts an error literal is handled elsewhere;
|
|
170
|
+
// one sitting immediately after a cell is only ever this.
|
|
171
|
+
if (src[i] === '#') {
|
|
172
|
+
i += 1;
|
|
173
|
+
out.push({ t: 'spill', ref: left });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
164
176
|
out.push({ t: 'ref', ref: left });
|
|
165
177
|
};
|
|
166
178
|
while (i < src.length) {
|
|
@@ -252,34 +264,73 @@ export function tokenize(src) {
|
|
|
252
264
|
if (/[A-Za-z$_]/.test(ch)) {
|
|
253
265
|
const word = readWord();
|
|
254
266
|
const upper = word.toUpperCase();
|
|
267
|
+
// A 3D reference: Sheet1:Sheet3!A1, the same cell on every sheet in the
|
|
268
|
+
// tab range. The `!` after the second name is what tells it apart from
|
|
269
|
+
// an ordinary A1:B2 range, so on anything else this backtracks.
|
|
270
|
+
if (src[i] === ':') {
|
|
271
|
+
const save = i;
|
|
272
|
+
i += 1;
|
|
273
|
+
const second = readWord();
|
|
274
|
+
if (second !== '' && src[i] === '!') {
|
|
275
|
+
i += 1;
|
|
276
|
+
const from = parseA1(readWord(), null);
|
|
277
|
+
if (!from || from.row === null)
|
|
278
|
+
throw new FormulaError('#PARSE!');
|
|
279
|
+
let to = from;
|
|
280
|
+
if (src[i] === ':') {
|
|
281
|
+
i += 1;
|
|
282
|
+
const right = parseA1(readWord(), null);
|
|
283
|
+
if (!right || right.row === null)
|
|
284
|
+
throw new FormulaError('#PARSE!');
|
|
285
|
+
to = right;
|
|
286
|
+
}
|
|
287
|
+
out.push({ t: 'range3d', sheetFrom: word, sheetTo: second, from, to });
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
i = save;
|
|
291
|
+
}
|
|
255
292
|
// Unquoted sheet name: Orders!A1
|
|
256
293
|
if (src[i] === '!') {
|
|
257
294
|
i += 1;
|
|
258
295
|
pushWord(readWord(), word);
|
|
259
296
|
continue;
|
|
260
297
|
}
|
|
261
|
-
if (upper === 'TRUE') {
|
|
262
|
-
out.push({ t: 'bool', v: true });
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
if (upper === 'FALSE') {
|
|
266
|
-
out.push({ t: 'bool', v: false });
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
298
|
// A word followed by `[` is a table: Orders[Amount].
|
|
270
299
|
if (src[i] === '[') {
|
|
271
300
|
i += 1;
|
|
272
301
|
out.push(readTableBracket(word));
|
|
273
302
|
continue;
|
|
274
303
|
}
|
|
275
|
-
// A word followed by `(` is a call, whatever else it might look like
|
|
304
|
+
// A word followed by `(` is a call, whatever else it might look like -
|
|
305
|
+
// TRUE and FALSE included, since Excel has TRUE() and FALSE() as
|
|
306
|
+
// functions and that is how LibreOffice writes a boolean cell.
|
|
276
307
|
if (src[skipSpace()] === '(') {
|
|
277
308
|
out.push({ t: 'fn', v: upper });
|
|
278
309
|
continue;
|
|
279
310
|
}
|
|
311
|
+
if (upper === 'TRUE') {
|
|
312
|
+
out.push({ t: 'bool', v: true });
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (upper === 'FALSE') {
|
|
316
|
+
out.push({ t: 'bool', v: false });
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
280
319
|
pushWord(word, null);
|
|
281
320
|
continue;
|
|
282
321
|
}
|
|
322
|
+
// An error written into the formula: =#N/A, =IFERROR(A1, #REF!). Excel
|
|
323
|
+
// reads one as a value, and a file can carry an error cell as a formula
|
|
324
|
+
// that is nothing else.
|
|
325
|
+
if (ch === '#') {
|
|
326
|
+
const code = ERROR_LITERAL.exec(src.slice(i));
|
|
327
|
+
if (code) {
|
|
328
|
+
out.push({ t: 'err', v: code[0].toUpperCase() });
|
|
329
|
+
i += code[0].length;
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
throw new FormulaError('#PARSE!');
|
|
333
|
+
}
|
|
283
334
|
// A bare `[` is the unqualified form, which only means something in a
|
|
284
335
|
// formula sitting inside a table: [@Amount].
|
|
285
336
|
if (ch === '[') {
|
|
@@ -37,6 +37,12 @@ export type ValidationRule = {
|
|
|
37
37
|
title?: string;
|
|
38
38
|
message?: string;
|
|
39
39
|
};
|
|
40
|
+
/** Excel's Input Message: shown under the cell while it is selected.
|
|
41
|
+
* Absent when the rule has none. */
|
|
42
|
+
input?: {
|
|
43
|
+
title?: string;
|
|
44
|
+
message?: string;
|
|
45
|
+
};
|
|
40
46
|
};
|
|
41
47
|
/** How a rule reads the sheet: the shell wires the workbook in. */
|
|
42
48
|
export type ValidationContext = {
|
|
@@ -90,3 +96,13 @@ export declare function shiftValidation(rules: ReadonlyArray<ValidationRule>, ed
|
|
|
90
96
|
export declare function removeValidation(rules: ReadonlyArray<ValidationRule>, rects: ReadonlyArray<Rect>): ValidationRule[];
|
|
91
97
|
/** A short description for a rule, the way Excel's dialog title reads it. */
|
|
92
98
|
export declare function describeRule(rule: ValidationRule): string;
|
|
99
|
+
/**
|
|
100
|
+
* Excel's Circle Invalid Data: every cell under a rule whose current text
|
|
101
|
+
* breaks it. Blank cells count when the rule says they do. The rectangles
|
|
102
|
+
* are clipped to `rows` by `cols`, the sheet's used extent, so a rule over
|
|
103
|
+
* a whole column costs the cells that hold something.
|
|
104
|
+
*/
|
|
105
|
+
export declare function invalidCells(rules: ReadonlyArray<ValidationRule>, ctx: ValidationContext, rawAt: (row: number, col: number) => string, rows: number, cols: number): Array<{
|
|
106
|
+
row: number;
|
|
107
|
+
col: number;
|
|
108
|
+
}>;
|