Filter cards

This commit is contained in:
Brieuc Dubois 2024-01-10 19:01:53 +01:00
parent ca3811a48b
commit 2092125fc5
4 changed files with 128 additions and 40 deletions

View File

@ -1,18 +1,20 @@
<script lang="ts">
import currentView from '$lib/stores/currentView';
import type Filter from '$lib/types/Filter';
import FilterMenuItem from './FilterMenuItem.svelte';
import Menu from './Menu.svelte';
export let isOpen = false;
export let filters: Filter[] = [];
</script>
{#if $currentView}
<Menu bind:isOpen>
{#each filters as filter}
{#each $currentView.filters as filter}
<FilterMenuItem {filter} />
{/each}
<FilterMenuItem />
</Menu>
{/if}
<style lang="less">
</style>

View File

@ -3,6 +3,7 @@
import Filter from '$lib/types/Filter';
import ProjectTag, { projectTags } from '$lib/types/ProjectTag';
import type TagOption from '$lib/types/TagOption';
import TrashIcon from '../icons/TrashIcon.svelte';
import Menu from './Menu.svelte';
export let filter: Filter | null = null;
@ -14,7 +15,8 @@
async function selectProjectTag(projectTag: ProjectTag) {
if (!$currentView) return;
if (!filter) {
filter = await $currentView?.addFilter(projectTag, 0, null);
await $currentView?.addFilter(projectTag, 0, null);
currentView.reload();
return;
}
@ -47,6 +49,14 @@
}
isOptionOpen = false;
}
async function deleteFilter() {
if (!filter) return;
const res = await $currentView?.removeFilter(filter);
if (!res) return;
currentView.reload();
}
</script>
<div class="item">
@ -83,7 +93,11 @@
role="button"
>
{#if filter}
{filter.filterType}
{#if filter.filterType === 0}
is
{:else if filter.filterType === 1}
is not
{/if}
{/if}
</div>
{#if filter}
@ -124,6 +138,19 @@
</Menu>
{/if}
</div>
{#if filter}
<div
class="delete"
tabindex="0"
role="button"
on:click={() => deleteFilter()}
on:keydown={(e) => {
if (e.key === 'Enter') deleteFilter();
}}
>
<TrashIcon size={20} />
</div>
{/if}
</div>
<style lang="less">
@ -134,11 +161,15 @@
.part {
min-width: 50px;
}
.part,
.delete {
height: 30px;
margin: 5px;
text-align: center;
line-height: 30px;
padding: 0 5px;
padding: 0 2px;
&:hover {
background-color: #fff2;
@ -146,6 +177,10 @@
}
}
.delete {
line-height: 35px;
}
button {
min-width: 100px;
text-align: left;

View File

@ -57,8 +57,13 @@
</div>
<button class:disabled={true}>Sub-group</button>
<div>
<button on:click={() => (filterMenuOpen = !filterMenuOpen)}>Filter</button>
<FilterMenu bind:isOpen={filterMenuOpen} filters={$currentView?.filters} />
<button
on:click={() => (filterMenuOpen = !filterMenuOpen)}
class:defined={$currentView?.filters && $currentView?.filters.length > 0}
>
Filter
</button>
<FilterMenu bind:isOpen={filterMenuOpen} />
</div>
<div>
<button on:click={() => (sortMenuOpen = !sortMenuOpen)} class:defined={$currentView?.sortTag}>

View File

@ -1,57 +1,103 @@
<script lang="ts">
import currentView from '$lib/stores/currentView';
import { cards } from '$lib/types/Card';
import Card, { cards } from '$lib/types/Card';
import type Filter from '$lib/types/Filter';
import type Project from '$lib/types/Project';
import type ProjectTag from '$lib/types/ProjectTag';
import { projectTags } from '$lib/types/ProjectTag';
import type TagOption from '$lib/types/TagOption';
import type View from '$lib/types/View';
import Column from './Column.svelte';
import Header from './Header.svelte';
export let project: Project;
$: allCards = $cards;
function cardComparator(a: Card, b: Card, sortTag: ProjectTag | null, sortDirection: number) {
if (!sortTag) return 0;
const aTag = a.cardTags.find((t) => t.projectTag === sortTag);
const bTag = b.cardTags.find((t) => t.projectTag === sortTag);
if (!aTag) return -sortDirection;
if (!bTag) return sortDirection;
const aValue = aTag.value || aTag.option?.value || '';
const bValue = bTag.value || bTag.option?.value || '';
return aValue < bValue ? sortDirection : -sortDirection;
}
function passFilters(card: Card, filters: Filter[]): boolean {
for (const projectTag of $projectTags) {
let is: TagOption[] = [];
const cardTag = card.cardTags.find((t) => t.projectTag === projectTag);
for (const filter of filters) {
if (projectTag !== filter.projectTag) continue;
if (!filter.tagOption) continue;
if (filter.filterType === 0) {
is.push(filter.tagOption);
} else if (filter.filterType === 1) {
if (cardTag?.option === filter.tagOption) return false;
}
}
if (is.length > 0) {
if (!cardTag) return false;
if (!is.some((o) => o === cardTag?.option)) return false;
}
}
return true;
}
function extractColumnCards(view: View | null, cards: Card[], tagOption: TagOption | null) {
if (!view) return cards;
const filteredCards = cards.filter((c) => passFilters(c, view.filters));
const primaryTag = view.primaryTag;
if (!primaryTag) return filteredCards;
if (!tagOption) {
return filteredCards.filter((c) => !c.cardTags.map((t) => t.projectTag).includes(primaryTag));
}
const rightOptionCards = filteredCards.filter((c) => {
const tag = c.cardTags.find((t) => t.projectTag === primaryTag);
if (!tag) return false;
return tag.option?.id === tagOption.id;
});
const sortedCards = rightOptionCards.sort((a, b) =>
cardComparator(a, b, view.sortTag, $currentView?.sortDirection || 1)
);
return sortedCards;
}
</script>
{#if project}
<section>
{#if $currentView}
<Header {project} />
{#if $cards && allCards}
{#if $cards}
<div class="grid">
{#if $currentView.primaryTag}
{#each $currentView.primaryTag.options as option}
<Column
{option}
primaryTag={$currentView.primaryTag}
columnCards={allCards
.filter((c) => c.cardTags.map((t) => t.option).includes(option))
.sort((a, b) => {
if (!$currentView?.sortTag) return 0;
const aTag = a.cardTags.find((t) => t.projectTag === $currentView?.sortTag);
const bTag = b.cardTags.find((t) => t.projectTag === $currentView?.sortTag);
if (!aTag) return -($currentView?.sortDirection || 1);
if (!bTag) return $currentView?.sortDirection || 1;
const aValue = aTag.value || aTag.option?.value || '';
const bValue = bTag.value || bTag.option?.value || '';
return aValue < bValue
? $currentView?.sortDirection || 1
: -($currentView?.sortDirection || 1);
})}
columnCards={extractColumnCards($currentView, $cards, option)}
{project}
/>
{/each}
{/if}
<Column
primaryTag={$currentView.primaryTag}
columnCards={$currentView.primaryTag != null
? (() => {
const primaryTag = $currentView.primaryTag;
return allCards.filter(
(c) => !c.cardTags.map((t) => t.projectTag).includes(primaryTag)
);
})()
: allCards}
columnCards={extractColumnCards($currentView, $cards, null)}
{project}
/>
</div>