This commit is contained in:
zhouwentao 2026-01-24 20:22:44 +08:00
parent b02d66cc2b
commit 8b9602ff4c
3 changed files with 84 additions and 101 deletions

View File

@ -20,11 +20,6 @@ const props = withDefaults(defineProps<Props>(), {
// Emits () // Emits ()
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'change', payload: { keyword: string, filters: FilterState }): void (e: 'change', payload: { keyword: string, filters: FilterState }): void
// //
// (e: 'confirm', filters: FilterState): void;
// //
// (e: 'search', keyword: string): void;
}>() }>()
// --- --- // --- ---
@ -45,41 +40,10 @@ const filters: FilterConfig[] = [
// //
const locations = [ const locations = [
'不限', '不限', '北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江',
'北京', '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南', '湖北',
'天津', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州', '云南', '西藏',
'河北', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾', '香港', '澳门',
'山西',
'内蒙古',
'辽宁',
'吉林',
'黑龙江',
'上海',
'江苏',
'浙江',
'安徽',
'福建',
'江西',
'山东',
'河南',
'湖北',
'湖南',
'广东',
'广西',
'海南',
'重庆',
'四川',
'贵州',
'云南',
'西藏',
'陕西',
'甘肃',
'青海',
'宁夏',
'新疆',
'台湾',
'香港',
'澳门',
] ]
// --- --- // --- ---
@ -95,19 +59,13 @@ const selectedFilters = reactive({
sort: '默认', sort: '默认',
}) })
// selectedFilters
//
// --- --- // --- ---
function getLabel(key: FilterKey) { function getLabel(key: FilterKey) {
//
//
const map = { location: '位置', type: '类型', major: '专业', sort: '排序' } const map = { location: '位置', type: '类型', major: '专业', sort: '排序' }
return map[key] return map[key]
} }
const countSelected = computed(() => { const countSelected = computed(() => {
// 10
if (activeFilter.value === 'location') { if (activeFilter.value === 'location') {
return selectedFilters.location !== '不限' ? 1 : 0 return selectedFilters.location !== '不限' ? 1 : 0
} }
@ -115,7 +73,6 @@ const countSelected = computed(() => {
}) })
function shouldShowFilter(filter: FilterConfig) { function shouldShowFilter(filter: FilterConfig) {
//
return filter.key !== 'sort' || (filter.key === 'sort' && props.sortEnabled) return filter.key !== 'sort' || (filter.key === 'sort' && props.sortEnabled)
} }
@ -146,15 +103,12 @@ function clearCurrentFilter() {
// //
function confirmSelection() { function confirmSelection() {
emit('change', { keyword: searchQuery.value, filters: { ...selectedFilters } }) emit('change', { keyword: searchQuery.value, filters: { ...selectedFilters } })
// 使 {...}
// emit('confirm', { ...selectedFilters });
activeFilter.value = null // activeFilter.value = null //
} }
// //
function handleSearch() { function handleSearch() {
emit('change', { keyword: searchQuery.value, filters: { ...selectedFilters } }) emit('change', { keyword: searchQuery.value, filters: { ...selectedFilters } })
// emit('search', searchQuery.value);
} }
// --- --- // --- ---
@ -174,8 +128,9 @@ onUnmounted(() => {
</script> </script>
<template> <template>
<!-- 最外层容器用于定位下拉菜单和监听点击外部事件 --> <!-- 最外层容器 -->
<div ref="containerRef" class="relative mx-auto max-w-5xl w-full select-none text-sm text-gray-600 font-sans"> <!-- Update: text-gray-600 -> dark:text-slate-300 -->
<div ref="containerRef" class="relative mx-auto w-full max-w-5xl select-none font-sans text-sm text-gray-600 dark:text-slate-300">
<!-- 顶部栏筛选按钮组 + 搜索框 --> <!-- 顶部栏筛选按钮组 + 搜索框 -->
<div class="flex flex-wrap items-center justify-between gap-4"> <div class="flex flex-wrap items-center justify-between gap-4">
<!-- 左侧4个下拉筛选器 --> <!-- 左侧4个下拉筛选器 -->
@ -187,14 +142,30 @@ onUnmounted(() => {
class="relative" class="relative"
> >
<!-- 筛选器按钮 --> <!-- 筛选器按钮 -->
<!-- Update:
bg-white -> dark:bg-slate-800
border-gray-200 -> dark:border-slate-700
ring-blue-200 -> dark:ring-blue-900 (Focus ring 变深)
-->
<button <button
class="h-9 w-24 flex items-center justify-between border rounded bg-white px-3 transition-colors hover:border-blue-400" class="flex h-9 w-24 items-center justify-between rounded border px-3 transition-colors hover:border-blue-400"
:class="activeFilter === filter.key ? 'border-blue-500 ring-1 ring-blue-200' : 'border-gray-200'" :class="[
activeFilter === filter.key
? 'border-blue-500 ring-1 ring-blue-200 dark:ring-blue-900'
: 'border-gray-200 dark:border-slate-700',
'bg-white dark:bg-slate-800'
]"
@click="toggleFilter(filter.key)" @click="toggleFilter(filter.key)"
> >
<span class="truncate">{{ getLabel(filter.key) }}</span> <span class="truncate">{{ getLabel(filter.key) }}</span>
<!-- 下箭头图标 --> <!-- 下箭头图标 -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 text-gray-400 transition-transform duration-200" :class="{ 'rotate-180': activeFilter === filter.key }" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <!-- Update: text-gray-400 -> dark:text-slate-500 -->
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3 text-gray-400 transition-transform duration-200 dark:text-slate-500"
:class="{ 'rotate-180': activeFilter === filter.key }"
fill="none" viewBox="0 0 24 24" stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg> </svg>
</button> </button>
@ -205,17 +176,19 @@ onUnmounted(() => {
<div class="flex flex-wrap items-center"> <div class="flex flex-wrap items-center">
<div class="relative"> <div class="relative">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<svg class="h-4 w-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg> <!-- Update: Icon 颜色 -->
<svg class="h-4 w-4 text-gray-400 dark:text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /></svg>
</div> </div>
<!-- Update: 输入框颜色适配 -->
<input <input
v-model="searchQuery" v-model="searchQuery"
type="text" type="text"
class="h-9 w-50 border border-gray-300 rounded-l bg-white py-2 pl-9 pr-4 text-gray-700 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-200 placeholder-gray-400" class="h-9 w-50 rounded-l border border-gray-300 py-2 pl-9 pr-4 text-gray-700 placeholder-gray-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-200 dark:bg-slate-800 dark:border-slate-700 dark:text-slate-200 dark:placeholder-slate-500 dark:focus:ring-blue-900"
placeholder="输入院校名称" placeholder="输入院校名称"
> >
</div> </div>
<button <button
class="h-9 rounded-r bg-blue-500 px-6 text-white transition-colors active:bg-blue-700 hover:bg-blue-600 focus:outline-none" class="h-9 rounded-r bg-blue-500 px-6 text-white transition-colors hover:bg-blue-600 focus:outline-none active:bg-blue-700"
@click="handleSearch" @click="handleSearch"
> >
搜索 搜索
@ -224,7 +197,6 @@ onUnmounted(() => {
</div> </div>
<!-- 下拉菜单面板 (绝对定位) --> <!-- 下拉菜单面板 (绝对定位) -->
<!-- 使用 Transition 添加简单的淡入淡出效果 -->
<transition <transition
enter-active-class="transition duration-100 ease-out" enter-active-class="transition duration-100 ease-out"
enter-from-class="transform scale-95 opacity-0" enter-from-class="transform scale-95 opacity-0"
@ -233,25 +205,27 @@ onUnmounted(() => {
leave-from-class="transform scale-100 opacity-100" leave-from-class="transform scale-100 opacity-100"
leave-to-class="transform scale-95 opacity-0" leave-to-class="transform scale-95 opacity-0"
> >
<!-- style="min-width: 600px;" --> <!-- Update: 面板背景与边框 -->
<div <div
v-if="activeFilter" v-if="activeFilter"
class="absolute left-0 top-full z-50 mt-2 border border-gray-100 rounded-lg bg-white p-5 shadow-xl" class="absolute left-0 top-full z-50 mt-2 border border-gray-100 bg-white p-5 shadow-xl rounded-lg dark:bg-slate-800 dark:border-slate-700"
> >
<!-- 下拉内容区域 --> <!-- 下拉内容区域 -->
<!-- 1. 位置 (Location) 内容 --> <!-- 1. 位置 (Location) 内容 -->
<div v-if="activeFilter === 'location'"> <div v-if="activeFilter === 'location'">
<div class="flex items-start gap-4"> <div class="flex items-start gap-4">
<span class="mt-1 shrink-0 text-gray-400 font-medium">院校所属</span> <!-- Update: 标签文字颜色 -->
<div class="xs:grid-cols-3 grid gap-2 lg:grid-cols-6 md:grid-cols-5 sm:grid-cols-3"> <span class="mt-1 shrink-0 font-medium text-gray-400 dark:text-slate-500">院校所属</span>
<div class="grid gap-2 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-6 xs:grid-cols-3">
<!-- Update: 选项 Tag 的选中/未选中态 -->
<button <button
v-for="city in locations" v-for="city in locations"
:key="city" :key="city"
class="rounded px-3 py-1.5 text-center transition-colors hover:text-blue-500" class="rounded px-3 py-1.5 text-center transition-colors hover:text-blue-500 dark:hover:text-blue-400"
:class="selectedFilters.location === city :class="selectedFilters.location === city
? 'bg-blue-50 text-blue-500 font-medium' ? 'bg-blue-50 text-blue-500 font-medium dark:bg-blue-900/40 dark:text-blue-400'
: 'text-gray-600 bg-gray-50 hover:bg-gray-100'" : 'text-gray-600 bg-gray-50 hover:bg-gray-100 dark:bg-slate-700 dark:text-slate-300 dark:hover:bg-slate-600'"
@click="selectOption('location', city)" @click="selectOption('location', city)"
> >
{{ city }} {{ city }}
@ -260,19 +234,21 @@ onUnmounted(() => {
</div> </div>
</div> </div>
<!-- 2. 其他筛选器占位内容 (类型专业排序) --> <!-- 2. 其他筛选器占位内容 -->
<div v-else class="p-4 text-center text-gray-400"> <div v-else class="p-4 text-center text-gray-400 dark:text-slate-500">
这里是 {{ filters.find(f => f.key === activeFilter)?.label }} 的筛选选项 这里是 {{ filters.find(f => f.key === activeFilter)?.label }} 的筛选选项
</div> </div>
<!-- 底部操作按钮 --> <!-- 底部操作按钮 -->
<div class="mt-6 flex items-center justify-between border-t border-gray-100 pt-4"> <!-- Update: 分割线颜色 -->
<div class="text-gray-500"> <div class="mt-6 flex items-center justify-between border-t border-gray-100 pt-4 dark:border-slate-700">
已选 <span class="text-blue-500 font-bold">{{ countSelected }}</span> <div class="text-gray-500 dark:text-slate-400">
已选 <span class="font-bold text-blue-500 dark:text-blue-400">{{ countSelected }}</span>
</div> </div>
<div class="flex gap-3"> <div class="flex gap-3">
<!-- Update: 清空按钮 Hover 背景 -->
<button <button
class="border border-blue-500 rounded px-4 py-1.5 text-blue-500 transition-colors hover:bg-blue-50" class="rounded border border-blue-500 px-4 py-1.5 text-blue-500 transition-colors hover:bg-blue-50 dark:text-blue-400 dark:border-blue-500 dark:hover:bg-blue-900/20"
@click="clearCurrentFilter" @click="clearCurrentFilter"
> >
清空已选 清空已选
@ -289,10 +265,3 @@ onUnmounted(() => {
</transition> </transition>
</div> </div>
</template> </template>
<style scoped>
/*
如果你的 tailwind 配置没有自动移除按钮的默认样式可能需要以下代码
一般在 tailwind base 中已经处理好了
*/
</style>

View File

@ -120,16 +120,14 @@ const overlayPositionClass = computed(() => {
// 2. ( Popover) // 2. ( Popover)
const arrowPositionClass = computed(() => { const arrowPositionClass = computed(() => {
const base = 'absolute h-3 w-3 bg-white border-slate-200 z-[-1]' // z-index -1 // Update: dark:bg-slate-800 dark:border-slate-700
const base = 'absolute h-3 w-3 bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700 z-[-1]'
// //
// const centerOffset = props.arrowPointAtCenter ? 'left-1/2 -translate-x-1/2' : ''
// PointAtCenter Left/Right
const hAlign = props.arrowPointAtCenter ? '' : (props.placement.includes('left') ? 'left-4' : props.placement.includes('right') ? 'right-4' : '') const hAlign = props.arrowPointAtCenter ? '' : (props.placement.includes('left') ? 'left-4' : props.placement.includes('right') ? 'right-4' : '')
const vAlign = props.arrowPointAtCenter ? '' : (props.placement.includes('top') ? 'top-3' : props.placement.includes('bottom') ? 'bottom-3' : '') const vAlign = props.arrowPointAtCenter ? '' : (props.placement.includes('top') ? 'top-3' : props.placement.includes('bottom') ? 'bottom-3' : '')
// 12 // 12
// Top Bottom (45)
if (props.placement.startsWith('top')) { if (props.placement.startsWith('top')) {
return `${base} -bottom-1.5 border-b border-r rotate-45 ${props.placement === 'top' || props.arrowPointAtCenter ? 'left-1/2 -translate-x-1/2' : hAlign}` return `${base} -bottom-1.5 border-b border-r rotate-45 ${props.placement === 'top' || props.arrowPointAtCenter ? 'left-1/2 -translate-x-1/2' : hAlign}`
} }
@ -145,7 +143,7 @@ const arrowPositionClass = computed(() => {
return '' return ''
}) })
// 3. (Transform Origin) // 3.
const transitionOriginClass = computed(() => { const transitionOriginClass = computed(() => {
if (props.placement.startsWith('top')) if (props.placement.startsWith('top'))
return 'origin-bottom' return 'origin-bottom'
@ -162,9 +160,13 @@ const transitionOriginClass = computed(() => {
const okBtnClass = computed(() => { const okBtnClass = computed(() => {
const base = 'rounded px-3 py-1 text-xs text-white transition-colors' const base = 'rounded px-3 py-1 text-xs text-white transition-colors'
switch (props.okType) { switch (props.okType) {
case 'danger': return `${base} bg-red-500 hover:bg-red-600 border border-red-500` case 'danger':
case 'default': return 'rounded px-3 py-1 text-xs text-slate-600 border border-slate-200 hover:border-blue-400 hover:text-blue-500 transition-colors bg-white' return `${base} bg-red-500 hover:bg-red-600 border border-red-500`
default: return `${base} bg-blue-600 hover:bg-blue-700 border border-blue-600` case 'default':
// Update: /
return 'rounded px-3 py-1 text-xs text-slate-600 dark:text-slate-300 border border-slate-200 dark:border-slate-600 hover:border-blue-400 hover:text-blue-500 transition-colors bg-white dark:bg-slate-800'
default:
return `${base} bg-blue-600 hover:bg-blue-700 border border-blue-600`
} }
}) })
</script> </script>
@ -187,8 +189,13 @@ const okBtnClass = computed(() => {
> >
<div <div
v-if="isVisible" v-if="isVisible"
class="absolute z-50 w-45 cursor-default border border-slate-200 rounded-lg bg-white p-3 shadow-xl" class="absolute z-50 w-45 cursor-default border rounded-lg p-3 shadow-xl"
:class="[overlayPositionClass, transitionOriginClass]" :class="[
overlayPositionClass,
transitionOriginClass,
// Update: Dark Mode
'bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700'
]"
@click.stop @click.stop
> >
<!-- Arrow --> <!-- Arrow -->
@ -198,18 +205,20 @@ const okBtnClass = computed(() => {
<div class="relative z-10 flex items-start gap-3"> <div class="relative z-10 flex items-start gap-3">
<div class="mt-0.5 flex-shrink-0"> <div class="mt-0.5 flex-shrink-0">
<slot name="icon"> <slot name="icon">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-orange-500" viewBox="0 0 20 20" fill="currentColor"> <!-- Update: Icon 颜色微调适配暗色 -->
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-orange-500 dark:text-orange-400" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" /> <path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
</svg> </svg>
</slot> </slot>
</div> </div>
<div class="flex-1"> <div class="flex-1">
<div class="break-words text-sm text-slate-800 font-bold"> <!-- Update: 文字颜色适配 -->
<div class="break-words text-sm font-bold text-slate-800 dark:text-slate-100">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
</slot> </slot>
</div> </div>
<div v-if="description || $slots.description" class="mt-1 break-words text-xs text-slate-500"> <div v-if="description || $slots.description" class="mt-1 break-words text-xs text-slate-500 dark:text-slate-400">
<slot name="description"> <slot name="description">
{{ description }} {{ description }}
</slot> </slot>
@ -222,7 +231,12 @@ const okBtnClass = computed(() => {
<slot name="cancelButton"> <slot name="cancelButton">
<button <button
v-if="showCancel" v-if="showCancel"
class="rounded px-2 py-1 text-xs text-slate-500 transition-colors hover:bg-slate-100" class="rounded px-2 py-1 text-xs transition-colors"
:class="[
// Update:
'text-slate-500 dark:text-slate-400',
'hover:bg-slate-100 dark:hover:bg-slate-700'
]"
:disabled="cancelButtonProps.disabled" :disabled="cancelButtonProps.disabled"
@click="handleCancel" @click="handleCancel"
> >

View File

@ -801,7 +801,7 @@ function deletePlan(planId: string) {
<!-- 注意bg-white 是为了遮挡滚动的文字group-hover:bg-slate-50 是为了保持 hover 效果 --> <!-- 注意bg-white 是为了遮挡滚动的文字group-hover:bg-slate-50 是为了保持 hover 效果 -->
<td <td
rowspan="4" rowspan="4"
class="sticky left-0 z-20 border-r border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-4 align-top shadow-[4px_0_8px_-4px_rgba(0,0,0,0.1)] group-hover:bg-slate-50 dark:group-hover:bg-slate-800/50" class="sticky left-0 z-20 border-r border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-4 align-top shadow-[4px_0_8px_-4px_rgba(0,0,0,0.1)] group-hover:bg-slate-50 dark:group-hover:bg-slate-800"
> >
<div class="mb-1 w-52 truncate text-left text-base text-slate-900 dark:text-slate-100 font-bold" :title="school.schoolName"> <div class="mb-1 w-52 truncate text-left text-base text-slate-900 dark:text-slate-100 font-bold" :title="school.schoolName">
{{ school.schoolName }} {{ school.schoolName }}
@ -831,7 +831,7 @@ function deletePlan(planId: string) {
<td <td
rowspan="4" rowspan="4"
class="border-r border-slate-100 bg-white p-2 text-center align-top group-hover:bg-slate-50" class="border-r border-slate-100 dark:border-slate-800 bg-white dark:bg-slate-900 p-2 text-center align-top group-hover:bg-slate-50 dark:group-hover:bg-slate-800"
> >
<div class="mb-2 text-lg font-bold text-slate-900 dark:text-slate-100"> <div class="mb-2 text-lg font-bold text-slate-900 dark:text-slate-100">
{{ school.enrollProbability }}% {{ school.enrollProbability }}%
@ -851,7 +851,7 @@ function deletePlan(planId: string) {
<td <td
rowspan="4" rowspan="4"
class="border-r border-slate-100 bg-white p-2 text-center align-top group-hover:bg-slate-50" class="border-r border-slate-100 dark:border-slate-800 bg-white dark:bg-slate-900 p-2 text-center align-top group-hover:bg-slate-50 dark:group-hover:bg-slate-800"
> >
<div class="text-lg font-medium text-slate-900 dark:text-slate-100"> <div class="text-lg font-medium text-slate-900 dark:text-slate-100">
{{ school.planNum }} {{ school.planNum }}
@ -870,7 +870,7 @@ function deletePlan(planId: string) {
<!-- Sticky Right: 操作 --> <!-- Sticky Right: 操作 -->
<td <td
rowspan="4" rowspan="4"
class="sticky right-0 z-20 border-l border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-4 text-center align-middle shadow-[-4px_0_8px_-4px_rgba(0,0,0,0.1)] group-hover:bg-slate-50 dark:group-hover:bg-slate-800/50" class="sticky right-0 z-20 border-l border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-4 text-center align-middle shadow-[-4px_0_8px_-4px_rgba(0,0,0,0.1)] group-hover:bg-slate-50 dark:group-hover:bg-slate-800"
> >
<button <button
class="whitespace-nowrap border border-blue-500 rounded-full bg-white dark:bg-slate-900 px-3 py-1.5 text-xs text-blue-500 dark:text-blue-400 transition-colors hover:bg-blue-50 dark:hover:bg-blue-900/20" class="whitespace-nowrap border border-blue-500 rounded-full bg-white dark:bg-slate-900 px-3 py-1.5 text-xs text-blue-500 dark:text-blue-400 transition-colors hover:bg-blue-50 dark:hover:bg-blue-900/20"
@ -1272,7 +1272,7 @@ function deletePlan(planId: string) {
省内招生人数 省内招生人数
</th> </th>
<th <th
class="sticky right-0 bg-slate-100 px-6 py-3 text-center shadow-[-4px_0_8px_-4px_rgba(0,0,0,0.1)]" class="sticky right-0 px-6 py-3 text-center shadow-[-4px_0_8px_-4px_rgba(0,0,0,0.1)]"
> >
加入志愿 加入志愿
</th> </th>
@ -1325,7 +1325,7 @@ function deletePlan(planId: string) {
<!-- 操作列加入/移除 --> <!-- 操作列加入/移除 -->
<td <td
class="sticky right-0 bg-white dark:bg-slate-900 px-6 py-4 text-center shadow-[-4px_0_8px_-4px_rgba(0,0,0,0.1)] group-hover:bg-blue-50/30 dark:group-hover:bg-blue-900/10" class="sticky right-0 border-l border-slate-100 dark:border-slate-800 bg-white dark:bg-slate-900 px-6 py-4 text-center shadow-[-4px_0_8px_-4px_rgba(0,0,0,0.1)] group-hover:bg-blue-50 dark:group-hover:bg-slate-800"
> >
<button <button
class="min-w-[90px] rounded-full px-3 py-1.5 text-xs font-medium transition-all duration-200" class="min-w-[90px] rounded-full px-3 py-1.5 text-xs font-medium transition-all duration-200"