@mozaic-ds/vue
Version:
Mozaic-Vue is the Vue.js implementation of ADEO Design system
150 lines (127 loc) • 4.08 kB
text/typescript
import { computed } from 'vue';
import type { ListboxOption } from './MOptionListbox.types';
type SelectionValue = string | number | null | (string | number)[];
type Props = {
modelValue: SelectionValue;
options: ListboxOption[];
multiple?: boolean;
readonly?: boolean;
checkableSections?: boolean;
};
export function useListboxSelection(
props: Props,
emit: (on: 'update:modelValue', value: SelectionValue) => void,
) {
const selection = computed<SelectionValue>({
get() {
return props.modelValue;
},
set(value) {
emit('update:modelValue', value);
},
});
const sectionMap = computed(() => {
const map = new Map<string, ListboxOption[]>();
let currentSection: ListboxOption | null = null;
props.options.forEach((option) => {
if (option.type === 'section') {
currentSection = option;
map.set(currentSection?.value?.toString() || currentSection.label, []);
} else if (currentSection) {
map
.get(currentSection?.value?.toString() || currentSection.label)!
.push(option);
}
});
return map;
});
function isSelectable(item: ListboxOption): boolean {
return !!(
(item.type !== 'section' && !item.disabled) ||
(item.type === 'section' && props.checkableSections && props.multiple)
);
}
function isOptionSelected(item: ListboxOption) {
if (item.value === undefined || item.value === null) return false;
if (Array.isArray(selection.value)) {
return (selection.value as (string | number)[]).includes(item.value);
}
return item.value === selection.value;
}
function isSectionSelected(item: ListboxOption) {
if (!props.checkableSections || !props.multiple) return false;
const sectionItems =
sectionMap.value.get(item.value?.toString() || item.label) || [];
const selectedItems = Array.isArray(selection.value)
? (selection.value as (string | number)[])
: [];
return sectionItems.every((opt) => selectedItems.includes(opt.value!));
}
function isIndeterminate(item: ListboxOption) {
const section = sectionMap.value.get(item.value?.toString() || item.label);
return (
section?.some((option) => isOptionSelected(option)) &&
!section?.every((option) => isOptionSelected(option))
);
}
function toggleValue(item?: ListboxOption) {
if (!item || !isSelectable(item)) return;
if (Array.isArray(selection.value)) {
if (isOptionSelected(item)) {
selection.value = (selection.value as (string | number)[]).filter(
(el) => el !== item.value,
);
} else {
selection.value = [...selection.value, item.value!];
}
} else {
selection.value = isOptionSelected(item) ? null : item.value!;
}
}
function toggleSection(item: ListboxOption) {
if (!props.checkableSections || !props.multiple) return;
const sectionItems =
sectionMap.value.get(item.value?.toString() || item.label) || [];
const selectedItems = Array.isArray(selection.value)
? (selection.value as (string | number)[])
: [];
if (isSectionSelected(item)) {
selection.value = selectedItems.filter(
(opt) => !sectionItems.find((i) => i.value === opt),
);
} else {
selection.value = [
...selectedItems,
...sectionItems
.filter((opt) => !selectedItems.includes(opt.value!))
.map((i) => i.value!),
];
}
}
function selectAll() {
selection.value = props.options
.filter(
(option) =>
option.value !== undefined &&
option.value !== null &&
!option.disabled &&
option.type !== 'section',
)
.map((item) => item.value!);
}
function clearSelection() {
selection.value = Array.isArray(selection.value) ? [] : null;
}
return {
selection,
sectionMap,
isSelectable,
isOptionSelected,
isSectionSelected,
isIndeterminate,
toggleValue,
toggleSection,
selectAll,
clearSelection,
};
}