@odoo/o-spreadsheet 14.0.1 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/dist/o_spreadsheet.cjs.js +31302 -0
  2. package/dist/o_spreadsheet.d.ts +6604 -0
  3. package/dist/o_spreadsheet.esm.js +31257 -0
  4. package/dist/o_spreadsheet.iife.js +31304 -0
  5. package/dist/o_spreadsheet.iife.min.js +3071 -0
  6. package/package.json +57 -15
  7. package/.eslintrc.json +0 -20
  8. package/.husky/post-checkout +0 -9
  9. package/.husky/pre-commit +0 -39
  10. package/.prettierignore +0 -1
  11. package/astToFormula.patch +0 -0
  12. package/demo/data.js +0 -255
  13. package/demo/index.html +0 -9
  14. package/demo/libs/Chart.bundle.js +0 -20755
  15. package/demo/main.js +0 -101
  16. package/doc/readme.md +0 -46
  17. package/rollup.config.js +0 -23
  18. package/src/base_plugin.ts +0 -102
  19. package/src/components/autofill.ts +0 -146
  20. package/src/components/bottom_bar.ts +0 -199
  21. package/src/components/color_picker.ts +0 -150
  22. package/src/components/composer/autocomplete_dropdown.ts +0 -135
  23. package/src/components/composer/composer.ts +0 -487
  24. package/src/components/composer/content_editable_helper.ts +0 -146
  25. package/src/components/figures/chart.ts +0 -70
  26. package/src/components/figures/container.ts +0 -268
  27. package/src/components/figures/text_figure.ts +0 -36
  28. package/src/components/grid.ts +0 -635
  29. package/src/components/helpers/dom_helpers.ts +0 -7
  30. package/src/components/helpers/drag_and_drop.ts +0 -17
  31. package/src/components/icons.ts +0 -36
  32. package/src/components/index.ts +0 -1
  33. package/src/components/menu.ts +0 -288
  34. package/src/components/overlay.ts +0 -457
  35. package/src/components/scrollbar.ts +0 -22
  36. package/src/components/selection_input.ts +0 -168
  37. package/src/components/side_panel/cell_is_rule_editor.ts +0 -169
  38. package/src/components/side_panel/chart_panel.ts +0 -104
  39. package/src/components/side_panel/color_scale_rule_editor.ts +0 -145
  40. package/src/components/side_panel/conditional_formatting.ts +0 -396
  41. package/src/components/side_panel/side_panel.ts +0 -127
  42. package/src/components/side_panel/translations_terms.ts +0 -45
  43. package/src/components/spreadsheet.ts +0 -198
  44. package/src/components/top_bar.ts +0 -535
  45. package/src/constants.ts +0 -26
  46. package/src/data.ts +0 -149
  47. package/src/fonts.ts +0 -41
  48. package/src/formulas/compiler.ts +0 -258
  49. package/src/formulas/composer_tokenizer.ts +0 -70
  50. package/src/formulas/index.ts +0 -15
  51. package/src/formulas/normalize.ts +0 -52
  52. package/src/formulas/parser.ts +0 -280
  53. package/src/formulas/range_tokenizer.ts +0 -97
  54. package/src/formulas/tokenizer.ts +0 -242
  55. package/src/functions/arguments.ts +0 -114
  56. package/src/functions/dates.ts +0 -302
  57. package/src/functions/helpers.ts +0 -592
  58. package/src/functions/index.ts +0 -52
  59. package/src/functions/module_database.ts +0 -282
  60. package/src/functions/module_date.ts +0 -743
  61. package/src/functions/module_info.ts +0 -69
  62. package/src/functions/module_logical.ts +0 -208
  63. package/src/functions/module_lookup.ts +0 -185
  64. package/src/functions/module_math.ts +0 -1252
  65. package/src/functions/module_operators.ts +0 -291
  66. package/src/functions/module_statistical.ts +0 -882
  67. package/src/functions/module_text.ts +0 -370
  68. package/src/helpers/color.ts +0 -30
  69. package/src/helpers/coordinates.ts +0 -71
  70. package/src/helpers/grid_manipulation.ts +0 -68
  71. package/src/helpers/index.ts +0 -7
  72. package/src/helpers/misc.ts +0 -76
  73. package/src/helpers/numbers.ts +0 -117
  74. package/src/helpers/uuid.ts +0 -19
  75. package/src/helpers/zones.ts +0 -263
  76. package/src/history.ts +0 -186
  77. package/src/index.ts +0 -84
  78. package/src/model.ts +0 -261
  79. package/src/plugins/autofill.ts +0 -412
  80. package/src/plugins/chart.ts +0 -404
  81. package/src/plugins/clipboard.ts +0 -461
  82. package/src/plugins/conditional_format.ts +0 -445
  83. package/src/plugins/core.ts +0 -1502
  84. package/src/plugins/edition.ts +0 -157
  85. package/src/plugins/evaluation.ts +0 -428
  86. package/src/plugins/figures.ts +0 -123
  87. package/src/plugins/formatting.ts +0 -630
  88. package/src/plugins/highlight.ts +0 -187
  89. package/src/plugins/index.ts +0 -32
  90. package/src/plugins/merge.ts +0 -524
  91. package/src/plugins/renderer.ts +0 -571
  92. package/src/plugins/selection.ts +0 -562
  93. package/src/plugins/selection_inputs.ts +0 -326
  94. package/src/registries/autofill_modifiers.ts +0 -76
  95. package/src/registries/autofill_rules.ts +0 -98
  96. package/src/registries/figure_registry.ts +0 -30
  97. package/src/registries/index.ts +0 -7
  98. package/src/registries/menu_items_registry.ts +0 -110
  99. package/src/registries/menus/cell_menu_registry.ts +0 -79
  100. package/src/registries/menus/col_menu_registry.ts +0 -63
  101. package/src/registries/menus/index.ts +0 -6
  102. package/src/registries/menus/menu_items_actions.ts +0 -425
  103. package/src/registries/menus/row_menu_registry.ts +0 -63
  104. package/src/registries/menus/sheet_menu_registry.ts +0 -75
  105. package/src/registries/menus/topbar_menu_registry.ts +0 -204
  106. package/src/registries/side_panel_registry.ts +0 -26
  107. package/src/registries/topbar_component_registry.ts +0 -12
  108. package/src/registry.ts +0 -51
  109. package/src/translation.ts +0 -44
  110. package/src/types/autofill.ts +0 -48
  111. package/src/types/chart.ts +0 -26
  112. package/src/types/commands.ts +0 -716
  113. package/src/types/conditional_formatting.ts +0 -124
  114. package/src/types/env.ts +0 -13
  115. package/src/types/functions.ts +0 -35
  116. package/src/types/getters.ts +0 -103
  117. package/src/types/index.ts +0 -20
  118. package/src/types/misc.ts +0 -143
  119. package/src/types/rendering.ts +0 -41
  120. package/src/types/workbook_data.ts +0 -62
  121. package/tests/__mocks__/uuid.ts +0 -7
  122. package/tests/canvas.mock.ts +0 -34
  123. package/tests/components/__mocks__/content_editable_helper.ts +0 -79
  124. package/tests/components/__mocks__/scrollbar.ts +0 -18
  125. package/tests/components/__snapshots__/autocomplete_dropdown_test.ts.snap +0 -29
  126. package/tests/components/__snapshots__/bottom_bar_test.ts.snap +0 -75
  127. package/tests/components/__snapshots__/context_menu_test.ts.snap +0 -88
  128. package/tests/components/__snapshots__/grid_test.ts.snap +0 -60
  129. package/tests/components/__snapshots__/spreadsheet_test.ts.snap +0 -455
  130. package/tests/components/__snapshots__/top_bar_test.ts.snap +0 -705
  131. package/tests/components/autocomplete_dropdown_test.ts +0 -315
  132. package/tests/components/autofill_test.ts +0 -167
  133. package/tests/components/bottom_bar_test.ts +0 -262
  134. package/tests/components/chart_side_panel_test.ts +0 -123
  135. package/tests/components/composer_test.ts +0 -400
  136. package/tests/components/context_menu_test.ts +0 -825
  137. package/tests/components/grid_manipulation_test.ts +0 -163
  138. package/tests/components/grid_test.ts +0 -581
  139. package/tests/components/overlay_test.ts +0 -541
  140. package/tests/components/selection_input_test.ts +0 -259
  141. package/tests/components/side_panel_test.ts +0 -148
  142. package/tests/components/spreadsheet_test.ts +0 -194
  143. package/tests/components/top_bar_test.ts +0 -427
  144. package/tests/data_test.ts +0 -175
  145. package/tests/dom_helper.ts +0 -42
  146. package/tests/formulas/__snapshots__/compiler_test.ts.snap +0 -302
  147. package/tests/formulas/compiler_test.ts +0 -266
  148. package/tests/formulas/composer_tokenizer_test.ts +0 -137
  149. package/tests/formulas/expressions_test.ts +0 -57
  150. package/tests/formulas/formulas_test.ts +0 -80
  151. package/tests/formulas/normalize_test.ts +0 -82
  152. package/tests/formulas/parser_test.ts +0 -224
  153. package/tests/formulas/range_tokenizer_test.ts +0 -187
  154. package/tests/formulas/tokenizer_test.ts +0 -164
  155. package/tests/functions/arguments_test.ts +0 -141
  156. package/tests/functions/dates_test.ts +0 -1081
  157. package/tests/functions/functions_test.ts +0 -53
  158. package/tests/functions/helper_test.ts +0 -98
  159. package/tests/functions/module_database_test.ts +0 -311
  160. package/tests/functions/module_date_test.ts +0 -941
  161. package/tests/functions/module_info_test.ts +0 -139
  162. package/tests/functions/module_logical_test.ts +0 -369
  163. package/tests/functions/module_lookup_test.ts +0 -541
  164. package/tests/functions/module_math_test.ts +0 -2482
  165. package/tests/functions/module_operators_test.ts +0 -955
  166. package/tests/functions/module_statistical_test.ts +0 -2014
  167. package/tests/functions/module_text_test.ts +0 -667
  168. package/tests/helpers/coordinates_test.ts +0 -25
  169. package/tests/helpers/misc_test.ts +0 -95
  170. package/tests/helpers/numbers_test.ts +0 -196
  171. package/tests/helpers/zones_test.ts +0 -97
  172. package/tests/helpers.ts +0 -384
  173. package/tests/menu_items_registry_test.ts +0 -540
  174. package/tests/model/plugins/__snapshots__/chart_test.ts.snap +0 -90
  175. package/tests/model/plugins/chart_test.ts +0 -562
  176. package/tests/model_test.ts +0 -82
  177. package/tests/plugins/__snapshots__/conditional_formatting_test.ts.snap +0 -131
  178. package/tests/plugins/__snapshots__/renderer_test.ts.snap +0 -371
  179. package/tests/plugins/autofill_test.ts +0 -457
  180. package/tests/plugins/borders_test.ts +0 -295
  181. package/tests/plugins/clipboard_test.ts +0 -1180
  182. package/tests/plugins/conditional_formatting_test.ts +0 -1253
  183. package/tests/plugins/core_test.ts +0 -614
  184. package/tests/plugins/edition_test.ts +0 -100
  185. package/tests/plugins/evaluation_async_test.ts +0 -332
  186. package/tests/plugins/evaluation_dates_test.ts +0 -79
  187. package/tests/plugins/evaluation_test.ts +0 -901
  188. package/tests/plugins/figures_test.ts +0 -327
  189. package/tests/plugins/formatting_test.ts +0 -391
  190. package/tests/plugins/grid_manipulation_test.ts +0 -1366
  191. package/tests/plugins/highlight_test.ts +0 -413
  192. package/tests/plugins/history_test.ts +0 -142
  193. package/tests/plugins/import_export_test.ts +0 -276
  194. package/tests/plugins/merges_test.ts +0 -490
  195. package/tests/plugins/navigation_test.ts +0 -181
  196. package/tests/plugins/plugins/evaluation_test.ts +0 -111
  197. package/tests/plugins/renderer_test.ts +0 -598
  198. package/tests/plugins/resizing_test.ts +0 -152
  199. package/tests/plugins/selection_input_test.ts +0 -409
  200. package/tests/plugins/selection_test.ts +0 -442
  201. package/tests/plugins/sheets_test.ts +0 -643
  202. package/tests/plugins/style_test.ts +0 -90
  203. package/tsconfig.json +0 -22
