Skip to content

Commit f547043

Browse files
authored
feat: supprt custom format (#142)
* ✨ feat: supprt custom format * test: add test case * chore: update picker input readonly
1 parent c7e1123 commit f547043

File tree

8 files changed

+111
-23
lines changed

8 files changed

+111
-23
lines changed

src/Picker.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ import PickerPanel, {
2222
PickerPanelTimeProps,
2323
} from './PickerPanel';
2424
import PickerTrigger from './PickerTrigger';
25-
import { isEqual } from './utils/dateUtil';
25+
import { formatValue, isEqual } from './utils/dateUtil';
2626
import getDataOrAriaProps, { toArray } from './utils/miscUtil';
2727
import PanelContext, { ContextOperationRefProps } from './PanelContext';
28-
import { PickerMode } from './interface';
28+
import { CustomFormat, PickerMode } from './interface';
2929
import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil';
3030
import usePickerInput from './hooks/usePickerInput';
3131
import useTextValueMapping from './hooks/useTextValueMapping';
@@ -54,7 +54,7 @@ export interface PickerSharedProps<DateType> extends React.AriaAttributes {
5454
id?: string;
5555

5656
// Value
57-
format?: string | string[];
57+
format?: string | CustomFormat<DateType> | Array<string | CustomFormat<DateType>>;
5858

5959
// Render
6060
suffixIcon?: React.ReactNode;
@@ -225,7 +225,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
225225
const [text, triggerTextChange, resetText] = useTextValueMapping({
226226
valueTexts,
227227
onTextChange: newText => {
228-
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList);
228+
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList as string[]);
229229
if (inputDate && (!disabledDate || !disabledDate(inputDate))) {
230230
setSelectedValue(inputDate);
231231
}
@@ -240,7 +240,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
240240
if (onChange && !isEqual(generateConfig, mergedValue, newValue)) {
241241
onChange(
242242
newValue,
243-
newValue ? generateConfig.locale.format(locale.locale, newValue, formatList[0]) : '',
243+
newValue ? formatValue(newValue, { generateConfig, locale, format: formatList[0] }) : '',
244244
);
245245
}
246246
};
@@ -491,7 +491,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
491491
id={id}
492492
tabIndex={tabIndex}
493493
disabled={disabled}
494-
readOnly={inputReadOnly || !typing}
494+
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !typing}
495495
value={hoverValue || text}
496496
onChange={e => {
497497
triggerTextChange(e.target.value);
@@ -501,7 +501,7 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
501501
ref={inputRef}
502502
title={text}
503503
{...inputProps}
504-
size={getInputSize(picker, formatList[0])}
504+
size={getInputSize(picker, formatList[0], generateConfig)}
505505
{...getDataOrAriaProps(props)}
506506
autoComplete={autoComplete}
507507
/>

src/RangePicker.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
isSameDate,
1919
isSameWeek,
2020
isSameQuarter,
21+
formatValue,
2122
} from './utils/dateUtil';
2223
import useValueTexts from './hooks/useValueTexts';
2324
import useTextValueMapping from './hooks/useTextValueMapping';
@@ -229,7 +230,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
229230
const endInputRef = useRef<HTMLInputElement>(null);
230231

231232
// ============================= Misc ==============================
232-
const formatList = toArray(getDefaultFormat(format, picker, showTime, use12Hours));
233+
const formatList = toArray(getDefaultFormat<DateType>(format, picker, showTime, use12Hours));
233234

234235
// Active picker
235236
const [mergedActivePickerIndex, setMergedActivePickerIndex] = useMergedState<0 | 1>(0, {
@@ -425,11 +426,11 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
425426

426427
const startStr =
427428
values && values[0]
428-
? generateConfig.locale.format(locale.locale, values[0], formatList[0])
429+
? formatValue(values[0], { generateConfig, locale, format: formatList[0] })
429430
: '';
430431
const endStr =
431432
values && values[1]
432-
? generateConfig.locale.format(locale.locale, values[1], formatList[0])
433+
? formatValue(values[1], { generateConfig, locale, format: formatList[0] })
433434
: '';
434435

435436
if (onCalendarChange) {
@@ -515,7 +516,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
515516
);
516517

517518
const onTextChange = (newText: string, index: 0 | 1) => {
518-
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList);
519+
const inputDate = generateConfig.locale.parse(locale.locale, newText, formatList as string[]);
519520

520521
const disabledFunc = index === 0 ? disabledStartDate : disabledEndDate;
521522

@@ -989,7 +990,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
989990
}
990991

991992
const inputSharedProps = {
992-
size: getInputSize(picker, formatList[0]),
993+
size: getInputSize(picker, formatList[0], generateConfig),
993994
};
994995

995996
let activeBarLeft: number = 0;
@@ -1068,7 +1069,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
10681069
<input
10691070
id={id}
10701071
disabled={mergedDisabled[0]}
1071-
readOnly={inputReadOnly || !startTyping}
1072+
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !startTyping}
10721073
value={startHoverValue || startText}
10731074
onChange={e => {
10741075
triggerStartTextChange(e.target.value);
@@ -1093,7 +1094,7 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
10931094
>
10941095
<input
10951096
disabled={mergedDisabled[1]}
1096-
readOnly={inputReadOnly || !endTyping}
1097+
readOnly={inputReadOnly || typeof formatList[0] === 'function' || !endTyping}
10971098
value={endHoverValue || endText}
10981099
onChange={e => {
10991100
triggerEndTextChange(e.target.value);

src/hooks/useValueTexts.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import shallowEqual from 'shallowequal';
22
import useMemo from 'rc-util/lib/hooks/useMemo';
33
import { GenerateConfig } from '../generate';
4-
import { Locale } from '../interface';
4+
import { CustomFormat, Locale } from '../interface';
5+
import { formatValue } from '../utils/dateUtil';
56

67
export interface ValueTextConfig<DateType> {
7-
formatList: string[];
8+
formatList: Array<string | CustomFormat<DateType>>;
89
generateConfig: GenerateConfig<DateType>;
910
locale: Locale;
1011
}
@@ -25,7 +26,7 @@ export default function useValueTexts<DateType>(
2526

2627
for (let i = 0; i < formatList.length; i += 1) {
2728
const format = formatList[i];
28-
const formatStr = generateConfig.locale.format(locale.locale, value, format);
29+
const formatStr = formatValue(value, { generateConfig, locale, format });
2930
fullValueTexts.push(formatStr);
3031

3132
if (i === 0) {

src/interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,5 @@ export type RangeList = {
105105
onMouseEnter: () => void;
106106
onMouseLeave: () => void;
107107
}[];
108+
109+
export type CustomFormat<DateType> = (value: DateType) => string;

src/utils/dateUtil.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GenerateConfig } from '../generate';
2-
import { NullableDateType, PickerMode } from '../interface';
2+
import { NullableDateType, PickerMode, Locale, CustomFormat } from '../interface';
33

44
export const WEEK_DAY_COUNT = 7;
55

@@ -192,3 +192,20 @@ export function getClosingViewDate<DateType>(
192192
return generateConfig.addMonth(viewDate, offset);
193193
}
194194
}
195+
196+
export function formatValue<DateType>(
197+
value: DateType,
198+
{
199+
generateConfig,
200+
locale,
201+
format,
202+
}: {
203+
generateConfig: GenerateConfig<DateType>;
204+
locale: Locale;
205+
format: string | CustomFormat<DateType>;
206+
},
207+
) {
208+
return typeof format === 'function'
209+
? format(value)
210+
: generateConfig.locale.format(locale.locale, value, format);
211+
}

src/utils/uiUtil.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import KeyCode from 'rc-util/lib/KeyCode';
2-
import { PanelMode, PickerMode } from '../interface';
2+
import { GenerateConfig } from '../generate';
3+
import { CustomFormat, PanelMode, PickerMode } from '../interface';
34

45
const scrollIds = new Map<HTMLElement, number>();
56

@@ -120,8 +121,8 @@ export function createKeyDownHandler(
120121
}
121122

122123
// ===================== Format =====================
123-
export function getDefaultFormat(
124-
format: string | string[] | undefined,
124+
export function getDefaultFormat<DateType>(
125+
format: string | CustomFormat<DateType> | Array<string | CustomFormat<DateType>> | undefined,
125126
picker: PickerMode | undefined,
126127
showTime: boolean | object | undefined,
127128
use12Hours: boolean | undefined,
@@ -157,9 +158,15 @@ export function getDefaultFormat(
157158
return mergedFormat;
158159
}
159160

160-
export function getInputSize(picker: PickerMode | undefined, format: string) {
161+
export function getInputSize<DateType>(
162+
picker: PickerMode | undefined,
163+
format: string | CustomFormat<DateType>,
164+
generateConfig: GenerateConfig<DateType>,
165+
) {
161166
const defaultSize = picker === 'time' ? 8 : 10;
162-
return Math.max(defaultSize, format.length) + 2;
167+
const length =
168+
typeof format === 'function' ? format(generateConfig.getNow()).length : format.length;
169+
return Math.max(defaultSize, length) + 2;
163170
}
164171

165172
// ===================== Window =====================

tests/picker.spec.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { act } from 'react-dom/test-utils';
44
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
55
import KeyCode from 'rc-util/lib/KeyCode';
66
import { resetWarned } from 'rc-util/lib/warning';
7+
import { Moment } from 'moment';
78
import { PanelMode, PickerMode } from '../src/interface';
89
import { mount, getMoment, isSame, MomentPicker } from './util/commonUtil';
910

@@ -725,6 +726,20 @@ describe('Picker.Basic', () => {
725726
expect(wrapper.find('input').prop('value')).toEqual('20000101');
726727
});
727728

729+
it('custom format', () => {
730+
const wrapper = mount(
731+
<MomentPicker
732+
defaultValue={getMoment('2020-09-17')}
733+
format={[(val: Moment) => `custom format:${val.format('YYYYMMDD')}`, 'YYYY-MM-DD']}
734+
/>,
735+
);
736+
expect(wrapper.find('input').prop('readOnly')).toBeTruthy();
737+
wrapper.openPicker();
738+
wrapper.selectCell(24);
739+
wrapper.closePicker();
740+
expect(wrapper.find('input').prop('value')).toEqual('custom format:20200924');
741+
});
742+
728743
it('panelRender', () => {
729744
const wrapper = mount(<MomentPicker open panelRender={() => <h1>Light</h1>} />);
730745
expect(wrapper.render()).toMatchSnapshot();

tests/range.spec.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,51 @@ describe('Picker.Range', () => {
12531253
).toEqual('19901128');
12541254
});
12551255

1256+
it('custom format', () => {
1257+
const wrapper = mount(
1258+
<MomentRangePicker
1259+
format={[(val: Moment) => `custom format:${val.format('YYYYMMDD')}`, 'YYYY-MM-DD']}
1260+
defaultValue={[getMoment('2020-09-17'), getMoment('2020-10-17')]}
1261+
/>,
1262+
);
1263+
1264+
expect(
1265+
wrapper
1266+
.find('input')
1267+
.first()
1268+
.prop('readOnly'),
1269+
).toBeTruthy();
1270+
expect(
1271+
wrapper
1272+
.find('input')
1273+
.last()
1274+
.prop('readOnly'),
1275+
).toBeTruthy();
1276+
1277+
// Start date
1278+
wrapper.openPicker();
1279+
wrapper.selectCell(24);
1280+
wrapper.closePicker();
1281+
1282+
// end date
1283+
wrapper.openPicker(1);
1284+
wrapper.selectCell(24, 1);
1285+
wrapper.closePicker(1);
1286+
1287+
expect(
1288+
wrapper
1289+
.find('input')
1290+
.first()
1291+
.prop('value'),
1292+
).toEqual('custom format:20200924');
1293+
expect(
1294+
wrapper
1295+
.find('input')
1296+
.last()
1297+
.prop('value'),
1298+
).toEqual('custom format:20201024');
1299+
});
1300+
12561301
describe('auto open', () => {
12571302
it('empty: start -> end -> close', () => {
12581303
const wrapper = mount(<MomentRangePicker />);

0 commit comments

Comments
 (0)