Skip to content

Commit 1034e93

Browse files
author
Flavien VACCARETTI
committed
refactor(dockview-core): rename tabReorderMode option to smoothTabReorder boolean
1 parent 7a5b810 commit 1034e93

File tree

9 files changed

+70
-73
lines changed

9 files changed

+70
-73
lines changed

packages/dockview-angular/src/lib/dockview/dockview-angular.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export class DockviewAngularComponent implements OnInit, OnDestroy, OnChanges {
8585
@Input() locked?: boolean;
8686
@Input() disableAutoResizing?: boolean;
8787
@Input() singleTabMode?: 'fullwidth' | 'default';
88-
@Input() tabReorderMode?: 'swap' | 'slide';
88+
@Input() smoothTabReorder?: boolean;
8989

9090
@Output() ready = new EventEmitter<DockviewReadyEvent>();
9191
@Output() didDrop = new EventEmitter<DockviewDidDropEvent>();

packages/dockview-core/src/__tests__/dockview/components/titlebar/tabsAnimation.spec.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ function createMockPanel(id: string): IDockviewPanel {
4545

4646
function createTabs(
4747
options: {
48-
tabReorderMode?: 'swap' | 'slide';
48+
smoothTabReorder?: boolean;
4949
disableDnd?: boolean;
5050
} = {}
5151
): { tabs: Tabs; accessor: DockviewComponent; group: DockviewGroupPanel } {
5252
const accessor = fromPartial<DockviewComponent>({
5353
id: 'test-accessor',
5454
options: {
55-
tabReorderMode: options.tabReorderMode,
55+
smoothTabReorder: options.smoothTabReorder,
5656
disableDnd: options.disableDnd,
5757
},
5858
});
@@ -117,7 +117,7 @@ describe('tabs - animation', () => {
117117

118118
describe('animation state initialization', () => {
119119
test('dragstart initializes animation state when animation enabled', () => {
120-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
120+
const { tabs } = createTabs({ smoothTabReorder: true });
121121
const panelA = createMockPanel('panel-a');
122122
const panelB = createMockPanel('panel-b');
123123

@@ -139,8 +139,8 @@ describe('tabs - animation', () => {
139139
expect(state.currentInsertionIndex).toBeNull();
140140
});
141141

142-
test('dragstart does not initialize state when tabReorderMode is swap', () => {
143-
const { tabs } = createTabs({ tabReorderMode: 'swap' });
142+
test('dragstart does not initialize state when smoothTabReorder is false', () => {
143+
const { tabs } = createTabs({ smoothTabReorder: false });
144144
const panel = createMockPanel('panel-a');
145145

146146
tabs.openPanel(panel, 0);
@@ -168,7 +168,7 @@ describe('tabs - animation', () => {
168168

169169
describe('source tab collapse', () => {
170170
test('dragstart adds dv-tab--dragging class when animation enabled', () => {
171-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
171+
const { tabs } = createTabs({ smoothTabReorder: true });
172172
const panel = createMockPanel('panel-a');
173173

174174
tabs.openPanel(panel, 0);
@@ -186,8 +186,8 @@ describe('tabs - animation', () => {
186186
).toBeTruthy();
187187
});
188188

189-
test('dragstart does not add dv-tab--dragging class when tabReorderMode is swap', () => {
190-
const { tabs } = createTabs({ tabReorderMode: 'swap' });
189+
test('dragstart does not add dv-tab--dragging class when smoothTabReorder is false', () => {
190+
const { tabs } = createTabs({ smoothTabReorder: false });
191191
const panel = createMockPanel('panel-a');
192192

193193
tabs.openPanel(panel, 0);
@@ -203,7 +203,7 @@ describe('tabs - animation', () => {
203203

204204
describe('drag cancellation', () => {
205205
test('dragend resets animation state and removes dragging class', () => {
206-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
206+
const { tabs } = createTabs({ smoothTabReorder: true });
207207
const panelA = createMockPanel('panel-a');
208208
const panelB = createMockPanel('panel-b');
209209

@@ -231,7 +231,7 @@ describe('tabs - animation', () => {
231231
});
232232

233233
test('dragend removes shifting classes and transforms from all tabs', () => {
234-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
234+
const { tabs } = createTabs({ smoothTabReorder: true });
235235
const panelA = createMockPanel('panel-a');
236236
const panelB = createMockPanel('panel-b');
237237

@@ -271,7 +271,7 @@ describe('tabs - animation', () => {
271271

272272
describe('FLIP animation', () => {
273273
test('runFlipAnimation applies inverse transforms for moved tabs', () => {
274-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
274+
const { tabs } = createTabs({ smoothTabReorder: true });
275275
const panelA = createMockPanel('panel-a');
276276
const panelB = createMockPanel('panel-b');
277277
const panelC = createMockPanel('panel-c');
@@ -313,7 +313,7 @@ describe('tabs - animation', () => {
313313
});
314314

315315
test('runFlipAnimation removes transforms in requestAnimationFrame', () => {
316-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
316+
const { tabs } = createTabs({ smoothTabReorder: true });
317317
const panelA = createMockPanel('panel-a');
318318
const panelB = createMockPanel('panel-b');
319319

@@ -343,7 +343,7 @@ describe('tabs - animation', () => {
343343
});
344344

345345
test('no animation when no tabs moved (drop at original position)', () => {
346-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
346+
const { tabs } = createTabs({ smoothTabReorder: true });
347347
const panelA = createMockPanel('panel-a');
348348
const panelB = createMockPanel('panel-b');
349349

@@ -373,7 +373,7 @@ describe('tabs - animation', () => {
373373

374374
describe('resetTabTransforms', () => {
375375
test('clears all transforms and shifting classes', () => {
376-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
376+
const { tabs } = createTabs({ smoothTabReorder: true });
377377
const panelA = createMockPanel('panel-a');
378378
const panelB = createMockPanel('panel-b');
379379

@@ -403,7 +403,7 @@ describe('tabs - animation', () => {
403403

404404
describe('dispose cleanup', () => {
405405
test('dispose during active drag clears animation state', () => {
406-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
406+
const { tabs } = createTabs({ smoothTabReorder: true });
407407
const panelA = createMockPanel('panel-a');
408408
const panelB = createMockPanel('panel-b');
409409

@@ -425,7 +425,7 @@ describe('tabs - animation', () => {
425425

426426
describe('delete cleanup', () => {
427427
test('deleting source tab during cross-group move clears animation state', () => {
428-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
428+
const { tabs } = createTabs({ smoothTabReorder: true });
429429
const panelA = createMockPanel('panel-a');
430430
const panelB = createMockPanel('panel-b');
431431

@@ -446,7 +446,7 @@ describe('tabs - animation', () => {
446446
});
447447

448448
test('deleting non-source tab does not clear animation state', () => {
449-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
449+
const { tabs } = createTabs({ smoothTabReorder: true });
450450
const panelA = createMockPanel('panel-a');
451451
const panelB = createMockPanel('panel-b');
452452
const panelC = createMockPanel('panel-c');
@@ -472,7 +472,7 @@ describe('tabs - animation', () => {
472472

473473
describe('handleDragOver', () => {
474474
test('updates currentInsertionIndex based on cursor position', () => {
475-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
475+
const { tabs } = createTabs({ smoothTabReorder: true });
476476
const panelA = createMockPanel('panel-a');
477477
const panelB = createMockPanel('panel-b');
478478
const panelC = createMockPanel('panel-c');
@@ -505,7 +505,7 @@ describe('tabs - animation', () => {
505505

506506
describe('dragover gap transforms', () => {
507507
test('tabs after insertion index shift right by source tab width', () => {
508-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
508+
const { tabs } = createTabs({ smoothTabReorder: true });
509509
const panelA = createMockPanel('panel-a');
510510
const panelB = createMockPanel('panel-b');
511511
const panelC = createMockPanel('panel-c');
@@ -555,7 +555,7 @@ describe('tabs - animation', () => {
555555
});
556556

557557
test('gap moves when cursor moves to different position', () => {
558-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
558+
const { tabs } = createTabs({ smoothTabReorder: true });
559559
const panelA = createMockPanel('panel-a');
560560
const panelB = createMockPanel('panel-b');
561561
const panelC = createMockPanel('panel-c');
@@ -597,7 +597,7 @@ describe('tabs - animation', () => {
597597
});
598598

599599
test('same insertion index skips transform update', () => {
600-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
600+
const { tabs } = createTabs({ smoothTabReorder: true });
601601
const panelA = createMockPanel('panel-a');
602602
const panelB = createMockPanel('panel-b');
603603

@@ -631,7 +631,7 @@ describe('tabs - animation', () => {
631631

632632
describe('cross-group animation (US3)', () => {
633633
test('external dragover initializes animation state with sourceIndex -1', () => {
634-
const { tabs, group } = createTabs({ tabReorderMode: 'slide' });
634+
const { tabs, group } = createTabs({ smoothTabReorder: true });
635635
const panelA = createMockPanel('panel-a');
636636
const panelB = createMockPanel('panel-b');
637637

@@ -665,8 +665,8 @@ describe('tabs - animation', () => {
665665
spy.mockRestore();
666666
});
667667

668-
test('external dragover does not initialize state when tabReorderMode is swap', () => {
669-
const { tabs } = createTabs({ tabReorderMode: 'swap' });
668+
test('external dragover does not initialize state when smoothTabReorder is false', () => {
669+
const { tabs } = createTabs({ smoothTabReorder: false });
670670
const panel = createMockPanel('panel-a');
671671
tabs.openPanel(panel, 0);
672672

@@ -689,7 +689,7 @@ describe('tabs - animation', () => {
689689
});
690690

691691
test('external dragover uses average tab width for gap', () => {
692-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
692+
const { tabs } = createTabs({ smoothTabReorder: true });
693693
const panelA = createMockPanel('panel-a');
694694
const panelB = createMockPanel('panel-b');
695695

@@ -719,7 +719,7 @@ describe('tabs - animation', () => {
719719
});
720720

721721
test('dragleave fully clears state for external drags', () => {
722-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
722+
const { tabs } = createTabs({ smoothTabReorder: true });
723723
const panelA = createMockPanel('panel-a');
724724
tabs.openPanel(panelA, 0);
725725

@@ -751,7 +751,7 @@ describe('tabs - animation', () => {
751751
});
752752

753753
test('same-group dragover does not trigger external detection', () => {
754-
const { tabs, group } = createTabs({ tabReorderMode: 'slide' });
754+
const { tabs, group } = createTabs({ smoothTabReorder: true });
755755
const panelA = createMockPanel('panel-a');
756756
tabs.openPanel(panelA, 0);
757757

@@ -776,7 +776,7 @@ describe('tabs - animation', () => {
776776
});
777777

778778
test('cross-group FLIP animates newly inserted tab with slide-in', () => {
779-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
779+
const { tabs } = createTabs({ smoothTabReorder: true });
780780
const panelA = createMockPanel('panel-a');
781781
const panelB = createMockPanel('panel-b');
782782
const panelC = createMockPanel('panel-c');
@@ -813,7 +813,7 @@ describe('tabs - animation', () => {
813813
});
814814

815815
test('cross-group FLIP does NOT animate source tab for same-group drops', () => {
816-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
816+
const { tabs } = createTabs({ smoothTabReorder: true });
817817
const panelA = createMockPanel('panel-a');
818818
const panelB = createMockPanel('panel-b');
819819

@@ -842,7 +842,7 @@ describe('tabs - animation', () => {
842842

843843
describe('dragleave', () => {
844844
test('resets transforms and insertion index when cursor leaves container', () => {
845-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
845+
const { tabs } = createTabs({ smoothTabReorder: true });
846846
const panelA = createMockPanel('panel-a');
847847
const panelB = createMockPanel('panel-b');
848848

@@ -875,7 +875,7 @@ describe('tabs - animation', () => {
875875
});
876876

877877
test('does not reset when moving between child elements', () => {
878-
const { tabs } = createTabs({ tabReorderMode: 'slide' });
878+
const { tabs } = createTabs({ smoothTabReorder: true });
879879
const panelA = createMockPanel('panel-a');
880880
const panelB = createMockPanel('panel-b');
881881

packages/dockview-core/src/dockview/components/tab/tab.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class Tab extends CompositeDisposable {
104104

105105
if (data && this.accessor.id === data.viewId) {
106106
if (
107-
this.accessor.options.tabReorderMode === 'slide' &&
107+
this.accessor.options.smoothTabReorder &&
108108
data.groupId === this.group.id
109109
) {
110110
return false;
@@ -149,7 +149,7 @@ export class Tab extends CompositeDisposable {
149149

150150
this._onDragStart.fire(event);
151151

152-
if (this.accessor.options.tabReorderMode === 'slide') {
152+
if (this.accessor.options.smoothTabReorder) {
153153
// Delay collapse to next frame so the browser
154154
// captures the full drag image first
155155
requestAnimationFrame(() => {

packages/dockview-core/src/dockview/components/titlebar/tabs.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export class Tabs extends CompositeDisposable {
171171
if (!this._animState) {
172172
// Check for external drag from another group
173173
if (
174-
this.accessor.options.tabReorderMode !== 'slide' ||
174+
!this.accessor.options.smoothTabReorder ||
175175
this.accessor.options.disableDnd
176176
) {
177177
return;
@@ -226,7 +226,7 @@ export class Tabs extends CompositeDisposable {
226226
'drop',
227227
(event) => {
228228
if (
229-
this.accessor.options.tabReorderMode !== 'slide' ||
229+
!this.accessor.options.smoothTabReorder ||
230230
!this._animState ||
231231
this._animState.currentInsertionIndex === null
232232
) {
@@ -333,7 +333,7 @@ export class Tabs extends CompositeDisposable {
333333
tab.onDragStart((event) => {
334334
this._onTabDragStart.fire({ nativeEvent: event, panel });
335335

336-
if (this.accessor.options.tabReorderMode === 'slide') {
336+
if (this.accessor.options.smoothTabReorder) {
337337
this._animState = {
338338
sourceTabId: panel.id,
339339
sourceIndex: this._tabs.findIndex(

packages/dockview-core/src/dockview/options.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,13 @@ export interface DockviewOptions {
7979
*/
8080
scrollbars?: 'native' | 'custom';
8181
/**
82-
* Controls tab reorder behavior during drag-and-drop.
82+
* When `true`, tabs animate smoothly during drag-and-drop reorder:
83+
* tabs slide apart to reveal the insertion gap, then animate to their
84+
* final positions on drop (Chrome-like behavior).
8385
*
84-
* - `'swap'` (default): Tabs swap positions instantly on drop (classic behavior).
85-
* - `'slide'`: Tabs slide smoothly to make room for the dragged tab (Chrome-like).
86+
* Defaults to `false`.
8687
*/
87-
tabReorderMode?: 'swap' | 'slide';
88+
smoothTabReorder?: boolean;
8889
}
8990

9091
export interface DockviewDndOverlayEvent extends IAcceptableEvent {
@@ -134,7 +135,7 @@ export const PROPERTY_KEYS_DOCKVIEW: (keyof DockviewOptions)[] = (() => {
134135
theme: undefined,
135136
disableTabsOverflowList: undefined,
136137
scrollbars: undefined,
137-
tabReorderMode: undefined,
138+
smoothTabReorder: undefined,
138139
};
139140

140141
return Object.keys(properties) as (keyof DockviewOptions)[];

packages/docs/docs/core/panels/tabs.mdx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -451,47 +451,47 @@ Controls how tabs animate when reordered via drag-and-drop.
451451
<CodeRunner id="dockview/tabview" height={300}/>
452452

453453
<FrameworkSpecific framework='React'>
454-
<DocRef declaration="IDockviewReactProps" methods={['tabReorderMode']} />
454+
<DocRef declaration="IDockviewReactProps" methods={['smoothTabReorder']} />
455455
</FrameworkSpecific>
456456

457457
<FrameworkSpecific framework='Vue'>
458-
<DocRef declaration="IDockviewVueProps" methods={['tabReorderMode']} />
458+
<DocRef declaration="IDockviewVueProps" methods={['smoothTabReorder']} />
459459
</FrameworkSpecific>
460460

461461
<FrameworkSpecific framework='Angular'>
462-
<DocRef declaration="IDockviewAngularProps" methods={['tabReorderMode']} />
462+
<DocRef declaration="IDockviewAngularProps" methods={['smoothTabReorder']} />
463463
</FrameworkSpecific>
464464

465465
<FrameworkSpecific framework='JavaScript'>
466-
<DocRef declaration="DockviewComponentOptions" methods={['tabReorderMode']} />
466+
<DocRef declaration="DockviewComponentOptions" methods={['smoothTabReorder']} />
467467
</FrameworkSpecific>
468468

469469
<FrameworkSpecific framework='React'>
470470
```tsx
471-
return <DockviewReactComponent tabReorderMode="slide" />
471+
return <DockviewReactComponent smoothTabReorder={true} />
472472
```
473473
</FrameworkSpecific>
474474

475475
<FrameworkSpecific framework='Vue'>
476476
```html
477-
<dockview-vue tabReorderMode="slide" />
477+
<dockview-vue :smoothTabReorder="true" />
478478
```
479479
</FrameworkSpecific>
480480

481481
<FrameworkSpecific framework='Angular'>
482482
```html
483-
<dv-dockview [tabReorderMode]="'slide'"></dv-dockview>
483+
<dv-dockview [smoothTabReorder]="true"></dv-dockview>
484484
```
485485
</FrameworkSpecific>
486486

487487
<FrameworkSpecific framework='JavaScript'>
488488
```ts
489489
const api = createDockview(parentElement, {
490-
tabReorderMode: 'slide',
490+
smoothTabReorder: true,
491491
});
492492

493493
// or toggle at runtime
494-
api.updateOptions({ tabReorderMode: 'slide' });
494+
api.updateOptions({ smoothTabReorder: true });
495495
```
496496
</FrameworkSpecific>
497497

0 commit comments

Comments
 (0)