Skip to content

Commit 8304d63

Browse files
committed
fix: fixed generic component type in useModal composable
1 parent 401d855 commit 8304d63

File tree

4 files changed

+38
-37
lines changed

4 files changed

+38
-37
lines changed

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

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
1-
import type { App, CSSProperties, Component, ComponentPublicInstance, ComputedRef, Raw, Ref, VNodeProps } from 'vue'
2-
3-
export type ComponentProps = ComponentPublicInstance['$props']
1+
import type { App, CSSProperties, ComputedRef, FunctionalComponent, Raw, Ref } from 'vue'
42

53
export type ModalId = number | string | symbol
64
export type StyleValue = string | CSSProperties | (string | CSSProperties)[]
75

8-
export interface Constructor<P = any> {
6+
/** A fake Component Constructor that is only used for extracting `$props` as type `P` */
7+
type Constructor<P = any> = {
98
__isFragment?: never
109
__isTeleport?: never
1110
__isSuspense?: never
1211
new (...args: any[]): { $props: P }
1312
}
1413

15-
export type RawProps = VNodeProps & {
16-
// used to differ from a single VNode object as children
17-
__v_isVNode?: never
18-
// used to differ from Array children
19-
[Symbol.iterator]?: never
20-
} & Record<string, any>
14+
export interface ModalSlotOptions { component: Raw<ComponentType>; attrs?: Record<string, any> }
15+
export type ModalSlot = string | ComponentType | ModalSlotOptions
16+
17+
type ComponentConstructor = (abstract new (...args: any) => any)
18+
/** Including both generic and non-generic vue components */
19+
export type ComponentType = ComponentConstructor | FunctionalComponent<any, any>
2120

22-
export interface ModalSlotOptions { component: Raw<Component>; attrs?: Record<string, any> }
23-
export type ModalSlot = string | Component | ModalSlotOptions
21+
type FunctionalComponentProps<T> = T extends FunctionalComponent<infer P> ? P : Record<any, any>
22+
type NonGenericComponentProps<T> = T extends Constructor<infer P> ? P : Record<any, any>
23+
export type ComponentProps<T extends ComponentType> =
24+
T extends ComponentConstructor
25+
? NonGenericComponentProps<T>
26+
: FunctionalComponentProps<T>
2427

25-
export type UseModalOptions<P> = {
28+
export type UseModalOptions<T extends ComponentType> = {
2629
defaultModelValue?: boolean
2730
keepAlive?: boolean
28-
component?: Constructor<P>
29-
attrs?: (RawProps & P) | ({} extends P ? null : never)
31+
component?: T
32+
attrs?: ComponentProps<T>
3033
slots?: {
3134
[key: string]: ModalSlot
3235
}
@@ -39,11 +42,11 @@ export type UseModalOptionsPrivate = {
3942
resolveClosed: () => void
4043
}
4144

42-
export interface UseModalReturnType<P> {
43-
options: UseModalOptions<P> & UseModalOptionsPrivate
45+
export interface UseModalReturnType<T extends ComponentType> {
46+
options: UseModalOptions<T> & UseModalOptionsPrivate
4447
open: () => Promise<string>
4548
close: () => Promise<string>
46-
patchOptions: (options: Partial<UseModalOptions<P>>) => void
49+
patchOptions: (options: Partial<UseModalOptions<T>>) => void
4750
destroy: () => void
4851
}
4952

packages/vue-final-modal/src/components/ModalsContainer.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ onBeforeUnmount(() => {
2323
:key="modal.id"
2424
v-bind="{
2525
displayDirective: modal?.keepAlive ? 'show' : undefined,
26-
...modal.attrs,
26+
...(typeof modal.attrs === 'object' ? modal.attrs : {}),
2727
}"
2828
v-model="modal.modelValue"
2929
@closed="() => _vfm.resolvedClosed?.(index)"

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

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { computed, inject, markRaw, nextTick, reactive, useAttrs } from 'vue'
22
import { tryOnUnmounted } from '@vueuse/core'
3-
import type { Component } from 'vue'
43
import VueFinalModal from './components/VueFinalModal/VueFinalModal.vue'
54
import type CoreModal from './components/CoreModal/CoreModal.vue'
65
import { internalVfmSymbol } from './injectionSymbols'
76

8-
import type { ComponentProps, Constructor, InternalVfm, ModalSlot, ModalSlotOptions, RawProps, UseModalOptions, UseModalOptionsPrivate, UseModalReturnType, Vfm } from './Modal'
7+
import type { ComponentProps, ComponentType, InternalVfm, ModalSlot, ModalSlotOptions, UseModalOptions, UseModalOptionsPrivate, UseModalReturnType, Vfm } from './Modal'
98
import { activeVfm, getActiveVfm } from './plugin'
109
import { isString } from '~/utils'
1110

@@ -34,7 +33,7 @@ export function useInternalVfm(): InternalVfm {
3433
return inject(internalVfmSymbol)!
3534
}
3635

37-
function withMarkRaw<P>(options: Partial<UseModalOptions<P>>, DefaultComponent: Component = VueFinalModal) {
36+
function withMarkRaw<T extends ComponentType>(options: Partial<UseModalOptions<T>>, DefaultComponent: ComponentType = VueFinalModal) {
3837
const { component, slots: innerSlots, ...rest } = options
3938

4039
const slots = typeof innerSlots === 'undefined'
@@ -55,23 +54,23 @@ function withMarkRaw<P>(options: Partial<UseModalOptions<P>>, DefaultComponent:
5554

5655
return {
5756
...rest,
58-
component: markRaw(component || DefaultComponent) as Constructor<P>,
57+
component: markRaw(component || DefaultComponent),
5958
slots,
6059
}
6160
}
6261

6362
/**
6463
* Create a dynamic modal.
6564
*/
66-
export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_options: UseModalOptions<P>): UseModalReturnType<P> {
65+
export function useModal<T extends ComponentType = typeof VueFinalModal>(_options: UseModalOptions<T>): UseModalReturnType<T> {
6766
const options = reactive({
6867
id: Symbol('useModal'),
6968
modelValue: !!_options?.defaultModelValue,
7069
resolveOpened: () => { },
7170
resolveClosed: () => { },
7271
attrs: {},
73-
...withMarkRaw<P>(_options),
74-
}) as UseModalOptions<P> & UseModalOptionsPrivate
72+
...withMarkRaw<T>(_options),
73+
}) as UseModalOptions<T> & UseModalOptionsPrivate
7574
tryOnUnmounted(() => {
7675
if (!options?.keepAlive)
7776
destroy()
@@ -122,7 +121,7 @@ export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_opti
122121
})
123122
}
124123

125-
function patchOptions(_options: Partial<UseModalOptions<P>>) {
124+
function patchOptions(_options: Partial<UseModalOptions<T>>) {
126125
const { slots, ...rest } = withMarkRaw(_options, options.component)
127126

128127
if (_options.defaultModelValue !== undefined)
@@ -147,9 +146,9 @@ export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_opti
147146
}
148147
}
149148

150-
function patchComponentOptions<P>(
151-
options: UseModalOptions<P> | ModalSlotOptions,
152-
newOptions: Partial<UseModalOptions<P>> | ModalSlotOptions,
149+
function patchComponentOptions<T extends ComponentType>(
150+
options: UseModalOptions<T> | ModalSlotOptions,
151+
newOptions: Partial<UseModalOptions<T>> | ModalSlotOptions,
153152
) {
154153
if (newOptions.component)
155154
options.component = newOptions.component
@@ -182,9 +181,9 @@ export function useModal<P = InstanceType<typeof VueFinalModal>['$props']>(_opti
182181
}
183182
}
184183

185-
export function useModalSlot<P>(options: {
186-
component: Constructor<P>
187-
attrs?: (RawProps & P) | ({} extends P ? null : never)
184+
export function useModalSlot<T extends ComponentType>(options: {
185+
component: T
186+
attrs?: ComponentProps<T>
188187
}) {
189188
return options
190189
}
@@ -217,8 +216,8 @@ export function byPassAllModalEvents(emit?: InstanceType<typeof CoreModal>['$emi
217216
}
218217

219218
export function useVfmAttrs(options: {
220-
props: ComponentProps
221-
modalProps: ComponentProps
219+
props: Record<any, any>
220+
modalProps: Record<any, any>
222221
emit?: any
223222
}) {
224223
const { props, modalProps, emit } = options

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ modal.open().then((res) => { console.log('res', res) })
1414
modal.open().then((res) => { console.log('res', res) })
1515
const modal1 = useModal({
1616
keepAlive: true,
17-
component: VueFinalModal,
1817
attrs: {
1918
// 'displayDirective': 'if',
2019
'background': 'interactive',
2120
'lockScroll': false,
2221
'contentStyle': { backgroundColor: '#fff' },
2322
'onUpdate:modelValue': function (val) {
24-
// console.log('onUpdate:modelValue', val)
23+
console.log('onUpdate:modelValue', val)
2524
},
2625
onClosed() { console.log('onClosed') },
2726
onBeforeClose() { console.log('onBeforeClose') },

0 commit comments

Comments
 (0)