196 lines
4.0 KiB
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>
|