diff --git a/.eslintrc.json b/.eslintrc.json index ec7f1bca4..f3aee3186 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -14,6 +14,7 @@ "react/no-string-refs": 0, "react/jsx-closing-bracket-location": 0, "react/no-did-mount-set-state": 0, + "react/jsx-first-prop-new-line": 0, "jsx-a11y/img-has-alt": 0, "jsx-a11y/label-has-for": 0, "import/no-extraneous-dependencies": 0, diff --git a/README.md b/README.md index 1190c9ab0..c6f7e17d4 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ npm i && npm start ... ``` - + ### PropTypes 继承了React所有的PropTypes, 并提供了一些通用的自定义类型, 所有的自定义组件不再使用React.PropTypes. @@ -44,19 +44,7 @@ npm i && npm start ### View * 属性 - * if [any], Vue中的`v-if`的实现, 参考[2]. - - ```js - // Vue - - - // React - - - - ``` - - * show [any], Vue的`v-show`的实现, 参考[3]. + * show [any], Vue的`v-show`的实现, 参考[2]. ```js // Vue @@ -68,7 +56,7 @@ npm i && npm start ``` - * transition [String], Vue的`transition`的实现, 参考[4]. + * transition [String], Vue的`transition`的实现, 参考[3]. ```js // Vue @@ -112,6 +100,5 @@ npm test ## References 1. https://github.com/JedWatson/classnames -2. https://vuejs.org/guide/conditional.html#v-if -3. https://vuejs.org/guide/conditional.html#v-show -4. https://vuejs.org/guide/transitions.html#Transitioning-Single-Elements-Components +2. https://vuejs.org/guide/conditional.html#v-show +3. https://vuejs.org/guide/transitions.html#Transitioning-Single-Elements-Components diff --git a/libs/markdown/index.js b/libs/markdown/index.js index cc48a93c5..f4d678375 100644 --- a/libs/markdown/index.js +++ b/libs/markdown/index.js @@ -44,7 +44,17 @@ export default class Markdown extends Component { const renderer = new marked.Renderer(); renderer.code = (text) => { - return text + if (/demo-block/.test(text)) { + return text; + } else { + return ` +
+
+              ${highlight.highlightAuto(text).value}
+            
+
+ ` + } } marked.setOptions({ @@ -53,13 +63,14 @@ export default class Markdown extends Component { } }); - const html = marked(this.props.children.replace(/:::\s?demo ([^]+?):::/g, (match, p1, offset) => { - return p1.replace(/(.+)\n([^]+)/, (match, p1, p2) => { + const html = marked(this.props.children.replace(/:::\s?demo\s?([^]+?):::/g, (match, p1, offset) => { + return p1.replace(/([^]*)\n?(```[^]+```)/, (match, p1, p2) => { const id = offset.toString(36); const matched = p2.match(/```(.*)\n([^]+)```/); const lang = matched[1], code = matched[2].replace(/this/g, 'context'); - let transformTarget = null + let transformTarget = null; + if (lang === 'javascript'){ transformTarget = code }else{ @@ -76,7 +87,7 @@ export default class Markdown extends Component {
-
${marked(p1)}
+
${p1 && marked(p1)}
${marked(p2)}
diff --git a/libs/view/index.js b/libs/view/index.js index 5318d20f5..2d7c27a92 100644 --- a/libs/view/index.js +++ b/libs/view/index.js @@ -4,18 +4,13 @@ import Component from '../component'; export default class View extends Component { render() { - let children = null; - - if (!this.props.hasOwnProperty('if') || Boolean(this.props.if)) { - const element = React.Children.only(this.props.children); - - children = React.cloneElement(element, this.props.hasOwnProperty('show') && !this.props.show && { - key: element, - style: Object.assign({}, element.props.style, { - display: 'none' - }) - }); - } + const element = React.Children.only(this.props.children); + const children = React.cloneElement(element, this.props.hasOwnProperty('show') && !this.props.show && { + key: element, + style: Object.assign({}, element.props.style, { + display: 'none' + }) + }); if (this.props.transition) { return ( @@ -31,7 +26,6 @@ export default class View extends Component { /* eslint-disable */ View.propTypes = { - if: PropTypes.any, show: PropTypes.any, transition: PropTypes.string, transitionKey: PropTypes.string diff --git a/site/docs/zh-CN/badge.md b/site/docs/zh-CN/badge.md new file mode 100644 index 000000000..d9334d674 --- /dev/null +++ b/site/docs/zh-CN/badge.md @@ -0,0 +1,71 @@ +## Badge 标记 + +出现在按钮、图标旁的数字或状态标记。 + +### 基础用法 +展示新消息数量。 + +:::demo 定义`value`属性,它接受`Number`或者`String`。 + +```html + + + + + + + +``` +::: + +### 最大值 +可自定义最大值。 + +:::demo 由`max`属性定义,它接受一个`Number`,需要注意的是,只有当`value`为`Number`时,它才会生效。 + +```html + + + + + + +``` +::: + +### 自定义内容 +可以显示数字以外的文本内容。 + +:::demo 定义`value`为`String`类型是时可以用于显示自定义文本。 + +```html + + + + + + +``` +::: + +### 小红点 +以红点的形式标注需要关注的内容。 + +:::demo 除了数字外,设置`isDot`属性,它接受一个`Boolean`。 + +```html + + 数据查询 + + + + +``` +::: + +### Attributes +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| value | 显示值 | string, number | — | — | +| max | 最大值,超过最大值会显示 '{max}+',要求 value 是 Number 类型 | number | — | — | +| isDot | 小圆点 | boolean | — | false | diff --git a/site/docs/zh-CN/card.md b/site/docs/zh-CN/card.md index e6ac71bac..693b02efb 100644 --- a/site/docs/zh-CN/card.md +++ b/site/docs/zh-CN/card.md @@ -60,7 +60,6 @@ - diff --git a/site/docs/zh-CN/notification.md b/site/docs/zh-CN/notification.md new file mode 100644 index 000000000..1db86ea91 --- /dev/null +++ b/site/docs/zh-CN/notification.md @@ -0,0 +1,50 @@ +## Notification 通知 + +悬浮出现在页面右上角,显示全局的通知提醒消息。 + +### 基本用法 + +适用性广泛的通知栏 + +::: demo Notification 组件提供通知功能,Element 注册了`$notify`方法,接收一个`options`字面量参数,在最简单的情况下,你可以设置`title`字段和`message`字段,用于设置通知的标题和正文。默认情况下,经过一段时间后 Notification 组件会自动关闭,但是通过设置`duration`,可以控制关闭的时间间隔,特别的是,如果设置为`0`,则不会自动关闭。注意:`duration`接收一个`Number`,单位为毫秒,默认为`4500`。 +```html + + +``` +::: + +### 带有倾向性 + +带有 icon,常用来显示「成功、警告、消息、错误」类的系统消息 + +::: demo Element 为 Notification 组件准备了四种通知类型:`success`, `warning`, `info`, `error`。通过`type`字段来设置,除此以外的值将被忽略。同时,我们也为 Notification 的各种 type 注册了方法,可以在不传入`type`字段的情况下像`open5`和`open6`那样直接调用。 +```html + + + + +``` +::: + +### 全局方法 + +Element 为 `Vue.prototype` 添加了全局方法 `$notify`。因此在 vue instance 中可以采用本页面中的方式调用 Notification。 + +### 单独引用 + +单独引入 Notification: + +```javascript +import { Notification } from 'element-ui'; +``` + +此时调用方法为 `Notification(options)`。我们也为每个 type 定义了各自的方法,如 `Notification.success(options)`。 + +### Options +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| title | 标题 | string | — | — | +| message | 说明文字 | string | — | — | +| type | 主题样式,如果不在可选值内将被忽略 | string | success/warning/info/error | — | +| duration | 显示时间, 毫秒。设为 0 则不会自动关闭 | number | — | 4500 | +| onClose | 关闭时的回调函数 | function | — | — | diff --git a/site/docs/zh-CN/progress.md b/site/docs/zh-CN/progress.md new file mode 100644 index 000000000..7191b70b3 --- /dev/null +++ b/site/docs/zh-CN/progress.md @@ -0,0 +1,52 @@ +## Progress 进度条 + +用于展示操作进度,告知用户当前状态和预期。 + +### 线形进度条 — 百分比外显 + +:::demo Progress 组件设置`percentage`属性即可,表示进度条对应的百分比,**必填**,必须在 0-100。 + +```html + + + + +``` +::: + +### 线形进度条 — 百分比内显 + +百分比不占用额外控件,适用于文件上传等场景。 + +:::demo Progress 组件可通过 `stroke-width` 属性更改进度条的高度,并可通过 `desc-inside` 属性来将进度条描述置于进度条内部。 + +```html + + + + +``` +::: + +### 环形进度条 + +:::demo Progress 组件可通过 `type` 属性来指定使用环形进度条,在环形进度条中,还可以通过 `width` 属性来设置其大小。 + +```html + + + + +``` +::: + +### Attributes +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| **percentage** | **百分比(必填)** | number | 0-100 | 0 | +| type | 进度条类型 | string | line/circle | line | +| stroke-width | 进度条的宽度,单位 px | number | — | 6 | +| text-inside | 进度条显示文字内置在进度条内(只在 type=line 时可用) | Boolean | — | false | +| status | 进度条当前状态 | string | success/exception | — | +| width | 环形进度条画布宽度(只在 type=circle 时可用) | number | | 126 | +| show-text | 是否显示进度条文字内容 | boolean | — | true | diff --git a/site/pages/badge/index.js b/site/pages/badge/index.js index 6da608325..ac196ffe5 100644 --- a/site/pages/badge/index.js +++ b/site/pages/badge/index.js @@ -1,76 +1,11 @@ -import React from 'react'; -import { Button, Badge } from '../../../src'; - import './style.scss'; +import React from 'react'; +import { Component, Markdown } from '../../../libs'; +import template from '../../docs/zh-CN/badge.md'; + export default class Playground extends React.Component { render() { - return ( -
-
-
-

Badge 标记

-

出现在按钮、图标旁的数字或状态标记。

-
-
-
-
-

基础用法

-

展示新消息数量。

-
-
- - - - - - - -
-
-
-
-

最大值

-

可自定义最大值。

-
-
- - - - - - -
-
-
-
-

自定义内容

-

可以显示数字以外的文本内容。

-
-
- - - - - - -
-
-
-
-

小红点

-

以红点的形式标注需要关注的内容。

-
-
- - 数据查询 - - - - -
-
-
- ) + return {template} } } diff --git a/site/pages/badge/style.scss b/site/pages/badge/style.scss index 341288aba..42ed4596f 100644 --- a/site/pages/badge/style.scss +++ b/site/pages/badge/style.scss @@ -1,3 +1,5 @@ .demo-badge { - margin-right: 40px; + .el-badge { + margin-right: 40px; + } } diff --git a/site/pages/index.js b/site/pages/index.js index f4f271bfa..5851612e6 100644 --- a/site/pages/index.js +++ b/site/pages/index.js @@ -12,6 +12,7 @@ import Radio from './radio'; import Card from './card'; import Message from './message'; import MessageBox from './message-box'; +import Notification from './notification'; import Loading from './loading'; import Dialog from './dialog'; import Progress from './progress'; @@ -20,38 +21,63 @@ import Tree from './tree' // pages是有序的Object, 会影响到左侧的菜单顺序. const pages = { - layout: { title: 'Layout 布局', component: Layout }, - button: { title: 'Button 按钮', component: Button }, - radio: { title: 'Radio 单选框', component: Radio }, - progress: { title: 'Progress 进度条', component: Progress }, - badge: { title: 'Badge 标记', component: Badge }, - alert: { title: 'Alert 警告', component: Alert }, - loading: { title: 'Loading 加载', component: Loading }, - message: { title: 'Message 消息提示', component: Message }, - messageBox: { title: 'Message Box 弹框', component: MessageBox }, - dialog: { title: 'Dialog 对话框', component: Dialog }, - card: { title: 'Card 卡片', component: Card }, - tree: { title: 'Tree 树形控件', component: Tree }, + 'Basic': { + layout: { title: 'Layout 布局', component: Layout }, + button: { title: 'Button 按钮', component: Button }, + }, + 'Form': { + radio: { title: 'Radio 单选框', component: Radio }, + }, + 'Data': { + progress: { title: 'Progress 进度条', component: Progress }, + tree: { title: 'Tree 树形控件', component: Tree }, + badge: { title: 'Badge 标记', component: Badge }, + }, + 'Notice': { + alert: { title: 'Alert 警告', component: Alert }, + loading: { title: 'Loading 加载', component: Loading }, + message: { title: 'Message 消息提示', component: Message }, + messageBox: { title: 'Message Box 弹框', component: MessageBox }, + notification: { title: 'Notification 通知', component: Notification }, + }, + 'Nav': { + + }, + 'Others': { + dialog: { title: 'Dialog 对话框', component: Dialog }, + card: { title: 'Card 卡片', component: Card } + } }; -const HASH_OFFSET = 1 class App extends React.Component { constructor(props) { super(props); this.state = { - page: location.hash.substr(HASH_OFFSET) || 'layout' // Do not change this line + page: this.getPage() || 'layout' // Do not change this line }; } componentDidMount() { window.addEventListener("hashchange", e => { this.setState({ - page: location.hash.substr(HASH_OFFSET) + page: this.getPage() }) }, false); } + getPage() { + return location.hash.substr(1); + } + + getComponent(page) { + this.components = this.components || Object.assign.apply(this, [{}].concat(Object.keys(pages).map(group => { + return pages[group] + }))); + + return this.components[page].component; + } + render() { return (
@@ -59,32 +85,42 @@ class App extends React.Component {

Element-React

-
) } - - onSelect(page) { - this.setState({ page }) - } } ReactDOM.render(, document.getElementById('app')); diff --git a/site/pages/message-box/index.js b/site/pages/message-box/index.js index 0ccbb9f36..bc2be70e9 100644 --- a/site/pages/message-box/index.js +++ b/site/pages/message-box/index.js @@ -4,7 +4,7 @@ import React from 'react'; import { Component, Markdown } from '../../../libs'; import template from '../../docs/zh-CN/message-box.md'; -import { MessageBox } from '../../../src'; +import { MessageBox, Message } from '../../../src'; export default class Playground extends React.Component { render() { diff --git a/site/pages/notification/index.js b/site/pages/notification/index.js new file mode 100644 index 000000000..c5d8aed61 --- /dev/null +++ b/site/pages/notification/index.js @@ -0,0 +1,59 @@ +import './style.scss'; + +import React from 'react'; +import { Component, Markdown } from '../../../libs'; +import template from '../../docs/zh-CN/notification.md'; + +import { Notification } from '../../../src'; + +export default class Playground extends React.Component { + render() { + return {template} + } + + onOpen(type) { + switch (type) { + case '1': + Notification({ + title: '标题名称', + message: '这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案这是提示文案' + }); + break; + case '2': + Notification({ + title: '提示', + message: '这是一条不会自动关闭的消息', + duration: 0 + }); + break; + case '3': + Notification({ + title: '成功', + message: '这是一条成功的提示消息', + type: 'success' + }); + break; + case '4': + Notification({ + title: '警告', + message: '这是一条警告的提示消息', + type: 'warning' + }); + break; + case '5': + Notification.info({ + title: '消息', + message: '这是一条消息的提示消息' + }); + break; + case '6': + Notification.error({ + title: '错误', + message: '这是一条错误的提示消息' + }); + break; + default: + break; + } + } +} diff --git a/site/pages/notification/style.scss b/site/pages/notification/style.scss new file mode 100644 index 000000000..36d3ebf9e --- /dev/null +++ b/site/pages/notification/style.scss @@ -0,0 +1,5 @@ +.demo-box.demo-notification { + .el-button + .el-button { + margin-left: 10px; + } +} diff --git a/site/pages/progress/index.js b/site/pages/progress/index.js index ed1bf8a0c..55d0fbad7 100644 --- a/site/pages/progress/index.js +++ b/site/pages/progress/index.js @@ -1,49 +1,11 @@ import './style.scss'; import React from 'react'; -import { Progress } from '../../../src'; +import { Component, Markdown } from '../../../libs'; +import template from '../../docs/zh-CN/progress.md'; export default class Playground extends React.Component { render() { - return ( -
-
-
-

Progress 进度条

-

用于展示操作进度,告知用户当前状态和预期。

-

线形进度条 — 百分比外显

-
-
- - - - -
-
-
-
-

线形进度条 — 百分比外显

-

百分比不占用额外控件,适用于文件上传等场景。

-
-
- - - - -
-
-
-
-

环形进度条

-
-
- - - - -
-
-
- ) + return {template} } } diff --git a/site/pages/progress/style.scss b/site/pages/progress/style.scss index dcfdacb02..b310c71e9 100644 --- a/site/pages/progress/style.scss +++ b/site/pages/progress/style.scss @@ -1,7 +1,9 @@ -.demo-progress .el-progress--line { - margin-bottom: 15px; - width: 350px; -} -.demo-progress .el-progress--circle { - margin-right: 15px; +.demo-box.demo-progress { + .el-progress--line { + margin-bottom: 15px; + width: 350px; + } + .el-progress--circle { + margin-right: 15px; + } } diff --git a/site/pages/style/base.scss b/site/pages/style/base.scss index d78b64c3c..3b694d6f1 100644 --- a/site/pages/style/base.scss +++ b/site/pages/style/base.scss @@ -30,94 +30,107 @@ body { .main { display: flex; + } +} - .menu { - padding: 25px; - border-right: 1px solid #f0f0f0; - - ul { - list-style: none; - } +.content { + flex: 1; + padding: 25px; + overflow: hidden; - .menu-item { - width: 200px; - font-size: 13px; - color: #5e6d82; - line-height: 40px; - height: 40px; - cursor: pointer; - - a { - display: block; - height: 40px; - line-height: 40px; - font-size: 13px; - padding-left: 24px; - color: #5e6d82; - margin: 0; - padding: 0; - text-decoration: none; - position: relative; - -webkit-transition: all .3s; - transition: all .3s; - } - - a:hover { - color: #20a0ff; - } - } - } + h2 { + font-size: 28px; + color: #1f2d3d; + margin: 0; + } - .content { - flex: 1; - padding: 25px; - overflow: hidden; + h3 { + font-size: 22px; + margin: 45px 0 15px; + } - h2 { - font-size: 28px; - color: #1f2d3d; - margin: 0; - } + h2, h3, h4, h5 { + font-weight: normal; + color: #1f2f3d; + } - h3 { - font-size: 22px; - margin: 45px 0 15px; - } + table { + border-collapse: collapse; + width: 100%; + background-color: #fff; + color: #5e6d82; + font-size: 14px; + margin-bottom: 45px; - h2, h3, h4, h5 { - font-weight: normal; - color: #1f2f3d; - } + strong { + font-weight: normal; + } - table { - border-collapse: collapse; - width: 100%; - background-color: #fff; - color: #5e6d82; - font-size: 14px; - margin-bottom: 45px; + th { + text-align: left; + border-top: 1px solid #eaeefb; + background-color: #EFF2F7; + } - strong { - font-weight: normal; - } + td, th { + border-bottom: 1px solid #eaeefb; + padding: 10px; + } - th { - text-align: left; - border-top: 1px solid #eaeefb; - background-color: #EFF2F7; - } + th:first-child, td:first-child { + padding-left: 10px; + } + } +} - td, th { - border-bottom: 1px solid #eaeefb; - padding: 10px; - } +.side-nav { + width: 200px; + padding: 30px; - th:first-child, td:first-child { - padding-left: 10px; + li { + list-style: none; + } + ul { + padding: 0; + margin: 0; + overflow: hidden; + } + .nav-item { + a { + font-size: 16px; + color: #5e6d82; + line-height: 40px; + height: 40px; + margin: 0; + padding: 0; + text-decoration: none; + display: block; + position: relative; + transition: all .3s; + &.active { + color: #20a0ff; + } + } + .nav-item { + a { + display: block; + height: 40px; + line-height: 40px; + font-size: 13px; + padding-left: 24px; + &:hover { + color: #20a0ff; } } } } + .nav-group__title { + font-size: 12px; + color: #99a9bf; + padding-left: 8px; + line-height: 26px; + margin-top: 10px; + } } .demo-block { @@ -171,15 +184,19 @@ body { .highlight { width: 60%; + display: flex; + } + + pre { + background-color: #f9fafc; + overflow-x: auto; + padding: 18px 24px; line-height: 1.8; font-size: 12px; - background-color: #f9fafc; - display: flex; + } - pre { - overflow-x: auto; - padding: 18px 24px; - } + pre.fixed { + white-space: pre-line; } .demo-block-control { diff --git a/src/alert/Alert.jsx b/src/alert/Alert.jsx index e91f09ab1..c79b83795 100644 --- a/src/alert/Alert.jsx +++ b/src/alert/Alert.jsx @@ -30,16 +30,20 @@ export default class Alert extends Component { return (
- - - + { + this.props.showIcon && + }
- - {this.props.title} - - -

{this.props.description}

-
+ { + this.props.title && {this.props.title} + } + { + this.props.description &&

{this.props.description}

+ } {this.props.closeText} diff --git a/src/button/Button.jsx b/src/button/Button.jsx index 56df7ba7d..81af60897 100644 --- a/src/button/Button.jsx +++ b/src/button/Button.jsx @@ -11,12 +11,8 @@ export default class Button extends Component { render() { return ( ) diff --git a/src/card/Card.jsx b/src/card/Card.jsx index 40a56b525..f9464f27b 100644 --- a/src/card/Card.jsx +++ b/src/card/Card.jsx @@ -5,12 +5,13 @@ export default class Card extends Component { render() { return (
- -
- { this.props.header } -
-
- + { + this.props.header && ( +
+ { this.props.header } +
+ ) + }
{ this.props.children }
diff --git a/src/index.js b/src/index.js index 51f0ba92f..9bce1bb6a 100644 --- a/src/index.js +++ b/src/index.js @@ -5,8 +5,9 @@ export { default as Layout } from './layout'; export { default as Loading } from './loading'; export { default as Message } from './message'; export { default as MessageBox } from './message-box'; +export { default as Notification } from './notification'; export { default as Radio } from './radio'; export { default as Dialog } from './dialog'; -export { default as Progress } from './progress'; -export { default as Badge } from './badge'; -export { default as Tree } from './tree'; \ No newline at end of file +export { default as Progress } from './progress'; +export { default as Badge } from './badge'; +export { default as Tree } from './tree'; diff --git a/src/message-box/MessageBox.jsx b/src/message-box/MessageBox.jsx index d5e2cb759..8cd98c0c6 100644 --- a/src/message-box/MessageBox.jsx +++ b/src/message-box/MessageBox.jsx @@ -65,27 +65,29 @@ export default class MessageBox extends Component {
- -
-
{this.props.title}
- - - -
-
- -
-
-
-

{this.props.message}

+ { + this.props.title && ( +
+
{this.props.title}
+ { this.props.showClose && }
- -
-
{this.state.editorErrorMessage}
+ ) + } + { + this.props.message && ( +
+
+
+

{this.props.message}

- -
- + +
+
{this.state.editorErrorMessage}
+
+
+
+ ) + }
diff --git a/src/message-box/index.js b/src/message-box/index.js index f7dbfe248..24035569b 100644 --- a/src/message-box/index.js +++ b/src/message-box/index.js @@ -48,7 +48,7 @@ function next(props) { document.body.style.setProperty('overflow', 'hidden'); } - const component = React.createElement(MessageBox, Object.assign(props, { + const component = React.createElement(MessageBox, Object.assign({}, props, { promise: { resolve, reject }, onClose: () => { ReactDOM.unmountComponentAtNode(div); diff --git a/src/message/Message.jsx b/src/message/Message.jsx index 29952391c..314b7ec06 100644 --- a/src/message/Message.jsx +++ b/src/message/Message.jsx @@ -19,12 +19,12 @@ export default function Message(props = {}, type) { } const component = React.createElement(Toast, Object.assign(props, { - onClose: (...args) => { + onClose: () => { ReactDOM.unmountComponentAtNode(div); document.body.removeChild(div); if (props.onClose instanceof Function) { - props.onClose.apply(this, args); + props.onClose(); } } })); diff --git a/src/message/Toast.jsx b/src/message/Toast.jsx index 495c1a0ce..f400fc3c1 100644 --- a/src/message/Toast.jsx +++ b/src/message/Toast.jsx @@ -25,6 +25,10 @@ export default class Toast extends Component { this.startTimer(); } + componentWillUnmount() { + this.stopTimer(); + } + onClose() { this.stopTimer(); @@ -50,9 +54,7 @@ export default class Toast extends Component {

{this.props.message}

- -
-
+ { this.props.showClose &&
}
diff --git a/src/notification/Notification.jsx b/src/notification/Notification.jsx new file mode 100644 index 000000000..5af700a81 --- /dev/null +++ b/src/notification/Notification.jsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { Component, PropTypes, View } from '../../libs'; + +const typeMap = { + success: 'circle-check', + info: 'information', + warning: 'warning', + error: 'circle-cross' +}; + +export default class Notification extends Component { + constructor(props) { + super(props); + + this.state = { + visible: false + }; + } + + componentDidMount() { + this.setState({ + visible: true + }) + + this.startTimer(); + } + + componentWillUnmount() { + this.stopTimer(); + } + + onClose() { + this.stopTimer(); + + this.setState({ + visible: false + }); + + if (this.props.onClose) { + this.props.onClose(); + } + } + + startTimer() { + if (this.props.duration) { + this.timeout = setTimeout(() => { + this.onClose(); + }, this.props.duration) + } + } + + stopTimer() { + clearTimeout(this.timeout); + } + + typeClass() { + return this.props.type && typeMap[this.props.type] ? `el-icon-${ typeMap[this.props.type] }` : ''; + } + + render() { + return ( + +
+ { + this.props.type && + } +
+ {this.props.title} +

{this.props.message}

+
+
+
+
+ ) + } +} + +Notification.propTypes = { + type: PropTypes.oneOf(['success', 'warning', 'info', 'error']), + title: PropTypes.string, + message: PropTypes.string, + duration: PropTypes.number, + top: PropTypes.number, +/* eslint-disable */ + onClose: PropTypes.func +/* eslint-enable */ +} + +Notification.defaultProps = { + duration: 4500, + top: 16 +} diff --git a/src/notification/NotificationCenter.jsx b/src/notification/NotificationCenter.jsx new file mode 100644 index 000000000..5b96b3f8b --- /dev/null +++ b/src/notification/NotificationCenter.jsx @@ -0,0 +1,67 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import Notification from './Notification'; + +export default function NotificationCenter(props = {}, type) { + const div = document.createElement('div'), className = 'el-notification'; + + document.body.appendChild(div); + + if (typeof props === 'string') { + props = { + message: props + }; + } + + if (type) { + props.type = type; + } + + const instances = document.getElementsByClassName(className); + + props.top = 0; + + for (let i = 0, len = instances.length; i < len; i++) { + props.top += instances[i].offsetHeight + 16; + } + + props.top += 16; + + const component = React.createElement(Notification, Object.assign({}, props, { + onClose: () => { + ReactDOM.unmountComponentAtNode(div); + document.body.removeChild(div); + + setTimeout(() => { + const instances = document.querySelectorAll('.el-notification'); + + for (let i = 0, len = instances.length; i < len; i++) { + const element = instances[i]; + + if (element.offsetTop > props.offsetHeight) { + element.style.top = `${element.offsetTop - props.offsetHeight - 16}px`; + } + } + }) + + if (props.onClose instanceof Function) { + props.onClose(); + } + } + })); + + ReactDOM.render(component, div, () => { + setTimeout(() => { + props.offsetHeight = div.getElementsByClassName(className)[0].offsetHeight; + }); + }); +} + +/* eslint-disable */ +['success', 'warning', 'info', 'error'].forEach(type => { + NotificationCenter[type] = (options = {}) => { + return NotificationCenter(options, type); + }; +}); +/* eslint-enable */ diff --git a/src/notification/index.js b/src/notification/index.js new file mode 100644 index 000000000..07cb1f033 --- /dev/null +++ b/src/notification/index.js @@ -0,0 +1,3 @@ +import NotificationCenter from './NotificationCenter'; + +export default NotificationCenter; diff --git a/src/progress/Progress.jsx b/src/progress/Progress.jsx index 3a99c4c64..0c04b23f6 100644 --- a/src/progress/Progress.jsx +++ b/src/progress/Progress.jsx @@ -60,9 +60,9 @@ export default class Progress extends Component {
- -
{`${percentage}%`}
-
+ { + showText && textInside &&
{`${percentage}%`}
+ }
@@ -77,19 +77,18 @@ export default class Progress extends Component {
) } - const progressInfo = ( - -
- { - status ? - : `${percentage}%` - } -
-
+ const progressInfo = showText && !textInside && ( +
+ { + status ? + : `${percentage}%` + } +
); + return (