@odoo/o-spreadsheet 1.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 (199) 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 +78 -0
  12. package/readme.md +39 -0
  13. package/rollup.config.js +23 -0
  14. package/src/base_plugin.ts +102 -0
  15. package/src/components/autofill.ts +146 -0
  16. package/src/components/bottom_bar.ts +199 -0
  17. package/src/components/color_picker.ts +150 -0
  18. package/src/components/composer/autocomplete_dropdown.ts +135 -0
  19. package/src/components/composer/composer.ts +487 -0
  20. package/src/components/composer/content_editable_helper.ts +146 -0
  21. package/src/components/figures/chart.ts +70 -0
  22. package/src/components/figures/container.ts +268 -0
  23. package/src/components/figures/text_figure.ts +36 -0
  24. package/src/components/grid.ts +635 -0
  25. package/src/components/helpers/dom_helpers.ts +7 -0
  26. package/src/components/helpers/drag_and_drop.ts +17 -0
  27. package/src/components/icons.ts +36 -0
  28. package/src/components/index.ts +1 -0
  29. package/src/components/menu.ts +288 -0
  30. package/src/components/overlay.ts +457 -0
  31. package/src/components/scrollbar.ts +22 -0
  32. package/src/components/selection_input.ts +168 -0
  33. package/src/components/side_panel/cell_is_rule_editor.ts +169 -0
  34. package/src/components/side_panel/chart_panel.ts +104 -0
  35. package/src/components/side_panel/color_scale_rule_editor.ts +145 -0
  36. package/src/components/side_panel/conditional_formatting.ts +396 -0
  37. package/src/components/side_panel/side_panel.ts +127 -0
  38. package/src/components/side_panel/translations_terms.ts +45 -0
  39. package/src/components/spreadsheet.ts +198 -0
  40. package/src/components/top_bar.ts +535 -0
  41. package/src/constants.ts +26 -0
  42. package/src/data.ts +149 -0
  43. package/src/fonts.ts +41 -0
  44. package/src/formulas/compiler.ts +258 -0
  45. package/src/formulas/composer_tokenizer.ts +70 -0
  46. package/src/formulas/index.ts +15 -0
  47. package/src/formulas/normalize.ts +52 -0
  48. package/src/formulas/parser.ts +280 -0
  49. package/src/formulas/range_tokenizer.ts +97 -0
  50. package/src/formulas/tokenizer.ts +242 -0
  51. package/src/functions/arguments.ts +114 -0
  52. package/src/functions/dates.ts +302 -0
  53. package/src/functions/helpers.ts +592 -0
  54. package/src/functions/index.ts +52 -0
  55. package/src/functions/module_database.ts +282 -0
  56. package/src/functions/module_date.ts +743 -0
  57. package/src/functions/module_info.ts +69 -0
  58. package/src/functions/module_logical.ts +208 -0
  59. package/src/functions/module_lookup.ts +185 -0
  60. package/src/functions/module_math.ts +1252 -0
  61. package/src/functions/module_operators.ts +291 -0
  62. package/src/functions/module_statistical.ts +882 -0
  63. package/src/functions/module_text.ts +370 -0
  64. package/src/helpers/color.ts +30 -0
  65. package/src/helpers/coordinates.ts +71 -0
  66. package/src/helpers/grid_manipulation.ts +68 -0
  67. package/src/helpers/index.ts +7 -0
  68. package/src/helpers/misc.ts +76 -0
  69. package/src/helpers/numbers.ts +117 -0
  70. package/src/helpers/uuid.ts +19 -0
  71. package/src/helpers/zones.ts +263 -0
  72. package/src/history.ts +186 -0
  73. package/src/index.ts +84 -0
  74. package/src/model.ts +261 -0
  75. package/src/plugins/autofill.ts +412 -0
  76. package/src/plugins/chart.ts +404 -0
  77. package/src/plugins/clipboard.ts +461 -0
  78. package/src/plugins/conditional_format.ts +445 -0
  79. package/src/plugins/core.ts +1502 -0
  80. package/src/plugins/edition.ts +157 -0
  81. package/src/plugins/evaluation.ts +428 -0
  82. package/src/plugins/figures.ts +123 -0
  83. package/src/plugins/formatting.ts +630 -0
  84. package/src/plugins/highlight.ts +187 -0
  85. package/src/plugins/index.ts +32 -0
  86. package/src/plugins/merge.ts +524 -0
  87. package/src/plugins/renderer.ts +571 -0
  88. package/src/plugins/selection.ts +562 -0
  89. package/src/plugins/selection_inputs.ts +326 -0
  90. package/src/registries/autofill_modifiers.ts +76 -0
  91. package/src/registries/autofill_rules.ts +98 -0
  92. package/src/registries/figure_registry.ts +30 -0
  93. package/src/registries/index.ts +7 -0
  94. package/src/registries/menu_items_registry.ts +110 -0
  95. package/src/registries/menus/cell_menu_registry.ts +79 -0
  96. package/src/registries/menus/col_menu_registry.ts +63 -0
  97. package/src/registries/menus/index.ts +6 -0
  98. package/src/registries/menus/menu_items_actions.ts +425 -0
  99. package/src/registries/menus/row_menu_registry.ts +63 -0
  100. package/src/registries/menus/sheet_menu_registry.ts +75 -0
  101. package/src/registries/menus/topbar_menu_registry.ts +204 -0
  102. package/src/registries/side_panel_registry.ts +26 -0
  103. package/src/registries/topbar_component_registry.ts +12 -0
  104. package/src/registry.ts +51 -0
  105. package/src/translation.ts +44 -0
  106. package/src/types/autofill.ts +48 -0
  107. package/src/types/chart.ts +26 -0
  108. package/src/types/commands.ts +716 -0
  109. package/src/types/conditional_formatting.ts +124 -0
  110. package/src/types/env.ts +13 -0
  111. package/src/types/functions.ts +35 -0
  112. package/src/types/getters.ts +103 -0
  113. package/src/types/index.ts +20 -0
  114. package/src/types/misc.ts +143 -0
  115. package/src/types/rendering.ts +41 -0
  116. package/src/types/workbook_data.ts +62 -0
  117. package/tests/__mocks__/uuid.ts +7 -0
  118. package/tests/canvas.mock.ts +34 -0
  119. package/tests/components/__mocks__/content_editable_helper.ts +79 -0
  120. package/tests/components/__mocks__/scrollbar.ts +18 -0
  121. package/tests/components/__snapshots__/autocomplete_dropdown_test.ts.snap +29 -0
  122. package/tests/components/__snapshots__/bottom_bar_test.ts.snap +75 -0
  123. package/tests/components/__snapshots__/context_menu_test.ts.snap +88 -0
  124. package/tests/components/__snapshots__/grid_test.ts.snap +60 -0
  125. package/tests/components/__snapshots__/spreadsheet_test.ts.snap +455 -0
  126. package/tests/components/__snapshots__/top_bar_test.ts.snap +705 -0
  127. package/tests/components/autocomplete_dropdown_test.ts +315 -0
  128. package/tests/components/autofill_test.ts +167 -0
  129. package/tests/components/bottom_bar_test.ts +262 -0
  130. package/tests/components/chart_side_panel_test.ts +123 -0
  131. package/tests/components/composer_test.ts +400 -0
  132. package/tests/components/context_menu_test.ts +825 -0
  133. package/tests/components/grid_manipulation_test.ts +163 -0
  134. package/tests/components/grid_test.ts +581 -0
  135. package/tests/components/overlay_test.ts +541 -0
  136. package/tests/components/selection_input_test.ts +259 -0
  137. package/tests/components/side_panel_test.ts +148 -0
  138. package/tests/components/spreadsheet_test.ts +194 -0
  139. package/tests/components/top_bar_test.ts +427 -0
  140. package/tests/data_test.ts +175 -0
  141. package/tests/dom_helper.ts +42 -0
  142. package/tests/formulas/__snapshots__/compiler_test.ts.snap +302 -0
  143. package/tests/formulas/compiler_test.ts +266 -0
  144. package/tests/formulas/composer_tokenizer_test.ts +137 -0
  145. package/tests/formulas/expressions_test.ts +57 -0
  146. package/tests/formulas/formulas_test.ts +80 -0
  147. package/tests/formulas/normalize_test.ts +82 -0
  148. package/tests/formulas/parser_test.ts +224 -0
  149. package/tests/formulas/range_tokenizer_test.ts +187 -0
  150. package/tests/formulas/tokenizer_test.ts +164 -0
  151. package/tests/functions/arguments_test.ts +141 -0
  152. package/tests/functions/dates_test.ts +1081 -0
  153. package/tests/functions/functions_test.ts +53 -0
  154. package/tests/functions/helper_test.ts +98 -0
  155. package/tests/functions/module_database_test.ts +311 -0
  156. package/tests/functions/module_date_test.ts +941 -0
  157. package/tests/functions/module_info_test.ts +139 -0
  158. package/tests/functions/module_logical_test.ts +369 -0
  159. package/tests/functions/module_lookup_test.ts +541 -0
  160. package/tests/functions/module_math_test.ts +2482 -0
  161. package/tests/functions/module_operators_test.ts +955 -0
  162. package/tests/functions/module_statistical_test.ts +2014 -0
  163. package/tests/functions/module_text_test.ts +667 -0
  164. package/tests/helpers/coordinates_test.ts +25 -0
  165. package/tests/helpers/misc_test.ts +95 -0
  166. package/tests/helpers/numbers_test.ts +196 -0
  167. package/tests/helpers/zones_test.ts +97 -0
  168. package/tests/helpers.ts +384 -0
  169. package/tests/menu_items_registry_test.ts +540 -0
  170. package/tests/model/plugins/__snapshots__/chart_test.ts.snap +90 -0
  171. package/tests/model/plugins/chart_test.ts +562 -0
  172. package/tests/model_test.ts +82 -0
  173. package/tests/plugins/__snapshots__/conditional_formatting_test.ts.snap +131 -0
  174. package/tests/plugins/__snapshots__/renderer_test.ts.snap +371 -0
  175. package/tests/plugins/autofill_test.ts +457 -0
  176. package/tests/plugins/borders_test.ts +295 -0
  177. package/tests/plugins/clipboard_test.ts +1180 -0
  178. package/tests/plugins/conditional_formatting_test.ts +1253 -0
  179. package/tests/plugins/core_test.ts +614 -0
  180. package/tests/plugins/edition_test.ts +100 -0
  181. package/tests/plugins/evaluation_async_test.ts +332 -0
  182. package/tests/plugins/evaluation_dates_test.ts +79 -0
  183. package/tests/plugins/evaluation_test.ts +901 -0
  184. package/tests/plugins/figures_test.ts +327 -0
  185. package/tests/plugins/formatting_test.ts +391 -0
  186. package/tests/plugins/grid_manipulation_test.ts +1366 -0
  187. package/tests/plugins/highlight_test.ts +413 -0
  188. package/tests/plugins/history_test.ts +142 -0
  189. package/tests/plugins/import_export_test.ts +276 -0
  190. package/tests/plugins/merges_test.ts +490 -0
  191. package/tests/plugins/navigation_test.ts +181 -0
  192. package/tests/plugins/plugins/evaluation_test.ts +111 -0
  193. package/tests/plugins/renderer_test.ts +598 -0
  194. package/tests/plugins/resizing_test.ts +152 -0
  195. package/tests/plugins/selection_input_test.ts +409 -0
  196. package/tests/plugins/selection_test.ts +442 -0
  197. package/tests/plugins/sheets_test.ts +643 -0
  198. package/tests/plugins/style_test.ts +90 -0
  199. package/tsconfig.json +22 -0
