Aratramba / design-manual

Lightweight Design System Generator
http://eightmedia.github.io/design-manual
15 stars 1 forks source link

move to vuepress #94

Open Aratramba opened 6 years ago

Aratramba commented 6 years ago

let Design Manual only do the generating of components, let Vuepress handle the pages.

Aratramba commented 6 years ago

add output data json file alongisde component html

{
  "height": 100,
  "description": "<p></p>",
  "source": "…"
}
Aratramba commented 6 years ago

add vue single file component for embedding components

<template>
  <div class="component">

    <!-- tools -->
    <div class="component__tools">

      <!-- code button -->
      <button v-on:click="toggleCodeView" class="component__tools__button" v-bind:class="{ 'active': showsource }" v-if="jsonLoaded && source">
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
          <path d="M0.7 9.3l4.8-4.8 1.4 1.42-4.060 4.080 4.070 4.070-1.41 1.42-5.5-5.49 0.7-0.7zM19.3 10.7l0.7-0.7-5.49-5.49-1.4 1.42 4.050 4.070-4.070 4.070 1.41 1.42 4.78-4.78z"></path>  
        </svg>
        <span>code</span>
      </button>

      <!-- link button -->
      <a :href="htmlFile" target="_blank" class="component__tools__button">
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
          <path d="M9.26 13c-0.167-0.286-0.266-0.63-0.266-0.996 0-0.374 0.103-0.724 0.281-1.023l-0.005 0.009c1.549-0.13 2.757-1.419 2.757-2.99 0-1.657-1.343-3-3-3-0.009 0-0.019 0-0.028 0l0.001-0h-4c-1.657 0-3 1.343-3 3s1.343 3 3 3v0h0.080c-0.053 0.301-0.083 0.647-0.083 1s0.030 0.699 0.088 1.036l-0.005-0.036h-0.080c-2.761 0-5-2.239-5-5s2.239-5 5-5v0h4c0.039-0.001 0.084-0.002 0.13-0.002 2.762 0 5.002 2.239 5.002 5.002 0 2.717-2.166 4.927-4.865 5l-0.007 0zM10.74 7c0.167 0.286 0.266 0.63 0.266 0.996 0 0.374-0.103 0.724-0.281 1.023l0.005-0.009c-1.549 0.13-2.757 1.419-2.757 2.99 0 1.657 1.343 3 3 3 0.009 0 0.019-0 0.028-0l-0.001 0h4c1.657 0 3-1.343 3-3s-1.343-3-3-3v0h-0.080c0.053-0.301 0.083-0.647 0.083-1s-0.030-0.699-0.088-1.036l0.005 0.036h0.080c2.761 0 5 2.239 5 5s-2.239 5-5 5v0h-4c-0.039 0.001-0.084 0.002-0.13 0.002-2.762 0-5.002-2.239-5.002-5.002 0-2.717 2.166-4.927 4.865-5l0.007-0z"></path>
        </svg>
        <span>{{ htmlFile }}</span>
      </a>
    </div>

    <!-- code view -->
    <div class="component__code" v-if="showsource && jsonLoaded && source">
      <pre class="language-html"><code v-html="prettifyHTML(source)"></code></pre>
    </div>

    <!-- component iframe -->
    <div class="component__preview" v-show="!showsource">

      <!-- resizer -->
      <span class="component__preview__handle">
        <span class="component__preview__handle__overlay"></span>
        <svg class="component__preview__handle__icon" width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
          <path d="M10 12c-1.105 0-2-0.895-2-2s0.895-2 2-2v0c1.105 0 2 0.895 2 2s-0.895 2-2 2v0zM10 6c-1.105 0-2-0.895-2-2s0.895-2 2-2v0c1.105 0 2 0.895 2 2s-0.895 2-2 2v0zM10 18c-1.105 0-2-0.895-2-2s0.895-2 2-2v0c1.105 0 2 0.895 2 2s-0.895 2-2 2v0z"></path>
        </svg>
      </span>

      <!-- height spacer -->
      <div ref="iframe_spacer" v-if="!htmlLoaded" v-bind:style="{ height: height + 'px' }"></div>

      <!-- component iframe -->
      <iframe class="component__preview__iframe" ref="iframe" v-if="inview" :src="htmlFile" frameborder="0" scrolling="no" v-on:load="onIframeLoad"></iframe>
    </div>
  </div>
</template>

