[ui] add note editing, Inspect handles bubble up Attribute edits
parent
dba543ca0e
commit
692d99b0b0
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue