your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import { Alert, Button, Subheading } from '@foxui/core';
3 import type { SettingsComponentProps } from '../types';
4 import { parseVCard, generateVCard, parseVCardName, emptyVCardFields, type VCardFields } from '.';
5
6 let { item = $bindable(), onclose }: SettingsComponentProps = $props();
7
8 let mode: 'easy' | 'expert' = $state('easy');
9 let fields: VCardFields = $state(
10 parseVCard(item.cardData.vcard || '') || { ...emptyVCardFields }
11 );
12
13 function syncFromFields() {
14 item.cardData.vcard = generateVCard(fields);
15 item.cardData.displayName = parseVCardName(item.cardData.vcard);
16 }
17
18 function handleTextarea(e: Event) {
19 const text = (e.target as HTMLTextAreaElement).value;
20 item.cardData.vcard = text;
21 item.cardData.displayName = parseVCardName(text);
22 fields = parseVCard(text);
23 }
24</script>
25
26<div class="flex w-72 flex-col gap-3 p-2">
27 <Subheading>Edit vCard</Subheading>
28
29 <Alert type="info" title="Privacy">
30 <p class="text-xs">All data is public, be aware.</p>
31 </Alert>
32
33 <div class="flex items-center gap-2 text-xs">
34 <button
35 class={[
36 'rounded px-2 py-1',
37 mode === 'easy' ? 'bg-accent-500 text-white' : 'bg-base-200 dark:bg-base-700'
38 ]}
39 onclick={() => (mode = 'easy')}
40 >
41 Easy
42 </button>
43 <button
44 class={[
45 'rounded px-2 py-1',
46 mode === 'expert' ? 'bg-accent-500 text-white' : 'bg-base-200 dark:bg-base-700'
47 ]}
48 onclick={() => (mode = 'expert')}
49 >
50 Expert
51 </button>
52 <a
53 href="https://wikipedia.org/wiki/VCard"
54 target="_blank"
55 class="text-accent-600 dark:text-accent-400 underline">Learn about the vCard format</a
56 >
57 </div>
58
59 {#if mode === 'easy'}
60 <div class="flex flex-col gap-1 text-xs">
61 <div class="grid grid-cols-2 gap-1">
62 <input
63 bind:value={fields.firstName}
64 oninput={syncFromFields}
65 placeholder="First name"
66 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
67 />
68 <input
69 bind:value={fields.lastName}
70 oninput={syncFromFields}
71 placeholder="Last name"
72 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
73 />
74 </div>
75 <input
76 bind:value={fields.org}
77 oninput={syncFromFields}
78 placeholder="Organization"
79 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
80 />
81 <input
82 bind:value={fields.title}
83 oninput={syncFromFields}
84 placeholder="Job title"
85 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
86 />
87 <input
88 bind:value={fields.email}
89 oninput={syncFromFields}
90 placeholder="Email"
91 type="email"
92 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
93 />
94 <input
95 bind:value={fields.bday}
96 oninput={syncFromFields}
97 placeholder="Birthday"
98 type="date"
99 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
100 />
101 <input
102 bind:value={fields.website}
103 oninput={syncFromFields}
104 placeholder="Website"
105 type="url"
106 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
107 />
108 <input
109 bind:value={fields.address}
110 oninput={syncFromFields}
111 placeholder="Address"
112 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 rounded border px-2 py-1"
113 />
114 </div>
115 {:else}
116 <textarea
117 class="bg-base-100 dark:bg-base-800 border-base-300 dark:border-base-700 h-40 w-full resize-none rounded border p-2 font-mono text-xs focus:outline-none"
118 value={item.cardData.vcard || ''}
119 oninput={handleTextarea}
120 placeholder="BEGIN:VCARD
121VERSION:4.0
122FN:John Doe
123END:VCARD"
124 ></textarea>
125 {/if}
126
127 <Button onclick={onclose} size="sm">Done</Button>
128</div>