This commit is contained in:
Shotaro Nakamura
2026-06-19 21:39:59 +09:00
parent 5a15172513
commit b843a5ff38
36 changed files with 3891 additions and 1008 deletions
+48 -18
View File
@@ -1,31 +1,61 @@
<script setup lang="ts" generic="T extends string">
defineProps<{
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>
<div
class="grid min-h-12 grid-cols-2 gap-1 rounded-full bg-[#111418] p-1 outline outline-1 outline-[#33383f]"
>
<button
v-for="option in options"
:key="option.value"
type="button"
:class="[
'min-h-10 rounded-full bg-transparent text-sm font-bold text-[#9aa0a9] transition hover:bg-[#22272e]',
modelValue === option.value && option.tone === 'buy' && '!bg-[#40dba2] !text-[#003824]',
modelValue === option.value && option.tone === 'sell' && '!bg-[#ffb4ab] !text-[#690005]',
modelValue === option.value && !option.tone && '!bg-[#263141] !text-[#d3e3fd]',
]"
@click="emit('update:modelValue', option.value)"
<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))` }"
>
{{ option.label }}
</button>
</div>
<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>