mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00
Introduce AutocompleteItem
* needs too much custom styling / behavior to be done with DropdownItem
This commit is contained in:
parent
b2d2cb8715
commit
b93646209a
5 changed files with 147 additions and 67 deletions
|
@ -50,10 +50,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
background: none;
|
background: none;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
|
||||||
|
|
||||||
.btn-day {
|
|
||||||
color: black;
|
|
||||||
|
|
||||||
&:active,
|
&:active,
|
||||||
&.active {
|
&.active {
|
||||||
|
@ -62,19 +58,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-day {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-night {
|
.btn-night {
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus {
|
||||||
&.focus {
|
|
||||||
@include button.btn-night-base;
|
@include button.btn-night-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active,
|
|
||||||
&.active {
|
|
||||||
background-color: button.$focus-color;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
81
ts/editor/AutocompleteItem.svelte
Normal file
81
ts/editor/AutocompleteItem.svelte
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<!--
|
||||||
|
Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
-->
|
||||||
|
<script lang="typescript">
|
||||||
|
import { onMount, createEventDispatcher, getContext } from "svelte";
|
||||||
|
import { nightModeKey } from "components/contextKeys";
|
||||||
|
|
||||||
|
export let id: string | undefined = undefined;
|
||||||
|
let className = "";
|
||||||
|
export { className as class };
|
||||||
|
|
||||||
|
export let selected = false;
|
||||||
|
export let active = false;
|
||||||
|
|
||||||
|
const nightMode = getContext<boolean>(nightModeKey);
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
let buttonRef: HTMLButtonElement;
|
||||||
|
|
||||||
|
onMount(() => dispatch("mount", { button: buttonRef }));
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<button
|
||||||
|
{id}
|
||||||
|
tabindex="-1"
|
||||||
|
bind:this={buttonRef}
|
||||||
|
class={`btn ${className}`}
|
||||||
|
class:btn-day={!nightMode}
|
||||||
|
class:btn-night={nightMode}
|
||||||
|
class:selected
|
||||||
|
class:active
|
||||||
|
on:mouseup
|
||||||
|
on:mouseenter
|
||||||
|
on:mousedown|preventDefault
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@use 'ts/sass/button_mixins' as button;
|
||||||
|
|
||||||
|
button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
font-size: calc(var(--buttons-size) / 2.3);
|
||||||
|
|
||||||
|
background: none;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: button.$focus-color;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset global CSS from buttons.scss */
|
||||||
|
:global(.nightMode) button:hover {
|
||||||
|
background-color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extra specificity bc of global CSS reset above */
|
||||||
|
button.btn-day {
|
||||||
|
color: black;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
border-color: #e9ecef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button.btn-night {
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
@include button.btn-night-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -245,6 +245,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Enter":
|
case "Enter":
|
||||||
|
console.log("choose");
|
||||||
autocomplete.chooseSelected();
|
autocomplete.chooseSelected();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -9,7 +9,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
import WithDropdownMenu from "components/WithDropdownMenu.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 AutocompleteItem from "./AutocompleteItem.svelte";
|
||||||
|
|
||||||
let className: string = "";
|
let className: string = "";
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
@ -26,8 +26,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
function selectNext() {
|
async function selectNext() {
|
||||||
suggestionsPromise.then((suggestions) => {
|
const suggestions = await suggestionsPromise;
|
||||||
|
|
||||||
if (selected === null) {
|
if (selected === null) {
|
||||||
selected = 0;
|
selected = 0;
|
||||||
} else if (selected >= suggestions.length - 1) {
|
} else if (selected >= suggestions.length - 1) {
|
||||||
|
@ -37,11 +38,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectPrevious() {
|
async function selectPrevious() {
|
||||||
suggestionsPromise.then((suggestions) => {
|
const suggestions = await suggestionsPromise;
|
||||||
|
|
||||||
if (selected === null) {
|
if (selected === null) {
|
||||||
selected = suggestions.length - 1;
|
selected = suggestions.length - 1;
|
||||||
} else if (selected === 0) {
|
} else if (selected === 0) {
|
||||||
|
@ -51,7 +52,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseSelected() {
|
function chooseSelected() {
|
||||||
|
@ -63,9 +63,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
dropdown.update();
|
dropdown.update();
|
||||||
dispatch("update");
|
dispatch("update");
|
||||||
|
|
||||||
await tick();
|
const [, suggestions] = await Promise.all([tick(), suggestionsPromise]);
|
||||||
|
|
||||||
suggestionsPromise.then((suggestions) => {
|
|
||||||
if (suggestions.length > 0) {
|
if (suggestions.length > 0) {
|
||||||
dropdown.show();
|
dropdown.show();
|
||||||
// disabled class will tell Bootstrap not to show menu on clicking
|
// disabled class will tell Bootstrap not to show menu on clicking
|
||||||
|
@ -74,7 +73,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
dropdown.hide();
|
dropdown.hide();
|
||||||
target.classList.add("disabled");
|
target.classList.add("disabled");
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const createAutocomplete =
|
const createAutocomplete =
|
||||||
|
@ -97,6 +95,21 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
};
|
};
|
||||||
|
|
||||||
onDestroy(() => dropdown?.dispose());
|
onDestroy(() => dropdown?.dispose());
|
||||||
|
|
||||||
|
function setSelected(index: number): void {
|
||||||
|
selected = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function chooseIndex(index: number): Promise<void> {
|
||||||
|
const suggestions = await suggestionsPromise;
|
||||||
|
dispatch("autocomplete", { selected: suggestions[index] });
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectIfMousedown(event: MouseEvent, index: number): void {
|
||||||
|
if (event.buttons === 1) {
|
||||||
|
setSelected(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<WithDropdownMenu let:menuId let:createDropdown>
|
<WithDropdownMenu let:menuId let:createDropdown>
|
||||||
|
@ -104,27 +117,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
<DropdownMenu id={menuId} class={className}>
|
<DropdownMenu id={menuId} class={className}>
|
||||||
{#await suggestionsPromise}
|
{#await suggestionsPromise}
|
||||||
<div class="suggestion-item">
|
<AutocompleteItem>...</AutocompleteItem>
|
||||||
<DropdownItem>...</DropdownItem>
|
|
||||||
</div>
|
|
||||||
{:then suggestions}
|
{:then suggestions}
|
||||||
{#each suggestions as suggestion, i}
|
{#each suggestions as suggestion, index}
|
||||||
<div class="suggestion-item">
|
<AutocompleteItem
|
||||||
<DropdownItem
|
selected={index === selected}
|
||||||
class={i === selected ? (active ? "active" : "focus") : ""}
|
active={index === selected && active}
|
||||||
on:click>{suggestion}</DropdownItem
|
on:mousedown={() => setSelected(index)}
|
||||||
|
on:mouseenter={(event) => selectIfMousedown(event, index)}
|
||||||
|
on:mouseup={() => chooseIndex(index)}>{suggestion}</AutocompleteItem
|
||||||
>
|
>
|
||||||
</div>
|
|
||||||
{/each}
|
{/each}
|
||||||
{/await}
|
{/await}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</WithDropdownMenu>
|
</WithDropdownMenu>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.suggestion-item {
|
|
||||||
:global(.dropdown-item:hover) {
|
|
||||||
background-color: inherit !important;
|
|
||||||
border-color: inherit !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -28,7 +28,8 @@ $btn-base-color-day: white;
|
||||||
@content ($btn-base-color-day);
|
@content ($btn-base-color-day);
|
||||||
|
|
||||||
@if ($with-hover) {
|
@if ($with-hover) {
|
||||||
&:hover {
|
&:hover,
|
||||||
|
&.hover {
|
||||||
background-color: darken($btn-base-color-day, 8%);
|
background-color: darken($btn-base-color-day, 8%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +77,8 @@ $btn-base-color-night: #666;
|
||||||
@content ($btn-base-color-night);
|
@content ($btn-base-color-night);
|
||||||
|
|
||||||
@if ($with-hover) {
|
@if ($with-hover) {
|
||||||
&:hover {
|
&:hover,
|
||||||
|
&.hover {
|
||||||
background-color: lighten($btn-base-color-night, 8%);
|
background-color: lighten($btn-base-color-night, 8%);
|
||||||
border-color: lighten($btn-base-color-night, 8%);
|
border-color: lighten($btn-base-color-night, 8%);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue