Skip to content

Commit e713042

Browse files
authored
feat: 优化calendar组件选中交互,日期滚动到可视区域内 (#1128)
* feat: 优化calendar组件选中交互,日期滚动到可视区域内 * feat: 日期选择后滚动视图,提供属性控制 * feat: 日期选择后滚动视图,提供属性控制 * feat: 日期选择后滚动视图,提供属性控制 * chore: 测试滚动到选中位置 * chore: 文档添加changedScrollIntoView属性 * chore: 文档添加changedScrollIntoView属性
1 parent 97f5b64 commit e713042

File tree

23 files changed

+218
-44
lines changed

23 files changed

+218
-44
lines changed

compiled/alipay/demo/pages/Calendar/index.axml

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
style="height: 1000rpx">
1717
<ant-calendar
1818
selectionMode="single"
19-
defaultValue="{{ demo2.defaultValue }}" />
19+
defaultValue="{{ demo2.defaultValue }}"
20+
changedScrollIntoView />
2021
</view>
2122
</collapse-container>
2223

compiled/alipay/demo/pages/Calendar/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Page({
4646
visible: true,
4747
},
4848
demo2: {
49-
defaultValue: Date.now(),
49+
defaultValue: dayjs().add(1, 'M').toDate().getTime(),
5050
visible: true,
5151
},
5252
demo3: {

compiled/alipay/src/Calendar/index.axml

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
data-elementsize="{{ elementSize }}"
3838
data-monthlist="{{ monthList }}"
3939
onScroll="{{ scroll.handleScroll }}"
40+
scroll-into-view="{{ scrollIntoViewId }}"
41+
scroll-with-animation
42+
scroll-animation-duration="{{ 300 }}"
4043
ref="handleRef">
4144
<block
4245
a:for="{{ monthList }}"
@@ -56,6 +59,7 @@
5659
<block>
5760
<view
5861
class="{{ helper.getClassName(item, index) }}"
62+
id="id_{{ item.time }}"
5963
data-time="{{ item }}"
6064
onTap="clickCell">
6165
<view class="ant-calendar-cell-container">

compiled/alipay/src/Calendar/index.md

+12-10
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,18 @@ toc: 'content'
2121

2222
以下为日历组件的属性及描述:
2323

24-
| 属性 | 说明 | 类型 | 默认值 |
25-
| ------------- | ---------------------------------------------- | ----------------------------------------------------------- | ----------- |
26-
| defaultValue | 初始值 | CalendarValue ||
27-
| value | 日历选择的日期,传入后即为受控模式 | CalendarValue ||
28-
| selectionMode | 设置选择模式,单选或者连续区间,默认为 `range` | `single` \| `range` | `range` |
29-
| monthRange | 月份范围,默认为最近 3 个月 | `[number, number]` | 最近 3 个月 |
30-
| weekStartsOn | 星期栏,以周几作为第一天显示。默认为 `Sunday` | `Sunday` \| `Monday` | `Sunday` |
31-
| onChange | 日期变化回调 | (date: CalendarValue) => void ||
32-
| onFormatter | 用于设置单元格的自定义数据 | (cell: CellState, currentValue: CalendarValue) => CellState ||
33-
| localeText | 国际化文案 | Partial`<LocaleText>` ||
24+
| 属性 | 说明 | 类型 | 默认值 |
25+
| --------------------- | ---------------------------------------------- | ----------------------------------------------------------- | ----------- |
26+
| defaultValue | 初始值 | CalendarValue ||
27+
| value | 日历选择的日期,传入后即为受控模式 | CalendarValue ||
28+
| selectionMode | 设置选择模式,单选或者连续区间,默认为 `range` | `single` \| `range` | `range` |
29+
| monthRange | 月份范围,默认为最近 3 个月 | `[number, number]` | 最近 3 个月 |
30+
| weekStartsOn | 星期栏,以周几作为第一天显示。默认为 `Sunday` | `Sunday` \| `Monday` | `Sunday` |
31+
| onChange | 日期变化回调 | (date: CalendarValue) => void ||
32+
| onFormatter | 用于设置单元格的自定义数据 | (cell: CellState, currentValue: CalendarValue) => CellState ||
33+
| localeText | 国际化文案 | Partial`<LocaleText>` ||
34+
| changedScrollIntoView | 选中值改变后是否滚动视图 | boolean ||
35+
3436
### 类型
3537

3638
**CalendarValue** : 日历的值类型,为数字或数字元组 `number | [number,number]`,表示单选或连续日期区间。单位为毫秒的时间戳。

compiled/alipay/src/Calendar/index.ts

+19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
useEvent,
55
useReady,
66
useState,
7+
useEffect,
78
} from 'functional-mini/component';
89
import { mountComponent } from '../_util/component';
910
import { useComponentEvent } from '../_util/hooks/useComponentEvent';
@@ -19,6 +20,7 @@ import {
1920
getMonthListFromRange,
2021
getSelectionModeFromValue,
2122
renderCells,
23+
getScrollIntoViewId,
2224
} from './utils';
2325

2426
function getBoundingClientRect(instance: any, selector: string) {
@@ -51,6 +53,8 @@ const Calendar = (props: ICalendarProps) => {
5153
value: props.value,
5254
});
5355

56+
const [scrollIntoViewId, setScrollIntoViewId] = useState<string>('');
57+
5458
const selectionModeFromValue = getSelectionModeFromValue(value);
5559
const selectionMode =
5660
props.selectionMode ?? selectionModeFromValue ?? 'range';
@@ -176,8 +180,22 @@ const Calendar = (props: ICalendarProps) => {
176180
});
177181
}
178182

183+
useEffect(() => {
184+
// 滚动到已选的位置
185+
props.changedScrollIntoView &&
186+
setScrollIntoViewId(getScrollIntoViewId(value));
187+
}, [value]);
188+
179189
useReady(() => {
180190
measurement();
191+
// 初始化默认值时,滚动到选中位置
192+
const isControl = hasValue(props.value);
193+
if (isControl) {
194+
setScrollIntoViewId(getScrollIntoViewId(props.value));
195+
} else {
196+
props.defaultValue &&
197+
setScrollIntoViewId(getScrollIntoViewId(props.defaultValue));
198+
}
181199
}, []);
182200

183201
useEvent('measurement', () => {
@@ -193,6 +211,7 @@ const Calendar = (props: ICalendarProps) => {
193211
markItems,
194212
monthList,
195213
headerState,
214+
scrollIntoViewId,
196215
};
197216
};
198217

compiled/alipay/src/Calendar/props.ts

+4
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ export interface ICalendarProps extends IBaseProps {
114114
* 国际化文案
115115
*/
116116
localeText?: Partial<LocaleText>;
117+
/**
118+
* 选中值改变后滚动视图
119+
*/
120+
changedScrollIntoView?: boolean;
117121
/**
118122
* 日期变化回调
119123
*/

compiled/alipay/src/Calendar/utils.ts

+13
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,16 @@ export function getSelectionModeFromValue(
161161
}
162162
return null;
163163
}
164+
165+
// 获取滚动视图的元素id
166+
export function getScrollIntoViewId(value: CalendarValue) {
167+
// 已选中时间滚动到可视区域内(微信不支持id为数字开头)
168+
return `id_${
169+
value &&
170+
dayjs(Array.isArray(value) ? value[0] : value)
171+
.startOf('d')
172+
.subtract(7, 'd') // 需要定位的地方往前推7天,让已选中时间定位到中间位置
173+
.toDate()
174+
.getTime()
175+
}`;
176+
}

compiled/wechat/demo/pages/Calendar/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Page({
4646
visible: true,
4747
},
4848
demo2: {
49-
defaultValue: Date.now(),
49+
defaultValue: dayjs().add(1, 'M').toDate().getTime(),
5050
visible: true,
5151
},
5252
demo3: {

compiled/wechat/demo/pages/Calendar/index.wxml

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
style="height: 1000rpx">
1717
<ant-calendar
1818
selectionMode="single"
19-
defaultValue="{{ demo2.defaultValue }}" />
19+
defaultValue="{{ demo2.defaultValue }}"
20+
changedScrollIntoView />
2021
</view>
2122
</collapse-container>
2223

compiled/wechat/src/Calendar/index.js

+20-4
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
1919
return to.concat(ar || Array.prototype.slice.call(from));
2020
};
2121
import dayjs from 'dayjs';
22-
import { useComponent, useEvent, useReady, useState, } from 'functional-mini/component';
22+
import { useComponent, useEvent, useReady, useState, useEffect, } from 'functional-mini/component';
2323
import { mountComponent } from '../_util/component';
2424
import { useComponentEvent } from '../_util/hooks/useComponentEvent';
2525
import { hasValue, useMergedState } from '../_util/hooks/useMergedState';
2626
import { defaultLocaleText, } from './props';
27-
import { defaultMonthRange, getMonthListFromRange, getSelectionModeFromValue, renderCells, } from './utils';
27+
import { defaultMonthRange, getMonthListFromRange, getSelectionModeFromValue, renderCells, getScrollIntoViewId, } from './utils';
2828
function getBoundingClientRect(instance, selector) {
2929
return new Promise(function (resolve, reject) {
3030
instance
@@ -54,6 +54,7 @@ var Calendar = function (props) {
5454
var _c = useMergedState(props.defaultValue, {
5555
value: props.value,
5656
}), value = _c[0], setValue = _c[1];
57+
var _d = useState(''), scrollIntoViewId = _d[0], setScrollIntoViewId = _d[1];
5758
var selectionModeFromValue = getSelectionModeFromValue(value);
5859
var selectionMode = (_b = (_a = props.selectionMode) !== null && _a !== void 0 ? _a : selectionModeFromValue) !== null && _b !== void 0 ? _b : 'range';
5960
var triggerEvent = useComponentEvent(props).triggerEvent;
@@ -125,11 +126,11 @@ var Calendar = function (props) {
125126
cells: cells,
126127
};
127128
});
128-
var _d = useState(0), headerState = _d[0], setHeaderState = _d[1];
129+
var _e = useState(0), headerState = _e[0], setHeaderState = _e[1];
129130
useEvent('setCurrentMonth', function (e) {
130131
setHeaderState(e.month);
131132
});
132-
var _e = useState(null), elementSize = _e[0], setElementSize = _e[1];
133+
var _f = useState(null), elementSize = _f[0], setElementSize = _f[1];
133134
var componentInstance = useComponent();
134135
function measurement() {
135136
Promise.all([
@@ -154,8 +155,22 @@ var Calendar = function (props) {
154155
setElementSize(null);
155156
});
156157
}
158+
useEffect(function () {
159+
// 滚动到已选的位置
160+
props.changedScrollIntoView &&
161+
setScrollIntoViewId(getScrollIntoViewId(value));
162+
}, [value]);
157163
useReady(function () {
158164
measurement();
165+
// 初始化默认值时,滚动到选中位置
166+
var isControl = hasValue(props.value);
167+
if (isControl) {
168+
setScrollIntoViewId(getScrollIntoViewId(props.value));
169+
}
170+
else {
171+
props.defaultValue &&
172+
setScrollIntoViewId(getScrollIntoViewId(props.defaultValue));
173+
}
159174
}, []);
160175
useEvent('measurement', function () {
161176
// 组件如果内嵌在 slot 里, 一定会被渲染出来, 但是此时 cellHight 为 0
@@ -169,6 +184,7 @@ var Calendar = function (props) {
169184
markItems: markItems,
170185
monthList: monthList,
171186
headerState: headerState,
187+
scrollIntoViewId: scrollIntoViewId,
172188
};
173189
};
174190
mountComponent(Calendar, {

compiled/wechat/src/Calendar/index.md

+12-10
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,18 @@ toc: 'content'
2121

2222
以下为日历组件的属性及描述:
2323

24-
| 属性 | 说明 | 类型 | 默认值 |
25-
| ------------- | ---------------------------------------------- | ----------------------------------------------------------- | ----------- |
26-
| defaultValue | 初始值 | CalendarValue ||
27-
| value | 日历选择的日期,传入后即为受控模式 | CalendarValue ||
28-
| selectionMode | 设置选择模式,单选或者连续区间,默认为 `range` | `single` \| `range` | `range` |
29-
| monthRange | 月份范围,默认为最近 3 个月 | `[number, number]` | 最近 3 个月 |
30-
| weekStartsOn | 星期栏,以周几作为第一天显示。默认为 `Sunday` | `Sunday` \| `Monday` | `Sunday` |
31-
| onChange | 日期变化回调 | (date: CalendarValue) => void ||
32-
| onFormatter | 用于设置单元格的自定义数据 | (cell: CellState, currentValue: CalendarValue) => CellState ||
33-
| localeText | 国际化文案 | Partial`<LocaleText>` ||
24+
| 属性 | 说明 | 类型 | 默认值 |
25+
| --------------------- | ---------------------------------------------- | ----------------------------------------------------------- | ----------- |
26+
| defaultValue | 初始值 | CalendarValue ||
27+
| value | 日历选择的日期,传入后即为受控模式 | CalendarValue ||
28+
| selectionMode | 设置选择模式,单选或者连续区间,默认为 `range` | `single` \| `range` | `range` |
29+
| monthRange | 月份范围,默认为最近 3 个月 | `[number, number]` | 最近 3 个月 |
30+
| weekStartsOn | 星期栏,以周几作为第一天显示。默认为 `Sunday` | `Sunday` \| `Monday` | `Sunday` |
31+
| onChange | 日期变化回调 | (date: CalendarValue) => void ||
32+
| onFormatter | 用于设置单元格的自定义数据 | (cell: CellState, currentValue: CalendarValue) => CellState ||
33+
| localeText | 国际化文案 | Partial`<LocaleText>` ||
34+
| changedScrollIntoView | 选中值改变后是否滚动视图 | boolean ||
35+
3436
### 类型
3537

3638
**CalendarValue** : 日历的值类型,为数字或数字元组 `number | [number,number]`,表示单选或连续日期区间。单位为毫秒的时间戳。

compiled/wechat/src/Calendar/index.wxml

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
data-elementsize="{{ elementSize }}"
3434
data-monthlist="{{ monthList }}"
3535
bind:scroll="{{ scroll.handleScroll }}"
36+
scroll-into-view="{{ scrollIntoViewId }}"
37+
scroll-with-animation
38+
scroll-animation-duration="{{ 300 }}"
3639
bind:ref="handleRef">
3740
<block
3841
wx:for="{{ monthList }}"
@@ -50,6 +53,7 @@
5053
<block>
5154
<view
5255
class="{{ helper.getClassName(item, index) }}"
56+
id="id_{{ item.time }}"
5357
data-time="{{ item }}"
5458
bind:tap="clickCell">
5559
<view class="ant-calendar-cell-container">

compiled/wechat/src/Calendar/utils.js

+10
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,13 @@ export function getSelectionModeFromValue(value) {
138138
}
139139
return null;
140140
}
141+
// 获取滚动视图的元素id
142+
export function getScrollIntoViewId(value) {
143+
// 已选中时间滚动到可视区域内(微信不支持id为数字开头)
144+
return "id_".concat(value &&
145+
dayjs(Array.isArray(value) ? value[0] : value)
146+
.startOf('d')
147+
.subtract(7, 'd') // 需要定位的地方往前推7天,让已选中时间定位到中间位置
148+
.toDate()
149+
.getTime());
150+
}

demo/pages/Calendar/index.axml.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export default ({
3535
<AntCalendar
3636
selectionMode="single"
3737
defaultValue={demo2.defaultValue}
38+
changedScrollIntoView
3839
/>
3940
</View>
4041
</CollapseContainer>

demo/pages/Calendar/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Page({
5454
visible: true,
5555
},
5656
demo2: {
57-
defaultValue: Date.now(),
57+
defaultValue: dayjs().add(1, 'M').toDate().getTime(),
5858
visible: true,
5959
},
6060
demo3: {

src/Calendar/index.axml.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ import {
1313

1414
export default (
1515
{ className, style }: TSXMLProps<ICalendarProps>,
16-
{ markItems, elementSize, monthList, headerState }: InternalData
16+
{
17+
markItems,
18+
elementSize,
19+
monthList,
20+
headerState,
21+
scrollIntoViewId,
22+
}: InternalData
1723
) => (
1824
<View class={`ant-calendar ${className ? className : ''}`} style={style}>
1925
<View class="ant-calendar-mark">
@@ -45,6 +51,9 @@ export default (
4551
data-elementsize={elementSize}
4652
data-monthlist={monthList}
4753
onScroll={scroll.handleScroll}
54+
scroll-into-view={scrollIntoViewId}
55+
scroll-with-animation
56+
scroll-animation-duration={300}
4857
ref="handleRef"
4958
>
5059
{monthList.map((currentMonth) => (
@@ -65,6 +74,7 @@ export default (
6574
<Block>
6675
<View
6776
class={helper.getClassName(item, index)}
77+
id={`id_${item.time}`}
6878
data-time={item}
6979
onTap="clickCell"
7080
>

0 commit comments

Comments
 (0)