[ui] add note editing, Inspect handles bubble up Attribute edits

feat/vaults
Tomáš Mládek 2022-01-22 20:19:26 +01:00
parent dba543ca0e
commit 692d99b0b0
No known key found for this signature in database
GPG Key ID: ED21612889E75EC5
3 changed files with 133 additions and 55 deletions

View File

@ -4,11 +4,9 @@
import { Component, UNTYPED, UpType, Widget } from "../lib/types";
import Table from "./widgets/Table.svelte";
import TableComponent from "./widgets/Table.svelte"; // silence false svelte(reactive-component) warnings
import type { AttributeChange } from "../types/base";
import type { UpEntry } from "upend";
import Icon from "./utils/Icon.svelte";
import IconButton from "./utils/IconButton.svelte";
const dispatch = createEventDispatcher();
export let address: string;
export let entries: UpEntry[];
@ -22,7 +20,7 @@
let availableWidgets: Widget[] = [];
$: {
availableWidgets = [
{
{
name: "table",
icon: "table",
components: [
@ -45,45 +43,9 @@
(w) => w.name === currentWidget
)!.components;
}
async function onChange(change: AttributeChange) {
switch (change.type) {
case "create":
await fetch(`/api/obj`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
entity: address,
attribute: change.attribute,
value: change.value,
}),
});
break;
case "delete":
await fetch(`/api/obj/${change.address}`, { method: "DELETE" });
break;
case "update":
// TODO
await fetch(`/api/obj/${change.address}`, { method: "DELETE" });
await fetch(`/api/obj`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
entity: address,
attribute: change.attribute,
value: change.value,
}),
});
break;
default:
console.error("Unimplemented AttributeChange", change);
return;
}
dispatch("changed");
}
</script>
<section class="attribute-view">
<section class="attribute-view labelborder">
<header>
<h3>
{#if type && type !== UNTYPED}
@ -120,7 +82,7 @@
{...component.props || {}}
{entries}
{editable}
on:change={(ev) => onChange(ev.detail)}
on:change
/>
{/each}
{:else}
@ -131,17 +93,17 @@
<style scoped lang="scss">
@use "./util";
.icon {
display: inline-block;
font-size: 1.25em;
margin-top: -0.3em;
position: relative;
bottom: -2px;
}
.icon {
display: inline-block;
font-size: 1.25em;
margin-top: -0.3em;
position: relative;
bottom: -2px;
}
.views {
display: flex;
right: 1ex;
font-size: 18px;
.views {
display: flex;
right: 1ex;
font-size: 18px;
}
</style>

View File

@ -8,6 +8,8 @@
import { writable } from "svelte/store";
import type { UpEntry } from "upend";
import Spinner from "./utils/Spinner.svelte";
import NotesEditor from "./utils/NotesEditor.svelte";
import type { AttributeChange } from "../types/base";
export let address: string;
export let index: number | undefined;
@ -84,6 +86,43 @@
$: currentUntypedAttributes = editable
? untypedAttributes
: filteredUntypedAttributes;
async function onChange(ev: CustomEvent<AttributeChange>) {
const change = ev.detail;
switch (change.type) {
case "create":
await fetch(`/api/obj`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
entity: address,
attribute: change.attribute,
value: change.value,
}),
});
break;
case "delete":
await fetch(`/api/obj/${change.address}`, { method: "DELETE" });
break;
case "update":
// TODO
await fetch(`/api/obj/${change.address}`, { method: "DELETE" });
await fetch(`/api/obj`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
entity: address,
attribute: change.attribute,
value: change.value,
}),
});
break;
default:
console.error("Unimplemented AttributeChange", change);
return;
}
revalidate();
}
</script>
<div class="inspect">
@ -95,6 +134,7 @@
{/if}
</h2>
<BlobPreview {address} />
<NotesEditor {address} {editable} on:change={onChange} />
{#if !$error}
{#if Boolean($allTypeEntries)}
<div class="attributes">
@ -104,7 +144,7 @@
{entries}
type={allTypes[typeAddr]}
{editable}
on:changed={revalidate}
on:change={onChange}
/>
{/each}
@ -114,7 +154,7 @@
{editable}
{address}
entries={currentUntypedAttributes}
on:changed={revalidate}
on:change={onChange}
/>
{/if}
@ -124,7 +164,7 @@
{address}
entries={$entity.backlinks}
reverse
on:changed={revalidate}
on:change={onChange}
/>
{/if}
</div>
@ -143,6 +183,11 @@
flex: auto;
display: flex;
flex-direction: column;
gap: 1rem;
h2 {
margin-bottom: 0;
}
}
.attributes {

View File

@ -0,0 +1,71 @@
<script lang="ts">
import { debounce } from "lodash";
import { createEventDispatcher } from "svelte";
import { useEntity } from "../../lib/entity";
import type { AttributeCreate, AttributeUpdate } from "../../types/base";
import type { UpEntry } from "upend";
const dispatch = createEventDispatcher();
export let address: string;
export let editable: boolean;
$: ({ entity } = useEntity(address));
let noteEntry: UpEntry | undefined;
let notes: String | undefined = undefined;
$: {
if ($entity?.attr["NOTE"]?.length && $entity?.attr["NOTE"][0]?.value?.c) {
noteEntry = $entity?.attr["NOTE"][0];
notes = String(noteEntry.value.c);
} else {
noteEntry = undefined;
notes = undefined;
}
}
let contentEl: HTMLDivElement;
const update = debounce(() => {
if (noteEntry) {
dispatch("change", {
type: "update",
address: noteEntry.address,
attribute: "NOTE",
value: { t: "Value", c: contentEl.innerText },
} as AttributeUpdate);
} else {
dispatch("change", {
type: "create",
address: address,
attribute: "NOTE",
value: { t: "Value", c: contentEl.innerText },
} as AttributeCreate);
}
}, 500);
</script>
{#if notes || editable}
<section class="notes labelborder">
<header>
<h3>Notes</h3>
</header>
<div
class="content"
contenteditable={editable ? "true" : "false"}
on:input={update}
bind:this={contentEl}
>
{notes ? notes : ""}
</div>
</section>
{/if}
<style lang="scss">
@use "../util";
.content {
background: var(--background);
border-radius: 4px;
padding: 0.5em 0.25em;
}
</style>