@v-c/picker 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/dist/PickerInput/Popup/index.d.ts +13 -1
- package/dist/PickerInput/Popup/index.js +10 -1
- package/dist/PickerInput/RangePicker.js +55 -72
- package/dist/PickerInput/Selector/ClearIcon.d.ts +11 -0
- package/dist/PickerInput/Selector/ClearIcon.js +53 -0
- package/dist/PickerInput/Selector/Icon.d.ts +0 -5
- package/dist/PickerInput/Selector/Icon.js +16 -57
- package/dist/PickerInput/Selector/Input.js +2 -5
- package/dist/PickerInput/Selector/RangeSelector.js +9 -5
- package/dist/PickerInput/Selector/SingleSelector/index.d.ts +1 -1
- package/dist/PickerInput/Selector/SingleSelector/index.js +5 -8
- package/dist/PickerInput/SinglePicker.js +56 -40
- package/dist/PickerInput/hooks/useFocusEvents.d.ts +28 -0
- package/dist/PickerInput/hooks/useFocusEvents.js +36 -0
- package/dist/PickerInput/hooks/useFocusLock.d.ts +14 -0
- package/dist/PickerInput/hooks/useFocusLock.js +25 -0
- package/dist/PickerInput/hooks/useRangeDisabledDate.d.ts +1 -1
- package/dist/PickerInput/hooks/useRangeDisabledDate.js +3 -3
- package/dist/PickerInput/hooks/useRangePickerValue.d.ts +7 -1
- package/dist/PickerInput/hooks/useRangePickerValue.js +10 -16
- package/dist/PickerInput/hooks/useRangeValue.d.ts +3 -3
- package/dist/PickerInput/hooks/useRangeValue.js +15 -13
- package/dist/PickerInput/hooks/useRangeValueChange.d.ts +109 -0
- package/dist/PickerInput/hooks/useRangeValueChange.js +226 -0
- package/dist/PickerPanel/PanelBody.js +2 -1
- package/dist/PickerPanel/PanelHeader.js +2 -1
- package/dist/locale/am_ET.js +4 -4
- package/dist/locale/ar_EG.js +4 -4
- package/dist/locale/az_AZ.js +4 -4
- package/dist/locale/bg_BG.js +4 -4
- package/dist/locale/bn_BD.js +4 -4
- package/dist/locale/by_BY.js +4 -4
- package/dist/locale/ca_ES.js +4 -4
- package/dist/locale/cs_CZ.js +4 -4
- package/dist/locale/da_DK.js +4 -4
- package/dist/locale/de_DE.js +4 -4
- package/dist/locale/el_GR.js +4 -4
- package/dist/locale/en_GB.js +4 -4
- package/dist/locale/en_US.js +4 -4
- package/dist/locale/es_ES.js +4 -4
- package/dist/locale/es_MX.js +4 -4
- package/dist/locale/et_EE.js +4 -4
- package/dist/locale/eu_ES.js +4 -4
- package/dist/locale/fa_IR.js +4 -4
- package/dist/locale/fi_FI.js +4 -4
- package/dist/locale/fr_BE.js +4 -4
- package/dist/locale/fr_CA.js +4 -4
- package/dist/locale/fr_FR.js +4 -4
- package/dist/locale/ga_IE.js +4 -4
- package/dist/locale/gl_ES.js +4 -4
- package/dist/locale/he_IL.js +4 -4
- package/dist/locale/hi_IN.js +4 -4
- package/dist/locale/hr_HR.js +4 -4
- package/dist/locale/hu_HU.js +4 -4
- package/dist/locale/id_ID.js +4 -4
- package/dist/locale/is_IS.js +4 -4
- package/dist/locale/it_IT.js +4 -4
- package/dist/locale/ja_JP.js +4 -4
- package/dist/locale/ka_GE.js +4 -4
- package/dist/locale/kk_KZ.js +4 -4
- package/dist/locale/km_KH.js +4 -4
- package/dist/locale/kmr_IQ.js +4 -4
- package/dist/locale/kn_IN.js +4 -4
- package/dist/locale/ko_KR.js +4 -4
- package/dist/locale/lt_LT.js +4 -4
- package/dist/locale/lv_LV.js +4 -4
- package/dist/locale/mk_MK.js +4 -4
- package/dist/locale/ml_IN.js +4 -4
- package/dist/locale/mn_MN.js +4 -4
- package/dist/locale/mr_IN.js +4 -4
- package/dist/locale/ms_MY.js +2 -2
- package/dist/locale/my_MM.js +4 -4
- package/dist/locale/nb_NO.js +4 -4
- package/dist/locale/ne_NP.js +4 -4
- package/dist/locale/nl_BE.js +4 -4
- package/dist/locale/nl_NL.js +4 -4
- package/dist/locale/pl_PL.js +4 -4
- package/dist/locale/pt_BR.js +4 -4
- package/dist/locale/pt_PT.js +4 -4
- package/dist/locale/ro_RO.js +4 -4
- package/dist/locale/ru_RU.js +4 -4
- package/dist/locale/si_LK.js +4 -4
- package/dist/locale/sk_SK.js +4 -4
- package/dist/locale/sl_SI.js +4 -4
- package/dist/locale/sr_Cyrl_RS.js +4 -4
- package/dist/locale/sr_RS.js +4 -4
- package/dist/locale/sv_SE.js +4 -4
- package/dist/locale/ta_IN.js +4 -4
- package/dist/locale/th_TH.js +4 -4
- package/dist/locale/tk_TK.js +4 -4
- package/dist/locale/tr_TR.js +4 -4
- package/dist/locale/ug_CN.js +4 -4
- package/dist/locale/uk_UA.js +4 -4
- package/dist/locale/ur_PK.js +4 -4
- package/dist/locale/uz_UZ.js +4 -4
- package/dist/locale/vi_VN.js +4 -4
- package/dist/locale/zh_CN.js +4 -4
- package/dist/locale/zh_TW.js +4 -4
- package/dist/utils/miscUtil.d.ts +1 -1
- package/dist/utils/miscUtil.js +3 -4
- package/package.json +4 -4
- package/dist/PickerInput/hooks/useRangeActive.d.ts +0 -9
- package/dist/PickerInput/hooks/useRangeActive.js +0 -59
|
@@ -7,11 +7,12 @@ import { formatValue, formatValues } from "../utils/valueUtil.js";
|
|
|
7
7
|
import useCellRender from "./hooks/useCellRender.js";
|
|
8
8
|
import useFieldsInvalidate from "./hooks/useFieldsInvalidate.js";
|
|
9
9
|
import useFilledProps from "./hooks/useFilledProps.js";
|
|
10
|
+
import useFocusEvents, { isTargetInContainers } from "./hooks/useFocusEvents.js";
|
|
10
11
|
import useOpen from "./hooks/useOpen.js";
|
|
11
12
|
import usePresets from "./hooks/usePresets.js";
|
|
12
|
-
import useRangeActive from "./hooks/useRangeActive.js";
|
|
13
13
|
import useRangePickerValue from "./hooks/useRangePickerValue.js";
|
|
14
14
|
import useRangeValue, { useInnerValue } from "./hooks/useRangeValue.js";
|
|
15
|
+
import useRangeValueChange from "./hooks/useRangeValueChange.js";
|
|
15
16
|
import useShowNow from "./hooks/useShowNow.js";
|
|
16
17
|
import useToggleDates from "../hooks/useToggleDates.js";
|
|
17
18
|
import Popup from "./Popup/index.js";
|
|
@@ -117,15 +118,15 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
117
118
|
};
|
|
118
119
|
const [mergedValue, setInnerValue, getCalendarValue, triggerCalendarChange, triggerOk] = useInnerValue(generateConfig, locale, formatList, ref(false), order, defaultValue, value, onInternalCalendarChange, onInternalOk);
|
|
119
120
|
const calendarValue = computed(() => getCalendarValue.value);
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
|
|
121
|
+
const popupRef = ref();
|
|
122
|
+
const isInternalPickerElement = (element) => isTargetInContainers(element, [selectorRef.value?.nativeElement, popupRef.value]);
|
|
123
|
+
const [focused, onFieldFocus, onFieldBlur] = useFocusEvents(isInternalPickerElement, (_index, event) => {
|
|
123
124
|
onFocus.value?.(event, {});
|
|
124
|
-
}
|
|
125
|
-
const onSharedBlur = (event) => {
|
|
126
|
-
triggerFocus(false);
|
|
125
|
+
}, (_index, event) => {
|
|
127
126
|
onBlur.value?.(event, {});
|
|
128
|
-
}
|
|
127
|
+
}, () => {
|
|
128
|
+
triggerOpen(false);
|
|
129
|
+
});
|
|
129
130
|
const mergedMode = ref(picker.value ?? mode.value);
|
|
130
131
|
watch(picker, () => {
|
|
131
132
|
mergedMode.value = picker.value;
|
|
@@ -139,12 +140,32 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
139
140
|
const onInternalChange = (dates, dateStrings) => {
|
|
140
141
|
if (props?.onChange) props?.onChange?.(toValueByFormat(dates), pickerParam(dateStrings));
|
|
141
142
|
};
|
|
142
|
-
const [, triggerSubmitChange] = useRangeValue(computed(() => {
|
|
143
|
+
const [, triggerSubmitChange, resetValue] = useRangeValue(computed(() => {
|
|
143
144
|
return {
|
|
144
145
|
...fp.value,
|
|
145
146
|
onChange: onInternalChange
|
|
146
147
|
};
|
|
147
|
-
}), mergedValue, setInnerValue, () => getCalendarValue.value, triggerCalendarChange, computed(() => []), formatList,
|
|
148
|
+
}), mergedValue, setInnerValue, () => getCalendarValue.value, triggerCalendarChange, computed(() => []), formatList, isInvalidateDate);
|
|
149
|
+
const getFieldCalendarValue = () => {
|
|
150
|
+
const values = getCalendarValue.value;
|
|
151
|
+
return [values.length ? values : null];
|
|
152
|
+
};
|
|
153
|
+
const triggerFieldCalendarChange = (_index, nextValues) => {
|
|
154
|
+
triggerCalendarChange(nextValues);
|
|
155
|
+
};
|
|
156
|
+
const flushFieldSubmit = (_index, needTriggerChange) => {
|
|
157
|
+
if (needTriggerChange) {
|
|
158
|
+
triggerSubmitChange(getCalendarValue.value);
|
|
159
|
+
triggerOpen(false, { force: true });
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const resetFieldValue = () => {
|
|
163
|
+
resetValue();
|
|
164
|
+
};
|
|
165
|
+
const [, activeIndex, , , triggerSingleValueChange, resetSingleValueChange] = useRangeValueChange(ref(1), needConfirm, ref([false]), getFieldCalendarValue, triggerFieldCalendarChange, flushFieldSubmit, resetFieldValue);
|
|
166
|
+
watch(mergedOpen, (open) => {
|
|
167
|
+
if (!open) triggerSingleValueChange(0, "popupClose");
|
|
168
|
+
});
|
|
148
169
|
const [submitInvalidates, onSelectorInvalid] = useFieldsInvalidate(calendarValue, isInvalidateDate);
|
|
149
170
|
const submitInvalidate = computed(() => submitInvalidates.value.some((invalidated) => invalidated));
|
|
150
171
|
const onInternalPickerValueChange = (dates, info) => {
|
|
@@ -157,7 +178,7 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
157
178
|
onPickerValueChange.value(dates[0], cleanInfo);
|
|
158
179
|
}
|
|
159
180
|
};
|
|
160
|
-
const [currentPickerValue, setCurrentPickerValue] = useRangePickerValue(generateConfig, locale, calendarValue, computed(() => [mergedMode.value]), mergedOpen, activeIndex, internalPicker, ref(false), defaultPickerValue, pickerValue, computed(() => toArray(showTime.value?.defaultOpenValue)), onInternalPickerValueChange, minDate, maxDate);
|
|
181
|
+
const [currentPickerValue, setCurrentPickerValue] = useRangePickerValue(generateConfig, locale, calendarValue, computed(() => [mergedMode.value]), mergedOpen, ref(false), activeIndex, internalPicker, ref(false), defaultPickerValue, pickerValue, computed(() => toArray(showTime.value?.defaultOpenValue)), onInternalPickerValueChange, minDate, maxDate);
|
|
161
182
|
const triggerModeChange = (nextPickerValue, nextMode, triggerEvent) => {
|
|
162
183
|
setMode(nextMode);
|
|
163
184
|
if (onPanelChange.value && triggerEvent !== false) {
|
|
@@ -166,12 +187,12 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
166
187
|
}
|
|
167
188
|
};
|
|
168
189
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
190
|
+
* Submit the complete value list stored in SinglePicker field `0`.
|
|
191
|
+
* 提交 SinglePicker field `0` 内保存的整组值。
|
|
171
192
|
*/
|
|
172
|
-
const triggerConfirm = () => {
|
|
173
|
-
|
|
174
|
-
|
|
193
|
+
const triggerConfirm = (source = "confirm") => {
|
|
194
|
+
triggerSingleValueChange(0, source);
|
|
195
|
+
resetSingleValueChange();
|
|
175
196
|
};
|
|
176
197
|
const onSelectorClick = (event) => {
|
|
177
198
|
if (!disabled.value && !selectorRef.value?.nativeElement?.contains(document.activeElement)) selectorRef.value?.focus();
|
|
@@ -217,14 +238,13 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
217
238
|
};
|
|
218
239
|
const onPanelFocus = (event) => {
|
|
219
240
|
triggerOpen(true);
|
|
220
|
-
|
|
241
|
+
onFieldFocus(0, "panel", event);
|
|
221
242
|
};
|
|
222
243
|
const onPanelSelect = (date) => {
|
|
223
|
-
lastOperation("panel");
|
|
224
244
|
if (multiple.value && internalMode.value !== picker.value) return;
|
|
225
245
|
const nextValues = multiple.value ? toggleDates(getCalendarValue.value, date) : [date];
|
|
226
|
-
|
|
227
|
-
|
|
246
|
+
const panelFinished = !complexPicker.value && internalPicker.value === internalMode.value;
|
|
247
|
+
triggerSingleValueChange(0, panelFinished ? "panel-final" : "panel-intermediate", nextValues);
|
|
228
248
|
};
|
|
229
249
|
const onPopupClose = () => {
|
|
230
250
|
triggerOpen(false);
|
|
@@ -247,23 +267,26 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
247
267
|
multiple: fp.value.multiple
|
|
248
268
|
};
|
|
249
269
|
});
|
|
250
|
-
const onSelectorChange = (date) => {
|
|
251
|
-
|
|
270
|
+
const onSelectorChange = (date, source = "input") => {
|
|
271
|
+
triggerSingleValueChange(0, source, date);
|
|
252
272
|
};
|
|
253
273
|
const onSelectorInputChange = () => {
|
|
254
|
-
|
|
274
|
+
triggerSingleValueChange(0, "input");
|
|
255
275
|
};
|
|
256
276
|
const onSelectorFocus = (event) => {
|
|
257
|
-
|
|
277
|
+
triggerSingleValueChange(0, "field-switch");
|
|
258
278
|
triggerOpen(true, { inherit: true });
|
|
259
|
-
|
|
279
|
+
onFieldFocus(0, "input", event);
|
|
260
280
|
};
|
|
261
281
|
const onSelectorBlur = (event) => {
|
|
262
|
-
|
|
263
|
-
onSharedBlur(event);
|
|
282
|
+
onFieldBlur(0, "input", event);
|
|
264
283
|
};
|
|
265
284
|
const onSelectorKeyDown = (event, preventDefault) => {
|
|
266
|
-
if (event.key === "Tab") triggerConfirm();
|
|
285
|
+
if (event.key === "Tab") triggerConfirm("keyboard-submit-weak");
|
|
286
|
+
else if (event.key === "Escape") {
|
|
287
|
+
triggerSingleValueChange(0, "esc");
|
|
288
|
+
triggerOpen(false);
|
|
289
|
+
}
|
|
267
290
|
onKeyDown.value?.(event, preventDefault);
|
|
268
291
|
};
|
|
269
292
|
providePickerContext(computed(() => {
|
|
@@ -285,14 +308,6 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
285
308
|
], () => {
|
|
286
309
|
if (mergedOpen.value && activeIndex.value !== void 0) triggerModeChange(null, picker.value, false);
|
|
287
310
|
}, { flush: "post" });
|
|
288
|
-
watch(mergedOpen, () => {
|
|
289
|
-
const lastOp = lastOperation();
|
|
290
|
-
if (!mergedOpen.value && lastOp === "input") {
|
|
291
|
-
triggerOpen(false);
|
|
292
|
-
triggerConfirm();
|
|
293
|
-
}
|
|
294
|
-
if (!mergedOpen.value && complexPicker.value && !needConfirm.value && lastOp === "panel") triggerConfirm();
|
|
295
|
-
}, { flush: "post" });
|
|
296
311
|
const popupProps = computed(() => {
|
|
297
312
|
const [mergedClassNames, mergedStyles] = semanticCtx.value;
|
|
298
313
|
return {
|
|
@@ -301,7 +316,7 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
301
316
|
showTime: showTime.value,
|
|
302
317
|
disabledDate: disabledDate.value,
|
|
303
318
|
onFocus: onPanelFocus,
|
|
304
|
-
onBlur:
|
|
319
|
+
onBlur: (event) => onFieldBlur(0, "panel", event),
|
|
305
320
|
picker: picker.value,
|
|
306
321
|
mode: mergedMode.value,
|
|
307
322
|
internalMode: internalMode.value,
|
|
@@ -317,7 +332,7 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
317
332
|
hoverValue: hoverValues.value,
|
|
318
333
|
onHover: onPanelHover,
|
|
319
334
|
needConfirm: needConfirm.value,
|
|
320
|
-
onSubmit: triggerConfirm,
|
|
335
|
+
onSubmit: () => triggerConfirm("confirm"),
|
|
321
336
|
onOk: triggerOk,
|
|
322
337
|
presets: presetList.value,
|
|
323
338
|
onPresetHover,
|
|
@@ -325,7 +340,8 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
325
340
|
onNow,
|
|
326
341
|
cellRender: onInternalCellRender,
|
|
327
342
|
classNames: mergedClassNames,
|
|
328
|
-
styles: mergedStyles
|
|
343
|
+
styles: mergedStyles,
|
|
344
|
+
popupContainerRef: popupRef.value
|
|
329
345
|
};
|
|
330
346
|
});
|
|
331
347
|
return () => {
|
|
@@ -354,7 +370,7 @@ var SinglePicker = /* @__PURE__ */ defineComponent((props, { expose }) => {
|
|
|
354
370
|
onFocus: onSelectorFocus,
|
|
355
371
|
onBlur: onSelectorBlur,
|
|
356
372
|
onKeyDown: onSelectorKeyDown,
|
|
357
|
-
onSubmit: triggerConfirm,
|
|
373
|
+
onSubmit: () => triggerConfirm("keyboard-submit"),
|
|
358
374
|
value: selectorValues.value,
|
|
359
375
|
maskFormat: maskFormat.value,
|
|
360
376
|
onChange: onSelectorChange,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
/** Focus event source. / 焦点事件来源。 */
|
|
3
|
+
export type FocusSource = 'input' | 'panel';
|
|
4
|
+
/** Focus event handler. / 聚焦事件处理函数。 */
|
|
5
|
+
export type FieldFocusHandler = (index: number, source: FocusSource, event: FocusEvent) => void;
|
|
6
|
+
/** Blur event handler. / 失焦事件处理函数。 */
|
|
7
|
+
export type FieldBlurHandler = (index: number, source: FocusSource, event: FocusEvent) => void;
|
|
8
|
+
/** Check whether an element belongs to the current focus scope. / 检查元素是否属于当前焦点范围。 */
|
|
9
|
+
export type IsInternalElement = (element: EventTarget | null) => boolean;
|
|
10
|
+
/** Notify a Picker focus or blur event. / 通知 Picker 的聚焦或失焦事件。 */
|
|
11
|
+
export type FocusEventHandler = (index: number, event: FocusEvent) => void;
|
|
12
|
+
export type UseFocusEventsReturn = [
|
|
13
|
+
focused: Ref<boolean>,
|
|
14
|
+
onFieldFocus: FieldFocusHandler,
|
|
15
|
+
onFieldBlur: FieldBlurHandler
|
|
16
|
+
];
|
|
17
|
+
/** Check whether the target belongs to any container. / 判断目标是否属于任意一个容器。 */
|
|
18
|
+
export declare function isTargetInContainers(target: EventTarget | null, containers: readonly (Element | null | undefined)[]): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Handle field focus and blur events.
|
|
21
|
+
* 处理 field 的聚焦与失焦事件。
|
|
22
|
+
*
|
|
23
|
+
* Always forward the actual element focus events. Only the internal Picker
|
|
24
|
+
* blur is skipped when `relatedTarget` still belongs to the Picker.
|
|
25
|
+
* 始终转发元素实际发生的焦点事件。仅当 `relatedTarget` 仍属于 Picker 时,
|
|
26
|
+
* 跳过 Picker 内部的整体失焦逻辑。
|
|
27
|
+
*/
|
|
28
|
+
export default function useFocusEvents(isInternalElement: IsInternalElement, onFocus?: FocusEventHandler, onBlur?: FocusEventHandler, onConfirmedBlur?: FocusEventHandler): UseFocusEventsReturn;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { computed, shallowRef } from "vue";
|
|
2
|
+
//#region src/PickerInput/hooks/useFocusEvents.ts
|
|
3
|
+
/** Check whether the target belongs to any container. / 判断目标是否属于任意一个容器。 */
|
|
4
|
+
function isTargetInContainers(target, containers) {
|
|
5
|
+
return containers.some((container) => !!container && (container === target || container.contains(target)));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Handle field focus and blur events.
|
|
9
|
+
* 处理 field 的聚焦与失焦事件。
|
|
10
|
+
*
|
|
11
|
+
* Always forward the actual element focus events. Only the internal Picker
|
|
12
|
+
* blur is skipped when `relatedTarget` still belongs to the Picker.
|
|
13
|
+
* 始终转发元素实际发生的焦点事件。仅当 `relatedTarget` 仍属于 Picker 时,
|
|
14
|
+
* 跳过 Picker 内部的整体失焦逻辑。
|
|
15
|
+
*/
|
|
16
|
+
function useFocusEvents(isInternalElement, onFocus, onBlur, onConfirmedBlur) {
|
|
17
|
+
const focusedIndex = shallowRef(null);
|
|
18
|
+
const onFieldFocus = (index, _source, event) => {
|
|
19
|
+
focusedIndex.value = index;
|
|
20
|
+
onFocus?.(index, event);
|
|
21
|
+
};
|
|
22
|
+
const onFieldBlur = (index, _source, event) => {
|
|
23
|
+
if (!isInternalElement(event.relatedTarget)) {
|
|
24
|
+
focusedIndex.value = null;
|
|
25
|
+
onConfirmedBlur?.(index, event);
|
|
26
|
+
}
|
|
27
|
+
onBlur?.(index, event);
|
|
28
|
+
};
|
|
29
|
+
return [
|
|
30
|
+
computed(() => focusedIndex.value !== null),
|
|
31
|
+
onFieldFocus,
|
|
32
|
+
onFieldBlur
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { useFocusEvents as default, isTargetInContainers };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
interface FocusLockSelectorRef {
|
|
3
|
+
startInput?: HTMLElement | null;
|
|
4
|
+
endInput?: HTMLElement | null;
|
|
5
|
+
focus: (index?: number | {
|
|
6
|
+
index?: number;
|
|
7
|
+
}) => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Keep focus on the specified input field while focus moves inside the Picker.
|
|
11
|
+
* 当焦点在 Picker 内移动时,将其锁定在指定的输入框上。
|
|
12
|
+
*/
|
|
13
|
+
export default function useFocusLock(index: Ref<number | null>, forceFocus: Ref<boolean>, selectorRef: Ref<FocusLockSelectorRef | undefined>, popupRef: Ref<HTMLElement | undefined | null>, triggerOpen: (open: boolean) => void): void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isTargetInContainers } from "./useFocusEvents.js";
|
|
2
|
+
import { onUpdated, watch } from "vue";
|
|
3
|
+
//#region src/PickerInput/hooks/useFocusLock.ts
|
|
4
|
+
/**
|
|
5
|
+
* Keep focus on the specified input field while focus moves inside the Picker.
|
|
6
|
+
* 当焦点在 Picker 内移动时,将其锁定在指定的输入框上。
|
|
7
|
+
*/
|
|
8
|
+
function useFocusLock(index, forceFocus, selectorRef, popupRef, triggerOpen) {
|
|
9
|
+
watch([index, forceFocus], () => {
|
|
10
|
+
if (index.value !== null && forceFocus.value) {
|
|
11
|
+
triggerOpen(true);
|
|
12
|
+
selectorRef.value?.focus({ index: index.value });
|
|
13
|
+
}
|
|
14
|
+
}, { flush: "post" });
|
|
15
|
+
const lockFocus = () => {
|
|
16
|
+
if (index.value === null) return;
|
|
17
|
+
const inputFields = [selectorRef.value?.startInput, selectorRef.value?.endInput];
|
|
18
|
+
const activeElement = (inputFields[index.value]?.getRootNode())?.activeElement ?? document.activeElement;
|
|
19
|
+
if (isTargetInContainers(activeElement, [popupRef.value])) return;
|
|
20
|
+
if (inputFields.some((field, fieldIndex) => fieldIndex !== index.value && isTargetInContainers(activeElement, [field]))) inputFields[index.value]?.focus();
|
|
21
|
+
};
|
|
22
|
+
onUpdated(lockFocus);
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { useFocusLock as default };
|
|
@@ -6,4 +6,4 @@ import { RangeValueType } from '../RangePicker';
|
|
|
6
6
|
* RangePicker need additional logic to handle the `disabled` case. e.g.
|
|
7
7
|
* [disabled, enabled] should end date not before start date
|
|
8
8
|
*/
|
|
9
|
-
export default function useRangeDisabledDate<DateType extends object = any>(values: Ref<RangeValueType<DateType>>, disabled: Ref<[boolean, boolean]>,
|
|
9
|
+
export default function useRangeDisabledDate<DateType extends object = any>(values: Ref<RangeValueType<DateType>>, disabled: Ref<[boolean, boolean]>, activeIndexRef: Ref<number>, triggeredFields: Ref<number[]>, generateConfig: Ref<GenerateConfig<DateType>>, locale: Ref<Locale>, disabledDate: Ref<DisabledDate<DateType> | undefined>): DisabledDate<DateType>;
|
|
@@ -5,13 +5,13 @@ import { isSame } from "../../utils/dateUtil.js";
|
|
|
5
5
|
* RangePicker need additional logic to handle the `disabled` case. e.g.
|
|
6
6
|
* [disabled, enabled] should end date not before start date
|
|
7
7
|
*/
|
|
8
|
-
function useRangeDisabledDate(values, disabled,
|
|
8
|
+
function useRangeDisabledDate(values, disabled, activeIndexRef, triggeredFields, generateConfig, locale, disabledDate) {
|
|
9
9
|
const rangeDisabledDate = (date, info) => {
|
|
10
|
-
const activeIndex =
|
|
10
|
+
const activeIndex = activeIndexRef.value;
|
|
11
11
|
const [start, end] = values.value;
|
|
12
12
|
const mergedInfo = {
|
|
13
13
|
...info,
|
|
14
|
-
from: getFromDate(values.value,
|
|
14
|
+
from: getFromDate(values.value, triggeredFields.value, activeIndex)
|
|
15
15
|
};
|
|
16
16
|
if (activeIndex === 1 && disabled.value[0] && start && !isSame(generateConfig.value, locale.value, start, date, mergedInfo.type) && generateConfig.value.isAfter(start, date)) return true;
|
|
17
17
|
if (activeIndex === 0 && disabled.value[1] && end && !isSame(generateConfig.value, locale.value, end, date, mergedInfo.type) && generateConfig.value.isAfter(date, end)) return true;
|
|
@@ -2,4 +2,10 @@ import { Ref } from 'vue';
|
|
|
2
2
|
import { GenerateConfig } from '../../generate';
|
|
3
3
|
import { InternalMode, Locale, PanelMode } from '../../interface';
|
|
4
4
|
export declare function offsetPanelDate<DateType = any>(generateConfig: GenerateConfig<DateType>, picker: InternalMode, date: DateType, offset: number): DateType;
|
|
5
|
-
export default function useRangePickerValue<DateType extends object, ValueType extends DateType[]>(generateConfig: Ref<GenerateConfig<DateType>>, locale: Ref<Locale>, calendarValue: Ref<ValueType>, modes: Ref<PanelMode[]>, open: Ref<boolean>,
|
|
5
|
+
export default function useRangePickerValue<DateType extends object, ValueType extends DateType[]>(generateConfig: Ref<GenerateConfig<DateType>>, locale: Ref<Locale>, calendarValue: Ref<ValueType>, modes: Ref<PanelMode[]>, open: Ref<boolean>,
|
|
6
|
+
/**
|
|
7
|
+
* Keep the current panels when focus moves between fields inside an open
|
|
8
|
+
* Picker, instead of jumping to the newly active field's value.
|
|
9
|
+
* Picker 打开状态下在 field 间移动焦点时保持当前面板,而不是跳到新 field 的值。
|
|
10
|
+
*/
|
|
11
|
+
preserveOnFieldChange: Ref<boolean>, activeIndex: Ref<number>, pickerMode: Ref<InternalMode>, multiplePanel: Ref<boolean>, defaultPickerValue?: Ref<any>, pickerValue?: Ref<any>, timeDefaultValue?: Ref<any>, onPickerValueChange?: ((dates: ValueType, info: any) => void) | Ref<((dates: ValueType, info: any) => void) | undefined>, minDate?: Ref<DateType | undefined>, maxDate?: Ref<DateType | undefined>): [currentIndexPickerValue: Ref<DateType>, setCurrentIndexPickerValue: (value: DateType, source?: 'reset' | 'panel') => void];
|
|
@@ -15,7 +15,7 @@ function offsetPanelDate(generateConfig, picker, date, offset) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
var EMPTY_LIST = [];
|
|
18
|
-
function useRangePickerValue(generateConfig, locale, calendarValue, modes, open, activeIndex, pickerMode, multiplePanel, defaultPickerValue = ref(EMPTY_LIST), pickerValue = ref(EMPTY_LIST), timeDefaultValue = ref(EMPTY_LIST), onPickerValueChange, minDate, maxDate) {
|
|
18
|
+
function useRangePickerValue(generateConfig, locale, calendarValue, modes, open, preserveOnFieldChange, activeIndex, pickerMode, multiplePanel, defaultPickerValue = ref(EMPTY_LIST), pickerValue = ref(EMPTY_LIST), timeDefaultValue = ref(EMPTY_LIST), onPickerValueChange, minDate, maxDate) {
|
|
19
19
|
const isTimePicker = computed(() => pickerMode.value === "time");
|
|
20
20
|
const mergedActiveIndex = computed(() => activeIndex.value || 0);
|
|
21
21
|
const getDefaultPickerValue = (index) => {
|
|
@@ -45,21 +45,15 @@ function useRangePickerValue(generateConfig, locale, calendarValue, modes, open,
|
|
|
45
45
|
mode: modes.value
|
|
46
46
|
});
|
|
47
47
|
};
|
|
48
|
+
const isSamePanel = (date1, date2) => {
|
|
49
|
+
if (pickerMode.value === "year") return Math.floor(generateConfig.value.getYear(date1) / 10) === Math.floor(generateConfig.value.getYear(date2) / 10);
|
|
50
|
+
const panelMode = pickerMode.value === "month" || pickerMode.value === "quarter" ? "year" : "month";
|
|
51
|
+
return isSame(generateConfig.value, locale.value, date1, date2, panelMode);
|
|
52
|
+
};
|
|
48
53
|
const getEndDatePickerValue = (startDate, endDate) => {
|
|
49
|
-
if (multiplePanel.value)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
datetime: "month",
|
|
53
|
-
week: "month",
|
|
54
|
-
month: "year",
|
|
55
|
-
quarter: "year"
|
|
56
|
-
}[pickerMode.value];
|
|
57
|
-
if (mode && !isSame(generateConfig.value, locale.value, startDate, endDate, mode)) return offsetPanelDate(generateConfig.value, pickerMode.value, endDate, -1);
|
|
58
|
-
if (pickerMode.value === "year" && startDate && endDate) {
|
|
59
|
-
if (Math.floor(generateConfig.value.getYear(startDate) / 10) !== Math.floor(generateConfig.value.getYear(endDate) / 10)) return offsetPanelDate(generateConfig.value, pickerMode.value, endDate, -1);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return endDate;
|
|
54
|
+
if (!multiplePanel.value || !startDate) return endDate;
|
|
55
|
+
const nextPanelDate = offsetPanelDate(generateConfig.value, pickerMode.value, startDate, 1);
|
|
56
|
+
return isSamePanel(startDate, endDate) || isSamePanel(nextPanelDate, endDate) ? startDate : offsetPanelDate(generateConfig.value, pickerMode.value, endDate, -1);
|
|
63
57
|
};
|
|
64
58
|
const prevActiveIndexRef = ref(null);
|
|
65
59
|
watch(() => [
|
|
@@ -70,7 +64,7 @@ function useRangePickerValue(generateConfig, locale, calendarValue, modes, open,
|
|
|
70
64
|
if (!open.value) return;
|
|
71
65
|
if (defaultPickerValue.value?.[mergedActiveIndex.value]) return;
|
|
72
66
|
let nextPickerValue = isTimePicker.value ? null : generateConfig.value.getNow();
|
|
73
|
-
if (prevActiveIndexRef.value !== null && prevActiveIndexRef.value !== mergedActiveIndex.value) nextPickerValue = [getStartPickerValue(true), getEndPickerValue(true)][mergedActiveIndex.value ^ 1];
|
|
67
|
+
if (preserveOnFieldChange.value && prevActiveIndexRef.value !== null && prevActiveIndexRef.value !== mergedActiveIndex.value) nextPickerValue = [getStartPickerValue(true), getEndPickerValue(true)][mergedActiveIndex.value ^ 1];
|
|
74
68
|
else if (calendarValue.value?.[mergedActiveIndex.value]) nextPickerValue = mergedActiveIndex.value === 0 ? calendarValue.value[0] : getEndDatePickerValue(calendarValue.value[0], calendarValue.value[1]);
|
|
75
69
|
else if (calendarValue.value?.[mergedActiveIndex.value ^ 1]) nextPickerValue = calendarValue.value[mergedActiveIndex.value ^ 1];
|
|
76
70
|
if (!nextPickerValue) return;
|
|
@@ -11,7 +11,7 @@ type UseInnerValueReturn<ValueType extends object[]> = readonly [
|
|
|
11
11
|
];
|
|
12
12
|
/**
|
|
13
13
|
* Control the internal `value` align with prop `value` and provide a temp `calendarValue` for ui.
|
|
14
|
-
*
|
|
14
|
+
* The caller controls the temporary `calendarValue` lifecycle through event handlers.
|
|
15
15
|
*/
|
|
16
16
|
export declare function useInnerValue<ValueType extends DateType[], DateType extends object = any>(generateConfig: Ref<GenerateConfig<DateType>>, locale: Ref<Locale>, formatList: Ref<FormatType[]>, rangeValue: Ref<boolean | undefined>, order: Ref<boolean | undefined>, defaultValue: Ref<ValueType | undefined>, value: Ref<ValueType | undefined>, onCalendarChange?: (dates: ValueType, dateStrings: [string, string], info: BaseInfo) => void, onOk?: (dates: ValueType) => void): UseInnerValueReturn<ValueType>;
|
|
17
17
|
export default function useRangeValue<ValueType extends DateType[], DateType extends object = any>(info: ComputedRef<{
|
|
@@ -21,8 +21,8 @@ export default function useRangeValue<ValueType extends DateType[], DateType ext
|
|
|
21
21
|
allowEmpty: boolean[];
|
|
22
22
|
order: boolean;
|
|
23
23
|
onChange?: (dates: ValueType | null, dateStrings: [string, string] | null) => void;
|
|
24
|
-
}>, mergedValue: Ref<ValueType> | ComputedRef<ValueType>, setInnerValue: (nextValue: ValueType) => void, getCalendarValue: () => ValueType, triggerCalendarChange: TriggerCalendarChange<ValueType>, disabled: Ref<boolean[]>, formatList: Ref<FormatType[]>,
|
|
24
|
+
}>, mergedValue: Ref<ValueType> | ComputedRef<ValueType>, setInnerValue: (nextValue: ValueType) => void, getCalendarValue: () => ValueType, triggerCalendarChange: TriggerCalendarChange<ValueType>, disabled: Ref<boolean[]>, formatList: Ref<FormatType[]>, isInvalidateDate: (date: DateType, info?: {
|
|
25
25
|
from?: DateType;
|
|
26
26
|
activeIndex: number;
|
|
27
|
-
}) => boolean): readonly [(index: number, needTriggerChange: boolean) => void, (nextValue?: ValueType) => boolean];
|
|
27
|
+
}) => boolean): readonly [(index: number, needTriggerChange: boolean) => void, (nextValue?: ValueType) => boolean, (index?: number) => void];
|
|
28
28
|
export {};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { fillIndex } from "../../utils/miscUtil.js";
|
|
2
2
|
import { formatValue, isSame, isSameTimestamp } from "../../utils/dateUtil.js";
|
|
3
|
-
import useLockEffect from "./useLockEffect.js";
|
|
4
3
|
import { computed, ref, shallowRef, watch } from "vue";
|
|
5
4
|
//#region src/PickerInput/hooks/useRangeValue.ts
|
|
6
5
|
var EMPTY_VALUE = [];
|
|
@@ -32,7 +31,7 @@ function orderDates(dates, generateConfig) {
|
|
|
32
31
|
}
|
|
33
32
|
/**
|
|
34
33
|
* Control the internal `value` align with prop `value` and provide a temp `calendarValue` for ui.
|
|
35
|
-
*
|
|
34
|
+
* The caller controls the temporary `calendarValue` lifecycle through event handlers.
|
|
36
35
|
*/
|
|
37
36
|
function useInnerValue(generateConfig, locale, formatList, rangeValue, order, defaultValue, value, onCalendarChange, onOk) {
|
|
38
37
|
const mergedValue = shallowRef((value.value === void 0 ? defaultValue.value : value.value) || EMPTY_VALUE);
|
|
@@ -74,7 +73,7 @@ function useInnerValue(generateConfig, locale, formatList, rangeValue, order, de
|
|
|
74
73
|
triggerOk
|
|
75
74
|
];
|
|
76
75
|
}
|
|
77
|
-
function useRangeValue(info, mergedValue, setInnerValue, getCalendarValue, triggerCalendarChange, disabled, formatList,
|
|
76
|
+
function useRangeValue(info, mergedValue, setInnerValue, getCalendarValue, triggerCalendarChange, disabled, formatList, isInvalidateDate) {
|
|
78
77
|
const orderOnChange = computed(() => disabled.value.some((d) => d) ? false : info.value.order);
|
|
79
78
|
const [getDateTexts, isSameDates] = useUtil(computed(() => info.value.generateConfig), computed(() => info.value.locale), formatList);
|
|
80
79
|
const submitValue = ref(mergedValue.value);
|
|
@@ -121,17 +120,20 @@ function useRangeValue(info, mergedValue, setInnerValue, getCalendarValue, trigg
|
|
|
121
120
|
setSubmitValue(nextSubmitValue);
|
|
122
121
|
if (needTriggerChange) triggerSubmit();
|
|
123
122
|
};
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
triggerCalendarChange(mergedValue.value);
|
|
130
|
-
submitValue.value = mergedValue.value;
|
|
131
|
-
}
|
|
123
|
+
const resetValue = (index) => {
|
|
124
|
+
if (index === void 0) {
|
|
125
|
+
triggerCalendarChange(mergedValue.value);
|
|
126
|
+
submitValue.value = mergedValue.value;
|
|
127
|
+
return;
|
|
132
128
|
}
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
triggerCalendarChange(fillIndex(getCalendarValue(), index, mergedValue.value[index]));
|
|
130
|
+
setSubmitValue(fillIndex(submitValue.value, index, mergedValue.value[index]));
|
|
131
|
+
};
|
|
132
|
+
return [
|
|
133
|
+
flushSubmit,
|
|
134
|
+
triggerSubmit,
|
|
135
|
+
resetValue
|
|
136
|
+
];
|
|
135
137
|
}
|
|
136
138
|
//#endregion
|
|
137
139
|
export { useRangeValue as default, useInnerValue };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ComputedRef, Ref } from 'vue';
|
|
2
|
+
/** Change source of a field. / Field 的变更来源。 */
|
|
3
|
+
export type RangeValueChangeSource = 'input' | 'remove' | 'keyboard-submit' | 'keyboard-submit-weak' | 'esc' | 'panel-intermediate' | 'panel-final' | 'popupClose' | 'field-switch' | 'confirm';
|
|
4
|
+
/** Resolved operation for one field interaction. / 一次 field 交互最终执行的操作。 */
|
|
5
|
+
export type RangeValueChangeAction = 'modify' | 'submitCurrent' | 'switchNext' | 'finish' | 'abort' | 'resetCurrent' | 'resetCurrentAndSwitchNext' | 'resetAll';
|
|
6
|
+
/** Receive a field interaction and its optional value. / 接收 field 交互及可选变更值。 */
|
|
7
|
+
export type TriggerChange<FieldValue> = (index: number, source: RangeValueChangeSource, value?: FieldValue) => void;
|
|
8
|
+
/** Read the latest temporary CalendarValue. / 读取最新的临时 CalendarValue。 */
|
|
9
|
+
export type GetCalendarValue<FieldValue> = () => readonly (FieldValue | null | undefined)[];
|
|
10
|
+
/** Update one field in CalendarValue. / 更新 CalendarValue 中的一个 field。 */
|
|
11
|
+
export type TriggerCalendarChange<FieldValue> = (index: number, value: FieldValue) => void;
|
|
12
|
+
/**
|
|
13
|
+
* Flush one field and optionally emit the final change.
|
|
14
|
+
* 提交一个 field,并按需触发最终 change。
|
|
15
|
+
*/
|
|
16
|
+
export type FlushSubmit = (index: number, needTriggerChange: boolean) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Reset one field, or all fields when index is omitted.
|
|
19
|
+
* 重置指定 field;未传 index 时重置全部 field。
|
|
20
|
+
*/
|
|
21
|
+
export type ResetValue = (index?: number) => void;
|
|
22
|
+
export type UseRangeValueChangeReturn<FieldValue> = [
|
|
23
|
+
currentIndex: Ref<number | null>,
|
|
24
|
+
activeIndex: ComputedRef<number>,
|
|
25
|
+
forceFocus: Ref<boolean>,
|
|
26
|
+
triggeredFields: ComputedRef<number[]>,
|
|
27
|
+
triggerChange: TriggerChange<FieldValue>,
|
|
28
|
+
reset: VoidFunction
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Coordinate CalendarValue updates, part submits and final submits for any
|
|
32
|
+
* number of fields.
|
|
33
|
+
* 统一管理任意数量 field 的 CalendarValue 更新、局部提交与最终提交。
|
|
34
|
+
*
|
|
35
|
+
* Flow / 流程:
|
|
36
|
+
* Every event is first resolved from `source`, `needConfirm`, `allowEmpty` and
|
|
37
|
+
* the field indexes to one action. State changes only happen while executing
|
|
38
|
+
* that action, so event sources never submit or reset values on their own.
|
|
39
|
+
* 每个事件先根据 `source`、`needConfirm`、`allowEmpty` 与 field index 得到唯一
|
|
40
|
+
* action。状态只在执行 action 时改变,事件来源本身不直接提交或重置值。
|
|
41
|
+
*
|
|
42
|
+
* Index transitions also expose whether focus must follow the next field.
|
|
43
|
+
* Confirm-like operations and a popup close following a panel operation
|
|
44
|
+
* switch focus strongly. A popup close following input, Tab and an actual
|
|
45
|
+
* field focus switch only update the expected index.
|
|
46
|
+
* index 切换还会同步给出是否必须跟随聚焦下一个 field。确认类操作,以及面板
|
|
47
|
+
* 操作后的 popup 关闭使用强切换;input 后的 popup 关闭、Tab 与真实 field
|
|
48
|
+
* 聚焦切换只更新预期 index。
|
|
49
|
+
*
|
|
50
|
+
* Source resolution / 事件解析:
|
|
51
|
+
*
|
|
52
|
+
* - `esc` always resolves to `resetAll`.
|
|
53
|
+
* `esc` 始终解析为 `resetAll`。
|
|
54
|
+
* - With no current field, `popupClose` resolves to `resetAll`; any other
|
|
55
|
+
* non-cancel event starts a new interaction from its field.
|
|
56
|
+
* 没有当前 field 时,`popupClose` 解析为 `resetAll`;其余非撤销事件从
|
|
57
|
+
* 对应 field 开始新一轮交互。
|
|
58
|
+
* - `field-switch` advances exactly one field in circular order. `needConfirm`
|
|
59
|
+
* locks an unconfirmed non-empty field unless it allows empty; an allow-empty
|
|
60
|
+
* field is reset before advancing.
|
|
61
|
+
* `field-switch` 只允许按循环顺序推进一个 field。`needConfirm` 会锁定未确认
|
|
62
|
+
* 且非空的 field;允许空值时先重置再推进。
|
|
63
|
+
* - Other sources must target the current field. `input` and
|
|
64
|
+
* `panel-intermediate` modify it; `remove` explicitly submits the removed
|
|
65
|
+
* value even when the field does not allow empty. `panel-final` advances only
|
|
66
|
+
* without confirmation; `keyboard-submit-weak` part-submits without
|
|
67
|
+
* advancing; `keyboard-submit` and `confirm` advance only when the field has
|
|
68
|
+
* a value or allows empty.
|
|
69
|
+
* 其余来源必须指向当前 field。`input` 与 `panel-intermediate` 只修改;
|
|
70
|
+
* `remove` 会明确提交删除后的值,即使 field 不允许为空;`panel-final`
|
|
71
|
+
* 仅在无需确认时推进;`keyboard-submit-weak` 只做局部提交而不推进;
|
|
72
|
+
* `keyboard-submit` 与 `confirm` 仅在有值或允许空值时推进。
|
|
73
|
+
* - `popupClose` finishes an untouched interaction. Without confirmation it
|
|
74
|
+
* submits a valid field; with confirmation it submits only after every field
|
|
75
|
+
* has participated, otherwise it resets all temporary values. A modified
|
|
76
|
+
* allow-empty field is reset before the final submit.
|
|
77
|
+
* `popupClose` 会直接结束未修改的交互。无需确认时提交有效 field;需要确认时
|
|
78
|
+
* 仅在所有 field 都参与过后提交,否则重置全部临时值。当前 field 已修改且
|
|
79
|
+
* 允许为空时,会先重置当前值再完成提交。
|
|
80
|
+
*
|
|
81
|
+
* Action execution / Action 执行:
|
|
82
|
+
*
|
|
83
|
+
* - `modify`: update or record the current CalendarValue.
|
|
84
|
+
* 更新或记录当前 CalendarValue。
|
|
85
|
+
* - `submitCurrent`: part-submit the current field without advancing.
|
|
86
|
+
* 局部提交当前 field,但不推进。
|
|
87
|
+
* - `switchNext`: submit the current field and advance to the next field.
|
|
88
|
+
* 提交当前 field 并推进到下一个 field。
|
|
89
|
+
* - `finish`: end an interaction in which no field was modified without
|
|
90
|
+
* resetting values. / 结束所有 field 均未修改的交互,不重置任何值。
|
|
91
|
+
* - `abort`: stop without changing any state.
|
|
92
|
+
* 直接短路,不改变任何状态。
|
|
93
|
+
* - `resetCurrent`: discard only the current field.
|
|
94
|
+
* 仅撤销当前 field。
|
|
95
|
+
* - `resetCurrentAndSwitchNext`: discard the current temporary value and
|
|
96
|
+
* advance without submitting. Revisiting a field starts a new round.
|
|
97
|
+
* 撤销当前临时值并直接推进,不触发提交;再次进入已访问 field 时开启新一轮。
|
|
98
|
+
* - `resetAll`: discard all temporary values and end the interaction.
|
|
99
|
+
* 撤销全部临时值并结束本轮交互。
|
|
100
|
+
*
|
|
101
|
+
* Vue note: upstream keeps `currentIndex` and `forceFocus` in `useSyncState`
|
|
102
|
+
* because React state is not readable synchronously inside the same handler.
|
|
103
|
+
* Vue refs already are, so plain refs carry both the render value and the
|
|
104
|
+
* synchronous read.
|
|
105
|
+
* Vue 说明:上游用 `useSyncState` 保存 `currentIndex` 与 `forceFocus`,是因为
|
|
106
|
+
* React state 无法在同一个 handler 内同步读回;Vue 的 ref 本身即可同步读写,
|
|
107
|
+
* 因此直接用 ref 同时承担渲染值与同步读取。
|
|
108
|
+
*/
|
|
109
|
+
export default function useRangeValueChange<FieldValue = unknown>(fieldCount: Ref<number> | ComputedRef<number>, needConfirm: Ref<boolean> | ComputedRef<boolean>, allowEmpty: Ref<readonly boolean[]> | ComputedRef<readonly boolean[]>, getCalendarValue: GetCalendarValue<FieldValue>, triggerCalendarChange: TriggerCalendarChange<FieldValue>, flushSubmit: FlushSubmit, resetValue: ResetValue): UseRangeValueChangeReturn<FieldValue>;
|