NodeBook.vue 6.4 KB

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