mirror of
https://github.com/certd/certd.git
synced 2026-05-17 05:37:30 +08:00
chore: code format
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import type { DrawerState } from '../drawer';
|
||||
import type { DrawerState } from "../drawer";
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { DrawerApi } from '../drawer-api';
|
||||
import { DrawerApi } from "../drawer-api";
|
||||
|
||||
// 模拟 Store 类
|
||||
vi.mock('/@/vben/shared/store', () => {
|
||||
vi.mock("/@/vben/shared/store", () => {
|
||||
return {
|
||||
isFunction: (fn: any) => typeof fn === 'function',
|
||||
isFunction: (fn: any) => typeof fn === "function",
|
||||
Store: class {
|
||||
get state() {
|
||||
return this._state;
|
||||
@@ -33,7 +33,7 @@ vi.mock('/@/vben/shared/store', () => {
|
||||
};
|
||||
});
|
||||
|
||||
describe('drawerApi', () => {
|
||||
describe("drawerApi", () => {
|
||||
let drawerApi: DrawerApi;
|
||||
let drawerState: DrawerState;
|
||||
|
||||
@@ -42,24 +42,24 @@ describe('drawerApi', () => {
|
||||
drawerState = drawerApi.store.state;
|
||||
});
|
||||
|
||||
it('should initialize with default state', () => {
|
||||
it("should initialize with default state", () => {
|
||||
expect(drawerState.isOpen).toBe(false);
|
||||
expect(drawerState.cancelText).toBe(undefined);
|
||||
expect(drawerState.confirmText).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should open the drawer', () => {
|
||||
it("should open the drawer", () => {
|
||||
drawerApi.open();
|
||||
expect(drawerApi.store.state.isOpen).toBe(true);
|
||||
});
|
||||
|
||||
it('should close the drawer if onBeforeClose allows it', () => {
|
||||
it("should close the drawer if onBeforeClose allows it", () => {
|
||||
drawerApi.open();
|
||||
drawerApi.close();
|
||||
expect(drawerApi.store.state.isOpen).toBe(false);
|
||||
});
|
||||
|
||||
it('should not close the drawer if onBeforeClose returns false', () => {
|
||||
it("should not close the drawer if onBeforeClose returns false", () => {
|
||||
const onBeforeClose = vi.fn(() => false);
|
||||
const drawerApiWithHook = new DrawerApi({ onBeforeClose });
|
||||
drawerApiWithHook.open();
|
||||
@@ -68,7 +68,7 @@ describe('drawerApi', () => {
|
||||
expect(onBeforeClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger onCancel and keep drawer open if onCancel is provided', () => {
|
||||
it("should trigger onCancel and keep drawer open if onCancel is provided", () => {
|
||||
const onCancel = vi.fn();
|
||||
const drawerApiWithHook = new DrawerApi({ onCancel });
|
||||
drawerApiWithHook.open();
|
||||
@@ -77,37 +77,37 @@ describe('drawerApi', () => {
|
||||
expect(drawerApiWithHook.store.state.isOpen).toBe(true); // 关闭逻辑不在 onCancel 内
|
||||
});
|
||||
|
||||
it('should update shared data correctly', () => {
|
||||
const testData = { key: 'value' };
|
||||
it("should update shared data correctly", () => {
|
||||
const testData = { key: "value" };
|
||||
drawerApi.setData(testData);
|
||||
expect(drawerApi.getData()).toEqual(testData);
|
||||
});
|
||||
|
||||
it('should set state correctly using an object', () => {
|
||||
drawerApi.setState({ title: 'New Title' });
|
||||
expect(drawerApi.store.state.title).toBe('New Title');
|
||||
it("should set state correctly using an object", () => {
|
||||
drawerApi.setState({ title: "New Title" });
|
||||
expect(drawerApi.store.state.title).toBe("New Title");
|
||||
});
|
||||
|
||||
it('should set state correctly using a function', () => {
|
||||
drawerApi.setState((prev) => ({ ...prev, confirmText: 'Yes' }));
|
||||
expect(drawerApi.store.state.confirmText).toBe('Yes');
|
||||
it("should set state correctly using a function", () => {
|
||||
drawerApi.setState(prev => ({ ...prev, confirmText: "Yes" }));
|
||||
expect(drawerApi.store.state.confirmText).toBe("Yes");
|
||||
});
|
||||
|
||||
it('should call onOpenChange when state changes', () => {
|
||||
it("should call onOpenChange when state changes", () => {
|
||||
const onOpenChange = vi.fn();
|
||||
const drawerApiWithHook = new DrawerApi({ onOpenChange });
|
||||
drawerApiWithHook.open();
|
||||
expect(onOpenChange).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should call onClosed callback when provided', () => {
|
||||
it("should call onClosed callback when provided", () => {
|
||||
const onClosed = vi.fn();
|
||||
const drawerApiWithHook = new DrawerApi({ onClosed });
|
||||
drawerApiWithHook.onClosed();
|
||||
expect(onClosed).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call onOpened callback when provided', () => {
|
||||
it("should call onOpened callback when provided", () => {
|
||||
const onOpened = vi.fn();
|
||||
const drawerApiWithHook = new DrawerApi({ onOpened });
|
||||
drawerApiWithHook.open();
|
||||
|
||||
@@ -6,7 +6,7 @@ import { bindMethods, isFunction } from "/@/vben/shared/utils";
|
||||
export class DrawerApi {
|
||||
// 共享数据
|
||||
public sharedData: Record<"payload", any> = {
|
||||
payload: {}
|
||||
payload: {},
|
||||
};
|
||||
public store: Store<DrawerState>;
|
||||
|
||||
@@ -34,13 +34,13 @@ export class DrawerApi {
|
||||
placement: "right",
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
title: ""
|
||||
title: "",
|
||||
};
|
||||
|
||||
this.store = new Store<DrawerState>(
|
||||
{
|
||||
...defaultState,
|
||||
...storeState
|
||||
...storeState,
|
||||
},
|
||||
{
|
||||
onUpdate: () => {
|
||||
@@ -51,7 +51,7 @@ export class DrawerApi {
|
||||
this.state = state;
|
||||
this.api.onOpenChange?.(!!state?.isOpen);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
this.state = this.store.state;
|
||||
@@ -61,7 +61,7 @@ export class DrawerApi {
|
||||
onClosed,
|
||||
onConfirm,
|
||||
onOpenChange,
|
||||
onOpened
|
||||
onOpened,
|
||||
};
|
||||
bindMethods(this);
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export class DrawerApi {
|
||||
// 如果 onBeforeClose 返回 false,则不关闭弹窗
|
||||
const allowClose = this.api.onBeforeClose?.() ?? true;
|
||||
if (allowClose) {
|
||||
this.store.setState((prev) => ({ ...prev, isOpen: false }));
|
||||
this.store.setState(prev => ({ ...prev, isOpen: false }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ export class DrawerApi {
|
||||
}
|
||||
|
||||
open() {
|
||||
this.store.setState((prev) => ({ ...prev, isOpen: true }));
|
||||
this.store.setState(prev => ({ ...prev, isOpen: true }));
|
||||
}
|
||||
|
||||
setData<T>(payload: T) {
|
||||
@@ -131,7 +131,7 @@ export class DrawerApi {
|
||||
if (isFunction(stateOrFn)) {
|
||||
this.store.setState(stateOrFn);
|
||||
} else {
|
||||
this.store.setState((prev) => ({ ...prev, ...stateOrFn }));
|
||||
this.store.setState(prev => ({ ...prev, ...stateOrFn }));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
appendToMain: false,
|
||||
closeIconPlacement: "right",
|
||||
drawerApi: undefined,
|
||||
zIndex: 1000
|
||||
zIndex: 1000,
|
||||
});
|
||||
|
||||
const components = globalShareState.getComponents();
|
||||
@@ -57,16 +57,16 @@ const {
|
||||
showConfirmButton,
|
||||
title,
|
||||
titleTooltip,
|
||||
zIndex
|
||||
zIndex,
|
||||
} = usePriorityValues(props, state);
|
||||
|
||||
watch(
|
||||
() => showLoading.value,
|
||||
(v) => {
|
||||
v => {
|
||||
if (v && wrapperRef.value) {
|
||||
wrapperRef.value.scrollTo({
|
||||
// behavior: 'smooth',
|
||||
top: 0
|
||||
top: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ const getAppendTo = computed(() => {
|
||||
:class="
|
||||
cn('flex w-[520px] flex-col', drawerClass, {
|
||||
'!w-full': isMobile || placement === 'bottom' || placement === 'top',
|
||||
'max-h-[100vh]': placement === 'bottom' || placement === 'top'
|
||||
'max-h-[100vh]': placement === 'bottom' || placement === 'top',
|
||||
})
|
||||
"
|
||||
:modal="modal"
|
||||
@@ -135,7 +135,7 @@ const getAppendTo = computed(() => {
|
||||
:class="
|
||||
cn('!flex flex-row items-center justify-between border-b px-6 py-5', headerClass, {
|
||||
'px-4 py-3': closable,
|
||||
'pl-2': closable && closeIconPlacement === 'left'
|
||||
'pl-2': closable && closeIconPlacement === 'left',
|
||||
})
|
||||
"
|
||||
>
|
||||
@@ -198,7 +198,7 @@ const getAppendTo = computed(() => {
|
||||
ref="wrapperRef"
|
||||
:class="
|
||||
cn('relative flex-1 overflow-y-auto p-3', contentClass, {
|
||||
'overflow-hidden': showLoading
|
||||
'overflow-hidden': showLoading,
|
||||
})
|
||||
"
|
||||
>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export type * from './drawer';
|
||||
export { default as VbenDrawer } from './drawer.vue';
|
||||
export { setDefaultDrawerProps, useVbenDrawer } from './use-drawer';
|
||||
export type * from "./drawer";
|
||||
export { default as VbenDrawer } from "./drawer.vue";
|
||||
export { setDefaultDrawerProps, useVbenDrawer } from "./use-drawer";
|
||||
|
||||
@@ -36,18 +36,18 @@ export function useVbenDrawer<TParentDrawerProps extends DrawerProps = DrawerPro
|
||||
isDrawerReady.value = false;
|
||||
await nextTick();
|
||||
isDrawerReady.value = true;
|
||||
}
|
||||
},
|
||||
});
|
||||
checkProps(extendedApi as ExtendedDrawerApi, {
|
||||
...props,
|
||||
...attrs,
|
||||
...slots
|
||||
...slots,
|
||||
});
|
||||
return () => h(isDrawerReady.value ? connectedComponent : "div", { ...props, ...attrs }, slots);
|
||||
},
|
||||
{
|
||||
name: "VbenParentDrawer",
|
||||
inheritAttrs: false
|
||||
inheritAttrs: false,
|
||||
}
|
||||
);
|
||||
return [Drawer, extendedApi as ExtendedDrawerApi] as const;
|
||||
@@ -58,7 +58,7 @@ export function useVbenDrawer<TParentDrawerProps extends DrawerProps = DrawerPro
|
||||
const mergedOptions = {
|
||||
...DEFAULT_DRAWER_PROPS,
|
||||
...injectData.options,
|
||||
...options
|
||||
...options,
|
||||
} as DrawerApiOptions;
|
||||
|
||||
mergedOptions.onOpenChange = (isOpen: boolean) => {
|
||||
@@ -77,7 +77,7 @@ export function useVbenDrawer<TParentDrawerProps extends DrawerProps = DrawerPro
|
||||
|
||||
const extendedApi: ExtendedDrawerApi = api as never;
|
||||
|
||||
extendedApi.useStore = (selector) => {
|
||||
extendedApi.useStore = selector => {
|
||||
return useStore(api.store, selector);
|
||||
};
|
||||
|
||||
@@ -87,7 +87,7 @@ export function useVbenDrawer<TParentDrawerProps extends DrawerProps = DrawerPro
|
||||
},
|
||||
{
|
||||
name: "VbenDrawer",
|
||||
inheritAttrs: false
|
||||
inheritAttrs: false,
|
||||
}
|
||||
);
|
||||
injectData.extendApi?.(extendedApi);
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from './drawer';
|
||||
export * from './modal';
|
||||
export * from "./drawer";
|
||||
export * from "./modal";
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { ModalState } from '../modal';
|
||||
import type { ModalState } from "../modal";
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { ModalApi } from '../modal-api';
|
||||
import { ModalApi } from "../modal-api";
|
||||
|
||||
vi.mock('/@/vben/shared/store', () => {
|
||||
vi.mock("/@/vben/shared/store", () => {
|
||||
return {
|
||||
isFunction: (fn: any) => typeof fn === 'function',
|
||||
isFunction: (fn: any) => typeof fn === "function",
|
||||
Store: class {
|
||||
get state() {
|
||||
return this._state;
|
||||
@@ -32,7 +32,7 @@ vi.mock('/@/vben/shared/store', () => {
|
||||
};
|
||||
});
|
||||
|
||||
describe('modalApi', () => {
|
||||
describe("modalApi", () => {
|
||||
let modalApi: ModalApi;
|
||||
// 使用 modalState 而不是 state
|
||||
let modalState: ModalState;
|
||||
@@ -43,23 +43,23 @@ describe('modalApi', () => {
|
||||
modalState = modalApi.store.state;
|
||||
});
|
||||
|
||||
it('should initialize with default state', () => {
|
||||
it("should initialize with default state", () => {
|
||||
expect(modalState.isOpen).toBe(false);
|
||||
expect(modalState.cancelText).toBe(undefined);
|
||||
expect(modalState.confirmText).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should open the modal', () => {
|
||||
it("should open the modal", () => {
|
||||
modalApi.open();
|
||||
expect(modalApi.store.state.isOpen).toBe(true);
|
||||
});
|
||||
|
||||
it('should close the modal if onBeforeClose allows it', () => {
|
||||
it("should close the modal if onBeforeClose allows it", () => {
|
||||
modalApi.close();
|
||||
expect(modalApi.store.state.isOpen).toBe(false);
|
||||
});
|
||||
|
||||
it('should not close the modal if onBeforeClose returns false', () => {
|
||||
it("should not close the modal if onBeforeClose returns false", () => {
|
||||
const onBeforeClose = vi.fn(() => false);
|
||||
const modalApiWithHook = new ModalApi({ onBeforeClose });
|
||||
modalApiWithHook.open();
|
||||
@@ -68,7 +68,7 @@ describe('modalApi', () => {
|
||||
expect(onBeforeClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should trigger onCancel and close the modal if no onCancel hook is provided', () => {
|
||||
it("should trigger onCancel and close the modal if no onCancel hook is provided", () => {
|
||||
const onCancel = vi.fn();
|
||||
const modalApiWithHook = new ModalApi({ onCancel });
|
||||
modalApiWithHook.open();
|
||||
@@ -77,37 +77,37 @@ describe('modalApi', () => {
|
||||
expect(modalApiWithHook.store.state.isOpen).toBe(true);
|
||||
});
|
||||
|
||||
it('should update shared data correctly', () => {
|
||||
const testData = { key: 'value' };
|
||||
it("should update shared data correctly", () => {
|
||||
const testData = { key: "value" };
|
||||
modalApi.setData(testData);
|
||||
expect(modalApi.getData()).toEqual(testData);
|
||||
});
|
||||
|
||||
it('should set state correctly using an object', () => {
|
||||
modalApi.setState({ title: 'New Title' });
|
||||
expect(modalApi.store.state.title).toBe('New Title');
|
||||
it("should set state correctly using an object", () => {
|
||||
modalApi.setState({ title: "New Title" });
|
||||
expect(modalApi.store.state.title).toBe("New Title");
|
||||
});
|
||||
|
||||
it('should set state correctly using a function', () => {
|
||||
modalApi.setState((prev) => ({ ...prev, confirmText: 'Yes' }));
|
||||
expect(modalApi.store.state.confirmText).toBe('Yes');
|
||||
it("should set state correctly using a function", () => {
|
||||
modalApi.setState(prev => ({ ...prev, confirmText: "Yes" }));
|
||||
expect(modalApi.store.state.confirmText).toBe("Yes");
|
||||
});
|
||||
|
||||
it('should call onOpenChange when state changes', () => {
|
||||
it("should call onOpenChange when state changes", () => {
|
||||
const onOpenChange = vi.fn();
|
||||
const modalApiWithHook = new ModalApi({ onOpenChange });
|
||||
modalApiWithHook.open();
|
||||
expect(onOpenChange).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should call onClosed callback when provided', () => {
|
||||
it("should call onClosed callback when provided", () => {
|
||||
const onClosed = vi.fn();
|
||||
const modalApiWithHook = new ModalApi({ onClosed });
|
||||
modalApiWithHook.onClosed();
|
||||
expect(onClosed).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call onOpened callback when provided', () => {
|
||||
it("should call onOpened callback when provided", () => {
|
||||
const onOpened = vi.fn();
|
||||
const modalApiWithHook = new ModalApi({ onOpened });
|
||||
modalApiWithHook.open();
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export type * from './modal';
|
||||
export { default as VbenModal } from './modal.vue';
|
||||
export { setDefaultModalProps, useVbenModal } from './use-modal';
|
||||
export type * from "./modal";
|
||||
export { default as VbenModal } from "./modal.vue";
|
||||
export { setDefaultModalProps, useVbenModal } from "./use-modal";
|
||||
|
||||
@@ -1,63 +1,46 @@
|
||||
import type { ModalApiOptions, ModalState } from './modal';
|
||||
import type { ModalApiOptions, ModalState } from "./modal";
|
||||
|
||||
import { Store } from '/@/vben/shared/store';
|
||||
import { bindMethods, isFunction } from '/@/vben/shared/utils';
|
||||
import { Store } from "/@/vben/shared/store";
|
||||
import { bindMethods, isFunction } from "/@/vben/shared/utils";
|
||||
|
||||
export class ModalApi {
|
||||
// 共享数据
|
||||
public sharedData: Record<'payload', any> = {
|
||||
public sharedData: Record<"payload", any> = {
|
||||
payload: {},
|
||||
};
|
||||
public store: Store<ModalState>;
|
||||
|
||||
private api: Pick<
|
||||
ModalApiOptions,
|
||||
| 'onBeforeClose'
|
||||
| 'onCancel'
|
||||
| 'onClosed'
|
||||
| 'onConfirm'
|
||||
| 'onOpenChange'
|
||||
| 'onOpened'
|
||||
>;
|
||||
private api: Pick<ModalApiOptions, "onBeforeClose" | "onCancel" | "onClosed" | "onConfirm" | "onOpenChange" | "onOpened">;
|
||||
|
||||
// private prevState!: ModalState;
|
||||
private state!: ModalState;
|
||||
|
||||
constructor(options: ModalApiOptions = {}) {
|
||||
const {
|
||||
connectedComponent: _,
|
||||
onBeforeClose,
|
||||
onCancel,
|
||||
onClosed,
|
||||
onConfirm,
|
||||
onOpenChange,
|
||||
onOpened,
|
||||
...storeState
|
||||
} = options;
|
||||
const { connectedComponent: _, onBeforeClose, onCancel, onClosed, onConfirm, onOpenChange, onOpened, ...storeState } = options;
|
||||
|
||||
const defaultState: ModalState = {
|
||||
bordered: true,
|
||||
centered: false,
|
||||
class: '',
|
||||
class: "",
|
||||
closeOnClickModal: true,
|
||||
closeOnPressEscape: true,
|
||||
confirmDisabled: false,
|
||||
confirmLoading: false,
|
||||
contentClass: '',
|
||||
contentClass: "",
|
||||
draggable: false,
|
||||
footer: true,
|
||||
footerClass: '',
|
||||
footerClass: "",
|
||||
fullscreen: false,
|
||||
fullscreenButton: true,
|
||||
header: true,
|
||||
headerClass: '',
|
||||
headerClass: "",
|
||||
isOpen: false,
|
||||
loading: false,
|
||||
modal: true,
|
||||
openAutoFocus: false,
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
title: '',
|
||||
title: "",
|
||||
};
|
||||
|
||||
this.store = new Store<ModalState>(
|
||||
@@ -77,7 +60,7 @@ export class ModalApi {
|
||||
this.api.onOpenChange?.(!!state?.isOpen);
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
this.state = this.store.state;
|
||||
@@ -102,7 +85,7 @@ export class ModalApi {
|
||||
// 如果 onBeforeClose 返回 false,则不关闭弹窗
|
||||
const allowClose = (await this.api.onBeforeClose?.()) ?? true;
|
||||
if (allowClose) {
|
||||
this.store.setState((prev) => ({
|
||||
this.store.setState(prev => ({
|
||||
...prev,
|
||||
isOpen: false,
|
||||
submitting: false,
|
||||
@@ -160,7 +143,7 @@ export class ModalApi {
|
||||
}
|
||||
|
||||
open() {
|
||||
this.store.setState((prev) => ({ ...prev, isOpen: true }));
|
||||
this.store.setState(prev => ({ ...prev, isOpen: true }));
|
||||
}
|
||||
|
||||
setData<T>(payload: T) {
|
||||
@@ -168,15 +151,11 @@ export class ModalApi {
|
||||
return this;
|
||||
}
|
||||
|
||||
setState(
|
||||
stateOrFn:
|
||||
| ((prev: ModalState) => Partial<ModalState>)
|
||||
| Partial<ModalState>,
|
||||
) {
|
||||
setState(stateOrFn: ((prev: ModalState) => Partial<ModalState>) | Partial<ModalState>) {
|
||||
if (isFunction(stateOrFn)) {
|
||||
this.store.setState(stateOrFn);
|
||||
} else {
|
||||
this.store.setState((prev) => ({ ...prev, ...stateOrFn }));
|
||||
this.store.setState(prev => ({ ...prev, ...stateOrFn }));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { Component, Ref } from 'vue';
|
||||
import type { Component, Ref } from "vue";
|
||||
|
||||
import type { MaybePromise } from '/@/vben/typings';
|
||||
import type { MaybePromise } from "/@/vben/typings";
|
||||
|
||||
import type { ModalApi } from './modal-api';
|
||||
import type { ModalApi } from "./modal-api";
|
||||
|
||||
export interface ModalProps {
|
||||
/**
|
||||
@@ -143,9 +143,7 @@ export interface ModalState extends ModalProps {
|
||||
}
|
||||
|
||||
export type ExtendedModalApi = ModalApi & {
|
||||
useStore: <T = NoInfer<ModalState>>(
|
||||
selector?: (state: NoInfer<ModalState>) => T,
|
||||
) => Readonly<Ref<T>>;
|
||||
useStore: <T = NoInfer<ModalState>>(selector?: (state: NoInfer<ModalState>) => T) => Readonly<Ref<T>>;
|
||||
};
|
||||
|
||||
export interface ModalApiOptions extends ModalState {
|
||||
|
||||
@@ -1,32 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ExtendedModalApi, ModalProps } from './modal';
|
||||
import type { ExtendedModalApi, ModalProps } from "./modal";
|
||||
|
||||
import { computed, nextTick, provide, ref, useId, watch } from 'vue';
|
||||
import { computed, nextTick, provide, ref, useId, watch } from "vue";
|
||||
|
||||
import {
|
||||
useIsMobile,
|
||||
usePriorityValues,
|
||||
useSimpleLocale,
|
||||
} from '/@/vben/composables';
|
||||
import { Expand, Shrink } from '/@/vben/icons';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
VbenButton,
|
||||
VbenHelpTooltip,
|
||||
VbenIconButton,
|
||||
VbenLoading,
|
||||
VisuallyHidden,
|
||||
} from '/@/vben/shadcn-ui';
|
||||
import { ELEMENT_ID_MAIN_CONTENT } from '/@/vben/shared/constants';
|
||||
import { globalShareState } from '/@/vben/shared/global-state';
|
||||
import { cn } from '/@/vben/shared/utils';
|
||||
import { useIsMobile, usePriorityValues, useSimpleLocale } from "/@/vben/composables";
|
||||
import { Expand, Shrink } from "/@/vben/icons";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, VbenButton, VbenHelpTooltip, VbenIconButton, VbenLoading, VisuallyHidden } from "/@/vben/shadcn-ui";
|
||||
import { ELEMENT_ID_MAIN_CONTENT } from "/@/vben/shared/constants";
|
||||
import { globalShareState } from "/@/vben/shared/global-state";
|
||||
import { cn } from "/@/vben/shared/utils";
|
||||
|
||||
import { useModalDraggable } from './use-modal-draggable';
|
||||
import { useModalDraggable } from "./use-modal-draggable";
|
||||
|
||||
interface Props extends ModalProps {
|
||||
modalApi?: ExtendedModalApi;
|
||||
@@ -47,7 +31,7 @@ const footerRef = ref();
|
||||
|
||||
const id = useId();
|
||||
|
||||
provide('DISMISSABLE_MODAL_ID', id);
|
||||
provide("DISMISSABLE_MODAL_ID", id);
|
||||
|
||||
const { $t } = useSimpleLocale();
|
||||
const { isMobile } = useIsMobile();
|
||||
@@ -86,23 +70,15 @@ const {
|
||||
zIndex,
|
||||
} = usePriorityValues(props, state);
|
||||
|
||||
const shouldFullscreen = computed(
|
||||
() => (fullscreen.value && header.value) || isMobile.value,
|
||||
);
|
||||
const shouldFullscreen = computed(() => (fullscreen.value && header.value) || isMobile.value);
|
||||
|
||||
const shouldDraggable = computed(
|
||||
() => draggable.value && !shouldFullscreen.value && header.value,
|
||||
);
|
||||
const shouldDraggable = computed(() => draggable.value && !shouldFullscreen.value && header.value);
|
||||
|
||||
const { dragging, transform } = useModalDraggable(
|
||||
dialogRef,
|
||||
headerRef,
|
||||
shouldDraggable,
|
||||
);
|
||||
const { dragging, transform } = useModalDraggable(dialogRef, headerRef, shouldDraggable);
|
||||
|
||||
watch(
|
||||
() => state?.value?.isOpen,
|
||||
async (v) => {
|
||||
async v => {
|
||||
if (v) {
|
||||
await nextTick();
|
||||
if (!contentRef.value) return;
|
||||
@@ -112,7 +88,7 @@ watch(
|
||||
const { offsetX, offsetY } = transform;
|
||||
dialogRef.value.style.transform = `translate(${offsetX}px, ${offsetY}px)`;
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
@@ -124,11 +100,11 @@ watch(
|
||||
top: 0,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function handleFullscreen() {
|
||||
props.modalApi?.setState((prev) => {
|
||||
props.modalApi?.setState(prev => {
|
||||
// if (prev.fullscreen) {
|
||||
// resetPosition();
|
||||
// }
|
||||
@@ -157,11 +133,7 @@ function handerOpenAutoFocus(e: Event) {
|
||||
function pointerDownOutside(e: Event) {
|
||||
const target = e.target as HTMLElement;
|
||||
const isDismissableModal = target?.dataset.dismissableModal;
|
||||
if (
|
||||
!closeOnClickModal.value ||
|
||||
isDismissableModal !== id ||
|
||||
submitting.value
|
||||
) {
|
||||
if (!closeOnClickModal.value || isDismissableModal !== id || submitting.value) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
@@ -176,27 +148,18 @@ const getAppendTo = computed(() => {
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<Dialog
|
||||
:modal="false"
|
||||
:open="state?.isOpen"
|
||||
@update:open="() => (!submitting ? modalApi?.close() : undefined)"
|
||||
>
|
||||
<Dialog :modal="false" :open="state?.isOpen" @update:open="() => (!submitting ? modalApi?.close() : undefined)">
|
||||
<DialogContent
|
||||
ref="contentRef"
|
||||
:append-to="getAppendTo"
|
||||
:class="
|
||||
cn(
|
||||
'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-[var(--radius)]',
|
||||
modalClass,
|
||||
{
|
||||
'border-border border': bordered,
|
||||
'shadow-3xl': !bordered,
|
||||
'left-0 top-0 size-full max-h-full !translate-x-0 !translate-y-0':
|
||||
shouldFullscreen,
|
||||
'top-1/2 !-translate-y-1/2': centered && !shouldFullscreen,
|
||||
'duration-300': !dragging,
|
||||
},
|
||||
)
|
||||
cn('left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-[var(--radius)]', modalClass, {
|
||||
'border-border border': bordered,
|
||||
'shadow-3xl': !bordered,
|
||||
'left-0 top-0 size-full max-h-full !translate-x-0 !translate-y-0': shouldFullscreen,
|
||||
'top-1/2 !-translate-y-1/2': centered && !shouldFullscreen,
|
||||
'duration-300': !dragging,
|
||||
})
|
||||
"
|
||||
:modal="modal"
|
||||
:open="state?.isOpen"
|
||||
@@ -223,7 +186,7 @@ const getAppendTo = computed(() => {
|
||||
hidden: !header,
|
||||
'cursor-move select-none': shouldDraggable,
|
||||
},
|
||||
headerClass,
|
||||
headerClass
|
||||
)
|
||||
"
|
||||
>
|
||||
@@ -256,11 +219,7 @@ const getAppendTo = computed(() => {
|
||||
})
|
||||
"
|
||||
>
|
||||
<VbenLoading
|
||||
v-if="showLoading || submitting"
|
||||
class="size-full h-auto min-h-full"
|
||||
spinning
|
||||
/>
|
||||
<VbenLoading v-if="showLoading || submitting" class="size-full h-auto min-h-full" spinning />
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
@@ -282,33 +241,21 @@ const getAppendTo = computed(() => {
|
||||
{
|
||||
'border-t': bordered,
|
||||
},
|
||||
footerClass,
|
||||
footerClass
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot name="prepend-footer"></slot>
|
||||
<slot name="footer">
|
||||
<component
|
||||
:is="components.DefaultButton || VbenButton"
|
||||
v-if="showCancelButton"
|
||||
variant="ghost"
|
||||
:disabled="submitting"
|
||||
@click="() => modalApi?.onCancel()"
|
||||
>
|
||||
<component :is="components.DefaultButton || VbenButton" v-if="showCancelButton" variant="ghost" :disabled="submitting" @click="() => modalApi?.onCancel()">
|
||||
<slot name="cancelText">
|
||||
{{ cancelText || $t('cancel') }}
|
||||
{{ cancelText || $t("cancel") }}
|
||||
</slot>
|
||||
</component>
|
||||
|
||||
<component
|
||||
:is="components.PrimaryButton || VbenButton"
|
||||
v-if="showConfirmButton"
|
||||
:disabled="confirmDisabled"
|
||||
:loading="confirmLoading || submitting"
|
||||
@click="() => modalApi?.onConfirm()"
|
||||
>
|
||||
<component :is="components.PrimaryButton || VbenButton" v-if="showConfirmButton" :disabled="confirmDisabled" :loading="confirmLoading || submitting" @click="() => modalApi?.onConfirm()">
|
||||
<slot name="confirmText">
|
||||
{{ confirmText || $t('confirm') }}
|
||||
{{ confirmText || $t("confirm") }}
|
||||
</slot>
|
||||
</component>
|
||||
</slot>
|
||||
|
||||
@@ -3,17 +3,13 @@
|
||||
* 调整部分细节
|
||||
*/
|
||||
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import type { ComputedRef, Ref } from "vue";
|
||||
|
||||
import { onBeforeUnmount, onMounted, reactive, ref, watchEffect } from 'vue';
|
||||
import { onBeforeUnmount, onMounted, reactive, ref, watchEffect } from "vue";
|
||||
|
||||
import { unrefElement } from '@vueuse/core';
|
||||
import { unrefElement } from "@vueuse/core";
|
||||
|
||||
export function useModalDraggable(
|
||||
targetRef: Ref<HTMLElement | undefined>,
|
||||
dragRef: Ref<HTMLElement | undefined>,
|
||||
draggable: ComputedRef<boolean>,
|
||||
) {
|
||||
export function useModalDraggable(targetRef: Ref<HTMLElement | undefined>, dragRef: Ref<HTMLElement | undefined>, draggable: ComputedRef<boolean>) {
|
||||
const transform = reactive({
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
@@ -63,25 +59,25 @@ export function useModalDraggable(
|
||||
|
||||
const onMouseup = () => {
|
||||
dragging.value = false;
|
||||
document.removeEventListener('mousemove', onMousemove);
|
||||
document.removeEventListener('mouseup', onMouseup);
|
||||
document.removeEventListener("mousemove", onMousemove);
|
||||
document.removeEventListener("mouseup", onMouseup);
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMousemove);
|
||||
document.addEventListener('mouseup', onMouseup);
|
||||
document.addEventListener("mousemove", onMousemove);
|
||||
document.addEventListener("mouseup", onMouseup);
|
||||
};
|
||||
|
||||
const onDraggable = () => {
|
||||
const dragDom = unrefElement(dragRef);
|
||||
if (dragDom && targetRef.value) {
|
||||
dragDom.addEventListener('mousedown', onMousedown);
|
||||
dragDom.addEventListener("mousedown", onMousedown);
|
||||
}
|
||||
};
|
||||
|
||||
const offDraggable = () => {
|
||||
const dragDom = unrefElement(dragRef);
|
||||
if (dragDom && targetRef.value) {
|
||||
dragDom.removeEventListener('mousedown', onMousedown);
|
||||
dragDom.removeEventListener("mousedown", onMousedown);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +87,7 @@ export function useModalDraggable(
|
||||
|
||||
const target = unrefElement(targetRef);
|
||||
if (target) {
|
||||
target.style.transform = 'none';
|
||||
target.style.transform = "none";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,21 +1,13 @@
|
||||
import type { ExtendedModalApi, ModalApiOptions, ModalProps } from './modal';
|
||||
import type { ExtendedModalApi, ModalApiOptions, ModalProps } from "./modal";
|
||||
|
||||
import {
|
||||
defineComponent,
|
||||
h,
|
||||
inject,
|
||||
nextTick,
|
||||
provide,
|
||||
reactive,
|
||||
ref,
|
||||
} from 'vue';
|
||||
import { defineComponent, h, inject, nextTick, provide, reactive, ref } from "vue";
|
||||
|
||||
import { useStore } from '/@/vben/shared/store';
|
||||
import { useStore } from "/@/vben/shared/store";
|
||||
|
||||
import { ModalApi } from './modal-api';
|
||||
import VbenModal from './modal.vue';
|
||||
import { ModalApi } from "./modal-api";
|
||||
import VbenModal from "./modal.vue";
|
||||
|
||||
const USER_MODAL_INJECT_KEY = Symbol('VBEN_MODAL_INJECT');
|
||||
const USER_MODAL_INJECT_KEY = Symbol("VBEN_MODAL_INJECT");
|
||||
|
||||
const DEFAULT_MODAL_PROPS: Partial<ModalProps> = {};
|
||||
|
||||
@@ -23,9 +15,7 @@ export function setDefaultModalProps(props: Partial<ModalProps>) {
|
||||
Object.assign(DEFAULT_MODAL_PROPS, props);
|
||||
}
|
||||
|
||||
export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
options: ModalApiOptions = {},
|
||||
) {
|
||||
export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(options: ModalApiOptions = {}) {
|
||||
// Modal一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
|
||||
// 外部的Modal通过provide/inject传递api
|
||||
|
||||
@@ -55,18 +45,18 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
});
|
||||
return () =>
|
||||
h(
|
||||
isModalReady.value ? connectedComponent : 'div',
|
||||
isModalReady.value ? connectedComponent : "div",
|
||||
{
|
||||
...props,
|
||||
...attrs,
|
||||
},
|
||||
slots,
|
||||
slots
|
||||
);
|
||||
},
|
||||
{
|
||||
inheritAttrs: false,
|
||||
name: 'VbenParentModal',
|
||||
},
|
||||
name: "VbenParentModal",
|
||||
}
|
||||
);
|
||||
return [Modal, extendedApi as ExtendedModalApi] as const;
|
||||
}
|
||||
@@ -96,7 +86,7 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
|
||||
const extendedApi: ExtendedModalApi = api as never;
|
||||
|
||||
extendedApi.useStore = (selector) => {
|
||||
extendedApi.useStore = selector => {
|
||||
return useStore(api.store, selector);
|
||||
};
|
||||
|
||||
@@ -110,13 +100,13 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
...attrs,
|
||||
modalApi: extendedApi,
|
||||
},
|
||||
slots,
|
||||
slots
|
||||
);
|
||||
},
|
||||
{
|
||||
inheritAttrs: false,
|
||||
name: 'VbenModal',
|
||||
},
|
||||
name: "VbenModal",
|
||||
}
|
||||
);
|
||||
injectData.extendApi?.(extendedApi);
|
||||
return [Modal, extendedApi] as const;
|
||||
@@ -137,11 +127,9 @@ async function checkProps(api: ExtendedModalApi, attrs: Record<string, any>) {
|
||||
const stateKeys = new Set(Object.keys(state));
|
||||
|
||||
for (const attr of Object.keys(attrs)) {
|
||||
if (stateKeys.has(attr) && !['class'].includes(attr)) {
|
||||
if (stateKeys.has(attr) && !["class"].includes(attr)) {
|
||||
// connectedComponent存在时,不要传入Modal的props,会造成复杂度提升,如果你需要修改Modal的props,请使用 useModal 或者api
|
||||
console.warn(
|
||||
`[Vben Modal]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Modal, please use useVbenModal or api.`,
|
||||
);
|
||||
console.warn(`[Vben Modal]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Modal, please use useVbenModal or api.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user