mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-21 03:15:49 +08:00
feat: add datepicker component
This commit is contained in:
parent
961f8c342e
commit
8ae10c38df
209
packages/arco-lib/src/components/DatePicker.tsx
Normal file
209
packages/arco-lib/src/components/DatePicker.tsx
Normal file
@ -0,0 +1,209 @@
|
||||
import { DatePicker as BaseDatePicker } from '@arco-design/web-react';
|
||||
import { implementRuntimeComponent } from '@sunmao-ui/runtime';
|
||||
import { css } from '@emotion/css';
|
||||
import { Type, Static } from '@sinclair/typebox';
|
||||
import { FALLBACK_METADATA, getComponentProps } from '../sunmao-helper';
|
||||
import {
|
||||
DatePickerPropsSpec as BaseDatePickerPropsSpec,
|
||||
RangePickerPropsSpec,
|
||||
} from '../generated/types/DatePicker';
|
||||
import {
|
||||
DisabledTimeFunc,
|
||||
DisabledTimeProps,
|
||||
} from '@arco-design/web-react/es/DatePicker/interface';
|
||||
|
||||
const DatePickerPropsSpec = Type.Object(BaseDatePickerPropsSpec);
|
||||
const DatePickerStateSpec = Type.Object({
|
||||
visible: Type.Boolean(),
|
||||
date: Type.Any(),
|
||||
dateString: Type.Union([Type.Array(Type.String()), Type.String()]),
|
||||
});
|
||||
|
||||
const DatePickerType = {
|
||||
month: BaseDatePicker.MonthPicker,
|
||||
year: BaseDatePicker.YearPicker,
|
||||
week: BaseDatePicker.WeekPicker,
|
||||
quarter: BaseDatePicker.QuarterPicker,
|
||||
date: BaseDatePicker,
|
||||
};
|
||||
const RangePicker = BaseDatePicker.RangePicker;
|
||||
|
||||
const exampleProperties: Static<typeof DatePickerPropsSpec> = {
|
||||
disabled: false,
|
||||
placeholder: 'Please Select',
|
||||
position: 'bl',
|
||||
dayStartOfWeek: 0,
|
||||
allowClear: false,
|
||||
type: 'date',
|
||||
range: false,
|
||||
defaultValue: '',
|
||||
showTime: false,
|
||||
panelOnly: false,
|
||||
size: 'default',
|
||||
disabledTime: {
|
||||
disabledHours: [0, 1, 2],
|
||||
disabledMinutes: new Array(30).fill(0).map((e, i) => i),
|
||||
disabledSeconds: [10],
|
||||
},
|
||||
rangePlaceholder: ['Start date', 'End Date'],
|
||||
disabledRangeTime: {
|
||||
disabledHours: [
|
||||
[0, 1, 2],
|
||||
[7, 9, 10],
|
||||
],
|
||||
disabledMinutes: [new Array(30).fill(0).map((e, i) => i), [58, 59]],
|
||||
disabledSeconds: [[], []],
|
||||
},
|
||||
disabledDate: {
|
||||
disabledType: 'range',
|
||||
dateRange: ['2022-5-23', '2022-5-26'],
|
||||
},
|
||||
rangeDisabled: [false, false],
|
||||
clearRangeOnReselect: false,
|
||||
};
|
||||
|
||||
export const DatePicker = implementRuntimeComponent({
|
||||
version: 'arco/v1',
|
||||
metadata: {
|
||||
...FALLBACK_METADATA,
|
||||
name: 'DatePicker',
|
||||
displayName: 'DatePicker',
|
||||
exampleProperties,
|
||||
annotations: {
|
||||
category: 'Display',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
properties: DatePickerPropsSpec,
|
||||
state: DatePickerStateSpec,
|
||||
methods: {},
|
||||
slots: ['footer', 'triggerElement'],
|
||||
styleSlots: ['content'],
|
||||
events: ['onChange', 'onClear', 'onVisibleChange'],
|
||||
},
|
||||
})(props => {
|
||||
const {
|
||||
disabledTime,
|
||||
disabledRangeTime,
|
||||
disabledDate,
|
||||
range,
|
||||
placeholder,
|
||||
rangePlaceholder,
|
||||
rangeDisabled,
|
||||
type,
|
||||
panelOnly,
|
||||
clearRangeOnReselect,
|
||||
...cProps
|
||||
} = getComponentProps(props);
|
||||
const { elementRef, customStyle, slotsElements, callbackMap, mergeState } = props;
|
||||
const Picker = DatePickerType[type];
|
||||
|
||||
return (
|
||||
<span ref={elementRef} className={css(customStyle?.content)}>
|
||||
{range ? (
|
||||
<RangePicker
|
||||
disabledTime={(date, type) =>
|
||||
getDisabledRangeTime(date, type, disabledRangeTime!)
|
||||
}
|
||||
disabledDate={current => getDisabledDate(current, disabledDate)}
|
||||
extra={slotsElements.footer}
|
||||
triggerElement={panelOnly ? null : slotsElements.triggerElement}
|
||||
mode={type}
|
||||
placeholder={rangePlaceholder?.length === 0 ? undefined : rangePlaceholder}
|
||||
clearRangeOnReselect={clearRangeOnReselect}
|
||||
{...cProps}
|
||||
disabled={rangeDisabled}
|
||||
onChange={(dateString, date) => {
|
||||
mergeState({
|
||||
date,
|
||||
dateString,
|
||||
});
|
||||
callbackMap?.onChange?.();
|
||||
}}
|
||||
onVisibleChange={visible => {
|
||||
mergeState({ visible });
|
||||
callbackMap?.onVisibleChange?.();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Picker
|
||||
disabledTime={date => getDisabledTime(date, disabledTime)}
|
||||
disabledDate={current => getDisabledDate(current, disabledDate)}
|
||||
extra={slotsElements.footer}
|
||||
triggerElement={panelOnly ? null : slotsElements.triggerElement}
|
||||
placeholder={placeholder}
|
||||
{...cProps}
|
||||
onChange={(dateString, date) => {
|
||||
mergeState({
|
||||
date,
|
||||
dateString,
|
||||
});
|
||||
callbackMap?.onChange?.();
|
||||
}}
|
||||
onVisibleChange={visible => {
|
||||
mergeState({ visible });
|
||||
callbackMap?.onVisibleChange?.();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
function getDisabledDate(
|
||||
date: Parameters<DisabledTimeFunc>[0],
|
||||
disabledDate: Static<typeof BaseDatePickerPropsSpec.disabledDate>
|
||||
) {
|
||||
if (disabledDate.disabledType === 'after') {
|
||||
return date!.isAfter(disabledDate.date);
|
||||
}
|
||||
if (disabledDate.disabledType === 'before') {
|
||||
return date!.isBefore(disabledDate.date);
|
||||
}
|
||||
if (disabledDate.disabledType === 'range') {
|
||||
if (!disabledDate.dateRange || !Array.isArray(disabledDate.dateRange)) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < disabledDate.dateRange.length; i++) {
|
||||
if (date!.isSame(disabledDate.dateRange[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getDisabledTime(
|
||||
date: Parameters<DisabledTimeFunc>[0],
|
||||
range: Static<typeof BaseDatePickerPropsSpec.disabledTime>
|
||||
): DisabledTimeProps {
|
||||
const result: DisabledTimeProps = {};
|
||||
|
||||
Object.keys(range).forEach(disabledItem => {
|
||||
if (['disabledHours', 'disabledMinutes', 'disabledSeconds'].includes(disabledItem)) {
|
||||
const key = disabledItem as keyof typeof range;
|
||||
result[key] = () => range[key];
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getDisabledRangeTime(
|
||||
date: Parameters<DisabledTimeFunc>[0],
|
||||
type: 'start' | 'end',
|
||||
range: Static<typeof RangePickerPropsSpec.disabledRangeTime>
|
||||
): DisabledTimeProps {
|
||||
const result: DisabledTimeProps = {};
|
||||
|
||||
Object.keys(range).forEach(disabledItem => {
|
||||
if (['disabledHours', 'disabledMinutes', 'disabledSeconds'].includes(disabledItem)) {
|
||||
const key = disabledItem as keyof typeof range;
|
||||
const [start, end] = range[key];
|
||||
result[key] = () => (type === 'start' ? start || [] : end || []);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
210
packages/arco-lib/src/generated/types/DatePicker.ts
Normal file
210
packages/arco-lib/src/generated/types/DatePicker.ts
Normal file
@ -0,0 +1,210 @@
|
||||
import { Type } from '@sinclair/typebox';
|
||||
import { Category } from '../../constants/category';
|
||||
import { StringUnion } from '../../sunmao-helper';
|
||||
import { CoreWidgetName, CORE_VERSION } from '@sunmao-ui/editor-sdk'
|
||||
|
||||
const DisabledDateSpec = {
|
||||
disabledType: StringUnion(['before', 'after', 'range'], {
|
||||
title: 'Type'
|
||||
}),
|
||||
date: Type.Optional(Type.Any({
|
||||
title: 'Date',
|
||||
conditions: [
|
||||
{ key: 'disabledType', not: 'range' },
|
||||
]
|
||||
})),
|
||||
dateRange: Type.Optional(Type.Array(Type.Any(), {
|
||||
title: 'Date Range',
|
||||
widget: `${CORE_VERSION}/${CoreWidgetName.Expression}`,
|
||||
conditions: [
|
||||
{ key: 'disabledType', value: 'range' }
|
||||
]
|
||||
}))
|
||||
}
|
||||
|
||||
const DisabledTimeSpec = {
|
||||
disabledHours: Type.Array(Type.Number(), {
|
||||
title: 'Disabled Hours',
|
||||
widget: `${CORE_VERSION}/${CoreWidgetName.Expression}`,
|
||||
}),
|
||||
disabledMinutes: Type.Array(Type.Number(), {
|
||||
title: 'Disabled Minutes',
|
||||
widget: `${CORE_VERSION}/${CoreWidgetName.Expression}`,
|
||||
}),
|
||||
disabledSeconds: Type.Array(Type.Number(), {
|
||||
title: 'Disabled Seconds',
|
||||
widget: `${CORE_VERSION}/${CoreWidgetName.Expression}`,
|
||||
}),
|
||||
}
|
||||
|
||||
const DisabledTimeRangeItemSpec = Type.Array(Type.Number(), {
|
||||
widget: `${CORE_VERSION}/${CoreWidgetName.Expression}`,
|
||||
})
|
||||
|
||||
const DisabledTimeRangeSpec = {
|
||||
disabledHours: Type.Tuple([DisabledTimeRangeItemSpec, DisabledTimeRangeItemSpec], {
|
||||
title: 'Disabled Hours'
|
||||
}),
|
||||
disabledMinutes: Type.Tuple([DisabledTimeRangeItemSpec, DisabledTimeRangeItemSpec], {
|
||||
title: 'Disabled Minutes'
|
||||
}),
|
||||
disabledSeconds: Type.Tuple([DisabledTimeRangeItemSpec, DisabledTimeRangeItemSpec], {
|
||||
title: 'Disabled Seconds'
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const RangePickerPropsSpec = {
|
||||
disabledRangeTime: Type.Optional(Type.Object(DisabledTimeRangeSpec, {
|
||||
title: 'Disabled Range Time',
|
||||
category: Category.Behavior,
|
||||
weight: 95,
|
||||
description: 'Specify the disable time, you need to specify two arrays, 0 for the start time and 1 for the end time',
|
||||
conditions: [
|
||||
{
|
||||
and: [
|
||||
{
|
||||
key: 'showTime',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
key: 'range',
|
||||
value: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})),
|
||||
rangePlaceholder: Type.Optional(Type.Array(Type.String(), {
|
||||
title: 'Placeholder',
|
||||
category: Category.Basic,
|
||||
conditions: [
|
||||
{
|
||||
key: 'range',
|
||||
value: true
|
||||
}
|
||||
]
|
||||
})),
|
||||
rangeDisabled: Type.Tuple([Type.Boolean(), Type.Boolean()], {
|
||||
title: 'Disabled',
|
||||
category: Category.Behavior,
|
||||
widget: `${CORE_VERSION}/${CoreWidgetName.Expression}`,
|
||||
weight: 99,
|
||||
conditions: [
|
||||
{
|
||||
key: 'range',
|
||||
value: true
|
||||
}
|
||||
]
|
||||
}),
|
||||
clearRangeOnReselect: Type.Boolean({
|
||||
title: 'Clear Range On Reselect',
|
||||
description: 'When reselect the range, the previous range will be cleared for next selection',
|
||||
category: Category.Behavior,
|
||||
conditions: [
|
||||
{
|
||||
key: 'range',
|
||||
value: true
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
export const DatePickerPropsSpec = {
|
||||
range: Type.Boolean({
|
||||
title: 'Range',
|
||||
category: Category.Behavior,
|
||||
weight: 100
|
||||
}),
|
||||
type: StringUnion(['date', 'week', 'month', 'year', 'quarter'], {
|
||||
title: 'Type',
|
||||
category: Category.Basic
|
||||
}),
|
||||
defaultValue: Type.Any({
|
||||
title: 'Default Value',
|
||||
category: Category.Basic
|
||||
}),
|
||||
disabled: Type.Boolean({
|
||||
title: 'Disabled',
|
||||
category: Category.Behavior,
|
||||
weight: 99,
|
||||
conditions: [
|
||||
{
|
||||
key: 'range',
|
||||
value: false
|
||||
}
|
||||
]
|
||||
}),
|
||||
dayStartOfWeek: StringUnion([0, 1, 2, 3, 4, 5, 6], {
|
||||
title: 'Day Start Of Week',
|
||||
category: Category.Basic,
|
||||
conditions: [
|
||||
{
|
||||
key: 'type',
|
||||
value: 'default'
|
||||
}
|
||||
]
|
||||
}),
|
||||
position: StringUnion(['top', 'tl', 'tr', 'bottom', 'bl', 'br'], {
|
||||
title: 'Position',
|
||||
category: Category.Layout,
|
||||
}),
|
||||
placeholder: Type.String({
|
||||
title: 'Placeholder',
|
||||
category: Category.Basic,
|
||||
conditions: [
|
||||
{
|
||||
key: 'range',
|
||||
value: false
|
||||
}
|
||||
]
|
||||
}),
|
||||
allowClear: Type.Boolean({
|
||||
title: 'Allow Clear',
|
||||
category: Category.Behavior,
|
||||
weight: 98
|
||||
}),
|
||||
showTime: Type.Boolean({
|
||||
title: 'Show Time',
|
||||
category: Category.Behavior,
|
||||
weight: 97,
|
||||
}),
|
||||
disabledDate: Type.Object(DisabledDateSpec, {
|
||||
title: 'Disabled Date',
|
||||
category: Category.Behavior,
|
||||
description: "Specify a date and use before to disable dates before that date and after to disable dates after that date. Or disable certain dates by passing an array, which supports the use of dayjs, e.g. ['2022-5-23', '2022-5-26', '2022-5-29'].",
|
||||
widget: `${CORE_VERSION}/${CoreWidgetName.Popover}`,
|
||||
weight: 96
|
||||
}),
|
||||
disabledTime: Type.Object(DisabledTimeSpec, {
|
||||
title: 'Disabled Time',
|
||||
category: Category.Behavior,
|
||||
description: '',
|
||||
weight: 95,
|
||||
conditions: [
|
||||
{
|
||||
and: [
|
||||
{
|
||||
key: 'showTime',
|
||||
value: true
|
||||
},
|
||||
{
|
||||
key: 'range',
|
||||
value: false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}),
|
||||
panelOnly: Type.Boolean({
|
||||
title: 'Only Use Panel',
|
||||
category: Category.Behavior,
|
||||
weight: 0
|
||||
}),
|
||||
size: StringUnion(['mini', 'small', 'default', 'large'], {
|
||||
title: 'Size',
|
||||
category: Category.Layout,
|
||||
}),
|
||||
...RangePickerPropsSpec
|
||||
};
|
@ -37,6 +37,7 @@ import { FormControl } from './components/Form/FormControl';
|
||||
import { Descriptions } from './components/Descriptions';
|
||||
import { Row, Col } from './components/Grid'
|
||||
import { Slider } from './components/Slider';
|
||||
import { DatePicker } from './components/DatePicker'
|
||||
|
||||
import './style.css';
|
||||
|
||||
@ -79,7 +80,8 @@ export const components: SunmaoLib['components'] = [
|
||||
FormControl,
|
||||
Descriptions,
|
||||
Row, Col,
|
||||
Slider
|
||||
Slider,
|
||||
DatePicker
|
||||
];
|
||||
export const traits: SunmaoLib['traits'] = [];
|
||||
export const modules: SunmaoLib['modules'] = [];
|
||||
|
@ -8,7 +8,7 @@ export type IntoStringUnion<T> = {
|
||||
[K in keyof T]: T[K] extends string ? TLiteral<T[K]> : never;
|
||||
};
|
||||
|
||||
export function StringUnion<T extends string[]>(values: [...T], options?: any) {
|
||||
export function StringUnion<T extends string[] | number[]>(values: [...T], options?: any) {
|
||||
return Type.KeyOf(
|
||||
Type.Object(
|
||||
values.reduce((prev, cur) => {
|
||||
|
Loading…
Reference in New Issue
Block a user