@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,60 @@
|
|
|
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 { Area, ICellAddress } from 'treb-base-types';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* FIXME: this is broken. we treat this as a simple javascript object,
|
|
26
|
+
* cloning and creating via JSON, but area is a class instance.
|
|
27
|
+
*
|
|
28
|
+
* that means cloned objects won't work properly (if anyone is relying on
|
|
29
|
+
* that object).
|
|
30
|
+
*/
|
|
31
|
+
export interface GridSelection {
|
|
32
|
+
|
|
33
|
+
/** target or main cell in the selection */
|
|
34
|
+
target: ICellAddress;
|
|
35
|
+
|
|
36
|
+
/** selection area */
|
|
37
|
+
area: Area;
|
|
38
|
+
|
|
39
|
+
/** there is nothing selected, even though this object exists */
|
|
40
|
+
empty?: boolean;
|
|
41
|
+
|
|
42
|
+
/** for cacheing addtional selections. optimally don't serialize */
|
|
43
|
+
rendered?: boolean;
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* create an empty selection
|
|
49
|
+
*/
|
|
50
|
+
export const CreateSelection = (): GridSelection => {
|
|
51
|
+
return {
|
|
52
|
+
target: {row: 0, column: 0},
|
|
53
|
+
area: new Area({row: 0, column: 0}),
|
|
54
|
+
empty: true,
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const CloneSelection = (rhs: GridSelection): GridSelection => {
|
|
59
|
+
return JSON.parse(JSON.stringify(rhs));
|
|
60
|
+
};
|
|
@@ -0,0 +1,369 @@
|
|
|
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 { IArea, Area } from 'treb-base-types';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* I want to repurpose named ranges (a little) to allow either values or
|
|
26
|
+
* arbitrary expressions. this is sort of 1/2 way between named ranges and
|
|
27
|
+
* "macro functions".
|
|
28
|
+
*
|
|
29
|
+
* not sure if we should change named ranges, or create a side path for
|
|
30
|
+
* "named expressions".
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export class NamedRangeCollection {
|
|
34
|
+
|
|
35
|
+
private forward: {[index: string]: Area} = {};
|
|
36
|
+
private backward: Array<{name: string; range: Area}> = [];
|
|
37
|
+
|
|
38
|
+
/** FIXME: why not an accessor? */
|
|
39
|
+
public Count(): number {
|
|
40
|
+
return this.backward.length;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** FIXME: why not just use toJSON? */
|
|
44
|
+
public Serialize(): Record<string, IArea> {
|
|
45
|
+
return JSON.parse(JSON.stringify(this.Map()));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public Deserialize(data?: Record<string, IArea>): void {
|
|
49
|
+
this.Reset();
|
|
50
|
+
if (data) {
|
|
51
|
+
for (const key of Object.keys(data)) {
|
|
52
|
+
this.SetName(key, new Area(data[key].start, data[key].end), false);
|
|
53
|
+
}
|
|
54
|
+
this.RebuildList();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* match an area, optionally a target within a larger area (for selections).
|
|
60
|
+
* we don't use the selection directly, as we may need to adjust target for
|
|
61
|
+
* merge area.
|
|
62
|
+
*/
|
|
63
|
+
public MatchSelection(area: Area, target?: Area): string|undefined {
|
|
64
|
+
|
|
65
|
+
if (!area.start.sheet_id) {
|
|
66
|
+
throw new Error('match selection without sheet id');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let label: string|undefined;
|
|
70
|
+
for (const entry of this.List()) {
|
|
71
|
+
if (entry.range.start.sheet_id === area.start.sheet_id) {
|
|
72
|
+
if (entry.range.Equals(area)) {
|
|
73
|
+
label = entry.name; // don't break, in case there's a match for target which takes precendence.
|
|
74
|
+
}
|
|
75
|
+
if (target?.Equals(entry.range)) {
|
|
76
|
+
return entry.name;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return label;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* add name. names are case-insensitive. if the name already
|
|
85
|
+
* exists, it will be overwritten.
|
|
86
|
+
*
|
|
87
|
+
* update: returns success (FIXME: proper errors)
|
|
88
|
+
*/
|
|
89
|
+
public SetName(name: string, range: Area, apply = true): boolean {
|
|
90
|
+
const validated = this.ValidateNamed(name);
|
|
91
|
+
if (!validated) {
|
|
92
|
+
console.warn('invalid name');
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
if (range.entire_column || range.entire_row) {
|
|
96
|
+
console.warn('invalid range');
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
this.forward[validated] = range;
|
|
100
|
+
if (apply) {
|
|
101
|
+
this.RebuildList();
|
|
102
|
+
}
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public SetNames(list: {[index: string]: IArea}): void {
|
|
107
|
+
for (const key of Object.keys(list)) {
|
|
108
|
+
const area = list[key];
|
|
109
|
+
this.SetName(key, new Area(area.start, area.end), false);
|
|
110
|
+
}
|
|
111
|
+
this.RebuildList();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
public ClearName(name: string, apply = true): void {
|
|
115
|
+
delete this.forward[name];
|
|
116
|
+
if (apply) {
|
|
117
|
+
this.RebuildList();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* if we delete a sheet, remove ranges in that sheet
|
|
123
|
+
* @param sheet_id
|
|
124
|
+
*/
|
|
125
|
+
public RemoveRangesForSheet(sheet_id: number, apply = true) {
|
|
126
|
+
|
|
127
|
+
const temp: {[index: string]: Area} = {};
|
|
128
|
+
const list = this.List();
|
|
129
|
+
|
|
130
|
+
for (const entry of list) {
|
|
131
|
+
if (entry.range.start.sheet_id !== sheet_id) {
|
|
132
|
+
temp[entry.name] = entry.range;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.forward = temp;
|
|
137
|
+
|
|
138
|
+
if (apply) {
|
|
139
|
+
this.RebuildList();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public Reset(): void {
|
|
144
|
+
this.forward = {};
|
|
145
|
+
this.backward = [];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
public Get(name: string) {
|
|
149
|
+
return this.forward[name.toUpperCase()];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** FIXME: accessor */
|
|
153
|
+
public Map() {
|
|
154
|
+
return this.forward;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** FIXME: accessor */
|
|
158
|
+
public List() {
|
|
159
|
+
return this.backward;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* named range rules:
|
|
164
|
+
*
|
|
165
|
+
* - legal characters are alphanumeric, underscore and dot.
|
|
166
|
+
* - must start with letter or underscore (not a number or dot).
|
|
167
|
+
* - cannot look like a spreadsheet address, which is 1-3 letters followed by numbers.
|
|
168
|
+
*
|
|
169
|
+
* returns a normalized name (just caps, atm)
|
|
170
|
+
*/
|
|
171
|
+
public ValidateNamed(name: string): string|false {
|
|
172
|
+
name = name.trim();
|
|
173
|
+
if (!name.length) return false;
|
|
174
|
+
if (/^[A-Za-z]{1,3}\d+$/.test(name)) return false;
|
|
175
|
+
if (/[^A-Za-z\d_.]/.test(name)) return false;
|
|
176
|
+
if (/^[^A-Za-z_]/.test(name)) return false;
|
|
177
|
+
return name.toUpperCase();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// was in sheet
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* fix named range references after row/column insert/delete
|
|
185
|
+
*/
|
|
186
|
+
public PatchNamedRanges(sheet_id: number, before_column: number, column_count: number, before_row: number, row_count: number) {
|
|
187
|
+
|
|
188
|
+
const copy = this.List().slice(0);
|
|
189
|
+
|
|
190
|
+
for (const entry of copy) {
|
|
191
|
+
|
|
192
|
+
const key = entry.name;
|
|
193
|
+
const range = entry.range;
|
|
194
|
+
|
|
195
|
+
if (range.start.sheet_id !== sheet_id) {
|
|
196
|
+
console.info('skipping name', key);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (column_count && before_column <= range.end.column) {
|
|
201
|
+
|
|
202
|
+
/*
|
|
203
|
+
// (1) we are before the insert point, not affected
|
|
204
|
+
|
|
205
|
+
if (before_column > range.end.column) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
if (column_count > 0) {
|
|
211
|
+
|
|
212
|
+
// (2) it's an insert and we are past the insert point:
|
|
213
|
+
// increment [start] and [end] by [count]
|
|
214
|
+
|
|
215
|
+
if (before_column <= range.start.column) {
|
|
216
|
+
range.Shift(0, column_count);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// (3) it's an insert and we contain the insert point:
|
|
220
|
+
// increment [end] by [count]
|
|
221
|
+
|
|
222
|
+
else if (before_column > range.start.column && before_column <= range.end.column) {
|
|
223
|
+
range.ConsumeAddress({row: range.end.row, column: range.end.column + column_count});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
else {
|
|
227
|
+
console.warn(`PNR X case 1`, before_column, column_count, JSON.stringify(range));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
}
|
|
231
|
+
else if (column_count < 0) {
|
|
232
|
+
|
|
233
|
+
// (4) it's a delete and we are past the delete point (before+count):
|
|
234
|
+
// decrement [start] and [end] by [count]
|
|
235
|
+
|
|
236
|
+
if (before_column - column_count <= range.start.column) {
|
|
237
|
+
range.Shift(0, column_count);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// (5) it's a delete and contains the entire range
|
|
241
|
+
|
|
242
|
+
else if (before_column <= range.start.column && before_column - column_count > range.end.column) {
|
|
243
|
+
this.ClearName(key, false);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// (6) it's a delete and contains part of the range. clip the range.
|
|
247
|
+
|
|
248
|
+
else if (before_column <= range.start.column) {
|
|
249
|
+
const last_column = before_column - column_count - 1;
|
|
250
|
+
this.SetName(key, new Area({
|
|
251
|
+
row: range.start.row, column: last_column + 1 + column_count, sheet_id }, {
|
|
252
|
+
row: range.end.row, column: range.end.column + column_count }), false);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
else if (before_column <= range.end.column) {
|
|
256
|
+
const last_column = before_column - column_count - 1;
|
|
257
|
+
|
|
258
|
+
if (last_column >= range.end.column) {
|
|
259
|
+
this.SetName(key, new Area({
|
|
260
|
+
row: range.start.row, column: range.start.column, sheet_id }, {
|
|
261
|
+
row: range.end.row, column: before_column - 1 }), false);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
this.SetName(key, new Area({
|
|
265
|
+
row: range.start.row, column: range.start.column, sheet_id }, {
|
|
266
|
+
row: range.end.row, column: range.start.column + range.columns + column_count - 1}), false);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
else {
|
|
272
|
+
console.warn(`PNR X case 2`, before_column, column_count, JSON.stringify(range));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
if (row_count && before_row <= range.end.row) {
|
|
280
|
+
|
|
281
|
+
/*
|
|
282
|
+
// (1) we are before the insert point, not affected
|
|
283
|
+
|
|
284
|
+
if (before_row > range.end.row) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
*/
|
|
288
|
+
|
|
289
|
+
if (row_count > 0) {
|
|
290
|
+
|
|
291
|
+
// (2) it's an insert and we are past the insert point:
|
|
292
|
+
// increment [start] and [end] by [count]
|
|
293
|
+
|
|
294
|
+
if (before_row <= range.start.row) {
|
|
295
|
+
range.Shift(row_count, 0);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// (3) it's an insert and we contain the insert point:
|
|
299
|
+
// increment [end] by [count]
|
|
300
|
+
|
|
301
|
+
else if (before_row > range.start.row && before_row <= range.end.row) {
|
|
302
|
+
range.ConsumeAddress({row: range.end.row + row_count, column: range.end.column});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
else {
|
|
306
|
+
console.warn(`PNR X case 3`, before_row, row_count, JSON.stringify(range));
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
}
|
|
310
|
+
else if (row_count < 0) {
|
|
311
|
+
|
|
312
|
+
// (4) it's a delete and we are past the delete point (before+count):
|
|
313
|
+
// decrement [start] and [end] by [count]
|
|
314
|
+
|
|
315
|
+
if (before_row - row_count <= range.start.row) {
|
|
316
|
+
range.Shift(row_count, 0);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// (5) it's a delete and contains the entire range
|
|
320
|
+
|
|
321
|
+
else if (before_row <= range.start.row && before_row - row_count > range.end.row) {
|
|
322
|
+
this.ClearName(key, false);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// (6) it's a delete and contains part of the range. clip the range.
|
|
326
|
+
|
|
327
|
+
else if (before_row <= range.start.row) {
|
|
328
|
+
const last_row = before_row - row_count - 1;
|
|
329
|
+
this.SetName(key, new Area({
|
|
330
|
+
column: range.start.column, row: last_row + 1 + row_count, sheet_id }, {
|
|
331
|
+
column: range.end.column, row: range.end.row + row_count }), false);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
else if (before_row <= range.end.row) {
|
|
335
|
+
const last_row = before_row - row_count - 1;
|
|
336
|
+
if (last_row >= range.end.row) {
|
|
337
|
+
this.SetName(key, new Area({
|
|
338
|
+
column: range.start.column, row: range.start.row, sheet_id }, {
|
|
339
|
+
column: range.end.column, row: before_row - 1 }), false);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
this.SetName(key, new Area({
|
|
343
|
+
column: range.start.column, row: range.start.row, sheet_id }, {
|
|
344
|
+
column: range.end.column, row: range.start.row + range.rows + row_count - 1 }), false);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
else {
|
|
350
|
+
console.warn(`PNR X case 4`, before_row, row_count, JSON.stringify(range));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
this.RebuildList();
|
|
359
|
+
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
public RebuildList(): void {
|
|
363
|
+
this.backward = [];
|
|
364
|
+
for (const key of Object.keys(this.forward)) {
|
|
365
|
+
this.backward.push({ name: key, range: this.forward[key] });
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
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 { DOMUtilities as DOM } from '../util/dom_utilities';
|
|
23
|
+
import { NumberFormat, NumberFormatCache, ValueParser } from 'treb-format';
|
|
24
|
+
import { ValueType } from 'treb-base-types';
|
|
25
|
+
import { EventSource } from 'treb-utils';
|
|
26
|
+
|
|
27
|
+
export interface ScaleEvent {
|
|
28
|
+
type: 'scale';
|
|
29
|
+
// action: 'increase'|'decrease'|number;
|
|
30
|
+
value: number;
|
|
31
|
+
keep_focus?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* updated scale control, broken out. this is based on what we did for
|
|
36
|
+
* project planning, which worked out rather nicely.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
export class ScaleControl extends EventSource<ScaleEvent> {
|
|
40
|
+
|
|
41
|
+
private input: HTMLInputElement;
|
|
42
|
+
private slider: HTMLInputElement;
|
|
43
|
+
private scale = 0;
|
|
44
|
+
private format: NumberFormat;
|
|
45
|
+
|
|
46
|
+
private timeout = 0;
|
|
47
|
+
|
|
48
|
+
public constructor(public container: HTMLElement) {
|
|
49
|
+
super();
|
|
50
|
+
|
|
51
|
+
this.format = NumberFormatCache.Get('0.0');
|
|
52
|
+
|
|
53
|
+
// not sure what this extra div was for, we don't need it
|
|
54
|
+
// const div = DOM.CreateDiv('treb-scale-control-2', container);
|
|
55
|
+
|
|
56
|
+
this.input = DOM.Create('input', 'treb-scale-input', /* div */ container);
|
|
57
|
+
const popup = DOM.CreateDiv('treb-slider-container', /* div */ container);
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
this.input.addEventListener('keyup', (event) => {
|
|
61
|
+
switch (event.key) {
|
|
62
|
+
case 'ArrowUp':
|
|
63
|
+
case 'ArrowDown':
|
|
64
|
+
event.stopPropagation();
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
// is this for some x-browser issue? or did we just not
|
|
72
|
+
// know which event to use and this is old junk?
|
|
73
|
+
|
|
74
|
+
this.input.addEventListener('keypress', (event) => {
|
|
75
|
+
switch (event.key) {
|
|
76
|
+
case 'ArrowUp':
|
|
77
|
+
case 'ArrowDown':
|
|
78
|
+
event.stopPropagation();
|
|
79
|
+
event.preventDefault();
|
|
80
|
+
console.info('mark?');
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
// this is the one we want
|
|
86
|
+
|
|
87
|
+
this.input.addEventListener('keydown', (event) => {
|
|
88
|
+
switch (event.key) {
|
|
89
|
+
case 'Enter':
|
|
90
|
+
this.input.blur();
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
case 'ArrowUp':
|
|
94
|
+
this.Tick(-1);
|
|
95
|
+
break;
|
|
96
|
+
|
|
97
|
+
case 'ArrowDown':
|
|
98
|
+
this.Tick(1);
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case 'Escape':
|
|
102
|
+
this.input.value = this.format.Format(this.scale) + '%';
|
|
103
|
+
this.input.blur();
|
|
104
|
+
break;
|
|
105
|
+
|
|
106
|
+
default:
|
|
107
|
+
return;
|
|
108
|
+
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
event.stopPropagation();
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// select text on click
|
|
117
|
+
this.input.addEventListener('focusin', () => this.input.select());
|
|
118
|
+
|
|
119
|
+
this.input.addEventListener('change', () => {
|
|
120
|
+
|
|
121
|
+
// what we're doing here is a little unusual. we always treat
|
|
122
|
+
// the value as a percent, even if there's no percent sign.
|
|
123
|
+
|
|
124
|
+
// for that to work, if there is a percent sign, we need to remove
|
|
125
|
+
// it before we continue. then try to parse as a number.
|
|
126
|
+
|
|
127
|
+
let text = this.input.value;
|
|
128
|
+
text = text.replace(/%/g, '');
|
|
129
|
+
|
|
130
|
+
const value = ValueParser.TryParse(text);
|
|
131
|
+
if (value.type === ValueType.number) {
|
|
132
|
+
this.UpdateScale(Number(value.value), true);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.input.value = this.format.Format(this.scale) + '%';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
this.slider = DOM.Create<HTMLInputElement>('input', undefined, popup, undefined, {
|
|
141
|
+
type: 'range',
|
|
142
|
+
min: '50',
|
|
143
|
+
max: '200',
|
|
144
|
+
value: '100',
|
|
145
|
+
step: '2.5',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
this.slider.addEventListener('input', () => {
|
|
149
|
+
this.UpdateScale(Number(this.slider.value), true);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
/* div */ container.addEventListener('wheel', (event: WheelEvent) => {
|
|
153
|
+
event.stopPropagation();
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
this.Tick(event.deltaY)
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public Tick(value: number): void {
|
|
161
|
+
|
|
162
|
+
// not sure what alternate case I am worried about here,
|
|
163
|
+
// sideways wheel? shift key?
|
|
164
|
+
|
|
165
|
+
// normalize
|
|
166
|
+
|
|
167
|
+
const scale = Math.round(this.scale / 2.5) * 2.5;
|
|
168
|
+
|
|
169
|
+
if (value > 0) {
|
|
170
|
+
this.UpdateScale(scale - 2.5, true, true);
|
|
171
|
+
}
|
|
172
|
+
else if (value < 0){
|
|
173
|
+
this.UpdateScale(scale + 2.5, true, true);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
public UpdateScale(scale: number, notify = false, keep_focus = false): void {
|
|
179
|
+
|
|
180
|
+
scale = Math.max(50, Math.min(200, scale));
|
|
181
|
+
if (scale !== this.scale) {
|
|
182
|
+
this.scale = scale;
|
|
183
|
+
this.input.value = this.format.Format(scale) + '%';
|
|
184
|
+
this.slider.value = scale.toFixed(1);
|
|
185
|
+
|
|
186
|
+
if (notify) {
|
|
187
|
+
if (!this.timeout) {
|
|
188
|
+
this.timeout = requestAnimationFrame(() => {
|
|
189
|
+
this.timeout = 0;
|
|
190
|
+
this.Publish({
|
|
191
|
+
type: 'scale',
|
|
192
|
+
value: this.scale / 100,
|
|
193
|
+
keep_focus,
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
}
|
|
202
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
* options for serializing data
|
|
24
|
+
*
|
|
25
|
+
* @privateRemarks
|
|
26
|
+
* essentially a placeholder for options; we only have one at the moment.
|
|
27
|
+
*
|
|
28
|
+
* note that the underlying cells container has its own set of options. we
|
|
29
|
+
* are explicitly not using that so we can add things that are beyond that
|
|
30
|
+
* scope.
|
|
31
|
+
*
|
|
32
|
+
* still, we might just wrap that object [FIXME/TODO].
|
|
33
|
+
*/
|
|
34
|
+
export interface SerializeOptions {
|
|
35
|
+
|
|
36
|
+
/** optimize for size */
|
|
37
|
+
optimize?: 'size'|'speed';
|
|
38
|
+
|
|
39
|
+
/** include the rendered/calculated value in export */
|
|
40
|
+
rendered_values?: boolean;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* preserve cell type
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
preserve_type?: boolean;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* expand arrays so cells have individual values
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
expand_arrays?: boolean;
|
|
53
|
+
|
|
54
|
+
/** translate colors to xlsx-friendly values */
|
|
55
|
+
export_colors?: boolean;
|
|
56
|
+
|
|
57
|
+
/** export cells that have no value, but have a border or background color */
|
|
58
|
+
decorated_cells?: boolean;
|
|
59
|
+
|
|
60
|
+
/** prune unused rows/columns */
|
|
61
|
+
shrink?: boolean;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* include tables. tables will be serialized in the model, so we can
|
|
65
|
+
* drop them from cells. but you can leave them in if that's useful.
|
|
66
|
+
*/
|
|
67
|
+
tables?: boolean;
|
|
68
|
+
|
|
69
|
+
/** share resources (images, for now) to prevent writing data URIs more than once */
|
|
70
|
+
share_resources?: boolean;
|
|
71
|
+
|
|
72
|
+
}
|