@trebco/treb 23.6.5 → 25.0.0-rc2

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 (217) hide show
  1. package/.eslintignore +8 -0
  2. package/.eslintrc.js +164 -0
  3. package/README-shadow-DOM.md +88 -0
  4. package/README.md +37 -130
  5. package/api-config.json +29 -0
  6. package/api-generator/api-generator-types.ts +82 -0
  7. package/api-generator/api-generator.ts +1172 -0
  8. package/api-generator/package.json +3 -0
  9. package/build/treb-spreadsheet.mjs +14 -0
  10. package/{treb.d.ts → build/treb.d.ts} +323 -271
  11. package/esbuild-custom-element.mjs +336 -0
  12. package/esbuild.js +305 -0
  13. package/package.json +49 -14
  14. package/treb-base-types/package.json +5 -0
  15. package/treb-base-types/src/api_types.ts +36 -0
  16. package/treb-base-types/src/area.ts +583 -0
  17. package/treb-base-types/src/basic_types.ts +45 -0
  18. package/treb-base-types/src/cell.ts +612 -0
  19. package/treb-base-types/src/cells.ts +1066 -0
  20. package/treb-base-types/src/color.ts +124 -0
  21. package/treb-base-types/src/import.ts +71 -0
  22. package/treb-base-types/src/index-standalone.ts +29 -0
  23. package/treb-base-types/src/index.ts +42 -0
  24. package/treb-base-types/src/layout.ts +47 -0
  25. package/treb-base-types/src/localization.ts +187 -0
  26. package/treb-base-types/src/rectangle.ts +145 -0
  27. package/treb-base-types/src/render_text.ts +72 -0
  28. package/treb-base-types/src/style.ts +545 -0
  29. package/treb-base-types/src/table.ts +109 -0
  30. package/treb-base-types/src/text_part.ts +54 -0
  31. package/treb-base-types/src/theme.ts +608 -0
  32. package/treb-base-types/src/union.ts +152 -0
  33. package/treb-base-types/src/value-type.ts +164 -0
  34. package/treb-base-types/style/resizable.css +59 -0
  35. package/treb-calculator/modern.tsconfig.json +11 -0
  36. package/treb-calculator/package.json +5 -0
  37. package/treb-calculator/src/calculator.ts +2546 -0
  38. package/treb-calculator/src/complex-math.ts +558 -0
  39. package/treb-calculator/src/dag/array-vertex.ts +198 -0
  40. package/treb-calculator/src/dag/graph.ts +951 -0
  41. package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
  42. package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
  43. package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
  44. package/treb-calculator/src/dag/vertex.ts +352 -0
  45. package/treb-calculator/src/descriptors.ts +162 -0
  46. package/treb-calculator/src/expression-calculator.ts +1069 -0
  47. package/treb-calculator/src/function-error.ts +103 -0
  48. package/treb-calculator/src/function-library.ts +103 -0
  49. package/treb-calculator/src/functions/base-functions.ts +1214 -0
  50. package/treb-calculator/src/functions/checkbox.ts +164 -0
  51. package/treb-calculator/src/functions/complex-functions.ts +253 -0
  52. package/treb-calculator/src/functions/finance-functions.ts +399 -0
  53. package/treb-calculator/src/functions/information-functions.ts +102 -0
  54. package/treb-calculator/src/functions/matrix-functions.ts +182 -0
  55. package/treb-calculator/src/functions/sparkline.ts +335 -0
  56. package/treb-calculator/src/functions/statistics-functions.ts +350 -0
  57. package/treb-calculator/src/functions/text-functions.ts +298 -0
  58. package/treb-calculator/src/index.ts +27 -0
  59. package/treb-calculator/src/notifier-types.ts +59 -0
  60. package/treb-calculator/src/primitives.ts +428 -0
  61. package/treb-calculator/src/utilities.ts +305 -0
  62. package/treb-charts/package.json +5 -0
  63. package/treb-charts/src/chart-functions.ts +156 -0
  64. package/treb-charts/src/chart-types.ts +230 -0
  65. package/treb-charts/src/chart.ts +1288 -0
  66. package/treb-charts/src/index.ts +24 -0
  67. package/treb-charts/src/main.ts +37 -0
  68. package/treb-charts/src/rectangle.ts +52 -0
  69. package/treb-charts/src/renderer.ts +1841 -0
  70. package/treb-charts/src/util.ts +122 -0
  71. package/treb-charts/style/charts.scss +221 -0
  72. package/treb-charts/style/old-charts.scss +250 -0
  73. package/treb-embed/markup/layout.html +137 -0
  74. package/treb-embed/markup/toolbar.html +175 -0
  75. package/treb-embed/modern.tsconfig.json +25 -0
  76. package/treb-embed/src/custom-element/content-types.d.ts +18 -0
  77. package/treb-embed/src/custom-element/global.d.ts +11 -0
  78. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1228 -0
  79. package/treb-embed/src/custom-element/treb-global.ts +44 -0
  80. package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
  81. package/treb-embed/src/embedded-spreadsheet.ts +5358 -0
  82. package/treb-embed/src/index.ts +16 -0
  83. package/treb-embed/src/language-model.ts +41 -0
  84. package/treb-embed/src/options.ts +298 -0
  85. package/treb-embed/src/progress-dialog.ts +228 -0
  86. package/treb-embed/src/selection-state.ts +16 -0
  87. package/treb-embed/src/spinner.ts +42 -0
  88. package/treb-embed/src/toolbar-message.ts +96 -0
  89. package/treb-embed/src/types.ts +167 -0
  90. package/treb-embed/style/autocomplete.scss +103 -0
  91. package/treb-embed/style/dark-theme.scss +114 -0
  92. package/treb-embed/style/defaults.scss +36 -0
  93. package/treb-embed/style/dialog.scss +181 -0
  94. package/treb-embed/style/dropdown-select.scss +101 -0
  95. package/treb-embed/style/formula-bar.scss +193 -0
  96. package/treb-embed/style/grid.scss +374 -0
  97. package/treb-embed/style/layout.scss +424 -0
  98. package/treb-embed/style/mouse-mask.scss +67 -0
  99. package/treb-embed/style/note.scss +92 -0
  100. package/treb-embed/style/overlay-editor.scss +102 -0
  101. package/treb-embed/style/spinner.scss +92 -0
  102. package/treb-embed/style/tab-bar.scss +228 -0
  103. package/treb-embed/style/table.scss +80 -0
  104. package/treb-embed/style/theme-defaults.scss +444 -0
  105. package/treb-embed/style/toolbar.scss +416 -0
  106. package/treb-embed/style/tooltip.scss +68 -0
  107. package/treb-embed/style/treb-icons.scss +130 -0
  108. package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
  109. package/treb-embed/style/z-index.scss +43 -0
  110. package/treb-export/docs/charts.md +68 -0
  111. package/treb-export/modern.tsconfig.json +19 -0
  112. package/treb-export/package.json +4 -0
  113. package/treb-export/src/address-type.ts +77 -0
  114. package/treb-export/src/base-template.ts +22 -0
  115. package/treb-export/src/column-width.ts +85 -0
  116. package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
  117. package/treb-export/src/drawing2/chart2.ts +282 -0
  118. package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
  119. package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
  120. package/treb-export/src/drawing2/drawing2.ts +355 -0
  121. package/treb-export/src/drawing2/embedded-image.ts +71 -0
  122. package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
  123. package/treb-export/src/export-worker/export-worker.ts +99 -0
  124. package/treb-export/src/export-worker/index-modern.ts +22 -0
  125. package/treb-export/src/export2.ts +2204 -0
  126. package/treb-export/src/import2.ts +882 -0
  127. package/treb-export/src/relationship.ts +36 -0
  128. package/treb-export/src/shared-strings2.ts +128 -0
  129. package/treb-export/src/template-2.ts +22 -0
  130. package/treb-export/src/unescape_xml.ts +47 -0
  131. package/treb-export/src/workbook-sheet2.ts +182 -0
  132. package/treb-export/src/workbook-style2.ts +1285 -0
  133. package/treb-export/src/workbook-theme2.ts +88 -0
  134. package/treb-export/src/workbook2.ts +491 -0
  135. package/treb-export/src/xml-utils.ts +201 -0
  136. package/treb-export/template/base/[Content_Types].xml +2 -0
  137. package/treb-export/template/base/_rels/.rels +2 -0
  138. package/treb-export/template/base/docProps/app.xml +2 -0
  139. package/treb-export/template/base/docProps/core.xml +12 -0
  140. package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
  141. package/treb-export/template/base/xl/sharedStrings.xml +2 -0
  142. package/treb-export/template/base/xl/styles.xml +2 -0
  143. package/treb-export/template/base/xl/theme/theme1.xml +2 -0
  144. package/treb-export/template/base/xl/workbook.xml +2 -0
  145. package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
  146. package/treb-export/template/base.xlsx +0 -0
  147. package/treb-format/package.json +8 -0
  148. package/treb-format/src/format.test.ts +213 -0
  149. package/treb-format/src/format.ts +942 -0
  150. package/treb-format/src/format_cache.ts +199 -0
  151. package/treb-format/src/format_parser.ts +723 -0
  152. package/treb-format/src/index.ts +25 -0
  153. package/treb-format/src/number_format_section.ts +100 -0
  154. package/treb-format/src/value_parser.ts +337 -0
  155. package/treb-grid/package.json +5 -0
  156. package/treb-grid/src/editors/autocomplete.ts +394 -0
  157. package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
  158. package/treb-grid/src/editors/formula_bar.ts +473 -0
  159. package/treb-grid/src/editors/formula_editor_base.ts +910 -0
  160. package/treb-grid/src/editors/overlay_editor.ts +511 -0
  161. package/treb-grid/src/index.ts +37 -0
  162. package/treb-grid/src/layout/base_layout.ts +2618 -0
  163. package/treb-grid/src/layout/grid_layout.ts +299 -0
  164. package/treb-grid/src/layout/rectangle_cache.ts +86 -0
  165. package/treb-grid/src/render/selection-renderer.ts +414 -0
  166. package/treb-grid/src/render/svg_header_overlay.ts +93 -0
  167. package/treb-grid/src/render/svg_selection_block.ts +187 -0
  168. package/treb-grid/src/render/tile_renderer.ts +2122 -0
  169. package/treb-grid/src/types/annotation.ts +216 -0
  170. package/treb-grid/src/types/border_constants.ts +34 -0
  171. package/treb-grid/src/types/clipboard_data.ts +31 -0
  172. package/treb-grid/src/types/data_model.ts +334 -0
  173. package/treb-grid/src/types/drag_mask.ts +81 -0
  174. package/treb-grid/src/types/grid.ts +7743 -0
  175. package/treb-grid/src/types/grid_base.ts +3644 -0
  176. package/treb-grid/src/types/grid_command.ts +470 -0
  177. package/treb-grid/src/types/grid_events.ts +124 -0
  178. package/treb-grid/src/types/grid_options.ts +97 -0
  179. package/treb-grid/src/types/grid_selection.ts +60 -0
  180. package/treb-grid/src/types/named_range.ts +369 -0
  181. package/treb-grid/src/types/scale-control.ts +202 -0
  182. package/treb-grid/src/types/serialize_options.ts +72 -0
  183. package/treb-grid/src/types/set_range_options.ts +52 -0
  184. package/treb-grid/src/types/sheet.ts +3099 -0
  185. package/treb-grid/src/types/sheet_types.ts +95 -0
  186. package/treb-grid/src/types/tab_bar.ts +464 -0
  187. package/treb-grid/src/types/tile.ts +59 -0
  188. package/treb-grid/src/types/update_flags.ts +75 -0
  189. package/treb-grid/src/util/dom_utilities.ts +44 -0
  190. package/treb-grid/src/util/fontmetrics2.ts +179 -0
  191. package/treb-grid/src/util/ua.ts +104 -0
  192. package/treb-logo.svg +18 -0
  193. package/treb-parser/package.json +5 -0
  194. package/treb-parser/src/csv-parser.ts +122 -0
  195. package/treb-parser/src/index.ts +25 -0
  196. package/treb-parser/src/md-parser.ts +526 -0
  197. package/treb-parser/src/parser-types.ts +397 -0
  198. package/treb-parser/src/parser.test.ts +298 -0
  199. package/treb-parser/src/parser.ts +2673 -0
  200. package/treb-utils/package.json +5 -0
  201. package/treb-utils/src/dispatch.ts +57 -0
  202. package/treb-utils/src/event_source.ts +147 -0
  203. package/treb-utils/src/ievent_source.ts +33 -0
  204. package/treb-utils/src/index.ts +31 -0
  205. package/treb-utils/src/measurement.ts +174 -0
  206. package/treb-utils/src/resizable.ts +160 -0
  207. package/treb-utils/src/scale.ts +137 -0
  208. package/treb-utils/src/serialize_html.ts +124 -0
  209. package/treb-utils/src/template.ts +70 -0
  210. package/treb-utils/src/validate_uri.ts +61 -0
  211. package/tsconfig.json +10 -0
  212. package/tsproject.json +30 -0
  213. package/util/license-plugin-esbuild.js +86 -0
  214. package/util/list-css-vars.sh +46 -0
  215. package/README-esm.md +0 -37
  216. package/treb-bundle.css +0 -2
  217. package/treb-bundle.mjs +0 -15
@@ -0,0 +1,414 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2023 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+
22
+ import { Theme, Rectangle, ICellAddress } from 'treb-base-types';
23
+ import type { BaseLayout } from '../layout/base_layout';
24
+ import { SVGSelectionBlock, SelectionOffset } from './svg_selection_block';
25
+ import type { GridSelection } from '../types/grid_selection';
26
+ import { HeaderOverlay, Orientation } from './svg_header_overlay';
27
+ import type { DataModel, ViewModel } from '../types/data_model';
28
+
29
+ // const SVGNS = 'http://www.w3.org/2000/svg';
30
+
31
+ export class SelectionRenderer {
32
+
33
+ public nub_rectangle: Rectangle = new Rectangle(-1, -1, 0, 0);
34
+
35
+ // tmp
36
+ public cached_additional_selections = '';
37
+
38
+ private grid_selections: SVGSelectionBlock[] = [];
39
+ private row_header_selections: SVGSelectionBlock[] = [];
40
+ private column_header_selections: SVGSelectionBlock[] = [];
41
+ private corner_selections: SVGSelectionBlock[] = [];
42
+
43
+ private row_overlay!: HeaderOverlay;
44
+ private column_overlay!: HeaderOverlay;
45
+ private corner_row_overlay!: HeaderOverlay;
46
+ private corner_column_overlay!: HeaderOverlay;
47
+
48
+ constructor(
49
+ private theme: Theme,
50
+ private layout: BaseLayout,
51
+ private model: DataModel,
52
+ private view: ViewModel,
53
+ private primary_selection: GridSelection,
54
+ // private highlight_selection: GridSelection,
55
+ private additional_selections: GridSelection[]) {
56
+
57
+ }
58
+
59
+ public Initialize(): void {
60
+
61
+ // create header overlays
62
+
63
+ this.row_overlay =
64
+ new HeaderOverlay(this.theme, this.layout.row_header_selection, Orientation.Horizontal);
65
+ this.column_overlay =
66
+ new HeaderOverlay(this.theme, this.layout.column_header_selection, Orientation.Vertical);
67
+ this.corner_row_overlay =
68
+ new HeaderOverlay(this.theme, this.layout.corner_selection, Orientation.Horizontal);
69
+ this.corner_column_overlay =
70
+ new HeaderOverlay(this.theme, this.layout.corner_selection, Orientation.Vertical);
71
+ }
72
+
73
+ /**
74
+ * we cache blocks that have inline style information. if style
75
+ * information updates we will have to flush the cache and rebuild.
76
+ * /
77
+ public Flush() {
78
+
79
+ // clean up, then call initialize to reconstruct
80
+
81
+ for (const overlay of [
82
+ this.row_overlay,
83
+ this.column_overlay,
84
+ this.corner_row_overlay,
85
+ this.corner_column_overlay,
86
+ ]) {
87
+ overlay.Remove();
88
+ }
89
+
90
+ this.Initialize();
91
+
92
+ // selections: remove nodes from DOM, if connected, before cleaning up
93
+
94
+ for (const group of [
95
+ this.grid_selections,
96
+ this.row_header_selections,
97
+ this.column_header_selections,
98
+ this.corner_selections,
99
+ ]) {
100
+ for (const block of group) {
101
+
102
+ // IE11 requires parentNode; seems to work in chrome/ffx,
103
+ // so unify (was originally using parentElement)
104
+
105
+ if (block.g.parentNode) {
106
+ block.g.parentNode.removeChild(block.g);
107
+ }
108
+ }
109
+ }
110
+
111
+ this.grid_selections = [];
112
+ this.row_header_selections = [];
113
+ this.column_header_selections = [];
114
+ this.corner_selections = [];
115
+
116
+ }
117
+ */
118
+
119
+ /**
120
+ * renders all (primary and additional) selections. selections are painted
121
+ * on a separate canvas which overlays the grid. unlike grid/header layers,
122
+ * the selection canvas is transparent (alpha = true, which is default, so
123
+ * omitted).
124
+ *
125
+ * updated for svg selections. erase is now required, so parameter is removed.
126
+ * update: add an optional (default true) parameter to re-render additional
127
+ * selections; this will support cache for selections that don't change.
128
+ */
129
+ public RenderSelections(show_primary_selection = true, rerender = true): void {
130
+
131
+ // this is a dumb way of doing this... it's also error prone,
132
+ // because it needs to track all the function exits (there are
133
+ // two, atm)
134
+
135
+ const cache_primary_empty = this.primary_selection.empty;
136
+ if (!show_primary_selection) {
137
+ this.primary_selection.empty = true;
138
+ }
139
+
140
+ // temp (we could change the signature and just take an array)
141
+ const aggregate = [this.primary_selection].concat(this.additional_selections);
142
+
143
+ this.RenderSelectionGroup(aggregate, this.layout.grid_selection, undefined, undefined, this.grid_selections, undefined, rerender);
144
+
145
+ // this is the layout rect for row/column header highlights (primary selection only)
146
+
147
+ let header_selection_rect = new Rectangle(-1, -1, 0, 0);
148
+ if (!this.primary_selection.empty) {
149
+ const area = this.view.active_sheet.RealArea(this.primary_selection.area);
150
+ header_selection_rect =
151
+ this.layout.CellAddressToRectangle(area.start).Combine(
152
+ this.layout.CellAddressToRectangle(area.end));
153
+ }
154
+
155
+ // highlight row header (if visible)
156
+
157
+ if (!this.primary_selection.empty && this.layout.header_offset.y > 2) {
158
+ this.row_overlay.Show(header_selection_rect.left, 0,
159
+ header_selection_rect.width, this.layout.header_offset.y);
160
+ this.corner_row_overlay.Show(header_selection_rect.left + this.layout.header_offset.x, 0,
161
+ header_selection_rect.width, this.layout.header_offset.y);
162
+ }
163
+ else {
164
+ this.row_overlay.Hide();
165
+ this.corner_row_overlay.Hide();
166
+ }
167
+
168
+ // highlight column header (if visible)
169
+
170
+ if (!this.primary_selection.empty && this.layout.header_offset.x > 2) {
171
+ this.column_overlay.Show(0, header_selection_rect.top,
172
+ this.layout.header_offset.x, header_selection_rect.height);
173
+ this.corner_column_overlay.Show(0, header_selection_rect.top + this.layout.header_offset.y,
174
+ this.layout.header_offset.x, header_selection_rect.height);
175
+ }
176
+ else {
177
+ this.column_overlay.Hide();
178
+ this.corner_column_overlay.Hide();
179
+ }
180
+
181
+ if (!this.view.active_sheet.freeze.columns && !this.view.active_sheet.freeze.rows) {
182
+ this.primary_selection.empty = cache_primary_empty;
183
+ return;
184
+ }
185
+
186
+ // check visibility for selections in frozen rows, columns
187
+
188
+ const visible_row: boolean[] = [];
189
+ const visible_column: boolean[] = [];
190
+
191
+ if (this.primary_selection.empty) {
192
+ visible_row.push(false);
193
+ visible_column.push(false);
194
+ }
195
+ else {
196
+ const start = this.primary_selection.area.start;
197
+ visible_row.push(
198
+ (start.row <= this.view.active_sheet.freeze.rows) ||
199
+ (start.row === Infinity));
200
+
201
+ visible_column.push(
202
+ (start.column <= this.view.active_sheet.freeze.columns) ||
203
+ (start.column === Infinity));
204
+ }
205
+
206
+ for (const {area} of this.additional_selections) {
207
+ visible_row.push(
208
+ (area.start.row <= this.view.active_sheet.freeze.rows) ||
209
+ (area.start.row === Infinity));
210
+
211
+ visible_column.push(
212
+ (area.start.column <= this.view.active_sheet.freeze.columns) ||
213
+ (area.start.column === Infinity));
214
+ }
215
+
216
+ // selections...
217
+
218
+ if (this.view.active_sheet.freeze.rows) {
219
+ this.RenderSelectionGroup(aggregate, this.layout.row_header_selection,
220
+ visible_row, undefined, this.row_header_selections,
221
+ {x: 0, y: this.layout.header_offset.y});
222
+ }
223
+
224
+ if (this.view.active_sheet.freeze.columns) {
225
+ this.RenderSelectionGroup(aggregate, this.layout.column_header_selection,
226
+ visible_column, undefined, this.column_header_selections,
227
+ {x: this.layout.header_offset.x, y: 0});
228
+ }
229
+
230
+ if (this.view.active_sheet.freeze.rows && this.view.active_sheet.freeze.columns) {
231
+ this.RenderSelectionGroup(aggregate, this.layout.corner_selection,
232
+ visible_column, visible_row, this.corner_selections, {...this.layout.header_offset});
233
+ }
234
+
235
+ this.primary_selection.empty = cache_primary_empty;
236
+
237
+ }
238
+
239
+ /**
240
+ * render a group of selections, optionally gated on one or two boolean
241
+ * arrays (used to check if the selection is within some bounds)
242
+ */
243
+ private RenderSelectionGroup(
244
+ aggregate: GridSelection[],
245
+ node: SVGElement,
246
+ visible_a: boolean[]|undefined,
247
+ visible_b: boolean[]|undefined,
248
+ group: SVGSelectionBlock[],
249
+ offset?: SelectionOffset,
250
+ rerender = true) {
251
+
252
+ for (let i = 0; i < aggregate.length; i++ ){
253
+
254
+ const sheet_match = (!aggregate[i].area.start.sheet_id) ||
255
+ (aggregate[i].area.start.sheet_id === this.view.active_sheet.id);
256
+
257
+ if (sheet_match && !aggregate[i].empty && (!visible_a || visible_a[i]) && (!visible_b || visible_b[i])) {
258
+ if (rerender || !aggregate[i].rendered) {
259
+ const block = this.EnsureGridSelectionBlock(node, group, i, offset);
260
+ this.RenderSVGSelection(aggregate[i], block, i);
261
+ }
262
+ }
263
+ else {
264
+ if (group[i]) group[i].Show(false);
265
+ }
266
+ }
267
+
268
+ for (let i = aggregate.length; i < group.length; i++) {
269
+ if (group[i]) group[i].Show(false);
270
+ }
271
+
272
+ }
273
+
274
+ /**
275
+ * create or return existing node. supports changing the offset,
276
+ * as that may be variable.
277
+ *
278
+ * FIXME: now that this is in a single method, could inline?
279
+ */
280
+ private EnsureGridSelectionBlock(
281
+ node: SVGElement,
282
+ node_set: SVGSelectionBlock[],
283
+ index: number,
284
+ offset?: SelectionOffset){
285
+
286
+ // ensure the selection
287
+
288
+ let selection_block: SVGSelectionBlock = node_set[index];
289
+ if (!selection_block) {
290
+ selection_block = new SVGSelectionBlock(!index, this.theme);
291
+ node_set[index] = selection_block;
292
+
293
+ if (index) {
294
+
295
+ // alternate, should indicate a different way
296
+
297
+ // we're adding a node to contain alternate selections just so that
298
+ // we can use 1n, 2n, 3n indexing in CSS... although nth-of-type looks
299
+ // like it might help, it won't. it doesn't mean nth-instance.
300
+
301
+ let group: SVGElement = node.querySelector('.alternate-selections') as SVGElement;
302
+ if (!group) {
303
+ group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
304
+ group.setAttribute('class', 'alternate-selections');
305
+ node.appendChild(group);
306
+ }
307
+ group?.appendChild(selection_block.g);
308
+ }
309
+ else {
310
+ // primary
311
+ node.appendChild(selection_block.g);
312
+ }
313
+
314
+ }
315
+
316
+ if (offset) selection_block.Offset(offset);
317
+
318
+ return selection_block;
319
+ }
320
+
321
+ private ClampEnd(address: ICellAddress) {
322
+
323
+ // NOTE: column/row can be infinity, but min handles that properly
324
+
325
+ return {
326
+ row: Math.min(address.row, this.view.active_sheet.rows - 1),
327
+ column: Math.min(address.column, this.view.active_sheet.columns - 1),
328
+ };
329
+
330
+ }
331
+
332
+ /**
333
+ * testing an SVG selection. index replaces primary; primary is always index 0.
334
+ */
335
+ private RenderSVGSelection(selection: GridSelection, block: SVGSelectionBlock, index = 0) {
336
+
337
+ const area = this.view.active_sheet.RealArea(selection.area, true);
338
+
339
+ let rect = this.layout.CellAddressToRectangle(area.start);
340
+ if (area.count > 1) {
341
+ rect = rect.Combine(this.layout.CellAddressToRectangle(area.end));
342
+ }
343
+ else if (index) {
344
+
345
+ // update: select merge areas for alternate selections when single
346
+
347
+ const data = this.view.active_sheet.CellData(selection.target);
348
+ if (data.merge_area) {
349
+ rect = this.layout.CellAddressToRectangle(data.merge_area.start);
350
+ rect = rect.Combine(this.layout.CellAddressToRectangle(data.merge_area.end));
351
+ }
352
+ }
353
+
354
+ // nub select target wants the base rectangle (not offset for tiles)
355
+ // FIXME: parameterize size
356
+
357
+ if (!index) {
358
+ this.nub_rectangle = new Rectangle(
359
+ rect.left + rect.width - 6,
360
+ rect.top + rect.height - 6,
361
+ 11, 11);
362
+ }
363
+ else {
364
+ // block.SetThemeColor(index - 1);
365
+ }
366
+
367
+ // FIXME: with giant selection svg, we should clip the selection rect
368
+ // to visible to prevent giant rects/paths
369
+
370
+ // when not showing headers...
371
+
372
+ if (rect.top === 0 && this.layout.header_offset.y <= 1) {
373
+ rect.top = 1;
374
+ rect.height -= 1;
375
+ }
376
+ if (rect.left === 0 && this.layout.header_offset.x <= 1) {
377
+ rect.left = 1;
378
+ rect.width -= 1;
379
+ }
380
+
381
+ // don't render if the rect is <= 0 height or width (cells are hidden)
382
+
383
+ if (rect.height <= 0 || rect.height <= 0) {
384
+ block.Show(false);
385
+ return;
386
+ }
387
+
388
+ // FIXME: this could be wrapped up in one call
389
+
390
+ block.SetOutline(rect, !!index);
391
+
392
+ if (!index) {
393
+
394
+ // get the target rect (primary only)
395
+
396
+ let target_rect = this.layout.CellAddressToRectangle(selection.target);
397
+ const data = this.view.active_sheet.CellData(selection.target);
398
+ if (data.merge_area) {
399
+ target_rect = this.layout.CellAddressToRectangle(data.merge_area.start);
400
+ target_rect = target_rect.Combine(this.layout.CellAddressToRectangle(data.merge_area.end));
401
+ }
402
+
403
+ block.SetFill(target_rect, rect);
404
+ block.SetNub(rect);
405
+ }
406
+ else {
407
+ selection.rendered = true;
408
+ }
409
+
410
+ block.Show();
411
+
412
+ }
413
+
414
+ }
@@ -0,0 +1,93 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2023 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+
22
+ import type { Theme } from 'treb-base-types';
23
+
24
+ const SVGNS = 'http://www.w3.org/2000/svg';
25
+
26
+ export enum Orientation {
27
+ Horizontal,
28
+ Vertical,
29
+ }
30
+
31
+ export class HeaderOverlay {
32
+
33
+ private g: SVGGElement;
34
+ private overlay: SVGRectElement;
35
+ private highlight: SVGRectElement;
36
+
37
+ constructor(
38
+ private theme: Theme,
39
+ private container: SVGElement,
40
+ private orientation: Orientation) {
41
+
42
+ this.g = document.createElementNS(SVGNS, 'g');
43
+ this.g.setAttribute('class', 'treb-header-overlay');
44
+
45
+ this.overlay = document.createElementNS(SVGNS, 'rect');
46
+ this.overlay.setAttribute('class', 'treb-overlay');
47
+
48
+ this.highlight = document.createElementNS(SVGNS, 'rect');
49
+ this.highlight.setAttribute('class', 'treb-highlight');
50
+
51
+ this.g.style.display = 'none';
52
+ this.g.appendChild(this.highlight);
53
+ this.g.appendChild(this.overlay);
54
+
55
+ container.appendChild(this.g);
56
+
57
+ }
58
+
59
+ /**
60
+ * remove from DOM, prior to cleanup
61
+ */
62
+ public Remove() {
63
+ this.container.removeChild(this.g);
64
+ }
65
+
66
+ public Hide() {
67
+ this.g.style.display = 'none';
68
+ }
69
+
70
+ public Show(x: number, y: number, width: number, height: number){
71
+
72
+ this.overlay.setAttribute('x', x.toString());
73
+ this.overlay.setAttribute('y', y.toString());
74
+ this.overlay.setAttribute('width', width.toString());
75
+ this.overlay.setAttribute('height', height.toString());
76
+
77
+ if (this.orientation === Orientation.Horizontal) {
78
+ this.highlight.setAttribute('x', x.toString());
79
+ this.highlight.setAttribute('y', (y + height - 2).toString());
80
+ this.highlight.setAttribute('width', width.toString());
81
+ this.highlight.setAttribute('height', '2');
82
+ }
83
+ else {
84
+ this.highlight.setAttribute('x', (x + width - 2).toString());
85
+ this.highlight.setAttribute('y', y.toString());
86
+ this.highlight.setAttribute('width', '2');
87
+ this.highlight.setAttribute('height', height.toString());
88
+ }
89
+
90
+ this.g.style.display = 'block';
91
+ }
92
+
93
+ }
@@ -0,0 +1,187 @@
1
+ /*
2
+ * This file is part of TREB.
3
+ *
4
+ * TREB is free software: you can redistribute it and/or modify it under the
5
+ * terms of the GNU General Public License as published by the Free Software
6
+ * Foundation, either version 3 of the License, or (at your option) any
7
+ * later version.
8
+ *
9
+ * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
+ * details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License along
15
+ * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
+ *
17
+ * Copyright 2022-2023 trebco, llc.
18
+ * info@treb.app
19
+ *
20
+ */
21
+
22
+ import type { Theme, Rectangle } from 'treb-base-types';
23
+
24
+ /**
25
+ * the original selections -- a canvas overlaid over the tile canvases --
26
+ * broke android chrome, hard. so we are switching to an svg overlay (seems
27
+ * ok on android chrome, at least for now).
28
+ *
29
+ * this class wraps up some of the svg-specific stuff, particularly setting
30
+ * attributes.
31
+ */
32
+
33
+ const SVGNS = 'http://www.w3.org/2000/svg';
34
+
35
+ export interface SelectionOffset {
36
+ x: number;
37
+ y: number;
38
+ }
39
+
40
+ export class SVGSelectionBlock {
41
+
42
+ public g: SVGGElement;
43
+ public outline: SVGRectElement;
44
+ public fill?: SVGElement; // SVGPathElement;
45
+ public nub?: SVGRectElement;
46
+
47
+ constructor( primary: boolean,
48
+ private theme: Theme,
49
+ private offset: SelectionOffset = {x: 0, y: 0}) {
50
+
51
+ this.g = document.createElementNS(SVGNS, 'g');
52
+ this.g.setAttribute('transform', `translate(${offset.x}, ${offset.y})`);
53
+
54
+ this.outline = document.createElementNS(SVGNS, 'rect');
55
+ this.outline.setAttribute('class', 'outline');
56
+
57
+ if (primary) {
58
+
59
+ this.g.setAttribute('class', 'selection primary-selection');
60
+
61
+ // primary selections have a separate fill, plus the nub. separate
62
+ // fill because the "target" is unfilled.
63
+
64
+ this.fill = document.createElementNS(SVGNS, 'path');
65
+ this.fill.setAttribute('class', 'fill');
66
+
67
+ this.nub = document.createElementNS(SVGNS, 'rect');
68
+ this.nub.setAttribute('class', 'nub');
69
+
70
+ this.g.appendChild(this.fill);
71
+ this.g.appendChild(this.outline);
72
+ this.g.appendChild(this.nub);
73
+
74
+ }
75
+ else {
76
+ this.g.setAttribute('class', 'selection alternate-selection');
77
+
78
+ // secondary selections. fill is not used, we just fill the rect
79
+
80
+ // UPDATE: adding the fill, for styling purposes; we can set color,
81
+ // and use currentColor, but we can't set opacity separately so we
82
+ // need another node. which is a waste, but ergonomics ftw!
83
+
84
+ this.fill = document.createElementNS(SVGNS, 'rect');
85
+ this.fill.setAttribute('class', 'fill');
86
+
87
+ // this.SetThemeColor(0);
88
+ // if (theme.additional_selection_line_dash_array) {
89
+ // this.outline.setAttribute('stroke-dasharray', theme.additional_selection_line_dash_array);
90
+ // }
91
+
92
+ this.g.appendChild(this.fill);
93
+ this.g.appendChild(this.outline);
94
+
95
+ }
96
+ }
97
+
98
+ public Offset(offset: SelectionOffset): void {
99
+ this.g.setAttribute('transform', `translate(${offset.x}, ${offset.y})`);
100
+ }
101
+
102
+ /*
103
+ public SetThemeColor(index = 0) {
104
+
105
+ if (Array.isArray(this.theme.additional_selection_color)) {
106
+ if (index >= this.theme.additional_selection_color.length) {
107
+ index = index % this.theme.additional_selection_color.length;
108
+ }
109
+ }
110
+
111
+ if (this.theme.additional_selection_overlay_color) {
112
+ if (typeof this.theme.additional_selection_overlay_color === 'string') {
113
+ this.outline.setAttribute('fill', this.theme.additional_selection_overlay_color);
114
+ }
115
+ else {
116
+ this.outline.setAttribute('fill', this.theme.additional_selection_overlay_color[index] || '');
117
+ }
118
+ }
119
+ else {
120
+ this.outline.setAttribute('fill', '');
121
+ }
122
+
123
+ if (this.theme.additional_selection_color) {
124
+ if (typeof this.theme.additional_selection_color === 'string') {
125
+ this.outline.setAttribute('stroke', this.theme.additional_selection_color);
126
+ }
127
+ else {
128
+ this.outline.setAttribute('stroke', this.theme.additional_selection_color[index] || '');
129
+ }
130
+ }
131
+ else {
132
+ this.outline.setAttribute('stroke', '');
133
+ }
134
+
135
+ }
136
+ */
137
+
138
+ public Show(show = true) {
139
+ this.g.style.display = show ? 'block' : 'none';
140
+ }
141
+
142
+ public SetOutline(rect: Rectangle, fill = false): void {
143
+ this.outline.setAttribute('x', (rect.left - 1).toString());
144
+ this.outline.setAttribute('y', (rect.top - 1).toString());
145
+ this.outline.setAttribute('width', (rect.width + 1).toString());
146
+ this.outline.setAttribute('height', (rect.height + 1).toString());
147
+
148
+ if (fill && this.fill) {
149
+ this.fill.setAttribute('x', (rect.left).toString());
150
+ this.fill.setAttribute('y', (rect.top).toString());
151
+ this.fill.setAttribute('width', (rect.width).toString());
152
+ this.fill.setAttribute('height', (rect.height).toString());
153
+ }
154
+
155
+ }
156
+
157
+ public SetFill(inside: Rectangle, outside: Rectangle) {
158
+ if (!this.fill) return;
159
+
160
+ const d: string[] = [];
161
+
162
+ // inner
163
+ d.push('M' + inside.left + ' ' + inside.top);
164
+ d.push('L' + inside.left + ' ' + inside.bottom);
165
+ d.push('L' + inside.right + ' ' + inside.bottom);
166
+ d.push('L' + inside.right + ' ' + inside.top);
167
+ d.push('Z');
168
+
169
+ // outer, reverse direction
170
+ d.push('M' + outside.left + ' ' + outside.top);
171
+ d.push('L' + outside.right + ' ' + outside.top);
172
+ d.push('L' + outside.right + ' ' + outside.bottom);
173
+ d.push('L' + outside.left + ' ' + outside.bottom);
174
+ d.push('Z');
175
+
176
+ this.fill.setAttribute('d', d.join(' '));
177
+ }
178
+
179
+ public SetNub(rect: Rectangle) {
180
+ if (!this.nub) return;
181
+ this.nub.setAttribute('x', (rect.left + rect.width - 4).toString());
182
+ this.nub.setAttribute('y', (rect.top + rect.height - 4).toString());
183
+ this.nub.setAttribute('width', '7');
184
+ this.nub.setAttribute('height', '7');
185
+ }
186
+
187
+ }