mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Completely decouple Tag from TagInput
This commit is contained in:
parent
ea1e5b5840
commit
52a705e839
4 changed files with 61 additions and 65 deletions
|
@ -5,12 +5,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
<script lang="typescript">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import Badge from "components/Badge.svelte";
|
||||
import TagInput from "./TagInput.svelte";
|
||||
import { deleteIcon } from "./icons";
|
||||
|
||||
export let name: string;
|
||||
export let input: HTMLInputElement;
|
||||
export let active: boolean;
|
||||
export let blink: boolean = false;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
@ -19,53 +16,23 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
setTimeout(() => (blink = false), 300);
|
||||
}
|
||||
|
||||
function checkForActivation(): void {
|
||||
const selection = window.getSelection()!;
|
||||
active = selection.isCollapsed;
|
||||
}
|
||||
|
||||
function deleteTag(event: Event): void {
|
||||
dispatch("tagdelete");
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
function deactivate() {
|
||||
active = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if active}
|
||||
<TagInput
|
||||
bind:name
|
||||
bind:input
|
||||
on:focus
|
||||
on:blur={deactivate}
|
||||
on:blur
|
||||
on:keydown
|
||||
on:tagupdate={deactivate}
|
||||
on:tagupdate
|
||||
on:tagdelete={deactivate}
|
||||
on:tagdelete
|
||||
on:tagadd
|
||||
on:tagjoinprevious
|
||||
on:tagjoinnext
|
||||
on:tagmoveprevious
|
||||
on:tagmovenext
|
||||
on:mount={(event) => event.detail.input.focus()}
|
||||
/>
|
||||
{:else}
|
||||
<button
|
||||
class="d-inline-flex align-items-center tag text-nowrap rounded ps-2 pe-1 me-1"
|
||||
class:blink
|
||||
tabindex="-1"
|
||||
on:click={checkForActivation}
|
||||
<button
|
||||
class="d-inline-flex align-items-center tag text-nowrap rounded ps-2 pe-1 me-1"
|
||||
class:blink
|
||||
tabindex="-1"
|
||||
on:click
|
||||
>
|
||||
<span>{name}</span>
|
||||
<Badge class="delete-icon rounded ms-1 mt-1" on:click={deleteTag}
|
||||
>{@html deleteIcon}</Badge
|
||||
>
|
||||
<span>{name}</span>
|
||||
<Badge class="delete-icon rounded ms-1 mt-1" on:click={deleteTag}
|
||||
>{@html deleteIcon}</Badge
|
||||
>
|
||||
</button>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
$white-translucent: rgba(255, 255, 255, 0.5);
|
||||
|
|
|
@ -8,6 +8,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import StickyBottom from "components/StickyBottom.svelte";
|
||||
import AddTagBadge from "./AddTagBadge.svelte";
|
||||
import Tag from "./Tag.svelte";
|
||||
import TagInput from "./TagInput.svelte";
|
||||
import TagAutocomplete from "./TagAutocomplete.svelte";
|
||||
import ButtonToolbar from "components/ButtonToolbar.svelte";
|
||||
import { attachId, getName } from "./tags";
|
||||
|
@ -27,24 +28,34 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
return index === tags.length - 1;
|
||||
}
|
||||
|
||||
function addEmptyTag(): void {
|
||||
if (tags[tags.length - 1].name.length === 0) {
|
||||
tags[tags.length - 1].active = true;
|
||||
async function addEmptyTag(): Promise<void> {
|
||||
const lastTag = tags[tags.length - 1];
|
||||
if (lastTag.name.length === 0) {
|
||||
lastTag.active = true;
|
||||
return;
|
||||
}
|
||||
|
||||
tags.push(attachId("", true));
|
||||
const idx = tags.push(attachId("", true));
|
||||
tags = tags;
|
||||
|
||||
await tick();
|
||||
tags[idx - 1].input?.focus();
|
||||
}
|
||||
|
||||
function insertEmptyTagAt(index: number): void {
|
||||
async function insertEmptyTagAt(index: number): Promise<void> {
|
||||
tags.splice(index, 0, attachId("", true));
|
||||
tags = tags;
|
||||
|
||||
await tick();
|
||||
tags[index].input?.focus();
|
||||
}
|
||||
|
||||
function appendEmptyTagAt(index: number): void {
|
||||
async function appendEmptyTagAt(index: number): Promise<void> {
|
||||
tags.splice(index + 1, 0, attachId("", true));
|
||||
tags = tags;
|
||||
|
||||
await tick();
|
||||
tags[index].input?.focus();
|
||||
}
|
||||
|
||||
function checkIfContainsNameAt(index: number): boolean {
|
||||
|
@ -65,6 +76,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
deleteTagAt(index);
|
||||
insertEmptyTagAt(index);
|
||||
} else {
|
||||
deactivate(index);
|
||||
appendEmptyTagAt(index);
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +91,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
}
|
||||
|
||||
function deleteTagAt(index: number): void {
|
||||
deactivate(index);
|
||||
tags.splice(index, 1);
|
||||
tags = tags;
|
||||
}
|
||||
|
@ -126,6 +139,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
await tick();
|
||||
nextTag.input?.setSelectionRange(0, 0);
|
||||
}
|
||||
|
||||
function deactivate(index: number): void {
|
||||
tags[index].active = false;
|
||||
}
|
||||
|
||||
function checkForActivation(index: number): void {
|
||||
const selection = window.getSelection()!;
|
||||
tags[index].active = selection.isCollapsed;
|
||||
}
|
||||
</script>
|
||||
|
||||
<StickyBottom>
|
||||
|
@ -140,19 +162,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
let:destroyAutocomplete
|
||||
>
|
||||
{#each tags as tag, index (tag.id)}
|
||||
<Tag
|
||||
bind:name={tag.name}
|
||||
bind:input={tag.input}
|
||||
bind:active={tag.active}
|
||||
bind:blink={tag.blink}
|
||||
on:tagupdate={() => addTagAt(index)}
|
||||
on:tagadd={() => insertTagAt(index)}
|
||||
on:tagdelete={() => deleteTagAt(index)}
|
||||
on:tagjoinprevious={() => joinWithPreviousTag(index)}
|
||||
on:tagjoinnext={() => joinWithNextTag(index)}
|
||||
on:tagmoveprevious={() => moveToPreviousTag(index)}
|
||||
on:tagmovenext={() => moveToNextTag(index)}
|
||||
/>
|
||||
{#if tag.active}
|
||||
<TagInput
|
||||
bind:name={tag.name}
|
||||
bind:input={tag.input}
|
||||
on:blur={() => deactivate(index)}
|
||||
on:tagupdate={() => addTagAt(index)}
|
||||
on:tagadd={() => insertTagAt(index)}
|
||||
on:tagdelete={() => deleteTagAt(index)}
|
||||
on:tagjoinprevious={() => joinWithPreviousTag(index)}
|
||||
on:tagjoinnext={() => joinWithNextTag(index)}
|
||||
on:tagmoveprevious={() => moveToPreviousTag(index)}
|
||||
on:tagmovenext={() => moveToNextTag(index)}
|
||||
/>
|
||||
{:else}
|
||||
<Tag
|
||||
bind:name={tag.name}
|
||||
bind:blink={tag.blink}
|
||||
on:click={() => checkForActivation(index)}
|
||||
on:tagdelete={() => deleteTagAt(index)}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<div
|
||||
|
|
|
@ -145,8 +145,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
name = last;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => dispatch("mount", { input }));
|
||||
</script>
|
||||
|
||||
<label class="ps-2 pe-1" data-value={name}>
|
||||
|
|
|
@ -18,7 +18,7 @@ export function normalizeTagname(tagname: string): string {
|
|||
interface Tag {
|
||||
id: string;
|
||||
name: string;
|
||||
input?: HTMLInputElement;
|
||||
input: HTMLInputElement;
|
||||
active: boolean;
|
||||
blink: boolean;
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ export function attachId(name: string, active = false): Tag {
|
|||
return {
|
||||
id: Math.random().toString(36).substring(2),
|
||||
name,
|
||||
input: null as unknown as HTMLInputElement,
|
||||
active,
|
||||
blink: false,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue