+
+## CalendarView
+
+### Props
+
+| Name | Description | Type | Default | Version |
+| --- | --- | --- | --- | --- |
+| allowClear | Whether to allow clearing after another click. | `boolean` | `true` |
+| defaultValue | The default selected date or date range. | Same as `value` prop. | - |
+| max | Maximum value of a selectable range. | `Date` | - |
+| min | Minimum value of a selectable range. | `Date` | - | - |
+| onChange | Trigger when selected date changes. | `(val: Date \| null) => void` when selection mode is "single". `(val: [Date, Date] \| null) => void` when selection mode is "range". | - |
+| renderTop | The top information of date render function. | `(date: Date) => ReactNode \| null \| undefined` | - |
+| renderBottom | The bottom information of date render function. | `(date: Date) => ReactNode \| null \| undefined` | - |
+| selectionMode | The selection mode. Disable selection when this prop is not set. | `'single' \| 'range'` | - |
+| shouldDisableDate | Set whether the date is disable selection. The min and max Settings are ignored | `(date: Date) => boolean` | - |
+| title | The title of calendar | `React.ReactNode` | `Date selection` |
+| value | The selected date or date range. | `Date \| null` when selection mode is "single". `[Date, Date] \| null` when selection mode is "range" | - |
+| weekStartsOn | Week starts on which day. | `'Monday' \| 'Sunday'` | `'Sunday'` |
+| renderDate | Custom date rendering. | `(date: Date) => ReactNode` | - | 5.28.0 |
+
+### CSS Variables
+
+Not supported yet.
+
+### Ref
+
+| Name | Description | Type |
+| --- | --- | --- |
+| jumpTo | Jump to specified page | `(page: Page \| ((page: Page) => Page)) => void` |
+| jumpToToday | Jump to today's page | `() => void` |
+| getDateRange | get date | `[Date, Date]` |
+
+```ts
+type Page = { month: number; year: number }
+```
+
+You can manually control the page turning of the calendar through Ref, for example:
+
+```ts
+// Jump to today's page
+ref.current.jumpToToday()
+
+// Jump to the specified year and month
+ref.current.jumpTo({ year: 2021, month: 1 })
+
+// Jump to three years later
+ref.current.jumpTo(page => ({
+ year: page.year + 3,
+ month: page.month,
+}))
+```
diff --git a/src/components/calendar/calendar.less b/src/components/calendar-view/calendar-view.less
similarity index 54%
rename from src/components/calendar/calendar.less
rename to src/components/calendar-view/calendar-view.less
index aaaf8d2e58..ce675711dd 100644
--- a/src/components/calendar/calendar.less
+++ b/src/components/calendar-view/calendar-view.less
@@ -1,72 +1,103 @@
-.adm-calendar {
+.adm-calendar,
+.adm-calendar-view,
+.adm-calendar-popup {
+ & &-title {
+ flex: auto;
+ font-size: var(--adm-font-size-10);
+ }
+
& &-header {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- padding-top: 4px;
- a.adm-calendar-arrow-button {
- padding: 4px 8px;
- display: block;
- flex: none;
- svg {
- height: 22px;
- }
- &.adm-calendar-arrow-button-right {
- svg {
- transform: rotate(180deg);
- }
- }
- }
- .adm-calendar-title {
- font-size: var(--adm-font-size-10);
- flex: auto;
+ padding: 12px;
+ border-bottom: 1px solid var(--adm-color-border);
+
+ .adm-calendar-view-title {
text-align: center;
}
}
+
& &-body {
- display: flex;
- flex-wrap: wrap;
+ height: 64vh;
+ overflow: auto;
+
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ .adm-calendar-view-title {
+ position: sticky;
+ top: 0;
+ padding: 8px 20px;
+ background-color: var(--adm-color-box);
+ }
+ }
+
+ & &-footer {
+ &-bottom {
+ padding: 0 20px 16px;
+ }
+
+ .adm-divider {
+ margin-top: 0;
+ }
+
+ .adm-button {
+ width: 100%;
+ }
}
+
&-cells {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: stretch;
- padding: 8px 8px 4px;
+ padding: 4px 8px;
}
+
&-cell {
flex: none;
box-sizing: border-box;
width: calc(100% / 7);
- height: 48px;
+ min-height: 55px;
margin-bottom: 4px;
padding: 2px;
color: var(--adm-color-text);
cursor: pointer;
+
&&-today {
color: var(--adm-color-primary);
}
+
&&-disabled {
color: var(--adm-color-light);
- .adm-calendar-cell-bottom {
+
+ .adm-calendar-view-cell-top,
+ .adm-calendar-view-cell-bottom {
color: var(--adm-color-light);
}
}
+
&&-selected {
&& {
- background: var(--adm-color-primary);
- color: var(--adm-color-white);
+ background: rgba(22, 119, 255, 10%);
+ color: var(--adm-color-text);
}
- & .adm-calendar-cell-bottom {
+
+ & .adm-calendar-view-cell-top,
+ & .adm-calendar-view-cell-bottom {
color: var(--adm-color-white);
}
+
&&-begin {
+ background: var(--adm-color-primary);
+ color: var(--adm-color-white);
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
+
&&-end {
+ background: var(--adm-color-primary);
+ color: var(--adm-color-white);
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
@@ -79,16 +110,20 @@
display: flex;
flex-direction: column;
align-items: center;
- justify-content: flex-end;
- & &-top {
+ justify-content: center;
+
+ & &-date {
flex: none;
- font-size: var(--adm-font-size-10);
+ line-height: 22px;
+ font-size: var(--adm-font-size-8);
}
+
+ & &-top,
& &-bottom {
flex: none;
- font-size: var(--adm-font-size-4);
- height: 12px;
- line-height: 12px;
+ font-size: var(--adm-font-size-1);
+ height: 14px;
+ line-height: 14px;
color: var(--adm-color-weak);
}
}
@@ -101,8 +136,9 @@
border-bottom: solid 1px var(--adm-color-border);
height: 45px;
box-sizing: border-box;
- font-size: var(--adm-font-size-7);
+ font-size: var(--adm-font-size-6);
padding: 0 8px;
+
& &-cell {
flex: 1;
text-align: center;
diff --git a/src/components/calendar-view/calendar-view.tsx b/src/components/calendar-view/calendar-view.tsx
new file mode 100644
index 0000000000..6ed42221bb
--- /dev/null
+++ b/src/components/calendar-view/calendar-view.tsx
@@ -0,0 +1,332 @@
+import React, {
+ forwardRef,
+ useState,
+ useImperativeHandle,
+ useMemo,
+} from 'react'
+import type { ReactNode } from 'react'
+import { NativeProps, withNativeProps } from '../../utils/native-props'
+import dayjs from 'dayjs'
+import classNames from 'classnames'
+import { mergeProps } from '../../utils/with-default-props'
+import { useConfig } from '../config-provider'
+import isoWeek from 'dayjs/plugin/isoWeek'
+import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
+import { usePropsValue } from '../../utils/use-props-value'
+import {
+ convertValueToRange,
+ convertPageToDayjs,
+ DateRange,
+ Page,
+} from './convert'
+
+dayjs.extend(isoWeek)
+dayjs.extend(isSameOrBefore)
+
+const classPrefix = 'adm-calendar-view'
+
+export type CalendarViewRef = {
+ jumpTo: (page: Page | ((page: Page) => Page)) => void
+ jumpToToday: () => void
+ getDateRange: () => DateRange
+}
+
+export type CalendarViewProps = {
+ title?: React.ReactNode
+ confirmText?: string
+ weekStartsOn?: 'Monday' | 'Sunday'
+ renderTop?: (date: Date) => React.ReactNode
+ renderDate?: (date: Date) => React.ReactNode
+ renderBottom?: (date: Date) => React.ReactNode
+ allowClear?: boolean
+ max?: Date
+ min?: Date
+ shouldDisableDate?: (date: Date) => boolean
+} & (
+ | {
+ selectionMode?: undefined
+ value?: undefined
+ defaultValue?: undefined
+ onChange?: undefined
+ }
+ | {
+ selectionMode: 'single'
+ value?: Date | null
+ defaultValue?: Date | null
+ onChange?: (val: Date | null) => void
+ }
+ | {
+ selectionMode: 'range'
+ value?: [Date, Date] | null
+ defaultValue?: [Date, Date] | null
+ onChange?: (val: [Date, Date] | null) => void
+ }
+) &
+ NativeProps
+
+const defaultProps = {
+ weekStartsOn: 'Sunday',
+ defaultValue: null,
+ allowClear: true,
+ usePopup: true,
+ selectionMode: 'single',
+}
+
+export const CalendarView = forwardRef
+
+## CalendarView
+
+### 属性
+
+| 属性 | 说明 | 类型 | 默认值 | 版本 |
+| --- | --- | --- | --- | --- |
+| allowClear | 是否允许再次点击后清除 | `boolean` | `true` |
+| defaultValue | 默认选择的日期 | 同 `value` 属性 | - |
+| max | 可选择范围的最大值 | `Date` | - |
+| min | 可选择范围的最小值 | `Date` | - |
+| onChange | 选择日期变化时触发 | 单选模式下为 `(val: Date \| null) => void`,多选模式下为 `(val: [Date, Date] \| null) => void` | - |
+| renderTop | 日期顶部信息的渲染函数 | `(date: Date) => ReactNode \| null \| undefined` | - |
+| renderBottom | 日期底部信息的渲染函数 | `(date: Date) => ReactNode \| null \| undefined` | - |
+| selectionMode | 选择模式,不设置的话表示不支持选择 | `'single' \| 'range'` | - |
+| shouldDisableDate | 判断日期是否可选,使用后会忽略 min 和 max 设置 | `(date: Date) => boolean` | - |
+| title | 日期选择器的标题 | `React.ReactNode` | `日期选择` |
+| value | 选择的日期 | 单选模式下为 `Date \| null`,多选模式下为 `[Date, Date] \| null` | - |
+| weekStartsOn | 每周以周几作为第一天 | `'Monday' \| 'Sunday'` | `'Sunday'` |
+| renderDate | 自定义日期渲染 | `(date: Date) => ReactNode` | - | 5.28.0 |
+
+### CSS 变量
+
+暂无
+
+### Ref
+
+| 属性 | 说明 | 类型 |
+| --- | --- | --- |
+| jumpTo | 跳转至指定日期的区间 | `(page: Page \| ((page: Page) => Page)) => void` |
+| jumpToToday | 跳转至今日 | `() => void` |
+| getDateRange | 获取日期 | `[Date, Date]` |
+
+```ts
+type Page = { month: number; year: number }
+```
+
+你可以通过 Ref 手动控制日历的翻页,例如:
+
+```ts
+// 跳回当月
+ref.current.jumpToToday()
+
+// 跳转至指定年月
+ref.current.jumpTo({ year: 2021, month: 1 })
+
+// 跳转到三年之后
+ref.current.jumpTo(page => ({
+ year: page.year + 3,
+ month: page.month,
+}))
+```
diff --git a/src/components/calendar/convert.ts b/src/components/calendar-view/convert.ts
similarity index 100%
rename from src/components/calendar/convert.ts
rename to src/components/calendar-view/convert.ts
diff --git a/src/components/calendar-view/demos/demo1.tsx b/src/components/calendar-view/demos/demo1.tsx
new file mode 100644
index 0000000000..eab94397bd
--- /dev/null
+++ b/src/components/calendar-view/demos/demo1.tsx
@@ -0,0 +1,11 @@
+import React from 'react'
+import { CalendarView } from 'antd-mobile'
+import { DemoBlock } from 'demos'
+
+export default () => {
+ return (
+
-
-
## Calendar
### Props
| Name | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
+| visible | To show or hide the Cclendar | `boolean` | `true` |
+| confirmText | The text of confirm button | `string` | `Confirm` |
+| popupClassName | The custom class name of the popup | `string` | - |
+| popupStyle | The custom style of the popup | `React.CSSProperties` | - |
+| popupBodyStyle | The custom style of the popup body | `React.CSSProperties` | - |
+| forceRender | Render content forcely,When ref is passed,always be true | `boolean` | `false` |
+| closeOnMaskClick | Whether to close after clicking the mask layer | `boolean` | `true` |
+| onClose | Triggered when closed | `() => void` | - |
+| onMaskClick | Triggered when the mask is clicked | `() => void` | - |
| allowClear | Whether to allow clearing after another click. | `boolean` | `true` |
| defaultValue | The default selected date or date range. | Same as `value` prop. | - |
| max | Maximum value of a selectable range. | `Date` | - |
| min | Minimum value of a selectable range. | `Date` | - | - |
-| maxPage | Maximum visible page of date. | `Page` |
-| minPage | Minimum visible page of date. | `Page` | - |
-| nextMonthButton | Contents of the Next Month button on the navigation pane | `React.ReactNode` | `>` |
-| nextYearButton | Contents of the next Year button on the navigation pane | `React.ReactNode` | `>>` |
| onChange | Trigger when selected date changes. | `(val: Date \| null) => void` when selection mode is "single". `(val: [Date, Date] \| null) => void` when selection mode is "range". | - |
-| onPageChange | Trigger when changed year or month. | `(year: number, month: number) => void` | - |
-| prevMonthButton | Contents of the Last Month button on the navigation pane | `React.ReactNode` | `<` |
-| prevYearButton | Contents of the Last year button on the navigation pane | `React.ReactNode` | `<<` |
-| renderLabel | The label render function. | `(date: Date) => ReactNode \| null \| undefined` | - |
+| onConfirm | Trigger when confirm button is clicked. | `(val: Date \| null) => void` when selection mode is "single",`(val: [Date, Date] \| null) => void` when selection mode is "range" | - |
+| renderTop | The top information of date render function. | `(date: Date) => ReactNode \| null \| undefined` | - |
+| renderBottom | The bottom information of date render function. | `(date: Date) => ReactNode \| null \| undefined` | - |
| selectionMode | The selection mode. Disable selection when this prop is not set. | `'single' \| 'range'` | - |
| shouldDisableDate | Set whether the date is disable selection. The min and max Settings are ignored | `(date: Date) => boolean` | - |
+| title | The title of calendar | `React.ReactNode` | `Date selection` |
| value | The selected date or date range. | `Date \| null` when selection mode is "single". `[Date, Date] \| null` when selection mode is "range" | - |
| weekStartsOn | Week starts on which day. | `'Monday' \| 'Sunday'` | `'Sunday'` |
| renderDate | Custom date rendering. | `(date: Date) => ReactNode` | - | 5.28.0 |
@@ -53,6 +56,7 @@ Not supported yet.
| --- | --- | --- |
| jumpTo | Jump to specified page | `(page: Page \| ((page: Page) => Page)) => void` |
| jumpToToday | Jump to today's page | `() => void` |
+| getDateRange | get date | `[Date, Date]` |
```ts
type Page = { month: number; year: number }
diff --git a/src/components/calendar/calendar.tsx b/src/components/calendar/calendar.tsx
index 641267eae3..057dabb8d1 100644
--- a/src/components/calendar/calendar.tsx
+++ b/src/components/calendar/calendar.tsx
@@ -1,333 +1,123 @@
-import React, {
- forwardRef,
- useState,
- useImperativeHandle,
- useMemo,
-} from 'react'
-import type { ReactNode } from 'react'
-import { NativeProps, withNativeProps } from '../../utils/native-props'
-import dayjs from 'dayjs'
+import React, { forwardRef, useRef } from 'react'
+import { withNativeProps } from '../../utils/native-props'
import classNames from 'classnames'
+import Button from '../button'
+import Divider from '../divider'
+import Popup from '../popup'
import { mergeProps } from '../../utils/with-default-props'
-import { ArrowLeft } from './arrow-left'
-import { ArrowLeftDouble } from './arrow-left-double'
import { useConfig } from '../config-provider'
-import isoWeek from 'dayjs/plugin/isoWeek'
-import { useUpdateEffect } from 'ahooks'
-import { usePropsValue } from '../../utils/use-props-value'
-import { replaceMessage } from '../../utils/replace-message'
-import {
- convertValueToRange,
- convertPageToDayjs,
- DateRange,
- Page,
-} from './convert'
-
-dayjs.extend(isoWeek)
+import CalendarView, {
+ CalendarViewProps,
+ CalendarViewRef,
+} from '../calendar-view'
const classPrefix = 'adm-calendar'
-export type CalendarRef = {
- jumpTo: (page: Page | ((page: Page) => Page)) => void
- jumpToToday: () => void
-}
-
-export type CalendarProps = {
- prevMonthButton?: ReactNode
- prevYearButton?: ReactNode
- nextMonthButton?: ReactNode
- nextYearButton?: ReactNode
- onPageChange?: (year: number, month: number) => void
- weekStartsOn?: 'Monday' | 'Sunday'
- renderLabel?: (date: Date) => ReactNode
- renderDate?: (date: Date) => ReactNode
- allowClear?: boolean
- max?: Date
- min?: Date
- shouldDisableDate?: (date: Date) => boolean
- minPage?: Page
- maxPage?: Page
+export type CalendarRef = CalendarViewRef
+
+export type CalendarProps = CalendarViewProps & {
+ visible?: boolean
+ confirmText?: string
+ popupClassName?: string
+ popupStyle?: React.CSSProperties
+ popupBodyStyle?: React.CSSProperties
+ forceRender?: true
+ closeOnMaskClick?: boolean
+ onClose?: () => void
+ onMaskClick?: () => void
} & (
- | {
- selectionMode?: undefined
- value?: undefined
- defaultValue?: undefined
- onChange?: undefined
- }
- | {
- selectionMode: 'single'
- value?: Date | null
- defaultValue?: Date | null
- onChange?: (val: Date | null) => void
- }
- | {
- selectionMode: 'range'
- value?: [Date, Date] | null
- defaultValue?: [Date, Date] | null
- onChange?: (val: [Date, Date] | null) => void
- }
-) &
- NativeProps
+ | {
+ selectionMode?: undefined
+ onConfirm?: undefined
+ }
+ | {
+ selectionMode: 'single'
+ onConfirm?: (val: Date | null) => void
+ }
+ | {
+ selectionMode: 'range'
+ onConfirm?: (val: [Date, Date] | null) => void
+ }
+ )
const defaultProps = {
weekStartsOn: 'Sunday',
defaultValue: null,
allowClear: true,
- prevMonthButton:
-
-
## Calendar
### 属性
| 属性 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
+| visible | 显示隐藏 | `boolean` | `true` |
+| confirmText | 确认按钮文案 | `string` | `确认` |
+| popupClassName | 弹出层类名 | `string` | - |
+| popupStyle | 弹层容器的自定义样式 | `React.CSSProperties` | - |
+| popupBodyStyle | 容器内部自定义样式 | `React.CSSProperties` | - |
+| forceRender | 强制渲染内容,当传递 ref 时,会强制设为 true | `boolean` | `false` |
+| closeOnMaskClick | 点击背景蒙层后是否关闭 | `boolean` | `true` |
+| onClose | 关闭时触发 | `() => void` | - |
+| onMaskClick | 点击背景蒙层时触发 | `() => void` | - |
| allowClear | 是否允许再次点击后清除 | `boolean` | `true` |
| defaultValue | 默认选择的日期 | 同 `value` 属性 | - |
| max | 可选择范围的最大值 | `Date` | - |
| min | 可选择范围的最小值 | `Date` | - |
-| maxPage | 可切换到的最晚日期 | `Page` | - |
-| minPage | 可切换到的最早日期 | `Page` | - |
-| nextMonthButton | 导航窗格上的“下一月”按钮的内容 | `React.ReactNode` | `>` |
-| nextYearButton | 导航窗格上的“下一年”按钮的内容 | `React.ReactNode` | `>>` |
| onChange | 选择日期变化时触发 | 单选模式下为 `(val: Date \| null) => void`,多选模式下为 `(val: [Date, Date] \| null) => void` | - |
-| onPageChange | 切换月或年时触发 | `(year: number, month: number) => void` | - |
-| prevMonthButton | 导航窗格上的“上一月”按钮的内容 | `React.ReactNode` | `<` |
-| prevYearButton | 导航窗格上的“上一年”按钮的内容 | `React.ReactNode` | `<<` |
-| renderLabel | 标注信息的渲染函数 | `(date: Date) => ReactNode \| null \| undefined` | - |
+| onConfirm | 点击确认按钮时触发 | 单选模式下为 `(val: Date \| null) => void`,多选模式下为 `(val: [Date, Date] \| null) => void` | - |
+| renderTop | 日期顶部信息的渲染函数 | `(date: Date) => ReactNode \| null \| undefined` | - |
+| renderBottom | 日期底部信息的渲染函数 | `(date: Date) => ReactNode \| null \| undefined` | - |
| selectionMode | 选择模式,不设置的话表示不支持选择 | `'single' \| 'range'` | - |
| shouldDisableDate | 判断日期是否可选,使用后会忽略 min 和 max 设置 | `(date: Date) => boolean` | - |
+| title | 日期选择器的标题 | `React.ReactNode` | `日期选择` |
| value | 选择的日期 | 单选模式下为 `Date \| null`,多选模式下为 `[Date, Date] \| null` | - |
| weekStartsOn | 每周以周几作为第一天 | `'Monday' \| 'Sunday'` | `'Sunday'` |
| renderDate | 自定义日期渲染 | `(date: Date) => ReactNode` | - | 5.28.0 |
@@ -51,8 +54,9 @@
| 属性 | 说明 | 类型 |
| --- | --- | --- |
-| jumpTo | 跳转至指定日期的页面 | `(page: Page \| ((page: Page) => Page)) => void` |
+| jumpTo | 跳转至指定日期的区间 | `(page: Page \| ((page: Page) => Page)) => void` |
| jumpToToday | 跳转至今日 | `() => void` |
+| getDateRange | 获取日期 | `[Date, Date]` |
```ts
type Page = { month: number; year: number }
diff --git a/src/components/calendar/demos/demo1.tsx b/src/components/calendar/demos/demo1.tsx
index 19d00b9aeb..528bc15b59 100644
--- a/src/components/calendar/demos/demo1.tsx
+++ b/src/components/calendar/demos/demo1.tsx
@@ -1,50 +1,73 @@
-import React from 'react'
-import { Calendar } from 'antd-mobile'
-import { DemoBlock, DemoDescription } from 'demos'
+import dayjs from 'dayjs'
+import React, { useState } from 'react'
+import { Calendar, List } from 'antd-mobile'
-const defaultSingle = new Date('2022-03-09')
const defaultRange: [Date, Date] = [
- new Date('2022-03-09'),
- new Date('2022-03-21'),
+ dayjs().toDate(),
+ dayjs().add(2, 'day').toDate(),
]
export default () => {
- return (
- <>
-