@odoo/o-spreadsheet 14.0.0 → 14.0.1

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 (198) hide show
  1. package/.eslintrc.json +20 -0
  2. package/.husky/post-checkout +9 -0
  3. package/.husky/pre-commit +39 -0
  4. package/.prettierignore +1 -0
  5. package/astToFormula.patch +0 -0
  6. package/demo/data.js +255 -0
  7. package/demo/index.html +9 -0
  8. package/demo/libs/Chart.bundle.js +20755 -0
  9. package/demo/main.js +101 -0
  10. package/doc/readme.md +46 -0
  11. package/package.json +3 -17
  12. package/rollup.config.js +23 -0
  13. package/src/base_plugin.ts +102 -0
  14. package/src/components/autofill.ts +146 -0
  15. package/src/components/bottom_bar.ts +199 -0
  16. package/src/components/color_picker.ts +150 -0
  17. package/src/components/composer/autocomplete_dropdown.ts +135 -0
  18. package/src/components/composer/composer.ts +487 -0
  19. package/src/components/composer/content_editable_helper.ts +146 -0
  20. package/src/components/figures/chart.ts +70 -0
  21. package/src/components/figures/container.ts +268 -0
  22. package/src/components/figures/text_figure.ts +36 -0
  23. package/src/components/grid.ts +635 -0
  24. package/src/components/helpers/dom_helpers.ts +7 -0
  25. package/src/components/helpers/drag_and_drop.ts +17 -0
  26. package/src/components/icons.ts +36 -0
  27. package/src/components/index.ts +1 -0
  28. package/src/components/menu.ts +288 -0
  29. package/src/components/overlay.ts +457 -0
  30. package/src/components/scrollbar.ts +22 -0
  31. package/src/components/selection_input.ts +168 -0
  32. package/src/components/side_panel/cell_is_rule_editor.ts +169 -0
  33. package/src/components/side_panel/chart_panel.ts +104 -0
  34. package/src/components/side_panel/color_scale_rule_editor.ts +145 -0
  35. package/src/components/side_panel/conditional_formatting.ts +396 -0
  36. package/src/components/side_panel/side_panel.ts +127 -0
  37. package/src/components/side_panel/translations_terms.ts +45 -0
  38. package/src/components/spreadsheet.ts +198 -0
  39. package/src/components/top_bar.ts +535 -0
  40. package/src/constants.ts +26 -0
  41. package/src/data.ts +149 -0
  42. package/src/fonts.ts +41 -0
  43. package/src/formulas/compiler.ts +258 -0
  44. package/src/formulas/composer_tokenizer.ts +70 -0
  45. package/src/formulas/index.ts +15 -0
  46. package/src/formulas/normalize.ts +52 -0
  47. package/src/formulas/parser.ts +280 -0
  48. package/src/formulas/range_tokenizer.ts +97 -0
  49. package/src/formulas/tokenizer.ts +242 -0
  50. package/src/functions/arguments.ts +114 -0
  51. package/src/functions/dates.ts +302 -0
  52. package/src/functions/helpers.ts +592 -0
  53. package/src/functions/index.ts +52 -0
  54. package/src/functions/module_database.ts +282 -0
  55. package/src/functions/module_date.ts +743 -0
  56. package/src/functions/module_info.ts +69 -0
  57. package/src/functions/module_logical.ts +208 -0
  58. package/src/functions/module_lookup.ts +185 -0
  59. package/src/functions/module_math.ts +1252 -0
  60. package/src/functions/module_operators.ts +291 -0
  61. package/src/functions/module_statistical.ts +882 -0
  62. package/src/functions/module_text.ts +370 -0
  63. package/src/helpers/color.ts +30 -0
  64. package/src/helpers/coordinates.ts +71 -0
  65. package/src/helpers/grid_manipulation.ts +68 -0
  66. package/src/helpers/index.ts +7 -0
  67. package/src/helpers/misc.ts +76 -0
  68. package/src/helpers/numbers.ts +117 -0
  69. package/src/helpers/uuid.ts +19 -0
  70. package/src/helpers/zones.ts +263 -0
  71. package/src/history.ts +186 -0
  72. package/src/index.ts +84 -0
  73. package/src/model.ts +261 -0
  74. package/src/plugins/autofill.ts +412 -0
  75. package/src/plugins/chart.ts +404 -0
  76. package/src/plugins/clipboard.ts +461 -0
  77. package/src/plugins/conditional_format.ts +445 -0
  78. package/src/plugins/core.ts +1502 -0
  79. package/src/plugins/edition.ts +157 -0
  80. package/src/plugins/evaluation.ts +428 -0
  81. package/src/plugins/figures.ts +123 -0
  82. package/src/plugins/formatting.ts +630 -0
  83. package/src/plugins/highlight.ts +187 -0
  84. package/src/plugins/index.ts +32 -0
  85. package/src/plugins/merge.ts +524 -0
  86. package/src/plugins/renderer.ts +571 -0
  87. package/src/plugins/selection.ts +562 -0
  88. package/src/plugins/selection_inputs.ts +326 -0
  89. package/src/registries/autofill_modifiers.ts +76 -0
  90. package/src/registries/autofill_rules.ts +98 -0
  91. package/src/registries/figure_registry.ts +30 -0
  92. package/src/registries/index.ts +7 -0
  93. package/src/registries/menu_items_registry.ts +110 -0
  94. package/src/registries/menus/cell_menu_registry.ts +79 -0
  95. package/src/registries/menus/col_menu_registry.ts +63 -0
  96. package/src/registries/menus/index.ts +6 -0
  97. package/src/registries/menus/menu_items_actions.ts +425 -0
  98. package/src/registries/menus/row_menu_registry.ts +63 -0
  99. package/src/registries/menus/sheet_menu_registry.ts +75 -0
  100. package/src/registries/menus/topbar_menu_registry.ts +204 -0
  101. package/src/registries/side_panel_registry.ts +26 -0
  102. package/src/registries/topbar_component_registry.ts +12 -0
  103. package/src/registry.ts +51 -0
  104. package/src/translation.ts +44 -0
  105. package/src/types/autofill.ts +48 -0
  106. package/src/types/chart.ts +26 -0
  107. package/src/types/commands.ts +716 -0
  108. package/src/types/conditional_formatting.ts +124 -0
  109. package/src/types/env.ts +13 -0
  110. package/src/types/functions.ts +35 -0
  111. package/src/types/getters.ts +103 -0
  112. package/src/types/index.ts +20 -0
  113. package/src/types/misc.ts +143 -0
  114. package/src/types/rendering.ts +41 -0
  115. package/src/types/workbook_data.ts +62 -0
  116. package/tests/__mocks__/uuid.ts +7 -0
  117. package/tests/canvas.mock.ts +34 -0
  118. package/tests/components/__mocks__/content_editable_helper.ts +79 -0
  119. package/tests/components/__mocks__/scrollbar.ts +18 -0
  120. package/tests/components/__snapshots__/autocomplete_dropdown_test.ts.snap +29 -0
  121. package/tests/components/__snapshots__/bottom_bar_test.ts.snap +75 -0
  122. package/tests/components/__snapshots__/context_menu_test.ts.snap +88 -0
  123. package/tests/components/__snapshots__/grid_test.ts.snap +60 -0
  124. package/tests/components/__snapshots__/spreadsheet_test.ts.snap +455 -0
  125. package/tests/components/__snapshots__/top_bar_test.ts.snap +705 -0
  126. package/tests/components/autocomplete_dropdown_test.ts +315 -0
  127. package/tests/components/autofill_test.ts +167 -0
  128. package/tests/components/bottom_bar_test.ts +262 -0
  129. package/tests/components/chart_side_panel_test.ts +123 -0
  130. package/tests/components/composer_test.ts +400 -0
  131. package/tests/components/context_menu_test.ts +825 -0
  132. package/tests/components/grid_manipulation_test.ts +163 -0
  133. package/tests/components/grid_test.ts +581 -0
  134. package/tests/components/overlay_test.ts +541 -0
  135. package/tests/components/selection_input_test.ts +259 -0
  136. package/tests/components/side_panel_test.ts +148 -0
  137. package/tests/components/spreadsheet_test.ts +194 -0
  138. package/tests/components/top_bar_test.ts +427 -0
  139. package/tests/data_test.ts +175 -0
  140. package/tests/dom_helper.ts +42 -0
  141. package/tests/formulas/__snapshots__/compiler_test.ts.snap +302 -0
  142. package/tests/formulas/compiler_test.ts +266 -0
  143. package/tests/formulas/composer_tokenizer_test.ts +137 -0
  144. package/tests/formulas/expressions_test.ts +57 -0
  145. package/tests/formulas/formulas_test.ts +80 -0
  146. package/tests/formulas/normalize_test.ts +82 -0
  147. package/tests/formulas/parser_test.ts +224 -0
  148. package/tests/formulas/range_tokenizer_test.ts +187 -0
  149. package/tests/formulas/tokenizer_test.ts +164 -0
  150. package/tests/functions/arguments_test.ts +141 -0
  151. package/tests/functions/dates_test.ts +1081 -0
  152. package/tests/functions/functions_test.ts +53 -0
  153. package/tests/functions/helper_test.ts +98 -0
  154. package/tests/functions/module_database_test.ts +311 -0
  155. package/tests/functions/module_date_test.ts +941 -0
  156. package/tests/functions/module_info_test.ts +139 -0
  157. package/tests/functions/module_logical_test.ts +369 -0
  158. package/tests/functions/module_lookup_test.ts +541 -0
  159. package/tests/functions/module_math_test.ts +2482 -0
  160. package/tests/functions/module_operators_test.ts +955 -0
  161. package/tests/functions/module_statistical_test.ts +2014 -0
  162. package/tests/functions/module_text_test.ts +667 -0
  163. package/tests/helpers/coordinates_test.ts +25 -0
  164. package/tests/helpers/misc_test.ts +95 -0
  165. package/tests/helpers/numbers_test.ts +196 -0
  166. package/tests/helpers/zones_test.ts +97 -0
  167. package/tests/helpers.ts +384 -0
  168. package/tests/menu_items_registry_test.ts +540 -0
  169. package/tests/model/plugins/__snapshots__/chart_test.ts.snap +90 -0
  170. package/tests/model/plugins/chart_test.ts +562 -0
  171. package/tests/model_test.ts +82 -0
  172. package/tests/plugins/__snapshots__/conditional_formatting_test.ts.snap +131 -0
  173. package/tests/plugins/__snapshots__/renderer_test.ts.snap +371 -0
  174. package/tests/plugins/autofill_test.ts +457 -0
  175. package/tests/plugins/borders_test.ts +295 -0
  176. package/tests/plugins/clipboard_test.ts +1180 -0
  177. package/tests/plugins/conditional_formatting_test.ts +1253 -0
  178. package/tests/plugins/core_test.ts +614 -0
  179. package/tests/plugins/edition_test.ts +100 -0
  180. package/tests/plugins/evaluation_async_test.ts +332 -0
  181. package/tests/plugins/evaluation_dates_test.ts +79 -0
  182. package/tests/plugins/evaluation_test.ts +901 -0
  183. package/tests/plugins/figures_test.ts +327 -0
  184. package/tests/plugins/formatting_test.ts +391 -0
  185. package/tests/plugins/grid_manipulation_test.ts +1366 -0
  186. package/tests/plugins/highlight_test.ts +413 -0
  187. package/tests/plugins/history_test.ts +142 -0
  188. package/tests/plugins/import_export_test.ts +276 -0
  189. package/tests/plugins/merges_test.ts +490 -0
  190. package/tests/plugins/navigation_test.ts +181 -0
  191. package/tests/plugins/plugins/evaluation_test.ts +111 -0
  192. package/tests/plugins/renderer_test.ts +598 -0
  193. package/tests/plugins/resizing_test.ts +152 -0
  194. package/tests/plugins/selection_input_test.ts +409 -0
  195. package/tests/plugins/selection_test.ts +442 -0
  196. package/tests/plugins/sheets_test.ts +643 -0
  197. package/tests/plugins/style_test.ts +90 -0
  198. package/tsconfig.json +22 -0
