NodeBook.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <template>
  2. <div
  3. class="node-book"
  4. >
  5. <section
  6. class="node-book-stack"
  7. :style="`--count: ${nodes.length - 1}; --h: ${stackHeight}px;`"
  8. >
  9. <div
  10. v-for="({ parent, children }, i) in nodes" :key="parent.id"
  11. class="node-book-stack-item node-book-stack-item-parent splitted no-events-container"
  12. :class="{ active: i === nodes.length - 1 }"
  13. :style="`--nth: ${i};`"
  14. >
  15. <node-view-child-list
  16. v-if="parent.children && parent.children.length"
  17. :children="parent.children"
  18. smartphone
  19. @open-child="$emit('open-node', { parentId: parent.id, ...$event })"
  20. />
  21. <node-view
  22. :ref="'parent-' + parent.id"
  23. :node="parent"
  24. type="ref"
  25. mode="view"
  26. @open-node="$emit('open-node', $event)"
  27. @open-child="$emit('open-node', { parentId: parent.id, ...$event })"
  28. @close-node="$emit('close-node', { parentId: $event })"
  29. @click.native="onNodeViewClick($event, i)"
  30. />
  31. <section
  32. class="node-book-stack no-events-container"
  33. :style="`--count: ${children.length }; --h: ${getChildrenStackHeight(children.length)}px;`"
  34. >
  35. <div
  36. v-for="(child, j) in children" :key="child.id + '-cont'"
  37. class="node-book-stack-item"
  38. :class="{ active: j === children.length - 1 && i === nodes.length - 1 }"
  39. :style="`--nth: ${j}`"
  40. >
  41. <node-view
  42. :ref="'child-' + child.id"
  43. :key="child.id"
  44. :node="child"
  45. mode="view"
  46. type="prod"
  47. @open-node="$emit('open-node', $event)"
  48. @close-node="$emit('close-node', { parentId: parent.id, childId: $event })"
  49. @click.native="onNodeViewClick($event, i, j)"
  50. />
  51. </div>
  52. </section>
  53. </div>
  54. </section>
  55. </div>
  56. </template>
  57. <script>
  58. import { NodeViewChildList, NodeView } from '@/components/nodes'
  59. export default {
  60. name: 'NodeBook',
  61. components: {
  62. NodeViewChildList,
  63. NodeView
  64. },
  65. props: {
  66. nodes: { type: Array, required: true }
  67. },
  68. computed: {
  69. stackHeight () {
  70. return Math.min(150 / (this.nodes.length - 1), 75)
  71. }
  72. },
  73. methods: {
  74. getChildrenStackHeight (childrenLen) {
  75. return this.stackHeight / childrenLen || this.stackHeight
  76. },
  77. onNodeViewClick (e, parentIndex, childIndex) {
  78. if (e.target.classList.contains('btn') || window.innerWidth < 1024) return
  79. const lastChildIndex = this.nodes[parentIndex].children.length - 1
  80. const parentIsLast = parentIndex === this.nodes.length - 1
  81. const childIsLast = childIndex !== undefined
  82. ? childIndex === lastChildIndex
  83. : true
  84. if (parentIsLast && childIsLast) return
  85. const ids = { parentId: this.nodes[parentIndex].parent.id }
  86. if (!parentIsLast) {
  87. this.$refs['parent-' + ids.parentId][0].onScroll({}, true)
  88. }
  89. if (childIndex !== undefined) {
  90. ids.childId = this.nodes[parentIndex].children[childIndex].id
  91. }
  92. if (childIndex !== undefined || lastChildIndex > -1) {
  93. const childId = childIndex !== undefined ? ids.childId : this.nodes[parentIndex].children[lastChildIndex].id
  94. console.log(childId)
  95. this.$refs['child-' + childId][0].onScroll({}, true)
  96. }
  97. this.$emit('select-node', ids)
  98. }
  99. },
  100. updated () {
  101. const smarphoneStickyNav = document.querySelector('.node-view-child-list.smartphone')
  102. const offset = smarphoneStickyNav ? smarphoneStickyNav.getBoundingClientRect().height : 0
  103. const nodeviews = this.$el.querySelectorAll('.node-view')
  104. document.querySelector('main').scrollTo(0, nodeviews[nodeviews.length - 1].offsetTop - offset)
  105. }
  106. }
  107. </script>
  108. <style lang="scss" scoped>
  109. .node-book {
  110. min-height: 100%;
  111. background-color: rgba($white, .5);
  112. @include media-breakpoint-up($layout-bp) {
  113. height: 100%;
  114. // ╭─╴╶┬╴╭─┐╭─╴╷ ╭
  115. // ╰─╮ │ ├─┤│ ├┴╮
  116. // ╶─╯ ╵ ╵ ╵╰─╴╵ ╵
  117. &-stack {
  118. position: relative;
  119. height: 100%;
  120. width: 100%;
  121. display: flex;
  122. flex-direction: column;
  123. &-item {
  124. width: 100%;
  125. height: 100%;
  126. position: absolute;
  127. top: calc(var(--h) * var(--nth));
  128. height: calc(100% - (var(--h) * var(--nth)));
  129. ::v-deep > .node-view {
  130. border-top: $line;
  131. }
  132. &-parent > .node-view {
  133. border-right: $line;
  134. }
  135. &:not(.active) {
  136. ::v-deep > .node-view {
  137. cursor: pointer;
  138. overflow-y: hidden;
  139. &:not(:hover) {
  140. .node-view-header-wrapper {
  141. opacity: .5;
  142. }
  143. }
  144. .node-view-body-wrapper,
  145. .node-view-footer-wrapper {
  146. opacity: .5;
  147. pointer-events: none;
  148. }
  149. .node-view-header {
  150. z-index: 0;
  151. padding-top: $node-view-spacer-y * .75;
  152. padding-bottom: $node-view-spacer-y * .75;
  153. }
  154. .node-view-header-ref {
  155. h4 {
  156. font-size: 1.2rem;
  157. }
  158. .edition {
  159. font-size: 0.8rem;
  160. display: none;
  161. }
  162. .btn-url {
  163. width: 20px;
  164. height: 20px;
  165. }
  166. }
  167. .node-view-child-list {
  168. display: none;
  169. }
  170. @each $color, $value in $theme-colors {
  171. &-#{$color} {
  172. background-color: lighten($value, 15%);
  173. .node-view-header,
  174. .node-view-footer {
  175. background-color: lighten($value, 15%);
  176. }
  177. }
  178. }
  179. &:hover {
  180. .node-view-header {
  181. z-index: 100;
  182. border-bottom: $line !important;
  183. padding-bottom: calc(#{$node-view-spacer-y * .75} - 2px);
  184. h4 {
  185. margin-bottom: 0;
  186. }
  187. }
  188. }
  189. }
  190. }
  191. &.splitted {
  192. display: flex;
  193. flex-basis: 50%;
  194. }
  195. }
  196. }
  197. }
  198. }
  199. </style>