]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: wip preview, need to wait for features on vitepress feat/example-preview
authorEduardo San Martin Morote <posva13@gmail.com>
Wed, 9 Sep 2020 13:12:56 +0000 (15:12 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Wed, 9 Sep 2020 13:12:56 +0000 (15:12 +0200)
13 files changed:
docs/.vitepress/components/Bit.vue [new file with mode: 0644]
docs/.vitepress/components/ExamplePreview.vue [new file with mode: 0644]
docs/.vitepress/components/ExamplePreviewBarButton.vue [new file with mode: 0644]
docs/.vitepress/example-preview/ExamplePreviewBar.vue [new file with mode: 0644]
docs/.vitepress/example-preview/ExamplePreviewExplorer.vue [new file with mode: 0644]
docs/.vitepress/example-preview/icons/brackets.vue [new file with mode: 0644]
docs/.vitepress/example-preview/icons/codesandbox.svg [new file with mode: 0644]
docs/.vitepress/example-preview/icons/left-arrow.svg [new file with mode: 0644]
docs/.vitepress/example-preview/icons/play-window.svg [new file with mode: 0644]
docs/.vitepress/example-preview/icons/right-arrow.svg [new file with mode: 0644]
docs/.vitepress/example-preview/utils.js [new file with mode: 0644]
docs/.vitepress/theme/index.js [new file with mode: 0644]
docs/index.md

diff --git a/docs/.vitepress/components/Bit.vue b/docs/.vitepress/components/Bit.vue
new file mode 100644 (file)
index 0000000..0c219ae
--- /dev/null
@@ -0,0 +1,8 @@
+<template>
+  <p class="bit-sponsor">
+    <a href="https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue" target="_blank">
+      <span>This project is sponsored by</span>
+      <img alt="bit" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/bit-wide.png">
+    </a>
+  </p>
+</template>
diff --git a/docs/.vitepress/components/ExamplePreview.vue b/docs/.vitepress/components/ExamplePreview.vue
new file mode 100644 (file)
index 0000000..140108f
--- /dev/null
@@ -0,0 +1,203 @@
+<template>
+  <div class="demo" :class="containerClasses">
+    <Promised :promise="examplePromise">
+      <span></span>
+      <span slot="catch" slot-scope="error"></span>
+      <ExamplePreviewBar slot-scope="then" :router="router" :view-code.sync="viewCode" :files="files" :current-file.sync="currentFile" :codesandbox-params="codesandboxParams" />
+    </Promised>
+    <Promised :promise="examplePromise">
+      <div class="example">Loading example...</div>
+      <div slot-scope="then">
+        <template v-if="viewCode">
+          <!-- <ExamplePreviewFilesTabs :files="files" :current-file.sync="currentFile" :view-code.sync="viewCode" /> -->
+          <ExamplePreviewExplorer v-if="currentFile" :file="currentFile" />
+        </template>
+        <template v-else>
+          <div class="example">
+            <component :is="page" />
+          </div>
+        </template>
+      </div>
+      <div class="example error" slot="catch" slot-scope="error">
+        <p>There was an error loading the example</p>
+
+        <button class="action-button" @click="loadPage">Retry</button>
+      </div>
+    </Promised>
+  </div>
+</template>
+
+<script>
+import Router from 'vue-router'
+import Promised from 'vue-promised'
+import ExamplePreviewBar from '../example-preview/ExamplePreviewBar'
+import 'focus-visible'
+import ExamplePreviewExplorer from '../example-preview/ExamplePreviewExplorer'
+import { getCodesandboxParameters, removeScriptSection } from '../example-preview/utils'
+
+export default {
+  props: {
+    name: String,
+    initialView: {
+      type: String,
+      default: 'demo'
+    }
+  },
+
+  data() {
+    return {
+      viewCode: this.initialView === 'code',
+      router: null,
+      page: null,
+      files: [],
+      currentFile: null,
+      codesandboxParams: null,
+
+      examplePromise: null
+    }
+  },
+
+  methods: {
+    async loadPage () {
+      if (!this.name) return
+      this.examplePromise = (this.pagePath ? import(`@docs/${this.pagePath}/examples/${this.name}/index.js`) : import(`@docs/examples/${this.name}/index.js`))
+      const Page = await this.examplePromise
+      if (!Page || !Page.App) return
+      let { App, files, codesandbox } = Page
+
+      // Do not create a new Router if the example has been loaded before
+      // otherwise, visiting a new page and coming back will break the navigation bar
+      if (App._exampleLoaded) {
+        this.router = App.router
+      } else {
+        // create the router again but force the mode to abstract
+        this.router = App.router = new Router({
+          ...App.router.options,
+          mode: 'abstract'
+        })
+        App._exampleLoaded = true
+      }
+
+      this.router.push('/')
+      this.page = App
+      // reset file
+      this.currentFile = null
+      this.codesandboxParams = null
+
+      this.files = []
+      // files is usually an object, transform if it's not
+      if (Array.isArray(files)) {
+        files = files.reduce((filesDict, name) => ({...filesDict, [name]: name}), {})
+      }
+
+      // load the content of all the files
+      Object.keys(files).forEach(name => {
+        const handleImport = ({ default: content }) => {
+          // remove the script part that contains the router
+          if (name === 'App.vue') {
+            content = removeScriptSection(content)
+          }
+          this.files.push({ name, content })
+          if (!this.currentFile) this.currentFile = this.files[this.files.length - 1]
+        }
+        if (this.pagePath) {
+          import(`!raw-loader!@docs/${this.pagePath}/examples/${this.name}/${files[name]}`).then(handleImport)
+        } else {
+          import(`!raw-loader!@docs/examples/${this.name}/${files[name]}`).then(handleImport)
+        }
+      })
+
+      if (!codesandbox || !codesandbox.length) return
+      const allFiles = codesandbox.map(filename => {
+        const transformFile = ({ default: content }) => {
+          // remove the script part from App.vue because it has the router
+          if (filename === 'App.vue') {
+            content = removeScriptSection(content)
+          }
+          return {
+            [filename]: { content }
+          }
+        }
+        if (this.pagePath) {
+          return import(`!raw-loader!@docs/${this.pagePath}/examples/${this.name}/${filename}`).then(transformFile)
+        } else {
+          return import(`!raw-loader!@docs/examples/${this.name}/${filename}`).then(transformFile)
+        }
+      })
+
+      // add the entry point (common for all examples) main.js
+      if (codesandbox.indexOf('main.js') < 0) {
+        allFiles.push(import(`!raw-loader!@docs/examples/common/main.js`).then(({ default: content })=> ({
+          'main.js': { content }
+        })))
+      }
+
+      this.codesandboxParams = await Promise.all(allFiles).then(getCodesandboxParameters)
+    }
+  },
+
+  computed: {
+    containerClasses() {
+      return { explorer: this.viewCode }
+    },
+    // this seems to be necessary to correctly code split
+    // it allows import path to have the slashes in them
+    pagePath () {
+      return this.$localePath.replace(/^\//, '').replace(/\/$/, '')
+    }
+  },
+
+  watch: {
+    name: {
+      handler: 'loadPage',
+      immediate: true
+    }
+  },
+
+  components: { ExamplePreviewBar, ExamplePreviewExplorer, Promised }
+}
+</script>
+
+<style scoped lang="stylus">
+@import '~@default-theme/styles/config.styl';
+
+.demo {
+  border: 1px solid #ddd;
+  border-radius: 4px;
+
+  & .example {
+    padding: 1rem 1.5rem;
+    overflow: hidden;
+
+    &.error {
+      color: #ff2828;
+    }
+  }
+}
+
+@media (max-width: $MQMobileNarrow) {
+  .demo {
+    margin-left: -1.5rem;
+    margin-right: -1.5rem;
+    border-radius: 0;
+  }
+}
+
+.action-button {
+  display: inline-block;
+  font-size: 1.2rem;
+  color: #fff;
+  background-color: #3eaf7c;
+  padding: 0.8rem 1.6rem;
+  border-radius: 4px;
+  transition: background-color 0.1s ease;
+  box-sizing: border-box;
+  border-bottom: 1px solid #389d70;
+
+  &:hover {
+    background-color: #4abf8a;
+    cursor: pointer;
+  }
+}
+</style>
+
diff --git a/docs/.vitepress/components/ExamplePreviewBarButton.vue b/docs/.vitepress/components/ExamplePreviewBarButton.vue
new file mode 100644 (file)
index 0000000..7afd1f8
--- /dev/null
@@ -0,0 +1,59 @@
+<template>
+  <button class="button">
+    <component :is="svgComponent" />
+  </button>
+</template>
+
+<script>
+import { defineComponent, shallowRef, watch } from 'vue'
+
+export default defineComponent({
+  props: {
+    icon: String,
+  },
+
+  setup(props) {
+    const svgComponent = shallowRef()
+
+    watch(
+      () => props.icon,
+      async icon => {
+        svgComponent.value = (
+          await import(`../example-preview/icons/${icon}.vue`)
+        ).default
+      },
+      { immediate: true }
+    )
+
+    return { svgComponent }
+  },
+})
+</script>
+
+<style scoped>
+.button {
+  /* reset button style */
+  border: none;
+  padding: 0;
+  width: auto;
+  overflow: visible;
+  background: transparent;
+  font: inherit;
+  /* acutal styles */
+  color: rgb(135, 135, 135);
+  font-size: 1.5rem;
+  line-height: 0.5;
+  vertical-align: middle;
+  text-align: center;
+  margin: 0px 0.1rem;
+}
+
+.button:not([disabled]):hover {
+  background-color: rgb(226, 226, 226);
+  cursor: pointer;
+}
+
+.button[disabled] {
+  color: rgb(192, 192, 192);
+}
+</style>
diff --git a/docs/.vitepress/example-preview/ExamplePreviewBar.vue b/docs/.vitepress/example-preview/ExamplePreviewBar.vue
new file mode 100644 (file)
index 0000000..1f5a402
--- /dev/null
@@ -0,0 +1,185 @@
+<template>
+  <div class="bar-container">
+    <template v-if="!viewCode">
+      <div class="actions-container">
+        <ExamplePreviewBarButton
+          icon="left-arrow"
+          @click.native="back"
+          :disabled="!previousPage"
+          title="Go back"
+        />
+        <ExamplePreviewBarButton
+          icon="right-arrow"
+          @click.native="forward"
+          :disabled="!nextPage"
+          title="Go forward"
+        />
+      </div>
+      <div class="uri-container">
+        <input
+          class="uri"
+          :disabled="!router"
+          :value="uri"
+          @keyup.enter="navigate"
+        />
+      </div>
+    </template>
+    <div class="tabs-container" v-else>
+      <button
+        class="reset-button tab"
+        v-for="file in files"
+        :class="file === currentFile ? 'is-selected' : ''"
+        @click="$emit('update:currentFile', file)"
+      >
+        {{ file.name }}
+      </button>
+    </div>
+    <div class="actions-container">
+      <form
+        v-if="viewCode && codesandboxParams"
+        action="https://codesandbox.io/api/v1/sandboxes/define"
+        method="POST"
+        target="_blank"
+      >
+        <input type="hidden" name="parameters" :value="codesandboxParams" />
+        <ExamplePreviewBarButton
+          icon="codesandbox"
+          title="Edit on CodeSandbox"
+        />
+      </form>
+      <ExamplePreviewBarButton
+        :icon="viewCode ? 'play-window' : 'brackets'"
+        @click.native="$emit('update:viewCode', !viewCode)"
+        :title="viewCode ? 'View Demo' : 'View Code'"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+import ExamplePreviewBarButton from '../components/ExamplePreviewBarButton'
+
+export default {
+  props: {
+    router: Object,
+    files: Array,
+    currentFile: Object,
+    viewCode: Boolean,
+    codesandboxParams: String,
+  },
+
+  computed: {
+    uri() {
+      return this.router ? this.router.currentRoute.fullPath : '/'
+    },
+    history() {
+      return this.router && this.router.history
+    },
+    previousPage() {
+      return this.history ? this.history.stack[this.history.index - 1] : ''
+    },
+    nextPage() {
+      return this.history ? this.history.stack[this.history.index + 1] : ''
+    },
+  },
+
+  methods: {
+    back() {
+      this.router.go(-1)
+    },
+    forward() {
+      this.router.go(1)
+    },
+    navigate({ target }) {
+      this.router.push(target.value)
+    },
+  },
+
+  components: { ExamplePreviewBarButton },
+}
+</script>
+
+<style scoped lang="stylus">
+@import '~@default-theme/styles/config.styl';
+
+.bar-container {
+  display: flex;
+  border-radius: 4px 4px 0 0;
+  background-color: rgb(242, 242, 242);
+  padding: 0 0.5rem;
+  align-items: center;
+  line-height: 1;
+  box-shadow: rgb(221, 221, 221) 0px 1px 3px;
+  height: 2.5rem;
+  min-height: 2.5rem;
+  margin-bottom: 0;
+}
+
+.actions-container {
+  // Make sure they appar in line
+  display: flex;
+}
+
+.uri-container, .tabs-container {
+  flex-grow: 1;
+}
+
+.tabs-container {
+  display: flex;
+  height: 100%;
+  margin-left: -0.5rem;
+  overflow-x: auto;
+}
+
+.tab {
+  height: 100%;
+  font-size: 0.9rem;
+  padding: 0 0.7rem;
+  line-height: 2.5rem;
+  color: #666;
+
+  &:hover {
+    cursor: pointer;
+  }
+
+  &.is-selected {
+    color: inherit;
+    font-weight: 500;
+    border-bottom: 3px solid $accentColor;
+    background-color: rgba(0, 0, 0, 0.075);
+  }
+}
+
+.uri {
+  box-sizing: border-box;
+  width: 100%;
+  color: rgba(0, 0, 0, 0.8);
+  box-sizing: border-box;
+  border-radius: 4px;
+  outline: none;
+  border-width: 1px;
+  border-style: solid;
+  border-color: rgb(204, 204, 204);
+  padding: 0.2rem 0.5rem;
+  vertical-align: middle;
+  font-size: 1rem;
+}
+
+.button {
+  color: rgb(135, 135, 135);
+  font-size: 1.5rem;
+  line-height: 0.5;
+  vertical-align: middle;
+  text-align: center;
+  margin: 0px 0.1rem;
+
+  &:not([disabled]):hover {
+    background-color: rgb(226, 226, 226);
+    cursor: pointer;
+  }
+
+  &[disabled] {
+    color: rgb(192, 192, 192);
+  }
+}
+</style>
diff --git a/docs/.vitepress/example-preview/ExamplePreviewExplorer.vue b/docs/.vitepress/example-preview/ExamplePreviewExplorer.vue
new file mode 100644 (file)
index 0000000..59b2b46
--- /dev/null
@@ -0,0 +1,74 @@
+<template>
+  <div v-html="content" class="code" :class="'language-' + language"></div>
+</template>
+
+<script>
+// pretty much copied from vuepress sourcecode
+const prism = require('prismjs')
+// const loadLanguages = require('prismjs/components/index')
+const escapeHtml = require('escape-html')
+
+// required to make embedded highlighting work...
+// loadLanguages(['markup', 'css', 'javascript'])
+
+function wrap (code, lang) {
+  if (lang === 'text') {
+    code = escapeHtml(code)
+  }
+  return `<pre class="language-${lang}" style="margin: 0"><code>${code}</code></pre>`
+}
+
+function highlightCode (str, lang) {
+  if (!lang) {
+    return wrap(str, 'text')
+  }
+  lang = lang.toLowerCase()
+  const rawLang = lang
+  if (lang === 'vue' || lang === 'html') {
+    lang = 'markup'
+  }
+  if (lang === 'md') {
+    lang = 'markdown'
+  }
+  if (lang === 'ts') {
+    lang = 'typescript'
+  }
+  if (lang === 'py') {
+    lang = 'python'
+  }
+  if (prism.languages[lang]) {
+    const code = prism.highlight(str, prism.languages[lang], lang)
+    return wrap(code, rawLang)
+  } else {
+      console.warn(`[vuepress] Syntax highlight for language "${lang}" is not supported.`)
+  }
+  return wrap(str, 'text')
+}
+
+export default {
+  props: {
+    file: Object
+  },
+
+  computed: {
+    language: ({ file: { name }}) => name.substring(name.lastIndexOf('.') + 1, name.length),
+    content: ({ file, language}) => highlightCode(file.content, language)
+  }
+}
+</script>
+
+<style scoped>
+.code {
+  border-radius: 0 0 4px 4px;
+}
+
+.code pre {
+  margin: 0;
+}
+
+@media (max-width: 419px) {
+  .code.code.code {
+    margin: 0;
+  }
+}
+</style>
diff --git a/docs/.vitepress/example-preview/icons/brackets.vue b/docs/.vitepress/example-preview/icons/brackets.vue
new file mode 100644 (file)
index 0000000..0733aa6
--- /dev/null
@@ -0,0 +1,27 @@
+<template>
+  <svg
+    fill="currentColor"
+    x="0px"
+    y="0px"
+    width="24px"
+    height="24px"
+    viewBox="0 0 24 24"
+    enable-background="new 0 0 24 24"
+  >
+    <g>
+      <path
+        id="left-bracket"
+        d="M4,12v-1h1c1,0,1,0,1-1V7.614C6,7.1,6.024,6.718,6.073,6.472C6.127,6.22,6.212,6.009,6.33,5.839   C6.534,5.56,6.803,5.364,7.138,5.255C7.473,5.14,8.01,5,8.973,5H10v1H9.248c-0.457,0-0.77,0.191-0.936,0.408   C8.145,6.623,8,6.853,8,7.476v1.857c0,0.729-0.041,1.18-0.244,1.493c-0.2,0.307-0.562,0.529-1.09,0.667   c0.535,0.155,0.9,0.385,1.096,0.688C7.961,12.484,8,12.938,8,13.665v1.862c0,0.619,0.145,0.848,0.312,1.062   c0.166,0.22,0.479,0.407,0.936,0.407L10,17l0,0v1H8.973c-0.963,0-1.5-0.133-1.835-0.248c-0.335-0.109-0.604-0.307-0.808-0.591   c-0.118-0.165-0.203-0.374-0.257-0.625C6.024,16.283,6,15.9,6,15.387V13c0-1,0-1-1-1H4z"
+      />
+      <use
+        transform="matrix(-1,0,0,1,24,0)"
+        id="right-bracket"
+        x="0"
+        y="0"
+        width="24"
+        height="24"
+        xlink:href="#left-bracket"
+      />
+    </g>
+  </svg>
+</template>
diff --git a/docs/.vitepress/example-preview/icons/codesandbox.svg b/docs/.vitepress/example-preview/icons/codesandbox.svg
new file mode 100644 (file)
index 0000000..d3653e0
--- /dev/null
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 1063 1063" fill="none">
+  <path d="M0 317.198V106.046L182.999 0V211.465L0 317.198Z" transform="translate(739.001 551.802)" fill="currentColor" />
+  <path d="M179.915 104.303L0 0V208.606L179.915 313.438V104.303Z" transform="translate(142.167 557.135)" fill="currentColor" />
+  <path d="M183.546 212.795L366.503 106.633L183.624 0L0 106.987L183.546 212.795Z" transform="translate(348.436 81)" fill="currentColor" />
+  <path d="M390 0L0 225.167V675.167" transform="translate(529 305.833)" stroke="currentColor" stroke-width="100" stroke-miterlimit="10" />
+  <path d="M0 0L389.333 224" transform="translate(142.167 307)" stroke="currentColor" stroke-width="100" stroke-miterlimit="10" />
+  <path d="M0 677.083L389.917 901.042L780 676.333V226L390 0L0 227V677.083Z" transform="translate(141 80)" stroke="currentColor" stroke-width="100" stroke-miterlimit="10" />
+</svg>
diff --git a/docs/.vitepress/example-preview/icons/left-arrow.svg b/docs/.vitepress/example-preview/icons/left-arrow.svg
new file mode 100644 (file)
index 0000000..6e3af46
--- /dev/null
@@ -0,0 +1,5 @@
+<svg fill="currentColor" preserveAspectRatio="xMidYMid meet" height="1em" width="1em" viewBox="0 0 40 40" style="vertical-align: middle;">
+  <g>
+    <path d="m26.5 12.1q0 0.3-0.2 0.6l-8.8 8.7 8.8 8.8q0.2 0.2 0.2 0.5t-0.2 0.5l-1.1 1.1q-0.3 0.3-0.6 0.3t-0.5-0.3l-10.4-10.4q-0.2-0.2-0.2-0.5t0.2-0.5l10.4-10.4q0.3-0.2 0.5-0.2t0.6 0.2l1.1 1.1q0.2 0.2 0.2 0.5z"></path>
+  </g>
+</svg>
diff --git a/docs/.vitepress/example-preview/icons/play-window.svg b/docs/.vitepress/example-preview/icons/play-window.svg
new file mode 100644 (file)
index 0000000..b41582f
--- /dev/null
@@ -0,0 +1,3 @@
+<svg fill="currentColor" preserveAspectRatio="xMidYMid meet" height="1em" width="1em" viewBox="0 0 200 200" style="vertical-align: middle;">
+  <path d="M27,33 L173,33 C178.522847,33 183,37.4771525 183,43 L183,157 C183,162.522847 178.522847,167 173,167 L27,167 C21.4771525,167 17,162.522847 17,157 L17,43 C17,37.4771525 21.4771525,33 27,33 Z M29,64 L29,156 L171,156 L171,64 L29,64 Z M84.260356,82.6998802 L119.974518,107.161635 C121.797124,108.409995 122.262642,110.899505 121.014282,112.722111 C120.734924,113.129973 120.38238,113.482517 119.974518,113.761875 L84.260356,138.223629 C82.4377502,139.471989 79.9482404,139.006471 78.6998802,137.183866 C78.2439706,136.518238 78,135.730302 78,134.92351 L78,86 C78,83.790861 79.790861,82 82,82 C82.8067925,82 83.594728,82.2439706 84.260356,82.6998802 Z" fill="inherit"></path>
+</svg>
diff --git a/docs/.vitepress/example-preview/icons/right-arrow.svg b/docs/.vitepress/example-preview/icons/right-arrow.svg
new file mode 100644 (file)
index 0000000..d917d07
--- /dev/null
@@ -0,0 +1,5 @@
+<svg fill="currentColor" preserveAspectRatio="xMidYMid meet" height="1em" width="1em" viewBox="0 0 40 40" style="vertical-align: middle;">
+  <g>
+    <path d="m26.3 21.4q0 0.3-0.2 0.5l-10.4 10.4q-0.3 0.3-0.6 0.3t-0.5-0.3l-1.1-1.1q-0.2-0.2-0.2-0.5t0.2-0.5l8.8-8.8-8.8-8.7q-0.2-0.3-0.2-0.6t0.2-0.5l1.1-1.1q0.3-0.2 0.5-0.2t0.6 0.2l10.4 10.4q0.2 0.2 0.2 0.5z"></path>
+  </g>
+</svg>
diff --git a/docs/.vitepress/example-preview/utils.js b/docs/.vitepress/example-preview/utils.js
new file mode 100644 (file)
index 0000000..a79ed32
--- /dev/null
@@ -0,0 +1,31 @@
+import { getParameters } from 'codesandbox/lib/api/define'
+
+export function getCodesandboxParameters(files) {
+  const data = {
+    files: {
+      'package.json': {
+        content: {
+          main: 'main.js',
+          dependencies: {
+            vue: 'latest',
+            'vue-router': 'latest',
+          },
+        },
+      },
+      ...files.reduce(
+        (fileMap, file) => ({
+          ...fileMap,
+          ...file,
+        }),
+        {}
+      ),
+    },
+  }
+
+  return getParameters(data)
+}
+
+const scriptRE = /\s*<script[^>]*>[\s\S]*<\/script>\s*/m
+export function removeScriptSection(content) {
+  return content.replace(scriptRE, '')
+}
diff --git a/docs/.vitepress/theme/index.js b/docs/.vitepress/theme/index.js
new file mode 100644 (file)
index 0000000..44a3989
--- /dev/null
@@ -0,0 +1,14 @@
+import DefaultTheme from 'vitepress/dist/client/theme-default'
+import Bit from '../components/Bit.vue'
+import ExampleButton from '../components/ExamplePreviewBarButton.vue'
+
+/** @type {import('vitepress').Theme} */
+const config = {
+  ...DefaultTheme,
+  enhanceApp({ app }) {
+    app.component('Bit', Bit)
+    app.component('ExampleButton', ExampleButton)
+  },
+}
+
+export default config
index 6b0397e82220117afebe0e224e1087e6d2ebc65a..1aed690b48c7ee87b6a4b83ca0542bcda5b08de0 100644 (file)
@@ -16,3 +16,5 @@ Vue Router is the official router for [Vue.js](http://vuejs.org). It deeply inte
 <!-- TODO: provide examples or remove this -->
 
 [Get started](./guide/) or play with the [playground](https://github.com/vuejs/vue-router-next/tree/master/playground) (see [`README.md`](https://github.com/vuejs/vue-router-next) to run them).
+
+<ExampleButton icon="brackets"/>