@@ -1,157 +0,0 @@
1
- import { BasePlugin } from "../base_plugin";
2
- import { tokenize } from "../formulas/index";
3
- import { toCartesian, toXC } from "../helpers/index";
4
- import { Mode } from "../model";
5
- import { Command, LAYERS } from "../types/index";
6
-
7
- export type EditionMode = "editing" | "selecting" | "inactive";
8
-
9
- export class EditionPlugin extends BasePlugin {
10
- static layers = [LAYERS.Highlights];
11
- static getters = ["getEditionMode", "getCurrentContent", "getEditionSheet"];
12
- static modes: Mode[] = ["normal", "readonly"];
13
-
14
- private col: number = 0;
15
- private row: number = 0;
16
- private mode: EditionMode = "inactive";
17
- private sheet: string = "";
18
- private currentContent: string = "";
19
-
20
- // ---------------------------------------------------------------------------
21
- // Command Handling
22
- // ---------------------------------------------------------------------------
23
-
24
- beforeHandle(cmd: Command) {
25
- switch (cmd.type) {
26
- case "ACTIVATE_SHEET":
27
- if (this.mode !== "selecting") {
28
- this.stopEdition();
29
- }
30
- break;
31
- }
32
- }
33
-
34
- handle(cmd: Command) {
35
- switch (cmd.type) {
36
- case "START_COMPOSER_SELECTION":
37
- this.mode = "selecting";
38
- this.dispatch("SET_SELECTION", {
39
- zones: this.getters.getSelectedZones(),
40
- anchor: this.getters.getPosition(),
41
- });
42
- break;
43
- case "STOP_COMPOSER_SELECTION":
44
- if (this.mode === "selecting") {
45
- this.mode = "editing";
46
- }
47
- break;
48
- case "START_EDITION":
49
- this.startEdition(cmd.text);
50
- break;
51
- case "STOP_EDITION":
52
- if (cmd.cancel) {
53
- this.cancelEdition();
54
- } else {
55
- this.stopEdition();
56
- }
57
- break;
58
- case "SET_CURRENT_CONTENT":
59
- this.currentContent = cmd.content;
60
- break;
61
- case "SELECT_CELL":
62
- case "MOVE_POSITION":
63
- if (this.mode === "editing") {
64
- this.stopEdition();
65
- }
66
- break;
67
- }
68
- }
69
-
70
- // ---------------------------------------------------------------------------
71
- // Getters
72
- // ---------------------------------------------------------------------------
73
-
74
- getEditionMode(): EditionMode {
75
- return this.mode;
76
- }
77
-
78
- getCurrentContent(): string {
79
- return this.currentContent;
80
- }
81
-
82
- getEditionSheet(): string {
83
- return this.sheet;
84
- }
85
-
86
- // ---------------------------------------------------------------------------
87
- // Misc
88
- // ---------------------------------------------------------------------------
89
-
90
- private startEdition(str?: string) {
91
- if (!str) {
92
- const cell = this.getters.getActiveCell();
93
- str = cell ? cell.content || "" : "";
94
- }
95
- this.mode = "editing";
96
- this.currentContent = str || "";
97
- this.dispatch("REMOVE_ALL_HIGHLIGHTS");
98
- const [col, row] = this.getters.getPosition();
99
- this.col = col;
100
- this.row = row;
101
- this.sheet = this.getters.getActiveSheet();
102
- }
103
-
104
- private stopEdition() {
105
- if (this.mode !== "inactive") {
106
- this.cancelEdition();
107
- let xc = toXC(this.col, this.row);
108
- const { mergeCellMap, merges, cells } = this.getters
109
- .getSheets()
110
- .find((sheet) => sheet.id === this.sheet)!;
111
- if (xc in mergeCellMap) {
112
- const mergeId = mergeCellMap[xc];
113
- xc = merges[mergeId].topLeft;
114
- }
115
- let content = this.currentContent;
116
- this.currentContent = "";
117
- const cell = cells[xc];
118
- const didChange = cell ? cell.content !== content : content !== "";
119
- if (!didChange) {
120
- return;
121
- }
122
- const [col, row] = toCartesian(xc);
123
- if (content) {
124
- if (content.startsWith("=")) {
125
- const tokens = tokenize(content);
126
- const left = tokens.filter((t) => t.type === "LEFT_PAREN").length;
127
- const right = tokens.filter((t) => t.type === "RIGHT_PAREN").length;
128
- const missing = left - right;
129
- if (missing > 0) {
130
- content += new Array(missing).fill(")").join("");
131
- }
132
- }
133
- this.dispatch("UPDATE_CELL", {
134
- sheet: this.sheet,
135
- col,
136
- row,
137
- content,
138
- });
139
- } else {
140
- this.dispatch("UPDATE_CELL", {
141
- sheet: this.sheet,
142
- content: "",
143
- col,
144
- row,
145
- });
146
- }
147
- if (this.getters.getActiveSheet() !== this.sheet) {
148
- this.dispatch("ACTIVATE_SHEET", { from: this.getters.getActiveSheet(), to: this.sheet });
149
- }
150
- }
151
- }
152
-
153
- private cancelEdition() {
154
- this.mode = "inactive";
155
- this.dispatch("REMOVE_ALL_HIGHLIGHTS");
156
- }
157
- }
@@ -1,428 +0,0 @@
1
- import { BasePlugin } from "../base_plugin";
2
- import { compile, normalize } from "../formulas/index";
3
- import { functionRegistry } from "../functions/index";
4
- import { mapCellsInZone, toCartesian, toXC, toZone } from "../helpers/index";
5
- import { WHistory } from "../history";
6
- import { Mode, ModelConfig } from "../model";
7
- import { _lt } from "../translation";
8
- import {
9
- Cell,
10
- Command,
11
- CommandDispatcher,
12
- EnsureRange,
13
- EvalContext,
14
- Getters,
15
- NormalizedFormula,
16
- ReferenceDenormalizer,
17
- UID,
18
- Workbook,
19
- } from "../types";
20
-
21
- function* makeObjectIterator(obj: Object) {
22
- for (let i in obj) {
23
- yield obj[i];
24
- }
25
- }
26
-
27
- function* makeSetIterator(set: Set<any>) {
28
- for (let elem of set) {
29
- yield elem;
30
- }
31
- }
32
-
33
- const functionMap = functionRegistry.mapping;
34
-
35
- type FormulaParameters = [ReferenceDenormalizer, EnsureRange, EvalContext];
36
-
37
- export const LOADING = "Loading...";
38
-
39
- export class EvaluationPlugin extends BasePlugin {
40
- static getters = ["evaluateFormula", "isIdle"];
41
- static modes: Mode[] = ["normal", "readonly"];
42
-
43
- private isUptodate: Set<string> = new Set();
44
- private loadingCells: number = 0;
45
- private isStarted: boolean = false;
46
- private evalContext: EvalContext;
47
-
48
- /**
49
- * For all cells that are being currently computed (asynchronously).
50
- *
51
- * For example: =Wait(3)
52
- */
53
- private PENDING: Set<Cell> = new Set();
54
-
55
- /**
56
- * For all cells that are NOT being currently computed, but depend on another
57
- * asynchronous computation.
58
- *
59
- * For example: A2 is in WAITING (initially) and A1 in PENDING
60
- * A1: =Wait(3)
61
- * A2: =A1
62
- */
63
- private WAITING: { [key: string]: Set<Cell> } = {};
64
-
65
- /**
66
- * For all cells that have been async computed.
67
- *
68
- * For example:
69
- * A1: =Wait(3)
70
- * A2: =A1
71
- *
72
- * When A1 is computed, A1 is moved in COMPUTED
73
- */
74
- private COMPUTED: Set<Cell> = new Set();
75
-
76
- constructor(
77
- workbook: Workbook,
78
- getters: Getters,
79
- history: WHistory,
80
- dispatch: CommandDispatcher["dispatch"],
81
- config: ModelConfig
82
- ) {
83
- super(workbook, getters, history, dispatch, config);
84
- this.evalContext = config.evalContext;
85
- }
86
-
87
- // ---------------------------------------------------------------------------
88
- // Command Handling
89
- // ---------------------------------------------------------------------------
90
-
91
- handle(cmd: Command) {
92
- switch (cmd.type) {
93
- case "START":
94
- this.evaluate();
95
- break;
96
- case "UPDATE_CELL":
97
- if ("content" in cmd) {
98
- this.isUptodate.clear();
99
- }
100
- break;
101
- case "EVALUATE_CELLS":
102
- if (cmd.onlyWaiting) {
103
- for (const sheetId in this.WAITING) {
104
- const cells = new Set(this.WAITING[sheetId]);
105
- this.WAITING[sheetId].clear();
106
- this.evaluateCells(makeSetIterator(cells), sheetId);
107
- }
108
- } else {
109
- for (const sheetId in this.WAITING) {
110
- delete this.WAITING[sheetId];
111
- }
112
- this.evaluate();
113
- }
114
- this.isUptodate.add(this.workbook.activeSheet.id);
115
- break;
116
- case "UNDO":
117
- case "REDO":
118
- this.isUptodate.clear();
119
- break;
120
- }
121
- }
122
-
123
- finalize() {
124
- if (!this.isUptodate.has(this.workbook.activeSheet.id)) {
125
- this.evaluate();
126
- this.isUptodate.add(this.workbook.activeSheet.id);
127
- }
128
- if (this.loadingCells > 0) {
129
- this.startScheduler();
130
- }
131
- }
132
-
133
- // ---------------------------------------------------------------------------
134
- // Getters
135
- // ---------------------------------------------------------------------------
136
-
137
- evaluateFormula(formula: string, sheet: UID = this.workbook.activeSheet.id): any {
138
- let formulaString: NormalizedFormula = normalize(formula);
139
-
140
- const compiledFormula = compile(formulaString);
141
- const params = this.getFormulaParameters(() => {});
142
- return compiledFormula(formulaString.dependencies, sheet, ...params);
143
- }
144
-
145
- isIdle() {
146
- return this.loadingCells === 0;
147
- }
148
-
149
- // ---------------------------------------------------------------------------
150
- // Scheduler
151
- // ---------------------------------------------------------------------------
152
-
153
- private startScheduler() {
154
- if (!this.isStarted) {
155
- this.isStarted = true;
156
- let current = this.loadingCells;
157
- const recomputeCells = () => {
158
- if (this.loadingCells !== current) {
159
- this.dispatch("EVALUATE_CELLS", { onlyWaiting: true });
160
- current = this.loadingCells;
161
- if (current === 0) {
162
- this.isStarted = false;
163
- }
164
- }
165
- if (current > 0) {
166
- window.setTimeout(recomputeCells, 15);
167
- }
168
- };
169
- window.setTimeout(recomputeCells, 5);
170
- }
171
- }
172
-
173
- // ---------------------------------------------------------------------------
174
- // Evaluator
175
- // ---------------------------------------------------------------------------
176
-
177
- private evaluate() {
178
- this.COMPUTED.clear();
179
- this.evaluateCells(
180
- makeObjectIterator(this.workbook.activeSheet.cells),
181
- this.workbook.activeSheet.id
182
- );
183
- }
184
-
185
- private evaluateCells(cells: Generator<Cell>, sheetId: string) {
186
- const self = this;
187
- const { COMPUTED, PENDING, WAITING } = this;
188
- const params = this.getFormulaParameters(computeValue);
189
- const visited: { [sheetId: string]: { [xc: string]: boolean | null } } = {};
190
-
191
- const _cells = [...cells];
192
-
193
- for (let cell of _cells) {
194
- // Do not reset error when it's during the compilation
195
- if (cell.value !== "#BAD_EXPR") {
196
- cell.error = undefined;
197
- }
198
- }
199
-
200
- for (let cell of _cells) {
201
- computeValue(cell, sheetId);
202
- }
203
-
204
- function handleError(e: Error | any, currentCell: Cell, currentSheetId: string) {
205
- if (!(e instanceof Error)) {
206
- e = new Error(e);
207
- }
208
- if (PENDING.has(currentCell)) {
209
- PENDING.delete(currentCell);
210
- self.loadingCells--;
211
- }
212
- if (e.message === "not ready") {
213
- if (!WAITING[currentSheetId]) {
214
- WAITING[currentSheetId] = new Set<Cell>();
215
- }
216
- WAITING[currentSheetId].add(currentCell);
217
- currentCell.pending = true;
218
- currentCell.value = LOADING;
219
- } else if (!currentCell.error) {
220
- currentCell.value = "#ERROR";
221
- const __lastFnCalled = params[2].__lastFnCalled || "";
222
- currentCell.error = e.message.replace("[[FUNCTION_NAME]]", __lastFnCalled);
223
- }
224
- }
225
-
226
- function computeValue(cell: Cell, sheetId: string) {
227
- if (cell.type !== "formula" || !cell.formula) {
228
- return;
229
- }
230
- const xc = cell.xc;
231
- visited[sheetId] = visited[sheetId] || {};
232
- if (xc in visited[sheetId]) {
233
- if (visited[sheetId][xc] === null) {
234
- cell.value = "#CYCLE";
235
- cell.error = _lt("Circular reference");
236
- }
237
- return;
238
- }
239
- if (COMPUTED.has(cell) || PENDING.has(cell)) {
240
- return;
241
- }
242
- visited[sheetId][xc] = null;
243
- cell.error = undefined;
244
- try {
245
- params[2].__originCellXC = xc;
246
- // todo: move formatting in grid and formatters.js
247
- if (cell.formula.compiledFormula.async) {
248
- cell.value = LOADING;
249
- cell.pending = true;
250
- PENDING.add(cell);
251
-
252
- cell.formula
253
- .compiledFormula(cell.formula.dependencies, sheetId, ...params)
254
- .then((val) => {
255
- cell.value = val;
256
- self.loadingCells--;
257
- if (PENDING.has(cell)) {
258
- PENDING.delete(cell);
259
- cell.pending = false;
260
- COMPUTED.add(cell);
261
- }
262
- })
263
- .catch((e: Error) => handleError(e, cell, sheetId));
264
- self.loadingCells++;
265
- } else {
266
- cell.value = cell.formula.compiledFormula(cell.formula.dependencies, sheetId, ...params);
267
- cell.pending = false;
268
- }
269
- if (Array.isArray(cell.value)) {
270
- // if a value returns an array (like =A1:A3)
271
- throw new Error(_lt("This formula depends on invalid values"));
272
- } else {
273
- cell.error = undefined;
274
- }
275
- } catch (e) {
276
- handleError(e, cell, sheetId);
277
- }
278
- visited[sheetId][xc] = true;
279
- }
280
- }
281
-
282
- /**
283
- * Return all functions necessary to properly evaluate a formula:
284
- * - a refFn function to read any reference, cell or range of a normalized formula
285
- * - a range function to convert any reference to a proper value array
286
- * - an evaluation context
287
- */
288
- private getFormulaParameters(computeValue: Function): FormulaParameters {
289
- const evalContext = Object.assign(Object.create(functionMap), this.evalContext, {
290
- getters: this.getters,
291
- });
292
- const sheets = this.workbook.sheets;
293
- const PENDING = this.PENDING;
294
- function readCell(xc: string, sheetId: UID): any {
295
- let cell;
296
- const s = sheets[sheetId];
297
- if (s) {
298
- cell = s.cells[xc.toUpperCase().replace(/\$/g, "")];
299
- } else {
300
- throw new Error(_lt("Invalid sheet name"));
301
- }
302
- if (!cell || cell.content === "") {
303
- return null;
304
- }
305
- const value = getCellValue(cell, sheetId);
306
- if (value === LOADING) {
307
- throw new Error("not ready");
308
- }
309
- return value;
310
- }
311
-
312
- function getCellValue(cell: Cell, sheetId: UID): any {
313
- if (cell.formula && cell.formula.compiledFormula.async && cell.error && !PENDING.has(cell)) {
314
- throw new Error(_lt("This formula depends on invalid values"));
315
- }
316
- computeValue(cell, sheetId);
317
- if (cell.error) {
318
- throw new Error(_lt("This formula depends on invalid values"));
319
- }
320
- return cell.value;
321
- }
322
-
323
- /**
324
- * Return a range of values. It is a list of col values.
325
- *
326
- * Note that each col is possibly sparse: it only contain the values of cells
327
- * that are actually present in the grid.
328
- */
329
- function _range(v1: string, v2: string, sheetId: UID): any[][] {
330
- const sheet = sheets[sheetId];
331
- let [left, top] = toCartesian(v1);
332
- let [right, bottom] = toCartesian(v2);
333
- right = Math.min(right, sheet.colNumber - 1);
334
- bottom = Math.min(bottom, sheet.rowNumber - 1);
335
-
336
- if (left > right) {
337
- const tmp = left;
338
- left = right;
339
- right = tmp;
340
- }
341
- if (top > bottom) {
342
- const tmp = top;
343
- top = bottom;
344
- bottom = tmp;
345
- }
346
-
347
- const zone = { left, top, right, bottom };
348
- const result = mapCellsInZone(zone, sheet, (cell) => getCellValue(cell, sheetId));
349
- for (const col of result) {
350
- for (const value of col) {
351
- if (value === LOADING) {
352
- throw new Error("not ready");
353
- }
354
- }
355
- }
356
- return result;
357
- }
358
-
359
- /**
360
- * Returns the value of the cell(s) used in reference
361
- *
362
- * @param position the index in the references array
363
- * @param references all the references used in the current formula
364
- * @param sheetId the sheet that is currently being evaluated, if a reference does not
365
- * include a sheet, it is the id of the sheet of the reference to be used
366
- */
367
- function refFn(
368
- position: number,
369
- references: string[],
370
- sheetId: UID,
371
- functionName: string,
372
- paramNumber: number
373
- ): any | any[][] {
374
- const referenceText = references[position];
375
-
376
- const [reference, sheetName] = referenceText.split("!").reverse();
377
- const referenceSheetId = sheetName
378
- ? evalContext.getters.getSheetIdByName(sheetName)
379
- : sheetId;
380
-
381
- const zone = toZone(referenceText);
382
-
383
- if (zone.top > zone.bottom || zone.left > zone.right) {
384
- throw new Error(
385
- _lt("invalid range %s:%s", toXC(zone.left, zone.top), toXC(zone.right, zone.bottom))
386
- );
387
- }
388
-
389
- // if the formula definition could have accepted a range, we would pass through the _range function and not here
390
- if (zone.bottom !== zone.top || zone.left !== zone.right) {
391
- throw new Error(
392
- _lt(
393
- "Function %s expects the parameter %s to be a single value or a single cell reference, not a range.",
394
- functionName.toString(),
395
- paramNumber.toString()
396
- )
397
- );
398
- }
399
- //it's a cell
400
- return readCell(reference, referenceSheetId);
401
- }
402
-
403
- /**
404
- * Return the values of the cell(s) used in reference, but always in the format of a range even
405
- * if a single cell is referenced. This is useful for the formulas that describe parameters as
406
- * range<number> etc.
407
- *
408
- * the parameters are the same as refFn, except that these parameters cannot be Meta
409
- */
410
- function range(position: number, references: string[], sheetId: UID): any[][] {
411
- const referenceText = references[position];
412
- const [reference, sheetName] = referenceText.split("!").reverse();
413
- const referenceSheetId = sheetName
414
- ? evalContext.getters.getSheetIdByName(sheetName)
415
- : sheetId;
416
-
417
- if (references[position] && !references[position].includes(":")) {
418
- //it's a cell reference, but it must be treated as a range
419
- return _range(reference, reference, referenceSheetId);
420
- } else {
421
- const [left, right] = reference.split(":");
422
- return _range(left, right, referenceSheetId);
423
- }
424
- }
425
-
426
- return [refFn, range, evalContext];
427
- }
428
- }
@@ -1,123 +0,0 @@
1
- import { BasePlugin } from "../base_plugin";
2
- import { Command, Figure, Viewport, WorkbookData } from "../types/index";
3
-
4
- export class FigurePlugin extends BasePlugin {
5
- static getters = ["getFigures", "getSelectedFigureId", "getFigure"];
6
-
7
- private selectedFigureId: string | null = null;
8
-
9
- private figures: { [figId: string]: Figure<any> } = {};
10
- private sheetFigures: { [sheetId: string]: Figure<any>[] } = {};
11
-
12
- // ---------------------------------------------------------------------------
13
- // Command Handling
14
- // ---------------------------------------------------------------------------
15
- handle(cmd: Command) {
16
- switch (cmd.type) {
17
- case "DELETE_SHEET":
18
- this.deleteSheet(cmd.sheet);
19
- break;
20
- case "CREATE_FIGURE":
21
- this.history.updateLocalState(["figures", cmd.figure.id], cmd.figure);
22
- const sheetFigures = (this.sheetFigures[cmd.sheet] || []).slice();
23
- sheetFigures.push(cmd.figure);
24
- this.history.updateLocalState(["sheetFigures", cmd.sheet], sheetFigures);
25
- break;
26
- case "UPDATE_FIGURE":
27
- if (cmd.x !== undefined) {
28
- this.history.updateLocalState(["figures", cmd.id, "x"], Math.max(cmd.x, 0));
29
- }
30
- if (cmd.y !== undefined) {
31
- this.history.updateLocalState(["figures", cmd.id, "y"], Math.max(cmd.y, 0));
32
- }
33
- if (cmd.width !== undefined) {
34
- this.history.updateLocalState(["figures", cmd.id, "width"], cmd.width);
35
- }
36
- if (cmd.height !== undefined) {
37
- this.history.updateLocalState(["figures", cmd.id, "height"], cmd.height);
38
- }
39
- if (cmd.data !== undefined) {
40
- this.history.updateLocalState(["figures", cmd.id, "data"], cmd.data);
41
- }
42
- break;
43
- case "SELECT_FIGURE":
44
- this.selectedFigureId = cmd.id;
45
- break;
46
- case "DELETE_FIGURE":
47
- this.history.updateLocalState(["figures", cmd.id], undefined);
48
- for (let s in this.sheetFigures) {
49
- let deletedFigureIndex = this.sheetFigures[s].findIndex((f) => f.id === cmd.id);
50
- if (deletedFigureIndex > -1) {
51
- const copy = this.sheetFigures[s].slice();
52
- copy.splice(deletedFigureIndex, 1);
53
- this.history.updateLocalState(["sheetFigures", s], copy);
54
- this.selectedFigureId = null;
55
- }
56
- }
57
- break;
58
- // some commands should not remove the current selection
59
- case "EVALUATE_CELLS":
60
- break;
61
- default:
62
- this.selectedFigureId = null;
63
- }
64
- }
65
-
66
- deleteSheet(sheet: string) {
67
- for (let figure of this.sheetFigures[sheet] || []) {
68
- this.history.updateLocalState(["figures", figure.id], undefined);
69
- }
70
- const sheetFigures = Object.assign({}, this.sheetFigures);
71
- delete sheetFigures[sheet];
72
- this.history.updateLocalState(["sheetFigures"], sheetFigures);
73
- }
74
-
75
- // ---------------------------------------------------------------------------
76
- // Getters
77
- // ---------------------------------------------------------------------------
78
-
79
- getFigures(viewport: Viewport): Figure<any>[] {
80
- const result: Figure<any>[] = [];
81
- const figures = this.sheetFigures[this.workbook.activeSheet.id] || [];
82
- const { offsetX, offsetY, width, height } = viewport;
83
- for (let figure of figures) {
84
- if (figure.x >= offsetX + width || figure.x + figure.width <= offsetX) {
85
- continue;
86
- }
87
- if (figure.y >= offsetY + height || figure.y + figure.height <= offsetY) {
88
- continue;
89
- }
90
- result.push(figure);
91
- }
92
- return result;
93
- }
94
-
95
- getSelectedFigureId(): string | null {
96
- return this.selectedFigureId;
97
- }
98
-
99
- getFigure<T>(figureId: string): Figure<T> {
100
- return this.figures[figureId];
101
- }
102
-
103
- // ---------------------------------------------------------------------------
104
- // Import/Export
105
- // ---------------------------------------------------------------------------
106
-
107
- import(data: WorkbookData) {
108
- for (let sheet of data.sheets) {
109
- const figList: Figure<any>[] = [];
110
- for (let f of sheet.figures) {
111
- this.figures[f.id] = f;
112
- figList.push(f);
113
- }
114
- this.sheetFigures[sheet.id] = figList;
115
- }
116
- }
117
-
118
- export(data: WorkbookData) {
119
- for (let sheetData of data.sheets) {
120
- sheetData.figures = this.sheetFigures[sheetData.id] || [];
121
- }
122
- }
123
- }