@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.
@@ -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 { ExternalEditorType, DependencyList, ExternalEditorCallback } from './types/external_editor';
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<HTMLButtonElement>(
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 { ExpressionUnit, UnitAddress, UnitStructuredReference, UnitRange } from 'treb-parser';
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, OverlayEditorResult } from '../editors/overlay_editor';
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 { ExternalEditorType } from './external_editor';
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?.UpdateTheme(value);
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
- private select_argument = false; // temp, WIP
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
- * 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.
279
+ * support for external editor. created on demand.
273
280
  */
274
- private external_editor: Partial<ExternalEditorType> | undefined;
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, parser, model);
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?.UpdateTheme(this.layout.scale);
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(external_editor: Partial<ExternalEditorType>|undefined) {
1668
+ public ExternalEditor(config: Partial<ExternalEditorConfig>|undefined) {
1669
+
1670
+ this.external_editor_config = config;
1663
1671
 
1664
- this.external_editor = external_editor;
1672
+ if (config) {
1665
1673
 
1666
- if (external_editor) {
1667
- if (external_editor.dependencies) {
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
- // FIXME: this is getting messy and we're doing it twice, consolidate
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.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)));
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.UpdateTheme(this.layout.scale);
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.external_editor) {
4025
- // ...
4026
- }
4027
- else if (this.select_argument) {
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.external_editor || this.active_sheet.id !== this.editing_cell.sheet_id) {
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.external_editor) {
4315
- if (this.external_editor.update) {
4316
- const result = this.external_editor.update.call(0, label);
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.external_editor)
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 OverlayEditorResult.handled:
4426
+ case 'handled':
4375
4427
  return;
4376
4428
 
4377
- case OverlayEditorResult.discard:
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 OverlayEditorResult.commit:
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<HTMLInputElement>('input', undefined, popup, undefined, {
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<HTMLDivElement>('div', classes, parent, scope);
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
- /** generic element constructor. shame we need the tag AND the type. */
30
- public static Create<E extends HTMLElement>(tag = '', classes = '', parent?: HTMLElement, scope?: string, attrs?: Record<string, string>){
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
  }