dockview-core 6.3.0 → 6.5.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.
Files changed (123) hide show
  1. package/README.md +1 -0
  2. package/dist/cjs/dnd/backend.d.ts +70 -0
  3. package/dist/cjs/dnd/backend.js +171 -0
  4. package/dist/cjs/dnd/dropOverlay.d.ts +20 -0
  5. package/dist/cjs/dnd/dropOverlay.js +197 -0
  6. package/dist/cjs/dnd/droptarget.d.ts +20 -6
  7. package/dist/cjs/dnd/droptarget.js +14 -208
  8. package/dist/cjs/dnd/pointer/index.d.ts +11 -0
  9. package/dist/cjs/dnd/pointer/index.js +13 -0
  10. package/dist/cjs/dnd/pointer/longPress.d.ts +32 -0
  11. package/dist/cjs/dnd/pointer/longPress.js +151 -0
  12. package/dist/cjs/dnd/pointer/pointerDragController.d.ts +60 -0
  13. package/dist/cjs/dnd/pointer/pointerDragController.js +241 -0
  14. package/dist/cjs/dnd/pointer/pointerDragSource.d.ts +61 -0
  15. package/dist/cjs/dnd/pointer/pointerDragSource.js +195 -0
  16. package/dist/cjs/dnd/pointer/pointerDropTarget.d.ts +39 -0
  17. package/dist/cjs/dnd/pointer/pointerDropTarget.js +198 -0
  18. package/dist/cjs/dnd/pointer/pointerGhost.d.ts +30 -0
  19. package/dist/cjs/dnd/pointer/pointerGhost.js +44 -0
  20. package/dist/cjs/dnd/pointer/types.d.ts +16 -0
  21. package/dist/cjs/dnd/pointer/types.js +2 -0
  22. package/dist/cjs/dockview/components/panel/content.d.ts +3 -1
  23. package/dist/cjs/dockview/components/panel/content.js +33 -16
  24. package/dist/cjs/dockview/components/popupService.js +34 -0
  25. package/dist/cjs/dockview/components/tab/tab.d.ts +11 -3
  26. package/dist/cjs/dockview/components/tab/tab.js +151 -117
  27. package/dist/cjs/dockview/components/titlebar/tabGroupChip.d.ts +9 -2
  28. package/dist/cjs/dockview/components/titlebar/tabGroupChip.js +15 -6
  29. package/dist/cjs/dockview/components/titlebar/tabGroups.d.ts +33 -5
  30. package/dist/cjs/dockview/components/titlebar/tabGroups.js +231 -40
  31. package/dist/cjs/dockview/components/titlebar/tabs.d.ts +38 -1
  32. package/dist/cjs/dockview/components/titlebar/tabs.js +372 -251
  33. package/dist/cjs/dockview/components/titlebar/tabsContainer.d.ts +5 -3
  34. package/dist/cjs/dockview/components/titlebar/voidContainer.d.ts +6 -2
  35. package/dist/cjs/dockview/components/titlebar/voidContainer.js +189 -27
  36. package/dist/cjs/dockview/contextMenu.js +19 -4
  37. package/dist/cjs/dockview/dndCapabilities.d.ts +19 -0
  38. package/dist/cjs/dockview/dndCapabilities.js +39 -0
  39. package/dist/cjs/dockview/dockviewComponent.d.ts +1 -0
  40. package/dist/cjs/dockview/dockviewComponent.js +54 -33
  41. package/dist/cjs/dockview/dockviewGroupPanelModel.d.ts +9 -5
  42. package/dist/cjs/dockview/dockviewGroupPanelModel.js +25 -11
  43. package/dist/cjs/dockview/events.d.ts +2 -1
  44. package/dist/cjs/dockview/events.js +1 -0
  45. package/dist/cjs/dockview/options.d.ts +18 -3
  46. package/dist/cjs/dockview/options.js +1 -0
  47. package/dist/cjs/dom.js +7 -3
  48. package/dist/cjs/overlay/overlay.d.ts +12 -0
  49. package/dist/cjs/overlay/overlay.js +84 -16
  50. package/dist/cjs/paneview/draggablePaneviewPanel.d.ts +3 -1
  51. package/dist/cjs/paneview/draggablePaneviewPanel.js +27 -26
  52. package/dist/cjs/paneview/options.d.ts +4 -3
  53. package/dist/dockview-core.js +2199 -834
  54. package/dist/dockview-core.min.js +2 -2
  55. package/dist/dockview-core.min.js.map +1 -1
  56. package/dist/dockview-core.min.noStyle.js +2 -2
  57. package/dist/dockview-core.min.noStyle.js.map +1 -1
  58. package/dist/dockview-core.noStyle.js +2202 -837
  59. package/dist/esm/dnd/backend.d.ts +70 -0
  60. package/dist/esm/dnd/backend.js +148 -0
  61. package/dist/esm/dnd/dropOverlay.d.ts +20 -0
  62. package/dist/esm/dnd/dropOverlay.js +192 -0
  63. package/dist/esm/dnd/droptarget.d.ts +20 -6
  64. package/dist/esm/dnd/droptarget.js +16 -210
  65. package/dist/esm/dnd/pointer/index.d.ts +11 -0
  66. package/dist/esm/dnd/pointer/index.js +5 -0
  67. package/dist/esm/dnd/pointer/longPress.d.ts +32 -0
  68. package/dist/esm/dnd/pointer/longPress.js +127 -0
  69. package/dist/esm/dnd/pointer/pointerDragController.d.ts +60 -0
  70. package/dist/esm/dnd/pointer/pointerDragController.js +191 -0
  71. package/dist/esm/dnd/pointer/pointerDragSource.d.ts +61 -0
  72. package/dist/esm/dnd/pointer/pointerDragSource.js +171 -0
  73. package/dist/esm/dnd/pointer/pointerDropTarget.d.ts +39 -0
  74. package/dist/esm/dnd/pointer/pointerDropTarget.js +168 -0
  75. package/dist/esm/dnd/pointer/pointerGhost.d.ts +30 -0
  76. package/dist/esm/dnd/pointer/pointerGhost.js +39 -0
  77. package/dist/esm/dnd/pointer/types.d.ts +16 -0
  78. package/dist/esm/dnd/pointer/types.js +1 -0
  79. package/dist/esm/dockview/components/panel/content.d.ts +3 -1
  80. package/dist/esm/dockview/components/panel/content.js +33 -16
  81. package/dist/esm/dockview/components/popupService.js +34 -0
  82. package/dist/esm/dockview/components/tab/tab.d.ts +11 -3
  83. package/dist/esm/dockview/components/tab/tab.js +139 -114
  84. package/dist/esm/dockview/components/titlebar/tabGroupChip.d.ts +9 -2
  85. package/dist/esm/dockview/components/titlebar/tabGroupChip.js +15 -6
  86. package/dist/esm/dockview/components/titlebar/tabGroups.d.ts +33 -5
  87. package/dist/esm/dockview/components/titlebar/tabGroups.js +177 -12
  88. package/dist/esm/dockview/components/titlebar/tabs.d.ts +38 -1
  89. package/dist/esm/dockview/components/titlebar/tabs.js +348 -227
  90. package/dist/esm/dockview/components/titlebar/tabsContainer.d.ts +5 -3
  91. package/dist/esm/dockview/components/titlebar/voidContainer.d.ts +6 -2
  92. package/dist/esm/dockview/components/titlebar/voidContainer.js +179 -31
  93. package/dist/esm/dockview/contextMenu.js +19 -4
  94. package/dist/esm/dockview/dndCapabilities.d.ts +19 -0
  95. package/dist/esm/dockview/dndCapabilities.js +36 -0
  96. package/dist/esm/dockview/dockviewComponent.d.ts +1 -0
  97. package/dist/esm/dockview/dockviewComponent.js +55 -34
  98. package/dist/esm/dockview/dockviewGroupPanelModel.d.ts +9 -5
  99. package/dist/esm/dockview/dockviewGroupPanelModel.js +24 -11
  100. package/dist/esm/dockview/events.d.ts +2 -1
  101. package/dist/esm/dockview/events.js +1 -0
  102. package/dist/esm/dockview/options.d.ts +18 -3
  103. package/dist/esm/dockview/options.js +1 -0
  104. package/dist/esm/dom.js +7 -3
  105. package/dist/esm/overlay/overlay.d.ts +12 -0
  106. package/dist/esm/overlay/overlay.js +85 -17
  107. package/dist/esm/paneview/draggablePaneviewPanel.d.ts +3 -1
  108. package/dist/esm/paneview/draggablePaneviewPanel.js +26 -20
  109. package/dist/esm/paneview/options.d.ts +4 -3
  110. package/dist/package/main.cjs.js +2202 -837
  111. package/dist/package/main.cjs.min.js +2 -2
  112. package/dist/package/main.esm.min.mjs +2 -2
  113. package/dist/package/main.esm.mjs +2202 -837
  114. package/dist/styles/dockview.css +117 -1
  115. package/package.json +3 -1
  116. package/dist/cjs/dnd/abstractDragHandler.d.ts +0 -14
  117. package/dist/cjs/dnd/abstractDragHandler.js +0 -86
  118. package/dist/cjs/dnd/groupDragHandler.d.ts +0 -12
  119. package/dist/cjs/dnd/groupDragHandler.js +0 -104
  120. package/dist/esm/dnd/abstractDragHandler.d.ts +0 -14
  121. package/dist/esm/dnd/abstractDragHandler.js +0 -63
  122. package/dist/esm/dnd/groupDragHandler.d.ts +0 -12
  123. package/dist/esm/dnd/groupDragHandler.js +0 -81
@@ -7,16 +7,18 @@ import { DockviewComponent } from '../../dockviewComponent';
7
7
  import { DockviewWillShowOverlayLocationEvent } from '../../events';
8
8
  import { DockviewHeaderDirection } from '../../options';
9
9
  export interface TabDropIndexEvent {
10
- readonly event: DragEvent;
10
+ readonly event: DragEvent | PointerEvent;
11
11
  readonly index: number;
12
12
  readonly targetTabGroupId?: string | null;
13
13
  }
14
14
  export interface TabDragEvent {
15
- readonly nativeEvent: DragEvent;
15
+ /** Narrow with `instanceof DragEvent` before reading `dataTransfer`. */
16
+ readonly nativeEvent: DragEvent | PointerEvent;
16
17
  readonly panel: IDockviewPanel;
17
18
  }
18
19
  export interface GroupDragEvent {
19
- readonly nativeEvent: DragEvent;
20
+ /** Narrow with `instanceof DragEvent` before reading `dataTransfer`. */
21
+ readonly nativeEvent: DragEvent | PointerEvent;
20
22
  readonly group: DockviewGroupPanel;
21
23
  }
22
24
  export interface ITabsContainer extends IDisposable {
@@ -8,13 +8,17 @@ export declare class VoidContainer extends CompositeDisposable {
8
8
  private readonly group;
9
9
  private readonly _element;
10
10
  private readonly dropTarget;
11
- private readonly handler;
11
+ private readonly pointerDropTarget;
12
+ private readonly html5DragSource;
13
+ private readonly pointerDragSource;
14
+ private readonly panelTransfer;
12
15
  private readonly _onDrop;
13
16
  readonly onDrop: Event<DroptargetEvent>;
14
17
  private readonly _onDragStart;
15
- readonly onDragStart: Event<DragEvent>;
18
+ readonly onDragStart: Event<PointerEvent | DragEvent>;
16
19
  readonly onWillShowOverlay: Event<WillShowOverlayEvent>;
17
20
  get element(): HTMLElement;
18
21
  constructor(accessor: DockviewComponent, group: DockviewGroupPanel);
19
22
  updateDragAndDropState(): void;
23
+ private getFloatingOverlay;
20
24
  }
@@ -1,56 +1,204 @@
1
- import { getPanelData } from '../../../dnd/dataTransfer';
2
- import { Droptarget, } from '../../../dnd/droptarget';
3
- import { GroupDragHandler } from '../../../dnd/groupDragHandler';
4
- import { addDisposableListener, Emitter } from '../../../events';
5
- import { CompositeDisposable } from '../../../lifecycle';
6
- import { toggleClass } from '../../../dom';
1
+ import { getPanelData, LocalSelectionTransfer, PanelTransfer, } from '../../../dnd/dataTransfer';
2
+ import { html5Backend, pointerBackend, } from '../../../dnd/backend';
3
+ import { addDisposableListener, Emitter, Event } from '../../../events';
4
+ import { CompositeDisposable, Disposable, MutableDisposable, } from '../../../lifecycle';
5
+ import { quasiPreventDefault, toggleClass } from '../../../dom';
6
+ import { resolveDndCapabilities } from '../../dndCapabilities';
7
+ // Floating-group redock via touch: require a deliberate long press so the
8
+ // "move the float around" gesture doesn't double-trigger the redock ghost.
9
+ // Infinity pressTolerance disables the pre-arm flick override; any motion
10
+ // during the wait is treated as drag-the-float, not redock intent.
11
+ const FLOATING_REDOCK_INITIATION_DELAY_MS = 500;
7
12
  export class VoidContainer extends CompositeDisposable {
8
13
  get element() {
9
14
  return this._element;
10
15
  }
11
16
  constructor(accessor, group) {
17
+ var _a, _b;
12
18
  super();
13
19
  this.accessor = accessor;
14
20
  this.group = group;
21
+ this.panelTransfer = LocalSelectionTransfer.getInstance();
15
22
  this._onDrop = new Emitter();
16
23
  this.onDrop = this._onDrop.event;
17
24
  this._onDragStart = new Emitter();
18
25
  this.onDragStart = this._onDragStart.event;
26
+ const caps = resolveDndCapabilities(this.accessor.options);
19
27
  this._element = document.createElement('div');
20
28
  this._element.className = 'dv-void-container';
21
- this._element.draggable = !this.accessor.options.disableDnd;
22
- toggleClass(this._element, 'dv-draggable', !this.accessor.options.disableDnd);
29
+ this._element.draggable = caps.html5;
30
+ toggleClass(this._element, 'dv-draggable', caps.html5 || caps.pointer);
23
31
  this.addDisposables(this._onDrop, this._onDragStart, addDisposableListener(this._element, 'pointerdown', () => {
24
32
  this.accessor.doSetGroupActive(this.group);
25
- }));
26
- this.handler = new GroupDragHandler(this._element, accessor, group, !!this.accessor.options.disableDnd);
27
- this.dropTarget = new Droptarget(this._element, {
33
+ }),
34
+ // Shift+pointerdown marks the event so the group's overlay
35
+ // drag (move-by-floating) sees it was consumed and doesn't
36
+ // fire alongside the HTML5 drag. quasiPreventDefault sets the
37
+ // marker without calling preventDefault — that would also
38
+ // block dragstart, which we need to fire.
39
+ addDisposableListener(this._element, 'pointerdown', (e) => {
40
+ if (e.shiftKey) {
41
+ quasiPreventDefault(e);
42
+ }
43
+ }, true));
44
+ const canDisplayOverlay = (event, position) => {
45
+ if (this.group.api.locked) {
46
+ // Dropping on the void/header space adds the panel
47
+ // to this group, which `locked` is meant to prevent
48
+ // (both `true` and `'no-drop-target'`).
49
+ return false;
50
+ }
51
+ const data = getPanelData();
52
+ if (data && this.accessor.id === data.viewId) {
53
+ return true;
54
+ }
55
+ return group.model.canDisplayOverlay(event, position, 'header_space');
56
+ };
57
+ this.dropTarget = html5Backend.createDropTarget(this._element, {
28
58
  acceptedTargetZones: ['center'],
29
- canDisplayOverlay: (event, position) => {
30
- if (this.group.api.locked) {
31
- // Dropping on the void/header space adds the panel
32
- // to this group, which `locked` is meant to prevent
33
- // (both `true` and `'no-drop-target'`).
34
- return false;
59
+ canDisplayOverlay,
60
+ getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
61
+ });
62
+ this.pointerDropTarget = pointerBackend.createDropTarget(this._element, {
63
+ acceptedTargetZones: ['center'],
64
+ canDisplayOverlay,
65
+ getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
66
+ });
67
+ const buildMultiPanelsGhost = () => {
68
+ const ghostEl = document.createElement('div');
69
+ const style = window.getComputedStyle(this._element);
70
+ const bgColor = style.getPropertyValue('--dv-activegroup-visiblepanel-tab-background-color');
71
+ const color = style.getPropertyValue('--dv-activegroup-visiblepanel-tab-color');
72
+ ghostEl.style.backgroundColor = bgColor;
73
+ ghostEl.style.color = color;
74
+ ghostEl.style.padding = '2px 8px';
75
+ ghostEl.style.height = '24px';
76
+ ghostEl.style.fontSize = '11px';
77
+ ghostEl.style.lineHeight = '20px';
78
+ ghostEl.style.borderRadius = '12px';
79
+ ghostEl.style.whiteSpace = 'nowrap';
80
+ ghostEl.style.boxSizing = 'border-box';
81
+ // HTML5 setDragImage snapshots the element as appended to the
82
+ // document; a default block-level div would stretch to the
83
+ // body's width and render as a viewport-wide bar.
84
+ ghostEl.style.display = 'inline-block';
85
+ ghostEl.textContent = `Multiple Panels (${this.group.size})`;
86
+ return ghostEl;
87
+ };
88
+ const buildGhostSpec = () => {
89
+ const createGhost = this.accessor.options.createGroupDragGhostComponent;
90
+ if (createGhost) {
91
+ const renderer = createGhost(this.group);
92
+ renderer.init({
93
+ group: this.group,
94
+ api: this.accessor.api,
95
+ });
96
+ return {
97
+ element: renderer.element,
98
+ offsetX: 30,
99
+ offsetY: -10,
100
+ dispose: renderer.dispose
101
+ ? () => { var _a; return (_a = renderer.dispose) === null || _a === void 0 ? void 0 : _a.call(renderer); }
102
+ : undefined,
103
+ };
104
+ }
105
+ return {
106
+ element: buildMultiPanelsGhost(),
107
+ offsetX: 30,
108
+ offsetY: -10,
109
+ };
110
+ };
111
+ const sharedDragOptions = {
112
+ getData: () => {
113
+ this.panelTransfer.setData([new PanelTransfer(this.accessor.id, this.group.id, null)], PanelTransfer.prototype);
114
+ return {
115
+ dispose: () => {
116
+ this.panelTransfer.clearData(PanelTransfer.prototype);
117
+ },
118
+ };
119
+ },
120
+ createGhost: buildGhostSpec,
121
+ onDragStart: (event) => {
122
+ this._onDragStart.fire(event);
123
+ },
124
+ };
125
+ this.html5DragSource = html5Backend.createDragSource(this._element, Object.assign(Object.assign({}, sharedDragOptions), { disabled: !caps.html5, isCancelled: (event) => {
126
+ // HTML5: floating groups need shift+drag as the explicit
127
+ // detach gesture (otherwise click-and-drag conflicts with
128
+ // moving the floating group itself).
129
+ if (this.group.api.location.type === 'floating' &&
130
+ !event.shiftKey) {
131
+ return true;
35
132
  }
36
- const data = getPanelData();
37
- if (data && this.accessor.id === data.viewId) {
133
+ if (this.group.api.location.type === 'edge' &&
134
+ this.group.size === 0) {
38
135
  return true;
39
136
  }
40
- return group.model.canDisplayOverlay(event, position, 'header_space');
41
- },
42
- getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
43
- });
44
- this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
45
- this.addDisposables(this.handler, this.handler.onDragStart((event) => {
46
- this._onDragStart.fire(event);
47
- }), this.dropTarget.onDrop((event) => {
137
+ return false;
138
+ } }));
139
+ const isFloating = () => { var _a, _b, _c; return ((_c = (_b = (_a = this.group) === null || _a === void 0 ? void 0 : _a.api) === null || _b === void 0 ? void 0 : _b.location) === null || _c === void 0 ? void 0 : _c.type) === 'floating'; };
140
+ this.pointerDragSource = pointerBackend.createDragSource(this._element, Object.assign(Object.assign({}, sharedDragOptions), { disabled: !caps.pointer, touchOnly: !caps.pointerHandlesMouse,
141
+ // Floating groups share this element with the overlay's
142
+ // move-the-float drag. Without a longer hold + tolerance
143
+ // override, both gestures commit simultaneously and the
144
+ // user sees the float follow their finger *and* a ghost.
145
+ touchInitiationDelay: () => isFloating() ? FLOATING_REDOCK_INITIATION_DELAY_MS : 250, pressTolerance: () => (isFloating() ? Infinity : 8), isCancelled: () => {
146
+ if (!resolveDndCapabilities(this.accessor.options).pointer) {
147
+ return true;
148
+ }
149
+ // Pointer: long-press IS the deliberate gesture, so
150
+ // floating groups don't need the shift gate.
151
+ if (this.group.api.location.type === 'edge' &&
152
+ this.group.size === 0) {
153
+ return true;
154
+ }
155
+ return false;
156
+ }, onDragStart: (event) => {
157
+ var _a;
158
+ // Redock just committed — abort any in-flight overlay
159
+ // move so the float stops following the finger while
160
+ // the ghost takes over.
161
+ (_a = this.getFloatingOverlay()) === null || _a === void 0 ? void 0 : _a.cancelPendingDrag();
162
+ this._onDragStart.fire(event);
163
+ } }));
164
+ // Mirror direction: once the overlay's move-the-float gesture has
165
+ // actually moved something, cancel the pending redock arm so the
166
+ // ghost doesn't appear mid-drag if the user holds past 500ms.
167
+ const overlayMoveSub = new MutableDisposable();
168
+ const refreshOverlayMoveSub = () => {
169
+ const overlay = this.getFloatingOverlay();
170
+ overlayMoveSub.value = overlay
171
+ ? overlay.onDidStartMoving(() => {
172
+ this.pointerDragSource.cancelPending();
173
+ })
174
+ : Disposable.NONE;
175
+ };
176
+ refreshOverlayMoveSub();
177
+ this.addDisposables(overlayMoveSub);
178
+ const locationChange = (_b = (_a = this.group) === null || _a === void 0 ? void 0 : _a.api) === null || _b === void 0 ? void 0 : _b.onDidLocationChange;
179
+ if (locationChange) {
180
+ this.addDisposables(locationChange(refreshOverlayMoveSub));
181
+ }
182
+ this.onWillShowOverlay = Event.any(this.dropTarget.onWillShowOverlay, this.pointerDropTarget.onWillShowOverlay);
183
+ this.addDisposables(this.html5DragSource, this.dropTarget.onDrop((event) => {
48
184
  this._onDrop.fire(event);
49
- }), this.dropTarget);
185
+ }), this.pointerDropTarget.onDrop((event) => {
186
+ this._onDrop.fire(event);
187
+ }), this.dropTarget, this.pointerDropTarget, this.pointerDragSource);
50
188
  }
51
189
  updateDragAndDropState() {
52
- this._element.draggable = !this.accessor.options.disableDnd;
53
- toggleClass(this._element, 'dv-draggable', !this.accessor.options.disableDnd);
54
- this.handler.setDisabled(!!this.accessor.options.disableDnd);
190
+ const caps = resolveDndCapabilities(this.accessor.options);
191
+ this._element.draggable = caps.html5;
192
+ toggleClass(this._element, 'dv-draggable', caps.html5 || caps.pointer);
193
+ this.html5DragSource.setDisabled(!caps.html5);
194
+ this.pointerDragSource.setDisabled(!caps.pointer);
195
+ this.pointerDragSource.setTouchOnly(!caps.pointerHandlesMouse);
196
+ }
197
+ getFloatingOverlay() {
198
+ var _a, _b;
199
+ if (!this.group) {
200
+ return undefined;
201
+ }
202
+ return (_b = (_a = this.accessor.floatingGroups) === null || _a === void 0 ? void 0 : _a.find((fg) => fg.group === this.group)) === null || _b === void 0 ? void 0 : _b.overlay;
55
203
  }
56
204
  }
@@ -40,6 +40,14 @@ function buildSeparator() {
40
40
  el.setAttribute('role', 'separator');
41
41
  return el;
42
42
  }
43
+ function isCoarsePrimaryInput() {
44
+ if (typeof window === 'undefined' || !window.matchMedia) {
45
+ return false;
46
+ }
47
+ const coarse = window.matchMedia('(pointer: coarse)').matches;
48
+ const fine = window.matchMedia('(pointer: fine)').matches;
49
+ return coarse && !fine;
50
+ }
43
51
  function buildRenameInput(tabGroup) {
44
52
  const wrapper = document.createElement('div');
45
53
  wrapper.className = 'dv-context-menu-rename';
@@ -60,10 +68,17 @@ function buildRenameInput(tabGroup) {
60
68
  e.stopPropagation();
61
69
  });
62
70
  wrapper.appendChild(input);
63
- requestAnimationFrame(() => {
64
- input.focus();
65
- input.select();
66
- });
71
+ // Skip auto-focus on touch-primary devices: focusing the input pops the
72
+ // on-screen keyboard, which fires `window resize`, which `PopupService`
73
+ // listens to and uses to dismiss the popover — so the menu opens, the
74
+ // keyboard appears, and the menu immediately closes before the user can
75
+ // type. The user can still tap the input to focus it intentionally.
76
+ if (!isCoarsePrimaryInput()) {
77
+ requestAnimationFrame(() => {
78
+ input.focus();
79
+ input.select();
80
+ });
81
+ }
67
82
  return wrapper;
