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