@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.
- package/.eslintignore +8 -0
- package/.eslintrc.js +164 -0
- package/README-shadow-DOM.md +88 -0
- package/README.md +37 -130
- package/api-config.json +29 -0
- package/api-generator/api-generator-types.ts +82 -0
- package/api-generator/api-generator.ts +1172 -0
- package/api-generator/package.json +3 -0
- package/build/treb-spreadsheet.mjs +14 -0
- package/{treb.d.ts → build/treb.d.ts} +323 -271
- package/esbuild-custom-element.mjs +336 -0
- package/esbuild.js +305 -0
- package/package.json +49 -14
- package/treb-base-types/package.json +5 -0
- package/treb-base-types/src/api_types.ts +36 -0
- package/treb-base-types/src/area.ts +583 -0
- package/treb-base-types/src/basic_types.ts +45 -0
- package/treb-base-types/src/cell.ts +612 -0
- package/treb-base-types/src/cells.ts +1066 -0
- package/treb-base-types/src/color.ts +124 -0
- package/treb-base-types/src/import.ts +71 -0
- package/treb-base-types/src/index-standalone.ts +29 -0
- package/treb-base-types/src/index.ts +42 -0
- package/treb-base-types/src/layout.ts +47 -0
- package/treb-base-types/src/localization.ts +187 -0
- package/treb-base-types/src/rectangle.ts +145 -0
- package/treb-base-types/src/render_text.ts +72 -0
- package/treb-base-types/src/style.ts +545 -0
- package/treb-base-types/src/table.ts +109 -0
- package/treb-base-types/src/text_part.ts +54 -0
- package/treb-base-types/src/theme.ts +608 -0
- package/treb-base-types/src/union.ts +152 -0
- package/treb-base-types/src/value-type.ts +164 -0
- package/treb-base-types/style/resizable.css +59 -0
- package/treb-calculator/modern.tsconfig.json +11 -0
- package/treb-calculator/package.json +5 -0
- package/treb-calculator/src/calculator.ts +2546 -0
- package/treb-calculator/src/complex-math.ts +558 -0
- package/treb-calculator/src/dag/array-vertex.ts +198 -0
- package/treb-calculator/src/dag/graph.ts +951 -0
- package/treb-calculator/src/dag/leaf_vertex.ts +118 -0
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +327 -0
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +44 -0
- package/treb-calculator/src/dag/vertex.ts +352 -0
- package/treb-calculator/src/descriptors.ts +162 -0
- package/treb-calculator/src/expression-calculator.ts +1069 -0
- package/treb-calculator/src/function-error.ts +103 -0
- package/treb-calculator/src/function-library.ts +103 -0
- package/treb-calculator/src/functions/base-functions.ts +1214 -0
- package/treb-calculator/src/functions/checkbox.ts +164 -0
- package/treb-calculator/src/functions/complex-functions.ts +253 -0
- package/treb-calculator/src/functions/finance-functions.ts +399 -0
- package/treb-calculator/src/functions/information-functions.ts +102 -0
- package/treb-calculator/src/functions/matrix-functions.ts +182 -0
- package/treb-calculator/src/functions/sparkline.ts +335 -0
- package/treb-calculator/src/functions/statistics-functions.ts +350 -0
- package/treb-calculator/src/functions/text-functions.ts +298 -0
- package/treb-calculator/src/index.ts +27 -0
- package/treb-calculator/src/notifier-types.ts +59 -0
- package/treb-calculator/src/primitives.ts +428 -0
- package/treb-calculator/src/utilities.ts +305 -0
- package/treb-charts/package.json +5 -0
- package/treb-charts/src/chart-functions.ts +156 -0
- package/treb-charts/src/chart-types.ts +230 -0
- package/treb-charts/src/chart.ts +1288 -0
- package/treb-charts/src/index.ts +24 -0
- package/treb-charts/src/main.ts +37 -0
- package/treb-charts/src/rectangle.ts +52 -0
- package/treb-charts/src/renderer.ts +1841 -0
- package/treb-charts/src/util.ts +122 -0
- package/treb-charts/style/charts.scss +221 -0
- package/treb-charts/style/old-charts.scss +250 -0
- package/treb-embed/markup/layout.html +137 -0
- package/treb-embed/markup/toolbar.html +175 -0
- package/treb-embed/modern.tsconfig.json +25 -0
- package/treb-embed/src/custom-element/content-types.d.ts +18 -0
- package/treb-embed/src/custom-element/global.d.ts +11 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +1228 -0
- package/treb-embed/src/custom-element/treb-global.ts +44 -0
- package/treb-embed/src/custom-element/treb-spreadsheet-element.ts +52 -0
- package/treb-embed/src/embedded-spreadsheet.ts +5358 -0
- package/treb-embed/src/index.ts +16 -0
- package/treb-embed/src/language-model.ts +41 -0
- package/treb-embed/src/options.ts +298 -0
- package/treb-embed/src/progress-dialog.ts +228 -0
- package/treb-embed/src/selection-state.ts +16 -0
- package/treb-embed/src/spinner.ts +42 -0
- package/treb-embed/src/toolbar-message.ts +96 -0
- package/treb-embed/src/types.ts +167 -0
- package/treb-embed/style/autocomplete.scss +103 -0
- package/treb-embed/style/dark-theme.scss +114 -0
- package/treb-embed/style/defaults.scss +36 -0
- package/treb-embed/style/dialog.scss +181 -0
- package/treb-embed/style/dropdown-select.scss +101 -0
- package/treb-embed/style/formula-bar.scss +193 -0
- package/treb-embed/style/grid.scss +374 -0
- package/treb-embed/style/layout.scss +424 -0
- package/treb-embed/style/mouse-mask.scss +67 -0
- package/treb-embed/style/note.scss +92 -0
- package/treb-embed/style/overlay-editor.scss +102 -0
- package/treb-embed/style/spinner.scss +92 -0
- package/treb-embed/style/tab-bar.scss +228 -0
- package/treb-embed/style/table.scss +80 -0
- package/treb-embed/style/theme-defaults.scss +444 -0
- package/treb-embed/style/toolbar.scss +416 -0
- package/treb-embed/style/tooltip.scss +68 -0
- package/treb-embed/style/treb-icons.scss +130 -0
- package/treb-embed/style/treb-spreadsheet-element.scss +20 -0
- package/treb-embed/style/z-index.scss +43 -0
- package/treb-export/docs/charts.md +68 -0
- package/treb-export/modern.tsconfig.json +19 -0
- package/treb-export/package.json +4 -0
- package/treb-export/src/address-type.ts +77 -0
- package/treb-export/src/base-template.ts +22 -0
- package/treb-export/src/column-width.ts +85 -0
- package/treb-export/src/drawing2/chart-template-components2.ts +389 -0
- package/treb-export/src/drawing2/chart2.ts +282 -0
- package/treb-export/src/drawing2/column-chart-template2.ts +521 -0
- package/treb-export/src/drawing2/donut-chart-template2.ts +296 -0
- package/treb-export/src/drawing2/drawing2.ts +355 -0
- package/treb-export/src/drawing2/embedded-image.ts +71 -0
- package/treb-export/src/drawing2/scatter-chart-template2.ts +555 -0
- package/treb-export/src/export-worker/export-worker.ts +99 -0
- package/treb-export/src/export-worker/index-modern.ts +22 -0
- package/treb-export/src/export2.ts +2204 -0
- package/treb-export/src/import2.ts +882 -0
- package/treb-export/src/relationship.ts +36 -0
- package/treb-export/src/shared-strings2.ts +128 -0
- package/treb-export/src/template-2.ts +22 -0
- package/treb-export/src/unescape_xml.ts +47 -0
- package/treb-export/src/workbook-sheet2.ts +182 -0
- package/treb-export/src/workbook-style2.ts +1285 -0
- package/treb-export/src/workbook-theme2.ts +88 -0
- package/treb-export/src/workbook2.ts +491 -0
- package/treb-export/src/xml-utils.ts +201 -0
- package/treb-export/template/base/[Content_Types].xml +2 -0
- package/treb-export/template/base/_rels/.rels +2 -0
- package/treb-export/template/base/docProps/app.xml +2 -0
- package/treb-export/template/base/docProps/core.xml +12 -0
- package/treb-export/template/base/xl/_rels/workbook.xml.rels +2 -0
- package/treb-export/template/base/xl/sharedStrings.xml +2 -0
- package/treb-export/template/base/xl/styles.xml +2 -0
- package/treb-export/template/base/xl/theme/theme1.xml +2 -0
- package/treb-export/template/base/xl/workbook.xml +2 -0
- package/treb-export/template/base/xl/worksheets/sheet1.xml +2 -0
- package/treb-export/template/base.xlsx +0 -0
- package/treb-format/package.json +8 -0
- package/treb-format/src/format.test.ts +213 -0
- package/treb-format/src/format.ts +942 -0
- package/treb-format/src/format_cache.ts +199 -0
- package/treb-format/src/format_parser.ts +723 -0
- package/treb-format/src/index.ts +25 -0
- package/treb-format/src/number_format_section.ts +100 -0
- package/treb-format/src/value_parser.ts +337 -0
- package/treb-grid/package.json +5 -0
- package/treb-grid/src/editors/autocomplete.ts +394 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +260 -0
- package/treb-grid/src/editors/formula_bar.ts +473 -0
- package/treb-grid/src/editors/formula_editor_base.ts +910 -0
- package/treb-grid/src/editors/overlay_editor.ts +511 -0
- package/treb-grid/src/index.ts +37 -0
- package/treb-grid/src/layout/base_layout.ts +2618 -0
- package/treb-grid/src/layout/grid_layout.ts +299 -0
- package/treb-grid/src/layout/rectangle_cache.ts +86 -0
- package/treb-grid/src/render/selection-renderer.ts +414 -0
- package/treb-grid/src/render/svg_header_overlay.ts +93 -0
- package/treb-grid/src/render/svg_selection_block.ts +187 -0
- package/treb-grid/src/render/tile_renderer.ts +2122 -0
- package/treb-grid/src/types/annotation.ts +216 -0
- package/treb-grid/src/types/border_constants.ts +34 -0
- package/treb-grid/src/types/clipboard_data.ts +31 -0
- package/treb-grid/src/types/data_model.ts +334 -0
- package/treb-grid/src/types/drag_mask.ts +81 -0
- package/treb-grid/src/types/grid.ts +7743 -0
- package/treb-grid/src/types/grid_base.ts +3644 -0
- package/treb-grid/src/types/grid_command.ts +470 -0
- package/treb-grid/src/types/grid_events.ts +124 -0
- package/treb-grid/src/types/grid_options.ts +97 -0
- package/treb-grid/src/types/grid_selection.ts +60 -0
- package/treb-grid/src/types/named_range.ts +369 -0
- package/treb-grid/src/types/scale-control.ts +202 -0
- package/treb-grid/src/types/serialize_options.ts +72 -0
- package/treb-grid/src/types/set_range_options.ts +52 -0
- package/treb-grid/src/types/sheet.ts +3099 -0
- package/treb-grid/src/types/sheet_types.ts +95 -0
- package/treb-grid/src/types/tab_bar.ts +464 -0
- package/treb-grid/src/types/tile.ts +59 -0
- package/treb-grid/src/types/update_flags.ts +75 -0
- package/treb-grid/src/util/dom_utilities.ts +44 -0
- package/treb-grid/src/util/fontmetrics2.ts +179 -0
- package/treb-grid/src/util/ua.ts +104 -0
- package/treb-logo.svg +18 -0
- package/treb-parser/package.json +5 -0
- package/treb-parser/src/csv-parser.ts +122 -0
- package/treb-parser/src/index.ts +25 -0
- package/treb-parser/src/md-parser.ts +526 -0
- package/treb-parser/src/parser-types.ts +397 -0
- package/treb-parser/src/parser.test.ts +298 -0
- package/treb-parser/src/parser.ts +2673 -0
- package/treb-utils/package.json +5 -0
- package/treb-utils/src/dispatch.ts +57 -0
- package/treb-utils/src/event_source.ts +147 -0
- package/treb-utils/src/ievent_source.ts +33 -0
- package/treb-utils/src/index.ts +31 -0
- package/treb-utils/src/measurement.ts +174 -0
- package/treb-utils/src/resizable.ts +160 -0
- package/treb-utils/src/scale.ts +137 -0
- package/treb-utils/src/serialize_html.ts +124 -0
- package/treb-utils/src/template.ts +70 -0
- package/treb-utils/src/validate_uri.ts +61 -0
- package/tsconfig.json +10 -0
- package/tsproject.json +30 -0
- package/util/license-plugin-esbuild.js +86 -0
- package/util/list-css-vars.sh +46 -0
- package/README-esm.md +0 -37
- package/treb-bundle.css +0 -2
- package/treb-bundle.mjs +0 -15
|
@@ -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
|
+
}
|