60 lines
1.5 KiB
Vue
60 lines
1.5 KiB
Vue
<script setup>
|
|
import { computed, defineModel } from 'vue';
|
|
|
|
const model = defineModel(undefined, { required: true });
|
|
const $props = defineProps({
|
|
prop: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
components: {
|
|
type: Object,
|
|
default: () => {},
|
|
},
|
|
value: {
|
|
type: [Object, Number, String, Boolean],
|
|
default: () => {},
|
|
},
|
|
});
|
|
|
|
const componentArray = computed(() => {
|
|
if (typeof $props.prop === 'object') return [$props.prop];
|
|
return $props.prop;
|
|
});
|
|
|
|
function mix(toComponent) {
|
|
const { component, attrs, event } = toComponent;
|
|
const customComponent = $props.components[component];
|
|
return {
|
|
component: customComponent?.component ?? component,
|
|
attrs: {
|
|
...toValueAttrs(attrs),
|
|
...toValueAttrs(customComponent?.attrs),
|
|
...toComponent,
|
|
...toValueAttrs(customComponent?.forceAttrs),
|
|
},
|
|
event: { ...customComponent?.event, ...event },
|
|
};
|
|
}
|
|
|
|
function toValueAttrs(attrs) {
|
|
if (!attrs) return;
|
|
return typeof attrs == 'function' ? attrs($props.value) : attrs;
|
|
}
|
|
</script>
|
|
<template>
|
|
<span
|
|
v-for="toComponent of componentArray"
|
|
:key="toComponent.name"
|
|
class="column flex-center fit"
|
|
>
|
|
<component
|
|
v-if="toComponent?.component"
|
|
:is="mix(toComponent).component"
|
|
v-bind="mix(toComponent).attrs"
|
|
v-on="mix(toComponent).event ?? {}"
|
|
v-model="model"
|
|
/>
|
|
</span>
|
|
</template>
|