upend/webui/src/components/InspectTypeEditor.svelte

135 lines
3.2 KiB
Svelte

<script lang="ts">
import UpObjectDisplay from "./display/UpObject.svelte";
import Selector, { type SelectorValue } from "./utils/Selector.svelte";
import IconButton from "./utils/IconButton.svelte";
import api from "../lib/api";
import { i18n } from "../i18n";
import type { UpObject, UpEntry } from "@upnd/upend";
import type { Readable } from "svelte/store";
import { ATTR_OF } from "@upnd/upend/constants";
import { createEventDispatcher } from "svelte";
import LabelBorder from "./utils/LabelBorder.svelte";
const dispatch = createEventDispatcher();
export let entity: Readable<UpObject>;
let adding = false;
let typeSelector: Selector;
$: if (adding && typeSelector) typeSelector.focus();
$: typeEntries = $entity?.attr[`~${ATTR_OF}`] || [];
async function add(ev: CustomEvent<SelectorValue>) {
if (ev.detail.t !== "Attribute") {
return;
}
await api.putEntry({
entity: {
t: "Attribute",
c: ev.detail.name,
},
attribute: ATTR_OF,
value: { t: "Address", c: $entity.address },
});
dispatch("change");
}
async function remove(entry: UpEntry) {
let really = confirm(
$i18n.t('Really remove "{{attributeName}}" from "{{typeName}}"?', {
attributeName: (await api.addressToComponents(entry.entity)).c,
typeName: $entity.identify().join("/"),
}),
);
if (really) {
await api.deleteEntry(entry.address);
dispatch("change");
}
}
</script>
{#if typeEntries.length || $entity?.attr["~IN"]?.length}
<LabelBorder hide={typeEntries.length === 0}>
<span slot="header">{$i18n.t("Type Attributes")}</span>
{#if adding}
<div class="selector">
<Selector
bind:this={typeSelector}
types={["Attribute", "NewAttribute"]}
on:input={add}
placeholder={$i18n.t("Assign an attribute to this type...")}
on:focus={(ev) => {
if (!ev.detail) adding = false;
}}
/>
</div>
{/if}
<div class="body">
<ul class="attributes">
{#each typeEntries as typeEntry}
<li class="attribute">
<div class="label">
<UpObjectDisplay address={typeEntry.entity} link />
</div>
<div class="controls">
<IconButton name="x-circle" on:click={() => remove(typeEntry)} />
</div>
</li>
{:else}
<li class="no-attributes">
{$i18n.t("No attributes assigned to this type.")}
</li>
{/each}
</ul>
<div class="add-button">
<IconButton
outline
small
name="plus-circle"
on:click={() => (adding = true)}
/>
</div>
</div>
</LabelBorder>
{/if}
<style lang="scss">
.attributes {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 0.25em;
}
.attribute {
display: flex;
}
.body {
display: flex;
align-items: start;
.attributes {
flex-grow: 1;
}
}
.selector {
width: 100%;
margin-bottom: 0.5rem;
}
.no-attributes {
opacity: 0.66;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
</style>