Filter cards
This commit is contained in:
parent
ca3811a48b
commit
2092125fc5
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue