81 lines
2.6 KiB
Vue
81 lines
2.6 KiB
Vue
<script setup>
|
|
import { computed, ref } from 'vue'
|
|
import { useChartContext } from './interface'
|
|
|
|
const props = defineProps({
|
|
order: { type: Array, required: false }, // explicit ordering of keys
|
|
modelValue: { type: Array, required: false }, // deprecated alias
|
|
activeKeys: { type: Array, required: false }, // v-model:activeKeys target
|
|
class: { type: [String, Array, Object], required: false },
|
|
})
|
|
|
|
const emit = defineEmits(['update:activeKeys'])
|
|
|
|
const { config } = useChartContext()
|
|
|
|
// Derive ordered keys from config
|
|
const allKeys = computed(() => {
|
|
const keys = Object.keys(config)
|
|
if (props.order && props.order.length) {
|
|
return props.order.filter(k => keys.includes(k))
|
|
}
|
|
return keys
|
|
})
|
|
|
|
// Internal active state (if parent not controlling)
|
|
const internalActive = ref(allKeys.value.reduce((acc, k) => { acc[k] = true; return acc }, {}))
|
|
|
|
const activeMap = computed(() => {
|
|
// If parent passes controlled array use that
|
|
if (props.activeKeys && props.activeKeys.length) {
|
|
return props.activeKeys.reduce((acc, k) => { acc[k] = true; return acc }, {})
|
|
}
|
|
return internalActive.value
|
|
})
|
|
|
|
const items = computed(() => allKeys.value.map(k => {
|
|
const series = config[k] || {}
|
|
const color = series.color || (series.theme && (series.theme.light || series.theme.dark)) || 'var(--foreground)'
|
|
return { key: k, label: series.label || k, color, active: !!activeMap.value[k] }
|
|
}))
|
|
|
|
function toggle(key) {
|
|
// controlled mode
|
|
if (props.activeKeys) {
|
|
const next = items.value.filter(i => i.key === key ? !i.active : i.active).map(i => i.key)
|
|
// If item was active we remove it, else add it
|
|
const wasActive = activeMap.value[key]
|
|
const result = wasActive
|
|
? props.activeKeys.filter(k => k !== key)
|
|
: [...props.activeKeys, key]
|
|
emit('update:activeKeys', result)
|
|
return
|
|
}
|
|
// uncontrolled mode
|
|
internalActive.value[key] = !internalActive.value[key]
|
|
const result = Object.entries(internalActive.value).filter(([, v]) => v).map(([k]) => k)
|
|
emit('update:activeKeys', result)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div :class="['flex items-center justify-center flex-wrap gap-4 text-xs select-none', props.class]">
|
|
<button
|
|
v-for="item in items"
|
|
:key="item.key"
|
|
type="button"
|
|
:class="[
|
|
'flex items-center gap-2 transition-colors',
|
|
item.active ? 'opacity-100' : 'opacity-40',
|
|
'focus:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm'
|
|
]"
|
|
@click="toggle(item.key)"
|
|
>
|
|
<span class="h-2.5 w-2.5 rounded-[3px] border border-border" :style="{ background: item.color }" />
|
|
<span>{{ item.label }}</span>
|
|
</button>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped></style>
|