[ui] add group add feature, fix group display
also add entity type to selector, input eventfeat/vaults
parent
859838f124
commit
4f426bca6f
|
@ -10,6 +10,8 @@
|
||||||
import Spinner from "./utils/Spinner.svelte";
|
import Spinner from "./utils/Spinner.svelte";
|
||||||
import NotesEditor from "./utils/NotesEditor.svelte";
|
import NotesEditor from "./utils/NotesEditor.svelte";
|
||||||
import type { AttributeChange } from "../types/base";
|
import type { AttributeChange } from "../types/base";
|
||||||
|
import Selector from "./utils/Selector.svelte";
|
||||||
|
import type { IValue } from "upend/types";
|
||||||
|
|
||||||
export let address: string;
|
export let address: string;
|
||||||
export let index: number | undefined;
|
export let index: number | undefined;
|
||||||
|
@ -99,7 +101,8 @@
|
||||||
|
|
||||||
$: groups = ($entity?.backlinks || [])
|
$: groups = ($entity?.backlinks || [])
|
||||||
.filter((e) => e.attribute === "HAS")
|
.filter((e) => e.attribute === "HAS")
|
||||||
.map((e) => e.entity);
|
.map((e) => e.entity)
|
||||||
|
.sort(); // TODO
|
||||||
|
|
||||||
async function onChange(ev: CustomEvent<AttributeChange>) {
|
async function onChange(ev: CustomEvent<AttributeChange>) {
|
||||||
const change = ev.detail;
|
const change = ev.detail;
|
||||||
|
@ -137,6 +140,27 @@
|
||||||
}
|
}
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let groupToAdd: IValue | undefined;
|
||||||
|
async function addGroup() {
|
||||||
|
if (!groupToAdd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await fetch(`/api/obj`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
entity: String(groupToAdd.c),
|
||||||
|
attribute: "HAS",
|
||||||
|
value: {
|
||||||
|
t: "Address",
|
||||||
|
c: address,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
revalidate();
|
||||||
|
groupToAdd = undefined;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="inspect">
|
<div class="inspect">
|
||||||
|
@ -154,6 +178,16 @@
|
||||||
{#each groups as address}
|
{#each groups as address}
|
||||||
<UpObject {address} link />
|
<UpObject {address} link />
|
||||||
{/each}
|
{/each}
|
||||||
|
{#if editable}
|
||||||
|
<div class="selector">
|
||||||
|
<Selector
|
||||||
|
type="entity"
|
||||||
|
bind:value={groupToAdd}
|
||||||
|
on:input={addGroup}
|
||||||
|
placeholder="Choose an entity..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</header>
|
</header>
|
||||||
|
@ -214,8 +248,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.groups {
|
.groups {
|
||||||
display: flex;
|
|
||||||
margin: 0.25rem 0;
|
margin: 0.25rem 0;
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.selector {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.attributes {
|
.attributes {
|
||||||
|
|
|
@ -1,15 +1,36 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
import type { IValue } from "upend/types";
|
import type { IValue } from "upend/types";
|
||||||
import { baseSearchOnce, getObjects } from "../../util/search";
|
import { baseSearchOnce, getObjects } from "../../util/search";
|
||||||
import UpObject from "../display/UpObject.svelte";
|
import UpObject from "../display/UpObject.svelte";
|
||||||
import IconButton from "./IconButton.svelte";
|
import IconButton from "./IconButton.svelte";
|
||||||
import Input from "./Input.svelte";
|
import Input from "./Input.svelte";
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
export let attribute: string | undefined = undefined;
|
export let attribute: string | undefined = undefined;
|
||||||
export let value: IValue | undefined = undefined;
|
export let value: IValue | undefined = undefined;
|
||||||
export let type: "attribute" | "value";
|
export let type: "attribute" | "value" | "entity";
|
||||||
|
export let placeholder = "";
|
||||||
|
|
||||||
|
let inputValue = "";
|
||||||
|
if (type == "attribute") {
|
||||||
|
inputValue = attribute || "";
|
||||||
|
} else {
|
||||||
|
inputValue = String(value?.c || "");
|
||||||
|
}
|
||||||
|
$: if (value === undefined) inputValue = undefined;
|
||||||
|
|
||||||
|
function onInput(ev: CustomEvent<string>) {
|
||||||
|
if (type == "attribute") {
|
||||||
|
attribute = ev.detail;
|
||||||
|
} else {
|
||||||
|
value = {
|
||||||
|
t: "String",
|
||||||
|
c: ev.detail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface SelectorOption {
|
interface SelectorOption {
|
||||||
attribute?: string;
|
attribute?: string;
|
||||||
|
@ -36,17 +57,23 @@
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "value": {
|
case "value":
|
||||||
|
case "entity": {
|
||||||
const result = await baseSearchOnce(query);
|
const result = await baseSearchOnce(query);
|
||||||
const objects = await getObjects(result.entries);
|
const objects = await getObjects(result.entries);
|
||||||
|
|
||||||
options = [
|
options = [];
|
||||||
{
|
|
||||||
|
if (type === "value") {
|
||||||
|
options.push({
|
||||||
value: {
|
value: {
|
||||||
t: "String",
|
t: "String",
|
||||||
c: query,
|
c: query,
|
||||||
},
|
},
|
||||||
},
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
options.push(
|
||||||
...objects.slice(0, 25).map(([address, label]) => {
|
...objects.slice(0, 25).map(([address, label]) => {
|
||||||
return {
|
return {
|
||||||
value: {
|
value: {
|
||||||
|
@ -54,8 +81,10 @@
|
||||||
c: address,
|
c: address,
|
||||||
},
|
},
|
||||||
} as SelectorOption;
|
} as SelectorOption;
|
||||||
}),
|
})
|
||||||
];
|
);
|
||||||
|
|
||||||
|
options = options;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,31 +98,15 @@
|
||||||
inputValue = option.attribute;
|
inputValue = option.attribute;
|
||||||
break;
|
break;
|
||||||
case "value":
|
case "value":
|
||||||
|
case "entity":
|
||||||
value = option.value;
|
value = option.value;
|
||||||
inputValue = String(option.value.c);
|
inputValue = String(option.value.c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
dispatch("input", value);
|
||||||
visible = false;
|
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: "String",
|
|
||||||
c: ev.detail,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputFocused = false;
|
let inputFocused = false;
|
||||||
let hover = false;
|
let hover = false;
|
||||||
$: visible = (inputFocused || hover) && Boolean(options.length);
|
$: visible = (inputFocused || hover) && Boolean(options.length);
|
||||||
|
@ -112,6 +125,7 @@
|
||||||
bind:value={inputValue}
|
bind:value={inputValue}
|
||||||
on:input={onInput}
|
on:input={onInput}
|
||||||
on:focusChange={(ev) => (inputFocused = ev.detail)}
|
on:focusChange={(ev) => (inputFocused = ev.detail)}
|
||||||
|
{placeholder}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<ul
|
<ul
|
||||||
|
|
Loading…
Reference in New Issue