|
@@ -0,0 +1,162 @@
|
|
|
+<template>
|
|
|
+ <article
|
|
|
+ class="node-view"
|
|
|
+ :class="['node-view-' + mode, 'node-view-' + type, 'node-view-' + nodeVariant]"
|
|
|
+ >
|
|
|
+ <div v-if="!loading" class="node-view-wrapper">
|
|
|
+ <component
|
|
|
+ :is="'node-view-header-' + type"
|
|
|
+ v-bind="{ node, mode }"
|
|
|
+ class="node-view-header"
|
|
|
+ />
|
|
|
+
|
|
|
+ <node-view-body v-bind="{ content: node.content, type: nodeType, mode }" />
|
|
|
+
|
|
|
+ <node-view-footer
|
|
|
+ v-bind="{ node, mode, type: nodeType }"
|
|
|
+ class="node-view-footer"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <b-overlay
|
|
|
+ :show="loading"
|
|
|
+ :spinner-variant="nodeVariant === 'depart' ? 'dark' : 'light'"
|
|
|
+ no-wrap
|
|
|
+ />
|
|
|
+
|
|
|
+ <slot name="bottom" />
|
|
|
+ </article>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ NodeViewHeaderRef,
|
|
|
+ NodeViewHeaderProd,
|
|
|
+ NodeViewBody,
|
|
|
+ NodeViewFooter
|
|
|
+} from '@/components/nodes'
|
|
|
+
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'NodeView',
|
|
|
+
|
|
|
+ components: {
|
|
|
+ NodeViewHeaderRef,
|
|
|
+ NodeViewHeaderProd,
|
|
|
+ NodeViewFooter,
|
|
|
+ NodeViewBody
|
|
|
+ },
|
|
|
+
|
|
|
+ props: {
|
|
|
+ node: { type: Object, default: undefined },
|
|
|
+ variant: { type: String, default: 'dark' },
|
|
|
+ type: { type: String, default: 'ref' },
|
|
|
+ mode: { type: String, default: 'view' }
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ loading () {
|
|
|
+ const dataLevel = this.mode === 'view' ? 2 : 1
|
|
|
+ return this.node === undefined || this.node.dataLevel < dataLevel
|
|
|
+ },
|
|
|
+
|
|
|
+ nodeVariant () {
|
|
|
+ return this.node !== undefined ? this.node.variant : this.variant
|
|
|
+ },
|
|
|
+
|
|
|
+ nodeType () {
|
|
|
+ return this.node !== undefined ? this.node.type : this.type
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.node-view {
|
|
|
+ position: relative;
|
|
|
+
|
|
|
+ &-wrapper {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // ╭─╴╭─┐┌─╮┌─╮
|
|
|
+ // │ ├─┤├┬╯│ │
|
|
|
+ // ╰─╴╵ ╵╵ ╰└─╯
|
|
|
+
|
|
|
+ &-card {
|
|
|
+ width: 560px;
|
|
|
+ height: 330px;
|
|
|
+ max-width: 560px;
|
|
|
+ max-height: 330px;
|
|
|
+ box-shadow: .5rem .5rem 1rem rgba($black, .25);
|
|
|
+ }
|
|
|
+
|
|
|
+ &-card &-header {
|
|
|
+ padding: $node-card-spacer-sm-y $node-card-spacer-sm-x 0;
|
|
|
+
|
|
|
+ @include media-breakpoint-up(md) {
|
|
|
+ padding: $node-card-spacer-y $node-card-spacer-x 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-card &-footer {
|
|
|
+ padding: $node-card-spacer-y $node-card-spacer-x $node-card-spacer-y * 2;
|
|
|
+
|
|
|
+ @include media-breakpoint-up(md) {
|
|
|
+ padding: $node-card-spacer-sm-y $node-card-spacer-sm-x $node-card-spacer-sm-y * 2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // ╷ ╷╶┬╴┌─╴╷╷╷
|
|
|
+ // │╭╯ │ ├─╴│││
|
|
|
+ // ╰╯ ╶┴╴╰─╴╰╯╯
|
|
|
+
|
|
|
+ &-view {
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ @include media-breakpoint-up(md) {
|
|
|
+ height: 100%;
|
|
|
+ overflow-y: auto;
|
|
|
+
|
|
|
+ .node-view-wrapper {
|
|
|
+ min-height: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-view &-header {
|
|
|
+ padding: $node-view-spacer-sm-y $node-view-spacer-sm-x 0;
|
|
|
+
|
|
|
+ @include media-breakpoint-up(md) {
|
|
|
+ padding: $node-view-spacer-y $node-view-spacer-x 0;
|
|
|
+ position: sticky;
|
|
|
+ z-index: 1;
|
|
|
+ top: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &-view &-footer {
|
|
|
+ padding: $node-view-spacer-sm-y $node-view-spacer-sm-x;
|
|
|
+
|
|
|
+ @include media-breakpoint-up(md) {
|
|
|
+ padding: $node-view-spacer-y $node-view-spacer-x;
|
|
|
+ position: sticky;
|
|
|
+ bottom: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @each $color, $value in $theme-colors {
|
|
|
+ &-#{$color} {
|
|
|
+ background-color: lighten($value, 3.25%);
|
|
|
+
|
|
|
+ .node-view-header,
|
|
|
+ .node-view-footer {
|
|
|
+ background-color: lighten($value, 3.25%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|