mirror of
https://github.com/ankitects/anki.git
synced 2025-11-11 07:07:13 -05:00
Align tagenter tagdelete and tagunique
This commit is contained in:
parent
754e49f9b8
commit
96325a4910
2 changed files with 134 additions and 78 deletions
|
|
@ -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,16 +29,26 @@ 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;
|
|
||||||
|
function setActiveAfterBlur(value: number): void {
|
||||||
|
if (activeAfterBlur === null) {
|
||||||
|
activeAfterBlur = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof activeAfterBlur === "number") {
|
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;
|
active = activeAfterBlur;
|
||||||
activeAfterBlur = null;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTagAt(index: number): void {
|
return true;
|
||||||
if (checkIfContainsNameAt(index)) {
|
}
|
||||||
deleteTagAt(index);
|
|
||||||
insertEmptyTagAt(index);
|
function enterTag(tag: TagType, index: number): void {
|
||||||
} else {
|
|
||||||
appendEmptyTagAt(index);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
function joinWithPreviousTag(index: number): void {
|
return active;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
|
||||||
|
|
@ -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,11 +69,44 @@ 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) {
|
||||||
|
case "Enter":
|
||||||
|
onEnter(event);
|
||||||
|
break;
|
||||||
|
case "Space":
|
||||||
|
// TODO
|
||||||
name += "::";
|
name += "::";
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
} else if (event.code === "ArrowLeft" && caretAtStart()) {
|
|
||||||
|
case "Backspace":
|
||||||
|
onBackspace(event);
|
||||||
|
break;
|
||||||
|
case "Delete":
|
||||||
|
onDelete(event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ArrowLeft":
|
||||||
|
if (!caretAtStart()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
normalize();
|
normalize();
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
joinWithPreviousTag(event);
|
joinWithPreviousTag(event);
|
||||||
|
|
@ -86,21 +114,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
dispatch("tagmoveprevious");
|
dispatch("tagmoveprevious");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
} else if (event.code === "ArrowRight" && caretAtEnd()) {
|
break;
|
||||||
|
|
||||||
|
case "ArrowRight":
|
||||||
|
if (!caretAtEnd()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (isEmpty()) {
|
if (isEmpty()) {
|
||||||
joinWithNextTag(event);
|
joinWithNextTag(event);
|
||||||
} else {
|
} else {
|
||||||
dispatch("tagmovenext");
|
dispatch("tagmovenext");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
} else if (event.code === "Backspace") {
|
|
||||||
onBackspace(event);
|
|
||||||
} else if (event.code === "Delete") {
|
|
||||||
onDelete(event);
|
|
||||||
} else if (event.code === "Enter") {
|
|
||||||
/* onAccept(); */
|
|
||||||
input.blur();
|
|
||||||
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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue