mirror of
https://github.com/Blah-IM/Weblah.git
synced 2025-05-01 08:41:08 +00:00
fix: enter to send
This commit is contained in:
parent
72b962fb77
commit
431f14b35d
7 changed files with 101 additions and 42 deletions
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class={tw(
|
class={tw(
|
||||||
'flex items-center gap-1 rounded-md px-2 py-1.5 shadow-[inset_0_1px_2px_0_rgb(0_0_0/0.05)] ring-1 ring-ss-secondary',
|
'flex items-center gap-1 rounded-md px-2 py-1.5 caret-accent-500 shadow-[inset_0_1px_2px_0_rgb(0_0_0/0.05)] ring-1 ring-ss-secondary',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import type { Delta } from 'typewriter-editor';
|
import type { Delta, Editor } from 'typewriter-editor';
|
||||||
import InputFrame from '$lib/components/InputFrame.svelte';
|
import InputFrame from '$lib/components/InputFrame.svelte';
|
||||||
import { tw } from '$lib/tw';
|
import { tw } from '$lib/tw';
|
||||||
|
|
||||||
export let delta: Delta | null = null;
|
export let delta: Delta | null = null;
|
||||||
export let plainText: string | undefined = undefined;
|
export let plainText: string | undefined = undefined;
|
||||||
|
export let keyboardSubmitMethod: 'enter' | 'shiftEnter' | undefined = undefined;
|
||||||
export let placeholder: string = '';
|
export let placeholder: string = '';
|
||||||
|
export let editor: Editor | undefined;
|
||||||
|
|
||||||
let className = '';
|
let className = '';
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
@ -24,7 +26,15 @@
|
||||||
<p>{placeholder}</p>
|
<p>{placeholder}</p>
|
||||||
</div>
|
</div>
|
||||||
{:then Input}
|
{:then Input}
|
||||||
<svelte:component this={Input} bind:delta bind:plainText {placeholder} on:keydown>
|
<svelte:component
|
||||||
|
this={Input}
|
||||||
|
bind:delta
|
||||||
|
bind:plainText
|
||||||
|
{placeholder}
|
||||||
|
bind:editor
|
||||||
|
{keyboardSubmitMethod}
|
||||||
|
on:keyboardSubmit
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</svelte:component>
|
</svelte:component>
|
||||||
{/await}
|
{/await}
|
||||||
|
|
|
@ -1,11 +1,24 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher } from 'svelte';
|
||||||
import { Delta, Editor, asRoot, h } from 'typewriter-editor';
|
import { Delta, Editor, asRoot, h } from 'typewriter-editor';
|
||||||
|
import { keyboardSubmit } from './keyboardSubmitModule';
|
||||||
|
|
||||||
export let delta: Delta = new Delta();
|
export let delta: Delta = new Delta();
|
||||||
export let plainText: string | undefined = undefined;
|
export let plainText: string | undefined = undefined;
|
||||||
export let placeholder: string = '';
|
export let placeholder: string = '';
|
||||||
|
export let keyboardSubmitMethod: 'enter' | 'shiftEnter' | undefined = undefined;
|
||||||
|
|
||||||
const editor = new Editor();
|
const dispatch = createEventDispatcher<{
|
||||||
|
keyboardSubmit: void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let editor: Editor;
|
||||||
|
|
||||||
|
function initEditor() {
|
||||||
|
const modules = keyboardSubmitMethod
|
||||||
|
? { keyboardSubmit: keyboardSubmit(() => dispatch('keyboardSubmit'), keyboardSubmitMethod) }
|
||||||
|
: undefined;
|
||||||
|
editor = new Editor({ modules });
|
||||||
editor.typeset.formats.add({
|
editor.typeset.formats.add({
|
||||||
name: 'underline',
|
name: 'underline',
|
||||||
selector: 'span[data-weblah-brt=underline]',
|
selector: 'span[data-weblah-brt=underline]',
|
||||||
|
@ -28,6 +41,9 @@
|
||||||
delta = editor.getDelta();
|
delta = editor.getDelta();
|
||||||
if (typeof plainText === 'string') plainText = editor.getText();
|
if (typeof plainText === 'string') plainText = editor.getText();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (keyboardSubmitMethod || typeof keyboardSubmitMethod === 'undefined') initEditor();
|
||||||
|
|
||||||
$: editor.setDelta(delta ?? new Delta());
|
$: editor.setDelta(delta ?? new Delta());
|
||||||
$: if (typeof plainText === 'string' && plainText !== editor.getText()) editor.setText(plainText);
|
$: if (typeof plainText === 'string' && plainText !== editor.getText()) editor.setText(plainText);
|
||||||
|
@ -40,7 +56,6 @@
|
||||||
? 'true'
|
? 'true'
|
||||||
: undefined}
|
: undefined}
|
||||||
data-weblah-placeholder={placeholder}
|
data-weblah-placeholder={placeholder}
|
||||||
on:keydown
|
|
||||||
role="textbox"
|
role="textbox"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
|
|
15
src/lib/components/RichTextInput/keyboardSubmitModule.ts
Normal file
15
src/lib/components/RichTextInput/keyboardSubmitModule.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import type { ModuleInitializer } from 'typewriter-editor';
|
||||||
|
|
||||||
|
export const keyboardSubmit = function keyboardSubmit(
|
||||||
|
onSubmit: () => void,
|
||||||
|
method: 'enter' | 'shiftEnter'
|
||||||
|
): ModuleInitializer {
|
||||||
|
return () => ({
|
||||||
|
commands: {
|
||||||
|
keyboardSubmit: onSubmit
|
||||||
|
},
|
||||||
|
shortcuts: {
|
||||||
|
[method === 'enter' ? 'Enter' : 'Shift+Enter']: 'keyboardSubmit'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -45,17 +45,22 @@ function deltaAttributesToBlahRichTextSpanAttributes(
|
||||||
return isObjectEmpty(blahRichTextSpanAttributes) ? null : blahRichTextSpanAttributes;
|
return isObjectEmpty(blahRichTextSpanAttributes) ? null : blahRichTextSpanAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deltaToBlahRichText(delta: Delta): BlahRichText {
|
export function deltaToBlahRichText(delta: Delta, trim?: boolean = true): BlahRichText {
|
||||||
const spans: BlahRichText = [];
|
const spans: BlahRichText = [];
|
||||||
|
|
||||||
let lastText = '';
|
let lastText = '';
|
||||||
let lastAttributes: BlahRichTextSpanAttributes | null = null;
|
let lastAttributes: BlahRichTextSpanAttributes | null = null;
|
||||||
let canonicalizedLastAttributes: string = 'null';
|
let canonicalizedLastAttributes: string = 'null';
|
||||||
|
|
||||||
function commitSpan() {
|
function commitSpan(trim?: 'start' | 'end'): boolean {
|
||||||
spans.push(lastAttributes === null ? lastText : [lastText, lastAttributes]);
|
const trimmedLastText =
|
||||||
|
trim === 'start' ? lastText.trimStart() : trim === 'end' ? lastText.trimEnd() : lastText;
|
||||||
|
if (trimmedLastText === '') return false;
|
||||||
|
spans.push(lastAttributes === null ? trimmedLastText : [trimmedLastText, lastAttributes]);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isFirstSpan = true;
|
||||||
for (const op of delta.ops) {
|
for (const op of delta.ops) {
|
||||||
// Not sure in what cases op.insert would not be a string, but let's be safe
|
// Not sure in what cases op.insert would not be a string, but let's be safe
|
||||||
if (typeof op.insert !== 'string') continue;
|
if (typeof op.insert !== 'string') continue;
|
||||||
|
@ -68,12 +73,27 @@ export function deltaToBlahRichText(delta: Delta): BlahRichText {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
commitSpan();
|
const commited = commitSpan(trim && isFirstSpan ? 'start' : undefined);
|
||||||
|
if (commited) isFirstSpan = false;
|
||||||
|
|
||||||
lastText = op.insert;
|
lastText = op.insert;
|
||||||
lastAttributes = attributes;
|
lastAttributes = attributes;
|
||||||
canonicalizedLastAttributes = canonicalizedAttributes;
|
canonicalizedLastAttributes = canonicalizedAttributes;
|
||||||
}
|
}
|
||||||
commitSpan();
|
const lastCommited = commitSpan(trim ? 'end' : undefined);
|
||||||
|
if (trim && !lastCommited) {
|
||||||
|
// The last segment is empty, so we need to trim the one before it
|
||||||
|
let lastSpan = spans.pop();
|
||||||
|
if (!lastSpan) return spans;
|
||||||
|
|
||||||
|
if (typeof lastSpan === 'string') {
|
||||||
|
lastSpan = lastSpan.trimEnd();
|
||||||
|
if (lastSpan !== '') spans.push(lastSpan);
|
||||||
|
} else {
|
||||||
|
lastSpan[0] = lastSpan[0].trimEnd();
|
||||||
|
if (lastSpan[0] !== '') spans.push(lastSpan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return spans;
|
return spans;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="relative box-border flex min-h-[calc(3rem+1px)] w-full items-center gap-2 border-b border-ss-secondary bg-sb-primary p-2 shadow-sm"
|
class="relative z-10 box-border flex min-h-[calc(3rem+1px)] w-full items-center gap-2 border-b border-ss-secondary bg-sb-primary p-2 shadow-sm"
|
||||||
>
|
>
|
||||||
<Button href="/" class="rounded-full sm:hidden">
|
<Button href="/" class="rounded-full sm:hidden">
|
||||||
<svg
|
<svg
|
||||||
|
|
|
@ -4,27 +4,24 @@
|
||||||
import Button from '$lib/components/Button.svelte';
|
import Button from '$lib/components/Button.svelte';
|
||||||
import RichTextInput from '$lib/components/RichTextInput.svelte';
|
import RichTextInput from '$lib/components/RichTextInput.svelte';
|
||||||
import { deltaToBlahRichText } from '$lib/richText';
|
import { deltaToBlahRichText } from '$lib/richText';
|
||||||
import type { Delta } from 'typewriter-editor';
|
import type { Delta, Editor } from 'typewriter-editor';
|
||||||
|
|
||||||
export let roomId: string;
|
export let roomId: string;
|
||||||
export let server: BlahChatServerConnection | undefined;
|
export let server: BlahChatServerConnection | undefined;
|
||||||
|
|
||||||
|
let editor: Editor | undefined;
|
||||||
let delta: Delta;
|
let delta: Delta;
|
||||||
let plainText: string = '';
|
let plainText: string = '';
|
||||||
let form: HTMLFormElement | null = null;
|
let form: HTMLFormElement | null = null;
|
||||||
let sendDisabled = false;
|
let sendDisabled = false;
|
||||||
|
|
||||||
function onInputKeydown(event: KeyboardEvent) {
|
function onKeyboardSubmit() {
|
||||||
console.log(event.key, event.shiftKey, event.isComposing, plainText);
|
editor?.select(null);
|
||||||
if (event.key === 'Enter' && !event.shiftKey && !event.isComposing) {
|
|
||||||
event.preventDefault();
|
|
||||||
form?.requestSubmit();
|
form?.requestSubmit();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
if (!server || plainText.trim() === '') return;
|
if (!server || plainText.trim() === '') return;
|
||||||
console.log('submit');
|
|
||||||
|
|
||||||
const brt = deltaToBlahRichText(delta);
|
const brt = deltaToBlahRichText(delta);
|
||||||
sendDisabled = true;
|
sendDisabled = true;
|
||||||
|
@ -44,7 +41,7 @@
|
||||||
plainText = '';
|
plainText = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$: sendDisabled = !!server;
|
$: sendDisabled = !server;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
|
@ -70,11 +67,13 @@
|
||||||
<span class="sr-only">Attach</span>
|
<span class="sr-only">Attach</span>
|
||||||
</Button>
|
</Button>
|
||||||
<RichTextInput
|
<RichTextInput
|
||||||
|
bind:editor
|
||||||
bind:delta
|
bind:delta
|
||||||
bind:plainText
|
bind:plainText
|
||||||
placeholder="Message"
|
placeholder="Message"
|
||||||
class="max-h-40 flex-1"
|
class="max-h-40 flex-1"
|
||||||
on:keydown={onInputKeydown}
|
keyboardSubmitMethod="enter"
|
||||||
|
on:keyboardSubmit={onKeyboardSubmit}
|
||||||
/>
|
/>
|
||||||
<Button class="p-1.5" variant="primary" type="submit" disabled={sendDisabled}>
|
<Button class="p-1.5" variant="primary" type="submit" disabled={sendDisabled}>
|
||||||
<svg
|
<svg
|
||||||
|
|
Loading…
Add table
Reference in a new issue