Skip to content

Commit 4eb8088

Browse files
committed
feat: datePicker add status & placement
1 parent 353f470 commit 4eb8088

File tree

21 files changed

+321
-38
lines changed

21 files changed

+321
-38
lines changed

components/config-provider/context.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import type { Locale } from '../locale-provider';
99
type GlobalFormCOntextProps = {
1010
validateMessages?: Ref<ValidateMessages>;
1111
};
12+
13+
export type DirectionType = 'ltr' | 'rtl' | undefined;
14+
1215
export const GlobalFormContextKey: InjectionKey<GlobalFormCOntextProps> =
1316
Symbol('GlobalFormContextKey');
1417

components/config-provider/index.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ import type { ValidateMessages } from '../form/interface';
1717
import type { ConfigProviderProps, Theme } from './context';
1818
import { configProviderProps, useProvideGlobalForm } from './context';
1919

20-
export type { ConfigProviderProps, Theme, SizeType, Direction, CSPConfig } from './context';
20+
export type {
21+
ConfigProviderProps,
22+
Theme,
23+
SizeType,
24+
Direction,
25+
CSPConfig,
26+
DirectionType,
27+
} from './context';
2128
export const defaultPrefixCls = 'ant';
2229
function getGlobalPrefixCls() {
2330
return globalConfigForApi.prefixCls || defaultPrefixCls;

components/date-picker/demo/index.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
<Mode />
1616
<Switchable />
1717
<Suffix />
18+
<statusVue />
19+
<placementVue />
1820
</demo-sort>
1921
</template>
2022
<script>
@@ -33,6 +35,8 @@ import Time from './time.vue';
3335
import Suffix from './suffix.vue';
3436
import Bordered from './bordered.vue';
3537
import RangePicker from './range-picker.vue';
38+
import placementVue from './placement.vue';
39+
import statusVue from './status.vue';
3640
import CN from '../index.zh-CN.md';
3741
import US from '../index.en-US.md';
3842
import { defineComponent } from 'vue';
@@ -41,6 +45,8 @@ export default defineComponent({
4145
CN,
4246
US,
4347
components: {
48+
statusVue,
49+
placementVue,
4450
Basic,
4551
DateRender,
4652
DisabledDate,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<docs>
2+
---
3+
order: 28
4+
title:
5+
zh-CN: 弹出位置
6+
en-US: Popup Placement
7+
---
8+
9+
## zh-CN
10+
11+
可以通过 `placement` 手动指定弹出的位置。
12+
13+
## en-US
14+
15+
You can manually specify the position of the popup via `placement`.
16+
17+
</docs>
18+
19+
<template>
20+
<a-radio-group v-model:value="placement">
21+
<a-radio-button value="topLeft">topLeft</a-radio-button>
22+
<a-radio-button value="topRight">topRight</a-radio-button>
23+
<a-radio-button value="bottomLeft">bottomLeft</a-radio-button>
24+
<a-radio-button value="bottomRight">bottomRight</a-radio-button>
25+
</a-radio-group>
26+
<br />
27+
<br />
28+
<a-space direction="vertical" :size="12">
29+
<a-date-picker v-model:value="value1" :placement="placement" />
30+
<a-range-picker v-model:value="value2" :placement="placement" />
31+
</a-space>
32+
</template>
33+
<script lang="ts">
34+
import { defineComponent, ref } from 'vue';
35+
import type { Dayjs } from 'dayjs';
36+
export default defineComponent({
37+
setup() {
38+
const placement = ref('topLeft' as const);
39+
return {
40+
placement,
41+
value1: ref<Dayjs>(),
42+
value2: ref<[Dayjs, Dayjs]>(),
43+
};
44+
},
45+
});
46+
</script>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<docs>
2+
---
3+
order: 19
4+
version: 4.19.0
5+
title:
6+
zh-CN: 自定义状态
7+
en-US: Status
8+
---
9+
10+
## zh-CN
11+
12+
使用 `status` 为 DatePicker 添加状态,可选 `error` 或者 `warning`。
13+
14+
## en-US
15+
16+
Add status to DatePicker with `status`, which could be `error` or `warning`.
17+
18+
</docs>
19+
20+
<template>
21+
<a-space direction="vertical" style="width: 100%">
22+
<a-date-picker status="error" />
23+
<a-date-picker status="warning" />
24+
<a-range-picker status="error" />
25+
<a-date-picker status="warning" />
26+
</a-space>
27+
</template>
28+
<script lang="ts">
29+
import { defineComponent } from 'vue';
30+
export default defineComponent({
31+
setup() {
32+
return {};
33+
},
34+
});
35+
</script>

components/date-picker/generatePicker/generateRangePicker.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { RangePicker as VCRangePicker } from '../../vc-picker';
66
import type { GenerateConfig } from '../../vc-picker/generate/index';
77
import enUS from '../locale/en_US';
88
import { useLocaleReceiver } from '../../locale-provider/LocaleReceiver';
9-
import { getRangePlaceholder } from '../util';
9+
import { getRangePlaceholder, transPlacement2DropdownAlign } from '../util';
1010
import { getTimeProps, Components } from '.';
1111
import { computed, defineComponent, nextTick, onMounted, ref } from 'vue';
1212
import useConfigInject from '../../_util/hooks/useConfigInject';
@@ -16,8 +16,9 @@ import { commonProps, rangePickerProps } from './props';
1616
import type { PanelMode, RangeValue } from '../../vc-picker/interface';
1717
import type { RangePickerSharedProps } from '../../vc-picker/RangePicker';
1818
import devWarning from '../../vc-util/devWarning';
19-
import { useInjectFormItemContext } from '../../form/FormItemContext';
19+
import { FormItemInputContext, useInjectFormItemContext } from '../../form/FormItemContext';
2020
import omit from '../../_util/omit';
21+
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
2122

2223
export default function generateRangePicker<DateType, ExtraProps = {}>(
2324
generateConfig: GenerateConfig<DateType>,
@@ -46,6 +47,7 @@ export default function generateRangePicker<DateType, ExtraProps = {}>(
4647
setup(_props, { expose, slots, attrs, emit }) {
4748
const props = _props as unknown as CommonProps<DateType> & RangePickerProps<DateType>;
4849
const formItemContext = useInjectFormItemContext();
50+
const formItemInputContext = FormItemInputContext.useInject();
4951
devWarning(
5052
!attrs.getCalendarContainer,
5153
'DatePicker',
@@ -166,6 +168,12 @@ export default function generateRangePicker<DateType, ExtraProps = {}>(
166168
: {}),
167169
};
168170
const pre = prefixCls.value;
171+
const suffixNode = (
172+
<>
173+
{suffixIcon || (picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />)}
174+
{formItemInputContext.hasFeedback && formItemInputContext.feedbackIcon}
175+
</>
176+
);
169177
return (
170178
<VCRangePicker
171179
dateRender={dateRender}
@@ -178,10 +186,9 @@ export default function generateRangePicker<DateType, ExtraProps = {}>(
178186
)
179187
}
180188
ref={pickerRef}
189+
dropdownAlign={transPlacement2DropdownAlign(direction.value, props.placement)}
181190
placeholder={getRangePlaceholder(picker, locale, placeholder as [string, string])}
182-
suffixIcon={
183-
suffixIcon || (picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />)
184-
}
191+
suffixIcon={suffixNode}
185192
clearIcon={clearIcon || <CloseCircleFilled />}
186193
allowClear={allowClear}
187194
transitionName={transitionName || `${rootPrefixCls.value}-slide-up`}
@@ -197,6 +204,11 @@ export default function generateRangePicker<DateType, ExtraProps = {}>(
197204
[`${pre}-${size.value}`]: size.value,
198205
[`${pre}-borderless`]: !bordered,
199206
},
207+
getStatusClassNames(
208+
pre,
209+
getMergedStatus(formItemInputContext.status, props.status),
210+
formItemInputContext.hasFeedback,
211+
),
200212
attrs.class,
201213
)}
202214
locale={locale!.lang}

components/date-picker/generatePicker/generateSinglePicker.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import RCPicker from '../../vc-picker';
55
import type { PanelMode, PickerMode } from '../../vc-picker/interface';
66
import type { GenerateConfig } from '../../vc-picker/generate/index';
77
import enUS from '../locale/en_US';
8-
import { getPlaceholder } from '../util';
8+
import { getPlaceholder, transPlacement2DropdownAlign } from '../util';
99
import { useLocaleReceiver } from '../../locale-provider/LocaleReceiver';
1010
import { getTimeProps, Components } from '.';
1111
import { computed, defineComponent, nextTick, onMounted, ref } from 'vue';
@@ -15,7 +15,8 @@ import type { CommonProps, DatePickerProps } from './props';
1515
import { commonProps, datePickerProps } from './props';
1616

1717
import devWarning from '../../vc-util/devWarning';
18-
import { useInjectFormItemContext } from '../../form/FormItemContext';
18+
import { FormItemInputContext, useInjectFormItemContext } from '../../form/FormItemContext';
19+
import { getMergedStatus, getStatusClassNames } from '../../_util/statusUtils';
1920

2021
export default function generateSinglePicker<DateType, ExtraProps = {}>(
2122
generateConfig: GenerateConfig<DateType>,
@@ -49,6 +50,7 @@ export default function generateSinglePicker<DateType, ExtraProps = {}>(
4950
DatePickerProps<DateType> &
5051
ExtraProps;
5152
const formItemContext = useInjectFormItemContext();
53+
const formItemInputContext = FormItemInputContext.useInject();
5254
devWarning(
5355
!(props.monthCellContentRender || slots.monthCellContentRender),
5456
'DatePicker',
@@ -185,17 +187,21 @@ export default function generateSinglePicker<DateType, ExtraProps = {}>(
185187
: {}),
186188
};
187189
const pre = prefixCls.value;
190+
const suffixNode = (
191+
<>
192+
{suffixIcon || (picker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />)}
193+
{formItemInputContext.hasFeedback && formItemInputContext.feedbackIcon}
194+
</>
195+
);
188196
return (
189197
<RCPicker
190198
monthCellRender={monthCellRender}
191199
dateRender={dateRender}
192200
renderExtraFooter={renderExtraFooter}
193201
ref={pickerRef}
194202
placeholder={getPlaceholder(mergedPicker, locale, placeholder)}
195-
suffixIcon={
196-
suffixIcon ||
197-
(mergedPicker === 'time' ? <ClockCircleOutlined /> : <CalendarOutlined />)
198-
}
203+
suffixIcon={suffixNode}
204+
dropdownAlign={transPlacement2DropdownAlign(direction.value, props.placement)}
199205
clearIcon={clearIcon || <CloseCircleFilled />}
200206
allowClear={allowClear}
201207
transitionName={transitionName || `${rootPrefixCls.value}-slide-up`}
@@ -213,6 +219,11 @@ export default function generateSinglePicker<DateType, ExtraProps = {}>(
213219
[`${pre}-${size.value}`]: size.value,
214220
[`${pre}-borderless`]: !bordered,
215221
},
222+
getStatusClassNames(
223+
pre,
224+
getMergedStatus(formItemInputContext.status, props.status),
225+
formItemInputContext.hasFeedback,
226+
),
216227
attrs.class,
217228
)}
218229
prefixCls={pre}

components/date-picker/generatePicker/index.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@ function toArray<T>(list: T | T[]): T[] {
1717
return Array.isArray(list) ? list : [list];
1818
}
1919

20-
export function getTimeProps<DateType>(
21-
props: { format?: string; picker?: PickerMode } & SharedTimeProps<DateType>,
20+
export function getTimeProps<DateType, DisabledTime>(
21+
props: { format?: string; picker?: PickerMode } & Omit<
22+
SharedTimeProps<DateType>,
23+
'disabledTime'
24+
> & {
25+
disabledTime?: DisabledTime;
26+
},
2227
) {
2328
const { format, picker, showHour, showMinute, showSecond, use12Hours } = props;
2429

2530
const firstFormat = toArray(format)[0];
26-
const showTimeObj: SharedTimeProps<DateType> = { ...props };
31+
const showTimeObj = { ...props };
2732

2833
if (firstFormat && typeof firstFormat === 'string') {
2934
if (!firstFormat.includes('s') && showSecond === undefined) {

components/date-picker/generatePicker/props.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import type { FocusEventHandler, MouseEventHandler } from '../../_util/EventInte
22
import type { CSSProperties, PropType } from 'vue';
33
import type { PickerLocale } from '.';
44
import type { SizeType } from '../../config-provider';
5-
import type { AlignType } from '../../vc-align/interface';
65
import type {
76
CustomFormat,
87
DisabledTime,
@@ -17,12 +16,16 @@ import type { MonthCellRender } from '../../vc-picker/panels/MonthPanel/MonthBod
1716
import type { SharedTimeProps } from '../../vc-picker/panels/TimePanel';
1817
import type { RangeDateRender, RangeInfo, RangeType } from '../../vc-picker/RangePicker';
1918
import type { VueNode } from '../../_util/type';
19+
import { tuple } from '../../_util/type';
20+
import type { InputStatus } from '../../_util/statusUtils';
21+
22+
const DataPickerPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
23+
type DataPickerPlacement = typeof DataPickerPlacements[number];
2024

2125
function commonProps<DateType = any>() {
2226
return {
2327
id: String,
2428
dropdownClassName: String,
25-
dropdownAlign: { type: Object as PropType<AlignType> },
2629
popupStyle: { type: Object as PropType<CSSProperties> },
2730
transitionName: String,
2831
placeholder: String,
@@ -82,14 +85,15 @@ function commonProps<DateType = any>() {
8285
mode: { type: String as PropType<PanelMode> },
8386
picker: { type: String as PropType<PickerMode> },
8487
valueFormat: String,
88+
placement: String as PropType<DataPickerPlacement>,
89+
status: String as PropType<InputStatus>,
8590
};
8691
}
8792

8893
export interface CommonProps<DateType> {
8994
id?: string;
9095
prefixCls?: string;
9196
dropdownClassName?: string;
92-
dropdownAlign?: AlignType;
9397
popupStyle?: CSSProperties;
9498
transitionName?: string;
9599
placeholder?: string;
@@ -136,6 +140,8 @@ export interface CommonProps<DateType> {
136140
mode?: PanelMode;
137141
picker?: PickerMode;
138142
valueFormat?: string;
143+
placement?: DataPickerPlacement;
144+
status?: InputStatus;
139145
}
140146

141147
function datePickerProps<DateType = any>() {

components/date-picker/index.en-US.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ The following APIs are shared by DatePicker, RangePicker.
9292
| open | The open state of picker | boolean | - | |
9393
| picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter` |
9494
| placeholder | The placeholder of date input | string \| \[string,string] | - | |
95+
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | 3.3.0 |
9596
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
9697
| prevIcon | The custom prev icon | slot | - | 3.0 |
9798
| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \| `middle` \| `small` | - | |
99+
| status | Set validation status | 'error' \| 'warning' | - | 3.3.0 |
98100
| suffixIcon | The custom suffix icon | v-slot:suffixIcon | - | |
99101
| superNextIcon | The custom super next icon | slot | - | 3.0 |
100102
| superPrevIcon | The custom super prev icon | slot | - | 3.0 |

0 commit comments

Comments
 (0)