85 lines
2.4 KiB
Vue
85 lines
2.4 KiB
Vue
<template>
|
|
<div class="relative w-full">
|
|
<div class="relative h-[18px] text-[16px] uppercase font-bold text-black mb-3">
|
|
<div
|
|
v-for="(label, index) in labels"
|
|
:key="label"
|
|
class="absolute top-0 whitespace-nowrap text-primary-500"
|
|
:class="labelClass(index)"
|
|
:style="positionStyle(index)"
|
|
>
|
|
{{ label }}
|
|
</div>
|
|
</div>
|
|
<div class="relative h-[22px]">
|
|
<div class="absolute left-0 right-0 top-1/2 h-[2px] -translate-y-1/2 bg-black"></div>
|
|
<div
|
|
v-for="(_, index) in labels"
|
|
:key="index"
|
|
class="absolute top-1/2 h-[22px] w-[22px] -translate-y-1/2 rounded-full border border-black"
|
|
:class="[
|
|
dotClass(index),
|
|
isActive(index) ? 'bg-black' : 'bg-white',
|
|
isClickable(index) ? 'cursor-pointer' : 'cursor-not-allowed'
|
|
]"
|
|
:style="positionStyle(index)"
|
|
@click="handleClick(index)"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
type Props = {
|
|
labels: string[]
|
|
currentStep: number
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
const emit = defineEmits<{
|
|
(e: 'select', step: number): void
|
|
}>()
|
|
|
|
const stepCount = computed(() => Math.max(props.labels.length, 1))
|
|
|
|
const positionStyle = (index: number) => {
|
|
if (stepCount.value <= 1) {
|
|
return { left: '0%' }
|
|
}
|
|
if (index === 0) {
|
|
return { left: '0%' }
|
|
}
|
|
if (index === stepCount.value - 1) {
|
|
return { right: '0%' }
|
|
}
|
|
const percent = (index / (stepCount.value - 1)) * 100
|
|
return { left: `${percent}%` }
|
|
}
|
|
|
|
const isMiddle = (index: number) => index > 0 && index < stepCount.value - 1
|
|
const isLast = (index: number) => index === stepCount.value - 1
|
|
|
|
const dotClass = (index: number) => (isMiddle(index) ? '-translate-x-1/2' : '')
|
|
|
|
const labelClass = (index: number) => {
|
|
if (isLast(index)) {
|
|
return 'text-right'
|
|
}
|
|
if (isMiddle(index)) {
|
|
return 'text-center -translate-x-1/2'
|
|
}
|
|
return 'text-left'
|
|
}
|
|
|
|
const isActive = (index: number) => index === props.currentStep
|
|
|
|
const isClickable = (index: number) => index <= props.currentStep
|
|
|
|
const handleClick = (index: number) => {
|
|
if (!isClickable(index)) {
|
|
return
|
|
}
|
|
emit('select', index)
|
|
}
|
|
</script>
|