@opensig/opendesign
Version:
472 lines (471 loc) • 17.1 kB
JavaScript
import { defineComponent, mergeModels, useModel, ref, watch, computed, toRef, nextTick, provide, toRefs, createBlock, openBlock, unref, mergeProps, createSlots, withCtx, createVNode, createElementVNode, normalizeClass, createCommentVNode, renderSlot, withModifiers, createElementBlock } from "vue";
import { useDebounceFn } from "@vueuse/core";
import { useI18n } from "../locale/index.mjs";
import { IconTime, IconClose } from "../_utils/icons.mjs";
import _sfc_main$1 from "../_components/in-box/InBox.vue.mjs";
import _sfc_main$2 from "../_components/in-input/InInput.vue.mjs";
import { useFormField } from "../_composables/use-form-field.mjs";
import { isEmptySlot } from "../_utils/vue-utils.mjs";
import { ArrowLeft, ArrowRight, Tab } from "../_utils/keycode.mjs";
import { useClickOutside } from "../date-picker/composables/use-click-outside.mjs";
import { timeRangePickerProps } from "./types.mjs";
import { timePickerInjectKey } from "./provide.mjs";
import _sfc_main$3 from "./components/TimeRangePanel.vue.mjs";
import { useTimeRangeConstraints } from "./use-time-range-constraints.mjs";
import { useTimeRangeInputValidation } from "./use-time-range-input-validation.mjs";
const _hoisted_1 = { class: "o_input-suffix-icon" };
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "OTimeRangePicker",
props: /* @__PURE__ */ mergeModels(timeRangePickerProps, {
"start": { default: void 0, required: true },
"startModifiers": {},
"end": { default: void 0, required: true },
"endModifiers": {}
}),
emits: /* @__PURE__ */ mergeModels(["change", "blur", "focus", "clear", "pressEnter"], ["update:start", "update:end"]),
setup(__props, { expose: __expose, emit: __emit }) {
const props = __props;
const emits = __emit;
const start = useModel(__props, "start");
const end = useModel(__props, "end");
const tempStart = ref(start.value ?? "");
watch(start, (newVal) => tempStart.value = newVal ?? "");
const tempEnd = ref(end.value ?? "");
watch(end, (newVal) => tempEnd.value = newVal ?? "");
const startFocused = ref(false);
const endFocused = ref(false);
const focused = computed(() => startFocused.value || endFocused.value);
watch(focused, (newVal, oldVal) => {
if (!newVal && oldVal) {
emits("blur");
}
});
const isClearable = computed(() => props.clearable && !props.disabled && !props.readonly && (!!tempStart.value || !!tempEnd.value));
const { t } = useI18n();
const inBoxRef = ref();
const startInputRef = ref();
const endInputRef = ref();
const panelRef = ref();
const { maxStartTime, minEndTime } = useTimeRangeConstraints({
startTime: tempStart,
endTime: tempEnd,
format: toRef(props, "format"),
hourStep: toRef(props, "hourStep"),
minuteStep: toRef(props, "minuteStep"),
secondStep: toRef(props, "secondStep")
});
const { isStartValid, isEndValid } = useTimeRangeInputValidation({
tempStart,
tempEnd,
format: toRef(props, "format"),
hourStep: toRef(props, "hourStep"),
minuteStep: toRef(props, "minuteStep"),
secondStep: toRef(props, "secondStep"),
minTime: toRef(props, "minTime"),
maxTime: toRef(props, "maxTime"),
maxStartTime,
minEndTime,
disabledHours: toRef(props, "disabledHours"),
disabledMinutes: toRef(props, "disabledMinutes"),
disabledSeconds: toRef(props, "disabledSeconds")
});
watch(
tempStart,
useDebounceFn(async () => {
var _a;
await nextTick();
if (isStartValid.value && tempStart.value) (_a = panelRef.value) == null ? void 0 : _a.setStartValue(tempStart.value);
}, 300)
);
watch(
tempEnd,
useDebounceFn(async () => {
var _a;
await nextTick();
if (isEndValid.value && tempEnd.value) (_a = panelRef.value) == null ? void 0 : _a.setEndValue(tempEnd.value);
}, 300)
);
const { effectiveColor: color, inputId: startInputId, notifyChange } = useFormField(props, emits);
let skipOpenPanel = false;
const openPanel = () => {
var _a;
if (props.disabled || props.readonly) return;
(_a = panelRef.value) == null ? void 0 : _a.open(tempStart.value || void 0, tempEnd.value || void 0);
};
const blurAndClose = () => {
var _a, _b, _c;
(_a = panelRef.value) == null ? void 0 : _a.close();
startFocused.value = false;
endFocused.value = false;
(_b = startInputRef.value) == null ? void 0 : _b.blur();
(_c = endInputRef.value) == null ? void 0 : _c.blur();
};
const cancelEdit = () => {
tempStart.value = start.value ?? "";
tempEnd.value = end.value ?? "";
blurAndClose();
};
useClickOutside({
targets: [() => {
var _a;
return (_a = inBoxRef.value) == null ? void 0 : _a.$el;
}, () => {
var _a;
return (_a = panelRef.value) == null ? void 0 : _a.getPopupEl();
}],
onOutside: cancelEdit
});
const onFocus = (e, rangeType) => {
if (!focused.value) {
emits("focus", e);
}
if (rangeType === "start") {
startFocused.value = true;
} else {
endFocused.value = true;
}
if (!skipOpenPanel) openPanel();
skipOpenPanel = false;
};
let prevRangeValue = { start: start.value, end: end.value };
let internalRangeChangePending = false;
watch([start, end], ([_newStart, _newEnd], [oldStart, oldEnd]) => {
if (internalRangeChangePending) {
internalRangeChangePending = false;
} else {
prevRangeValue = { start: oldStart, end: oldEnd };
}
});
const onChange = () => {
const empty = !tempStart.value && !tempEnd.value;
const full = tempStart.value && tempEnd.value;
if (empty || full) {
const oldVal = prevRangeValue;
internalRangeChangePending = true;
start.value = tempStart.value;
end.value = tempEnd.value;
prevRangeValue = { start: start.value, end: end.value };
emits("change", prevRangeValue, oldVal);
notifyChange();
}
};
const confirmAndClose = () => {
onChange();
blurAndClose();
emits("pressEnter");
};
const onPanelChange = (startVal, endVal) => {
tempStart.value = startVal ?? "";
tempEnd.value = endVal ?? "";
};
const onPanelConfirm = (startVal, endVal) => {
tempStart.value = startVal ?? "";
tempEnd.value = endVal ?? "";
onChange();
startFocused.value = false;
endFocused.value = false;
};
const switchFocusTo = (targetRange) => {
const targetRef = targetRange === "start" ? startInputRef : endInputRef;
startFocused.value = targetRange === "start";
endFocused.value = targetRange === "end";
requestAnimationFrame(() => {
var _a;
return (_a = targetRef.value) == null ? void 0 : _a.focus();
});
};
const isCursorAtEdge = (inputRef, position) => {
var _a;
const inputEl = (_a = inputRef.value) == null ? void 0 : _a.inputEl;
if (!inputEl) return false;
const { selectionStart, selectionEnd, value } = inputEl;
return selectionStart === selectionEnd && (position === "start" ? selectionStart === 0 : selectionStart === value.length);
};
const handleTab = (e, rangeType) => {
const toEnd = rangeType === "start" && !e.shiftKey;
const toStart = rangeType === "end" && e.shiftKey;
if (toEnd || toStart) {
e.preventDefault();
switchFocusTo(
toEnd ? "end" : "start"
/* s */
);
} else {
const bothValid = isStartValid.value && isEndValid.value;
if (bothValid) confirmAndClose();
else cancelEdit();
}
};
const handleArrowRight = (e, rangeType) => {
if (rangeType === "start" && isCursorAtEdge(startInputRef, "end")) {
e.preventDefault();
switchFocusTo(
"end"
/* e */
);
}
};
const handleArrowLeft = (e, rangeType) => {
if (rangeType === "end" && isCursorAtEdge(endInputRef, "start")) {
e.preventDefault();
switchFocusTo(
"start"
/* s */
);
}
};
const keyHandlers = {
[Tab.key]: handleTab,
[ArrowRight.key]: handleArrowRight,
[ArrowLeft.key]: handleArrowLeft
};
const onKeydown = (e, rangeType) => {
var _a;
(_a = keyHandlers[e.key]) == null ? void 0 : _a.call(keyHandlers, e, rangeType);
};
const confirmOrSwitchFocus = (rangeType) => {
const bothValid = isStartValid.value && isEndValid.value;
if (rangeType === "start") {
if (!tempEnd.value) {
switchFocusTo(
"end"
/* e */
);
} else if (bothValid) {
confirmAndClose();
}
} else {
if (!tempStart.value) {
switchFocusTo(
"start"
/* s */
);
} else if (bothValid) {
confirmAndClose();
}
}
};
const onPressEnter = (rangeType) => {
if (rangeType === "start" && !isStartValid.value) {
cancelEdit();
return;
}
if (rangeType === "end" && !isEndValid.value) {
cancelEdit();
return;
}
confirmOrSwitchFocus(rangeType);
};
const onClear = () => {
const oldVal = prevRangeValue;
internalRangeChangePending = true;
start.value = void 0;
end.value = void 0;
tempStart.value = "";
tempEnd.value = "";
prevRangeValue = { start: void 0, end: void 0 };
emits("clear");
emits("change", prevRangeValue, oldVal);
notifyChange();
blurAndClose();
};
provide(timePickerInjectKey, {
...toRefs(props),
isRange: ref(true)
});
__expose({
/**
* @zh-CN 使输入框获取焦点,open 为 false 时仅聚焦不打开面板
* @en-US Focus the input. Pass false to focus without opening the panel.
*/
focus: (open = true) => {
var _a;
if (!open) skipOpenPanel = true;
(_a = startInputRef.value) == null ? void 0 : _a.focus();
},
/**
* @zh-CN 使输入框失去焦点,校验合法则应用当前值,否则回滚
* @en-US Blur the input. Applies the current value if valid, otherwise rolls back.
*/
blur: () => {
if (isStartValid.value && isEndValid.value) {
confirmAndClose();
} else {
cancelEdit();
}
},
/**
* @zh-CN 清除输入值
* @en-US Clear the input value
*/
clear: () => onClear()
});
return (_ctx, _cache) => {
return openBlock(), createBlock(unref(_sfc_main$1), mergeProps({
ref_key: "inBoxRef",
ref: inBoxRef
}, {
size: props.size,
variant: props.variant,
color: unref(color),
disabled: props.disabled,
readonly: props.readonly,
round: props.round,
focused: !!focused.value
}, {
class: ["o-time-picker", "o-time-range-picker", { "o_input-clearable": isClearable.value }, "o-input"]
}), createSlots({
default: withCtx(() => {
var _a, _b;
return [
createVNode(unref(_sfc_main$2), {
ref_key: "startInputRef",
ref: startInputRef,
modelValue: tempStart.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => tempStart.value = $event),
class: normalizeClass(["o-input-wrap o-range-start-input-wrap", { "o-input-wrap-focused": startFocused.value }]),
disabled: _ctx.disabled,
readonly: _ctx.readonly,
"input-id": unref(startInputId),
placeholder: props.placeholderStart ?? unref(t)("timePicker.startTime"),
"max-length": ((_a = props.format) == null ? void 0 : _a.length) ?? 8,
"input-on-outlimit": false,
"show-length": "never",
"no-keyboard": "",
onFocus: _cache[1] || (_cache[1] = (e) => onFocus(
e,
"start"
/* s */
)),
onKeydown: _cache[2] || (_cache[2] = (e) => onKeydown(
e,
"start"
/* s */
)),
onPressEnter: _cache[3] || (_cache[3] = () => onPressEnter(
"start"
/* s */
))
}, {
extra: withCtx(() => {
var _a2;
return [
!_ctx.disabled && !_ctx.readonly ? (openBlock(), createBlock(_sfc_main$3, {
key: 0,
ref_key: "panelRef",
ref: panelRef,
target: (_a2 = inBoxRef.value) == null ? void 0 : _a2.$el,
"option-title": props.optionTitle,
onChange: onPanelChange,
onConfirm: onPanelConfirm
}, createSlots({
_: 2
/* DYNAMIC */
}, [
!unref(isEmptySlot)(_ctx.$slots.shortcut) ? {
name: "shortcut",
fn: withCtx(({ setValue, emitChange }) => [
renderSlot(_ctx.$slots, "shortcut", {
setValue,
emitChange
})
]),
key: "0"
} : void 0
]), 1032, ["target", "option-title"])) : createCommentVNode("v-if", true)
];
}),
_: 3
/* FORWARDED */
}, 8, ["modelValue", "class", "disabled", "readonly", "input-id", "placeholder", "max-length"]),
_cache[10] || (_cache[10] = createElementVNode(
"div",
{ class: "o-time-range-picker-divider" },
"-",
-1
/* CACHED */
)),
createVNode(unref(_sfc_main$2), {
ref_key: "endInputRef",
ref: endInputRef,
modelValue: tempEnd.value,
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => tempEnd.value = $event),
class: normalizeClass(["o-input-wrap o-range-end-input-wrap", { "o-input-wrap-focused": endFocused.value }]),
disabled: _ctx.disabled,
readonly: _ctx.readonly,
placeholder: props.placeholderEnd ?? unref(t)("timePicker.endTime"),
"max-length": ((_b = props.format) == null ? void 0 : _b.length) ?? 8,
"input-on-outlimit": false,
"show-length": "never",
"no-keyboard": "",
onFocus: _cache[5] || (_cache[5] = (e) => onFocus(
e,
"end"
/* e */
)),
onKeydown: _cache[6] || (_cache[6] = (e) => onKeydown(
e,
"end"
/* e */
)),
onPressEnter: _cache[7] || (_cache[7] = () => onPressEnter(
"end"
/* e */
))
}, null, 8, ["modelValue", "class", "disabled", "readonly", "placeholder", "max-length"]),
createElementVNode(
"div",
{
class: "o_input-suffix",
onMousedown: _cache[9] || (_cache[9] = withModifiers(() => {
}, ["prevent"]))
},
[
createElementVNode("span", _hoisted_1, [
createVNode(unref(IconTime))
]),
isClearable.value ? (openBlock(), createElementBlock(
"div",
{
key: 0,
class: "o_input-clear",
onClick: onClear,
onMousedown: _cache[8] || (_cache[8] = withModifiers(() => {
}, ["prevent"]))
},
[
createVNode(unref(IconClose), { class: "o_input-clear-icon" })
],
32
/* NEED_HYDRATION */
)) : createCommentVNode("v-if", true)
],
32
/* NEED_HYDRATION */
)
];
}),
_: 2
/* DYNAMIC */
}, [
!unref(isEmptySlot)(_ctx.$slots.prepend) ? {
name: "prepend",
fn: withCtx(() => [
renderSlot(_ctx.$slots, "prepend")
]),
key: "0"
} : void 0,
!unref(isEmptySlot)(_ctx.$slots.append) ? {
name: "append",
fn: withCtx(() => [
renderSlot(_ctx.$slots, "append")
]),
key: "1"
} : void 0
]), 1040, ["class"]);
};
}
});
export {
_sfc_main as default
};