refactor: date
parent
16fc2a10a9
commit
53f3c6e1a5
|
@ -1,3 +1,8 @@
|
|||
export type FocusEventHandler = (e: FocusEvent) => void;
|
||||
export type MouseEventHandler = (e: MouseEvent) => void;
|
||||
export type KeyboardEventHandler = (e: KeyboardEvent) => void;
|
||||
export type ChangeEvent = Event & {
|
||||
target: {
|
||||
value?: string | undefined;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -20,8 +20,7 @@ import PickerPanel from './PickerPanel';
|
|||
import PickerTrigger from './PickerTrigger';
|
||||
import { formatValue, isEqual, parseValue } from './utils/dateUtil';
|
||||
import getDataOrAriaProps, { toArray } from './utils/miscUtil';
|
||||
import type { ContextOperationRefProps } from './PanelContext';
|
||||
import PanelContext from './PanelContext';
|
||||
import { ContextOperationRefProps, useProvidePanel } from './PanelContext';
|
||||
import type { CustomFormat, PickerMode } from './interface';
|
||||
import { getDefaultFormat, getInputSize, elementsContains } from './utils/uiUtil';
|
||||
import usePickerInput from './hooks/usePickerInput';
|
||||
|
@ -30,20 +29,21 @@ import useValueTexts from './hooks/useValueTexts';
|
|||
import useHoverValue from './hooks/useHoverValue';
|
||||
import {
|
||||
computed,
|
||||
createVNode,
|
||||
CSSProperties,
|
||||
defineComponent,
|
||||
HtmlHTMLAttributes,
|
||||
ref,
|
||||
Ref,
|
||||
toRef,
|
||||
toRefs,
|
||||
watch,
|
||||
} from 'vue';
|
||||
import { FocusEventHandler, MouseEventHandler } from '../_util/EventInterface';
|
||||
import { ChangeEvent, FocusEventHandler, MouseEventHandler } from '../_util/EventInterface';
|
||||
import { VueNode } from '../_util/type';
|
||||
import { AlignType } from '../vc-align/interface';
|
||||
import useMergedState from '../_util/hooks/useMergedState';
|
||||
import { locale } from 'dayjs';
|
||||
import { warning } from '../vc-util/warning';
|
||||
import classNames from '../_util/classNames';
|
||||
|
||||
export type PickerRefConfig = {
|
||||
focus: () => void;
|
||||
|
@ -92,10 +92,6 @@ export type PickerSharedProps<DateType> = {
|
|||
onContextMenu?: MouseEventHandler;
|
||||
onKeyDown?: (event: KeyboardEvent, preventDefault: () => void) => void;
|
||||
|
||||
// Internal
|
||||
/** @private Internal usage, do not use in production mode!!! */
|
||||
pickerRef?: Ref<PickerRefConfig>;
|
||||
|
||||
// WAI-ARIA
|
||||
role?: string;
|
||||
name?: string;
|
||||
|
@ -169,7 +165,6 @@ function Picker<DateType>() {
|
|||
'disabledDate',
|
||||
'placeholder',
|
||||
'getPopupContainer',
|
||||
'pickerRef',
|
||||
'panelRender',
|
||||
'onChange',
|
||||
'onOpenChange',
|
||||
|
@ -196,7 +191,7 @@ function Picker<DateType>() {
|
|||
'superNextIcon',
|
||||
'panelRender',
|
||||
],
|
||||
setup(props, { slots, attrs, expose }) {
|
||||
setup(props, { attrs, expose }) {
|
||||
const inputRef = ref(null);
|
||||
const needConfirmButton = computed(
|
||||
() => (props.picker === 'date' && !!props.showTime) || props.picker === 'time',
|
||||
|
@ -242,13 +237,11 @@ function Picker<DateType>() {
|
|||
});
|
||||
|
||||
// ============================= Text ==============================
|
||||
const texts = useValueTexts(selectedValue, {
|
||||
const [valueTexts, firstValueText] = useValueTexts(selectedValue, {
|
||||
formatList,
|
||||
generateConfig: toRef(props, 'generateConfig'),
|
||||
locale: toRef(props, 'locale'),
|
||||
});
|
||||
const valueTexts = computed(() => texts.value[0]);
|
||||
const firstValueText = computed(() => texts.value[1]);
|
||||
|
||||
const [text, triggerTextChange, resetText] = useTextValueMapping({
|
||||
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 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);
|
||||
|
|
|
@ -33,7 +33,16 @@ import getExtraFooter from './utils/getExtraFooter';
|
|||
import getRanges from './utils/getRanges';
|
||||
import { getLowerBoundTime, setDateTime, setTime } from './utils/timeUtil';
|
||||
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 { warning } from '../vc-util/warning';
|
||||
import KeyCode from '../_util/KeyCode';
|
||||
|
@ -83,7 +92,7 @@ export type PickerPanelSharedProps<DateType> = {
|
|||
|
||||
/** @private Internal usage. Do not use in your production env */
|
||||
components?: Components;
|
||||
};
|
||||
} & HTMLAttributes;
|
||||
|
||||
export type PickerPanelBaseProps<DateType> = {
|
||||
picker: Exclude<PickerMode, 'date' | 'time'>;
|
||||
|
@ -598,5 +607,6 @@ function PickerPanel<DateType>() {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default PickerPanel();
|
||||
const InterPickerPanel = PickerPanel<any>();
|
||||
export default <DateType extends any>(props: MergedPickerPanelProps<DateType>): JSX.Element =>
|
||||
createVNode(InterPickerPanel, props);
|
||||
|
|
|
@ -47,7 +47,6 @@ export type PickerTriggerProps = {
|
|||
visible: boolean;
|
||||
popupElement: VueNode;
|
||||
popupStyle?: CSSProperties;
|
||||
children: VueNode;
|
||||
dropdownClassName?: string;
|
||||
transitionName?: string;
|
||||
getPopupContainer?: (node: HTMLElement) => HTMLElement;
|
||||
|
|
|
@ -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';
|
||||
|
||||
export type RangeContextProps = {
|
||||
|
@ -12,6 +22,17 @@ export type RangeContextProps = {
|
|||
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');
|
||||
|
||||
export const useProvideRange = (props: RangeContextProps) => {
|
||||
|
@ -22,4 +43,36 @@ export const useInjectRange = () => {
|
|||
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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 useValueTexts from './useValueTexts';
|
||||
|
||||
export default function useHoverValue<DateType>(
|
||||
valueText: string,
|
||||
valueText: Ref<string>,
|
||||
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>,
|
||||
): [string, (date: DateType) => void, (immediately?: boolean) => void] {
|
||||
const [value, internalSetValue] = useState<DateType>(null);
|
||||
const raf = useRef(null);
|
||||
): [ComputedRef<string>, (date: DateType) => void, (immediately?: boolean) => void] {
|
||||
const innerValue = ref<DateType>(null);
|
||||
const raf = ref(null);
|
||||
|
||||
function setValue(val: DateType, immediately = false) {
|
||||
cancelAnimationFrame(raf.current);
|
||||
cancelAnimationFrame(raf.value);
|
||||
if (immediately) {
|
||||
internalSetValue(val);
|
||||
innerValue.value = val as UnwrapRef<DateType>;
|
||||
return;
|
||||
}
|
||||
raf.current = requestAnimationFrame(() => {
|
||||
internalSetValue(val);
|
||||
raf.value = requestAnimationFrame(() => {
|
||||
innerValue.value = val as UnwrapRef<DateType>;
|
||||
});
|
||||
}
|
||||
|
||||
const [, firstText] = useValueTexts(value, {
|
||||
const [, firstText] = useValueTexts(innerValue as Ref<DateType>, {
|
||||
formatList,
|
||||
generateConfig,
|
||||
locale,
|
||||
});
|
||||
|
||||
function onEnter(date: DateType) {
|
||||
setValue(date);
|
||||
}
|
||||
|
@ -34,11 +34,12 @@ export default function useHoverValue<DateType>(
|
|||
setValue(null, immediately);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
watch(valueText, () => {
|
||||
onLeave(true);
|
||||
}, [valueText]);
|
||||
|
||||
useEffect(() => () => cancelAnimationFrame(raf.current), []);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
cancelAnimationFrame(raf.value);
|
||||
});
|
||||
|
||||
return [firstText, onEnter, onLeave];
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import * as React from 'react';
|
||||
import type { RangeValue, PickerMode, Locale } from '../interface';
|
||||
import { getValue } from '../utils/miscUtil';
|
||||
import type { GenerateConfig } from '../generate';
|
||||
import { isSameDate, getQuarter } from '../utils/dateUtil';
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
export default function useRangeDisabled<DateType>(
|
||||
{
|
||||
|
@ -13,101 +14,101 @@ export default function useRangeDisabled<DateType>(
|
|||
disabled,
|
||||
generateConfig,
|
||||
}: {
|
||||
picker: PickerMode;
|
||||
selectedValue: RangeValue<DateType>;
|
||||
disabledDate?: (date: DateType) => boolean;
|
||||
disabled: [boolean, boolean];
|
||||
locale: Locale;
|
||||
generateConfig: GenerateConfig<DateType>;
|
||||
picker: Ref<PickerMode>;
|
||||
selectedValue: Ref<RangeValue<DateType>>;
|
||||
disabledDate?: Ref<(date: DateType) => boolean>;
|
||||
disabled: ComputedRef<[boolean, boolean]>;
|
||||
locale: Ref<Locale>;
|
||||
generateConfig: Ref<GenerateConfig<DateType>>;
|
||||
},
|
||||
disabledStart: boolean,
|
||||
disabledEnd: boolean,
|
||||
openRecordsRef: Ref<{
|
||||
[x: number]: boolean;
|
||||
}>,
|
||||
) {
|
||||
const startDate = getValue(selectedValue, 0);
|
||||
const endDate = getValue(selectedValue, 1);
|
||||
const startDate = computed(() => getValue(selectedValue.value, 0));
|
||||
const endDate = computed(() => getValue(selectedValue.value, 1));
|
||||
|
||||
function weekFirstDate(date: DateType) {
|
||||
return generateConfig.locale.getWeekFirstDate(locale.locale, date);
|
||||
return generateConfig.value.locale.getWeekFirstDate(locale.value.locale, date);
|
||||
}
|
||||
|
||||
function monthNumber(date: DateType) {
|
||||
const year = generateConfig.getYear(date);
|
||||
const month = generateConfig.getMonth(date);
|
||||
const year = generateConfig.value.getYear(date);
|
||||
const month = generateConfig.value.getMonth(date);
|
||||
return year * 100 + month;
|
||||
}
|
||||
|
||||
function quarterNumber(date: DateType) {
|
||||
const year = generateConfig.getYear(date);
|
||||
const quarter = getQuarter(generateConfig, date);
|
||||
const year = generateConfig.value.getYear(date);
|
||||
const quarter = getQuarter(generateConfig.value, date);
|
||||
return year * 10 + quarter;
|
||||
}
|
||||
|
||||
const disabledStartDate = React.useCallback(
|
||||
(date: DateType) => {
|
||||
if (disabledDate && disabledDate(date)) {
|
||||
return true;
|
||||
const disabledStartDate = (date: DateType) => {
|
||||
if (disabledDate && disabledDate.value(date)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disabled range
|
||||
if (disabled[1] && endDate) {
|
||||
return (
|
||||
!isSameDate(generateConfig.value, date, endDate.value) &&
|
||||
generateConfig.value.isAfter(date, endDate.value)
|
||||
);
|
||||
}
|
||||
|
||||
// Disabled part
|
||||
if (openRecordsRef.value[1] && endDate.value) {
|
||||
switch (picker.value) {
|
||||
case 'quarter':
|
||||
return quarterNumber(date) > quarterNumber(endDate.value);
|
||||
case 'month':
|
||||
return monthNumber(date) > monthNumber(endDate.value);
|
||||
case 'week':
|
||||
return weekFirstDate(date) > weekFirstDate(endDate.value);
|
||||
default:
|
||||
return (
|
||||
!isSameDate(generateConfig.value, date, endDate.value) &&
|
||||
generateConfig.value.isAfter(date, endDate.value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled range
|
||||
if (disabled[1] && endDate) {
|
||||
return !isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(date, endDate);
|
||||
return false;
|
||||
};
|
||||
|
||||
const disabledEndDate = (date: DateType) => {
|
||||
if (disabledDate.value?.(date)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disabled range
|
||||
if (disabled[0] && startDate) {
|
||||
return (
|
||||
!isSameDate(generateConfig.value, date, endDate.value) &&
|
||||
generateConfig.value.isAfter(startDate.value, date)
|
||||
);
|
||||
}
|
||||
|
||||
// Disabled part
|
||||
if (openRecordsRef.value[0] && startDate.value) {
|
||||
switch (picker.value) {
|
||||
case 'quarter':
|
||||
return quarterNumber(date) < quarterNumber(startDate.value);
|
||||
case 'month':
|
||||
return monthNumber(date) < monthNumber(startDate.value);
|
||||
case 'week':
|
||||
return weekFirstDate(date) < weekFirstDate(startDate.value);
|
||||
default:
|
||||
return (
|
||||
!isSameDate(generateConfig.value, date, startDate.value) &&
|
||||
generateConfig.value.isAfter(startDate.value, date)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled part
|
||||
if (disabledStart && endDate) {
|
||||
switch (picker) {
|
||||
case 'quarter':
|
||||
return quarterNumber(date) > quarterNumber(endDate);
|
||||
case 'month':
|
||||
return monthNumber(date) > monthNumber(endDate);
|
||||
case 'week':
|
||||
return weekFirstDate(date) > weekFirstDate(endDate);
|
||||
default:
|
||||
return (
|
||||
!isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(date, endDate)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
[disabledDate, disabled[1], endDate, disabledStart],
|
||||
);
|
||||
|
||||
const disabledEndDate = React.useCallback(
|
||||
(date: DateType) => {
|
||||
if (disabledDate && disabledDate(date)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disabled range
|
||||
if (disabled[0] && startDate) {
|
||||
return (
|
||||
!isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(startDate, date)
|
||||
);
|
||||
}
|
||||
|
||||
// Disabled part
|
||||
if (disabledEnd && startDate) {
|
||||
switch (picker) {
|
||||
case 'quarter':
|
||||
return quarterNumber(date) < quarterNumber(startDate);
|
||||
case 'month':
|
||||
return monthNumber(date) < monthNumber(startDate);
|
||||
case 'week':
|
||||
return weekFirstDate(date) < weekFirstDate(startDate);
|
||||
default:
|
||||
return (
|
||||
!isSameDate(generateConfig, date, startDate) &&
|
||||
generateConfig.isAfter(startDate, date)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
[disabledDate, disabled[0], startDate, disabledEnd],
|
||||
);
|
||||
return false;
|
||||
};
|
||||
|
||||
return [disabledStartDate, disabledEndDate];
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import * as React from 'react';
|
||||
import type { RangeValue, PickerMode } from '../interface';
|
||||
import type { GenerateConfig } from '../generate';
|
||||
import { getValue, updateValues } from '../utils/miscUtil';
|
||||
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>(
|
||||
startDate: DateType,
|
||||
|
@ -67,55 +69,64 @@ export default function useRangeViewDates<DateType>({
|
|||
defaultDates,
|
||||
generateConfig,
|
||||
}: {
|
||||
values: RangeValue<DateType>;
|
||||
picker: PickerMode;
|
||||
values: Ref<RangeValue<DateType>>;
|
||||
picker: Ref<PickerMode>;
|
||||
defaultDates: RangeValue<DateType> | undefined;
|
||||
generateConfig: GenerateConfig<DateType>;
|
||||
}): [(activePickerIndex: 0 | 1) => DateType, (viewDate: DateType | null, index: 0 | 1) => void] {
|
||||
const [defaultViewDates, setDefaultViewDates] = React.useState<
|
||||
[DateType | null, DateType | null]
|
||||
>(() => [getValue(defaultDates, 0), getValue(defaultDates, 1)]);
|
||||
const [viewDates, setInternalViewDates] = React.useState<RangeValue<DateType>>(null);
|
||||
|
||||
const startDate = getValue(values, 0);
|
||||
const endDate = getValue(values, 1);
|
||||
generateConfig: Ref<GenerateConfig<DateType>>;
|
||||
}): [
|
||||
ComputedRef<DateType>,
|
||||
ComputedRef<DateType>,
|
||||
(viewDate: DateType | null, index: 0 | 1) => void,
|
||||
] {
|
||||
const defaultViewDates = ref<[DateType | null, DateType | null]>([
|
||||
getValue(defaultDates, 0),
|
||||
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 {
|
||||
// If set default view date, use it
|
||||
if (defaultViewDates[index]) {
|
||||
return defaultViewDates[index]!;
|
||||
if (defaultViewDates.value[index]) {
|
||||
return defaultViewDates.value[index]! as DateType;
|
||||
}
|
||||
|
||||
return (
|
||||
getValue(viewDates, index) ||
|
||||
getRangeViewDate(values, index, picker, generateConfig) ||
|
||||
startDate ||
|
||||
endDate ||
|
||||
generateConfig.getNow()
|
||||
(getValue(viewDates.value, index) as any) ||
|
||||
getRangeViewDate(values.value, index, picker.value, generateConfig.value) ||
|
||||
startDate.value ||
|
||||
endDate.value ||
|
||||
generateConfig.value.getNow()
|
||||
);
|
||||
}
|
||||
const startViewDate = computed(() => {
|
||||
return getViewDate(0);
|
||||
});
|
||||
|
||||
const endViewDate = computed(() => {
|
||||
return getViewDate(1);
|
||||
});
|
||||
|
||||
function setViewDate(viewDate: DateType | null, index: 0 | 1) {
|
||||
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
|
||||
setDefaultViewDates(
|
||||
// Should always be an array
|
||||
updateValues(defaultViewDates, null, index) || [null, null],
|
||||
);
|
||||
// Should always be an array
|
||||
defaultViewDates.value = updateValues(defaultViewDates.value, null, index) || [null, null];
|
||||
|
||||
// Reset another one when not have value
|
||||
const anotherIndex = (index + 1) % 2;
|
||||
if (!getValue(values, anotherIndex)) {
|
||||
if (!getValue(values.value, anotherIndex)) {
|
||||
newViewDates = updateValues(newViewDates, viewDate, anotherIndex);
|
||||
}
|
||||
|
||||
setInternalViewDates(newViewDates);
|
||||
} else if (startDate || endDate) {
|
||||
viewDates.value = newViewDates;
|
||||
} else if (startDate.value || endDate.value) {
|
||||
// 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];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { ComputedRef, Ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import useMemo from '../../_util/hooks/useMemo';
|
||||
import shallowequal from '../../_util/shallowequal';
|
||||
import type { GenerateConfig } from '../generate';
|
||||
|
@ -14,8 +15,8 @@ export type ValueTextConfig<DateType> = {
|
|||
export default function useValueTexts<DateType>(
|
||||
value: Ref<DateType | null>,
|
||||
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>,
|
||||
) {
|
||||
return useMemo<[string[], string]>(
|
||||
): [ComputedRef<string[]>, ComputedRef<string>] {
|
||||
const texts = useMemo<[string[], string]>(
|
||||
() => {
|
||||
if (!value.value) {
|
||||
return [[''], ''];
|
||||
|
@ -44,4 +45,7 @@ export default function useValueTexts<DateType>(
|
|||
[value, formatList],
|
||||
(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];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue