Handle selection within generic CardList
VueJS generics are kinda insane. And awesome.
This commit is contained in:
parent
80b06c2a52
commit
da229592f5
2 changed files with 77 additions and 6 deletions
|
|
@ -29,7 +29,12 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ListCard :items="items" :loading="loading">
|
<ListCard
|
||||||
|
:items="items"
|
||||||
|
:loading="loading"
|
||||||
|
selection-mode="single"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
<!-- Filters Slot -->
|
<!-- Filters Slot -->
|
||||||
<template #filters="{ applyFilter, clearFilters, activeFilters }">
|
<template #filters="{ applyFilter, clearFilters, activeFilters }">
|
||||||
<div class="user-filters">
|
<div class="user-filters">
|
||||||
|
|
@ -50,11 +55,11 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Item Slot -->
|
<!-- Item Slot -->
|
||||||
<template #item="{ item }">
|
<template #item="{ item, isSelected, toggleSelection }">
|
||||||
<ListCardContact
|
<ListCardContact
|
||||||
:contact="item"
|
:contact="item"
|
||||||
@click="handleContactClick(item)"
|
@click="toggleSelection"
|
||||||
:isSelected="false"
|
:is-selected="isSelected"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</ListCard>
|
</ListCard>
|
||||||
|
|
@ -96,6 +101,10 @@ const handleClearFilters = (clearFilters: () => void) => {
|
||||||
const handleContactClick = (contact: any) => {
|
const handleContactClick = (contact: any) => {
|
||||||
console.log("Clicked contact:", contact);
|
console.log("Clicked contact:", contact);
|
||||||
};
|
};
|
||||||
|
const handleSelectionChange = (selectedContacts: Contact[]) => {
|
||||||
|
console.log("Selected contacts:", selectedContacts);
|
||||||
|
// Do something with the selected contacts
|
||||||
|
};
|
||||||
|
|
||||||
const items = computed((): Contact[] => {
|
const items = computed((): Contact[] => {
|
||||||
if (props.contacts) {
|
if (props.contacts) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,13 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else v-for="item in filteredItems" :key="item.id">
|
<div v-else v-for="item in filteredItems" :key="item.id">
|
||||||
<slot name="item" :item="item" :index="item.id"></slot>
|
<slot
|
||||||
|
name="item"
|
||||||
|
:item="item"
|
||||||
|
:index="item.id"
|
||||||
|
:is-selected="isSelected(item)"
|
||||||
|
:toggle-selection="() => toggleSelection(item)"
|
||||||
|
></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -47,11 +53,13 @@ interface Props {
|
||||||
items: T[];
|
items: T[];
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
itemKey?: keyof T | ((item: T) => string | number);
|
itemKey?: keyof T | ((item: T) => string | number);
|
||||||
|
selectionMode?: "single" | "multiple" | "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
loading: false,
|
loading: false,
|
||||||
itemKey: "id",
|
itemKey: "id",
|
||||||
|
selectionMode: "none",
|
||||||
});
|
});
|
||||||
|
|
||||||
defineSlots<{
|
defineSlots<{
|
||||||
|
|
@ -60,12 +68,22 @@ defineSlots<{
|
||||||
clearFilters: () => void;
|
clearFilters: () => void;
|
||||||
activeFilters: number;
|
activeFilters: number;
|
||||||
}): any;
|
}): any;
|
||||||
item(props: { item: T; index: number }): any;
|
item(props: {
|
||||||
|
item: T;
|
||||||
|
index: string | number;
|
||||||
|
isSelected: boolean;
|
||||||
|
toggleSelection: () => void;
|
||||||
|
}): any;
|
||||||
footer(props: { total: number }): any;
|
footer(props: { total: number }): any;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"update:selectedItems": [items: T[]];
|
||||||
|
"selection-change": [items: T[]];
|
||||||
|
}>();
|
||||||
const filterFunctions = ref<Array<(item: T) => boolean>>([]);
|
const filterFunctions = ref<Array<(item: T) => boolean>>([]);
|
||||||
|
const selectedItemKeys = ref<Set<string | number>>(new Set());
|
||||||
|
|
||||||
// Computed
|
// Computed
|
||||||
const filteredItems = computed(() => {
|
const filteredItems = computed(() => {
|
||||||
|
|
@ -78,6 +96,11 @@ const filteredItems = computed(() => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
const activeFilters = computed(() => filterFunctions.value.length);
|
const activeFilters = computed(() => filterFunctions.value.length);
|
||||||
|
const selectedItems = computed(() => {
|
||||||
|
return props.items.filter((item) =>
|
||||||
|
selectedItemKeys.value.has(getItemKey(item)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const applyFilter = (filterFn: (item: T) => boolean) => {
|
const applyFilter = (filterFn: (item: T) => boolean) => {
|
||||||
|
|
@ -95,4 +118,43 @@ const getItemKey = (item: T): string | number => {
|
||||||
}
|
}
|
||||||
return item[props.itemKey] as string | number;
|
return item[props.itemKey] as string | number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isSelected = (item: T): boolean => {
|
||||||
|
return selectedItemKeys.value.has(getItemKey(item));
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelection = (item: T) => {
|
||||||
|
if (props.selectionMode === "none") return;
|
||||||
|
|
||||||
|
const key = getItemKey(item);
|
||||||
|
|
||||||
|
if (props.selectionMode === "single") {
|
||||||
|
if (selectedItemKeys.value.has(key)) {
|
||||||
|
selectedItemKeys.value.clear();
|
||||||
|
} else {
|
||||||
|
selectedItemKeys.value.clear();
|
||||||
|
selectedItemKeys.value.add(key);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (selectedItemKeys.value.has(key)) {
|
||||||
|
selectedItemKeys.value.delete(key);
|
||||||
|
} else {
|
||||||
|
selectedItemKeys.value.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit("update:selectedItems", selectedItems.value);
|
||||||
|
emit("selection-change", selectedItems.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearSelection = () => {
|
||||||
|
selectedItemKeys.value.clear();
|
||||||
|
emit("update:selectedItems", []);
|
||||||
|
emit("selection-change", []);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
clearSelection,
|
||||||
|
selectedItems,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue