@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,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
|
+
};
|