Introduce AutocompleteItem
* needs too much custom styling / behavior to be done with DropdownItem
This commit is contained in:
parent
b2d2cb8715
commit
b93646209a
@ -50,10 +50,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
background: none;
|
||||
box-shadow: none !important;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-day {
|
||||
color: black;
|
||||
|
||||
&: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 {
|
||||
color: white;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.focus {
|
||||
&:focus {
|
||||
@include button.btn-night-base;
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.active {
|
||||
background-color: button.$focus-color;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
</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;
|
||||
|
||||
case "Enter":
|
||||
console.log("choose");
|
||||
autocomplete.chooseSelected();
|
||||
event.preventDefault();
|
||||
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 DropdownMenu from "components/DropdownMenu.svelte";
|
||||
import DropdownItem from "components/DropdownItem.svelte";
|
||||
import AutocompleteItem from "./AutocompleteItem.svelte";
|
||||
|
||||
let className: string = "";
|
||||
export { className as class };
|
||||
@ -26,32 +26,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function selectNext() {
|
||||
suggestionsPromise.then((suggestions) => {
|
||||
if (selected === null) {
|
||||
selected = 0;
|
||||
} else if (selected >= suggestions.length - 1) {
|
||||
selected = null;
|
||||
} else {
|
||||
selected++;
|
||||
}
|
||||
async function selectNext() {
|
||||
const suggestions = await suggestionsPromise;
|
||||
|
||||
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
||||
});
|
||||
if (selected === null) {
|
||||
selected = 0;
|
||||
} else if (selected >= suggestions.length - 1) {
|
||||
selected = null;
|
||||
} else {
|
||||
selected++;
|
||||
}
|
||||
|
||||
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
||||
}
|
||||
|
||||
function selectPrevious() {
|
||||
suggestionsPromise.then((suggestions) => {
|
||||
if (selected === null) {
|
||||
selected = suggestions.length - 1;
|
||||
} else if (selected === 0) {
|
||||
selected = null;
|
||||
} else {
|
||||
selected--;
|
||||
}
|
||||
async function selectPrevious() {
|
||||
const suggestions = await suggestionsPromise;
|
||||
|
||||
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
||||
});
|
||||
if (selected === null) {
|
||||
selected = suggestions.length - 1;
|
||||
} else if (selected === 0) {
|
||||
selected = null;
|
||||
} else {
|
||||
selected--;
|
||||
}
|
||||
|
||||
dispatch("autocomplete", { selected: suggestions[selected ?? -1] });
|
||||
}
|
||||
|
||||
function chooseSelected() {
|
||||
@ -63,18 +63,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
dropdown.update();
|
||||
dispatch("update");
|
||||
|
||||
await tick();
|
||||
const [, suggestions] = await Promise.all([tick(), suggestionsPromise]);
|
||||
|
||||
suggestionsPromise.then((suggestions) => {
|
||||
if (suggestions.length > 0) {
|
||||
dropdown.show();
|
||||
// disabled class will tell Bootstrap not to show menu on clicking
|
||||
target.classList.remove("disabled");
|
||||
} else {
|
||||
dropdown.hide();
|
||||
target.classList.add("disabled");
|
||||
}
|
||||
});
|
||||
if (suggestions.length > 0) {
|
||||
dropdown.show();
|
||||
// disabled class will tell Bootstrap not to show menu on clicking
|
||||
target.classList.remove("disabled");
|
||||
} else {
|
||||
dropdown.hide();
|
||||
target.classList.add("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
const createAutocomplete =
|
||||
@ -97,6 +95,21 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
};
|
||||
|
||||
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>
|
||||
|
||||
<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}>
|
||||
{#await suggestionsPromise}
|
||||
<div class="suggestion-item">
|
||||
<DropdownItem>...</DropdownItem>
|
||||
</div>
|
||||
<AutocompleteItem>...</AutocompleteItem>
|
||||
{:then suggestions}
|
||||
{#each suggestions as suggestion, i}
|
||||
<div class="suggestion-item">
|
||||
<DropdownItem
|
||||
class={i === selected ? (active ? "active" : "focus") : ""}
|
||||
on:click>{suggestion}</DropdownItem
|
||||
>
|
||||
</div>
|
||||
{#each suggestions as suggestion, index}
|
||||
<AutocompleteItem
|
||||
selected={index === selected}
|
||||
active={index === selected && active}
|
||||
on:mousedown={() => setSelected(index)}
|
||||
on:mouseenter={(event) => selectIfMousedown(event, index)}
|
||||
on:mouseup={() => chooseIndex(index)}>{suggestion}</AutocompleteItem
|
||||
>
|
||||
{/each}
|
||||
{/await}
|
||||
</DropdownMenu>
|
||||
</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);
|
||||
|
||||
@if ($with-hover) {
|
||||
&:hover {
|
||||
&:hover,
|
||||
&.hover {
|
||||
background-color: darken($btn-base-color-day, 8%);
|
||||
}
|
||||
}
|
||||
@ -76,7 +77,8 @@ $btn-base-color-night: #666;
|
||||
@content ($btn-base-color-night);
|
||||
|
||||
@if ($with-hover) {
|
||||
&:hover {
|
||||
&:hover,
|
||||
&.hover {
|
||||
background-color: lighten($btn-base-color-night, 8%);
|
||||
border-color: lighten($btn-base-color-night, 8%);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user