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-docpull/4499/head
parent
a501b592a2
commit
16fc2a10a9
|
@ -0,0 +1,3 @@
|
|||
export type FocusEventHandler = (e: FocusEvent) => void;
|
||||
export type MouseEventHandler = (e: MouseEvent) => void;
|
||||
export type KeyboardEventHandler = (e: KeyboardEvent) => void;
|
|
@ -1,9 +1,9 @@
|
|||
import type { Ref } from 'vue';
|
||||
import type { Ref, WatchSource } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
export default function useMemo<T>(
|
||||
getValue: () => T,
|
||||
condition: any[],
|
||||
condition: (WatchSource<unknown> | object)[],
|
||||
shouldUpdate?: (prev: any[], next: any[]) => boolean,
|
||||
) {
|
||||
const cacheRef: Ref<T> = ref(getValue() as any);
|
||||
|
|
|
@ -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];
|
||||
}
|
|
@ -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];
|
||||
}
|
|
@ -9,22 +9,21 @@ export type ContextOperationRefProps = {
|
|||
export type PanelContextProps = {
|
||||
operationRef?: Ref<ContextOperationRefProps | null>;
|
||||
/** Only work with time panel */
|
||||
hideHeader?: boolean;
|
||||
hideHeader?: Ref<boolean>;
|
||||
panelRef?: Ref<HTMLDivElement>;
|
||||
hidePrevBtn?: boolean;
|
||||
hideNextBtn?: boolean;
|
||||
hidePrevBtn?: Ref<boolean>;
|
||||
hideNextBtn?: Ref<boolean>;
|
||||
onDateMouseEnter?: (date: any) => void;
|
||||
onDateMouseLeave?: (date: any) => void;
|
||||
onSelect?: OnSelect<any>;
|
||||
hideRanges?: boolean;
|
||||
open?: boolean;
|
||||
mode?: PanelMode;
|
||||
hideRanges?: Ref<boolean>;
|
||||
open?: Ref<boolean>;
|
||||
mode?: Ref<PanelMode>;
|
||||
|
||||
/** Only used for TimePicker and this is a deprecated prop */
|
||||
defaultOpenValue?: any;
|
||||
defaultOpenValue?: Ref<any>;
|
||||
};
|
||||
|
||||
|
||||
const PanelContextKey: InjectionKey<PanelContextProps> = Symbol('PanelContextProps');
|
||||
|
||||
export const useProvidePanel = (props: PanelContextProps) => {
|
||||
|
|
|
@ -11,11 +11,6 @@
|
|||
* 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 {
|
||||
PickerPanelBaseProps,
|
||||
PickerPanelDateProps,
|
||||
|
@ -33,6 +28,22 @@ import usePickerInput from './hooks/usePickerInput';
|
|||
import useTextValueMapping from './hooks/useTextValueMapping';
|
||||
import useValueTexts from './hooks/useValueTexts';
|
||||
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 = {
|
||||
focus: () => void;
|
||||
|
@ -42,13 +53,13 @@ export type PickerRefConfig = {
|
|||
export type PickerSharedProps<DateType> = {
|
||||
dropdownClassName?: string;
|
||||
dropdownAlign?: AlignType;
|
||||
popupStyle?: React.CSSProperties;
|
||||
popupStyle?: CSSProperties;
|
||||
transitionName?: string;
|
||||
placeholder?: string;
|
||||
allowClear?: boolean;
|
||||
autoFocus?: boolean;
|
||||
autofocus?: boolean;
|
||||
disabled?: boolean;
|
||||
tabIndex?: number;
|
||||
tabindex?: number;
|
||||
open?: boolean;
|
||||
defaultOpen?: boolean;
|
||||
/** Make input readOnly to avoid popup keyboard in mobile */
|
||||
|
@ -59,39 +70,39 @@ export type PickerSharedProps<DateType> = {
|
|||
format?: string | CustomFormat<DateType> | (string | CustomFormat<DateType>)[];
|
||||
|
||||
// Render
|
||||
suffixIcon?: React.ReactNode;
|
||||
clearIcon?: React.ReactNode;
|
||||
prevIcon?: React.ReactNode;
|
||||
nextIcon?: React.ReactNode;
|
||||
superPrevIcon?: React.ReactNode;
|
||||
superNextIcon?: React.ReactNode;
|
||||
suffixIcon?: VueNode;
|
||||
clearIcon?: VueNode;
|
||||
prevIcon?: VueNode;
|
||||
nextIcon?: VueNode;
|
||||
superPrevIcon?: VueNode;
|
||||
superNextIcon?: VueNode;
|
||||
getPopupContainer?: (node: HTMLElement) => HTMLElement;
|
||||
panelRender?: (originPanel: React.ReactNode) => React.ReactNode;
|
||||
panelRender?: (originPanel: VueNode) => VueNode;
|
||||
|
||||
// Events
|
||||
onChange?: (value: DateType | null, dateString: string) => void;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
||||
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
||||
onMouseDown?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseUp?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onContextMenu?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>, preventDefault: () => void) => void;
|
||||
onFocus?: FocusEventHandler;
|
||||
onBlur?: FocusEventHandler;
|
||||
onMouseDown?: MouseEventHandler;
|
||||
onMouseUp?: MouseEventHandler;
|
||||
onMouseEnter?: MouseEventHandler;
|
||||
onMouseLeave?: MouseEventHandler;
|
||||
onClick?: MouseEventHandler;
|
||||
onContextMenu?: MouseEventHandler;
|
||||
onKeyDown?: (event: KeyboardEvent, preventDefault: () => void) => void;
|
||||
|
||||
// Internal
|
||||
/** @private Internal usage, do not use in production mode!!! */
|
||||
pickerRef?: React.MutableRefObject<PickerRefConfig>;
|
||||
pickerRef?: Ref<PickerRefConfig>;
|
||||
|
||||
// WAI-ARIA
|
||||
role?: string;
|
||||
name?: string;
|
||||
|
||||
autoComplete?: string;
|
||||
autocomplete?: string;
|
||||
direction?: 'ltr' | 'rtl';
|
||||
} & React.AriaAttributes;
|
||||
} & HtmlHTMLAttributes;
|
||||
|
||||
type OmitPanelProps<Props> = Omit<
|
||||
Props,
|
||||
|
@ -127,435 +138,224 @@ type MergedPickerProps<DateType> = {
|
|||
picker?: PickerMode;
|
||||
} & OmitType<DateType>;
|
||||
|
||||
function InnerPicker<DateType>(props: PickerProps<DateType>) {
|
||||
const {
|
||||
prefixCls = 'rc-picker',
|
||||
id,
|
||||
tabIndex,
|
||||
style,
|
||||
className,
|
||||
dropdownClassName,
|
||||
dropdownAlign,
|
||||
popupStyle,
|
||||
transitionName,
|
||||
generateConfig,
|
||||
locale,
|
||||
inputReadOnly,
|
||||
allowClear,
|
||||
autoFocus,
|
||||
showTime,
|
||||
picker = 'date',
|
||||
format,
|
||||
use12Hours,
|
||||
value,
|
||||
defaultValue,
|
||||
open,
|
||||
defaultOpen,
|
||||
defaultOpenValue,
|
||||
suffixIcon,
|
||||
clearIcon,
|
||||
disabled,
|
||||
disabledDate,
|
||||
placeholder,
|
||||
getPopupContainer,
|
||||
pickerRef,
|
||||
panelRender,
|
||||
onChange,
|
||||
onOpenChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onContextMenu,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onSelect,
|
||||
direction,
|
||||
autoComplete = 'off',
|
||||
} = props as MergedPickerProps<DateType>;
|
||||
function Picker<DateType>() {
|
||||
return defineComponent<MergedPickerProps<DateType>>({
|
||||
name: 'Picker',
|
||||
props: [
|
||||
'prefixCls',
|
||||
'id',
|
||||
'tabindex',
|
||||
'dropdownClassName',
|
||||
'dropdownAlign',
|
||||
'popupStyle',
|
||||
'transitionName',
|
||||
'generateConfig',
|
||||
'locale',
|
||||
'inputReadOnly',
|
||||
'allowClear',
|
||||
'autofocus',
|
||||
'showTime',
|
||||
'picker',
|
||||
'format',
|
||||
'use12Hours',
|
||||
'value',
|
||||
'defaultValue',
|
||||
'open',
|
||||
'defaultOpen',
|
||||
'defaultOpenValue',
|
||||
'suffixIcon',
|
||||
'clearIcon',
|
||||
'disabled',
|
||||
'disabledDate',
|
||||
'placeholder',
|
||||
'getPopupContainer',
|
||||
'pickerRef',
|
||||
'panelRender',
|
||||
'onChange',
|
||||
'onOpenChange',
|
||||
'onFocus',
|
||||
'onBlur',
|
||||
'onMouseDown',
|
||||
'onMouseUp',
|
||||
'onMouseEnter',
|
||||
'onMouseLeave',
|
||||
'onContextMenu',
|
||||
'onClick',
|
||||
'onKeyDown',
|
||||
'onSelect',
|
||||
'direction',
|
||||
'autocomplete',
|
||||
] as any,
|
||||
inheritAttrs: false,
|
||||
slots: [
|
||||
'suffixIcon',
|
||||
'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',
|
||||
);
|
||||
|
||||
const inputRef = React.useRef<HTMLInputElement>(null);
|
||||
// ============================= State =============================
|
||||
const formatList = computed(() =>
|
||||
toArray(getDefaultFormat(props.format, props.picker, props.showTime, props.use12Hours)),
|
||||
);
|
||||
|
||||
const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time';
|
||||
// Panel ref
|
||||
const panelDivRef = ref(null);
|
||||
const inputDivRef = ref(null);
|
||||
|
||||
// ============================= State =============================
|
||||
const formatList = toArray(getDefaultFormat(format, picker, showTime, use12Hours));
|
||||
|
||||
// Panel ref
|
||||
const panelDivRef = React.useRef<HTMLDivElement>(null);
|
||||
const inputDivRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
// Real value
|
||||
const [mergedValue, setInnerValue] = useMergedState(null, {
|
||||
value,
|
||||
defaultValue,
|
||||
});
|
||||
|
||||
// Selected value
|
||||
const [selectedValue, setSelectedValue] = React.useState<DateType | null>(mergedValue);
|
||||
|
||||
// Operation ref
|
||||
const operationRef: React.MutableRefObject<ContextOperationRefProps | null> =
|
||||
React.useRef<ContextOperationRefProps>(null);
|
||||
|
||||
// Open
|
||||
const [mergedOpen, triggerInnerOpen] = useMergedState(false, {
|
||||
value: open,
|
||||
defaultValue: defaultOpen,
|
||||
postState: (postOpen) => (disabled ? false : postOpen),
|
||||
onChange: (newOpen) => {
|
||||
if (onOpenChange) {
|
||||
onOpenChange(newOpen);
|
||||
}
|
||||
|
||||
if (!newOpen && operationRef.current && operationRef.current.onClose) {
|
||||
operationRef.current.onClose();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// ============================= Text ==============================
|
||||
const [valueTexts, firstValueText] = useValueTexts(selectedValue, {
|
||||
formatList,
|
||||
generateConfig,
|
||||
locale,
|
||||
});
|
||||
|
||||
const [text, triggerTextChange, resetText] = useTextValueMapping({
|
||||
valueTexts,
|
||||
onTextChange: (newText) => {
|
||||
const inputDate = parseValue(newText, {
|
||||
locale,
|
||||
formatList,
|
||||
generateConfig,
|
||||
// Real value
|
||||
const [mergedValue, setInnerValue] = useMergedState<DateType>(null, {
|
||||
value: toRef(props, 'value'),
|
||||
defaultValue: props.defaultValue,
|
||||
});
|
||||
if (inputDate && (!disabledDate || !disabledDate(inputDate))) {
|
||||
setSelectedValue(inputDate);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// ============================ Trigger ============================
|
||||
const triggerChange = (newValue: DateType | null) => {
|
||||
setSelectedValue(newValue);
|
||||
setInnerValue(newValue);
|
||||
const selectedValue = ref(mergedValue.value) as Ref<DateType>;
|
||||
const setSelectedValue = (val: DateType) => {
|
||||
selectedValue.value = val;
|
||||
};
|
||||
|
||||
if (onChange && !isEqual(generateConfig, mergedValue, newValue)) {
|
||||
onChange(
|
||||
newValue,
|
||||
newValue ? formatValue(newValue, { generateConfig, locale, format: formatList[0] }) : '',
|
||||
);
|
||||
}
|
||||
};
|
||||
// Operation ref
|
||||
const operationRef = ref<ContextOperationRefProps>(null);
|
||||
|
||||
const triggerOpen = (newOpen: boolean) => {
|
||||
if (disabled && newOpen) {
|
||||
return;
|
||||
}
|
||||
// Open
|
||||
const [mergedOpen, triggerInnerOpen] = useMergedState(false, {
|
||||
value: toRef(props, 'open'),
|
||||
defaultValue: props.defaultOpen,
|
||||
postState: postOpen => (props.disabled ? false : postOpen),
|
||||
onChange: newOpen => {
|
||||
if (props.onOpenChange) {
|
||||
props.onOpenChange(newOpen);
|
||||
}
|
||||
|
||||
triggerInnerOpen(newOpen);
|
||||
};
|
||||
if (!newOpen && operationRef.value && operationRef.value.onClose) {
|
||||
operationRef.value.onClose();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const forwardKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
|
||||
if (mergedOpen && operationRef.current && operationRef.current.onKeyDown) {
|
||||
// Let popup panel handle keyboard
|
||||
return operationRef.current.onKeyDown(e);
|
||||
}
|
||||
// ============================= Text ==============================
|
||||
const texts = useValueTexts(selectedValue, {
|
||||
formatList,
|
||||
generateConfig: toRef(props, 'generateConfig'),
|
||||
locale: toRef(props, 'locale'),
|
||||
});
|
||||
const valueTexts = computed(() => texts.value[0]);
|
||||
const firstValueText = computed(() => texts.value[1]);
|
||||
|
||||
/* istanbul ignore next */
|
||||
/* eslint-disable no-lone-blocks */
|
||||
{
|
||||
warning(
|
||||
false,
|
||||
'Picker not correct forward KeyDown operation. Please help to fire issue about this.',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const [text, triggerTextChange, resetText] = useTextValueMapping({
|
||||
valueTexts,
|
||||
onTextChange: newText => {
|
||||
const inputDate = parseValue(newText, {
|
||||
locale: props.locale,
|
||||
formatList: formatList.value,
|
||||
generateConfig: props.generateConfig,
|
||||
});
|
||||
if (inputDate && (!props.disabledDate || !props.disabledDate(inputDate))) {
|
||||
setSelectedValue(inputDate);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const onInternalMouseUp: React.MouseEventHandler<HTMLDivElement> = (...args) => {
|
||||
if (onMouseUp) {
|
||||
onMouseUp(...args);
|
||||
}
|
||||
// ============================ Trigger ============================
|
||||
const triggerChange = (newValue: DateType | null) => {
|
||||
const { onChange, generateConfig, locale } = props;
|
||||
setSelectedValue(newValue);
|
||||
setInnerValue(newValue);
|
||||
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
triggerOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
// ============================= Input =============================
|
||||
const [inputProps, { focused, typing }] = usePickerInput({
|
||||
blurToCancel: needConfirmButton,
|
||||
open: mergedOpen,
|
||||
value: text,
|
||||
triggerOpen,
|
||||
forwardKeyDown,
|
||||
isClickOutside: (target) =>
|
||||
!elementsContains([panelDivRef.current, inputDivRef.current], target as HTMLElement),
|
||||
onSubmit: () => {
|
||||
if (disabledDate && disabledDate(selectedValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
triggerChange(selectedValue);
|
||||
triggerOpen(false);
|
||||
resetText();
|
||||
return true;
|
||||
},
|
||||
onCancel: () => {
|
||||
triggerOpen(false);
|
||||
setSelectedValue(mergedValue);
|
||||
resetText();
|
||||
},
|
||||
onKeyDown: (e, preventDefault) => {
|
||||
onKeyDown?.(e, preventDefault);
|
||||
},
|
||||
onFocus,
|
||||
onBlur,
|
||||
});
|
||||
|
||||
// ============================= Sync ==============================
|
||||
// Close should sync back with text value
|
||||
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();
|
||||
if (onChange && !isEqual(generateConfig, mergedValue.value, newValue)) {
|
||||
onChange(
|
||||
newValue,
|
||||
newValue
|
||||
? formatValue(newValue, { generateConfig, locale, format: formatList[0] })
|
||||
: '',
|
||||
);
|
||||
}
|
||||
},
|
||||
blur: () => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.blur();
|
||||
};
|
||||
|
||||
const triggerOpen = (newOpen: boolean) => {
|
||||
if (props.disabled && newOpen) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const [hoverValue, onEnter, onLeave] = useHoverValue(text, {
|
||||
formatList,
|
||||
generateConfig,
|
||||
locale,
|
||||
});
|
||||
triggerInnerOpen(newOpen);
|
||||
};
|
||||
|
||||
// ============================= 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,
|
||||
};
|
||||
const forwardKeyDown = (e: KeyboardEvent) => {
|
||||
if (mergedOpen && operationRef.value && operationRef.value.onKeyDown) {
|
||||
// Let popup panel handle keyboard
|
||||
return operationRef.value.onKeyDown(e);
|
||||
}
|
||||
|
||||
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);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
/* istanbul ignore next */
|
||||
/* eslint-disable no-lone-blocks */
|
||||
{
|
||||
warning(
|
||||
false,
|
||||
'Picker not correct forward KeyDown operation. Please help to fire issue about this.',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if (panelRender) {
|
||||
panelNode = panelRender(panelNode);
|
||||
}
|
||||
const onInternalMouseUp: MouseEventHandler = (...args) => {
|
||||
if (props.onMouseUp) {
|
||||
props.onMouseUp(...args);
|
||||
}
|
||||
|
||||
const panel = (
|
||||
<div
|
||||
class={`${prefixCls}-panel-container`}
|
||||
onMousedown={(e) => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
{panelNode}
|
||||
</div>
|
||||
);
|
||||
if (inputRef.value) {
|
||||
inputRef.value.focus();
|
||||
triggerOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
// ============================= Input =============================
|
||||
const [inputProps, { focused, typing }] = usePickerInput({
|
||||
blurToCancel: needConfirmButton,
|
||||
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>
|
||||
);
|
||||
value: text,
|
||||
triggerOpen,
|
||||
forwardKeyDown,
|
||||
isClickOutside: target =>
|
||||
!elementsContains([panelDivRef.current, inputDivRef.current], target as HTMLElement),
|
||||
onSubmit: () => {
|
||||
if (props.disabledDate && props.disabledDate(selectedValue.value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
triggerChange(selectedValue.value);
|
||||
triggerOpen(false);
|
||||
resetText();
|
||||
return true;
|
||||
},
|
||||
onCancel: () => {
|
||||
triggerOpen(false);
|
||||
setSelectedValue(mergedValue.value);
|
||||
resetText();
|
||||
},
|
||||
onKeyDown: (e, preventDefault) => {
|
||||
props.onKeyDown?.(e, preventDefault);
|
||||
},
|
||||
onFocus: (e: FocusEvent) => {
|
||||
props.onFocus?.(e);
|
||||
},
|
||||
onBlur: (e: FocusEvent) => {
|
||||
props.onBlur?.(e);
|
||||
},
|
||||
});
|
||||
|
||||
return () => {
|
||||
return null;
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Wrap with class component to enable pass generic with instance method
|
||||
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;
|
||||
export default Picker();
|
||||
|
|
|
@ -4,12 +4,6 @@
|
|||
* click will trigger `onSelect` (if value changed trigger `onChange` also).
|
||||
* 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 TimePanel from './panels/TimePanel';
|
||||
import DatetimePanel from './panels/DatetimePanel';
|
||||
|
@ -30,22 +24,28 @@ import type {
|
|||
Components,
|
||||
} from './interface';
|
||||
import { isEqual } from './utils/dateUtil';
|
||||
import PanelContext from './PanelContext';
|
||||
import { useInjectPanel, useProvidePanel } from './PanelContext';
|
||||
import type { DateRender } from './panels/DatePanel/DateBody';
|
||||
import { PickerModeMap } from './utils/uiUtil';
|
||||
import type { MonthCellRender } from './panels/MonthPanel/MonthBody';
|
||||
import RangeContext from './RangeContext';
|
||||
import { useInjectRange } from './RangeContext';
|
||||
import getExtraFooter from './utils/getExtraFooter';
|
||||
import getRanges from './utils/getRanges';
|
||||
import { getLowerBoundTime, setDateTime, setTime } from './utils/timeUtil';
|
||||
import { VueNode } from '../_util/type';
|
||||
import { computed, defineComponent, ref, toRef, watch, watchEffect } from 'vue';
|
||||
import 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> = {
|
||||
prefixCls?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
// className?: string;
|
||||
// style?: React.CSSProperties;
|
||||
/** @deprecated Will be removed in next big version. Please use `mode` instead */
|
||||
mode?: PanelMode;
|
||||
tabIndex?: number;
|
||||
tabindex?: number;
|
||||
|
||||
// Locale
|
||||
locale: Locale;
|
||||
|
@ -65,13 +65,13 @@ export type PickerPanelSharedProps<DateType> = {
|
|||
// Render
|
||||
dateRender?: DateRender<DateType>;
|
||||
monthCellRender?: MonthCellRender<DateType>;
|
||||
renderExtraFooter?: (mode: PanelMode) => React.ReactNode;
|
||||
renderExtraFooter?: (mode: PanelMode) => VueNode;
|
||||
|
||||
// Event
|
||||
onSelect?: (value: DateType) => void;
|
||||
onChange?: (value: DateType) => void;
|
||||
onPanelChange?: OnPanelChange<DateType>;
|
||||
onMouseDown?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseDown?: (e: MouseEvent) => void;
|
||||
onOk?: (date: DateType) => void;
|
||||
|
||||
direction?: 'ltr' | 'rtl';
|
||||
|
@ -101,7 +101,8 @@ export type PickerPanelDateProps<DateType> = {
|
|||
|
||||
export type PickerPanelTimeProps<DateType> = {
|
||||
picker: 'time';
|
||||
} & PickerPanelSharedProps<DateType> & SharedTimeProps<DateType>;
|
||||
} & PickerPanelSharedProps<DateType> &
|
||||
SharedTimeProps<DateType>;
|
||||
|
||||
export type PickerPanelProps<DateType> =
|
||||
| PickerPanelBaseProps<DateType>
|
||||
|
@ -116,454 +117,486 @@ type MergedPickerPanelProps<DateType> = {
|
|||
picker?: PickerMode;
|
||||
} & OmitType<DateType>;
|
||||
|
||||
function PickerPanel<DateType>(props: PickerPanelProps<DateType>) {
|
||||
const {
|
||||
prefixCls = 'rc-picker',
|
||||
className,
|
||||
style,
|
||||
locale,
|
||||
generateConfig,
|
||||
value,
|
||||
defaultValue,
|
||||
pickerValue,
|
||||
defaultPickerValue,
|
||||
disabledDate,
|
||||
mode,
|
||||
picker = 'date',
|
||||
tabIndex = 0,
|
||||
showNow,
|
||||
showTime,
|
||||
showToday,
|
||||
renderExtraFooter,
|
||||
hideHeader,
|
||||
onSelect,
|
||||
onChange,
|
||||
onPanelChange,
|
||||
onMouseDown,
|
||||
onPickerValueChange,
|
||||
onOk,
|
||||
components,
|
||||
direction,
|
||||
hourStep = 1,
|
||||
minuteStep = 1,
|
||||
secondStep = 1,
|
||||
} = props as MergedPickerPanelProps<DateType>;
|
||||
function PickerPanel<DateType>() {
|
||||
return defineComponent<MergedPickerPanelProps<DateType>>({
|
||||
name: 'PickerPanel',
|
||||
inheritAttrs: false,
|
||||
props: [
|
||||
'prefixCls',
|
||||
'locale',
|
||||
'generateConfig',
|
||||
'value',
|
||||
'defaultValue',
|
||||
'pickerValue',
|
||||
'defaultPickerValue',
|
||||
'disabledDate',
|
||||
'mode',
|
||||
{ picker: { default: 'date' } },
|
||||
{ tabindex: { default: 0 } },
|
||||
'showNow',
|
||||
'showTime',
|
||||
'showToday',
|
||||
'renderExtraFooter',
|
||||
'hideHeader',
|
||||
'onSelect',
|
||||
'onChange',
|
||||
'onPanelChange',
|
||||
'onMouseDown',
|
||||
'onPickerValueChange',
|
||||
'onOk',
|
||||
'components',
|
||||
'direction',
|
||||
{ hourStep: { default: 1 } },
|
||||
{ minuteStep: { default: 1 } },
|
||||
{ secondStep: { default: 1 } },
|
||||
] as any,
|
||||
setup(props, { attrs }) {
|
||||
const needConfirmButton = computed(
|
||||
() => (props.picker === 'date' && !!props.showTime) || props.picker === 'time',
|
||||
);
|
||||
|
||||
const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time';
|
||||
|
||||
const isHourStepValid = 24 % hourStep === 0;
|
||||
const isMinuteStepValid = 60 % minuteStep === 0;
|
||||
const isSecondStepValid = 60 % secondStep === 0;
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
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(
|
||||
isMinuteStepValid,
|
||||
`\`minuteStep\` ${minuteStep} is invalid. It should be a factor of 60.`,
|
||||
);
|
||||
warning(
|
||||
isSecondStepValid,
|
||||
`\`secondStep\` ${secondStep} is invalid. It should be a factor of 60.`,
|
||||
);
|
||||
}
|
||||
|
||||
// ============================ State =============================
|
||||
|
||||
const panelContext = React.useContext(PanelContext);
|
||||
const {
|
||||
operationRef,
|
||||
panelRef: panelDivRef,
|
||||
onSelect: onContextSelect,
|
||||
hideRanges,
|
||||
defaultOpenValue,
|
||||
} = panelContext;
|
||||
|
||||
const { inRange, panelPosition, rangedValue, hoverRangedValue } = React.useContext(RangeContext);
|
||||
const panelRef = React.useRef<PanelRefProps>({});
|
||||
|
||||
// Handle init logic
|
||||
const initRef = React.useRef(true);
|
||||
|
||||
// Value
|
||||
const [mergedValue, setInnerValue] = useMergedState(null, {
|
||||
value,
|
||||
defaultValue,
|
||||
postState: (val) => {
|
||||
if (!val && defaultOpenValue && picker === 'time') {
|
||||
return defaultOpenValue;
|
||||
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') {
|
||||
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 `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.`,
|
||||
);
|
||||
warning(
|
||||
isSecondStepValid.value,
|
||||
`\`secondStep\` ${secondStep} is invalid. It should be a factor of 60.`,
|
||||
);
|
||||
});
|
||||
}
|
||||
return val;
|
||||
},
|
||||
});
|
||||
|
||||
// View date control
|
||||
const [viewDate, setInnerViewDate] = useMergedState<DateType | null, DateType>(null, {
|
||||
value: pickerValue,
|
||||
defaultValue: defaultPickerValue || mergedValue,
|
||||
postState: (date) => {
|
||||
const now = generateConfig.getNow();
|
||||
if (!date) return now;
|
||||
// When value is null and set showTime
|
||||
if (!mergedValue && showTime) {
|
||||
if (typeof showTime === 'object') {
|
||||
return setDateTime(generateConfig, date, showTime.defaultValue || now);
|
||||
const panelContext = useInjectPanel();
|
||||
const {
|
||||
operationRef,
|
||||
panelRef: panelDivRef,
|
||||
onSelect: onContextSelect,
|
||||
hideRanges,
|
||||
defaultOpenValue,
|
||||
} = panelContext;
|
||||
const { inRange, panelPosition, rangedValue, hoverRangedValue } = useInjectRange();
|
||||
const panelRef = ref<PanelRefProps>({});
|
||||
// Value
|
||||
const [mergedValue, setInnerValue] = useMergedState<DateType | null>(null, {
|
||||
value: toRef(props, 'value'),
|
||||
defaultValue: props.defaultValue,
|
||||
postState: val => {
|
||||
if (!val && defaultOpenValue.value && props.picker === 'time') {
|
||||
return defaultOpenValue.value;
|
||||
}
|
||||
return val;
|
||||
},
|
||||
});
|
||||
|
||||
// View date control
|
||||
const [viewDate, setInnerViewDate] = useMergedState<DateType | null>(null, {
|
||||
value: toRef(props, 'pickerValue'),
|
||||
defaultValue: props.defaultPickerValue || mergedValue.value,
|
||||
postState: date => {
|
||||
const { generateConfig, showTime, defaultValue } = props;
|
||||
const now = generateConfig.getNow();
|
||||
if (!date) return now;
|
||||
// When value is null and set showTime
|
||||
if (!mergedValue && props.showTime) {
|
||||
if (typeof showTime === 'object') {
|
||||
return setDateTime(generateConfig, date, showTime.defaultValue || now);
|
||||
}
|
||||
if (defaultValue) {
|
||||
return setDateTime(generateConfig, date, defaultValue);
|
||||
}
|
||||
return setDateTime(generateConfig, date, now);
|
||||
}
|
||||
return date;
|
||||
},
|
||||
});
|
||||
|
||||
const setViewDate = (date: DateType) => {
|
||||
setInnerViewDate(date);
|
||||
if (props.onPickerValueChange) {
|
||||
props.onPickerValueChange(date);
|
||||
}
|
||||
if (defaultValue) {
|
||||
return setDateTime(generateConfig, date, defaultValue);
|
||||
};
|
||||
|
||||
// Panel control
|
||||
const getInternalNextMode = (nextMode: PanelMode): PanelMode => {
|
||||
const getNextMode = PickerModeMap[props.picker!];
|
||||
if (getNextMode) {
|
||||
return getNextMode(nextMode);
|
||||
}
|
||||
return setDateTime(generateConfig, date, now);
|
||||
}
|
||||
return date;
|
||||
},
|
||||
});
|
||||
|
||||
const setViewDate = (date: DateType) => {
|
||||
setInnerViewDate(date);
|
||||
if (onPickerValueChange) {
|
||||
onPickerValueChange(date);
|
||||
}
|
||||
};
|
||||
return nextMode;
|
||||
};
|
||||
|
||||
// Panel control
|
||||
const getInternalNextMode = (nextMode: PanelMode): PanelMode => {
|
||||
const getNextMode = PickerModeMap[picker!];
|
||||
if (getNextMode) {
|
||||
return getNextMode(nextMode);
|
||||
}
|
||||
|
||||
return nextMode;
|
||||
};
|
||||
|
||||
// Save panel is changed from which panel
|
||||
const [mergedMode, setInnerMode] = useMergedState(
|
||||
() => {
|
||||
if (picker === 'time') {
|
||||
return 'time';
|
||||
}
|
||||
return getInternalNextMode('date');
|
||||
},
|
||||
{
|
||||
value: mode,
|
||||
},
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
setInnerMode(picker);
|
||||
}, [picker]);
|
||||
|
||||
const [sourceMode, setSourceMode] = React.useState<PanelMode>(() => mergedMode);
|
||||
|
||||
const onInternalPanelChange = (newMode: PanelMode | null, viewValue: DateType) => {
|
||||
const nextMode = getInternalNextMode(newMode || mergedMode);
|
||||
setSourceMode(mergedMode);
|
||||
setInnerMode(nextMode);
|
||||
|
||||
if (onPanelChange && (mergedMode !== nextMode || isEqual(generateConfig, viewDate, viewDate))) {
|
||||
onPanelChange(viewValue, nextMode);
|
||||
}
|
||||
};
|
||||
|
||||
const triggerSelect = (
|
||||
date: DateType,
|
||||
type: 'key' | 'mouse' | 'submit',
|
||||
forceTriggerSelect: boolean = false,
|
||||
) => {
|
||||
if (mergedMode === picker || forceTriggerSelect) {
|
||||
setInnerValue(date);
|
||||
|
||||
if (onSelect) {
|
||||
onSelect(date);
|
||||
}
|
||||
|
||||
if (onContextSelect) {
|
||||
onContextSelect(date, type);
|
||||
}
|
||||
|
||||
if (onChange && !isEqual(generateConfig, date, mergedValue) && !disabledDate?.(date)) {
|
||||
onChange(date);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ========================= Interactive ==========================
|
||||
const onInternalKeyDown = (e: React.KeyboardEvent<HTMLElement>) => {
|
||||
if (panelRef.current && panelRef.current.onKeyDown) {
|
||||
if (
|
||||
[
|
||||
KeyCode.LEFT,
|
||||
KeyCode.RIGHT,
|
||||
KeyCode.UP,
|
||||
KeyCode.DOWN,
|
||||
KeyCode.PAGE_UP,
|
||||
KeyCode.PAGE_DOWN,
|
||||
KeyCode.ENTER,
|
||||
].includes(e.which)
|
||||
) {
|
||||
e.preventDefault();
|
||||
}
|
||||
return panelRef.current.onKeyDown(e);
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
/* eslint-disable no-lone-blocks */
|
||||
{
|
||||
warning(
|
||||
false,
|
||||
'Panel not correct handle keyDown event. Please help to fire issue about this.',
|
||||
// Save panel is changed from which panel
|
||||
const [mergedMode, setInnerMode] = useMergedState(
|
||||
() => {
|
||||
if (props.picker === 'time') {
|
||||
return 'time';
|
||||
}
|
||||
return getInternalNextMode('date');
|
||||
},
|
||||
{
|
||||
value: toRef(props, 'mode'),
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.picker,
|
||||
() => {
|
||||
setInnerMode(props.picker);
|
||||
},
|
||||
);
|
||||
return false;
|
||||
}
|
||||
/* eslint-enable no-lone-blocks */
|
||||
};
|
||||
|
||||
const onInternalBlur: React.FocusEventHandler<HTMLElement> = (e) => {
|
||||
if (panelRef.current && panelRef.current.onBlur) {
|
||||
panelRef.current.onBlur(e);
|
||||
}
|
||||
};
|
||||
const sourceMode = ref(mergedMode.value);
|
||||
const setSourceMode = (val: PanelMode) => {
|
||||
sourceMode.value = val;
|
||||
};
|
||||
|
||||
if (operationRef && panelPosition !== 'right') {
|
||||
operationRef.current = {
|
||||
onKeyDown: onInternalKeyDown,
|
||||
onClose: () => {
|
||||
if (panelRef.current && panelRef.current.onClose) {
|
||||
panelRef.current.onClose();
|
||||
const onInternalPanelChange = (newMode: PanelMode | null, viewValue: DateType) => {
|
||||
const { onPanelChange, generateConfig } = props;
|
||||
const nextMode = getInternalNextMode(newMode || mergedMode.value);
|
||||
setSourceMode(mergedMode.value);
|
||||
setInnerMode(nextMode);
|
||||
|
||||
if (
|
||||
onPanelChange &&
|
||||
(mergedMode.value !== nextMode || isEqual(generateConfig, viewDate.value, viewDate.value))
|
||||
) {
|
||||
onPanelChange(viewValue, nextMode);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// ============================ Effect ============================
|
||||
React.useEffect(() => {
|
||||
if (value && !initRef.current) {
|
||||
setInnerViewDate(value);
|
||||
}
|
||||
}, [value]);
|
||||
const triggerSelect = (
|
||||
date: DateType,
|
||||
type: 'key' | 'mouse' | 'submit',
|
||||
forceTriggerSelect: boolean = false,
|
||||
) => {
|
||||
const { picker, generateConfig, onSelect, onChange, disabledDate } = props;
|
||||
if (mergedMode.value === picker || forceTriggerSelect) {
|
||||
setInnerValue(date);
|
||||
|
||||
React.useEffect(() => {
|
||||
initRef.current = false;
|
||||
}, []);
|
||||
if (onSelect) {
|
||||
onSelect(date);
|
||||
}
|
||||
|
||||
// ============================ Panels ============================
|
||||
let panelNode: React.ReactNode;
|
||||
if (onContextSelect) {
|
||||
onContextSelect(date, type);
|
||||
}
|
||||
|
||||
const pickerProps = {
|
||||
...(props as MergedPickerPanelProps<DateType>),
|
||||
operationRef: panelRef,
|
||||
prefixCls,
|
||||
viewDate,
|
||||
value: mergedValue,
|
||||
onViewDateChange: setViewDate,
|
||||
sourceMode,
|
||||
onPanelChange: onInternalPanelChange,
|
||||
disabledDate,
|
||||
};
|
||||
delete pickerProps.onChange;
|
||||
delete pickerProps.onSelect;
|
||||
|
||||
switch (mergedMode) {
|
||||
case 'decade':
|
||||
panelNode = (
|
||||
<DecadePanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
panelNode = (
|
||||
<YearPanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
panelNode = (
|
||||
<MonthPanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'quarter':
|
||||
panelNode = (
|
||||
<QuarterPanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'week':
|
||||
panelNode = (
|
||||
<WeekPanel
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'time':
|
||||
delete pickerProps.showTime;
|
||||
panelNode = (
|
||||
<TimePanel<DateType>
|
||||
{...pickerProps}
|
||||
{...(typeof showTime === 'object' ? showTime : null)}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (showTime) {
|
||||
panelNode = (
|
||||
<DatetimePanel
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
panelNode = (
|
||||
<DatePanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Footer ============================
|
||||
let extraFooter: React.ReactNode;
|
||||
let rangesNode: React.ReactNode;
|
||||
|
||||
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) {
|
||||
extraFooter = getExtraFooter(prefixCls, mergedMode, renderExtraFooter);
|
||||
rangesNode = getRanges({
|
||||
prefixCls,
|
||||
components,
|
||||
needConfirmButton,
|
||||
okDisabled: !mergedValue || (disabledDate && disabledDate(mergedValue)),
|
||||
locale,
|
||||
showNow,
|
||||
onNow: needConfirmButton && onNow,
|
||||
onOk: () => {
|
||||
if (mergedValue) {
|
||||
triggerSelect(mergedValue, 'submit', true);
|
||||
if (onOk) {
|
||||
onOk(mergedValue);
|
||||
if (
|
||||
onChange &&
|
||||
!isEqual(generateConfig, date, mergedValue.value) &&
|
||||
!disabledDate?.(date)
|
||||
) {
|
||||
onChange(date);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let todayNode: React.ReactNode;
|
||||
|
||||
if (showToday && mergedMode === 'date' && picker === 'date' && !showTime) {
|
||||
const now = generateConfig.getNow();
|
||||
const todayCls = `${prefixCls}-today-btn`;
|
||||
const disabled = disabledDate && disabledDate(now);
|
||||
todayNode = (
|
||||
<a
|
||||
class={classNames(todayCls, disabled && `${todayCls}-disabled`)}
|
||||
aria-disabled={disabled}
|
||||
onClick={() => {
|
||||
if (!disabled) {
|
||||
triggerSelect(now, 'mouse', true);
|
||||
// ========================= Interactive ==========================
|
||||
const onInternalKeyDown = (e: KeyboardEvent) => {
|
||||
if (panelRef.value && panelRef.value.onKeyDown) {
|
||||
if (
|
||||
[
|
||||
KeyCode.LEFT,
|
||||
KeyCode.RIGHT,
|
||||
KeyCode.UP,
|
||||
KeyCode.DOWN,
|
||||
KeyCode.PAGE_UP,
|
||||
KeyCode.PAGE_DOWN,
|
||||
KeyCode.ENTER,
|
||||
].includes(e.which)
|
||||
) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{locale.today}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return panelRef.value.onKeyDown(e);
|
||||
}
|
||||
|
||||
return (
|
||||
<PanelContext.Provider
|
||||
value={{
|
||||
/* istanbul ignore next */
|
||||
/* eslint-disable no-lone-blocks */
|
||||
{
|
||||
warning(
|
||||
false,
|
||||
'Panel not correct handle keyDown event. Please help to fire issue about this.',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
/* eslint-enable no-lone-blocks */
|
||||
};
|
||||
|
||||
const onInternalBlur = (e: FocusEvent) => {
|
||||
if (panelRef.value && panelRef.value.onBlur) {
|
||||
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');
|
||||
};
|
||||
|
||||
const classString = computed(() => {
|
||||
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: 'hideHeader' in props ? hideHeader : panelContext.hideHeader,
|
||||
hidePrevBtn: inRange && panelPosition === 'right',
|
||||
hideNextBtn: inRange && panelPosition === 'left',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
tabindex={tabIndex}
|
||||
class={classNames(`${prefixCls}-panel`, className, {
|
||||
[`${prefixCls}-panel-has-range`]: rangedValue && rangedValue[0] && rangedValue[1],
|
||||
[`${prefixCls}-panel-has-range-hover`]:
|
||||
hoverRangedValue && hoverRangedValue[0] && hoverRangedValue[1],
|
||||
[`${prefixCls}-panel-rtl`]: direction === 'rtl',
|
||||
})}
|
||||
style={style}
|
||||
onKeydown={onInternalKeyDown}
|
||||
onBlur={onInternalBlur}
|
||||
onMousedown={onMouseDown}
|
||||
ref={panelDivRef}
|
||||
>
|
||||
{panelNode}
|
||||
{extraFooter || rangesNode || todayNode ? (
|
||||
<div class={`${prefixCls}-footer`}>
|
||||
{extraFooter}
|
||||
{rangesNode}
|
||||
{todayNode}
|
||||
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,
|
||||
onClose: () => {
|
||||
if (panelRef.value && panelRef.value.onClose) {
|
||||
panelRef.value.onClose();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// ============================ Panels ============================
|
||||
let panelNode: VueNode;
|
||||
|
||||
const pickerProps = {
|
||||
...(props as MergedPickerPanelProps<DateType>),
|
||||
operationRef: panelRef,
|
||||
prefixCls,
|
||||
viewDate: viewDate.value,
|
||||
value: mergedValue.value,
|
||||
onViewDateChange: setViewDate,
|
||||
sourceMode: sourceMode.value,
|
||||
onPanelChange: onInternalPanelChange,
|
||||
disabledDate,
|
||||
};
|
||||
delete pickerProps.onChange;
|
||||
delete pickerProps.onSelect;
|
||||
|
||||
switch (mergedMode.value) {
|
||||
case 'decade':
|
||||
panelNode = (
|
||||
<DecadePanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'year':
|
||||
panelNode = (
|
||||
<YearPanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
panelNode = (
|
||||
<MonthPanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'quarter':
|
||||
panelNode = (
|
||||
<QuarterPanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'week':
|
||||
panelNode = (
|
||||
<WeekPanel
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
case 'time':
|
||||
delete pickerProps.showTime;
|
||||
panelNode = (
|
||||
<TimePanel<DateType>
|
||||
{...pickerProps}
|
||||
{...(typeof showTime === 'object' ? showTime : null)}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (showTime) {
|
||||
panelNode = (
|
||||
<DatetimePanel
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
panelNode = (
|
||||
<DatePanel<DateType>
|
||||
{...pickerProps}
|
||||
onSelect={(date, type) => {
|
||||
setViewDate(date);
|
||||
triggerSelect(date, type);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================ Footer ============================
|
||||
let extraFooter: VueNode;
|
||||
let rangesNode: VueNode;
|
||||
|
||||
if (!hideRanges) {
|
||||
extraFooter = getExtraFooter(prefixCls, mergedMode.value, renderExtraFooter);
|
||||
rangesNode = getRanges({
|
||||
prefixCls,
|
||||
components,
|
||||
needConfirmButton: needConfirmButton.value,
|
||||
okDisabled: !mergedValue || (disabledDate && disabledDate(mergedValue.value)),
|
||||
locale,
|
||||
showNow,
|
||||
onNow: needConfirmButton.value && onNow,
|
||||
onOk: () => {
|
||||
if (mergedValue) {
|
||||
triggerSelect(mergedValue.value, 'submit', true);
|
||||
if (onOk) {
|
||||
onOk(mergedValue.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let todayNode: VueNode;
|
||||
|
||||
if (showToday && mergedMode.value === 'date' && picker === 'date' && !showTime) {
|
||||
const now = generateConfig.getNow();
|
||||
const todayCls = `${prefixCls}-today-btn`;
|
||||
const disabled = disabledDate && disabledDate(now);
|
||||
todayNode = (
|
||||
<a
|
||||
class={classNames(todayCls, disabled && `${todayCls}-disabled`)}
|
||||
aria-disabled={disabled}
|
||||
onClick={() => {
|
||||
if (!disabled) {
|
||||
triggerSelect(now, 'mouse', true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{locale.today}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
tabindex={tabindex}
|
||||
class={classNames(classString.value, attrs.class)}
|
||||
style={attrs.style}
|
||||
onKeydown={onInternalKeyDown}
|
||||
onBlur={onInternalBlur}
|
||||
onMousedown={onMouseDown}
|
||||
ref={panelDivRef}
|
||||
>
|
||||
{panelNode}
|
||||
{extraFooter || rangesNode || todayNode ? (
|
||||
<div class={`${prefixCls}-footer`}>
|
||||
{extraFooter}
|
||||
{rangesNode}
|
||||
{todayNode}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</PanelContext.Provider>
|
||||
);
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default PickerPanel;
|
||||
/* eslint-enable */
|
||||
export default PickerPanel();
|
||||
|
|
|
@ -3,6 +3,7 @@ import { AlignType } from '../vc-align/interface';
|
|||
import Trigger from '../vc-trigger';
|
||||
import classNames from '../_util/classNames';
|
||||
import { VueNode } from '../_util/type';
|
||||
import useMergeProps from './hooks/useMergeProps';
|
||||
|
||||
const BUILT_IN_PLACEMENTS = {
|
||||
bottomLeft: {
|
||||
|
@ -56,8 +57,8 @@ export type PickerTriggerProps = {
|
|||
direction?: 'ltr' | 'rtl';
|
||||
};
|
||||
|
||||
function PickerTrigger(
|
||||
{
|
||||
function PickerTrigger(props: PickerTriggerProps, { slots }) {
|
||||
const {
|
||||
prefixCls,
|
||||
popupElement,
|
||||
popupStyle,
|
||||
|
@ -69,9 +70,7 @@ function PickerTrigger(
|
|||
range,
|
||||
popupPlacement,
|
||||
direction,
|
||||
}: PickerTriggerProps,
|
||||
{ slots },
|
||||
) {
|
||||
} = useMergeProps(props);
|
||||
const dropdownPrefixCls = `${prefixCls}-dropdown`;
|
||||
|
||||
const getPopupPlacement = () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { inject, InjectionKey, provide } from 'vue';
|
||||
import { inject, InjectionKey, provide, Ref } from 'vue';
|
||||
import type { NullableDateType, RangeValue } from './interface';
|
||||
|
||||
export type RangeContextProps = {
|
||||
|
@ -6,13 +6,12 @@ export type RangeContextProps = {
|
|||
* Set displayed range value style.
|
||||
* Panel only has one value, this is only style effect.
|
||||
*/
|
||||
rangedValue?: [NullableDateType<any>, NullableDateType<any>] | null;
|
||||
hoverRangedValue?: RangeValue<any>;
|
||||
inRange?: boolean;
|
||||
panelPosition?: 'left' | 'right' | false;
|
||||
rangedValue?: Ref<[NullableDateType<any>, NullableDateType<any>] | null>;
|
||||
hoverRangedValue?: Ref<RangeValue<any>>;
|
||||
inRange?: Ref<boolean>;
|
||||
panelPosition?: Ref<'left' | 'right' | false>;
|
||||
};
|
||||
|
||||
|
||||
const RangeContextKey: InjectionKey<RangeContextProps> = Symbol('RangeContextProps');
|
||||
|
||||
export const useProvideRange = (props: RangeContextProps) => {
|
||||
|
@ -23,5 +22,4 @@ export const useInjectRange = () => {
|
|||
return inject(RangeContextKey);
|
||||
};
|
||||
|
||||
|
||||
export default RangeContextKey;
|
||||
|
|
|
@ -16,10 +16,7 @@ export default function useCellClassName<DateType>({
|
|||
}: {
|
||||
cellPrefixCls: string;
|
||||
generateConfig: GenerateConfig<DateType>;
|
||||
isSameCell: (
|
||||
current: NullableDateType<DateType>,
|
||||
target: NullableDateType<DateType>,
|
||||
) => boolean;
|
||||
isSameCell: (current: NullableDateType<DateType>, target: NullableDateType<DateType>) => boolean;
|
||||
offsetCell: (date: DateType, offset: number) => DateType;
|
||||
isInView: (date: DateType) => boolean;
|
||||
rangedValue?: RangeValue<DateType>;
|
||||
|
@ -37,12 +34,7 @@ export default function useCellClassName<DateType>({
|
|||
const hoverStart = getValue(hoverRangedValue, 0);
|
||||
const hoverEnd = getValue(hoverRangedValue, 1);
|
||||
|
||||
const isRangeHovered = isInRange(
|
||||
generateConfig,
|
||||
hoverStart,
|
||||
hoverEnd,
|
||||
currentDate,
|
||||
);
|
||||
const isRangeHovered = isInRange(generateConfig, hoverStart, hoverEnd, currentDate);
|
||||
|
||||
function isRangeStart(date: DateType) {
|
||||
return isSameCell(rangeStart, date);
|
||||
|
@ -54,11 +46,9 @@ export default function useCellClassName<DateType>({
|
|||
const isHoverEnd = isSameCell(hoverEnd, currentDate);
|
||||
|
||||
const isHoverEdgeStart =
|
||||
(isRangeHovered || isHoverEnd) &&
|
||||
(!isInView(prevDate) || isRangeEnd(prevDate));
|
||||
(isRangeHovered || isHoverEnd) && (!isInView(prevDate) || isRangeEnd(prevDate));
|
||||
const isHoverEdgeEnd =
|
||||
(isRangeHovered || isHoverStart) &&
|
||||
(!isInView(nextDate) || isRangeStart(nextDate));
|
||||
(isRangeHovered || isHoverStart) && (!isInView(nextDate) || isRangeStart(nextDate));
|
||||
|
||||
return {
|
||||
// In view
|
||||
|
@ -73,10 +63,8 @@ export default function useCellClassName<DateType>({
|
|||
),
|
||||
[`${cellPrefixCls}-range-start`]: isRangeStart(currentDate),
|
||||
[`${cellPrefixCls}-range-end`]: isRangeEnd(currentDate),
|
||||
[`${cellPrefixCls}-range-start-single`]:
|
||||
isRangeStart(currentDate) && !rangeEnd,
|
||||
[`${cellPrefixCls}-range-end-single`]:
|
||||
isRangeEnd(currentDate) && !rangeStart,
|
||||
[`${cellPrefixCls}-range-start-single`]: isRangeStart(currentDate) && !rangeEnd,
|
||||
[`${cellPrefixCls}-range-end-single`]: isRangeEnd(currentDate) && !rangeStart,
|
||||
[`${cellPrefixCls}-range-start-near-hover`]:
|
||||
isRangeStart(currentDate) &&
|
||||
(isSameCell(prevDate, hoverStart) ||
|
||||
|
|
|
@ -9,7 +9,7 @@ export default function useHoverValue<DateType>(
|
|||
const [value, internalSetValue] = useState<DateType>(null);
|
||||
const raf = useRef(null);
|
||||
|
||||
function setValue(val: DateType, immediately: boolean = false) {
|
||||
function setValue(val: DateType, immediately = false) {
|
||||
cancelAnimationFrame(raf.current);
|
||||
if (immediately) {
|
||||
internalSetValue(val);
|
||||
|
@ -30,7 +30,7 @@ export default function useHoverValue<DateType>(
|
|||
setValue(date);
|
||||
}
|
||||
|
||||
function onLeave(immediately: boolean = false) {
|
||||
function onLeave(immediately = false) {
|
||||
setValue(null, immediately);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 };
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
import type * as React from 'react';
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import KeyCode from 'rc-util/lib/KeyCode';
|
||||
import type { ComputedRef, HTMLAttributes, Ref } from 'vue';
|
||||
import { onBeforeUnmount } from 'vue';
|
||||
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';
|
||||
|
||||
export default function usePickerInput({
|
||||
|
@ -16,51 +21,51 @@ export default function usePickerInput({
|
|||
onFocus,
|
||||
onBlur,
|
||||
}: {
|
||||
open: boolean;
|
||||
value: string;
|
||||
open: Ref<boolean>;
|
||||
value: Ref<string>;
|
||||
isClickOutside: (clickElement: EventTarget | null) => boolean;
|
||||
triggerOpen: (open: boolean) => void;
|
||||
forwardKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => boolean;
|
||||
onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>, preventDefault: () => void) => void;
|
||||
blurToCancel?: boolean;
|
||||
forwardKeyDown: (e: KeyboardEvent) => boolean;
|
||||
onKeyDown: (e: KeyboardEvent, preventDefault: () => void) => void;
|
||||
blurToCancel?: ComputedRef<boolean>;
|
||||
onSubmit: () => void | boolean;
|
||||
onCancel: () => void;
|
||||
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
||||
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
||||
}): [React.DOMAttributes<HTMLInputElement>, { focused: boolean; typing: boolean }] {
|
||||
const [typing, setTyping] = useState(false);
|
||||
const [focused, setFocused] = useState(false);
|
||||
onFocus?: FocusEventHandler;
|
||||
onBlur?: FocusEventHandler;
|
||||
}): [ComputedRef<HTMLAttributes>, { focused: Ref<boolean>; typing: Ref<boolean> }] {
|
||||
const typing = ref(false);
|
||||
const focused = ref(false);
|
||||
|
||||
/**
|
||||
* We will prevent blur to handle open event when user click outside,
|
||||
* 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> = {
|
||||
onMouseDown: () => {
|
||||
setTyping(true);
|
||||
const inputProps = computed<HTMLAttributes>(() => ({
|
||||
onMousedown: () => {
|
||||
typing.value = true;
|
||||
triggerOpen(true);
|
||||
},
|
||||
onKeyDown: (e) => {
|
||||
onKeydown: e => {
|
||||
const preventDefault = (): void => {
|
||||
preventDefaultRef.current = true;
|
||||
preventDefaultRef.value = true;
|
||||
};
|
||||
|
||||
onKeyDown(e, preventDefault);
|
||||
|
||||
if (preventDefaultRef.current) return;
|
||||
if (preventDefaultRef.value) return;
|
||||
|
||||
switch (e.which) {
|
||||
case KeyCode.ENTER: {
|
||||
if (!open) {
|
||||
if (!open.value) {
|
||||
triggerOpen(true);
|
||||
} else if (onSubmit() !== false) {
|
||||
setTyping(true);
|
||||
typing.value = true;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
@ -68,12 +73,12 @@ export default function usePickerInput({
|
|||
}
|
||||
|
||||
case KeyCode.TAB: {
|
||||
if (typing && open && !e.shiftKey) {
|
||||
setTyping(false);
|
||||
if (typing.value && open.value && !e.shiftKey) {
|
||||
typing.value = false;
|
||||
e.preventDefault();
|
||||
} else if (!typing && open) {
|
||||
} else if (!typing.value && open.value) {
|
||||
if (!forwardKeyDown(e) && e.shiftKey) {
|
||||
setTyping(true);
|
||||
typing.value = true;
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
@ -81,32 +86,32 @@ export default function usePickerInput({
|
|||
}
|
||||
|
||||
case KeyCode.ESC: {
|
||||
setTyping(true);
|
||||
typing.value = true;
|
||||
onCancel();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!open && ![KeyCode.SHIFT].includes(e.which)) {
|
||||
if (!open.value && ![KeyCode.SHIFT].includes(e.which)) {
|
||||
triggerOpen(true);
|
||||
} else if (!typing) {
|
||||
} else if (!typing.value) {
|
||||
// Let popup panel handle keyboard
|
||||
forwardKeyDown(e);
|
||||
}
|
||||
},
|
||||
|
||||
onFocus: (e) => {
|
||||
setTyping(true);
|
||||
setFocused(true);
|
||||
onFocus: e => {
|
||||
typing.value = true;
|
||||
focused.value = true;
|
||||
|
||||
if (onFocus) {
|
||||
onFocus(e);
|
||||
}
|
||||
},
|
||||
|
||||
onBlur: (e) => {
|
||||
if (preventBlurRef.current || !isClickOutside(document.activeElement)) {
|
||||
preventBlurRef.current = false;
|
||||
onBlur: e => {
|
||||
if (preventBlurRef.value || !isClickOutside(document.activeElement)) {
|
||||
preventBlurRef.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -121,51 +126,58 @@ export default function usePickerInput({
|
|||
onCancel();
|
||||
}
|
||||
}, 0);
|
||||
} else if (open) {
|
||||
} else if (open.value) {
|
||||
triggerOpen(false);
|
||||
|
||||
if (valueChangedRef.current) {
|
||||
if (valueChangedRef.value) {
|
||||
onSubmit();
|
||||
}
|
||||
}
|
||||
setFocused(false);
|
||||
focused.value = false;
|
||||
|
||||
if (onBlur) {
|
||||
onBlur(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
}));
|
||||
|
||||
// check if value changed
|
||||
useEffect(() => {
|
||||
valueChangedRef.current = false;
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
valueChangedRef.current = true;
|
||||
}, [value]);
|
||||
watch(open, () => {
|
||||
valueChangedRef.value = false;
|
||||
});
|
||||
|
||||
watch(value, () => {
|
||||
valueChangedRef.value = true;
|
||||
});
|
||||
const globalMouseDownEvent = ref();
|
||||
// Global click handler
|
||||
useEffect(() =>
|
||||
addGlobalMouseDownEvent((e: MouseEvent) => {
|
||||
const target = getTargetFromEvent(e);
|
||||
watchEffect(
|
||||
() =>
|
||||
globalMouseDownEvent.value &&
|
||||
globalMouseDownEvent.value()(
|
||||
(globalMouseDownEvent.value = addGlobalMouseDownEvent((e: MouseEvent) => {
|
||||
const target = getTargetFromEvent(e);
|
||||
|
||||
if (open) {
|
||||
const clickedOutside = isClickOutside(target);
|
||||
if (open) {
|
||||
const clickedOutside = isClickOutside(target);
|
||||
|
||||
if (!clickedOutside) {
|
||||
preventBlurRef.current = true;
|
||||
if (!clickedOutside) {
|
||||
preventBlurRef.value = true;
|
||||
|
||||
// Always set back in case `onBlur` prevented by user
|
||||
requestAnimationFrame(() => {
|
||||
preventBlurRef.current = false;
|
||||
});
|
||||
} else if (!focused || clickedOutside) {
|
||||
triggerOpen(false);
|
||||
}
|
||||
}
|
||||
}),
|
||||
// Always set back in case `onBlur` prevented by user
|
||||
requestAnimationFrame(() => {
|
||||
preventBlurRef.value = false;
|
||||
});
|
||||
} else if (!focused.value || clickedOutside) {
|
||||
triggerOpen(false);
|
||||
}
|
||||
}
|
||||
})),
|
||||
),
|
||||
);
|
||||
onBeforeUnmount(() => {
|
||||
globalMouseDownEvent.value && globalMouseDownEvent.value();
|
||||
});
|
||||
|
||||
return [inputProps, { focused, typing }];
|
||||
}
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
import * as React from 'react';
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
export default function useTextValueMapping({
|
||||
valueTexts,
|
||||
onTextChange,
|
||||
}: {
|
||||
/** Must useMemo, to assume that `valueTexts` only match on the first change */
|
||||
valueTexts: string[];
|
||||
valueTexts: ComputedRef<string[]>;
|
||||
onTextChange: (text: string) => void;
|
||||
}): [string, (text: string) => void, () => void] {
|
||||
const [text, setInnerText] = React.useState('');
|
||||
const valueTextsRef = React.useRef<string[]>([]);
|
||||
valueTextsRef.current = valueTexts;
|
||||
}): [Ref<string>, (text: string) => void, () => void] {
|
||||
const text = ref('');
|
||||
|
||||
function triggerTextChange(value: string) {
|
||||
setInnerText(value);
|
||||
text.value = value;
|
||||
onTextChange(value);
|
||||
}
|
||||
|
||||
function resetText() {
|
||||
setInnerText(valueTextsRef.current[0]);
|
||||
text.value = valueTexts.value[0];
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (valueTexts.every(valText => valText !== text)) {
|
||||
resetText();
|
||||
}
|
||||
}, [valueTexts.join('||')]);
|
||||
watch(
|
||||
() => [...valueTexts.value],
|
||||
(cur, pre) => {
|
||||
if (
|
||||
cur.join('||') !== pre.join('||') &&
|
||||
valueTexts.value.every(valText => valText !== text.value)
|
||||
) {
|
||||
resetText();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return [text, triggerTextChange, resetText];
|
||||
}
|
||||
|
|
|
@ -1,32 +1,37 @@
|
|||
import shallowEqual from 'shallowequal';
|
||||
import useMemo from 'rc-util/lib/hooks/useMemo';
|
||||
import type { ComputedRef, Ref } from 'vue';
|
||||
import useMemo from '../../_util/hooks/useMemo';
|
||||
import shallowequal from '../../_util/shallowequal';
|
||||
import type { GenerateConfig } from '../generate';
|
||||
import type { CustomFormat, Locale } from '../interface';
|
||||
import { formatValue } from '../utils/dateUtil';
|
||||
|
||||
export type ValueTextConfig<DateType> = {
|
||||
formatList: (string | CustomFormat<DateType>)[];
|
||||
generateConfig: GenerateConfig<DateType>;
|
||||
locale: Locale;
|
||||
formatList: ComputedRef<(string | CustomFormat<DateType>)[]>;
|
||||
generateConfig: Ref<GenerateConfig<DateType>>;
|
||||
locale: Ref<Locale>;
|
||||
};
|
||||
|
||||
export default function useValueTexts<DateType>(
|
||||
value: DateType | null,
|
||||
value: Ref<DateType | null>,
|
||||
{ formatList, generateConfig, locale }: ValueTextConfig<DateType>,
|
||||
) {
|
||||
return useMemo<[string[], string]>(
|
||||
() => {
|
||||
if (!value) {
|
||||
if (!value.value) {
|
||||
return [[''], ''];
|
||||
}
|
||||
|
||||
// We will convert data format back to first format
|
||||
let firstValueText: string = '';
|
||||
let firstValueText = '';
|
||||
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 formatStr = formatValue(value, { generateConfig, locale, format });
|
||||
const formatStr = formatValue(value.value, {
|
||||
generateConfig: generateConfig.value,
|
||||
locale: locale.value,
|
||||
format,
|
||||
});
|
||||
fullValueTexts.push(formatStr);
|
||||
|
||||
if (i === 0) {
|
||||
|
@ -37,6 +42,6 @@ export default function useValueTexts<DateType>(
|
|||
return [fullValueTexts, firstValueText];
|
||||
},
|
||||
[value, formatList],
|
||||
(prev, next) => prev[0] !== next[0] || !shallowEqual(prev[1], next[1]),
|
||||
(next, prev) => prev[0] !== next[0] || !shallowequal(prev[1], next[1]),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import type { Ref } from 'vue';
|
||||
import type { GenerateConfig } from './generate';
|
||||
|
||||
export type Locale = {
|
||||
|
@ -46,7 +47,7 @@ export type PickerMode = Exclude<PanelMode, 'datetime' | 'decade'>;
|
|||
|
||||
export type PanelRefProps = {
|
||||
onKeyDown?: (e: KeyboardEvent) => boolean;
|
||||
onBlur?: (e: FocusEvent)=> void;
|
||||
onBlur?: (e: FocusEvent) => void;
|
||||
onClose?: () => void;
|
||||
};
|
||||
|
||||
|
@ -74,7 +75,7 @@ export type PanelSharedProps<DateType> = {
|
|||
// * Thus, move ref into operationRef.
|
||||
// * This is little hack which should refactor after typescript support.
|
||||
// */
|
||||
// operationRef: React.MutableRefObject<PanelRefProps>;
|
||||
operationRef: Ref<PanelRefProps>;
|
||||
|
||||
onSelect: OnSelect<DateType>;
|
||||
onViewDateChange: (value: DateType) => void;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import {
|
||||
WEEK_DAY_COUNT,
|
||||
|
@ -12,6 +11,7 @@ import useCellClassName from '../../hooks/useCellClassName';
|
|||
import PanelBody from '../PanelBody';
|
||||
import { VueNode } from '../../../_util/type';
|
||||
import { useInjectRange } from '../../RangeContext';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type DateRender<DateType> = (currentDate: DateType, today: DateType) => VueNode;
|
||||
|
||||
|
@ -34,19 +34,12 @@ export type DateBodyProps<DateType> = {
|
|||
onSelect: (value: DateType) => void;
|
||||
} & DateBodyPassProps<DateType>;
|
||||
|
||||
function DateBody<DateType>(props: DateBodyProps<DateType>) {
|
||||
const {
|
||||
prefixCls,
|
||||
generateConfig,
|
||||
prefixColumn,
|
||||
locale,
|
||||
rowCount,
|
||||
viewDate,
|
||||
value,
|
||||
dateRender,
|
||||
} = props;
|
||||
function DateBody<DateType>(_props: DateBodyProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const { prefixCls, generateConfig, prefixColumn, locale, rowCount, viewDate, value, dateRender } =
|
||||
props;
|
||||
|
||||
const { rangedValue, hoverRangedValue } =useInjectRange()
|
||||
const { rangedValue, hoverRangedValue } = useInjectRange();
|
||||
|
||||
const baseDate = getWeekStartDate(locale.locale, generateConfig, viewDate);
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
|
@ -74,8 +67,8 @@ function DateBody<DateType>(props: DateBodyProps<DateType>) {
|
|||
today,
|
||||
value,
|
||||
generateConfig,
|
||||
rangedValue: prefixColumn ? null : rangedValue,
|
||||
hoverRangedValue: prefixColumn ? null : hoverRangedValue,
|
||||
rangedValue: prefixColumn ? null : rangedValue.value,
|
||||
hoverRangedValue: prefixColumn ? null : hoverRangedValue.value,
|
||||
isSameCell: (current, target) => isSameDate(generateConfig, current, target),
|
||||
isInView: date => isSameMonth(generateConfig, date, viewDate),
|
||||
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.props = [
|
||||
'prefixCls',
|
||||
'generateConfig',
|
||||
'value?',
|
||||
'viewDate',
|
||||
'locale',
|
||||
'rowCount',
|
||||
'onSelect',
|
||||
'dateRender?',
|
||||
'disabledDate?',
|
||||
// Used for week panel
|
||||
'prefixColumn?',
|
||||
'rowClassName?',
|
||||
];
|
||||
export default DateBody;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
import Header from '../Header';
|
||||
import type { Locale } from '../../interface';
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { useInjectPanel } from '../../PanelContext';
|
||||
import { formatValue } from '../../utils/dateUtil';
|
||||
import { VueNode } from '../../../_util/type';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type DateHeaderProps<DateType> = {
|
||||
prefixCls: string;
|
||||
|
@ -21,7 +21,8 @@ export type DateHeaderProps<DateType> = {
|
|||
onMonthClick: () => void;
|
||||
};
|
||||
|
||||
function DateHeader<DateType>(props: DateHeaderProps<DateType>) {
|
||||
function DateHeader<DateType>(_props: DateHeaderProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const {
|
||||
prefixCls,
|
||||
generateConfig,
|
||||
|
@ -35,8 +36,8 @@ function DateHeader<DateType>(props: DateHeaderProps<DateType>) {
|
|||
onMonthClick,
|
||||
} = props;
|
||||
|
||||
const { hideHeader } = useInjectPanel()
|
||||
if (hideHeader) {
|
||||
const { hideHeader } = useInjectPanel();
|
||||
if (hideHeader.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -100,6 +101,6 @@ function DateHeader<DateType>(props: DateHeaderProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
DateHeader.displayName = 'DateHeader'
|
||||
DateHeader.displayName = 'DateHeader';
|
||||
DateHeader.inheritAttrs = false;
|
||||
export default DateHeader;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import type { DateBodyPassProps, DateRender } from './DateBody';
|
||||
import DateBody from './DateBody';
|
||||
import DateHeader from './DateHeader';
|
||||
|
@ -8,6 +7,7 @@ import type { KeyboardConfig } from '../../utils/uiUtil';
|
|||
import { createKeyDownHandler } from '../../utils/uiUtil';
|
||||
import classNames from '../../../_util/classNames';
|
||||
import { ref } from '@vue/reactivity';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
const DATE_ROW_COUNT = 6;
|
||||
|
||||
|
@ -18,14 +18,17 @@ export type DatePanelProps<DateType> = {
|
|||
// Used for week panel
|
||||
panelName?: string;
|
||||
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 {
|
||||
prefixCls,
|
||||
panelName = 'date',
|
||||
keyboardConfig,
|
||||
active,
|
||||
operationRef,
|
||||
generateConfig,
|
||||
value,
|
||||
viewDate,
|
||||
|
@ -34,10 +37,9 @@ function DatePanel<DateType>(props: DatePanelProps<DateType>) {
|
|||
onSelect,
|
||||
} = props;
|
||||
const panelPrefixCls = `${prefixCls}-${panelName}-panel`;
|
||||
const operationRef = ref()
|
||||
// ======================= Keyboard =======================
|
||||
operationRef.value = {
|
||||
onKeyDown: event =>
|
||||
onKeyDown: (event: KeyboardEvent) =>
|
||||
createKeyDownHandler(event, {
|
||||
onLeftRight: diff => {
|
||||
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;
|
||||
|
||||
export default DatePanel;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import type { DatePanelProps } from '../DatePanel';
|
||||
import DatePanel from '../DatePanel';
|
||||
import type { SharedTimeProps } from '../TimePanel';
|
||||
|
@ -9,20 +8,19 @@ import type { PanelRefProps, DisabledTime } from '../../interface';
|
|||
import KeyCode from '../../../_util/KeyCode';
|
||||
import classNames from '../../../_util/classNames';
|
||||
import { ref } from '@vue/reactivity';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type DatetimePanelProps<DateType> = {
|
||||
disabledTime?: DisabledTime<DateType>;
|
||||
showTime?: boolean | SharedTimeProps<DateType>;
|
||||
defaultValue?: DateType;
|
||||
} & Omit<
|
||||
DatePanelProps<DateType>,
|
||||
'disabledHours' | 'disabledMinutes' | 'disabledSeconds'
|
||||
>;
|
||||
} & Omit<DatePanelProps<DateType>, 'disabledHours' | 'disabledMinutes' | 'disabledSeconds'>;
|
||||
|
||||
const ACTIVE_PANEL = tuple('date', 'time');
|
||||
type ActivePanelType = typeof ACTIVE_PANEL[number];
|
||||
|
||||
function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
||||
function DatetimePanel<DateType>(_props: DatetimePanelProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const {
|
||||
prefixCls,
|
||||
operationRef,
|
||||
|
@ -34,9 +32,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
|||
onSelect,
|
||||
} = props;
|
||||
const panelPrefixCls = `${prefixCls}-datetime-panel`;
|
||||
const activePanel = ref<ActivePanelType | null>(
|
||||
null,
|
||||
);
|
||||
const activePanel = ref<ActivePanelType | null>(null);
|
||||
|
||||
const dateOperationRef = ref<PanelRefProps>({});
|
||||
const timeOperationRef = ref<PanelRefProps>({});
|
||||
|
@ -57,12 +53,12 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
|||
activePanel.value = null;
|
||||
};
|
||||
|
||||
operationRef.current = {
|
||||
onKeyDown: event => {
|
||||
operationRef.value = {
|
||||
onKeyDown: (event: KeyboardEvent) => {
|
||||
// Switch active panel
|
||||
if (event.which === KeyCode.TAB) {
|
||||
const nextActivePanel = getNextActive(event.shiftKey ? -1 : 1);
|
||||
activePanel.value = nextActivePanel
|
||||
activePanel.value = nextActivePanel;
|
||||
|
||||
if (nextActivePanel) {
|
||||
event.preventDefault();
|
||||
|
@ -73,8 +69,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
|||
|
||||
// Operate on current active panel
|
||||
if (activePanel.value) {
|
||||
const ref =
|
||||
activePanel.value === 'date' ? dateOperationRef : timeOperationRef;
|
||||
const ref = activePanel.value === 'date' ? dateOperationRef : timeOperationRef;
|
||||
|
||||
if (ref.value && ref.value.onKeyDown) {
|
||||
ref.value.onKeyDown(event);
|
||||
|
@ -84,12 +79,8 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
|||
}
|
||||
|
||||
// Switch first active panel if operate without panel
|
||||
if (
|
||||
[KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN].includes(
|
||||
event.which,
|
||||
)
|
||||
) {
|
||||
activePanel.value = 'date'
|
||||
if ([KeyCode.LEFT, KeyCode.RIGHT, KeyCode.UP, KeyCode.DOWN].includes(event.which)) {
|
||||
activePanel.value = 'date';
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -118,18 +109,9 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
|||
generateConfig.getSecond(timeProps.defaultValue),
|
||||
);
|
||||
} else if (source === 'time' && !value && defaultValue) {
|
||||
selectedDate = generateConfig.setYear(
|
||||
selectedDate,
|
||||
generateConfig.getYear(defaultValue),
|
||||
);
|
||||
selectedDate = generateConfig.setMonth(
|
||||
selectedDate,
|
||||
generateConfig.getMonth(defaultValue),
|
||||
);
|
||||
selectedDate = generateConfig.setDate(
|
||||
selectedDate,
|
||||
generateConfig.getDate(defaultValue),
|
||||
);
|
||||
selectedDate = generateConfig.setYear(selectedDate, generateConfig.getYear(defaultValue));
|
||||
selectedDate = generateConfig.setMonth(selectedDate, generateConfig.getMonth(defaultValue));
|
||||
selectedDate = generateConfig.setDate(selectedDate, generateConfig.getDate(defaultValue));
|
||||
}
|
||||
|
||||
if (onSelect) {
|
||||
|
@ -155,9 +137,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
|||
setTime(
|
||||
generateConfig,
|
||||
date,
|
||||
showTime && typeof showTime === 'object'
|
||||
? showTime.defaultValue
|
||||
: null,
|
||||
showTime && typeof showTime === 'object' ? showTime.defaultValue : null,
|
||||
),
|
||||
'date',
|
||||
);
|
||||
|
@ -179,8 +159,7 @@ function DatetimePanel<DateType>(props: DatetimePanelProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
DatetimePanel.displayName ='DatetimePanel'
|
||||
DatetimePanel.displayName = 'DatetimePanel';
|
||||
DatetimePanel.inheritAttrs = false;
|
||||
|
||||
export default DatetimePanel;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { DECADE_DISTANCE_COUNT, DECADE_UNIT_DIFF } from '.';
|
||||
import PanelBody from '../PanelBody';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export const DECADE_COL_COUNT = 3;
|
||||
const DECADE_ROW_COUNT = 4;
|
||||
|
@ -14,7 +14,8 @@ export type YearBodyProps<DateType> = {
|
|||
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 { prefixCls, viewDate, generateConfig } = props;
|
||||
|
||||
|
@ -61,8 +62,7 @@ function DecadeBody<DateType>(props: YearBodyProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
DecadeBody.displayName ='DecadeBody'
|
||||
DecadeBody.displayName = 'DecadeBody';
|
||||
DecadeBody.inheritAttrs = false;
|
||||
|
||||
export default DecadeBody;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
import Header from '../Header';
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { DECADE_DISTANCE_COUNT } from '.';
|
||||
import { useInjectPanel } from '../../PanelContext';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type YearHeaderProps<DateType> = {
|
||||
prefixCls: string;
|
||||
|
@ -13,15 +13,10 @@ export type YearHeaderProps<DateType> = {
|
|||
onNextDecades: () => void;
|
||||
};
|
||||
|
||||
function DecadeHeader<DateType>(props: YearHeaderProps<DateType>) {
|
||||
const {
|
||||
prefixCls,
|
||||
generateConfig,
|
||||
viewDate,
|
||||
onPrevDecades,
|
||||
onNextDecades,
|
||||
} = props;
|
||||
const { hideHeader } =useInjectPanel()
|
||||
function DecadeHeader<DateType>(_props: YearHeaderProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const { prefixCls, generateConfig, viewDate, onPrevDecades, onNextDecades } = props;
|
||||
const { hideHeader } = useInjectPanel();
|
||||
if (hideHeader) {
|
||||
return null;
|
||||
}
|
||||
|
@ -29,8 +24,7 @@ function DecadeHeader<DateType>(props: YearHeaderProps<DateType>) {
|
|||
const headerPrefixCls = `${prefixCls}-header`;
|
||||
|
||||
const yearNumber = generateConfig.getYear(viewDate);
|
||||
const startYear =
|
||||
Math.floor(yearNumber / DECADE_DISTANCE_COUNT) * DECADE_DISTANCE_COUNT;
|
||||
const startYear = Math.floor(yearNumber / DECADE_DISTANCE_COUNT) * DECADE_DISTANCE_COUNT;
|
||||
const endYear = startYear + DECADE_DISTANCE_COUNT - 1;
|
||||
|
||||
return (
|
||||
|
@ -45,7 +39,7 @@ function DecadeHeader<DateType>(props: YearHeaderProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
DecadeHeader.displayName ='DecadeHeader'
|
||||
DecadeHeader.displayName = 'DecadeHeader';
|
||||
DecadeHeader.inheritAttrs = false;
|
||||
|
||||
export default DecadeHeader;
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
|
||||
import DecadeHeader from './DecadeHeader';
|
||||
import DecadeBody, { DECADE_COL_COUNT } from './DecadeBody';
|
||||
import type { PanelSharedProps } from '../../interface';
|
||||
import { createKeyDownHandler } from '../../utils/uiUtil';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type DecadePanelProps<DateType> = PanelSharedProps<DateType>;
|
||||
|
||||
export const 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 {
|
||||
prefixCls,
|
||||
onViewDateChange,
|
||||
|
@ -23,27 +24,18 @@ function DecadePanel<DateType>(props: DecadePanelProps<DateType>) {
|
|||
const panelPrefixCls = `${prefixCls}-decade-panel`;
|
||||
|
||||
// ======================= Keyboard =======================
|
||||
operationRef.current = {
|
||||
onKeyDown: event =>
|
||||
operationRef.value = {
|
||||
onKeyDown: (event: KeyboardEvent) =>
|
||||
createKeyDownHandler(event, {
|
||||
onLeftRight: diff => {
|
||||
onSelect(
|
||||
generateConfig.addYear(viewDate, diff * DECADE_UNIT_DIFF),
|
||||
'key',
|
||||
);
|
||||
onSelect(generateConfig.addYear(viewDate, diff * DECADE_UNIT_DIFF), 'key');
|
||||
},
|
||||
onCtrlLeftRight: diff => {
|
||||
onSelect(
|
||||
generateConfig.addYear(viewDate, diff * DECADE_DISTANCE_COUNT),
|
||||
'key',
|
||||
);
|
||||
onSelect(generateConfig.addYear(viewDate, diff * DECADE_DISTANCE_COUNT), 'key');
|
||||
},
|
||||
onUpDown: diff => {
|
||||
onSelect(
|
||||
generateConfig.addYear(
|
||||
viewDate,
|
||||
diff * DECADE_UNIT_DIFF * DECADE_COL_COUNT,
|
||||
),
|
||||
generateConfig.addYear(viewDate, diff * DECADE_UNIT_DIFF * DECADE_COL_COUNT),
|
||||
'key',
|
||||
);
|
||||
},
|
||||
|
@ -55,10 +47,7 @@ function DecadePanel<DateType>(props: DecadePanelProps<DateType>) {
|
|||
|
||||
// ==================== View Operation ====================
|
||||
const onDecadesChange = (diff: number) => {
|
||||
const newDate = generateConfig.addYear(
|
||||
viewDate,
|
||||
diff * DECADE_DISTANCE_COUNT,
|
||||
);
|
||||
const newDate = generateConfig.addYear(viewDate, diff * DECADE_DISTANCE_COUNT);
|
||||
onViewDateChange(newDate);
|
||||
onPanelChange(null, newDate);
|
||||
};
|
||||
|
@ -80,17 +69,12 @@ function DecadePanel<DateType>(props: DecadePanelProps<DateType>) {
|
|||
onDecadesChange(1);
|
||||
}}
|
||||
/>
|
||||
<DecadeBody
|
||||
{...props}
|
||||
prefixCls={prefixCls}
|
||||
onSelect={onInternalSelect}
|
||||
/>
|
||||
<DecadeBody {...props} prefixCls={prefixCls} onSelect={onInternalSelect} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
DecadePanel.displayName ='DecadePanel'
|
||||
DecadePanel.displayName = 'DecadePanel';
|
||||
DecadePanel.inheritAttrs = false;
|
||||
|
||||
export default DecadePanel;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { CSSProperties } from '@vue/runtime-dom';
|
||||
import { CSSProperties } from 'vue';
|
||||
import { VueNode } from '../../_util/type';
|
||||
import useMergeProps from '../hooks/useMergeProps';
|
||||
import { useInjectPanel } from '../PanelContext';
|
||||
|
||||
const HIDDEN_STYLE: CSSProperties = {
|
||||
|
@ -27,8 +28,9 @@ export type HeaderProps = {
|
|||
children?: VueNode;
|
||||
};
|
||||
|
||||
function Header(
|
||||
{
|
||||
function Header(_props: HeaderProps, { slots }) {
|
||||
const props = useMergeProps(_props);
|
||||
const {
|
||||
prefixCls,
|
||||
prevIcon = '\u2039',
|
||||
nextIcon = '\u203A',
|
||||
|
@ -38,9 +40,7 @@ function Header(
|
|||
onSuperNext,
|
||||
onPrev,
|
||||
onNext,
|
||||
}: HeaderProps,
|
||||
{ slots },
|
||||
) {
|
||||
} = props;
|
||||
const { hideNextBtn, hidePrevBtn } = useInjectPanel();
|
||||
|
||||
return (
|
||||
|
@ -51,7 +51,7 @@ function Header(
|
|||
onClick={onSuperPrev}
|
||||
tabindex={-1}
|
||||
class={`${prefixCls}-super-prev-btn`}
|
||||
style={hidePrevBtn ? HIDDEN_STYLE : {}}
|
||||
style={hidePrevBtn.value ? HIDDEN_STYLE : {}}
|
||||
>
|
||||
{superPrevIcon}
|
||||
</button>
|
||||
|
@ -62,7 +62,7 @@ function Header(
|
|||
onClick={onPrev}
|
||||
tabindex={-1}
|
||||
class={`${prefixCls}-prev-btn`}
|
||||
style={hidePrevBtn ? HIDDEN_STYLE : {}}
|
||||
style={hidePrevBtn.value ? HIDDEN_STYLE : {}}
|
||||
>
|
||||
{prevIcon}
|
||||
</button>
|
||||
|
@ -74,7 +74,7 @@ function Header(
|
|||
onClick={onNext}
|
||||
tabindex={-1}
|
||||
class={`${prefixCls}-next-btn`}
|
||||
style={hideNextBtn ? HIDDEN_STYLE : {}}
|
||||
style={hideNextBtn.value ? HIDDEN_STYLE : {}}
|
||||
>
|
||||
{nextIcon}
|
||||
</button>
|
||||
|
@ -85,7 +85,7 @@ function Header(
|
|||
onClick={onSuperNext}
|
||||
tabindex={-1}
|
||||
class={`${prefixCls}-super-next-btn`}
|
||||
style={hideNextBtn ? HIDDEN_STYLE : {}}
|
||||
style={hideNextBtn.value ? HIDDEN_STYLE : {}}
|
||||
>
|
||||
{superNextIcon}
|
||||
</button>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import type { Locale } from '../../interface';
|
||||
import { formatValue, isSameMonth } from '../../utils/dateUtil';
|
||||
|
@ -6,6 +5,7 @@ import { useInjectRange } from '../../RangeContext';
|
|||
import useCellClassName from '../../hooks/useCellClassName';
|
||||
import PanelBody from '../PanelBody';
|
||||
import { VueNode } from '../../../_util/type';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export const MONTH_COL_COUNT = 3;
|
||||
const MONTH_ROW_COUNT = 4;
|
||||
|
@ -23,10 +23,11 @@ export type MonthBodyProps<DateType> = {
|
|||
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 { rangedValue, hoverRangedValue } = useInjectRange()
|
||||
const { rangedValue, hoverRangedValue } = useInjectRange();
|
||||
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
|
||||
|
@ -34,8 +35,8 @@ function MonthBody<DateType>(props: MonthBodyProps<DateType>) {
|
|||
cellPrefixCls,
|
||||
value,
|
||||
generateConfig,
|
||||
rangedValue,
|
||||
hoverRangedValue,
|
||||
rangedValue: rangedValue.value,
|
||||
hoverRangedValue: hoverRangedValue.value,
|
||||
isSameCell: (current, target) => isSameMonth(generateConfig, current, target),
|
||||
isInView: () => true,
|
||||
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;
|
||||
|
||||
export default MonthBody;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
import Header from '../Header';
|
||||
import type { Locale } from '../../interface';
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { useInjectPanel } from '../../PanelContext';
|
||||
import { formatValue } from '../../utils/dateUtil';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type MonthHeaderProps<DateType> = {
|
||||
prefixCls: string;
|
||||
|
@ -16,18 +16,12 @@ export type MonthHeaderProps<DateType> = {
|
|||
onYearClick: () => void;
|
||||
};
|
||||
|
||||
function MonthHeader<DateType>(props: MonthHeaderProps<DateType>) {
|
||||
const {
|
||||
prefixCls,
|
||||
generateConfig,
|
||||
locale,
|
||||
viewDate,
|
||||
onNextYear,
|
||||
onPrevYear,
|
||||
onYearClick,
|
||||
} = props;
|
||||
const { hideHeader } = useInjectPanel()
|
||||
if (hideHeader) {
|
||||
function MonthHeader<DateType>(_props: MonthHeaderProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const { prefixCls, generateConfig, locale, viewDate, onNextYear, onPrevYear, onYearClick } =
|
||||
props;
|
||||
const { hideHeader } = useInjectPanel();
|
||||
if (hideHeader.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -51,8 +45,7 @@ function MonthHeader<DateType>(props: MonthHeaderProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
MonthHeader.displayName ='MonthHeader'
|
||||
MonthHeader.displayName = 'MonthHeader';
|
||||
MonthHeader.inheritAttrs = false;
|
||||
|
||||
export default MonthHeader;
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
|
||||
import MonthHeader from './MonthHeader';
|
||||
import type { MonthCellRender } from './MonthBody';
|
||||
import MonthBody, { MONTH_COL_COUNT } from './MonthBody';
|
||||
import type { PanelSharedProps } from '../../interface';
|
||||
import { createKeyDownHandler } from '../../utils/uiUtil';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type MonthPanelProps<DateType> = {
|
||||
monthCellContentRender?: MonthCellRender<DateType>;
|
||||
} & PanelSharedProps<DateType>;
|
||||
|
||||
function MonthPanel<DateType>(props: MonthPanelProps<DateType>) {
|
||||
function MonthPanel<DateType>(_props: MonthPanelProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const {
|
||||
prefixCls,
|
||||
operationRef,
|
||||
|
@ -24,8 +25,8 @@ function MonthPanel<DateType>(props: MonthPanelProps<DateType>) {
|
|||
const panelPrefixCls = `${prefixCls}-month-panel`;
|
||||
|
||||
// ======================= Keyboard =======================
|
||||
operationRef.current = {
|
||||
onKeyDown: event =>
|
||||
operationRef.value = {
|
||||
onKeyDown: (event: KeyboardEvent) =>
|
||||
createKeyDownHandler(event, {
|
||||
onLeftRight: diff => {
|
||||
onSelect(generateConfig.addMonth(value || viewDate, diff), 'key');
|
||||
|
@ -34,10 +35,7 @@ function MonthPanel<DateType>(props: MonthPanelProps<DateType>) {
|
|||
onSelect(generateConfig.addYear(value || viewDate, diff), 'key');
|
||||
},
|
||||
onUpDown: diff => {
|
||||
onSelect(
|
||||
generateConfig.addMonth(value || viewDate, diff * MONTH_COL_COUNT),
|
||||
'key',
|
||||
);
|
||||
onSelect(generateConfig.addMonth(value || viewDate, diff * MONTH_COL_COUNT), 'key');
|
||||
},
|
||||
onEnter: () => {
|
||||
onPanelChange('date', value || viewDate);
|
||||
|
@ -79,8 +77,7 @@ function MonthPanel<DateType>(props: MonthPanelProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
MonthPanel.displayName ='MonthPanel'
|
||||
MonthPanel.displayName = 'MonthPanel';
|
||||
MonthPanel.inheritAttrs = false;
|
||||
|
||||
export default MonthPanel;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { useInjectPanel } from '../PanelContext';
|
||||
import type { GenerateConfig } from '../generate';
|
||||
import { getLastDay } from '../utils/timeUtil';
|
||||
|
@ -6,6 +5,7 @@ import type { PanelMode } from '../interface';
|
|||
import { getCellDateDisabled } from '../utils/dateUtil';
|
||||
import { VueNode } from '../../_util/type';
|
||||
import classNames from '../../_util/classNames';
|
||||
import useMergeProps from '../hooks/useMergeProps';
|
||||
|
||||
export type PanelBodyProps<DateType> = {
|
||||
prefixCls: string;
|
||||
|
@ -30,25 +30,26 @@ export type PanelBodyProps<DateType> = {
|
|||
rowClassName?: (date: DateType) => string;
|
||||
};
|
||||
|
||||
function PanelBody<DateType>({
|
||||
prefixCls,
|
||||
disabledDate,
|
||||
onSelect,
|
||||
picker,
|
||||
rowNum,
|
||||
colNum,
|
||||
prefixColumn,
|
||||
rowClassName,
|
||||
baseDate,
|
||||
getCellClassName,
|
||||
getCellText,
|
||||
getCellNode,
|
||||
getCellDate,
|
||||
generateConfig,
|
||||
titleCell,
|
||||
headerCells,
|
||||
}: PanelBodyProps<DateType>) {
|
||||
const { onDateMouseEnter, onDateMouseLeave, mode } = useInjectPanel()
|
||||
function PanelBody<DateType>(_props: PanelBodyProps<DateType>) {
|
||||
const {
|
||||
prefixCls,
|
||||
disabledDate,
|
||||
onSelect,
|
||||
picker,
|
||||
rowNum,
|
||||
colNum,
|
||||
prefixColumn,
|
||||
rowClassName,
|
||||
baseDate,
|
||||
getCellClassName,
|
||||
getCellText,
|
||||
getCellNode,
|
||||
getCellDate,
|
||||
generateConfig,
|
||||
titleCell,
|
||||
headerCells,
|
||||
} = useMergeProps(_props);
|
||||
const { onDateMouseEnter, onDateMouseLeave, mode } = useInjectPanel();
|
||||
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
|
||||
|
@ -64,7 +65,7 @@ function PanelBody<DateType>({
|
|||
const currentDate = getCellDate(baseDate, offset);
|
||||
const disabled = getCellDateDisabled({
|
||||
cellDate: currentDate,
|
||||
mode,
|
||||
mode: mode.value,
|
||||
disabledDate,
|
||||
generateConfig,
|
||||
});
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import type { Locale } from '../../interface';
|
||||
import { formatValue, isSameQuarter } from '../../utils/dateUtil';
|
||||
import RangeContext, { useInjectRange } from '../../RangeContext';
|
||||
import useCellClassName from '../../hooks/useCellClassName';
|
||||
import PanelBody from '../PanelBody';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export const QUARTER_COL_COUNT = 4;
|
||||
const QUARTER_ROW_COUNT = 1;
|
||||
|
@ -19,10 +19,11 @@ export type QuarterBodyProps<DateType> = {
|
|||
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 { rangedValue, hoverRangedValue } = useInjectRange()
|
||||
const { rangedValue, hoverRangedValue } = useInjectRange();
|
||||
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
|
||||
|
@ -30,8 +31,8 @@ function QuarterBody<DateType>(props: QuarterBodyProps<DateType>) {
|
|||
cellPrefixCls,
|
||||
value,
|
||||
generateConfig,
|
||||
rangedValue,
|
||||
hoverRangedValue,
|
||||
rangedValue: rangedValue.value,
|
||||
hoverRangedValue: hoverRangedValue.value,
|
||||
isSameCell: (current, target) => isSameQuarter(generateConfig, current, target),
|
||||
isInView: () => true,
|
||||
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;
|
||||
export default QuarterBody;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
import Header from '../Header';
|
||||
import type { Locale } from '../../interface';
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { useInjectPanel } from '../../PanelContext';
|
||||
import { formatValue } from '../../utils/dateUtil';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type QuarterHeaderProps<DateType> = {
|
||||
prefixCls: string;
|
||||
|
@ -16,18 +16,12 @@ export type QuarterHeaderProps<DateType> = {
|
|||
onYearClick: () => void;
|
||||
};
|
||||
|
||||
function QuarterHeader<DateType>(props: QuarterHeaderProps<DateType>) {
|
||||
const {
|
||||
prefixCls,
|
||||
generateConfig,
|
||||
locale,
|
||||
viewDate,
|
||||
onNextYear,
|
||||
onPrevYear,
|
||||
onYearClick,
|
||||
} = props;
|
||||
const { hideHeader } =useInjectPanel()
|
||||
if (hideHeader) {
|
||||
function QuarterHeader<DateType>(_props: QuarterHeaderProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const { prefixCls, generateConfig, locale, viewDate, onNextYear, onPrevYear, onYearClick } =
|
||||
props;
|
||||
const { hideHeader } = useInjectPanel();
|
||||
if (hideHeader.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -50,8 +44,7 @@ function QuarterHeader<DateType>(props: QuarterHeaderProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
QuarterHeader.displayName ='QuarterHeader'
|
||||
QuarterHeader.displayName = 'QuarterHeader';
|
||||
QuarterHeader.inheritAttrs = false;
|
||||
|
||||
export default QuarterHeader;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
|
||||
import QuarterHeader from './QuarterHeader';
|
||||
import QuarterBody from './QuarterBody';
|
||||
import type { PanelSharedProps } from '../../interface';
|
||||
import { createKeyDownHandler } from '../../utils/uiUtil';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type QuarterPanelProps<DateType> = {} & PanelSharedProps<DateType>;
|
||||
|
||||
function QuarterPanel<DateType>(props: QuarterPanelProps<DateType>) {
|
||||
function QuarterPanel<DateType>(_props: QuarterPanelProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const {
|
||||
prefixCls,
|
||||
operationRef,
|
||||
|
@ -21,8 +22,8 @@ function QuarterPanel<DateType>(props: QuarterPanelProps<DateType>) {
|
|||
const panelPrefixCls = `${prefixCls}-quarter-panel`;
|
||||
|
||||
// ======================= Keyboard =======================
|
||||
operationRef.current = {
|
||||
onKeyDown: event =>
|
||||
operationRef.value = {
|
||||
onKeyDown: (event: KeyboardEvent) =>
|
||||
createKeyDownHandler(event, {
|
||||
onLeftRight: diff => {
|
||||
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;
|
||||
|
||||
export default QuarterPanel;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import type { Locale, OnSelect } from '../../interface';
|
||||
import type { Unit } from './TimeUnitColumn';
|
||||
|
@ -8,17 +7,8 @@ import type { SharedTimeProps } from '.';
|
|||
import { setTime as utilSetTime } from '../../utils/timeUtil';
|
||||
import { cloneElement } from '../../../_util/vnode';
|
||||
import { VueNode } from '../../../_util/type';
|
||||
import { ref, Ref } from '@vue/reactivity';
|
||||
import { computed, defineComponent, watchEffect } from '@vue/runtime-core';
|
||||
|
||||
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;
|
||||
}
|
||||
import type { Ref } from 'vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
|
||||
function generateUnits(
|
||||
start: number,
|
||||
|
@ -51,7 +41,6 @@ export type TimeBodyProps<DateType> = {
|
|||
operationRef: Ref<BodyOperationRef | undefined>;
|
||||
} & SharedTimeProps<DateType>;
|
||||
|
||||
|
||||
const TimeBody = defineComponent({
|
||||
name: 'TimeBody',
|
||||
inheritAttrs: false,
|
||||
|
@ -75,26 +64,26 @@ const TimeBody = defineComponent({
|
|||
'onSelect',
|
||||
],
|
||||
setup(props) {
|
||||
const originHour = computed(() => props.value ? props.generateConfig.getHour(props.value) : -1);
|
||||
const isPM = computed(()=> {
|
||||
const originHour = computed(() =>
|
||||
props.value ? props.generateConfig.getHour(props.value) : -1,
|
||||
);
|
||||
const isPM = computed(() => {
|
||||
if (props.use12Hours) {
|
||||
return originHour.value >= 12; // -1 means should display AM
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
let hour = computed(()=> {
|
||||
// Should additional logic to handle 12 hours
|
||||
if (props.use12Hours) {
|
||||
return originHour.value % 12
|
||||
} else {
|
||||
return originHour.value
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const minute = computed(()=> props.value ? props.generateConfig.getMinute(props.value) : -1);
|
||||
const second = computed(()=> props.value ? props.generateConfig.getSecond(props.value) : -1);
|
||||
|
||||
|
||||
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));
|
||||
const second = computed(() => (props.value ? props.generateConfig.getSecond(props.value) : -1));
|
||||
|
||||
const setTime = (
|
||||
isNewPM: boolean | undefined,
|
||||
|
@ -120,7 +109,9 @@ const TimeBody = defineComponent({
|
|||
};
|
||||
|
||||
// ========================= 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);
|
||||
|
||||
|
@ -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(
|
||||
0,
|
||||
59,
|
||||
props.secondStep ?? 1,
|
||||
props.disabledSeconds && props.disabledSeconds(originHour.value, minute),
|
||||
));
|
||||
const seconds = computed(() =>
|
||||
generateUnits(
|
||||
0,
|
||||
59,
|
||||
props.secondStep ?? 1,
|
||||
props.disabledSeconds && props.disabledSeconds(originHour.value, minute),
|
||||
),
|
||||
);
|
||||
|
||||
return ()=> {
|
||||
return () => {
|
||||
const {
|
||||
prefixCls,
|
||||
operationRef,
|
||||
|
@ -188,7 +188,7 @@ const TimeBody = defineComponent({
|
|||
|
||||
// ====================== Operations ======================
|
||||
operationRef.value = {
|
||||
onUpDown: diff => {
|
||||
onUpDown: (diff: number) => {
|
||||
const column = columns[activeColumnIndex];
|
||||
if (column) {
|
||||
const valueIndex = column.units.findIndex(unit => unit.value === column.value);
|
||||
|
@ -237,14 +237,26 @@ const TimeBody = defineComponent({
|
|||
});
|
||||
|
||||
// Minute
|
||||
addColumnNode(showMinute, <TimeUnitColumn key="minute" />, minute.value, minutes.value, num => {
|
||||
onSelect(setTime(isPM.value, hour.value, num, second.value), 'mouse');
|
||||
});
|
||||
addColumnNode(
|
||||
showMinute,
|
||||
<TimeUnitColumn key="minute" />,
|
||||
minute.value,
|
||||
minutes.value,
|
||||
num => {
|
||||
onSelect(setTime(isPM.value, hour.value, num, second.value), 'mouse');
|
||||
},
|
||||
);
|
||||
|
||||
// Second
|
||||
addColumnNode(showSecond, <TimeUnitColumn key="second" />, second.value, seconds.value, num => {
|
||||
onSelect(setTime(isPM.value, hour.value, minute.value, num), 'mouse');
|
||||
});
|
||||
addColumnNode(
|
||||
showSecond,
|
||||
<TimeUnitColumn key="second" />,
|
||||
second.value,
|
||||
seconds.value,
|
||||
num => {
|
||||
onSelect(setTime(isPM.value, hour.value, minute.value, num), 'mouse');
|
||||
},
|
||||
);
|
||||
|
||||
// 12 Hours
|
||||
let PMIndex = -1;
|
||||
|
@ -266,9 +278,8 @@ const TimeBody = defineComponent({
|
|||
);
|
||||
|
||||
return <div class={contentPrefixCls}>{columns.map(({ node }) => node)}</div>;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default TimeBody;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
import Header from '../Header';
|
||||
import type { Locale } from '../../interface';
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { useInjectPanel } from '../../PanelContext';
|
||||
import { formatValue } from '../../utils/dateUtil';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type TimeHeaderProps<DateType> = {
|
||||
prefixCls: string;
|
||||
|
@ -13,9 +13,10 @@ export type TimeHeaderProps<DateType> = {
|
|||
format: string;
|
||||
};
|
||||
|
||||
function TimeHeader<DateType>(props: TimeHeaderProps<DateType>) {
|
||||
const { hideHeader } = useInjectPanel()
|
||||
if (hideHeader) {
|
||||
function TimeHeader<DateType>(_props: TimeHeaderProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const { hideHeader } = useInjectPanel();
|
||||
if (hideHeader.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -35,8 +36,7 @@ function TimeHeader<DateType>(props: TimeHeaderProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
TimeHeader.displayName ='TimeHeader'
|
||||
TimeHeader.displayName = 'TimeHeader';
|
||||
TimeHeader.inheritAttrs = false;
|
||||
|
||||
export default TimeHeader;
|
||||
|
|
|
@ -32,7 +32,7 @@ function TimeUnitColumn(props: TimeUnitColumnProps) {
|
|||
() => props.value,
|
||||
() => {
|
||||
const li = liRefs.value.get(value!);
|
||||
if (li && open !== false) {
|
||||
if (li && open.value !== false) {
|
||||
scrollTo(ulRef.value!, li.offsetTop, 120);
|
||||
}
|
||||
},
|
||||
|
@ -43,7 +43,7 @@ function TimeUnitColumn(props: TimeUnitColumnProps) {
|
|||
|
||||
watch(open, () => {
|
||||
scrollRef.value?.();
|
||||
if (open) {
|
||||
if (open.value) {
|
||||
const li = liRefs.value.get(value!);
|
||||
if (li) {
|
||||
scrollRef.value = waitElementReady(li, () => {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import TimeHeader from './TimeHeader';
|
||||
import type { BodyOperationRef } from './TimeBody';
|
||||
import TimeBody from './TimeBody';
|
||||
|
@ -6,6 +5,7 @@ import type { PanelSharedProps, DisabledTimes } from '../../interface';
|
|||
import { createKeyDownHandler } from '../../utils/uiUtil';
|
||||
import classNames from '../../../_util/classNames';
|
||||
import { ref } from '@vue/reactivity';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type SharedTimeProps<DateType> = {
|
||||
format?: string;
|
||||
|
@ -24,12 +24,14 @@ export type SharedTimeProps<DateType> = {
|
|||
export type TimePanelProps<DateType> = {
|
||||
format?: string;
|
||||
active?: boolean;
|
||||
} & PanelSharedProps<DateType> & SharedTimeProps<DateType>;
|
||||
} & PanelSharedProps<DateType> &
|
||||
SharedTimeProps<DateType>;
|
||||
|
||||
const countBoolean = (boolList: (boolean | undefined)[]) =>
|
||||
boolList.filter(bool => bool !== false).length;
|
||||
|
||||
function TimePanel<DateType>(props: TimePanelProps<DateType>) {
|
||||
function TimePanel<DateType>(_props: TimePanelProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const {
|
||||
generateConfig,
|
||||
format = 'HH:mm:ss',
|
||||
|
@ -50,27 +52,27 @@ function TimePanel<DateType>(props: TimePanelProps<DateType>) {
|
|||
const activeColumnIndex = ref(-1);
|
||||
const columnsCount = countBoolean([showHour, showMinute, showSecond, use12Hours]);
|
||||
|
||||
operationRef.current = {
|
||||
onKeyDown: event =>
|
||||
operationRef.value = {
|
||||
onKeyDown: (event: KeyboardEvent) =>
|
||||
createKeyDownHandler(event, {
|
||||
onLeftRight: diff => {
|
||||
activeColumnIndex.value = (activeColumnIndex.value + diff + columnsCount) % columnsCount;
|
||||
},
|
||||
onUpDown: diff => {
|
||||
if (activeColumnIndex.value === -1) {
|
||||
activeColumnIndex.value = 0
|
||||
activeColumnIndex.value = 0;
|
||||
} else if (bodyOperationRef.value) {
|
||||
bodyOperationRef.value.onUpDown(diff);
|
||||
}
|
||||
},
|
||||
onEnter: () => {
|
||||
onSelect(value || generateConfig.getNow(), 'key');
|
||||
activeColumnIndex.value = -1
|
||||
activeColumnIndex.value = -1;
|
||||
},
|
||||
}),
|
||||
|
||||
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;
|
||||
|
||||
export default TimePanel;
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
|
||||
import DatePanel from '../DatePanel';
|
||||
import type { PanelSharedProps } from '../../interface';
|
||||
import { isSameWeek } from '../../utils/dateUtil';
|
||||
import classNames from '../../../_util/classNames';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
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;
|
||||
|
||||
// Render additional column
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
const prefixColumn = (date: DateType) => (
|
||||
<td
|
||||
key="week"
|
||||
class={classNames(cellPrefixCls, `${cellPrefixCls}-week`)}
|
||||
>
|
||||
<td key="week" class={classNames(cellPrefixCls, `${cellPrefixCls}-week`)}>
|
||||
{generateConfig.locale.getWeek(locale.locale, date)}
|
||||
</td>
|
||||
);
|
||||
|
@ -24,12 +22,7 @@ function WeekPanel<DateType>(props: WeekPanelProps<DateType>) {
|
|||
const rowPrefixCls = `${prefixCls}-week-panel-row`;
|
||||
const rowClassName = (date: DateType) =>
|
||||
classNames(rowPrefixCls, {
|
||||
[`${rowPrefixCls}-selected`]: isSameWeek(
|
||||
generateConfig,
|
||||
locale.locale,
|
||||
value,
|
||||
date,
|
||||
),
|
||||
[`${rowPrefixCls}-selected`]: isSameWeek(generateConfig, locale.locale, value, date),
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -45,7 +38,6 @@ function WeekPanel<DateType>(props: WeekPanelProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
WeekPanel.displayName = 'WeekPanel';
|
||||
WeekPanel.inheritAttrs = false;
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { YEAR_DECADE_COUNT } from '.';
|
||||
import type { Locale, NullableDateType } from '../../interface';
|
||||
import useCellClassName from '../../hooks/useCellClassName';
|
||||
import { formatValue, isSameYear } from '../../utils/dateUtil';
|
||||
import RangeContext, { useInjectRange } from '../../RangeContext';
|
||||
import { useInjectRange } from '../../RangeContext';
|
||||
import PanelBody from '../PanelBody';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export const YEAR_COL_COUNT = 3;
|
||||
const YEAR_ROW_COUNT = 4;
|
||||
|
@ -20,9 +20,10 @@ export type YearBodyProps<DateType> = {
|
|||
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 { rangedValue, hoverRangedValue } = useInjectRange()
|
||||
const { rangedValue, hoverRangedValue } = useInjectRange();
|
||||
|
||||
const yearPrefixCls = `${prefixCls}-cell`;
|
||||
|
||||
|
@ -44,8 +45,8 @@ function YearBody<DateType>(props: YearBodyProps<DateType>) {
|
|||
cellPrefixCls: yearPrefixCls,
|
||||
value,
|
||||
generateConfig,
|
||||
rangedValue,
|
||||
hoverRangedValue,
|
||||
rangedValue: rangedValue.value,
|
||||
hoverRangedValue: hoverRangedValue.value,
|
||||
isSameCell: (current, target) => isSameYear(generateConfig, current, target),
|
||||
isInView,
|
||||
offsetCell: (date, offset) => generateConfig.addYear(date, offset),
|
||||
|
@ -71,8 +72,6 @@ function YearBody<DateType>(props: YearBodyProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
YearBody.displayName = 'YearBody';
|
||||
YearBody.inheritAttrs = false;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
import Header from '../Header';
|
||||
import type { GenerateConfig } from '../../generate';
|
||||
import { YEAR_DECADE_COUNT } from '.';
|
||||
import { useInjectPanel } from '../../PanelContext';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type YearHeaderProps<DateType> = {
|
||||
prefixCls: string;
|
||||
|
@ -15,10 +15,11 @@ export type YearHeaderProps<DateType> = {
|
|||
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 { hideHeader } = useInjectPanel()
|
||||
if (hideHeader) {
|
||||
const { hideHeader } = useInjectPanel();
|
||||
if (hideHeader.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -42,8 +43,6 @@ function YearHeader<DateType>(props: YearHeaderProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
YearHeader.displayName = 'YearHeader';
|
||||
YearHeader.inheritAttrs = false;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
import YearHeader from './YearHeader';
|
||||
import YearBody, { YEAR_COL_COUNT } from './YearBody';
|
||||
import type { PanelSharedProps, PanelMode } from '../../interface';
|
||||
import { createKeyDownHandler } from '../../utils/uiUtil';
|
||||
import useMergeProps from '../../hooks/useMergeProps';
|
||||
|
||||
export type YearPanelProps<DateType> = {
|
||||
sourceMode: PanelMode;
|
||||
|
@ -10,7 +10,8 @@ export type YearPanelProps<DateType> = {
|
|||
|
||||
export const YEAR_DECADE_COUNT = 10;
|
||||
|
||||
function YearPanel<DateType>(props: YearPanelProps<DateType>) {
|
||||
function YearPanel<DateType>(_props: YearPanelProps<DateType>) {
|
||||
const props = useMergeProps(_props);
|
||||
const {
|
||||
prefixCls,
|
||||
operationRef,
|
||||
|
@ -26,29 +27,20 @@ function YearPanel<DateType>(props: YearPanelProps<DateType>) {
|
|||
const panelPrefixCls = `${prefixCls}-year-panel`;
|
||||
|
||||
// ======================= Keyboard =======================
|
||||
operationRef.current = {
|
||||
onKeyDown: event =>
|
||||
operationRef.value = {
|
||||
onKeyDown: (event: KeyboardEvent) =>
|
||||
createKeyDownHandler(event, {
|
||||
onLeftRight: diff => {
|
||||
onSelect(generateConfig.addYear(value || viewDate, diff), 'key');
|
||||
},
|
||||
onCtrlLeftRight: diff => {
|
||||
onSelect(
|
||||
generateConfig.addYear(value || viewDate, diff * YEAR_DECADE_COUNT),
|
||||
'key',
|
||||
);
|
||||
onSelect(generateConfig.addYear(value || viewDate, diff * YEAR_DECADE_COUNT), 'key');
|
||||
},
|
||||
onUpDown: diff => {
|
||||
onSelect(
|
||||
generateConfig.addYear(value || viewDate, diff * YEAR_COL_COUNT),
|
||||
'key',
|
||||
);
|
||||
onSelect(generateConfig.addYear(value || viewDate, diff * YEAR_COL_COUNT), 'key');
|
||||
},
|
||||
onEnter: () => {
|
||||
onPanelChange(
|
||||
sourceMode === 'date' ? 'date' : 'month',
|
||||
value || viewDate,
|
||||
);
|
||||
onPanelChange(sourceMode === 'date' ? 'date' : 'month', value || viewDate);
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
@ -87,8 +79,6 @@ function YearPanel<DateType>(props: YearPanelProps<DateType>) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
YearPanel.displayName = 'YearPanel';
|
||||
YearPanel.inheritAttrs = false;
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import type { PanelMode } from '../interface';
|
||||
|
||||
export default function getExtraFooter(
|
||||
|
@ -10,7 +9,5 @@ export default function getExtraFooter(
|
|||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class={`${prefixCls}-footer-extra`}>{renderExtraFooter(mode)}</div>
|
||||
);
|
||||
return <div class={`${prefixCls}-footer-extra`}>{renderExtraFooter(mode)}</div>;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
import { VueNode } from '../../_util/type';
|
||||
import type { Components, RangeList, Locale } from '../interface';
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -24,22 +24,12 @@ export function setDateTime<DateType>(
|
|||
}
|
||||
|
||||
let newDate = date;
|
||||
newDate = generateConfig.setHour(
|
||||
newDate,
|
||||
generateConfig.getHour(defaultDate),
|
||||
);
|
||||
newDate = generateConfig.setMinute(
|
||||
newDate,
|
||||
generateConfig.getMinute(defaultDate),
|
||||
);
|
||||
newDate = generateConfig.setSecond(
|
||||
newDate,
|
||||
generateConfig.getSecond(defaultDate),
|
||||
);
|
||||
newDate = generateConfig.setHour(newDate, generateConfig.getHour(defaultDate));
|
||||
newDate = generateConfig.setMinute(newDate, generateConfig.getMinute(defaultDate));
|
||||
newDate = generateConfig.setSecond(newDate, generateConfig.getSecond(defaultDate));
|
||||
return newDate;
|
||||
}
|
||||
|
||||
|
||||
export function getLowerBoundTime(
|
||||
hour: number,
|
||||
minute: number,
|
||||
|
|
|
@ -201,7 +201,7 @@ export function addGlobalMouseDownEvent(callback: ClickEventHandler) {
|
|||
if (!globalClickFunc && typeof window !== 'undefined' && window.addEventListener) {
|
||||
globalClickFunc = (e: MouseEvent) => {
|
||||
// Clone a new list to avoid repeat trigger events
|
||||
[...clickCallbacks].forEach((queueFunc) => {
|
||||
[...clickCallbacks].forEach(queueFunc => {
|
||||
queueFunc(e);
|
||||
});
|
||||
};
|
||||
|
@ -272,5 +272,5 @@ export function elementsContains(
|
|||
elements: (HTMLElement | undefined | null)[],
|
||||
target: HTMLElement,
|
||||
) {
|
||||
return elements.some((ele) => ele && ele.contains(target));
|
||||
return elements.some(ele => ele && ele.contains(target));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue