59 lines
1.4 KiB
Vue
59 lines
1.4 KiB
Vue
<template>
|
|
<label style="display: grid; gap: 6px;">
|
|
<span>{{ t('labels.emplacement') }}</span>
|
|
<select v-model="selectedId">
|
|
<option value="">{{ t('labels.chooseEmplacement') }}</option>
|
|
<option v-for="opt in options" :key="opt.id" :value="opt.id">
|
|
{{ opt.label }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const { t } = useI18n()
|
|
|
|
type Emplacement = {
|
|
id: string
|
|
nom: string
|
|
parent_id?: string | null
|
|
}
|
|
|
|
type Option = { id: string; label: string }
|
|
|
|
const props = defineProps<{
|
|
items: Emplacement[]
|
|
modelValue: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: string): void
|
|
}>()
|
|
|
|
const selectedId = computed({
|
|
get: () => props.modelValue,
|
|
set: (value) => emit('update:modelValue', value)
|
|
})
|
|
|
|
const options = computed<Option[]>(() => {
|
|
const map = new Map(props.items.map((item) => [item.id, item]))
|
|
const cache = new Map<string, number>()
|
|
|
|
const depthOf = (item: Emplacement): number => {
|
|
if (!item.parent_id) return 0
|
|
if (cache.has(item.id)) return cache.get(item.id) || 0
|
|
const parent = map.get(item.parent_id)
|
|
const depth = parent ? depthOf(parent) + 1 : 0
|
|
cache.set(item.id, depth)
|
|
return depth
|
|
}
|
|
|
|
return props.items
|
|
.map((item) => ({
|
|
id: item.id,
|
|
label: `${' '.repeat(depthOf(item) * 2)}${item.nom}`
|
|
}))
|
|
.sort((a, b) => a.label.localeCompare(b.label))
|
|
})
|
|
</script>
|