<script>
export default {
  props: ["name"],
  data() {
    return {
      htmlLoaded: false,
      jsonLoaded: false,
      height: 0,
      source: null,
      showsource: false,
      inview: false,
      htmlFile: this.getHTMLFilePath(this.$props.name),
      jsonFile: this.getJSONFilePath(this.$props.name),
    };
  },
  mounted() {
    /**
     * set in view observer for async loading iframe
     */

    var observer = new IntersectionObserver(
      function(entries, observer) {
        entries.forEach(
          function(entry) {
            if (entry.isIntersecting) {
              if (!this.inview) {
                this.inview = true;
              }
            }
          }.bind(this)
        );
      }.bind(this)
    );
    observer.observe(this.$el);

    /**
     * get meta info from lib/file.json
     */

    fetch(this.jsonFile).then((res) => {
      if (res.status === 200) {
        return res.json();
      }
    }).then((obj) => {
      this.jsonLoaded = true;

      if (obj) {
        this.height = obj.height;
        this.source = obj.source;
      }
    }).catch((err) => {});
  },
  methods: {

    /**
     * get html file path
     */

    getHTMLFilePath: function(str) {
      return "/lib/" + this.slugify(str) + ".html";
    },

    /**
     * get json file path
     */

    getJSONFilePath: function(str) {
      return "/lib/" + this.slugify(str) + ".json";
    },

    /**
     * beautify and highlight html
     */
    prettifyHTML: function(str) {
      return window.Prism.highlight(window.html_beautify(str), window.Prism.languages.markup)
    },

    /**
     * create slug-from-string
     */

    slugify: function(str) {
      return str
        .toString()
        .toLowerCase()
        .replace(/\s+/g, "-") // Replace spaces with -
        .replace(/[^\w-]+/g, "") // Remove all non-word chars
        .replace(/--+/g, "-") // Replace multiple - with single -
        .replace(/^-+/, "") // Trim - from start of text
        .replace(/-+$/, ""); // Trim - from end of text
    },

    /**
     * toggle code/preview view
     */

    toggleCodeView: function (e) {
      this.showsource = !this.showsource;

      if (!this.showsource) {
        window.iFrameResize({ checkOrigin: false }, this.$refs.iframe);
      }
    },

    /**
     * iframe loaded handler
     */

    onIframeLoad: function(e) {

      // resize iframe
      window.iFrameResize({ checkOrigin: false }, this.$refs.iframe);

      // make preview draggable
      window.interact(this.$el.querySelector(".component__preview")).resizable({
        edges: {
          left: false,
          right: ".component__preview__handle",
          bottom: false,
          top: false
        },
        onmove: function(e) {
          e.target.style.width = e.rect.width - 24 + "px";
          e.target.querySelector('iframe').contentWindow.parentIFrame.size();
          if (!e.target.classList.contains("component-is-resizing")) {
            e.target.classList.add("component-is-resizing");
          }
        },
        onend: function(e) {
          e.target.querySelector("iframe").contentWindow.parentIFrame.size();
          if (e.target.classList.contains("component-is-resizing")) {
            e.target.classList.remove("component-is-resizing");
          }
        }
      });

      // set loaded state
      this.htmlLoaded = true;
    }
  }
};
</script>

<style scoped>
iframe {
  width: 100%;
}

.component {
  --grey: #eaecef;
  --darkgrey: #111;
  margin-top: 20px;
  margin-bottom: 150px;
}

/**
 * Tools
 */

.component__tools {
  display: flex;
}

.component__tools__button {
  border: 1px solid var(--grey);
  border-radius: 3px;
  padding: 6px 8px;
  margin-left: 5px;
  display: inline-flex;
  outline: 0;
  text-decoration: none !important;
  cursor: pointer;
}

.component__tools__button:hover,
.component__tools__button.active {
  background-color: var(--grey);
}

.component__tools__button svg {
  margin: auto;
}

.component__tools__button span {
  font-size: 12px;
  font-weight: normal;
  color: var(--darkgrey);
  margin: auto 0 auto 6px;
}

/**
 * Code
 */

.component__code {
  max-width: 100vw;
  margin-top: 20px;
  width: 1200px;
  transform: translateX(-50%) translateX(370px);
  position: relative;
  min-width: 275px;
}

/**
 * Preview
 */

.component__preview {
  max-width: 100vw;
  margin-top: 20px;
  width: 1200px;
  transform: translateX(-50%) translateX(370px);
  position: relative;
  min-width: 275px;
  padding-right: 24px;
  border: 1px solid var(--grey);
}

.component__preview__iframe {
  display: block;
  width: 100%;
}

/**
 * Resize handle
 */

.component__preview__handle {
  display: block;
  position: absolute;
  width: 24px;
  height: 100%;
  right: 0;
  top: 0;
  bottom: 0;
}

.component-is-resizing >>> .component__preview__handle {
  left: 0;
  width: 100%;
}

.component__preview__handle__overlay {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 24px;
  content: "";
  background-color: white;
  transition: background-color 0.15s, border-color 0.15s;
  border-left: 1px solid var(--grey);
}

.component__preview__handle:hover >>> .component__preview__handle__overlay,
.component-is-resizing >>> .component__preview__handle__overlay {
  background-color: #f5f5f5;
  border-color: var(--grey);
}

/**
 * Resize handle icon
 */

.component__preview__handle__icon {
  display: block;
  width: 20px;
  height: 20px;
  position: absolute;
  top: 50%;
  right: 2px;
  transform: translate(0, -50%);
  opacity: 0.25;
  transition: opacity 0.15s;
}

.component-is-resizing >>> .component__preview__handle__icon {
  opacity: 0.5;
}
</style>
Aratramba commented 6 years ago

Add copyable vuepress config

module.exports = {
  title: "Design Manual",
  description: "this is my design manual",
  head: [
    ['script', { src: 'https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/3.6.0/iframeResizer.min.js' }],
    ['script', { src: 'https://cdnjs.cloudflare.com/ajax/libs/vanilla-lazyload/10.14.0/lazyload.min.js' }],
    ['script', { src: 'https://unpkg.com/interactjs@1.3.3/dist/interact.min.js' }],
    ['script', { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.14.0/prism.min.js' }],
    ['script', { src: 'https://cdnjs.cloudflare.com/ajax/libs/prism/1.14.0/components/prism-markup-templating.js' }],
    ['script', { src: 'https://cdnjs.cloudflare.com/ajax/libs/js-beautify/1.7.5/beautify-html.min.js' }],
  ],
  themeConfig: {
    nav: [
      { text: "Home", link: "/" },
      { text: "Guide", link: "/guide/" },
      { text: "External", link: "https://google.com" }
    ],
    sidebar: [
      {
        title: "Components",
        collapsable: false,
        children: ["/"]
      },
      {
        title: "Content",
        children: [
          /* ... */
        ]
      },
      {
        title: "Whatever",
        children: [
          /* ... */
        ]
      }
    ]
  }
};