your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import { cn, Toggle, toggleVariants, Tooltip } from '@foxui/core';
3 import type { Editor } from '@tiptap/core';
4 import Select from './Select.svelte';
5 import type { RichTextTypes } from '.';
6
7 let {
8 editor,
9 isBold,
10 isImage,
11 isItalic,
12 isUnderline,
13 isStrikethrough,
14 isLink,
15 clickedLink,
16 selectedType = $bindable('paragraph'),
17 ref = $bindable(null),
18 processImageFile,
19 switchTo
20 }: {
21 editor: Editor | null;
22 isBold: boolean;
23 isImage: boolean;
24 isItalic: boolean;
25 isUnderline: boolean;
26 isStrikethrough: boolean;
27 isLink: boolean;
28 clickedLink: () => void;
29 selectedType: RichTextTypes;
30 ref: HTMLElement | null;
31 processImageFile: (file: File, input: HTMLInputElement) => void;
32 switchTo: (value: RichTextTypes) => void;
33 } = $props();
34
35 function handleFileProcess(event: Event) {
36 const input = event.target as HTMLInputElement;
37 if (!input.files?.length) return;
38 const file = input.files[0];
39 if (!file || !file.type.startsWith('image/')) return;
40 processImageFile(file, input);
41 }
42
43 let fileInput = $state<HTMLInputElement | null>(null);
44</script>
45
46<div
47 bind:this={ref}
48 style="visibility: hidden; opacity: 0;"
49 class="bg-base-50 dark:bg-base-900 border-base-500/20 dark:border-base-700/20 relative w-fit rounded-2xl border px-1 py-1 shadow-lg backdrop-blur-sm"
50>
51 <Select
52 onValueChange={(value) => {
53 switchTo(value as RichTextTypes);
54 }}
55 type="single"
56 items={[
57 { value: 'paragraph', label: 'Text' },
58 { value: 'heading-1', label: 'Heading 1' },
59 { value: 'heading-2', label: 'Heading 2' },
60 { value: 'heading-3', label: 'Heading 3' },
61 { value: 'blockquote', label: 'Blockquote' },
62 { value: 'code', label: 'Code Block' },
63 { value: 'bullet-list', label: 'Bullet List' },
64 { value: 'ordered-list', label: 'Ordered List' }
65 ]}
66 bind:value={selectedType}
67 />
68 <!-- <Tooltip withContext text="Bold" delayDuration={0}>
69 {#snippet child({ props })} -->
70 <Toggle
71 size="sm"
72 onclick={() => editor?.chain().focus().toggleBold().run()}
73 bind:pressed={() => isBold, (bold) => {}}
74 >
75 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
76 <path
77 fill-rule="evenodd"
78 d="M5.246 3.744a.75.75 0 0 1 .75-.75h7.125a4.875 4.875 0 0 1 3.346 8.422 5.25 5.25 0 0 1-2.97 9.58h-7.5a.75.75 0 0 1-.75-.75V3.744Zm7.125 6.75a2.625 2.625 0 0 0 0-5.25H8.246v5.25h4.125Zm-4.125 2.251v6h4.5a3 3 0 0 0 0-6h-4.5Z"
79 clip-rule="evenodd"
80 />
81 </svg>
82
83 <span class="sr-only">Bold</span>
84 </Toggle>
85 <!-- {/snippet}
86 </Tooltip> -->
87 <Toggle
88 size="sm"
89 onclick={() => editor?.chain().focus().toggleItalic().run()}
90 bind:pressed={() => isItalic, (italic) => {}}
91 >
92 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
93 <path
94 fill-rule="evenodd"
95 d="M10.497 3.744a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-3.275l-5.357 15.002h2.632a.75.75 0 1 1 0 1.5h-7.5a.75.75 0 1 1 0-1.5h3.275l5.357-15.002h-2.632a.75.75 0 0 1-.75-.75Z"
96 clip-rule="evenodd"
97 />
98 </svg>
99
100 <span class="sr-only">Italic</span>
101 </Toggle>
102
103 <Toggle
104 size="sm"
105 onclick={() => editor?.chain().focus().toggleUnderline().run()}
106 bind:pressed={() => isUnderline, (underline) => {}}
107 >
108 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
109 <path
110 fill-rule="evenodd"
111 d="M5.995 2.994a.75.75 0 0 1 .75.75v7.5a5.25 5.25 0 1 0 10.5 0v-7.5a.75.75 0 0 1 1.5 0v7.5a6.75 6.75 0 1 1-13.5 0v-7.5a.75.75 0 0 1 .75-.75Zm-3 17.252a.75.75 0 0 1 .75-.75h16.5a.75.75 0 0 1 0 1.5h-16.5a.75.75 0 0 1-.75-.75Z"
112 clip-rule="evenodd"
113 />
114 </svg>
115
116 <span class="sr-only">Underline</span>
117 </Toggle>
118
119 <Toggle
120 size="sm"
121 onclick={() => editor?.chain().focus().toggleStrike().run()}
122 bind:pressed={() => isStrikethrough, (strikethrough) => {}}
123 >
124 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
125 <path
126 fill-rule="evenodd"
127 d="M9.657 4.728c-1.086.385-1.766 1.057-1.979 1.85-.214.8.046 1.733.81 2.616.746.862 1.93 1.612 3.388 2.003.07.019.14.037.21.053h8.163a.75.75 0 0 1 0 1.5h-8.24a.66.66 0 0 1-.02 0H3.75a.75.75 0 0 1 0-1.5h4.78a7.108 7.108 0 0 1-1.175-1.074C6.372 9.042 5.849 7.61 6.229 6.19c.377-1.408 1.528-2.38 2.927-2.876 1.402-.497 3.127-.55 4.855-.086A8.937 8.937 0 0 1 16.94 4.6a.75.75 0 0 1-.881 1.215 7.437 7.437 0 0 0-2.436-1.14c-1.473-.394-2.885-.331-3.966.052Zm6.533 9.632a.75.75 0 0 1 1.03.25c.592.974.846 2.094.55 3.2-.378 1.408-1.529 2.38-2.927 2.876-1.402.497-3.127.55-4.855.087-1.712-.46-3.168-1.354-4.134-2.47a.75.75 0 0 1 1.134-.982c.746.862 1.93 1.612 3.388 2.003 1.473.394 2.884.331 3.966-.052 1.085-.384 1.766-1.056 1.978-1.85.169-.628.046-1.33-.381-2.032a.75.75 0 0 1 .25-1.03Z"
128 clip-rule="evenodd"
129 />
130 </svg>
131
132 <span class="sr-only">Strikethrough</span>
133 </Toggle>
134
135 <Toggle
136 size="sm"
137 onclick={() => {
138 clickedLink();
139 }}
140 bind:pressed={() => isLink, (link) => {}}
141 >
142 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-6">
143 <path
144 fill-rule="evenodd"
145 d="M19.902 4.098a3.75 3.75 0 0 0-5.304 0l-4.5 4.5a3.75 3.75 0 0 0 1.035 6.037.75.75 0 0 1-.646 1.353 5.25 5.25 0 0 1-1.449-8.45l4.5-4.5a5.25 5.25 0 1 1 7.424 7.424l-1.757 1.757a.75.75 0 1 1-1.06-1.06l1.757-1.757a3.75 3.75 0 0 0 0-5.304Zm-7.389 4.267a.75.75 0 0 1 1-.353 5.25 5.25 0 0 1 1.449 8.45l-4.5 4.5a5.25 5.25 0 1 1-7.424-7.424l1.757-1.757a.75.75 0 1 1 1.06 1.06l-1.757 1.757a3.75 3.75 0 1 0 5.304 5.304l4.5-4.5a3.75 3.75 0 0 0-1.035-6.037.75.75 0 0 1-.354-1Z"
146 clip-rule="evenodd"
147 />
148 </svg>
149
150 <span class="sr-only">Link</span>
151 </Toggle>
152
153 <!-- <Toggle
154 size="sm"
155 onclick={() => {
156 fileInput?.click();
157 }}
158 bind:pressed={() => isImage, (image) => {}}
159 >
160 <svg
161 xmlns="http://www.w3.org/2000/svg"
162 viewBox="0 0 24 24"
163 fill="none"
164 stroke="currentColor"
165 stroke-width="2"
166 stroke-linecap="round"
167 stroke-linejoin="round"
168 ><rect width="18" height="18" x="3" y="3" rx="2" ry="2" /><circle cx="9" cy="9" r="2" /><path
169 d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"
170 /></svg
171 >
172 </Toggle> -->
173
174 <input
175 type="file"
176 accept="image/*"
177 class="hidden"
178 onchange={handleFileProcess}
179 tabindex="-1"
180 bind:this={fileInput}
181 />
182</div>