[refactor] Toast: 升级为自定义组件 (#191)

* toast 组件初始化

* toast 增加示例 && 逻辑补全

* toast 支持直接 loading 调用

* 增加 toast readme

* 去除多余的引用
This commit is contained in:
Yao 2018-04-12 21:58:54 +08:00 committed by GitHub
parent 7ceeca9d4c
commit 1d7c23b289
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 223 additions and 176 deletions

View File

@ -1,40 +1,50 @@
const Zan = require('../../dist/index');
const Toast = require('../../dist/toast/toast');
Page(Object.assign({}, Zan.Toast, {
Page({
data: {},
showToast() {
this.showZanToast('toast的内容');
Toast({
message: 'toast的内容',
selector: '#zan-toast-test'
})
},
showIconToast() {
this.showZanToast({
title: 'toast的内容',
icon: 'fail'
Toast({
type: 'fail',
message: 'toast的内容',
selector: '#zan-toast-test'
});
},
showImageToast() {
this.showZanToast({
title: 'toast的内容',
Toast({
message: 'toast的内容',
selector: '#zan-toast-test',
image: 'https://b.yzcdn.cn/v2/image/dashboard/secured_transaction/suc_green@2x.png'
});
},
showLoadingToast() {
this.showZanToast({
title: 'toast的内容',
icon: 'loading'
Toast({
type: 'loading',
message: 'toast的内容',
selector: '#zan-toast-test'
});
},
showOnlyIcon() {
this.showZanToast({
icon: 'fail'
Toast({
type: 'fail',
selector: '#zan-toast-test'
});
},
showLoading() {
this.showZanLoading('加载中');
Toast.loading({
message: '加载中',
selector: '#zan-toast-test'
});
}
}));
});

View File

@ -1,3 +1,7 @@
{
"navigationBarTitleText": "Toast 轻提示"
"navigationBarTitleText": "Toast 轻提示",
"usingComponents": {
"zan-button": "../../dist/btn/index",
"zan-toast": "../../dist/toast/index"
}
}

View File

@ -5,30 +5,31 @@
<view class="doc-title zan-hairline--bottom">TOAST</view>
<view class="zan-btns" style="margin-top: 15vh;">
<button class="zan-btn" bindtap="showToast">
<zan-button bind:btnclick="showToast">
显示toast
</button>
</zan-button>
<button class="zan-btn" bindtap="showIconToast">
<zan-button bind:btnclick="showIconToast">
显示 Icon 图标的toast
</button>
</zan-button>
<button class="zan-btn" bindtap="showImageToast">
<zan-button bind:btnclick="showImageToast">
自定义图片作为图标的toast
</button>
</zan-button>
<button class="zan-btn" bindtap="showLoadingToast">
<zan-button bind:btnclick="showLoadingToast">
显示 Loading toast
</button>
</zan-button>
<button class="zan-btn" bindtap="showOnlyIcon">
<zan-button bind:btnclick="showOnlyIcon">
只显示图标的toast
</button>
</zan-button>
<button class="zan-btn" bindtap="showLoading">
<zan-button bind:btnclick="showLoading">
显示 Loading
</button>
</zan-button>
</view>
</view>
<template is="zan-toast" data="{{ zanToast }}"></template>
<!-- <template is="zan-toast" data="{{ zanToast }}"></template> -->
<zan-toast id="zan-toast-test"></zan-toast>

View File

@ -4,10 +4,16 @@ Component({
type: Boolean,
value: false
},
// 是否有遮罩层
overlay: {
type: Boolean,
value: true
},
// 遮罩层是否会显示
showOverlay: {
type: Boolean,
value: true
},
// 内容从哪个方向出,可选 center top bottom left right
type: {
type: String,

View File

@ -2,7 +2,8 @@
class="pop pop--{{ type }} {{ show ? 'pop--show' : '' }}"
>
<view
class="pop__mask {{ overlay ? '' : 'pop__mask--hide' }}"
wx:if="{{ overlay }}"
class="pop__mask {{ showOverlay ? '' : 'pop__mask--hide' }}"
bindtap="handleMaskClick"
></view>
<view class="pop__container">

View File

@ -1,7 +1,6 @@
<pop-manager
show="{{ show }}"
type="center"
overlay="{{ true }}"
>
<view class="zan-dialog--container">
<view

View File

@ -1,5 +1,4 @@
Component({
externalClasses: ['class'],
properties: {
type: {
type: String,

View File

@ -1,16 +1,4 @@
exports.Actionsheet = require('./actionsheet/index');
exports.Dialog = require('./dialog/index');
exports.Field = require('./field/index');
exports.NoticeBar = require('./noticebar/index');
exports.Select = require('./select/index');
exports.Stepper = require('./stepper/index');
exports.Switch = require('./switch/index');
exports.Tab = require('./tab/index');
exports.Toast = require('./toast/index');
exports.TopTips = require('./toptips/index');
exports.Dialog = require('./dialog/dialog');
exports.Toast = require('./toast/toast');
// exports.TopTips = require('./toptips/index');
// 兼容老版本,在下次大版本发布时会被移除
exports.CheckLabel = require('./select/index');
const { extend } = require('./common/helper');
exports.extend = extend;

View File

@ -1,7 +1,7 @@
<pop-manager
show="{{ show }}"
type="{{ type }}"
overlay="{{ overlay }}"
show-overlay="{{ overlay }}"
bindclickmask="handleMaskClick"
>
<slot></slot>

View File

@ -16,7 +16,7 @@
.zan-tab__item {
flex: 1;
display: inline-block;
padding: 0 10px;
padding: 0 5px;
line-height: 0;
box-sizing: border-box;
overflow: hidden;

View File

@ -1,64 +1,53 @@
## Toast 轻提示
### 使用指南
在 app.wxss 中引入组件库所有样式
```css
@import "path/to/zanui-weapp/dist/index.wxss";
在 json 文件中配置 toast 组件
```json
"usingComponents": {
"zan-toast": "/path/to/zanui-weapp/dist/toast/index"
}
```
在需要使用的页面里引入组件库模板和脚本
```html
<import src="path/to/zanui-weapp/dist/toast/index.wxml" />
<!-- 直接使用 zan-toast 模板,并且直接传入 zanToast -->
<template is="zan-toast" data="{{ zanToast }}"></template>
<zan-toast id="zan-toast-test"></zan-toast>
```
```js
const { Toast, extend } = require('path/to/zanui-weapp/dist/index');
const Toast = require('path/to/zanui-weapp/dist/toast/toast');
// 在 Page 中混入 Toast 里面声明的方法
Page(extend({}, Toast, {
Page({
// ...
}));
```
### 代码演示
在 js 中直接调用 this.showZanToast 即可
```js
this.showZanToast('toast的内容');
this.showZanToast({
title: 'toast的内容'
// 可以在任意方法里直接调用,即可唤起
handleClick() {
Toast({
message: 'toast me',
selector: '#zan-toast-test'
});
}
});
```
Toast 支持在文字上展示图标,用法如下
#### 加载提示
```js
this.showZanToast({
title: 'toast的内容',
// icon 仅支持 Icon 组件内提供的
icon: 'fail'
Toast.loading({
selector: '#zan-toast-test'
});
```
Toast 组件扩展了一个 showZanLoading 的方法,快速展示加载中
```js
this.showZanLoading('toast的内容');
```
### 参数说明
#### 方法
| 方法名 | 参数 | 返回值 | 介绍 |
|-----------|-----------|-----------|-------------|
| showZanToast | `title \| options`, `timeout` | - | 展示提示 |
| showZanLoading | `title \| options` | - | 展示加载提示 |
| clearZanToast | | - | 关闭提示 |
| Toast | `options`, `timeout` | - | 展示提示 |
| Toast.loading | `options` | - | 展示加载提示 |
| Toast.clear | - | - | 关闭提示 |
#### options 具体参数如下
| 参数 | 说明 | 类型 | 默认值 | 必须 |
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| title | toast 显示文案 | String | - | |
| icon | toast 显示图标,仅支持 Icon 组件内提供的和 `loading` | String | - | |
| message | toast 显示文案 | String | - | |
| type | 提示类型 | String | - | loading success fail |
| icon | toast 显示图标,可以用 icon 里面支持的所有图标 | String | - | - |
| image | toast 显示图标,为图片的链接,传入此值后会覆盖 icon 值 | String | - | |
| timeout | toast 显示时间小于0则会一直显示需要手动调用 clearZanToast 清除 | Number | - | |

View File

@ -1,66 +1,40 @@
module.exports = {
showZanToast(title, timeout) {
const options = formatParameter(title, timeout);
// 清除上一轮的计时器
const { timer = 0 } = this.data.zanToast || {};
clearTimeout(timer);
// 弹层设置~
const zanToast = {
show: true,
icon: options.icon,
image: options.image,
title: options.title
};
this.setData({
zanToast
});
// 传入的显示时长小于0就认为需要一直显示
if (timeout < 0) {
return;
}
// 下一轮计时器
const nextTimer = setTimeout(() => {
this.clearZanToast();
}, timeout || 3000);
this.setData({
'zanToast.timer': nextTimer
});
},
// 清除所有 toast
clearZanToast() {
const { timer = 0 } = this.data.zanToast || {};
clearTimeout(timer);
this.setData({
'zanToast.show': false
});
},
// 快捷方法,显示 loading
showZanLoading(title) {
const options = formatParameter(title);
this.showZanToast({
...options,
icon: 'loading'
});
}
const DEFAULT_DATA = {
show: false,
message: '',
icon: '',
image: '',
mask: false
};
function formatParameter(title, timeout = 0) {
// 如果传入的 title 是对象,那么认为所有的配置属性都在这个对象中了
if (typeof title === 'object') {
return title;
}
const SUPPORT_TYPE = ['loading', 'success', 'fail'];
return {
title,
timeout
};
}
Component({
data: {
...DEFAULT_DATA
},
methods: {
show(options) {
const toastOptions = { ...options };
let icon = options.icon || '';
let image = options.image || '';
if (SUPPORT_TYPE.indexOf(options.type) > -1) {
icon = options.type;
image = '';
}
this.setData({
...toastOptions,
icon,
image
});
},
clear() {
this.setData({
...DEFAULT_DATA
});
}
}
});

View File

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"zan-icon": "../icon/index"
}
}

View File

@ -21,6 +21,7 @@
}
.zan-toast__icon {
display: block;
width: 40px;
height: 40px;
line-height: 40px;
@ -35,6 +36,16 @@
line-height: 0;
}
.zan-loading {
width:20px;
height:20px;
display: inline-block;
vertical-align: middle;
animation: weuiLoading 1s steps(12, end) infinite;
background: transparent url(data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iciIgd2lkdGg9JzEyMHB4JyBoZWlnaHQ9JzEyMHB4JyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj4KICAgIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIiBmaWxsPSJub25lIiBjbGFzcz0iYmsiPjwvcmVjdD4KICAgIDxyZWN0IHg9JzQ2LjUnIHk9JzQwJyB3aWR0aD0nNycgaGVpZ2h0PScyMCcgcng9JzUnIHJ5PSc1JyBmaWxsPScjRTlFOUU5JwogICAgICAgICAgdHJhbnNmb3JtPSdyb3RhdGUoMCA1MCA1MCkgdHJhbnNsYXRlKDAgLTMwKSc+CiAgICA8L3JlY3Q+CiAgICA8cmVjdCB4PSc0Ni41JyB5PSc0MCcgd2lkdGg9JzcnIGhlaWdodD0nMjAnIHJ4PSc1JyByeT0nNScgZmlsbD0nIzk4OTY5NycKICAgICAgICAgIHRyYW5zZm9ybT0ncm90YXRlKDMwIDUwIDUwKSB0cmFuc2xhdGUoMCAtMzApJz4KICAgICAgICAgICAgICAgICByZXBlYXRDb3VudD0naW5kZWZpbml0ZScvPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyM5Qjk5OUEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSg2MCA1MCA1MCkgdHJhbnNsYXRlKDAgLTMwKSc+CiAgICAgICAgICAgICAgICAgcmVwZWF0Q291bnQ9J2luZGVmaW5pdGUnLz4KICAgIDwvcmVjdD4KICAgIDxyZWN0IHg9JzQ2LjUnIHk9JzQwJyB3aWR0aD0nNycgaGVpZ2h0PScyMCcgcng9JzUnIHJ5PSc1JyBmaWxsPScjQTNBMUEyJwogICAgICAgICAgdHJhbnNmb3JtPSdyb3RhdGUoOTAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNBQkE5QUEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgxMjAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNCMkIyQjInCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgxNTAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNCQUI4QjknCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgxODAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNDMkMwQzEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgyMTAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNDQkNCQ0InCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgyNDAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNEMkQyRDInCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgyNzAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNEQURBREEnCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgzMDAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0PgogICAgPHJlY3QgeD0nNDYuNScgeT0nNDAnIHdpZHRoPSc3JyBoZWlnaHQ9JzIwJyByeD0nNScgcnk9JzUnIGZpbGw9JyNFMkUyRTInCiAgICAgICAgICB0cmFuc2Zvcm09J3JvdGF0ZSgzMzAgNTAgNTApIHRyYW5zbGF0ZSgwIC0zMCknPgogICAgPC9yZWN0Pgo8L3N2Zz4=) no-repeat;
background-size: 100%;
}
.zan-toast__icon-loading .zan-loading {
width: 40px;
height: 40px;

View File

@ -1,31 +1,30 @@
<template name="zan-toast">
<view
class="zan-toast {{ !zanToast.title ? 'zan-toast--notitle' : '' }}"
wx:if="{{ zanToast.show }}"
bindtap="clearZanToast"
<view
class="zan-toast {{ !message ? 'zan-toast--notitle' : '' }}"
wx:if="{{ show }}"
bindtap="clearZanToast"
>
<!-- icon 展示 -->
<block
wx:if="{{ icon || image }}"
>
<!-- icon 展示 -->
<block
wx:if="{{ zanToast.icon || zanToast.image }}"
<view
wx:if="{{ image }}"
class="zan-toast__icon zan-toast__icon-image"
style="background-image: url({{ image }});"
></view>
<view
wx:elif="{{ icon === 'loading' }}"
class="zan-toast__icon zan-toast__icon-loading"
>
<view
wx:if="{{ zanToast.image }}"
class="zan-toast__icon zan-toast__icon-image"
style="background-image: url({{ zanToast.image }});"
></view>
<view
wx:elif="{{ zanToast.icon === 'loading' }}"
class="zan-toast__icon zan-toast__icon-loading"
>
<view class="zan-loading"></view>
</view>
<view
wx:else
class="zan-toast__icon zan-icon zan-icon-{{ zanToast.icon }}"
></view>
</block>
<view class="zan-loading"></view>
</view>
<zan-icon
wx:else
type="{{ icon }}"
class="zan-toast__icon"
></zan-icon>
</block>
<!-- 文案展示 -->
<view wx:if="{{ zanToast.title }}">{{ zanToast.title }}</view>
</view>
</template>
<!-- 文案展示 -->
<view wx:if="{{ message }}">{{ message }}</view>
</view>

60
packages/toast/toast.js Normal file
View File

@ -0,0 +1,60 @@
let timeoutData = {
timeoutId: 0,
toastCtx: null
}
function Toast(options = {}, pageCtx) {
let ctx = pageCtx;
if (!ctx) {
const pages = getCurrentPages();
ctx = pages[pages.length - 1];
}
const toastCtx = ctx.selectComponent(options.selector);
if (!toastCtx) {
console.error('无法找到对应的toast组件请于页面中注册并在 wxml 中声明 toast 自定义组件');
return;
}
if (timeoutData.timeoutId) {
Toast.clear();
}
toastCtx.show({
...options,
show: true
});
const timeoutId = setTimeout(() => {
toastCtx.clear();
}, options.timeout || 3000);
timeoutData = {
timeoutId,
toastCtx
}
};
// 清理所有 toast
Toast.clear = function() {
clearTimeout(timeoutData.timeoutId);
try {
timeoutData.toastCtx && timeoutData.toastCtx.clear();
} catch (e) {}
timeoutData = {
timeoutId: 0,
toastCtx: null
}
}
// 显示 loading
Toast.loading = function(options = {}) {
Toast({
...options,
type: 'loading'
});
}
module.exports = Toast;