diff --git a/docs/content/2.get-started/1.guide/4.types.md b/docs/content/2.get-started/1.guide/4.types.md index 5de9f844..4d29d825 100644 --- a/docs/content/2.get-started/1.guide/4.types.md +++ b/docs/content/2.get-started/1.guide/4.types.md @@ -8,37 +8,36 @@ The exported types in VueFinalModal. export type ModalId = number | string | symbol ``` -## StyleValue +## Template ```ts -export type StyleValue = string | CSSProperties | (string | CSSProperties)[] -``` - -## ModalSlot - -```ts -export interface ModalSlotOptions { component: Raw; attrs?: Record } -export type ModalSlot = string | ComponentType | ModalSlotOptions +export type Template = { + component: T + attrs?: ComponentProps + slots?: { + [K in keyof ComponentSlots]?: string | Component | Template + } +} ``` -## UseModalOptions +## UseModalTemplate ```ts -export type UseModalOptions = { +export type UseModalTemplate = { defaultModelValue?: boolean keepAlive?: boolean component?: T attrs?: ComponentProps slots?: { - [key: string]: ModalSlot + [K in keyof ComponentSlots]?: string | Component | Template } } ``` -## UseModalOptionsPrivate +## UseModalTemplatePrivate ```ts -export type UseModalOptionsPrivate = { +export type UseModalTemplatePrivate = { id: symbol modelValue: boolean resolveOpened: () => void @@ -49,11 +48,11 @@ export type UseModalOptionsPrivate = { ## UseModalReturnType ```ts -export interface UseModalReturnType { - options: UseModalOptions & UseModalOptionsPrivate +export interface UseModalReturnType { + template: UseModalTemplate & UseModalTemplatePrivate open: () => Promise close: () => Promise - patchOptions: (options: Partial>) => void + patchTemplate: (template: Partial>) => void destroy: () => void } ``` @@ -63,26 +62,46 @@ export interface UseModalReturnType { ```ts export type Vfm = { install(app: App): void - modals: ComputedRef[] - openedModals: ComputedRef[] - openedModalOverlays: ComputedRef[] - dynamicModals: (UseModalOptions & UseModalOptionsPrivate)[] - modalsContainers: Ref - get: (modalId: ModalId) => undefined | ComputedRef + modals: ComputedRef[] + openedModals: ComputedRef[] + openedModalOverlays: ComputedRef[] + get: (modalId: ModalId) => undefined | ComputedRef toggle: (modalId: ModalId, show?: boolean) => undefined | Promise open: (modalId: ModalId) => undefined | Promise close: (modalId: ModalId) => undefined | Promise - closeAll: () => Promise<[PromiseSettledResult[]>]> -} + closeAll: () => Promise[]> +} & Partial ``` -## Modal +## ModalExposed ```ts -export type Modal = { - modalId?: ModalId - hideOverlay: Ref | undefined +export type ModalExposed = { + modalId?: Ref + hideOverlay?: Ref + overlayBehavior: Ref<'auto' | 'persist'> overlayVisible: Ref toggle: (show?: boolean) => Promise } ``` + +## CreateContainer + +```ts +export type CreateContainer = { + Container: Component + useTemplate: UseTemplate +} +``` + +## UseTemplate + +```ts +export type UseTemplate = ( + template: Template, + options?: { onUnmounted?: (() => void) } +) => { + show: () => Promise + hide: () => Promise +} +``` diff --git a/docs/content/3.api/2.composables/1.use-modal.md b/docs/content/3.api/2.composables/1.use-modal.md index 2a792f20..54f5cf1d 100644 --- a/docs/content/3.api/2.composables/1.use-modal.md +++ b/docs/content/3.api/2.composables/1.use-modal.md @@ -115,7 +115,7 @@ const modalInstance = useModal({ #### with `Component`, `Props` and `Events` ```ts -import { VueFinalModal, useModal, useModalSlot } from 'vue-final-modal' +import { VueFinalModal, useModal, h } from 'vue-final-modal' // ModalContent is the component you want to put into the modal content import ModalContent from './ModalContent.vue' @@ -123,7 +123,7 @@ const modalInstance = useModal({ component: VueFinalModal, attrs: { ... }, slots: { - default: useModalSlot({ + default: h({ component: ModalContent, attrs: { // Bind ModalContent props @@ -137,7 +137,7 @@ const modalInstance = useModal({ ``` ::alert{type=info} -`useModalSlot()` is a function that provides better DX for type checking. It just returns the same object you passed in. +`h()` is a function that provides better DX for type checking. It just returns the same object you passed in. :: ## Type Declarations diff --git a/docs/package.json b/docs/package.json index 5f8b17ff..cefa5af9 100644 --- a/docs/package.json +++ b/docs/package.json @@ -16,7 +16,7 @@ "dependencies": { "@vorms/core": "^1.1.0", "@vue-final-modal/nuxt": "workspace:1.0.3", - "vue-final-modal": "workspace:4.5.2", + "vue-final-modal": "workspace:5.0.0-beta.5", "vue3-drag-resize": "^2.0.5" } } diff --git a/examples/nuxt3/package.json b/examples/nuxt3/package.json index 35fc55cb..60c63d20 100644 --- a/examples/nuxt3/package.json +++ b/examples/nuxt3/package.json @@ -15,6 +15,6 @@ }, "dependencies": { "@vue-final-modal/nuxt": "^1.0.3", - "vue-final-modal": "^4.5.2" + "vue-final-modal": "^4.5.3" } } diff --git a/examples/vue3/package.json b/examples/vue3/package.json index 74f03111..c70e4143 100644 --- a/examples/vue3/package.json +++ b/examples/vue3/package.json @@ -9,8 +9,8 @@ "preview": "vite preview" }, "dependencies": { - "vue": "^3.3.7", - "vue-final-modal": "^4.5.2" + "vue": "3.3.7", + "vue-final-modal": "^4.5.3" }, "devDependencies": { "@iconify/vue": "^4.1.1", diff --git a/package.json b/package.json index 422ebbd9..f0f84663 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,6 @@ "prepare:module": "pnpm --filter @vue-final-modal/nuxt dev:prepare && pnpm --filter @vue-final-modal/nuxt prepack", "postinstall": "pnpm build:vfm && pnpm prepare:module" }, - "dependencies": { - "vue": "^3.3.7" - }, "devDependencies": { "@antfu/eslint-config": "^0.37.0", "@types/node": "^20.10.0", diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index ab9533b5..ff648f2c 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -24,7 +24,7 @@ }, "dependencies": { "@nuxt/kit": "^3.8.2", - "vue-final-modal": "^4.5.2" + "vue-final-modal": "^4.5.3" }, "devDependencies": { "@nuxt/module-builder": "^0.5.4", diff --git a/packages/vue-final-modal/CHANGELOG.md b/packages/vue-final-modal/CHANGELOG.md index a0068760..c84b7bb4 100644 --- a/packages/vue-final-modal/CHANGELOG.md +++ b/packages/vue-final-modal/CHANGELOG.md @@ -1,5 +1,21 @@ +# [5.0.0-beta.5](https://github.com/vue-final/vue-final-modal/compare/v5.0.0-beta.4...v5.0.0-beta.5) (2024-01-31) + +# [5.0.0-beta.4](https://github.com/vue-final/vue-final-modal/compare/v5.0.0-beta.3...v5.0.0-beta.4) (2024-01-30) + +# [5.0.0-beta.3](https://github.com/vue-final/vue-final-modal/compare/v5.0.0-beta.2...v5.0.0-beta.3) (2024-01-30) + +# [5.0.0-beta.2](https://github.com/vue-final/vue-final-modal/compare/v5.0.0-beta.1...v5.0.0-beta.2) (2024-01-11) + +# [5.0.0-beta.1](https://github.com/vue-final/vue-final-modal/compare/v5.0.0-beta.0...v5.0.0-beta.1) (2024-01-11) + +# [5.0.0-beta.0](https://github.com/vue-final/vue-final-modal/compare/v4.5.3...v5.0.0-beta.0) (2024-01-11) + +### Features + +* replace body-scroll-lock with scroll-lock [#403](https://github.com/vue-final/vue-final-modal/issues/403) ([a9a2e8a](https://github.com/vue-final/vue-final-modal/commit/a9a2e8a7794c6d9c67eb7650a74f6894b2b558b0)) + ## [4.5.3](https://github.com/vue-final/vue-final-modal/compare/v4.5.2...v4.5.3) (2023-12-15) ## [4.5.2](https://github.com/vue-final/vue-final-modal/compare/v4.5.1...v4.5.2) (2023-12-15) diff --git a/packages/vue-final-modal/cypress/components/ModalCloseByScopedSlot.vue b/packages/vue-final-modal/cypress/components/ModalCloseByScopedSlot.vue new file mode 100644 index 00000000..5a29d139 --- /dev/null +++ b/packages/vue-final-modal/cypress/components/ModalCloseByScopedSlot.vue @@ -0,0 +1,11 @@ + + + diff --git a/packages/vue-final-modal/cypress/components/scopedSlots.spec.ts b/packages/vue-final-modal/cypress/components/scopedSlots.spec.ts new file mode 100644 index 00000000..afbf778e --- /dev/null +++ b/packages/vue-final-modal/cypress/components/scopedSlots.spec.ts @@ -0,0 +1,27 @@ +import App from './App.vue' +import ModalCloseByScopedSlot from './ModalCloseByScopedSlot.vue' +import { createVfm, useModal } from '~/index' + +describe('Test scopedSlot', () => { + it('close() scoped slot ', () => { + const vfm = createVfm() + + cy.mount(App, { + global: { + plugins: [vfm], + stubs: { transition: false }, + }, + }).as('app') + + const modalName = 'modal-close-by-scoped-slot' + useModal({ + defaultModelValue: true, + component: ModalCloseByScopedSlot, + attrs: { class: modalName }, + }) + + cy.get(`.${modalName}`).should('exist') + cy.get(`.${modalName}`).find('button').click() + cy.get(`.${modalName}`).should('not.exist') + }) +}) diff --git a/packages/vue-final-modal/cypress/components/useZIndex.spec.ts b/packages/vue-final-modal/cypress/components/useZIndex.spec.ts index 9075bb98..7897c58b 100644 --- a/packages/vue-final-modal/cypress/components/useZIndex.spec.ts +++ b/packages/vue-final-modal/cypress/components/useZIndex.spec.ts @@ -42,7 +42,7 @@ describe('Test useZIndex()', () => { }) cy.get('@app').then(() => { - thirdModal.patchOptions({ + thirdModal.patchTemplate({ attrs: { zIndexFn: ({ index }) => 1234 + 2 * index, }, diff --git a/packages/vue-final-modal/package.json b/packages/vue-final-modal/package.json index 8e18eca8..77ffe91d 100644 --- a/packages/vue-final-modal/package.json +++ b/packages/vue-final-modal/package.json @@ -1,7 +1,7 @@ { "name": "vue-final-modal", "private": false, - "version": "4.5.3", + "version": "5.0.0-beta.5", "type": "module", "source": "src/index.ts", "main": "./dist/index.umd.js", @@ -24,32 +24,28 @@ "scripts": { "dev": "vite build -w", "build": "vite build", - "cypress:open": "cypress open --browser chrome --component", + "cypress:open": "cypress open --browser electron --component", "cypress:run": "cypress run --browser chrome --component", "typecheck": "vue-tsc --noEmit", "release": "pnpm build && pnpm cypress:run && release-it" }, - "dependencies": { - "@vueuse/core": "^10.5.0", - "@vueuse/integrations": "^10.5.0", - "focus-trap": "^7.5.4" - }, "devDependencies": { - "@cypress/vue": "^5.0.5", "@release-it/conventional-changelog": "^5.1.1", - "@vue-macros/volar": "^0.8.4", - "cypress": "^13.6.0", + "@vueuse/core": "^10.7.1", + "@vueuse/integrations": "^10.7.1", + "cypress": "^13.6.4", + "focus-trap": "^7.5.4", "release-it": "^16.1.3", - "unplugin-vue-define-options": "^1.3.8", - "unplugin-vue-macros": "^2.3.0", "vite-plugin-dts": "^3.6.3", - "vue": "^3.3.7" + "vue": "3.3.7", + "vue-use-template": "0.0.0-beta.5" }, "peerDependencies": { "@vueuse/core": ">=10.0.0", "@vueuse/integrations": ">=10.0.0", "focus-trap": ">=7.2.0", - "vue": ">=3.2.0" + "vue": ">=3.0.0", + "vue-use-template": "0.0.0-beta.5" }, "homepage": "https://vue-final-modal.org/", "bugs": { diff --git a/packages/vue-final-modal/src/Modal.ts b/packages/vue-final-modal/src/Modal.ts deleted file mode 100644 index 2e958b9d..00000000 --- a/packages/vue-final-modal/src/Modal.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { App, CSSProperties, Component, ComponentInternalInstance, FunctionalComponent, Raw, Ref } from 'vue' -import type { ComponentProps, ComponentSlots } from './Component' - -export type ModalId = number | string | symbol -export type StyleValue = string | CSSProperties | (string | CSSProperties)[] - -export interface ModalSlotOptions { component: Raw; attrs?: Record } -export type ModalSlot = string | Component | ModalSlotOptions - -type ComponentConstructor = (abstract new (...args: any) => any) -/** Including both generic and non-generic vue components */ -export type ComponentType = ComponentConstructor | FunctionalComponent - -export type UseModalOptions = { - defaultModelValue?: boolean - keepAlive?: boolean - component?: T - attrs?: ComponentProps - slots?: { - [K in keyof ComponentSlots]?: ModalSlot - } -} - -export type UseModalOptionsPrivate = { - id: symbol - modelValue: boolean - resolveOpened: () => void - resolveClosed: () => void -} - -export interface UseModalReturnType { - options: UseModalOptions & UseModalOptionsPrivate - open: () => Promise - close: () => Promise - patchOptions: (options: Partial>) => void - destroy: () => void -} - -export type Vfm = { - install(app: App): void - modals: ComponentInternalInstance[] - openedModals: ComponentInternalInstance[] - openedModalOverlays: ComponentInternalInstance[] - dynamicModals: (UseModalOptions & UseModalOptionsPrivate)[] - modalsContainers: Ref - get: (modalId: ModalId) => undefined | ComponentInternalInstance - toggle: (modalId: ModalId, show?: boolean) => undefined | Promise - open: (modalId: ModalId) => undefined | Promise - close: (modalId: ModalId) => undefined | Promise - closeAll: () => Promise[]> -} - -export type ModalExposed = { - modalId?: Ref - hideOverlay?: Ref - overlayBehavior: Ref<'auto' | 'persist'> - overlayVisible: Ref - toggle: (show?: boolean) => Promise -} diff --git a/packages/vue-final-modal/src/components/ModalsContainer.ts b/packages/vue-final-modal/src/components/ModalsContainer.ts new file mode 100644 index 00000000..5755812e --- /dev/null +++ b/packages/vue-final-modal/src/components/ModalsContainer.ts @@ -0,0 +1,3 @@ +import { Container } from 'vue-use-template' + +export const ModalsContainer = Container diff --git a/packages/vue-final-modal/src/components/ModalsContainer.vue b/packages/vue-final-modal/src/components/ModalsContainer.vue deleted file mode 100644 index 91b3bb0d..00000000 --- a/packages/vue-final-modal/src/components/ModalsContainer.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - diff --git a/packages/vue-final-modal/src/components/UseModal.ts b/packages/vue-final-modal/src/components/UseModal.ts new file mode 100644 index 00000000..8d7bc62c --- /dev/null +++ b/packages/vue-final-modal/src/components/UseModal.ts @@ -0,0 +1,39 @@ +import type { Component, PropType } from 'vue' +import { defineComponent } from 'vue' +import { templateToVNode } from 'vue-use-template' +import type { UseModalTemplate, UseModalTemplatePrivate } from '~/types' + +export const UseModal = defineComponent({ + name: 'UseModal', + props: { + template: { + type: Object as PropType & UseModalTemplatePrivate>, + required: true, + }, + }, + setup(props) { + function renderModalTemplate(template: (UseModalTemplate & UseModalTemplatePrivate)) { + if (!template.component) + return null + return templateToVNode({ + component: template.component, + attrs: { + 'modelValue': template.modelValue, + 'displayDirective': template?.keepAlive ? 'show' : undefined, + ...(typeof template.attrs === 'object' ? template.attrs : {}), + 'onUpdate:modelValue': (value: boolean) => { + template.modelValue = value + const onUpdateModelValue = template.attrs?.['onUpdate:modelValue'] + if (onUpdateModelValue) + onUpdateModelValue(value) + }, + 'on_closed': () => template?.resolveClosed?.(), + 'on_opened': () => template?.resolveOpened?.(), + }, + slots: template.slots, + }) + } + + return () => renderModalTemplate(props.template) + }, +}) diff --git a/packages/vue-final-modal/src/components/VueFinalModal/VueFinalModal.vue b/packages/vue-final-modal/src/components/VueFinalModal.vue similarity index 67% rename from packages/vue-final-modal/src/components/VueFinalModal/VueFinalModal.vue rename to packages/vue-final-modal/src/components/VueFinalModal.vue index df66c455..7a18dd08 100644 --- a/packages/vue-final-modal/src/components/VueFinalModal/VueFinalModal.vue +++ b/packages/vue-final-modal/src/components/VueFinalModal.vue @@ -1,18 +1,17 @@ -const modalId = toRef(() => props.modalId) -const hideOverlay = toRef(() => props.hideOverlay) -const overlayBehavior = toRef(() => props.overlayBehavior) -const modalExposed = computed(() => ({ - modalId, - hideOverlay, - overlayBehavior, - overlayVisible, - toggle(show?: boolean): Promise { - return new Promise((resolve) => { - resolveToggle = once((res: string) => resolve(res)) - - const value = typeof show === 'boolean' ? show : !modelValueLocal.value - modelValueLocal.value = value - }) - }, -})) - -defineExpose({ - modalExposed, -}) +