feat: picker add disabledTime
parent
2573fe41f5
commit
afafbd62fc
|
@ -28,7 +28,7 @@ import usePickerInput from './hooks/usePickerInput';
|
|||
import useTextValueMapping from './hooks/useTextValueMapping';
|
||||
import useValueTexts from './hooks/useValueTexts';
|
||||
import useHoverValue from './hooks/useHoverValue';
|
||||
import type { CSSProperties, Ref } from 'vue';
|
||||
import type { CSSProperties, HTMLAttributes, Ref } from 'vue';
|
||||
import { computed, defineComponent, ref, toRef, watch } from 'vue';
|
||||
import type { ChangeEvent, FocusEventHandler, MouseEventHandler } from '../_util/EventInterface';
|
||||
import type { VueNode } from '../_util/type';
|
||||
|
@ -38,6 +38,7 @@ import { warning } from '../vc-util/warning';
|
|||
import classNames from '../_util/classNames';
|
||||
import type { SharedTimeProps } from './panels/TimePanel';
|
||||
import { useProviderTrigger } from '../vc-trigger/context';
|
||||
import { legacyPropsWarning } from './utils/warnUtil';
|
||||
|
||||
export type PickerRefConfig = {
|
||||
focus: () => void;
|
||||
|
@ -72,6 +73,7 @@ export type PickerSharedProps<DateType> = {
|
|||
superNextIcon?: VueNode;
|
||||
getPopupContainer?: (node: HTMLElement) => HTMLElement;
|
||||
panelRender?: (originPanel: VueNode) => VueNode;
|
||||
inputRender?: (props: HTMLAttributes) => VueNode;
|
||||
|
||||
// Events
|
||||
onChange?: (value: DateType | null, dateString: string) => void;
|
||||
|
@ -167,6 +169,7 @@ function Picker<DateType>() {
|
|||
'placeholder',
|
||||
'getPopupContainer',
|
||||
'panelRender',
|
||||
'inputRender',
|
||||
'onChange',
|
||||
'onOpenChange',
|
||||
'onFocus',
|
||||
|
@ -200,15 +203,19 @@ function Picker<DateType>() {
|
|||
const needConfirmButton = computed(
|
||||
() => (picker.value === 'date' && !!props.showTime) || picker.value === 'time',
|
||||
);
|
||||
|
||||
// ============================ Warning ============================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
legacyPropsWarning(props);
|
||||
}
|
||||
// ============================= State =============================
|
||||
const formatList = computed(() =>
|
||||
toArray(getDefaultFormat(props.format, picker.value, props.showTime, props.use12Hours)),
|
||||
);
|
||||
|
||||
// Panel ref
|
||||
const panelDivRef = ref(null);
|
||||
const inputDivRef = ref(null);
|
||||
const panelDivRef = ref<HTMLDivElement>(null);
|
||||
const inputDivRef = ref<HTMLDivElement>(null);
|
||||
const containerRef = ref<HTMLDivElement>(null);
|
||||
|
||||
// Real value
|
||||
const [mergedValue, setInnerValue] = useMergedState<DateType>(null, {
|
||||
|
@ -318,9 +325,17 @@ function Picker<DateType>() {
|
|||
triggerOpen,
|
||||
forwardKeydown,
|
||||
isClickOutside: target =>
|
||||
!elementsContains([panelDivRef.value, inputDivRef.value], target as HTMLElement),
|
||||
!elementsContains(
|
||||
[panelDivRef.value, inputDivRef.value, containerRef.value],
|
||||
target as HTMLElement,
|
||||
),
|
||||
onSubmit: () => {
|
||||
if (props.disabledDate && props.disabledDate(selectedValue.value)) {
|
||||
if (
|
||||
// When user typing disabledDate with keyboard and enter, this value will be empty
|
||||
!selectedValue.value ||
|
||||
// Normal disabled check
|
||||
(props.disabledDate && props.disabledDate(selectedValue.value))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -520,6 +535,31 @@ function Picker<DateType>() {
|
|||
);
|
||||
}
|
||||
|
||||
const mergedInputProps: HTMLAttributes = {
|
||||
id,
|
||||
tabindex,
|
||||
disabled,
|
||||
readonly: inputReadOnly || typeof formatList.value[0] === 'function' || !typing.value,
|
||||
value: hoverValue.value || text.value,
|
||||
onInput: (e: ChangeEvent) => {
|
||||
triggerTextChange(e.target.value);
|
||||
},
|
||||
autofocus,
|
||||
placeholder,
|
||||
ref: inputRef,
|
||||
title: text.value,
|
||||
...inputProps.value,
|
||||
size: getInputSize(picker, formatList.value[0], generateConfig),
|
||||
...getDataOrAriaProps(props),
|
||||
autocomplete,
|
||||
};
|
||||
|
||||
const inputNode = props.inputRender ? (
|
||||
props.inputRender(mergedInputProps)
|
||||
) : (
|
||||
<input {...mergedInputProps} />
|
||||
);
|
||||
|
||||
// ============================ Warning ============================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
|
@ -547,6 +587,7 @@ function Picker<DateType>() {
|
|||
}}
|
||||
>
|
||||
<div
|
||||
ref={containerRef}
|
||||
class={classNames(prefixCls, attrs.class, {
|
||||
[`${prefixCls}-disabled`]: disabled,
|
||||
[`${prefixCls}-focused`]: focused.value,
|
||||
|
@ -566,26 +607,7 @@ function Picker<DateType>() {
|
|||
})}
|
||||
ref={inputDivRef}
|
||||
>
|
||||
<input
|
||||
id={id}
|
||||
tabindex={tabindex}
|
||||
disabled={disabled}
|
||||
readonly={
|
||||
inputReadOnly || typeof formatList.value[0] === 'function' || !typing.value
|
||||
}
|
||||
value={hoverValue.value || text.value}
|
||||
onInput={(e: ChangeEvent) => {
|
||||
triggerTextChange(e.target.value);
|
||||
}}
|
||||
autofocus={autofocus}
|
||||
placeholder={placeholder}
|
||||
ref={inputRef}
|
||||
title={text.value}
|
||||
{...inputProps.value}
|
||||
size={getInputSize(picker, formatList.value[0], generateConfig)}
|
||||
{...getDataOrAriaProps(props)}
|
||||
autocomplete={autocomplete}
|
||||
/>
|
||||
{inputNode}
|
||||
{suffixNode}
|
||||
{clearNode}
|
||||
</div>
|
||||
|
|
|
@ -215,12 +215,20 @@ function PickerPanel<DateType>() {
|
|||
// When value is null and set showTime
|
||||
if (!mergedValue.value && props.showTime) {
|
||||
if (typeof showTime === 'object') {
|
||||
return setDateTime(generateConfig, date, showTime.defaultValue || now);
|
||||
return setDateTime(
|
||||
generateConfig,
|
||||
Array.isArray(date) ? date[0] : date,
|
||||
showTime.defaultValue || now,
|
||||
);
|
||||
}
|
||||
if (defaultValue) {
|
||||
return setDateTime(generateConfig, date, defaultValue);
|
||||
return setDateTime(
|
||||
generateConfig,
|
||||
Array.isArray(date) ? date[0] : date,
|
||||
defaultValue,
|
||||
);
|
||||
}
|
||||
return setDateTime(generateConfig, date, now);
|
||||
return setDateTime(generateConfig, Array.isArray(date) ? date[0] : date, now);
|
||||
}
|
||||
return date;
|
||||
},
|
||||
|
|
|
@ -36,6 +36,7 @@ import { warning } from '../vc-util/warning';
|
|||
import useState from '../_util/hooks/useState';
|
||||
import classNames from '../_util/classNames';
|
||||
import { useProviderTrigger } from '../vc-trigger/context';
|
||||
import { legacyPropsWarning } from './utils/warnUtil';
|
||||
|
||||
function reorderValues<DateType>(
|
||||
values: RangeValue<DateType>,
|
||||
|
@ -105,8 +106,11 @@ export type RangePickerSharedProps<DateType> = {
|
|||
onPanelChange?: (values: RangeValue<DateType>, modes: [PanelMode, PanelMode]) => void;
|
||||
onFocus?: FocusEventHandler;
|
||||
onBlur?: FocusEventHandler;
|
||||
onMousedown?: MouseEventHandler;
|
||||
onMouseup?: MouseEventHandler;
|
||||
onMouseenter?: MouseEventHandler;
|
||||
onMouseleave?: MouseEventHandler;
|
||||
onClick?: MouseEventHandler;
|
||||
onOk?: (dates: RangeValue<DateType>) => void;
|
||||
direction?: 'ltr' | 'rtl';
|
||||
autocomplete?: string;
|
||||
|
@ -216,8 +220,11 @@ function RangerPicker<DateType>() {
|
|||
'onCalendarChange',
|
||||
'onFocus',
|
||||
'onBlur',
|
||||
'onMousedown',
|
||||
'onMouseup',
|
||||
'onMouseenter',
|
||||
'onMouseleave',
|
||||
'onClick',
|
||||
'onOk',
|
||||
'onKeydown',
|
||||
'components',
|
||||
|
@ -241,6 +248,12 @@ function RangerPicker<DateType>() {
|
|||
const separatorRef = ref<HTMLDivElement>(null);
|
||||
const startInputRef = ref<HTMLInputElement>(null);
|
||||
const endInputRef = ref<HTMLInputElement>(null);
|
||||
const arrowRef = ref<HTMLDivElement>(null);
|
||||
|
||||
// ============================ Warning ============================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
legacyPropsWarning(props);
|
||||
}
|
||||
|
||||
// ============================= Misc ==============================
|
||||
const formatList = computed(() =>
|
||||
|
@ -604,7 +617,7 @@ function RangerPicker<DateType>() {
|
|||
},
|
||||
isClickOutside: (target: EventTarget | null) =>
|
||||
!elementsContains(
|
||||
[panelDivRef.value, startInputDivRef.value, endInputDivRef.value],
|
||||
[panelDivRef.value, startInputDivRef.value, endInputDivRef.value, containerRef.value],
|
||||
target as HTMLElement,
|
||||
),
|
||||
onFocus: (e: FocusEvent) => {
|
||||
|
@ -615,6 +628,14 @@ function RangerPicker<DateType>() {
|
|||
triggerOpen(newOpen, index);
|
||||
},
|
||||
onSubmit: () => {
|
||||
if (
|
||||
// When user typing disabledDate with keyboard and enter, this value will be empty
|
||||
!selectedValue.value ||
|
||||
// Normal disabled check
|
||||
(props.disabledDate && props.disabledDate(selectedValue.value[index]))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
triggerChange(selectedValue.value, index);
|
||||
resetText();
|
||||
},
|
||||
|
@ -649,6 +670,7 @@ function RangerPicker<DateType>() {
|
|||
const onPickerClick = (e: MouseEvent) => {
|
||||
// When click inside the picker & outside the picker's input elements
|
||||
// the panel should still be opened
|
||||
props.onClick?.(e);
|
||||
if (
|
||||
!mergedOpen.value &&
|
||||
!startInputRef.value.contains(e.target as Node) &&
|
||||
|
@ -664,6 +686,7 @@ function RangerPicker<DateType>() {
|
|||
|
||||
const onPickerMousedown = (e: MouseEvent) => {
|
||||
// shouldn't affect input elements if picker is active
|
||||
props.onMousedown?.(e);
|
||||
if (
|
||||
mergedOpen.value &&
|
||||
(startFocused.value || endFocused.value) &&
|
||||
|
@ -880,7 +903,6 @@ function RangerPicker<DateType>() {
|
|||
? getValue(selectedValue.value, 1)
|
||||
: getValue(selectedValue.value, 0)
|
||||
}
|
||||
defaultPickerValue={undefined}
|
||||
/>
|
||||
</RangeContextProvider>
|
||||
);
|
||||
|
@ -938,6 +960,7 @@ function RangerPicker<DateType>() {
|
|||
renderExtraFooter,
|
||||
onMouseenter,
|
||||
onMouseleave,
|
||||
onMouseup,
|
||||
onOk,
|
||||
components,
|
||||
direction,
|
||||
|
@ -954,7 +977,14 @@ function RangerPicker<DateType>() {
|
|||
// Arrow offset
|
||||
arrowLeft = startInputDivRef.value.offsetWidth + separatorRef.value.offsetWidth;
|
||||
|
||||
if (panelDivRef.value.offsetWidth && arrowLeft > panelDivRef.value.offsetWidth) {
|
||||
if (
|
||||
panelDivRef.value.offsetWidth &&
|
||||
arrowRef.value.offsetWidth &&
|
||||
arrowLeft >
|
||||
panelDivRef.value.offsetWidth -
|
||||
arrowRef.value.offsetWidth -
|
||||
(direction === 'rtl' ? 0 : arrowRef.value.offsetLeft)
|
||||
) {
|
||||
panelLeft = arrowLeft;
|
||||
}
|
||||
}
|
||||
|
@ -1066,7 +1096,7 @@ function RangerPicker<DateType>() {
|
|||
class={classNames(`${prefixCls}-range-wrapper`, `${prefixCls}-${picker}-range-wrapper`)}
|
||||
style={{ minWidth: `${popupMinWidth.value}px` }}
|
||||
>
|
||||
<div class={`${prefixCls}-range-arrow`} style={arrowPositionStyle} />
|
||||
<div ref={arrowRef} class={`${prefixCls}-range-arrow`} style={arrowPositionStyle} />
|
||||
{renderPanels()}
|
||||
</div>
|
||||
);
|
||||
|
@ -1157,6 +1187,7 @@ function RangerPicker<DateType>() {
|
|||
onMouseenter={onMouseenter}
|
||||
onMouseleave={onMouseleave}
|
||||
onMousedown={onPickerMousedown}
|
||||
onMouseup={onMouseup}
|
||||
{...getDataOrAriaProps(props)}
|
||||
>
|
||||
<div
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// 2.5.12
|
||||
// 2.6.4
|
||||
import type { PickerProps } from './Picker';
|
||||
import Picker from './Picker';
|
||||
import PickerPanel from './PickerPanel';
|
||||
|
|
|
@ -137,7 +137,7 @@ function DatetimePanel<DateType>(_props: DatetimePanelProps<DateType>) {
|
|||
setTime(
|
||||
generateConfig,
|
||||
date,
|
||||
showTime && typeof showTime === 'object' ? showTime.defaultValue : null,
|
||||
!value && typeof showTime === 'object' ? showTime.defaultValue : null,
|
||||
),
|
||||
'date',
|
||||
);
|
||||
|
@ -148,6 +148,7 @@ function DatetimePanel<DateType>(_props: DatetimePanelProps<DateType>) {
|
|||
format={undefined}
|
||||
{...timeProps}
|
||||
{...disabledTimes}
|
||||
disabledTime={null}
|
||||
defaultValue={undefined}
|
||||
operationRef={timeOperationRef}
|
||||
active={activePanel.value === 'time'}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { setTime as utilSetTime } from '../../utils/timeUtil';
|
|||
import { cloneElement } from '../../../_util/vnode';
|
||||
import type { VueNode } from '../../../_util/type';
|
||||
import type { Ref } from 'vue';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { onBeforeUpdate, ref, watchEffect, computed, defineComponent } from 'vue';
|
||||
|
||||
function generateUnits(
|
||||
start: number,
|
||||
|
@ -60,6 +60,7 @@ const TimeBody = defineComponent({
|
|||
'disabledHours',
|
||||
'disabledMinutes',
|
||||
'disabledSeconds',
|
||||
'disabledTime',
|
||||
'hideDisabledOptions',
|
||||
'onSelect',
|
||||
],
|
||||
|
@ -85,6 +86,29 @@ const TimeBody = defineComponent({
|
|||
const minute = computed(() => (props.value ? props.generateConfig.getMinute(props.value) : -1));
|
||||
const second = computed(() => (props.value ? props.generateConfig.getSecond(props.value) : -1));
|
||||
|
||||
const now = ref(props.generateConfig.getNow());
|
||||
const mergedDisabledHours = ref();
|
||||
const mergedDisabledMinutes = ref();
|
||||
const mergedDisabledSeconds = ref();
|
||||
onBeforeUpdate(() => {
|
||||
now.value = props.generateConfig.getNow();
|
||||
});
|
||||
watchEffect(() => {
|
||||
if (props.disabledTime) {
|
||||
const disabledConfig = props.disabledTime(now);
|
||||
[mergedDisabledHours.value, mergedDisabledMinutes.value, mergedDisabledSeconds.value] = [
|
||||
disabledConfig.disabledHours,
|
||||
disabledConfig.disabledMinutes,
|
||||
disabledConfig.disabledSeconds,
|
||||
];
|
||||
}
|
||||
|
||||
[mergedDisabledHours.value, mergedDisabledMinutes.value, mergedDisabledSeconds.value] = [
|
||||
props.disabledHours,
|
||||
props.disabledMinutes,
|
||||
props.disabledSeconds,
|
||||
];
|
||||
});
|
||||
const setTime = (
|
||||
isNewPM: boolean | undefined,
|
||||
newHour: number,
|
||||
|
@ -110,7 +134,12 @@ const TimeBody = defineComponent({
|
|||
|
||||
// ========================= Unit =========================
|
||||
const rawHours = computed(() =>
|
||||
generateUnits(0, 23, props.hourStep ?? 1, props.disabledHours && props.disabledHours()),
|
||||
generateUnits(
|
||||
0,
|
||||
23,
|
||||
props.hourStep ?? 1,
|
||||
mergedDisabledHours.value && mergedDisabledHours.value(),
|
||||
),
|
||||
);
|
||||
|
||||
// const memorizedRawHours = useMemo(() => rawHours, rawHours, shouldUnitsUpdate);
|
||||
|
@ -151,7 +180,7 @@ const TimeBody = defineComponent({
|
|||
0,
|
||||
59,
|
||||
props.minuteStep ?? 1,
|
||||
props.disabledMinutes && props.disabledMinutes(originHour.value),
|
||||
mergedDisabledMinutes.value && mergedDisabledMinutes.value(originHour.value),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -160,7 +189,7 @@ const TimeBody = defineComponent({
|
|||
0,
|
||||
59,
|
||||
props.secondStep ?? 1,
|
||||
props.disabledSeconds && props.disabledSeconds(originHour.value, minute),
|
||||
mergedDisabledSeconds.value && mergedDisabledSeconds.value(originHour.value, minute),
|
||||
),
|
||||
);
|
||||
|
||||
|
|
|
@ -19,7 +19,16 @@ export type SharedTimeProps<DateType> = {
|
|||
secondStep?: number;
|
||||
hideDisabledOptions?: boolean;
|
||||
defaultValue?: DateType;
|
||||
} & DisabledTimes;
|
||||
|
||||
/** @deprecated Please use `disabledTime` instead. */
|
||||
disabledHours?: DisabledTimes['disabledHours'];
|
||||
/** @deprecated Please use `disabledTime` instead. */
|
||||
disabledMinutes?: DisabledTimes['disabledMinutes'];
|
||||
/** @deprecated Please use `disabledTime` instead. */
|
||||
disabledSeconds?: DisabledTimes['disabledSeconds'];
|
||||
|
||||
disabledTime?: (date: DateType) => DisabledTimes;
|
||||
};
|
||||
|
||||
export type TimePanelProps<DateType> = {
|
||||
format?: string;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { warning } from '../../vc-util/warning';
|
||||
import type { DisabledTimes, PickerMode } from '../interface';
|
||||
|
||||
export interface WarningProps extends DisabledTimes {
|
||||
picker?: PickerMode;
|
||||
}
|
||||
|
||||
export function legacyPropsWarning(props: WarningProps) {
|
||||
const { picker, disabledHours, disabledMinutes, disabledSeconds } = props;
|
||||
|
||||
if (picker === 'time' && (disabledHours || disabledMinutes || disabledSeconds)) {
|
||||
warning(
|
||||
false,
|
||||
`'disabledHours', 'disabledMinutes', 'disabledSeconds' will be removed in the next major version, please use 'disabledTime' instead.`,
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue