<template> <div class="tw-flex tw-items-center" role="radiogroup"> <template v-for="(choice, index) in normalizedChoices" :key="choice.value"> <span v-if="index !== 0" class="tw-self-stretch tw-w-px tw-bg-black/10" /> <button type="button" role="radio" class="btn tw-p-2 tw-m-0 tw-rounded-none tw-justify-center first:tw-rounded-l last:tw-rounded-r tw-relative tw-ring-offset-2 focus:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-z-10" tabindex="0" :aria-checked="modelValue === choice.value" :class="choice.value === modelValue ? 'tw-bg-aura-primary tw-text-white' : 'tw-bg-gray-100'" @keyup.enter="modelValue = choice.value" @click="modelValue = choice.value" > <slot name="icon" :value="choice.value" /> <span v-if="choice.label">{{ choice.label }}</span> </button> </template> </div> </template> <script lang="ts" setup generic="T extends string"> import { computed } from 'vue' defineOptions({ compatConfig: { MODE: 3 } }) type Choice<T> = { value: T; label?: string } type Choices<T> = (Choice<T> | T)[] const modelValue = defineModel({ required: true }) const props = defineProps<{ name: string choices: Choices<T> }>() const normalizedChoices = computed(() => { return props.choices.map((choice) => { if (typeof choice === 'string') { return { value: choice } } return choice }) }) </script>