Browse Source

add notes to NodeViewBody

axolotle 3 năm trước cách đây
mục cha
commit
600972b8cd

+ 15 - 0
src/assets/scss/abstracts/_variables.scss

@@ -130,6 +130,21 @@ $custom-range-thumb-active-bg:               $black;
 // SPINNER
 $spinner-width: 50px;
 
+// POPOVERS
+$popover-font-size: $font-size-base;
+$popover-bg: $black;
+$popover-max-width: 450px;
+$popover-border-width: 0;
+$popover-box-shadow: null;
+
+$popover-body-color: $white;
+$popover-body-padding-y: 18px;
+$popover-body-padding-x: 18px;
+
+$popover-arrow-width: 0;
+$popover-arrow-height: 1rem;
+
+$popover-arrow-outer-color: null;
 
 // ╭─╴╷ ╷╭─╴╶┬╴╭─╮╭╮╮
 // │  │ │╰─╮ │ │ ││││

+ 9 - 0
src/assets/scss/base/_bootstrap-overrides.scss

@@ -2,6 +2,11 @@ legend {
   font-weight: $font-weight-bold;
 }
 
+a,
+a:hover {
+  color: inherit;
+}
+
 .dropdown-menu {
   border: 0;
   padding: 0;
@@ -22,3 +27,7 @@ legend {
     }
   }
 }
+
+.popover {
+  font-family: $font-family-text;
+}

+ 2 - 2
src/components/nodes/NodeView.vue

@@ -3,14 +3,14 @@
     class="node-view"
     :class="['node-view-' + mode, 'node-view-' + type, 'node-view-' + nodeVariant]"
   >
-    <div v-if="!loading" class="node-view-wrapper">
+    <div v-if="!loading" class="node-view-wrapper" :id="'node-' + node.id">
       <component
         :is="'node-view-header-' + nodeType"
         v-bind="{ node, mode }"
         class="node-view-header"
       />
 
-      <node-view-body v-bind="{ content: node.content, type: nodeType, mode }" />
+      <node-view-body v-bind="{ content: node.content, nodeId: node.id, notes: node.notes, type: nodeType, mode }" />
 
       <node-view-footer
         v-bind="{ node, mode, type: nodeType }"

+ 111 - 7
src/components/nodes/NodeViewBody.vue

@@ -5,47 +5,121 @@
   >
     <slot name="default">
       <div class="node-view-body-wrapper" v-html="content" />
+
+      <b-popover
+        v-for="note in notes" :key="note.number"
+        custom-class="footnote"
+        :target="'note-' + note.number" :container="'node-' + nodeId"
+        placement="top" :fallback-placement="['bottom', 'right', 'left']"
+        :triggers="['focus']" :boundary-padding="0"
+      >
+        <div class="footnote-content" v-html="note.content" />
+
+        <div class="footnote-child-list" v-if="note.links">
+          <node-view-title
+            v-for="link in note.links" :key="link.id"
+            :node="link.parents && link.parents.length ? link.parents[0] : link"
+            link
+            @open-node="onFootnoteLinkClick(link)"
+          >
+            <dot-button
+              :variant="link.variant"
+              class="mr-2"
+              active
+            >
+              {{ $t('variants.' + link.variant) }}
+            </dot-button>
+          </node-view-title>
+        </div>
+      </b-popover>
     </slot>
   </div>
 </template>
 
 <script>
+import { NodeViewTitle } from '@/components/nodes'
+
+
 export default {
   name: 'NodeViewBody',
 
+  components: {
+    NodeViewTitle
+  },
+
   props: {
     content: { type: String, default: null },
+    notes: { type: Array, default: null },
+    nodeId: { type: Number, required: true },
     type: { type: String, required: true },
     mode: { type: String, required: true }
+  },
+
+  methods: {
+    addFootnotes () {
+      const links = this.$el.querySelectorAll('a')
+      links.forEach((link, i) => {
+        const number = parseInt(link.hash.replace('#', ''))
+        if (!isNaN(number)) {
+          link.classList.add('footnote-link')
+          link.id = 'note-' + number
+          link.innerHTML = link.textContent
+          if (link.parentElement.nodeName === 'SUP') {
+            link.parentElement.replaceWith(link)
+          }
+        }
+      })
+    },
+
+    onFootnoteLinkClick (node) {
+      if (node.parents && node.parents.length) {
+        this.$parent.$emit('open-node', { parentId: node.parents[0].id, childId: node.id })
+      } else {
+        this.$parent.$emit('open-node', { parentId: node.id })
+      }
+    }
+  },
+
+  created () {
+    if (this.mode === 'view' && this.notes) {
+      // Query partial data for linked nodes in notes
+      const ids = this.notes.filter(note => (note.links)).reduce((ids, note) => {
+        return [...ids, ...note.links.map(link => link.id)]
+      }, [])
+      this.$store.dispatch('GET_NODES', { ids, dataLevel: 'initial' })
+    }
+  },
+
+  mounted () {
+    if (this.mode === 'view' && this.notes) this.addFootnotes()
   }
 }
 </script>
 
-<style lang="scss" scoped>
+<style lang="scss">
 .node-view-body {
   width: 100%;
 
   font: {
     family: $font-family-text;
     line-height: inherit;
-
   }
 
   @include media-breakpoint-up(sm) {
     font-size: 2rem;
   }
 
-  .node-view-view & {
+  &-view {
     padding: 0 $node-view-spacer-sm-x;
     font-size: 1.25rem;
 
     @include media-breakpoint-up(sm) {
-      &-ref {
+      &.node-view-body-ref {
         font-size: 2.6rem;
       }
 
-      &-prod {
-        font-size: 1.5rem !important;
+      &.node-view-body-prod {
+        font-size: 1.5rem;
       }
     }
 
@@ -54,7 +128,7 @@ export default {
     }
   }
 
-  .node-view-card & {
+  &-card {
     padding: 0 $node-card-spacer-sm-x;
     font-size: 1.1rem;
 
@@ -70,5 +144,35 @@ export default {
   &-card &-wrapper {
     @include line-clamp(3, 1.5em);
   }
+  &-card#{&}-prod &-wrapper {
+    @include line-clamp(4, 1.5em);
+  }
+
+  .footnote-link {
+    display: inline-block;
+    text-align: center;
+    background-color: $black;
+    color: $white;
+    font-family: $font-family-text;
+    font-weight: $font-weight-bold;
+    font-size: 0.8em;
+    text-decoration: none;
+    border-radius: 1em;
+    padding: 0 .5em;
+    min-width: 1.25em;
+    max-height: 1.4em;
+    margin: 0 .2em;
+  }
+}
+
+.footnote-content,
+.footnote-child-list {
+  > :last-child {
+    margin-bottom: 0;
+  }
+}
+
+.footnote-child-list {
+  margin-top: .5rem;
 }
 </style>