123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- <template>
- <article
- class="node-view"
- :class="['node-view-' + mode, 'node-view-' + type, 'node-view-' + nodeVariant, { 'preview': preview }]"
- :style="`--scroll-top: ${scrollValue}px; --h-height: ${h}px;`"
- >
- <div v-if="!loading" class="node-view-wrapper" :id="`node-${mode}-${node.id}`">
- <component
- ref="header"
- :is="'node-view-header-' + (nodeType === 'prod' && (node.preTitle || node.italTitle) ? 'ref' : nodeType)"
- v-bind="{ node, mode, showOrigin }"
- class="node-view-header"
- :class="{ scrolling: scrollValue }"
- :first-char="firstChar"
- />
- <node-view-body v-bind="{ node, type: nodeType, mode }" :class="{ scrolling: scrollValue }"/>
- <node-view-footer
- v-bind="{ node, mode, type: nodeType, preview, showOrigin }"
- class="node-view-footer"
- />
- </div>
- <node-view-child-list-group
- v-if="!loading && preview && node.children && node.children.length"
- :children="node.children" :id="node.id"
- />
- <b-overlay
- :show="loading"
- :spinner-variant="nodeVariant === 'depart' ? 'dark' : 'light'"
- no-wrap
- />
- </article>
- </template>
- <script>
- import {
- NodeViewChildListGroup,
- NodeViewHeaderRef,
- NodeViewHeaderProd,
- NodeViewBody,
- NodeViewFooter
- } from '@/components/nodes'
- export default {
- name: 'NodeView',
- components: {
- NodeViewChildListGroup,
- NodeViewHeaderRef,
- NodeViewHeaderProd,
- NodeViewFooter,
- NodeViewBody
- },
- props: {
- node: { type: Object, default: undefined },
- variant: { type: String, default: 'dark' },
- type: { type: String, default: 'ref' },
- forceType: { type: String, default: null },
- mode: { type: String, default: 'view' },
- preview: { type: Boolean, default: false },
- showOrigin: { type: Boolean, default: false },
- firstChar: { type: [String, null], default: null }
- },
- data () {
- return {
- scrollValue: 0,
- h: 0
- }
- },
- 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 () {
- if (this.forceType) return this.forceType
- return this.node !== undefined ? this.node.type : this.type
- }
- },
- methods: {
- onScroll ({ target = this.$el }, fix = false) {
- const prev = this.scrollValue
- this.scrollValue = target.scrollTop
- if (this.scrollValue && !prev) {
- const h = this.$refs.header.$el.getBoundingClientRect().height
- this.$nextTick(() => {
- this.h = h - this.$refs.header.$el.getBoundingClientRect().height
- })
- }
- if (fix) {
- setTimeout(() => {
- if (this.scrollValue > 0 && target.scrollTop === 0) {
- target.scrollTop = this.scrollValue
- }
- }, 50)
- }
- }
- },
- mounted () {
- this.scrolling = this.$el.scrollTop !== 0
- this.$el.addEventListener('scroll', this.onScroll)
- },
- beforeDestroy () {
- this.$el.removeEventListener('scroll', this.onScroll)
- }
- }
- </script>
- <style lang="scss">
- .node-view {
- position: relative;
- &-wrapper {
- display: flex;
- flex-direction: column;
- }
- // ╭─╴╭─┐┌─╮┌─╮
- // │ ├─┤├┬╯│ │
- // ╰─╴╵ ╵╵ ╰└─╯
- &-card {
- width: 100%;
- .node-view-wrapper {
- pointer-events: none;
- width: 100%;
- height: 100%;
- overflow: hidden;
- }
- &.node-view-depart {
- box-shadow: .5rem .5rem 1rem rgba($black, .25);
- .node-view-child-list-group {
- 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($size-bp) {
- padding: $node-card-spacer-y $node-card-spacer-x 0;
- }
- }
- &-card &-footer {
- padding: $node-card-spacer-sm-y $node-card-spacer-sm-x $node-card-spacer-sm-y * 2;
- position: sticky;
- bottom: 0;
- @include media-breakpoint-up($size-bp) {
- padding: $node-card-spacer-y $node-card-spacer-x $node-card-spacer-y * 2;
- }
- }
- // ╷ ╷╶┬╴┌─╴╷╷╷
- // │╭╯ │ ├─╴│││
- // ╰╯ ╶┴╴╰─╴╰╯╯
- &-view {
- width: 100%;
- @include media-breakpoint-up($layout-bp) {
- 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($size-bp) {
- padding: $node-view-spacer-y $node-view-spacer-x $node-view-spacer-y * .75;
- h4 {
- margin-bottom: 0;
- }
- }
- @include media-breakpoint-up($layout-bp) {
- position: sticky;
- top: 0;
- z-index: 1;
- &-ref.scrolling {
- h4 {
- font-size: 1.2rem;
- }
- .edition {
- font-size: 0.8rem;
- }
- .btn-url {
- width: 20px;
- height: 20px;
- }
- }
- }
- }
- &-view &-body {
- @include media-breakpoint-up($layout-bp) {
- max-height: 100%;
- &.scrolling {
- clip-path: inset(var(--scroll-top) 0px 0px 0px);
- &.node-view-body-ref {
- padding-top: var(--h-height);
- }
- }
- }
- }
- &-view &-footer {
- padding: $node-view-spacer-sm-y $node-view-spacer-sm-x;
- @include media-breakpoint-up($size-bp) {
- padding: $node-view-spacer-y $node-view-spacer-x;
- }
- @include media-breakpoint-up($layout-bp) {
- 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>
|