1
0
Fork 0

WIP Design and Search

This commit is contained in:
Leon Grünewald 2024-03-20 00:22:10 +01:00
parent 0464014ae0
commit b07210507e
12 changed files with 207 additions and 130 deletions

View file

@ -4,3 +4,4 @@
@tailwind utilities; @tailwind utilities;
body { @apply bg-surface-50-900-token; } body { @apply bg-surface-50-900-token; }
html, body { @apply min-h-full; }

View file

@ -0,0 +1,36 @@
<script>
import {RangeSlider, Tab, TabGroup} from "@skeletonlabs/skeleton";
import {imageGridColumns, postGridPreviewQuality} from "$lib/stores.js";
let tabPos = 0;
</script>
<div class="flex flex-col">
<h2 class="h2 mb-2 text-dark-token">Settings</h2>
<TabGroup>
<Tab class="text-dark-token" bind:group={tabPos} name="tab1" value={0}>Search Grid</Tab>
<svelte:fragment slot="panel">
{#if tabPos === 0}
<div class="flex flex-col gap-3">
<div class="basis-1/2">
<RangeSlider name="grid-columns" min={1} max={12} bind:value={$imageGridColumns} ticked={true}>
<div class="flex flex-grow justify-between items-center">
<div class="text-dark-token font-bold">Grid Columns</div>
<div class="text-xs text-dark-token">{$imageGridColumns} / {12}</div>
</div>
</RangeSlider>
</div>
<div class="basis-1/2">
<label class="label">
<span class="text-dark-token font-bold">Preview type</span>
<select class="select" bind:value={$postGridPreviewQuality}>
<option value="preview">Preview</option>
<option value="sample">Sample</option>
</select>
</label>
</div>
</div>
{/if}
</svelte:fragment>
</TabGroup>
</div>

View file

@ -1,12 +1,12 @@
<script> <script>
import {faArrowLeft} from "@fortawesome/free-solid-svg-icons"; import {faArrowLeft} from '@fortawesome/free-solid-svg-icons';
import {Fa} from "svelte-fa"; import {Fa} from 'svelte-fa';
import {AppRailAnchor} from "@skeletonlabs/skeleton"; import {AppRailAnchor} from '@skeletonlabs/skeleton';
export let popCount = 1; export let popCount = 1;
let href; let href;
let pathnameParts = window.location.pathname.split('/'); let pathnameParts = window.location.pathname.split('/');
for(let i = 0; i < popCount; i++) { for(let i = 0; i < popCount; i++) {
pathnameParts.pop(); pathnameParts.pop();
} }
@ -23,9 +23,9 @@ switch (pathnameParts.length) {
break; break;
} }
console.log(href);
</script> </script>
<AppRailAnchor {href} target="_self" title="Back">
<AppRailAnchor {href} title="Back">
<div class="flex flex-col gap-2 place-content-center"> <div class="flex flex-col gap-2 place-content-center">
<Fa size={'2x'} icon={faArrowLeft} /> <Fa size={'2x'} icon={faArrowLeft} />
<p>Back</p> <p>Back</p>

View file

