Skip to content

Commit 184dd46

Browse files
committed
feat: rename useModalSlot to c2v and support infinite nested slots to useModal
1 parent dbee02f commit 184dd46

File tree

13 files changed

+103
-55
lines changed

13 files changed

+103
-55
lines changed

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,25 @@ export type ModalId = number | string | symbol
1414
export type StyleValue = string | CSSProperties | (string | CSSProperties)[]
1515
```
1616
17-
## ModalSlot
17+
## C2VOptions
1818
1919
```ts
20-
export interface ModalSlotOptions { component: Raw<ComponentType>; attrs?: Record<string, any> }
21-
export type ModalSlot = string | ComponentType | ModalSlotOptions
20+
export type C2VOptions<T extends Component> = {
21+
component: T
22+
attrs?: ComponentProps<T>
23+
slots?: {
24+
[K in keyof ComponentSlots<T>]?: string | Component | C2VOptions<Component>
25+
}
26+
}
2227
```
2328
2429
## UseModalOptions
2530
2631
```ts
27-
export type UseModalOptions<T extends ComponentType> = {
32+
export type UseModalOptions<T extends Component> = {
2833
defaultModelValue?: boolean
2934
keepAlive?: boolean
30-
component?: T
31-
attrs?: ComponentProps<T>
32-
slots?: {
33-
[key: string]: ModalSlot
34-
}
35-
}
35+
} & C2VOptions<T>
3636
```
3737
3838
## UseModalOptionsPrivate
@@ -49,7 +49,7 @@ export type UseModalOptionsPrivate = {
4949
## UseModalReturnType
5050
5151
```ts
52-
export interface UseModalReturnType<T extends ComponentType> {
52+
export interface UseModalReturnType<T extends Component> {
5353
options: UseModalOptions<T> & UseModalOptionsPrivate
5454
open: () => Promise<string>
5555
close: () => Promise<string>

docs/content/3.api/2.composables/1.use-modal.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,15 +115,15 @@ const modalInstance = useModal({
115115
#### with `Component`, `Props` and `Events`
116116

117117
```ts
118-
import { VueFinalModal, useModal, useModalSlot } from 'vue-final-modal'
118+
import { VueFinalModal, useModal, c2v } from 'vue-final-modal'
119119
// ModalContent is the component you want to put into the modal content
120120
import ModalContent from './ModalContent.vue'
121121

122122
const modalInstance = useModal({
123123
component: VueFinalModal,
124124
attrs: { ... },
125125
slots: {
126-
default: useModalSlot({
126+
default: c2v({
127127
component: ModalContent,
128128
attrs: {
129129
// Bind ModalContent props
@@ -137,7 +137,7 @@ const modalInstance = useModal({
137137
```
138138

139139
::alert{type=info}
140-
`useModalSlot()` is a function that provides better DX for type checking. It just returns the same object you passed in.
140+
`c2v()` is a function that provides better DX for type checking. It just returns the same object you passed in.
141141
::
142142

143143
## Type Declarations

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
1-
import type { App, CSSProperties, Component, ComponentInternalInstance, FunctionalComponent, Raw, Ref } from 'vue'
1+
import type { App, CSSProperties, Component, ComponentInternalInstance, Ref } from 'vue'
22
import type { ComponentProps, ComponentSlots } from './Component'
33
import type { H } from './h'
44

55
export type ModalId = number | string | symbol
66
export type StyleValue = string | CSSProperties | (string | CSSProperties)[]
77

8-
export interface ModalSlotOptions { component: Raw<Component>; attrs?: Record<string, any> }
9-
export type ModalSlot = string | Component | ModalSlotOptions
10-
11-
type ComponentConstructor = (abstract new (...args: any) => any)
12-
/** Including both generic and non-generic vue components */
13-
export type ComponentType = ComponentConstructor | FunctionalComponent<any, any>
8+
export type C2VOptions<T extends Component> = {
9+
component: T
10+
attrs?: ComponentProps<T>
11+
slots?: {
12+
[K in keyof ComponentSlots<T>]?: string | Component | C2VOptions<Component>
13+
}
14+
}
1415

1516
export type UseModalOptions<T extends Component> = {
1617
defaultModelValue?: boolean
1718
keepAlive?: boolean
1819
component?: T
1920
attrs?: ComponentProps<T>
2021
slots?: {
21-
[K in keyof ComponentSlots<T>]?: ModalSlot
22+
[K in keyof ComponentSlots<T>]?: string | Component | C2VOptions<Component>
2223
}
2324
}
2425

packages/vue-final-modal/src/components/DynamicModal.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Component, PropType } from 'vue'
22
import { defineComponent, h } from 'vue'
3-
import type { ModalSlotOptions, UseModalOptions, UseModalOptionsPrivate } from '..'
4-
import { isModalSlotOptions } from '~/useApi'
3+
import type { C2VOptions, UseModalOptions, UseModalOptionsPrivate } from '..'
4+
import { getSlots, isC2VOptions } from '~/useApi'
55
import { isString, objectEntries } from '~/utils'
66

77
export const DynamicModal = defineComponent({
@@ -18,11 +18,11 @@ export const DynamicModal = defineComponent({
1818
return null
1919
const slots = objectEntries(modal.slots || {}).reduce((acc, cur) => {
2020
const slotName = cur[0] as string
21-
const slot = cur[1] as string | Component | ModalSlotOptions
21+
const slot = cur[1] as string | Component | C2VOptions<Component>
2222
if (isString(slot))
2323
acc[slotName] = () => h('div', { innerHTML: slot })
24-
else if (isModalSlotOptions(slot))
25-
acc[slotName] = () => h(slot.component, slot.attrs)
24+
else if (isC2VOptions(slot))
25+
acc[slotName] = () => h(slot.component, slot.attrs, slot.slots ? getSlots(slot.slots) : undefined)
2626
else
2727
acc[slotName] = () => h(slot)
2828
return acc

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export { vueFinalModalProps } from './components/VueFinalModal/VueFinalModalProp
2121
export type { VueFinalModalEmits } from './components/VueFinalModal/VueFinalModal.vue'
2222

2323
/** Composables */
24-
export { useVfm, useModal, useVfmAttrs, useModalSlot } from './useApi'
24+
export { useVfm, useModal, useVfmAttrs, c2v } from './useApi'
2525

2626
declare module '@vue/runtime-core' {
2727
export interface ComponentCustomProperties {

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

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import type { Component, VNode } from 'vue'
22
import { computed, h, markRaw, nextTick, reactive, useAttrs } from 'vue'
33
import { tryOnUnmounted } from '@vueuse/core'
44
import VueFinalModal from './components/VueFinalModal/VueFinalModal.vue'
5-
import type { ModalSlotOptions, UseModalOptions, UseModalOptionsPrivate, UseModalReturnType, Vfm } from './Modal'
5+
import type { C2VOptions, UseModalOptions, UseModalOptionsPrivate, UseModalReturnType, Vfm } from './Modal'
66
import { activeVfm, getActiveVfm } from './plugin'
7-
import type { ComponentEmit, ComponentProps } from './Component'
7+
import type { ComponentEmit, ComponentProps, ComponentSlots } from './Component'
88
import { DynamicModal } from './components/DynamicModal'
99
import { isString, objectEntries } from '~/utils'
1010

@@ -35,7 +35,7 @@ function withMarkRaw<T extends Component>(options: Partial<UseModalOptions<T>>,
3535
if (isString(maybeComponent))
3636
return [name, maybeComponent] as const
3737

38-
if (isModalSlotOptions(maybeComponent)) {
38+
if (isC2VOptions(maybeComponent)) {
3939
return [name, {
4040
...maybeComponent,
4141
component: markRaw(maybeComponent.component),
@@ -125,7 +125,7 @@ export function useModal<T extends Component = typeof VueFinalModal>(_options: U
125125
const originSlot = options.slots![name]
126126
if (isString(originSlot))
127127
options.slots![name] = slot
128-
else if (isModalSlotOptions(originSlot) && isModalSlotOptions(slot))
128+
else if (isC2VOptions(originSlot) && isC2VOptions(slot))
129129
patchComponentOptions(originSlot, slot)
130130
else
131131
options.slots![name] = slot
@@ -143,8 +143,8 @@ export function useModal<T extends Component = typeof VueFinalModal>(_options: U
143143
}
144144

145145
function patchComponentOptions<T extends Component>(
146-
options: UseModalOptions<T> | ModalSlotOptions,
147-
newOptions: Partial<UseModalOptions<T>> | ModalSlotOptions,
146+
options: UseModalOptions<T> | C2VOptions<Component>,
147+
newOptions: Partial<UseModalOptions<T>> | C2VOptions<Component>,
148148
) {
149149
if (newOptions.component)
150150
options.component = newOptions.component
@@ -182,20 +182,34 @@ function removeVNode(vNode: VNode): void {
182182
vfm.h.remove(vNode)
183183
}
184184

185-
export function useModalSlot<T extends Component>(options: {
186-
component: T
187-
attrs?: ComponentProps<T>
188-
}) {
189-
return options
190-
}
191-
192-
export function isModalSlotOptions(value: unknown): value is ModalSlotOptions {
185+
export function isC2VOptions<T extends Component>(value: unknown): value is C2VOptions<T> {
193186
if (typeof value === 'object' && value !== null)
194187
return 'component' in value
195188
else
196189
return false
197190
}
198191

192+
/** Convert Component Options to VNode */
193+
export function c2v<T extends Component>(options: C2VOptions<T>) {
194+
return options
195+
}
196+
197+
export function getSlots<T extends Component>(slots: {
198+
[K in keyof ComponentSlots<T>]?: string | Component | C2VOptions<Component>
199+
}) {
200+
return objectEntries(slots || {}).reduce((acc, cur) => {
201+
const slotName = cur[0] as string
202+
const slot = cur[1] as string | Component | C2VOptions<any>
203+
if (isString(slot))
204+
acc[slotName] = () => h('div', { innerHTML: slot })
205+
else if (isC2VOptions(slot))
206+
acc[slotName] = () => h(slot.component, slot.attrs, slot.slots ? getSlots(slot.slots) : undefined)
207+
else
208+
acc[slotName] = () => h(slot)
209+
return acc
210+
}, {} as Record<string, () => VNode>)
211+
}
212+
199213
export function pickModalProps(props: Record<string, any>, modalProps: Record<string, any>) {
200214
return Object.keys(modalProps).reduce<Record<string, any>>((acc, propName) => {
201215
acc[propName] = props?.[propName]

viteplay/src/components/DefaultSlot.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ const count = ref(0)
1919
<div>default slot component {{ text }}</div>
2020
<button>Click Button!</button>
2121
<input v-model="count" type="number">
22+
<slot />
2223
</div>
2324
</template>

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

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { ref } from 'vue'
3-
import { ModalsContainer, VueFinalModal, useModal, useModalSlot, useVfm } from 'vue-final-modal'
3+
import { ModalsContainer, VueFinalModal, c2v, useModal, useVfm } from 'vue-final-modal'
44
import DefaultSlot from '../DefaultSlot.vue'
55
import { modal } from './modalsHelpers'
66
import TestModal from './TestModal.vue'
@@ -28,13 +28,26 @@ const modal1 = useModal({
2828
onBeforeOpen() { console.log('onBeforeOpen') },
2929
},
3030
slots: {
31-
default: useModalSlot({
31+
default: c2v({
3232
component: DefaultSlot,
3333
attrs: {
3434
text: '123',
35-
onCreate() {
36-
// console.log('onCreated')
37-
},
35+
},
36+
slots: {
37+
default: c2v({
38+
component: DefaultSlot,
39+
attrs: {
40+
text: '456',
41+
},
42+
slots: {
43+
default: c2v({
44+
component: DefaultSlot,
45+
attrs: {
46+
text: '789',
47+
},
48+
}),
49+
},
50+
}),
3851
},
3952
}),
4053
},

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { ModalsContainer, useModal, useModalSlot } from 'vue-final-modal'
2+
import { ModalsContainer, useModal, c2v } from 'vue-final-modal'
33
import DefaultSlot from '../DefaultSlot.vue'
44
55
const bottomSheet = useModal({
@@ -19,7 +19,7 @@ const bottomSheet = useModal({
1919
contentTransition: { name: 'vfm-slide-down' },
2020
},
2121
slots: {
22-
default: useModalSlot({
22+
default: c2v({
2323
component: DefaultSlot,
2424
attrs: {
2525
text: 'a bottom sheet modal',
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<script setup lang="ts">
2+
import { ModalsContainer, useModal } from 'vue-final-modal'
3+
import DefaultSlot from '../DefaultSlot.vue'
4+
5+
useModal({
6+
defaultModelValue: true,
7+
component: DefaultSlot,
8+
attrs: {
9+
text: '123',
10+
onCreate() {
11+
// console.log('onCreated')
12+
},
13+
},
14+
})
15+
</script>
16+
17+
<template>
18+
<ModalsContainer />
19+
</template>

0 commit comments

Comments
 (0)