193 lines
5.4 KiB
Svelte
193 lines
5.4 KiB
Svelte
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
- file, You can obtain one at https://mozilla.org/MPL/2.0/. -->
|
|
|
|
<script lang="ts">
|
|
import type { Entry as EntryType, EntryKind, KnownFeeling } from '$lib/entry';
|
|
import ExternalLink from './utils/ExternalLink.svelte';
|
|
import FeelingPill from './utils/FeelingPill.svelte';
|
|
import Entry from './utils/Entry.svelte';
|
|
import EntryDescription from './utils/EntryDescription.svelte';
|
|
import AssetPreview from './utils/AssetPreview.svelte';
|
|
import { createEventDispatcher } from 'svelte';
|
|
import Fuse from 'fuse.js';
|
|
import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
|
|
import { faLocationDot } from '@fortawesome/free-solid-svg-icons';
|
|
|
|
let dispatch = createEventDispatcher();
|
|
|
|
export let entries: EntryType[];
|
|
let filteredEntries = entries;
|
|
export let filters: {
|
|
fromDate: null | Date;
|
|
toDate: null | Date;
|
|
kind: null | EntryKind[];
|
|
feelings: null | {
|
|
exclusive: boolean;
|
|
feelings: KnownFeeling[];
|
|
};
|
|
searchQuery: null | string;
|
|
};
|
|
let extended: string[] = [];
|
|
|
|
function applyFilters(filters: {
|
|
fromDate: null | Date;
|
|
toDate: null | Date;
|
|
kind: null | EntryKind[];
|
|
feelings: null | {
|
|
exclusive: boolean;
|
|
feelings: KnownFeeling[];
|
|
};
|
|
searchQuery: null | string;
|
|
}) {
|
|
filteredEntries = entries;
|
|
|
|
if (filters.fromDate != null) {
|
|
filteredEntries = entries.filter((v) => new Date(v.creationDate) >= filters.fromDate!);
|
|
}
|
|
if (filters.toDate != null) {
|
|
filteredEntries = entries.filter((v) => new Date(v.creationDate) <= filters.toDate!);
|
|
}
|
|
if (filters.kind != null) {
|
|
filteredEntries = entries.filter((v) => filters.kind!.includes(v.base.kind));
|
|
}
|
|
if (filters.feelings != null) {
|
|
let feelings = filters.feelings!.feelings;
|
|
if (filters.feelings.exclusive) {
|
|
filteredEntries = entries.filter((v) => {
|
|
let v1 = v.feelings.filter((f) => typeof f === 'string' && feelings.includes(f));
|
|
return v.feelings.length === v1.length;
|
|
});
|
|
} else {
|
|
filteredEntries = entries.filter((v) => {
|
|
let includes = false;
|
|
feelings.forEach((f) => {
|
|
if (v.feelings.includes(f)) {
|
|
includes = true;
|
|
}
|
|
});
|
|
|
|
return includes;
|
|
});
|
|
}
|
|
}
|
|
if (filters.searchQuery != null) {
|
|
let fuse = new Fuse(entries, {
|
|
keys: [
|
|
{
|
|
name: 'title',
|
|
weight: 2
|
|
},
|
|
'description'
|
|
]
|
|
});
|
|
|
|
let results = fuse.search(filters.searchQuery!);
|
|
filteredEntries = results.map((v) => v.item);
|
|
}
|
|
|
|
if (filteredEntries.length !== entries.length) {
|
|
dispatch('updatedFilterStatus', true);
|
|
} else {
|
|
dispatch('updatedFilterStatus', false);
|
|
}
|
|
}
|
|
|
|
$: applyFilters(filters);
|
|
</script>
|
|
|
|
{#if entries.length != filteredEntries.length && filteredEntries.length === 0}
|
|
<div class="flex justify-center py-6">
|
|
<div role="status" class="flex items-center justify-center gap-5">
|
|
<span class="text-xl">No results found</span>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
{#each filteredEntries as entry (entry.id)}
|
|
<Entry
|
|
on:extended={(e) => (extended = [e.detail.id, ...extended])}
|
|
on:contracted={(e) => (extended = extended.filter((v) => v !== e.detail.id))}
|
|
on:deleted={(e) => {
|
|
dispatch('deleted', e.detail);
|
|
}}
|
|
id={entry.id}
|
|
kind={entry.base.kind}
|
|
creationDate={new Date(entry.creationDate)}
|
|
title={entry.base.kind === 'date'
|
|
? new Date(entry.base.referencedDate).toLocaleDateString()
|
|
: entry.title}
|
|
isExtended={extended.includes(entry.id)}
|
|
>
|
|
<div slot="contracted">
|
|
{#if entry.base.kind === 'song' || entry.base.kind === 'album'}
|
|
{#if entry.base.link[0] != null}
|
|
<ExternalLink href={entry.base.link[0]}>
|
|
{entry.base.artist} ‐ {entry.base.title}
|
|
</ExternalLink>
|
|
{:else}
|
|
<span class="font-bold">{entry.base.artist} ‐ {entry.base.title}</span>
|
|
{/if}
|
|
{/if}
|
|
|
|
{#if entry.base.kind === 'feeling'}
|
|
<div class="flex gap-1">
|
|
{#each entry.feelings as feeling}
|
|
{#if typeof feeling === 'string'}
|
|
<FeelingPill {feeling} />
|
|
{:else}
|
|
<FeelingPill
|
|
feeling={feeling.identifier}
|
|
bgColor={feeling.backgroundColor}
|
|
textColor={feeling.textColor}
|
|
/>
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<div slot="extended">
|
|
<div class="mb-2 flex gap-1">
|
|
{#each entry.feelings as feeling}
|
|
{#if typeof feeling === 'string'}
|
|
<FeelingPill {feeling} />
|
|
{:else}
|
|
<FeelingPill
|
|
feeling={feeling.identifier}
|
|
bgColor={feeling.backgroundColor}
|
|
textColor={feeling.textColor}
|
|
/>
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
|
|
{#if entry.base.kind === 'song' || entry.base.kind === 'album'}
|
|
{#if entry.base.link[0] != null}
|
|
<ExternalLink href={entry.base.link[0]}>
|
|
{entry.base.artist} ‐ {entry.base.title}
|
|
</ExternalLink>
|
|
{:else}
|
|
<span class="font-bold">{entry.base.artist} ‐ {entry.base.title}</span>
|
|
{/if}
|
|
{/if}
|
|
|
|
{#if entry.base.kind === "environment" && typeof entry.base.location === "string"}
|
|
<div class="mb-1">
|
|
<FontAwesomeIcon class="pr-2" icon={faLocationDot}/>
|
|
<span class="text-xl">{entry.base.location}</span>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if entry.description != null}
|
|
<EntryDescription>{entry.description}</EntryDescription>
|
|
{/if}
|
|
|
|
<div class="mt-2 flex gap-1">
|
|
{#each entry.assets as asset}
|
|
<AssetPreview asset_id={asset} />
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
</Entry>
|
|
{/each}
|