wunderbaum 0.0.9 → 0.1.0

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/src/wunderbaum.ts CHANGED
@@ -26,15 +26,20 @@ import {
26
26
  ExpandAllOptions,
27
27
  FilterModeType,
28
28
  MatcherCallback,
29
- NavigationOptions,
29
+ NavModeEnum,
30
30
  NodeStatusType,
31
31
  NodeStringCallback,
32
- NodeTypeDefinitions,
32
+ NodeTypeDefinitionMap,
33
33
  ScrollToOptions,
34
34
  SetActiveOptions,
35
35
  SetModifiedOptions,
36
36
  SetStatusOptions,
37
- TargetType as NodeRegion,
37
+ NodeRegion,
38
+ WbEventInfo,
39
+ ApplyCommandOptions,
40
+ AddChildrenOptions,
41
+ UpdateColumnsOptions,
42
+ VisitRowsOptions,
38
43
  } from "./types";
39
44
  import {
40
45
  DEFAULT_DEBUGLEVEL,
@@ -79,8 +84,8 @@ export class Wunderbaum {
79
84
  public readonly element: HTMLDivElement;
80
85
  /** The `div.wb-header` element if any. */
81
86
  public readonly headerElement: HTMLDivElement;
82
- /** The `div.wb-scroll-container` element that contains the `nodeListElement`. */
83
- public readonly scrollContainerElement: HTMLDivElement;
87
+ /** The `div.wb-list-container` element that contains the `nodeListElement`. */
88
+ public readonly listContainerElement: HTMLDivElement;
84
89
  /** The `div.wb-node-list` element that contains all visible div.wb-row child elements. */
85
90
  public readonly nodeListElement: HTMLDivElement;
86
91
  /** Contains additional data that was sent as response to an Ajax source load request. */
@@ -104,7 +109,7 @@ export class Wunderbaum {
104
109
  public focusNode: WunderbaumNode | null = null;
105
110
 
106
111
  /** Shared properties, referenced by `node.type`. */
107
- public types: NodeTypeDefinitions = {};
112
+ public types: NodeTypeDefinitionMap = {};
108
113
  /** List of column definitions. */
109
114
  public columns: ColumnDefinitionList = []; // any[] = [];
110
115
 
@@ -160,7 +165,7 @@ export class Wunderbaum {
160
165
  skeleton: false,
161
166
  connectTopBreadcrumb: null, // HTMLElement that receives the top nodes breadcrumb
162
167
  // --- KeyNav ---
163
- navigationModeOption: null, // NavigationOptions.startRow,
168
+ navigationModeOption: null, // NavModeEnum.startRow,
164
169
  quicksearch: true,
165
170
  // --- Events ---
166
171
  change: util.noop,
@@ -293,13 +298,13 @@ export class Wunderbaum {
293
298
 
294
299
  //
295
300
  this.element.innerHTML += `
296
- <div class="wb-scroll-container">
301
+ <div class="wb-list-container">
297
302
  <div class="wb-node-list"></div>
298
303
  </div>`;
299
- this.scrollContainerElement = this.element.querySelector(
300
- "div.wb-scroll-container"
304
+ this.listContainerElement = this.element.querySelector(
305
+ "div.wb-list-container"
301
306
  ) as HTMLDivElement;
302
- this.nodeListElement = this.scrollContainerElement.querySelector(
307
+ this.nodeListElement = this.listContainerElement.querySelector(
303
308
  "div.wb-node-list"
304
309
  ) as HTMLDivElement;
305
310
  this.headerElement = this.element.querySelector(
@@ -328,9 +333,9 @@ export class Wunderbaum {
328
333
  // The source may have defined columns, so we may adjust the nav mode
329
334
  if (opts.navigationModeOption == null) {
330
335
  if (this.isGrid()) {
331
- this.setNavigationOption(NavigationOptions.cell);
336
+ this.setNavigationOption(NavModeEnum.cell);
332
337
  } else {
333
- this.setNavigationOption(NavigationOptions.row);
338
+ this.setNavigationOption(NavModeEnum.row);
334
339
  }
335
340
  } else {
336
341
  this.setNavigationOption(opts.navigationModeOption);
@@ -354,13 +359,9 @@ export class Wunderbaum {
354
359
 
355
360
  // --- Bind listeners
356
361
  this.element.addEventListener("scroll", (e: Event) => {
357
- // this.log("scroll", e);
362
+ // this.log(`scroll, scrollTop:${e.target.scrollTop}`, e);
358
363
  this.setModified(ChangeType.vscroll);
359
364
  });
360
- // this.scrollContainerElement.addEventListener("scroll", (e: Event) => {
361
- // this.log("scroll", e)
362
- // this.setModified(ChangeType.vscroll);
363
- // });
364
365
 
365
366
  this.resizeObserver = new ResizeObserver((entries) => {
366
367
  this.setModified(ChangeType.vscroll);
@@ -373,16 +374,6 @@ export class Wunderbaum {
373
374
  const node = info.node;
374
375
  // this.log("click", info, e);
375
376
 
376
- // if( (e.target as HTMLElement).matches("input[type=checkbox]")){
377
- // // Click on an embedded checkbox triggers a change event.
378
- // // We return here, before the `setActive()` performs a render
379
- // this.log("click - cb", info, e);
380
- // // e.preventDefault()
381
- // setTimeout(()=>{
382
- // // (e.target as HTMLElement).click()
383
- // }, 50)
384
- // // return
385
- // }
386
377
  if (
387
378
  this._callEvent("click", { event: e, node: node, info: info }) === false
388
379
  ) {
@@ -417,6 +408,22 @@ export class Wunderbaum {
417
408
  this.lastClickTime = Date.now();
418
409
  });
419
410
 
411
+ util.onEvent(this.nodeListElement, "dblclick", "div.wb-row", (e) => {
412
+ const info = Wunderbaum.getEventInfo(e);
413
+ const node = info.node;
414
+ // this.log("dblclick", info, e);
415
+
416
+ if (
417
+ this._callEvent("dblclick", { event: e, node: node, info: info }) ===
418
+ false
419
+ ) {
420
+ return false;
421
+ }
422
+ if (node && info.colIdx === 0 && node.isExpandable()) {
423
+ node.setExpanded(!node.isExpanded());
424
+ }
425
+ });
426
+
420
427
  util.onEvent(this.element, "keydown", (e) => {
421
428
  const info = Wunderbaum.getEventInfo(e);
422
429
  const eventName = util.eventToString(e);
@@ -724,20 +731,24 @@ export class Wunderbaum {
724
731
  *
725
732
  * @see {@link WunderbaumNode.addChildren}
726
733
  */
727
- addChildren(nodeData: any, options?: any): WunderbaumNode {
734
+ addChildren(nodeData: any, options?: AddChildrenOptions): WunderbaumNode {
728
735
  return this.root.addChildren(nodeData, options);
729
736
  }
730
737
 
731
738
  /**
732
739
  * Apply a modification (or navigation) operation on the **tree or active node**.
733
740
  */
734
- applyCommand(cmd: ApplyCommandType, options?: any): any;
741
+ applyCommand(cmd: ApplyCommandType, options?: ApplyCommandOptions): any;
735
742
 
736
743
  /**
737
744
  * Apply a modification (or navigation) operation on a **node**.
738
745
  * @see {@link WunderbaumNode.applyCommand}
739
746
  */
740
- applyCommand(cmd: ApplyCommandType, node: WunderbaumNode, options?: any): any;
747
+ applyCommand(
748
+ cmd: ApplyCommandType,
749
+ node: WunderbaumNode,
750
+ options?: ApplyCommandOptions
751
+ ): any;
741
752
 
742
753
  /**
743
754
  * Apply a modification or navigation operation.
@@ -758,7 +769,7 @@ export class Wunderbaum {
758
769
  applyCommand(
759
770
  cmd: ApplyCommandType,
760
771
  nodeOrOpts?: WunderbaumNode | any,
761
- options?: any
772
+ options?: ApplyCommandOptions
762
773
  ): any {
763
774
  let // clipboard,
764
775
  node,
@@ -1112,7 +1123,7 @@ export class Wunderbaum {
1112
1123
  findRelatedNode(node: WunderbaumNode, where: string, includeHidden = false) {
1113
1124
  let res = null;
1114
1125
  const pageSize = Math.floor(
1115
- this.scrollContainerElement.clientHeight / ROW_HEIGHT
1126
+ this.listContainerElement.clientHeight / ROW_HEIGHT
1116
1127
  );
1117
1128
 
1118
1129
  switch (where) {
@@ -1270,14 +1281,14 @@ export class Wunderbaum {
1270
1281
  * @returns {object} Return a {node: WunderbaumNode, region: TYPE} object
1271
1282
  * TYPE: 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined
1272
1283
  */
1273
- static getEventInfo(event: Event) {
1284
+ static getEventInfo(event: Event): WbEventInfo {
1274
1285
  let target = <Element>event.target,
1275
1286
  cl = target.classList,
1276
1287
  parentCol = target.closest("span.wb-col") as HTMLSpanElement,
1277
1288
  node = Wunderbaum.getNode(target),
1278
1289
  tree = node ? node.tree : Wunderbaum.getTree(event),
1279
- res = {
1280
- tree: tree,
1290
+ res: WbEventInfo = {
1291
+ tree: tree!,
1281
1292
  node: node,
1282
1293
  region: NodeRegion.unknown,
1283
1294
  colDef: undefined,
@@ -1324,15 +1335,6 @@ export class Wunderbaum {
1324
1335
  return res;
1325
1336
  }
1326
1337
 
1327
- // /** Return a string describing the affected node region for a mouse event.
1328
- // *
1329
- // * @param {Event} event Mouse event, e.g. click, mousemove, ...
1330
- // * @returns {string} 'title' | 'prefix' | 'expander' | 'checkbox' | 'icon' | undefined
1331
- // */
1332
- // getEventNodeRegion(event: Event) {
1333
- // return this.getEventInfo(event).region;
1334
- // }
1335
-
1336
1338
  /**
1337
1339
  * Return readable string representation for this instance.
1338
1340
  * @internal
@@ -1445,7 +1447,8 @@ export class Wunderbaum {
1445
1447
  const vpRowBottom = vpRowTop + ROW_HEIGHT;
1446
1448
  const topNode = options?.topNode;
1447
1449
 
1448
- // this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts );
1450
+ // this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts , options);
1451
+
1449
1452
  let newScrollTop: number | null = null;
1450
1453
  if (vpRowTop >= vpTop) {
1451
1454
  if (vpRowBottom <= vpHeight) {
@@ -1481,9 +1484,6 @@ export class Wunderbaum {
1481
1484
  const fixedWidth = this.columns[0]._widthPx!;
1482
1485
  const vpWidth = this.element.clientWidth;
1483
1486
  const scrollLeft = this.element.scrollLeft;
1484
- // if (scrollLeft <= 0) {
1485
- // return; // Not scrolled horizontally: Nothing to do
1486
- // }
1487
1487
  const colElem = this.getActiveColElem()!;
1488
1488
  const colLeft = Number.parseInt(colElem?.style.left, 10);
1489
1489
  const colRight = colLeft + Number.parseInt(colElem?.style.width, 10);
@@ -1496,13 +1496,13 @@ export class Wunderbaum {
1496
1496
  // The current column is scrolled outside the right side
1497
1497
  newLeft = colRight - vpWidth;
1498
1498
  }
1499
+ newLeft = Math.max(0, newLeft);
1499
1500
  // util.assert(node._rowIdx != null);
1500
- // this.log(
1501
- // `scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`
1502
- // );
1501
+ this.log(
1502
+ `scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`
1503
+ );
1503
1504
  this.element.scrollLeft = newLeft;
1504
1505
  // this.setModified(ChangeType.vscroll);
1505
- // }
1506
1506
  }
1507
1507
  /**
1508
1508
  * Set column #colIdx to 'active'.
@@ -1555,12 +1555,16 @@ export class Wunderbaum {
1555
1555
  }
1556
1556
 
1557
1557
  /** Schedule an update request to reflect a tree change. */
1558
- setModified(change: ChangeType, options?: any): void;
1558
+ setModified(change: ChangeType, options?: SetModifiedOptions): void;
1559
1559
 
1560
1560
  /** Schedule an update request to reflect a single node modification.
1561
1561
  * @see {@link WunderbaumNode.setModified}
1562
1562
  */
1563
- setModified(change: ChangeType, node: WunderbaumNode, options?: any): void;
1563
+ setModified(
1564
+ change: ChangeType,
1565
+ node: WunderbaumNode,
1566
+ options?: SetModifiedOptions
1567
+ ): void;
1564
1568
 
1565
1569
  setModified(
1566
1570
  change: ChangeType,
@@ -1662,26 +1666,26 @@ export class Wunderbaum {
1662
1666
  }
1663
1667
 
1664
1668
  /** Set the tree's navigation mode option. */
1665
- setNavigationOption(mode: NavigationOptions, reset = false) {
1666
- if (!this.isGrid() && mode !== NavigationOptions.row) {
1669
+ setNavigationOption(mode: NavModeEnum, reset = false) {
1670
+ if (!this.isGrid() && mode !== NavModeEnum.row) {
1667
1671
  this.logWarn("Plain trees only support row navigation mode.");
1668
1672
  return;
1669
1673
  }
1670
1674
  this.options.navigationModeOption = mode;
1671
1675
 
1672
1676
  switch (mode) {
1673
- case NavigationOptions.cell:
1677
+ case NavModeEnum.cell:
1674
1678
  this.setCellNav(true);
1675
1679
  break;
1676
- case NavigationOptions.row:
1680
+ case NavModeEnum.row:
1677
1681
  this.setCellNav(false);
1678
1682
  break;
1679
- case NavigationOptions.startCell:
1683
+ case NavModeEnum.startCell:
1680
1684
  if (reset) {
1681
1685
  this.setCellNav(true);
1682
1686
  }
1683
1687
  break;
1684
- case NavigationOptions.startRow:
1688
+ case NavModeEnum.startRow:
1685
1689
  if (reset) {
1686
1690
  this.setCellNav(false);
1687
1691
  }
@@ -1714,7 +1718,7 @@ export class Wunderbaum {
1714
1718
  }
1715
1719
  }
1716
1720
  /** Update column headers and width. */
1717
- updateColumns(options?: any) {
1721
+ updateColumns(options?: UpdateColumnsOptions) {
1718
1722
  options = Object.assign({ calculateCols: true, updateRows: true }, options);
1719
1723
  const defaultMinWidth = 4;
1720
1724
  const vpWidth = this.element.clientWidth;
@@ -1789,7 +1793,7 @@ export class Wunderbaum {
1789
1793
  // 'position: fixed' requires that the content has the correct size
1790
1794
  const tw = `${totalWidth}px`;
1791
1795
  this.headerElement.style.width = tw;
1792
- this.scrollContainerElement!.style.width = tw;
1796
+ this.listContainerElement!.style.width = tw;
1793
1797
  // }
1794
1798
 
1795
1799
  // Every column has now a calculated `_ofsPx` and `_widthPx`
@@ -1837,6 +1841,9 @@ export class Wunderbaum {
1837
1841
  resizer = '<span class="wb-col-resizer"></span>';
1838
1842
  }
1839
1843
  colElem.innerHTML = `<span class="wb-col-title"${tooltip}>${title}</span>${resizer}`;
1844
+ if (this.isCellNav()) {
1845
+ colElem.classList.toggle("wb-active", i === this.activeColIdx);
1846
+ }
1840
1847
  }
1841
1848
  }
1842
1849
 
@@ -1879,7 +1886,7 @@ export class Wunderbaum {
1879
1886
  this.changeRedrawRequestPending = false;
1880
1887
  this.changeScrollRequestPending = false;
1881
1888
 
1882
- let height = this.scrollContainerElement.clientHeight;
1889
+ let height = this.listContainerElement.clientHeight;
1883
1890
  // We cannot get the height for absolute positioned parent, so look at first col
1884
1891
  // let headerHeight = this.headerElement.clientHeight
1885
1892
  // let headerHeight = this.headerElement.children[0].children[0].clientHeight;
@@ -1890,7 +1897,7 @@ export class Wunderbaum {
1890
1897
 
1891
1898
  if (Math.abs(height - wantHeight) > 1.0) {
1892
1899
  // this.log("resize", height, wantHeight);
1893
- this.scrollContainerElement.style.height = wantHeight + "px";
1900
+ this.listContainerElement.style.height = wantHeight + "px";
1894
1901
  height = wantHeight;
1895
1902
  }
1896
1903
  // console.profile(`_updateViewportImmediately()`)
@@ -2028,7 +2035,7 @@ export class Wunderbaum {
2028
2035
  // Resize tree container
2029
2036
  this.nodeListElement.style.height = `${top}px`;
2030
2037
  // this.log(
2031
- // `render(scrollOfs:${ofs}, ${startIdx}..${endIdx})`,
2038
+ // `_updateRows(scrollOfs:${ofs}, ${startIdx}..${endIdx})`,
2032
2039
  // this.nodeListElement.style.height
2033
2040
  // );
2034
2041
  // this.logTimeEnd(label);
@@ -2065,7 +2072,10 @@ export class Wunderbaum {
2065
2072
  * {start: First tree node, reverse: false, includeSelf: true, includeHidden: false, wrap: false}
2066
2073
  * @returns {boolean} false if iteration was canceled
2067
2074
  */
2068
- visitRows(callback: (node: WunderbaumNode) => any, options?: any): boolean {
2075
+ visitRows(
2076
+ callback: (node: WunderbaumNode) => any,
2077
+ options?: VisitRowsOptions
2078
+ ): boolean {
2069
2079
  if (!this.root.hasChildren()) {
2070
2080
  return false;
2071
2081
  }
@@ -2146,7 +2156,7 @@ export class Wunderbaum {
2146
2156
  if (!parent && options.wrap) {
2147
2157
  this.logDebug("visitRows(): wrap around");
2148
2158
  util.assert(options.start, "`wrap` option requires `start`");
2149
- stopNode = options.start;
2159
+ stopNode = options.start!;
2150
2160
  options.wrap = false;
2151
2161
  parent = this.root;
2152
2162
  siblingOfs = 0;
@@ -2161,22 +2171,22 @@ export class Wunderbaum {
2161
2171
  */
2162
2172
  protected _visitRowsUp(
2163
2173
  callback: (node: WunderbaumNode) => any,
2164
- opts: any
2174
+ options: VisitRowsOptions
2165
2175
  ): boolean {
2166
2176
  let children,
2167
2177
  idx,
2168
2178
  parent,
2169
- includeHidden = !!opts.includeHidden,
2170
- node = opts.start || this.root.children![0];
2179
+ includeHidden = !!options.includeHidden,
2180
+ node = options.start || this.root.children![0];
2171
2181
 
2172
- if (opts.includeSelf !== false) {
2182
+ if (options.includeSelf !== false) {
2173
2183
  if (callback(node) === false) {
2174
2184
  return false;
2175
2185
  }
2176
2186
  }
2177
2187
  while (true) {
2178
2188
  parent = node.parent;
2179
- children = parent.children;
2189
+ children = parent.children!;
2180
2190
 
2181
2191
  if (children[0] === node) {
2182
2192
  // If this is already the first sibling, goto parent