Library.vue 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <template>
  2. <div class="library">
  3. <!-- BACKGROUND (mode) -->
  4. <component :is="mode" @open="openText" />
  5. <!-- FOREGROUND (texts) -->
  6. <div v-if="parents.length" class="main-texts-container">
  7. <section
  8. v-for="(parent, i) in parents" :key="parent.id"
  9. class="main-text-container text-break"
  10. :style="`--n: ${i};`"
  11. :class="{ 'text-blur': i !== parents.length - 1 }"
  12. >
  13. <text-card
  14. :id="parent.id"
  15. :children="parent.children"
  16. class="text-card-main"
  17. @open="openText" @close="closeText"
  18. @click.native="onMainTextClick(i)"
  19. />
  20. <section class="sub-texts-container">
  21. <text-card
  22. v-for="(id, j) in parent.children" :key="id"
  23. :id="id"
  24. class="text-card-sub text-break"
  25. :style="`--n: ${j};`"
  26. @close="closeText(parent.id, $event)"
  27. @click.native="onSubTextClick(i, j)"
  28. />
  29. <!-- :class="{ 'text-blur': i !== parents.length - 1 || j !== parent.children.length - 1 }" -->
  30. </section>
  31. </section>
  32. </div>
  33. </div>
  34. </template>
  35. <script>
  36. import { CardList, CardMap, TreeMap } from './library'
  37. import TextCard from '@/components/text/TextCard'
  38. export default {
  39. name: 'Library',
  40. components: {
  41. CardList,
  42. CardMap,
  43. TreeMap,
  44. TextCard
  45. },
  46. props: {
  47. mode: { type: String, required: true },
  48. texts: { type: Array, required: true }
  49. },
  50. computed: {
  51. parents () {
  52. return this.texts.map(text => {
  53. const [id, ...children] = text
  54. return { id, children }
  55. })
  56. }
  57. },
  58. methods: {
  59. openText (id, childId) {
  60. const parent = this.parents.find(p => p.id === id)
  61. if (parent && (childId === undefined || parent.children.includes(childId))) return
  62. if (!parent) {
  63. this.parents.push({ id, children: childId ? [childId] : [] })
  64. } else if (childId) {
  65. parent.children.push(childId)
  66. }
  67. this.updateQuery(this.parents)
  68. },
  69. closeText (id, childId) {
  70. const parent = this.parents.find(p => p.id === id)
  71. if (!childId) {
  72. this.updateQuery(this.parents.filter(p => p !== parent))
  73. } else {
  74. parent.children = parent.children.filter(childId_ => childId_ !== childId)
  75. this.updateQuery(this.parents)
  76. }
  77. },
  78. updateQuery (parents) {
  79. // Update the route's query (will not reload the page) and let vue determine what changed.
  80. this.$router.push({
  81. query: {
  82. mode: this.mode,
  83. texts: parents.map(parent => [parent.id, ...parent.children])
  84. }
  85. })
  86. },
  87. onMainTextClick (mainTextIndex) {
  88. if (mainTextIndex === this.texts.length - 1) return
  89. this.parents.push(this.parents.splice(mainTextIndex, 1)[0])
  90. this.updateQuery(this.parents)
  91. },
  92. onSubTextClick (mainTextIndex, subTextIndex) {
  93. // FIXME clean this
  94. const children = this.parents[mainTextIndex].children
  95. let needUpdate = false
  96. if (subTextIndex !== children.length - 1) {
  97. children.push(children.splice(subTextIndex, 1)[0])
  98. needUpdate = true
  99. }
  100. if (mainTextIndex !== this.texts.length - 1) {
  101. this.parents.push(this.parents.splice(mainTextIndex, 1)[0])
  102. needUpdate = true
  103. }
  104. if (needUpdate) {
  105. this.updateQuery(this.parents)
  106. }
  107. }
  108. }
  109. }
  110. </script>
  111. <style lang="scss" scoped>
  112. .library {
  113. height: 100%;
  114. width: 100%;
  115. overflow: hidden;
  116. }
  117. .main-texts-container {
  118. position: relative;
  119. top: -100%;
  120. height: 100%;
  121. pointer-events: none;
  122. }
  123. .main-text-container {
  124. border-top: 2px solid $black;
  125. display: flex;
  126. width: 100%;
  127. > * {
  128. flex-basis: 50%;
  129. }
  130. }
  131. .sub-texts-container {
  132. position: relative;
  133. height: 100%;
  134. }
  135. .text-card {
  136. pointer-events: auto;
  137. width: 100%;
  138. }
  139. .text-break {
  140. position: absolute;
  141. height: calc(100% - (var(--n) * #{$text-card-header-height}));
  142. top: calc(var(--n) * #{$text-card-header-height});
  143. z-index: var(--n);
  144. }
  145. .text-blur::before {
  146. content: '';
  147. pointer-events: none;
  148. position: absolute;
  149. z-index: 1;
  150. display: block;
  151. width: 100%;
  152. height: 100%;
  153. background-color: rgba($white, .5);
  154. }
  155. </style>