upend/ui/src/components/AttributeView.vue

196 lines
4.0 KiB
Vue

<template>
<section class="attribute-view">
<header>
<h3>
<up-link v-if="type" :to="{ entity: type.address }">
<sl-icon v-if="type.icon" :name="type.icon" />
{{ type.name || "???" }}
</up-link>
<template v-else> {{ title || "???" }} </template>
</h3>
<div class="views" v-if="availableWidgets.length > 1 || editable">
<sl-icon-button
v-for="widget in availableWidgets"
:key="widget.name"
:name="widget.icon || 'question-diamond'"
:class="{ active: widget.name === currentWidget }"
@click="currentWidget = widget.name"
/>
</div>
</header>
<component
v-for="component in components"
:key="component.id"
:is="component.name"
v-bind="component.props"
:attributes="attributes"
:editable="editable"
:reverse="reverse"
@edit="processChange"
/>
</section>
</template>
<script lang="ts">
import UpLink from "@/components/UpLink.vue";
import Compass from "@/components/widgets/Compass.vue";
import Table from "@/components/widgets/Table.vue";
import { UpType, Widget } from "@/lib/types";
import { AttributeChange, IEntry } from "@/types/base";
import { ComponentOptions, defineComponent, PropType } from "vue";
export default defineComponent({
name: "AttributeView",
components: {
UpLink,
Table,
Compass,
},
emits: ["edit"],
props: {
attributes: {
type: Array as PropType<[string, IEntry][]>,
required: true,
},
type: {
type: Object as PropType<UpType>,
},
address: {
type: String,
required: true,
},
title: {
type: String,
required: false,
},
editable: {
type: Boolean,
default: false,
},
reverse: {
type: Boolean,
default: false,
},
},
data() {
return {
currentWidget: "table",
};
},
computed: {
availableWidgets(): Widget[] {
const result = [] as Widget[];
if (this.type?.widgetInfo) {
result.push(this.type.widgetInfo);
}
result.push({
name: "table",
icon: "table",
components: [
{
name: "Table",
},
],
});
return result;
},
components(): ComponentOptions[] {
return this.availableWidgets.find((w) => w.name === this.currentWidget)!
.components;
},
},
methods: {
async processChange(change: AttributeChange) {
switch (change.type) {
case "create":
await fetch(`/api/obj`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
entity: this.address,
attribute: change.attribute,
value: {
t: "Value",
c: change.value,
},
}),
});
break;
case "delete":
await fetch(`/api/obj/${change.addr}`, { method: "DELETE" });
break;
default:
console.error(`Unimplemented: ${JSON.stringify(change)}`);
}
this.$emit("edit");
},
},
mounted() {
this.currentWidget = this.availableWidgets[0].name;
},
});
</script>
<style scoped lang="scss">
section {
position: relative;
overflow: visible;
margin-top: 1.66em;
padding: 1ex 1em;
border: 1px solid var(--foreground);
border-radius: 4px;
header {
margin-bottom: 0.2em;
& > * {
position: absolute;
top: -0.66em;
margin: 0;
background: var(--background);
font-weight: 600;
line-height: 1;
padding: 0 0.75ex;
sl-icon {
margin-bottom: -2px;
}
a {
text-decoration: none;
}
}
h3 {
left: 1ex;
}
.views {
right: 1ex;
font-size: 18px;
sl-icon-button {
&::part(base) {
padding: 0 calc(0.75ex / 2);
}
&.active {
&::part(base) {
color: var(--foreground);
}
}
}
}
}
}
</style>