Input two-way binding, first version of an autocomplete Selector
parent
091a02d530
commit
337ba75603
|
@ -1,17 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let placeholder = "";
|
||||
export let type = "text";
|
||||
export let value = "";
|
||||
|
||||
let focused = false;
|
||||
$: dispatch("focusChange", focused);
|
||||
</script>
|
||||
|
||||
<div class="input" class:focused>
|
||||
<slot name="prefix" />
|
||||
<input
|
||||
{type}
|
||||
{placeholder}
|
||||
{value}
|
||||
bind:value
|
||||
on:input
|
||||
on:focus={() => (focused = true)}
|
||||
on:blur={() => (focused = false)}
|
||||
|
@ -27,9 +30,9 @@
|
|||
|
||||
border: 1px solid var(--foreground-lighter);
|
||||
border-radius: 4px;
|
||||
background: var(--background-lighter);
|
||||
background: var(--background);
|
||||
|
||||
transition: box-shadow .25s;
|
||||
transition: box-shadow 0.25s;
|
||||
&.focused {
|
||||
box-shadow: 0 0 2px 3px var(--primary);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<script lang="ts">
|
||||
import { tick } from "svelte";
|
||||
import Input from "./Input.svelte";
|
||||
|
||||
export let value = "";
|
||||
export let type: "entity" | "attribute" | "value";
|
||||
|
||||
let options = [];
|
||||
async function updateOptions(query: string) {
|
||||
switch (type) {
|
||||
case "entity":
|
||||
throw new Error("unimplemented");
|
||||
case "attribute":
|
||||
const req = await fetch("/api/all/attributes");
|
||||
const allAttributes: string[] = await req.json();
|
||||
options = allAttributes.filter((attr) =>
|
||||
attr.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
break;
|
||||
case "value":
|
||||
throw new Error("unimplemented");
|
||||
}
|
||||
}
|
||||
$: updateOptions(value);
|
||||
|
||||
let inputFocused = false;
|
||||
let hover = false;
|
||||
$: visible = (inputFocused || hover) && Boolean(options.length);
|
||||
</script>
|
||||
|
||||
<div class="selector">
|
||||
<Input bind:value on:focusChange={(ev) => (inputFocused = ev.detail)} />
|
||||
<ul
|
||||
class="options"
|
||||
class:visible
|
||||
on:mouseenter={() => (hover = true)}
|
||||
on:mouseleave={() => (hover = false)}
|
||||
>
|
||||
{#each options as option}
|
||||
<li
|
||||
on:click={() => {
|
||||
value = option;
|
||||
visible = false;
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.selector {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.options {
|
||||
position: absolute;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 1px solid var(--foreground-lighter);
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
margin-top: 2px;
|
||||
background: var(--background);
|
||||
font-size: smaller;
|
||||
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
|
||||
transition: opacity 0.2s;
|
||||
|
||||
z-index: 99;
|
||||
|
||||
&.visible {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
li {
|
||||
cursor: pointer;
|
||||
padding: 0.5em;
|
||||
|
||||
transition: background-color 0.2s;
|
||||
&:hover {
|
||||
background-color: var(--background-lighter);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -8,8 +8,9 @@
|
|||
import { useParams } from "svelte-navigator";
|
||||
import type { Writable } from "svelte/store";
|
||||
import type { UpEntry } from "upend";
|
||||
import IconButton from "../utils/IconButton.svelte";
|
||||
import Input from "../utils/Input.svelte";
|
||||
import IconButton from "../utils/IconButton.svelte";
|
||||
import Input from "../utils/Input.svelte";
|
||||
import Selector from "../utils/Selector.svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
const params = useParams();
|
||||
|
||||
|
@ -26,7 +27,7 @@ import Input from "../utils/Input.svelte";
|
|||
$: showValue = columns.includes("value");
|
||||
|
||||
// Editing
|
||||
let newEntryAttribute = "'";
|
||||
let newEntryAttribute = "";
|
||||
let newEntryValue = "";
|
||||
|
||||
async function addEntry() {
|
||||
|
@ -303,16 +304,12 @@ import Input from "../utils/Input.svelte";
|
|||
</td>
|
||||
{#if showAttribute}
|
||||
<td>
|
||||
<Input
|
||||
on:input={(ev) => (newEntryAttribute = ev.target.value)}
|
||||
/>
|
||||
<Selector type="attribute" bind:value={newEntryAttribute} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if showValue}
|
||||
<td>
|
||||
<Input
|
||||
on:input={(ev) => (newEntryValue = ev.target.value)}
|
||||
/>
|
||||
<Input bind:value={newEntryValue} />
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
|
@ -328,7 +325,7 @@ import Input from "../utils/Input.svelte";
|
|||
|
||||
border-spacing: 0.5em 0;
|
||||
|
||||
tr {
|
||||
tr {
|
||||
&.left-active {
|
||||
td:first-child {
|
||||
background: linear-gradient(
|
||||
|
|
Loading…
Reference in New Issue