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` | | color | Background color | _string_ | `#ee0a24` |
| dot | Whether to show dot | _boolean_ | `false` | | dot | Whether to show dot | _boolean_ | `false` |
| max | Max valueshow `{max}+` when exceedonly works when content is number | _number \| string_ | - | | max | Max valueshow `{max}+` when exceedonly works when content is number | _number \| string_ | - |
| offset `v3.0.5` | Offset of badge dot | _[number, number]_ | - |
### Slots ### Slots

View File

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

View File

@ -3,6 +3,12 @@
<van-badge content="5"> <van-badge content="5">
<div class="child" /> <div class="child" />
</van-badge> </van-badge>
<van-badge content="10">
<div class="child" />
</van-badge>
<van-badge content="Hot">
<div class="child" />
</van-badge>
<van-badge dot> <van-badge dot>
<div class="child" /> <div class="child" />
</van-badge> </van-badge>
@ -12,6 +18,9 @@
<van-badge content="20" max="9"> <van-badge content="20" max="9">
<div class="child" /> <div class="child" />
</van-badge> </van-badge>
<van-badge content="50" max="20">
<div class="child" />
</van-badge>
<van-badge content="200" max="99"> <van-badge content="200" max="99">
<div class="child" /> <div class="child" />
</van-badge> </van-badge>
@ -21,12 +30,37 @@
<van-badge content="5" color="#1989fa"> <van-badge content="5" color="#1989fa">
<div class="child" /> <div class="child" />
</van-badge> </van-badge>
<van-badge content="10" color="#1989fa">
<div class="child" />
</van-badge>
<van-badge dot color="#1989fa"> <van-badge dot color="#1989fa">
<div class="child" /> <div class="child" />
</van-badge> </van-badge>
</demo-block> </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')"> <demo-block :title="t('standalone')">
<van-badge content="20" style="margin-left: 16px" />
<van-badge content="200" max="99" style="margin-left: 16px" /> <van-badge content="200" max="99" style="margin-left: 16px" />
</demo-block> </demo-block>
</template> </template>
@ -76,6 +110,7 @@ export default {
.badge-icon { .badge-icon {
display: block; display: block;
margin-left: 0;
font-size: 10px; font-size: 10px;
line-height: 16px; 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 { isDef, createNamespace } from '../utils';
import { isNumeric } from '../utils/validate/number'; import { isNumeric } from '../utils/validate/number';
@ -9,6 +9,7 @@ export default createComponent({
dot: Boolean, dot: Boolean,
max: [Number, String], max: [Number, String],
color: String, color: String,
offset: (Array as unknown) as PropType<[number, number]>,
content: [Number, String], content: [Number, String],
tag: { tag: {
type: String as PropType<keyof HTMLElementTagNameMap>, type: String as PropType<keyof HTMLElementTagNameMap>,
@ -38,10 +39,25 @@ export default createComponent({
const renderBadge = () => { const renderBadge = () => {
if (hasContent() || props.dot) { 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 ( return (
<div <div
class={bem({ dot: props.dot, fixed: !!slots.default })} class={bem({ dot: props.dot, fixed: !!slots.default })}
style={{ background: props.color }} style={style}
> >
{renderContent()} {renderContent()}
</div> </div>

View File

@ -9,6 +9,20 @@ exports[`should render demo and match snapshot 1`] = `
5 5
</div> </div>
</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="van-badge__wrapper">
<div class="child"> <div class="child">
</div> </div>
@ -24,6 +38,13 @@ exports[`should render demo and match snapshot 1`] = `
9+ 9+
</div> </div>
</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="van-badge__wrapper">
<div class="child"> <div class="child">
</div> </div>
@ -42,6 +63,15 @@ exports[`should render demo and match snapshot 1`] = `
5 5
</div> </div>
</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="van-badge__wrapper">
<div class="child"> <div class="child">
</div> </div>
@ -52,6 +82,37 @@ exports[`should render demo and match snapshot 1`] = `
</div> </div>
</div> </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" <div class="van-badge"
style="margin-left: 16px;" style="margin-left: 16px;"
> >

View File

@ -40,3 +40,32 @@ test('should render content slot correctly', () => {
expect(wrapper.html()).toMatchSnapshot(); 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 | | 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_ | - | | scrollToDate | Scroll to date | _date: Date_ | - |
### Less Variables ### Less Variables

View File

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

View File

@ -311,8 +311,8 @@ export default createComponent({
}); });
}; };
const reset = () => { const reset = (date = getInitialDate()) => {
state.currentDate = getInitialDate(state.currentDate); state.currentDate = date;
scrollIntoView(); scrollIntoView();
}; };
@ -500,7 +500,9 @@ export default createComponent({
); );
watch(() => props.show, init); watch(() => props.show, init);
watch([() => props.type, () => props.minDate, () => props.maxDate], reset); watch([() => props.type, () => props.minDate, () => props.maxDate], () => {
reset(getInitialDate(state.currentDate));
});
watch( watch(
() => props.defaultDate, () => props.defaultDate,
(value) => { (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 () => { test('set show-confirm to false', async () => {
const wrapper = mount(Calendar, { const wrapper = mount(Calendar, {
props: { 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) { if (message) {
const hasTitle = title || slots.title; const hasTitle = title || slots.title;
return ( 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 <div
class={bem('message', { class={bem('message', {
'has-title': hasTitle, 'has-title': hasTitle,

View File

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

View File

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

View File

@ -127,7 +127,7 @@ export default {
| showIndex | Whether to show index | _boolean_ | `true` | | showIndex | Whether to show index | _boolean_ | `true` |
| showIndicators | Whether to show indicators | _boolean_ | `false` | | showIndicators | Whether to show indicators | _boolean_ | `false` |
| loop | Whether to enable loop | _boolean_ | `true` | | 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_ | - | | onClose | Emitted when ImagePreview is closed | _Function_ | - |
| onChange | Emitted when current image changed | _Function_ | - | | onChange | Emitted when current image changed | _Function_ | - |
| onScale | Emitted when scaling current image | _Function_ | - | | onScale | Emitted when scaling current image | _Function_ | - |
@ -147,7 +147,7 @@ export default {
| --- | --- | --- | --- | | --- | --- | --- | --- |
| images | Images URL list | _string[]_ | `[]` | | images | Images URL list | _string[]_ | `[]` |
| start-position | Start position | _number \| string_ | `0` | | 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-index | Whether to show index | _boolean_ | `true` |
| show-indicators | Whether to show indicators | _boolean_ | `false` | | show-indicators | Whether to show indicators | _boolean_ | `false` |
| loop | Whether to enable loop | _boolean_ | `true` | | loop | Whether to enable loop | _boolean_ | `true` |

View File

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

View File

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

View File

@ -22,7 +22,7 @@ exports[`should render cover slot correctly 1`] = `
<transition-stub> <transition-stub>
<div class="van-popup van-popup--center van-image-preview"> <div class="van-popup van-popup--center van-image-preview">
<div class="van-swipe van-image-preview__swipe"> <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" class="van-swipe__track"
> >
</div> </div>

View File

@ -100,7 +100,7 @@ export default {
### 级联选择 ### 级联选择
使用 `columns``children` 字段可以实现选项级联的效果。 使用 `columns``children` 字段可以实现选项级联的效果。如果级联层级较多,推荐使用 [Cascader 级联选项组件](#/zh-CN/cascader)。
```html ```html
<van-picker title="标题" :columns="columns" /> <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'; import { VanComponent } from './component';
export class Calendar extends VanComponent { export class Calendar extends VanComponent {
reset(): void; reset(date?: Date | Date[]): void;
scrollToDate(date: Date): void; scrollToDate(date: Date): void;
} }