feat: add datepicker component

This commit is contained in:
xzdry 2022-05-24 12:12:07 +08:00
parent 961f8c342e
commit 8ae10c38df
4 changed files with 423 additions and 2 deletions

View 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;
}

View 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
};

View File

@ -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'] = [];

View File

@ -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) => {