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
13 showingMobileView = $bindable(),
14 isSaving = $bindable(),
15
16 save,
17
18 handleImageInputChange,
19 handleVideoInputChange
20 }: {
21 data: WebsiteData;
22 linkValue: string;
23 newCard: (type: string) => void;
24 addLink: (url: string) => void;
25
26 showingMobileView: boolean;
27
28 isSaving: boolean;
29
30 save: () => Promise<void>;
31
32 handleImageInputChange: (evt: Event) => void;
33 handleVideoInputChange: (evt: Event) => void;
34 } = $props();
35
36 let linkPopoverOpen = $state(false);
37
38 let imageInputRef: HTMLInputElement | undefined = $state();
39 let videoInputRef: HTMLInputElement | undefined = $state();
40
41 let shareModalOpen = $state(false);
42</script>
43
44<input
45 type="file"
46 accept="image/*"
47 onchange={handleImageInputChange}
48 class="hidden"
49 multiple
50 bind:this={imageInputRef}
51/>
52
53<input
54 type="file"
55 accept="video/*"
56 onchange={handleVideoInputChange}
57 class="hidden"
58 multiple
59 bind:this={videoInputRef}
60/>
61
62<Modal bind:open={shareModalOpen}></Modal>
63
64{#if dev || (user.isLoggedIn && user.profile?.did === data.did)}
65 <Navbar
66 class={[
67 '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',
68 !dev ? 'hidden' : ''
69 ]}
70 >
71 <div class="flex items-center gap-2">
72 <Button
73 size="iconLg"
74 variant="ghost"
75 class="backdrop-blur-none"
76 onclick={() => {
77 newCard('section');
78 }}
79 >
80 <svg
81 xmlns="http://www.w3.org/2000/svg"
82 viewBox="0 0 24 24"
83 fill="none"
84 stroke="currentColor"
85 stroke-width="2"
86 stroke-linecap="round"
87 stroke-linejoin="round"
88 ><path d="M6 12h12" /><path d="M6 20V4" /><path d="M18 20V4" /></svg
89 >
90 </Button>
91
92 <Button
93 size="iconLg"
94 variant="ghost"
95 class="backdrop-blur-none"
96 onclick={() => {
97 newCard('text');
98 }}
99 >
100 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
101 ><path
102 fill="none"
103 stroke="currentColor"
104 stroke-linecap="round"
105 stroke-linejoin="round"
106 stroke-width="2"
107 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"
108 /></svg
109 >
110 </Button>
111
112 <Popover sideOffset={16} bind:open={linkPopoverOpen} class="bg-base-100 dark:bg-base-900">
113 {#snippet child({ props })}
114 <Button
115 size="iconLg"
116 variant="ghost"
117 class="backdrop-blur-none"
118 onclick={() => {
119 newCard('link');
120 }}
121 {...props}
122 >
123 <svg
124 xmlns="http://www.w3.org/2000/svg"
125 fill="none"
126 viewBox="-2 -2 28 28"
127 stroke-width="2"
128 stroke="currentColor"
129 >
130 <path
131 stroke-linecap="round"
132 stroke-linejoin="round"
133 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"
134 />
135 </svg>
136 </Button>
137 {/snippet}
138 <Input
139 spellcheck={false}
140 type="url"
141 bind:value={linkValue}
142 onkeydown={(event) => {
143 if (event.code === 'Enter') {
144 addLink(linkValue);
145 event.preventDefault();
146 }
147 }}
148 placeholder="Enter link"
149 />
150 <Button onclick={() => addLink(linkValue)} size="icon"
151 ><svg
152 xmlns="http://www.w3.org/2000/svg"
153 fill="none"
154 viewBox="0 0 24 24"
155 stroke-width="2"
156 stroke="currentColor"
157 class="size-6"
158 >
159 <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
160 </svg>
161 </Button>
162 </Popover>
163
164 <Button
165 size="iconLg"
166 variant="ghost"
167 class="backdrop-blur-none"
168 onclick={() => {
169 imageInputRef?.click();
170 }}
171 >
172 <svg
173 xmlns="http://www.w3.org/2000/svg"
174 fill="none"
175 viewBox="0 0 24 24"
176 stroke-width="2"
177 stroke="currentColor"
178 >
179 <path
180 stroke-linecap="round"
181 stroke-linejoin="round"
182 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"
183 />
184 </svg>
185 </Button>
186
187 {#if dev}
188 <Button
189 size="iconLg"
190 variant="ghost"
191 class="backdrop-blur-none"
192 onclick={() => {
193 videoInputRef?.click();
194 }}
195 >
196 <svg
197 xmlns="http://www.w3.org/2000/svg"
198 fill="none"
199 viewBox="0 0 24 24"
200 stroke-width="1.5"
201 stroke="currentColor"
202 >
203 <path
204 stroke-linecap="round"
205 stroke-linejoin="round"
206 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"
207 />
208 </svg>
209 </Button>
210 {/if}
211
212 <Button size="iconLg" variant="ghost" class="backdrop-blur-none" popovertarget="mobile-menu">
213 <svg
214 xmlns="http://www.w3.org/2000/svg"
215 fill="none"
216 viewBox="0 0 24 24"
217 stroke-width="1.5"
218 stroke="currentColor"
219 >
220 <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
221 </svg>
222 </Button>
223 </div>
224 <div class="flex items-center gap-2">
225 <Toggle
226 class="hidden bg-transparent backdrop-blur-none lg:block dark:bg-transparent"
227 bind:pressed={showingMobileView}
228 >
229 <svg
230 xmlns="http://www.w3.org/2000/svg"
231 fill="none"
232 viewBox="0 0 24 24"
233 stroke-width="1.5"
234 stroke="currentColor"
235 class="size-6"
236 >
237 <path
238 stroke-linecap="round"
239 stroke-linejoin="round"
240 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"
241 />
242 </svg>
243 </Toggle>
244 <Button
245 disabled={isSaving}
246 onclick={async () => {
247 save();
248 }}>{isSaving ? 'Saving...' : 'Save'}</Button
249 >
250 </div>
251 </Navbar>
252{/if}