@trebco/treb 27.7.6 → 27.11.1
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/dist/treb-spreadsheet.mjs +14 -14
- package/dist/treb.d.ts +28 -26
- package/notes/conditional-fomratring.md +29 -0
- package/package.json +1 -1
- package/treb-base-types/src/area.ts +181 -0
- package/{treb-grid/src/util/dom_utilities.ts → treb-base-types/src/dom-utilities.ts} +29 -20
- package/treb-base-types/src/evaluate-options.ts +21 -0
- package/treb-base-types/src/gradient.ts +97 -0
- package/treb-base-types/src/import.ts +2 -1
- package/treb-base-types/src/index.ts +3 -0
- package/treb-base-types/src/theme.ts +3 -5
- package/treb-calculator/src/calculator.ts +190 -28
- package/treb-calculator/src/dag/calculation_leaf_vertex.ts +97 -0
- package/treb-calculator/src/dag/graph.ts +10 -22
- package/treb-calculator/src/dag/{leaf_vertex.ts → state_leaf_vertex.ts} +3 -3
- package/treb-calculator/src/descriptors.ts +10 -3
- package/treb-calculator/src/expression-calculator.ts +1 -1
- package/treb-calculator/src/function-library.ts +25 -22
- package/treb-calculator/src/functions/base-functions.ts +166 -5
- package/treb-calculator/src/index.ts +6 -6
- package/treb-calculator/src/notifier-types.ts +1 -1
- package/treb-calculator/src/utilities.ts +2 -2
- package/treb-charts/src/util.ts +2 -2
- package/treb-embed/src/custom-element/global.d.ts +3 -1
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +13 -19
- package/treb-embed/src/embedded-spreadsheet.ts +378 -132
- package/treb-embed/src/spinner.ts +3 -3
- package/treb-embed/style/layout.scss +1 -1
- package/treb-export/src/drawing2/chart2.ts +11 -2
- package/treb-export/src/export-worker/export-worker.ts +0 -13
- package/treb-export/src/export2.ts +197 -2
- package/treb-export/src/import2.ts +169 -4
- package/treb-export/src/workbook-style2.ts +59 -10
- package/treb-export/src/workbook2.ts +10 -1
- package/treb-grid/src/editors/autocomplete.ts +28 -24
- package/treb-grid/src/editors/editor.ts +3 -4
- package/treb-grid/src/editors/formula_bar.ts +1 -1
- package/treb-grid/src/index.ts +2 -1
- package/treb-grid/src/layout/base_layout.ts +34 -31
- package/treb-grid/src/layout/grid_layout.ts +17 -28
- package/treb-grid/src/render/selection-renderer.ts +2 -3
- package/treb-grid/src/render/svg_header_overlay.ts +4 -11
- package/treb-grid/src/render/svg_selection_block.ts +27 -34
- package/treb-grid/src/render/tile_renderer.ts +8 -6
- package/treb-grid/src/types/conditional_format.ts +168 -0
- package/treb-grid/src/types/grid.ts +37 -47
- package/treb-grid/src/types/grid_base.ts +188 -33
- package/treb-grid/src/types/scale-control.ts +2 -2
- package/treb-grid/src/types/sheet.ts +332 -28
- package/treb-grid/src/types/sheet_types.ts +4 -0
- package/treb-grid/src/types/tab_bar.ts +4 -8
- package/treb-utils/src/index.ts +0 -1
- package/treb-utils/src/resizable.ts +26 -27
- package/treb-utils/src/template.ts +0 -70
- /package/{README-shadow-DOM.md → notes/shadow-DOM.md} +0 -0
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API v27.
|
|
1
|
+
/*! API v27.11. Copyright 2018-2023 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* add our tag to the map
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
declare global {
|
|
7
7
|
interface HTMLElementTagNameMap {
|
|
8
8
|
'treb-spreadsheet': HTMLElement & {
|
|
9
|
-
|
|
9
|
+
instance: {
|
|
10
|
+
sheet: EmbeddedSpreadsheet | undefined;
|
|
11
|
+
} | undefined;
|
|
10
12
|
};
|
|
11
13
|
}
|
|
12
14
|
}
|
|
@@ -385,11 +387,11 @@ export declare class EmbeddedSpreadsheet {
|
|
|
385
387
|
* @param formula - annotation formula. For charts, the chart formula.
|
|
386
388
|
* @param type - annotation type. Defaults to `treb-chart`.
|
|
387
389
|
* @param rect - coordinates, or a range reference for layout.
|
|
388
|
-
*
|
|
389
|
-
*
|
|
390
|
-
*
|
|
390
|
+
* @param options - evaluate options. because this function used to take
|
|
391
|
+
* the argument separator, we allow that to be passed directly, but this
|
|
392
|
+
* is deprecated. new code should use the options object.
|
|
391
393
|
*/
|
|
392
|
-
InsertAnnotation(formula: string, type?: AnnotationType, rect?: IRectangle | RangeReference,
|
|
394
|
+
InsertAnnotation(formula: string, type?: AnnotationType, rect?: IRectangle | RangeReference, options?: EvaluateOptions | ',' | ';'): void;
|
|
393
395
|
|
|
394
396
|
/**
|
|
395
397
|
* Insert an image. This method will open a file chooser and (if an image
|
|
@@ -1291,6 +1293,26 @@ export interface TableSortOptions {
|
|
|
1291
1293
|
}
|
|
1292
1294
|
export type TableSortType = 'text' | 'numeric' | 'auto';
|
|
1293
1295
|
|
|
1296
|
+
/**
|
|
1297
|
+
* options for the evaluate function
|
|
1298
|
+
*/
|
|
1299
|
+
export interface EvaluateOptions {
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* argument separator to use when parsing input. set this option to
|
|
1303
|
+
* use a consistent argument separator independent of current locale.
|
|
1304
|
+
*/
|
|
1305
|
+
argument_separator?: ',' | ';';
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* allow R1C1-style references. the Evaluate function cannot use
|
|
1309
|
+
* relative references (e.g. R[-1]C[0]), so those will always fail.
|
|
1310
|
+
* however it may be useful to use direct R1C1 references (e.g. R3C4),
|
|
1311
|
+
* so we optionally support that behind this flag.
|
|
1312
|
+
*/
|
|
1313
|
+
r1c1?: boolean;
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1294
1316
|
/**
|
|
1295
1317
|
* this is the document type used by TREB. it has a lot of small variations
|
|
1296
1318
|
* for historical reasons and backwards compatibility. usually it's preferable
|
|
@@ -1821,23 +1843,3 @@ export interface ExportOptions {
|
|
|
1821
1843
|
/** use number formats when exporting numbers */
|
|
1822
1844
|
formatted?: boolean;
|
|
1823
1845
|
}
|
|
1824
|
-
|
|
1825
|
-
/**
|
|
1826
|
-
* options for the evaluate function
|
|
1827
|
-
*/
|
|
1828
|
-
export interface EvaluateOptions {
|
|
1829
|
-
|
|
1830
|
-
/**
|
|
1831
|
-
* argument separator to use when parsing input. set this option to
|
|
1832
|
-
* use a consistent argument separator independent of current locale.
|
|
1833
|
-
*/
|
|
1834
|
-
argument_separator?: ',' | ';';
|
|
1835
|
-
|
|
1836
|
-
/**
|
|
1837
|
-
* allow R1C1-style references. the Evaluate function cannot use
|
|
1838
|
-
* relative references (e.g. R[-1]C[0]), so those will always fail.
|
|
1839
|
-
* however it may be useful to use direct R1C1 references (e.g. R3C4),
|
|
1840
|
-
* so we optionally support that behind this flag.
|
|
1841
|
-
*/
|
|
1842
|
-
r1c1?: boolean;
|
|
1843
|
-
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
conditional formatting TODO list
|
|
2
|
+
|
|
3
|
+
- import/export
|
|
4
|
+
|
|
5
|
+
- run through the command queue
|
|
6
|
+
|
|
7
|
+
- dragging selection knob should expand conditional formats
|
|
8
|
+
(if you have the whole thing selected). at the moment it
|
|
9
|
+
seems to be hard-copying the format, which is bad.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
done
|
|
14
|
+
|
|
15
|
+
- consolidate expression and cell-match types, in the sheet part,
|
|
16
|
+
handle arrays and single cells
|
|
17
|
+
|
|
18
|
+
note that these are now essentially the same thing, with a small
|
|
19
|
+
difference when we attach the conditional format to the graph. so
|
|
20
|
+
we could combine them. but, because there's a hard distinction in
|
|
21
|
+
XLSX, we will probably need to keep track of which is which.
|
|
22
|
+
|
|
23
|
+
- if you do the above you can remove the "applied" flag
|
|
24
|
+
|
|
25
|
+
- update range (and formula) based on insert/delete row/col
|
|
26
|
+
(+ sheet name change)
|
|
27
|
+
|
|
28
|
+
- use a gradient function
|
|
29
|
+
|
package/package.json
CHANGED
|
@@ -55,6 +55,13 @@ export interface IArea {
|
|
|
55
55
|
end: ICellAddress;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export interface PatchOptions {
|
|
59
|
+
before_column: number;
|
|
60
|
+
column_count: number;
|
|
61
|
+
before_row: number;
|
|
62
|
+
row_count: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
/**
|
|
59
66
|
* type guard function
|
|
60
67
|
* FIXME: is there a naming convention for these?
|
|
@@ -197,7 +204,181 @@ export class Area implements IArea { // }, IterableIterator<ICellAddress> {
|
|
|
197
204
|
});
|
|
198
205
|
}
|
|
199
206
|
|
|
207
|
+
/**
|
|
208
|
+
* adjust an area in response to an insert/delete operation.
|
|
209
|
+
* I noticed we were doing this in several places. moved here to unify.
|
|
210
|
+
*
|
|
211
|
+
* @param source - the starting area. we'll create a new object to return
|
|
212
|
+
* (we will not mutate in place)
|
|
213
|
+
*/
|
|
214
|
+
public static PatchArea(source: IArea, options: PatchOptions): Area | false {
|
|
215
|
+
|
|
216
|
+
const { before_column, column_count, before_row, row_count } = options;
|
|
217
|
+
|
|
218
|
+
let area = new Area(source.start, source.end);
|
|
219
|
+
const sheet_id = source.start.sheet_id;
|
|
220
|
+
|
|
221
|
+
if (column_count && before_column <= area.end.column) {
|
|
222
|
+
|
|
223
|
+
/*
|
|
224
|
+
// (1) we are before the insert point, not affected
|
|
225
|
+
|
|
226
|
+
if (before_column > range.end.column) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
if (column_count > 0) {
|
|
232
|
+
|
|
233
|
+
// (2) it's an insert and we are past the insert point:
|
|
234
|
+
// increment [start] and [end] by [count]
|
|
235
|
+
|
|
236
|
+
if (before_column <= area.start.column) {
|
|
237
|
+
area.Shift(0, column_count);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// (3) it's an insert and we contain the insert point:
|
|
241
|
+
// increment [end] by [count]
|
|
242
|
+
|
|
243
|
+
else if (before_column > area.start.column && before_column <= area.end.column) {
|
|
244
|
+
area.ConsumeAddress({row: area.end.row, column: area.end.column + column_count});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
else {
|
|
248
|
+
console.warn(`AA X case 1`, before_column, column_count, JSON.stringify(area));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
else if (column_count < 0) {
|
|
253
|
+
|
|
254
|
+
// (4) it's a delete and we are past the delete point (before+count):
|
|
255
|
+
// decrement [start] and [end] by [count]
|
|
256
|
+
|
|
257
|
+
if (before_column - column_count <= area.start.column) {
|
|
258
|
+
area.Shift(0, column_count);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// (5) it's a delete and contains the entire range
|
|
262
|
+
|
|
263
|
+
else if (before_column <= area.start.column && before_column - column_count > area.end.column) {
|
|
264
|
+
|
|
265
|
+
// we can actually just return at this point
|
|
266
|
+
return false;
|
|
267
|
+
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// (6) it's a delete and contains part of the range. clip the range.
|
|
271
|
+
|
|
272
|
+
else if (before_column <= area.start.column) {
|
|
273
|
+
const last_column = before_column - column_count - 1;
|
|
274
|
+
area = new Area({
|
|
275
|
+
row: area.start.row, column: last_column + 1 + column_count, sheet_id }, {
|
|
276
|
+
row: area.end.row, column: area.end.column + column_count });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
else if (before_column <= area.end.column) {
|
|
280
|
+
const last_column = before_column - column_count - 1;
|
|
281
|
+
|
|
282
|
+
if (last_column >= area.end.column) {
|
|
283
|
+
area = new Area({
|
|
284
|
+
row: area.start.row, column: area.start.column, sheet_id }, {
|
|
285
|
+
row: area.end.row, column: before_column - 1 });
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
area = new Area({
|
|
289
|
+
row: area.start.row, column: area.start.column, sheet_id }, {
|
|
290
|
+
row: area.end.row, column: area.start.column + area.columns + column_count - 1});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
else {
|
|
296
|
+
console.warn(`AA X case 2`, before_column, column_count, JSON.stringify(area));
|
|
297
|
+
}
|
|
200
298
|
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (row_count && before_row <= area.end.row) {
|
|
303
|
+
|
|
304
|
+
/*
|
|
305
|
+
// (1) we are before the insert point, not affected
|
|
306
|
+
|
|
307
|
+
if (before_column > range.end.column) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
if (row_count > 0) {
|
|
313
|
+
|
|
314
|
+
// (2) it's an insert and we are past the insert point:
|
|
315
|
+
// increment [start] and [end] by [count]
|
|
316
|
+
|
|
317
|
+
if (before_row <= area.start.row) {
|
|
318
|
+
area.Shift(row_count, 0);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// (3) it's an insert and we contain the insert point:
|
|
322
|
+
// increment [end] by [count]
|
|
323
|
+
|
|
324
|
+
else if (before_row > area.start.row && before_row <= area.end.row) {
|
|
325
|
+
area.ConsumeAddress({row: area.end.row + row_count, column: area.end.column});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
else {
|
|
329
|
+
console.warn(`AA X case 3`, before_row, row_count, JSON.stringify(area));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
}
|
|
333
|
+
else if (row_count < 0) {
|
|
334
|
+
|
|
335
|
+
// (4) it's a delete and we are past the delete point (before+count):
|
|
336
|
+
// decrement [start] and [end] by [count]
|
|
337
|
+
|
|
338
|
+
if (before_row - row_count <= area.start.row) {
|
|
339
|
+
area.Shift(row_count, 0);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// (5) it's a delete and contains the entire range
|
|
343
|
+
|
|
344
|
+
else if (before_row <= area.start.row && before_row - row_count > area.end.row) {
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// (6) it's a delete and contains part of the range. clip the range.
|
|
349
|
+
|
|
350
|
+
else if (before_row <= area.start.row) {
|
|
351
|
+
const last_row = before_row - row_count - 1;
|
|
352
|
+
area = new Area({
|
|
353
|
+
column: area.start.column, row: last_row + 1 + row_count, sheet_id }, {
|
|
354
|
+
column: area.end.column, row: area.end.row + row_count });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
else if (before_row <= area.end.row) {
|
|
358
|
+
const last_row = before_row - row_count - 1;
|
|
359
|
+
if (last_row >= area.end.row) {
|
|
360
|
+
area = new Area({
|
|
361
|
+
column: area.start.column, row: area.start.row, sheet_id }, {
|
|
362
|
+
column: area.end.column, row: before_row - 1 });
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
area = new Area({
|
|
366
|
+
column: area.start.column, row: area.start.row, sheet_id }, {
|
|
367
|
+
column: area.end.column, row: area.start.row + area.rows + row_count - 1 });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
else {
|
|
373
|
+
console.warn(`AA X case 4`, before_row, row_count, JSON.stringify(area));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return area;
|
|
380
|
+
|
|
381
|
+
}
|
|
201
382
|
|
|
202
383
|
/** accessor returns a _copy_ of the start address */
|
|
203
384
|
public get start(): ICellAddress {
|
|
@@ -19,25 +19,50 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
const SVGNS = 'http://www.w3.org/2000/svg';
|
|
23
|
+
|
|
22
24
|
export class DOMUtilities {
|
|
23
25
|
|
|
24
26
|
/** creates a div and assigns class name/names */
|
|
25
|
-
public static
|
|
27
|
+
public static Div(classes?: string|string[], parent?: HTMLElement, scope?: string): HTMLDivElement {
|
|
26
28
|
return this.Create('div', classes, parent, scope);
|
|
27
29
|
}
|
|
28
30
|
|
|
31
|
+
public static ClassNames(element: HTMLElement|SVGElement, classes: string|string[]) {
|
|
32
|
+
element.classList.add(...(Array.isArray(classes) ? classes : [classes]).reduce((arr: string[], entry) => [...arr, ...entry.split(/\s+/g)], []));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public static SVG<K extends keyof SVGElementTagNameMap>(
|
|
36
|
+
tag: K,
|
|
37
|
+
classes?: string|string[],
|
|
38
|
+
parent?: HTMLElement|SVGElement
|
|
39
|
+
): SVGElementTagNameMap[K] {
|
|
40
|
+
|
|
41
|
+
const element = document.createElementNS(SVGNS, tag);
|
|
42
|
+
|
|
43
|
+
if (classes) {
|
|
44
|
+
this.ClassNames(element, classes);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (parent) {
|
|
48
|
+
parent.appendChild(element);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return element;
|
|
52
|
+
}
|
|
53
|
+
|
|
29
54
|
/** better typing */
|
|
30
55
|
public static Create<K extends keyof HTMLElementTagNameMap>(
|
|
31
56
|
tag: K,
|
|
32
|
-
|
|
57
|
+
classes?: string|string[],
|
|
33
58
|
parent?: HTMLElement,
|
|
34
59
|
scope?: string,
|
|
35
60
|
attrs?: Record<string, string>): HTMLElementTagNameMap[K] {
|
|
36
61
|
|
|
37
62
|
const element = document.createElement(tag);
|
|
38
63
|
|
|
39
|
-
if (
|
|
40
|
-
element
|
|
64
|
+
if (classes) {
|
|
65
|
+
this.ClassNames(element, classes);
|
|
41
66
|
}
|
|
42
67
|
|
|
43
68
|
if (scope) {
|
|
@@ -58,20 +83,4 @@ export class DOMUtilities {
|
|
|
58
83
|
return element;
|
|
59
84
|
}
|
|
60
85
|
|
|
61
|
-
/* * generic element constructor. shame we need the tag AND the type. * /
|
|
62
|
-
public static Create1<E extends HTMLElement>(tag = '', classes = '', parent?: HTMLElement, scope?: string, attrs?: Record<string, string>){
|
|
63
|
-
const element = document.createElement(tag) as E;
|
|
64
|
-
if (classes) element.setAttribute('class', classes);
|
|
65
|
-
if (scope) element.setAttribute(scope, '');
|
|
66
|
-
if (attrs) {
|
|
67
|
-
const keys = Object.keys(attrs);
|
|
68
|
-
for (const key of keys) {
|
|
69
|
-
element.setAttribute(key, attrs[key]);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (parent) parent.appendChild(element);
|
|
73
|
-
return element;
|
|
74
|
-
}
|
|
75
|
-
*/
|
|
76
|
-
|
|
77
86
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* options for the evaluate function
|
|
4
|
+
*/
|
|
5
|
+
export interface EvaluateOptions {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* argument separator to use when parsing input. set this option to
|
|
9
|
+
* use a consistent argument separator independent of current locale.
|
|
10
|
+
*/
|
|
11
|
+
argument_separator?: ','|';';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* allow R1C1-style references. the Evaluate function cannot use
|
|
15
|
+
* relative references (e.g. R[-1]C[0]), so those will always fail.
|
|
16
|
+
* however it may be useful to use direct R1C1 references (e.g. R3C4),
|
|
17
|
+
* so we optionally support that behind this flag.
|
|
18
|
+
*/
|
|
19
|
+
r1c1?: boolean;
|
|
20
|
+
|
|
21
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
|
|
2
|
+
import { Measurement } from 'treb-utils';
|
|
3
|
+
import { type Color } from './style';
|
|
4
|
+
import { type Theme, ThemeColor2 } from './theme';
|
|
5
|
+
import { ColorFunctions } from './color';
|
|
6
|
+
|
|
7
|
+
export interface GradientStop {
|
|
8
|
+
value: number;
|
|
9
|
+
color: Color;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ColorSpace = 'RGB'|'HSL';
|
|
13
|
+
|
|
14
|
+
export class Gradient {
|
|
15
|
+
|
|
16
|
+
public mapped: Array<GradientStop & { resolved: number[] }>;
|
|
17
|
+
|
|
18
|
+
constructor(stops: GradientStop[], theme: Theme, public color_space: ColorSpace = 'HSL') {
|
|
19
|
+
|
|
20
|
+
this.mapped = stops.map(stop => {
|
|
21
|
+
|
|
22
|
+
if (stop.value < 0 || stop.value > 1) {
|
|
23
|
+
throw new Error('invalid stop value');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const rgb = Measurement.MeasureColor(ThemeColor2(theme, stop.color));
|
|
27
|
+
|
|
28
|
+
let resolved: number[] = [];
|
|
29
|
+
|
|
30
|
+
if (color_space === 'HSL') {
|
|
31
|
+
const hsl = ColorFunctions.RGBToHSL(rgb[0], rgb[1], rgb[2]);
|
|
32
|
+
resolved = [hsl.h, hsl.s, hsl.l];
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
resolved = [...rgb];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
...stop, resolved,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
this.mapped.sort((a, b) => a.value - b.value);
|
|
45
|
+
|
|
46
|
+
// FIXME: we should expand the gradient, but for now we'll just clamp
|
|
47
|
+
|
|
48
|
+
if (this.mapped[0].value > 0) {
|
|
49
|
+
this.mapped.unshift({
|
|
50
|
+
...this.mapped[0], value: 0
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (this.mapped[this.mapped.length - 1].value < 1) {
|
|
55
|
+
this.mapped.push({
|
|
56
|
+
...this.mapped[this.mapped.length - 1], value: 1
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public RenderColor(values: number[]) {
|
|
63
|
+
if (this.color_space === 'RGB') {
|
|
64
|
+
return { text: `rgb(${values})` };
|
|
65
|
+
}
|
|
66
|
+
return { text: `hsl(${values[0]},${values[1] * 100}%,${values[2] * 100}%)` };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public Interpolate(value: number): Color {
|
|
70
|
+
|
|
71
|
+
value = Math.min(1, Math.max(0, value));
|
|
72
|
+
for (const [index, stop] of this.mapped.entries()) {
|
|
73
|
+
|
|
74
|
+
if (value === stop.value) {
|
|
75
|
+
return this.RenderColor(stop.resolved);
|
|
76
|
+
}
|
|
77
|
+
if (value < stop.value) {
|
|
78
|
+
const a = this.mapped[index - 1];
|
|
79
|
+
const b = stop;
|
|
80
|
+
|
|
81
|
+
const range = b.value - a.value; // FIXME: cache
|
|
82
|
+
const advance = value - a.value;
|
|
83
|
+
|
|
84
|
+
const values = [0,1,2].map(index => {
|
|
85
|
+
return a.resolved[index] + (b.resolved[index] - a.resolved[index]) / range * advance;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return this.RenderColor(values);
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { text: '' };
|
|
94
|
+
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
}
|
|
@@ -25,7 +25,7 @@ import type { IArea } from './area';
|
|
|
25
25
|
import type { AnnotationLayout } from './layout';
|
|
26
26
|
import type { DataValidation } from './cell';
|
|
27
27
|
import type { Table } from './table';
|
|
28
|
-
import type { AnnotationType } from 'treb-grid';
|
|
28
|
+
import type { AnnotationType, ConditionalFormat } from 'treb-grid';
|
|
29
29
|
|
|
30
30
|
export interface CellParseResult {
|
|
31
31
|
row: number,
|
|
@@ -58,6 +58,7 @@ export interface ImportedSheetData {
|
|
|
58
58
|
column_widths: number[];
|
|
59
59
|
row_heights: number[];
|
|
60
60
|
styles: CellStyle[];
|
|
61
|
+
conditional_formats: ConditionalFormat[];
|
|
61
62
|
|
|
62
63
|
// optional, for backcompat
|
|
63
64
|
sheet_style?: number;
|
|
@@ -36,6 +36,9 @@ export * from './layout';
|
|
|
36
36
|
export * from './render_text';
|
|
37
37
|
export * from './api_types';
|
|
38
38
|
export * from './table';
|
|
39
|
+
export * from './gradient';
|
|
40
|
+
export * from './evaluate-options';
|
|
41
|
+
export * from './dom-utilities';
|
|
39
42
|
|
|
40
43
|
// import * as Style from './style';
|
|
41
44
|
// export { Style };
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import type { Color, CellStyle } from './style';
|
|
23
23
|
import { ColorFunctions } from './color';
|
|
24
|
+
import { DOMUtilities } from './dom-utilities';
|
|
24
25
|
|
|
25
26
|
/*
|
|
26
27
|
* so this is a little strange. we use CSS to populate a theme object,
|
|
@@ -494,10 +495,7 @@ export const LoadThemeProperties = (container: HTMLElement): Theme => {
|
|
|
494
495
|
const theme: Theme = JSON.parse(JSON.stringify(DefaultTheme));
|
|
495
496
|
|
|
496
497
|
const Append = (parent: HTMLElement, classes: string): HTMLDivElement => {
|
|
497
|
-
|
|
498
|
-
node.setAttribute('class', classes);
|
|
499
|
-
parent.appendChild(node);
|
|
500
|
-
return node;
|
|
498
|
+
return DOMUtilities.Div(classes, parent);
|
|
501
499
|
}
|
|
502
500
|
|
|
503
501
|
const ElementCSS = (parent: HTMLElement, classes: string): CSSStyleDeclaration => {
|
|
@@ -574,7 +572,7 @@ export const LoadThemeProperties = (container: HTMLElement): Theme => {
|
|
|
574
572
|
// we could just parse, we know the returned css format is going
|
|
575
573
|
// to be an rgb triple (I think?)
|
|
576
574
|
|
|
577
|
-
const canvas =
|
|
575
|
+
const canvas = DOMUtilities.Create('canvas');
|
|
578
576
|
|
|
579
577
|
canvas.width = 3;
|
|
580
578
|
canvas.height = 3;
|