Composant MalioSiteSelector : bande horizontale pour choisir un site (usine ou lieu) parmi une liste. Tuiles flex proportionnelles, couleur du site sélectionné partagée par toutes les tuiles (opacité 1 / 0.4). Expose update:modelValue (id) + change (objet site complet) pour faciliter les appels API côté consommateur. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> | Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #29 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
105 lines
2.2 KiB
Vue
105 lines
2.2 KiB
Vue
<template>
|
|
<div
|
|
v-bind="$attrs"
|
|
:id="componentId"
|
|
role="radiogroup"
|
|
:class="mergedGroupClass"
|
|
>
|
|
<button
|
|
v-for="site in sites"
|
|
:key="site.id"
|
|
type="button"
|
|
role="radio"
|
|
:aria-checked="activeId === site.id"
|
|
:tabindex="activeId === site.id ? 0 : -1"
|
|
:style="{
|
|
backgroundColor: activeColor,
|
|
opacity: activeId === site.id ? 1 : 0.4,
|
|
}"
|
|
:class="mergedTileClass"
|
|
@click="select(site.id)"
|
|
>
|
|
<span :class="mergedLabelClass">{{ site.name }}</span>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {computed, ref, useId} from 'vue'
|
|
import {twMerge} from 'tailwind-merge'
|
|
|
|
defineOptions({name: 'MalioSiteSelector', inheritAttrs: false})
|
|
|
|
type Site = {
|
|
id: string
|
|
name: string
|
|
color: string
|
|
}
|
|
|
|
const props = withDefaults(defineProps<{
|
|
sites: Site[]
|
|
modelValue?: string
|
|
id?: string
|
|
groupClass?: string
|
|
tileClass?: string
|
|
labelClass?: string
|
|
}>(), {
|
|
modelValue: undefined,
|
|
id: '',
|
|
groupClass: '',
|
|
tileClass: '',
|
|
labelClass: '',
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: string): void
|
|
(e: 'change', site: Site): void
|
|
}>()
|
|
|
|
const generatedId = useId()
|
|
const componentId = computed(() => props.id || `malio-site-selector-${generatedId}`)
|
|
|
|
const isControlled = computed(() => props.modelValue !== undefined)
|
|
const localValue = ref(props.sites.length > 0 ? props.sites[0]!.id : '')
|
|
|
|
const activeId = computed(() =>
|
|
isControlled.value ? props.modelValue! : localValue.value,
|
|
)
|
|
|
|
const activeColor = computed(() =>
|
|
props.sites.find((s) => s.id === activeId.value)?.color ?? '',
|
|
)
|
|
|
|
const mergedGroupClass = computed(() =>
|
|
twMerge(
|
|
'flex w-full',
|
|
props.groupClass,
|
|
),
|
|
)
|
|
|
|
const mergedTileClass = computed(() =>
|
|
twMerge(
|
|
'flex-1 cursor-pointer px-6 py-4 text-center transition-opacity focus:outline-none',
|
|
props.tileClass,
|
|
),
|
|
)
|
|
|
|
const mergedLabelClass = computed(() =>
|
|
twMerge(
|
|
'text-white font-bold uppercase tracking-wide',
|
|
props.labelClass,
|
|
),
|
|
)
|
|
|
|
function select(id: string) {
|
|
const site = props.sites.find((s) => s.id === id)
|
|
if (!site) return
|
|
|
|
if (!isControlled.value) {
|
|
localValue.value = id
|
|
}
|
|
emit('update:modelValue', id)
|
|
emit('change', site)
|
|
}
|
|
</script>
|