Decouple Autocomplete from TagInput:

Allows to only have one autocompletion for all tags, rather than every
tag having its own
This commit is contained in:
Henrik Giesel 2021-06-25 15:44:44 +02:00
parent c5b10c1117
commit 1ba6909495
4 changed files with 72 additions and 73 deletions

View file

@ -16,23 +16,30 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<WithShortcut shortcut="Control+Shift+T" let:createShortcut let:shortcutLabel> <WithShortcut shortcut="Control+Shift+T" let:createShortcut let:shortcutLabel>
<Badge <div class="add-icon">
class="add-icon d-flex me-1" <Badge
tooltip={appendInParentheses(tooltip, shortcutLabel)} class="me-1"
on:click tooltip={appendInParentheses(tooltip, shortcutLabel)}
on:mouseenter={() => (theTagIcon = addTagIcon)} on:click
on:mouseleave={() => (theTagIcon = tagIcon)} on:mouseenter={() => (theTagIcon = addTagIcon)}
on:mount={withSpan(createShortcut)}>{@html theTagIcon}</Badge on:mouseleave={() => (theTagIcon = tagIcon)}
> on:mount={withSpan(createShortcut)}>{@html theTagIcon}</Badge
>
</div>
</WithShortcut> </WithShortcut>
<style lang="scss"> <style lang="scss">
:global(.add-icon > svg) { .add-icon {
cursor: pointer; line-height: 1;
opacity: 0.6;
}
:global(.add-icon > svg:hover) { :global(svg) {
opacity: 1; cursor: pointer;
fill: currentColor;
opacity: 0.6;
}
:global(svg:hover) {
opacity: 1;
}
} }
</style> </style>

View file

@ -8,8 +8,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export const suggestions = ["en::idioms", "anki::functionality", "math"]; export const suggestions = ["en::idioms", "anki::functionality", "math"];
const triggerId = "tagLabel" + String(Math.random()).slice(2); let className: string = "";
const triggerClass = "dropdown-toggle"; export { className as class };
let menu: HTMLDivElement; let menu: HTMLDivElement;
@ -33,10 +33,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function updateActiveItem(even: FocusEvent): void {} function updateActiveItem(even: FocusEvent): void {}
</script> </script>
<div bind:this={menu} class="dropup dropdown-reverse"> <div bind:this={menu} class={`dropup dropdown-reverse ${className}`}>
<slot {triggerId} {triggerClass} /> <slot />
<DropdownMenu labelledby={triggerId}> <DropdownMenu>
{#each suggestions as tag} {#each suggestions as tag}
<DropdownItem on:focus={updateActiveItem} on:keydown={switchUpDown} <DropdownItem on:focus={updateActiveItem} on:keydown={switchUpDown}
>{tag}</DropdownItem >{tag}</DropdownItem

View file

@ -6,6 +6,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 TagAutocomplete from "./TagAutocomplete.svelte";
import TagInput from "./TagInput.svelte"; import TagInput from "./TagInput.svelte";
import { attachId, getName } from "./tags"; import { attachId, getName } from "./tags";
@ -86,33 +87,38 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</script> </script>
<StickyBottom> <StickyBottom>
<div class="d-flex flex-wrap"> <div class="font-size-13 row-gap">
<AddTagBadge on:click={focusNewInput} /> <TagAutocomplete class="d-flex flex-wrap align-items-center row-gap">
<AddTagBadge on:click={focusNewInput} />
{#each tags as tag, index (tag.id)} {#each tags as tag, index (tag.id)}
<Tag <Tag
bind:name={tag.name} bind:name={tag.name}
on:tagupdate={() => checkForDuplicateAt(index)} on:tagupdate={() => checkForDuplicateAt(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)}
/>
{/each}
<TagInput
bind:input={newInput}
bind:name={newName}
on:tagupdate={appendTag}
on:tagadd={appendTag}
on:tagjoinprevious={joinWithLastTag}
/> />
{/each} </TagAutocomplete>
<TagInput
bind:input={newInput}
bind:name={newName}
on:tagupdate={appendTag}
on:tagadd={appendTag}
on:tagjoinprevious={joinWithLastTag}
/>
</div> </div>
</StickyBottom> </StickyBottom>
<style lang="scss"> <style lang="scss">
div { .font-size-13 {
font-size: 13px; font-size: 13px;
fill: currentColor; }
.row-gap > :global(.d-flex > *) {
margin-bottom: 2px;
} }
</style> </style>

View file

@ -96,43 +96,29 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
} }
function setTagname({ detail }: CustomEvent): void {
name = detail.name;
}
onMount(() => dispatch("mount", { input })); onMount(() => dispatch("mount", { input }));
</script> </script>
<TagAutocomplete <label
bind:name class="ps-2 pe-1"
let:triggerId data-value={name}
let:triggerClass data-bs-toggle="dropdown"
on:nameChosen={setTagname} aria-expanded="false"
on:accept={onAccept}
> >
<label <input
id={triggerId} bind:this={input}
class={`ps-2 pe-1 ${triggerClass}`} type="text"
data-value={name} tabindex="-1"
data-bs-toggle="dropdown" size="1"
aria-expanded="false" bind:value={name}
data-bs-reference="parent" on:focus={onFocus}
> on:blur={dropdownBlur}
<input on:focusout
bind:this={input} on:keydown={onKeydown}
type="text" on:paste={onPaste}
tabindex="-1" on:click
size="1" />
bind:value={name} </label>
on:focus={onFocus}
on:blur={dropdownBlur}
on:focusout
on:keydown={onKeydown}
on:paste={onPaste}
on:click
/>
</label>
</TagAutocomplete>
<style lang="scss"> <style lang="scss">
label { label {