add Message component

add Message component
This commit is contained in:
梁灏 2016-09-19 14:50:32 +08:00
parent c1cfacb2d4
commit 7c15ac9e31
27 changed files with 653 additions and 38 deletions

View file

@ -0,0 +1,34 @@
import Notification from './notification.vue';
import Vue from 'vue';
import { camelcaseToHyphen } from '../../../utils/assist';
Notification.newInstance = properties => {
const _props = properties || {};
let props = '';
Object.keys(_props).forEach(prop => {
props += ' :' + camelcaseToHyphen(prop) + '=' + prop;
});
const div = document.createElement('div');
div.innerHTML = `<notification${props}></notification>`;
document.body.appendChild(div);
const notification = new Vue({
el: div,
data: _props,
components: { Notification }
}).$children[0];
return {
notice (noticeProps) {
notification.add(noticeProps);
},
component: notification,
destroy () {
document.body.removeChild(div);
}
}
};
export default Notification;

View file

@ -0,0 +1,92 @@
<template>
<div :class="classes" :style="style" transition="move-up">
<div :class="[`${baseClass}-content`]" v-el:content>{{{ content }}}</div>
<span v-if="closable">
<a :class="[`${baseClass}-close`]" @click="close">
<span :class="[`${baseClass}-close-x`]"></span>
</a>
</span>
</div>
</template>
<script>
export default {
props: {
prefixCls: {
type: String,
default: ''
},
duration: {
type: Number,
default: 1.5
},
content: {
type: String,
default: ''
},
style: {
type: Object,
default: function() {
return {
right: '50%'
}
}
},
closable: {
type: Boolean,
default: false
},
className: {
type: String
},
key: {
type: String,
required: true
},
onClose: {
type: Function
}
},
computed: {
baseClass () {
return `${this.prefixCls}-notice`;
},
classes () {
return [
this.baseClass,
{
[`${this.className}`]: !!this.className,
[`${this.baseClass}-closable`]: this.closable
}
]
},
contentClasses () {
return `${this.baseClass}-content`;
}
},
methods: {
clearCloseTimer () {
if (this.closeTimer) {
clearTimeout(this.closeTimer);
this.closeTimer = null;
}
},
close () {
this.clearCloseTimer();
this.onClose();
this.$parent.close(this.key);
}
},
compiled () {
this.clearCloseTimer();
if (this.duration !== 0) {
this.closeTimer = setTimeout(() => {
this.close();
}, this.duration * 1000)
}
},
beforeDestroy () {
this.clearCloseTimer();
}
}
</script>

View file

@ -0,0 +1,92 @@
<template>
<div :class="classes" :style="style">
<Notice v-for="notice in notices"
:prefix-cls="prefixCls"
:style="notice.style"
:content="notice.content"
:duration="notice.duration"
:closable="notice.closable"
:key="notice.key"
:on-close="notice.onClose">
</Notice>
</div>
</template>
<script>
import Notice from './notice.vue';
const prefixCls = 'ivu-notification';
let seed = 0;
const now = Date.now();
function getUuid () {
return 'ivuNotification_' + now + '_' + (seed++);
}
export default {
components: { Notice },
props: {
prefixCls: {
type: String,
default: prefixCls
},
style: {
type: Object,
default: function () {
return {
top: '65px',
left: '50%'
}
}
},
content: {
type: String
},
className: {
type: String
},
transitionName: String
},
data () {
return {
notices: []
}
},
computed: {
classes () {
return [
`${this.prefixCls}`,
{
[`${this.className}`]: !!this.className
}
]
}
},
methods: {
add (notice) {
const key = getUuid();
let _notice = Object.assign({
style: {
right: '50%'
},
content: '',
duration: 1.5,
closable: false,
key: key
}, notice);
this.notices.push(_notice);
},
close (key) {
const notices = this.notices;
for (let i = 0; i < notices.length; i++) {
if (notices[i].key === key) {
this.notices.splice(i, 1);
break;
}
}
}
}
}
</script>

View file

@ -0,0 +1,81 @@
import Notification from '../base/notification';
const prefixCls = 'ivu-message';
const iconPrefixCls = 'ivu-icon';
let defaultDuration = 1.5;
let top;
let messageInstance;
const iconTypes = {
'info': 'information-circled',
'success': 'checkmark-circled',
'warning': 'android-alert',
'error': 'close-circled',
'loading': 'load-c'
};
function getMessageInstance () {
messageInstance = messageInstance || Notification.newInstance({
prefixCls: prefixCls,
transitionName: 'slide',
style: {
top: `${top}px`
}
});
return messageInstance;
}
function notice (content, duration = defaultDuration, type, onClose) {
if (!onClose) {
onClose = function () {
}
}
let iconType = iconTypes[type];
// if loading
const loadCls = type === 'loading' ? ' ivu-load-loop' : '';
let instance = getMessageInstance();
instance.notice({
duration: duration,
style: {},
content: `
<div class="${prefixCls}-custom-content ${prefixCls}-${type}">
<i class="${iconPrefixCls} ${iconPrefixCls}-${iconType}${loadCls}"></i>
<span>${content}</span>
</div>
`,
onClose: onClose
});
}
export default {
info (content, duration, onClose) {
return notice(content, duration, 'info', onClose);
},
success (content, duration, onClose) {
return notice(content, duration, 'success', onClose);
},
warning (content, duration, onClose) {
return notice(content, duration, 'warning', onClose);
},
error (content, duration, onClose) {
return notice(content, duration, 'error', onClose);
},
loading (content, duration, onClose) {
return notice(content, duration, 'loading', onClose);
},
config (options) {
if (options.top) {
top = options.top;
}
if (options.duration) {
defaultDuration = options.duration;
}
}
}

View file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
/*! /*!
* iView * iView
* Web: http://www.iviewui.com * Web: https://www.iviewui.com
* Github: https://github.com/iviewui/iview * Github: https://github.com/iview/iview
* Author: Aresn * Author: Aresn
*/.signin{color:red}.signup{color:#f60} */.signin{color:red}.signup{color:#f60}

View file

@ -20,6 +20,7 @@ import Breadcrumb from './components/breadcrumb';
import Alert from './components/alert'; import Alert from './components/alert';
import Collapse from './components/collapse'; import Collapse from './components/collapse';
import Card from './components/card'; import Card from './components/card';
import Message from './components/message';
const iview = { const iview = {
Button, Button,
@ -44,7 +45,8 @@ const iview = {
Breadcrumb, Breadcrumb,
Alert, Alert,
Collapse, Collapse,
Card Card,
Message
}; };
module.exports = iview; module.exports = iview;

View file

@ -45,6 +45,11 @@ router.map({
component: function (resolve) { component: function (resolve) {
require(['./routers/radio.vue'], resolve); require(['./routers/radio.vue'], resolve);
} }
},
'/msg': {
component: function (resolve) {
require(['./routers/msg.vue'], resolve);
}
} }
}); });

55
local/routers/msg.vue Normal file
View file

@ -0,0 +1,55 @@
<template>
<Button @click="info">info</Button>
<Button @click="success">success</Button>
<Button @click="error">error</Button>
<Button @click="warning">warning</Button>
<Button @click="loading">loading</Button>
</template>
<script>
import { Message, Button } from 'iview';
export default {
components: {
Message,
Button
},
props: {
},
data () {
return {
}
},
computed: {
},
methods: {
info () {
Message.info('欢迎来到iView', 3, () => {
console.log('close info');
});
},
success () {
Message.success('成功啦', 5, () => {
console.log('close successs');
});
},
error () {
Message.error('错误啦');
},
warning () {
Message.warning('来个警告');
},
loading () {
Message.loading('我是loading');
}
},
ready () {
Message.config({
top: 50,
duration: 8
});
}
}
</script>

View file

