@trebco/treb 23.6.2 → 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} +293 -299
  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,5 @@
1
+ {
2
+ "name": "treb-utils",
3
+ "version": "16.0.0",
4
+ "main": "src/index.ts"
5
+ }
@@ -0,0 +1,57 @@
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
+ /**
23
+ * UPDATE: dropping dispatch altogether. there were really very few
24
+ * cases where queue length > 1, so it seems like unecessary overhead.
25
+ *
26
+ * it's still somewhat useful to have an interface, in the event we
27
+ * change this again, so keep using the Yield() function.
28
+ *
29
+ * TODO: is anyone using the callback version? could drop...
30
+ *
31
+ * I'm guessing no one uses it, because it's broken amd we never noticed
32
+ */
33
+
34
+ /* *
35
+ * yield and then call the passed function
36
+ * /
37
+ export function Yield(fn: () => void): void;
38
+
39
+ / * *
40
+ * returns a promise that resolves after yield
41
+ * /
42
+ export function Yield(): Promise<void>;
43
+
44
+ / * * implementation * /
45
+ export function Yield(fn?: () => void) {
46
+ return fn ? Promise.resolve().then(fn) : Promise.resolve();
47
+ }
48
+ */
49
+
50
+ /* for perf testing, we don't need this anymore
51
+ (self as any).__dispatcher_instance = {
52
+ Call: Yield
53
+ }
54
+ */
55
+
56
+ export const Yield = () => Promise.resolve();
57
+
@@ -0,0 +1,147 @@
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 { Yield } from './dispatch';
23
+ // import { IEventSource } from './ievent_source';
24
+
25
+ let subscription_token_generator = 1000;
26
+
27
+ interface EventSubscription<T> {
28
+ subscriber: (event: T) => any;
29
+ token: number;
30
+ }
31
+
32
+ /**
33
+ * relatively simple event publish/subscribe mechanism.
34
+ * not as simple as it used to be.
35
+ *
36
+ * UPDATE removing unecessary interface (not sure what that
37
+ * was for, but no one else is using it).
38
+ */
39
+ export class EventSource<T> { // implements IEventSource<T> {
40
+
41
+ /** pending events */
42
+ private queue: T[] = [];
43
+
44
+ /** flag indicating whether we have already triggered a callback */
45
+ private dispatched = false;
46
+
47
+ /** regular subscriptions */
48
+ private subscribers: Array<EventSubscription<T>> = [];
49
+
50
+ /* * pass-through modules: these are peers * /
51
+ private pass_through: Array<EventSource<T>> = [];
52
+ */
53
+
54
+ constructor(private verbose = false, private log_id?: string) {
55
+
56
+ }
57
+
58
+ /**
59
+ * FIXME: does anybody call this with an array? it's no longer
60
+ * necessary for multiple messages to prevent extra callbacks...
61
+ */
62
+ public Publish(event: T | T[]) {
63
+
64
+ if (this.verbose) {
65
+ console.info(`es publish (${this.log_id})`, event);
66
+ }
67
+
68
+ // here's our updated synchronous mechanism, passing through
69
+ // FIXME: no one uses this (I think). drop it.
70
+
71
+ // this.pass_through.forEach((source) => source.Publish(event));
72
+
73
+ // don't bother if there are no subscribers (implies you must
74
+ // subscribe before first event... not sure if that's reasonable)
75
+
76
+ /*
77
+ if (!this.subscribers.length) {
78
+ return; // ...
79
+ }
80
+ */
81
+
82
+ // queue event or events
83
+
84
+ if (Array.isArray(event)) { this.queue.push(...event); }
85
+ else { this.queue.push(event); }
86
+
87
+ // then call the dispatch function. gate this in case we get
88
+ // this call multiple times before a callback.
89
+
90
+ if (!this.dispatched) {
91
+ this.dispatched = true;
92
+
93
+ Yield().then(() => {
94
+
95
+ const events = this.queue.slice(0);
96
+ this.dispatched = false;
97
+ this.queue = [];
98
+
99
+ // FIXME: should we cache subscribers as well? (...)
100
+
101
+ for (const queued_event of events) {
102
+ for (const subscription of this.subscribers) {
103
+ subscription.subscriber(queued_event);
104
+ }
105
+ }
106
+
107
+ });
108
+
109
+ }
110
+
111
+ }
112
+
113
+ /**
114
+ * subscription returns a token which can be used to cancel subscription.
115
+ * this token is a number, guaranteed to be !0 so you can test for falsy.
116
+ */
117
+ public Subscribe(subscriber: (event: T) => void): number {
118
+ const token = subscription_token_generator++;
119
+ this.subscribers.push({ subscriber, token });
120
+ return token;
121
+ }
122
+
123
+ /** cancel a single subscription */
124
+ public Cancel(token: number) {
125
+ this.subscribers = this.subscribers.filter((subscription) => subscription.token !== token);
126
+ }
127
+
128
+ /**
129
+ * cancel all subscriptions AND ALL PASS-THROUGH SOURCES.
130
+ */
131
+ public CancelAll() {
132
+ this.subscribers = [];
133
+ // this.pass_through = [];
134
+ }
135
+
136
+ /* *
137
+ * pass-through (redirected) subscription, synchronous on this end. does
138
+ * not support unsubscribe atm (FIXME)
139
+ * /
140
+ public PassThrough(source: EventSource<T>) {
141
+ this.pass_through.push(source);
142
+ }
143
+ */
144
+
145
+ }
146
+
147
+
@@ -0,0 +1,33 @@
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
+ /**
23
+ * async event source
24
+ */
25
+ export interface IEventSource<T> {
26
+
27
+ /** subscribe. returns a token (number) used to manage the subscription. */
28
+ Subscribe(subscriber: (event: T) => void): number;
29
+
30
+ /** cancel a single subscription */
31
+ Cancel(token: number): void;
32
+
33
+ }
@@ -0,0 +1,31 @@
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
+ export * from './dispatch';
23
+ export * from './event_source';
24
+ export * from './ievent_source';
25
+ export * from './resizable';
26
+ export * from './measurement';
27
+ // export * from './color';
28
+ export * from './serialize_html';
29
+ export * from './validate_uri';
30
+ export * from './scale';
31
+ export * from './template';
@@ -0,0 +1,174 @@
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
+ /** size, really */
23
+ export interface Metrics {
24
+ width: number;
25
+ height: number;
26
+ }
27
+
28
+ export class Measurement {
29
+
30
+ /** canvas used for color measurement */
31
+ private static color_measurement_canvas: HTMLCanvasElement;
32
+
33
+ /**
34
+ * node used for text metrics. this has to be added to
35
+ * the DOM, so it's fixed and shifted off screen.
36
+ */
37
+ private static text_measurement_node: HTMLElement;
38
+
39
+ /** cache for color lookups */
40
+ private static color_cache: {[index: string]: Uint8ClampedArray} = {};
41
+
42
+ public static MeasureColorARGB(color: string): string {
43
+ const bytes = this.MeasureColor(color);
44
+ let argb = 'FF'; // always 100%
45
+ for (let i = 0; i < 3; i++) {
46
+ const hex = bytes[i].toString(16);
47
+ if (hex.length === 0) argb += '00';
48
+ else if (hex.length === 1) argb += `0${hex}`;
49
+ else argb += hex;
50
+ }
51
+ return argb.toUpperCase();
52
+ }
53
+
54
+ /**
55
+ * measure a color. turns symbolic or rgb colors into rgb values.
56
+ *
57
+ * UPDATE: prefill with #fff. that prevents it from randomly returning
58
+ * the last value, if the color doesn't work for some reason.
59
+ */
60
+ public static MeasureColor(color: string): Uint8ClampedArray {
61
+
62
+ let cached = this.color_cache[color];
63
+ if (cached) {
64
+ return cached;
65
+ }
66
+
67
+ if (!this.color_measurement_canvas) {
68
+ this.color_measurement_canvas = document.createElement('canvas');
69
+ this.color_measurement_canvas.width = 1;
70
+ this.color_measurement_canvas.height = 1;
71
+ }
72
+
73
+ const context = this.color_measurement_canvas.getContext('2d', {
74
+ willReadFrequently: true,
75
+ });
76
+
77
+ if (context) {
78
+ context.fillStyle = '#fff';
79
+ context.fillRect(0, 0, 1, 1);
80
+
81
+ context.fillStyle = color;
82
+ context.fillRect(0, 0, 1, 1);
83
+
84
+ // cached = context.getImageData(0, 0, 1, 1).data; // FIXME: should clone this
85
+ cached = new Uint8ClampedArray(context.getImageData(0, 0, 1, 1).data);
86
+
87
+ this.color_cache[color] = cached;
88
+ return cached;
89
+ }
90
+
91
+ return new Uint8ClampedArray(3);
92
+
93
+ }
94
+
95
+ public static EnsureMeasurementNode(): void {
96
+
97
+ if (!this.text_measurement_node) {
98
+ const node = document.querySelector('.treb-chart-measurement-node');
99
+ if (node) {
100
+ this.text_measurement_node = node as HTMLElement;
101
+ }
102
+ else {
103
+ this.text_measurement_node = document.createElement('div');
104
+ this.text_measurement_node.classList.add('treb-chart-measurement-node');
105
+ this.text_measurement_node.style.margin = '0px';
106
+ this.text_measurement_node.style.padding = '0px';
107
+ this.text_measurement_node.style.height = 'initial';
108
+ this.text_measurement_node.style.width = 'initial';
109
+
110
+ this.text_measurement_node.style.whiteSpace = 'nowrap';
111
+ this.text_measurement_node.style.position = 'fixed';
112
+ this.text_measurement_node.style.border = '0px';
113
+ this.text_measurement_node.style.border = '1px solid red';
114
+ this.text_measurement_node.style.boxSizing = 'content-box';
115
+ this.text_measurement_node.style.top =
116
+ this.text_measurement_node.style.left = '-1000px';
117
+ document.body.appendChild(this.text_measurement_node);
118
+ }
119
+ }
120
+
121
+ }
122
+
123
+ /**
124
+ * check if font is loaded, based on the theory that the alternatives
125
+ * will be different sizes. note that this probably doesn't test weights
126
+ * or italics properly, as those can be emulated without the specific face.
127
+ *
128
+ * I guess the thing to do in that case would be to load the alternate faces
129
+ * first, and assume they are loaded serially (they're not).
130
+ *
131
+ * @param font_face
132
+ * @param italic
133
+ * @param bold
134
+ */
135
+ public static FontLoaded(font_face: string, italic = false, weight = 400): boolean {
136
+ const face = `${italic ? 'italic' : ''} ${weight} 20pt ${font_face}`;
137
+ const m1 = this.MeasureText(`${face}, sans-serif`, `check font`);
138
+ const m2 = this.MeasureText(`${face}, serif`, `check font`);
139
+ const m3 = this.MeasureText(`${face}, monospace`, `check font`);
140
+ return (m1.width === m2.width && m2.width === m3.width);
141
+ }
142
+
143
+ /**
144
+ * measure width, height of text, accounting for rotation
145
+ */
146
+ public static MeasureText(font: string, text: string, angle = 0): Metrics {
147
+
148
+ this.EnsureMeasurementNode();
149
+ this.text_measurement_node.style.font = font;
150
+ if (/\n/.test(text)) {
151
+ text = text.replace(/\n/g, '<BR/>');
152
+ this.text_measurement_node.innerHTML = text;
153
+ }
154
+ else {
155
+ this.text_measurement_node.textContent = text;
156
+ }
157
+ this.text_measurement_node.style.lineHeight = '1em';
158
+ if (angle) {
159
+ this.text_measurement_node.style.transform = `rotate(${angle}deg)`;
160
+ }
161
+ else this.text_measurement_node.style.transform = '';
162
+
163
+ const rect = this.text_measurement_node.getBoundingClientRect();
164
+
165
+ return {
166
+ width: rect.width,
167
+ height: rect.height,
168
+ };
169
+
170
+ }
171
+
172
+
173
+ }
174
+
@@ -0,0 +1,160 @@
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
+ /**
23
+ * support for resizable node, drag handle, drag rect, mask
24
+ * FIXME: make this composable (decorator?)
25
+ * FIXME: make this generic, we can use it for some other stuff (charts?)
26
+ */
27
+ export class Resizable {
28
+
29
+ private static resize_mask: HTMLElement;
30
+ private static resize_rect: HTMLElement;
31
+
32
+ /**
33
+ * adding layout_reference to move the handle. to keep this backwards
34
+ * compatible, we add it as a last, optional parameter. at some point
35
+ * we can create a replacement class and migrate.
36
+ *
37
+ * this is a weird pattern, we don't need an instance of this class...
38
+ * goint to refactor
39
+ *
40
+ */
41
+ constructor(container: HTMLElement, node: HTMLElement, resize_callback: () => void,
42
+ layout_reference: HTMLElement = container) {
43
+
44
+ Resizable.Create({
45
+ container,
46
+ node,
47
+ resize_callback,
48
+ layout_reference});
49
+
50
+ }
51
+
52
+ public static Create(options: {
53
+ container: HTMLElement;
54
+ node: HTMLElement;
55
+ resize_callback?: () => void;
56
+ layout_reference?: HTMLElement;
57
+ }): void {
58
+
59
+ const resize_handle = document.createElement('div');
60
+ resize_handle.classList.add('treb-embed-resize-handle');
61
+
62
+ (options.layout_reference || options.container).appendChild(resize_handle);
63
+
64
+ if (!Resizable.resize_mask) {
65
+ let mask = document.querySelector('.treb-embed-mouse-mask');
66
+ if (!mask) {
67
+ mask = document.createElement('div');
68
+ mask.classList.add('treb-embed-mouse-mask');
69
+ document.body.appendChild(mask);
70
+ }
71
+ Resizable.resize_mask = mask as HTMLElement;
72
+ }
73
+
74
+ if (!Resizable.resize_rect) {
75
+ let rect = document.querySelector('.treb-embed-resize-rect');
76
+ if (!rect) {
77
+ rect = document.createElement('div');
78
+ rect.classList.add('treb-embed-resize-rect');
79
+ Resizable.resize_mask.appendChild(rect);
80
+ }
81
+ Resizable.resize_rect = rect as HTMLElement;
82
+ }
83
+
84
+ // eslint-disable-next-line prefer-const
85
+ let mouseup: () => void;
86
+
87
+ // eslint-disable-next-line prefer-const
88
+ let mousemove: (event: MouseEvent) => void;
89
+
90
+ let container_rect = { width: 0, height: 0 };
91
+ let offset = { x: 0, y: 0 };
92
+ let delta = { x: 0, y: 0 };
93
+
94
+ const cleanup = () => {
95
+
96
+ Resizable.resize_mask.removeEventListener('mousemove', mousemove);
97
+ Resizable.resize_mask.removeEventListener('mouseup', mouseup);
98
+ Resizable.resize_mask.style.display = 'none';
99
+
100
+ if (delta.x || delta.y) {
101
+ const bounding_rect = options.container.getBoundingClientRect();
102
+ const width = bounding_rect.width + delta.x;
103
+ const height = bounding_rect.height + delta.y;
104
+ options.container.style.width = `${width}px`;
105
+ options.container.style.height = `${height}px`;
106
+ if (options.resize_callback) {
107
+ options.resize_callback();
108
+ }
109
+ }
110
+
111
+ };
112
+
113
+ mousemove = (event: MouseEvent) => {
114
+
115
+ if (!event.buttons) {
116
+ cleanup();
117
+ return;
118
+ }
119
+
120
+ if (delta.x !== event.clientX - offset.x) {
121
+ delta.x = event.clientX - offset.x;
122
+ Resizable.resize_rect.style.width = `${container_rect.width + delta.x + 4}px`;
123
+ }
124
+ if (delta.y !== event.clientY - offset.y) {
125
+ delta.y = event.clientY - offset.y;
126
+ Resizable.resize_rect.style.height = `${container_rect.height + delta.y + 4}px`;
127
+ }
128
+ };
129
+
130
+ mouseup = () => {
131
+ cleanup();
132
+ };
133
+
134
+ resize_handle.addEventListener('mousedown', (event) => {
135
+
136
+ event.stopPropagation();
137
+ event.preventDefault();
138
+
139
+ const bounding_rect = options.node.getBoundingClientRect();
140
+ container_rect = { width: bounding_rect.width, height: bounding_rect.height };
141
+
142
+ if (Resizable.resize_rect) {
143
+ Resizable.resize_rect.style.top = `${bounding_rect.top - 2}px`;
144
+ Resizable.resize_rect.style.left = `${bounding_rect.left - 2}px`;
145
+ Resizable.resize_rect.style.width = `${bounding_rect.width + 4}px`;
146
+ Resizable.resize_rect.style.height = `${bounding_rect.height + 4}px`;
147
+ }
148
+
149
+ offset = { x: event.clientX, y: event.clientY };
150
+ delta = { x: 0, y: 0 };
151
+
152
+ Resizable.resize_mask.style.display = 'block';
153
+ Resizable.resize_mask.addEventListener('mousemove', mousemove);
154
+ Resizable.resize_mask.addEventListener('mouseup', mouseup);
155
+
156
+ });
157
+
158
+ }
159
+
160
+ }