[ui] rudimentary attribute editing
also refactor to use actual correct types throughoutfeat/vaults
parent
8ceb310d9e
commit
a099cbd85f
|
@ -1,4 +1,4 @@
|
|||
import type { IEntry, ListingResult, VALUE_TYPE } from "./types";
|
||||
import type { IEntry, IValue, ListingResult, VALUE_TYPE } from "./types";
|
||||
|
||||
// export function listingAsOrdered(listing: ListingResult): OrderedListing {
|
||||
// const entries = Object.entries(listing) as [Address, IEntry][];
|
||||
|
@ -103,7 +103,7 @@ export class UpObject {
|
|||
export class UpEntry extends UpObject implements IEntry {
|
||||
entity: string;
|
||||
attribute: string;
|
||||
value: { t: VALUE_TYPE; c: string | number };
|
||||
value: IValue;
|
||||
|
||||
constructor(address: string, entry: IEntry, listing: UpListing) {
|
||||
super(address, listing);
|
||||
|
|
|
@ -4,7 +4,12 @@ export type VALUE_TYPE = "Value" | "Address" | "Invalid";
|
|||
export interface IEntry {
|
||||
entity: Address;
|
||||
attribute: string;
|
||||
value: { t: VALUE_TYPE; c: string | number };
|
||||
value: IValue;
|
||||
}
|
||||
|
||||
export interface IValue {
|
||||
t: VALUE_TYPE;
|
||||
c: string | number;
|
||||
}
|
||||
|
||||
export interface ListingResult {
|
||||
|
@ -28,8 +33,7 @@ export interface IJob {
|
|||
state: "InProgress" | "Done" | "Failed";
|
||||
}
|
||||
|
||||
|
||||
export interface VaultInfo {
|
||||
name: string | null;
|
||||
location: string;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,16 +55,26 @@
|
|||
body: JSON.stringify({
|
||||
entity: address,
|
||||
attribute: change.attribute,
|
||||
value: {
|
||||
t: "Value",
|
||||
c: change.value,
|
||||
},
|
||||
value: change.value,
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case "delete":
|
||||
await fetch(`/api/obj/${change.addr}`, { method: "DELETE" });
|
||||
break;
|
||||
case "update":
|
||||
// TODO
|
||||
await fetch(`/api/obj/${change.addr}`, { 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;
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<script lang="ts">
|
||||
import Selector from "./Selector.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import type { IValue } from "upend/types";
|
||||
import IconButton from "./IconButton.svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let editable: boolean;
|
||||
export let value: IValue;
|
||||
let newValue: IValue = value;
|
||||
</script>
|
||||
|
||||
<div class="editable">
|
||||
{#if editable}
|
||||
<div class="inner">
|
||||
<div class="selector">
|
||||
<Selector type="value" bind:value={newValue} />
|
||||
</div>
|
||||
<IconButton
|
||||
name="check-circle"
|
||||
on:click={() => dispatch("edit", newValue)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.inner {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.selector {
|
||||
flex-grow: 1;
|
||||
}
|
||||
</style>
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
let focused = false;
|
||||
$: dispatch("focusChange", focused);
|
||||
|
||||
function onInput() {
|
||||
dispatch("input", value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="input" class:focused>
|
||||
|
@ -15,7 +19,7 @@
|
|||
<input
|
||||
{placeholder}
|
||||
bind:value
|
||||
on:input
|
||||
on:input={onInput}
|
||||
on:focus={() => (focused = true)}
|
||||
on:blur={() => (focused = false)}
|
||||
/>
|
||||
|
|
|
@ -1,27 +1,68 @@
|
|||
<script lang="ts">
|
||||
import { tick } from "svelte";
|
||||
import { debounce } from "lodash";
|
||||
|
||||
import type { IValue } from "upend/types";
|
||||
import Input from "./Input.svelte";
|
||||
|
||||
export let value = "";
|
||||
export let attribute: string | undefined = undefined;
|
||||
export let value: IValue | undefined = undefined;
|
||||
export let type: "entity" | "attribute" | "value";
|
||||
|
||||
let options = [];
|
||||
async function updateOptions(query: string) {
|
||||
interface SelectorOption {
|
||||
attribute?: string;
|
||||
value?: IValue;
|
||||
}
|
||||
|
||||
let options: SelectorOption[] = [];
|
||||
const updateOptions = debounce(async (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())
|
||||
);
|
||||
options = allAttributes
|
||||
.filter((attr) => attr.toLowerCase().includes(query.toLowerCase()))
|
||||
.map((attribute) => {
|
||||
return {
|
||||
attribute,
|
||||
};
|
||||
});
|
||||
break;
|
||||
case "value":
|
||||
throw new Error("unimplemented");
|
||||
break;
|
||||
}
|
||||
}, 200);
|
||||
$: updateOptions(inputValue);
|
||||
|
||||
function set(option: SelectorOption) {
|
||||
if (type == "attribute") {
|
||||
attribute = option.attribute;
|
||||
inputValue = option.attribute;
|
||||
} else {
|
||||
value = option.value;
|
||||
inputValue = String(option.value.c); // todo;
|
||||
}
|
||||
visible = false;
|
||||
}
|
||||
|
||||
let inputValue = "";
|
||||
if (type == "attribute") {
|
||||
inputValue = attribute || "";
|
||||
} else {
|
||||
inputValue = String(value?.c || "");
|
||||
}
|
||||
|
||||
function onInput(ev: CustomEvent<string>) {
|
||||
if (type == "attribute") {
|
||||
attribute = ev.detail;
|
||||
} else {
|
||||
value = {
|
||||
t: "Value",
|
||||
c: ev.detail,
|
||||
};
|
||||
}
|
||||
}
|
||||
$: updateOptions(value);
|
||||
|
||||
let inputFocused = false;
|
||||
let hover = false;
|
||||
|
@ -29,7 +70,11 @@
|
|||
</script>
|
||||
|
||||
<div class="selector">
|
||||
<Input bind:value on:focusChange={(ev) => (inputFocused = ev.detail)} />
|
||||
<Input
|
||||
value={inputValue}
|
||||
on:input={onInput}
|
||||
on:focusChange={(ev) => (inputFocused = ev.detail)}
|
||||
/>
|
||||
<ul
|
||||
class="options"
|
||||
class:visible
|
||||
|
@ -37,13 +82,12 @@
|
|||
on:mouseleave={() => (hover = false)}
|
||||
>
|
||||
{#each options as option}
|
||||
<li
|
||||
on:click={() => {
|
||||
value = option;
|
||||
visible = false;
|
||||
}}
|
||||
>
|
||||
{option}
|
||||
<li on:click={() => set(option)}>
|
||||
{#if option.attribute}
|
||||
{option.attribute}
|
||||
{:else if option.value}
|
||||
RESOLVING LOGIC
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
import IconButton from "../utils/IconButton.svelte";
|
||||
import Input from "../utils/Input.svelte";
|
||||
import Selector from "../utils/Selector.svelte";
|
||||
import type { IValue } from "upend/types";
|
||||
import Editable from "../utils/Editable.svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
const params = useParams();
|
||||
|
||||
|
@ -28,7 +30,7 @@
|
|||
|
||||
// Editing
|
||||
let newEntryAttribute = "";
|
||||
let newEntryValue = "";
|
||||
let newEntryValue: IValue | undefined;
|
||||
|
||||
async function addEntry() {
|
||||
dispatch("change", {
|
||||
|
@ -37,14 +39,14 @@
|
|||
value: newEntryValue,
|
||||
} as AttributeChange);
|
||||
newEntryAttribute = "";
|
||||
newEntryValue = "";
|
||||
newEntryValue = undefined;
|
||||
}
|
||||
async function removeEntry(addr: string) {
|
||||
if (confirm("Are you sure you want to remove the attribute?")) {
|
||||
dispatch("change", { type: "delete", addr } as AttributeChange);
|
||||
}
|
||||
}
|
||||
async function updateEntry(addr: string, attribute: string, value: string) {
|
||||
async function updateEntry(addr: string, attribute: string, value: IValue) {
|
||||
dispatch("change", {
|
||||
type: "update",
|
||||
addr,
|
||||
|
@ -180,11 +182,11 @@
|
|||
const handler = VALUE_FORMATTERS[attribute];
|
||||
if (handler) {
|
||||
try {
|
||||
return handler(value);
|
||||
return handler(value);
|
||||
} catch (error) {
|
||||
console.warn(`Error while formatting "${value}": ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
|
@ -264,10 +266,11 @@
|
|||
|
||||
{#if showValue}
|
||||
<td class="value">
|
||||
<text-input
|
||||
<Editable
|
||||
{editable}
|
||||
value={entry.value.c}
|
||||
on:edit={(val) => updateEntry(entry.address, entry.attribute, val)}
|
||||
value={entry.value}
|
||||
on:edit={(ev) =>
|
||||
updateEntry(entry.address, entry.attribute, ev.detail)}
|
||||
>
|
||||
{#if entry.value.t === "Address"}
|
||||
<UpObject
|
||||
|
@ -293,25 +296,25 @@
|
|||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</text-input>
|
||||
</Editable>
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
|
||||
{#if editable}
|
||||
<tr>
|
||||
<tr class="add-row">
|
||||
<td class="attr-action">
|
||||
<IconButton name="plus-circle" on:click={addEntry} />
|
||||
</td>
|
||||
{#if showAttribute}
|
||||
<td>
|
||||
<Selector type="attribute" bind:value={newEntryAttribute} />
|
||||
<Selector type="attribute" bind:attribute={newEntryAttribute} />
|
||||
</td>
|
||||
{/if}
|
||||
{#if showValue}
|
||||
<td>
|
||||
<Input bind:value={newEntryValue} />
|
||||
<Selector type="value" bind:value={newEntryValue} />
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import type { IValue } from "upend/types";
|
||||
|
||||
export type AttributeChange =
|
||||
| AttributeCreate
|
||||
| AttributeUpdate
|
||||
|
@ -6,16 +8,17 @@ export type AttributeChange =
|
|||
export interface AttributeCreate {
|
||||
type: "create";
|
||||
attribute: string;
|
||||
value: any;
|
||||
value: IValue;
|
||||
}
|
||||
|
||||
export interface AttributeUpdate {
|
||||
type: "update";
|
||||
addr: string;
|
||||
value: any;
|
||||
attribute: string; // TODO: remove
|
||||
value: IValue;
|
||||
}
|
||||
|
||||
export interface AttributeDelete {
|
||||
type: "delete";
|
||||
addr: string;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3474,8 +3474,8 @@ __metadata:
|
|||
|
||||
"upend@file:../tools/upend_js::locator=svelte-app%40workspace%3A.":
|
||||
version: 0.0.1
|
||||
resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=a954aa&locator=svelte-app%40workspace%3A."
|
||||
checksum: 1b0605428365b2a672cb119e795292102f80171f03efec8290a5811b1514107455557a21157e7376aa7dcb7285484fd1db791d6d876893ec92f1794a0f61120f
|
||||
resolution: "upend@file:../tools/upend_js#../tools/upend_js::hash=2fee87&locator=svelte-app%40workspace%3A."
|
||||
checksum: 51fd21439f8a42d901e03c06e4797f89fd7c505c21ce53d9cb7340ae7495f05d774dd7f0ff758b5470ebb1fb6725e33e06b09d98ec91f17e9766ae1c05d2bd58
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
Loading…
Reference in New Issue