Skip to content

Commit 9bf8eeb

Browse files
committed
Migrate TreeView to pointer events
1 parent 4eae9f1 commit 9bf8eeb

File tree

3 files changed

+66
-36
lines changed

3 files changed

+66
-36
lines changed

src/components/TreeView/index.ts

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,13 @@ class TreeView extends Container {
213213

214214
window.addEventListener('keydown', this._updateModifierKeys);
215215
window.addEventListener('keyup', this._updateModifierKeys);
216-
window.addEventListener('mousedown', this._updateModifierKeys);
216+
window.addEventListener('pointerdown', this._updateModifierKeys);
217217

218-
this.dom.addEventListener('mouseleave', this._onMouseLeave);
218+
this.dom.addEventListener('pointerleave', this._onPointerLeave);
219219

220-
this._dragHandle.dom.addEventListener('mousemove', this._onDragMove);
220+
this._dragHandle.dom.addEventListener('pointermove', this._onDragMove);
221221
this._dragHandle.on('destroy', (dom) => {
222-
dom.removeEventListener('mousemove', this._onDragMove);
222+
dom.removeEventListener('pointermove', this._onDragMove);
223223
});
224224
}
225225

@@ -228,10 +228,10 @@ class TreeView extends Container {
228228

229229
window.removeEventListener('keydown', this._updateModifierKeys);
230230
window.removeEventListener('keyup', this._updateModifierKeys);
231-
window.removeEventListener('mousedown', this._updateModifierKeys);
232-
window.removeEventListener('mousemove', this._onMouseMove);
231+
window.removeEventListener('pointerdown', this._updateModifierKeys);
232+
window.removeEventListener('pointermove', this._onPointerMove);
233233

234-
this.dom.removeEventListener('mouseleave', this._onMouseLeave);
234+
this.dom.removeEventListener('pointerleave', this._onPointerLeave);
235235

236236
if (this._dragScrollInterval) {
237237
window.clearInterval(this._dragScrollInterval);
@@ -241,7 +241,7 @@ class TreeView extends Container {
241241
super.destroy();
242242
}
243243

244-
protected _updateModifierKeys = (evt: KeyboardEvent | MouseEvent) => {
244+
protected _updateModifierKeys = (evt: KeyboardEvent | PointerEvent) => {
245245
this._pressedCtrl = evt.ctrlKey || evt.metaKey;
246246
this._pressedShift = evt.shiftKey;
247247
};
@@ -565,7 +565,7 @@ class TreeView extends Container {
565565
}
566566

567567
// Called when we start dragging a TreeViewItem.
568-
protected _onChildDragStart(evt: MouseEvent, item: TreeViewItem) {
568+
protected _onChildDragStart(evt: PointerEvent, item: TreeViewItem) {
569569
if (!this.allowDrag || this._dragging) return;
570570

571571
this._dragItems = [];
@@ -622,7 +622,7 @@ class TreeView extends Container {
622622
}
623623

624624
// Called when we stop dragging a TreeViewItem.
625-
protected _onChildDragEnd(evt: MouseEvent, item: TreeViewItem) {
625+
protected _onChildDragEnd(evt: PointerEvent, item: TreeViewItem) {
626626
if (!this.allowDrag || !this._dragging) return;
627627

628628
this._dragItems.forEach(item => item.class.remove(CLASS_DRAGGED_ITEM));
@@ -769,7 +769,7 @@ class TreeView extends Container {
769769
}
770770

771771
// Called when we drag over a TreeViewItem.
772-
protected _onChildDragOver(evt: MouseEvent, item: TreeViewItem) {
772+
protected _onChildDragOver(evt: PointerEvent, item: TreeViewItem) {
773773
if (!this._allowDrag || !this._dragging) return;
774774

775775
if (item.allowDrop && this._dragItems.indexOf(item) === -1) {
@@ -782,16 +782,16 @@ class TreeView extends Container {
782782
this._onDragMove(evt);
783783
}
784784

785-
// Called when the mouse cursor leaves the tree view.
786-
protected _onMouseLeave = (evt: MouseEvent) => {
785+
// Called when the pointer leaves the tree view.
786+
protected _onPointerLeave = (evt: PointerEvent) => {
787787
if (!this._allowDrag || !this._dragging) return;
788788

789789
this._dragOverItem = null;
790790
this._updateDragHandle();
791791
};
792792

793-
// Called when the mouse moves while dragging
794-
protected _onMouseMove = (evt: MouseEvent) => {
793+
// Called when the pointer moves while dragging
794+
protected _onPointerMove = (evt: PointerEvent) => {
795795
if (!this._dragging) return;
796796

797797
// Determine if we need to scroll the treeview if we are dragging towards the edges
@@ -814,6 +814,24 @@ class TreeView extends Container {
814814
} else if (evt.pageY > bottom - 32 && this._dragScrollElement.dom.scrollHeight > this._dragScrollElement.height + this._dragScrollElement.dom.scrollTop) {
815815
this._dragScroll = 1;
816816
}
817+
818+
// For touch/pen, use hit-testing to find drag-over target since pointerover doesn't fire
819+
if (evt.pointerType !== 'mouse') {
820+
const element = document.elementFromPoint(evt.clientX, evt.clientY);
821+
if (element) {
822+
// Find the TreeViewItem contents element
823+
const contentsElement = element.closest('.pcui-treeview-item-contents');
824+
if (contentsElement) {
825+
const item = (contentsElement.parentElement as any)?.ui as TreeViewItem;
826+
if (item && item instanceof TreeViewItem) {
827+
this._onChildDragOver(evt, item);
828+
}
829+
} else {
830+
this._dragOverItem = null;
831+
this._updateDragHandle();
832+
}
833+
}
834+
}
817835
};
818836

819837
// Scroll treeview if we are dragging towards the edges
@@ -827,7 +845,7 @@ class TreeView extends Container {
827845
}
828846

829847
// Called while we drag the drag handle
830-
protected _onDragMove = (evt: MouseEvent) => {
848+
protected _onDragMove = (evt: PointerEvent) => {
831849
evt.preventDefault();
832850
evt.stopPropagation();
833851

@@ -1112,23 +1130,21 @@ class TreeView extends Container {
11121130
this._dragging = true;
11131131
this._updateDragHandle();
11141132

1115-
// handle mouse move to scroll when dragging if necessary
1116-
if (this.scrollable || this._dragScrollElement !== this) {
1117-
window.removeEventListener('mousemove', this._onMouseMove);
1118-
window.addEventListener('mousemove', this._onMouseMove);
1119-
if (!this._dragScrollInterval) {
1120-
this._dragScrollInterval = window.setInterval(() => {
1121-
this._scrollWhileDragging();
1122-
}, 1000 / 60);
1123-
}
1133+
// Handle pointer move for scrolling and touch drag-over detection
1134+
window.removeEventListener('pointermove', this._onPointerMove);
1135+
window.addEventListener('pointermove', this._onPointerMove);
1136+
if (!this._dragScrollInterval) {
1137+
this._dragScrollInterval = window.setInterval(() => {
1138+
this._scrollWhileDragging();
1139+
}, 1000 / 60);
11241140
}
11251141
} else {
11261142
this._dragOverItem = null;
11271143
this._updateDragHandle();
11281144

11291145
this._dragging = false;
11301146

1131-
window.removeEventListener('mousemove', this._onMouseMove);
1147+
window.removeEventListener('pointermove', this._onPointerMove);
11321148
if (this._dragScrollInterval) {
11331149
window.clearInterval(this._dragScrollInterval);
11341150
this._dragScrollInterval = null;

src/components/TreeView/style.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
align-items: center;
4545
height: 24px;
4646
box-sizing: border-box;
47+
touch-action: none; // Prevent browser from cancelling pointer events for touch gestures
4748

4849
&:hover {
4950
cursor: pointer;
@@ -156,6 +157,7 @@
156157
z-index: 4;
157158
margin-top: -1px;
158159
margin-left: -1px;
160+
pointer-events: none; // Don't interfere with hit-testing during drag
159161

160162
&.before {
161163
border-top: 4px solid $text-active;

src/components/TreeViewItem/index.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,8 @@ class TreeViewItem extends Container {
193193
dom.addEventListener('blur', this._onContentBlur);
194194
dom.addEventListener('keydown', this._onContentKeyDown);
195195
dom.addEventListener('dragstart', this._onContentDragStart);
196-
dom.addEventListener('mousedown', this._onContentMouseDown);
197-
dom.addEventListener('mouseover', this._onContentMouseOver);
196+
dom.addEventListener('pointerdown', this._onContentPointerDown);
197+
dom.addEventListener('pointerover', this._onContentPointerOver);
198198
dom.addEventListener('click', this._onContentClick);
199199
dom.addEventListener('dblclick', this._onContentDblClick);
200200
dom.addEventListener('contextmenu', this._onContentContextMenu);
@@ -208,13 +208,13 @@ class TreeViewItem extends Container {
208208
dom.removeEventListener('blur', this._onContentBlur);
209209
dom.removeEventListener('keydown', this._onContentKeyDown);
210210
dom.removeEventListener('dragstart', this._onContentDragStart);
211-
dom.removeEventListener('mousedown', this._onContentMouseDown);
212-
dom.removeEventListener('mouseover', this._onContentMouseOver);
211+
dom.removeEventListener('pointerdown', this._onContentPointerDown);
212+
dom.removeEventListener('pointerover', this._onContentPointerOver);
213213
dom.removeEventListener('click', this._onContentClick);
214214
dom.removeEventListener('dblclick', this._onContentDblClick);
215215
dom.removeEventListener('contextmenu', this._onContentContextMenu);
216216

217-
window.removeEventListener('mouseup', this._onContentMouseUp);
217+
window.removeEventListener('pointerup', this._onContentPointerUp);
218218

219219
super.destroy();
220220
}
@@ -263,24 +263,33 @@ class TreeViewItem extends Container {
263263
}
264264
};
265265

266-
protected _onContentMouseDown = (evt: MouseEvent) => {
266+
protected _onContentPointerDown = (evt: PointerEvent) => {
267267
if (!this._treeView || !this._treeView.allowDrag || !this.allowDrag) return;
268268

269269
this._treeView._updateModifierKeys(evt);
270270
evt.stopPropagation();
271+
272+
// For touch/pen input, start drag immediately since dragstart doesn't fire reliably
273+
if (evt.pointerType !== 'mouse') {
274+
if (this.class.contains(CLASS_RENAME)) return;
275+
276+
this._treeView._onChildDragStart(evt, this);
277+
278+
window.addEventListener('pointerup', this._onContentPointerUp);
279+
}
271280
};
272281

273-
protected _onContentMouseUp = (evt: MouseEvent) => {
282+
protected _onContentPointerUp = (evt: PointerEvent) => {
274283
evt.stopPropagation();
275284
evt.preventDefault();
276285

277-
window.removeEventListener('mouseup', this._onContentMouseUp);
286+
window.removeEventListener('pointerup', this._onContentPointerUp);
278287
if (this._treeView) {
279288
this._treeView._onChildDragEnd(evt, this);
280289
}
281290
};
282291

283-
protected _onContentMouseOver = (evt: MouseEvent) => {
292+
protected _onContentPointerOver = (evt: PointerEvent) => {
284293
evt.stopPropagation();
285294

286295
if (this._treeView) {
@@ -296,11 +305,14 @@ class TreeViewItem extends Container {
296305

297306
if (!this._treeView || !this._treeView.allowDrag) return;
298307

308+
// Skip if already dragging (e.g., drag was initiated by pointerdown for touch)
309+
if (this._treeView.isDragging) return;
310+
299311
if (this.class.contains(CLASS_RENAME)) return;
300312

301313
this._treeView._onChildDragStart(evt, this);
302314

303-
window.addEventListener('mouseup', this._onContentMouseUp);
315+
window.addEventListener('pointerup', this._onContentPointerUp);
304316
};
305317

306318
protected _onContentClick = (evt: MouseEvent) => {

0 commit comments

Comments
 (0)