@trebco/treb 23.6.5 → 25.0.0-rc1

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} +285 -269
  11. package/esbuild-custom-element.mjs +336 -0
  12. package/esbuild.js +305 -0
  13. package/package.json +43 -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 +1227 -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 +5362 -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 +320 -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,608 @@
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 { Style } from './style';
23
+ import { Color } from './color';
24
+
25
+ /*
26
+ * so this is a little strange. we use CSS to populate a theme object,
27
+ * then we create HTML nodes and assign styles to them from the theme
28
+ * object. we should cut out the middleman and use CSS to style nodes.
29
+ *
30
+ * how did we get here? because we paint, we originally used a theme object.
31
+ * it made sense to put non-painted theme info in there so it was all in one
32
+ * place.
33
+ *
34
+ * then subsequently we figured out we could use css, apply it, and read out
35
+ * values to populate the theme. so the theme object became internal-only,
36
+ * but still held all the HTML theme data.
37
+ *
38
+ * I guess it didn't occur to me at the time that you could use CSS for
39
+ * everything except painting, and then pull out only those values you
40
+ * actually need for painting.
41
+ *
42
+ * It has since occurred to me, and this should be a focus of future
43
+ * development (thinking v9). not sure what the overall benefits will be,
44
+ * but it should reduce complexity at least a little.
45
+ *
46
+ * As part of that I want to generally switch to percentage font sizes for
47
+ * spreadsheet cells.
48
+ */
49
+
50
+ /**
51
+ * composite styling for tables.
52
+ *
53
+ * @privateRemarks
54
+ *
55
+ * we used to have a "footer", now removed. use borders on rows.
56
+ */
57
+ export interface TableTheme {
58
+
59
+ /** the first row in a table, showing column titles. */
60
+ header?: Style.Properties;
61
+
62
+ /**
63
+ * odd rows in the table. we count the title row as zero, so
64
+ * the first row in the table containing data is 1, hence odd.
65
+ */
66
+ odd?: Style.Properties;
67
+
68
+ /**
69
+ * even rows in the table.
70
+ */
71
+ even?: Style.Properties;
72
+
73
+ /**
74
+ * styling for the totals row, if included. this will be the last
75
+ * row in the table.
76
+ */
77
+ total?: Style.Properties;
78
+ }
79
+
80
+ /** theme options - colors and fonts */
81
+ export interface Theme {
82
+
83
+ /** grid headers (composite) */
84
+ headers?: Style.Properties;
85
+
86
+ /** grid cell defaults (composite: size, font face, color, background) */
87
+ grid_cell?: Style.Properties;
88
+
89
+ /** gridlines color */
90
+ grid_color: string;
91
+
92
+ /**
93
+ * new: gridlines color for headers. should default to the regular grid
94
+ * color unless it's explicitly set.
95
+ */
96
+ headers_grid_color?: string;
97
+
98
+ /** color of grid lines */
99
+ // grid?: Style.Properties;
100
+
101
+ /** color of in-cell note marker */
102
+ note_marker_color: string;
103
+
104
+ /** theme colors */
105
+ theme_colors?: string[];
106
+
107
+ /** as RGB, so we can adjust them */
108
+ theme_colors_rgb?: number[][];
109
+
110
+ /**
111
+ * cache tinted colors. the way this works is we index by the
112
+ * theme color first, then by the tint value.
113
+ *
114
+ * TODO: we could reduce the tint space... the values look like
115
+ * they are fairly regular (todo)
116
+ *
117
+ * what we are doing now is rounding to 2 decimal places on import, that
118
+ * cleans up the super precise values we get from excel to more reasonable
119
+ * values for keys, and I don't think most people can tell the difference
120
+ * between tinting 25% vs 24.99999872%
121
+ */
122
+ tint_cache?: Record<number, string>[];
123
+
124
+ /**
125
+ * cache for offset colors
126
+ */
127
+ offset_cache?: Record<string, string>;
128
+
129
+ /**
130
+ * this is now default, but you can set explicitly per-table
131
+ */
132
+ table?: TableTheme;
133
+
134
+ /**
135
+ * this is for tinting. we're experimenting with tinting towards black
136
+ * or white, as opposed to lightening/darkening colors. this should improve
137
+ * swapping themed colors.
138
+ *
139
+ * how to derive this value? @see DeriveColorScheme
140
+ */
141
+ mode: 'light'|'dark';
142
+
143
+ /** light color for offset (against dark background) */
144
+ offset_light: string;
145
+
146
+ /** dark color for offset (against light background) */
147
+ offset_dark: string;
148
+
149
+ }
150
+
151
+ /**
152
+ * @internal
153
+ */
154
+ export const DefaultTheme: Theme = {
155
+ grid_color: '#ccc',
156
+ note_marker_color: '#d2c500',
157
+ mode: 'light',
158
+ offset_cache: {},
159
+ offset_light: '#fff',
160
+ offset_dark: '#000',
161
+ };
162
+
163
+ /**
164
+ * now just a wrapper, we should remove
165
+ * @deprecated
166
+ * @internal
167
+ */
168
+ export const ThemeColor = (theme: Theme, color?: Style.Color): string => {
169
+ return ThemeColor2(theme, color, 0);
170
+ };
171
+
172
+ /**
173
+ * we cache values in the theme object so that we can dump it when we
174
+ * reload or update the theme.
175
+ *
176
+ * we're now inverting tint for dark themes. the idea is that if you
177
+ * are using a dark theme, it's more natural to go in that direction, and
178
+ * you can use the same foreground color.
179
+ *
180
+ * because this is ephemeral it won't impact export.
181
+ */
182
+ const TintedColor = (theme: Theme, index: number, tint: number) => {
183
+
184
+ if (theme.mode === 'dark') {
185
+ tint = -tint; // invert;
186
+ }
187
+
188
+ if (!theme.tint_cache) {
189
+ theme.tint_cache = [];
190
+ }
191
+
192
+ if (!theme.tint_cache[index]) {
193
+ theme.tint_cache[index] = {};
194
+ }
195
+
196
+ let color = theme.tint_cache[index][tint];
197
+ if (!color) {
198
+
199
+ const rgb = (theme.theme_colors_rgb ? theme.theme_colors_rgb[index] : [0, 0, 0]) || [0, 0, 0];
200
+ let tinted: {r: number, g: number, b: number};
201
+ if (tint > 0) {
202
+ tinted = Color.Lighten(rgb[0], rgb[1], rgb[2], tint * 100, true);
203
+ }
204
+ else {
205
+ tinted = Color.Darken(rgb[0], rgb[1], rgb[2], -tint * 100, true);
206
+ }
207
+ color = `rgb(${tinted.r},${tinted.g},${tinted.b})`;
208
+ theme.tint_cache[index][tint] = color;
209
+
210
+ }
211
+
212
+ return color;
213
+
214
+ };
215
+
216
+ /**
217
+ * this includes an implicit check for valid color, if a color
218
+ * can't be resolved it returns ''. now supports offset colors.
219
+ * offset returns a light color against a dark background, and
220
+ * vice versa. what constitutes a dark background is not entirely
221
+ * clear; atm using lightness = .65.
222
+ *
223
+ * @internal
224
+ */
225
+ export const ThemeColor2 = (theme: Theme, color?: Style.Color, default_index?: number): string => {
226
+
227
+ if (color?.offset) {
228
+
229
+ // don't do this
230
+ if (color.offset.offset) {
231
+ console.warn('invalid offset color');
232
+ return '';
233
+ }
234
+
235
+ const resolved = ThemeColor2(theme, color.offset);
236
+
237
+ // check cache
238
+ if (theme.offset_cache && theme.offset_cache[resolved]) {
239
+ return theme.offset_cache[resolved];
240
+ }
241
+
242
+ let offset = theme.offset_light;
243
+
244
+ if (resolved) {
245
+ // ok figure it out?
246
+ const match = resolved.match(/rgb\((\d+), (\d+), (\d+)\)/);
247
+ if (match) {
248
+ const hsl = Color.RGBToHSL(Number(match[1]), Number(match[2]), Number(match[3]));
249
+ // console.info('resolved', resolved, {hsl});
250
+ if (hsl.l > .65) {
251
+ offset = theme.offset_dark;
252
+ }
253
+ }
254
+ else {
255
+ // ...
256
+ console.warn(`can't offset against color`, resolved);
257
+ }
258
+
259
+ if (!theme.offset_cache) {
260
+ theme.offset_cache = {};
261
+ }
262
+ theme.offset_cache[resolved] = offset;
263
+ }
264
+ else {
265
+ console.warn(`can't resolve offset color`, color.offset);
266
+ }
267
+
268
+ return offset;
269
+ }
270
+
271
+ // explicit color, or none
272
+
273
+ if (color?.text) {
274
+ return color.text === 'none' ? '' : color.text;
275
+ }
276
+
277
+ // theme color. we need a way to cache these lookups, especially for tinting
278
+
279
+ if (color?.theme || color?.theme === 0) {
280
+ if (color.tint) {
281
+ return TintedColor(theme, color.theme, color.tint);
282
+ }
283
+ return theme.theme_colors ? theme.theme_colors[color.theme] : '';
284
+ }
285
+
286
+ // default from argument
287
+
288
+ if (default_index || default_index === 0) {
289
+ return theme.theme_colors ? theme.theme_colors[default_index] : '';
290
+ }
291
+
292
+ // actual default, which is nothing
293
+
294
+ return '';
295
+
296
+ };
297
+
298
+ const ParseFontSize = (size: string): { value: number, unit: 'pt'|'px'|'em'|'%' } => {
299
+
300
+ let value = 10;
301
+ let unit:'pt'|'px'|'em'|'%' = 'pt';
302
+
303
+ const match = size.match(/^([\d.]+)(\D.*)$/); // pt, px, em, rem, %
304
+ if (match) {
305
+ value = Number(match[1]);
306
+ unit = match[2] as 'pt'|'px'|'em'|'%';
307
+ }
308
+
309
+ return { value, unit };
310
+ };
311
+
312
+ /* *
313
+ * pull out styles we apply to tables, if they differ from the base.
314
+ * setting "initial" or "inherit" doesn't work to clear them (at least atm);
315
+ * use "transparent" to unset.
316
+ * /
317
+ const TableStyleFromCSS = (base: CSSStyleDeclaration, style: CSSStyleDeclaration): Style.Properties => {
318
+
319
+ const props: Style.Properties = {};
320
+
321
+ if (style.borderTopColor !== base.borderTopColor) {
322
+ props.border_top = 1;
323
+ props.border_top_fill = { text: style.borderTopColor };
324
+ if (style.borderTopStyle === 'double') {
325
+ props.border_top = 2;
326
+ }
327
+ }
328
+
329
+ if (style.borderBottomColor !== base.borderBottomColor) {
330
+ props.border_bottom = 1;
331
+ props.border_bottom_fill = { text: style.borderBottomColor };
332
+ if (style.borderBottomStyle === 'double') {
333
+ props.border_bottom = 2;
334
+ }
335
+ }
336
+
337
+ if (style.backgroundColor !== base.backgroundColor) {
338
+ props.fill = { text: style.backgroundColor };
339
+ }
340
+
341
+ if (style.color !== base.color) {
342
+ props.text = { text: style.color };
343
+ }
344
+
345
+ if (style.fontWeight !== base.fontWeight) {
346
+ props.bold = /(?:700|bold)/.test(style.fontWeight);
347
+ }
348
+
349
+ if (style.fontStyle !== base.fontStyle) {
350
+ props.italic = /italic/.test(style.fontStyle);
351
+ }
352
+
353
+ if (style.textDecoration !== base.textDecoration) {
354
+ const style_underline = /underline/.test(style.textDecoration);
355
+ const base_underline = /underline/.test(base.textDecoration);
356
+
357
+ if (base_underline !== style_underline) {
358
+ props.underline = style_underline;
359
+ }
360
+
361
+ const style_strike = /line-through/.test(style.textDecoration);
362
+ const base_strike = /line-through/.test(base.textDecoration);
363
+
364
+ if (base_strike !== style_strike) {
365
+ props.strike = style_strike;
366
+ }
367
+
368
+ }
369
+
370
+ return props;
371
+
372
+ };
373
+ */
374
+
375
+ // testing
376
+ const StyleFromCSS = (css: CSSStyleDeclaration): Style.Properties => {
377
+
378
+ const { value, unit } = ParseFontSize(css.fontSize||'');
379
+
380
+ const style: Style.Properties = {
381
+ fill: { text: css.backgroundColor }, // || 'none',
382
+ text: { text: css.color },
383
+ font_size: {
384
+ unit, value,
385
+ },
386
+ font_face: css.fontFamily,
387
+ };
388
+
389
+ // the default border comes from the "theme colors", not from
390
+ // the CSS property (it used to come from the CSS property, which
391
+ // is why we have the CSS property set).
392
+ //
393
+ // default border is theme color 1.
394
+ //
395
+
396
+ if (/italic/i.test(css.font)) {
397
+ style.italic = true;
398
+ }
399
+
400
+ // for painting we only support regular (400) and bold (700), those
401
+ // are the only values allowed by canvas (I think)
402
+
403
+ if (css.fontWeight === '700') {
404
+ style.bold = true;
405
+ }
406
+
407
+ return style;
408
+ }
409
+
410
+ /**
411
+ * how to derive the light/dark theme? it's complicated, as it turns out.
412
+ * there's almost nothing we can do to reliably determine what theme
413
+ * is set. the best thing would be color-scheme, which affects (among other
414
+ * things) scrollbars, but that might be set to something like 'light dark'
415
+ * which is indeterminate.
416
+ *
417
+ * so what we are going to do is check the grid foreground and background;
418
+ * if the foreground is lighter than the background, we're in dark mode.
419
+ * and vice-versa.
420
+ *
421
+ */
422
+ const DeriveColorScheme = (theme: Theme, context: CanvasRenderingContext2D): 'light' | 'dark' => {
423
+
424
+ const foreground_color = theme.grid_cell?.text;
425
+ const background_color = theme.grid_cell?.fill;
426
+
427
+ // because these are rendered to a canvas, we know that A is 255
428
+
429
+ context.fillStyle = foreground_color?.text || '';
430
+ context.fillRect(0, 0, 3, 3);
431
+ const fg = Color.RGBToHSL(...(Array.from(context.getImageData(1, 1, 1, 1).data) as [number, number, number]));
432
+
433
+ context.fillStyle = background_color?.text || '';
434
+ context.fillRect(0, 0, 3, 3);
435
+ const bg = Color.RGBToHSL(...(Array.from(context.getImageData(1, 1, 1, 1).data) as [number, number, number]));
436
+
437
+ // console.info({fg, bg});
438
+
439
+ return fg.l > bg.l ? 'dark' : 'light';
440
+
441
+ }
442
+
443
+ /**
444
+ * this is a shortcut for creating table formats based on theme colors.
445
+ * TODO: we might want to swap styles based on light/dark mode?
446
+ *
447
+ * @internal
448
+ */
449
+ export const ThemeColorTable = (theme_color: number, tint = .7): TableTheme => {
450
+
451
+ const borders: Style.Properties = {
452
+ border_top: 1,
453
+ border_top_fill: { theme: theme_color },
454
+ border_bottom: 1,
455
+ border_bottom_fill: { theme: theme_color },
456
+ };
457
+
458
+ return {
459
+ header: {
460
+ // text: { theme: theme.mode === 'dark' ? 1 : 0, },
461
+ // text: { text: '#fff' },
462
+ text: { offset: {theme: theme_color} },
463
+ fill: {theme: theme_color},
464
+ bold: true,
465
+ ...borders,
466
+ },
467
+ odd: {
468
+ fill: { theme: theme_color, tint },
469
+ ...borders,
470
+ },
471
+ even: {
472
+ ...borders,
473
+ },
474
+ total: {
475
+ ...borders,
476
+ border_top: 2,
477
+ },
478
+ }
479
+
480
+ }
481
+
482
+ /**
483
+ * @internal
484
+ */
485
+ export const LoadThemeProperties = (container: HTMLElement): Theme => {
486
+
487
+ const theme: Theme = JSON.parse(JSON.stringify(DefaultTheme));
488
+
489
+ const Append = (parent: HTMLElement, classes: string): HTMLDivElement => {
490
+ const node = document.createElement('div');
491
+ node.setAttribute('class', classes);
492
+ parent.appendChild(node);
493
+ return node;
494
+ }
495
+
496
+ const ElementCSS = (parent: HTMLElement, classes: string): CSSStyleDeclaration => {
497
+ return window.getComputedStyle(Append(parent, classes));
498
+ }
499
+
500
+ const node = Append(container, '');
501
+ const CSS = ElementCSS.bind(0, node);
502
+
503
+ let css = CSS('grid-cells');
504
+ theme.grid_cell = StyleFromCSS(css);
505
+ theme.grid_color = css.stroke || '';
506
+
507
+ css = CSS('grid-headers');
508
+ theme.headers = StyleFromCSS(css);
509
+ theme.headers_grid_color = css.stroke;
510
+ if (!theme.headers_grid_color || theme.headers_grid_color === 'none') {
511
+ theme.headers_grid_color = theme.grid_color;
512
+ }
513
+
514
+ css = CSS('treb-offset-dark');
515
+ if (css.color) {
516
+ theme.offset_dark = css.color;
517
+ }
518
+
519
+ css = CSS('treb-offset-light');
520
+ if (css.color) {
521
+ theme.offset_light = css.color;
522
+ }
523
+
524
+ /*
525
+ css = CSS('grid-background');
526
+ if (css.backgroundImage) {
527
+ const match = css.backgroundImage.match(/url\("*(.*?)"*\)/);
528
+ if (match) {
529
+ theme.background_image = new Image();
530
+ theme.background_image.src = match[1];
531
+ }
532
+ }
533
+ */
534
+
535
+ // this _is_ painted, but it doesn't necessarily need to be -- we
536
+ // could use a node. that would require moving it around, though.
537
+ // let's leave it for now.
538
+
539
+ css = CSS('note-marker');
540
+ theme.note_marker_color = css.backgroundColor;
541
+
542
+ // updating tables. we're now defining tables as Style.Properties
543
+ // directly. the aim is to use theme colors so we can have multiple
544
+ // table styles without too much fuss.
545
+
546
+ /*
547
+ const root_css = CSS('');
548
+ theme.table = {
549
+ header: TableStyleFromCSS(root_css, CSS('treb-table header')),
550
+ odd: TableStyleFromCSS(root_css, CSS('treb-table row-odd')),
551
+ even: TableStyleFromCSS(root_css, CSS('treb-table row-even')),
552
+ // footer: TableStyleFromCSS(root_css, CSS('treb-table footer')),
553
+ total: TableStyleFromCSS(root_css, CSS('treb-table total')),
554
+ }
555
+ */
556
+
557
+ // console.info(theme.table);
558
+
559
+ // theme colors
560
+
561
+ node.style.color='rgba(1,2,3,.4)'; // this is an attempt at a unique identifier
562
+ css = CSS('');
563
+ const compare = css.color;
564
+
565
+ theme.theme_colors = [
566
+ theme.grid_cell.fill?.text || 'rgb(255, 255, 255)',
567
+ theme.grid_cell.text?.text || 'rgb(51, 51, 51)',
568
+ ];
569
+
570
+ for (let i = 1; i < 32; i++) {
571
+ css = CSS(`theme-color-${i}`);
572
+ if (!css.color || css.color === compare) {
573
+ break;
574
+ }
575
+ theme.theme_colors.push(css.color);
576
+ }
577
+
578
+ // we could just parse, we know the returned css format is going
579
+ // to be an rgb triple (I think?)
580
+
581
+ const canvas = document.createElement('canvas');
582
+
583
+ canvas.width = 3;
584
+ canvas.height = 3;
585
+ const context = canvas.getContext('2d', { willReadFrequently: true });
586
+
587
+ if (context) {
588
+ theme.mode = DeriveColorScheme(theme, context);
589
+ }
590
+
591
+ if (context) {
592
+ theme.theme_colors_rgb = theme.theme_colors.map((color) => {
593
+ context.fillStyle = color;
594
+ context.fillRect(0, 0, 3, 3);
595
+ const imagedata = context.getImageData(1, 1, 1, 1);
596
+ return Array.from(imagedata.data);
597
+ });
598
+ }
599
+
600
+ theme.table = ThemeColorTable(4);
601
+
602
+ // this is a little odd, since we have the check above for "existing element";
603
+ // should we switch on that? or is that never used, and we can drop it? (...)
604
+
605
+ (node.parentElement as Element)?.removeChild(node);
606
+
607
+ return theme;
608
+ };