refactor: date

pull/4499/head
tangjinzhou 2021-07-20 17:32:49 +08:00
parent 16fc2a10a9
commit 53f3c6e1a5
10 changed files with 1506 additions and 1132 deletions

View File

@ -1,3 +1,8 @@
export type FocusEventHandler = (e: FocusEvent) => void; export type FocusEventHandler = (e: FocusEvent) => void;
export type MouseEventHandler = (e: MouseEvent) => void; export type MouseEventHandler = (e: MouseEvent) => void;
export type KeyboardEventHandler = (e: KeyboardEvent) => void; export type KeyboardEventHandler = (e: KeyboardEvent) => void;
export type ChangeEvent = Event & {
target: {
value?: string | undefined;
};
};

View File

@ -20,8 +20,7 @@ import PickerPanel from './PickerPanel';
import PickerTrigger from './PickerTrigger'; import PickerTrigger from './PickerTrigger';
import { formatValue, isEqual, parseValue } from './utils/dateUtil'; import { formatValue, isEqual, parseValue } from './utils/dateUtil';
import getDataOrAriaProps, { toArray } from './utils/miscUtil'; import getDataOrAriaProps, { toArray } from './utils/miscUtil';
import type { ContextOperationRefProps } from './PanelContext'; import { ContextOperationRefProps, useProvidePanel } from './PanelContext';
import PanelContext from './PanelContext';
import type { CustomFormat, PickerMode } from './interface'; import type { CustomFormat, PickerMode } from './interface';
import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil'; import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil';
import usePickerInput from './hooks/usePickerInput'; import usePickerInput from './hooks/usePickerInput';
@ -30,20 +29,21 @@ import useValueTexts from './hooks/useValueTexts';
import useHoverValue from './hooks/useHoverValue'; import useHoverValue from './hooks/useHoverValue';
import { import {
computed, computed,
createVNode,
CSSProperties, CSSProperties,
defineComponent, defineComponent,
HtmlHTMLAttributes, HtmlHTMLAttributes,
ref, ref,
Ref, Ref,
toRef, toRef,
toRefs, watch,
} from 'vue'; } from 'vue';
import { FocusEventHandler, MouseEventHandler } from '../_util/EventInterface'; import { ChangeEvent, FocusEventHandler, MouseEventHandler } from '../_util/EventInterface';
import { VueNode } from '../_util/type'; import { VueNode } from '../_util/type';
import { AlignType } from '../vc-align/interface'; import { AlignType } from '../vc-align/interface';
import useMergedState from '../_util/hooks/useMergedState'; import useMergedState from '../_util/hooks/useMergedState';
import { locale } from 'dayjs';
import { warning } from '../vc-util/warning'; import { warning } from '../vc-util/warning';
import classNames from '../_util/classNames';
export type PickerRefConfig = { export type PickerRefConfig = {
focus: () => void; focus: () => void;
@ -92,10 +92,6 @@ export type PickerSharedProps<DateType> = {
onContextMenu?: MouseEventHandler; onContextMenu?: MouseEventHandler;
onKeyDown?: (event: KeyboardEvent, preventDefault: () => void) => void; onKeyDown?: (event: KeyboardEvent, preventDefault: () => void) => void;
// Internal
/** @private Internal usage, do not use in production mode!!! */
pickerRef?: Ref<PickerRefConfig>;
// WAI-ARIA // WAI-ARIA
role?: string; role?: string;
name?: string; name?: string;
@ -169,7 +165,6 @@ function Picker<DateType>() {
'disabledDate', 'disabledDate',
'placeholder', 'placeholder',
'getPopupContainer', 'getPopupContainer',
'pickerRef',
'panelRender', 'panelRender',
'onChange', 'onChange',
'onOpenChange', 'onOpenChange',
@ -196,7 +191,7 @@ function Picker<DateType>() {
'superNextIcon', 'superNextIcon',
'panelRender', 'panelRender',
], ],
setup(props, { slots, attrs, expose }) { setup(props, { attrs, expose }) {
const inputRef = ref(null); const inputRef = ref(null);
const needConfirmButton = computed( const needConfirmButton = computed(
() => (props.picker === 'date' && !!props.showTime) || props.picker === 'time', () => (props.picker === 'date' && !!props.showTime) || props.picker === 'time',
@ -242,13 +237,11 @@ function Picker<DateType>() {
}); });
// ============================= Text ============================== // ============================= Text ==============================
const texts = useValueTexts(selectedValue, { const [valueTexts, firstValueText] = useValueTexts(selectedValue, {
formatList, formatList,
generateConfig: toRef(props, 'generateConfig'), generateConfig: toRef(props, 'generateConfig'),
locale: toRef(props, 'locale'), locale: toRef(props, 'locale'),
}); });
const valueTexts = computed(() => texts.value[0]);
const firstValueText = computed(() => texts.value[1]);
const [text, triggerTextChange, resetText] = useTextValueMapping({ const [text, triggerTextChange, resetText] = useTextValueMapping({
valueTexts, valueTexts,
@ -351,11 +344,256 @@ function Picker<DateType>() {
}, },
}); });
// ============================= Sync ==============================
// Close should sync back with text value
watch([mergedOpen, valueTexts], () => {
if (!mergedOpen.value) {
setSelectedValue(mergedValue.value);
if (!valueTexts.value.length || valueTexts.value[0] === '') {
triggerTextChange('');
} else if (firstValueText.value !== text.value) {
resetText();
}
}
});
// Change picker should sync back with text value
watch(
() => props.picker,
() => {
if (!mergedOpen.value) {
resetText();
}
},
);
// Sync innerValue with control mode
watch(mergedValue, () => {
// Sync select value
setSelectedValue(mergedValue.value);
});
const [hoverValue, onEnter, onLeave] = useHoverValue(text, {
formatList,
generateConfig: toRef(props, 'generateConfig'),
locale: toRef(props, 'locale'),
});
const onContextSelect = (date: DateType, type: 'key' | 'mouse' | 'submit') => {
if (type === 'submit' || (type !== 'key' && !needConfirmButton.value)) {
// triggerChange will also update selected values
triggerChange(date);
triggerOpen(false);
}
};
useProvidePanel({
operationRef,
hideHeader: computed(() => props.picker === 'time'),
panelRef: panelDivRef,
onSelect: onContextSelect,
open: mergedOpen,
defaultOpenValue: toRef(props, 'defaultOpenValue'),
onDateMouseEnter: onEnter,
onDateMouseLeave: onLeave,
});
expose({
focus: () => {
if (inputRef.value) {
inputRef.value.focus();
}
},
blur: () => {
if (inputRef.value) {
inputRef.value.blur();
}
},
});
return () => { return () => {
return null; const {
prefixCls = 'rc-picker',
id,
tabindex,
dropdownClassName,
dropdownAlign,
popupStyle,
transitionName,
generateConfig,
locale,
inputReadOnly,
allowClear,
autofocus,
picker = 'date',
defaultOpenValue,
suffixIcon,
clearIcon,
disabled,
placeholder,
getPopupContainer,
panelRender,
onMouseDown,
onMouseEnter,
onMouseLeave,
onContextMenu,
onClick,
onSelect,
direction,
autocomplete = 'off',
} = props;
// ============================= Panel =============================
const panelProps = {
// Remove `picker` & `format` here since TimePicker is little different with other panel
...(props as Omit<MergedPickerProps<DateType>, 'picker' | 'format'>),
pickerValue: undefined,
onPickerValueChange: undefined,
onChange: null,
};
let panelNode: VueNode = (
<PickerPanel
{...panelProps}
generateConfig={generateConfig}
class={classNames({
[`${prefixCls}-panel-focused`]: !typing.value,
})}
value={selectedValue.value}
locale={locale}
tabindex={-1}
onSelect={date => {
onSelect?.(date);
setSelectedValue(date);
}}
direction={direction}
onPanelChange={(viewDate, mode) => {
const { onPanelChange } = props;
onLeave(true);
onPanelChange?.(viewDate, mode);
}}
/>
);
if (panelRender) {
panelNode = panelRender(panelNode);
}
const panel = (
<div
class={`${prefixCls}-panel-container`}
onMousedown={e => {
e.preventDefault();
}}
>
{panelNode}
</div>
);
let suffixNode: VueNode;
if (suffixIcon) {
suffixNode = <span class={`${prefixCls}-suffix`}>{suffixIcon}</span>;
}
let clearNode: VueNode;
if (allowClear && mergedValue.value && !disabled) {
clearNode = (
<span
onMousedown={e => {
e.preventDefault();
e.stopPropagation();
}}
onMouseup={e => {
e.preventDefault();
e.stopPropagation();
triggerChange(null);
triggerOpen(false);
}}
class={`${prefixCls}-clear`}
role="button"
>
{clearIcon || <span class={`${prefixCls}-clear-btn`} />}
</span>
);
}
// ============================ Warning ============================
if (process.env.NODE_ENV !== 'production') {
warning(
!defaultOpenValue,
'`defaultOpenValue` may confuse user for the current value status. Please use `defaultValue` instead.',
);
}
// ============================ Return =============================
const popupPlacement = direction === 'rtl' ? 'bottomRight' : 'bottomLeft';
return (
<PickerTrigger
visible={mergedOpen.value}
popupElement={panel}
popupStyle={popupStyle}
prefixCls={prefixCls}
dropdownClassName={dropdownClassName}
dropdownAlign={dropdownAlign}
getPopupContainer={getPopupContainer}
transitionName={transitionName}
popupPlacement={popupPlacement}
direction={direction}
>
<div
class={classNames(prefixCls, attrs.class, {
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-focused`]: focused,
[`${prefixCls}-rtl`]: direction === 'rtl',
})}
style={attrs.style}
onMousedown={onMouseDown}
onMouseup={onInternalMouseUp}
onMouseenter={onMouseEnter}
onMouseleave={onMouseLeave}
onContextmenu={onContextMenu}
onClick={onClick}
>
<div
class={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-placeholder`]: !!hoverValue.value,
})}
ref={inputDivRef}
>
<input
id={id}
tabindex={tabindex}
disabled={disabled}
readonly={
inputReadOnly || typeof formatList.value[0] === 'function' || !typing.value
}
value={hoverValue || text}
onChange={(e: ChangeEvent) => {
triggerTextChange(e.target.value);
}}
autofocus={autofocus}
placeholder={placeholder}
ref={inputRef}
title={text.value}
{...inputProps.value}
size={getInputSize(picker, formatList.value[0], generateConfig)}
{...getDataOrAriaProps(props)}
autocomplete={autocomplete}
/>
{suffixNode}
{clearNode}
</div>
</div>
</PickerTrigger>
);
}; };
}, },
}); });
} }
export default Picker(); const InterPicker = Picker<any>();
export default <DateType extends any>(props: MergedPickerProps<DateType>, { slots }): JSX.Element =>
createVNode(InterPicker, props, slots);

