feat, wip: modeless Editable, functional type attributes

feat/axum
Tomáš Mládek 2023-09-02 19:30:01 +02:00
parent c4b09ea234
commit 3294299c5d
3 changed files with 89 additions and 49 deletions

View File

@ -12,7 +12,6 @@
import IconButton from "./utils/IconButton.svelte";
import type { BrowseContext } from "../util/browse";
import { Link, useParams } from "svelte-navigator";
import Icon from "./utils/Icon.svelte";
import BlobViewer from "./display/BlobViewer.svelte";
import { i18n } from "../i18n";
import EntryList from "./widgets/EntryList.svelte";

View File

@ -1,50 +1,80 @@
<script lang="ts">
import Selector from "./Selector.svelte";
import { createEventDispatcher } from "svelte";
import type { IValue, VALUE_TYPE } from "upend/types";
import type { IValue } from "upend/types";
import IconButton from "./IconButton.svelte";
import { isEqual } from "lodash";
const dispatch = createEventDispatcher();
let editable = false;
export let attribute: string | undefined = undefined;
export let value: IValue;
export let value: IValue | undefined = undefined;
let newValue: IValue = value;
// todo - grab from db
const TYPES_FOR_ATTR: { [key: string]: VALUE_TYPE[] } = {
LBL: ["String"],
FILE_SIZE: ["Number"],
FILE_MIME: ["String"],
ADDED: ["Number"],
LAST_VISITED: ["Number"],
};
let editing = false;
let selector: Selector;
let hover = false;
let focus = false;
$: if (editing && selector) selector.focus();
$: if (!focus && !hover) editing = false;
</script>
<div class="editable">
{#if editable}
<div class="inner">
<div class="selector">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="editable"
class:editing
on:mouseenter={() => (hover = true)}
on:mouseleave={() => (hover = false)}
>
<div class="inner">
{#if editing}
<div
class="selector"
on:keydown={(ev) => {
if (ev.key === "Escape") {
editing = false;
}
}}
>
<Selector
type="value"
valueTypes={TYPES_FOR_ATTR[attribute]}
bind:value={newValue}
bind:this={selector}
on:focus={(ev) => (focus = ev.detail)}
on:input={() => selector.focus()}
/>
</div>
<IconButton
name="check-circle"
disabled={isEqual(value, newValue)}
on:click={() => dispatch("edit", newValue)}
name="save"
on:click={() => {
dispatch("edit", newValue);
editing = false;
}}
/>
</div>
{:else}
<slot />
{/if}
{:else}
<div class="content">
<slot />
</div>
<div class="edit-icon">
<IconButton name="edit" on:click={() => (editing = true)} />
</div>
{/if}
</div>
</div>
<style>
<style lang="scss">
.edit-icon {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.editable:hover .edit-icon {
opacity: 0.8;
}
.inner {
display: flex;
gap: 0.25em;
align-items: center;
}
.selector {

View File

@ -18,9 +18,6 @@
import { i18n } from "../../i18n";
import UpLink from "../display/UpLink.svelte";
import { ATTR_ADDED, ATTR_LABEL } from "upend/constants";
import api from "../../lib/api";
import { AddressComponents } from "upend/api";
import Icon from "../utils/Icon.svelte";
const dispatch = createEventDispatcher();
export let columns: string | undefined = undefined;
@ -30,7 +27,7 @@
export let columnWidths: string[] | undefined = undefined;
export let entries: UpEntry[];
export let attributes: string[] = [];
export let attributes: string[] | undefined = undefined;
export let attributeOptions: string[] | undefined = undefined;
// Display
@ -48,11 +45,11 @@
let newEntryAttribute = "";
let newEntryValue: IValue | undefined;
async function addEntry() {
async function addEntry(attribute: string, value: IValue) {
dispatch("change", {
type: "create",
attribute: newEntryAttribute,
value: newEntryValue,
attribute,
value,
} as AttributeChange);
newEntryAttribute = "";
newEntryValue = undefined;
@ -218,12 +215,9 @@
$: (async () => {
unusedAttributes = await Promise.all(
attributes
.filter((attr) => !entries.some((entry) => entry.attribute === attr))
.map(async (attribute) => {
const components = new AddressComponents("Attribute", attribute);
return await api.componentsToAddress(components);
}),
(attributes || []).filter(
(attr) => !entries.some((entry) => entry.attribute === attr),
),
);
})();
</script>
@ -297,7 +291,6 @@
{:else if column == VALUE_COL}
<td class="value mark-value">
<Editable
attribute={entry.attribute}
value={entry.value}
on:edit={(ev) =>
updateEntry(entry.address, entry.attribute, ev.detail)}
@ -348,16 +341,31 @@
</tr>
{/each}
{#each unusedAttributes as attributeAddress}
{#each unusedAttributes as attribute}
<tr>
{#each displayColumns as column}
{#if column == ATTR_COL}
<td>
<UpObject link address={attributeAddress} />
<td
class:formatted={Boolean(
Object.keys($attributeLabels).includes(attribute),
)}
class="mark-attribute"
>
<UpLink to={{ attribute }}>
<Ellipsis
value={$attributeLabels[attribute] || attribute}
title={$attributeLabels[attribute]
? `${$attributeLabels[attribute]} (${attribute})`
: attribute}
/>
</UpLink>
</td>
{:else if column == VALUE_COL}
<!-- <Selector type="value" bind:value={newEntryValue} /> -->
<td class="unset">{$i18n.t("(unset)")}</td>
<td>
<Editable on:edit={(ev) => addEntry(attribute, ev.detail)}>
<span class="unset">{$i18n.t("(unset)")}</span>
</Editable>
</td>
{:else}
<td></td>
{/if}
@ -366,7 +374,7 @@
</tr>
{/each}
{#if !attributes.length}
{#if !attributes?.length}
<tr class="add-row">
{#if displayColumns.includes(ATTR_COL)}
<td>
@ -383,7 +391,10 @@
</td>
{/if}
<td class="attr-action">
<IconButton name="plus-circle" on:click={addEntry} />
<IconButton
name="plus-circle"
on:click={() => addEntry(newEntryAttribute, newEntryValue)}
/>
</td>
</tr>
{/if}
@ -392,7 +403,7 @@
<style lang="scss" scoped>
.container {
overflow: hidden;
overflow-x: clip;
}
table {