your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import { COLUMNS, margin, mobileMargin } from '$lib';
3 import type { Item } from '$lib/types';
4 import type { WithElementRef } from 'bits-ui';
5 import type { Snippet } from 'svelte';
6 import type { HTMLAttributes } from 'svelte/elements';
7 import { getColor } from '../..';
8
9 const colors = {
10 base: 'bg-base-200/50 dark:bg-base-950/50',
11 accent: 'bg-accent-400 dark:bg-accent-500 accent',
12 transparent: ''
13 } as Record<string, string>;
14
15 export type BaseCardProps = {
16 item: Item;
17 controls?: Snippet<[]>;
18 isEditing?: boolean;
19 showOutline?: boolean;
20 locked?: boolean;
21 fillPage?: boolean;
22 } & WithElementRef<HTMLAttributes<HTMLDivElement>>;
23
24 let {
25 item,
26 children,
27 ref = $bindable(null),
28 isEditing = false,
29 controls,
30 showOutline,
31 locked = false,
32 fillPage = false,
33 class: className,
34 ...rest
35 }: BaseCardProps = $props();
36
37 let color = $derived(getColor(item));
38</script>
39
40<div
41 id={item.id}
42 data-flip-id={item.id}
43 data-fill-page={fillPage ? 'true' : undefined}
44 bind:this={ref}
45 draggable={false}
46 class={[
47 fillPage
48 ? 'card group/card selection:bg-accent-600/50 focus-within:outline-accent-500 @container/card relative isolate z-0 h-full w-full outline-offset-2 transition-all duration-200 focus-within:outline-2'
49 : 'card group/card selection:bg-accent-600/50 focus-within:outline-accent-500 @container/card absolute isolate z-0 rounded-3xl outline-offset-2 transition-all duration-200 focus-within:outline-2',
50 !fillPage ? (color ? (colors[color] ?? colors.accent) : colors.base) : '',
51 color !== 'accent' && item.color !== 'base' && item.color !== 'transparent' ? color : '',
52 showOutline ? 'outline-2' : '',
53 className
54 ]}
55 style={`
56 --mx: ${item.mobileX};
57 --my: ${item.mobileY};
58 --mw: ${item.mobileW};
59 --mh: ${item.mobileH};
60 --mm: ${mobileMargin}px;
61
62 --dx: ${item.x};
63 --dy: ${item.y};
64 --dw: ${item.w};
65 --dh: ${item.h};
66 --dm: ${margin}px;
67
68 --columns: ${COLUMNS}`}
69 {...rest}
70>
71 <div
72 class={[
73 'text-base-900 dark:text-base-50 relative isolate h-full w-full overflow-hidden',
74 !fillPage ? 'rounded-[23px]' : '',
75 color !== 'base' && color != 'transparent' ? 'light' : ''
76 ]}
77 >
78 {@render children?.()}
79
80 {#if !isEditing && item.cardData.label}
81 <div
82 class="text-base-900 dark:text-base-50 bg-base-200/50 dark:bg-base-900/50 absolute top-2 left-2 z-30 max-w-[calc(100%-1rem)] rounded-xl p-1 px-2 text-base font-semibold backdrop-blur-md"
83 >
84 {item.cardData.label}
85 </div>
86 {/if}
87 </div>
88 {@render controls?.()}
89</div>
90
91<style>
92 .card {
93 container-name: card;
94 container-type: size;
95 translate: calc((var(--mx) / var(--columns)) * 100cqw + var(--mm))
96 calc((var(--my) / var(--columns)) * 100cqw + var(--mm));
97 width: calc((var(--mw) / var(--columns)) * 100cqw - (var(--mm) * 2));
98 height: calc((var(--mh) / var(--columns)) * 100cqw - (var(--mm) * 2));
99 }
100
101 .card[data-fill-page='true'] {
102 translate: none;
103 width: 100%;
104 height: 100%;
105 }
106
107 @container grid (width >= 42rem) {
108 .card:not([data-fill-page='true']) {
109 translate: calc((var(--dx) / var(--columns)) * 100cqw + var(--dm))
110 calc((var(--dy) / var(--columns)) * 100cqw + var(--dm));
111 width: calc((var(--dw) / var(--columns)) * 100cqw - (var(--dm) * 2));
112 height: calc((var(--dh) / var(--columns)) * 100cqw - (var(--dm) * 2));
113 }
114 }
115</style>