[improvement] rename packages dir to src (#3659)

This commit is contained in:
neverland
2019-06-27 11:25:57 +08:00
committed by GitHub
parent 8489918dca
commit 75c79b7044
619 changed files with 21 additions and 21 deletions
+125
View File
@@ -0,0 +1,125 @@
import { createNamespace, isDef } from '../utils';
import { PopupMixin } from '../mixins/popup';
import Icon from '../icon';
import Loading from '../loading';
const [createComponent, bem] = createNamespace('toast');
export default createComponent({
mixins: [PopupMixin],
props: {
icon: String,
className: null,
loadingType: String,
forbidClick: Boolean,
message: [String, Number],
type: {
type: String,
default: 'text'
},
position: {
type: String,
default: 'middle'
},
lockScroll: {
type: Boolean,
default: false
}
},
data() {
return {
clickable: false
};
},
mounted() {
this.toggleClickable();
},
destroyed() {
this.toggleClickable();
},
watch: {
value() {
this.toggleClickable();
},
forbidClick() {
this.toggleClickable();
}
},
methods: {
toggleClickable() {
const clickable = this.value && this.forbidClick;
if (this.clickable !== clickable) {
this.clickable = clickable;
const action = clickable ? 'add' : 'remove';
document.body.classList[action]('van-toast--unclickable');
}
},
/* istanbul ignore next */
onAfterEnter() {
this.$emit('opened');
if (this.onOpened) {
this.onOpened();
}
},
onAfterLeave() {
this.$emit('closed');
}
},
render(h) {
const { type, icon, message, loadingType } = this;
const hasIcon = icon || (type === 'success' || type === 'fail');
function ToastIcon() {
if (hasIcon) {
return <Icon class={bem('icon')} name={icon || type} />;
}
if (type === 'loading') {
return <Loading class={bem('loading')} color="white" type={loadingType} />;
}
}
function Message() {
if (!isDef(message) || message === '') {
return;
}
if (type === 'html') {
return <div class={bem('text')} domPropsInnerHTML={message} />;
}
return <div class={bem('text')}>{message}</div>;
}
return (
<transition
name="van-fade"
onAfterEnter={this.onAfterEnter}
onAfterLeave={this.onAfterLeave}
>
<div
vShow={this.value}
class={[
bem([this.position, { text: !hasIcon && type !== 'loading' }]),
this.className
]}
>
{ToastIcon()}
{Message()}
</div>
</transition>
);
}
});
+167
View File
@@ -0,0 +1,167 @@
<template>
<demo-section>
<demo-block :title="$t('title1')">
<van-button
type="primary"
@click="$toast($t('text'))"
>
{{ $t('title1') }}
</van-button>
<van-button
type="primary"
@click="$toast($t('longText'))"
>
{{ $t('longTextButton') }}
</van-button>
</demo-block>
<demo-block :title="$t('title2')">
<van-button
type="primary"
@click="showLoadingToast"
>
{{ $t('title2') }}
</van-button>
</demo-block>
<demo-block :title="$t('title3')">
<van-button
type="info"
@click="showSuccessToast"
>
{{ $t('success') }}
</van-button>
<van-button
type="danger"
@click="showFailToast"
>
{{ $t('fail') }}
</van-button>
</demo-block>
<demo-block
v-if="!$attrs.weapp"
:title="$t('customIcon')"
>
<van-button
type="primary"
@click="showIconToast"
>
{{ $t('customIcon') }}
</van-button>
<van-button
type="primary"
@click="showImageToast"
>
{{ $t('customImage') }}
</van-button>
</demo-block>
<demo-block :title="$t('advancedUsage')">
<van-button
type="primary"
@click="showCustomizedToast"
>
{{ $t('advancedUsage') }}
</van-button>
</demo-block>
</demo-section>
</template>
<script>
export default {
i18n: {
'zh-CN': {
title1: '文字提示',
title2: '加载提示',
title3: '成功/失败提示',
success: '成功提示',
fail: '失败提示',
text: '提示内容',
longText: '这是一条长文字提示,超过一定字数就会换行',
text2: '成功文案',
text3: '失败文案',
customIcon: '自定义图标',
customImage: '展示图片',
text4: second => `倒计时 ${second}`,
longTextButton: '长文字提示'
},
'en-US': {
title1: 'Text',
title2: 'Loading',
title3: 'Success/Fail',
success: 'Success',
fail: 'Fail',
text: 'Some messages',
longText: 'This is a long message, text will wrap when over a certain length',
text2: 'Success',
text3: 'Fail',
customIcon: 'Custom Icon',
customImage: 'Custom Image',
text4: second => `${second} seconds`,
longTextButton: 'Long Text'
}
},
methods: {
showLoadingToast() {
this.$toast.loading({ mask: true, message: this.$t('loading') });
},
showSuccessToast() {
this.$toast.success(this.$t('text2'));
},
showFailToast() {
this.$toast.fail(this.$t('text3'));
},
showIconToast() {
this.$toast({
message: this.$t('customIcon'),
icon: 'like-o'
});
},
showImageToast() {
this.$toast({
message: this.$t('customImage'),
icon: 'https://img.yzcdn.cn/vant/logo.png'
});
},
showCustomizedToast() {
const toast = this.$toast.loading({
duration: 0,
forbidClick: true,
loadingType: 'spinner',
message: this.$t('text4', 3)
});
let second = 3;
const timer = setInterval(() => {
second--;
if (second) {
toast.message = this.$t('text4', second);
} else {
clearInterval(timer);
this.$toast.clear();
}
}, 1000);
}
}
};
</script>
<style lang="less">
@import "../../style/var";
.demo-toast {
background-color: @white;
.van-button {
margin-left: 15px;
}
}
</style>
+127
View File
@@ -0,0 +1,127 @@
# Toast
### Install
```javascript
import { Toast } from 'vant';
Vue.use(Toast);
```
## Usage
### Text
```javascript
Toast('Some messages');
```
### Loading
```javascript
Toast.loading({
mask: true,
message: 'Loading...'
});
```
### Success/Fail
```javascript
Toast.success('Success');
Toast.fail('Fail');
```
### Custom Icon
```js
Toast({
message: 'Custom Icon',
icon: 'like-o'
});
Toast({
message: 'Custom Image',
icon: 'https://img.yzcdn.cn/vant/logo.png'
});
```
### Advanced Usage
```javascript
const toast = Toast.loading({
duration: 0, // continuous display toast
forbidClick: true, // forbid click background
loadingType: 'spinner',
message: '3 seconds'
});
let second = 3;
const timer = setInterval(() => {
second--;
if (second) {
toast.message = `${second} seconds`;
} else {
clearInterval(timer);
Toast.clear();
}
}, 1000);
```
### $toast Method
After import the Toast component, the $toast method is automatically mounted on Vue.prototype, making it easy to call within a vue component.
```js
export default {
mounted() {
this.$toast('Some messages');
}
}
```
### Singleton
Toast use singleton mode by default, if you need to pop multiple Toast at the same time, you can refer to the following example
```js
Toast.allowMultiple();
const toast1 = Toast('First Toast');
const toast2 = Toast.success('Second Toast');
toast1.clear();
toast2.clear();
```
## API
### Methods
| Methods | Attribute | Return value | Description |
|------|------|------|------|
| Toast | `options | message` | toast instance | Show toast |
| Toast.loading | `options | message` | toast instance | Show loading toast |
| Toast.success | `options | message` | toast instance | Show success toast |
| Toast.fail | `options | message` | toast instance | Show fail toast |
| Toast.clear | `clearAll: boolean` | `void` | Close toast |
| Toast.allowMultiple | - | `void` | Allow multlple toast at the same time |
| Toast.setDefaultOptions | `options` | `void` | Set default options of all toasts |
| Toast.resetDefaultOptions | - | `void` | Reset default options of all toasts |
### Options
| Attribute | Description | Type | Default |
|------|------|------|------|
| type | Can be set to `loading` `success` `fail` `html` | `String` | `text` |
| position | Can be set to `top` `middle` `bottom` | `String` | `middle` |
| message | Message | `String` | `''` |
| icon | Custom icon | `String` | - |
| mask | Whether to show mask | `Boolean` | `false` |
| forbidClick | Whether to forbid click background | `Boolean` | `false` |
| loadingType | Loading icon type, can be set to `spinner` | `String` | `circular` |
| duration | Toast duration(ms), won't disappear if value is 0 | `Number` | `3000` |
| className | Custom className | `String | Array | Object` | - |
| onOpened | Callback function after opened | `Function` | - |
| onClose | Callback function after close | `Function` | - |
| getContainer | Return the mount node for Toast | `String | () => HTMLElement` | `body` |
+147
View File
@@ -0,0 +1,147 @@
import Vue from 'vue';
import VueToast from './Toast';
import { isObj, isServer } from '../utils';
const defaultOptions = {
icon: '',
type: 'text',
mask: false,
value: true,
message: '',
className: '',
onClose: null,
onOpened: null,
duration: 3000,
position: 'middle',
forbidClick: false,
loadingType: undefined,
getContainer: 'body',
overlayStyle: null
};
let queue = [];
let multiple = false;
let currentOptions = {
...defaultOptions
};
function parseOptions(message) {
if (isObj(message)) {
return message;
}
return { message };
}
function createInstance() {
/* istanbul ignore if */
if (isServer) {
return {};
}
if (!queue.length || multiple) {
const toast = new (Vue.extend(VueToast))({
el: document.createElement('div')
});
queue.push(toast);
}
return queue[queue.length - 1];
}
// transform toast options to popup props
function transformOptions(options) {
options.overlay = options.mask;
return options;
}
function Toast(options = {}) {
const toast = createInstance();
// should add z-index if previous toast has not disappeared
if (toast.value) {
toast.updateZIndex();
}
options = {
...currentOptions,
...parseOptions(options),
clear() {
toast.value = false;
if (options.onClose) {
options.onClose();
}
if (multiple && !isServer) {
toast.$on('closed', () => {
clearTimeout(toast.timer);
queue = queue.filter(item => item !== toast);
const parent = toast.$el.parentNode;
if (parent) {
parent.removeChild(toast.$el);
}
toast.$destroy();
});
}
}
};
Object.assign(toast, transformOptions(options));
clearTimeout(toast.timer);
if (options.duration > 0) {
toast.timer = setTimeout(() => {
toast.clear();
}, options.duration);
}
return toast;
}
const createMethod = type => options =>
Toast({
type,
...parseOptions(options)
});
['loading', 'success', 'fail'].forEach(method => {
Toast[method] = createMethod(method);
});
Toast.clear = all => {
if (queue.length) {
if (all) {
queue.forEach(toast => {
toast.clear();
});
queue = [];
} else if (!multiple) {
queue[0].clear();
} else {
queue.shift().clear();
}
}
};
Toast.setDefaultOptions = options => {
Object.assign(currentOptions, options);
};
Toast.resetDefaultOptions = () => {
currentOptions = { ...defaultOptions };
};
Toast.allowMultiple = (value = true) => {
multiple = value;
};
Toast.install = () => {
Vue.use(VueToast);
};
Vue.prototype.$toast = Toast;
export default Toast;
+69
View File
@@ -0,0 +1,69 @@
@import '../style/var';
.van-toast {
position: fixed;
top: 50%;
left: 50%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-sizing: content-box;
// hack for avoid max-width when use left & fixed
width: @toast-default-width;
max-width: @toast-max-width;
min-height: @toast-default-min-height;
padding: @toast-default-padding;
color: @toast-text-color;
font-size: @toast-font-size;
line-height: @toast-line-height;
// allow newline charactor
white-space: pre-wrap;
text-align: center;
word-break: break-all;
background-color: @toast-background-color;
border-radius: @toast-border-radius;
transform: translate3d(-50%, -50%, 0);
// should not add pointer-events: none directly to body tag
// that will cause unexpected tap-highlight-color in mobile safari
&--unclickable {
* {
pointer-events: none;
}
}
&--text {
width: fit-content;
min-width: @toast-text-min-width;
min-height: unset;
padding: @toast-text-padding;
.van-toast__text {
margin-top: 0;
}
}
&--top {
top: @toast-position-top-distance;
}
&--bottom {
top: auto;
bottom: @toast-position-bottom-distance;
}
&__icon {
font-size: @toast-icon-size;
}
&__loading {
padding: 5px;
}
&__text {
margin-top: 10px;
}
}
@@ -0,0 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders demo correctly 1`] = `
<div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">
文字提示
</span></button> <button class="van-button van-button--primary van-button--normal"><span class="van-button__text">
长文字提示
</span></button></div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">
加载提示
</span></button></div>
<div><button class="van-button van-button--info van-button--normal"><span class="van-button__text">
成功提示
</span></button> <button class="van-button van-button--danger van-button--normal"><span class="van-button__text">
失败提示
</span></button></div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">
自定义图标
</span></button> <button class="van-button van-button--primary van-button--normal"><span class="van-button__text">
展示图片
</span></button></div>
<div><button class="van-button van-button--primary van-button--normal"><span class="van-button__text">
高级用法
</span></button></div>
</div>
`;
@@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`create a forbidClick toast 1`] = `
<div class="van-toast van-toast--middle" style="z-index: 2000;" name="van-fade"><i class="van-icon van-icon-success van-toast__icon">
<!----></i></div>
`;
exports[`icon prop 1`] = `
<div class="van-toast van-toast--middle" style="z-index: 2004;" name="van-fade"><i class="van-icon van-icon-star-o van-toast__icon">
<!----></i>
<div class="van-toast__text">Message</div>
</div>
`;
exports[`show html toast 1`] = `
<div class="van-toast van-toast--middle van-toast--text" style="z-index: 2003;" name="van-fade">
<div class="van-toast__text">
<div>Message</div>
</div>
</div>
`;
exports[`show loading toast 1`] = `
<div class="van-toast van-toast--middle" style="z-index: 2002;" name="van-fade">
<div class="van-loading van-loading--circular van-toast__loading"><span class="van-loading__spinner van-loading__spinner--circular" style="color: white;"><svg viewBox="25 25 50 50" class="van-loading__circular"><circle cx="50" cy="50" r="20" fill="none"></circle></svg></span></div>
<div class="van-toast__text">Message</div>
</div>
`;
+4
View File
@@ -0,0 +1,4 @@
import Demo from '../demo';
import demoTest from '../../../test/demo-test';
demoTest(Demo);
+140
View File
@@ -0,0 +1,140 @@
import Vue from 'vue';
import Toast from '..';
import ToastVue from '../Toast';
import { transitionStub, later } from '../../../test/utils';
transitionStub();
test('create a forbidClick toast', async () => {
const toast = Toast({
forbidClick: true,
type: 'success'
});
await later();
expect(toast.$el.outerHTML).toMatchSnapshot();
await later();
expect(document.body.classList.contains('van-toast--unclickable')).toBeTruthy();
toast.forbidClick = false;
await later();
expect(document.body.classList.contains('van-toast--unclickable')).toBeFalsy();
});
it('toast disappeared after duration', async () => {
const toast = Toast({
duration: 10
});
await later(50);
expect(toast.$el.style.display).toEqual('none');
});
test('show loading toast', async () => {
const toast = Toast.loading({
message: 'Message'
});
await later();
expect(toast.$el.outerHTML).toMatchSnapshot();
});
test('show html toast', async () => {
const toast = Toast({
type: 'html',
message: '<div>Message</div>'
});
await later();
expect(toast.$el.outerHTML).toMatchSnapshot();
});
test('icon prop', async () => {
const toast = Toast({
message: 'Message',
icon: 'star-o'
});
await later();
expect(toast.$el.outerHTML).toMatchSnapshot();
});
test('clear toast', () => {
const toast1 = Toast();
expect(toast1.value).toBeTruthy();
Toast.clear();
expect(toast1.value).toBeFalsy();
Toast.allowMultiple();
const toast2 = Toast('2');
const toast3 = Toast('3');
Toast.clear(true);
expect(toast2.value).toBeFalsy();
expect(toast3.value).toBeFalsy();
Toast.allowMultiple(false);
});
test('clear multiple toast', () => {
Toast.allowMultiple();
Toast.clear(true);
const toast1 = Toast.success('1');
const toast2 = Toast.success('2');
Toast.clear();
expect(toast1.value).toBeFalsy();
expect(toast2.value).toBeTruthy();
Toast.clear();
Toast.clear();
expect(toast2.value).toBeFalsy();
Toast.allowMultiple(false);
});
test('remove toast DOM when cleared in multiple mode', async () => {
Toast.allowMultiple();
Toast.clear(true);
const toast = Toast({ message: '1' });
await later();
// mock onAfterLeave
toast.clear();
toast.onAfterLeave();
await later();
expect(toast.$el.parentNode).toEqual(null);
Toast.allowMultiple(false);
});
test('set default options', () => {
Toast.setDefaultOptions({ duration: 1000 });
expect(Toast().duration).toEqual(1000);
Toast.resetDefaultOptions();
expect(Toast().duration).toEqual(3000);
});
test('toast duration 0', () => {
Toast.allowMultiple();
const toast = Toast({
message: 'toast',
duration: 0
});
expect(toast.timer).toBeFalsy();
Toast.allowMultiple(false);
});
test('onClose callback', () => {
Toast.allowMultiple();
const onClose = jest.fn();
const toast = Toast({
message: 'toast',
onClose
});
toast.clear();
Toast.allowMultiple(false);
expect(onClose).toHaveBeenCalledTimes(1);
});
test('register component', () => {
Vue.use(Toast);
expect(Vue.component(ToastVue.name)).toBeTruthy();
});
+127
View File
@@ -0,0 +1,127 @@
# Toast 轻提示
### 引入
```js
import { Toast } from 'vant';
Vue.use(Toast);
```
## 代码演示
### 文字提示
```js
Toast('提示内容');
```
### 加载提示
```js
Toast.loading({
mask: true,
message: '加载中...'
});
```
### 成功/失败提示
```js
Toast.success('成功文案');
Toast.fail('失败文案');
```
### 自定义图标
```js
Toast({
message: '自定义图标',
icon: 'like-o'
});
Toast({
message: '展示图片',
icon: 'https://img.yzcdn.cn/vant/logo.png'
});
```
### 高级用法
```js
const toast = Toast.loading({
duration: 0, // 持续展示 toast
forbidClick: true, // 禁用背景点击
loadingType: 'spinner',
message: '倒计时 3 秒'
});
let second = 3;
const timer = setInterval(() => {
second--;
if (second) {
toast.message = `倒计时 ${second}`;
} else {
clearInterval(timer);
Toast.clear();
}
}, 1000);
```
### 组件内调用
引入 Toast 组件后,会自动在 Vue 的 prototype 上挂载 $toast 方法,便于在组件内调用。
```js
export default {
mounted() {
this.$toast('提示文案');
}
}
```
### 单例模式
Toast 默认采用单例模式,即同一时间只会存在一个 Toast,如果需要在同一时间弹出多个 Toast,可以参考下面的示例
```js
Toast.allowMultiple();
const toast1 = Toast('第一个 Toast');
const toast2 = Toast.success('第二个 Toast');
toast1.clear();
toast2.clear();
```
## API
### 方法
| 方法名 | 参数 | 返回值 | 介绍 |
|------|------|------|------|
| Toast | `options | message` | toast 实例 | 展示提示 |
| Toast.loading | `options | message` | toast 实例 | 展示加载提示 |
| Toast.success | `options | message` | toast 实例 | 展示成功提示 |
| Toast.fail | `options | message` | toast 实例 | 展示失败提示 |
| Toast.clear | `clearAll: boolean` | `void` | 关闭提示 |
| Toast.allowMultiple | - | `void` | 允许同时存在多个 Toast |
| Toast.setDefaultOptions | `options` | `void` | 修改默认配置,对所有 Toast 生效 |
| Toast.resetDefaultOptions | - | `void` | 重置默认配置,对所有 Toast 生效 |
### Options
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|------|------|------|------|------|
| type | 提示类型,可选值为 `loading` `success`<br>`fail` `html` | `String` | `text` | - |
| position | 位置,可选值为 `top` `bottom` | `String` | `middle` | - |
| message | 文本内容,支持通过`\n`换行 | `String` | `''` | - | - |
| icon | 自定义图标,支持传入图标名称或图片链接,可选值见 Icon 组件 | `String` | - | 2.0.1 |
| mask | 是否显示背景遮罩层 | `Boolean` | `false` | - |
| forbidClick | 是否禁止背景点击 | `Boolean` | `false` | - |
| loadingType | 加载图标类型, 可选值为 `spinner` | `String` | `circular` | 1.1.3 |
| duration | 展示时长(ms),值为 0 时,toast 不会消失 | `Number` | `3000` | - |
| className | 自定义类名 | `String | Array | Object` | - | 1.6.0 |
| onOpened | 完全展示后的回调函数 | `Function` | - | 2.0.0 |
| onClose | 关闭时的回调函数 | `Function` | - | 1.6.10 |
| getContainer | 指定挂载的节点,可以传入选择器,<br>或一个返回节点的函数 | `String | () => HTMLElement` | `body` | 1.6.3 |