BREAKING CHANGE: `createApp` API has been adjusted.
  - `createApp()` now accepts the root component, and optionally a props
  object to pass to the root component.
  - `app.mount()` now accepts a single argument (the root container)
  - `app.unmount()` no longer requires arguments.
  New behavior looks like the following:
  ``` js
  const app = createApp(RootComponent)
  app.mount('#app')
  app.unmount()
  ```
		
	
			
		
			
				
	
	
		
			163 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			163 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<script src="../../dist/vue.global.js"></script>
 | 
						|
<script>
 | 
						|
// math helper...
 | 
						|
function valueToPoint (value, index, total) {
 | 
						|
  var x     = 0
 | 
						|
  var y     = -value * 0.8
 | 
						|
  var angle = Math.PI * 2 / total * index
 | 
						|
  var cos   = Math.cos(angle)
 | 
						|
  var sin   = Math.sin(angle)
 | 
						|
  var tx    = x * cos - y * sin + 100
 | 
						|
  var ty    = x * sin + y * cos + 100
 | 
						|
  return {
 | 
						|
    x: tx,
 | 
						|
    y: ty
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const AxisLabel = {
 | 
						|
  template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
 | 
						|
  props: {
 | 
						|
    stat: Object,
 | 
						|
    index: Number,
 | 
						|
    total: Number
 | 
						|
  },
 | 
						|
  computed: {
 | 
						|
    point: function () {
 | 
						|
      return valueToPoint(
 | 
						|
        +this.stat.value + 10,
 | 
						|
        this.index,
 | 
						|
        this.total
 | 
						|
      )
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
</script>
 | 
						|
 | 
						|
<!-- template for the polygraph component. -->
 | 
						|
<script type="text/x-template" id="polygraph-template">
 | 
						|
  <g>
 | 
						|
    <polygon :points="points"></polygon>
 | 
						|
    <circle cx="100" cy="100" r="80"></circle>
 | 
						|
    <axis-label
 | 
						|
      v-for="(stat, index) in stats"
 | 
						|
      :stat="stat"
 | 
						|
      :index="index"
 | 
						|
      :total="stats.length">
 | 
						|
    </axis-label>
 | 
						|
  </g>
 | 
						|
</script>
 | 
						|
 | 
						|
<script>
 | 
						|
const Polygraph = {
 | 
						|
  props: ['stats'],
 | 
						|
  template: '#polygraph-template',
 | 
						|
  computed: {
 | 
						|
    // a computed property for the polygon's points
 | 
						|
    points() {
 | 
						|
      const total = this.stats.length
 | 
						|
      return this.stats.map((stat, i) => {
 | 
						|
        const point = valueToPoint(stat.value, i, total)
 | 
						|
        return point.x + ',' + point.y
 | 
						|
      }).join(' ')
 | 
						|
    }
 | 
						|
  },
 | 
						|
  components: {
 | 
						|
    AxisLabel
 | 
						|
  }
 | 
						|
}
 | 
						|
</script>
 | 
						|
 | 
						|
<!-- demo root element -->
 | 
						|
<div id="demo">
 | 
						|
  <!-- Use the polygraph component -->
 | 
						|
  <svg width="200" height="200">
 | 
						|
    <polygraph :stats="stats"></polygraph>
 | 
						|
  </svg>
 | 
						|
  <!-- controls -->
 | 
						|
  <div v-for="stat in stats">
 | 
						|
    <label>{{stat.label}}</label>
 | 
						|
    <input type="range" v-model="stat.value" min="0" max="100">
 | 
						|
    <span>{{stat.value}}</span>
 | 
						|
    <button @click="remove(stat)" class="remove">X</button>
 | 
						|
  </div>
 | 
						|
  <form id="add">
 | 
						|
    <input name="newlabel" v-model="newLabel">
 | 
						|
    <button @click="add">Add a Stat</button>
 | 
						|
  </form>
 | 
						|
  <pre id="raw">{{ stats }}</pre>
 | 
						|
</div>
 | 
						|
 | 
						|
<script>
 | 
						|
const globalStats = [
 | 
						|
  { label: 'A', value: 100 },
 | 
						|
  { label: 'B', value: 100 },
 | 
						|
  { label: 'C', value: 100 },
 | 
						|
  { label: 'D', value: 100 },
 | 
						|
  { label: 'E', value: 100 },
 | 
						|
  { label: 'F', value: 100 }
 | 
						|
]
 | 
						|
 | 
						|
Vue.createApp({
 | 
						|
  components: {
 | 
						|
    Polygraph
 | 
						|
  },
 | 
						|
  data: () => ({
 | 
						|
    newLabel: '',
 | 
						|
    stats: globalStats
 | 
						|
  }),
 | 
						|
  methods: {
 | 
						|
    add(e) {
 | 
						|
      e.preventDefault()
 | 
						|
      if (!this.newLabel) return
 | 
						|
      this.stats.push({
 | 
						|
        label: this.newLabel,
 | 
						|
        value: 100
 | 
						|
      })
 | 
						|
      this.newLabel = ''
 | 
						|
    },
 | 
						|
    remove(stat) {
 | 
						|
      if (this.stats.length > 3) {
 | 
						|
        this.stats.splice(this.stats.indexOf(stat), 1)
 | 
						|
      } else {
 | 
						|
        alert('Can\'t delete more!')
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}).mount('#demo')
 | 
						|
</script>
 | 
						|
 | 
						|
<style>
 | 
						|
body {
 | 
						|
    font-family: Helvetica Neue, Arial, sans-serif;
 | 
						|
}
 | 
						|
 | 
						|
polygon {
 | 
						|
    fill: #42b983;
 | 
						|
    opacity: .75;
 | 
						|
}
 | 
						|
 | 
						|
circle {
 | 
						|
    fill: transparent;
 | 
						|
    stroke: #999;
 | 
						|
}
 | 
						|
 | 
						|
text {
 | 
						|
    font-family: Helvetica Neue, Arial, sans-serif;
 | 
						|
    font-size: 10px;
 | 
						|
    fill: #666;
 | 
						|
}
 | 
						|
 | 
						|
label {
 | 
						|
    display: inline-block;
 | 
						|
    margin-left: 10px;
 | 
						|
    width: 20px;
 | 
						|
}
 | 
						|
 | 
						|
#raw {
 | 
						|
    position: absolute;
 | 
						|
    top: 0;
 | 
						|
    left: 300px;
 | 
						|
}
 | 
						|
</style>
 |