@ -1,6 +1,6 @@
{ {
"name": "iview", "name": "iview",
"version": "0.0.6", "version": "0.0.7",
"title": "iView", "title": "iView",
"description": "A high quality UI components Library with Vue.js", "description": "A high quality UI components Library with Vue.js",
"homepage": "http://www.iviewui.com", "homepage": "http://www.iviewui.com",

View file

@ -0,0 +1,30 @@
.fade-motion(@className, @keyframeName) {
.make-motion(@className, @keyframeName);
.@{className}-enter, .@{className}-appear {
opacity: 0;
animation-timing-function: linear;
}
.@{className}-leave {
animation-timing-function: linear;
}
}
.fade-motion(fade, ivuFade);
@keyframes ivuFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes ivuFadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View file

@ -0,0 +1,26 @@
.motion-common() {
animation-duration: @animation-time;
animation-fill-mode: both;
}
.make-motion(@className, @keyframeName) {
.@{className}-enter, .@{className}-appear {
.motion-common();
animation-play-state: paused;
}
.@{className}-leave {
.motion-common();
animation-play-state: paused;
}
.@{className}-enter, .@{className}-appear {
animation-name: ~"@{keyframeName}In";
animation-play-state: running;
}
.@{className}-leave {
animation-name: ~"@{keyframeName}Out";
animation-play-state: running;
}
}
@import "fade";
@import "move";

119
styles/animation/move.less Normal file
View file

@ -0,0 +1,119 @@
.move-motion(@className, @keyframeName) {
.make-motion(@className, @keyframeName);
.@{className}-enter, .@{className}-appear {
opacity: 0;
animation-timing-function: @ease-in-out;
}
.@{className}-leave {
animation-timing-function: @ease-in-out;
}
}
.move-motion(move-up, ivuMoveUp);
.move-motion(move-down, ivuMoveDown);
.move-motion(move-left, ivuMoveLeft);
.move-motion(move-right, ivuMoveRight);
@keyframes ivuMoveDownIn {
0% {
transform-origin: 0 0;
transform: translateY(100%);
opacity: 0;
}
100% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
}
@keyframes ivuMoveDownOut {
0% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateY(100%);
opacity: 0;
}
}
@keyframes ivuMoveLeftIn {
0% {
transform-origin: 0 0;
transform: translateX(-100%);
opacity: 0;
}
100% {
transform-origin: 0 0;
transform: translateX(0%);
opacity: 1;
}
}
@keyframes ivuMoveLeftOut {
0% {
transform-origin: 0 0;
transform: translateX(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateX(-100%);
opacity: 0;
}
}
@keyframes ivuMoveRightIn {
0% {
opacity: 0;
transform-origin: 0 0;
transform: translateX(100%);
}
100% {
opacity: 1;
transform-origin: 0 0;
transform: translateX(0%);
}
}
@keyframes ivuMoveRightOut {
0% {
transform-origin: 0 0;
transform: translateX(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateX(100%);
opacity: 0;
}
}
@keyframes ivuMoveUpIn {
0% {
transform-origin: 0 0;
transform: translateY(-100%);
opacity: 0;
}
100% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
}
@keyframes ivuMoveUpOut {
0% {
transform-origin: 0 0;
transform: translateY(0%);
opacity: 1;
}
100% {
transform-origin: 0 0;
transform: translateY(-100%);
opacity: 0;
}
}

View file

@ -1,4 +1,3 @@
@import "base"; @import "base";
@import "iconfont/ionicons"; @import "iconfont/ionicons";
@import "layout"; @import "layout";
@import "transition";

View file

@ -1,16 +0,0 @@
// Vue transition
.fade-transition {
.transition(opacity @transition-time @ease-in-out);
}
.fade-enter, .fade-leave {
opacity: 0;
}
.height-transition{
.transition(all @transition-time @ease-in-out);
}
.height-enter, .height-leave {
height: 0;
}

View file

@ -1,5 +1,5 @@
@alert-prefix-cls: ~"@{css-prefix}alert"; @alert-prefix-cls: ~"@{css-prefix}alert";
@icon--prefix-cls: ~"@{css-prefix}icon"; @icon-prefix-cls: ~"@{css-prefix}icon";
.@{alert-prefix-cls}{ .@{alert-prefix-cls}{
position: relative; position: relative;
@ -68,7 +68,7 @@
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
.@{icon--prefix-cls}-ios-close-empty { .@{icon-prefix-cls}-ios-close-empty {
font-size: 22px; font-size: 22px;
color: @legend-color; color: @legend-color;
transition: color @transition-time ease; transition: color @transition-time ease;

View file

@ -7,3 +7,4 @@
@import "alert"; @import "alert";
@import "collapse"; @import "collapse";
@import "card"; @import "card";
@import "message";

View file

@ -0,0 +1,53 @@
@message-prefix-cls: ~"@{css-prefix}message";
@icon-prefix-cls: ~"@{css-prefix}icon";
.@{message-prefix-cls} {
font-size: 12px;
position: fixed;
z-index: @zindex-message;
width: 100%;
top: 16px;
left: 0;
&-notice {
width: auto;
vertical-align: middle;
position: absolute;
left: 50%;
}
&-notice-content {
position: relative;
right: 50%;
padding: 8px 16px;
border-radius: @border-radius-base;
border: 1px solid @border-color-base;
box-shadow: @shadow-base;
background: #fff;
display: block;
}
&-success .@{icon-prefix-cls} {
color: @success-color;
}
&-error .@{icon-prefix-cls} {
color: @error-color;
}
&-warning .@{icon-prefix-cls} {
color: @warning-color;
}
&-info .@{icon-prefix-cls},
&-loading .@{icon-prefix-cls} {
color: @primary-color;
}
.@{icon-prefix-cls} {
margin-right: 8px;
font-size: 14px;
top: 1px;
position: relative;
}
}

View file

@ -1,6 +1,6 @@
/*! /*!
* iView * iView
* Web: http://www.iviewui.com * Web: https://www.iviewui.com
* Github: https://github.com/iviewui/iview * Github: https://github.com/iview/iview
* Author: Aresn * Author: Aresn
*/ */

View file

@ -1,4 +1,5 @@
@import "./themes/default/index"; @import "./themes/default/index";
@import "./mixins/index"; @import "./mixins/index";
@import "./common/index"; @import "./common/index";
@import "./animation/index";
@import "./components/index"; @import "./components/index";

View file

@ -3,3 +3,27 @@
-moz-animation: @string; -moz-animation: @string;
animation: @string; animation: @string;
} }
.animation-duration(@string) {
-webkit-animation-duration : @string;
-moz-animation-duration : @string;
animation-duration : @string;
}
.animation-fill-mode(@string) {
-webkit-animation-fill-mode : @string;
-moz-animation-fill-mode : @string;
animation-fill-mode : @string;
}
.animation-play-state(@string) {
-webkit-animation-play-state : @string;
-moz-animation-play-state : @string;
animation-play-state : @string;
}
.animation-name(@string) {
-webkit-animation-name : @string;
-moz-animation-name : @string;
animation-name : @string;
}

View file

@ -6,3 +6,4 @@
@import "button"; @import "button";
@import "layout"; @import "layout";
@import "size"; @import "size";
@import "loading";

View file

@ -0,0 +1,10 @@
// Loading for loop
.ivu-load-loop{
.animation(ani-load-loop 1s linear infinite);
}
@keyframes ani-load-loop {
from { transform: rotate(0deg);}
50% { transform: rotate(180deg);}
to { transform: rotate(360deg);}
}

View file

@ -49,7 +49,9 @@
@zindex-affix : 10; @zindex-affix : 10;
@zindex-back-top : 10; @zindex-back-top : 10;
@zindex-spin : 8; @zindex-spin : 8;
@zindex-message : 1010;
// Animation // Animation
@animation-time : .3s;
@transition-time : .2s; @transition-time : .2s;
@ease-in-out : ease-in-out; @ease-in-out : ease-in-out;

View file

@ -1,5 +1,5 @@
// 判断参数是否是其中之一 // 判断参数是否是其中之一
export function oneOf(value, validList) { export function oneOf (value, validList) {
for (let i = 0; i < validList.length; i++) { for (let i = 0; i < validList.length; i++) {
if (value === validList[i]) { if (value === validList[i]) {
return true; return true;
@ -7,3 +7,7 @@ export function oneOf(value, validList) {
} }
return false; return false;
} }
export function camelcaseToHyphen (str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}