@ -0,0 +1,18 @@
<script>
import { showSearch, lastSearch, loading } from '$lib/stores.js';
import { goto } from '$app/navigation';
let searchValue = $lastSearch;
</script>
{#if $showSearch}
<form class="w-80 pt-2 px-2 flex flex-row gap-2"
on:submit|preventDefault={() => {
$loading = true;
goto('/')
lastSearch.set(searchValue);
}}>
<input class="input py-1 px-2" type="text" placeholder="Search" bind:value={searchValue} />
<button class="btn btn-md variant-filled" type="submit">Sumbit</button>
</form>
{/if}

View file

@ -1,14 +1,17 @@
<script> <script>
import {imageGridPreviewQuality, imageGridColumns} from '$lib/stores'; import {postGridPreviewQuality, imageGridColumns, showSearch, loading} from '$lib/stores';
export let posts = []; export let posts = [];
</script> </script>
<div class="grid gap-3" style={`grid-template-columns: repeat(${$imageGridColumns}, minmax(0, 1fr))`}> <div class="grid gap-3" style={`grid-template-columns: repeat(${$imageGridColumns}, minmax(0, 1fr))`}>
{#each posts as post} {#each posts as post}
<a href={`/posts/${post.id}`} class="card flex flex-col justify-end items-center"> <a href={`/posts/${post.id}`} class="card p-3 flex flex-col justify-end items-center" on:click={() => {
$loading = true;
$showSearch = false
}}>
<section class="flex"> <section class="flex">
<img src={post[$imageGridPreviewQuality].url} <img src={post[$postGridPreviewQuality].url}
loading="lazy" loading="lazy"
alt={`Post ${post.id}`} /> alt={`Post ${post.id}`} />
</section> </section>

View file

@ -0,0 +1,17 @@
<script>
export let post = null;
</script>
<div>
{#if post?.preview?.has}
<img class="mb-2" src={post.preview.url} alt={`Post ${post.id}`} />
{:else}
{#if ['webm', 'mp4'].includes(post.file.ext)}
<video class="mb-2" controls playsinline>
<source src={post.file.url} />
</video>
{:else}
<img class="mb-2" src={post.file.url} alt={`Post ${post.id}}`} />
{/if}
{/if}
</div>

View file

@ -0,0 +1,13 @@
<script>
import { Fa } from 'svelte-fa';
import { AppRailAnchor } from '@skeletonlabs/skeleton';
import { showSearch } from '$lib/stores';
import { faSearch } from '@fortawesome/free-solid-svg-icons';
</script>
<AppRailAnchor on:click={() => $showSearch = !$showSearch}>
<div class="flex flex-col gap-2 place-content-center">
<Fa size={'2x'} icon={faSearch} />
<p>Search</p>
</div>
</AppRailAnchor>

View file

@ -1,5 +1,15 @@
import AppSettings from './AppSettings.svelte';
import BackAppRailAnchor from './BackAppRailAnchor.svelte';
import GlobalSearch from './GlobalSearch.svelte';
import ImageGrid from './ImageGrid.svelte'; import ImageGrid from './ImageGrid.svelte';
import PostMedia from './PostMedia.svelte';
import SearchAppRailAnchor from './SearchAppRailAnchor.svelte';
export { export {
AppSettings,
BackAppRailAnchor,
GlobalSearch,
ImageGrid, ImageGrid,
PostMedia,
SearchAppRailAnchor
} }

View file

@ -2,8 +2,8 @@ import { localStorageStore } from '@skeletonlabs/skeleton';
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
export const lastSearch = localStorageStore('lastSearch', ''); export const lastSearch = localStorageStore('lastSearch', '');
export const imageGridColumns = localStorageStore('gridColumns', 8); export const imageGridColumns = localStorageStore('gridColumns', 6);
export const imageSearchResults = writable(null); export const postSearchResults = writable(null);
export const imageGridPreviewQuality = localStorageStore('gridPreviewQuality', 'preview'); export const postGridPreviewQuality = localStorageStore('gridPreviewQuality', 'preview');
export const loading = writable(false); export const loading = writable(false);
export const showSearch = writable(true);

View file

@ -1,14 +1,21 @@
<script> <script>
import '../app.pcss'; import '../app.pcss';
import {initializeStores, ProgressRadial} from '@skeletonlabs/skeleton'; import {Drawer, initializeStores, Modal, ProgressRadial} from '@skeletonlabs/skeleton';
import {loading} from '$lib/stores'; import {loading} from '$lib/stores';
import {AppSettings} from '$lib/components';
initializeStores(); initializeStores();
</script> </script>
<div class="relative min-h-dvh"> <div class="relative h-dvh">
<Modal />
<Drawer bgDrawer="bg-surface-500/90" width="w-6/12" height="h-dvh" position="left">
<div class="h-full p-4">
<AppSettings />
</div>
</Drawer>
{#if $loading} {#if $loading}
<div class="fixed inset-x-1/2 top-1 w-8 h-8"> <div class="fixed bottom-3 right-3 w-8 h-8">
<ProgressRadial width="w-8" height="h-8" stroke={120} strokeLinecap="butt" /> <ProgressRadial width="w-8" height="h-8" stroke={120} strokeLinecap="butt" />
</div> </div>
{/if} {/if}

View file

@ -1,94 +1,69 @@
<script> <script>
import { Fa } from 'svelte-fa'; import { Fa } from 'svelte-fa';
import { ImageGrid } from '$lib/components'; import {GlobalSearch, ImageGrid, SearchAppRailAnchor} from '$lib/components';
import { faGear } from '@fortawesome/free-solid-svg-icons'; import { faGear } from '@fortawesome/free-solid-svg-icons';
import { invoke } from '@tauri-apps/api/tauri' import { invoke } from '@tauri-apps/api/tauri'
import { lastSearch, imageGridColumns, imageGridPreviewQuality, imageSearchResults, loading } from '$lib/stores'; import { lastSearch, postSearchResults, loading } from '$lib/stores';
import { onMount } from 'svelte';
import { import {
AppShell,
Drawer,
RangeSlider,
getDrawerStore,
AppRail, AppRail,
AppRailAnchor, AppRailAnchor,
ProgressRadial AppShell,
getDrawerStore
} from '@skeletonlabs/skeleton'; } from '@skeletonlabs/skeleton';
import {onMount} from 'svelte';
const drawerStore = getDrawerStore(); const drawerStore = getDrawerStore();
onMount(async () => { onMount(() => {
if ($imageSearchResults === null) { $loading = false;
$loading = true;
$imageSearchResults = invoke('get_posts', { query: $lastSearch });
$imageSearchResults.then(() => {
$loading = false;
});
}
}); });
function onSearch() { function onSearch() {
$loading = true; $loading = true;
$imageSearchResults = invoke('get_posts', { query: $lastSearch }); invoke('get_posts', { query: $lastSearch }).then((resPosts) => {
$imageSearchResults.then(() => { postSearchResults.set(resPosts);
$loading = false; requestAnimationFrame(() => {
$loading = false;
});
}); });
} }
if ($postSearchResults === null) {
onSearch();
}
lastSearch.subscribe(() => {
onSearch();
})
</script> </script>
<Drawer bgDrawer="bg-surface-500/90" width="w-6/12" height="h-dvh" position="left">
<div class="h-full p-4 flex flex-col justify-between bg-token">
<div class="flex flex-row">
<label class="label basis-1 flex-grow">
<span class="text-dark-token font-bold">Query</span>
<input class="input" type="text" bind:value={$lastSearch} />
</label>
<button class="btn variant-filled flex-shrink" type="button" on:click={() => {onSearch()}}>Search</button>
</div>
<div class="flex flex-row gap-3">
<div class="basis-1/2">
<RangeSlider name="grid-columns" min={1} max={12} bind:value={$imageGridColumns} ticked={true}>
<div class="flex flex-grow justify-between items-center">
<div class="text-dark-token font-bold">Grid Columns</div>
<div class="text-xs text-dark-token">{$imageGridColumns} / {12}</div>
</div>
</RangeSlider>
</div>
<div class="basis-1/2">
<label class="label">
<span class="text-dark-token font-bold">Preview type</span>
<select class="select" bind:value={$imageGridPreviewQuality}>
<option value="preview">Preview</option>
<option value="sample">Sample</option>
</select>
</label>
</div>
</div>
</div>
</Drawer>
<AppShell> <AppShell>
<svelte:fragment slot="sidebarLeft"> <svelte:fragment slot="pageHeader">
<AppRail height="h-dvh"> <GlobalSearch />
<svelte:fragment slot="trail">
<AppRailAnchor on:click={() => drawerStore.open()}>
<div class="flex flex-col gap-2 place-content-center">
<Fa size={'2x'} icon={faGear} />
<p>Settings</p>
</div>
</AppRailAnchor>
</svelte:fragment>
</AppRail>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="sidebarLeft">
<div class="h-dvh">
<AppRail>
<svelte:fragment slot="lead">
<SearchAppRailAnchor />
</svelte:fragment>
<svelte:fragment slot="trail">
<AppRailAnchor on:click={() => drawerStore.open()}>
<div class="flex flex-col gap-2 place-content-center">
<Fa size={'2x'} icon={faGear} />
<p>Settings</p>
</div>
</AppRailAnchor>
</svelte:fragment>
</AppRail>
</div>
</svelte:fragment>
<svelte:fragment> <svelte:fragment>
<div class="max-h-dvh p-2"> <div class="overflow-scroll p-2">
{#if $imageSearchResults} {#if $postSearchResults}
{#await $imageSearchResults} <ImageGrid posts={$postSearchResults} />
{:then result}
<ImageGrid posts={result} />
{:catch err}
<p>Error: {err}</p>
{/await}
{/if} {/if}
</div> </div>
</svelte:fragment> </svelte:fragment>

View file

@ -3,19 +3,16 @@
AppRail, AppRail,
AppRailAnchor, AppRailAnchor,
AppShell, AppShell,
Modal,
getDrawerStore, getDrawerStore,
getModalStore,
Drawer
} from '@skeletonlabs/skeleton'; } from '@skeletonlabs/skeleton';
import {faCode, faGear} from '@fortawesome/free-solid-svg-icons'; import { faGear, faCopy } from '@fortawesome/free-solid-svg-icons';
import { Fa } from 'svelte-fa'; import { Fa } from 'svelte-fa';
import BackAppRailAnchor from "$lib/components/BackAppRailAnchor.svelte"; import { BackAppRailAnchor, GlobalSearch, PostMedia, SearchAppRailAnchor } from '$lib/components';
import {onMount} from 'svelte';
import {loading} from '$lib/stores.js';
export let data; export let data;
let drawerStore = getDrawerStore(); let drawerStore = getDrawerStore();
let modalStore = getModalStore();
const tagTypes = Object.entries({ const tagTypes = Object.entries({
artist: 'Artist', artist: 'Artist',
character: 'Character', character: 'Character',
@ -25,13 +22,17 @@
meta: 'Meta', meta: 'Meta',
invalid: 'Invalid' invalid: 'Invalid'
}); });
onMount(() => {
$loading = false;
});
</script> </script>
<Modal />
<Drawer>
</Drawer>
<AppShell> <AppShell>
<svelte:fragment slot="pageHeader">
<GlobalSearch />
</svelte:fragment>
<svelte:fragment slot="sidebarRight"> <svelte:fragment slot="sidebarRight">
<div class="p-2 h-dvh"> <div class="p-2 h-dvh">
{#each tagTypes as [name, label]} {#each tagTypes as [name, label]}
@ -44,35 +45,36 @@
{/each} {/each}
</div> </div>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="sidebarLeft"> <svelte:fragment slot="sidebarLeft">
<AppRail height="h-dvh"> <div class="h-dvh">
<svelte:fragment slot="lead"> <AppRail>
<BackAppRailAnchor popCount={2} /> <svelte:fragment slot="lead">
</svelte:fragment> <BackAppRailAnchor popCount={2} />
<svelte:fragment slot="trail"> <SearchAppRailAnchor />
<!-- <AppRailAnchor on:click={() => modalStore.trigger({body: JSON.stringify(data.post, null, 2)})}> </svelte:fragment>
<div class="flex flex-col gap-2 place-content-center"> <svelte:fragment slot="trail">
<Fa size={'2x'} icon={faCode} /> <AppRailAnchor on:click={() => {}}>
<p>Post data</p> <div class="flex flex-col gap-2 place-content-center">
</div> <Fa size={'2x'} icon={faCopy} />
</AppRailAnchor> --> <p>Copy link</p>
<AppRailAnchor on:click={() => drawerStore.open()}> </div>
<div class="flex flex-col gap-2 place-content-center"> </AppRailAnchor>
<Fa size={'2x'} icon={faGear} /> <AppRailAnchor on:click={() => drawerStore.open()}>
<p>Settings</p> <div class="flex flex-col gap-2 place-content-center">
</div> <Fa size={'2x'} icon={faGear} />
</AppRailAnchor> <p>Settings</p>
</svelte:fragment> </div>
</AppRail> </AppRailAnchor>
</svelte:fragment>
</AppRail>
</div>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="default"> <svelte:fragment slot="default">
<div class="max-h-dvh container mx-auto p-2"> <div class="container mx-auto p-2">
<div class="flex flex-row place-content-center"> <div class="flex flex-row place-content-center">
{#if data?.post?.preview?.has} <PostMedia post={data.post} />
<img class="mb-2" src={data.post.preview.url} alt={`Post ${data.post.id}`} />
{:else}
<img class="mb-2" src={data.post.file.url} alt={`Post ${data.post.id}}`} />
{/if}
</div> </div>
<div class="card"> <div class="card">
<header class="px-3 pt-2"> <header class="px-3 pt-2">
@ -82,11 +84,6 @@
<p>{data.post.description}</p> <p>{data.post.description}</p>
</section> </section>
</div> </div>
<!-- <div>
<pre>
{JSON.stringify(data, null, 2)}
</pre>
</div>-->
</div> </div>
</svelte:fragment> </svelte:fragment>
</AppShell> </AppShell>