feat, wip: modeless Editable, functional type attributes
parent
c4b09ea234
commit
3294299c5d
|
@ -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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue