Merge branch 'refactor-date' of github.com:vueComponent/ant-design-vue into refactor-date

# Conflicts:
#	components/vc-picker/generate/dayjs.ts
#	components/vc-picker/generate/moment.ts
#	v2-doc
pull/4499/head
tangjinzhou 2021-07-19 23:25:36 +08:00
parent a501b592a2
commit 16fc2a10a9
44 changed files with 1187 additions and 1341 deletions

View File

@ -0,0 +1,3 @@
export type FocusEventHandler = (e: FocusEvent) => void;
export type MouseEventHandler = (e: MouseEvent) => void;
export type KeyboardEventHandler = (e: KeyboardEvent) => void;

View File

@ -1,9 +1,9 @@
import type { Ref } from 'vue'; import type { Ref, WatchSource } from 'vue';
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
export default function useMemo<T>( export default function useMemo<T>(
getValue: () => T, getValue: () => T,
condition: any[], condition: (WatchSource<unknown> | object)[],
shouldUpdate?: (prev: any[], next: any[]) => boolean, shouldUpdate?: (prev: any[], next: any[]) => boolean,
) { ) {
const cacheRef: Ref<T> = ref(getValue() as any); const cacheRef: Ref<T> = ref(getValue() as any);

View File

@ -0,0 +1,50 @@
import type { Ref, UnwrapRef } from 'vue';
import { watchEffect } from 'vue';
import { unref } from 'vue';
import { watch } from 'vue';
import { ref } from 'vue';
export default function useMergedState<T, R = Ref<T>>(
defaultStateValue: T | (() => T),
option?: {
defaultValue?: T | (() => T);
value?: Ref<T> | Ref<UnwrapRef<T>>;
onChange?: (val: T, prevValue: T) => void;
postState?: (val: T) => T;
},
): [R, (val: T) => void] {
const { defaultValue, value } = option || {};
let initValue: T =
typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue;
if (value.value !== undefined) {
initValue = unref(value as any) as T;
}
if (defaultValue !== undefined) {
initValue = typeof defaultValue === 'function' ? (defaultValue as any)() : defaultValue;
}
const innerValue = ref(initValue) as Ref<T>;
const mergedValue = ref(initValue) as Ref<T>;
watchEffect(() => {
let val = value.value !== undefined ? value.value : innerValue.value;
if (option.postState) {
val = option.postState(val as T);
}
mergedValue.value = val as T;
});
function triggerChange(newValue: T) {
const preVal = mergedValue.value;
innerValue.value = newValue;
if (mergedValue.value !== newValue && option.onChange) {
option.onChange(newValue, preVal);
}
}
// Effect of reset value to `undefined`
watch(value, () => {
innerValue.value = value.value as T;
});
return [mergedValue as unknown as R, triggerChange];
}

View File

@ -0,0 +1,17 @@
import type { Ref } from 'vue';
import { ref } from 'vue';
export default function useState<T, R = Ref<T>>(
defaultStateValue: T | (() => T),
): [R, (val: T) => void] {
const initValue: T =
typeof defaultStateValue === 'function' ? (defaultStateValue as any)() : defaultStateValue;
const innerValue = ref(initValue) as Ref<T>;
function triggerChange(newValue: T) {
innerValue.value = newValue;
}
return [innerValue as unknown as R, triggerChange];
}

View File

@ -9,22 +9,21 @@ export type ContextOperationRefProps = {
export type PanelContextProps = { export type PanelContextProps = {
operationRef?: Ref<ContextOperationRefProps | null>; operationRef?: Ref<ContextOperationRefProps | null>;
/** Only work with time panel */ /** Only work with time panel */
hideHeader?: boolean; hideHeader?: Ref<boolean>;
panelRef?: Ref<HTMLDivElement>; panelRef?: Ref<HTMLDivElement>;
hidePrevBtn?: boolean; hidePrevBtn?: Ref<boolean>;
hideNextBtn?: boolean; hideNextBtn?: Ref<boolean>;
onDateMouseEnter?: (date: any) => void; onDateMouseEnter?: (date: any) => void;
onDateMouseLeave?: (date: any) => void; onDateMouseLeave?: (date: any) => void;
onSelect?: OnSelect<any>; onSelect?: OnSelect<any>;
hideRanges?: boolean; hideRanges?: Ref<boolean>;
open?: boolean; open?: Ref<boolean>;
mode?: PanelMode; mode?: Ref<PanelMode>;
/** Only used for TimePicker and this is a deprecated prop */ /** Only used for TimePicker and this is a deprecated prop */
defaultOpenValue?: any; defaultOpenValue?: Ref<any>;
}; };
const PanelContextKey: InjectionKey<PanelContextProps> = Symbol('PanelContextProps'); const PanelContextKey: InjectionKey<PanelContextProps> = Symbol('PanelContextProps');
export const useProvidePanel = (props: PanelContextProps) => { export const useProvidePanel = (props: PanelContextProps) => {

View File

@ -11,11 +11,6 @@
* Tips: Should add faq about `datetime` mode with `defaultValue` * Tips: Should add faq about `datetime` mode with `defaultValue`
*/ */
import * as React from 'react';
import classNames from 'classnames';
import type { AlignType } from 'rc-trigger/lib/interface';
import warning from 'rc-util/lib/warning';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { import type {
PickerPanelBaseProps, PickerPanelBaseProps,
PickerPanelDateProps, PickerPanelDateProps,
@ -33,6 +28,22 @@ import usePickerInput from './hooks/usePickerInput';
import useTextValueMapping from './hooks/useTextValueMapping'; import useTextValueMapping from './hooks/useTextValueMapping';
import useValueTexts from './hooks/useValueTexts'; import useValueTexts from './hooks/useValueTexts';
import useHoverValue from './hooks/useHoverValue'; import useHoverValue from './hooks/useHoverValue';
import {
computed,
CSSProperties,
defineComponent,
HtmlHTMLAttributes,
ref,
Ref,
toRef,
toRefs,
} from 'vue';
import { 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';
export type PickerRefConfig = { export type PickerRefConfig = {
focus: () => void; focus: () => void;
@ -42,13 +53,13 @@ export type PickerRefConfig = {
export type PickerSharedProps<DateType> = { export type PickerSharedProps<DateType> = {
dropdownClassName?: string; dropdownClassName?: string;
dropdownAlign?: AlignType; dropdownAlign?: AlignType;
popupStyle?: React.CSSProperties; popupStyle?: CSSProperties;
transitionName?: string; transitionName?: string;
placeholder?: string; placeholder?: string;
allowClear?: boolean; allowClear?: boolean;
autoFocus?: boolean; autofocus?: boolean;
disabled?: boolean; disabled?: boolean;
tabIndex?: number; tabindex?: number;
open?: boolean; open?: boolean;
defaultOpen?: boolean; defaultOpen?: boolean;
/** Make input readOnly to avoid popup keyboard in mobile */ /** Make input readOnly to avoid popup keyboard in mobile */
@ -59,39 +70,39 @@ export type PickerSharedProps<DateType> = {
format?: string | CustomFormat<DateType> | (string | CustomFormat<DateType>)[]; format?: string | CustomFormat<DateType> | (string | CustomFormat<DateType>)[];
// Render // Render
suffixIcon?: React.ReactNode; suffixIcon?: VueNode;
clearIcon?: React.ReactNode; clearIcon?: VueNode;
prevIcon?: React.ReactNode; prevIcon?: VueNode;
nextIcon?: React.ReactNode; nextIcon?: VueNode;
superPrevIcon?: React.ReactNode; superPrevIcon?: VueNode;
superNextIcon?: React.ReactNode; superNextIcon?: VueNode;
getPopupContainer?: (node: HTMLElement) => HTMLElement; getPopupContainer?: (node: HTMLElement) => HTMLElement;
panelRender?: (originPanel: React.ReactNode) => React.ReactNode; panelRender?: (originPanel: VueNode) => VueNode;
// Events // Events
onChange?: (value: DateType | null, dateString: string) => void; onChange?: (value: DateType | null, dateString: string) => void;
onOpenChange?: (open: boolean) => void; onOpenChange?: (open: boolean) => void;
onFocus?: React.FocusEventHandler<HTMLInputElement>; onFocus?: FocusEventHandler;
onBlur?: React.FocusEventHandler<HTMLInputElement>; onBlur?: FocusEventHandler;
onMouseDown?: React.MouseEventHandler<HTMLDivElement>; onMouseDown?: MouseEventHandler;
onMouseUp?: React.MouseEventHandler<HTMLDivElement>; onMouseUp?: MouseEventHandler;
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>; onMouseEnter?: MouseEventHandler;
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>; onMouseLeave?: MouseEventHandler;
onClick?: React.MouseEventHandler<HTMLDivElement>; onClick?: MouseEventHandler;
onContextMenu?: React.MouseEventHandler<HTMLDivElement>; onContextMenu?: MouseEventHandler;
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>, preventDefault: () => void) => void; onKeyDown?: (event: KeyboardEvent, preventDefault: () => void) => void;
// Internal // Internal
/** @private Internal usage, do not use in production mode!!! */ /** @private Internal usage, do not use in production mode!!! */
pickerRef?: React.MutableRefObject<PickerRefConfig>; pickerRef?: Ref<PickerRefConfig>;
// WAI-ARIA // WAI-ARIA
role?: string; role?: string;
name?: string; name?: string;
autoComplete?: string; autocomplete?: string;
direction?: 'ltr' | 'rtl'; direction?: 'ltr' | 'rtl';
} & React.AriaAttributes; } & HtmlHTMLAttributes;
type OmitPanelProps<Props> = Omit< type OmitPanelProps<Props> = Omit<
Props, Props,
@ -127,111 +138,127 @@ type MergedPickerProps<DateType> = {
picker?: PickerMode; picker?: PickerMode;
} & OmitType<DateType>; } & OmitType<DateType>;
function InnerPicker<DateType>(props: PickerProps<DateType>) { function Picker<DateType>() {
const { return defineComponent<MergedPickerProps<DateType>>({
prefixCls = 'rc-picker', name: 'Picker',
id, props: [
tabIndex, 'prefixCls',
style, 'id',
className, 'tabindex',
dropdownClassName, 'dropdownClassName',
dropdownAlign, 'dropdownAlign',
popupStyle, 'popupStyle',
transitionName, 'transitionName',
generateConfig, 'generateConfig',
locale, 'locale',
inputReadOnly, 'inputReadOnly',
allowClear, 'allowClear',
autoFocus, 'autofocus',
showTime, 'showTime',
picker = 'date', 'picker',
format, 'format',
use12Hours, 'use12Hours',
value, 'value',
defaultValue, 'defaultValue',
open, 'open',
defaultOpen, 'defaultOpen',
defaultOpenValue, 'defaultOpenValue',
suffixIcon, 'suffixIcon',
clearIcon, 'clearIcon',
disabled, 'disabled',
disabledDate, 'disabledDate',
placeholder, 'placeholder',
getPopupContainer, 'getPopupContainer',
pickerRef, 'pickerRef',
panelRender, 'panelRender',
onChange, 'onChange',
onOpenChange, 'onOpenChange',
onFocus, 'onFocus',
onBlur, 'onBlur',
onMouseDown, 'onMouseDown',
onMouseUp, 'onMouseUp',
onMouseEnter, 'onMouseEnter',
onMouseLeave, 'onMouseLeave',
onContextMenu, 'onContextMenu',
onClick, 'onClick',
onKeyDown, 'onKeyDown',
onSelect, 'onSelect',
direction, 'direction',
autoComplete = 'off', 'autocomplete',
} = props as MergedPickerProps<DateType>; ] as any,
inheritAttrs: false,
const inputRef = React.useRef<HTMLInputElement>(null); slots: [
'suffixIcon',
const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time'; 'clearIcon',
'prevIcon',
'nextIcon',
'superPrevIcon',
'superNextIcon',
'panelRender',
],
setup(props, { slots, attrs, expose }) {
const inputRef = ref(null);
const needConfirmButton = computed(
() => (props.picker === 'date' && !!props.showTime) || props.picker === 'time',
);
// ============================= State ============================= // ============================= State =============================
const formatList = toArray(getDefaultFormat(format, picker, showTime, use12Hours)); const formatList = computed(() =>
toArray(getDefaultFormat(props.format, props.picker, props.showTime, props.use12Hours)),
);
// Panel ref // Panel ref
const panelDivRef = React.useRef<HTMLDivElement>(null); const panelDivRef = ref(null);
const inputDivRef = React.useRef<HTMLDivElement>(null); const inputDivRef = ref(null);
// Real value // Real value
const [mergedValue, setInnerValue] = useMergedState(null, { const [mergedValue, setInnerValue] = useMergedState<DateType>(null, {
value, value: toRef(props, 'value'),
defaultValue, defaultValue: props.defaultValue,
}); });
// Selected value const selectedValue = ref(mergedValue.value) as Ref<DateType>;
const [selectedValue, setSelectedValue] = React.useState<DateType | null>(mergedValue); const setSelectedValue = (val: DateType) => {
selectedValue.value = val;
};
// Operation ref // Operation ref
const operationRef: React.MutableRefObject<ContextOperationRefProps | null> = const operationRef = ref<ContextOperationRefProps>(null);
React.useRef<ContextOperationRefProps>(null);
// Open // Open
const [mergedOpen, triggerInnerOpen] = useMergedState(false, { const [mergedOpen, triggerInnerOpen] = useMergedState(false, {
value: open, value: toRef(props, 'open'),
defaultValue: defaultOpen, defaultValue: props.defaultOpen,
postState: (postOpen) => (disabled ? false : postOpen), postState: postOpen => (props.disabled ? false : postOpen),
onChange: (newOpen) => { onChange: newOpen => {
if (onOpenChange) { if (props.onOpenChange) {
onOpenChange(newOpen); props.onOpenChange(newOpen);
} }
if (!newOpen && operationRef.current && operationRef.current.onClose) { if (!newOpen && operationRef.value && operationRef.value.onClose) {
operationRef.current.onClose(); operationRef.value.onClose();
} }
}, },
}); });
// ============================= Text ============================== // ============================= Text ==============================
const [valueTexts, firstValueText] = useValueTexts(selectedValue, { const texts = useValueTexts(selectedValue, {
formatList, formatList,
generateConfig, generateConfig: toRef(props, 'generateConfig'),
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,
onTextChange: (newText) => { onTextChange: newText => {
const inputDate = parseValue(newText, { const inputDate = parseValue(newText, {
locale, locale: props.locale,
formatList, formatList: formatList.value,
generateConfig, generateConfig: props.generateConfig,
}); });
if (inputDate && (!disabledDate || !disabledDate(inputDate))) { if (inputDate && (!props.disabledDate || !props.disabledDate(inputDate))) {
setSelectedValue(inputDate); setSelectedValue(inputDate);
} }
}, },
@ -239,29 +266,32 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
// ============================ Trigger ============================ // ============================ Trigger ============================
const triggerChange = (newValue: DateType | null) => { const triggerChange = (newValue: DateType | null) => {
const { onChange, generateConfig, locale } = props;
setSelectedValue(newValue); setSelectedValue(newValue);
setInnerValue(newValue); setInnerValue(newValue);
if (onChange && !isEqual(generateConfig, mergedValue, newValue)) { if (onChange && !isEqual(generateConfig, mergedValue.value, newValue)) {
onChange( onChange(
newValue, newValue,
newValue ? formatValue(newValue, { generateConfig, locale, format: formatList[0] }) : '', newValue
? formatValue(newValue, { generateConfig, locale, format: formatList[0] })
: '',
); );
} }
}; };
const triggerOpen = (newOpen: boolean) => { const triggerOpen = (newOpen: boolean) => {
if (disabled && newOpen) { if (props.disabled && newOpen) {
return; return;
} }
triggerInnerOpen(newOpen); triggerInnerOpen(newOpen);
}; };
const forwardKeyDown = (e: React.KeyboardEvent<HTMLElement>) => { const forwardKeyDown = (e: KeyboardEvent) => {
if (mergedOpen && operationRef.current && operationRef.current.onKeyDown) { if (mergedOpen && operationRef.value && operationRef.value.onKeyDown) {
// Let popup panel handle keyboard // Let popup panel handle keyboard
return operationRef.current.onKeyDown(e); return operationRef.value.onKeyDown(e);
} }
/* istanbul ignore next */ /* istanbul ignore next */
@ -275,13 +305,13 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
} }
}; };
const onInternalMouseUp: React.MouseEventHandler<HTMLDivElement> = (...args) => { const onInternalMouseUp: MouseEventHandler = (...args) => {
if (onMouseUp) { if (props.onMouseUp) {
onMouseUp(...args); props.onMouseUp(...args);
} }
if (inputRef.current) { if (inputRef.value) {
inputRef.current.focus(); inputRef.value.focus();
triggerOpen(true); triggerOpen(true);
} }
}; };
@ -293,269 +323,39 @@ function InnerPicker<DateType>(props: PickerProps<DateType>) {
value: text, value: text,
triggerOpen, triggerOpen,
forwardKeyDown, forwardKeyDown,
isClickOutside: (target) => isClickOutside: target =>
!elementsContains([panelDivRef.current, inputDivRef.current], target as HTMLElement), !elementsContains([panelDivRef.current, inputDivRef.current], target as HTMLElement),
onSubmit: () => { onSubmit: () => {
if (disabledDate && disabledDate(selectedValue)) { if (props.disabledDate && props.disabledDate(selectedValue.value)) {
return false; return false;
} }
triggerChange(selectedValue); triggerChange(selectedValue.value);
triggerOpen(false); triggerOpen(false);
resetText(); resetText();
return true; return true;
}, },
onCancel: () => { onCancel: () => {
triggerOpen(false); triggerOpen(false);
setSelectedValue(mergedValue); setSelectedValue(mergedValue.value);
resetText(); resetText();
}, },
onKeyDown: (e, preventDefault) => { onKeyDown: (e, preventDefault) => {
onKeyDown?.(e, preventDefault); props.onKeyDown?.(e, preventDefault);
},
onFocus: (e: FocusEvent) => {
props.onFocus?.(e);
},
onBlur: (e: FocusEvent) => {
props.onBlur?.(e);
}, },
onFocus,
onBlur,
}); });
// ============================= Sync ============================== return () => {
// Close should sync back with text value return null;
React.useEffect(() => {
if (!mergedOpen) {
setSelectedValue(mergedValue);
if (!valueTexts.length || valueTexts[0] === '') {
triggerTextChange('');
} else if (firstValueText !== text) {
resetText();
}
}
}, [mergedOpen, valueTexts]);
// Change picker should sync back with text value
React.useEffect(() => {
if (!mergedOpen) {
resetText();
}
}, [picker]);
// Sync innerValue with control mode
React.useEffect(() => {
// Sync select value
setSelectedValue(mergedValue);
}, [mergedValue]);
// ============================ Private ============================
if (pickerRef) {
pickerRef.current = {
focus: () => {
if (inputRef.current) {
inputRef.current.focus();
}
},
blur: () => {
if (inputRef.current) {
inputRef.current.blur();
}
},
}; };
} },
const [hoverValue, onEnter, onLeave] = useHoverValue(text, {
formatList,
generateConfig,
locale,
}); });
// ============================= Panel =============================
const panelProps = {
// Remove `picker` & `format` here since TimePicker is little different with other panel
...(props as Omit<MergedPickerProps<DateType>, 'picker' | 'format'>),
className: undefined,
style: undefined,
pickerValue: undefined,
onPickerValueChange: undefined,
onChange: null,
};
let panelNode: React.ReactNode = (
<PickerPanel<DateType>
{...panelProps}
generateConfig={generateConfig}
class={classNames({
[`${prefixCls}-panel-focused`]: !typing,
})}
value={selectedValue}
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: React.ReactNode;
if (suffixIcon) {
suffixNode = <span class={`${prefixCls}-suffix`}>{suffixIcon}</span>;
}
let clearNode: React.ReactNode;
if (allowClear && mergedValue && !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 onContextSelect = (date: DateType, type: 'key' | 'mouse' | 'submit') => {
if (type === 'submit' || (type !== 'key' && !needConfirmButton)) {
// triggerChange will also update selected values
triggerChange(date);
triggerOpen(false);
}
};
const popupPlacement = direction === 'rtl' ? 'bottomRight' : 'bottomLeft';
return (
<PanelContext.Provider
value={{
operationRef,
hideHeader: picker === 'time',
panelRef: panelDivRef,
onSelect: onContextSelect,
open: mergedOpen,
defaultOpenValue,
onDateMouseEnter: onEnter,
onDateMouseLeave: onLeave,
}}
>
<PickerTrigger
visible={mergedOpen}
popupElement={panel}
popupStyle={popupStyle}
prefixCls={prefixCls}
dropdownClassName={dropdownClassName}
dropdownAlign={dropdownAlign}
getPopupContainer={getPopupContainer}
transitionName={transitionName}
popupPlacement={popupPlacement}
direction={direction}
>
<div
class={classNames(prefixCls, className, {
[`${prefixCls}-disabled`]: disabled,
[`${prefixCls}-focused`]: focused,
[`${prefixCls}-rtl`]: direction === 'rtl',
})}
style={style}
onMousedown={onMouseDown}
onMouseup={onInternalMouseUp}
onMouseenter={onMouseEnter}
onMouseleave={onMouseLeave}
onContextmenu={onContextMenu}
onClick={onClick}
>
<div
class={classNames(`${prefixCls}-input`, {
[`${prefixCls}-input-placeholder`]: !!hoverValue,
})}
ref={inputDivRef}
>
<input
id={id}
tabIndex={tabIndex}
disabled={disabled}
readonly={inputReadOnly || typeof formatList[0] === 'function' || !typing}
value={hoverValue || text}
onChange={(e) => {
triggerTextChange(e.target.value);
}}
autofocus={autoFocus}
placeholder={placeholder}
ref={inputRef}
title={text}
{...inputProps}
size={getInputSize(picker, formatList[0], generateConfig)}
{...getDataOrAriaProps(props)}
autocomplete={autoComplete}
/>
{suffixNode}
{clearNode}
</div>
</div>
</PickerTrigger>
</PanelContext.Provider>
);
} }
// Wrap with class component to enable pass generic with instance method export default Picker();
class Picker<DateType> extends React.Component<PickerProps<DateType>> {
pickerRef = React.createRef<PickerRefConfig>();
focus = () => {
if (this.pickerRef.current) {
this.pickerRef.current.focus();
}
};
blur = () => {
if (this.pickerRef.current) {
this.pickerRef.current.blur();
}
};
render() {
return (
<InnerPicker<DateType>
{...this.props}
pickerRef={this.pickerRef as React.MutableRefObject<PickerRefConfig>}
/>
);
}
}
export default Picker;

View File

@ -4,12 +4,6 @@
* click will trigger `onSelect` (if value changed trigger `onChange` also). * click will trigger `onSelect` (if value changed trigger `onChange` also).
* Panel change will not trigger `onSelect` but trigger `onPanelChange` * Panel change will not trigger `onSelect` but trigger `onPanelChange`
*/ */
import * as React from 'react';
import classNames from 'classnames';
import KeyCode from 'rc-util/lib/KeyCode';
import warning from 'rc-util/lib/warning';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import type { SharedTimeProps } from './panels/TimePanel'; import type { SharedTimeProps } from './panels/TimePanel';
import TimePanel from './panels/TimePanel'; import TimePanel from './panels/TimePanel';
import DatetimePanel from './panels/DatetimePanel'; import DatetimePanel from './panels/DatetimePanel';
@ -30,22 +24,28 @@ import type {
Components, Components,
} from './interface'; } from './interface';
import { isEqual } from './utils/dateUtil'; import { isEqual } from './utils/dateUtil';
import PanelContext from './PanelContext'; import { useInjectPanel, useProvidePanel } from './PanelContext';
import type { DateRender } from './panels/DatePanel/DateBody'; import type { DateRender } from './panels/DatePanel/DateBody';
import { PickerModeMap } from './utils/uiUtil'; import { PickerModeMap } from './utils/uiUtil';
import type { MonthCellRender } from './panels/MonthPanel/MonthBody'; import type { MonthCellRender } from './panels/MonthPanel/MonthBody';
import RangeContext from './RangeContext'; import { useInjectRange } from './RangeContext';
import getExtraFooter from './utils/getExtraFooter'; 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 { computed, defineComponent, ref, toRef, watch, watchEffect } from 'vue';
import useMergedState from '../_util/hooks/useMergedState';
import { warning } from '../vc-util/warning';
import KeyCode from '../_util/KeyCode';
import classNames from '../_util/classNames';
export type PickerPanelSharedProps<DateType> = { export type PickerPanelSharedProps<DateType> = {
prefixCls?: string; prefixCls?: string;
className?: string; // className?: string;
style?: React.CSSProperties; // style?: React.CSSProperties;
/** @deprecated Will be removed in next big version. Please use `mode` instead */ /** @deprecated Will be removed in next big version. Please use `mode` instead */
mode?: PanelMode; mode?: PanelMode;
tabIndex?: number; tabindex?: number;
// Locale // Locale
locale: Locale; locale: Locale;
@ -65,13 +65,13 @@ export type PickerPanelSharedProps<DateType> = {
// Render // Render
dateRender?: DateRender<DateType>; dateRender?: DateRender<DateType>;
monthCellRender?: MonthCellRender<DateType>; monthCellRender?: MonthCellRender<DateType>;
renderExtraFooter?: (mode: PanelMode) => React.ReactNode; renderExtraFooter?: (mode: PanelMode) => VueNode;
// Event // Event
onSelect?: (value: DateType) => void; onSelect?: (value: DateType) => void;
onChange?: (value: DateType) => void; onChange?: (value: DateType) => void;
onPanelChange?: OnPanelChange<DateType>; onPanelChange?: OnPanelChange<DateType>;
onMouseDown?: React.MouseEventHandler<HTMLDivElement>; onMouseDown?: (e: MouseEvent) => void;
onOk?: (date: DateType) => void; onOk?: (date: DateType) => void;
direction?: 'ltr' | 'rtl'; direction?: 'ltr' | 'rtl';
@ -101,7 +101,8 @@ export type PickerPanelDateProps<DateType> = {
export type PickerPanelTimeProps<DateType> = { export type PickerPanelTimeProps<DateType> = {
picker: 'time'; picker: 'time';
} & PickerPanelSharedProps<DateType> & SharedTimeProps<DateType>; } & PickerPanelSharedProps<DateType> &
SharedTimeProps<DateType>;
export type PickerPanelProps<DateType> = export type PickerPanelProps<DateType> =
| PickerPanelBaseProps<DateType> | PickerPanelBaseProps<DateType>
@ -116,62 +117,71 @@ type MergedPickerPanelProps<DateType> = {
picker?: PickerMode; picker?: PickerMode;
} & OmitType<DateType>; } & OmitType<DateType>;
function PickerPanel<DateType>(props: PickerPanelProps<DateType>) { function PickerPanel<DateType>() {
const { return defineComponent<MergedPickerPanelProps<DateType>>({
prefixCls = 'rc-picker', name: 'PickerPanel',
className, inheritAttrs: false,
style, props: [
locale, 'prefixCls',
generateConfig, 'locale',
value, 'generateConfig',
defaultValue, 'value',
pickerValue, 'defaultValue',
defaultPickerValue, 'pickerValue',
disabledDate, 'defaultPickerValue',
mode, 'disabledDate',
picker = 'date', 'mode',
tabIndex = 0, { picker: { default: 'date' } },
showNow, { tabindex: { default: 0 } },
showTime, 'showNow',
showToday, 'showTime',
renderExtraFooter, 'showToday',
hideHeader, 'renderExtraFooter',
onSelect, 'hideHeader',
onChange, 'onSelect',
onPanelChange, 'onChange',
onMouseDown, 'onPanelChange',
onPickerValueChange, 'onMouseDown',
onOk, 'onPickerValueChange',
components, 'onOk',
direction, 'components',
hourStep = 1, 'direction',
minuteStep = 1, { hourStep: { default: 1 } },
secondStep = 1, { minuteStep: { default: 1 } },
} = props as MergedPickerPanelProps<DateType>; { secondStep: { default: 1 } },
] as any,
const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time'; setup(props, { attrs }) {
const needConfirmButton = computed(
const isHourStepValid = 24 % hourStep === 0; () => (props.picker === 'date' && !!props.showTime) || props.picker === 'time',
const isMinuteStepValid = 60 % minuteStep === 0; );
const isSecondStepValid = 60 % secondStep === 0;
const isHourStepValid = computed(() => 24 % props.hourStep === 0);
const isMinuteStepValid = computed(() => 60 % props.minuteStep === 0);
const isSecondStepValid = computed(() => 60 % props.secondStep === 0);
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
watchEffect(() => {
const { generateConfig, value, hourStep = 1, minuteStep = 1, secondStep = 1 } = props;
warning(!value || generateConfig.isValidate(value), 'Invalidate date pass to `value`.'); warning(!value || generateConfig.isValidate(value), 'Invalidate date pass to `value`.');
warning(!value || generateConfig.isValidate(value), 'Invalidate date pass to `defaultValue`.');
warning(isHourStepValid, `\`hourStep\` ${hourStep} is invalid. It should be a factor of 24.`);
warning( warning(
isMinuteStepValid, !value || generateConfig.isValidate(value),
'Invalidate date pass to `defaultValue`.',
);
warning(
isHourStepValid.value,
`\`hourStep\` ${hourStep} is invalid. It should be a factor of 24.`,
);
warning(
isMinuteStepValid.value,
`\`minuteStep\` ${minuteStep} is invalid. It should be a factor of 60.`, `\`minuteStep\` ${minuteStep} is invalid. It should be a factor of 60.`,
); );
warning( warning(
isSecondStepValid, isSecondStepValid.value,
`\`secondStep\` ${secondStep} is invalid. It should be a factor of 60.`, `\`secondStep\` ${secondStep} is invalid. It should be a factor of 60.`,
); );
});
} }
// ============================ State ============================= const panelContext = useInjectPanel();
const panelContext = React.useContext(PanelContext);
const { const {
operationRef, operationRef,
panelRef: panelDivRef, panelRef: panelDivRef,
@ -179,34 +189,30 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
hideRanges, hideRanges,
defaultOpenValue, defaultOpenValue,
} = panelContext; } = panelContext;
const { inRange, panelPosition, rangedValue, hoverRangedValue } = useInjectRange();
const { inRange, panelPosition, rangedValue, hoverRangedValue } = React.useContext(RangeContext); const panelRef = ref<PanelRefProps>({});
const panelRef = React.useRef<PanelRefProps>({});
// Handle init logic
const initRef = React.useRef(true);
// Value // Value
const [mergedValue, setInnerValue] = useMergedState(null, { const [mergedValue, setInnerValue] = useMergedState<DateType | null>(null, {
value, value: toRef(props, 'value'),
defaultValue, defaultValue: props.defaultValue,
postState: (val) => { postState: val => {
if (!val && defaultOpenValue && picker === 'time') { if (!val && defaultOpenValue.value && props.picker === 'time') {
return defaultOpenValue; return defaultOpenValue.value;
} }
return val; return val;
}, },
}); });
// View date control // View date control
const [viewDate, setInnerViewDate] = useMergedState<DateType | null, DateType>(null, { const [viewDate, setInnerViewDate] = useMergedState<DateType | null>(null, {
value: pickerValue, value: toRef(props, 'pickerValue'),
defaultValue: defaultPickerValue || mergedValue, defaultValue: props.defaultPickerValue || mergedValue.value,
postState: (date) => { postState: date => {
const { generateConfig, showTime, defaultValue } = props;
const now = generateConfig.getNow(); const now = generateConfig.getNow();
if (!date) return now; if (!date) return now;
// When value is null and set showTime // When value is null and set showTime
if (!mergedValue && showTime) { if (!mergedValue && props.showTime) {
if (typeof showTime === 'object') { if (typeof showTime === 'object') {
return setDateTime(generateConfig, date, showTime.defaultValue || now); return setDateTime(generateConfig, date, showTime.defaultValue || now);
} }
@ -221,14 +227,14 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
const setViewDate = (date: DateType) => { const setViewDate = (date: DateType) => {
setInnerViewDate(date); setInnerViewDate(date);
if (onPickerValueChange) { if (props.onPickerValueChange) {
onPickerValueChange(date); props.onPickerValueChange(date);
} }
}; };
// Panel control // Panel control
const getInternalNextMode = (nextMode: PanelMode): PanelMode => { const getInternalNextMode = (nextMode: PanelMode): PanelMode => {
const getNextMode = PickerModeMap[picker!]; const getNextMode = PickerModeMap[props.picker!];
if (getNextMode) { if (getNextMode) {
return getNextMode(nextMode); return getNextMode(nextMode);
} }
@ -239,28 +245,37 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
// Save panel is changed from which panel // Save panel is changed from which panel
const [mergedMode, setInnerMode] = useMergedState( const [mergedMode, setInnerMode] = useMergedState(
() => { () => {
if (picker === 'time') { if (props.picker === 'time') {
return 'time'; return 'time';
} }
return getInternalNextMode('date'); return getInternalNextMode('date');
}, },
{ {
value: mode, value: toRef(props, 'mode'),
},
);
watch(
() => props.picker,
() => {
setInnerMode(props.picker);
}, },
); );
React.useEffect(() => { const sourceMode = ref(mergedMode.value);
setInnerMode(picker); const setSourceMode = (val: PanelMode) => {
}, [picker]); sourceMode.value = val;
};
const [sourceMode, setSourceMode] = React.useState<PanelMode>(() => mergedMode);
const onInternalPanelChange = (newMode: PanelMode | null, viewValue: DateType) => { const onInternalPanelChange = (newMode: PanelMode | null, viewValue: DateType) => {
const nextMode = getInternalNextMode(newMode || mergedMode); const { onPanelChange, generateConfig } = props;
setSourceMode(mergedMode); const nextMode = getInternalNextMode(newMode || mergedMode.value);
setSourceMode(mergedMode.value);
setInnerMode(nextMode); setInnerMode(nextMode);
if (onPanelChange && (mergedMode !== nextMode || isEqual(generateConfig, viewDate, viewDate))) { if (
onPanelChange &&
(mergedMode.value !== nextMode || isEqual(generateConfig, viewDate.value, viewDate.value))
) {
onPanelChange(viewValue, nextMode); onPanelChange(viewValue, nextMode);
} }
}; };
@ -270,7 +285,8 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
type: 'key' | 'mouse' | 'submit', type: 'key' | 'mouse' | 'submit',
forceTriggerSelect: boolean = false, forceTriggerSelect: boolean = false,
) => { ) => {
if (mergedMode === picker || forceTriggerSelect) { const { picker, generateConfig, onSelect, onChange, disabledDate } = props;
if (mergedMode.value === picker || forceTriggerSelect) {
setInnerValue(date); setInnerValue(date);
if (onSelect) { if (onSelect) {
@ -281,15 +297,19 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
onContextSelect(date, type); onContextSelect(date, type);
} }
if (onChange && !isEqual(generateConfig, date, mergedValue) && !disabledDate?.(date)) { if (
onChange &&
!isEqual(generateConfig, date, mergedValue.value) &&
!disabledDate?.(date)
) {
onChange(date); onChange(date);
} }
} }
}; };
// ========================= Interactive ========================== // ========================= Interactive ==========================
const onInternalKeyDown = (e: React.KeyboardEvent<HTMLElement>) => { const onInternalKeyDown = (e: KeyboardEvent) => {
if (panelRef.current && panelRef.current.onKeyDown) { if (panelRef.value && panelRef.value.onKeyDown) {
if ( if (
[ [
KeyCode.LEFT, KeyCode.LEFT,
@ -303,7 +323,7 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
) { ) {
e.preventDefault(); e.preventDefault();
} }
return panelRef.current.onKeyDown(e); return panelRef.value.onKeyDown(e);
} }
/* istanbul ignore next */ /* istanbul ignore next */
@ -318,52 +338,99 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
/* eslint-enable no-lone-blocks */ /* eslint-enable no-lone-blocks */
}; };
const onInternalBlur: React.FocusEventHandler<HTMLElement> = (e) => { const onInternalBlur = (e: FocusEvent) => {
if (panelRef.current && panelRef.current.onBlur) { if (panelRef.value && panelRef.value.onBlur) {
panelRef.current.onBlur(e); panelRef.value.onBlur(e);
} }
}; };
const onNow = () => {
const { generateConfig, hourStep, minuteStep, secondStep } = props;
const now = generateConfig.getNow();
const lowerBoundTime = getLowerBoundTime(
generateConfig.getHour(now),
generateConfig.getMinute(now),
generateConfig.getSecond(now),
isHourStepValid ? hourStep : 1,
isMinuteStepValid ? minuteStep : 1,
isSecondStepValid ? secondStep : 1,
);
const adjustedNow = setTime(
generateConfig,
now,
lowerBoundTime[0], // hour
lowerBoundTime[1], // minute
lowerBoundTime[2], // second
);
triggerSelect(adjustedNow, 'submit');
};
if (operationRef && panelPosition !== 'right') { const classString = computed(() => {
operationRef.current = { const { prefixCls, direction } = props;
return classNames(`${prefixCls}-panel`, {
[`${prefixCls}-panel-has-range`]:
rangedValue && rangedValue.value && rangedValue.value[0] && rangedValue.value[1],
[`${prefixCls}-panel-has-range-hover`]:
hoverRangedValue &&
hoverRangedValue.value &&
hoverRangedValue.value[0] &&
hoverRangedValue.value[1],
[`${prefixCls}-panel-rtl`]: direction === 'rtl',
});
});
useProvidePanel({
...panelContext,
mode: mergedMode,
hideHeader: computed(() =>
props.hideHeader !== undefined ? props.hideHeader : panelContext.hideHeader?.value,
),
hidePrevBtn: computed(() => inRange.value && panelPosition.value === 'right'),
hideNextBtn: computed(() => inRange.value && panelPosition.value === 'left'),
});
return () => {
const {
prefixCls = 'ant-picker',
locale,
generateConfig,
disabledDate,
picker = 'date',
tabindex = 0,
showNow,
showTime,
showToday,
renderExtraFooter,
onMouseDown,
onOk,
components,
} = props;
if (operationRef && panelPosition.value !== 'right') {
operationRef.value = {
onKeyDown: onInternalKeyDown, onKeyDown: onInternalKeyDown,
onClose: () => { onClose: () => {
if (panelRef.current && panelRef.current.onClose) { if (panelRef.value && panelRef.value.onClose) {
panelRef.current.onClose(); panelRef.value.onClose();
} }
}, },
}; };
} }
// ============================ Effect ============================
React.useEffect(() => {
if (value && !initRef.current) {
setInnerViewDate(value);
}
}, [value]);
React.useEffect(() => {
initRef.current = false;
}, []);
// ============================ Panels ============================ // ============================ Panels ============================
let panelNode: React.ReactNode; let panelNode: VueNode;
const pickerProps = { const pickerProps = {
...(props as MergedPickerPanelProps<DateType>), ...(props as MergedPickerPanelProps<DateType>),
operationRef: panelRef, operationRef: panelRef,
prefixCls, prefixCls,
viewDate, viewDate: viewDate.value,
value: mergedValue, value: mergedValue.value,
onViewDateChange: setViewDate, onViewDateChange: setViewDate,
sourceMode, sourceMode: sourceMode.value,
onPanelChange: onInternalPanelChange, onPanelChange: onInternalPanelChange,
disabledDate, disabledDate,
}; };
delete pickerProps.onChange; delete pickerProps.onChange;
delete pickerProps.onSelect; delete pickerProps.onSelect;
switch (mergedMode) { switch (mergedMode.value) {
case 'decade': case 'decade':
panelNode = ( panelNode = (
<DecadePanel<DateType> <DecadePanel<DateType>
@ -463,53 +530,33 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
} }
// ============================ Footer ============================ // ============================ Footer ============================
let extraFooter: React.ReactNode; let extraFooter: VueNode;
let rangesNode: React.ReactNode; let rangesNode: VueNode;
const onNow = () => {
const now = generateConfig.getNow();
const lowerBoundTime = getLowerBoundTime(
generateConfig.getHour(now),
generateConfig.getMinute(now),
generateConfig.getSecond(now),
isHourStepValid ? hourStep : 1,
isMinuteStepValid ? minuteStep : 1,
isSecondStepValid ? secondStep : 1,
);
const adjustedNow = setTime(
generateConfig,
now,
lowerBoundTime[0], // hour
lowerBoundTime[1], // minute
lowerBoundTime[2], // second
);
triggerSelect(adjustedNow, 'submit');
};
if (!hideRanges) { if (!hideRanges) {
extraFooter = getExtraFooter(prefixCls, mergedMode, renderExtraFooter); extraFooter = getExtraFooter(prefixCls, mergedMode.value, renderExtraFooter);
rangesNode = getRanges({ rangesNode = getRanges({
prefixCls, prefixCls,
components, components,
needConfirmButton, needConfirmButton: needConfirmButton.value,
okDisabled: !mergedValue || (disabledDate && disabledDate(mergedValue)), okDisabled: !mergedValue || (disabledDate && disabledDate(mergedValue.value)),
locale, locale,
showNow, showNow,
onNow: needConfirmButton && onNow, onNow: needConfirmButton.value && onNow,
onOk: () => { onOk: () => {
if (mergedValue) { if (mergedValue) {
triggerSelect(mergedValue, 'submit', true); triggerSelect(mergedValue.value, 'submit', true);
if (onOk) { if (onOk) {
onOk(mergedValue); onOk(mergedValue.value);
} }
} }
}, },
}); });
} }
let todayNode: React.ReactNode; let todayNode: VueNode;
if (showToday && mergedMode === 'date' && picker === 'date' && !showTime) { if (showToday && mergedMode.value === 'date' && picker === 'date' && !showTime) {
const now = generateConfig.getNow(); const now = generateConfig.getNow();
const todayCls = `${prefixCls}-today-btn`; const todayCls = `${prefixCls}-today-btn`;
const disabled = disabledDate && disabledDate(now); const disabled = disabledDate && disabledDate(now);
@ -527,26 +574,11 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
</a> </a>
); );
} }
return ( return (
<PanelContext.Provider
value={{
...panelContext,
mode: mergedMode,
hideHeader: 'hideHeader' in props ? hideHeader : panelContext.hideHeader,
hidePrevBtn: inRange && panelPosition === 'right',
hideNextBtn: inRange && panelPosition === 'left',
}}
>
<div <div
tabindex={tabIndex} tabindex={tabindex}
class={classNames(`${prefixCls}-panel`, className, { class={classNames(classString.value, attrs.class)}
[`${prefixCls}-panel-has-range`]: rangedValue && rangedValue[0] && rangedValue[1], style={attrs.style}
[`${prefixCls}-panel-has-range-hover`]:
hoverRangedValue && hoverRangedValue[0] && hoverRangedValue[1],
[`${prefixCls}-panel-rtl`]: direction === 'rtl',
})}
style={style}
onKeydown={onInternalKeyDown} onKeydown={onInternalKeyDown}
onBlur={onInternalBlur} onBlur={onInternalBlur}
onMousedown={onMouseDown} onMousedown={onMouseDown}
@ -561,9 +593,10 @@ function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
</div> </div>
) : null} ) : null}
</div> </div>
</PanelContext.Provider>
); );
};
},
});
} }
export default PickerPanel; export default PickerPanel();
/* eslint-enable */

View File

@ -3,6 +3,7 @@ import { AlignType } from '../vc-align/interface';
import Trigger from '../vc-trigger'; import Trigger from '../vc-trigger';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import { VueNode } from '../_util/type'; import { VueNode } from '../_util/type';
import useMergeProps from './hooks/useMergeProps';
const BUILT_IN_PLACEMENTS = { const BUILT_IN_PLACEMENTS = {
bottomLeft: { bottomLeft: {
@ -56,8 +57,8 @@ export type PickerTriggerProps = {
direction?: 'ltr' | 'rtl'; direction?: 'ltr' | 'rtl';
}; };
function PickerTrigger( function PickerTrigger(props: PickerTriggerProps, { slots }) {
{ const {
prefixCls, prefixCls,
popupElement, popupElement,
popupStyle, popupStyle,
@ -69,9 +70,7 @@ function PickerTrigger(
range, range,
popupPlacement, popupPlacement,
direction, direction,
}: PickerTriggerProps, } = useMergeProps(props);
{ slots },
) {
const dropdownPrefixCls = `${prefixCls}-dropdown`; const dropdownPrefixCls = `${prefixCls}-dropdown`;
const getPopupPlacement = () => { const getPopupPlacement = () => {

View File

@ -1,4 +1,4 @@
import { inject, InjectionKey, provide } from 'vue'; import { inject, InjectionKey, provide, Ref } from 'vue';
import type { NullableDateType, RangeValue } from './interface'; import type { NullableDateType, RangeValue } from './interface';
export type RangeContextProps = { export type RangeContextProps = {
@ -6,13 +6,12 @@ export type RangeContextProps = {
* Set displayed range value style. * Set displayed range value style.
* Panel only has one value, this is only style effect. * Panel only has one value, this is only style effect.
*/ */
rangedValue?: [NullableDateType<any>, NullableDateType<any>] | null; rangedValue?: Ref<[NullableDateType<any>, NullableDateType<any>] | null>;
hoverRangedValue?: RangeValue<any>; hoverRangedValue?: Ref<RangeValue<any>>;
inRange?: boolean; inRange?: Ref<boolean>;
panelPosition?: 'left' | 'right' | false; panelPosition?: Ref<'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) => {
@ -23,5 +22,4 @@ export const useInjectRange = () => {
return inject(RangeContextKey); return inject(RangeContextKey);
}; };
export default RangeContextKey; export default RangeContextKey;

View File

@ -16,10 +16,7 @@ export default function useCellClassName<DateType>({
}: { }: {
cellPrefixCls: string; cellPrefixCls: string;
generateConfig: GenerateConfig<DateType>; generateConfig: GenerateConfig<DateType>;
isSameCell: ( isSameCell: (current: NullableDateType<DateType>, target: NullableDateType<DateType>) => boolean;
current: NullableDateType<DateType>,
target: NullableDateType<DateType>,
) => boolean;
offsetCell: (date: DateType, offset: number) => DateType; offsetCell: (date: DateType, offset: number) => DateType;
isInView: (date: DateType) => boolean; isInView: (date: DateType) => boolean;
rangedValue?: RangeValue<DateType>; rangedValue?: RangeValue<DateType>;
@ -37,12 +34,7 @@ export default function useCellClassName<DateType>({
const hoverStart = getValue(hoverRangedValue, 0); const hoverStart = getValue(hoverRangedValue, 0);
const hoverEnd = getValue(hoverRangedValue, 1); const hoverEnd = getValue(hoverRangedValue, 1);
const isRangeHovered = isInRange( const isRangeHovered = isInRange(generateConfig, hoverStart, hoverEnd, currentDate);
generateConfig,
hoverStart,
hoverEnd,
currentDate,
);
function isRangeStart(date: DateType) { function isRangeStart(date: DateType) {
return isSameCell(rangeStart, date); return isSameCell(rangeStart, date);
@ -54,11 +46,9 @@ export default function useCellClassName<DateType>({
const isHoverEnd = isSameCell(hoverEnd, currentDate); const isHoverEnd = isSameCell(hoverEnd, currentDate);
const isHoverEdgeStart = const isHoverEdgeStart =
(isRangeHovered || isHoverEnd) && (isRangeHovered || isHoverEnd) && (!isInView(prevDate) || isRangeEnd(prevDate));
(!isInView(prevDate) || isRangeEnd(prevDate));
const isHoverEdgeEnd = const isHoverEdgeEnd =
(isRangeHovered || isHoverStart) && (isRangeHovered || isHoverStart) && (!isInView(nextDate) || isRangeStart(nextDate));
(!isInView(nextDate) || isRangeStart(nextDate));
return { return {
// In view // In view
@ -73,10 +63,8 @@ export default function useCellClassName<DateType>({
), ),
[`${cellPrefixCls}-range-start`]: isRangeStart(currentDate), [`${cellPrefixCls}-range-start`]: isRangeStart(currentDate),
[`${cellPrefixCls}-range-end`]: isRangeEnd(currentDate), [`${cellPrefixCls}-range-end`]: isRangeEnd(currentDate),
[`${cellPrefixCls}-range-start-single`]: [`${cellPrefixCls}-range-start-single`]: isRangeStart(currentDate) && !rangeEnd,
isRangeStart(currentDate) && !rangeEnd, [`${cellPrefixCls}-range-end-single`]: isRangeEnd(currentDate) && !rangeStart,
[`${cellPrefixCls}-range-end-single`]:
isRangeEnd(currentDate) && !rangeStart,
[`${cellPrefixCls}-range-start-near-hover`]: [`${cellPrefixCls}-range-start-near-hover`]:
isRangeStart(currentDate) && isRangeStart(currentDate) &&
(isSameCell(prevDate, hoverStart) || (isSameCell(prevDate, hoverStart) ||

View File

@ -9,7 +9,7 @@ export default function useHoverValue<DateType>(
const [value, internalSetValue] = useState<DateType>(null); const [value, internalSetValue] = useState<DateType>(null);
const raf = useRef(null); const raf = useRef(null);
function setValue(val: DateType, immediately: boolean = false) { function setValue(val: DateType, immediately = false) {
cancelAnimationFrame(raf.current); cancelAnimationFrame(raf.current);
if (immediately) { if (immediately) {
internalSetValue(val); internalSetValue(val);
@ -30,7 +30,7 @@ export default function useHoverValue<DateType>(
setValue(date); setValue(date);
} }
function onLeave(immediately: boolean = false) { function onLeave(immediately = false) {
setValue(null, immediately); setValue(null, immediately);
} }

View File

@ -0,0 +1,8 @@
import type { HTMLAttributes } from 'vue';
import { useAttrs } from 'vue';
// 仅用在函数式组件中,不用考虑响应式问题
export default function useMergeProps<T>(props: T) {
const attrs: HTMLAttributes = useAttrs();
return { ...props, ...attrs };
}

View File

@ -1,6 +1,11 @@
import type * as React from 'react'; import type { ComputedRef, HTMLAttributes, Ref } from 'vue';
import { useState, useEffect, useRef } from 'react'; import { onBeforeUnmount } from 'vue';
import KeyCode from 'rc-util/lib/KeyCode'; import { watchEffect } from 'vue';
import { watch } from 'vue';
import { ref } from 'vue';
import { computed } from 'vue';
import type { FocusEventHandler } from '../../_util/EventInterface';
import KeyCode from '../../_util/KeyCode';
import { addGlobalMouseDownEvent, getTargetFromEvent } from '../utils/uiUtil'; import { addGlobalMouseDownEvent, getTargetFromEvent } from '../utils/uiUtil';
export default function usePickerInput({ export default function usePickerInput({
@ -16,51 +21,51 @@ export default function usePickerInput({
onFocus, onFocus,
onBlur, onBlur,
}: { }: {
open: boolean; open: Ref<boolean>;
value: string; value: Ref<string>;
isClickOutside: (clickElement: EventTarget | null) => boolean; isClickOutside: (clickElement: EventTarget | null) => boolean;
triggerOpen: (open: boolean) => void; triggerOpen: (open: boolean) => void;
forwardKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => boolean; forwardKeyDown: (e: KeyboardEvent) => boolean;
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>, preventDefault: () => void) => void; onKeyDown: (e: KeyboardEvent, preventDefault: () => void) => void;
blurToCancel?: boolean; blurToCancel?: ComputedRef<boolean>;
onSubmit: () => void | boolean; onSubmit: () => void | boolean;
onCancel: () => void; onCancel: () => void;
onFocus?: React.FocusEventHandler<HTMLInputElement>; onFocus?: FocusEventHandler;
onBlur?: React.FocusEventHandler<HTMLInputElement>; onBlur?: FocusEventHandler;
}): [React.DOMAttributes<HTMLInputElement>, { focused: boolean; typing: boolean }] { }): [ComputedRef<HTMLAttributes>, { focused: Ref<boolean>; typing: Ref<boolean> }] {
const [typing, setTyping] = useState(false); const typing = ref(false);
const [focused, setFocused] = useState(false); const focused = ref(false);
/** /**
* We will prevent blur to handle open event when user click outside, * We will prevent blur to handle open event when user click outside,
* since this will repeat trigger `onOpenChange` event. * since this will repeat trigger `onOpenChange` event.
*/ */
const preventBlurRef = useRef<boolean>(false); const preventBlurRef = ref<boolean>(false);
const valueChangedRef = useRef<boolean>(false); const valueChangedRef = ref<boolean>(false);
const preventDefaultRef = useRef<boolean>(false); const preventDefaultRef = ref<boolean>(false);
const inputProps: React.DOMAttributes<HTMLInputElement> = { const inputProps = computed<HTMLAttributes>(() => ({
onMouseDown: () => { onMousedown: () => {
setTyping(true); typing.value = true;
triggerOpen(true); triggerOpen(true);
}, },
onKeyDown: (e) => { onKeydown: e => {
const preventDefault = (): void => { const preventDefault = (): void => {
preventDefaultRef.current = true; preventDefaultRef.value = true;
}; };
onKeyDown(e, preventDefault); onKeyDown(e, preventDefault);
if (preventDefaultRef.current) return; if (preventDefaultRef.value) return;
switch (e.which) { switch (e.which) {
case KeyCode.ENTER: { case KeyCode.ENTER: {
if (!open) { if (!open.value) {
triggerOpen(true); triggerOpen(true);
} else if (onSubmit() !== false) { } else if (onSubmit() !== false) {
setTyping(true); typing.value = true;
} }
e.preventDefault(); e.preventDefault();
@ -68,12 +73,12 @@ export default function usePickerInput({
} }
case KeyCode.TAB: { case KeyCode.TAB: {
if (typing && open && !e.shiftKey) { if (typing.value && open.value && !e.shiftKey) {
setTyping(false); typing.value = false;
e.preventDefault(); e.preventDefault();
} else if (!typing && open) { } else if (!typing.value && open.value) {
if (!forwardKeyDown(e) && e.shiftKey) { if (!forwardKeyDown(e) && e.shiftKey) {
setTyping(true); typing.value = true;
e.preventDefault(); e.preventDefault();
} }
} }
@ -81,32 +86,32 @@ export default function usePickerInput({
} }
case KeyCode.ESC: { case KeyCode.ESC: {
setTyping(true); typing.value = true;
onCancel(); onCancel();
return; return;
} }
} }
if (!open && ![KeyCode.SHIFT].includes(e.which)) { if (!open.value && ![KeyCode.SHIFT].includes(e.which)) {
triggerOpen(true); triggerOpen(true);
} else if (!typing) { } else if (!typing.value) {
// Let popup panel handle keyboard // Let popup panel handle keyboard
forwardKeyDown(e); forwardKeyDown(e);
} }
}, },
onFocus: (e) => { onFocus: e => {
setTyping(true); typing.value = true;
setFocused(true); focused.value = true;
if (onFocus) { if (onFocus) {
onFocus(e); onFocus(e);
} }
}, },
onBlur: (e) => { onBlur: e => {
if (preventBlurRef.current || !isClickOutside(document.activeElement)) { if (preventBlurRef.value || !isClickOutside(document.activeElement)) {
preventBlurRef.current = false; preventBlurRef.value = false;
return; return;
} }
@ -121,51 +126,58 @@ export default function usePickerInput({
onCancel(); onCancel();
} }
}, 0); }, 0);
} else if (open) { } else if (open.value) {
triggerOpen(false); triggerOpen(false);
if (valueChangedRef.current) { if (valueChangedRef.value) {
onSubmit(); onSubmit();
} }
} }
setFocused(false); focused.value = false;
if (onBlur) { if (onBlur) {
onBlur(e); onBlur(e);
} }
}, },
}; }));
// check if value changed // check if value changed
useEffect(() => { watch(open, () => {
valueChangedRef.current = false; valueChangedRef.value = false;
}, [open]); });
useEffect(() => {
valueChangedRef.current = true;
}, [value]);
watch(value, () => {
valueChangedRef.value = true;
});
const globalMouseDownEvent = ref();
// Global click handler // Global click handler
useEffect(() => watchEffect(
addGlobalMouseDownEvent((e: MouseEvent) => { () =>
globalMouseDownEvent.value &&
globalMouseDownEvent.value()(
(globalMouseDownEvent.value = addGlobalMouseDownEvent((e: MouseEvent) => {
const target = getTargetFromEvent(e); const target = getTargetFromEvent(e);
if (open) { if (open) {
const clickedOutside = isClickOutside(target); const clickedOutside = isClickOutside(target);
if (!clickedOutside) { if (!clickedOutside) {
preventBlurRef.current = true; preventBlurRef.value = true;
// Always set back in case `onBlur` prevented by user // Always set back in case `onBlur` prevented by user
requestAnimationFrame(() => { requestAnimationFrame(() => {
preventBlurRef.current = false; preventBlurRef.value = false;
}); });
} else if (!focused || clickedOutside) { } else if (!focused.value || clickedOutside) {
triggerOpen(false); triggerOpen(false);
} }
} }
}), })),
),
); );
onBeforeUnmount(() => {
globalMouseDownEvent.value && globalMouseDownEvent.value();
});
return [inputProps, { focused, typing }]; return [inputProps, { focused, typing }];
} }

View File

@ -1,31 +1,36 @@
import * as React from 'react'; import type { ComputedRef, Ref } from 'vue';
import { ref, watch } from 'vue';
export default function useTextValueMapping({ export default function useTextValueMapping({
valueTexts, valueTexts,
onTextChange, onTextChange,
}: { }: {
/** Must useMemo, to assume that `valueTexts` only match on the first change */ /** Must useMemo, to assume that `valueTexts` only match on the first change */
valueTexts: string[]; valueTexts: ComputedRef<string[]>;
onTextChange: (text: string) => void; onTextChange: (text: string) => void;
}): [string, (text: string) => void, () => void] { }): [Ref<string>, (text: string) => void, () => void] {
const [text, setInnerText] = React.useState(''); const text = ref('');
const valueTextsRef = React.useRef<string[]>([]);
valueTextsRef.current = valueTexts;
function triggerTextChange(value: string) { function triggerTextChange(value: string) {
setInnerText(value); text.value = value;
onTextChange(value); onTextChange(value);
} }
function resetText() { function resetText() {
setInnerText(valueTextsRef.current[0]); text.value = valueTexts.value[0];
} }
React.useEffect(() => { watch(
if (valueTexts.every(valText => valText !== text)) { () => [...valueTexts.value],
(cur, pre) => {
if (
cur.join('||') !== pre.join('||') &&
valueTexts.value.every(valText => valText !== text.value)
) {
resetText(); resetText();
} }
}, [valueTexts.join('||')]); },
);
return [text, triggerTextChange, resetText]; return [text, triggerTextChange, resetText];
} }

View File

@ -1,32 +1,37 @@
import shallowEqual from 'shallowequal'; import type { ComputedRef, Ref } from 'vue';
import useMemo from 'rc-util/lib/hooks/useMemo'; import useMemo from '../../_util/hooks/useMemo';
import shallowequal from '../../_util/shallowequal';
import type { GenerateConfig } from '../generate'; import type { GenerateConfig } from '../generate';
import type { CustomFormat, Locale } from '../interface'; import type { CustomFormat, Locale } from '../interface';
import { formatValue } from '../utils/dateUtil'; import { formatValue } from '../utils/dateUtil';
export type ValueTextConfig<DateType> = { export type ValueTextConfig<DateType> = {
formatList: (string | CustomFormat<DateType>)[]; formatList: ComputedRef<(string | CustomFormat<DateType>)[]>;
generateConfig: GenerateConfig<DateType>; generateConfig: Ref<GenerateConfig<DateType>>;
locale: Locale; locale: Ref<Locale>;
}; };
export default function useValueTexts<DateType>( export default function useValueTexts<DateType>(
value: DateType | null, value: Ref<DateType | null>,
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>, { formatList, generateConfig, locale }: ValueTextConfig<DateType>,
) { ) {
return useMemo<[string[], string]>( return useMemo<[string[], string]>(
() => { () => {
if (!value) { if (!value.value) {
return [[''], '']; return [[''], ''];
} }
// We will convert data format back to first format // We will convert data format back to first format
let firstValueText: string = ''; let firstValueText = '';
const fullValueTexts: string[] = []; const fullValueTexts: string[] = [];
for (let i = 0; i < formatList.length; i += 1) { for (let i = 0; i < formatList.value.length; i += 1) {
const format = formatList[i]; const format = formatList[i];
const formatStr = formatValue(value, { generateConfig, locale, format }); const formatStr = formatValue(value.value, {
generateConfig: generateConfig.value,
locale: locale.value,
format,
});
fullValueTexts.push(formatStr); fullValueTexts.push(formatStr);
if (i === 0) { if (i === 0) {
@ -37,6 +42,6 @@ export default function useValueTexts<DateType>(
return [fullValueTexts, firstValueText]; return [fullValueTexts, firstValueText];
}, },
[value, formatList], [value, formatList],
(prev, next) => prev[0] !== next[0] || !shallowEqual(prev[1], next[1]), (next, prev) => prev[0] !== next[0] || !shallowequal(prev[1], next[1]),
); );
} }

View File

@ -1,3 +1,4 @@
import type { Ref } from 'vue';
import type { GenerateConfig } from './generate'; import type { GenerateConfig } from './generate';
export type Locale = { export type Locale = {
@ -46,7 +47,7 @@ export type PickerMode = Exclude<PanelMode, 'datetime' | 'decade'>;
export type PanelRefProps = { export type PanelRefProps = {
onKeyDown?: (e: KeyboardEvent) => boolean; onKeyDown?: (e: KeyboardEvent) => boolean;
onBlur?: (e: FocusEvent)=> void; onBlur?: (e: FocusEvent) => void;
onClose?: () => void; onClose?: () => void;
}; };
@ -74,7 +75,7 @@ export type PanelSharedProps<DateType> = {
// * Thus, move ref into operationRef. // * Thus, move ref into operationRef.
// * This is little hack which should refactor after typescript support. // * This is little hack which should refactor after typescript support.
// */ // */
// operationRef: React.MutableRefObject<PanelRefProps>; operationRef: Ref<PanelRefProps>;
onSelect: OnSelect<DateType>; onSelect: OnSelect<DateType>;
onViewDateChange: (value: DateType) => void; onViewDateChange: (value: DateType) => void;

View File

@ -1,4 +1,3 @@
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { import {
WEEK_DAY_COUNT, WEEK_DAY_COUNT,
@ -12,6 +11,7 @@ import useCellClassName from '../../hooks/useCellClassName';
import PanelBody from '../PanelBody'; import PanelBody from '../PanelBody';
import { VueNode } from '../../../_util/type'; import { VueNode } from '../../../_util/type';
import { useInjectRange } from '../../RangeContext'; import { useInjectRange } from '../../RangeContext';
import useMergeProps from '../../hooks/useMergeProps';
export type DateRender<DateType> = (currentDate: DateType, today: DateType) => VueNode; export type DateRender<DateType> = (currentDate: DateType, today: DateType) => VueNode;
@ -34,19 +34,12 @@ export type DateBodyProps<DateType> = {
onSelect: (value: DateType) => void; onSelect: (value: DateType) => void;
} & DateBodyPassProps<DateType>; } & DateBodyPassProps<DateType>;
function DateBody<DateType>(props: DateBodyProps<DateType>) { function DateBody<DateType>(_props: DateBodyProps<DateType>) {
const { const props = useMergeProps(_props);
prefixCls, const { prefixCls, generateConfig, prefixColumn, locale, rowCount, viewDate, value, dateRender } =
generateConfig, props;
prefixColumn,
locale,
rowCount,
viewDate,
value,
dateRender,
} = props;
const { rangedValue, hoverRangedValue } =useInjectRange() const { rangedValue, hoverRangedValue } = useInjectRange();
const baseDate = getWeekStartDate(locale.locale, generateConfig, viewDate); const baseDate = getWeekStartDate(locale.locale, generateConfig, viewDate);
const cellPrefixCls = `${prefixCls}-cell`; const cellPrefixCls = `${prefixCls}-cell`;
@ -74,8 +67,8 @@ function DateBody<DateType>(props: DateBodyProps<DateType>) {
today, today,
value, value,
generateConfig, generateConfig,
rangedValue: prefixColumn ? null : rangedValue, rangedValue: prefixColumn ? null : rangedValue.value,
hoverRangedValue: prefixColumn ? null : hoverRangedValue, hoverRangedValue: prefixColumn ? null : hoverRangedValue.value,
isSameCell: (current, target) => isSameDate(generateConfig, current, target), isSameCell: (current, target) => isSameDate(generateConfig, current, target),
isInView: date => isSameMonth(generateConfig, date, viewDate), isInView: date => isSameMonth(generateConfig, date, viewDate),
offsetCell: (date, offset) => generateConfig.addDate(date, offset), offsetCell: (date, offset) => generateConfig.addDate(date, offset),
@ -105,7 +98,20 @@ function DateBody<DateType>(props: DateBodyProps<DateType>) {
); );
} }
DateBody.displayName = 'DateBody' DateBody.displayName = 'DateBody';
DateBody.inheritAttrs = false; DateBody.inheritAttrs = false;
DateBody.props = [
'prefixCls',
'generateConfig',
'value?',
'viewDate',
'locale',
'rowCount',
'onSelect',
'dateRender?',
'disabledDate?',
// Used for week panel
'prefixColumn?',
'rowClassName?',
];
export default DateBody; export default DateBody;

View File

@ -1,10 +1,10 @@
import Header from '../Header'; import Header from '../Header';
import type { Locale } from '../../interface'; import type { Locale } from '../../interface';
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { useInjectPanel } from '../../PanelContext'; import { useInjectPanel } from '../../PanelContext';
import { formatValue } from '../../utils/dateUtil'; import { formatValue } from '../../utils/dateUtil';
import { VueNode } from '../../../_util/type'; import { VueNode } from '../../../_util/type';
import useMergeProps from '../../hooks/useMergeProps';
export type DateHeaderProps<DateType> = { export type DateHeaderProps<DateType> = {
prefixCls: string; prefixCls: string;
@ -21,7 +21,8 @@ export type DateHeaderProps<DateType> = {
onMonthClick: () => void; onMonthClick: () => void;
}; };
function DateHeader<DateType>(props: DateHeaderProps<DateType>) { function DateHeader<DateType>(_props: DateHeaderProps<DateType>) {
const props = useMergeProps(_props);
const { const {
prefixCls, prefixCls,
generateConfig, generateConfig,
@ -35,8 +36,8 @@ function DateHeader<DateType>(props: DateHeaderProps<DateType>) {
onMonthClick, onMonthClick,
} = props; } = props;
const { hideHeader } = useInjectPanel() const { hideHeader } = useInjectPanel();
if (hideHeader) { if (hideHeader.value) {
return null; return null;
} }
@ -100,6 +101,6 @@ function DateHeader<DateType>(props: DateHeaderProps<DateType>) {
); );
} }
DateHeader.displayName = 'DateHeader' DateHeader.displayName = 'DateHeader';
DateHeader.inheritAttrs = false; DateHeader.inheritAttrs = false;
export default DateHeader; export default DateHeader;

View File

@ -1,4 +1,3 @@
import type { DateBodyPassProps, DateRender } from './DateBody'; import type { DateBodyPassProps, DateRender } from './DateBody';
import DateBody from './DateBody'; import DateBody from './DateBody';
import DateHeader from './DateHeader'; import DateHeader from './DateHeader';
@ -8,6 +7,7 @@ import type { KeyboardConfig } from '../../utils/uiUtil';
import { createKeyDownHandler } from '../../utils/uiUtil'; import { createKeyDownHandler } from '../../utils/uiUtil';
import classNames from '../../../_util/classNames'; import classNames from '../../../_util/classNames';
import { ref } from '@vue/reactivity'; import { ref } from '@vue/reactivity';
import useMergeProps from '../../hooks/useMergeProps';
const DATE_ROW_COUNT = 6; const DATE_ROW_COUNT = 6;
@ -18,14 +18,17 @@ export type DatePanelProps<DateType> = {
// Used for week panel // Used for week panel
panelName?: string; panelName?: string;
keyboardConfig?: KeyboardConfig; keyboardConfig?: KeyboardConfig;
} & PanelSharedProps<DateType> & DateBodyPassProps<DateType>; } & PanelSharedProps<DateType> &
DateBodyPassProps<DateType>;
function DatePanel<DateType>(props: DatePanelProps<DateType>) { function DatePanel<DateType>(_props: DatePanelProps<DateType>) {
const props = useMergeProps(_props);
const { const {
prefixCls, prefixCls,
panelName = 'date', panelName = 'date',
keyboardConfig, keyboardConfig,
active, active,
operationRef,
generateConfig, generateConfig,
value, value,
viewDate, viewDate,
@ -34,10 +37,9 @@ function DatePanel<DateType>(props: DatePanelProps<DateType>) {
onSelect, onSelect,
} = props; } = props;
const panelPrefixCls = `${prefixCls}-${panelName}-panel`; const panelPrefixCls = `${prefixCls}-${panelName}-panel`;
const operationRef = ref()
// ======================= Keyboard ======================= // ======================= Keyboard =======================
operationRef.value = { operationRef.value = {
onKeyDown: event => onKeyDown: (event: KeyboardEvent) =>
createKeyDownHandler(event, { createKeyDownHandler(event, {
onLeftRight: diff => { onLeftRight: diff => {
onSelect(generateConfig.addDate(value || viewDate, diff), 'key'); onSelect(generateConfig.addDate(value || viewDate, diff), 'key');
@ -110,7 +112,7 @@ function DatePanel<DateType>(props: DatePanelProps<DateType>) {
); );
} }
DatePanel.displayName ='DatePanel' DatePanel.displayName = 'DatePanel';
DatePanel.inheritAttrs = false; DatePanel.inheritAttrs = false;
export default DatePanel; export default DatePanel;

View File

@ -1,4 +1,3 @@
import type { DatePanelProps } from '../DatePanel'; import type { DatePanelProps } from '../DatePanel';
import DatePanel from '../DatePanel'; import DatePanel from '../DatePanel';
import type { SharedTimeProps } from '../TimePanel'; import type { SharedTimeProps } from '../TimePanel';
@ -9,20 +8,19 @@ import type { PanelRefProps, DisabledTime } from '../../interface';
import KeyCode from '../../../_util/KeyCode'; import KeyCode from '../../../_util/KeyCode';
import classNames from '../../../_util/classNames'; import classNames from '../../../_util/classNames';
import { ref } from '@vue/reactivity'; import { ref } from '@vue/reactivity';
import useMergeProps from '../../hooks/useMergeProps';
export type DatetimePanelProps<DateType> = { export type DatetimePanelProps<DateType> = {
disabledTime?: DisabledTime<DateType>; disabledTime?: DisabledTime<DateType>;
showTime?: boolean | SharedTimeProps<DateType>; showTime?: boolean | SharedTimeProps<DateType>;
defaultValue?: DateType; defaultValue?: DateType;
} & Omit< } & Omit<DatePanelProps<DateType>, 'disabledHours' | 'disabledMinutes' | 'disabledSeconds'>;
DatePanelProps<DateType>,
'disabledHours' | 'disabledMinutes' | 'disabledSeconds'
>;
const ACTIVE_PANEL = tuple('date', 'time'); const ACTIVE_PANEL = tuple('date', 'time');
type ActivePanelType = typeof ACTIVE_PANEL[number]; type ActivePanelType = typeof ACTIVE_PANEL[number];
function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) { function DatetimePanel<DateType>(_props: DatetimePanelProps<DateType>) {
const props = useMergeProps(_props);
const { const {
prefixCls, prefixCls,
operationRef, operationRef,
@ -34,9 +32,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
onSelect, onSelect,
} = props; } = props;
const panelPrefixCls = `${prefixCls}-datetime-panel`; const panelPrefixCls = `${prefixCls}-datetime-panel`;
const activePanel = ref<ActivePanelType | null>( const activePanel = ref<ActivePanelType | null>(null);
null,
);
const dateOperationRef = ref<PanelRefProps>({}); const dateOperationRef = ref<PanelRefProps>({});
const timeOperationRef = ref<PanelRefProps>({}); const timeOperationRef = ref<PanelRefProps>({});
@ -57,12 +53,12 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
activePanel.value = null; activePanel.value = null;
}; };
operationRef.current = { operationRef.value = {
onKeyDown: event => { onKeyDown: (event: KeyboardEvent) => {
// Switch active panel // Switch active panel
if (event.which === KeyCode.TAB) { if (event.which === KeyCode.TAB) {
const nextActivePanel = getNextActive(event.shiftKey ? -1 : 1); const nextActivePanel = getNextActive(event.shiftKey ? -1 : 1);
activePanel.value = nextActivePanel activePanel.value = nextActivePanel;
if (nextActivePanel) { if (nextActivePanel) {
event.preventDefault(); event.preventDefault();
@ -73,8 +69,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
// Operate on current active panel // Operate on current active panel
if (activePanel.value) { if (activePanel.value) {
const ref = const ref = activePanel.value === 'date' ? dateOperationRef : timeOperationRef;
activePanel.value === 'date' ? dateOperationRef : timeOperationRef;
if (ref.value && ref.value.onKeyDown) { if (ref.value && ref.value.onKeyDown) {
ref.value.onKeyDown(event); ref.value.onKeyDown(event);
@ -84,12 +79,8 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
} }
// Switch first active panel if operate without panel // Switch first active panel if operate without panel
if ( if ([KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN].includes(event.which)) {
[KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN].includes( activePanel.value = 'date';
event.which,
)
) {
activePanel.value = 'date'
return true; return true;
} }
@ -118,18 +109,9 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
generateConfig.getSecond(timeProps.defaultValue), generateConfig.getSecond(timeProps.defaultValue),
); );
} else if (source === 'time' && !value && defaultValue) { } else if (source === 'time' && !value && defaultValue) {
selectedDate = generateConfig.setYear( selectedDate = generateConfig.setYear(selectedDate, generateConfig.getYear(defaultValue));
selectedDate, selectedDate = generateConfig.setMonth(selectedDate, generateConfig.getMonth(defaultValue));
generateConfig.getYear(defaultValue), selectedDate = generateConfig.setDate(selectedDate, generateConfig.getDate(defaultValue));
);
selectedDate = generateConfig.setMonth(
selectedDate,
generateConfig.getMonth(defaultValue),
);
selectedDate = generateConfig.setDate(
selectedDate,
generateConfig.getDate(defaultValue),
);
} }
if (onSelect) { if (onSelect) {
@ -155,9 +137,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
setTime( setTime(
generateConfig, generateConfig,
date, date,
showTime && typeof showTime === 'object' showTime && typeof showTime === 'object' ? showTime.defaultValue : null,
? showTime.defaultValue
: null,
), ),
'date', 'date',
); );
@ -179,8 +159,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
); );
} }
DatetimePanel.displayName = 'DatetimePanel';
DatetimePanel.displayName ='DatetimePanel'
DatetimePanel.inheritAttrs = false; DatetimePanel.inheritAttrs = false;
export default DatetimePanel; export default DatetimePanel;

View File

@ -1,7 +1,7 @@
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { DECADE_DISTANCE_COUNT, DECADE_UNIT_DIFF } from '.'; import { DECADE_DISTANCE_COUNT, DECADE_UNIT_DIFF } from '.';
import PanelBody from '../PanelBody'; import PanelBody from '../PanelBody';
import useMergeProps from '../../hooks/useMergeProps';
export const DECADE_COL_COUNT = 3; export const DECADE_COL_COUNT = 3;
const DECADE_ROW_COUNT = 4; const DECADE_ROW_COUNT = 4;
@ -14,7 +14,8 @@ export type YearBodyProps<DateType> = {
onSelect: (value: DateType) => void; onSelect: (value: DateType) => void;
}; };
function DecadeBody<DateType>(props: YearBodyProps<DateType>) { function DecadeBody<DateType>(_props: YearBodyProps<DateType>) {
const props = useMergeProps(_props);
const DECADE_UNIT_DIFF_DES = DECADE_UNIT_DIFF - 1; const DECADE_UNIT_DIFF_DES = DECADE_UNIT_DIFF - 1;
const { prefixCls, viewDate, generateConfig } = props; const { prefixCls, viewDate, generateConfig } = props;
@ -61,8 +62,7 @@ function DecadeBody<DateType>(props: YearBodyProps<DateType>) {
); );
} }
DecadeBody.displayName = 'DecadeBody';
DecadeBody.displayName ='DecadeBody'
DecadeBody.inheritAttrs = false; DecadeBody.inheritAttrs = false;
export default DecadeBody; export default DecadeBody;

View File

@ -1,8 +1,8 @@
import Header from '../Header'; import Header from '../Header';
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { DECADE_DISTANCE_COUNT } from '.'; import { DECADE_DISTANCE_COUNT } from '.';
import { useInjectPanel } from '../../PanelContext'; import { useInjectPanel } from '../../PanelContext';
import useMergeProps from '../../hooks/useMergeProps';
export type YearHeaderProps<DateType> = { export type YearHeaderProps<DateType> = {
prefixCls: string; prefixCls: string;
@ -13,15 +13,10 @@ export type YearHeaderProps<DateType> = {
onNextDecades: () => void; onNextDecades: () => void;
}; };
function DecadeHeader<DateType>(props: YearHeaderProps<DateType>) { function DecadeHeader<DateType>(_props: YearHeaderProps<DateType>) {
const { const props = useMergeProps(_props);
prefixCls, const { prefixCls, generateConfig, viewDate, onPrevDecades, onNextDecades } = props;
generateConfig, const { hideHeader } = useInjectPanel();
viewDate,
onPrevDecades,
onNextDecades,
} = props;
const { hideHeader } =useInjectPanel()
if (hideHeader) { if (hideHeader) {
return null; return null;
} }
@ -29,8 +24,7 @@ function DecadeHeader<DateType>(props: YearHeaderProps<DateType>) {
const headerPrefixCls = `${prefixCls}-header`; const headerPrefixCls = `${prefixCls}-header`;
const yearNumber = generateConfig.getYear(viewDate); const yearNumber = generateConfig.getYear(viewDate);
const startYear = const startYear = Math.floor(yearNumber / DECADE_DISTANCE_COUNT) * DECADE_DISTANCE_COUNT;
Math.floor(yearNumber / DECADE_DISTANCE_COUNT) * DECADE_DISTANCE_COUNT;
const endYear = startYear + DECADE_DISTANCE_COUNT - 1; const endYear = startYear + DECADE_DISTANCE_COUNT - 1;
return ( return (
@ -45,7 +39,7 @@ function DecadeHeader<DateType>(props: YearHeaderProps<DateType>) {
); );
} }
DecadeHeader.displayName ='DecadeHeader' DecadeHeader.displayName = 'DecadeHeader';
DecadeHeader.inheritAttrs = false; DecadeHeader.inheritAttrs = false;
export default DecadeHeader; export default DecadeHeader;

View File

@ -1,15 +1,16 @@
import DecadeHeader from './DecadeHeader'; import DecadeHeader from './DecadeHeader';
import DecadeBody, { DECADE_COL_COUNT } from './DecadeBody'; import DecadeBody, { DECADE_COL_COUNT } from './DecadeBody';
import type { PanelSharedProps } from '../../interface'; import type { PanelSharedProps } from '../../interface';
import { createKeyDownHandler } from '../../utils/uiUtil'; import { createKeyDownHandler } from '../../utils/uiUtil';
import useMergeProps from '../../hooks/useMergeProps';
export type DecadePanelProps<DateType> = PanelSharedProps<DateType>; export type DecadePanelProps<DateType> = PanelSharedProps<DateType>;
export const DECADE_UNIT_DIFF = 10; export const DECADE_UNIT_DIFF = 10;
export const DECADE_DISTANCE_COUNT = DECADE_UNIT_DIFF * 10; export const DECADE_DISTANCE_COUNT = DECADE_UNIT_DIFF * 10;
function DecadePanel<DateType>(props: DecadePanelProps<DateType>) { function DecadePanel<DateType>(_props: DecadePanelProps<DateType>) {
const props = useMergeProps(_props);
const { const {
prefixCls, prefixCls,
onViewDateChange, onViewDateChange,
@ -23,27 +24,18 @@ function DecadePanel<DateType>(props: DecadePanelProps<DateType>) {
const panelPrefixCls = `${prefixCls}-decade-panel`; const panelPrefixCls = `${prefixCls}-decade-panel`;
// ======================= Keyboard ======================= // ======================= Keyboard =======================
operationRef.current = { operationRef.value = {
onKeyDown: event => onKeyDown: (event: KeyboardEvent) =>
createKeyDownHandler(event, { createKeyDownHandler(event, {
onLeftRight: diff => { onLeftRight: diff => {
onSelect( onSelect(generateConfig.addYear(viewDate, diff * DECADE_UNIT_DIFF), 'key');
generateConfig.addYear(viewDate, diff * DECADE_UNIT_DIFF),
'key',
);
}, },
onCtrlLeftRight: diff => { onCtrlLeftRight: diff => {
onSelect( onSelect(generateConfig.addYear(viewDate, diff * DECADE_DISTANCE_COUNT), 'key');
generateConfig.addYear(viewDate, diff * DECADE_DISTANCE_COUNT),
'key',
);
}, },
onUpDown: diff => { onUpDown: diff => {
onSelect( onSelect(
generateConfig.addYear( generateConfig.addYear(viewDate, diff * DECADE_UNIT_DIFF * DECADE_COL_COUNT),
viewDate,
diff * DECADE_UNIT_DIFF * DECADE_COL_COUNT,
),
'key', 'key',
); );
}, },
@ -55,10 +47,7 @@ function DecadePanel<DateType>(props: DecadePanelProps<DateType>) {
// ==================== View Operation ==================== // ==================== View Operation ====================
const onDecadesChange = (diff: number) => { const onDecadesChange = (diff: number) => {
const newDate = generateConfig.addYear( const newDate = generateConfig.addYear(viewDate, diff * DECADE_DISTANCE_COUNT);
viewDate,
diff * DECADE_DISTANCE_COUNT,
);
onViewDateChange(newDate); onViewDateChange(newDate);
onPanelChange(null, newDate); onPanelChange(null, newDate);
}; };
@ -80,17 +69,12 @@ function DecadePanel<DateType>(props: DecadePanelProps<DateType>) {
onDecadesChange(1); onDecadesChange(1);
}} }}
/> />
<DecadeBody <DecadeBody {...props} prefixCls={prefixCls} onSelect={onInternalSelect} />
{...props}
prefixCls={prefixCls}
onSelect={onInternalSelect}
/>
</div> </div>
); );
} }
DecadePanel.displayName = 'DecadePanel';
DecadePanel.displayName ='DecadePanel'
DecadePanel.inheritAttrs = false; DecadePanel.inheritAttrs = false;
export default DecadePanel; export default DecadePanel;

View File

@ -1,5 +1,6 @@
import { CSSProperties } from '@vue/runtime-dom'; import { CSSProperties } from 'vue';
import { VueNode } from '../../_util/type'; import { VueNode } from '../../_util/type';
import useMergeProps from '../hooks/useMergeProps';
import { useInjectPanel } from '../PanelContext'; import { useInjectPanel } from '../PanelContext';
const HIDDEN_STYLE: CSSProperties = { const HIDDEN_STYLE: CSSProperties = {
@ -27,8 +28,9 @@ export type HeaderProps = {
children?: VueNode; children?: VueNode;
}; };
function Header( function Header(_props: HeaderProps, { slots }) {
{ const props = useMergeProps(_props);
const {
prefixCls, prefixCls,
prevIcon = '\u2039', prevIcon = '\u2039',
nextIcon = '\u203A', nextIcon = '\u203A',
@ -38,9 +40,7 @@ function Header(
onSuperNext, onSuperNext,
onPrev, onPrev,
onNext, onNext,
}: HeaderProps, } = props;
{ slots },
) {
const { hideNextBtn, hidePrevBtn } = useInjectPanel(); const { hideNextBtn, hidePrevBtn } = useInjectPanel();
return ( return (
@ -51,7 +51,7 @@ function Header(
onClick={onSuperPrev} onClick={onSuperPrev}
tabindex={-1} tabindex={-1}
class={`${prefixCls}-super-prev-btn`} class={`${prefixCls}-super-prev-btn`}
style={hidePrevBtn ? HIDDEN_STYLE : {}} style={hidePrevBtn.value ? HIDDEN_STYLE : {}}
> >
{superPrevIcon} {superPrevIcon}
</button> </button>
@ -62,7 +62,7 @@ function Header(
onClick={onPrev} onClick={onPrev}
tabindex={-1} tabindex={-1}
class={`${prefixCls}-prev-btn`} class={`${prefixCls}-prev-btn`}
style={hidePrevBtn ? HIDDEN_STYLE : {}} style={hidePrevBtn.value ? HIDDEN_STYLE : {}}
> >
{prevIcon} {prevIcon}
</button> </button>
@ -74,7 +74,7 @@ function Header(
onClick={onNext} onClick={onNext}
tabindex={-1} tabindex={-1}
class={`${prefixCls}-next-btn`} class={`${prefixCls}-next-btn`}
style={hideNextBtn ? HIDDEN_STYLE : {}} style={hideNextBtn.value ? HIDDEN_STYLE : {}}
> >
{nextIcon} {nextIcon}
</button> </button>
@ -85,7 +85,7 @@ function Header(
onClick={onSuperNext} onClick={onSuperNext}
tabindex={-1} tabindex={-1}
class={`${prefixCls}-super-next-btn`} class={`${prefixCls}-super-next-btn`}
style={hideNextBtn ? HIDDEN_STYLE : {}} style={hideNextBtn.value ? HIDDEN_STYLE : {}}
> >
{superNextIcon} {superNextIcon}
</button> </button>

View File

@ -1,4 +1,3 @@
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import type { Locale } from '../../interface'; import type { Locale } from '../../interface';
import { formatValue, isSameMonth } from '../../utils/dateUtil'; import { formatValue, isSameMonth } from '../../utils/dateUtil';
@ -6,6 +5,7 @@ import { useInjectRange } from '../../RangeContext';
import useCellClassName from '../../hooks/useCellClassName'; import useCellClassName from '../../hooks/useCellClassName';
import PanelBody from '../PanelBody'; import PanelBody from '../PanelBody';
import { VueNode } from '../../../_util/type'; import { VueNode } from '../../../_util/type';
import useMergeProps from '../../hooks/useMergeProps';
export const MONTH_COL_COUNT = 3; export const MONTH_COL_COUNT = 3;
const MONTH_ROW_COUNT = 4; const MONTH_ROW_COUNT = 4;
@ -23,10 +23,11 @@ export type MonthBodyProps<DateType> = {
onSelect: (value: DateType) => void; onSelect: (value: DateType) => void;
}; };
function MonthBody<DateType>(props: MonthBodyProps<DateType>) { function MonthBody<DateType>(_props: MonthBodyProps<DateType>) {
const props = useMergeProps(_props);
const { prefixCls, locale, value, viewDate, generateConfig, monthCellRender } = props; const { prefixCls, locale, value, viewDate, generateConfig, monthCellRender } = props;
const { rangedValue, hoverRangedValue } = useInjectRange() const { rangedValue, hoverRangedValue } = useInjectRange();
const cellPrefixCls = `${prefixCls}-cell`; const cellPrefixCls = `${prefixCls}-cell`;
@ -34,8 +35,8 @@ function MonthBody<DateType>(props: MonthBodyProps<DateType>) {
cellPrefixCls, cellPrefixCls,
value, value,
generateConfig, generateConfig,
rangedValue, rangedValue: rangedValue.value,
hoverRangedValue, hoverRangedValue: hoverRangedValue.value,
isSameCell: (current, target) => isSameMonth(generateConfig, current, target), isSameCell: (current, target) => isSameMonth(generateConfig, current, target),
isInView: () => true, isInView: () => true,
offsetCell: (date, offset) => generateConfig.addMonth(date, offset), offsetCell: (date, offset) => generateConfig.addMonth(date, offset),
@ -82,8 +83,7 @@ function MonthBody<DateType>(props: MonthBodyProps<DateType>) {
); );
} }
MonthBody.displayName = 'MonthBody';
MonthBody.displayName ='MonthBody'
MonthBody.inheritAttrs = false; MonthBody.inheritAttrs = false;
export default MonthBody; export default MonthBody;

View File

@ -1,9 +1,9 @@
import Header from '../Header'; import Header from '../Header';
import type { Locale } from '../../interface'; import type { Locale } from '../../interface';
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { useInjectPanel } from '../../PanelContext'; import { useInjectPanel } from '../../PanelContext';
import { formatValue } from '../../utils/dateUtil'; import { formatValue } from '../../utils/dateUtil';
import useMergeProps from '../../hooks/useMergeProps';
export type MonthHeaderProps<DateType> = { export type MonthHeaderProps<DateType> = {
prefixCls: string; prefixCls: string;
@ -16,18 +16,12 @@ export type MonthHeaderProps<DateType> = {
onYearClick: () => void; onYearClick: () => void;
}; };
function MonthHeader<DateType>(props: MonthHeaderProps<DateType>) { function MonthHeader<DateType>(_props: MonthHeaderProps<DateType>) {
const { const props = useMergeProps(_props);
prefixCls, const { prefixCls, generateConfig, locale, viewDate, onNextYear, onPrevYear, onYearClick } =
generateConfig, props;
locale, const { hideHeader } = useInjectPanel();
viewDate, if (hideHeader.value) {
onNextYear,
onPrevYear,
onYearClick,
} = props;
const { hideHeader } = useInjectPanel()
if (hideHeader) {
return null; return null;
} }
@ -51,8 +45,7 @@ function MonthHeader<DateType>(props: MonthHeaderProps<DateType>) {
); );
} }
MonthHeader.displayName = 'MonthHeader';
MonthHeader.displayName ='MonthHeader'
MonthHeader.inheritAttrs = false; MonthHeader.inheritAttrs = false;
export default MonthHeader; export default MonthHeader;

View File

@ -1,15 +1,16 @@
import MonthHeader from './MonthHeader'; import MonthHeader from './MonthHeader';
import type { MonthCellRender } from './MonthBody'; import type { MonthCellRender } from './MonthBody';
import MonthBody, { MONTH_COL_COUNT } from './MonthBody'; import MonthBody, { MONTH_COL_COUNT } from './MonthBody';
import type { PanelSharedProps } from '../../interface'; import type { PanelSharedProps } from '../../interface';
import { createKeyDownHandler } from '../../utils/uiUtil'; import { createKeyDownHandler } from '../../utils/uiUtil';
import useMergeProps from '../../hooks/useMergeProps';
export type MonthPanelProps<DateType> = { export type MonthPanelProps<DateType> = {
monthCellContentRender?: MonthCellRender<DateType>; monthCellContentRender?: MonthCellRender<DateType>;
} & PanelSharedProps<DateType>; } & PanelSharedProps<DateType>;
function MonthPanel<DateType>(props: MonthPanelProps<DateType>) { function MonthPanel<DateType>(_props: MonthPanelProps<DateType>) {
const props = useMergeProps(_props);
const { const {
prefixCls, prefixCls,
operationRef, operationRef,
@ -24,8 +25,8 @@ function MonthPanel<DateType>(props: MonthPanelProps<DateType>) {
const panelPrefixCls = `${prefixCls}-month-panel`; const panelPrefixCls = `${prefixCls}-month-panel`;
// ======================= Keyboard ======================= // ======================= Keyboard =======================
operationRef.current = { operationRef.value = {
onKeyDown: event => onKeyDown: (event: KeyboardEvent) =>
createKeyDownHandler(event, { createKeyDownHandler(event, {
onLeftRight: diff => { onLeftRight: diff => {
onSelect(generateConfig.addMonth(value || viewDate, diff), 'key'); onSelect(generateConfig.addMonth(value || viewDate, diff), 'key');
@ -34,10 +35,7 @@ function MonthPanel<DateType>(props: MonthPanelProps<DateType>) {
onSelect(generateConfig.addYear(value || viewDate, diff), 'key'); onSelect(generateConfig.addYear(value || viewDate, diff), 'key');
}, },
onUpDown: diff => { onUpDown: diff => {
onSelect( onSelect(generateConfig.addMonth(value || viewDate, diff * MONTH_COL_COUNT), 'key');
generateConfig.addMonth(value || viewDate, diff * MONTH_COL_COUNT),
'key',
);
}, },
onEnter: () => { onEnter: () => {
onPanelChange('date', value || viewDate); onPanelChange('date', value || viewDate);
@ -79,8 +77,7 @@ function MonthPanel<DateType>(props: MonthPanelProps<DateType>) {
); );
} }
MonthPanel.displayName = 'MonthPanel';
MonthPanel.displayName ='MonthPanel'
MonthPanel.inheritAttrs = false; MonthPanel.inheritAttrs = false;
export default MonthPanel; export default MonthPanel;

View File

@ -1,4 +1,3 @@
import { useInjectPanel } from '../PanelContext'; import { useInjectPanel } from '../PanelContext';
import type { GenerateConfig } from '../generate'; import type { GenerateConfig } from '../generate';
import { getLastDay } from '../utils/timeUtil'; import { getLastDay } from '../utils/timeUtil';
@ -6,6 +5,7 @@ import type { PanelMode } from '../interface';
import { getCellDateDisabled } from '../utils/dateUtil'; import { getCellDateDisabled } from '../utils/dateUtil';
import { VueNode } from '../../_util/type'; import { VueNode } from '../../_util/type';
import classNames from '../../_util/classNames'; import classNames from '../../_util/classNames';
import useMergeProps from '../hooks/useMergeProps';
export type PanelBodyProps<DateType> = { export type PanelBodyProps<DateType> = {
prefixCls: string; prefixCls: string;
@ -30,7 +30,8 @@ export type PanelBodyProps<DateType> = {
rowClassName?: (date: DateType) => string; rowClassName?: (date: DateType) => string;
}; };
function PanelBody<DateType>({ function PanelBody<DateType>(_props: PanelBodyProps<DateType>) {
const {
prefixCls, prefixCls,
disabledDate, disabledDate,
onSelect, onSelect,
@ -47,8 +48,8 @@ function PanelBody<DateType>({
generateConfig, generateConfig,
titleCell, titleCell,
headerCells, headerCells,
}: PanelBodyProps<DateType>) { } = useMergeProps(_props);
const { onDateMouseEnter, onDateMouseLeave, mode } = useInjectPanel() const { onDateMouseEnter, onDateMouseLeave, mode } = useInjectPanel();
const cellPrefixCls = `${prefixCls}-cell`; const cellPrefixCls = `${prefixCls}-cell`;
@ -64,7 +65,7 @@ function PanelBody<DateType>({
const currentDate = getCellDate(baseDate, offset); const currentDate = getCellDate(baseDate, offset);
const disabled = getCellDateDisabled({ const disabled = getCellDateDisabled({
cellDate: currentDate, cellDate: currentDate,
mode, mode: mode.value,
disabledDate, disabledDate,
generateConfig, generateConfig,
}); });

View File

@ -1,10 +1,10 @@
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import type { Locale } from '../../interface'; import type { Locale } from '../../interface';
import { formatValue, isSameQuarter } from '../../utils/dateUtil'; import { formatValue, isSameQuarter } from '../../utils/dateUtil';
import RangeContext, { useInjectRange } from '../../RangeContext'; import RangeContext, { useInjectRange } from '../../RangeContext';
import useCellClassName from '../../hooks/useCellClassName'; import useCellClassName from '../../hooks/useCellClassName';
import PanelBody from '../PanelBody'; import PanelBody from '../PanelBody';
import useMergeProps from '../../hooks/useMergeProps';
export const QUARTER_COL_COUNT = 4; export const QUARTER_COL_COUNT = 4;
const QUARTER_ROW_COUNT = 1; const QUARTER_ROW_COUNT = 1;
@ -19,10 +19,11 @@ export type QuarterBodyProps<DateType> = {
onSelect: (value: DateType) => void; onSelect: (value: DateType) => void;
}; };
function QuarterBody<DateType>(props: QuarterBodyProps<DateType>) { function QuarterBody<DateType>(_props: QuarterBodyProps<DateType>) {
const props = useMergeProps(_props);
const { prefixCls, locale, value, viewDate, generateConfig } = props; const { prefixCls, locale, value, viewDate, generateConfig } = props;
const { rangedValue, hoverRangedValue } = useInjectRange() const { rangedValue, hoverRangedValue } = useInjectRange();
const cellPrefixCls = `${prefixCls}-cell`; const cellPrefixCls = `${prefixCls}-cell`;
@ -30,8 +31,8 @@ function QuarterBody<DateType>(props: QuarterBodyProps<DateType>) {
cellPrefixCls, cellPrefixCls,
value, value,
generateConfig, generateConfig,
rangedValue, rangedValue: rangedValue.value,
hoverRangedValue, hoverRangedValue: hoverRangedValue.value,
isSameCell: (current, target) => isSameQuarter(generateConfig, current, target), isSameCell: (current, target) => isSameQuarter(generateConfig, current, target),
isInView: () => true, isInView: () => true,
offsetCell: (date, offset) => generateConfig.addMonth(date, offset * 3), offsetCell: (date, offset) => generateConfig.addMonth(date, offset * 3),
@ -65,7 +66,6 @@ function QuarterBody<DateType>(props: QuarterBodyProps<DateType>) {
); );
} }
QuarterBody.displayName = 'QuarterBody';
QuarterBody.displayName ='QuarterBody'
QuarterBody.inheritAttrs = false; QuarterBody.inheritAttrs = false;
export default QuarterBody; export default QuarterBody;

View File

@ -1,9 +1,9 @@
import Header from '../Header'; import Header from '../Header';
import type { Locale } from '../../interface'; import type { Locale } from '../../interface';
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { useInjectPanel } from '../../PanelContext'; import { useInjectPanel } from '../../PanelContext';
import { formatValue } from '../../utils/dateUtil'; import { formatValue } from '../../utils/dateUtil';
import useMergeProps from '../../hooks/useMergeProps';
export type QuarterHeaderProps<DateType> = { export type QuarterHeaderProps<DateType> = {
prefixCls: string; prefixCls: string;
@ -16,18 +16,12 @@ export type QuarterHeaderProps<DateType> = {
onYearClick: () => void; onYearClick: () => void;
}; };
function QuarterHeader<DateType>(props: QuarterHeaderProps<DateType>) { function QuarterHeader<DateType>(_props: QuarterHeaderProps<DateType>) {
const { const props = useMergeProps(_props);
prefixCls, const { prefixCls, generateConfig, locale, viewDate, onNextYear, onPrevYear, onYearClick } =
generateConfig, props;
locale, const { hideHeader } = useInjectPanel();
viewDate, if (hideHeader.value) {
onNextYear,
onPrevYear,
onYearClick,
} = props;
const { hideHeader } =useInjectPanel()
if (hideHeader) {
return null; return null;
} }
@ -50,8 +44,7 @@ function QuarterHeader<DateType>(props: QuarterHeaderProps<DateType>) {
); );
} }
QuarterHeader.displayName = 'QuarterHeader';
QuarterHeader.displayName ='QuarterHeader'
QuarterHeader.inheritAttrs = false; QuarterHeader.inheritAttrs = false;
export default QuarterHeader; export default QuarterHeader;

View File

@ -1,12 +1,13 @@
import QuarterHeader from './QuarterHeader'; import QuarterHeader from './QuarterHeader';
import QuarterBody from './QuarterBody'; import QuarterBody from './QuarterBody';
import type { PanelSharedProps } from '../../interface'; import type { PanelSharedProps } from '../../interface';
import { createKeyDownHandler } from '../../utils/uiUtil'; import { createKeyDownHandler } from '../../utils/uiUtil';
import useMergeProps from '../../hooks/useMergeProps';
export type QuarterPanelProps<DateType> = {} & PanelSharedProps<DateType>; export type QuarterPanelProps<DateType> = {} & PanelSharedProps<DateType>;
function QuarterPanel<DateType>(props: QuarterPanelProps<DateType>) { function QuarterPanel<DateType>(_props: QuarterPanelProps<DateType>) {
const props = useMergeProps(_props);
const { const {
prefixCls, prefixCls,
operationRef, operationRef,
@ -21,8 +22,8 @@ function QuarterPanel<DateType>(props: QuarterPanelProps<DateType>) {
const panelPrefixCls = `${prefixCls}-quarter-panel`; const panelPrefixCls = `${prefixCls}-quarter-panel`;
// ======================= Keyboard ======================= // ======================= Keyboard =======================
operationRef.current = { operationRef.value = {
onKeyDown: event => onKeyDown: (event: KeyboardEvent) =>
createKeyDownHandler(event, { createKeyDownHandler(event, {
onLeftRight: diff => { onLeftRight: diff => {
onSelect(generateConfig.addMonth(value || viewDate, diff * 3), 'key'); onSelect(generateConfig.addMonth(value || viewDate, diff * 3), 'key');
@ -69,8 +70,7 @@ function QuarterPanel<DateType>(props: QuarterPanelProps<DateType>) {
); );
} }
QuarterPanel.displayName = 'QuarterPanel';
QuarterPanel.displayName ='QuarterPanel'
QuarterPanel.inheritAttrs = false; QuarterPanel.inheritAttrs = false;
export default QuarterPanel; export default QuarterPanel;

View File

@ -1,4 +1,3 @@
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import type { Locale, OnSelect } from '../../interface'; import type { Locale, OnSelect } from '../../interface';
import type { Unit } from './TimeUnitColumn'; import type { Unit } from './TimeUnitColumn';
@ -8,17 +7,8 @@ import type { SharedTimeProps } from '.';
import { setTime as utilSetTime } from '../../utils/timeUtil'; import { setTime as utilSetTime } from '../../utils/timeUtil';
import { cloneElement } from '../../../_util/vnode'; import { cloneElement } from '../../../_util/vnode';
import { VueNode } from '../../../_util/type'; import { VueNode } from '../../../_util/type';
import { ref, Ref } from '@vue/reactivity'; import type { Ref } from 'vue';
import { computed, defineComponent, watchEffect } from '@vue/runtime-core'; import { computed, defineComponent } from 'vue';
function shouldUnitsUpdate(prevUnits: Unit[], nextUnits: Unit[]) {
if (prevUnits.length !== nextUnits.length) return true;
// if any unit's disabled status is different, the units should be re-evaluted
for (let i = 0; i < prevUnits.length; i += 1) {
if (prevUnits[i].disabled !== nextUnits[i].disabled) return true;
}
return false;
}
function generateUnits( function generateUnits(
start: number, start: number,
@ -51,7 +41,6 @@ export type TimeBodyProps<DateType> = {
operationRef: Ref<BodyOperationRef | undefined>; operationRef: Ref<BodyOperationRef | undefined>;
} & SharedTimeProps<DateType>; } & SharedTimeProps<DateType>;
const TimeBody = defineComponent({ const TimeBody = defineComponent({
name: 'TimeBody', name: 'TimeBody',
inheritAttrs: false, inheritAttrs: false,
@ -75,26 +64,26 @@ const TimeBody = defineComponent({
'onSelect', 'onSelect',
], ],
setup(props) { setup(props) {
const originHour = computed(() => props.value ? props.generateConfig.getHour(props.value) : -1); const originHour = computed(() =>
const isPM = computed(()=> { props.value ? props.generateConfig.getHour(props.value) : -1,
);
const isPM = computed(() => {
if (props.use12Hours) { if (props.use12Hours) {
return originHour.value >= 12; // -1 means should display AM return originHour.value >= 12; // -1 means should display AM
} else { } else {
return false return false;
}
})
let hour = computed(()=> {
// Should additional logic to handle 12 hours
if (props.use12Hours) {
return originHour.value % 12
} else {
return originHour.value
} }
}); });
const minute = computed(()=> props.value ? props.generateConfig.getMinute(props.value) : -1); let hour = computed(() => {
const second = computed(()=> props.value ? props.generateConfig.getSecond(props.value) : -1); // Should additional logic to handle 12 hours
if (props.use12Hours) {
return originHour.value % 12;
} else {
return originHour.value;
}
});
const minute = computed(() => (props.value ? props.generateConfig.getMinute(props.value) : -1));
const second = computed(() => (props.value ? props.generateConfig.getSecond(props.value) : -1));
const setTime = ( const setTime = (
isNewPM: boolean | undefined, isNewPM: boolean | undefined,
@ -120,7 +109,9 @@ const TimeBody = defineComponent({
}; };
// ========================= Unit ========================= // ========================= Unit =========================
const rawHours = computed(()=> generateUnits(0, 23, props.hourStep ?? 1, props.disabledHours && props.disabledHours())); const rawHours = computed(() =>
generateUnits(0, 23, props.hourStep ?? 1, props.disabledHours && props.disabledHours()),
);
// const memorizedRawHours = useMemo(() => rawHours, rawHours, shouldUnitsUpdate); // const memorizedRawHours = useMemo(() => rawHours, rawHours, shouldUnitsUpdate);
@ -155,16 +146,25 @@ const TimeBody = defineComponent({
}); });
}); });
const minutes = computed(()=> generateUnits(0, 59, props.minuteStep ?? 1, props.disabledMinutes && props.disabledMinutes(originHour.value))); const minutes = computed(() =>
generateUnits(
0,
59,
props.minuteStep ?? 1,
props.disabledMinutes && props.disabledMinutes(originHour.value),
),
);
const seconds = computed(()=> generateUnits( const seconds = computed(() =>
generateUnits(
0, 0,
59, 59,
props.secondStep ?? 1, props.secondStep ?? 1,
props.disabledSeconds && props.disabledSeconds(originHour.value, minute), props.disabledSeconds && props.disabledSeconds(originHour.value, minute),
)); ),
);
return ()=> { return () => {
const { const {
prefixCls, prefixCls,
operationRef, operationRef,
@ -188,7 +188,7 @@ const TimeBody = defineComponent({
// ====================== Operations ====================== // ====================== Operations ======================
operationRef.value = { operationRef.value = {
onUpDown: diff => { onUpDown: (diff: number) => {
const column = columns[activeColumnIndex]; const column = columns[activeColumnIndex];
if (column) { if (column) {
const valueIndex = column.units.findIndex(unit => unit.value === column.value); const valueIndex = column.units.findIndex(unit => unit.value === column.value);
@ -237,14 +237,26 @@ const TimeBody = defineComponent({
}); });
// Minute // Minute
addColumnNode(showMinute, <TimeUnitColumn key="minute" />, minute.value, minutes.value, num => { addColumnNode(
showMinute,
<TimeUnitColumn key="minute" />,
minute.value,
minutes.value,
num => {
onSelect(setTime(isPM.value, hour.value, num, second.value), 'mouse'); onSelect(setTime(isPM.value, hour.value, num, second.value), 'mouse');
}); },
);
// Second // Second
addColumnNode(showSecond, <TimeUnitColumn key="second" />, second.value, seconds.value, num => { addColumnNode(
showSecond,
<TimeUnitColumn key="second" />,
second.value,
seconds.value,
num => {
onSelect(setTime(isPM.value, hour.value, minute.value, num), 'mouse'); onSelect(setTime(isPM.value, hour.value, minute.value, num), 'mouse');
}); },
);
// 12 Hours // 12 Hours
let PMIndex = -1; let PMIndex = -1;
@ -266,9 +278,8 @@ const TimeBody = defineComponent({
); );
return <div class={contentPrefixCls}>{columns.map(({ node }) => node)}</div>; return <div class={contentPrefixCls}>{columns.map(({ node }) => node)}</div>;
} };
} },
}) });
export default TimeBody; export default TimeBody;

View File

@ -1,9 +1,9 @@
import Header from '../Header'; import Header from '../Header';
import type { Locale } from '../../interface'; import type { Locale } from '../../interface';
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { useInjectPanel } from '../../PanelContext'; import { useInjectPanel } from '../../PanelContext';
import { formatValue } from '../../utils/dateUtil'; import { formatValue } from '../../utils/dateUtil';
import useMergeProps from '../../hooks/useMergeProps';
export type TimeHeaderProps<DateType> = { export type TimeHeaderProps<DateType> = {
prefixCls: string; prefixCls: string;
@ -13,9 +13,10 @@ export type TimeHeaderProps<DateType> = {
format: string; format: string;
}; };
function TimeHeader<DateType>(props: TimeHeaderProps<DateType>) { function TimeHeader<DateType>(_props: TimeHeaderProps<DateType>) {
const { hideHeader } = useInjectPanel() const props = useMergeProps(_props);
if (hideHeader) { const { hideHeader } = useInjectPanel();
if (hideHeader.value) {
return null; return null;
} }
@ -35,8 +36,7 @@ function TimeHeader<DateType>(props: TimeHeaderProps<DateType>) {
); );
} }
TimeHeader.displayName = 'TimeHeader';
TimeHeader.displayName ='TimeHeader'
TimeHeader.inheritAttrs = false; TimeHeader.inheritAttrs = false;
export default TimeHeader; export default TimeHeader;

View File

@ -32,7 +32,7 @@ function TimeUnitColumn(props: TimeUnitColumnProps) {
() => props.value, () => props.value,
() => { () => {
const li = liRefs.value.get(value!); const li = liRefs.value.get(value!);
if (li && open !== false) { if (li && open.value !== false) {
scrollTo(ulRef.value!, li.offsetTop, 120); scrollTo(ulRef.value!, li.offsetTop, 120);
} }
}, },
@ -43,7 +43,7 @@ function TimeUnitColumn(props: TimeUnitColumnProps) {
watch(open, () => { watch(open, () => {
scrollRef.value?.(); scrollRef.value?.();
if (open) { if (open.value) {
const li = liRefs.value.get(value!); const li = liRefs.value.get(value!);
if (li) { if (li) {
scrollRef.value = waitElementReady(li, () => { scrollRef.value = waitElementReady(li, () => {

View File

@ -1,4 +1,3 @@
import TimeHeader from './TimeHeader'; import TimeHeader from './TimeHeader';
import type { BodyOperationRef } from './TimeBody'; import type { BodyOperationRef } from './TimeBody';
import TimeBody from './TimeBody'; import TimeBody from './TimeBody';
@ -6,6 +5,7 @@ import type { PanelSharedProps, DisabledTimes } from '../../interface';
import { createKeyDownHandler } from '../../utils/uiUtil'; import { createKeyDownHandler } from '../../utils/uiUtil';
import classNames from '../../../_util/classNames'; import classNames from '../../../_util/classNames';
import { ref } from '@vue/reactivity'; import { ref } from '@vue/reactivity';
import useMergeProps from '../../hooks/useMergeProps';
export type SharedTimeProps<DateType> = { export type SharedTimeProps<DateType> = {
format?: string; format?: string;
@ -24,12 +24,14 @@ export type SharedTimeProps<DateType> = {
export type TimePanelProps<DateType> = { export type TimePanelProps<DateType> = {
format?: string; format?: string;
active?: boolean; active?: boolean;
} & PanelSharedProps<DateType> & SharedTimeProps<DateType>; } & PanelSharedProps<DateType> &
SharedTimeProps<DateType>;
const countBoolean = (boolList: (boolean | undefined)[]) => const countBoolean = (boolList: (boolean | undefined)[]) =>
boolList.filter(bool => bool !== false).length; boolList.filter(bool => bool !== false).length;
function TimePanel<DateType>(props: TimePanelProps<DateType>) { function TimePanel<DateType>(_props: TimePanelProps<DateType>) {
const props = useMergeProps(_props);
const { const {
generateConfig, generateConfig,
format = 'HH:mm:ss', format = 'HH:mm:ss',
@ -50,27 +52,27 @@ function TimePanel<DateType>(props: TimePanelProps<DateType>) {
const activeColumnIndex = ref(-1); const activeColumnIndex = ref(-1);
const columnsCount = countBoolean([showHour, showMinute, showSecond, use12Hours]); const columnsCount = countBoolean([showHour, showMinute, showSecond, use12Hours]);
operationRef.current = { operationRef.value = {
onKeyDown: event => onKeyDown: (event: KeyboardEvent) =>
createKeyDownHandler(event, { createKeyDownHandler(event, {
onLeftRight: diff => { onLeftRight: diff => {
activeColumnIndex.value = (activeColumnIndex.value + diff + columnsCount) % columnsCount; activeColumnIndex.value = (activeColumnIndex.value + diff + columnsCount) % columnsCount;
}, },
onUpDown: diff => { onUpDown: diff => {
if (activeColumnIndex.value === -1) { if (activeColumnIndex.value === -1) {
activeColumnIndex.value = 0 activeColumnIndex.value = 0;
} else if (bodyOperationRef.value) { } else if (bodyOperationRef.value) {
bodyOperationRef.value.onUpDown(diff); bodyOperationRef.value.onUpDown(diff);
} }
}, },
onEnter: () => { onEnter: () => {
onSelect(value || generateConfig.getNow(), 'key'); onSelect(value || generateConfig.getNow(), 'key');
activeColumnIndex.value = -1 activeColumnIndex.value = -1;
}, },
}), }),
onBlur: () => { onBlur: () => {
activeColumnIndex.value = -1 activeColumnIndex.value = -1;
}, },
}; };
@ -91,8 +93,7 @@ function TimePanel<DateType>(props: TimePanelProps<DateType>) {
); );
} }
TimePanel.displayName = 'TimePanel';
TimePanel.displayName ='TimePanel'
TimePanel.inheritAttrs = false; TimePanel.inheritAttrs = false;
export default TimePanel; export default TimePanel;

View File

@ -1,21 +1,19 @@
import DatePanel from '../DatePanel'; import DatePanel from '../DatePanel';
import type { PanelSharedProps } from '../../interface'; import type { PanelSharedProps } from '../../interface';
import { isSameWeek } from '../../utils/dateUtil'; import { isSameWeek } from '../../utils/dateUtil';
import classNames from '../../../_util/classNames'; import classNames from '../../../_util/classNames';
import useMergeProps from '../../hooks/useMergeProps';
export type WeekPanelProps<DateType> = PanelSharedProps<DateType>; export type WeekPanelProps<DateType> = PanelSharedProps<DateType>;
function WeekPanel<DateType>(props: WeekPanelProps<DateType>) { function WeekPanel<DateType>(_props: WeekPanelProps<DateType>) {
const props = useMergeProps(_props);
const { prefixCls, generateConfig, locale, value } = props; const { prefixCls, generateConfig, locale, value } = props;
// Render additional column // Render additional column
const cellPrefixCls = `${prefixCls}-cell`; const cellPrefixCls = `${prefixCls}-cell`;
const prefixColumn = (date: DateType) => ( const prefixColumn = (date: DateType) => (
<td <td key="week" class={classNames(cellPrefixCls, `${cellPrefixCls}-week`)}>
key="week"
class={classNames(cellPrefixCls, `${cellPrefixCls}-week`)}
>
{generateConfig.locale.getWeek(locale.locale, date)} {generateConfig.locale.getWeek(locale.locale, date)}
</td> </td>
); );
@ -24,12 +22,7 @@ function WeekPanel<DateType>(props: WeekPanelProps<DateType>) {
const rowPrefixCls = `${prefixCls}-week-panel-row`; const rowPrefixCls = `${prefixCls}-week-panel-row`;
const rowClassName = (date: DateType) => const rowClassName = (date: DateType) =>
classNames(rowPrefixCls, { classNames(rowPrefixCls, {
[`${rowPrefixCls}-selected`]: isSameWeek( [`${rowPrefixCls}-selected`]: isSameWeek(generateConfig, locale.locale, value, date),
generateConfig,
locale.locale,
value,
date,
),
}); });
return ( return (
@ -45,7 +38,6 @@ function WeekPanel<DateType>(props: WeekPanelProps<DateType>) {
); );
} }
WeekPanel.displayName = 'WeekPanel'; WeekPanel.displayName = 'WeekPanel';
WeekPanel.inheritAttrs = false; WeekPanel.inheritAttrs = false;

View File

@ -1,11 +1,11 @@
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { YEAR_DECADE_COUNT } from '.'; import { YEAR_DECADE_COUNT } from '.';
import type { Locale, NullableDateType } from '../../interface'; import type { Locale, NullableDateType } from '../../interface';
import useCellClassName from '../../hooks/useCellClassName'; import useCellClassName from '../../hooks/useCellClassName';
import { formatValue, isSameYear } from '../../utils/dateUtil'; import { formatValue, isSameYear } from '../../utils/dateUtil';
import RangeContext, { useInjectRange } from '../../RangeContext'; import { useInjectRange } from '../../RangeContext';
import PanelBody from '../PanelBody'; import PanelBody from '../PanelBody';
import useMergeProps from '../../hooks/useMergeProps';
export const YEAR_COL_COUNT = 3; export const YEAR_COL_COUNT = 3;
const YEAR_ROW_COUNT = 4; const YEAR_ROW_COUNT = 4;
@ -20,9 +20,10 @@ export type YearBodyProps<DateType> = {
onSelect: (value: DateType) => void; onSelect: (value: DateType) => void;
}; };
function YearBody<DateType>(props: YearBodyProps<DateType>) { function YearBody<DateType>(_props: YearBodyProps<DateType>) {
const props = useMergeProps(_props);
const { prefixCls, value, viewDate, locale, generateConfig } = props; const { prefixCls, value, viewDate, locale, generateConfig } = props;
const { rangedValue, hoverRangedValue } = useInjectRange() const { rangedValue, hoverRangedValue } = useInjectRange();
const yearPrefixCls = `${prefixCls}-cell`; const yearPrefixCls = `${prefixCls}-cell`;
@ -44,8 +45,8 @@ function YearBody<DateType>(props: YearBodyProps<DateType>) {
cellPrefixCls: yearPrefixCls, cellPrefixCls: yearPrefixCls,
value, value,
generateConfig, generateConfig,
rangedValue, rangedValue: rangedValue.value,
hoverRangedValue, hoverRangedValue: hoverRangedValue.value,
isSameCell: (current, target) => isSameYear(generateConfig, current, target), isSameCell: (current, target) => isSameYear(generateConfig, current, target),
isInView, isInView,
offsetCell: (date, offset) => generateConfig.addYear(date, offset), offsetCell: (date, offset) => generateConfig.addYear(date, offset),
@ -71,8 +72,6 @@ function YearBody<DateType>(props: YearBodyProps<DateType>) {
); );
} }
YearBody.displayName = 'YearBody'; YearBody.displayName = 'YearBody';
YearBody.inheritAttrs = false; YearBody.inheritAttrs = false;

View File

@ -1,8 +1,8 @@
import Header from '../Header'; import Header from '../Header';
import type { GenerateConfig } from '../../generate'; import type { GenerateConfig } from '../../generate';
import { YEAR_DECADE_COUNT } from '.'; import { YEAR_DECADE_COUNT } from '.';
import { useInjectPanel } from '../../PanelContext'; import { useInjectPanel } from '../../PanelContext';
import useMergeProps from '../../hooks/useMergeProps';
export type YearHeaderProps<DateType> = { export type YearHeaderProps<DateType> = {
prefixCls: string; prefixCls: string;
@ -15,10 +15,11 @@ export type YearHeaderProps<DateType> = {
onDecadeClick: () => void; onDecadeClick: () => void;
}; };
function YearHeader<DateType>(props: YearHeaderProps<DateType>) { function YearHeader<DateType>(_props: YearHeaderProps<DateType>) {
const props = useMergeProps(_props);
const { prefixCls, generateConfig, viewDate, onPrevDecade, onNextDecade, onDecadeClick } = props; const { prefixCls, generateConfig, viewDate, onPrevDecade, onNextDecade, onDecadeClick } = props;
const { hideHeader } = useInjectPanel() const { hideHeader } = useInjectPanel();
if (hideHeader) { if (hideHeader.value) {
return null; return null;
} }
@ -42,8 +43,6 @@ function YearHeader<DateType>(props: YearHeaderProps<DateType>) {
); );
} }
YearHeader.displayName = 'YearHeader'; YearHeader.displayName = 'YearHeader';
YearHeader.inheritAttrs = false; YearHeader.inheritAttrs = false;

View File

@ -1,8 +1,8 @@
import YearHeader from './YearHeader'; import YearHeader from './YearHeader';
import YearBody, { YEAR_COL_COUNT } from './YearBody'; import YearBody, { YEAR_COL_COUNT } from './YearBody';
import type { PanelSharedProps, PanelMode } from '../../interface'; import type { PanelSharedProps, PanelMode } from '../../interface';
import { createKeyDownHandler } from '../../utils/uiUtil'; import { createKeyDownHandler } from '../../utils/uiUtil';
import useMergeProps from '../../hooks/useMergeProps';
export type YearPanelProps<DateType> = { export type YearPanelProps<DateType> = {
sourceMode: PanelMode; sourceMode: PanelMode;
@ -10,7 +10,8 @@ export type YearPanelProps<DateType> = {
export const YEAR_DECADE_COUNT = 10; export const YEAR_DECADE_COUNT = 10;
function YearPanel<DateType>(props: YearPanelProps<DateType>) { function YearPanel<DateType>(_props: YearPanelProps<DateType>) {
const props = useMergeProps(_props);
const { const {
prefixCls, prefixCls,
operationRef, operationRef,
@ -26,29 +27,20 @@ function YearPanel<DateType>(props: YearPanelProps<DateType>) {
const panelPrefixCls = `${prefixCls}-year-panel`; const panelPrefixCls = `${prefixCls}-year-panel`;
// ======================= Keyboard ======================= // ======================= Keyboard =======================
operationRef.current = { operationRef.value = {
onKeyDown: event => onKeyDown: (event: KeyboardEvent) =>
createKeyDownHandler(event, { createKeyDownHandler(event, {
onLeftRight: diff => { onLeftRight: diff => {
onSelect(generateConfig.addYear(value || viewDate, diff), 'key'); onSelect(generateConfig.addYear(value || viewDate, diff), 'key');
}, },
onCtrlLeftRight: diff => { onCtrlLeftRight: diff => {
onSelect( onSelect(generateConfig.addYear(value || viewDate, diff * YEAR_DECADE_COUNT), 'key');
generateConfig.addYear(value || viewDate, diff * YEAR_DECADE_COUNT),
'key',
);
}, },
onUpDown: diff => { onUpDown: diff => {
onSelect( onSelect(generateConfig.addYear(value || viewDate, diff * YEAR_COL_COUNT), 'key');
generateConfig.addYear(value || viewDate, diff * YEAR_COL_COUNT),
'key',
);
}, },
onEnter: () => { onEnter: () => {
onPanelChange( onPanelChange(sourceMode === 'date' ? 'date' : 'month', value || viewDate);
sourceMode === 'date' ? 'date' : 'month',
value || viewDate,
);
}, },
}), }),
}; };
@ -87,8 +79,6 @@ function YearPanel<DateType>(props: YearPanelProps<DateType>) {
); );
} }
YearPanel.displayName = 'YearPanel'; YearPanel.displayName = 'YearPanel';
YearPanel.inheritAttrs = false; YearPanel.inheritAttrs = false;

View File

@ -1,4 +1,3 @@
import type { PanelMode } from '../interface'; import type { PanelMode } from '../interface';
export default function getExtraFooter( export default function getExtraFooter(
@ -10,7 +9,5 @@ export default function getExtraFooter(
return null; return null;
} }
return ( return <div class={`${prefixCls}-footer-extra`}>{renderExtraFooter(mode)}</div>;
<div class={`${prefixCls}-footer-extra`}>{renderExtraFooter(mode)}</div>
);
} }

View File

@ -1,4 +1,3 @@
import { VueNode } from '../../_util/type'; import { VueNode } from '../../_util/type';
import type { Components, RangeList, Locale } from '../interface'; import type { Components, RangeList, Locale } from '../interface';

View File

@ -51,8 +51,8 @@ export function updateValues<T, R = [T | null, T | null] | null>(
typeof value === 'function' ? (value as UpdateValue<T | null>)(newValues[index]) : value; typeof value === 'function' ? (value as UpdateValue<T | null>)(newValues[index]) : value;
if (!newValues[0] && !newValues[1]) { if (!newValues[0] && !newValues[1]) {
return (null as unknown) as R; return null as unknown as R;
} }
return (newValues as unknown) as R; return newValues as unknown as R;
} }

View File

@ -24,22 +24,12 @@ export function setDateTime<DateType>(
} }
let newDate = date; let newDate = date;
newDate = generateConfig.setHour( newDate = generateConfig.setHour(newDate, generateConfig.getHour(defaultDate));
newDate, newDate = generateConfig.setMinute(newDate, generateConfig.getMinute(defaultDate));
generateConfig.getHour(defaultDate), newDate = generateConfig.setSecond(newDate, generateConfig.getSecond(defaultDate));
);
newDate = generateConfig.setMinute(
newDate,
generateConfig.getMinute(defaultDate),
);
newDate = generateConfig.setSecond(
newDate,
generateConfig.getSecond(defaultDate),
);
return newDate; return newDate;
} }
export function getLowerBoundTime( export function getLowerBoundTime(
hour: number, hour: number,
minute: number, minute: number,

View File

@ -201,7 +201,7 @@ export function addGlobalMouseDownEvent(callback: ClickEventHandler) {
if (!globalClickFunc && typeof window !== 'undefined' && window.addEventListener) { if (!globalClickFunc && typeof window !== 'undefined' && window.addEventListener) {
globalClickFunc = (e: MouseEvent) => { globalClickFunc = (e: MouseEvent) => {
// Clone a new list to avoid repeat trigger events // Clone a new list to avoid repeat trigger events
[...clickCallbacks].forEach((queueFunc) => { [...clickCallbacks].forEach(queueFunc => {
queueFunc(e); queueFunc(e);
}); });
}; };
@ -272,5 +272,5 @@ export function elementsContains(
elements: (HTMLElement | undefined | null)[], elements: (HTMLElement | undefined | null)[],
target: HTMLElement, target: HTMLElement,
) { ) {
return elements.some((ele) => ele && ele.contains(target)); return elements.some(ele => ele && ele.contains(target));
} }