130 lines
3.2 KiB
Vue
130 lines
3.2 KiB
Vue
<template>
|
||
<div class="border border-base-200 rounded-lg bg-base-100" :class="depthPadding">
|
||
<div class="flex items-center justify-between gap-3 px-4 py-3">
|
||
<div class="flex-1">
|
||
<select
|
||
v-model="node.typeComposantId"
|
||
class="select select-bordered select-sm w-full"
|
||
@change="handleComponentTypeSelect(node)"
|
||
>
|
||
<option value="">
|
||
Sélectionner une famille de composant
|
||
</option>
|
||
<option
|
||
v-for="type in componentTypes"
|
||
:key="type.id"
|
||
:value="type.id"
|
||
>
|
||
{{ formatComponentTypeOption(type) }}
|
||
</option>
|
||
</select>
|
||
<p class="mt-1 text-[11px] text-gray-500">
|
||
{{ node.typeComposantId ? `Sélection : ${getComponentTypeLabel(node.typeComposantId) || 'Inconnue'}` : 'Aucune famille sélectionnée' }}
|
||
</p>
|
||
</div>
|
||
<button type="button" class="btn btn-error btn-xs btn-square" @click="emit('remove')">
|
||
<IconLucideTrash class="w-4 h-4" aria-hidden="true" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, watch } from 'vue'
|
||
import IconLucideTrash from '~icons/lucide/trash'
|
||
|
||
defineOptions({ name: 'StructureSubComponentEditor' })
|
||
|
||
type ModelTypeOption = {
|
||
id: string
|
||
name: string
|
||
code?: string | null
|
||
}
|
||
|
||
const props = withDefaults(defineProps<{
|
||
node: Record<string, any>
|
||
depth?: number
|
||
componentTypes?: ModelTypeOption[]
|
||
}>(), {
|
||
depth: 0,
|
||
componentTypes: () => [],
|
||
})
|
||
|
||
const emit = defineEmits(['remove'])
|
||
|
||
const componentTypes = computed(() => props.componentTypes ?? [])
|
||
|
||
const depthPadding = computed(() => {
|
||
const level = props.depth ?? 0
|
||
return level > 0 ? `ml-${Math.min(level * 4, 12)}` : ''
|
||
})
|
||
|
||
const formatModelTypeOption = (type: ModelTypeOption | undefined | null) => {
|
||
if (!type) {
|
||
return ''
|
||
}
|
||
return type.code ? `${type.name} (${type.code})` : type.name
|
||
}
|
||
|
||
const formatComponentTypeOption = (type: ModelTypeOption | undefined | null) =>
|
||
formatModelTypeOption(type)
|
||
|
||
const componentTypeMap = computed(() => {
|
||
const map = new Map<string, ModelTypeOption>()
|
||
componentTypes.value.forEach((type) => {
|
||
if (type && typeof type.id === 'string') {
|
||
map.set(type.id, type)
|
||
}
|
||
})
|
||
return map
|
||
})
|
||
|
||
const getComponentTypeLabel = (id?: string) => {
|
||
if (!id) return ''
|
||
return formatModelTypeOption(componentTypeMap.value.get(id))
|
||
}
|
||
|
||
const syncComponentType = (component: any) => {
|
||
if (!component) {
|
||
return
|
||
}
|
||
const id = typeof component.typeComposantId === 'string'
|
||
? component.typeComposantId
|
||
: ''
|
||
|
||
if (!id) {
|
||
component.typeComposantLabel = ''
|
||
component.name = ''
|
||
return
|
||
}
|
||
|
||
const option = componentTypeMap.value.get(id)
|
||
if (!option) {
|
||
component.typeComposantLabel = ''
|
||
component.name = ''
|
||
return
|
||
}
|
||
|
||
component.typeComposantLabel = formatModelTypeOption(option)
|
||
component.name = option.name || component.typeComposantLabel
|
||
}
|
||
|
||
const handleComponentTypeSelect = (component: any) => {
|
||
syncComponentType(component)
|
||
}
|
||
|
||
watch(componentTypes, () => {
|
||
syncComponentType(props.node)
|
||
}, { deep: true, immediate: true })
|
||
|
||
watch(
|
||
() => props.node.typeComposantId,
|
||
() => {
|
||
syncComponentType(props.node)
|
||
}
|
||
)
|
||
</script>
|
||
|
||
<style scoped>
|
||
</style>
|