Useful front-end materials

2018-04-05

About Input

  • Autocomplete: https://www.w3schools.com/howto/howto_js_autocomplete.asp

  • Regular expression:

    • Email :

      1
      const pattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
    • Number:

      1
      const pattern = /^\d+$/;
    • US phone:

      1
      2

    Expiration of login session

    authLog:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    export const authLogIn = (email, password) => {
    return dispatch => {
    dispatch(authStart());
    const authData = {
    email: email,
    password: password
    };
    let url = '/login';
    axios.post(url, authData)
    .then(response => {
    if(response.data.status == "Success"){
    const expirationDate = new Date(new Date().getTime() + 3600*1000);
    localStorage.setItem('username',response.data.info.uname);
    localStorage.setItem('expirationDate', expirationDate);
    localStorage.setItem('email', response.data.info.email);
    localStorage.setItem('uid', response.data.info.uid);
    localStorage.setItem('token', response.data.info.token); //split
    dispatch(authSuccess(response.data.info.email, response.data.info.uname, response.data.info.uid,response.data.info.token));
    dispatch(checkAuthTimeout(3600));
    }
    else{
    alert(response.data.info);
    }
    }).catch(error => {
    console.log(error);
    alert("Connection Failed....");
    });
    }
    }

    authCheckState:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export const authCheckState = () => {
    return dispatch => {
    const email = localStorage.getItem('email');
    if(!email){
    dispatch(logout());
    }
    else{
    const expirationDate = new Date(localStorage.getItem('expirationDate'));
    if(expirationDate > new Date()){
    const username = localStorage.getItem('username');
    const userId = localStorage.getItem('uid');
    const token = localStorage.getItem('token');
    dispatch(authSuccess(email, username, userId, token));
    dispatch(checkAuthTimeout((expirationDate.getTime() - new Date().getTime())/1000));
    }
    else{
    alert("Your token has been expired.. Please log in again!");
    dispatch(logout());
    }
    }
    };
    }

    logout:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    export const logout = () => {
    localStorage.removeItem('email');
    localStorage.removeItem('expirationDate');
    localStorage.removeItem('username');
    localStorage.removeItem("token");
    localStorage.removeItem("uid");
    return {
    type: actionTypes.AUTH_LOGOUT
    };
    }

    checkAuthTimeOut:

    1
    2
    3
    4
    5
    6
    7
    8
    export const checkAuthTimeout = (expirationTime) => {
    return dispatch => {
    setTimeout(() => {
    alert("You have logged in for 1 hour, for security, please log in again!");
    dispatch(logout());
    },expirationTime * 1000);
    };
    };

    React 404

1
<Route path='*' exact={true} component={my404Component} />

Customize your own modal box

Create modal: https://www.w3schools.com/howto/howto_css_modals.asp

Modal Animation: https://codepen.io/designcouch/pen/obvKxm

React solution:

css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@keyframes loadAlert{
from{padding-top: 0px}
to{padding-top: 200px}
}
.alertModalBox {
display: block;
position: fixed;
z-index: 1;
/*padding-top: 0px; Location of the box */
animation: loadAlert 0.5s;
animation-fill-mode: forwards;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
transition: padding-top 2s;
-moz-transition:padding-top 2s; /* Firefox 4 */
-webkit-transition:padding-top 2s; /* Safari and Chrome */
-o-transition:padding-top 2s; /* Opera */
}
.alertmodalcontent {
border-radius: 2em;
background-color: #fefefe;
margin: auto;
padding: 20px;
/* border: 1px solid #888; */
width: 500px;
min-height: 150px;
display: table;
position: relative;
}
.alertmodalcontent p{
font-weight: bold;
text-align: center;
/* float: left; */
font-size: 1.3em;
display: table-cell;
vertical-align: middle;
}
/* The Close Button */
.close {
color: #C0392B;
float: right;
font-size: 28px;
font-weight: bold;
position: absolute;
top: 0px;
right: 20px;
display: table-cell;
}
.close:hover,
.close:focus {
color: brown;
text-decoration: none;
cursor: pointer;
}
.alertImg{
width: 100px;
float: left;
}

JS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React, { Component } from 'react';
import * as classes from './alertBox.css';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions/index';
class alertBox extends Component{
closeBox = () => {
this.props.closeAlert();
}
render(){
return (
<div id={classes.alertModal} className={classes.alertModalBox} style={this.props.display}>
<div className={classes.alertmodalcontent}>
{this.props.isError?
<img src="/static/img/cuteerror.png" className={classes.alertImg}/>
:
<img src="/static/img/cutesuccess.png" className={classes.alertImg}/>
}
<p>{this.props.alertMsg}</p>
{/* <p>Upload information successfully</p> */}
<span className={classes.close} onClick={this.closeBox}>&times;</span>
</div>
</div>
);
}
}
const mapStateToProps = state => {
return {
alertMsg: state.auth.error,
display: state.auth.alertDisplay,
isError: state.auth.isError
};
}
const mapDispatchToProps = dispatch => {
return {
closeAlert: () => dispatch(actions.closeAlert())
};
}
export default connect(mapStateToProps, mapDispatchToProps)(alertBox);

Vue HTML5 editor

