Skip to content

Commit ad78cc8

Browse files
Merge pull request #355 from vue-final/feat/singleton
feat: get rid of context and made useModal can be execute everywhere
2 parents fc15a7c + 0081db6 commit ad78cc8

File tree

9 files changed

+67
-44
lines changed

9 files changed

+67
-44
lines changed

docs/content/2.get-started/1.guide/4.types.md

-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export type ModalSlot = string | Component | ModalSlotOptions
3333
export type UseModalOptions<P> = {
3434
defaultModelValue?: boolean
3535
keepAlive?: boolean
36-
context?: Vfm
3736
component?: Constructor<P>
3837
attrs?: (RawProps & P) | ({} extends P ? null : never)
3938
slots?: {

packages/vue-final-modal/cypress/components/focusTrap.spec.ts

-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ describe('Test focusTrap', () => {
77
const vfm = createVfm()
88

99
const firstModal = useModal({
10-
context: vfm,
1110
component: VueFinalModal,
1211
attrs: { contentClass: 'first-modal-content' },
1312
slots: {
@@ -16,7 +15,6 @@ describe('Test focusTrap', () => {
1615
})
1716

1817
const secondModal = useModal({
19-
context: vfm,
2018
component: VueFinalModal,
2119
attrs: { contentClass: 'second-modal-content' },
2220
slots: {

packages/vue-final-modal/cypress/components/useModal.spec.ts

-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ describe('Test useModal()', () => {
66
it('Should be closed by default', () => {
77
const vfm = createVfm()
88
const modal = useModal({
9-
context: vfm,
109
slots: { default: 'Hello World!' },
1110
})
1211

@@ -25,7 +24,6 @@ describe('Test useModal()', () => {
2524
it('Should be opened by given defaultModelValue: true', () => {
2625
const vfm = createVfm()
2726
useModal({
28-
context: vfm,
2927
defaultModelValue: true,
3028
slots: {
3129
default: 'Hello World!',
@@ -51,7 +49,6 @@ describe('Test useModal()', () => {
5149
const onClosed = cy.spy().as('onClosed')
5250

5351
const modal = useModal({
54-
context: vfm,
5552
attrs: {
5653
onBeforeOpen,
5754
onOpened,

packages/vue-final-modal/cypress/components/useZIndex.spec.ts

-3
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,16 @@ describe('Test useZIndex()', () => {
55
it('Props: zIndexFn()', () => {
66
const vfm = createVfm()
77
const firstModal = useModal({
8-
context: vfm,
98
component: VueFinalModal,
109
attrs: { class: 'first-modal' },
1110
})
1211

1312
const secondModal = useModal({
14-
context: vfm,
1513
component: VueFinalModal,
1614
attrs: { class: 'second-modal' },
1715
})
1816

1917
const thirdModal = useModal({
20-
context: vfm,
2118
component: VueFinalModal,
2219
attrs: { class: 'third-modal' },
2320
})

packages/vue-final-modal/src/Modal.ts

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export type ModalSlot = string | Component | ModalSlotOptions
2525
export type UseModalOptions<P> = {
2626
defaultModelValue?: boolean
2727
keepAlive?: boolean
28-
context?: Vfm
2928
component?: Constructor<P>
3029
attrs?: (RawProps & P) | ({} extends P ? null : never)
3130
slots?: {

packages/vue-final-modal/src/plugin.ts

-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ export function createVfm() {
2121

2222
const vfm: Vfm = markRaw({
2323
install(app: App) {
24-
setActiveVfm(vfm)
25-
2624
app.provide(vfmSymbol, vfm)
2725
app.config.globalProperties.$vfm = vfm
2826

packages/vue-final-modal/src/useApi.ts

+40-31
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
1+
import { computed, inject, markRaw, nextTick, reactive, useAttrs } from 'vue'
12
import { isString, tryOnUnmounted } from '@vueuse/core'
2-
import { computed, getCurrentInstance, inject, markRaw, reactive, useAttrs } from 'vue'
33
import type { Component } from 'vue'
44
import VueFinalModal from './components/VueFinalModal/VueFinalModal.vue'
55
import type CoreModal from './components/CoreModal/CoreModal.vue'
6-
import { internalVfmSymbol, vfmSymbol } from './injectionSymbols'
6+
import { internalVfmSymbol } from './injectionSymbols'
77

88
import type { ComponentProps, Constructor, InternalVfm, ModalSlot, ModalSlotOptions, RawProps, UseModalOptions, UseModalOptionsPrivate, UseModalReturnType, Vfm } from './Modal'
9-
import { activeVfm, getActiveVfm, setActiveVfm } from './plugin'
9+
import { activeVfm, getActiveVfm } from './plugin'
1010

1111
/**
1212
* Returns the vfm instance. Equivalent to using `$vfm` inside
1313
* templates.
1414
*/
1515
export function useVfm(): Vfm {
16-
return getActiveVfm()!
16+
const vfm = getActiveVfm()
17+
if (__DEV__ && !vfm) {
18+
throw new Error(
19+
'[Vue Final Modal]: getActiveVfm was called with no active Vfm. Did you forget to install vfm?\n'
20+
+ '\tconst vfm = createVfm()\n'
21+
+ '\tapp.use(vfm)\n'
22+
+ 'This will fail in production.',
23+
)
24+
}
25+
26+
return vfm!
1727
}
1828

1929
/**
@@ -53,25 +63,8 @@ function withMarkRaw<P>(options: Partial<UseModalOptions<P>>, DefaultComponent:
5363
* Create a dynamic modal.
5464
*/
5565
export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_options: UseModalOptions<P>): UseModalReturnType<P> {
56-
const currentInstance = getCurrentInstance()
57-
let vfm = _options.context || (currentInstance && inject(vfmSymbol))
58-
if (vfm)
59-
setActiveVfm(vfm)
60-
61-
if (__DEV__ && !activeVfm) {
62-
throw new Error(
63-
'[🍍]: getActiveVfm was called with no active Vfm. Did you forget to install vfm?\n'
64-
+ '\tconst vfm = createVfm()\n'
65-
+ '\tapp.use(vfm)\n'
66-
+ 'This will fail in production.',
67-
)
68-
}
69-
70-
vfm = activeVfm
71-
7266
const options = reactive({
7367
id: Symbol('useModal'),
74-
context: vfm,
7568
modelValue: !!_options?.defaultModelValue,
7669
resolveOpened: () => { },
7770
resolveClosed: () => { },
@@ -83,16 +76,35 @@ export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_opti
8376
destroy()
8477
})
8578

86-
if (options.modelValue === true)
87-
options.context?.dynamicModals.push(options)
79+
if (options.modelValue === true) {
80+
// nextTick will break the SSR, so use `activeVfm` first and then `useVfm()`
81+
if (activeVfm) {
82+
activeVfm?.dynamicModals.push(options)
83+
}
84+
else {
85+
nextTick(() => {
86+
const vfm = useVfm()
87+
vfm?.dynamicModals.push(options)
88+
})
89+
}
90+
}
8891

89-
function open(): Promise<string> {
92+
async function open(): Promise<string> {
93+
// nextTick will break the SSR, so use `activeVfm` first and then `useVfm()`
94+
let vfm: Vfm
95+
if (activeVfm) {
96+
vfm = activeVfm
97+
}
98+
else {
99+
await nextTick()
100+
vfm = useVfm()
101+
}
90102
if (options.modelValue)
91103
return Promise.resolve('[Vue Final Modal] modal is already opened.')
92104

93105
destroy()
94106
options.modelValue = true
95-
options.context?.dynamicModals.push(options)
107+
vfm.dynamicModals.push(options)
96108

97109
return new Promise((resolve) => {
98110
options.resolveOpened = () => resolve('opened')
@@ -116,8 +128,6 @@ export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_opti
116128
options.defaultModelValue = _options.defaultModelValue
117129
if (_options?.keepAlive !== undefined)
118130
options.keepAlive = _options?.keepAlive
119-
if (_options.context)
120-
options.context = _options.context
121131

122132
// patch options.component and options.attrs
123133
patchComponentOptions(options, rest)
@@ -156,11 +166,10 @@ export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_opti
156166
}
157167

158168
function destroy(): void {
159-
if (!options.context)
160-
return
161-
const index = options.context.dynamicModals.indexOf(options)
169+
const vfm = useVfm()
170+
const index = vfm.dynamicModals.indexOf(options)
162171
if (index !== -1)
163-
options.context.dynamicModals.splice(index, 1)
172+
vfm.dynamicModals.splice(index, 1)
164173
}
165174

166175
return {

viteplay/src/components/VueFinalModal/Basic.example.vue

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22
import { ref } from 'vue'
33
import { ModalsContainer, VueFinalModal, useModal, useModalSlot, useVfm } from 'vue-final-modal'
44
import DefaultSlot from '../DefaultSlot.vue'
5+
import { modal } from './modalsHelpers'
56
import TestModal from './TestModal.vue'
67
7-
const { toggle, closeAll } = useVfm()
8+
console.log('modal → ', modal)
89
10+
const { toggle, closeAll } = useVfm()
11+
modal.open().then((res) => { console.log('res', res) })
12+
modal.open().then((res) => { console.log('res', res) })
13+
modal.open().then((res) => { console.log('res', res) })
14+
modal.open().then((res) => { console.log('res', res) })
915
const modal1 = useModal({
1016
keepAlive: true,
1117
component: VueFinalModal,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { VueFinalModal, useModal, useModalSlot } from 'vue-final-modal'
2+
import DefaultSlot from '../DefaultSlot.vue'
3+
4+
console.log('helper')
5+
export const modal = useModal({
6+
component: VueFinalModal,
7+
// defaultModelValue: true,
8+
slots: {
9+
default: useModalSlot({
10+
component: DefaultSlot,
11+
attrs: {
12+
text: '123',
13+
onCreate() {
14+
// console.log('onCreated')
15+
},
16+
},
17+
}),
18+
},
19+
})
20+
// modal.open()

0 commit comments

Comments
 (0)