@@ -0,0 +1,535 @@
1
+ import * as owl from "@odoo/owl";
2
+ import { DEFAULT_FONT_SIZE } from "../constants";
3
+ import { fontSizes } from "../fonts";
4
+ import { isEqual } from "../helpers/index";
5
+ import { setFormatter, setStyle, topbarComponentRegistry } from "../registries/index";
6
+ import { topbarMenuRegistry } from "../registries/menus/topbar_menu_registry";
7
+ import { FullMenuItem } from "../registries/menu_items_registry";
8
+ import { Align, BorderCommand, SpreadsheetEnv, Style } from "../types/index";
9
+ import { ColorPicker } from "./color_picker";
10
+ import { isChildEvent } from "./helpers/dom_helpers";
11
+ import * as icons from "./icons";
12
+ import { Menu, MenuState } from "./menu";
13
+
14
+ const { Component, useState, hooks } = owl;
15
+ const { xml, css } = owl.tags;
16
+ const { useExternalListener, useRef } = hooks;
17
+
18
+ type Tool =
19
+ | ""
20
+ | "formatTool"
21
+ | "alignTool"
22
+ | "textColorTool"
23
+ | "fillColorTool"
24
+ | "borderTool"
25
+ | "fontSizeTool";
26
+
27
+ interface State {
28
+ menuState: MenuState;
29
+ activeTool: Tool;
30
+ }
31
+
32
+ const FORMATS = [
33
+ { name: "auto", text: "Automatic" },
34
+ { name: "number", text: "Number (1,000.12)", value: "#,##0.00" },
35
+ { name: "percent", text: "Percent (10.12%)", value: "0.00%" },
36
+ { name: "date", text: "Date (9/26/2008)", value: "m/d/yyyy" },
37
+ { name: "time", text: "Time (10:43:00 PM)", value: "hh:mm:ss a" },
38
+ { name: "datetime", text: "Date time (9/26/2008 22:43:00)", value: "m/d/yyyy hh:mm:ss" },
39
+ { name: "duration", text: "Duration (27:51:38)", value: "hhhh:mm:ss" },
40
+ ];
41
+
42
+ // -----------------------------------------------------------------------------
43
+ // TopBar
44
+ // -----------------------------------------------------------------------------
45
+ export class TopBar extends Component<any, SpreadsheetEnv> {
46
+ static template = xml/* xml */ `
47
+ <div class="o-spreadsheet-topbar">
48
+ <div class="o-topbar-top">
49
+ <!-- Menus -->
50
+ <div class="o-topbar-topleft">
51
+ <t t-foreach="menus" t-as="menu" t-key="menu_index">
52
+ <div t-if="menu.children.length !== 0"
53
+ class="o-topbar-menu"
54
+ t-on-click="toggleContextMenu(menu)"
55
+ t-on-mouseover="onMenuMouseOver(menu)"
56
+ t-att-data-id="menu.id">
57
+ <t t-esc="getMenuName(menu)"/>
58
+ </div>
59
+ </t>
60
+ <Menu t-if="state.menuState.isOpen"
61
+ position="state.menuState.position"
62
+ menuItems="state.menuState.menuItems"
63
+ t-ref="menuRef"
64
+ t-on-close="state.menuState.isOpen=false"/>
65
+ </div>
66
+ <div class="o-topbar-topright">
67
+ <div t-foreach="topbarComponents" t-as="comp" t-key="comp_index">
68
+ <t t-component="comp.component"/>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ <!-- Toolbar and Cell Content -->
73
+ <div class="o-topbar-toolbar">
74
+ <!-- Toolbar -->
75
+ <div class="o-toolbar-tools">
76
+ <div class="o-tool" title="Undo" t-att-class="{'o-disabled': !undoTool}" t-on-click="undo" >${icons.UNDO_ICON}</div>
77
+ <div class="o-tool" t-att-class="{'o-disabled': !redoTool}" title="Redo" t-on-click="redo">${icons.REDO_ICON}</div>
78
+ <div class="o-tool" title="Paint Format" t-att-class="{active:paintFormatTool}" t-on-click="paintFormat">${icons.PAINT_FORMAT_ICON}</div>
79
+ <div class="o-tool" title="Clear Format" t-on-click="clearFormatting()">${icons.CLEAR_FORMAT_ICON}</div>
80
+ <div class="o-divider"/>
81
+ <div class="o-tool" title="Format as percent" t-on-click="toogleFormat('percent')">%</div>
82
+ <div class="o-tool" title="Decrease decimal places" t-on-click="setDecimal(-1)">.0</div>
83
+ <div class="o-tool" title="Increase decimal places" t-on-click="setDecimal(+1)">.00</div>
84
+ <div class="o-tool o-dropdown" title="More formats" t-on-click="toggleDropdownTool('formatTool')">
85
+ <div class="o-text-icon">123${icons.TRIANGLE_DOWN_ICON}</div>
86
+ <div class="o-dropdown-content o-text-options o-format-tool " t-if="state.activeTool === 'formatTool'" t-on-click="setFormat">
87
+ <t t-foreach="formats" t-as="format" t-key="format.name">
88
+ <div t-att-data-format="format.name" t-att-class="{active: currentFormat === format.name}"><t t-esc="format.text"/></div>
89
+ </t>
90
+ </div>
91
+ </div>
92
+ <div class="o-divider"/>
93
+ <!-- <div class="o-tool" title="Font"><span>Roboto</span> ${icons.TRIANGLE_DOWN_ICON}</div> -->
94
+ <div class="o-tool o-dropdown" title="Font Size" t-on-click="toggleDropdownTool('fontSizeTool')">
95
+ <div class="o-text-icon"><t t-esc="style.fontSize || ${DEFAULT_FONT_SIZE}"/> ${icons.TRIANGLE_DOWN_ICON}</div>
96
+ <div class="o-dropdown-content o-text-options " t-if="state.activeTool === 'fontSizeTool'" t-on-click="setSize">
97
+ <t t-foreach="fontSizes" t-as="font" t-key="font_index">
98
+ <div t-esc="font.pt" t-att-data-size="font.pt"/>
99
+ </t>
100
+ </div>
101
+ </div>
102
+ <div class="o-divider"/>
103
+ <div class="o-tool" title="Bold" t-att-class="{active:style.bold}" t-on-click="toogleStyle('bold')">${icons.BOLD_ICON}</div>
104
+ <div class="o-tool" title="Italic" t-att-class="{active:style.italic}" t-on-click="toogleStyle('italic')">${icons.ITALIC_ICON}</div>
105
+ <div class="o-tool" title="Strikethrough" t-att-class="{active:style.strikethrough}" t-on-click="toogleStyle('strikethrough')">${icons.STRIKE_ICON}</div>
106
+ <div class="o-tool o-dropdown o-with-color">
107
+ <span t-attf-style="border-color:{{textColor}}" title="Text Color" t-on-click="toggleDropdownTool('textColorTool')">${icons.TEXT_COLOR_ICON}</span>
108
+ <ColorPicker t-if="state.activeTool === 'textColorTool'" t-on-color-picked="setColor('textColor')" t-key="textColor"/>
109
+ </div>
110
+ <div class="o-divider"/>
111
+ <div class="o-tool o-dropdown o-with-color">
112
+ <span t-attf-style="border-color:{{fillColor}}" title="Fill Color" t-on-click="toggleDropdownTool('fillColorTool')">${icons.FILL_COLOR_ICON}</span>
113
+ <ColorPicker t-if="state.activeTool === 'fillColorTool'" t-on-color-picked="setColor('fillColor')" t-key="fillColor"/>
114
+ </div>
115
+ <div class="o-tool o-dropdown">
116
+ <span title="Borders" t-on-click="toggleDropdownTool('borderTool')">${icons.BORDERS_ICON}</span>
117
+ <div class="o-dropdown-content o-border" t-if="state.activeTool === 'borderTool'">
118
+ <div class="o-dropdown-line">
119
+ <span class="o-line-item" t-on-click="setBorder('all')">${icons.BORDERS_ICON}</span>
120
+ <span class="o-line-item" t-on-click="setBorder('hv')">${icons.BORDER_HV}</span>
121
+ <span class="o-line-item" t-on-click="setBorder('h')">${icons.BORDER_H}</span>
122
+ <span class="o-line-item" t-on-click="setBorder('v')">${icons.BORDER_V}</span>
123
+ <span class="o-line-item" t-on-click="setBorder('external')">${icons.BORDER_EXTERNAL}</span>
124
+ </div>
125
+ <div class="o-dropdown-line">
126
+ <span class="o-line-item" t-on-click="setBorder('left')">${icons.BORDER_LEFT}</span>
127
+ <span class="o-line-item" t-on-click="setBorder('top')">${icons.BORDER_TOP}</span>
128
+ <span class="o-line-item" t-on-click="setBorder('right')">${icons.BORDER_RIGHT}</span>
129
+ <span class="o-line-item" t-on-click="setBorder('bottom')">${icons.BORDER_BOTTOM}</span>
130
+ <span class="o-line-item" t-on-click="setBorder('clear')">${icons.BORDER_CLEAR}</span>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ <div class="o-tool" title="Merge Cells" t-att-class="{active:inMerge, 'o-disabled': cannotMerge}" t-on-click="toggleMerge">${icons.MERGE_CELL_ICON}</div>
135
+ <div class="o-divider"/>
136
+ <div class="o-tool o-dropdown" title="Horizontal align" t-on-click="toggleDropdownTool('alignTool')">
137
+ <span>
138
+ <t t-if="style.align === 'right'">${icons.ALIGN_RIGHT_ICON}</t>
139
+ <t t-elif="style.align === 'center'">${icons.ALIGN_CENTER_ICON}</t>
140
+ <t t-else="">${icons.ALIGN_LEFT_ICON}</t>
141
+ ${icons.TRIANGLE_DOWN_ICON}
142
+ </span>
143
+ <div t-if="state.activeTool === 'alignTool'" class="o-dropdown-content">
144
+ <div class="o-dropdown-item" t-on-click="toggleAlign('left')">${icons.ALIGN_LEFT_ICON}</div>
145
+ <div class="o-dropdown-item" t-on-click="toggleAlign('center')">${icons.ALIGN_CENTER_ICON}</div>
146
+ <div class="o-dropdown-item" t-on-click="toggleAlign('right')">${icons.ALIGN_RIGHT_ICON}</div>
147
+ </div>
148
+ </div>
149
+ <!-- <div class="o-tool" title="Vertical align"><span>${icons.ALIGN_MIDDLE_ICON}</span> ${icons.TRIANGLE_DOWN_ICON}</div> -->
150
+ <!-- <div class="o-tool" title="Text Wrapping">${icons.TEXT_WRAPPING_ICON}</div> -->
151
+ <div class="o-divider"/>
152
+ </div>
153
+
154
+ <!-- Cell content -->
155
+ <div class="o-toolbar-cell-content">
156
+ <t t-set="cell" t-value="getters.getActiveCell()"/>
157
+ <t t-esc="cell and cell.content"/>
158
+ </div>
159
+
160
+ </div>
161
+ </div>`;
162
+ static style = css/* scss */ `
163
+ .o-spreadsheet-topbar {
164
+ background-color: white;
165
+ display: flex;
166
+ flex-direction: column;
167
+ font-size: 13px;
168
+ user-select: none;
169
+ .o-topbar-top {
170
+ border-bottom: 1px solid #e0e2e4;
171
+ display: flex;
172
+ padding: 2px 10px;
173
+ justify-content: space-between;
174
+
175
+ /* Menus */
176
+ .o-topbar-topleft {
177
+ display: flex;
178
+ .o-topbar-menu {
179
+ padding: 4px 6px;
180
+ margin: 0 2px;
181
+ cursor: pointer;
182
+ }
183
+
184
+ .o-topbar-menu:hover {
185
+ background-color: #f1f3f4;
186
+ border-radius: 2px;
187
+ }
188
+ }
189
+
190
+ .o-topbar-topright {
191
+ display: flex;
192
+ justify-content: flex-end;
193
+ }
194
+ }
195
+ /* Toolbar + Cell Content */
196
+ .o-topbar-toolbar {
197
+ border-bottom: 1px solid #e0e2e4;
198
+ display: flex;
199
+
200
+ /* Toolbar */
201
+ .o-toolbar-tools {
202
+ display: flex;
203
+ flex-shrink: 0;
204
+ margin-left: 20px;
205
+ color: #333;
206
+ cursor: default;
207
+
208
+ .o-tool {
209
+ display: flex;
210
+ align-items: center;
211
+ margin: 2px;
212
+ padding: 0 3px;
213
+ border-radius: 2px;
214
+ cursor: pointer;
215
+ min-width: fit-content;
216
+ }
217
+
218
+ .o-tool.active,
219
+ .o-tool:not(.o-disabled):hover {
220
+ background-color: #f1f3f4;
221
+ }
222
+
223
+ .o-with-color > span {
224
+ border-bottom: 4px solid;
225
+ height: 16px;
226
+ margin-top: 2px;
227
+ }
228
+
229
+ .o-with-color {
230
+ .o-line-item:hover {
231
+ outline: 1px solid gray;
232
+ }
233
+ }
234
+
235
+ .o-border {
236
+ .o-line-item {
237
+ padding: 4px;
238
+ margin: 1px;
239
+ }
240
+ }
241
+
242
+ .o-divider {
243
+ display: inline-block;
244
+ border-right: 1px solid #e0e2e4;
245
+ width: 0;
246
+ margin: 0 6px;
247
+ }
248
+
249
+ .o-disabled {
250
+ opacity: 0.6;
251
+ }
252
+
253
+ .o-dropdown {
254
+ position: relative;
255
+
256
+ .o-text-icon {
257
+ height: 100%;
258
+ line-height: 30px;
259
+ > svg {
260
+ margin-bottom: -5px;
261
+ }
262
+ }
263
+
264
+ .o-text-options > div {
265
+ line-height: 26px;
266
+ padding: 3px 12px;
267
+ &:hover {
268
+ background-color: rgba(0, 0, 0, 0.08);
269
+ }
270
+ }
271
+
272
+ .o-dropdown-content {
273
+ position: absolute;
274
+ top: calc(100% + 5px);
275
+ left: 0;
276
+ z-index: 10;
277
+ box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
278
+ background-color: white;
279
+
280
+ .o-dropdown-item {
281
+ padding: 7px 10px;
282
+ }
283
+
284
+ .o-dropdown-item:hover {
285
+ background-color: rgba(0, 0, 0, 0.08);
286
+ }
287
+
288
+ .o-dropdown-line {
289
+ display: flex;
290
+ padding: 3px 6px;
291
+
292
+ .o-line-item {
293
+ width: 16px;
294
+ height: 16px;
295
+ margin: 1px 3px;
296
+
297
+ &:hover {
298
+ background-color: rgba(0, 0, 0, 0.08);
299
+ }
300
+ }
301
+ }
302
+
303
+ &.o-format-tool {
304
+ width: 180px;
305
+ padding: 7px 0;
306
+ > div {
307
+ padding-left: 25px;
308
+
309
+ &.active:before {
310
+ content: "✓";
311
+ font-weight: bold;
312
+ position: absolute;
313
+ left: 10px;
314
+ }
315
+ }
316
+ }
317
+ }
318
+ }
319
+ }
320
+
321
+ /* Cell Content */
322
+ .o-toolbar-cell-content {
323
+ font-size: 12px;
324
+ font-weight: 500;
325
+ padding: 0 12px;
326
+ margin: 0;
327
+ line-height: 34px;
328
+ white-space: nowrap;
329
+ user-select: text;
330
+ }
331
+ }
332
+ }
333
+ `;
334
+ static components = { ColorPicker, Menu };
335
+ formats = FORMATS;
336
+ currentFormat = "auto";
337
+ fontSizes = fontSizes;
338
+ dispatch = this.env.dispatch;
339
+ getters = this.env.getters;
340
+
341
+ style: Style = {};
342
+ state: State = useState({
343
+ menuState: { isOpen: false, position: null, menuItems: [] },
344
+ activeTool: "",
345
+ });
346
+ isSelectingMenu = false;
347
+ openedEl: HTMLElement | null = null;
348
+ inMerge = false;
349
+ cannotMerge = false;
350
+ undoTool = false;
351
+ redoTool = false;
352
+ paintFormatTool = false;
353
+ fillColor: string = "white";
354
+ textColor: string = "black";
355
+ menus: FullMenuItem[] = [];
356
+ menuRef = useRef("menuRef");
357
+
358
+ constructor() {
359
+ super(...arguments);
360
+ useExternalListener(window as any, "click", this.onClick);
361
+ }
362
+
363
+ get topbarComponents() {
364
+ return topbarComponentRegistry
365
+ .getAll()
366
+ .filter((item) => !item.isVisible || item.isVisible(this.env));
367
+ }
368
+
369
+ async willStart() {
370
+ this.updateCellState();
371
+ }
372
+ async willUpdateProps() {
373
+ this.updateCellState();
374
+ }
375
+
376
+ onClick(ev: MouseEvent) {
377
+ if (this.openedEl && isChildEvent(this.openedEl, ev)) {
378
+ return;
379
+ }
380
+ this.closeMenus();
381
+ }
382
+
383
+ toogleStyle(style: string) {
384
+ setStyle(this.env, { [style]: !this.style[style] });
385
+ }
386
+
387
+ toogleFormat(format: string) {
388
+ const formatter = FORMATS.find((f) => f.name === format);
389
+ const value = (formatter && formatter.value) || "";
390
+ setFormatter(this.env, value);
391
+ }
392
+
393
+ toggleAlign(align: Align) {
394
+ setStyle(this.env, { ["align"]: align });
395
+ }
396
+
397
+ onMenuMouseOver(menu: FullMenuItem, ev: MouseEvent) {
398
+ if (this.isSelectingMenu) {
399
+ this.toggleContextMenu(menu, ev);
400
+ }
401
+ }
402
+
403
+ toggleDropdownTool(tool: Tool, ev: MouseEvent) {
404
+ const isOpen = this.state.activeTool === tool;
405
+ this.closeMenus();
406
+ this.state.activeTool = isOpen ? "" : tool;
407
+ this.openedEl = isOpen ? null : (ev.target as HTMLElement);
408
+ }
409
+
410
+ toggleContextMenu(menu: FullMenuItem, ev: MouseEvent) {
411
+ this.closeMenus();
412
+ const x = (ev.target as HTMLElement).offsetLeft;
413
+ const y = (ev.target as HTMLElement).clientHeight + (ev.target as HTMLElement).offsetTop;
414
+ this.state.menuState.isOpen = true;
415
+ const width = this.el!.clientWidth;
416
+ const height = this.el!.parentElement!.clientHeight;
417
+ this.state.menuState.position = { x, y, width, height };
418
+ this.state.menuState.menuItems = topbarMenuRegistry
419
+ .getChildren(menu, this.env)
420
+ .filter((item) => !item.isVisible || item.isVisible(this.env));
421
+ this.isSelectingMenu = true;
422
+ this.openedEl = ev.target as HTMLElement;
423
+ }
424
+
425
+ closeMenus() {
426
+ this.state.activeTool = "";
427
+ this.state.menuState.isOpen = false;
428
+ this.isSelectingMenu = false;
429
+ this.openedEl = null;
430
+ if (this.menuRef.comp) {
431
+ (<Menu>this.menuRef.comp).closeSubMenus();
432
+ }
433
+ }
434
+
435
+ updateCellState() {
436
+ this.style = this.getters.getCurrentStyle();
437
+ this.fillColor = this.style.fillColor || "white";
438
+ this.textColor = this.style.textColor || "black";
439
+ const zones = this.getters.getSelectedZones();
440
+ const { top, left, right, bottom } = zones[0];
441
+ this.cannotMerge = zones.length > 1 || (top === bottom && left === right);
442
+ this.inMerge = false;
443
+ if (!this.cannotMerge) {
444
+ const [col, row] = this.getters.getPosition();
445
+ const zone = this.getters.expandZone({ left: col, right: col, top: row, bottom: row });
446
+ this.inMerge = isEqual(zones[0], zone);
447
+ }
448
+ this.undoTool = this.getters.canUndo();
449
+ this.redoTool = this.getters.canRedo();
450
+ this.paintFormatTool = this.getters.isPaintingFormat();
451
+ const cell = this.getters.getActiveCell();
452
+ if (cell && cell.format) {
453
+ const format = this.formats.find((f) => f.value === cell.format);
454
+ this.currentFormat = format ? format.name : "";
455
+ } else {
456
+ this.currentFormat = "auto";
457
+ }
458
+ this.menus = topbarMenuRegistry
459
+ .getAll()
460
+ .filter((item) => !item.isVisible || item.isVisible(this.env));
461
+ }
462
+
463
+ getMenuName(menu: FullMenuItem) {
464
+ return topbarMenuRegistry.getName(menu, this.env);
465
+ }
466
+
467
+ toggleMerge() {
468
+ const zones = this.getters.getSelectedZones();
469
+ const zone = zones[zones.length - 1];
470
+ const sheet = this.getters.getActiveSheet();
471
+ if (this.inMerge) {
472
+ this.dispatch("REMOVE_MERGE", { sheet, zone });
473
+ } else {
474
+ this.dispatch("ADD_MERGE", { sheet, zone, interactive: true });
475
+ }
476
+ }
477
+
478
+ setColor(target: string, ev: CustomEvent) {
479
+ setStyle(this.env, { [target]: ev.detail.color });
480
+ }
481
+
482
+ setBorder(command: BorderCommand) {
483
+ this.dispatch("SET_FORMATTING", {
484
+ sheet: this.getters.getActiveSheet(),
485
+ target: this.getters.getSelectedZones(),
486
+ border: command,
487
+ });
488
+ }
489
+
490
+ setFormat(ev: MouseEvent) {
491
+ const format = (ev.target as HTMLElement).dataset.format;
492
+ if (format) {
493
+ this.toogleFormat(format);
494
+ }
495
+ }
496
+
497
+ setDecimal(step: number) {
498
+ this.dispatch("SET_DECIMAL", {
499
+ sheet: this.getters.getActiveSheet(),
500
+ target: this.getters.getSelectedZones(),
501
+ step: step,
502
+ });
503
+ }
504
+
505
+ paintFormat() {
506
+ this.dispatch("ACTIVATE_PAINT_FORMAT", {
507
+ target: this.getters.getSelectedZones(),
508
+ });
509
+ }
510
+
511
+ clearFormatting() {
512
+ this.dispatch("CLEAR_FORMATTING", {
513
+ sheet: this.getters.getActiveSheet(),
514
+ target: this.getters.getSelectedZones(),
515
+ });
516
+ }
517
+
518
+ setSize(ev: MouseEvent) {
519
+ const fontSize = parseFloat((ev.target as HTMLElement).dataset.size!);
520
+ setStyle(this.env, { fontSize });
521
+ }
522
+
523
+ doAction(action: (env: SpreadsheetEnv) => void) {
524
+ action(this.env);
525
+ this.closeMenus();
526
+ }
527
+
528
+ undo() {
529
+ this.dispatch("UNDO");
530
+ }
531
+
532
+ redo() {
533
+ this.dispatch("REDO");
534
+ }
535
+ }
@@ -0,0 +1,26 @@
1
+ // Colors
2
+ export const BACKGROUND_GRAY_COLOR = "#f5f5f5";
3
+ export const BACKGROUND_HEADER_COLOR = "#F8F9FA";
4
+ export const BACKGROUND_HEADER_SELECTED_COLOR = "#E8EAED";
5
+ export const BACKGROUND_HEADER_ACTIVE_COLOR = "#595959";
6
+ export const TEXT_HEADER_COLOR = "#666666";
7
+ export const SELECTION_BORDER_COLOR = "#3266ca";
8
+ export const HEADER_BORDER_COLOR = "#C0C0C0";
9
+ export const CELL_BORDER_COLOR = "#E2E3E3";
10
+
11
+ // Dimensions
12
+ export const MIN_ROW_HEIGHT = 10;
13
+ export const MIN_COL_WIDTH = 5;
14
+ export const HEADER_HEIGHT = 26;
15
+ export const HEADER_WIDTH = 48;
16
+ export const TOPBAR_HEIGHT = 63;
17
+ export const BOTTOMBAR_HEIGHT = 36;
18
+ export const DEFAULT_CELL_WIDTH = 96;
19
+ export const DEFAULT_CELL_HEIGHT = 23;
20
+ export const SCROLLBAR_WIDTH = 15;
21
+
22
+ // Fonts
23
+ export const DEFAULT_FONT_WEIGHT = "400";
24
+ export const DEFAULT_FONT_SIZE = 10;
25
+ export const HEADER_FONT_SIZE = 11;
26
+ export const DEFAULT_FONT = "'Roboto', arial";