Decouple TagInput from TagAutocomplete completely

This commit is contained in:
Henrik Giesel 2021-06-25 18:09:58 +02:00
parent 8d95503189
commit 808d01c4fb
5 changed files with 81 additions and 39 deletions

View file

@ -11,7 +11,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export { className as class }; export { className as class };
export let tooltip: string | undefined = undefined; export let tooltip: string | undefined = undefined;
export let tabbable: boolean = true; export let tabbable: boolean = false;
let buttonRef: HTMLButtonElement; let buttonRef: HTMLButtonElement;
@ -54,7 +54,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
.btn-day { .btn-day {
color: black; color: black;
&:active { &:active,
&.active {
background-color: button.$focus-color; background-color: button.$focus-color;
color: white; color: white;
} }
@ -64,11 +65,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
color: white; color: white;
&:hover, &:hover,
&:focus { &:focus,
&.focus {
@include button.btn-night-base; @include button.btn-night-base;
} }
&:active { &:active,
&.active {
background-color: button.$focus-color; background-color: button.$focus-color;
color: white; color: white;
} }

View file

@ -0,0 +1,51 @@
<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="typescript">
import Dropdown from "bootstrap/js/dist/dropdown";
import { setContext } from "svelte";
import { dropdownKey } from "./contextKeys";
export let disabled = false;
setContext(dropdownKey, {
dropdown: true,
"data-bs-toggle": "dropdown",
"aria-expanded": "false",
});
const menuId = Math.random().toString(36).substring(2);
let dropdown: Dropdown;
function activateDropdown(): void {
if (!disabled) {
dropdown.toggle();
}
}
/* Normally dropdown and trigger are associated with a
/* common ancestor with .dropdown class */
function createDropdown(element: HTMLElement): Dropdown {
/* Prevent focus on menu activation */
const noop = () => {};
Object.defineProperty(element, "focus", { value: noop });
const menu = (element.getRootNode() as Document) /* or shadow root */
.getElementById(menuId);
if (!menu) {
console.log(`Could not find menu "${menuId}" for dropdown menu.`);
} else {
dropdown = new Dropdown(element);
/* Set custom menu without using common element with .dropdown */
(dropdown as any)._menu = menu;
}
return dropdown;
}
</script>
<slot {createDropdown} {activateDropdown} {menuId} />

View file

@ -4,10 +4,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
--> -->
<script lang="typescript"> <script lang="typescript">
import ButtonToolbar from "components/ButtonToolbar.svelte"; import ButtonToolbar from "components/ButtonToolbar.svelte";
import WithDropdownMenu from "components/WithDropdownMenu.svelte";
import DropdownMenu from "components/DropdownMenu.svelte"; import DropdownMenu from "components/DropdownMenu.svelte";
import DropdownItem from "components/DropdownItem.svelte"; import DropdownItem from "components/DropdownItem.svelte";
export const suggestions = ["en::idioms", "anki::functionality", "math"]; export let suggestions: string[];
export let size: number; export let size: number;
let className: string = ""; let className: string = "";
@ -29,22 +30,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
event.preventDefault(); event.preventDefault();
} }
} }
function updateActiveItem(event: FocusEvent): void {
event.preventDefault();
}
</script> </script>
<ButtonToolbar class={`dropup ${className}`} {size}> <ButtonToolbar class={`dropup ${className}`} {size}>
<slot /> <WithDropdownMenu let:menuId let:createDropdown let:activateDropdown>
<slot {createDropdown} {activateDropdown} />
<DropdownMenu class="d-flex flex-column-reverse"> <DropdownMenu id={menuId} class="d-flex flex-column-reverse">
{#each suggestions as tag} {#each suggestions as tag}
<DropdownItem <DropdownItem on:keydown={switchUpDown}>{tag}</DropdownItem>
tabbable={false} {/each}
on:focus={updateActiveItem} </DropdownMenu>
on:keydown={switchUpDown}>{tag}</DropdownItem </WithDropdownMenu>
>
{/each}
</DropdownMenu>
</ButtonToolbar> </ButtonToolbar>

View file

@ -12,6 +12,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { attachId, getName } from "./tags"; import { attachId, getName } from "./tags";
export let initialNames = ["en::foobar", "test", "def"]; export let initialNames = ["en::foobar", "test", "def"];
export let suggestions = ["en::idioms", "anki::functionality", "math"];
export let size = isApplePlatform() ? 1.6 : 2.0; export let size = isApplePlatform() ? 1.6 : 2.0;
let tags = initialNames.map(attachId); let tags = initialNames.map(attachId);
@ -90,7 +92,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<StickyBottom> <StickyBottom>
<div class="row-gap"> <div class="row-gap">
<TagAutocomplete class="d-flex flex-wrap align-items-center" {size}> <TagAutocomplete
class="d-flex flex-wrap align-items-center"
{suggestions}
{size}
let:createDropdown
let:activateDropdown
>
<AddTagBadge on:click={focusNewInput} /> <AddTagBadge on:click={focusNewInput} />
{#each tags as tag, index (tag.id)} {#each tags as tag, index (tag.id)}
@ -107,6 +115,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<TagInput <TagInput
bind:input={newInput} bind:input={newInput}
bind:name={newName} bind:name={newName}
on:focus={(event) => createDropdown(event.currentTarget)}
on:tagupdate={appendTag} on:tagupdate={appendTag}
on:tagadd={appendTag} on:tagadd={appendTag}
on:tagjoinprevious={joinWithLastTag} on:tagjoinprevious={joinWithLastTag}

View file

@ -6,27 +6,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { createEventDispatcher, onMount } from "svelte"; import { createEventDispatcher, onMount } from "svelte";
import { normalizeTagname } from "./tags"; import { normalizeTagname } from "./tags";
import TagAutocomplete from "./TagAutocomplete.svelte";
export let name: string; export let name: string;
export let input: HTMLInputElement; export let input: HTMLInputElement;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
function onFocus(): void {
/* dropdown.show(); */
}
function onAccept(): void { function onAccept(): void {
name = normalizeTagname(name); name = normalizeTagname(name);
dispatch("tagupdate", { name }); dispatch("tagupdate", { name });
} }
function dropdownBlur(): void {
onAccept();
/* dropdown.hide(); */
}
function onBackspace(event: KeyboardEvent) { function onBackspace(event: KeyboardEvent) {
if (input.selectionStart === 0 && input.selectionEnd === 0) { if (input.selectionStart === 0 && input.selectionEnd === 0) {
dispatch("tagjoinprevious"); dispatch("tagjoinprevious");
@ -99,21 +88,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
onMount(() => dispatch("mount", { input })); onMount(() => dispatch("mount", { input }));
</script> </script>
<label <label class="ps-2 pe-1" data-value={name}>
class="ps-2 pe-1"
data-value={name}
data-bs-toggle="dropdown"
aria-expanded="false"
>
<input <input
bind:this={input} bind:this={input}
type="text" type="text"
tabindex="-1" tabindex="-1"
size="1" size="1"
bind:value={name} bind:value={name}
on:focus={onFocus} on:focus
on:blur={dropdownBlur}
on:focusout on:focusout
on:blur={onAccept}
on:keydown={onKeydown} on:keydown={onKeydown}
on:paste={onPaste} on:paste={onPaste}
on:click on:click