@trebco/treb 27.2.1 → 27.5.3
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 +8 -8
- package/dist/treb.d.ts +48 -3
- package/package.json +1 -1
- package/treb-base-types/src/rectangle.ts +26 -21
- package/treb-embed/src/embedded-spreadsheet.ts +80 -44
- package/treb-format/src/value_parser.ts +30 -10
- package/treb-grid/package.json +1 -1
- package/treb-grid/src/index.ts +2 -1
- package/treb-grid/src/render/tile_renderer.ts +7 -1
- package/treb-grid/src/types/external_editor.ts +27 -0
- package/treb-grid/src/types/grid.ts +104 -26
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
|
|
61
61
|
import { Yield, SerializeHTML } from 'treb-utils';
|
|
62
62
|
import type { ParseResult as ParseResult2 } from 'treb-format';
|
|
63
|
-
import { NumberFormatCache, LotusDate, ValueParser, Hints, NumberFormat } from 'treb-format';
|
|
63
|
+
import { NumberFormatCache, LotusDate, ValueParser, type Hints, NumberFormat } from 'treb-format';
|
|
64
64
|
import { SelectionRenderer } from '../render/selection-renderer';
|
|
65
65
|
|
|
66
66
|
import { TabBar } from './tab_bar';
|
|
@@ -114,6 +114,8 @@ import { GridBase } from './grid_base';
|
|
|
114
114
|
import type { SetRangeOptions } from './set_range_options';
|
|
115
115
|
import type { ClipboardCellData } from './clipboard_data';
|
|
116
116
|
|
|
117
|
+
import type { ExternalEditorType } from './external_editor';
|
|
118
|
+
|
|
117
119
|
interface DoubleClickData {
|
|
118
120
|
timeout?: number;
|
|
119
121
|
address?: ICellAddress;
|
|
@@ -125,6 +127,7 @@ enum EditingState {
|
|
|
125
127
|
FormulaBar = 2,
|
|
126
128
|
}
|
|
127
129
|
|
|
130
|
+
|
|
128
131
|
export class Grid extends GridBase {
|
|
129
132
|
|
|
130
133
|
// --- public members --------------------------------------------------------
|
|
@@ -263,6 +266,13 @@ export class Grid extends GridBase {
|
|
|
263
266
|
|
|
264
267
|
private select_argument = false; // temp, WIP
|
|
265
268
|
|
|
269
|
+
/**
|
|
270
|
+
* not sure what select_argument was for (possibly outside client), but
|
|
271
|
+
* we're formalizing the concept of external selection to support outside
|
|
272
|
+
* tooling.
|
|
273
|
+
*/
|
|
274
|
+
private external_editor: Partial<ExternalEditorType> | undefined;
|
|
275
|
+
|
|
266
276
|
/**
|
|
267
277
|
* flag indicating we're resizing, or hovering over a resize.
|
|
268
278
|
* we use this so we know what to do when we see a click on the headers.
|
|
@@ -1644,6 +1654,33 @@ export class Grid extends GridBase {
|
|
|
1644
1654
|
this.RenderSelections();
|
|
1645
1655
|
}
|
|
1646
1656
|
|
|
1657
|
+
/**
|
|
1658
|
+
* set or remove external editor. this is not an accessor because I don't
|
|
1659
|
+
* want to have to use a duplicate internal field, it's clumsy and this
|
|
1660
|
+
* class isn't public-facing so it's not super important.
|
|
1661
|
+
*/
|
|
1662
|
+
public ExternalEditor(external_editor: Partial<ExternalEditorType>|undefined) {
|
|
1663
|
+
|
|
1664
|
+
this.external_editor = external_editor;
|
|
1665
|
+
|
|
1666
|
+
if (external_editor) {
|
|
1667
|
+
if (external_editor.dependencies) {
|
|
1668
|
+
|
|
1669
|
+
// FIXME: this is getting messy and we're doing it twice, consolidate
|
|
1670
|
+
|
|
1671
|
+
this.HighlightDependencies(external_editor.dependencies.filter(
|
|
1672
|
+
<T>(entry: T|undefined): entry is T => !!entry).map(reference =>
|
|
1673
|
+
IsCellAddress(reference) ? new Area(reference) : new Area(reference.start, reference.end)));
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
else {
|
|
1677
|
+
this.ClearAdditionalSelections();
|
|
1678
|
+
this.RenderSelections(true);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
|
|
1647
1684
|
/** API method */
|
|
1648
1685
|
public SelectRange(range?: Area): void {
|
|
1649
1686
|
this.Select(this.primary_selection, range);
|
|
@@ -3984,6 +4021,9 @@ export class Grid extends GridBase {
|
|
|
3984
4021
|
if (this.overlay_editor?.editing) {
|
|
3985
4022
|
// ...
|
|
3986
4023
|
}
|
|
4024
|
+
else if (this.external_editor) {
|
|
4025
|
+
// ...
|
|
4026
|
+
}
|
|
3987
4027
|
else if (this.select_argument) {
|
|
3988
4028
|
// ...
|
|
3989
4029
|
}
|
|
@@ -4237,8 +4277,6 @@ export class Grid extends GridBase {
|
|
|
4237
4277
|
// if this is a single merged block, we want to insert it as the
|
|
4238
4278
|
// root cell and not the range.
|
|
4239
4279
|
|
|
4240
|
-
// let label = selection.area.spreadsheet_label;
|
|
4241
|
-
|
|
4242
4280
|
const data = this.active_sheet.CellData(selection.area.start);
|
|
4243
4281
|
const target = new Area(data.merge_area ? data.merge_area.start : selection.target);
|
|
4244
4282
|
|
|
@@ -4246,13 +4284,12 @@ export class Grid extends GridBase {
|
|
|
4246
4284
|
|
|
4247
4285
|
if (!label) {
|
|
4248
4286
|
|
|
4249
|
-
// label = Area.CellAddressToLabel(target.start);
|
|
4250
4287
|
label = selection.area.spreadsheet_label;
|
|
4251
4288
|
if (data.merge_area && data.merge_area.Equals(selection.area)) {
|
|
4252
4289
|
label = Area.CellAddressToLabel(data.merge_area.start);
|
|
4253
4290
|
}
|
|
4254
4291
|
|
|
4255
|
-
if (this.active_sheet.id !== this.editing_cell.sheet_id) {
|
|
4292
|
+
if (this.external_editor || this.active_sheet.id !== this.editing_cell.sheet_id) {
|
|
4256
4293
|
const name = this.active_sheet.name;
|
|
4257
4294
|
|
|
4258
4295
|
if (QuotedSheetNameRegex.test(name)) {
|
|
@@ -4265,12 +4302,25 @@ export class Grid extends GridBase {
|
|
|
4265
4302
|
|
|
4266
4303
|
}
|
|
4267
4304
|
|
|
4305
|
+
// the external editor should just handle normal select events
|
|
4306
|
+
// for now, we might update that in the future.
|
|
4307
|
+
|
|
4268
4308
|
if (this.overlay_editor?.editing && this.overlay_editor.selecting) {
|
|
4269
4309
|
this.overlay_editor.InsertReference(label, 0);
|
|
4270
4310
|
}
|
|
4271
4311
|
else if (this.formula_bar && this.formula_bar.selecting) {
|
|
4272
4312
|
this.formula_bar.InsertReference(label, 0);
|
|
4273
4313
|
}
|
|
4314
|
+
else if (this.external_editor) {
|
|
4315
|
+
if (this.external_editor.update) {
|
|
4316
|
+
const result = this.external_editor.update.call(0, label);
|
|
4317
|
+
if (result && Array.isArray(result)) {
|
|
4318
|
+
this.HighlightDependencies(
|
|
4319
|
+
result.filter(<T>(entry: T|undefined): entry is T => !!entry).map(reference =>
|
|
4320
|
+
IsCellAddress(reference) ? new Area(reference) : new Area(reference.start, reference.end)));
|
|
4321
|
+
}
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4274
4324
|
else if (this.select_argument) {
|
|
4275
4325
|
this.grid_events.Publish({
|
|
4276
4326
|
type: 'alternate-selection',
|
|
@@ -4288,6 +4338,7 @@ export class Grid extends GridBase {
|
|
|
4288
4338
|
private SelectingArgument() {
|
|
4289
4339
|
return (this.overlay_editor?.editing && this.overlay_editor?.selecting)
|
|
4290
4340
|
|| (this.formula_bar && this.formula_bar.selecting)
|
|
4341
|
+
|| (this.external_editor)
|
|
4291
4342
|
|| (this.select_argument);
|
|
4292
4343
|
}
|
|
4293
4344
|
|
|
@@ -4911,6 +4962,12 @@ export class Grid extends GridBase {
|
|
|
4911
4962
|
if (!cell.style || !cell.style.number_format || NumberFormatCache.Equals(cell.style.number_format, 'General')) {
|
|
4912
4963
|
const func = formula_parse_result.expression.name.toLowerCase();
|
|
4913
4964
|
let number_format: string|undefined;
|
|
4965
|
+
|
|
4966
|
+
// FIXME: these should be defined on the functions themselves,
|
|
4967
|
+
// so we don't have to maintain a list. that also implies we
|
|
4968
|
+
// need the list of functions, which we don't have atm? maybe
|
|
4969
|
+
// through the AC instance? (...)
|
|
4970
|
+
|
|
4914
4971
|
switch (func) {
|
|
4915
4972
|
case 'today':
|
|
4916
4973
|
number_format = 'Short Date';
|
|
@@ -4928,25 +4985,51 @@ export class Grid extends GridBase {
|
|
|
4928
4985
|
}
|
|
4929
4986
|
}
|
|
4930
4987
|
if (formula_parse_result.dependencies) {
|
|
4988
|
+
|
|
4989
|
+
// this was set up to just use the first format we found.
|
|
4990
|
+
// updating to change priority -- if the first one is a
|
|
4991
|
+
// percentage formula, look for another one before using
|
|
4992
|
+
// the percentage. this is almost always what you want.
|
|
4993
|
+
|
|
4994
|
+
let found_number_format: string|undefined = undefined;
|
|
4995
|
+
|
|
4931
4996
|
const list = formula_parse_result.dependencies;
|
|
4932
4997
|
for (const key of Object.keys(list.addresses)) {
|
|
4933
4998
|
const address = list.addresses[key];
|
|
4934
4999
|
if (this.active_sheet.HasCellStyle({ ...address })) {
|
|
5000
|
+
|
|
5001
|
+
// FIXME: this should not be active_sheet
|
|
5002
|
+
|
|
4935
5003
|
const test = this.active_sheet.CellData({ ...address });
|
|
4936
5004
|
if (test.style && test.style.number_format) {
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
5005
|
+
if (!found_number_format || /%/.test(found_number_format)) {
|
|
5006
|
+
|
|
5007
|
+
// convert to a string format if it's symbolic. that
|
|
5008
|
+
// is purely so we can check for a %. FIXME: I don't
|
|
5009
|
+
// like the name of this method (Translate)
|
|
5010
|
+
|
|
5011
|
+
found_number_format = NumberFormatCache.Translate(test.style.number_format);
|
|
5012
|
+
if (!/%/.test(found_number_format)) {
|
|
5013
|
+
break;
|
|
5014
|
+
}
|
|
5015
|
+
|
|
5016
|
+
}
|
|
4946
5017
|
}
|
|
4947
|
-
break;
|
|
4948
5018
|
}
|
|
4949
5019
|
}
|
|
5020
|
+
|
|
5021
|
+
if (found_number_format) {
|
|
5022
|
+
|
|
5023
|
+
const style: CellStyle = {
|
|
5024
|
+
number_format: found_number_format,
|
|
5025
|
+
};
|
|
5026
|
+
|
|
5027
|
+
commands.push({
|
|
5028
|
+
key: CommandKey.UpdateStyle,
|
|
5029
|
+
area: array ? selection.area : target, style, delta: true
|
|
5030
|
+
});
|
|
5031
|
+
}
|
|
5032
|
+
|
|
4950
5033
|
}
|
|
4951
5034
|
}
|
|
4952
5035
|
}
|
|
@@ -4988,7 +5071,7 @@ export class Grid extends GridBase {
|
|
|
4988
5071
|
// const text = value.toString();
|
|
4989
5072
|
|
|
4990
5073
|
let number_format = '';
|
|
4991
|
-
const hints = parse_result.hints ||
|
|
5074
|
+
const hints: Hints = parse_result.hints || {};
|
|
4992
5075
|
|
|
4993
5076
|
// be stricter about number format. don't implicitly /change/
|
|
4994
5077
|
// the number format (you can /set/, but don't /change/).
|
|
@@ -4999,24 +5082,19 @@ export class Grid extends GridBase {
|
|
|
4999
5082
|
|
|
5000
5083
|
if (!cell.style || !cell.style.number_format || NumberFormatCache.Equals(cell.style.number_format, 'General')) {
|
|
5001
5084
|
|
|
5002
|
-
|
|
5003
|
-
if (hints & Hints.Date) {
|
|
5085
|
+
if (hints.Date) {
|
|
5004
5086
|
number_format = 'Short Date';
|
|
5005
5087
|
}
|
|
5006
|
-
|
|
5007
|
-
else if (hints & Hints.Exponential) {
|
|
5088
|
+
else if (hints.Exponential) {
|
|
5008
5089
|
number_format = 'Exponential';
|
|
5009
5090
|
}
|
|
5010
|
-
|
|
5011
|
-
else if (hints & Hints.Percent) {
|
|
5091
|
+
else if (hints.Percent) {
|
|
5012
5092
|
number_format = 'Percent';
|
|
5013
5093
|
}
|
|
5014
|
-
|
|
5015
|
-
else if (hints & Hints.Currency) {
|
|
5094
|
+
else if (hints.Currency) {
|
|
5016
5095
|
number_format = 'Currency';
|
|
5017
5096
|
}
|
|
5018
|
-
|
|
5019
|
-
else if ((hints & Hints.Grouping) || (hints & Hints.Parens)) {
|
|
5097
|
+
else if (hints.Grouping || hints.Parens) {
|
|
5020
5098
|
number_format = 'Accounting';
|
|
5021
5099
|
}
|
|
5022
5100
|
|