feat: update navigation labels and add new components for item listing
Deploy to Cloudflare Pages / deploy (push) Failing after 41s
Deploy to Cloudflare Pages / deploy (push) Failing after 41s
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { cn } from "$lib/utils";
|
||||
import FileExclamationPointIcon from "@lucide/svelte/icons/file-exclamation-point";
|
||||
import type { HTMLImgAttributes } from "svelte/elements";
|
||||
|
||||
const props: HTMLImgAttributes = $props();
|
||||
let loaded = $state(false);
|
||||
let error = $state(false);
|
||||
$effect(() => {
|
||||
void props.src;
|
||||
loaded = false;
|
||||
error = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if error}
|
||||
<div class="flex items-center justify-center opacity-50">
|
||||
<FileExclamationPointIcon />
|
||||
</div>{:else}
|
||||
<img
|
||||
{...props}
|
||||
onload={() => (loaded = true)}
|
||||
onerror={() => (error = true)}
|
||||
class={cn("opacity-0", loaded && "opacity-100")}
|
||||
/>
|
||||
{/if}
|
||||
@@ -0,0 +1,49 @@
|
||||
<script lang="ts" module>
|
||||
import { type VariantProps, tv } from "tailwind-variants";
|
||||
|
||||
export const badgeVariants = tv({
|
||||
base: "h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive group/badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap transition-colors focus-visible:ring-[3px] [&>svg]:pointer-events-none",
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
||||
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
||||
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
|
||||
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
||||
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
});
|
||||
|
||||
export type BadgeVariant = VariantProps<typeof badgeVariants>["variant"];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { HTMLAnchorAttributes } from "svelte/elements";
|
||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
href,
|
||||
class: className,
|
||||
variant = "default",
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAnchorAttributes> & {
|
||||
variant?: BadgeVariant;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
this={href ? "a" : "span"}
|
||||
bind:this={ref}
|
||||
data-slot="badge"
|
||||
{href}
|
||||
class={cn(badgeVariants({ variant }), className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</svelte:element>
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as Badge } from "./badge.svelte";
|
||||
export { badgeVariants, type BadgeVariant } from "./badge.svelte";
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"nav": {
|
||||
"home": "Home",
|
||||
"search": "Search",
|
||||
"list": "Fridge",
|
||||
"scan": "Scan",
|
||||
"guide": "Guide",
|
||||
"settings": "Settings"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"nav": {
|
||||
"home": "หน้าหลัก",
|
||||
"search": "ค้นหา",
|
||||
"list": "รายการ",
|
||||
"scan": "สแกน",
|
||||
"guide": "ไกด์",
|
||||
"settings": "การตั้งค่า"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<script>
|
||||
import Item from "./item.svelte";
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2 p-2">
|
||||
{#each Array(20)}
|
||||
<Item
|
||||
name="ข้าวมันไก่"
|
||||
expiry={new Date()}
|
||||
icon="abc"
|
||||
category="อาหารจารเดียว"
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
import Image from "$lib/components/image.svelte";
|
||||
import Badge from "$lib/components/ui/badge/badge.svelte";
|
||||
|
||||
const {
|
||||
icon,
|
||||
name,
|
||||
expiry,
|
||||
category,
|
||||
}: { icon: string; name: string; expiry: Date; category: string } = $props();
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
title={`Item ${name}`}
|
||||
class="flex cursor-pointer items-start gap-3 rounded-md border p-3 text-left transition-colors hover:bg-accent"
|
||||
>
|
||||
<div
|
||||
class="flex size-16 shrink-0 items-center justify-center rounded bg-muted"
|
||||
>
|
||||
<!-- {r.paid ? (
|
||||
<Check class="size-5 text-green-500" />
|
||||
) : r.expires && new Date(r.expires) <= new Date() ? (
|
||||
<Circle class="size-5 text-muted-foreground" />
|
||||
) : (
|
||||
<Clock class="size-5 text-yellow-500" />
|
||||
)} -->
|
||||
<Image src={icon} />
|
||||
</div>
|
||||
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="truncate font-medium">{name}</span>
|
||||
</div>
|
||||
<span class="truncate text-xs text-muted-foreground">{expiry}</span>
|
||||
<div class="flex items-center gap-2 text-xs">
|
||||
<Badge variant="outline">{category}</Badge>
|
||||
<span class="font-medium">123 ฿</span>
|
||||
<!-- {r.paid ? (
|
||||
<span class="text-green-500">ชำระแล้ว</span>
|
||||
) : r.expires && new Date(r.expires) <= new Date() ? (
|
||||
<span class="text-muted-foreground">หมดเวลา</span>
|
||||
) : (
|
||||
<span class="text-yellow-500">รอชำระ</span>
|
||||
)} -->
|
||||
<span class="text-green-500">ชำระแล้ว</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -2,7 +2,7 @@
|
||||
import NavButton from "$lib/components/nav/nav-button.svelte";
|
||||
import NavCam from "$lib/components/nav/nav-cam.svelte";
|
||||
import HomeIcon from "@lucide/svelte/icons/house";
|
||||
import SearchIcon from "@lucide/svelte/icons/search";
|
||||
import ListIcon from "@lucide/svelte/icons/list";
|
||||
import GuideIcon from "@lucide/svelte/icons/book-image";
|
||||
import SettingsIcon from "@lucide/svelte/icons/settings";
|
||||
import { _ } from "svelte-i18n";
|
||||
@@ -10,13 +10,13 @@
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="wrap flex h-dvh flex-col overflow-hidden">
|
||||
<div class="content">
|
||||
{@render children()}
|
||||
</div>
|
||||
<div class="navbar flex items-center justify-evenly bg-sidebar">
|
||||
<NavButton label={$_("nav.home")} icon={HomeIcon} href="/app/home" />
|
||||
<NavButton label={$_("nav.search")} icon={SearchIcon} href="/app/search" />
|
||||
<NavButton label={$_("nav.list")} icon={ListIcon} href="/app/list" />
|
||||
<NavCam />
|
||||
<NavButton label={$_("nav.guide")} icon={GuideIcon} href="/app/guide" />
|
||||
<NavButton
|
||||
@@ -29,14 +29,21 @@
|
||||
|
||||
<style>
|
||||
.navbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 64px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-bottom: 64px;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.content {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
search here
|
||||
Reference in New Issue
Block a user