tangled
alpha
login
or
join now
moth11.net
/
xcvr
2
fork
atom
frontend for xcvr appview
2
fork
atom
overview
issues
pulls
pipelines
some emoji list stuff
moth11.net
6 months ago
9d62a529
b10e3bd2
+105
-9
1 changed file
expand all
collapse all
unified
split
src
lib
components
AutoGrowTextArea.svelte
+105
-9
src/lib/components/AutoGrowTextArea.svelte
···
2
import { onMount } from "svelte";
3
import Fuse from "fuse.js";
4
import emojis from "$lib/keyword-emojis.json";
0
5
6
interface Props {
7
onBeforeInput?: (event: InputEvent) => void;
···
24
color,
25
fs,
26
}: Props = $props();
27
-
let curemoji: [string, number, number] | null = $state(null);
0
0
0
0
0
0
0
28
29
let inputEl: HTMLTextAreaElement;
0
30
function adjust(event: Event) {
31
onInputEl?.(inputEl);
32
-
curemoji = checkAndSearch();
0
0
0
33
}
34
35
function bi(event: InputEvent) {
···
79
return [res, colonPos, selectionStart];
80
}
81
const fuseOptions = {
0
82
keys: ["keywords"],
83
};
84
const fuse = new Fuse(emojis, fuseOptions);
85
-
function searchEmoji(s: string): string | null {
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
86
const results = fuse.search(s);
87
if (results.length === 0) {
88
return null;
89
}
90
-
return results[0].item.emoji;
91
}
92
-
function checkAndSearch(): [string, number, number] | null {
93
const query = checkEmoji(inputEl.selectionStart, inputEl.selectionEnd);
94
if (query === null) {
95
return null;
···
99
return [emoji, query[1], query[2]];
100
}
101
function emojifier(e: KeyboardEvent) {
102
-
if (curemoji === null) {
0
0
0
0
103
return;
104
}
105
switch (e.key) {
···
107
e.preventDefault();
108
e.stopPropagation();
109
inputEl.value =
110
-
inputEl.value.slice(0, curemoji[1]) +
111
-
curemoji[0] +
112
-
inputEl.value.slice(curemoji[2]);
113
onInputEl?.(inputEl);
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
114
return;
115
}
116
}
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
117
</script>
118
119
<div class="autogrowwrapper">
···
130
style:font-size={fs ?? "1rem"}
131
{placeholder}
132
></textarea>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
133
</div>
134
135
<style>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
136
textarea {
137
width: 100%;
138
font: inherit;
···
2
import { onMount } from "svelte";
3
import Fuse from "fuse.js";
4
import emojis from "$lib/keyword-emojis.json";
5
+
import { computePosition, flip } from "@floating-ui/dom";
6
7
interface Props {
8
onBeforeInput?: (event: InputEvent) => void;
···
25
color,
26
fs,
27
}: Props = $props();
28
+
let curemojiresults: [EmojiSearchResults, number, number] | null =
29
+
$state(null);
30
+
let curemojinumber: null | number = $state(null);
31
+
let curemoji: null | string = $derived(
32
+
curemojiresults && curemojinumber
33
+
? curemojiresults[0][curemojinumber]
34
+
: null,
35
+
);
36
37
let inputEl: HTMLTextAreaElement;
38
+
let emojilist: HTMLElement | undefined = $state();
39
function adjust(event: Event) {
40
onInputEl?.(inputEl);
41
+
curemojiresults = checkAndSearch();
42
+
if (curemojiresults !== null) {
43
+
curemojinumber = 0;
44
+
}
45
}
46
47
function bi(event: InputEvent) {
···
91
return [res, colonPos, selectionStart];
92
}
93
const fuseOptions = {
94
+
includeMatches: true,
95
keys: ["keywords"],
96
};
97
const fuse = new Fuse(emojis, fuseOptions);
98
+
type RangeTuple = [number, number];
99
+
100
+
type FuseResultMatch = {
101
+
indices: ReadonlyArray<RangeTuple>;
102
+
key?: string;
103
+
refIndex?: number;
104
+
value?: string;
105
+
};
106
+
type FuseResult<T> = {
107
+
item: T;
108
+
refIndex: number;
109
+
score?: number;
110
+
matches?: ReadonlyArray<FuseResultMatch>;
111
+
};
112
+
type EmojiSearchResults = FuseResult<{
113
+
emoji: string;
114
+
keywords: string[];
115
+
}>[];
116
+
function searchEmoji(s: string): null | EmojiSearchResults {
117
const results = fuse.search(s);
118
if (results.length === 0) {
119
return null;
120
}
121
+
return results;
122
}
123
+
function checkAndSearch(): [EmojiSearchResults, number, number] | null {
124
const query = checkEmoji(inputEl.selectionStart, inputEl.selectionEnd);
125
if (query === null) {
126
return null;
···
130
return [emoji, query[1], query[2]];
131
}
132
function emojifier(e: KeyboardEvent) {
133
+
if (
134
+
curemojiresults === null ||
135
+
curemojinumber === null ||
136
+
curemoji === null
137
+
) {
138
return;
139
}
140
switch (e.key) {
···
142
e.preventDefault();
143
e.stopPropagation();
144
inputEl.value =
145
+
inputEl.value.slice(0, curemojiresults[1]) +
146
+
curemoji +
147
+
inputEl.value.slice(curemojiresults[2]);
148
onInputEl?.(inputEl);
149
+
curemoji = null;
150
+
curemojiresults = null;
151
+
curemojinumber = null;
152
+
return;
153
+
case "ArrowDown":
154
+
e.preventDefault();
155
+
e.stopPropagation();
156
+
curemojinumber = curemojinumber + 1;
157
+
if (curemojinumber > curemojiresults[0].length - 1)
158
+
curemojinumber = 0;
159
+
return;
160
+
case "ArrowUp":
161
+
e.preventDefault();
162
+
e.stopPropagation();
163
+
curemojinumber = curemojinumber - 1;
164
+
if (curemojinumber < 0)
165
+
curemojinumber = curemojiresults[0].length - 1;
166
return;
167
}
168
}
169
+
$effect(() => {
170
+
if (inputEl && emojilist) {
171
+
computePosition(inputEl, emojilist, {
172
+
placement: "top",
173
+
middleware: [flip()],
174
+
}).then(({ x, y }) => {
175
+
if (emojilist !== undefined) {
176
+
Object.assign(emojilist.style, {
177
+
left: `${x}px`,
178
+
top: `${y}px`,
179
+
});
180
+
}
181
+
});
182
+
}
183
+
});
184
</script>
185
186
<div class="autogrowwrapper">
···
197
style:font-size={fs ?? "1rem"}
198
{placeholder}
199
></textarea>
200
+
{#if curemojiresults !== null && curemojinumber !== null}
201
+
<div bind:this={emojilist} class="emoji-selector">
202
+
{#each curemojiresults[0] as result, idx}
203
+
<div
204
+
class={curemojinumber === idx
205
+
? "selected emoji-result"
206
+
: "emoji-result"}
207
+
>
208
+
{result.item.emoji}
209
+
{#if result.matches && result.matches[0] !== null}{result
210
+
.matches[0].value}{/if}
211
+
</div>
212
+
{/each}
213
+
</div>
214
+
{/if}
215
</div>
216
217
<style>
218
+
.emoji-selector {
219
+
width: max-content;
220
+
position: absolute;
221
+
top: 0;
222
+
left: 0;
223
+
}
224
+
.selected.emoji-result {
225
+
background: var(--fg);
226
+
color: var(--bg);
227
+
}
228
+
.emoji-result {
229
+
color: var(--fg);
230
+
background: var(--bg);
231
+
}
232
textarea {
233
width: 100%;
234
font: inherit;