Merge pull request #93 from chenjiahan/dev

Dialog: support both function call and component call
This commit is contained in:
neverland 2017-08-26 21:18:10 +08:00 committed by GitHub
commit 0348a4d561
27 changed files with 311 additions and 246 deletions

View File

@ -9,33 +9,29 @@
<script>
import { Dialog } from 'packages/index';
const message = '弹窗内容';
export default {
methods: {
handleAlertClick() {
onClickAlert() {
Dialog.alert({
title: 'alert标题',
message: '弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。'
}).then((action) => {
console.log(action);
title: '标题',
message
});
},
handleAlert2Click() {
onClickAlert2() {
Dialog.alert({
message: '无标题alert'
}).then((action) => {
console.log(action);
message
});
},
handleConfirmClick() {
onClickConfirm() {
Dialog.confirm({
title: 'confirm标题',
message: '弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。'
}).then((action) => {
title: '标题',
message
}).catch(action => {
console.log(action);
}, (error) => {
console.log(error);
});
}
}
@ -46,8 +42,6 @@ export default {
### 使用指南
`Dialog`和其他组件不同不是通过HTML结构的方式来使用而是通过函数调用的方式。使用前需要先引入它它接受一个数组作为参数数组中的每一项对应了图片链接。
```js
import { Dialog } from 'vant';
```
@ -56,30 +50,30 @@ import { Dialog } from 'vant';
#### 消息提示
用于提示一些消息,只包含一个确认按钮
用于提示一些消息,只包含一个确认按钮
:::demo 消息提示
```html
<van-button @click="handleAlertClick">alert</van-button>
<van-button @click="handleAlert2Click">无标题alert</van-button>
<van-button @click="onClickAlert">Alert</van-button>
<van-button @click="onClickAlert2">无标题 Alert</van-button>
<script>
export default {
methods: {
handleAlertClick() {
onClickAlert() {
Dialog.alert({
title: 'alert标题',
message: '弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。'
}).then((action) => {
console.log(action);
title: '标题',
message: '弹窗内容'
}).then(() => {
// on close
});
},
handleAlert2Click() {
onClickAlert2() {
Dialog.alert({
message: '弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。'
}).then((action) => {
console.log(action);
message: '弹窗内容'
}).then(() => {
// on close
});
}
}
@ -90,23 +84,23 @@ export default {
#### 消息确认
用于确认消息,包含取消和确认按钮
用于确认消息,包含取消和确认按钮
:::demo 消息确认
```html
<van-button @click="handleConfirmClick">confirm</van-button>
<van-button @click="onClickConfirm">Confirm</van-button>
<script>
export default {
methods: {
handleConfirmClick() {
onClickConfirm() {
Dialog.confirm({
title: 'confirm标题',
message: '弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。弹窗提示文字左右始终距离边20PX上下距离20PX文字左对齐。'
}).then((action) => {
console.log(action);
}, (error) => {
console.log(error);
title: '标题',
message: '弹窗内容'
}).then(() => {
// on confirm
}).catch(() => {
// on cancel
});
}
}
@ -117,19 +111,22 @@ export default {
### 方法
#### Dialog.alert(options)
消息提示时使用该方法。
#### Dialog.confirm(options)
消息确认时使用该方法。
| 方法名 | 参数 | 返回值 | 介绍 |
|-----------|-----------|-----------|-------------|
| Dialog.alert | options | `Promise` | 展示消息提示弹窗 |
| Dialog.confirm | options | `Promise` | 展示消息确认弹窗 |
| Dialog.close | - | `void` | 关闭弹窗 |
### Options
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| title | 标题 | `string` | | |
| message | 内容 | `string` | | |
| confirmButtonText | 确认按钮的文案 | `string` | `确认` | |
| cancelButtonText | 取消按钮的文案 | `string` | `取消` | |
| title | 标题 | `String` | | |
| message | 内容 | `String` | | |
| showConfirmButton | 是否展示确认按钮 | `Boolean` | `true` | |
| showCancelButton | 是否展示取消按钮 | `Boolean` | `false` | |
| confirmButtonText | 确认按钮的文案 | `String` | `确认` | |
| cancelButtonText | 取消按钮的文案 | `String` | `取消` | |
| overlay | 是否展示蒙层 | `Boolean` | `true` | |
| closeOnClickOverlay | 点击蒙层时是否关闭弹窗 | `Boolean` | `false` | |
| lockOnScroll | 是否禁用背景滚动 | `Boolean` | `true` | |

View File

@ -28,8 +28,8 @@
<script>
import Popup from '../mixins/popup';
import VanIcon from '../icon';
import VanLoading from '../loading';
import Icon from '../icon';
import Loading from '../loading';
export default {
name: 'van-actionsheet',
@ -37,8 +37,8 @@ export default {
mixins: [Popup],
components: {
[VanIcon.name]: VanIcon,
[VanLoading.name]: VanLoading
[Icon.name]: Icon,
[Loading.name]: Loading
},
props: {

View File

@ -5,21 +5,21 @@
</template>
<script>
export default {
name: 'van-badge-group',
export default {
name: 'van-badge-group',
props: {
// tab key
activeKey: {
type: [Number, String],
default: 0
}
},
data() {
return {
badges: []
};
props: {
// tab key
activeKey: {
type: [Number, String],
default: 0
}
};
},
data() {
return {
badges: []
};
}
};
</script>

View File

@ -51,7 +51,7 @@ export default {
wrapperStyle() {
return {
transform: `translate3d(${this.offset}px, 0, 0)`
}
};
}
},
@ -72,9 +72,8 @@ export default {
if (direction > 0 && -offset > rightWidth * 0.4 && rightWidth > 0) {
this.swipeMove(-rightWidth);
this.resetSwipeStatus();
}
// left
else if (direction < 0 && offset >leftWidth * 0.4 && leftWidth > 0) {
} else if (direction < 0 && offset > leftWidth * 0.4 && leftWidth > 0) {
this.swipeMove(leftWidth);
this.resetSwipeStatus();
} else {
@ -114,7 +113,7 @@ export default {
endDrag() {
if (this.swiping) {
this.swipeLeaveTransition(this.offset > 0 ? -1 : 1);
this.swipeLeaveTransition(this.offset > 0 ? -1 : 1);
};
}
}

View File

@ -19,7 +19,7 @@ export default {
name: 'van-datetime-picker',
components: {
'van-picker': Picker
[Picker.name]: Picker
},
props: {
@ -270,6 +270,9 @@ export default {
});
},
setColumnByValues(values) {
if (!this.$refs.picker) {
return;
}
const setColumnValue = this.$refs.picker.setColumnValue;
if (this.type === 'time') {
setColumnValue(0, values[0]);

View File

@ -2,8 +2,10 @@
<transition name="van-dialog-bounce">
<div class="van-dialog" v-show="value">
<div class="van-dialog__header" v-if="title" v-text="title" />
<div class="van-dialog__content" v-if="message">
<div class="van-dialog__message" :class="{ 'van-dialog__message--withtitle': title }" v-html="message" />
<div class="van-dialog__content">
<slot>
<div class="van-dialog__message" v-if="message" :class="{ 'van-dialog__message--withtitle': title }" v-html="message" />
</slot>
</div>
<div class="van-dialog__footer" :class="{ 'is-twobtn': showCancelButton && showConfirmButton }">
<van-button size="large" class="van-dialog__cancel" v-show="showCancelButton" @click="handleAction('cancel')">{{ cancelButtonText }}</van-button>
@ -27,33 +29,48 @@ export default {
mixins: [Popup],
props: {
title: {
type: String,
default: ''
},
message: {
type: String,
default: ''
},
showConfirmButton: {
type: Boolean,
default: true
},
showCancelButton: {
type: Boolean,
default: false
},
confirmButtonText: {
type: String,
default: '确认'
},
cancelButtonText: {
type: String,
default: '取消'
},
callback: {
type: Function
},
overlay: {
default: true
},
closeOnClickOverlay: {
default: true
default: false
},
lockOnScroll: {
default: true
}
},
data() {
return {
title: '',
message: '',
type: '',
showConfirmButton: true,
showCancelButton: false,
confirmButtonText: '确认',
cancelButtonText: '取消',
callback: null
};
},
methods: {
handleAction(action) {
this.$emit('input', false);
this.$emit(action);
this.callback && this.callback(action);
}
}

View File

@ -1,97 +1,62 @@
import Vue from 'vue';
import Dialog from './dialog';
import DialogComponent from './dialog';
const DialogConstructor = Vue.extend(Dialog);
let currentDialog;
let instance;
let dialogQueue = [];
const defaultCallback = action => {
/* istanbul ignore else */
if (currentDialog) {
if (currentDialog.resolve && action === 'confirm') {
currentDialog.resolve(action);
} else if (currentDialog.reject && action === 'cancel') {
currentDialog.reject(action);
}
const defaultConfig = {
value: true,
title: '',
message: '',
showCancelButton: false,
closeOnClickOverlay: false,
callback: action => {
instance[action === 'confirm' ? 'resolve' : 'reject'](action);
}
};
const initInstance = () => {
const DialogConstructor = Vue.extend(DialogComponent);
instance = new DialogConstructor({
el: document.createElement('div')
});
instance.$on('input', value => {
instance.value = value;
})
instance.callback = defaultCallback;
});
document.body.appendChild(instance.$el);
};
const showNextDialog = () => {
if (!instance) {
initInstance();
}
/* istanbul ignore else */
if (!instance.value && dialogQueue.length > 0) {
currentDialog = dialogQueue.shift();
const { options } = currentDialog;
for (const prop in options) {
/* istanbul ignore else */
if (options.hasOwnProperty(prop)) {
instance[prop] = options[prop];
}
const Dialog = options => {
return new Promise((resolve, reject) => {
if (!instance) {
initInstance();
}
instance.callback = options.callback || defaultCallback;
instance.value = true;
document.body.appendChild(instance.$el);
}
};
const DialogBox = options => {
return new Promise((resolve, reject) => { // eslint-disable-line
dialogQueue.push({
options: { ...options },
callback: options.callback,
resolve: resolve,
reject: reject
Object.assign(instance, {
resolve,
reject,
...options
});
showNextDialog();
});
};
DialogBox.alert = function(options) {
return DialogBox({
type: 'alert',
title: '',
message: '',
closeOnClickOverlay: false,
showCancelButton: false,
...options
});
};
Dialog.alert = options => Dialog({
...defaultConfig,
...options
});
DialogBox.confirm = function(options) {
return DialogBox({
type: 'confirm',
title: '',
message: '',
closeOnClickOverlay: true,
showCancelButton: true,
...options
});
};
Dialog.confirm = options => Dialog({
...defaultConfig,
showCancelButton: true,
...options
});
DialogBox.close = function() {
Dialog.close = () => {
instance.value = false;
dialogQueue = [];
currentDialog = null;
};
export default DialogBox;
export default Dialog;
export {
Dialog
};

View File

@ -48,15 +48,15 @@
<script>
const VALID_TYPES = ['text', 'number', 'email', 'url', 'tel', 'date', 'time', 'datetime', 'password', 'textarea'];
import vanCell from '../cell';
import vanIcon from '../icon';
import Cell from '../cell';
import Icon from '../icon';
export default {
name: 'van-field',
components: {
vanCell,
vanIcon
[Cell.name]: Cell,
[Icon.name]: Icon
},
props: {

View File

@ -13,8 +13,8 @@
<script>
import Vue from 'vue';
import Popup from '../mixins/popup';
import VanSwipe from '../swipe';
import VanSwipeItem from '../swipe-item';
import Swipe from '../swipe';
import SwipeItem from '../swipe-item';
export default {
name: 'van-image-preview',
@ -22,8 +22,8 @@ export default {
mixins: [Popup],
components: {
VanSwipe,
VanSwipeItem
[Swipe.name]: Swipe,
[SwipeItem.name]: SwipeItem
},
props: {

View File

@ -1,12 +1,13 @@
<template>
<div class="van-loading" :class="['van-loading--' + type]">
<span class="van-loading__spinner" :class="['van-loading__spinner--' + type, 'van-loading__spinner--' + color]"></span>
<div :class="['van-loading', 'van-loading--' + type]">
<span :class="['van-loading__spinner', 'van-loading__spinner--' + type, 'van-loading__spinner--' + color]"></span>
</div>
</template>
<script>
const VALID_TYPES = ['gradient-circle', 'circle'];
const VALID_COLORS = ['black', 'white'];
export default {
name: 'van-loading',
@ -14,16 +15,12 @@ export default {
type: {
type: String,
default: 'gradient-circle',
validator(value) {
return VALID_TYPES.indexOf(value) > -1;
}
validator: value => VALID_TYPES.indexOf(value) > -1
},
color: {
type: String,
default: 'black',
validator(value) {
return VALID_COLORS.indexOf(value) > -1;
}
validator: value => VALID_COLORS.indexOf(value) > -1
}
}
};

View File

@ -138,7 +138,7 @@ export default {
if (this.preventScroll) {
document.addEventListener('touchstart', this.recordPosition, false);
document.addEventListener('touchmove', this.watchTouchMove, false);
document.addEventListener('touchmove', this.watchTouchMove, false);
}
},

View File

@ -82,7 +82,7 @@ const PopupManager = {
let domParentNode;
if (dom && dom.parentNode && dom.parentNode.nodeType !== 11) {
domParentNode = dom.parentNode
domParentNode = dom.parentNode;
} else {
domParentNode = document.body;
}

View File

@ -8,7 +8,7 @@
</slot>
</div>
<div class="van-picker__columns" :class="['van-picker__columns--' + columns.length]">
<picker-column
<van-picker-column
v-for="(item, index) in columns"
:key="index"
v-model="values[index]"
@ -17,8 +17,8 @@
:itemHeight="itemHeight"
:visible-item-count="visibileColumnCount"
:value-key="valueKey"
@columnChange="columnValueChange(index)">
</picker-column>
@columnChange="columnValueChange(index)"
/>
<div class="van-picker-center-highlight" :style="{ height: itemHeight + 'px', marginTop: -itemHeight / 2 + 'px' }"></div>
</div>
</div>
@ -33,7 +33,7 @@ export default {
name: 'van-picker',
components: {
PickerColumn
[PickerColumn.name]: PickerColumn
},
props: {

View File

@ -20,14 +20,14 @@
</template>
<script>
import VanIcon from '../icon';
import Icon from '../icon';
import Clickoutside from '../utils/clickoutside';
export default {
name: 'van-search',
components: {
VanIcon
[Icon.name]: Icon
},
props: {

View File

@ -30,7 +30,7 @@ export default {
name: 'van-steps',
components: {
'van-icon': Icon
[Icon.name]: Icon
},
props: {

View File

@ -13,9 +13,9 @@ export default {
},
destroyed() {
const index = this.$parent.swipes.indexOf(this)
const index = this.$parent.swipes.indexOf(this);
if (index > -1) {
this.$parent.swipes.splice(index, 1)
this.$parent.swipes.splice(index, 1);
}
}
};

View File

@ -61,27 +61,27 @@ export default {
}).on('autoPlay', function(dist, isEnd) {
scroll.movePage(dist.x, isEnd);
});
this.dummy = dummy
this.dummy = dummy;
},
watch: {
swipes(value) {
if (this.autoPlay && value.length > 1) {
this.dummy.initMove()
this.dummy.initMove();
} else {
this.dummy.clearMove()
this.dummy.clearMove();
}
this.scroll.update();
return value
return value;
},
autoPlay(value) {
if (value && this.swipes.length > 1) {
this.dummy.initMove()
this.dummy.initMove();
} else {
this.dummy.clearMove()
this.dummy.clearMove();
}
return value
return value;
}
},

View File

@ -8,22 +8,12 @@
</template>
<script>
import VanLoading from '../loading';
/**
* van-switch
* @module components/switch
* @desc 开关
* @param {boolean} [value=false] - 开关状态
* @param {boolean} [disabled=false] - 禁用
* @param {boolean} [loading=false] - loading状态
*
* @example
* <van-switch :checked="true" :disabled="false"></van-switch>
*/
import Loading from '../loading';
export default {
name: 'van-switch',
components: {
'van-loading': VanLoading
[Loading.name]: Loading
},
props: {
value: Boolean,

View File

@ -192,7 +192,7 @@
const translate = swipeState.startTranslateLeft + deltaX;
/* istanbul ignore else */
if (translate > 0 || (translate * -1) > this.maxTranslate ) return;
if (translate > 0 || (translate * -1) > this.maxTranslate) return;
translateUtil.translateElement(el, translate, null);
},
@ -200,7 +200,7 @@
end: () => {
this.isSwiping = false;
}
})
});
},
doOnValueChange() {

View File

@ -12,9 +12,7 @@
props: {
type: {
type: String,
validator: function (val) {
return ~ALLOW_TYPE.indexOf(val);
}
validator: val => ~ALLOW_TYPE.indexOf(val)
},
mark: Boolean,
plain: Boolean

View File

@ -20,8 +20,8 @@
</template>
<script>
import vanLoading from '../loading';
import vanIcon from '../icon';
import Icon from '../icon';
import Loading from '../loading';
const TOAST_TYPES = ['text', 'html', 'loading', 'success', 'fail'];
const DEFAULT_STYLE_LIST = ['success', 'fail'];
@ -37,8 +37,8 @@ export default {
name: 'van-toast',
components: {
'van-loading': vanLoading,
'van-icon': vanIcon
[Icon.name]: Icon,
[Loading.name]: Loading
},
props: {
type: {

View File

@ -11,7 +11,6 @@
font-size: 16px;
text-align: center;
outline: 0;
overflow: hidden;
-webkit-appearance: none;
&::after {

View File

@ -42,9 +42,10 @@
overflow: hidden;
&.is-twobtn {
display: flex;
.van-button {
width: 50%;
float: left;
flex: 1;
}
.van-dialog__cancel {
@ -63,7 +64,10 @@
}
&__confirm {
color: #00C000;
&,
&:active {
color: #00C000;
}
}
&-bounce-enter {

View File

@ -1,5 +1,8 @@
require('packages/vant-css/src/index.css');
// hack for test touch event
window.ontouchstart = {};
// 读取配置文件,判断运行单个测试文件还是所有测试文件
const testsReq = require.context('./specs', true, /\.spec$/);
if (process.env.TEST_FILE) {

View File

@ -1,5 +1,20 @@
import DatetimePicker from 'packages/datetime-picker';
import { mount } from 'avoriaz';
import { triggerTouch } from '../utils';
const dragHelper = (el, position) => {
triggerTouch(el, 'touchstart', 0, 0);
triggerTouch(el, 'touchmove', 0, position / 4);
triggerTouch(el, 'touchmove', 0, position / 3);
triggerTouch(el, 'touchmove', 0, position / 2);
triggerTouch(el, 'touchmove', 0, position);
triggerTouch(el, 'touchend', 0, position);
};
const testTime = '10:00';
const testDate = new Date('2017/03/10 10:00');
const minDate = new Date('2000/01/01 00:00');
const maxDate = new Date('3000/01/01 00:00');
describe('DatetimePicker', () => {
let wrapper;
@ -7,36 +22,117 @@ describe('DatetimePicker', () => {
wrapper && wrapper.destroy();
});
it('create a datetime picker', () => {
const date = new Date();
it('create a time picker', () => {
wrapper = mount(DatetimePicker, {
attachToDocument: true,
propsData: {
type: 'datetime',
value: date
type: 'time',
value: testTime
}
});
expect(wrapper.data().innerValue.getTime()).to.equal(date.getTime());
expect(wrapper.vm.innerValue).to.equal(testTime);
});
it('create a date picker', () => {
const date = new Date();
wrapper = mount(DatetimePicker, {
attachToDocument: true,
propsData: {
type: 'date',
value: date
value: testDate
}
});
expect(wrapper.data().innerValue.getTime()).to.equal(date.getTime());
expect(wrapper.vm.innerValue.getTime()).to.equal(testDate.getTime());
});
it('create a time picker', () => {
const time = '10:00';
it('create a datetime picker', () => {
wrapper = mount(DatetimePicker, {
attachToDocument: true,
propsData: {
type: 'time',
value: time
type: 'datetime',
value: testDate
}
});
expect(wrapper.data().innerValue).to.equal(time);
expect(wrapper.vm.innerValue.getTime()).to.equal(testDate.getTime());
});
it('drag time picker', (done) => {
wrapper = mount(DatetimePicker, {
attachToDocument: true,
propsData: {
type: 'time',
value: testTime
}
});
const [hour, minute] = wrapper.find('.van-picker-column-wrapper');
dragHelper(hour, -50);
dragHelper(minute, -50);
setTimeout(() => {
expect(wrapper.vm.innerValue).to.equal('5:05');
done();
}, 10);
});
it('drag date picker', (done) => {
wrapper = mount(DatetimePicker, {
attachToDocument: true,
propsData: {
type: 'date',
value: testDate,
minDate,
maxDate
}
});
setTimeout(() => {
const [year, month, day] = wrapper.find('.van-picker-column-wrapper');
dragHelper(year, -50);
dragHelper(month, -50);
dragHelper(day, -50);
setTimeout(() => {
const newYear = wrapper.vm.innerValue.getFullYear();
const newMonth = wrapper.vm.innerValue.getMonth() + 1;
const newDay = wrapper.vm.innerValue.getDate();
expect(newYear).to.equal(2022);
expect(newMonth).to.equal(8);
expect(newDay).to.equal(15);
done();
}, 10);
}, 10);
});
it('drag datetime picker', (done) => {
wrapper = mount(DatetimePicker, {
attachToDocument: true,
propsData: {
type: 'datetime',
value: testDate,
minDate,
maxDate
}
});
setTimeout(() => {
const [year, month, day, hour, minute] = wrapper.find('.van-picker-column-wrapper');
dragHelper(year, -50);
dragHelper(month, -50);
dragHelper(day, -50);
dragHelper(hour, -50);
dragHelper(minute, -50);
setTimeout(() => {
const newYear = wrapper.vm.innerValue.getFullYear();
const newMonth = wrapper.vm.innerValue.getMonth() + 1;
const newDay = wrapper.vm.innerValue.getDate();
const newHour = wrapper.vm.innerValue.getHours();
const newMinute = wrapper.vm.innerValue.getMinutes();
expect(newYear).to.equal(2022);
expect(newMonth).to.equal(8);
expect(newDay).to.equal(15);
expect(newHour).to.equal(15);
expect(newMinute).to.equal(5);
done();
}, 10);
}, 10);
});
});

View File

@ -3,11 +3,6 @@ import Vue from 'vue';
describe('Dialog', () => {
afterEach(() => {
const el = document.querySelector('.van-dialog');
if (!el) return;
if (el.parentNode) {
el.parentNode.removeChild(el);
}
Dialog.close();
});
@ -28,30 +23,32 @@ describe('Dialog', () => {
}, 500);
});
it('create a confirm dialog', () => {
it('create a confirm dialog', (done) => {
Dialog.confirm({
title: 'title',
message: 'message'
}).catch((action) => {
expect(action).to.equal('cancel');
done();
});
expect(document.querySelector('.van-dialog')).to.exist;
setTimeout(() => {
document.querySelector('.van-dialog__cancel').click();
}, 500);
});
it('create a confirm dialog with callback', (done) => {
let dialogAction;
Dialog.confirm({
title: 'title',
message: 'message',
callback: (action) => {
dialogAction = action;
expect(action).to.equal('cancel');
done();
}
});
expect(document.querySelector('.van-dialog')).to.exist;
setTimeout(() => {
document.querySelector('.van-dialog__cancel').click();
expect(dialogAction).to.equal('cancel');
done();
}, 500);
});
});

View File

@ -62,7 +62,7 @@ export function triggerTouch(wrapper, eventName, x, y) {
radiusX: 2.5,
radiusY: 2.5,
rotationAngle: 10,
force: 0.5,
force: 0.5
};
const event = document.createEvent('CustomEvent');