wunderbaum 0.0.6 → 0.0.8

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
@@ -23,8 +23,9 @@ import {
23
23
  ApplyCommandType,
24
24
  ChangeType,
25
25
  ColumnDefinitionList,
26
+ ExpandAllOptions,
26
27
  FilterModeType,
27
- MatcherType,
28
+ MatcherCallback,
28
29
  NavigationOptions,
29
30
  NodeStatusType,
30
31
  NodeTypeDefinitions,
@@ -710,13 +711,13 @@ export class Wunderbaum {
710
711
  /**
711
712
  * Apply a modification (or navigation) operation on the **tree or active node**.
712
713
  */
713
- applyCommand(cmd: ApplyCommandType, opts?: any): any;
714
+ applyCommand(cmd: ApplyCommandType, options?: any): any;
714
715
 
715
716
  /**
716
717
  * Apply a modification (or navigation) operation on a **node**.
717
718
  * @see {@link WunderbaumNode.applyCommand}
718
719
  */
719
- applyCommand(cmd: ApplyCommandType, node: WunderbaumNode, opts?: any): any;
720
+ applyCommand(cmd: ApplyCommandType, node: WunderbaumNode, options?: any): any;
720
721
 
721
722
  /**
722
723
  * Apply a modification or navigation operation.
@@ -737,23 +738,23 @@ export class Wunderbaum {
737
738
  applyCommand(
738
739
  cmd: ApplyCommandType,
739
740
  nodeOrOpts?: WunderbaumNode | any,
740
- opts?: any
741
+ options?: any
741
742
  ): any {
742
743
  let // clipboard,
743
744
  node,
744
745
  refNode;
745
- // opts = $.extend(
746
+ // options = $.extend(
746
747
  // { setActive: true, clipboard: CLIPBOARD },
747
- // opts_
748
+ // options_
748
749
  // );
749
750
  if (nodeOrOpts instanceof WunderbaumNode) {
750
751
  node = nodeOrOpts;
751
752
  } else {
752
753
  node = this.getActiveNode()!;
753
- util.assert(opts === undefined);
754
- opts = nodeOrOpts;
754
+ util.assert(options === undefined);
755
+ options = nodeOrOpts;
755
756
  }
756
- // clipboard = opts.clipboard;
757
+ // clipboard = options.clipboard;
757
758
 
758
759
  switch (cmd) {
759
760
  // Sorting and indentation:
@@ -973,15 +974,8 @@ export class Wunderbaum {
973
974
  }
974
975
 
975
976
  /** Recursively expand all expandable nodes (triggers lazy load id needed). */
976
- async expandAll(flag: boolean = true) {
977
- const tag = this.logTime("expandAll(" + flag + ")");
978
- try {
979
- this.enableUpdate(false);
980
- await this.root.expandAll(flag);
981
- } finally {
982
- this.enableUpdate(true);
983
- this.logTimeEnd(tag);
984
- }
977
+ async expandAll(flag: boolean = true, options?: ExpandAllOptions) {
978
+ await this.root.expandAll(flag, options);
985
979
  }
986
980
 
987
981
  /** Recursively select all nodes. */
@@ -1018,26 +1012,20 @@ export class Wunderbaum {
1018
1012
  }
1019
1013
 
1020
1014
  /**
1021
- * Find all nodes that matches condition.
1022
- *
1023
- * @param match title string to search for, or a
1024
- * callback function that returns `true` if a node is matched.
1015
+ * Find all nodes that match condition.
1025
1016
  *
1026
1017
  * @see {@link WunderbaumNode.findAll}
1027
1018
  */
1028
- findAll(match: string | MatcherType) {
1019
+ findAll(match: string | RegExp | MatcherCallback) {
1029
1020
  return this.root.findAll(match);
1030
1021
  }
1031
1022
 
1032
1023
  /**
1033
1024
  * Find first node that matches condition.
1034
1025
  *
1035
- * @param match title string to search for, or a
1036
- * callback function that returns `true` if a node is matched.
1037
1026
  * @see {@link WunderbaumNode.findFirst}
1038
- *
1039
1027
  */
1040
- findFirst(match: string | MatcherType) {
1028
+ findFirst(match: string | RegExp | MatcherCallback) {
1041
1029
  return this.root.findFirst(match);
1042
1030
  }
1043
1031
 
@@ -1049,8 +1037,8 @@ export class Wunderbaum {
1049
1037
  * @see {@link WunderbaumNode.findFirst}
1050
1038
  *
1051
1039
  */
1052
- findKey(key: string): WunderbaumNode | undefined {
1053
- return this.keyMap.get(key);
1040
+ findKey(key: string): WunderbaumNode | null {
1041
+ return this.keyMap.get(key) || null;
1054
1042
  }
1055
1043
 
1056
1044
  /**
@@ -1058,7 +1046,7 @@ export class Wunderbaum {
1058
1046
  * and wrap-around at the end.
1059
1047
  */
1060
1048
  findNextNode(
1061
- match: string | MatcherType,
1049
+ match: string | MatcherCallback,
1062
1050
  startNode?: WunderbaumNode | null
1063
1051
  ): WunderbaumNode | null {
1064
1052
  //, visibleOnly) {
@@ -1374,19 +1362,22 @@ export class Wunderbaum {
1374
1362
 
1375
1363
  /**
1376
1364
  * Make sure that this node is vertically scrolled into the viewport.
1365
+ *
1366
+ * Nodes that are above the visible area become the top row, nodes that are
1367
+ * below the viewport become the bottom row.
1377
1368
  */
1378
1369
  scrollTo(nodeOrOpts: ScrollToOptions | WunderbaumNode) {
1379
1370
  const PADDING = 2; // leave some pixels between viewport bounds
1380
1371
 
1381
1372
  let node;
1382
1373
  WunderbaumNode;
1383
- let opts: ScrollToOptions | undefined;
1374
+ let options: ScrollToOptions | undefined;
1384
1375
 
1385
1376
  if (nodeOrOpts instanceof WunderbaumNode) {
1386
1377
  node = nodeOrOpts;
1387
1378
  } else {
1388
- opts = nodeOrOpts;
1389
- node = opts.node;
1379
+ options = nodeOrOpts;
1380
+ node = options.node;
1390
1381
  }
1391
1382
  util.assert(node && node._rowIdx != null);
1392
1383
 
@@ -1398,6 +1389,7 @@ export class Wunderbaum {
1398
1389
  const vpTop = headerHeight;
1399
1390
  const vpRowTop = rowTop - scrollTop;
1400
1391
  const vpRowBottom = vpRowTop + ROW_HEIGHT;
1392
+ const topNode = options?.topNode;
1401
1393
 
1402
1394
  // this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts );
1403
1395
  let newScrollTop: number | null = null;
@@ -1408,16 +1400,20 @@ export class Wunderbaum {
1408
1400
  } else {
1409
1401
  // Node is below viewport
1410
1402
  // this.log("Below viewport");
1411
- newScrollTop = rowTop + ROW_HEIGHT - vpHeight + PADDING; // leave some pixels between vieeport bounds
1403
+ newScrollTop = rowTop + ROW_HEIGHT - vpHeight + PADDING; // leave some pixels between viewport bounds
1412
1404
  }
1413
1405
  } else {
1414
1406
  // Node is above viewport
1415
1407
  // this.log("Above viewport");
1416
- newScrollTop = rowTop - vpTop - PADDING; // leave some pixels between vieeport bounds
1408
+ newScrollTop = rowTop - vpTop - PADDING; // leave some pixels between viewport bounds
1417
1409
  }
1418
1410
  if (newScrollTop != null) {
1419
1411
  this.log(`scrollTo(${rowTop}): ${scrollTop} => ${newScrollTop}`);
1420
1412
  scrollParent.scrollTop = newScrollTop;
1413
+ if (topNode) {
1414
+ // Make sure the topNode is always visible
1415
+ this.scrollTo(topNode);
1416
+ }
1421
1417
  // this.setModified(ChangeType.vscroll);
1422
1418
  }
1423
1419
  }
@@ -1447,13 +1443,9 @@ export class Wunderbaum {
1447
1443
  newLeft = colRight - vpWidth;
1448
1444
  }
1449
1445
  // util.assert(node._rowIdx != null);
1450
- // const curLeft = this.scrollContainer.scrollLeft;
1451
- this.log(
1452
- `scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`
1453
- );
1454
- // const nodeOfs = node._rowIdx * ROW_HEIGHT;
1455
- // let newLeft;
1456
-
1446
+ // this.log(
1447
+ // `scrollToHorz(${this.activeColIdx}): ${colLeft}..${colRight}, fixedOfs=${fixedWidth}, vpWidth=${vpWidth}, curLeft=${scrollLeft} -> ${newLeft}`
1448
+ // );
1457
1449
  this.element.scrollLeft = newLeft;
1458
1450
  // this.setModified(ChangeType.vscroll);
1459
1451
  // }
@@ -1666,11 +1658,13 @@ export class Wunderbaum {
1666
1658
  }
1667
1659
  }
1668
1660
  /** Update column headers and width. */
1669
- updateColumns(opts?: any) {
1670
- opts = Object.assign({ calculateCols: true, updateRows: true }, opts);
1661
+ updateColumns(options?: any) {
1662
+ options = Object.assign({ calculateCols: true, updateRows: true }, options);
1671
1663
  const defaultMinWidth = 4;
1672
1664
  const vpWidth = this.element.clientWidth;
1673
1665
  const isGrid = this.isGrid();
1666
+ // Shorten last column width to avoid h-scrollbar
1667
+ const FIX_ADJUST_LAST_COL = 2;
1674
1668
 
1675
1669
  let totalWidth = 0;
1676
1670
  let totalWeight = 0;
@@ -1682,7 +1676,7 @@ export class Wunderbaum {
1682
1676
  this.setCellNav(false);
1683
1677
  }
1684
1678
 
1685
- if (opts.calculateCols) {
1679
+ if (options.calculateCols) {
1686
1680
  // Gather width definitions
1687
1681
  this._columnsById = {};
1688
1682
  for (let col of this.columns) {
@@ -1732,7 +1726,8 @@ export class Wunderbaum {
1732
1726
  col._ofsPx = ofsPx;
1733
1727
  ofsPx += col._widthPx!;
1734
1728
  }
1735
- totalWidth = ofsPx;
1729
+ this.columns[this.columns.length - 1]._widthPx! -= FIX_ADJUST_LAST_COL;
1730
+ totalWidth = ofsPx - FIX_ADJUST_LAST_COL;
1736
1731
  }
1737
1732
  // if (this.options.fixedCol) {
1738
1733
  // 'position: fixed' requires that the content has the correct size
@@ -1747,7 +1742,7 @@ export class Wunderbaum {
1747
1742
  // util.error("BREAK");
1748
1743
  if (modified) {
1749
1744
  this._renderHeaderMarkup();
1750
- if (opts.updateRows) {
1745
+ if (options.updateRows) {
1751
1746
  this._updateRows();
1752
1747
  }
1753
1748
  }
@@ -1815,6 +1810,9 @@ export class Wunderbaum {
1815
1810
  * @internal
1816
1811
  */
1817
1812
  protected _updateViewportImmediately() {
1813
+ // Shorten container height to avoid v-scrollbar
1814
+ const FIX_ADJUST_HEIGHT = 1;
1815
+
1818
1816
  if (this._disableUpdateCount) {
1819
1817
  this.log(
1820
1818
  `IGNORED _updateViewportImmediately() disable level: ${this._disableUpdateCount}`
@@ -1831,7 +1829,8 @@ export class Wunderbaum {
1831
1829
  // let headerHeight = this.headerElement.children[0].children[0].clientHeight;
1832
1830
  // const headerHeight = this.options.headerHeightPx;
1833
1831
  const headerHeight = this.headerElement.clientHeight; // May be 0
1834
- const wantHeight = this.element.clientHeight - headerHeight;
1832
+ const wantHeight =
1833
+ this.element.clientHeight - headerHeight - FIX_ADJUST_HEIGHT;
1835
1834
 
1836
1835
  if (Math.abs(height - wantHeight) > 1.0) {
1837
1836
  // this.log("resize", height, wantHeight);
@@ -1895,15 +1894,16 @@ export class Wunderbaum {
1895
1894
  * (including upper and lower prefetch)
1896
1895
  * -
1897
1896
  */
1898
- protected _updateRows(opts?: any): boolean {
1897
+ protected _updateRows(options?: any): boolean {
1899
1898
  // const label = this.logTime("_updateRows");
1900
1899
  // this.log("_updateRows", opts)
1901
- opts = Object.assign({ newNodesOnly: false }, opts);
1902
- const newNodesOnly = !!opts.newNodesOnly;
1900
+ options = Object.assign({ newNodesOnly: false }, options);
1901
+ const newNodesOnly = !!options.newNodesOnly;
1903
1902
 
1904
1903
  const row_height = ROW_HEIGHT;
1905
1904
  const vp_height = this.element.clientHeight;
1906
1905
  const prefetch = RENDER_MAX_PREFETCH;
1906
+ // const grace_prefetch = RENDER_MAX_PREFETCH - RENDER_MIN_PREFETCH;
1907
1907
  const ofs = this.element.scrollTop;
1908
1908
 
1909
1909
  let startIdx = Math.max(0, ofs / row_height - prefetch);
@@ -2008,15 +2008,15 @@ export class Wunderbaum {
2008
2008
  * {start: First tree node, reverse: false, includeSelf: true, includeHidden: false, wrap: false}
2009
2009
  * @returns {boolean} false if iteration was canceled
2010
2010
  */
2011
- visitRows(callback: (node: WunderbaumNode) => any, opts?: any): boolean {
2011
+ visitRows(callback: (node: WunderbaumNode) => any, options?: any): boolean {
2012
2012
  if (!this.root.hasChildren()) {
2013
2013
  return false;
2014
2014
  }
2015
- if (opts && opts.reverse) {
2016
- delete opts.reverse;
2017
- return this._visitRowsUp(callback, opts);
2015
+ if (options && options.reverse) {
2016
+ delete options.reverse;
2017
+ return this._visitRowsUp(callback, options);
2018
2018
  }
2019
- opts = opts || {};
2019
+ options = options || {};
2020
2020
  let i,
2021
2021
  nextIdx,
2022
2022
  parent,
@@ -2024,10 +2024,10 @@ export class Wunderbaum {
2024
2024
  siblings,
2025
2025
  stopNode: WunderbaumNode,
2026
2026
  siblingOfs = 0,
2027
- skipFirstNode = opts.includeSelf === false,
2028
- includeHidden = !!opts.includeHidden,
2027
+ skipFirstNode = options.includeSelf === false,
2028
+ includeHidden = !!options.includeHidden,
2029
2029
  checkFilter = !includeHidden && this.filterMode === "hide",
2030
- node: WunderbaumNode = opts.start || this.root.children![0];
2030
+ node: WunderbaumNode = options.start || this.root.children![0];
2031
2031
 
2032
2032
  parent = node.parent;
2033
2033
  while (parent) {
@@ -2086,11 +2086,11 @@ export class Wunderbaum {
2086
2086
  parent = parent.parent;
2087
2087
  siblingOfs = 1; //
2088
2088
 
2089
- if (!parent && opts.wrap) {
2089
+ if (!parent && options.wrap) {
2090
2090
  this.logDebug("visitRows(): wrap around");
2091
- util.assert(opts.start, "`wrap` option requires `start`");
2092
- stopNode = opts.start;
2093
- opts.wrap = false;
2091
+ util.assert(options.start, "`wrap` option requires `start`");
2092
+ stopNode = options.start;
2093
+ options.wrap = false;
2094
2094
  parent = this.root;
2095
2095
  siblingOfs = 0;
2096
2096
  }