diff --git a/src/Picker.tsx b/src/Picker.tsx index 89aee099d..5873b1352 100644 --- a/src/Picker.tsx +++ b/src/Picker.tsx @@ -30,6 +30,7 @@ import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil import usePickerInput from './hooks/usePickerInput'; import useTextValueMapping from './hooks/useTextValueMapping'; import useValueTexts from './hooks/useValueTexts'; +import useHoverPlaceholder from './hooks/useHoverPlaceholder'; export interface PickerRefConfig { focus: () => void; @@ -435,6 +436,12 @@ function InnerPicker(props: PickerProps) { }; const popupPlacement = direction === 'rtl' ? 'bottomRight' : 'bottomLeft'; + const [hoverPlaceholder, onEnter, onLeave] = useHoverPlaceholder(placeholder, text, { + formatList, + generateConfig, + locale, + }); + return ( (props: PickerProps) { onSelect: onContextSelect, open: mergedOpen, defaultOpenValue, + onDateMouseEnter: onEnter, + onDateMouseLeave: onLeave, }} > (props: PickerProps) { triggerTextChange(e.target.value); }} autoFocus={autoFocus} - placeholder={placeholder} + placeholder={hoverPlaceholder} ref={inputRef} title={text} {...inputProps} diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx index 3b232da18..31ba3e125 100644 --- a/src/RangePicker.tsx +++ b/src/RangePicker.tsx @@ -29,6 +29,7 @@ import getExtraFooter from './utils/getExtraFooter'; import getRanges from './utils/getRanges'; import useRangeViewDates from './hooks/useRangeViewDates'; import { DateRender } from './panels/DatePanel/DateBody'; +import useHoverPlaceholder from './hooks/useHoverPlaceholder'; function reorderValues( values: RangeValue, @@ -284,18 +285,6 @@ function InnerRangePicker(props: RangePickerProps) { }, }); - const [rangeHoverValue, setRangeHoverValue] = useState>(null); - - // ========================== Hover Range ========================== - const [hoverRangedValue, setHoverRangedValue] = useState>(null); - - const onDateMouseEnter = (date: DateType) => { - setHoverRangedValue(updateValues(selectedValue, date, mergedActivePickerIndex)); - }; - const onDateMouseLeave = () => { - setHoverRangedValue(updateValues(selectedValue, null, mergedActivePickerIndex)); - }; - // ============================= Modes ============================= const [mergedModes, setInnerModes] = useMergedState<[PanelMode, PanelMode]>([picker, picker], { value: mode, @@ -546,6 +535,49 @@ function InnerRangePicker(props: RangePickerProps) { onTextChange: newText => onTextChange(newText, 1), }); + const [rangeHoverValue, setRangeHoverValue] = useState>(null); + + // ========================== Hover Range ========================== + const [hoverRangedValue, setHoverRangedValue] = useState>(null); + + const [startPlaceholder, onStartEnter, onStartLeave] = useHoverPlaceholder( + getValue(placeholder, 0) || '', + startText, + { + formatList, + generateConfig, + locale, + }, + ); + + const [endPlaceholder, onEndEnter, onEndLeave] = useHoverPlaceholder( + getValue(placeholder, 1) || '', + endText, + { + formatList, + generateConfig, + locale, + }, + ); + + const onDateMouseEnter = (date: DateType) => { + setHoverRangedValue(updateValues(selectedValue, date, mergedActivePickerIndex)); + if (mergedActivePickerIndex === 0) { + onStartEnter(date); + } else { + onEndEnter(date); + } + }; + + const onDateMouseLeave = () => { + setHoverRangedValue(updateValues(selectedValue, null, mergedActivePickerIndex)); + if (mergedActivePickerIndex === 0) { + onStartLeave(null); + } else { + onEndLeave(null); + } + }; + // ============================= Input ============================= const getSharedInputHookProps = (index: 0 | 1, resetText: () => void) => ({ blurToCancel: needConfirmButton, @@ -1040,7 +1072,7 @@ function InnerRangePicker(props: RangePickerProps) { triggerStartTextChange(e.target.value); }} autoFocus={autoFocus} - placeholder={getValue(placeholder, 0) || ''} + placeholder={startPlaceholder} ref={startInputRef} {...startInputProps} {...inputSharedProps} @@ -1063,7 +1095,7 @@ function InnerRangePicker(props: RangePickerProps) { onChange={e => { triggerEndTextChange(e.target.value); }} - placeholder={getValue(placeholder, 1) || ''} + placeholder={endPlaceholder} ref={endInputRef} {...endInputProps} {...inputSharedProps} diff --git a/src/generate/dayjs.ts b/src/generate/dayjs.ts index bdaf2ce0d..160fc0a6b 100644 --- a/src/generate/dayjs.ts +++ b/src/generate/dayjs.ts @@ -35,16 +35,13 @@ const localeMap: IlocaleMapObject = { const parseLocale = (locale: string) => { const mapLocale = localeMap[locale]; - return mapLocale || locale.split('_')[0]; + return (mapLocale || locale.split('_')[0]) as LocalePresetType; }; const parseNoMatchNotice = () => { /* istanbul ignore next */ - noteOnce( - false, - 'Not match any format. Please help to fire a issue about this.', - ); -} + noteOnce(false, 'Not match any format. Please help to fire a issue about this.'); +}; const generateConfig: GenerateConfig = { // get @@ -92,24 +89,26 @@ const generateConfig: GenerateConfig = { .locale(parseLocale(locale)) .localeData() .monthsShort(), - format: (locale, date, format) => - date.locale(parseLocale(locale)).format(format), + format: (locale, date, format) => date.locale(parseLocale(locale)).format(format), parse: (locale, text, formats) => { - const localeStr = parseLocale(locale) + const localeStr = parseLocale(locale); for (let i = 0; i < formats.length; i += 1) { const format = formats[i]; const formatText = text; - if (format.includes('wo') || format.includes('Wo')) { // parse Wo - const year = formatText.split('-')[0] - const weekStr = formatText.split('-')[1] - const firstWeek = dayjs(year, 'YYYY').startOf('year').locale(localeStr) + if (format.includes('wo') || format.includes('Wo')) { + // parse Wo + const year = formatText.split('-')[0]; + const weekStr = formatText.split('-')[1]; + const firstWeek = dayjs(year, 'YYYY') + .startOf('year') + .locale(localeStr); for (let j = 0; j <= 52; j += 1) { - const nextWeek = firstWeek.add(j, 'week') + const nextWeek = firstWeek.add(j, 'week'); if (nextWeek.format('Wo') === weekStr) { - return nextWeek + return nextWeek; } } - parseNoMatchNotice() + parseNoMatchNotice(); return null; } const date = dayjs(formatText, format).locale(localeStr); diff --git a/src/hooks/useHoverPlaceholder.ts b/src/hooks/useHoverPlaceholder.ts new file mode 100644 index 000000000..cd2fcff60 --- /dev/null +++ b/src/hooks/useHoverPlaceholder.ts @@ -0,0 +1,28 @@ +import { useState } from 'react'; +import useValueTexts, { ValueTextConfig } from './useValueTexts'; + +export default function useHoverPlaceholder( + placeholder: string, + text: string, + { formatList, generateConfig, locale }: ValueTextConfig, +): [string, (date: DateType) => void, (date: DateType) => void] { + const [value, setValue] = useState(null); + + const [valueTexts] = useValueTexts(value, { + formatList, + generateConfig, + locale, + }); + + function onEnter(date: DateType) { + if (!text) { + setValue(date); + } + } + + function onLeave() { + setValue(null); + } + + return [(valueTexts && valueTexts[0]) || placeholder, onEnter, onLeave]; +} diff --git a/src/hooks/useValueTexts.ts b/src/hooks/useValueTexts.ts index 5aa7de037..8a88fb010 100644 --- a/src/hooks/useValueTexts.ts +++ b/src/hooks/useValueTexts.ts @@ -3,7 +3,7 @@ import useMemo from 'rc-util/lib/hooks/useMemo'; import { GenerateConfig } from '../generate'; import { Locale } from '../interface'; -interface ValueTextConfig { +export interface ValueTextConfig { formatList: string[]; generateConfig: GenerateConfig; locale: Locale; diff --git a/tests/picker.spec.tsx b/tests/picker.spec.tsx index b1f603cf9..4544b4125 100644 --- a/tests/picker.spec.tsx +++ b/tests/picker.spec.tsx @@ -729,4 +729,34 @@ describe('Picker.Basic', () => { const wrapper = mount(