View File

@ -33,7 +33,16 @@ import getExtraFooter from './utils/getExtraFooter';
import getRanges from './utils/getRanges'; import getRanges from './utils/getRanges';
import { getLowerBoundTime, setDateTime, setTime } from './utils/timeUtil'; import { getLowerBoundTime, setDateTime, setTime } from './utils/timeUtil';
import { VueNode } from '../_util/type'; import { VueNode } from '../_util/type';
import { computed, defineComponent, ref, toRef, watch, watchEffect } from 'vue'; import {
computed,
createVNode,
defineComponent,
HTMLAttributes,
ref,
toRef,
watch,
watchEffect,
} from 'vue';
import useMergedState from '../_util/hooks/useMergedState'; import useMergedState from '../_util/hooks/useMergedState';
import { warning } from '../vc-util/warning'; import { warning } from '../vc-util/warning';
import KeyCode from '../_util/KeyCode'; import KeyCode from '../_util/KeyCode';
@ -83,7 +92,7 @@ export type PickerPanelSharedProps<DateType> = {
/** @private Internal usage. Do not use in your production env */ /** @private Internal usage. Do not use in your production env */
components?: Components; components?: Components;
}; } & HTMLAttributes;
export type PickerPanelBaseProps<DateType> = { export type PickerPanelBaseProps<DateType> = {
picker: Exclude<PickerMode, 'date' | 'time'>; picker: Exclude<PickerMode, 'date' | 'time'>;
@ -598,5 +607,6 @@ function PickerPanel<DateType>() {
}, },
}); });
} }
const InterPickerPanel = PickerPanel<any>();
export default PickerPanel(); export default <DateType extends any>(props: MergedPickerPanelProps<DateType>): JSX.Element =>
createVNode(InterPickerPanel, props);

View File

@ -47,7 +47,6 @@ export type PickerTriggerProps = {
visible: boolean; visible: boolean;
popupElement: VueNode; popupElement: VueNode;
popupStyle?: CSSProperties; popupStyle?: CSSProperties;
children: VueNode;
dropdownClassName?: string; dropdownClassName?: string;
transitionName?: string; transitionName?: string;
getPopupContainer?: (node: HTMLElement) => HTMLElement; getPopupContainer?: (node: HTMLElement) => HTMLElement;

View File

@ -1,4 +1,14 @@
import { inject, InjectionKey, provide, Ref } from 'vue'; import {
defineComponent,
inject,
InjectionKey,
PropType,
provide,
ref,
Ref,
toRef,
watch,
} from 'vue';
import type { NullableDateType, RangeValue } from './interface'; import type { NullableDateType, RangeValue } from './interface';
export type RangeContextProps = { export type RangeContextProps = {
@ -12,6 +22,17 @@ export type RangeContextProps = {
panelPosition?: Ref<'left' | 'right' | false>; panelPosition?: Ref<'left' | 'right' | false>;
}; };
type RangeContextProviderValue = {
/**
* Set displayed range value style.
* Panel only has one value, this is only style effect.
*/
rangedValue?: [NullableDateType<any>, NullableDateType<any>] | null;
hoverRangedValue?: RangeValue<any>;
inRange?: boolean;
panelPosition?: 'left' | 'right' | false;
};
const RangeContextKey: InjectionKey<RangeContextProps> = Symbol('RangeContextProps'); const RangeContextKey: InjectionKey<RangeContextProps> = Symbol('RangeContextProps');
export const useProvideRange = (props: RangeContextProps) => { export const useProvideRange = (props: RangeContextProps) => {
@ -22,4 +43,36 @@ export const useInjectRange = () => {
return inject(RangeContextKey); return inject(RangeContextKey);
}; };
export const RangeContextProvider = defineComponent({
name: 'PanelContextProvider',
inheritAttrs: false,
props: {
value: {
type: Object as PropType<RangeContextProviderValue>,
default: () => ({} as RangeContextProviderValue),
},
},
setup(props, { slots }) {
const value: RangeContextProps = {
rangedValue: ref(props.value.rangedValue),
hoverRangedValue: ref(props.value.hoverRangedValue),
inRange: ref(props.value.inRange),
panelPosition: ref(props.value.panelPosition),
};
useProvideRange(value);
toRef;
watch(
() => props.value,
() => {
Object.keys(props.value).forEach(key => {
if (value[key]) {
value[key].value = props.value[key];
}
});
},
);
return () => slots.default?.();
},
});
export default RangeContextKey; export default RangeContextKey;

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,31 @@
import { useState, useEffect, useRef } from 'react'; import type { ComputedRef, Ref, UnwrapRef } from 'vue';
import { ref, onBeforeUnmount, watch } from 'vue';
import type { ValueTextConfig } from './useValueTexts'; import type { ValueTextConfig } from './useValueTexts';
import useValueTexts from './useValueTexts'; import useValueTexts from './useValueTexts';
export default function useHoverValue<DateType>( export default function useHoverValue<DateType>(
valueText: string, valueText: Ref<string>,
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>, { formatList, generateConfig, locale }: ValueTextConfig<DateType>,
): [string, (date: DateType) => void, (immediately?: boolean) => void] { ): [ComputedRef<string>, (date: DateType) => void, (immediately?: boolean) => void] {
const [value, internalSetValue] = useState<DateType>(null); const innerValue = ref<DateType>(null);
const raf = useRef(null); const raf = ref(null);
function setValue(val: DateType, immediately = false) { function setValue(val: DateType, immediately = false) {
cancelAnimationFrame(raf.current); cancelAnimationFrame(raf.value);
if (immediately) { if (immediately) {
internalSetValue(val); innerValue.value = val as UnwrapRef<DateType>;
return; return;
} }
raf.current = requestAnimationFrame(() => { raf.value = requestAnimationFrame(() => {
internalSetValue(val); innerValue.value = val as UnwrapRef<DateType>;
}); });
} }
const [, firstText] = useValueTexts(value, { const [, firstText] = useValueTexts(innerValue as Ref<DateType>, {
formatList, formatList,
generateConfig, generateConfig,
locale, locale,
}); });
function onEnter(date: DateType) { function onEnter(date: DateType) {
setValue(date); setValue(date);
} }
@ -34,11 +34,12 @@ export default function useHoverValue<DateType>(
setValue(null, immediately); setValue(null, immediately);
} }
useEffect(() => { watch(valueText, () => {
onLeave(true); onLeave(true);
}, [valueText]); });
onBeforeUnmount(() => {
useEffect(() => () => cancelAnimationFrame(raf.current), []); cancelAnimationFrame(raf.value);
});
return [firstText, onEnter, onLeave]; return [firstText, onEnter, onLeave];
} }

View File

@ -1,8 +1,9 @@
import * as React from 'react';
import type { RangeValue, PickerMode, Locale } from '../interface'; import type { RangeValue, PickerMode, Locale } from '../interface';
import { getValue } from '../utils/miscUtil'; import { getValue } from '../utils/miscUtil';
import type { GenerateConfig } from '../generate'; import type { GenerateConfig } from '../generate';
import { isSameDate, getQuarter } from '../utils/dateUtil'; import { isSameDate, getQuarter } from '../utils/dateUtil';
import type { ComputedRef, Ref } from 'vue';
import { computed } from 'vue';
export default function useRangeDisabled<DateType>( export default function useRangeDisabled<DateType>(
{ {
@ -13,101 +14,101 @@ export default function useRangeDisabled<DateType>(
disabled, disabled,
generateConfig, generateConfig,
}: { }: {
picker: PickerMode; picker: Ref<PickerMode>;
selectedValue: RangeValue<DateType>; selectedValue: Ref<RangeValue<DateType>>;
disabledDate?: (date: DateType) => boolean; disabledDate?: Ref<(date: DateType) => boolean>;
disabled: [boolean, boolean]; disabled: ComputedRef<[boolean, boolean]>;
locale: Locale; locale: Ref<Locale>;
generateConfig: GenerateConfig<DateType>; generateConfig: Ref<GenerateConfig<DateType>>;
}, },
disabledStart: boolean, openRecordsRef: Ref<{
disabledEnd: boolean, [x: number]: boolean;
}>,
) { ) {
const startDate = getValue(selectedValue, 0); const startDate = computed(() => getValue(selectedValue.value, 0));
const endDate = getValue(selectedValue, 1); const endDate = computed(() => getValue(selectedValue.value, 1));
function weekFirstDate(date: DateType) { function weekFirstDate(date: DateType) {
return generateConfig.locale.getWeekFirstDate(locale.locale, date); return generateConfig.value.locale.getWeekFirstDate(locale.value.locale, date);
} }
function monthNumber(date: DateType) { function monthNumber(date: DateType) {
const year = generateConfig.getYear(date); const year = generateConfig.value.getYear(date);
const month = generateConfig.getMonth(date); const month = generateConfig.value.getMonth(date);
return year * 100 + month; return year * 100 + month;
} }
function quarterNumber(date: DateType) { function quarterNumber(date: DateType) {
const year = generateConfig.getYear(date); const year = generateConfig.value.getYear(date);
const quarter = getQuarter(generateConfig, date); const quarter = getQuarter(generateConfig.value, date);
return year * 10 + quarter; return year * 10 + quarter;
} }
const disabledStartDate = React.useCallback( const disabledStartDate = (date: DateType) => {
(date: DateType) => { if (disabledDate && disabledDate.value(date)) {
if (disabledDate && disabledDate(date)) {
return true; return true;
} }
// Disabled range // Disabled range
if (disabled[1] && endDate) { if (disabled[1] && endDate) {
return !isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(date, endDate); return (
!isSameDate(generateConfig.value, date, endDate.value) &&
generateConfig.value.isAfter(date, endDate.value)
);
} }
// Disabled part // Disabled part
if (disabledStart && endDate) { if (openRecordsRef.value[1] && endDate.value) {
switch (picker) { switch (picker.value) {
case 'quarter': case 'quarter':
return quarterNumber(date) > quarterNumber(endDate); return quarterNumber(date) > quarterNumber(endDate.value);
case 'month': case 'month':
return monthNumber(date) > monthNumber(endDate); return monthNumber(date) > monthNumber(endDate.value);
case 'week': case 'week':
return weekFirstDate(date) > weekFirstDate(endDate); return weekFirstDate(date) > weekFirstDate(endDate.value);
default: default:
return ( return (
!isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(date, endDate) !isSameDate(generateConfig.value, date, endDate.value) &&
generateConfig.value.isAfter(date, endDate.value)
); );
} }
} }
return false; return false;
}, };
[disabledDate, disabled[1], endDate, disabledStart],
);
const disabledEndDate = React.useCallback( const disabledEndDate = (date: DateType) => {
(date: DateType) => { if (disabledDate.value?.(date)) {
if (disabledDate && disabledDate(date)) {
return true; return true;
} }
// Disabled range // Disabled range
if (disabled[0] && startDate) { if (disabled[0] && startDate) {
return ( return (
!isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(startDate, date) !isSameDate(generateConfig.value, date, endDate.value) &&
generateConfig.value.isAfter(startDate.value, date)
); );
} }
// Disabled part // Disabled part
if (disabledEnd && startDate) { if (openRecordsRef.value[0] && startDate.value) {
switch (picker) { switch (picker.value) {
case 'quarter': case 'quarter':
return quarterNumber(date) < quarterNumber(startDate); return quarterNumber(date) < quarterNumber(startDate.value);
case 'month': case 'month':
return monthNumber(date) < monthNumber(startDate); return monthNumber(date) < monthNumber(startDate.value);
case 'week': case 'week':
return weekFirstDate(date) < weekFirstDate(startDate); return weekFirstDate(date) < weekFirstDate(startDate.value);
default: default:
return ( return (
!isSameDate(generateConfig, date, startDate) && !isSameDate(generateConfig.value, date, startDate.value) &&
generateConfig.isAfter(startDate, date) generateConfig.value.isAfter(startDate.value, date)
); );
} }
} }
return false; return false;
}, };
[disabledDate, disabled[0], startDate, disabledEnd],
);
return [disabledStartDate, disabledEndDate]; return [disabledStartDate, disabledEndDate];
} }

View File

@ -1,8 +1,10 @@
import * as React from 'react';
import type { RangeValue, PickerMode } from '../interface'; import type { RangeValue, PickerMode } from '../interface';
import type { GenerateConfig } from '../generate'; import type { GenerateConfig } from '../generate';
import { getValue, updateValues } from '../utils/miscUtil'; import { getValue, updateValues } from '../utils/miscUtil';
import { getClosingViewDate, isSameYear, isSameMonth, isSameDecade } from '../utils/dateUtil'; import { getClosingViewDate, isSameYear, isSameMonth, isSameDecade } from '../utils/dateUtil';
import type { ComputedRef, Ref } from 'vue';
import { computed } from 'vue';
import { ref } from 'vue';
function getStartEndDistance<DateType>( function getStartEndDistance<DateType>(
startDate: DateType, startDate: DateType,
@ -67,55 +69,64 @@ export default function useRangeViewDates<DateType>({
defaultDates, defaultDates,
generateConfig, generateConfig,
}: { }: {
values: RangeValue<DateType>; values: Ref<RangeValue<DateType>>;
picker: PickerMode; picker: Ref<PickerMode>;
defaultDates: RangeValue<DateType> | undefined; defaultDates: RangeValue<DateType> | undefined;
generateConfig: GenerateConfig<DateType>; generateConfig: Ref<GenerateConfig<DateType>>;
}): [(activePickerIndex: 0 | 1) => DateType, (viewDate: DateType | null, index: 0 | 1) => void] { }): [
const [defaultViewDates, setDefaultViewDates] = React.useState< ComputedRef<DateType>,
[DateType | null, DateType | null] ComputedRef<DateType>,
>(() => [getValue(defaultDates, 0), getValue(defaultDates, 1)]); (viewDate: DateType | null, index: 0 | 1) => void,
const [viewDates, setInternalViewDates] = React.useState<RangeValue<DateType>>(null); ] {
const defaultViewDates = ref<[DateType | null, DateType | null]>([
const startDate = getValue(values, 0); getValue(defaultDates, 0),
const endDate = getValue(values, 1); getValue(defaultDates, 1),
]);
const viewDates = ref<RangeValue<DateType>>(null);
const startDate = computed(() => getValue(values.value, 0));
const endDate = computed(() => getValue(values.value, 1));
function getViewDate(index: 0 | 1): DateType { function getViewDate(index: 0 | 1): DateType {
// If set default view date, use it // If set default view date, use it
if (defaultViewDates[index]) { if (defaultViewDates.value[index]) {
return defaultViewDates[index]!; return defaultViewDates.value[index]! as DateType;
} }
return ( return (
getValue(viewDates, index) || (getValue(viewDates.value, index) as any) ||
getRangeViewDate(values, index, picker, generateConfig) || getRangeViewDate(values.value, index, picker.value, generateConfig.value) ||
startDate || startDate.value ||
endDate || endDate.value ||
generateConfig.getNow() generateConfig.value.getNow()
); );
} }
const startViewDate = computed(() => {
return getViewDate(0);
});
const endViewDate = computed(() => {
return getViewDate(1);
});
function setViewDate(viewDate: DateType | null, index: 0 | 1) { function setViewDate(viewDate: DateType | null, index: 0 | 1) {
if (viewDate) { if (viewDate) {
let newViewDates = updateValues(viewDates, viewDate, index); let newViewDates = updateValues(viewDates.value, viewDate as any, index);
// Set view date will clean up default one // Set view date will clean up default one
setDefaultViewDates(
// Should always be an array // Should always be an array
updateValues(defaultViewDates, null, index) || [null, null], defaultViewDates.value = updateValues(defaultViewDates.value, null, index) || [null, null];
);
// Reset another one when not have value // Reset another one when not have value
const anotherIndex = (index + 1) % 2; const anotherIndex = (index + 1) % 2;
if (!getValue(values, anotherIndex)) { if (!getValue(values.value, anotherIndex)) {
newViewDates = updateValues(newViewDates, viewDate, anotherIndex); newViewDates = updateValues(newViewDates, viewDate, anotherIndex);
} }
setInternalViewDates(newViewDates); viewDates.value = newViewDates;
} else if (startDate || endDate) { } else if (startDate.value || endDate.value) {
// Reset all when has values when `viewDate` is `null` which means from open trigger // Reset all when has values when `viewDate` is `null` which means from open trigger
setInternalViewDates(null); viewDates.value = null;
} }
} }
return [getViewDate, setViewDate]; return [startViewDate, endViewDate, setViewDate];
} }

View File

@ -1,4 +1,5 @@
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import { computed } from 'vue';
import useMemo from '../../_util/hooks/useMemo'; import useMemo from '../../_util/hooks/useMemo';
import shallowequal from '../../_util/shallowequal'; import shallowequal from '../../_util/shallowequal';
import type { GenerateConfig } from '../generate'; import type { GenerateConfig } from '../generate';
@ -14,8 +15,8 @@ export type ValueTextConfig<DateType> = {
export default function useValueTexts<DateType>( export default function useValueTexts<DateType>(
value: Ref<DateType | null>, value: Ref<DateType | null>,
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>, { formatList, generateConfig, locale }: ValueTextConfig<DateType>,
) { ): [ComputedRef<string[]>, ComputedRef<string>] {
return useMemo<[string[], string]>( const texts = useMemo<[string[], string]>(
() => { () => {
if (!value.value) { if (!value.value) {
return [[''], '']; return [[''], ''];
@ -44,4 +45,7 @@ export default function useValueTexts<DateType>(
[value, formatList], [value, formatList],
(next, prev) => prev[0] !== next[0] || !shallowequal(prev[1], next[1]), (next, prev) => prev[0] !== next[0] || !shallowequal(prev[1], next[1]),
); );
const fullValueTexts = computed(() => texts.value[0]);
const firstValueText = computed(() => texts.value[1]);
return [fullValueTexts, firstValueText];
} }