-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCheckBoxGroup.vue
91 lines (77 loc) · 2.47 KB
/
CheckBoxGroup.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<script setup lang="ts" generic="T extends string">
import { CFormCheck, CFormLabel, CListGroup, CListGroupItem } from '@coreui/vue-pro'
interface CheckBoxGroupOptions<T = string> {
value: T
label?: string
children?: CheckBoxGroupOptions<T>[]
}
defineProps<{
options?: CheckBoxGroupOptions<T>[]
}>()
const selected = defineModel<T[]>({ required: false, default: [] })
const childrenIdsCache = new Map<T, T[]>()
function getAllChildrenIds(item: CheckBoxGroupOptions<T>): T[] {
const cached = childrenIdsCache.get(item.value)
if (cached)
return cached
let ids: T[] = []
if (item.children) {
item.children.forEach((child) => {
ids = [...ids, child.value, ...getAllChildrenIds(child)]
})
}
childrenIdsCache.set(item.value, ids)
return ids
}
const getChildrenState = computed(() => (item: CheckBoxGroupOptions<T>): boolean => {
if (!item.children?.length)
return selected.value.includes(item.value)
const childrenIds = getAllChildrenIds(item)
return childrenIds.length > 0 && childrenIds.every(id => selected.value.includes(id))
})
function onSelectedChange(id: T, children: CheckBoxGroupOptions<T>[] = []) {
if (!children.length) {
selected.value = selected.value.includes(id)
? selected.value.filter(v => v !== id)
: [...selected.value, id]
return
}
const childrenIds = children.reduce((acc, child) => {
return [...acc, child.value, ...getAllChildrenIds(child)]
}, [] as T[])
const allSelected = childrenIds.every(id => selected.value.includes(id))
selected.value = allSelected
? selected.value.filter(v => !childrenIds.includes(v) && v !== id)
: [...new Set([...selected.value, id, ...childrenIds])]
}
</script>
<template>
<CListGroup>
<CListGroupItem v-for="item in options" :key="item.value">
<div flex="~ items-center gap-x-2">
<CFormCheck
:id="item.value"
:model-value="getChildrenState(item)"
@change="onSelectedChange(item.value, item.children)"
/>
<CFormLabel select-none :for="item.value">
{{ item.label ?? item.value }}
</CFormLabel>
</div>
<div v-if="item.children?.length" class="ms-2">
<CheckBoxGroup
v-model="selected"
:options="item.children"
/>
</div>
</CListGroupItem>
</CListGroup>
</template>
<style scoped>
* {
--cui-list-group-bg: transparent;
--cui-border-width: 0;
--cui-list-group-item-padding-x: 0;
--cui-list-group-item-padding-y: 0;
}
</style>