68
83
  }
69
84
  function buildColorPicker(tabGroup, palette) {
@@ -0,0 +1,19 @@
1
+ import { DockviewOptions } from './options';
2
+ /**
3
+ * Internal mapping of the user-facing `dndStrategy` option into the
4
+ * per-backend capability flags consumed by drag-source / drop-target
5
+ * construction sites. Not part of the public API — consumers only see
6
+ * the `dndStrategy` option itself.
7
+ */
8
+ export interface DndCapabilities {
9
+ /** HTML5 drag/drop wiring active (draggable attr, dragstart). */
10
+ readonly html5: boolean;
11
+ /** Pointer-event drag source active. */
12
+ readonly pointer: boolean;
13
+ /**
14
+ * When true, the pointer source handles mouse pointers too
15
+ * (`touchOnly: false`). Implies `pointer` is true.
16
+ */
17
+ readonly pointerHandlesMouse: boolean;
18
+ }
19
+ export declare function resolveDndCapabilities(options: Pick<DockviewOptions, 'dndStrategy' | 'disableDnd'>): DndCapabilities;
@@ -0,0 +1,36 @@
1
+ export function resolveDndCapabilities(options) {
2
+ if (options.disableDnd) {
3
+ return { html5: false, pointer: false, pointerHandlesMouse: false };
4
+ }
5
+ switch (options.dndStrategy) {
6
+ case 'pointer':
7
+ return { html5: false, pointer: true, pointerHandlesMouse: true };
8
+ case 'html5':
9
+ return { html5: true, pointer: false, pointerHandlesMouse: false };
10
+ case 'auto':
11
+ case undefined:
12
+ default:
13
+ // On touch-primary devices (phones / basic tablets) HTML5 DnD's
14
+ // native long-press intercepts the gesture before our pointer
15
+ // backend can react — Android Chrome launches a system drag with
16
+ // its half-transparent thumbnail, and the long-press context menu
17
+ // never opens. Disable HTML5 there so the pointer backend owns
18
+ // every gesture. Hybrid devices (touchscreen laptops, Surface,
19
+ // iPad with mouse) keep both backends — mouse uses HTML5, touch
20
+ // falls back to whichever backend the underlying element wired.
21
+ return isCoarsePrimaryInput()
22
+ ? { html5: false, pointer: true, pointerHandlesMouse: true }
23
+ : { html5: true, pointer: true, pointerHandlesMouse: false };
24
+ }
25
+ }
26
+ function isCoarsePrimaryInput() {
27
+ if (typeof window === 'undefined' || !window.matchMedia) {
28
+ return false;
29
+ }
30
+ // Coarse pointer without any fine pointer = phone-class device. A laptop
31
+ // touchscreen reports both, and we want HTML5 to remain available there
32
+ // because a real mouse is also plugged in.
33
+ const coarse = window.matchMedia('(pointer: coarse)').matches;
34
+ const fine = window.matchMedia('(pointer: fine)').matches;
35
+ return coarse && !fine;
36
+ }
@@ -261,6 +261,7 @@ export declare class DockviewComponent extends BaseGrid<DockviewGroupPanel> impl
261
261
  private readonly _floatingGroups;
262
262
  private readonly _popoutGroups;
263
263
  private readonly _rootDropTarget;
264
+ private readonly _rootPointerDropTarget;
264
265
  private _popoutRestorationPromise;
265
266
  private readonly _popoutRestorationCleanups;
266
267
  private readonly _onDidRemoveGroup;
@@ -1,5 +1,6 @@
1
1
  import { getRelativeLocation, getGridLocation, orthogonal, } from '../gridview/gridview';
2
- import { directionToPosition, Droptarget, } from '../dnd/droptarget';
2
+ import { directionToPosition, } from '../dnd/droptarget';
3
+ import { html5Backend, pointerBackend } from '../dnd/backend';
3
4
  import { tail, sequenceEquals, remove } from '../array';
4
5
  import { DockviewPanel } from './dockviewPanel';
5
6
  import { CompositeDisposable, Disposable } from '../lifecycle';
@@ -98,7 +99,7 @@ export class DockviewComponent extends BaseGrid {
98
99
  return this._popoutRestorationPromise;
99
100
  }
100
101
  constructor(container, options) {
101
- var _a, _b, _c, _d, _e, _f;
102
+ var _a, _b, _c, _d, _e, _f, _g;
102
103
  super(container, {
103
104
  proportionalLayout: true,
104
105
  orientation: Orientation.HORIZONTAL,
@@ -197,37 +198,51 @@ export class DockviewComponent extends BaseGrid {
197
198
  this._floatingOverlayHost = document.createElement('div');
198
199
  this._floatingOverlayHost.className = 'dv-floating-overlay-host';
199
200
  this._shellManager.element.appendChild(this._floatingOverlayHost);
200
- this._rootDropTarget = new Droptarget(this.element, {
201
- className: 'dv-drop-target-edge',
202
- canDisplayOverlay: (event, position) => {
203
- const data = getPanelData();
204
- if (data) {
205
- if (data.viewId !== this.id) {
206
- return false;
207
- }
208
- if (position === 'center') {
209
- // center drop target is only allowed if there are no panels in the grid
210
- // floating panels are allowed
211
- return this.gridview.length === 0;
212
- }
213
- return true;
214
- }
215
- if (position === 'center' && this.gridview.length !== 0) {
216
- /**
217
- * for external events only show the four-corner drag overlays, disable
218
- * the center position so that external drag events can fall through to the group
219
- * and panel drop target handlers
220
- */
201
+ const rootCanDisplayOverlay = (event, position) => {
202
+ const data = getPanelData();
203
+ if (data) {
204
+ if (data.viewId !== this.id) {
221
205
  return false;
222
206
  }
223
- const firedEvent = new DockviewUnhandledDragOverEvent(event, 'edge', position, getPanelData);
224
- this._onUnhandledDragOverEvent.fire(firedEvent);
225
- return firedEvent.isAccepted;
226
- },
207
+ if (position === 'center') {
208
+ // center drop target is only allowed if there are no panels in the grid
209
+ // floating panels are allowed
210
+ return this.gridview.length === 0;
211
+ }
212
+ return true;
213
+ }
214
+ if (position === 'center' && this.gridview.length !== 0) {
215
+ /**
216
+ * for external events only show the four-corner drag overlays, disable
217
+ * the center position so that external drag events can fall through to the group
218
+ * and panel drop target handlers
219
+ */
220
+ return false;
221
+ }
222
+ const firedEvent = new DockviewUnhandledDragOverEvent(event, 'edge', position, getPanelData);
223
+ this._onUnhandledDragOverEvent.fire(firedEvent);
224
+ return firedEvent.isAccepted;
225
+ };
226
+ this._rootDropTarget = html5Backend.createDropTarget(this.element, {
227
+ className: 'dv-drop-target-edge',
228
+ canDisplayOverlay: rootCanDisplayOverlay,
227
229
  acceptedTargetZones: ['top', 'bottom', 'left', 'right', 'center'],
228
230
  overlayModel: (_f = options.rootOverlayModel) !== null && _f !== void 0 ? _f : DEFAULT_ROOT_OVERLAY_MODEL,
229
231
  getOverrideTarget: () => { var _a; return (_a = this.rootDropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
230
232
  });
233
+ this._rootPointerDropTarget = pointerBackend.createDropTarget(this.element, {
234
+ className: 'dv-drop-target-edge',
235
+ canDisplayOverlay: rootCanDisplayOverlay,
236
+ acceptedTargetZones: [
237
+ 'top',
238
+ 'bottom',
239
+ 'left',
240
+ 'right',
241
+ 'center',
242
+ ],
243
+ overlayModel: (_g = options.rootOverlayModel) !== null && _g !== void 0 ? _g : DEFAULT_ROOT_OVERLAY_MODEL,
244
+ getOverrideTarget: () => { var _a; return (_a = this.rootDropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
245
+ });
231
246
  this.updateDropTargetModel(options);
232
247
  toggleClass(this.gridview.element, 'dv-dockview', true);
233
248
  toggleClass(this.element, 'dv-debug', !!options.debug);
@@ -289,7 +304,7 @@ export class DockviewComponent extends BaseGrid {
289
304
  d.dispose();
290
305
  }
291
306
  this._edgeGroupDisposables.clear();
292
- }), this._rootDropTarget, this._rootDropTarget.onWillShowOverlay((event) => {
307
+ }), this._rootDropTarget, this._rootPointerDropTarget, Event.any(this._rootDropTarget.onWillShowOverlay, this._rootPointerDropTarget.onWillShowOverlay)((event) => {
293
308
  if (this.gridview.length > 0 && event.position === 'center') {
294
309
  // option only available when no panels in primary grid
295
310
  return;
@@ -301,7 +316,7 @@ export class DockviewComponent extends BaseGrid {
301
316
  group: undefined,
302
317
  getData: getPanelData,
303
318
  }));
304
- }), this._rootDropTarget.onDrop((event) => {
319
+ }), Event.any(this._rootDropTarget.onDrop, this._rootPointerDropTarget.onDrop)((event) => {
305
320
  var _a;
306
321
  const willDropEvent = new DockviewWillDropEvent({
307
322
  nativeEvent: event.nativeEvent,
@@ -339,7 +354,7 @@ export class DockviewComponent extends BaseGrid {
339
354
  getData: getPanelData,
340
355
  }));
341
356
  }
342
- }), this._rootDropTarget);
357
+ }));
343
358
  }
344
359
  setVisible(panel, visible) {
345
360
  switch (panel.api.location.type) {
@@ -856,9 +871,12 @@ export class DockviewComponent extends BaseGrid {
856
871
  }
857
872
  this.updateDropTargetModel(options);
858
873
  const oldDisableDnd = this.options.disableDnd;
874
+ const oldDndStrategy = this.options.dndStrategy;
859
875
  this._options = Object.assign(Object.assign({}, this.options), options);
860
876
  const newDisableDnd = this.options.disableDnd;
861
- if (oldDisableDnd !== newDisableDnd) {
877
+ const newDndStrategy = this.options.dndStrategy;
878
+ if (oldDisableDnd !== newDisableDnd ||
879
+ oldDndStrategy !== newDndStrategy) {
862
880
  this.updateDragAndDropState();
863
881
  }
864
882
  if ('theme' in options) {
@@ -2422,15 +2440,18 @@ export class DockviewComponent extends BaseGrid {
2422
2440
  }
2423
2441
  updateDropTargetModel(options) {
2424
2442
  if ('dndEdges' in options) {
2425
- this._rootDropTarget.disabled =
2426
- typeof options.dndEdges === 'boolean' &&
2427
- options.dndEdges === false;
2443
+ const disabled = typeof options.dndEdges === 'boolean' &&
2444
+ options.dndEdges === false;
2445
+ this._rootDropTarget.disabled = disabled;
2446
+ this._rootPointerDropTarget.disabled = disabled;
2428
2447
  if (typeof options.dndEdges === 'object' &&
2429
2448
  options.dndEdges !== null) {
2430
2449
  this._rootDropTarget.setOverlayModel(options.dndEdges);
2450
+ this._rootPointerDropTarget.setOverlayModel(options.dndEdges);
2431
2451
  }
2432
2452
  else {
2433
2453
  this._rootDropTarget.setOverlayModel(DEFAULT_ROOT_OVERLAY_MODEL);
2454
+ this._rootPointerDropTarget.setOverlayModel(DEFAULT_ROOT_OVERLAY_MODEL);
2434
2455
  }
2435
2456
  }
2436
2457
  if ('rootOverlayModel' in options) {
@@ -52,13 +52,17 @@ export interface CreateTabGroupOptions extends TabGroupOptions {
52
52
  }
53
53
  export declare class DockviewDidDropEvent extends DockviewEvent {
54
54
  private readonly options;
55
- get nativeEvent(): DragEvent;
55
+ /**
56
+ * `PointerEvent` for touch drags has no `dataTransfer`; use
57
+ * `getData()` for the dockview payload regardless of input method.
58
+ */
59
+ get nativeEvent(): DragEvent | PointerEvent;
56
60
  get position(): Position;
57
61
  get panel(): IDockviewPanel | undefined;
58
62
  get group(): DockviewGroupPanel | undefined;
59
63
  get api(): DockviewApi;
60
64
  constructor(options: {
61
- readonly nativeEvent: DragEvent;
65
+ readonly nativeEvent: DragEvent | PointerEvent;
62
66
  readonly position: Position;
63
67
  readonly panel?: IDockviewPanel;
64
68
  getData(): PanelTransfer | undefined;
@@ -71,7 +75,7 @@ export declare class DockviewWillDropEvent extends DockviewDidDropEvent {
71
75
  private readonly _kind;
72
76
  get kind(): DockviewGroupDropLocation;
73
77
  constructor(options: {
74
- readonly nativeEvent: DragEvent;
78
+ readonly nativeEvent: DragEvent | PointerEvent;
75
79
  readonly position: Position;
76
80
  readonly panel?: IDockviewPanel;
77
81
  getData(): PanelTransfer | undefined;
@@ -122,7 +126,7 @@ export interface IDockviewGroupPanelModel extends IPanel {
122
126
  panel?: IDockviewPanel;
123
127
  suppressRoll?: boolean;
124
128
  }): void;
125
- canDisplayOverlay(event: DragEvent, position: Position, target: DockviewGroupDropLocation): boolean;
129
+ canDisplayOverlay(event: DragEvent | PointerEvent, position: Position, target: DockviewGroupDropLocation): boolean;
126
130
  }
127
131
  export type DockviewGroupLocation = {
128
132
  type: 'grid';
@@ -324,7 +328,7 @@ export declare class DockviewGroupPanelModel extends CompositeDisposable impleme
324
328
  private doSetActivePanel;
325
329
  private updateMru;
326
330
  private updateContainer;
327
- canDisplayOverlay(event: DragEvent, position: Position, target: DockviewGroupDropLocation): boolean;
331
+ canDisplayOverlay(event: DragEvent | PointerEvent, position: Position, target: DockviewGroupDropLocation): boolean;
328
332
  private handleDropEvent;
329
333
  updateDragAndDropState(): void;
330
334
  dispose(): void;