export type { DefineComponent as VuesticComponent } from 'vue'
diff --git a/packages/ui/src/services/component-config/utils/use-component-config-props.ts b/packages/ui/src/services/component-config/utils/use-component-config-props.ts
index 7c853e8f1b..bd4d61ba8a 100644
--- a/packages/ui/src/services/component-config/utils/use-component-config-props.ts
+++ b/packages/ui/src/services/component-config/utils/use-component-config-props.ts
@@ -1,31 +1,65 @@
-import type { VuesticComponent, VuesticComponentName, Props } from '../types'
+import { VuesticComponentName, Props, VuesticComponent } from '../types'
import { useLocalConfig } from '../../../composables/useLocalConfig'
import { useGlobalConfig } from '../../global-config/global-config'
import { computed } from 'vue'
-import { injectChildPresetPropFromParent } from '../../../composables/useChildComponents'
+import { injectChildPropsFromParent } from '../../../composables/useChildComponents'
+import { ComponentPresetProp, PresetPropValue } from '../../../composables'
+import { notNil } from '../../../utils/isNilValue'
+import { head } from 'lodash'
+import { getObject } from '../../../utils/object-or-getter'
+
+const withPresetProp = (props: P): props is P & ComponentPresetProp => 'preset' in props
+const getPresetProp =
(props: P) => withPresetProp(props) ? props.preset : undefined
export const useComponentConfigProps = (component: T, originalProps: Props) => {
const localConfig = useLocalConfig()
const { globalConfig } = useGlobalConfig()
- const instancePreset = computed(() => originalProps.preset)
- const getPresetProps = (presetName: string) => globalConfig.value.components?.presets?.[component.name as VuesticComponentName]?.[presetName]
- const parentPropPreset = injectChildPresetPropFromParent()
+ const componentName = component.name as VuesticComponentName
+
+ const getPresetProps = (presetPropValue: PresetPropValue): Props => {
+ return (presetPropValue instanceof Array ? presetPropValue : [presetPropValue]).reduce((acc, presetName) => {
+ const preset = globalConfig.value.components?.presets?.[componentName]?.[presetName]
+
+ if (!preset) {
+ return acc
+ }
+
+ const presetProps = getObject(preset, originalProps)
+
+ const extendedPresets = getPresetProp(presetProps)
+
+ return {
+ ...acc,
+ ...(extendedPresets ? getPresetProps(extendedPresets) : undefined),
+ ...presetProps,
+ }
+ }, {})
+ }
+ const parentInjectedProps = injectChildPropsFromParent()
return computed(() => {
const globalConfigProps: Props = {
...globalConfig.value.components?.all,
- ...globalConfig.value.components?.[component.name as VuesticComponentName],
+ ...globalConfig.value.components?.[componentName],
}
- const localConfigProps: Props = localConfig.value
- .reduce((finalConfig, config) => config[component.name as VuesticComponentName]
- ? { ...finalConfig, ...config[component.name as VuesticComponentName] }
- : finalConfig
- , {})
+ const localConfigProps = localConfig.value
+ .reduce((finalConfig, config) => {
+ const componentConfigProps = config[componentName]
+
+ return componentConfigProps
+ ? { ...finalConfig, ...componentConfigProps }
+ : finalConfig
+ }, {})
- const presetName = parentPropPreset?.value || instancePreset.value || localConfigProps.preset || globalConfigProps.preset
- const presetProps = presetName && getPresetProps(presetName)
+ const presetProp = head([
+ parentInjectedProps?.value,
+ originalProps,
+ localConfigProps,
+ globalConfigProps,
+ ].filter(notNil).map(getPresetProp).filter(notNil))
+ const presetProps = presetProp ? getPresetProps(presetProp) : undefined
return { ...globalConfigProps, ...localConfigProps, ...presetProps }
})
diff --git a/packages/ui/src/services/config-transport/createRenderFn.ts b/packages/ui/src/services/config-transport/createRenderFn.ts
index e7113a882f..e6bf5f70eb 100644
--- a/packages/ui/src/services/config-transport/createRenderFn.ts
+++ b/packages/ui/src/services/config-transport/createRenderFn.ts
@@ -1,4 +1,5 @@
import { withCtx, h, DefineComponent, VNode, isVNode, Text, createBlock } from 'vue'
+import type { SlotProp } from '../component-config'
type VueInternalRenderFunction = Function
@@ -6,7 +7,7 @@ export const renderSlotNode = (node: VNode, ctx = null) => {
return withCtx(() => [node], ctx)
}
-export const makeVNode = (node: VNode | string | DefineComponent) => {
+export const makeVNode = (node: SlotProp) => {
if (typeof node === 'string') {
return h(Text, node)
}
diff --git a/packages/ui/src/utils/component-options/types.ts b/packages/ui/src/utils/component-options/types.ts
index e7040eb95b..045438bf3b 100644
--- a/packages/ui/src/utils/component-options/types.ts
+++ b/packages/ui/src/utils/component-options/types.ts
@@ -6,6 +6,11 @@ export type ComponentProps =
T extends (props: infer P, ...args: any) => any ? P :
unknown;
+export type ComponentSlots =
+ T extends new () => { $slots: infer S; } ? NonNullable :
+ T extends (props: any, ctx: { slots: infer S; attrs: any; emit: any; }, ...args: any) => any ? NonNullable :
+ {};
+
export type UnKeyofString = T extends infer E & ThisType ? E : never
export type ExtractVolarEmitsType = 'emits' extends keyof T
? UnKeyofString<(T['emits'] extends infer E | undefined ? E : never)>
diff --git a/packages/ui/src/utils/isNilValue.ts b/packages/ui/src/utils/isNilValue.ts
index 9290399394..c7e46f998f 100644
--- a/packages/ui/src/utils/isNilValue.ts
+++ b/packages/ui/src/utils/isNilValue.ts
@@ -1,3 +1,5 @@
+const nilValues = [null, undefined, '' as const]
+
/**
* Checks if provided value not exists.
*
@@ -5,5 +7,7 @@
*/
export const isNilValue = (value: any): value is null | undefined | '' => {
// lodash `isNil` isn't an alternative, because we also want to handle empty string values
- return [null, undefined, ''].includes(value)
+ return nilValues.includes(value)
}
+
+export const notNil = (value: T): value is NonNullable => !isNilValue(value)
diff --git a/packages/ui/src/utils/object-or-getter.ts b/packages/ui/src/utils/object-or-getter.ts
new file mode 100644
index 0000000000..eb161823c2
--- /dev/null
+++ b/packages/ui/src/utils/object-or-getter.ts
@@ -0,0 +1,9 @@
+import { ObjectOrGetter } from './types/object-or-getter'
+
+export const getObject = (objectOrGetter: ObjectOrGetter, baseProps: P) => {
+ if (typeof objectOrGetter === 'function') {
+ return objectOrGetter(baseProps)
+ }
+
+ return objectOrGetter
+}
diff --git a/packages/ui/src/utils/types/global.d.ts b/packages/ui/src/utils/types/global.d.ts
new file mode 100644
index 0000000000..70353e2762
--- /dev/null
+++ b/packages/ui/src/utils/types/global.d.ts
@@ -0,0 +1,11 @@
+type RemoveIndex = {
+ [ K in keyof T as
+ string extends K
+ ? never
+ : number extends K
+ ? never
+ : symbol extends K
+ ? never
+ : K
+ ]: T[K];
+}
diff --git a/packages/ui/src/utils/types/object-or-getter.ts b/packages/ui/src/utils/types/object-or-getter.ts
new file mode 100644
index 0000000000..ce8f0efb69
--- /dev/null
+++ b/packages/ui/src/utils/types/object-or-getter.ts
@@ -0,0 +1 @@
+export type ObjectOrGetter = T | ((props: P) => T)