Align tagenter tagdelete and tagunique

This commit is contained in:
Henrik Giesel 2021-06-27 01:18:27 +02:00
parent 754e49f9b8
commit 96325a4910
2 changed files with 134 additions and 78 deletions

View file

@ -3,7 +3,6 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="typescript"> <script lang="typescript">
import { tick } from "svelte";
import { isApplePlatform } from "lib/platform"; import { isApplePlatform } from "lib/platform";
import StickyBottom from "components/StickyBottom.svelte"; import StickyBottom from "components/StickyBottom.svelte";
import AddTagBadge from "./AddTagBadge.svelte"; import AddTagBadge from "./AddTagBadge.svelte";
@ -11,6 +10,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import TagInput from "./TagInput.svelte"; import TagInput from "./TagInput.svelte";
import TagAutocomplete from "./TagAutocomplete.svelte"; import TagAutocomplete from "./TagAutocomplete.svelte";
import ButtonToolbar from "components/ButtonToolbar.svelte"; import ButtonToolbar from "components/ButtonToolbar.svelte";
import type { Tag as TagType } from "./tags";
import { attachId, getName } from "./tags"; import { attachId, getName } from "./tags";
export let initialNames = ["en::foobar", "test", "def"]; export let initialNames = ["en::foobar", "test", "def"];
@ -18,9 +18,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let size = isApplePlatform() ? 1.6 : 2.0; export let size = isApplePlatform() ? 1.6 : 2.0;
let active: number | null = null;
let activeAfterBlur: number | null = null;
let input: HTMLInputElement; let input: HTMLInputElement;
let tags = initialNames.map(attachId); let tags = initialNames.map(attachId);
@ -32,17 +29,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return index === tags.length - 1; return index === tags.length - 1;
} }
function decideNextActive() { let active: number | null = null;
if (typeof active === "number") { let activeAfterBlur: number | null = null;
active = activeAfterBlur;
}
if (typeof activeAfterBlur === "number") { function setActiveAfterBlur(value: number): void {
active = activeAfterBlur; if (activeAfterBlur === null) {
activeAfterBlur = null; activeAfterBlur = value;
} }
} }
function updateActiveAfterBlur(update: (value: number) => number | null): void {
if (activeAfterBlur !== null) {
activeAfterBlur = update(activeAfterBlur);
}
}
function decideNextActive() {
console.log("dna", active, activeAfterBlur, JSON.stringify(tags));
active = activeAfterBlur;
activeAfterBlur = null;
}
async function addEmptyTag(): Promise<void> { async function addEmptyTag(): Promise<void> {
const lastTag = tags[tags.length - 1]; const lastTag = tags[tags.length - 1];
@ -58,52 +65,69 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function insertEmptyTagAt(index: number): void { function insertEmptyTagAt(index: number): void {
tags.splice(index, 0, attachId("")); tags.splice(index, 0, attachId(""));
tags = tags; tags = tags;
activeAfterBlur = index + 1; setActiveAfterBlur(index);
} }
function appendEmptyTagAt(index: number): void { function appendEmptyTagAt(index: number): void {
tags.splice(index + 1, 0, attachId("")); tags.splice(index + 1, 0, attachId(""));
tags = tags; tags = tags;
activeAfterBlur = index + 1; setActiveAfterBlur(index + 1);
} }
function checkIfContainsNameAt(index: number): boolean { function checkIfUniqueNameAt(index: number): boolean {
const names = tags.map(getName); const names = tags.map(getName);
const newName = names.splice(index, 1, "")[0]; const newName = names.splice(index, 1, "")[0];
const contained = names.indexOf(newName); const contained = names.indexOf(newName);
if (contained >= 0) { if (contained >= 0) {
tags[contained].blink = true; tags[contained].blink = true;
return true; return false;
} }
return false; return true;
} }
function addTagAt(index: number): void { function enterTag(tag: TagType, index: number): void {
if (checkIfContainsNameAt(index)) { appendEmptyTagAt(index);
deleteTagAt(index);
insertEmptyTagAt(index);
} else {
appendEmptyTagAt(index);
}
} }
function insertTagAt(index: number): void { function insertTag(tag: TagType, index: number): void {
const name = tags.map(getName).splice(index, 1)[0]; const name = tags.map(getName).splice(index, 1)[0];
if (!checkIfContainsNameAt(index)) { if (!checkIfUniqueNameAt(index)) {
tags.splice(index, 0, attachId(name)); tags.splice(index, 0, attachId(name));
tags = tags; tags = tags;
} }
} }
function deleteTagAt(index: number): void { function deleteTag(tag: TagType, index: number): void {
tags.splice(index, 1); tags.splice(index, 1);
tags = tags; tags = tags;
active = null;
updateActiveAfterBlur((active: number) => {
if (active === index) {
return null;
} else if (active > index) {
return active - 1;
}
return active;
});
} }
function joinWithPreviousTag(index: number): void { function deleteTagIfNotUnique(tag: TagType, index: number): void {
if (!tags.includes(tag)) {
// already deleted
return;
}
if (!checkIfUniqueNameAt(index)) {
deleteTag(tag, index);
}
}
function joinWithPreviousTag(tag: TagType, index: number): void {
if (isFirst(index)) { if (isFirst(index)) {
return; return;
} }
@ -113,7 +137,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tags = tags; tags = tags;
} }
function joinWithNextTag(index: number): void { function joinWithNextTag(tag: TagType, index: number): void {
if (isLast(index)) { if (isLast(index)) {
return; return;
} }
@ -123,28 +147,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tags = tags; tags = tags;
} }
function moveToPreviousTag(index: number): void { function moveToPreviousTag(tag: TagType, index: number): void {
if (isFirst(index)) { if (isFirst(index)) {
return; return;
} }
console.log("moveprev", index);
active = index - 1; active = null;
activeAfterBlur = index - 1;
} }
async function moveToNextTag(index: number): Promise<void> { async function moveToNextTag(tag: TagType, index: number): Promise<void> {
if (isLast(index)) { if (isLast(index)) {
addEmptyTag(); addEmptyTag();
return; return;
} }
console.log("movenext", index);
active = index + 1;
await tick();
input.setSelectionRange(0, 0);
}
function deactivate(index: number): void {
active = null; active = null;
activeAfterBlur = index + 1;
/* await tick(); */
/* input.setSelectionRange(0, 0); */
} }
async function activate(index: number): Promise<void> { async function activate(index: number): Promise<void> {
@ -176,21 +199,29 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
id={tag.id} id={tag.id}
bind:name={tag.name} bind:name={tag.name}
bind:input bind:input
on:focus={() =>
console.log(
"focused",
tag,
tag.name,
JSON.stringify(tags)
)}
on:blur={decideNextActive} on:blur={decideNextActive}
on:tagupdate={() => addTagAt(index)} on:tagenter={() => enterTag(tag, index)}
on:tagadd={() => insertTagAt(index)} on:tagadd={() => insertTag(tag, index)}
on:tagdelete={() => deleteTagAt(index)} on:tagdelete={() => deleteTag(tag, index)}
on:tagjoinprevious={() => joinWithPreviousTag(index)} on:tagunique={() => deleteTagIfNotUnique(tag, index)}
on:tagjoinnext={() => joinWithNextTag(index)} on:tagjoinprevious={() => joinWithPreviousTag(tag, index)}
on:tagmoveprevious={() => moveToPreviousTag(index)} on:tagjoinnext={() => joinWithNextTag(tag, index)}
on:tagmovenext={() => moveToNextTag(index)} on:tagmoveprevious={() => moveToPreviousTag(tag, index)}
on:tagmovenext={() => moveToNextTag(tag, index)}
/> />
{:else} {:else}
<Tag <Tag
name={tag.name} name={tag.name}
bind:blink={tag.blink} bind:blink={tag.blink}
on:click={() => checkForActivation(index)} on:click={() => checkForActivation(index)}
on:tagdelete={() => deleteTagAt(index)} on:tagdelete={() => deleteTag(tag, index)}
/> />
{/if} {/if}
{/each} {/each}

View file

@ -35,11 +35,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
name = normalizeTagname(name); name = normalizeTagname(name);
} }
function onAccept(): void {
normalize();
dispatch(name.length > 0 ? "tagupdate" : "tagdelete");
}
async function joinWithPreviousTag(event: Event): Promise<void> { async function joinWithPreviousTag(event: Event): Promise<void> {
const length = input.value.length; const length = input.value.length;
dispatch("tagjoinprevious"); dispatch("tagjoinprevious");
@ -74,33 +69,63 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
function onBlur(event: Event): void {
event.preventDefault();
normalize();
if (name.length === 0) {
dispatch("tagdelete");
}
dispatch("tagunique");
}
function onEnter(event: Event): void {
event.preventDefault();
dispatch("tagenter");
input.blur();
}
function onKeydown(event: KeyboardEvent): void { function onKeydown(event: KeyboardEvent): void {
if (event.code === "Space") { switch (event.code) {
name += "::"; case "Enter":
event.preventDefault(); onEnter(event);
} else if (event.code === "ArrowLeft" && caretAtStart()) { break;
normalize(); case "Space":
if (isEmpty()) { // TODO
joinWithPreviousTag(event); name += "::";
} else {
dispatch("tagmoveprevious");
event.preventDefault(); event.preventDefault();
}
} else if (event.code === "ArrowRight" && caretAtEnd()) { case "Backspace":
if (isEmpty()) { onBackspace(event);
joinWithNextTag(event); break;
} else { case "Delete":
dispatch("tagmovenext"); onDelete(event);
event.preventDefault(); break;
}
} else if (event.code === "Backspace") { case "ArrowLeft":
onBackspace(event); if (!caretAtStart()) {
} else if (event.code === "Delete") { break;
onDelete(event); }
} else if (event.code === "Enter") { normalize();
/* onAccept(); */ if (isEmpty()) {
input.blur(); joinWithPreviousTag(event);
event.preventDefault(); } else {
dispatch("tagmoveprevious");
event.preventDefault();
}
break;
case "ArrowRight":
if (!caretAtEnd()) {
break;
}
if (isEmpty()) {
joinWithNextTag(event);
} else {
dispatch("tagmovenext");
event.preventDefault();
}
} }
} }
@ -148,7 +173,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tabindex="-1" tabindex="-1"
size="1" size="1"
on:focus on:focus
on:blur={onAccept} on:blur={onBlur}
on:blur on:blur
on:keydown={onKeydown} on:keydown={onKeydown}
on:keydown on:keydown