@trebco/treb 27.5.3 → 27.7.6
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 +17 -3
- package/package.json +3 -3
- package/treb-calculator/src/calculator.ts +15 -104
- package/treb-embed/src/embedded-spreadsheet.ts +30 -30
- package/treb-embed/style/formula-bar.scss +2 -0
- package/treb-embed/style/theme-defaults.scss +46 -15
- package/treb-grid/src/editors/editor.ts +1276 -0
- package/treb-grid/src/editors/external_editor.ts +113 -0
- package/treb-grid/src/editors/formula_bar.ts +450 -474
- package/treb-grid/src/editors/overlay_editor.ts +437 -512
- package/treb-grid/src/index.ts +1 -1
- package/treb-grid/src/layout/base_layout.ts +1 -1
- package/treb-grid/src/types/data_model.ts +130 -3
- package/treb-grid/src/types/external_editor_config.ts +47 -0
- package/treb-grid/src/types/grid.ts +91 -39
- package/treb-grid/src/types/grid_base.ts +1 -2
- package/treb-grid/src/types/scale-control.ts +1 -1
- package/treb-grid/src/util/dom_utilities.ts +58 -25
- package/treb-grid/src/editors/formula_editor_base.ts +0 -912
- package/treb-grid/src/types/external_editor.ts +0 -27
package/treb-grid/src/index.ts
CHANGED
|
@@ -39,4 +39,4 @@ export type { FunctionDescriptor, ArgumentDescriptor } from './editors/autocompl
|
|
|
39
39
|
export { UA } from './util/ua';
|
|
40
40
|
export type { SetRangeOptions } from './types/set_range_options';
|
|
41
41
|
export type { AnnotationData, AnnotationType } from './types/annotation';
|
|
42
|
-
export type {
|
|
42
|
+
export type { ExternalEditorConfig, DependencyList, ExternalEditorCallback } from './types/external_editor_config';
|
|
@@ -357,7 +357,7 @@ export abstract class BaseLayout {
|
|
|
357
357
|
this.note_node = DOMUtilities.CreateDiv('treb-note');
|
|
358
358
|
this.title_node = DOMUtilities.CreateDiv('treb-hover-title');
|
|
359
359
|
|
|
360
|
-
this.sort_button = DOMUtilities.Create
|
|
360
|
+
this.sort_button = DOMUtilities.Create(
|
|
361
361
|
'button',
|
|
362
362
|
'treb-sort-button', undefined, undefined, { title: 'Sort table', tabindex: '-1'});
|
|
363
363
|
|
|
@@ -23,8 +23,8 @@ import type { Sheet } from './sheet';
|
|
|
23
23
|
import type { IArea, ICellAddress, Table, CellStyle } from 'treb-base-types';
|
|
24
24
|
import type { SerializedSheet } from './sheet_types';
|
|
25
25
|
import { NamedRangeCollection } from './named_range';
|
|
26
|
-
import type
|
|
27
|
-
import { Style } from 'treb-base-types';
|
|
26
|
+
import { type ExpressionUnit, type UnitAddress, type UnitStructuredReference, type UnitRange, Parser, QuotedSheetNameRegex } from 'treb-parser';
|
|
27
|
+
import { Area, IsCellAddress, Style } from 'treb-base-types';
|
|
28
28
|
|
|
29
29
|
export interface SerializedMacroFunction {
|
|
30
30
|
name: string;
|
|
@@ -159,6 +159,8 @@ export class SheetCollection {
|
|
|
159
159
|
*/
|
|
160
160
|
export class DataModel {
|
|
161
161
|
|
|
162
|
+
public readonly parser: Parser = new Parser();
|
|
163
|
+
|
|
162
164
|
/** document metadata */
|
|
163
165
|
public document_name?: string;
|
|
164
166
|
|
|
@@ -317,7 +319,132 @@ export class DataModel {
|
|
|
317
319
|
|
|
318
320
|
return undefined;
|
|
319
321
|
}
|
|
320
|
-
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* return an address label for this address (single cell or range)
|
|
325
|
+
*
|
|
326
|
+
* @param address
|
|
327
|
+
* @param active_sheet
|
|
328
|
+
*/
|
|
329
|
+
public AddressToLabel(address: ICellAddress|IArea, active_sheet?: Sheet) {
|
|
330
|
+
|
|
331
|
+
const start = IsCellAddress(address) ? address : address.start;
|
|
332
|
+
const parts = IsCellAddress(address) ?
|
|
333
|
+
[Area.CellAddressToLabel(address)] :
|
|
334
|
+
[Area.CellAddressToLabel(address.start), Area.CellAddressToLabel(address.end)];
|
|
335
|
+
|
|
336
|
+
const sheet = this.sheets.Find(start.sheet_id || 0);
|
|
337
|
+
const name = (sheet?.name) ?
|
|
338
|
+
(QuotedSheetNameRegex.test(sheet.name) ? `'${sheet.name}'` : sheet.name) : '';
|
|
339
|
+
|
|
340
|
+
return name + (name ? '!' : '') + (parts[0] === parts[1] ? parts[0] : parts.join(':'));
|
|
341
|
+
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// --- resolution api, moved from calculator ---------------------------------
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* returns false if the sheet cannot be resolved, which probably
|
|
348
|
+
* means the name changed (that's the case we are working on with
|
|
349
|
+
* this fix).
|
|
350
|
+
*/
|
|
351
|
+
public ResolveSheetID(expr: UnitAddress|UnitRange, context?: ICellAddress, active_sheet?: Sheet): boolean {
|
|
352
|
+
|
|
353
|
+
const target = expr.type === 'address' ? expr : expr.start;
|
|
354
|
+
|
|
355
|
+
if (target.sheet_id) {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (target.sheet) {
|
|
360
|
+
const sheet = this.sheets.Find(target.sheet);
|
|
361
|
+
if (sheet) {
|
|
362
|
+
target.sheet_id = sheet.id;
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/*
|
|
367
|
+
const lc = target.sheet.toLowerCase();
|
|
368
|
+
for (const sheet of this.model.sheets.list) {
|
|
369
|
+
if (sheet.name.toLowerCase() === lc) {
|
|
370
|
+
target.sheet_id = sheet.id;
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
*/
|
|
375
|
+
}
|
|
376
|
+
else if (context?.sheet_id) {
|
|
377
|
+
target.sheet_id = context.sheet_id;
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
else if (active_sheet?.id) {
|
|
381
|
+
target.sheet_id = active_sheet.id;
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return false; // the error
|
|
386
|
+
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** wrapper method ensures it always returns an Area (instance, not interface) */
|
|
390
|
+
public ResolveArea(address: string|ICellAddress|IArea, active_sheet: Sheet): Area {
|
|
391
|
+
const resolved = this.ResolveAddress(address, active_sheet);
|
|
392
|
+
return IsCellAddress(resolved) ? new Area(resolved) : new Area(resolved.start, resolved.end);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* moved from embedded sheet. also modified to preserve ranges, so it
|
|
397
|
+
* might return a range (area). if you are expecting the old behavior
|
|
398
|
+
* you need to check (perhaps we could have a wrapper, or make it optional?)
|
|
399
|
+
*
|
|
400
|
+
* Q: why does this not go in grid? or model? (...)
|
|
401
|
+
* Q: why are we not preserving absoute/relative? (...)
|
|
402
|
+
*
|
|
403
|
+
*/
|
|
404
|
+
public ResolveAddress(address: string|ICellAddress|IArea, active_sheet: Sheet): ICellAddress|IArea {
|
|
405
|
+
|
|
406
|
+
if (typeof address === 'string') {
|
|
407
|
+
const parse_result = this.parser.Parse(address);
|
|
408
|
+
if (parse_result.expression && parse_result.expression.type === 'address') {
|
|
409
|
+
this.ResolveSheetID(parse_result.expression, undefined, active_sheet);
|
|
410
|
+
return {
|
|
411
|
+
row: parse_result.expression.row,
|
|
412
|
+
column: parse_result.expression.column,
|
|
413
|
+
sheet_id: parse_result.expression.sheet_id,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
else if (parse_result.expression && parse_result.expression.type === 'range') {
|
|
417
|
+
this.ResolveSheetID(parse_result.expression, undefined, active_sheet);
|
|
418
|
+
return {
|
|
419
|
+
start: {
|
|
420
|
+
row: parse_result.expression.start.row,
|
|
421
|
+
column: parse_result.expression.start.column,
|
|
422
|
+
sheet_id: parse_result.expression.start.sheet_id,
|
|
423
|
+
},
|
|
424
|
+
end: {
|
|
425
|
+
row: parse_result.expression.end.row,
|
|
426
|
+
column: parse_result.expression.end.column,
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
else if (parse_result.expression && parse_result.expression.type === 'identifier') {
|
|
431
|
+
|
|
432
|
+
// is named range guaranteed to have a sheet ID? (I think yes...)
|
|
433
|
+
|
|
434
|
+
const named_range = this.named_ranges.Get(parse_result.expression.name);
|
|
435
|
+
if (named_range) {
|
|
436
|
+
return named_range;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return { row: 0, column: 0 }; // default for string types -- broken
|
|
441
|
+
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return address; // already range or address
|
|
445
|
+
|
|
446
|
+
}
|
|
447
|
+
|
|
321
448
|
}
|
|
322
449
|
|
|
323
450
|
export interface ViewModel {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
import type { IArea, ICellAddress } from 'treb-base-types';
|
|
3
|
+
|
|
4
|
+
export type DependencyList = Array<IArea|ICellAddress|undefined>;
|
|
5
|
+
export type ExternalEditorCallback = (selection?: string) => DependencyList|undefined;
|
|
6
|
+
|
|
7
|
+
// FIXME: if you want to keep the old interface, split into to separate types
|
|
8
|
+
|
|
9
|
+
export interface ExternalEditorConfig {
|
|
10
|
+
|
|
11
|
+
// --- old interface ---------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* list of dependencies to highlight. we support undefined entries in
|
|
15
|
+
* this list so you can use the result of `EmbeddedSpreadsheet.Resolve`,
|
|
16
|
+
* which may return undefined.
|
|
17
|
+
*/
|
|
18
|
+
dependencies: DependencyList;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* this callback will be called when the selection changes in the
|
|
22
|
+
* spreadsheet and this external editor is active. return an updated
|
|
23
|
+
* list of dependencies to highlight.
|
|
24
|
+
*
|
|
25
|
+
* NOTE: this is currently synchronous, but don't rely on that. it
|
|
26
|
+
* might switch to async in the future depending on how it works in
|
|
27
|
+
* practice.
|
|
28
|
+
*/
|
|
29
|
+
update: ExternalEditorCallback;
|
|
30
|
+
|
|
31
|
+
// --- new interface ---------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* a list of nodes that will serve as editors. when you attach, we will do
|
|
35
|
+
* an initial pass of context highlighting. we highlight on text changes
|
|
36
|
+
* and insert references if you make a selection in the spreadsheet while
|
|
37
|
+
* an editor is focused.
|
|
38
|
+
*/
|
|
39
|
+
nodes: HTMLElement[];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* assume that we're editing a formula. does not require leading `=`.
|
|
43
|
+
* defaults to `true` for historical reasons.
|
|
44
|
+
*/
|
|
45
|
+
assume_formula?: boolean;
|
|
46
|
+
|
|
47
|
+
}
|
|
@@ -80,13 +80,15 @@ import { TileRange } from '../layout/base_layout';
|
|
|
80
80
|
import { GridLayout } from '../layout/grid_layout';
|
|
81
81
|
|
|
82
82
|
import type { GridSelection } from './grid_selection';
|
|
83
|
-
import { OverlayEditor
|
|
83
|
+
import { OverlayEditor } from '../editors/overlay_editor';
|
|
84
84
|
|
|
85
85
|
import { TileRenderer } from '../render/tile_renderer';
|
|
86
86
|
import type { GridEvent } from './grid_events';
|
|
87
87
|
import { ErrorCode } from './grid_events';
|
|
88
88
|
import type { LegacySerializedSheet } from './sheet_types';
|
|
89
|
+
// import { FormulaBar } from '../editors/formula_bar';
|
|
89
90
|
import { FormulaBar } from '../editors/formula_bar';
|
|
91
|
+
|
|
90
92
|
import type { GridOptions } from './grid_options';
|
|
91
93
|
import { BorderConstants } from './border_constants';
|
|
92
94
|
import type { SerializeOptions } from './serialize_options';
|
|
@@ -114,7 +116,8 @@ import { GridBase } from './grid_base';
|
|
|
114
116
|
import type { SetRangeOptions } from './set_range_options';
|
|
115
117
|
import type { ClipboardCellData } from './clipboard_data';
|
|
116
118
|
|
|
117
|
-
import type {
|
|
119
|
+
import type { ExternalEditorConfig } from './external_editor_config';
|
|
120
|
+
import { ExternalEditor } from '../editors/external_editor';
|
|
118
121
|
|
|
119
122
|
interface DoubleClickData {
|
|
120
123
|
timeout?: number;
|
|
@@ -148,7 +151,7 @@ export class Grid extends GridBase {
|
|
|
148
151
|
this.UpdateAnnotationLayout();
|
|
149
152
|
this.layout.UpdateAnnotation(this.active_sheet.annotations);
|
|
150
153
|
this.layout.ApplyTheme(this.theme);
|
|
151
|
-
this.overlay_editor?.
|
|
154
|
+
this.overlay_editor?.UpdateScale(value);
|
|
152
155
|
this.tab_bar?.UpdateScale(value);
|
|
153
156
|
|
|
154
157
|
this.grid_events.Publish({
|
|
@@ -264,14 +267,18 @@ export class Grid extends GridBase {
|
|
|
264
267
|
|
|
265
268
|
private RESIZE_PIXEL_BUFFER = 5;
|
|
266
269
|
|
|
267
|
-
|
|
270
|
+
/**
|
|
271
|
+
* formalizing the concept of external selection to support outside tooling.
|
|
272
|
+
*
|
|
273
|
+
* FIXME: stop testing on this field. we need a better way to figure out
|
|
274
|
+
* if the external editor is active.
|
|
275
|
+
*/
|
|
276
|
+
private external_editor_config?: Partial<ExternalEditorConfig>;
|
|
268
277
|
|
|
269
278
|
/**
|
|
270
|
-
*
|
|
271
|
-
* we're formalizing the concept of external selection to support outside
|
|
272
|
-
* tooling.
|
|
279
|
+
* support for external editor. created on demand.
|
|
273
280
|
*/
|
|
274
|
-
private external_editor
|
|
281
|
+
private external_editor?: ExternalEditor;
|
|
275
282
|
|
|
276
283
|
/**
|
|
277
284
|
* flag indicating we're resizing, or hovering over a resize.
|
|
@@ -357,12 +364,11 @@ export class Grid extends GridBase {
|
|
|
357
364
|
*/
|
|
358
365
|
constructor(
|
|
359
366
|
options: GridOptions = {},
|
|
360
|
-
parser: Parser,
|
|
361
367
|
model: DataModel,
|
|
362
368
|
theme: Theme = DefaultTheme,
|
|
363
369
|
initialze_dom = true ) {
|
|
364
370
|
|
|
365
|
-
super(options,
|
|
371
|
+
super(options, model);
|
|
366
372
|
|
|
367
373
|
this.decimal_separator_code = Localization.decimal_separator.charCodeAt(0);
|
|
368
374
|
|
|
@@ -1234,7 +1240,7 @@ export class Grid extends GridBase {
|
|
|
1234
1240
|
this.UpdateLayout(); // in case we have changed font size
|
|
1235
1241
|
// this.selection_renderer.Flush();
|
|
1236
1242
|
|
|
1237
|
-
this.overlay_editor?.
|
|
1243
|
+
this.overlay_editor?.UpdateScale(this.layout.scale);
|
|
1238
1244
|
|
|
1239
1245
|
// if (this.formula_bar) this.formula_bar.UpdateTheme();
|
|
1240
1246
|
|
|
@@ -1659,21 +1665,48 @@ export class Grid extends GridBase {
|
|
|
1659
1665
|
* want to have to use a duplicate internal field, it's clumsy and this
|
|
1660
1666
|
* class isn't public-facing so it's not super important.
|
|
1661
1667
|
*/
|
|
1662
|
-
public ExternalEditor(
|
|
1668
|
+
public ExternalEditor(config: Partial<ExternalEditorConfig>|undefined) {
|
|
1669
|
+
|
|
1670
|
+
this.external_editor_config = config;
|
|
1663
1671
|
|
|
1664
|
-
|
|
1672
|
+
if (config) {
|
|
1665
1673
|
|
|
1666
|
-
|
|
1667
|
-
|
|
1674
|
+
const areas: Area[] = (config.dependencies || []).filter(
|
|
1675
|
+
<T>(test: T|undefined): test is T => !!test).map(reference =>
|
|
1676
|
+
IsCellAddress(reference) ? new Area(reference) : new Area(reference.start, reference.end));
|
|
1668
1677
|
|
|
1669
|
-
|
|
1678
|
+
if (config.nodes?.length) {
|
|
1679
|
+
|
|
1680
|
+
if (!this.external_editor) {
|
|
1681
|
+
const editor = new ExternalEditor(this.model, this.view);
|
|
1682
|
+
this.external_editor = editor;
|
|
1683
|
+
|
|
1684
|
+
// should this persist, or should we only subscribe when we're active? (...)
|
|
1685
|
+
// in theory, at least, it won't send any events unless something changes
|
|
1686
|
+
|
|
1687
|
+
editor.Subscribe(event => this.HighlightDependencies(editor.dependencies));
|
|
1688
|
+
}
|
|
1670
1689
|
|
|
1671
|
-
this.
|
|
1672
|
-
|
|
1673
|
-
IsCellAddress(reference) ? new Area(reference) : new Area(reference.start, reference.end)));
|
|
1690
|
+
this.external_editor.AttachNodes(config.nodes, config.assume_formula ?? true);
|
|
1691
|
+
|
|
1674
1692
|
}
|
|
1693
|
+
else {
|
|
1694
|
+
if (this.external_editor) {
|
|
1695
|
+
this.external_editor.Reset();
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
if (config.dependencies) {
|
|
1700
|
+
this.HighlightDependencies(areas);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1675
1703
|
}
|
|
1676
1704
|
else {
|
|
1705
|
+
|
|
1706
|
+
if (this.external_editor) {
|
|
1707
|
+
this.external_editor.Reset();
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1677
1710
|
this.ClearAdditionalSelections();
|
|
1678
1711
|
this.RenderSelections(true);
|
|
1679
1712
|
}
|
|
@@ -2534,8 +2567,8 @@ export class Grid extends GridBase {
|
|
|
2534
2567
|
|
|
2535
2568
|
this.formula_bar = new FormulaBar(
|
|
2536
2569
|
grid_container,
|
|
2537
|
-
this.parser,
|
|
2538
|
-
this.theme,
|
|
2570
|
+
// this.parser,
|
|
2571
|
+
// this.theme,
|
|
2539
2572
|
this.model,
|
|
2540
2573
|
this.view,
|
|
2541
2574
|
this.options, autocomplete);
|
|
@@ -2711,13 +2744,12 @@ export class Grid extends GridBase {
|
|
|
2711
2744
|
|
|
2712
2745
|
this.overlay_editor = new OverlayEditor(
|
|
2713
2746
|
this.container,
|
|
2714
|
-
this.parser,
|
|
2715
2747
|
this.theme,
|
|
2716
2748
|
this.model,
|
|
2717
2749
|
this.view,
|
|
2718
2750
|
autocomplete);
|
|
2719
2751
|
|
|
2720
|
-
this.overlay_editor.
|
|
2752
|
+
this.overlay_editor.UpdateScale(this.layout.scale);
|
|
2721
2753
|
this.overlay_editor.autocomplete_matcher = this.autocomplete_matcher;
|
|
2722
2754
|
|
|
2723
2755
|
this.overlay_editor.Subscribe(event => {
|
|
@@ -2745,7 +2777,11 @@ export class Grid extends GridBase {
|
|
|
2745
2777
|
break;
|
|
2746
2778
|
|
|
2747
2779
|
case 'end-selection':
|
|
2780
|
+
case 'reset-selection':
|
|
2748
2781
|
this.ClearSelection(this.active_selection);
|
|
2782
|
+
if (this.overlay_editor?.target_address?.sheet_id && this.active_sheet.id !== this.overlay_editor.target_address.sheet_id) {
|
|
2783
|
+
this.ActivateSheetID(this.overlay_editor.target_address.sheet_id);
|
|
2784
|
+
}
|
|
2749
2785
|
this.DelayedRender();
|
|
2750
2786
|
break;
|
|
2751
2787
|
|
|
@@ -3732,7 +3768,7 @@ export class Grid extends GridBase {
|
|
|
3732
3768
|
this.ClearAdditionalSelections();
|
|
3733
3769
|
}
|
|
3734
3770
|
|
|
3735
|
-
if (!selecting_argument || !this.formula_bar?.selecting) {
|
|
3771
|
+
if (!selecting_argument || (!this.formula_bar?.selecting && !this.external_editor?.selecting)) {
|
|
3736
3772
|
|
|
3737
3773
|
// not sure why this breaks the formula bar handler
|
|
3738
3774
|
|
|
@@ -4021,11 +4057,19 @@ export class Grid extends GridBase {
|
|
|
4021
4057
|
if (this.overlay_editor?.editing) {
|
|
4022
4058
|
// ...
|
|
4023
4059
|
}
|
|
4024
|
-
else if (this.
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4060
|
+
else if (this.external_editor_config) {
|
|
4061
|
+
|
|
4062
|
+
// there are two possible cases: either an update function
|
|
4063
|
+
// or a full-on editor. we should probably test for both?
|
|
4064
|
+
|
|
4065
|
+
if (this.external_editor?.active) {
|
|
4066
|
+
this.external_editor.FocusEditor();
|
|
4067
|
+
}
|
|
4068
|
+
if (this.external_editor_config.update) {
|
|
4069
|
+
// not necessary?
|
|
4070
|
+
// console.info('call update?');
|
|
4071
|
+
}
|
|
4072
|
+
|
|
4029
4073
|
}
|
|
4030
4074
|
else if (this.formula_bar) {
|
|
4031
4075
|
this.formula_bar.FocusEditor();
|
|
@@ -4289,7 +4333,7 @@ export class Grid extends GridBase {
|
|
|
4289
4333
|
label = Area.CellAddressToLabel(data.merge_area.start);
|
|
4290
4334
|
}
|
|
4291
4335
|
|
|
4292
|
-
if (this.
|
|
4336
|
+
if (this.external_editor_config || this.active_sheet.id !== this.editing_cell.sheet_id) {
|
|
4293
4337
|
const name = this.active_sheet.name;
|
|
4294
4338
|
|
|
4295
4339
|
if (QuotedSheetNameRegex.test(name)) {
|
|
@@ -4311,9 +4355,15 @@ export class Grid extends GridBase {
|
|
|
4311
4355
|
else if (this.formula_bar && this.formula_bar.selecting) {
|
|
4312
4356
|
this.formula_bar.InsertReference(label, 0);
|
|
4313
4357
|
}
|
|
4314
|
-
else if (this.
|
|
4315
|
-
|
|
4316
|
-
|
|
4358
|
+
else if (this.external_editor_config) {
|
|
4359
|
+
|
|
4360
|
+
if (this.external_editor?.active) {
|
|
4361
|
+
this.external_editor.FocusEditor();
|
|
4362
|
+
this.external_editor.InsertReference(label, 0);
|
|
4363
|
+
}
|
|
4364
|
+
|
|
4365
|
+
if (this.external_editor_config.update) {
|
|
4366
|
+
const result = this.external_editor_config.update.call(0, label);
|
|
4317
4367
|
if (result && Array.isArray(result)) {
|
|
4318
4368
|
this.HighlightDependencies(
|
|
4319
4369
|
result.filter(<T>(entry: T|undefined): entry is T => !!entry).map(reference =>
|
|
@@ -4321,12 +4371,14 @@ export class Grid extends GridBase {
|
|
|
4321
4371
|
}
|
|
4322
4372
|
}
|
|
4323
4373
|
}
|
|
4374
|
+
/*
|
|
4324
4375
|
else if (this.select_argument) {
|
|
4325
4376
|
this.grid_events.Publish({
|
|
4326
4377
|
type: 'alternate-selection',
|
|
4327
4378
|
selection: this.active_selection,
|
|
4328
4379
|
});
|
|
4329
4380
|
}
|
|
4381
|
+
*/
|
|
4330
4382
|
}
|
|
4331
4383
|
|
|
4332
4384
|
/**
|
|
@@ -4335,11 +4387,11 @@ export class Grid extends GridBase {
|
|
|
4335
4387
|
*
|
|
4336
4388
|
* FIXME: why is this not an accessor?
|
|
4337
4389
|
*/
|
|
4338
|
-
private SelectingArgument() {
|
|
4390
|
+
private SelectingArgument(): boolean {
|
|
4339
4391
|
return (this.overlay_editor?.editing && this.overlay_editor?.selecting)
|
|
4340
4392
|
|| (this.formula_bar && this.formula_bar.selecting)
|
|
4341
|
-
|| (this.
|
|
4342
|
-
|| (this.select_argument);
|
|
4393
|
+
|| (!!this.external_editor_config);
|
|
4394
|
+
// || (this.select_argument);
|
|
4343
4395
|
}
|
|
4344
4396
|
|
|
4345
4397
|
/**
|
|
@@ -4371,16 +4423,16 @@ export class Grid extends GridBase {
|
|
|
4371
4423
|
editor_open = true;
|
|
4372
4424
|
const result = this.overlay_editor.HandleKeyDown(event);
|
|
4373
4425
|
switch (result) {
|
|
4374
|
-
case
|
|
4426
|
+
case 'handled':
|
|
4375
4427
|
return;
|
|
4376
4428
|
|
|
4377
|
-
case
|
|
4429
|
+
case 'discard':
|
|
4378
4430
|
this.editing_state = EditingState.NotEditing;
|
|
4379
4431
|
this.DismissEditor();
|
|
4380
4432
|
this.DelayedRender();
|
|
4381
4433
|
return;
|
|
4382
4434
|
|
|
4383
|
-
case
|
|
4435
|
+
case 'commit':
|
|
4384
4436
|
|
|
4385
4437
|
// FIXME: unify this (to the extent possible) w/ the other editor
|
|
4386
4438
|
|
|
@@ -132,7 +132,6 @@ export class GridBase {
|
|
|
132
132
|
|
|
133
133
|
constructor(
|
|
134
134
|
options: GridOptions = {},
|
|
135
|
-
parser: Parser,
|
|
136
135
|
model: DataModel) {
|
|
137
136
|
|
|
138
137
|
this.model = model;
|
|
@@ -144,7 +143,7 @@ export class GridBase {
|
|
|
144
143
|
|
|
145
144
|
// shared parser
|
|
146
145
|
|
|
147
|
-
this.parser = parser;
|
|
146
|
+
this.parser = model.parser;
|
|
148
147
|
|
|
149
148
|
// apply default options, meaning that you need to explicitly set/unset
|
|
150
149
|
// in order to change behavior. FIXME: this is ok for flat structure, but
|
|
@@ -137,7 +137,7 @@ export class ScaleControl extends EventSource<ScaleEvent> {
|
|
|
137
137
|
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
this.slider = DOM.Create
|
|
140
|
+
this.slider = DOM.Create('input', undefined, popup, undefined, {
|
|
141
141
|
type: 'range',
|
|
142
142
|
min: '50',
|
|
143
143
|
max: '200',
|
|
@@ -1,33 +1,65 @@
|
|
|
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
|
-
|
|
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
22
|
export class DOMUtilities {
|
|
23
23
|
|
|
24
24
|
/** creates a div and assigns class name/names */
|
|
25
|
-
public static CreateDiv(classes = '', parent?: HTMLElement, scope?: string){
|
|
26
|
-
return this.Create
|
|
25
|
+
public static CreateDiv(classes = '', parent?: HTMLElement, scope?: string): HTMLDivElement {
|
|
26
|
+
return this.Create('div', classes, parent, scope);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** better typing */
|
|
30
|
+
public static Create<K extends keyof HTMLElementTagNameMap>(
|
|
31
|
+
tag: K,
|
|
32
|
+
class_name = '',
|
|
33
|
+
parent?: HTMLElement,
|
|
34
|
+
scope?: string,
|
|
35
|
+
attrs?: Record<string, string>): HTMLElementTagNameMap[K] {
|
|
36
|
+
|
|
37
|
+
const element = document.createElement(tag);
|
|
38
|
+
|
|
39
|
+
if (class_name) {
|
|
40
|
+
element.className = class_name;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (scope) {
|
|
44
|
+
element.setAttribute(scope, ''); // scope?
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (attrs) {
|
|
48
|
+
const keys = Object.keys(attrs);
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
element.setAttribute(key, attrs[key]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (parent) {
|
|
55
|
+
parent.appendChild(element);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return element;
|
|
27
59
|
}
|
|
28
60
|
|
|
29
|
-
|
|
30
|
-
public static
|
|
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>){
|
|
31
63
|
const element = document.createElement(tag) as E;
|
|
32
64
|
if (classes) element.setAttribute('class', classes);
|
|
33
65
|
if (scope) element.setAttribute(scope, '');
|
|
@@ -40,5 +72,6 @@ export class DOMUtilities {
|
|
|
40
72
|
if (parent) parent.appendChild(element);
|
|
41
73
|
return element;
|
|
42
74
|
}
|
|
75
|
+
*/
|
|
43
76
|
|
|
44
77
|
}
|