NodeViewBody.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <template>
  2. <div
  3. class="node-view-body"
  4. :class="['node-view-body-' + mode, 'node-view-body-' + type]"
  5. >
  6. <slot name="default">
  7. <div class="node-view-body-wrapper" v-html="content" />
  8. <b-popover
  9. v-for="note in notes" :key="note.number"
  10. custom-class="footnote"
  11. :target="'note-' + note.number" :container="`node-${mode}-${nodeId}`"
  12. placement="top" :fallback-placement="['bottom', 'right', 'left']"
  13. :triggers="['focus']"
  14. >
  15. <div class="footnote-content" v-html="note.content" />
  16. <div class="footnote-child-list" v-if="note.links">
  17. <node-view-title
  18. v-for="link in note.links" :key="link.id"
  19. :node="link.parents && link.parents.length ? link.parents[0] : link"
  20. link
  21. @open-node="onFootnoteLinkClick(link, 'note-' + note.number)"
  22. >
  23. <dot-button
  24. :variant="link.variant"
  25. class="mr-2"
  26. active
  27. >
  28. {{ $t('variants.' + link.variant) }}
  29. </dot-button>
  30. </node-view-title>
  31. </div>
  32. </b-popover>
  33. </slot>
  34. </div>
  35. </template>
  36. <script>
  37. import { getRelation } from '@/store/utils'
  38. import { NodeViewTitle } from '@/components/nodes'
  39. export default {
  40. name: 'NodeViewBody',
  41. components: {
  42. NodeViewTitle
  43. },
  44. props: {
  45. content: { type: String, default: null },
  46. notes: { type: Array, default: null },
  47. nodeId: { type: Number, required: true },
  48. type: { type: String, required: true },
  49. mode: { type: String, required: true }
  50. },
  51. methods: {
  52. addFootnotes () {
  53. const links = this.$el.querySelectorAll('a')
  54. links.forEach((link, i) => {
  55. const number = parseInt(link.hash.replace('#', ''))
  56. if (!isNaN(number)) {
  57. link.classList.add('footnote-link')
  58. link.id = 'note-' + number
  59. link.innerHTML = link.textContent
  60. if (link.parentElement.nodeName === 'SUP') {
  61. link.parentElement.replaceWith(link)
  62. }
  63. link.setAttribute('href', 'javascript:;')
  64. }
  65. })
  66. },
  67. onFootnoteLinkClick (node, popoverBtnId) {
  68. this.$root.$emit('bv::hide::popover', popoverBtnId)
  69. this.$parent.$emit('open-node', getRelation(node))
  70. }
  71. },
  72. created () {
  73. if (this.mode === 'view' && this.notes) {
  74. // Query partial data for linked nodes in notes
  75. const ids = this.notes.filter(note => (note.links)).reduce((ids, note) => {
  76. return [...ids, ...note.links.map(link => link.id)]
  77. }, [])
  78. this.$store.dispatch('GET_NODES', { ids, dataLevel: 'initial' })
  79. }
  80. },
  81. mounted () {
  82. if (this.mode === 'view' && this.notes) this.addFootnotes()
  83. this.$el.querySelectorAll('img, video, audio').forEach(elem => elem.parentElement.classList.add('media-container'))
  84. }
  85. }
  86. </script>
  87. <style lang="scss">
  88. .node-view-body {
  89. width: 100%;
  90. img {
  91. max-width: 100%;
  92. }
  93. font: {
  94. family: $font-family-text;
  95. line-height: inherit;
  96. }
  97. @include media-breakpoint-up($size-bp) {
  98. font-size: 2rem;
  99. }
  100. &-view {
  101. padding: 0 $node-view-spacer-sm-x;
  102. font-size: 1.25rem;
  103. word-wrap: break-word;
  104. @include media-breakpoint-up($size-bp) {
  105. padding: 0 $node-view-spacer-x;
  106. &.node-view-body-ref {
  107. font-size: 2.6rem;
  108. }
  109. &.node-view-body-prod {
  110. font-size: 1.5rem;
  111. }
  112. }
  113. }
  114. &-card {
  115. padding: 0 $node-card-spacer-sm-x;
  116. font-size: 1.1rem;
  117. word-wrap: anywhere;
  118. @include media-breakpoint-up($size-bp) {
  119. font-size: 2rem;
  120. padding: 0 $node-card-spacer-x;
  121. }
  122. }
  123. &-card &-wrapper {
  124. @include line-clamp(3, 1.5em);
  125. }
  126. &-card#{&}-prod &-wrapper {
  127. @include line-clamp(4, 1.5em);
  128. }
  129. .footnote-link {
  130. display: inline-block;
  131. text-align: center;
  132. background-color: $black;
  133. color: $white;
  134. font-family: $font-family-text;
  135. font-weight: $font-weight-bold;
  136. font-size: 0.8em;
  137. text-decoration: none;
  138. border-radius: 1em;
  139. padding: 0 .5em;
  140. min-width: 1.25em;
  141. max-height: 1.4em;
  142. margin: 0 .2em;
  143. }
  144. }
  145. .footnote {
  146. background-color: $black;
  147. color: $white;
  148. font-size: 1.25rem;
  149. @include media-breakpoint-up($size-bp) {
  150. font-size: 1.5rem;
  151. }
  152. h6 {
  153. font-size: inherit;
  154. }
  155. &-content,
  156. &-child-list {
  157. > :last-child {
  158. margin-bottom: 0;
  159. }
  160. }
  161. &-child-list {
  162. margin-top: .5rem;
  163. }
  164. }
  165. </style>