2
0
Эх сурвалжийг харах

feat: anchor - copy link to clipboard

NGPixel 8 жил өмнө
parent
commit
13bdb2edb7

+ 3 - 1
client/js/app.js

@@ -6,6 +6,7 @@
 import $ from 'jquery'
 import Vue from 'vue'
 import VueResource from 'vue-resource'
+import VueClipboards from 'vue-clipboards'
 import store from './store'
 import io from 'socket.io-client'
 import i18next from 'i18next'
@@ -36,6 +37,7 @@ import sourceComponent from './pages/source.component.js'
 // ====================================
 
 Vue.use(VueResource)
+Vue.use(VueClipboards)
 Vue.use(VueI18Next)
 
 i18next
@@ -91,7 +93,7 @@ $(() => {
     i18n,
     el: '#root',
     mounted() {
-      $('a').smoothScroll({ speed: 500, offset: -50 })
+      $('a:not(.toc-anchor)').smoothScroll({ speed: 500, offset: -50 })
       $('#header').sticky({ topSpacing: 0 })
       $('.sidebar-pagecontents').sticky({ topSpacing: 15, bottomSpacing: 75 })
     }

+ 44 - 7
client/js/components/anchor.vue

@@ -1,16 +1,53 @@
-<template>
-    <div>
-        <p>{{ msg }}</p>
-        <input type="text" v-model="msg" />
-    </div>
+<template lang="pug">
+  .modal(v-bind:class='{ "is-active": isShown }')
+    .modal-background
+    .modal-container
+      .modal-content
+        header.is-blue
+          span Copy link to this section
+        section
+          p.control.is-fullwidth
+            input.input(type='text', ref='anchorURLinput', v-model='anchorURL')
+        footer
+          a.button.is-grey.is-outlined(v-on:click='cancel') Discard
+          a.button.is-blue(v-clipboard='anchorURL', @success="clipboardSuccess", @error="clipboardError") Copy to Clipboard
 </template>
 
 <script>
+  import * as _ from 'lodash'
+
   export default {
     name: 'anchor',
     data () {
-      return {
-          msg: 'Welcome to Your Vue.js App'
+      return {}
+    },
+    computed: {
+      anchorURL () {
+        return window.location.href.split('#')[0] + '#' + this.$store.state.anchor.hash
+      },
+      isShown () {
+        return this.$store.state.anchor.shown
+      }
+    },
+    methods: {
+      cancel () {
+        this.$store.dispatch('anchorClose')
+      },
+      clipboardSuccess () {
+        this.$store.dispatch('alert', {
+          style: 'blue',
+          icon: 'clipboard',
+          msg: 'The URL has been copied to your clipboard.'
+        })
+        this.$store.dispatch('anchorClose')
+      },
+      clipboardError () {
+        this.$store.dispatch('alert', {
+          style: 'red',
+          icon: 'clipboard',
+          msg: 'Clipboard copy failed. Copy the URL manually.'
+        })
+        this.$refs.anchorURLinput.select()
       }
     }
   }

+ 2 - 0
client/js/store/index.js

@@ -2,6 +2,7 @@ import Vue from 'vue'
 import Vuex from 'vuex'
 
 import alert from './modules/alert'
+import anchor from './modules/anchor'
 import adminUsersCreate from './modules/admin-users-create'
 
 Vue.use(Vuex)
@@ -20,6 +21,7 @@ export default new Vuex.Store({
   getters: {},
   modules: {
     alert,
+    anchor,
     adminUsersCreate
   }
 })

+ 23 - 0
client/js/store/modules/anchor.js

@@ -0,0 +1,23 @@
+'use strict'
+
+export default {
+  state: {
+    shown: false,
+    hash: ''
+  },
+  getters: {},
+  mutations: {
+    anchorChange: (state, opts) => {
+      state.shown = (opts.shown === true)
+      state.hash = opts.hash || ''
+    }
+  },
+  actions: {
+    anchorOpen({ commit, dispatch }, hash) {
+      commit('anchorChange', { shown: true, hash })
+    },
+    anchorClose({ commit, dispatch }) {
+      commit('anchorChange', { shown: false })
+    }
+  }
+}

+ 1 - 0
package.json

@@ -157,6 +157,7 @@
     "uglify-js": "latest",
     "vee-validate": "^2.0.0-rc.3",
     "vue": "^2.3.3",
+    "vue-clipboards": "^1.0.0",
     "vue-resource": "^1.3.1",
     "vue-template-compiler": "^2.3.3",
     "vue-template-es2015-compiler": "^1.5.2",

+ 9 - 2
server/libs/markdown.js

@@ -20,7 +20,7 @@ var mkdown = md({
   html: true,
   linkify: true,
   typography: true,
-  highlight (str, lang) {
+  highlight(str, lang) {
     if (lang && hljs.getLanguage(lang)) {
       try {
         return '<pre class="hljs"><code>' + hljs.highlight(lang, str, true).value + '</code></pre>'
@@ -206,6 +206,13 @@ const parseContent = (content) => {
     cr(elm).replaceWith(txtLink)
   })
 
+  // -> Add anchor handler
+
+  cr('a.toc-anchor').each((i, elm) => {
+    let hashText = cr(elm).attr('href').slice(1)
+    cr(elm).attr('v-on:click.stop.prevent', "$store.dispatch('anchorOpen', '" + hashText + "')")
+  })
+
   // -> Re-attach blockquote styling classes to their parents
 
   cr.root().children('blockquote').each((i, elm) => {
@@ -313,7 +320,7 @@ module.exports = {
    * @param      {String}  content  Markdown-formatted content
    * @return     {Object}  Object containing meta, html and tree data
    */
-  parse (content) {
+  parse(content) {
     return {
       meta: parseMeta(content),
       html: parseContent(content),

+ 1 - 1
server/views/pages/admin/users.pug

@@ -1,7 +1,7 @@
 extends ./_layout.pug
 
 block rootNavRight
-  i.nav-item#notifload
+  loading-spinner
   .nav-item
     a.button(v-on:click='$store.dispatch("adminUsersCreateOpen")')
       i.icon-plus

+ 2 - 1
server/views/pages/view.pug

@@ -9,7 +9,7 @@ mixin tocMenu(ti)
           +tocMenu(node.nodes)
 
 block rootNavRight
-  i.nav-item#notifload
+  loading-spinner
   .nav-item
     if rights.write
       a.button.is-outlined.btn-move-prompt.is-hidden
@@ -83,3 +83,4 @@ block content
 
   include ../modals/create.pug
   include ../modals/move.pug
+  anchor

+ 0 - 2
server/views/pages/welcome.pug

@@ -2,8 +2,6 @@ extends ../layout.pug
 
 block rootNavCenter
 
-block rootNavRight
-  i.nav-item#notifload
 
 block content
 

+ 32 - 0
yarn.lock

@@ -1223,6 +1223,14 @@ cli-width@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
 
+clipboard@^1.5.15:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-1.6.1.tgz#65c5b654812466b0faab82dc6ba0f1d2f8e4be53"
+  dependencies:
+    good-listener "^1.2.0"
+    select "^1.1.2"
+    tiny-emitter "^1.0.0"
+
 clite@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/clite/-/clite-0.3.0.tgz#e7fcbc8cc5bd3e7f8b84ed48db12e9474cc73441"
@@ -1659,6 +1667,10 @@ delayed-stream@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
 
+delegate@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.1.2.tgz#1e1bc6f5cadda6cb6cbf7e6d05d0bcdd5712aebe"
+
 delegates@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -2749,6 +2761,12 @@ globule@^1.0.0:
     lodash "~4.16.4"
     minimatch "~3.0.2"
 
+good-listener@^1.2.0:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+  dependencies:
+    delegate "^3.1.2"
+
 got@^3.2.0:
   version "3.3.1"
   resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca"
@@ -5978,6 +5996,10 @@ scss-tokenizer@^0.2.3:
     lodash.union "^4.6.0"
     lodash.uniq "^4.5.0"
 
+select@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+
 semver-diff@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
@@ -6641,6 +6663,10 @@ timed-out@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
 
+tiny-emitter@^1.0.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-1.2.0.tgz#6dc845052cb08ebefc1874723b58f24a648c3b6f"
+
 tinycolor2@^1.1.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
@@ -6992,6 +7018,12 @@ void-elements@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
 
+vue-clipboards@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/vue-clipboards/-/vue-clipboards-1.0.0.tgz#60e356dcfd627af9de499ea5c102ac021f8d9adc"
+  dependencies:
+    clipboard "^1.5.15"
+
 vue-resource@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.1.tgz#bf2f7b70bfe21b397c9d7607878f776a3acea2cf"