Files
csbi/apps/csbie-ui/src/components/ui/UiSegmented.vue
T
2026-06-19 21:39:59 +09:00

62 lines
2.0 KiB
Vue

<script setup lang="ts" generic="T extends string">
import { LayoutGroup, motion } from 'motion-v'
import { uiMotion } from '../../constants/motion'
import { computed } from 'vue'
const props = defineProps<{
modelValue: T
options: Array<{ label: string; value: T; tone?: 'buy' | 'sell' }>
}>()
const groupId = `segmented-indicator-${Math.random().toString(36).slice(2)}`
const activeOption = computed(() =>
props.options.find((option) => option.value === props.modelValue),
)
const emit = defineEmits<{
'update:modelValue': [value: T]
}>()
const toneClass = (tone?: 'buy' | 'sell') => {
if (tone === 'buy') return 'text-[#003824]'
if (tone === 'sell') return 'text-[#690005]'
return 'text-[#d3e3fd]'
}
</script>
<template>
<LayoutGroup :id="groupId">
<div
class="relative grid min-h-12 overflow-hidden rounded-full bg-[#111418] p-1 outline outline-1 outline-[#33383f]"
:style="{ gridTemplateColumns: `repeat(${props.options.length}, minmax(0, 1fr))` }"
>
<button
v-for="option in props.options"
:key="option.value"
type="button"
:class="[
'relative min-h-10 rounded-full text-sm font-bold transition hover:bg-[#22272e]',
'text-[#9aa0a9]',
props.modelValue === option.value && toneClass(option.tone),
]"
@click="emit('update:modelValue', option.value)"
>
<motion.span
v-if="activeOption?.value === option.value"
:key="`${option.value}:active-indicator`"
:layout-id="`${groupId}-indicator`"
:animate="{ scaleX: [1, 1.1, 1], scaleY: [1, 0.92, 1] }"
:transition="uiMotion.trade.segmentedIndicator"
class="pointer-events-none absolute inset-0 rounded-full"
:class="[
option.tone === 'buy' && 'bg-[#40dba2]',
option.tone === 'sell' && 'bg-[#ffb4ab]',
!option.tone && 'bg-[#263141]',
]"
/>
<span class="relative z-10">{{ option.label }}</span>
</button>
</div>
</LayoutGroup>
</template>