Upload.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<template>
<div class="uploadContainer">
<h1>Upload New Item</h1>
<br/>
<div class="form-group">
<label>Title:</label>
<input type="text" class="form-control" placeholder="What's your item?">
</div>
<vue-html5-editor :content="content" :height="400"
@change="updateData"></vue-html5-editor>
<div class="form-group">
<label>Price:</label>
<input type="text" class="form-control" placeholder="How much is it?">
</div>
</div>
</template>
<script>
export default {
data(){
return {content: 'Enter something 0.0'}
},
methods: {
updateData(e = ''){
this.content = e;
console.info(e);
}
}
}
</script>
<style scoped>
.uploadContainer{
width: 60%;
margin: 0 auto;
}
.form-group{
height: 40px;
}
.form-group label{
float: left;
width: 20%;
vertical-align: middle;
text-align: left;
}
.form-group input{
float: left;
width: 80%;
}
</style>

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import App from './App';
import router from './router';
import 'font-awesome.css';
import initRichText from './components/shared/initHTMLEditor';
initRichText();
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App},
template: '<App/>'
});

initHTMLEditor.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import Vue from 'vue'
import VueHtml5Editor from 'vue-html5-editor'
export default function () {
let opt = {
// 全局组件名称,使用new VueHtml5Editor(options)时该选项无效
name: "vue-html5-editor",
// 是否显示模块名称,开启的话会在工具栏的图标后台直接显示名称
showModuleName: true,
// 自定义各个图标的class,默认使用的是font-awesome提供的图标
icons: {
text: "fa fa-pencil",
color: "fa fa-paint-brush",
font: "fa fa-font",
align: "fa fa-align-justify",
list: "fa fa-list",
link: "fa fa-chain",
unlink: "fa fa-chain-broken",
tabulation: "fa fa-table",
image: "fa fa-file-image-o",
hr: "fa fa-minus",
eraser: "fa fa-eraser",
undo: "fa-undo fa",
"full-screen": "fa fa-arrows-alt",
info: "fa fa-info",
},
// 配置图片模块
image: {
// 文件最大体积,单位字节
sizeLimit: 512 * 1024 * 10,
// 上传参数,默认把图片转为base64而不上传
// upload config,default null and convert image to base64
upload: {
url: null,
headers: {},
params: {},
fieldName: {}
},
// 压缩参数,默认使用localResizeIMG进行压缩,设置为null禁止压缩
// width和height是文件的最大宽高
compress: {
width: 600,
height: 600,
quality: 80
},
// 响应数据处理,最终返回图片链接
uploadHandler(responseText){
//default accept json data like {ok:false,msg:"unexpected"} or {ok:true,data:"image url"}
var json = JSON.parse(responseText);
console.info(json);
if (!json.ok) {
alert(json.msg)
} else {
return json.data
}
}
},
// 语言,内建的有英文(en-us)和中文(zh-cn)
language: "zh-cn",
// 自定义语言
i18n: {
"zh-cn": {
"align": "对齐方式",
"image": "图片",
"list": "列表",
"link": "链接",
"unlink": "去除链接",
"table": "表格",
"font": "文字",
"full screen": "全屏",
"text": "排版",
"eraser": "格式清除",
"info": "关于",
"color": "颜色",
"please enter a url": "请输入地址",
"create link": "创建链接",
"bold": "加粗",
"italic": "倾斜",
"underline": "下划线",
"strike through": "删除线",
"subscript": "上标",
"superscript": "下标",
"heading": "标题",
"font name": "字体",
"font size": "文字大小",
"left justify": "左对齐",
"center justify": "居中",
"right justify": "右对齐",
"ordered list": "有序列表",
"unordered list": "无序列表",
"fore color": "前景色",
"background color": "背景色",
"row count": "行数",
"column count": "列数",
"save": "确定",
"upload": "上传",
"progress": "进度",
"unknown": "未知",
"please wait": "请稍等",
"error": "错误",
"abort": "中断",
"reset": "重置"
}
},
// 隐藏不想要显示出来的模块
hiddenModules: [],
// 自定义要显示的模块,并控制顺序
visibleModules: [
"text",
"color",
"font",
"align",
"list",
"link",
"unlink",
"tabulation",
"image",
"hr",
"eraser",
"undo",
"full-screen",
"info",
],
// 扩展模块,具体可以参考examples或查看源码
// extended modules
modules: {
//omit,reference to source code of build-in modules
}
};
Vue.use(VueHtml5Editor, opt);
}

Vue load data before render()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
import Axios from 'axios';
export default {
name: "ProductList",
data(){
return {
items:null
}
},
created() {
Axios.get('URL', {
responseType: 'json'
}).then((response) => {
console.log("Success");
console.log(response);
this.items = response.data.Items;
console.log(this.items);
}).catch(function(error){
console.log("Error");
console.log(error);
});
}
}
</script>

Save JSON object to a .json file

1
2
3
4
5
6
7
8
9
10
var dataToWrite = {"Hello": "world"};
var fs = require('fs');
fs.writeFile('output.json', JSON.stringify(dataToWrite), 'utf8', function(err){
if(err){
console.log("Some error occured!");
}
else{
console.log("It's saved!");
}
})

CSS unit

https://github.com/simaQ/cssfun/issues/1

CSS FlexBox

People will easily face a problem that is using flexbox to display contents, and set the justify-content as space-between However, if the last line is not full-filled, we may see that it looks like

1
2
A B C
D E

Rather than

1
2
A B C
D E

Here comes a good solution to solve this

1
2
3
4
5
6
7
8
9
10
.productList{
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-between;
}
.productList:after {
content: "";
width: 18rem; // this value should be the same as the items in the productList class
}

Resource: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa


Comments: