UPD: Fancy animated TabSelect
This commit is contained in:
@@ -1,26 +1,61 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex font-heading px-4 py-2 gap-2">
|
<div class="relative">
|
||||||
<div class="absolute"/>
|
<div
|
||||||
<TabSelectButton
|
:style="backgroundStyle"
|
||||||
v-for="(option, key) in options" :key="key"
|
class="absolute bg-zinc-300 outline outline-zinc-100 rounded-xl transition-all ease-out duration-75" />
|
||||||
:title="$t(option.titleKey)"
|
<div class="flex font-heading px-4 py-2 gap-2 relative">
|
||||||
:icon="option.icon" :selected="model===key"
|
<TabSelectButton
|
||||||
@select="model=key">
|
v-for="(option, key) in options" :key="key"
|
||||||
<template v-if="$slots[key]" #replace>
|
:ref="(el) => buttons[key] = el"
|
||||||
<slot :name="key" />
|
:title="$t(option.titleKey)"
|
||||||
</template>
|
:icon="option.icon" :selected="model===key"
|
||||||
</TabSelectButton>
|
@select="model=key">
|
||||||
|
<template v-if="$slots[key]" #replace>
|
||||||
|
<slot :name="key" />
|
||||||
|
</template>
|
||||||
|
</TabSelectButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { CircleDot } from 'lucide-vue-next'
|
import { CircleDot } from 'lucide-vue-next'
|
||||||
import TabSelectButton from '@/components/config/TabSelectButton.vue'
|
import TabSelectButton from '@/components/config/TabSelectButton.vue'
|
||||||
|
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
const model = defineModel({
|
const model = defineModel({
|
||||||
type: String,
|
type: String,
|
||||||
default: 'a',
|
default: 'a',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const buttons = ref({})
|
||||||
|
|
||||||
|
const backgroundStyle = ref({})
|
||||||
|
|
||||||
|
const updateBackgroundStyle = () => {
|
||||||
|
const selected = buttons.value[model.value]
|
||||||
|
if (selected) {
|
||||||
|
backgroundStyle.value = {
|
||||||
|
top: `${selected.$el.offsetTop}px`,
|
||||||
|
left: `${selected.$el.offsetLeft}px`,
|
||||||
|
width: `${selected.$el.offsetWidth}px`,
|
||||||
|
height: `${selected.$el.offsetHeight}px`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([model, buttons], updateBackgroundStyle)
|
||||||
|
|
||||||
|
let observer = null
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
observer = new ResizeObserver(updateBackgroundStyle)
|
||||||
|
observer.observe(buttons.value[model.value].$el)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
observer.disconnect()
|
||||||
|
})
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
options: {
|
options: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
class="flex-1 flex flex-col items-center rounded-xl p-1 gap-2"
|
class="flex-1 flex flex-col items-center rounded-xl p-1 gap-2 transition-colors"
|
||||||
:class="{'text-black bg-zinc-300 hover:bg-zinc-200 outline outline-zinc-100': selected,
|
:class="{'text-black hover:bg-zinc-200': selected,
|
||||||
'hover:bg-zinc-800 text-muted-foreground' : !selected}"
|
'hover:bg-zinc-800 text-muted-foreground' : !selected}"
|
||||||
@click="$emit('select'); $refs.title.scramble()">
|
@click="$emit('select'); $refs.title.scramble()">
|
||||||
<slot v-if="$slots['replace']" name="replace" />
|
<slot v-if="$slots['replace']" name="replace" />
|
||||||
|
|||||||
Reference in New Issue
Block a user