wunderbaum 0.11.0 → 0.11.1
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/README.md +4 -5
- package/dist/wunderbaum.css +10 -10
- package/dist/wunderbaum.d.ts +16 -7
- package/dist/wunderbaum.esm.js +675 -611
- package/dist/wunderbaum.esm.min.js +24 -24
- package/dist/wunderbaum.esm.min.js.map +1 -1
- package/dist/wunderbaum.umd.js +675 -611
- package/dist/wunderbaum.umd.min.js +30 -30
- package/dist/wunderbaum.umd.min.js.map +1 -1
- package/package.json +7 -5
- package/src/common.ts +1 -1
- package/src/types.ts +4 -4
- package/src/util.ts +12 -0
- package/src/wb_ext_dnd.ts +9 -4
- package/src/wb_ext_edit.ts +4 -0
- package/src/wb_node.ts +48 -12
- package/src/wunderbaum.ts +32 -18
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wunderbaum",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"title": "A treegrid control.",
|
|
5
5
|
"description": "JavaScript tree/grid/treegrid control.",
|
|
6
6
|
"homepage": "https://github.com/mar10/wunderbaum",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"eslint-plugin-prettier": "^4.2.1",
|
|
61
61
|
"grunt": "^1.6.1",
|
|
62
62
|
"grunt-contrib-connect": "^3.0.0",
|
|
63
|
-
"grunt-contrib-qunit": "^
|
|
63
|
+
"grunt-contrib-qunit": "^10.1.1",
|
|
64
64
|
"grunt-contrib-watch": "^1.1.0",
|
|
65
65
|
"grunt-exec": "^3.0.0",
|
|
66
66
|
"grunt-yabs": "^1.3.0",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"postcss-url": "^10.1.3",
|
|
71
71
|
"prettier": "^2.8.8",
|
|
72
72
|
"pretty-quick": "^3.1.3",
|
|
73
|
-
"puppeteer": "^
|
|
73
|
+
"puppeteer": "^23.10.0",
|
|
74
74
|
"qunit": "^2.19.4",
|
|
75
75
|
"rollup": "^3.23.0",
|
|
76
76
|
"rollup-plugin-scss": "^4.0.0",
|
|
@@ -80,7 +80,8 @@
|
|
|
80
80
|
"ts-node": "^10.9.1",
|
|
81
81
|
"tslib": "^2.5.2",
|
|
82
82
|
"typedoc": "^0.25.2",
|
|
83
|
-
"typescript": "^5.2.2"
|
|
83
|
+
"typescript": "^5.2.2",
|
|
84
|
+
"yarn-audit-fix": "^10.1.1"
|
|
84
85
|
},
|
|
85
86
|
"nodemonConfig": {
|
|
86
87
|
"watch": [
|
|
@@ -131,5 +132,6 @@
|
|
|
131
132
|
"wunderbaum.umd.min.js.map"
|
|
132
133
|
]
|
|
133
134
|
}
|
|
134
|
-
]
|
|
135
|
+
],
|
|
136
|
+
"packageManager": "yarn@4.4.1+sha512.f825273d0689cc9ead3259c14998037662f1dcd06912637b21a450e8da7cfeb4b1965bbee73d16927baa1201054126bc385c6f43ff4aa705c8631d26e12460f1"
|
|
135
137
|
}
|
package/src/common.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const DEFAULT_DEBUGLEVEL = 4; // Replaced by rollup script
|
|
|
12
12
|
/**
|
|
13
13
|
* Fixed height of a row in pixel. Must match the SCSS variable `$row-outer-height`.
|
|
14
14
|
*/
|
|
15
|
-
export const
|
|
15
|
+
export const DEFAULT_ROW_HEIGHT = 22;
|
|
16
16
|
/**
|
|
17
17
|
* Fixed width of node icons in pixel. Must match the SCSS variable `$icon-outer-width`.
|
|
18
18
|
*/
|
package/src/types.ts
CHANGED
|
@@ -119,7 +119,7 @@ export interface WbNodeData {
|
|
|
119
119
|
colspan?: boolean;
|
|
120
120
|
expanded?: boolean;
|
|
121
121
|
icon?: IconOption;
|
|
122
|
-
iconTooltip?:
|
|
122
|
+
iconTooltip?: TooltipOption;
|
|
123
123
|
key?: string;
|
|
124
124
|
lazy?: boolean;
|
|
125
125
|
/** Make child nodes single-select radio buttons. */
|
|
@@ -128,7 +128,7 @@ export interface WbNodeData {
|
|
|
128
128
|
selected?: boolean;
|
|
129
129
|
statusNodeType?: NodeStatusType;
|
|
130
130
|
title: string;
|
|
131
|
-
tooltip?:
|
|
131
|
+
tooltip?: TooltipOption;
|
|
132
132
|
type?: string;
|
|
133
133
|
unselectable?: boolean;
|
|
134
134
|
/** @internal */
|
|
@@ -335,8 +335,8 @@ export interface NodeTypeDefinition {
|
|
|
335
335
|
colspan?: boolean;
|
|
336
336
|
/** Default icon for matching nodes. */
|
|
337
337
|
icon?: IconOption;
|
|
338
|
-
/** Default icon for matching nodes. */
|
|
339
|
-
iconTooltip?:
|
|
338
|
+
/** Default icon tooltip for matching nodes. */
|
|
339
|
+
iconTooltip?: TooltipOption;
|
|
340
340
|
// and more
|
|
341
341
|
[key: string]: unknown;
|
|
342
342
|
}
|
package/src/util.ts
CHANGED
|
@@ -814,6 +814,18 @@ export function toBool(
|
|
|
814
814
|
throw new Error("No default boolean value provided");
|
|
815
815
|
}
|
|
816
816
|
|
|
817
|
+
/**
|
|
818
|
+
* Return `val` unless `val` is a number in which case we convert to boolean.
|
|
819
|
+
* This is useful when a boolean value is stored as a 0/1 (e.g. in JSON) and
|
|
820
|
+
* we still want to maintain string values. null and undefined are returned as
|
|
821
|
+
* is. E.g. `checkbox` may be boolean or 'radio'.
|
|
822
|
+
*/
|
|
823
|
+
export function intToBool(
|
|
824
|
+
val: boolean | number | string | undefined
|
|
825
|
+
): boolean | string | undefined {
|
|
826
|
+
return typeof val === "number" ? !!val : val;
|
|
827
|
+
}
|
|
828
|
+
|
|
817
829
|
// /** Check if a string is contained in an Array or Set. */
|
|
818
830
|
// export function isAnyOf(s: string, items: Array<string>|Set<string>): boolean {
|
|
819
831
|
// return Array.prototype.includes.call(items, s)
|
package/src/wb_ext_dnd.ts
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
DropRegionType,
|
|
15
15
|
DropRegionTypeSet,
|
|
16
16
|
} from "./types";
|
|
17
|
-
import { ROW_HEIGHT } from "./common";
|
|
18
17
|
import { DebouncedFunction, throttle } from "./debounce";
|
|
19
18
|
|
|
20
19
|
const nodeMimeType = "application/x-wunderbaum-node";
|
|
@@ -135,21 +134,22 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
|
|
|
135
134
|
e: DragEvent,
|
|
136
135
|
allowed: DropRegionTypeSet | null
|
|
137
136
|
): DropRegionType | false {
|
|
137
|
+
const rowHeight = this.tree.options.rowHeightPx!;
|
|
138
138
|
const dy = e.offsetY;
|
|
139
139
|
|
|
140
140
|
if (!allowed) {
|
|
141
141
|
return false;
|
|
142
142
|
} else if (allowed.size === 3) {
|
|
143
|
-
return dy < 0.25 *
|
|
143
|
+
return dy < 0.25 * rowHeight
|
|
144
144
|
? "before"
|
|
145
|
-
: dy > 0.75 *
|
|
145
|
+
: dy > 0.75 * rowHeight
|
|
146
146
|
? "after"
|
|
147
147
|
: "over";
|
|
148
148
|
} else if (allowed.size === 1 && allowed.has("over")) {
|
|
149
149
|
return "over";
|
|
150
150
|
} else {
|
|
151
151
|
// Only 'before' and 'after':
|
|
152
|
-
return dy >
|
|
152
|
+
return dy > rowHeight / 2 ? "after" : "before";
|
|
153
153
|
}
|
|
154
154
|
// return "over";
|
|
155
155
|
}
|
|
@@ -452,7 +452,12 @@ export class DndExtension extends WunderbaumExtension<DndOptionsType> {
|
|
|
452
452
|
}
|
|
453
453
|
this.lastAllowedDropRegions = regionSet;
|
|
454
454
|
this.lastDropEffect = dt.dropEffect;
|
|
455
|
+
|
|
456
|
+
const region = this._calcDropRegion(e, this.lastAllowedDropRegions);
|
|
455
457
|
targetNode.setClass("wb-drop-target");
|
|
458
|
+
targetNode.setClass("wb-drop-over", region === "over");
|
|
459
|
+
targetNode.setClass("wb-drop-before", region === "before");
|
|
460
|
+
targetNode.setClass("wb-drop-after", region === "after");
|
|
456
461
|
|
|
457
462
|
e.preventDefault(); // Allow drop (Drop operation is denied by default)
|
|
458
463
|
return false;
|
package/src/wb_ext_edit.ts
CHANGED
|
@@ -213,6 +213,10 @@ export class EditExtension extends WunderbaumExtension<EditOptionsType> {
|
|
|
213
213
|
if (!node) {
|
|
214
214
|
return;
|
|
215
215
|
}
|
|
216
|
+
if (node.isStatusNode()) {
|
|
217
|
+
node.logWarn("Cannot edit status node.");
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
216
220
|
this.tree.logDebug(`startEditTitle(node=${node})`);
|
|
217
221
|
let inputHtml = node._callEvent("edit.beforeEdit");
|
|
218
222
|
if (inputHtml === false) {
|
package/src/wb_node.ts
CHANGED
|
@@ -50,7 +50,6 @@ import {
|
|
|
50
50
|
makeNodeTitleMatcher,
|
|
51
51
|
nodeTitleSorter,
|
|
52
52
|
RESERVED_TREE_SOURCE_KEYS,
|
|
53
|
-
ROW_HEIGHT,
|
|
54
53
|
TEST_IMG,
|
|
55
54
|
TITLE_SPAN_PAD_Y,
|
|
56
55
|
} from "./common";
|
|
@@ -83,6 +82,21 @@ const NODE_DICT_PROPS = new Set<string>(NODE_PROPS);
|
|
|
83
82
|
NODE_DICT_PROPS.delete("_partsel");
|
|
84
83
|
NODE_DICT_PROPS.delete("unselectable");
|
|
85
84
|
|
|
85
|
+
// /** Node properties that are of type bool (or boolean & string).
|
|
86
|
+
// * When parsing, we accept 0 for false and 1 for true for better JSON compression.
|
|
87
|
+
// */
|
|
88
|
+
// export const NODE_BOOL_PROPS: Set<string> = new Set([
|
|
89
|
+
// "checkbox",
|
|
90
|
+
// "colspan",
|
|
91
|
+
// "expanded",
|
|
92
|
+
// "icon",
|
|
93
|
+
// "iconTooltip",
|
|
94
|
+
// "radiogroup",
|
|
95
|
+
// "selected",
|
|
96
|
+
// "tooltip",
|
|
97
|
+
// "unselectable",
|
|
98
|
+
// ]);
|
|
99
|
+
|
|
86
100
|
/**
|
|
87
101
|
* A single tree node.
|
|
88
102
|
*
|
|
@@ -141,7 +155,9 @@ export class WunderbaumNode {
|
|
|
141
155
|
*/
|
|
142
156
|
public type?: string;
|
|
143
157
|
/** Tooltip definition (`true`: use node's title). */
|
|
144
|
-
public tooltip?:
|
|
158
|
+
public tooltip?: TooltipOption;
|
|
159
|
+
/** Icon tooltip definition (`true`: use node's title). */
|
|
160
|
+
public iconTooltip?: TooltipOption;
|
|
145
161
|
/** Additional classes added to `div.wb-row`.
|
|
146
162
|
* @see {@link hasClass}, {@link setClass}. */
|
|
147
163
|
public classes: Set<string> | null = null; //new Set<string>();
|
|
@@ -171,24 +187,30 @@ export class WunderbaumNode {
|
|
|
171
187
|
|
|
172
188
|
this.tree = tree;
|
|
173
189
|
this.parent = parent;
|
|
174
|
-
|
|
175
190
|
this.key = "" + (data.key ?? ++WunderbaumNode.sequence);
|
|
176
191
|
this.title = "" + (data.title ?? "<" + this.key + ">");
|
|
192
|
+
this.expanded = !!data.expanded;
|
|
193
|
+
this.lazy = !!data.lazy;
|
|
194
|
+
|
|
195
|
+
// We set the following node properties only if a matching data value is
|
|
196
|
+
// passed
|
|
177
197
|
data.refKey != null ? (this.refKey = "" + data.refKey) : 0;
|
|
178
198
|
data.type != null ? (this.type = "" + data.type) : 0;
|
|
179
|
-
this.
|
|
180
|
-
data.
|
|
181
|
-
|
|
199
|
+
data.icon != null ? (this.icon = util.intToBool(data.icon)) : 0;
|
|
200
|
+
data.tooltip != null ? (this.tooltip = util.intToBool(data.tooltip)) : 0;
|
|
201
|
+
data.iconTooltip != null
|
|
202
|
+
? (this.iconTooltip = util.intToBool(data.iconTooltip))
|
|
203
|
+
: 0;
|
|
182
204
|
data.statusNodeType != null
|
|
183
205
|
? (this.statusNodeType = ("" + data.statusNodeType) as NodeStatusType)
|
|
184
206
|
: 0;
|
|
185
207
|
data.colspan != null ? (this.colspan = !!data.colspan) : 0;
|
|
186
208
|
|
|
187
209
|
// Selection
|
|
188
|
-
data.checkbox != null ? (
|
|
210
|
+
data.checkbox != null ? util.intToBool(data.checkbox) : 0;
|
|
189
211
|
data.radiogroup != null ? (this.radiogroup = !!data.radiogroup) : 0;
|
|
190
|
-
this.selected = data.selected
|
|
191
|
-
data.unselectable
|
|
212
|
+
data.selected != null ? (this.selected = !!data.selected) : 0;
|
|
213
|
+
data.unselectable != null ? (this.unselectable = !!data.unselectable) : 0;
|
|
192
214
|
|
|
193
215
|
if (data.classes) {
|
|
194
216
|
this.setClass(data.classes);
|
|
@@ -1140,9 +1162,9 @@ export class WunderbaumNode {
|
|
|
1140
1162
|
// Check for overlapping requests
|
|
1141
1163
|
if (this._requestId) {
|
|
1142
1164
|
this.logWarn(
|
|
1143
|
-
`Recursive load request #${requestId} while #${this._requestId} is pending
|
|
1165
|
+
`Recursive load request #${requestId} while #${this._requestId} is pending. ` +
|
|
1166
|
+
"The previous request will be ignored."
|
|
1144
1167
|
);
|
|
1145
|
-
// node.debug("Send load request #" + requestId);
|
|
1146
1168
|
}
|
|
1147
1169
|
this._requestId = requestId;
|
|
1148
1170
|
|
|
@@ -1660,6 +1682,7 @@ export class WunderbaumNode {
|
|
|
1660
1682
|
protected _render_markup(opts: RenderOptions) {
|
|
1661
1683
|
const tree = this.tree;
|
|
1662
1684
|
const treeOptions = tree.options;
|
|
1685
|
+
const rowHeight = treeOptions.rowHeightPx!;
|
|
1663
1686
|
const checkbox = this.getOption("checkbox");
|
|
1664
1687
|
const columns = tree.columns;
|
|
1665
1688
|
const level = this.getLevel();
|
|
@@ -1681,7 +1704,7 @@ export class WunderbaumNode {
|
|
|
1681
1704
|
rowDiv = document.createElement("div");
|
|
1682
1705
|
rowDiv.classList.add("wb-row");
|
|
1683
1706
|
|
|
1684
|
-
rowDiv.style.top = this._rowIdx! *
|
|
1707
|
+
rowDiv.style.top = this._rowIdx! * rowHeight + "px";
|
|
1685
1708
|
|
|
1686
1709
|
this._rowElem = rowDiv;
|
|
1687
1710
|
|
|
@@ -2763,6 +2786,19 @@ export class WunderbaumNode {
|
|
|
2763
2786
|
av = a.data[propName];
|
|
2764
2787
|
bv = b.data[propName];
|
|
2765
2788
|
}
|
|
2789
|
+
if (av == null && bv == null) {
|
|
2790
|
+
return 0;
|
|
2791
|
+
}
|
|
2792
|
+
if (av == null) {
|
|
2793
|
+
av = typeof bv === "string" ? "" : 0;
|
|
2794
|
+
} else if (typeof av === "boolean") {
|
|
2795
|
+
av = av ? 1 : 0;
|
|
2796
|
+
}
|
|
2797
|
+
if (bv == null) {
|
|
2798
|
+
bv = typeof av === "string" ? "" : 0;
|
|
2799
|
+
} else if (typeof bv === "boolean") {
|
|
2800
|
+
bv = bv ? 1 : 0;
|
|
2801
|
+
}
|
|
2766
2802
|
if (caseInsensitive) {
|
|
2767
2803
|
if (typeof av === "string") {
|
|
2768
2804
|
av = av.toLowerCase();
|
package/src/wunderbaum.ts
CHANGED
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
makeNodeTitleStartMatcher,
|
|
62
62
|
nodeTitleSorter,
|
|
63
63
|
RENDER_MAX_PREFETCH,
|
|
64
|
-
|
|
64
|
+
DEFAULT_ROW_HEIGHT,
|
|
65
65
|
} from "./common";
|
|
66
66
|
import { WunderbaumNode } from "./wb_node";
|
|
67
67
|
import { Deferred } from "./deferred";
|
|
@@ -197,7 +197,7 @@ export class Wunderbaum {
|
|
|
197
197
|
debugLevel: DEFAULT_DEBUGLEVEL, // 0:quiet, 1:errors, 2:warnings, 3:info, 4:verbose
|
|
198
198
|
header: null, // Show/hide header (pass bool or string)
|
|
199
199
|
// headerHeightPx: ROW_HEIGHT,
|
|
200
|
-
rowHeightPx:
|
|
200
|
+
rowHeightPx: DEFAULT_ROW_HEIGHT,
|
|
201
201
|
iconMap: "bootstrap",
|
|
202
202
|
columns: null,
|
|
203
203
|
types: null,
|
|
@@ -294,6 +294,16 @@ export class Wunderbaum {
|
|
|
294
294
|
this.element.tabIndex = 0;
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
if (opts.rowHeightPx !== DEFAULT_ROW_HEIGHT) {
|
|
298
|
+
this.element.style.setProperty(
|
|
299
|
+
"--wb-row-outer-height",
|
|
300
|
+
opts.rowHeightPx + "px"
|
|
301
|
+
);
|
|
302
|
+
this.element.style.setProperty(
|
|
303
|
+
"--wb-row-inner-height",
|
|
304
|
+
opts.rowHeightPx - 2 + "px"
|
|
305
|
+
);
|
|
306
|
+
}
|
|
297
307
|
// Attach tree instance to <div>
|
|
298
308
|
(<any>this.element)._wb_tree = this;
|
|
299
309
|
|
|
@@ -759,6 +769,7 @@ export class Wunderbaum {
|
|
|
759
769
|
|
|
760
770
|
/** Return the topmost visible node in the viewport. */
|
|
761
771
|
getTopmostVpNode(complete = true) {
|
|
772
|
+
const rowHeight = this.options.rowHeightPx!;
|
|
762
773
|
const gracePx = 1; // ignore subpixel scrolling
|
|
763
774
|
const scrollParent = this.element;
|
|
764
775
|
// const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
@@ -766,15 +777,16 @@ export class Wunderbaum {
|
|
|
766
777
|
let topIdx: number;
|
|
767
778
|
|
|
768
779
|
if (complete) {
|
|
769
|
-
topIdx = Math.ceil((scrollTop - gracePx) /
|
|
780
|
+
topIdx = Math.ceil((scrollTop - gracePx) / rowHeight);
|
|
770
781
|
} else {
|
|
771
|
-
topIdx = Math.floor(scrollTop /
|
|
782
|
+
topIdx = Math.floor(scrollTop / rowHeight);
|
|
772
783
|
}
|
|
773
784
|
return this._getNodeByRowIdx(topIdx)!;
|
|
774
785
|
}
|
|
775
786
|
|
|
776
787
|
/** Return the lowest visible node in the viewport. */
|
|
777
788
|
getLowestVpNode(complete = true) {
|
|
789
|
+
const rowHeight = this.options.rowHeightPx!;
|
|
778
790
|
const scrollParent = this.element;
|
|
779
791
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
780
792
|
const scrollTop = scrollParent.scrollTop;
|
|
@@ -782,9 +794,9 @@ export class Wunderbaum {
|
|
|
782
794
|
let bottomIdx: number;
|
|
783
795
|
|
|
784
796
|
if (complete) {
|
|
785
|
-
bottomIdx = Math.floor((scrollTop + clientHeight) /
|
|
797
|
+
bottomIdx = Math.floor((scrollTop + clientHeight) / rowHeight) - 1;
|
|
786
798
|
} else {
|
|
787
|
-
bottomIdx = Math.ceil((scrollTop + clientHeight) /
|
|
799
|
+
bottomIdx = Math.ceil((scrollTop + clientHeight) / rowHeight) - 1;
|
|
788
800
|
}
|
|
789
801
|
bottomIdx = Math.min(bottomIdx, this.count(true) - 1);
|
|
790
802
|
return this._getNodeByRowIdx(bottomIdx)!;
|
|
@@ -1282,9 +1294,10 @@ export class Wunderbaum {
|
|
|
1282
1294
|
* @param includeHidden Not yet implemented
|
|
1283
1295
|
*/
|
|
1284
1296
|
findRelatedNode(node: WunderbaumNode, where: string, includeHidden = false) {
|
|
1297
|
+
const rowHeight = this.options.rowHeightPx!;
|
|
1285
1298
|
let res = null;
|
|
1286
1299
|
const pageSize = Math.floor(
|
|
1287
|
-
this.listContainerElement.clientHeight /
|
|
1300
|
+
this.listContainerElement.clientHeight / rowHeight
|
|
1288
1301
|
);
|
|
1289
1302
|
|
|
1290
1303
|
switch (where) {
|
|
@@ -1633,7 +1646,7 @@ export class Wunderbaum {
|
|
|
1633
1646
|
const PADDING = 2; // leave some pixels between viewport bounds
|
|
1634
1647
|
|
|
1635
1648
|
let node;
|
|
1636
|
-
WunderbaumNode;
|
|
1649
|
+
// WunderbaumNode;
|
|
1637
1650
|
let options: ScrollToOptions | undefined;
|
|
1638
1651
|
|
|
1639
1652
|
if (nodeOrOpts instanceof WunderbaumNode) {
|
|
@@ -1644,14 +1657,15 @@ export class Wunderbaum {
|
|
|
1644
1657
|
}
|
|
1645
1658
|
util.assert(node && node._rowIdx != null, `Invalid node: ${node}`);
|
|
1646
1659
|
|
|
1660
|
+
const rowHeight = this.options.rowHeightPx!;
|
|
1647
1661
|
const scrollParent = this.element;
|
|
1648
1662
|
const headerHeight = this.headerElement.clientHeight; // May be 0
|
|
1649
1663
|
const scrollTop = scrollParent.scrollTop;
|
|
1650
1664
|
const vpHeight = scrollParent.clientHeight;
|
|
1651
|
-
const rowTop = node._rowIdx! *
|
|
1665
|
+
const rowTop = node._rowIdx! * rowHeight + headerHeight;
|
|
1652
1666
|
const vpTop = headerHeight;
|
|
1653
1667
|
const vpRowTop = rowTop - scrollTop;
|
|
1654
|
-
const vpRowBottom = vpRowTop +
|
|
1668
|
+
const vpRowBottom = vpRowTop + rowHeight;
|
|
1655
1669
|
const topNode = options?.topNode;
|
|
1656
1670
|
|
|
1657
1671
|
// this.log( `scrollTo(${node.title}), vpTop:${vpTop}px, scrollTop:${scrollTop}, vpHeight:${vpHeight}, rowTop:${rowTop}, vpRowTop:${vpRowTop}`, nodeOrOpts , options);
|
|
@@ -1664,7 +1678,7 @@ export class Wunderbaum {
|
|
|
1664
1678
|
} else {
|
|
1665
1679
|
// Node is below viewport
|
|
1666
1680
|
// this.log("Below viewport");
|
|
1667
|
-
newScrollTop = rowTop +
|
|
1681
|
+
newScrollTop = rowTop + rowHeight - vpHeight + PADDING; // leave some pixels between viewport bounds
|
|
1668
1682
|
}
|
|
1669
1683
|
} else {
|
|
1670
1684
|
// Node is above viewport
|
|
@@ -2362,20 +2376,20 @@ export class Wunderbaum {
|
|
|
2362
2376
|
options = Object.assign({ newNodesOnly: false }, options);
|
|
2363
2377
|
const newNodesOnly = !!options.newNodesOnly;
|
|
2364
2378
|
|
|
2365
|
-
const
|
|
2366
|
-
const
|
|
2379
|
+
const rowHeight = this.options.rowHeightPx!;
|
|
2380
|
+
const vpHeight = this.element.clientHeight;
|
|
2367
2381
|
const prefetch = RENDER_MAX_PREFETCH;
|
|
2368
2382
|
// const grace_prefetch = RENDER_MAX_PREFETCH - RENDER_MIN_PREFETCH;
|
|
2369
2383
|
const ofs = this.element.scrollTop;
|
|
2370
2384
|
|
|
2371
|
-
let startIdx = Math.max(0, ofs /
|
|
2385
|
+
let startIdx = Math.max(0, ofs / rowHeight - prefetch);
|
|
2372
2386
|
startIdx = Math.floor(startIdx);
|
|
2373
2387
|
// Make sure start is always even, so the alternating row colors don't
|
|
2374
2388
|
// change when scrolling:
|
|
2375
2389
|
if (startIdx % 2) {
|
|
2376
2390
|
startIdx--;
|
|
2377
2391
|
}
|
|
2378
|
-
let endIdx = Math.max(0, (ofs +
|
|
2392
|
+
let endIdx = Math.max(0, (ofs + vpHeight) / rowHeight + prefetch);
|
|
2379
2393
|
endIdx = Math.ceil(endIdx);
|
|
2380
2394
|
|
|
2381
2395
|
// this.debug("render", opts);
|
|
@@ -2408,20 +2422,20 @@ export class Wunderbaum {
|
|
|
2408
2422
|
} else if (rowDiv && newNodesOnly) {
|
|
2409
2423
|
obsoleteNodes.delete(node);
|
|
2410
2424
|
// no need to update existing node markup
|
|
2411
|
-
rowDiv.style.top = idx *
|
|
2425
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
2412
2426
|
prevElem = rowDiv;
|
|
2413
2427
|
} else {
|
|
2414
2428
|
obsoleteNodes.delete(node);
|
|
2415
2429
|
// Create new markup
|
|
2416
2430
|
if (rowDiv) {
|
|
2417
|
-
rowDiv.style.top = idx *
|
|
2431
|
+
rowDiv.style.top = idx * rowHeight + "px";
|
|
2418
2432
|
}
|
|
2419
2433
|
node._render({ top: top, after: prevElem });
|
|
2420
2434
|
// node.log("render", top, prevElem, "=>", node._rowElem);
|
|
2421
2435
|
prevElem = node._rowElem!;
|
|
2422
2436
|
}
|
|
2423
2437
|
idx++;
|
|
2424
|
-
top +=
|
|
2438
|
+
top += rowHeight;
|
|
2425
2439
|
});
|
|
2426
2440
|
this.treeRowCount = idx;
|
|
2427
2441
|
for (const n of obsoleteNodes) {
|