Merge branch 'dev' of https://github.com/youzan/vant into dev

This commit is contained in:
chenjiahan 2021-01-24 09:18:48 +08:00
commit 2399dc8460
21 changed files with 237 additions and 52 deletions

5
SECURITY.md Normal file
View File

@ -0,0 +1,5 @@
# Security Policy
## Reporting a Vulnerability
chenjiahan@buaa.edu.cn

View File

@ -117,6 +117,7 @@ Use `content` slot to custom :content of badge.
| color | Background color | _string_ | `#ee0a24` |
| dot | Whether to show dot | _boolean_ | `false` |
| max | Max valueshow `{max}+` when exceedonly works when content is number | _number \| string_ | - |
| offset `v3.0.5` | Offset of badge dot | _[number, number]_ | - |
### Slots

View File

@ -129,6 +129,7 @@ app.use(Badge);
| color | 徽标背景颜色 | _string_ | `#ee0a24` |
| dot | 是否展示为小红点 | _boolean_ | `false` |
| max | 最大值,超过最大值会显示 `{max}+`,仅当 content 为数字时有效 | _number \| string_ | - |
| offset `v3.0.5` | 设置徽标的偏移量,数组的两项分别对应水平和垂直方向的偏移量 | _[number, number]_ | - |
### Slots

View File

@ -3,6 +3,12 @@
<van-badge content="5">
<div class="child" />
</van-badge>
<van-badge content="10">
<div class="child" />
</van-badge>
<van-badge content="Hot">
<div class="child" />
</van-badge>
<van-badge dot>
<div class="child" />
</van-badge>
@ -12,6 +18,9 @@
<van-badge content="20" max="9">
<div class="child" />
</van-badge>
<van-badge content="50" max="20">
<div class="child" />
</van-badge>
<van-badge content="200" max="99">
<div class="child" />
</van-badge>
@ -21,12 +30,37 @@
<van-badge content="5" color="#1989fa">
<div class="child" />
</van-badge>
<van-badge content="10" color="#1989fa">
<div class="child" />
</van-badge>
<van-badge dot color="#1989fa">
<div class="child" />
</van-badge>
</demo-block>
<demo-block :title="t('customContent')">
<van-badge>
<div class="child" />
<template #content>
<van-icon name="success" class="badge-icon" />
</template>
</van-badge>
<van-badge>
<div class="child" />
<template #content>
<van-icon name="cross" class="badge-icon" />
</template>
</van-badge>
<van-badge>
<div class="child" />
<template #content>
<van-icon name="down" class="badge-icon" />
</template>
</van-badge>
</demo-block>
<demo-block :title="t('standalone')">
<van-badge content="20" style="margin-left: 16px" />
<van-badge content="200" max="99" style="margin-left: 16px" />
</demo-block>
</template>
@ -76,6 +110,7 @@ export default {
.badge-icon {
display: block;
margin-left: 0;
font-size: 10px;
line-height: 16px;
}

View File

@ -1,4 +1,4 @@
import type { PropType } from 'vue';
import type { PropType, CSSProperties } from 'vue';
import { isDef, createNamespace } from '../utils';
import { isNumeric } from '../utils/validate/number';
@ -9,6 +9,7 @@ export default createComponent({
dot: Boolean,
max: [Number, String],
color: String,
offset: (Array as unknown) as PropType<[number, number]>,
content: [Number, String],
tag: {
type: String as PropType<keyof HTMLElementTagNameMap>,
@ -38,10 +39,25 @@ export default createComponent({
const renderBadge = () => {
if (hasContent() || props.dot) {
const style: CSSProperties = {
background: props.color,
};
if (props.offset) {
const [x, y] = props.offset;
if (slots.default) {
style.top = `${y}px`;
style.right = `${-x}px`;
} else {
style.marginTop = `${y}px`;
style.marginLeft = `${x}px`;
}
}
return (
<div
class={bem({ dot: props.dot, fixed: !!slots.default })}
style={{ background: props.color }}
style={style}
>
{renderContent()}
</div>

View File

@ -9,6 +9,20 @@ exports[`should render demo and match snapshot 1`] = `
5
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
<div class="van-badge van-badge--fixed">
10
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
<div class="van-badge van-badge--fixed">
Hot
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
@ -24,6 +38,13 @@ exports[`should render demo and match snapshot 1`] = `
9+
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
<div class="van-badge van-badge--fixed">
20+
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
@ -42,6 +63,15 @@ exports[`should render demo and match snapshot 1`] = `
5
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
<div class="van-badge van-badge--fixed"
style="background: rgb(25, 137, 250);"
>
10
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
@ -52,6 +82,37 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
<div>
<div class="van-badge__wrapper">
<div class="child">
</div>
<div class="van-badge van-badge--fixed">
<i class="van-badge__wrapper van-icon van-icon-success badge-icon">
</i>
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
<div class="van-badge van-badge--fixed">
<i class="van-badge__wrapper van-icon van-icon-cross badge-icon">
</i>
</div>
</div>
<div class="van-badge__wrapper">
<div class="child">
</div>
<div class="van-badge van-badge--fixed">
<i class="van-badge__wrapper van-icon van-icon-down badge-icon">
</i>
</div>
</div>
</div>
<div>
<div class="van-badge"
style="margin-left: 16px;"
>
20
</div>
<div class="van-badge"
style="margin-left: 16px;"
>

View File

@ -40,3 +40,32 @@ test('should render content slot correctly', () => {
expect(wrapper.html()).toMatchSnapshot();
});
test('should change dot position when using offset prop', () => {
const wrapper = mount(Badge, {
props: {
dot: true,
offset: [2, 4],
},
slots: {
default: () => 'Child',
},
});
const badge = wrapper.find('.van-badge').element;
expect(badge.style.top).toEqual('4px');
expect(badge.style.right).toEqual('-2px');
});
test('should change dot position when using offset prop without children', () => {
const wrapper = mount(Badge, {
props: {
dot: true,
offset: [2, 4],
},
});
const badge = wrapper.find('.van-badge').element;
expect(badge.style.marginTop).toEqual('4px');
expect(badge.style.marginLeft).toEqual('2px');
});

View File

@ -336,7 +336,7 @@ Use [ref](https://v3.vuejs.org/guide/component-template-refs.html) to get Calend
| Name | Description | Attribute | Return value |
| --- | --- | --- | --- |
| reset | Reset selected date to default date | - | - |
| reset | Reset selected date, will reset to default date when no params passed | _date?: Date \| Date[]_ | - |
| scrollToDate | Scroll to date | _date: Date_ | - |
### Less Variables

View File

@ -340,10 +340,10 @@ export default {
通过 ref 可以获取到 Calendar 实例并调用实例方法,详见[组件实例方法](#/zh-CN/advanced-usage#zu-jian-shi-li-fang-fa)。
| 方法名 | 说明 | 参数 | 返回值 |
| ------------ | ---------------------- | ------------ | ------ |
| reset | 重置选中的日期到默认值 | - | - |
| scrollToDate | 滚动到某个日期 | _date: Date_ | - |
| 方法名 | 说明 | 参数 | 返回值 |
| --- | --- | --- | --- |
| reset | 将选中的日期重置到指定日期,未传参时会重置到默认日期 | _date?: Date \| Date[]_ | - |
| scrollToDate | 滚动到某个日期 | _date: Date_ | - |
### 样式变量

View File

@ -311,8 +311,8 @@ export default createComponent({
});
};
const reset = () => {
state.currentDate = getInitialDate(state.currentDate);
const reset = (date = getInitialDate()) => {
state.currentDate = date;
scrollIntoView();
};
@ -500,7 +500,9 @@ export default createComponent({
);
watch(() => props.show, init);
watch([() => props.type, () => props.minDate, () => props.maxDate], reset);
watch([() => props.type, () => props.minDate, () => props.maxDate], () => {
reset(getInitialDate(state.currentDate));
});
watch(
() => props.defaultDate,
(value) => {

View File

@ -196,31 +196,6 @@ test('default range date', async () => {
);
});
test('reset method', async () => {
const wrapper = mount(Calendar, {
props: {
minDate,
maxDate,
type: 'range',
poppable: false,
defaultDate: [minDate, getNextDay(minDate)],
},
});
await later();
const days = wrapper.findAll('.van-calendar__day');
days.at(15).trigger('click');
days.at(18).trigger('click');
wrapper.vm.reset();
wrapper.find('.van-calendar__confirm').trigger('click');
expect(formatRange(wrapper.emitted('confirm')[0][0])).toEqual(
'2010/1/10-2010/1/11'
);
});
test('set show-confirm to false', async () => {
const wrapper = mount(Calendar, {
props: {

View File

@ -0,0 +1,50 @@
import Calendar from '..';
import { mount } from '../../../test';
import { getNextDay, getPrevDay } from '../utils';
import { minDate, maxDate } from './utils';
test('should reset to default date when calling reset method', async () => {
const defaultDate = [minDate, getNextDay(minDate)];
const wrapper = mount(Calendar, {
props: {
minDate,
maxDate,
type: 'range',
poppable: false,
lazyRender: false,
defaultDate,
},
});
const days = wrapper.findAll('.van-calendar__day');
await days[15].trigger('click');
await days[18].trigger('click');
wrapper.vm.reset();
wrapper.find('.van-calendar__confirm').trigger('click');
expect(wrapper.emitted('confirm')[0][0]).toEqual(defaultDate);
});
test('should reset to specific date when calling reset method with date', async () => {
const wrapper = mount(Calendar, {
props: {
minDate,
maxDate,
type: 'range',
poppable: false,
lazyRender: false,
defaultDate: [minDate, getNextDay(minDate)],
},
});
const days = wrapper.findAll('.van-calendar__day');
await days[15].trigger('click');
await days[18].trigger('click');
const newDate = [getPrevDay(maxDate), maxDate];
wrapper.vm.reset(newDate);
wrapper.find('.van-calendar__confirm').trigger('click');
expect(wrapper.emitted('confirm')[0][0]).toEqual(newDate);
});

View File

@ -120,7 +120,12 @@ export default createComponent({
if (message) {
const hasTitle = title || slots.title;
return (
<div class={bem('content', { isolated: !hasTitle })}>
<div
// add key to force re-render
// see: https://github.com/youzan/vant/issues/7963
key={allowHtml ? 1 : 0}
class={bem('content', { isolated: !hasTitle })}
>
<div
class={bem('message', {
'has-title': hasTitle,

View File

@ -47,7 +47,7 @@ export default createComponent({
},
swipeDuration: {
type: [Number, String],
default: 500,
default: 300,
},
startPosition: {
type: [Number, String],

View File

@ -94,11 +94,15 @@ export default {
});
const setScale = (scale) => {
state.scale = range(scale, +props.minZoom, +props.maxZoom);
emit('scale', {
scale: state.scale,
index: state.active,
});
scale = range(scale, +props.minZoom, +props.maxZoom);
if (scale !== state.scale) {
state.scale = scale;
emit('scale', {
scale,
index: props.active,
});
}
};
const resetScale = () => {
@ -235,6 +239,7 @@ export default {
state.imageRatio = naturalHeight / naturalWidth;
};
watch(() => props.active, resetScale);
watch(
() => props.show,
(value) => {

View File

@ -127,7 +127,7 @@ export default {
| showIndex | Whether to show index | _boolean_ | `true` |
| showIndicators | Whether to show indicators | _boolean_ | `false` |
| loop | Whether to enable loop | _boolean_ | `true` |
| swipeDuration | Animation duration (ms) | _number \| string_ | `500` |
| swipeDuration | Animation duration (ms) | _number \| string_ | `300` |
| onClose | Emitted when ImagePreview is closed | _Function_ | - |
| onChange | Emitted when current image changed | _Function_ | - |
| onScale | Emitted when scaling current image | _Function_ | - |
@ -147,7 +147,7 @@ export default {
| --- | --- | --- | --- |
| images | Images URL list | _string[]_ | `[]` |
| start-position | Start position | _number \| string_ | `0` |
| swipe-duration | Animation duration (ms) | _number \| string_ | `500` |
| swipe-duration | Animation duration (ms) | _number \| string_ | `300` |
| show-index | Whether to show index | _boolean_ | `true` |
| show-indicators | Whether to show indicators | _boolean_ | `false` |
| loop | Whether to enable loop | _boolean_ | `true` |

View File

@ -161,7 +161,7 @@ export default {
| --- | --- | --- | --- |
| images | 需要预览的图片 URL 数组 | _string[]_ | `[]` |
| startPosition | 图片预览起始位置索引 | _number \| string_ | `0` |
| swipeDuration | 动画时长,单位为`ms` | _number \| string_ | `500` |
| swipeDuration | 动画时长,单位为 `ms` | _number \| string_ | `300` |
| showIndex | 是否显示页码 | _boolean_ | `true` |
| showIndicators | 是否显示轮播指示器 | _boolean_ | `false` |
| loop | 是否开启循环播放 | _boolean_ | `true` |
@ -175,7 +175,7 @@ export default {
| minZoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` |
| closeable | 是否显示关闭图标 | _boolean_ | `false` |
| closeIcon | 关闭图标名称或图片链接 | _string_ | `clear` |
| closeIconPosition | 关闭图标位置,可选值为`top-left`<br>`bottom-left` `bottom-right` | _string_ | `top-right` |
| closeIconPosition | 关闭图标位置,可选值为 `top-left`<br>`bottom-left` `bottom-right` | _string_ | `top-right` |
| teleport | 指定挂载的节点,[用法示例](#/zh-CN/popup#zhi-ding-gua-zai-wei-zhi) | _string \| Element_ | - |
### Props
@ -186,7 +186,7 @@ export default {
| --- | --- | --- | --- |
| images | 需要预览的图片 URL 数组 | _string[]_ | `[]` |
| start-position | 图片预览起始位置索引 | _number \| string_ | `0` |
| swipe-duration | 动画时长,单位为 ms | _number \| string_ | `500` |
| swipe-duration | 动画时长,单位为 ms | _number \| string_ | `300` |
| show-index | 是否显示页码 | _boolean_ | `true` |
| show-indicators | 是否显示轮播指示器 | _boolean_ | `false` |
| loop | 是否开启循环播放 | _boolean_ | `true` |

View File

@ -19,7 +19,7 @@ const defaultConfig = {
closeIcon: 'clear',
beforeClose: null,
startPosition: 0,
swipeDuration: 500,
swipeDuration: 300,
showIndicators: false,
closeOnPopstate: true,
closeIconPosition: 'top-right',

View File

@ -22,7 +22,7 @@ exports[`should render cover slot correctly 1`] = `
<transition-stub>
<div class="van-popup van-popup--center van-image-preview">
<div class="van-swipe van-image-preview__swipe">
<div style="transition-duration: 500ms; transform: translateX(0px);"
<div style="transition-duration: 300ms; transform: translateX(0px);"
class="van-swipe__track"
>
</div>

View File

@ -100,7 +100,7 @@ export default {
### 级联选择
使用 `columns``children` 字段可以实现选项级联的效果。
使用 `columns``children` 字段可以实现选项级联的效果。如果级联层级较多,推荐使用 [Cascader 级联选项组件](#/zh-CN/cascader)。
```html
<van-picker title="标题" :columns="columns" />
@ -143,7 +143,7 @@ export default {
};
```
> 级联选择的数据嵌套深度需要保持一致,如果部分选项没有子选项,可以使用空字符串进行占位
> 级联选择的数据嵌套深度需要保持一致,如果部分选项没有子选项,可以使用空字符串进行占位
### 禁用选项

2
types/calendar.d.ts vendored
View File

@ -1,7 +1,7 @@
import { VanComponent } from './component';
export class Calendar extends VanComponent {
reset(): void;
reset(date?: Date | Date[]): void;
scrollToDate(date: Date): void;
}