chore: remove tab drag sortable runtime
This commit is contained in:
@@ -62,7 +62,6 @@
|
|||||||
"@vue-flow/minimap": "^1.5.4",
|
"@vue-flow/minimap": "^1.5.4",
|
||||||
"dompurify": "^3.3.1",
|
"dompurify": "^3.3.1",
|
||||||
"marked": "^17.0.1",
|
"marked": "^17.0.1",
|
||||||
"sortablejs": "catalog:",
|
|
||||||
"tippy.js": "catalog:",
|
"tippy.js": "catalog:",
|
||||||
"turndown": "^7.2.2"
|
"turndown": "^7.2.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -37,10 +37,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@vben-core/shared": "workspace:*",
|
"@vben-core/shared": "workspace:*",
|
||||||
"@vueuse/core": "catalog:",
|
"@vueuse/core": "catalog:",
|
||||||
"sortablejs": "catalog:",
|
|
||||||
"vue": "catalog:"
|
"vue": "catalog:"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/sortablejs": "catalog:"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
import type { SortableOptions } from 'sortablejs';
|
|
||||||
|
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
||||||
|
|
||||||
import { useSortable } from '../use-sortable';
|
|
||||||
|
|
||||||
describe('useSortable', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
vi.mock('sortablejs/modular/sortable.complete.esm.js', () => ({
|
|
||||||
default: {
|
|
||||||
create: vi.fn(),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
it('should call Sortable.create with the correct options', async () => {
|
|
||||||
// Create a mock element
|
|
||||||
const mockElement = document.createElement('div') as HTMLDivElement;
|
|
||||||
|
|
||||||
// Define custom options
|
|
||||||
const customOptions: SortableOptions = {
|
|
||||||
group: 'test-group',
|
|
||||||
sort: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use the useSortable function
|
|
||||||
const { initializeSortable } = useSortable(mockElement, customOptions);
|
|
||||||
|
|
||||||
// Initialize sortable
|
|
||||||
await initializeSortable();
|
|
||||||
|
|
||||||
// Import sortablejs to access the mocked create function
|
|
||||||
const Sortable =
|
|
||||||
await import('sortablejs/modular/sortable.complete.esm.js');
|
|
||||||
|
|
||||||
// Verify that Sortable.create was called with the correct parameters
|
|
||||||
expect(Sortable.default.create).toHaveBeenCalledTimes(1);
|
|
||||||
expect(Sortable.default.create).toHaveBeenCalledWith(
|
|
||||||
mockElement,
|
|
||||||
expect.objectContaining({
|
|
||||||
animation: 300,
|
|
||||||
delay: 400,
|
|
||||||
delayOnTouchOnly: true,
|
|
||||||
...customOptions,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -5,4 +5,3 @@ export * from './use-namespace';
|
|||||||
export * from './use-priority-value';
|
export * from './use-priority-value';
|
||||||
export * from './use-scroll-lock';
|
export * from './use-scroll-lock';
|
||||||
export * from './use-simple-locale';
|
export * from './use-simple-locale';
|
||||||
export * from './use-sortable';
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import type { SortableOptions } from 'sortablejs';
|
|
||||||
import type Sortable from 'sortablejs';
|
|
||||||
|
|
||||||
function useSortable<T extends HTMLElement>(
|
|
||||||
sortableContainer: T,
|
|
||||||
options: SortableOptions = {},
|
|
||||||
) {
|
|
||||||
const initializeSortable = async () => {
|
|
||||||
const Sortable = await import(
|
|
||||||
// @ts-expect-error - This is a dynamic import
|
|
||||||
'sortablejs/modular/sortable.complete.esm.js'
|
|
||||||
);
|
|
||||||
const sortable = Sortable?.default?.create?.(sortableContainer, {
|
|
||||||
animation: 300,
|
|
||||||
delay: 400,
|
|
||||||
delayOnTouchOnly: true,
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
return sortable as Sortable;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
initializeSortable,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export { useSortable };
|
|
||||||
|
|
||||||
export type { Sortable };
|
|
||||||
@@ -6,7 +6,6 @@ import { ChevronLeft, ChevronRight } from '@vben-core/icons';
|
|||||||
import { VbenScrollbar } from '@vben-core/shadcn-ui';
|
import { VbenScrollbar } from '@vben-core/shadcn-ui';
|
||||||
|
|
||||||
import { Tabs, TabsChrome } from './components';
|
import { Tabs, TabsChrome } from './components';
|
||||||
import { useTabsDrag } from './use-tabs-drag';
|
|
||||||
import { useTabsViewScroll } from './use-tabs-view-scroll';
|
import { useTabsViewScroll } from './use-tabs-view-scroll';
|
||||||
|
|
||||||
interface Props extends TabsProps {}
|
interface Props extends TabsProps {}
|
||||||
@@ -43,8 +42,6 @@ function onWheel(e: WheelEvent) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useTabsDrag(props, emit);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
import type { Sortable } from '@vben-core/composables';
|
|
||||||
import type { EmitType } from '@vben-core/typings';
|
|
||||||
|
|
||||||
import type { TabsProps } from './types';
|
|
||||||
|
|
||||||
import { nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
||||||
|
|
||||||
import { useIsMobile, useSortable } from '@vben-core/composables';
|
|
||||||
|
|
||||||
// 可能会找到拖拽的子元素,这里需要确保拖拽的dom时tab元素
|
|
||||||
function findParentElement(element: HTMLElement) {
|
|
||||||
const parentCls = 'group';
|
|
||||||
return element.classList.contains(parentCls)
|
|
||||||
? element
|
|
||||||
: element.closest(`.${parentCls}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useTabsDrag(props: TabsProps, emit: EmitType) {
|
|
||||||
const sortableInstance = ref<null | Sortable>(null);
|
|
||||||
|
|
||||||
async function initTabsSortable() {
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
const el = document.querySelectorAll(
|
|
||||||
`.${props.contentClass}`,
|
|
||||||
)?.[0] as HTMLElement;
|
|
||||||
|
|
||||||
if (!el) {
|
|
||||||
console.warn('Element not found for sortable initialization');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetElState = async () => {
|
|
||||||
el.style.cursor = 'default';
|
|
||||||
// el.classList.remove('dragging');
|
|
||||||
el.querySelector('.draggable')?.classList.remove('dragging');
|
|
||||||
};
|
|
||||||
|
|
||||||
const { initializeSortable } = useSortable(el, {
|
|
||||||
filter: (_evt, target: HTMLElement) => {
|
|
||||||
const parent = findParentElement(target);
|
|
||||||
const draggable = parent?.classList.contains('draggable');
|
|
||||||
return !draggable || !props.draggable;
|
|
||||||
},
|
|
||||||
onEnd(evt) {
|
|
||||||
const { newIndex, oldIndex } = evt;
|
|
||||||
// const fromElement = evt.item;
|
|
||||||
const { srcElement } = (evt as any).originalEvent;
|
|
||||||
|
|
||||||
if (!srcElement) {
|
|
||||||
resetElState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const srcParent = findParentElement(srcElement);
|
|
||||||
|
|
||||||
if (!srcParent) {
|
|
||||||
resetElState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!srcParent.classList.contains('draggable')) {
|
|
||||||
resetElState();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
oldIndex !== undefined &&
|
|
||||||
newIndex !== undefined &&
|
|
||||||
!Number.isNaN(oldIndex) &&
|
|
||||||
!Number.isNaN(newIndex) &&
|
|
||||||
oldIndex !== newIndex
|
|
||||||
) {
|
|
||||||
emit('sortTabs', oldIndex, newIndex);
|
|
||||||
}
|
|
||||||
resetElState();
|
|
||||||
},
|
|
||||||
onMove(evt) {
|
|
||||||
const parent = findParentElement(evt.related);
|
|
||||||
if (parent?.classList.contains('draggable') && props.draggable) {
|
|
||||||
const isCurrentAffix = evt.dragged.classList.contains('affix-tab');
|
|
||||||
const isRelatedAffix = evt.related.classList.contains('affix-tab');
|
|
||||||
// 不允许在固定的tab和非固定的tab之间互相拖拽
|
|
||||||
return isCurrentAffix === isRelatedAffix;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onStart: () => {
|
|
||||||
el.style.cursor = 'grabbing';
|
|
||||||
el.querySelector('.draggable')?.classList.add('dragging');
|
|
||||||
// el.classList.add('dragging');
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
sortableInstance.value = await initializeSortable();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
const { isMobile } = useIsMobile();
|
|
||||||
|
|
||||||
// 移动端下tab不需要拖拽
|
|
||||||
if (isMobile.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await nextTick();
|
|
||||||
initTabsSortable();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(init);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.styleType,
|
|
||||||
() => {
|
|
||||||
sortableInstance.value?.destroy();
|
|
||||||
init();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
sortableInstance.value?.destroy();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Generated
+2
-22
@@ -105,9 +105,6 @@ catalogs:
|
|||||||
'@types/qs':
|
'@types/qs':
|
||||||
specifier: ^6.14.0
|
specifier: ^6.14.0
|
||||||
version: 6.14.0
|
version: 6.14.0
|
||||||
'@types/sortablejs':
|
|
||||||
specifier: ^1.15.8
|
|
||||||
version: 1.15.9
|
|
||||||
'@typescript-eslint/eslint-plugin':
|
'@typescript-eslint/eslint-plugin':
|
||||||
specifier: ^8.35.1
|
specifier: ^8.35.1
|
||||||
version: 8.48.0
|
version: 8.48.0
|
||||||
@@ -375,9 +372,6 @@ catalogs:
|
|||||||
sass:
|
sass:
|
||||||
specifier: ^1.89.2
|
specifier: ^1.89.2
|
||||||
version: 1.94.2
|
version: 1.94.2
|
||||||
sortablejs:
|
|
||||||
specifier: 1.15.6
|
|
||||||
version: 1.15.6
|
|
||||||
stylelint:
|
stylelint:
|
||||||
specifier: ^16.21.0
|
specifier: ^16.21.0
|
||||||
version: 16.26.1
|
version: 16.26.1
|
||||||
@@ -532,9 +526,6 @@ importers:
|
|||||||
marked:
|
marked:
|
||||||
specifier: ^17.0.1
|
specifier: ^17.0.1
|
||||||
version: 17.0.1
|
version: 17.0.1
|
||||||
sortablejs:
|
|
||||||
specifier: 'catalog:'
|
|
||||||
version: 1.15.6
|
|
||||||
tippy.js:
|
tippy.js:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 6.3.7
|
version: 6.3.7
|
||||||
@@ -1196,16 +1187,9 @@ importers:
|
|||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 13.4.0(vue@3.5.25(typescript@5.9.3))
|
version: 13.4.0(vue@3.5.25(typescript@5.9.3))
|
||||||
sortablejs:
|
|
||||||
specifier: 'catalog:'
|
|
||||||
version: 1.15.6
|
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.5.17
|
specifier: ^3.5.17
|
||||||
version: 3.5.25(typescript@5.9.3)
|
version: 3.5.25(typescript@5.9.3)
|
||||||
devDependencies:
|
|
||||||
'@types/sortablejs':
|
|
||||||
specifier: 'catalog:'
|
|
||||||
version: 1.15.9
|
|
||||||
|
|
||||||
packages/@core/preferences:
|
packages/@core/preferences:
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -4334,9 +4318,6 @@ packages:
|
|||||||
'@types/resolve@1.20.2':
|
'@types/resolve@1.20.2':
|
||||||
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
|
||||||
|
|
||||||
'@types/sortablejs@1.15.9':
|
|
||||||
resolution: {integrity: sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==}
|
|
||||||
|
|
||||||
'@types/trusted-types@2.0.7':
|
'@types/trusted-types@2.0.7':
|
||||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||||
|
|
||||||
@@ -13775,8 +13756,6 @@ snapshots:
|
|||||||
|
|
||||||
'@types/resolve@1.20.2': {}
|
'@types/resolve@1.20.2': {}
|
||||||
|
|
||||||
'@types/sortablejs@1.15.9': {}
|
|
||||||
|
|
||||||
'@types/trusted-types@2.0.7': {}
|
'@types/trusted-types@2.0.7': {}
|
||||||
|
|
||||||
'@types/turndown@5.0.6': {}
|
'@types/turndown@5.0.6': {}
|
||||||
@@ -19318,7 +19297,8 @@ snapshots:
|
|||||||
|
|
||||||
smob@1.5.0: {}
|
smob@1.5.0: {}
|
||||||
|
|
||||||
sortablejs@1.15.6: {}
|
sortablejs@1.15.6:
|
||||||
|
optional: true
|
||||||
|
|
||||||
source-map-js@1.2.1: {}
|
source-map-js@1.2.1: {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user