mirror of
https://github.com/vuejs/vue.git
synced 2024-11-21 20:28:54 +00:00
test: e2e test for composition API examples
This commit is contained in:
parent
460856510d
commit
e5a6fe5da7
@ -24,7 +24,7 @@
|
||||
},
|
||||
computed: {
|
||||
compiledMarkdown: function () {
|
||||
return marked(this.input, { sanitize: true })
|
||||
return marked.marked(this.input)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
@ -1,5 +1,5 @@
|
||||
// The raw data to observe
|
||||
var stats = [
|
||||
var globalStats = [
|
||||
{ label: 'A', value: 100 },
|
||||
{ label: 'B', value: 100 },
|
||||
{ label: 'C', value: 100 },
|
||||
@ -16,10 +16,12 @@ Vue.component('polygraph', {
|
||||
// a computed property for the polygon's points
|
||||
points: function () {
|
||||
var total = this.stats.length
|
||||
return this.stats.map(function (stat, i) {
|
||||
var point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
}).join(' ')
|
||||
return this.stats
|
||||
.map(function (stat, i) {
|
||||
var point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
})
|
||||
.join(' ')
|
||||
}
|
||||
},
|
||||
components: {
|
||||
@ -33,11 +35,7 @@ Vue.component('polygraph', {
|
||||
template: '#axis-label-template',
|
||||
computed: {
|
||||
point: function () {
|
||||
return valueToPoint(
|
||||
+this.stat.value + 10,
|
||||
this.index,
|
||||
this.total
|
||||
)
|
||||
return valueToPoint(+this.stat.value + 10, this.index, this.total)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,14 +43,14 @@ Vue.component('polygraph', {
|
||||
})
|
||||
|
||||
// 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
|
||||
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
|
||||
@ -64,7 +62,7 @@ new Vue({
|
||||
el: '#demo',
|
||||
data: {
|
||||
newLabel: '',
|
||||
stats: stats
|
||||
stats: globalStats
|
||||
},
|
||||
methods: {
|
||||
add: function (e) {
|
||||
@ -80,7 +78,7 @@ new Vue({
|
||||
if (this.stats.length > 3) {
|
||||
this.stats.splice(this.stats.indexOf(stat), 1)
|
||||
} else {
|
||||
alert('Can\'t delete more!')
|
||||
alert("Can't delete more!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
75
examples/composition/commits.html
Normal file
75
examples/composition/commits.html
Normal file
@ -0,0 +1,75 @@
|
||||
<script src="../../dist/vue.min.js"></script>
|
||||
|
||||
<div id="demo">
|
||||
<h1>Latest Vue.js Commits</h1>
|
||||
<template v-for="branch in branches">
|
||||
<input type="radio"
|
||||
:id="branch"
|
||||
:value="branch"
|
||||
name="branch"
|
||||
v-model="currentBranch">
|
||||
<label :for="branch">{{ branch }}</label>
|
||||
</template>
|
||||
<p>vuejs/vue@{{ currentBranch }}</p>
|
||||
<ul>
|
||||
<li v-for="{ html_url, sha, author, commit } in commits">
|
||||
<a :href="html_url" target="_blank" class="commit">{{ sha.slice(0, 7) }}</a>
|
||||
- <span class="message">{{ truncate(commit.message) }}</span><br>
|
||||
by <span class="author"><a :href="author.html_url" target="_blank">{{ commit.author.name }}</a></span>
|
||||
at <span class="date">{{ formatDate(commit.author.date) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { ref, watchEffect } = Vue
|
||||
const API_URL = `https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=`
|
||||
|
||||
const truncate = v => {
|
||||
const newline = v.indexOf('\n')
|
||||
return newline > 0 ? v.slice(0, newline) : v
|
||||
}
|
||||
|
||||
const formatDate = v => v.replace(/T|Z/g, ' ')
|
||||
|
||||
new Vue({
|
||||
setup() {
|
||||
const currentBranch = ref('main')
|
||||
const commits = ref(null)
|
||||
|
||||
watchEffect(() => {
|
||||
fetch(`${API_URL}${currentBranch.value}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
commits.value = data
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
branches: ['main', 'dev'],
|
||||
currentBranch,
|
||||
commits,
|
||||
truncate,
|
||||
formatDate
|
||||
}
|
||||
}
|
||||
}).$mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#demo {
|
||||
font-family: 'Helvetica', Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #f66;
|
||||
}
|
||||
li {
|
||||
line-height: 1.5em;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.author, .date {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
173
examples/composition/grid.html
Normal file
173
examples/composition/grid.html
Normal file
@ -0,0 +1,173 @@
|
||||
<script src="../../dist/vue.min.js"></script>
|
||||
|
||||
<!-- DemoGrid component template -->
|
||||
<script type="text/x-template" id="grid-template">
|
||||
<table v-if="filteredData.length">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="key in columns"
|
||||
@click="sortBy(key)"
|
||||
:class="{ active: state.sortKey == key }">
|
||||
{{ capitalize(key) }}
|
||||
<span class="arrow" :class="state.sortOrders[key] > 0 ? 'asc' : 'dsc'">
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="entry in filteredData">
|
||||
<td v-for="key in columns">
|
||||
{{entry[key]}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p v-else>No matches found.</p>
|
||||
</script>
|
||||
<!-- DemoGrid component script -->
|
||||
<script>
|
||||
const { reactive, computed } = Vue
|
||||
|
||||
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
const DemoGrid = {
|
||||
template: '#grid-template',
|
||||
props: {
|
||||
data: Array,
|
||||
columns: Array,
|
||||
filterKey: String
|
||||
},
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
sortKey: '',
|
||||
sortOrders: props.columns.reduce((o, key) => (o[key] = 1, o), {})
|
||||
})
|
||||
|
||||
const filteredData = computed(() => {
|
||||
let { data, filterKey } = props
|
||||
if (filterKey) {
|
||||
filterKey = filterKey.toLowerCase()
|
||||
data = data.filter(row => {
|
||||
return Object.keys(row).some(key => {
|
||||
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
|
||||
})
|
||||
})
|
||||
}
|
||||
const { sortKey } = state
|
||||
if (sortKey) {
|
||||
const order = state.sortOrders[sortKey]
|
||||
data = data.slice().sort((a, b) => {
|
||||
a = a[sortKey]
|
||||
b = b[sortKey]
|
||||
return (a === b ? 0 : a > b ? 1 : -1) * order
|
||||
})
|
||||
}
|
||||
return data
|
||||
})
|
||||
|
||||
function sortBy(key) {
|
||||
state.sortKey = key
|
||||
state.sortOrders[key] *= -1
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
filteredData,
|
||||
sortBy,
|
||||
capitalize
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- App template (in DOM) -->
|
||||
<div id="demo">
|
||||
<form id="search">
|
||||
Search <input name="query" v-model="searchQuery">
|
||||
</form>
|
||||
<demo-grid
|
||||
:data="gridData"
|
||||
:columns="gridColumns"
|
||||
:filter-key="searchQuery">
|
||||
</demo-grid>
|
||||
</div>
|
||||
<!-- App script -->
|
||||
<script>
|
||||
new Vue({
|
||||
components: {
|
||||
DemoGrid
|
||||
},
|
||||
data: () => ({
|
||||
searchQuery: '',
|
||||
gridColumns: ['name', 'power'],
|
||||
gridData: [
|
||||
{ name: 'Chuck Norris', power: Infinity },
|
||||
{ name: 'Bruce Lee', power: 9000 },
|
||||
{ name: 'Jackie Chan', power: 7000 },
|
||||
{ name: 'Jet Li', power: 8000 }
|
||||
]
|
||||
})
|
||||
}).$mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 2px solid #42b983;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #42b983;
|
||||
color: rgba(255,255,255,0.66);
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
th, td {
|
||||
min-width: 120px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
th.active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
th.active .arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
|
||||
.arrow.asc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid #fff;
|
||||
}
|
||||
|
||||
.arrow.dsc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid #fff;
|
||||
}
|
||||
</style>
|
66
examples/composition/markdown.html
Normal file
66
examples/composition/markdown.html
Normal file
@ -0,0 +1,66 @@
|
||||
<script src="../../node_modules/marked/marked.min.js"></script>
|
||||
<script src="../../node_modules/lodash/lodash.min.js"></script>
|
||||
<script src="../../dist/vue.min.js"></script>
|
||||
|
||||
<div id="editor">
|
||||
<textarea :value="input" @input="update"></textarea>
|
||||
<div v-html="output"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { ref, computed } = Vue
|
||||
|
||||
new Vue({
|
||||
setup() {
|
||||
const input = ref('# hello')
|
||||
const output = computed(() => marked.marked(input.value))
|
||||
const update = _.debounce(e => {
|
||||
input.value = e.target.value
|
||||
}, 300)
|
||||
|
||||
return {
|
||||
input,
|
||||
output,
|
||||
update
|
||||
}
|
||||
}
|
||||
}).$mount('#editor')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#editor {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
textarea,
|
||||
#editor div {
|
||||
display: inline-block;
|
||||
width: 49%;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: none;
|
||||
border-right: 1px solid #ccc;
|
||||
resize: none;
|
||||
outline: none;
|
||||
background-color: #f6f6f6;
|
||||
font-size: 14px;
|
||||
font-family: 'Monaco', courier, monospace;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #f66;
|
||||
}
|
||||
</style>
|
172
examples/composition/svg.html
Normal file
172
examples/composition/svg.html
Normal file
@ -0,0 +1,172 @@
|
||||
<script src="../../dist/vue.min.js"></script>
|
||||
<script>
|
||||
const { ref, computed, createApp } = Vue
|
||||
|
||||
// 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
|
||||
},
|
||||
setup(props) {
|
||||
return {
|
||||
point: computed(() =>
|
||||
valueToPoint(+props.stat.value + 10, props.index, props.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',
|
||||
setup(props) {
|
||||
return {
|
||||
points: computed(() => {
|
||||
const total = props.stats.length
|
||||
return props.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 }
|
||||
]
|
||||
|
||||
new Vue({
|
||||
components: {
|
||||
Polygraph
|
||||
},
|
||||
setup() {
|
||||
const newLabel = ref('')
|
||||
const stats = ref(globalStats)
|
||||
|
||||
function add(e) {
|
||||
e.preventDefault()
|
||||
if (!newLabel.value) return
|
||||
stats.value.push({
|
||||
label: newLabel.value,
|
||||
value: 100
|
||||
})
|
||||
newLabel.value = ''
|
||||
}
|
||||
|
||||
function remove(stat) {
|
||||
if (stats.value.length > 3) {
|
||||
stats.value.splice(stats.value.indexOf(stat), 1)
|
||||
} else {
|
||||
alert("Can't delete more!")
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
newLabel,
|
||||
stats,
|
||||
add,
|
||||
remove
|
||||
}
|
||||
}
|
||||
}).$mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
}
|
||||
|
||||
polygon {
|
||||
fill: #42b983;
|
||||
opacity: 0.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>
|
241
examples/composition/todomvc.html
Normal file
241
examples/composition/todomvc.html
Normal file
@ -0,0 +1,241 @@
|
||||
<script src="../../dist/vue.min.js"></script>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../../node_modules/todomvc-app-css/index.css"
|
||||
/>
|
||||
|
||||
<div id="app">
|
||||
<section class="todoapp">
|
||||
<header class="header">
|
||||
<h1>todos</h1>
|
||||
<input
|
||||
class="new-todo"
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
placeholder="What needs to be done?"
|
||||
v-model="state.newTodo"
|
||||
@keyup.enter="addTodo"
|
||||
/>
|
||||
</header>
|
||||
<section class="main" v-show="state.todos.length">
|
||||
<input
|
||||
id="toggle-all"
|
||||
class="toggle-all"
|
||||
type="checkbox"
|
||||
v-model="state.allDone"
|
||||
/>
|
||||
<label for="toggle-all">Mark all as complete</label>
|
||||
<ul class="todo-list">
|
||||
<li
|
||||
v-for="todo in state.filteredTodos"
|
||||
class="todo"
|
||||
:key="todo.id"
|
||||
:class="{ completed: todo.completed, editing: todo === state.editedTodo }"
|
||||
>
|
||||
<div class="view">
|
||||
<input class="toggle" type="checkbox" v-model="todo.completed" />
|
||||
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
|
||||
<button class="destroy" @click="removeTodo(todo)"></button>
|
||||
</div>
|
||||
<input
|
||||
class="edit"
|
||||
type="text"
|
||||
v-model="todo.title"
|
||||
v-todo-focus="todo === state.editedTodo"
|
||||
@blur="doneEdit(todo)"
|
||||
@keyup.enter="doneEdit(todo)"
|
||||
@keyup.escape="cancelEdit(todo)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer class="footer" v-show="state.todos.length">
|
||||
<span class="todo-count">
|
||||
<strong>{{ state.remaining }}</strong>
|
||||
<span>{{ state.remainingText }}</span>
|
||||
</span>
|
||||
<ul class="filters">
|
||||
<li>
|
||||
<a href="#/all" :class="{ selected: state.visibility === 'all' }"
|
||||
>All</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#/active"
|
||||
:class="{ selected: state.visibility === 'active' }"
|
||||
>Active</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#/completed"
|
||||
:class="{ selected: state.visibility === 'completed' }"
|
||||
>Completed</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button
|
||||
class="clear-completed"
|
||||
@click="removeCompleted"
|
||||
v-show="state.todos.length > state.remaining"
|
||||
>
|
||||
Clear completed
|
||||
</button>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { reactive, computed, watchEffect, onMounted, onUnmounted } = Vue
|
||||
|
||||
const STORAGE_KEY = 'todos-vuejs-3.x'
|
||||
const todoStorage = {
|
||||
fetch() {
|
||||
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
|
||||
todos.forEach((todo, index) => {
|
||||
todo.id = index
|
||||
})
|
||||
todoStorage.uid = todos.length
|
||||
return todos
|
||||
},
|
||||
save(todos) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
|
||||
}
|
||||
}
|
||||
|
||||
const filters = {
|
||||
all(todos) {
|
||||
return todos
|
||||
},
|
||||
active(todos) {
|
||||
return todos.filter(todo => {
|
||||
return !todo.completed
|
||||
})
|
||||
},
|
||||
completed(todos) {
|
||||
return todos.filter(function (todo) {
|
||||
return todo.completed
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function pluralize(n) {
|
||||
return n === 1 ? 'item' : 'items'
|
||||
}
|
||||
|
||||
new Vue({
|
||||
setup() {
|
||||
const state = reactive({
|
||||
todos: todoStorage.fetch(),
|
||||
editedTodo: null,
|
||||
newTodo: '',
|
||||
beforeEditCache: '',
|
||||
visibility: 'all',
|
||||
remaining: computed(() => {
|
||||
return filters.active(state.todos).length
|
||||
}),
|
||||
remainingText: computed(() => {
|
||||
return ` ${pluralize(state.remaining)} left`
|
||||
}),
|
||||
filteredTodos: computed(() => {
|
||||
return filters[state.visibility](state.todos)
|
||||
}),
|
||||
allDone: computed({
|
||||
get: function () {
|
||||
return state.remaining === 0
|
||||
},
|
||||
set: function (value) {
|
||||
state.todos.forEach(todo => {
|
||||
todo.completed = value
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
todoStorage.save(state.todos)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('hashchange', onHashChange)
|
||||
onHashChange()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('hashchange', onHashChange)
|
||||
})
|
||||
|
||||
function onHashChange() {
|
||||
const visibility = window.location.hash.replace(/#\/?/, '')
|
||||
if (filters[visibility]) {
|
||||
state.visibility = visibility
|
||||
} else {
|
||||
window.location.hash = ''
|
||||
state.visibility = 'all'
|
||||
}
|
||||
}
|
||||
|
||||
function addTodo() {
|
||||
const value = state.newTodo && state.newTodo.trim()
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
state.todos.push({
|
||||
id: todoStorage.uid++,
|
||||
title: value,
|
||||
completed: false
|
||||
})
|
||||
state.newTodo = ''
|
||||
}
|
||||
|
||||
function removeTodo(todo) {
|
||||
state.todos.splice(state.todos.indexOf(todo), 1)
|
||||
}
|
||||
|
||||
function editTodo(todo) {
|
||||
state.beforeEditCache = todo.title
|
||||
state.editedTodo = todo
|
||||
}
|
||||
|
||||
function doneEdit(todo) {
|
||||
if (!state.editedTodo) {
|
||||
return
|
||||
}
|
||||
state.editedTodo = null
|
||||
todo.title = todo.title.trim()
|
||||
if (!todo.title) {
|
||||
removeTodo(todo)
|
||||
}
|
||||
}
|
||||
|
||||
function cancelEdit(todo) {
|
||||
state.editedTodo = null
|
||||
todo.title = state.beforeEditCache
|
||||
}
|
||||
|
||||
function removeCompleted() {
|
||||
state.todos = filters.active(state.todos)
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
addTodo,
|
||||
removeTodo,
|
||||
editTodo,
|
||||
doneEdit,
|
||||
cancelEdit,
|
||||
removeCompleted
|
||||
}
|
||||
},
|
||||
|
||||
directives: {
|
||||
'todo-focus': (el, { value }) => {
|
||||
if (value) {
|
||||
el.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}).$mount('#app')
|
||||
</script>
|
124
examples/composition/tree.html
Normal file
124
examples/composition/tree.html
Normal file
@ -0,0 +1,124 @@
|
||||
<script src="../../dist/vue.min.js"></script>
|
||||
|
||||
<!-- item template -->
|
||||
<script type="text/x-template" id="item-template">
|
||||
<li>
|
||||
<div
|
||||
:class="{bold: isFolder}"
|
||||
@click="toggle"
|
||||
@dblclick="changeType">
|
||||
{{model.name}}
|
||||
<span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
|
||||
</div>
|
||||
<ul v-if="isFolder" v-show="open">
|
||||
<tree-item
|
||||
class="item"
|
||||
v-for="model in model.children"
|
||||
:model="model">
|
||||
</tree-item>
|
||||
<li class="add" @click="addChild">+</li>
|
||||
</ul>
|
||||
</li>
|
||||
</script>
|
||||
<!-- item script -->
|
||||
<script>
|
||||
const { reactive, computed, toRefs } = Vue
|
||||
|
||||
const TreeItem = {
|
||||
name: 'TreeItem', // necessary for self-reference
|
||||
template: '#item-template',
|
||||
props: {
|
||||
model: Object
|
||||
},
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
open: false,
|
||||
isFolder: computed(() => {
|
||||
return props.model.children && props.model.children.length
|
||||
})
|
||||
})
|
||||
|
||||
function toggle() {
|
||||
state.open = !state.open
|
||||
}
|
||||
|
||||
function changeType() {
|
||||
if (!state.isFolder) {
|
||||
Vue.set(props.model, 'children', [])
|
||||
addChild()
|
||||
state.open = true
|
||||
}
|
||||
}
|
||||
|
||||
function addChild() {
|
||||
props.model.children.push({ name: 'new stuff' })
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
toggle,
|
||||
changeType,
|
||||
addChild
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>(You can double click on an item to turn it into a folder.)</p>
|
||||
|
||||
<!-- the app root element -->
|
||||
<ul id="demo">
|
||||
<tree-item class="item" :model="treeData"></tree-item>
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
const treeData = {
|
||||
name: 'My Tree',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [{ name: 'hello' }, { name: 'wat' }]
|
||||
},
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [{ name: 'hello' }, { name: 'wat' }]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
new Vue({
|
||||
components: {
|
||||
TreeItem
|
||||
},
|
||||
data: () => ({
|
||||
treeData
|
||||
})
|
||||
}).$mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Menlo, Consolas, monospace;
|
||||
color: #444;
|
||||
}
|
||||
.item {
|
||||
cursor: pointer;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
ul {
|
||||
padding-left: 1em;
|
||||
line-height: 1.5em;
|
||||
list-style-type: dot;
|
||||
}
|
||||
</style>
|
@ -93,7 +93,7 @@
|
||||
"lodash.template": "^4.4.0",
|
||||
"lodash.uniq": "^4.5.0",
|
||||
"lru-cache": "^7.8.1",
|
||||
"marked": "^3.0.8",
|
||||
"marked": "^4.0.6",
|
||||
"memory-fs": "^0.5.0",
|
||||
"prettier": "^2.6.2",
|
||||
"puppeteer": "^14.1.1",
|
||||
|
@ -32,7 +32,7 @@ specifiers:
|
||||
lodash.template: ^4.4.0
|
||||
lodash.uniq: ^4.5.0
|
||||
lru-cache: ^7.8.1
|
||||
marked: ^3.0.8
|
||||
marked: ^4.0.6
|
||||
memory-fs: ^0.5.0
|
||||
prettier: ^2.6.2
|
||||
puppeteer: ^14.1.1
|
||||
@ -83,7 +83,7 @@ devDependencies:
|
||||
lodash.template: 4.5.0
|
||||
lodash.uniq: 4.5.0
|
||||
lru-cache: 7.10.1
|
||||
marked: 3.0.8
|
||||
marked: 4.0.16
|
||||
memory-fs: 0.5.0
|
||||
prettier: 2.6.2
|
||||
puppeteer: 14.1.1
|
||||
@ -3747,8 +3747,8 @@ packages:
|
||||
object-visit: 1.0.1
|
||||
dev: true
|
||||
|
||||
/marked/3.0.8:
|
||||
resolution: {integrity: sha512-0gVrAjo5m0VZSJb4rpL59K1unJAMb/hm8HRXqasD8VeC8m91ytDPMritgFSlKonfdt+rRYYpP/JfLxgIX8yoSw==}
|
||||
/marked/4.0.16:
|
||||
resolution: {integrity: sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==}
|
||||
engines: {node: '>= 12'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
@ -52,11 +52,11 @@ describe('e2e: commits', () => {
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
|
||||
// test(
|
||||
// 'composition',
|
||||
// async () => {
|
||||
// await testCommits('composition')
|
||||
// },
|
||||
// E2E_TIMEOUT
|
||||
// )
|
||||
test(
|
||||
'composition',
|
||||
async () => {
|
||||
await testCommits('composition')
|
||||
},
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
})
|
||||
|
@ -5,9 +5,10 @@ export function getExampleUrl(
|
||||
name: string,
|
||||
apiType: 'classic' | 'composition'
|
||||
) {
|
||||
const file = apiType === 'composition' ? `${name}.html` : `${name}/index.html`
|
||||
return `file://${path.resolve(
|
||||
__dirname,
|
||||
`../../examples/${apiType}/${name}/index.html`
|
||||
`../../examples/${apiType}/${file}`
|
||||
)}`
|
||||
}
|
||||
|
||||
|
@ -105,11 +105,11 @@ describe('e2e: grid', () => {
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
|
||||
// test(
|
||||
// 'composition',
|
||||
// async () => {
|
||||
// await testGrid('composition')
|
||||
// },
|
||||
// E2E_TIMEOUT
|
||||
// )
|
||||
test(
|
||||
'composition',
|
||||
async () => {
|
||||
await testGrid('composition')
|
||||
},
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
})
|
||||
|
@ -36,11 +36,11 @@ describe('e2e: markdown', () => {
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
|
||||
// test(
|
||||
// 'composition',
|
||||
// async () => {
|
||||
// await testMarkdown('composition')
|
||||
// },
|
||||
// E2E_TIMEOUT
|
||||
// )
|
||||
test(
|
||||
'composition',
|
||||
async () => {
|
||||
await testMarkdown('composition')
|
||||
},
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { setupPuppeteer, getExampleUrl, E2E_TIMEOUT } from './e2eUtils'
|
||||
|
||||
declare const stats: {
|
||||
declare const globalStats: {
|
||||
label: string
|
||||
value: number
|
||||
}[]
|
||||
@ -22,7 +22,7 @@ describe('e2e: svg', () => {
|
||||
expect(
|
||||
await page().evaluate(
|
||||
total => {
|
||||
const points = stats
|
||||
const points = globalStats
|
||||
.map((stat, i) => {
|
||||
const point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
@ -41,7 +41,7 @@ describe('e2e: svg', () => {
|
||||
async function assertLabels(total: number) {
|
||||
const positions = await page().evaluate(
|
||||
total => {
|
||||
return stats.map((stat, i) => {
|
||||
return globalStats.map((stat, i) => {
|
||||
const point = valueToPoint(+stat.value + 10, i, total)
|
||||
return [point.x, point.y]
|
||||
})
|
||||
@ -60,7 +60,7 @@ describe('e2e: svg', () => {
|
||||
// assert each value of stats is correct
|
||||
async function assertStats(expected: number[]) {
|
||||
const statsValue = await page().evaluate(() => {
|
||||
return stats.map(stat => +stat.value)
|
||||
return globalStats.map(stat => +stat.value)
|
||||
})
|
||||
expect(statsValue).toEqual(expected)
|
||||
}
|
||||
@ -141,11 +141,11 @@ describe('e2e: svg', () => {
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
|
||||
// test(
|
||||
// 'composition',
|
||||
// async () => {
|
||||
// await testSvg('composition')
|
||||
// },
|
||||
// E2E_TIMEOUT
|
||||
// )
|
||||
test(
|
||||
'composition',
|
||||
async () => {
|
||||
await testSvg('composition')
|
||||
},
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
})
|
||||
|
@ -172,11 +172,11 @@ describe('e2e: todomvc', () => {
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
|
||||
// test(
|
||||
// 'composition',
|
||||
// async () => {
|
||||
// await testTodomvc('composition')
|
||||
// },
|
||||
// E2E_TIMEOUT
|
||||
// )
|
||||
test(
|
||||
'composition',
|
||||
async () => {
|
||||
await testTodomvc('composition')
|
||||
},
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
})
|
||||
|
@ -98,11 +98,11 @@ describe('e2e: tree', () => {
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
|
||||
// test(
|
||||
// 'composition',
|
||||
// async () => {
|
||||
// await testTree('composition')
|
||||
// },
|
||||
// E2E_TIMEOUT
|
||||
// )
|
||||
test(
|
||||
'composition',
|
||||
async () => {
|
||||
await testTree('composition')
|
||||
},
|
||||
E2E_TIMEOUT
|
||||
)
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user