Improve space behavior

This commit is contained in:
Henrik Giesel 2021-07-06 23:37:49 +02:00
parent ab429ec413
commit 6ccf75a077
3 changed files with 91 additions and 39 deletions

View file

@ -117,9 +117,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const splitOff = activeName.slice(end);
activeName = current;
// await tag to update its name, so it can normalize correctly
await tick();
appendTagAndFocusAt(index, splitOff);
active = null;
await tick();
if (index === active) {
@ -273,7 +275,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
}
function onKeyup(_event: KeyboardEvent): void {
function onKeyup(): void {
if (activeName.length === 0) {
autocomplete.hide();
}

View file

@ -15,6 +15,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const dispatch = createEventDispatcher();
function isCollapsed(): boolean {
return input.selectionStart === input.selectionEnd;
}
function caretAtStart(): boolean {
return input.selectionStart === 0 && input.selectionEnd === 0;
}
@ -34,10 +38,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return name.length === 0;
}
function normalize(): void {
name = normalizeTagname(name);
}
async function joinWithPreviousTag(event: Event): Promise<void> {
const length = input.value.length;
dispatch("tagjoinprevious");
@ -48,12 +48,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
event.preventDefault();
}
async function onBackspace(event: KeyboardEvent): Promise<void> {
async function maybeDeleteDelimiter(event: Event, position: number): Promise<void> {
if (position > name.length) {
return;
}
const nameUptoCaret = name.slice(0, position);
if (nameUptoCaret.endsWith("::")) {
name = name.slice(0, position - 2) + name.slice(position, name.length);
await tick();
setPosition(position - 2);
event.preventDefault();
}
}
function onBackspace(event: KeyboardEvent): void {
if (caretAtStart()) {
joinWithPreviousTag(event);
} else if (name.endsWith("::")) {
name = name.slice(0, -2);
event.preventDefault();
} else {
maybeDeleteDelimiter(event, input.selectionStart!);
}
}
@ -67,19 +82,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
event.preventDefault();
}
async function onDelete(event: KeyboardEvent): Promise<void> {
function onDelete(event: KeyboardEvent): void {
if (caretAtEnd()) {
joinWithNextTag(event);
} else if (name.endsWith("::")) {
name = name.slice(0, -2);
event.preventDefault();
} else {
maybeDeleteDelimiter(event, input.selectionStart! + 2);
}
}
function onBlur(event: Event): void {
event.preventDefault();
function onBlur(): void {
name = normalizeTagname(name);
normalize();
if (name.length === 0) {
dispatch("tagdelete");
}
@ -88,54 +101,91 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
function onEnter(event: Event): void {
event.preventDefault();
dispatch("tagsplit", { start: input.selectionStart, end: input.selectionEnd });
event.preventDefault();
}
async function onDelimiter(event: Event, single: boolean = false): Promise<void> {
event.preventDefault();
const positionStart = input.selectionStart!;
const positionEnd = input.selectionEnd!;
const before = name.slice(0, positionStart);
if (before.endsWith("::")) {
event.stopPropagation();
return;
}
name = `${before}${single ? ":" : "::"}${name.slice(positionEnd, name.length)}`;
await tick();
setPosition(positionStart + (single ? 1 : 2));
}
function maybeMovePastDelimiter(event: Event, forwards: boolean): void {
const position = input.selectionStart!;
const before = name.slice(0, position);
const after = name.slice(position, name.length);
if (!forwards && before.endsWith("::")) {
setPosition(position - 2);
event.preventDefault();
} else if (forwards && after.startsWith("::")) {
setPosition(position + 2);
event.preventDefault();
}
}
function onKeydown(event: KeyboardEvent): void {
switch (event.code) {
case "Enter":
onEnter(event);
break;
case "Space":
// TODO
name += "::";
event.preventDefault();
onDelimiter(event);
break;
case "Backspace":
if (isCollapsed()) {
onBackspace(event);
}
break;
case "Delete":
if (isCollapsed()) {
onDelete(event);
}
break;
case "ArrowLeft":
if (!caretAtStart()) {
break;
}
normalize();
if (isEmpty()) {
joinWithPreviousTag(event);
} else {
event.preventDefault();
} else if (caretAtStart()) {
dispatch("tagmoveprevious");
event.preventDefault();
} else if (isCollapsed()) {
maybeMovePastDelimiter(event, false);
}
break;
case "ArrowRight":
if (!caretAtEnd()) {
break;
}
if (isEmpty()) {
joinWithNextTag(event);
} else {
event.preventDefault();
} else if (caretAtEnd()) {
dispatch("tagmovenext");
event.preventDefault();
} else if (isCollapsed()) {
maybeMovePastDelimiter(event, true);
}
break;
}
if (event.key === ":") {
onDelimiter(event, true);
}
}
function onPaste(event: ClipboardEvent): void {
@ -175,7 +225,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tabindex="-1"
size="1"
on:focus
on:blur={onBlur}
on:blur|preventDefault={onBlur}
on:keydown={onKeydown}
on:keydown
on:keyup

View file

@ -5,10 +5,10 @@ export function normalizeTagname(tagname: string): string {
let trimmed = tagname.trim();
while (true) {
if (trimmed.startsWith("::")) {
trimmed = trimmed.slice(2).trimStart();
} else if (trimmed.endsWith("::")) {
trimmed = trimmed.slice(0, -2).trimEnd();
if (trimmed.startsWith(":")) {
trimmed = trimmed.slice(1).trimStart();
} else if (trimmed.endsWith(":")) {
trimmed = trimmed.slice(0, -1).trimEnd();
} else {
return trimmed;
}