Added Vue.js todo sample. (#106)

This commit is contained in:
Sergei Sergeev 2017-02-03 18:55:11 +03:00 committed by Vesa Juvonen
parent 96c84a2c68
commit d42b180100
30 changed files with 537 additions and 0 deletions

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -0,0 +1 @@
* text=auto

View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,14 @@
# Folders
.vscode
coverage
node_modules
sharepoint
src
temp
# Files
*.csproj
.git*
.yo-rc.json
gulpfile.js
tsconfig.json

View File

@ -0,0 +1,7 @@
{
"vsicons.presets.angular": false,
"typescript.tsdk": "./node_modules/typescript/lib",
"files.associations": {
"*.vue": "vue"
}
}

View File

@ -0,0 +1,48 @@
# Todo Client Web Part built with Vue.js and Vue's single-file components
## Summary
Sample Todo web part demonstrating how you can utilize [Vue](https://vuejs.org/v2) (a progressive framework for building user interfaces) with SharePoint Framework using handy [single-file components](https://vuejs.org/v2/guide/single-file-components.html) approach.
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/drop-RC0-green.svg)
## Applies to
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
## Solution
Solution|Author(s)
--------|---------
vuejs-todo-single-file-component|Sergei Sergeev ([@sergeev_srg](https://twitter.com/sergeev_srg))
## Version history
Version|Date|Comments
-------|----|--------
0.0.1|January 27, 2017|Initial version.
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
---
## Minimal Path to Awesome
- Clone this repo
- In the command line run:
- `npm i`
- `gulp serve`
## Features
Demonstrates\uses below features:
- integration between third party frontend framework Vue.js and SharePoint Framework
- build pipeline customizations
- modern component-based architecture
- separation of concerns between markup, styling and code
- single-file components architecture for Vue.js
- custom webpack loaders

View File

@ -0,0 +1,15 @@
{
"entries": [
{
"entry": "./lib/webparts/todo/TodoWebPart.js",
"manifest": "./src/webparts/todo/TodoWebPart.manifest.json",
"outputPath": "./dist/todo.bundle.js"
}
],
"externals": {
"vue": "node_modules/vue/dist/vue.runtime.js"
},
"localizedResources": {
"todoStrings": "webparts/todo/loc/{locale}.js"
}
}

View File

@ -0,0 +1,6 @@
{
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "vue-todo",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,10 @@
{
"solution": {
"name": "vue-todo-client-side-solution",
"id": "3a3be1f4-edfd-4816-8cfb-f100f1727031",
"version": "1.0.0.0"
},
"paths": {
"zippedPackage": "solution/vue-todo.sppkg"
}
}

View File

@ -0,0 +1,3 @@
{
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,9 @@
{
"port": 4321,
"initialPage": "https://localhost:5432/workbench",
"https": true,
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,50 @@
{
// Display errors as warnings
"displayAsWarning": true,
// The TSLint task may have been configured with several custom lint rules
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
// project). If true, this flag will deactivate any of these rules.
"removeExistingRules": true,
// When true, the TSLint task is configured with some default TSLint "rules.":
"useDefaultConfigAsBase": false,
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
// which are active, other than the list of rules below.
"lintConfig": {
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"label-undefined": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-case": true,
"no-duplicate-key": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-unused-imports": true,
"no-unused-variable": true,
"no-unreachable": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"valid-typeof": true,
"variable-name": false,
"whitespace": false,
"prefer-const": true
}
}
}

View File

@ -0,0 +1,3 @@
{
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,35 @@
'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
var merge = require('webpack-merge');
build.sass.setConfig({
sassMatch: []
});
build.configureWebpack.setConfig({
additionalConfiguration: function (config) {
var vueConfig = {
module: {
loaders: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
vue: {
esModule: true
}
};
return merge(config, vueConfig);
}
});
build.copyStaticAssets.setConfig({
includeExtensions: ['vue', 'scss']
});
build.initialize(gulp);

View File

@ -0,0 +1,37 @@
{
"name": "vue-todo",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"@microsoft/sp-client-base": "~0.7.0",
"@microsoft/sp-client-preview": "~0.9.0",
"@microsoft/sp-core-library": "~0.1.2",
"@microsoft/sp-webpart-base": "~0.4.0",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"vue": "^2.1.10",
"vue-class-component": "^4.4.0",
"vue-property-decorator": "^3.4.0",
"vue-template-compiler": "^2.1.10"
},
"devDependencies": {
"@microsoft/sp-build-web": "~0.9.0",
"@microsoft/sp-module-interfaces": "~0.7.0",
"@microsoft/sp-webpart-workbench": "~0.8.0",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0",
"gulp": "~3.9.1",
"node-sass": "^4.3.0",
"sass-loader": "^4.1.1",
"css-loader": "^0.26.1",
"vue-loader": "^10.0.2",
"webpack-merge": "^2.4.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
}
}

View File

@ -0,0 +1,5 @@
var context = require.context('.', true, /.+\.test\.js?$/);
context.keys().forEach(context);
module.exports = context;

View File

@ -0,0 +1,4 @@
export interface ITodoWebPartProps {
message: string;
todos: string[];
}

View File

@ -0,0 +1,21 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"id": "1c993233-909a-4662-8643-8548d961f2b3",
"alias": "TodoWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"preconfiguredEntries": [{
"groupId": "1c993233-909a-4662-8643-8548d961f2b3",
"group": { "default": "Under Development" },
"title": { "default": "Todo" },
"description": { "default": "My Todo&#39;s" },
"officeFabricIconFontName": "Page",
"properties": {
"message": "todos",
"todos": []
}
}]
}

View File

@ -0,0 +1,67 @@
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as Vue from 'vue';
import TodoComponent from './components/todo/Todo.vue';
import * as strings from 'todoStrings';
import { ITodoWebPartProps } from './ITodoWebPartProps';
export default class TodoWebPart extends BaseClientSideWebPart<ITodoWebPartProps> {
public data: ITodoWebPartProps;
public render(): void {
this.domElement.innerHTML = `
<div id="app-${this.context.instanceId}">
</div>`;
this.data = {
message: this.properties.message,
todos: this.properties.todos
};
new Vue({
el: `#app-${this.context.instanceId}`,
render: h => h(TodoComponent, {
props: this.data
})
});
}
public onBeforeSerialize(): any {
this.properties.message = this.data.message;
this.properties.todos = this.data.todos;
return undefined;
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('message', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,3 @@
li {
font-size: 30px;
}

View File

@ -0,0 +1,12 @@
import * as Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
@Component
export default class TodoItem extends Vue {
@Prop
public todoText: string;
public onComplete(): void {
this.$emit('completed', this.todoText);
}
}

View File

@ -0,0 +1,14 @@
<template>
<li>
<div class="view">
<input type="checkbox" @click="onComplete">
<label>{{todoText}}</label>
</div>
</li>
</template>
<script>
module.exports = require('./todoitem');
</script>
<style scoped src="./todoitem.scss" lang="sass"></style>

View File

@ -0,0 +1,20 @@
h1 {
text-align: center;
margin: 0;
color: rgba(175, 47, 47, 0.15);
font-size: 80px;
}
ul{
list-style: none;
}
#new-todo {
text-align: center;
input {
padding: 16px 16px 16px 16px;
border: 1px solid #d6d4d4;
width: 300px;
}
}

View File

@ -0,0 +1,35 @@
import * as Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
import TodoItem from '../todo-item/Todoitem.vue';
import { ITodoWebPartProps } from '../../ITodoWebPartProps';
@Component({
components: {
'todo-item': TodoItem
}
})
export default class Todo extends Vue implements ITodoWebPartProps {
@Prop
public message: string;
@Prop
public todos: string[];
public mytodos: string[] = this.todos;
public todoTitle: string = '';
public addTodo(): void {
if(!this.todoTitle){
return;
}
this.mytodos.push(this.todoTitle);
this.todoTitle = '';
}
public completed(todo: string): void {
const index: number = this.mytodos.indexOf(todo, 0);
if (index > -1) {
this.mytodos.splice(index, 1);
}
}
}

View File

@ -0,0 +1,17 @@
<template>
<div>
<h1>{{message}}</h1>
<div id="new-todo">
<input type="text" @keyup.enter="addTodo" v-model="todoTitle" placeholder="what needs to be done?">
</div>
<ul>
<todo-item v-for="(todo, index) in mytodos" :key="index + todo" :todoText="todo" v-on:completed="completed"></todo-item>
</ul>
</div>
</template>
<script>
module.exports = require('./todo');
</script>
<style scoped src="./todo.scss" lang="sass"></style>

View File

@ -0,0 +1,7 @@
define([], function() {
return {
"PropertyPaneDescription": "Todo web part settings",
"BasicGroupName": "Title",
"DescriptionFieldLabel": "Change the title"
}
});

View File

@ -0,0 +1,10 @@
declare interface ITodoStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
}
declare module 'todoStrings' {
const strings: ITodoStrings;
export = strings;
}

View File

@ -0,0 +1,4 @@
declare module "*.vue" {
import Vue = require('vue');
export default typeof Vue;
}

View File

@ -0,0 +1,9 @@
/// <reference types="mocha" />
import { assert } from 'chai';
describe('TodoWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});

View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"types": [
"webpack-env"
]
}
}