prototype anime list input
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { AnimeListCodec } from "./schema";
|
||||
import { ChipGroup } from "$lib/components/ui/chip-group";
|
||||
import { AnimeListWatchStatus } from "$lib/utils/list";
|
||||
import NativeSelect from "$lib/components/ui/native-select/native-select.svelte";
|
||||
import NativeSelectOption from "$lib/components/ui/native-select/native-select-option.svelte";
|
||||
import Input from "$lib/components/ui/input/input.svelte";
|
||||
import { Label } from "$lib/components/ui/label";
|
||||
import { z } from "zod";
|
||||
|
||||
let { value = $bindable() }: { value: z.infer<typeof AnimeListCodec> } =
|
||||
$props();
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label for="list-kind">Kind</Label>
|
||||
<NativeSelect id="list-kind" bind:value={value.kind}>
|
||||
<NativeSelectOption value="mal">MAL</NativeSelectOption>
|
||||
<NativeSelectOption value="anilist">AniList</NativeSelectOption>
|
||||
<NativeSelectOption value="kitsu">Kitsu</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label for="list-username">Username</Label>
|
||||
<Input id="list-username" bind:value={value.username} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label for="list-status">Status</Label>
|
||||
<ChipGroup
|
||||
items={AnimeListWatchStatus.options.map((v) => ({
|
||||
label: v.toUpperCase(),
|
||||
value: v,
|
||||
}))}
|
||||
bind:value={value.status}
|
||||
/>
|
||||
</div>
|
||||
2
src/lib/components/inputs/anime-list-input/index.ts
Normal file
2
src/lib/components/inputs/anime-list-input/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as AnimeListInput } from "./AnimeListInput.svelte";
|
||||
export * from "./schema";
|
||||
16
src/lib/components/inputs/anime-list-input/schema.ts
Normal file
16
src/lib/components/inputs/anime-list-input/schema.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { AnimeList, AnimeListWatchStatus } from "$lib/utils/list";
|
||||
import { z } from "zod";
|
||||
|
||||
const SEP_FIELD = ":";
|
||||
const SEP_VALUE = ",";
|
||||
|
||||
export const AnimeListCodec = z.codec(z.string(), AnimeList, {
|
||||
decode: (s) => {
|
||||
const [kind, ...rest] = decodeURIComponent(s).split(SEP_FIELD);
|
||||
const statusStr = rest.pop();
|
||||
const status = statusStr ? statusStr.split(SEP_VALUE).map((v) => AnimeListWatchStatus.parse(v)) : [];
|
||||
const username = rest.join("");
|
||||
return AnimeList.parse({ kind, username, status });
|
||||
},
|
||||
encode: (list) => encodeURIComponent(`${list.kind}${SEP_FIELD}${list.username}${SEP_FIELD}${list.status.join(SEP_VALUE)}`),
|
||||
});
|
||||
@@ -21,7 +21,7 @@ export const AnimeListWatchStatus = z.enum({
|
||||
export const AnimeList = z.object({
|
||||
kind: AnimeListKind,
|
||||
username: z.string(),
|
||||
status: z.array(AnimeListWatchStatus).default([]),
|
||||
status: z.array(AnimeListWatchStatus),
|
||||
});
|
||||
|
||||
export function listExternalUrl(list: z.infer<typeof AnimeList>) {
|
||||
|
||||
@@ -2,46 +2,43 @@
|
||||
import { useSearchParams } from "runed/kit";
|
||||
import type { PageData } from "./$types";
|
||||
import { SearchParamsSchema } from "./schema";
|
||||
import { ChipGroup } from "$lib/components/ui/chip-group";
|
||||
import { AnimeListWatchStatus } from "$lib/utils/list";
|
||||
import NativeSelect from "$lib/components/ui/native-select/native-select.svelte";
|
||||
import NativeSelectOption from "$lib/components/ui/native-select/native-select-option.svelte";
|
||||
import Input from "$lib/components/ui/input/input.svelte";
|
||||
import { Label } from "$lib/components/ui/label";
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AnimeListCodec,
|
||||
AnimeListInput,
|
||||
} from "$lib/components/inputs/anime-list-input";
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
const params = useSearchParams(SearchParamsSchema, {
|
||||
showDefaults: true,
|
||||
updateURL: false,
|
||||
pushHistory: false,
|
||||
});
|
||||
|
||||
let formState: z.infer<typeof AnimeListCodec> = $state({
|
||||
kind: "mal",
|
||||
username: "",
|
||||
status: [],
|
||||
});
|
||||
|
||||
// $inspect(formState);
|
||||
|
||||
$effect(() => {
|
||||
console.log("formState", formState);
|
||||
});
|
||||
</script>
|
||||
|
||||
<h1 class="text-2xl font-semibold">List Search WIP</h1>
|
||||
|
||||
<form class="flex flex-wrap gap-2">
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label for="list-kind">Kind</Label>
|
||||
<NativeSelect id="list-kind" bind:value={params.kind}>
|
||||
<NativeSelectOption value="mal">MAL</NativeSelectOption>
|
||||
<NativeSelectOption value="anilist">AniList</NativeSelectOption>
|
||||
<NativeSelectOption value="kitsu">Kitsu</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label for="list-username">Username</Label>
|
||||
<Input id="list-username" bind:value={params.username} />
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label for="list-status">Status</Label>
|
||||
<ChipGroup
|
||||
items={AnimeListWatchStatus.options.map((v) => ({
|
||||
label: v.toUpperCase(),
|
||||
value: v,
|
||||
}))}
|
||||
bind:value={params.status}
|
||||
/>
|
||||
</div>
|
||||
<form
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
params.kind = formState.kind;
|
||||
params.username = formState.username;
|
||||
params.status = formState.status;
|
||||
}}
|
||||
class="flex flex-wrap items-end gap-2"
|
||||
>
|
||||
<AnimeListInput bind:value={formState} />
|
||||
<Button type="submit">Search</Button>
|
||||
</form>
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
import { AnimeListKind, AnimeListWatchStatus } from "$lib/utils/list";
|
||||
import { z } from "zod";
|
||||
|
||||
const valueSep = ".";
|
||||
|
||||
const statusCodec = z.codec(z.string(), z.array(AnimeListWatchStatus), {
|
||||
decode: (s) => s ? decodeURIComponent(s).split(valueSep).map((s) => AnimeListWatchStatus.parse(s)) : [],
|
||||
encode: (v) => v.length ? encodeURIComponent(v.join(valueSep)) : "",
|
||||
})
|
||||
|
||||
export const SearchParamsSchema = z.object({
|
||||
kind: AnimeListKind.default("mal"),
|
||||
username: z.string().optional().default(""),
|
||||
status: statusCodec.default([]),
|
||||
username: z.string().default(""),
|
||||
status: z.array(AnimeListWatchStatus).default([]),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user