your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import { dev } from '$app/environment';
3 import { user } from '$lib/atproto';
4 import type { WebsiteData } from '$lib/types';
5 import { Button, Input, Modal, Navbar, Popover, Toggle } from '@foxui/core';
6
7 let {
8 data,
9 linkValue = $bindable(),
10 newCard,
11 addLink,
12 showSettings = $bindable(),
13
14 showingMobileView = $bindable(),
15 isSaving = $bindable(),
16
17 save,
18
19 handleImageInputChange,
20 handleVideoInputChange
21 }: {
22 data: WebsiteData;
23 linkValue: string;
24 newCard: (type: string) => void;
25 addLink: (url: string) => void;
26
27 showSettings: boolean;
28
29 showingMobileView: boolean;
30
31 isSaving: boolean;
32
33 save: () => Promise<void>;
34
35 handleImageInputChange: (evt: Event) => void;
36 handleVideoInputChange: (evt: Event) => void;
37 } = $props();
38
39 let linkPopoverOpen = $state(false);
40
41 let imageInputRef: HTMLInputElement | undefined = $state();
42 let videoInputRef: HTMLInputElement | undefined = $state();
43
44 let shareModalOpen = $state(false);
45</script>
46
47<input
48 type="file"
49 accept="image/*"
50 onchange={handleImageInputChange}
51 class="hidden"
52 multiple
53 bind:this={imageInputRef}
54/>
55
56<input
57 type="file"
58 accept="video/*"
59 onchange={handleVideoInputChange}
60 class="hidden"
61 multiple
62 bind:this={videoInputRef}
63/>
64
65<Modal bind:open={shareModalOpen}></Modal>
66
67{#if dev || (user.isLoggedIn && user.profile?.did === data.did)}
68 <Navbar
69 class={[
70 'dark:bg-base-900 bg-base-100 top-auto bottom-2 mx-4 mt-3 max-w-3xl rounded-full px-4 md:mx-auto lg:inline-flex',
71 !dev ? 'hidden' : ''
72 ]}
73 >
74 <div class="flex items-center gap-2">
75 <Button
76 size="iconLg"
77 variant="ghost"
78 class="backdrop-blur-none"
79 onclick={() => {
80 newCard('section');
81 }}
82 >
83 <svg
84 xmlns="http://www.w3.org/2000/svg"
85 viewBox="0 0 24 24"
86 fill="none"
87 stroke="currentColor"
88 stroke-width="2"
89 stroke-linecap="round"
90 stroke-linejoin="round"
91 ><path d="M6 12h12" /><path d="M6 20V4" /><path d="M18 20V4" /></svg
92 >
93 </Button>
94
95 <Button
96 size="iconLg"
97 variant="ghost"
98 class="backdrop-blur-none"
99 onclick={() => {
100 newCard('text');
101 }}
102 >
103 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
104 ><path
105 fill="none"
106 stroke="currentColor"
107 stroke-linecap="round"
108 stroke-linejoin="round"
109 stroke-width="2"
110 d="m15 16l2.536-7.328a1.02 1.02 1 0 1 1.928 0L22 16m-6.303-2h5.606M2 16l4.039-9.69a.5.5 0 0 1 .923 0L11 16m-7.696-3h6.392"
111 /></svg
112 >
113 </Button>
114
115 <Popover sideOffset={16} bind:open={linkPopoverOpen} class="bg-base-100 dark:bg-base-900">
116 {#snippet child({ props })}
117 <Button
118 size="iconLg"
119 variant="ghost"
120 class="backdrop-blur-none"
121 onclick={() => {
122 newCard('link');
123 }}
124 {...props}
125 >
126 <svg
127 xmlns="http://www.w3.org/2000/svg"
128 fill="none"
129 viewBox="-2 -2 28 28"
130 stroke-width="2"
131 stroke="currentColor"
132 >
133 <path
134 stroke-linecap="round"
135 stroke-linejoin="round"
136 d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
137 />
138 </svg>
139 </Button>
140 {/snippet}
141 <Input
142 spellcheck={false}
143 type="url"
144 bind:value={linkValue}
145 onkeydown={(event) => {
146 if (event.code === 'Enter') {
147 addLink(linkValue);
148 event.preventDefault();
149 }
150 }}
151 placeholder="Enter link"
152 />
153 <Button onclick={() => addLink(linkValue)} size="icon"
154 ><svg
155 xmlns="http://www.w3.org/2000/svg"
156 fill="none"
157 viewBox="0 0 24 24"
158 stroke-width="2"
159 stroke="currentColor"
160 class="size-6"
161 >
162 <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
163 </svg>
164 </Button>
165 </Popover>
166
167 <Button
168 size="iconLg"
169 variant="ghost"
170 class="backdrop-blur-none"
171 onclick={() => {
172 imageInputRef?.click();
173 }}
174 >
175 <svg
176 xmlns="http://www.w3.org/2000/svg"
177 fill="none"
178 viewBox="0 0 24 24"
179 stroke-width="2"
180 stroke="currentColor"
181 >
182 <path
183 stroke-linecap="round"
184 stroke-linejoin="round"
185 d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"
186 />
187 </svg>
188 </Button>
189
190 {#if dev}
191 <Button
192 size="iconLg"
193 variant="ghost"
194 class="backdrop-blur-none"
195 onclick={() => {
196 videoInputRef?.click();
197 }}
198 >
199 <svg
200 xmlns="http://www.w3.org/2000/svg"
201 fill="none"
202 viewBox="0 0 24 24"
203 stroke-width="1.5"
204 stroke="currentColor"
205 >
206 <path
207 stroke-linecap="round"
208 stroke-linejoin="round"
209 d="m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z"
210 />
211 </svg>
212 </Button>
213 {/if}
214
215 <Button size="iconLg" variant="ghost" class="backdrop-blur-none" popovertarget="mobile-menu">
216 <svg
217 xmlns="http://www.w3.org/2000/svg"
218 fill="none"
219 viewBox="0 0 24 24"
220 stroke-width="1.5"
221 stroke="currentColor"
222 >
223 <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
224 </svg>
225 </Button>
226 </div>
227 <div class="flex items-center gap-2">
228 <Button
229 size="iconLg"
230 variant="ghost"
231 class="backdrop-blur-none"
232 onclick={() => {
233 showSettings = true;
234 }}
235 >
236 <svg
237 xmlns="http://www.w3.org/2000/svg"
238 fill="none"
239 viewBox="0 0 24 24"
240 stroke-width="1.5"
241 stroke="currentColor"
242 >
243 <path
244 stroke-linecap="round"
245 stroke-linejoin="round"
246 d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
247 />
248 <path
249 stroke-linecap="round"
250 stroke-linejoin="round"
251 d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
252 />
253 </svg>
254 </Button>
255 <Toggle
256 class="hidden bg-transparent backdrop-blur-none lg:block dark:bg-transparent"
257 bind:pressed={showingMobileView}
258 >
259 <svg
260 xmlns="http://www.w3.org/2000/svg"
261 fill="none"
262 viewBox="0 0 24 24"
263 stroke-width="1.5"
264 stroke="currentColor"
265 class="size-6"
266 >
267 <path
268 stroke-linecap="round"
269 stroke-linejoin="round"
270 d="M10.5 1.5H8.25A2.25 2.25 0 0 0 6 3.75v16.5a2.25 2.25 0 0 0 2.25 2.25h7.5A2.25 2.25 0 0 0 18 20.25V3.75a2.25 2.25 0 0 0-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3"
271 />
272 </svg>
273 </Toggle>
274 <Button
275 disabled={isSaving}
276 onclick={async () => {
277 save();
278 }}>{isSaving ? 'Saving...' : 'Save'}</Button
279 >
280 </div>
281 </Navbar>
282{/if}