Light

} />); expect(wrapper.render()).toMatchSnapshot(); }); + + describe('hover placeholder', () => { + const placeholder = 'custom placeholder'; + it('placeholder should be origin value when input value is not empty', () => { + const wrapper = mount( + , + ); + const cell = wrapper.findCell(24); + cell.simulate('mouseEnter'); + expect(wrapper.find('input').prop('placeholder')).toBe(placeholder); + }); + + it('placeholder should be target date when input value is empty', () => { + const wrapper = mount( + , + ); + wrapper.openPicker(); + wrapper.find('input').simulate('change', { + target: { + value: '', + }, + }); + const cell = wrapper.findCell(24); + cell.simulate('mouseEnter'); + expect(wrapper.find('input').prop('placeholder')).toBe('2020-07-24'); + cell.simulate('mouseLeave'); + expect(wrapper.find('input').prop('placeholder')).toBe(placeholder); + wrapper.closePicker(); + }); + }); }); diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index 78919ce47..6ca193e76 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -1426,4 +1426,79 @@ describe('Picker.Range', () => { ); }); }); + + describe('hover placeholder', () => { + const placeholder: [string, string] = ['custom placeholder1', 'custom placeholder2']; + const defaultValue: [Moment, Moment] = [getMoment('2020-07-22'), getMoment('2020-08-22')]; + + it('placeholder should be target date when input value is empty', () => { + const wrapper = mount( + , + ); + wrapper.openPicker(0); + wrapper.inputValue(''); + // left + const leftCell = wrapper.findCell('24'); + leftCell.simulate('mouseEnter'); + expect( + wrapper + .find('input') + .first() + .prop('placeholder'), + ).toBe('2020-07-24'); + expect( + wrapper + .find('input') + .last() + .prop('placeholder'), + ).toBe(placeholder[1]); + leftCell.simulate('mouseLeave'); + expect( + wrapper + .find('input') + .first() + .prop('placeholder'), + ).toBe(placeholder[0]); + expect( + wrapper + .find('input') + .last() + .prop('placeholder'), + ).toBe(placeholder[1]); + wrapper.closePicker(0); + + // right + wrapper.openPicker(1); + wrapper.inputValue('', 1); + + const rightCell = wrapper.findCell('24', 1); + rightCell.simulate('mouseEnter'); + expect( + wrapper + .find('input') + .last() + .prop('placeholder'), + ).toBe('2020-08-24'); + expect( + wrapper + .find('input') + .first() + .prop('placeholder'), + ).toBe(placeholder[0]); + rightCell.simulate('mouseLeave'); + expect( + wrapper + .find('input') + .first() + .prop('placeholder'), + ).toBe(placeholder[0]); + expect( + wrapper + .find('input') + .last() + .prop('placeholder'), + ).toBe(placeholder[1]); + wrapper.closePicker(1); + }); + }); });