Compare commits
3 commits
c7d7ca349d
...
387b7216a4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
387b7216a4 | ||
![]() |
f977fc254a | ||
![]() |
8360d5d6de |
34 changed files with 297 additions and 279 deletions
|
@ -1,19 +1,25 @@
|
|||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import { dndzone, SHADOW_ITEM_MARKER_PROPERTY_NAME } from 'svelte-dnd-action';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { expoOut } from 'svelte/easing';
|
||||
import { truncate } from '$lib/utils';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { truncate } from '$lib/utils';
|
||||
import type { AlbumDataField } from '$lib/types';
|
||||
import {
|
||||
dndzone,
|
||||
SHADOW_ITEM_MARKER_PROPERTY_NAME,
|
||||
type DndEvent,
|
||||
type Item
|
||||
} from 'svelte-dnd-action';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
|
||||
let { items = $bindable(), image = false, type = 'default' } = $props();
|
||||
|
||||
const flipDurationMs = 300;
|
||||
|
||||
function handleDndConsider(e: CustomEvent<any>) {
|
||||
function handleDndConsider(e: CustomEvent<DndEvent<Item>>) {
|
||||
items = e.detail.items;
|
||||
}
|
||||
function handleDndFinalize(e: CustomEvent<any>) {
|
||||
function handleDndFinalize(e: CustomEvent<DndEvent<Item>>) {
|
||||
items = e.detail.items;
|
||||
}
|
||||
function transformDraggedElement(draggedEl: HTMLElement | undefined) {
|
||||
|
@ -40,7 +46,7 @@
|
|||
);
|
||||
</script>
|
||||
|
||||
{#snippet card(item, i)}
|
||||
{#snippet card(item: AlbumDataField, i: number)}
|
||||
{#if image}
|
||||
<img class="aspect-square w-full object-cover" alt="Album Art" src={item.value} />
|
||||
<input type="hidden" name="{type}_{i}" value={item.value} />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
|
@ -12,6 +12,6 @@
|
|||
|
||||
<AlertDialogPrimitive.Cancel
|
||||
bind:ref
|
||||
class={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
|
||||
class={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive, type WithoutChild } from "bits-ui";
|
||||
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { AlertDialog as AlertDialogPrimitive, type WithoutChild } from 'bits-ui';
|
||||
import AlertDialogOverlay from './alert-dialog-overlay.svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -18,7 +18,7 @@
|
|||
<AlertDialogPrimitive.Content
|
||||
bind:ref
|
||||
class={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg",
|
||||
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
|
@ -11,6 +11,6 @@
|
|||
|
||||
<AlertDialogPrimitive.Description
|
||||
bind:ref
|
||||
class={cn("text-muted-foreground text-sm", className)}
|
||||
class={cn('text-sm text-muted-foreground', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
||||
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
|
||||
class={cn('flex flex-col space-y-2 text-center sm:text-left', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
|
@ -12,7 +12,7 @@
|
|||
<AlertDialogPrimitive.Overlay
|
||||
bind:ref
|
||||
class={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80",
|
||||
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
|
@ -12,7 +12,7 @@
|
|||
|
||||
<AlertDialogPrimitive.Title
|
||||
bind:ref
|
||||
class={cn("text-lg font-semibold", className)}
|
||||
class={cn('text-lg font-semibold', className)}
|
||||
{level}
|
||||
{...restProps}
|
||||
/>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
|
||||
import Title from "./alert-dialog-title.svelte";
|
||||
import Action from "./alert-dialog-action.svelte";
|
||||
import Cancel from "./alert-dialog-cancel.svelte";
|
||||
import Footer from "./alert-dialog-footer.svelte";
|
||||
import Header from "./alert-dialog-header.svelte";
|
||||
import Overlay from "./alert-dialog-overlay.svelte";
|
||||
import Content from "./alert-dialog-content.svelte";
|
||||
import Description from "./alert-dialog-description.svelte";
|
||||
import Title from './alert-dialog-title.svelte';
|
||||
import Action from './alert-dialog-action.svelte';
|
||||
import Cancel from './alert-dialog-cancel.svelte';
|
||||
import Footer from './alert-dialog-footer.svelte';
|
||||
import Header from './alert-dialog-header.svelte';
|
||||
import Overlay from './alert-dialog-overlay.svelte';
|
||||
import Content from './alert-dialog-content.svelte';
|
||||
import Description from './alert-dialog-description.svelte';
|
||||
|
||||
const Root = AlertDialogPrimitive.Root;
|
||||
const Trigger = AlertDialogPrimitive.Trigger;
|
||||
|
@ -36,5 +36,5 @@ export {
|
|||
Trigger as AlertDialogTrigger,
|
||||
Overlay as AlertDialogOverlay,
|
||||
Content as AlertDialogContent,
|
||||
Description as AlertDialogDescription,
|
||||
Description as AlertDialogDescription
|
||||
};
|
||||
|
|
|
@ -1,36 +1,35 @@
|
|||
<script lang="ts" module>
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
|
||||
import { type VariantProps, tv } from "tailwind-variants";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
export const buttonVariants = tv({
|
||||
base: "focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
base: 'focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 shadow",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm",
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm',
|
||||
outline:
|
||||
"border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
'border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm',
|
||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
default: 'h-9 px-4 py-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
|
||||
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
|
||||
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
|
||||
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
|
||||
|
||||
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
|
||||
WithElementRef<HTMLAnchorAttributes> & {
|
||||
|
@ -40,27 +39,22 @@
|
|||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
ref = $bindable(null),
|
||||
href = undefined,
|
||||
type = "button",
|
||||
type = 'button',
|
||||
children,
|
||||
...restProps
|
||||
}: ButtonProps = $props();
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a
|
||||
bind:this={ref}
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
{href}
|
||||
{...restProps}
|
||||
>
|
||||
<a bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {href} {...restProps}>
|
||||
{@render children?.()}
|
||||
</a>
|
||||
{:else}
|
||||
|
|
|
@ -2,8 +2,8 @@ import Root, {
|
|||
type ButtonProps,
|
||||
type ButtonSize,
|
||||
type ButtonVariant,
|
||||
buttonVariants,
|
||||
} from "./button.svelte";
|
||||
buttonVariants
|
||||
} from './button.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
|
@ -13,5 +13,5 @@ export {
|
|||
buttonVariants,
|
||||
type ButtonProps,
|
||||
type ButtonSize,
|
||||
type ButtonVariant,
|
||||
type ButtonVariant
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -11,6 +11,6 @@
|
|||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div bind:this={ref} class={cn("p-6", className)} {...restProps}>
|
||||
<div bind:this={ref} class={cn('p-6', className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -11,6 +11,6 @@
|
|||
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
||||
</script>
|
||||
|
||||
<p bind:this={ref} class={cn("text-muted-foreground text-sm", className)} {...restProps}>
|
||||
<p bind:this={ref} class={cn('text-sm text-muted-foreground', className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -11,6 +11,6 @@
|
|||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div bind:this={ref} class={cn("flex items-center p-6 pt-0", className)} {...restProps}>
|
||||
<div bind:this={ref} class={cn('flex items-center p-6 pt-0', className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -11,6 +11,6 @@
|
|||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div bind:this={ref} class={cn("flex flex-col space-y-1.5 p-6 pb-0", className)} {...restProps}>
|
||||
<div bind:this={ref} class={cn('flex flex-col space-y-1.5 p-6 pb-0', className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -18,7 +18,7 @@
|
|||
role="heading"
|
||||
aria-level={level}
|
||||
bind:this={ref}
|
||||
class={cn("font-semibold leading-none tracking-tight", className)}
|
||||
class={cn('font-semibold leading-none tracking-tight', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class={cn("bg-card text-card-foreground rounded-xl border shadow", className)}
|
||||
class={cn('rounded-xl border bg-card text-card-foreground shadow', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Root from "./card.svelte";
|
||||
import Content from "./card-content.svelte";
|
||||
import Description from "./card-description.svelte";
|
||||
import Footer from "./card-footer.svelte";
|
||||
import Header from "./card-header.svelte";
|
||||
import Title from "./card-title.svelte";
|
||||
import Root from './card.svelte';
|
||||
import Content from './card-content.svelte';
|
||||
import Description from './card-description.svelte';
|
||||
import Footer from './card-footer.svelte';
|
||||
import Header from './card-header.svelte';
|
||||
import Title from './card-title.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
|
@ -18,5 +18,5 @@ export {
|
|||
Description as CardDescription,
|
||||
Footer as CardFooter,
|
||||
Header as CardHeader,
|
||||
Title as CardTitle,
|
||||
Title as CardTitle
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Root from "./separator.svelte";
|
||||
import Root from './separator.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Separator,
|
||||
Root as Separator
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { Separator as SeparatorPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import { Separator as SeparatorPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
orientation = "horizontal",
|
||||
orientation = 'horizontal',
|
||||
...restProps
|
||||
}: SeparatorPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
@ -13,8 +13,8 @@
|
|||
<SeparatorPrimitive.Root
|
||||
bind:ref
|
||||
class={cn(
|
||||
"bg-border shrink-0",
|
||||
orientation === "horizontal" ? "h-[1px] w-full" : "min-h-full w-[1px]",
|
||||
'shrink-0 bg-border',
|
||||
orientation === 'horizontal' ? 'h-[1px] w-full' : 'min-h-full w-[1px]',
|
||||
className
|
||||
)}
|
||||
{orientation}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Root from "./skeleton.svelte";
|
||||
import Root from './skeleton.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Skeleton,
|
||||
Root as Skeleton
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import type { WithElementRef, WithoutChildren } from "bits-ui";
|
||||
import type { HTMLAttributes } from "svelte/elements";
|
||||
import { cn } from "$lib/utils.js";
|
||||
import type { WithElementRef, WithoutChildren } from 'bits-ui';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
@ -12,6 +12,6 @@
|
|||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class={cn("bg-primary/10 animate-pulse rounded-md", className)}
|
||||
class={cn('animate-pulse rounded-md bg-primary/10', className)}
|
||||
{...restProps}
|
||||
></div>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import type { AlbumSolveState } from '$lib/types';
|
||||
|
||||
class AlbumState {
|
||||
private albums: SpotifyApi.AlbumObjectSimplified[] | undefined = undefined;
|
||||
private albums: AlbumSolveState[] | undefined = undefined;
|
||||
|
||||
setAlbums(data: SpotifyApi.AlbumObjectSimplified[]) {
|
||||
setAlbums(data: AlbumSolveState[]) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
@ -29,16 +29,14 @@ class AlbumState {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (matching.images.at(0)?.url !== solve.imageUrl) {
|
||||
if (matching.image !== solve.image) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const artistName of solve.artists) {
|
||||
if (!matching.artists.find((artist) => artist.name === artistName)) {
|
||||
if (matching.artists !== solve.artists) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,18 @@
|
|||
export type AlbumData = {
|
||||
names: AlbumDataField[];
|
||||
artists: AlbumDataField[];
|
||||
images: AlbumDataField[];
|
||||
};
|
||||
|
||||
export type AlbumDataField = {
|
||||
id: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type AlbumSolveState = {
|
||||
name: string;
|
||||
artists: string[];
|
||||
imageUrl: string;
|
||||
artists: string;
|
||||
image: string;
|
||||
};
|
||||
|
||||
export type Player = {
|
||||
|
|
|
@ -38,17 +38,17 @@ export const load: PageServerLoad = async ({ fetch, locals }) => {
|
|||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
const albumNames = data.albums.map((album: SpotifyApi.AlbumObjectSimplified) => ({
|
||||
const albumNames = data.albums.map((album: AlbumSolveState) => ({
|
||||
id: nanoid(),
|
||||
value: album.name
|
||||
}));
|
||||
const albumImages = data.albums.map((album: SpotifyApi.AlbumObjectSimplified) => ({
|
||||
const albumImages = data.albums.map((album: AlbumSolveState) => ({
|
||||
id: nanoid(),
|
||||
value: album.images.at(0)?.url || ''
|
||||
value: album.image
|
||||
}));
|
||||
const albumArtists = data.albums.map((album: SpotifyApi.AlbumObjectSimplified) => ({
|
||||
const albumArtists = data.albums.map((album: AlbumSolveState) => ({
|
||||
id: nanoid(),
|
||||
value: album.artists.map((artist) => artist.name).join(', ')
|
||||
value: album.artists
|
||||
}));
|
||||
|
||||
return {
|
||||
|
@ -84,9 +84,7 @@ export const actions = {
|
|||
const image = data.get(`images_${i}`);
|
||||
const artists = data.get(`artists_${i}`);
|
||||
|
||||
const artistList = artists.split(', ');
|
||||
|
||||
state.push({ name: name, imageUrl: image, artists: artistList });
|
||||
state.push({ name: name, image: image, artists: artists });
|
||||
}
|
||||
|
||||
const solved = albumState.checkSolve(state);
|
||||
|
|
|
@ -7,17 +7,18 @@
|
|||
import DndGroup from '$lib/components/DNDGroup.svelte';
|
||||
import type { PageData } from './$types';
|
||||
import { enhance } from '$app/forms';
|
||||
import type { AlbumData } from '$lib/types';
|
||||
|
||||
let { data }: { data: PageData; form: FormData } = $props();
|
||||
|
||||
let loading = $state(false);
|
||||
let oldAlbums: SpotifyApi.AlbumObjectSimplified[] = $state([]);
|
||||
let oldAlbums: AlbumData | undefined = $state();
|
||||
|
||||
// Used when user answers wrong and no new data comes in
|
||||
$effect(() => {
|
||||
if (data.streamed?.albums) {
|
||||
data.streamed.albums.then((data) => {
|
||||
oldAlbums = data;
|
||||
oldAlbums = data as AlbumData;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -38,11 +39,11 @@
|
|||
</div>
|
||||
{/snippet}
|
||||
|
||||
{#snippet playArea(albums, placeholder = false)}
|
||||
{#snippet playArea(albums: AlbumData | undefined, placeholder = false)}
|
||||
{#if placeholder}
|
||||
{#each { length: 2 } as _}
|
||||
{#each { length: 2 }}
|
||||
<section class="grid grid-cols-3 items-center gap-14">
|
||||
{#each { length: 3 } as _}
|
||||
{#each { length: 3 }}
|
||||
<Skeleton class="h-[5.25rem] w-full rounded-xl " />
|
||||
{/each}
|
||||
</section>
|
||||
|
@ -50,11 +51,11 @@
|
|||
{/each}
|
||||
|
||||
<section class="grid grid-cols-3 items-center gap-14">
|
||||
{#each { length: 3 } as _}
|
||||
{#each { length: 3 }}
|
||||
<Skeleton class="aspect-square h-auto max-w-full rounded-xl object-cover" />
|
||||
{/each}
|
||||
</section>
|
||||
{:else}
|
||||
{:else if albums}
|
||||
<DndGroup items={albums.names} type="names"></DndGroup>
|
||||
<Separator />
|
||||
<DndGroup items={albums.artists} type="artists"></DndGroup>
|
||||
|
@ -91,7 +92,7 @@
|
|||
</AlertDialog.Content>
|
||||
</AlertDialog.Root>
|
||||
|
||||
<header class="font-title mb-24 flex flex-col items-center">
|
||||
<header class="mb-24 flex flex-col items-center font-title">
|
||||
<h1 class="mb-1 scroll-m-20 text-6xl font-extrabold tracking-tight lg:text-7xl">Paku biiti</h1>
|
||||
<p class="text-2xl font-semibold text-muted-foreground">
|
||||
Lohista kokku õiged albumi <span class="text-red-600 dark:text-red-400">nimed</span>,
|
||||
|
@ -108,9 +109,9 @@
|
|||
>
|
||||
{#if data?.streamed?.albums}
|
||||
{#await data.streamed.albums}
|
||||
{@render playArea({}, true)}
|
||||
{@render playArea(undefined, true)}
|
||||
{:then albums}
|
||||
{@render playArea(albums)}
|
||||
{@render playArea(albums as AlbumData)}
|
||||
{/await}
|
||||
{:else}
|
||||
{@render playArea(oldAlbums)}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<meta property="og:image" content={baseURL + '/favicon.svg'} />
|
||||
</svelte:head>
|
||||
|
||||
<header class="font-title mb-24 flex flex-col items-center">
|
||||
<header class="mb-24 flex flex-col items-center font-title">
|
||||
<h1 class="mb-1 scroll-m-20 text-6xl font-extrabold tracking-tight lg:text-7xl">
|
||||
stuff.kasterpalu.ee
|
||||
</h1>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<main class="grid w-full max-w-4xl justify-items-center">
|
||||
{#each Object.entries(games) as [href, { image, name }]}
|
||||
<a
|
||||
class="shadow-sharp flex aspect-[4/1] w-full max-w-sm items-center justify-center rounded-xl border-2 border-current bg-contain bg-no-repeat transition-shadow transition-transform"
|
||||
class="shadow-sharp flex aspect-[4/1] w-full max-w-sm items-center justify-center rounded-xl border-2 border-current bg-contain bg-no-repeat transition-all"
|
||||
style="background-image: url('{image}')"
|
||||
draggable="false"
|
||||
{href}
|
||||
|
|
|
@ -1,16 +1,26 @@
|
|||
import { albumState } from '$lib/server/AlbumState.svelte';
|
||||
import { spotifyAPI } from '$lib/server/Spotify.svelte';
|
||||
import type { AlbumSolveState } from '$lib/types';
|
||||
import { json } from '@sveltejs/kit';
|
||||
|
||||
export async function GET({ params }) {
|
||||
const count = params.count || 1;
|
||||
|
||||
const albums: SpotifyApi.AlbumObjectSimplified[] = [];
|
||||
const albums: AlbumSolveState[] = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const album = await spotifyAPI.getRandomAlbum();
|
||||
if (album) {
|
||||
albums.push(album);
|
||||
const image = album.images.at(0);
|
||||
if (!image?.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
albums.push({
|
||||
name: album.name,
|
||||
artists: album.artists.map((artist) => artist.name).join(', '),
|
||||
image: image.url
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue