Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for multiple lists #218

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"devDependencies": {
"@eslint/js": "^9.18.0",
"@floating-ui/dom": "^1.6.13",
"@skeletonlabs/skeleton": "^2.10.4",
"@skeletonlabs/tw-plugin": "^0.4.0",
"@skeletonlabs/skeleton": "^2.11.0",
"@skeletonlabs/tw-plugin": "^0.4.1",
"@sveltejs/adapter-node": "^5.2.11",
"@sveltejs/kit": "^2.15.2",
"@sveltejs/vite-plugin-svelte": "^5.0.3",
Expand Down
648 changes: 328 additions & 320 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@
"suggestions-are-disabled": "Suggestions are disabled",
"the-page-you-were-looking-for-wasnt-found": "The page you were looking for wasn't found",
"this-instance-is-invite-only": "This instance is invite only",
"unable-to-create-list": "Unable to create list",
"unable-to-delete-items": "Unable to delete items",
"unable-to-find-product-information": "Unable to find product information. You can still fill in the details manually.",
"unable-to-update-list-settings": "Unable to update list settings",
Expand All @@ -185,12 +186,15 @@
},
"general": {
"add-user": "Add User",
"all": "All",
"apply": "Apply",
"cancel": "Cancel",
"cannot-delete-default-group-msg": "You cannot delete the default group. Please change the default group before deleting this group.",
"change-group": "Change Group",
"confirm": "Confirm",
"copied": "Copied!",
"copy-to-clipboard": "Copy to clipboard",
"create": "Create",
"create-group": "Create Group",
"dismiss": "Dismiss",
"enable": "Enable",
Expand Down Expand Up @@ -276,8 +280,10 @@
"claimed-item": "{claimed, select, true {Claimed} other {Unclaimed}} item",
"create": "Create Wish",
"create-for": "Create Wish for {listOwner}",
"create-list": "Create List",
"default-sort": "Default Sort",
"delete": "Delete",
"delete-list-confirmation": "Are you sure you want to delete this list?",
"deny": "Deny",
"edit": "Edit",
"edit-wish": "Edit Wish",
Expand All @@ -294,6 +300,7 @@
"lists": "Lists",
"manage": "Manage",
"manage-list": "Manage List",
"my-lists": "My Lists",
"my-wishes": "My Wishes",
"no-wishes-yet": "No wishes yet",
"note-placeholder": "i.e. size, color, etc.",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/IconSelector.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
}}
oninput={(e) => (search = e.currentTarget.value)}
placeholder="gift"
showClearButton={() => iconValue !== null || iconValue !== undefined}
showClearButton={() => iconValue !== null && iconValue !== undefined}
type="text"
value={iconValue}
>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/ListCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import Avatar from "./Avatar.svelte";
import { t } from "svelte-i18n";

interface ListWithCounts extends Pick<List, "id" | "name" | "icon" | "iconColor"> {
interface ListWithCounts extends Partial<Pick<List, "id" | "name" | "icon" | "iconColor">> {
owner: Pick<User, "name" | "username" | "picture">;
itemCount?: number;
claimedCount?: number;
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/navigation/navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const navItems = [
icon: "ion:home"
},
{
labelKey: "wishes.my-wishes",
labelKey: "wishes.my-lists",
href: "/wishlists/me",
icon: "ion:gift"
},
Expand Down
145 changes: 145 additions & 0 deletions src/lib/components/wishlists/ManageListForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<script lang="ts">
import ListCard from "$lib/components/ListCard.svelte";
import { t } from "svelte-i18n";
import IconSelector from "$lib/components/IconSelector.svelte";
import { enhance } from "$app/forms";
import ClearableInput from "$lib/components/ClearableInput.svelte";
import { rgbToHex } from "$lib/util";
import type { List, User } from "@prisma/client";
import { getModalStore } from "@skeletonlabs/skeleton";

interface ListProps extends Partial<Pick<List, "id" | "icon" | "iconColor" | "name">> {
owner: Pick<User, "name" | "username" | "picture">;
}

interface Props {
data: {
list: ListProps;
};
persistButtonName: string;
editing?: boolean;
}

const { data, persistButtonName, editing = false }: Props = $props();
const modalStore = getModalStore();
let list = $state(data.list);
let colorElement: Element | undefined = $state();
let defaultColor: string = $derived.by(() => {
if (colorElement) {
const rgbColor = getComputedStyle(colorElement).backgroundColor;
const rgbValues = rgbColor.match(/\d+/g)?.map(Number.parseFloat);
return rgbValues ? rgbToHex(rgbValues[0], rgbValues[1], rgbValues[2]) : "";
}
return list.iconColor || "";
});
let colorValue: string | null = $state((() => defaultColor)());

const handleDelete = async () => {
return new Promise((resolve, reject) => {
return modalStore.trigger({
type: "confirm",
title: $t("general.please-confirm"),
body: $t("wishes.delete-list-confirmation"),
response(confirmed: boolean) {
if (!confirmed) {
reject();
} else {
resolve(null);
}
},
buttonTextCancel: $t("general.cancel"),
buttonTextConfirm: $t("general.confirm")
});
});
};

$effect(() => {
if (!colorValue) colorValue = defaultColor;
});
</script>

<form
action="?/persist"
method="POST"
use:enhance={async (e) => {
if (e.formData.get("iconColor") === defaultColor) {
e.formData.delete("iconColor");
}
if (e.action.search === "?/delete") {
await handleDelete().catch(() => e.cancel());
}
}}
>
<div class="grid grid-cols-1 gap-4 pb-4 md:grid-cols-2">
<label class="col-span-1 md:col-span-2" for="name">
<span>{$t("auth.name")}</span>
<ClearableInput
id="name"
name="name"
class="input"
autocomplete="off"
clearButtonLabel={$t("a11y.clear-name-field")}
onValueClear={() => (list.name = null)}
placeholder={$t("wishes.wishes-for", { values: { listOwner: list.owner.name } })}
showClearButton={() => list.name !== null}
type="text"
bind:value={list.name}
/>
</label>

<label class="col-span-1 flex flex-col" for="name">
<span>{$t("general.icon-bg-color")}</span>
<div class="grid grid-cols-[auto_1fr] gap-2">
<input
id="iconColor"
name="iconColor"
class="input"
onchange={(e) => (list.iconColor = e.currentTarget?.value)}
type="color"
bind:value={colorValue}
/>
<ClearableInput
class="input"
clearButtonLabel={$t("a11y.clear-color-field")}
onValueClear={() => {
colorValue = defaultColor;
list.iconColor = defaultColor;
}}
readonly
showClearButton={() => colorValue !== defaultColor}
tabindex={-1}
type="text"
value={colorValue}
/>
</div>
</label>
<div class="col-span-1">
<IconSelector id="icon" icon={list.icon} onIconSelected={(icon) => (list.icon = icon)} />
</div>

<div class="col-span-1 md:col-span-2">
<div class="flex flex-col space-y-2">
<span>{$t("wishes.preview")}</span>
<ListCard hideCount {list} preventNavigate />
</div>
</div>
</div>

<div class="flex flex-row justify-between">
<button class="variant-ghost-secondary btn w-min" onclick={() => history.back()} type="button">
{$t("general.cancel")}
</button>
<div class="flex flex-row space-x-4">
{#if editing}
<button class="variant-filled-error btn w-min" formaction="?/delete" type="submit">
{$t("wishes.delete")}
</button>
{/if}
<button class="variant-filled-primary btn w-min" type="submit">
{persistButtonName}
</button>
</div>
</div>
</form>

<div bind:this={colorElement} class="bg-primary-400-500-token hidden"></div>
Loading