@@ -0,0 +1,169 @@
1
+ import * as owl from "@odoo/owl";
2
+ import { CellIsRule, ConditionalFormat, SpreadsheetEnv, Style } from "../../types";
3
+ import { ColorPicker } from "../color_picker";
4
+ import * as icons from "../icons";
5
+ import { cellIsOperators, terms } from "./translations_terms";
6
+
7
+ const { Component, useState, hooks } = owl;
8
+ const { useExternalListener } = hooks;
9
+ const { xml, css } = owl.tags;
10
+
11
+ export const PREVIEW_TEMPLATE = xml/* xml */ `
12
+ <div class="o-cf-preview-line"
13
+ t-attf-style="font-weight:{{currentStyle.bold ?'bold':'normal'}};
14
+ text-decoration:{{currentStyle.strikethrough ? 'line-through':'none'}};
15
+ font-style:{{currentStyle.italic?'italic':'normal'}};
16
+ color:{{currentStyle.textColor}};
17
+ background-color:{{currentStyle.fillColor}};"
18
+ t-esc="previewText || env._t('${terms.PREVIEWTEXT}')" />
19
+ `;
20
+
21
+ const TEMPLATE = xml/* xml */ `
22
+ <div>
23
+ <div class="o-section-title" t-esc="env._t('${terms.CF_TITLE}')"></div>
24
+ <div class="o-cf-title-text" t-esc="env._t('${terms.IS_RULE}')"></div>
25
+ <select t-model="state.condition.operator" class="o-input o-cell-is-operator">
26
+ <t t-foreach="Object.keys(cellIsOperators)" t-as="op" t-key="op_index">
27
+ <option t-att-value="op" t-esc="cellIsOperators[op]"/>
28
+ </t>
29
+ </select>
30
+ <input type="text" placeholder="Value" t-model="state.condition.value1" class="o-input o-cell-is-value"/>
31
+ <t t-if="state.condition.operator === 'Between' || state.condition.operator === 'NotBetween'">
32
+ <input type="text" placeholder="and value" t-model="state.condition.value2" class="o-input"/>
33
+ </t>
34
+ <div class="o-cf-title-text" t-esc="env._t('${terms.FORMATTING_STYLE}')"></div>
35
+
36
+ <t t-call="${PREVIEW_TEMPLATE}">
37
+ <t t-set="currentStyle" t-value="state.style"/>
38
+ </t>
39
+ <div class="o-tools">
40
+ <div class="o-tool" t-att-title="env._t('${terms.BOLD}')" t-att-class="{active:state.style.bold}" t-on-click="toggleTool('bold')">
41
+ ${icons.BOLD_ICON}
42
+ </div>
43
+ <div class="o-tool" t-att-title="env._t('${terms.ITALIC}')" t-att-class="{active:state.style.italic}" t-on-click="toggleTool('italic')">
44
+ ${icons.ITALIC_ICON}
45
+ </div>
46
+ <div class="o-tool" t-att-title="env._t('${terms.STRIKETHROUGH}')" t-att-class="{active:state.style.strikethrough}"
47
+ t-on-click="toggleTool('strikethrough')">${icons.STRIKE_ICON}
48
+ </div>
49
+ <div class="o-tool o-dropdown o-with-color">
50
+ <span t-att-title="env._t('${terms.TEXTCOLOR}')" t-attf-style="border-color:{{state.style.textColor}}"
51
+ t-on-click.stop="toggleMenu('textColorTool')">${icons.TEXT_COLOR_ICON}</span>
52
+ <ColorPicker t-if="state.textColorTool" t-on-color-picked="setColor('textColor')" t-key="textColor"/>
53
+ </div>
54
+ <div class="o-divider"/>
55
+ <div class="o-tool o-dropdown o-with-color">
56
+ <span t-att-title="env._t('${terms.FILLCOLOR}')" t-attf-style="border-color:{{state.style.fillColor}}"
57
+ t-on-click.stop="toggleMenu('fillColorTool')">${icons.FILL_COLOR_ICON}</span>
58
+ <ColorPicker t-if="state.fillColorTool" t-on-color-picked="setColor('fillColor')" t-key="fillColor"/>
59
+ </div>
60
+ </div>
61
+ <div class="o-sidePanelButtons">
62
+ <button t-on-click="onCancel" class="o-sidePanelButton o-cf-cancel" t-esc="env._t('${terms.CANCEL}')"></button>
63
+ <button t-on-click="onSave" class="o-sidePanelButton o-cf-save" t-esc="env._t('${terms.SAVE}')"></button>
64
+ </div>
65
+ </div>
66
+ `;
67
+
68
+ const CSS = css/* scss */ `
69
+ .o-cf-title-text {
70
+ font-size: 12px;
71
+ line-height: 14px;
72
+ margin-bottom: 6px;
73
+ margin-top: 18px;
74
+ }
75
+ .o-cf-preview-line {
76
+ border: 1px solid darkgrey;
77
+ padding: 10px;
78
+ }
79
+ `;
80
+
81
+ interface Props {
82
+ conditionalFormat: ConditionalFormat;
83
+ }
84
+
85
+ export class CellIsRuleEditor extends Component<Props, SpreadsheetEnv> {
86
+ static template = TEMPLATE;
87
+ static style = CSS;
88
+ static components = { ColorPicker };
89
+
90
+ // @ts-ignore used in XML template
91
+ private cellIsOperators = cellIsOperators;
92
+
93
+ cf = this.props.conditionalFormat as ConditionalFormat;
94
+ rule = this.cf.rule as CellIsRule;
95
+ state = useState({
96
+ condition: {
97
+ operator: this.rule && this.rule.operator ? this.rule.operator : "Equal",
98
+ value1: this.rule && this.rule.values.length > 0 ? this.rule.values[0] : "",
99
+ value2: this.cf && this.rule.values.length > 1 ? this.rule.values[1] : "",
100
+ },
101
+
102
+ textColorTool: false,
103
+ fillColorTool: false,
104
+ style: {
105
+ fillColor: this.cf && this.rule.style.fillColor,
106
+ textColor: this.cf && this.rule.style.textColor,
107
+ bold: this.cf && this.rule.style.bold,
108
+ italic: this.cf && this.rule.style.italic,
109
+ strikethrough: this.cf && this.rule.style.strikethrough,
110
+ },
111
+ });
112
+ constructor() {
113
+ super(...arguments);
114
+ useExternalListener(window as any, "click", this.closeMenus);
115
+ }
116
+
117
+ toggleMenu(tool) {
118
+ const current = this.state[tool];
119
+ this.closeMenus();
120
+ this.state[tool] = !current;
121
+ }
122
+
123
+ toggleTool(tool: string) {
124
+ this.state.style[tool] = !this.state.style[tool];
125
+ this.closeMenus();
126
+ }
127
+ setColor(target: string, ev: CustomEvent) {
128
+ const color = ev.detail.color;
129
+ this.state.style[target] = color;
130
+ this.closeMenus();
131
+ }
132
+ closeMenus() {
133
+ this.state.textColorTool = false;
134
+ this.state.fillColorTool = false;
135
+ }
136
+
137
+ onSave() {
138
+ const newStyle: Style = {};
139
+ const style = this.state.style;
140
+ if (style.bold !== undefined) {
141
+ newStyle.bold = style.bold;
142
+ }
143
+ if (style.italic !== undefined) {
144
+ newStyle.italic = style.italic;
145
+ }
146
+ if (style.strikethrough !== undefined) {
147
+ newStyle.strikethrough = style.strikethrough;
148
+ }
149
+ if (style.fillColor) {
150
+ newStyle.fillColor = style.fillColor;
151
+ }
152
+ if (style.textColor) {
153
+ newStyle.textColor = style.textColor;
154
+ }
155
+
156
+ this.trigger("modify-rule", {
157
+ rule: {
158
+ type: "CellIsRule",
159
+ operator: this.state.condition.operator,
160
+ values: [this.state.condition.value1, this.state.condition.value2],
161
+ stopIfTrue: false,
162
+ style: newStyle,
163
+ },
164
+ });
165
+ }
166
+ onCancel() {
167
+ this.trigger("cancel-edit");
168
+ }
169
+ }
@@ -0,0 +1,104 @@
1
+ import * as owl from "@odoo/owl";
2
+ import { uuidv4 } from "../../helpers/index";
3
+ import { ChartFigure, ChartTypes, CreateChartDefinition, SpreadsheetEnv } from "../../types/index";
4
+ import { SelectionInput } from "../selection_input";
5
+ import { chartTerms } from "./translations_terms";
6
+
7
+ const { Component, useState } = owl;
8
+ const { xml } = owl.tags;
9
+
10
+ const TEMPLATE = xml/* xml */ `
11
+ <div class="o-chart">
12
+ <div class="o-section">
13
+ <div class="o-section-title"><t t-esc="env._t('${chartTerms.ChartType}')"/></div>
14
+ <select t-model="state.type" class="o-input o-type-selector">
15
+ <option value="bar" t-esc="env._t('${chartTerms.Bar}')"/>
16
+ <option value="line" t-esc="env._t('${chartTerms.Line}')"/>
17
+ <option value="pie" t-esc="env._t('${chartTerms.Pie}')"/>
18
+ </select>
19
+ </div>
20
+ <div class="o-section o-chart-title">
21
+ <div class="o-section-title" t-esc="env._t('${chartTerms.Title}')"/>
22
+ <input type="text" t-model="state.title" class="o-input" t-att-placeholder="env._t('${chartTerms.TitlePlaceholder}')"/>
23
+ </div>
24
+ <div class="o-section o-data-series">
25
+ <div class="o-section-title" t-esc="env._t('${chartTerms.DataSeries}')"/>
26
+ <SelectionInput ranges="state.ranges" t-on-selection-changed="onSeriesChanged"/>
27
+ <input type="checkbox" t-model="state.seriesHasTitle"/><t t-esc="env._t('${chartTerms.MyDataHasTitle}')"/>
28
+ </div>
29
+ <div class="o-section o-data-labels">
30
+ <div class="o-section-title" t-esc="env._t('${chartTerms.DataCategories}')"/>
31
+ <SelectionInput ranges="[state.labelRange]" t-on-selection-changed="onLabelRangeChanged" maximumRanges="1"/>
32
+ </div>
33
+ <div class="o-sidePanelButtons">
34
+ <button t-if="props.figure" t-on-click="updateChart(props.figure)" class="o-sidePanelButton" t-esc="env._t('${chartTerms.UpdateChart}')"/>
35
+ <button t-else="" t-on-click="createChart" class="o-sidePanelButton" t-esc="env._t('${chartTerms.CreateChart}')"/>
36
+ </div>
37
+ </div>
38
+ `;
39
+
40
+ interface Props {
41
+ figure?: ChartFigure;
42
+ }
43
+
44
+ interface ChartPanelState {
45
+ type: ChartTypes;
46
+ title: string;
47
+ ranges: string[];
48
+ labelRange: string;
49
+ seriesHasTitle: boolean;
50
+ }
51
+
52
+ export class ChartPanel extends Component<Props, SpreadsheetEnv> {
53
+ static template = TEMPLATE;
54
+ static components = { SelectionInput };
55
+ private getters = this.env.getters;
56
+
57
+ private state: ChartPanelState = useState(this.initialState());
58
+
59
+ onSeriesChanged(ev: CustomEvent) {
60
+ this.state.ranges = ev.detail.ranges;
61
+ }
62
+
63
+ onLabelRangeChanged(ev: CustomEvent) {
64
+ this.state.labelRange = ev.detail.ranges[0];
65
+ }
66
+
67
+ createChart() {
68
+ this.env.dispatch("CREATE_CHART", {
69
+ sheetId: this.getters.getActiveSheet(),
70
+ id: uuidv4(),
71
+ definition: this.getChartDefinition(),
72
+ });
73
+ this.trigger("close-side-panel");
74
+ }
75
+
76
+ updateChart(chart: ChartFigure) {
77
+ this.env.dispatch("UPDATE_CHART", {
78
+ id: chart.id,
79
+ definition: this.getChartDefinition(),
80
+ });
81
+ this.trigger("close-side-panel");
82
+ }
83
+
84
+ private getChartDefinition(): CreateChartDefinition {
85
+ return {
86
+ type: this.state.type,
87
+ title: this.state.title,
88
+ labelRange: this.state.labelRange.trim() || "",
89
+ dataSets: this.state.ranges.slice(),
90
+ seriesHasTitle: this.state.seriesHasTitle,
91
+ };
92
+ }
93
+
94
+ private initialState(): ChartPanelState {
95
+ const data = this.props.figure ? this.props.figure.data : undefined;
96
+ return {
97
+ title: data && data.title ? data.title : "",
98
+ ranges: data ? data.dataSets.map((ds) => ds.dataRange) : [],
99
+ labelRange: data ? data.labelRange : "",
100
+ type: data ? data.type : "bar",
101
+ seriesHasTitle: data ? data.title !== undefined : false,
102
+ };
103
+ }
104
+ }
@@ -0,0 +1,145 @@
1
+ import * as owl from "@odoo/owl";
2
+ import { colorNumberString } from "../../helpers/index";
3
+ import { ColorScaleRule, ConditionalFormat, SpreadsheetEnv } from "../../types";
4
+ import { ColorPicker } from "../color_picker";
5
+ import * as icons from "../icons";
6
+
7
+ const { Component, useState, hooks } = owl;
8
+ const { useExternalListener } = hooks;
9
+ const { xml, css } = owl.tags;
10
+
11
+ export const PREVIEW_TEMPLATE = xml/* xml */ `
12
+ <div class="o-cf-preview-gradient" t-attf-style="background-image: linear-gradient(to right, #{{colorNumberString(state.minimum.color)}}, #{{colorNumberString(state.maximum.color)}})">
13
+ <div t-esc="previewText">Preview text</div>
14
+ </div>
15
+ `;
16
+
17
+ const THRESHOLD_TEMPLATE = xml/* xml */ `
18
+ <div t-attf-class="o-threshold o-threshold-{{thresholdType}}">
19
+ <div class="o-tools">
20
+ <div class="o-tool o-dropdown o-with-color">
21
+ <span title="Fill Color" t-attf-style="border-color:#{{colorNumberString(threshold.color)}}"
22
+ t-on-click.stop="toggleMenu(thresholdType+'ColorTool')">${icons.FILL_COLOR_ICON}</span>
23
+ <ColorPicker t-if="state[thresholdType+'ColorTool']" t-on-color-picked="setColor(thresholdType)"/>
24
+ </div>
25
+ </div>
26
+ <select name="valueType" t-model="threshold.type" t-on-click="closeMenus">
27
+ <option value="value">Cell values</option>
28
+ <!-- <option value="number">Fixed number</option>--> <!-- not yet implemented -->
29
+ <!-- <option value="percentage">Percentage</option>-->
30
+ <!-- <option value="percentile">Percentile</option>-->
31
+ <!-- <option value="formula">Formula</option>-->
32
+ </select>
33
+
34
+ <input type="text" t-model="threshold.value" class="o-threshold-value"
35
+ t-att-disabled="threshold.type !== 'number'"/>
36
+ </div>`;
37
+
38
+ const TEMPLATE = xml/* xml */ `
39
+ <div>
40
+ <div class="o-section-title">Format rules</div>
41
+ <div class="o-cf-title-text">Preview</div>
42
+ <t t-call="${PREVIEW_TEMPLATE}"/>
43
+ <div class="o-cf-title-text">Minpoint</div>
44
+ <t t-call="${THRESHOLD_TEMPLATE}">
45
+ <t t-set="threshold" t-value="state.minimum" ></t>
46
+ <t t-set="thresholdType" t-value="'minimum'" ></t>
47
+ </t>
48
+ <div class="o-cf-title-text">MaxPoint</div>
49
+ <t t-call="${THRESHOLD_TEMPLATE}">
50
+ <t t-set="threshold" t-value="state.maximum" ></t>
51
+ <t t-set="thresholdType" t-value="'maximum'" ></t>
52
+ </t>
53
+ <div class="o-sidePanelButtons">
54
+ <button t-on-click="onCancel" class="o-sidePanelButton o-cf-cancel">Cancel</button>
55
+ <button t-on-click="onSave" class="o-sidePanelButton o-cf-save">Save</button>
56
+ </div>
57
+ </div>`;
58
+
59
+ const CSS = css/* scss */ `
60
+ .o-cf-title-text {
61
+ font-size: 12px;
62
+ line-height: 14px;
63
+ margin-bottom: 6px;
64
+ margin-top: 18px;
65
+ }
66
+ .o-threshold {
67
+ display: flex;
68
+ flex-direction: horizontal;
69
+
70
+ .o-threshold-value {
71
+ width: 5em;
72
+ margin-left: 15px;
73
+ margin-right: 15px;
74
+ }
75
+ }
76
+ .o-cf-preview-gradient {
77
+ border: 1px solid darkgrey;
78
+ padding: 10px;
79
+ }
80
+ `;
81
+
82
+ interface Props {
83
+ conditionalFormat: ConditionalFormat;
84
+ }
85
+
86
+ export class ColorScaleRuleEditor extends Component<Props, SpreadsheetEnv> {
87
+ static template = TEMPLATE;
88
+ static style = CSS;
89
+ static components = { ColorPicker };
90
+
91
+ cf = this.props.conditionalFormat;
92
+ colorNumberString = colorNumberString;
93
+ rule = this.cf ? (this.cf.rule as ColorScaleRule) : null;
94
+ state = useState({
95
+ minimum: this.rule
96
+ ? Object.assign({}, this.rule.minimum)
97
+ : { color: 0xffffff, type: "value", value: null },
98
+ maximum: this.rule
99
+ ? Object.assign({}, this.rule.maximum)
100
+ : { color: 0x000000, type: "value", value: null },
101
+ midpoint:
102
+ this.rule && Object.assign({}, this.rule.midpoint)
103
+ ? this.rule.midpoint
104
+ : { color: 0xffffff, type: "value", value: null },
105
+ maximumColorTool: false,
106
+ minimumColorTool: false,
107
+ });
108
+ constructor() {
109
+ super(...arguments);
110
+ useExternalListener(window as any, "click", this.closeMenus);
111
+ }
112
+
113
+ toggleMenu(tool) {
114
+ const current = this.state[tool];
115
+ this.closeMenus();
116
+ this.state[tool] = !current;
117
+ }
118
+
119
+ toggleTool(tool: string) {
120
+ this.closeMenus();
121
+ }
122
+ setColor(target: string, ev: CustomEvent) {
123
+ const color: string = ev.detail.color;
124
+ this.state[target].color = Number.parseInt(color.substr(1), 16);
125
+ this.closeMenus();
126
+ }
127
+ closeMenus() {
128
+ this.state.minimumColorTool = false;
129
+ this.state.maximumColorTool = false;
130
+ }
131
+
132
+ onSave() {
133
+ this.trigger("modify-rule", {
134
+ rule: {
135
+ type: "ColorScaleRule",
136
+ minimum: this.state.minimum,
137
+ maximum: this.state.maximum,
138
+ midpoint: this.state.midpoint,
139
+ },
140
+ });
141
+ }
142
+ onCancel() {
143
+ this.trigger("cancel-edit");
144
+ }
145
+ }