upend/ui/src/components/AttributeView.svelte

181 lines
4.3 KiB
Svelte
Raw Normal View History

2021-11-11 23:37:42 +01:00
<script lang="ts">
2021-11-30 22:59:34 +01:00
import { createEventDispatcher } from "svelte";
2021-12-21 20:02:47 +01:00
import UpLink from "./display/UpLink.svelte";
import { Component, UNTYPED, UpType, Widget } from "../lib/types";
2021-11-11 23:37:42 +01:00
import Table from "./widgets/Table.svelte";
2021-11-30 22:59:34 +01:00
import TableComponent from "./widgets/Table.svelte"; // silence false svelte(reactive-component) warnings
2021-12-02 18:45:29 +01:00
import type { AttributeChange } from "../types/base";
import type { UpEntry } from "upend";
import Icon from "./utils/Icon.svelte";
import IconButton from "./utils/IconButton.svelte";
2021-12-02 18:45:29 +01:00
const dispatch = createEventDispatcher();
2021-11-11 23:37:42 +01:00
export let address: string;
export let entries: UpEntry[];
2021-11-11 23:37:42 +01:00
export let type: UpType | undefined = undefined;
export let title: String | undefined = undefined;
export let editable = false;
export let reverse = false;
let currentWidget: string | undefined;
2021-11-11 23:37:42 +01:00
let availableWidgets: Widget[] = [];
$: {
availableWidgets = [
{
name: "table",
icon: "table",
components: [
{
2021-11-30 22:59:34 +01:00
component: TableComponent,
2021-11-11 23:37:42 +01:00
},
],
},
];
if (type?.widgetInfo) {
availableWidgets = [type.widgetInfo, ...availableWidgets];
}
currentWidget = availableWidgets[0].name;
2021-11-11 23:37:42 +01:00
}
let components: Component[] = [];
$: {
components = availableWidgets.find(
(w) => w.name === currentWidget
)!.components;
}
2021-12-02 18:45:29 +01:00
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,
2021-12-02 18:45:29 +01:00
}),
});
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;
2021-12-02 18:45:29 +01:00
default:
console.error("Unimplemented AttributeChange", change);
return;
}
dispatch("changed");
2021-11-11 23:37:42 +01:00
}
</script>
<section class="attribute-view">
<header>
<h3>
{#if type && type !== UNTYPED}
2021-11-11 23:37:42 +01:00
<UpLink to={{ entity: type.address }}>
{#if type.icon}
2021-12-02 20:49:59 +01:00
<div class="icon">
<Icon name={type.icon} />
2021-12-02 20:49:59 +01:00
</div>
2021-11-11 23:37:42 +01:00
{/if}
{type.name || "???"}
</UpLink>
{:else}
{title || "???"}
{/if}
</h3>
{#if currentWidget && (availableWidgets.length > 1 || editable)}
2021-11-11 23:37:42 +01:00
<div class="views">
{#each availableWidgets as widget (widget.name)}
<IconButton
2021-11-11 23:37:42 +01:00
name={widget.icon || "question-diamond"}
active={widget.name === currentWidget}
--active-color="var(--foreground)"
2021-11-11 23:37:42 +01:00
on:click={() => (currentWidget = widget.name)}
/>
{/each}
</div>
{/if}
</header>
2021-11-30 22:59:34 +01:00
{#if !reverse}
{#each components as component}
<svelte:component
this={component.component}
{...component.props || {}}
{entries}
2021-11-30 22:59:34 +01:00
{editable}
2021-12-02 18:45:29 +01:00
on:change={(ev) => onChange(ev.detail)}
2021-11-30 22:59:34 +01:00
/>
{/each}
{:else}
<Table columns="entity, attribute" {entries} />
2021-11-30 22:59:34 +01:00
{/if}
2021-11-11 23:37:42 +01:00
</section>
<style scoped lang="scss">
section {
position: relative;
overflow: visible;
margin-top: 1.66em;
padding: 1ex 0.95ex;
2021-11-11 23:37:42 +01:00
border: 0.1em solid var(--foreground);
2021-11-11 23:37:42 +01:00
border-radius: 4px;
header {
margin-bottom: 0.2em;
& > * {
position: absolute;
top: -0.66em;
margin: 0;
2021-12-30 23:24:38 +01:00
background: var(--background-lighter);
2021-11-11 23:37:42 +01:00
font-weight: 600;
line-height: 1;
padding: 0 0.75ex;
2021-12-02 20:49:59 +01:00
.icon {
display: inline-block;
font-size: 1.25em;
2021-12-02 20:49:59 +01:00
margin-top: -0.3em;
}
.icon {
position: relative;
bottom: -2px;
2021-11-11 23:37:42 +01:00
}
}
h3 {
left: 1ex;
}
.views {
2022-01-09 21:26:50 +01:00
display: flex;
2021-11-11 23:37:42 +01:00
right: 1ex;
font-size: 18px;
}
}
}
</style>