I try to achieve a theme changer with vue3 and tailwind. The problem I face is with the emit event and I don't know how to show the selected current button. First the emit event problem is I setup "correctly" way, I think, see on code above. The second is I try when click the current color theme then an icon is active on the current button. If I set the code out side without be a component then works fine.
theme changer:
<template>
<div class="flex space-x-1 bg-white rounded-full px-2 py-1">
<button
class="rounded-full"
v-for="(themeColor, index) in themeColors"
:key="`theme-changer-${index}`"
:class="[index ? 'p-3' : 'p-1']"
:style="{ backgroundColor: colors[themeColor][500] }"
@click="$emit('color', colors[themeColor])"
>
<svg v-if="!index" class="text-white w-4 h-4" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M19.916 4.626a.75.75 0 01.208 1.04l-9 13.5a.75.75 0 01-1.154.114l-6-6a.75.75 0 011.06-1.06l5.353 5.353 8.493-12.739a.75.75 0 011.04-.208z" clip-rule="evenodd" />
</svg>
</button>
</div>
</template>
<script setup>
import colors from 'tailwindcss/colors'
defineProps({
themeColors: Array
})
defineEmits(['color'])
</script>
code implantation:
<template>
<ThemeChanger :theme-colors="themeColors" @colors="c => color = c" />
</template>
<script setup>
import { ref } from 'vue'
import ThemeChanger from '@/components/ThemeChanger.vue'
import colors from 'tailwindcss/colors'
const themeColors = [
'indigo',
'emerald',
'blue',
'yellow',
'orange',
'red',
]
const color = ref(colors[themeColors[0]])
</script>
You are emitting an event called color
at @click="$emit('color', colors[themeColor])"
,
but you are listening to an event called colors
at <ThemeChanger :theme-colors="themeColors" @colors="c => color = c" />
I think changing the event names so that these two match would be sufficient to get your code working.
In regards to getting the check mark change with the current theme, there are multiple ways you could go about it. I think the best way (easy, but scalabole) to do this would be to have another prop called something like currentThemeColor
on your ThemeChanger
.
Then, in your v-for
loop, you could do something like:
<button
class="rounded-full"
v-for="(themeColor, index) in themeColors"
:key="`theme-changer-${index}`"
:class="[index ? 'p-3' : 'p-1']"
:style="{ backgroundColor: colors[themeColor][500] }"
@click="$emit('color', colors[themeColor])"
>
<svg v-if="currentThemeColor === themeColor" ... >
</div>
This way, your ThemeChanger
would be re-rendered every time the prop changes and react accordingly. Using ref
s would be another way, but I think later on, you will want to eventually move the theme logic out of the parent component and move it to some kind of global provider, and using props would work with that kind of setup as well with minimal changes.