@opensig/opendesign
Version:
352 lines (351 loc) • 12.8 kB
JavaScript
import { defineComponent, mergeModels, useModel, toRefs, computed, ref, provide, watch, nextTick, createBlock, openBlock, unref, mergeProps, createSlots, withCtx, createVNode, withKeys, normalizeClass, createCommentVNode, renderSlot } from "vue";
import { useDebounceFn } from "@vueuse/core";
import dayjs from "dayjs";
import _sfc_main$1 from "../_components/in-box/InBox.vue.mjs";
import _sfc_main$2 from "../_components/in-input/InInput.vue.mjs";
import { isEmptySlot } from "../_utils/vue-utils.mjs";
import { useFormField } from "../_composables/use-form-field.mjs";
import "../_utils/is.mjs";
import "../hooks/use-theme.mjs";
import { useScreen } from "../hooks/use-screen.mjs";
import { useI18n } from "../locale/index.mjs";
import { IconTime } from "../_utils/icons.mjs";
import { useClickOutside } from "../date-picker/composables/use-click-outside.mjs";
import { timePickerProps } from "./types.mjs";
import { timePickerInjectKey } from "./provide.mjs";
import { useTimePickerOptions } from "./use-time-picker-options.mjs";
import { findNearestTime } from "./find-nearest-time.mjs";
import { isValidTimeUnit } from "./use-time-range-input-validation.mjs";
import _sfc_main$3 from "./components/TimePanel.vue.mjs";
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "OTimePicker",
props: /* @__PURE__ */ mergeModels(timePickerProps, {
"modelValue": { default: void 0 },
"modelModifiers": {}
}),
emits: /* @__PURE__ */ mergeModels(["change", "blur", "focus", "clear", "pressEnter"], ["update:modelValue"]),
setup(__props, { expose: __expose, emit: __emit }) {
const props = __props;
const emits = __emit;
const modelValue = useModel(__props, "modelValue");
const {
effectiveColor: color,
inputId,
isFocus,
onFocus: formOnFocus,
onBlur: formOnBlur,
onClear: formOnClear,
onPressEnter: formOnPressEnter,
notifyChange
} = useFormField(props, emits);
const propsRefs = toRefs(props);
const { format, hourStep, minuteStep, secondStep, disabled, readonly, noResponsive } = propsRefs;
const { t } = useI18n();
const { isPhonePad } = useScreen();
const isResponding = computed(() => !(noResponsive == null ? void 0 : noResponsive.value) && isPhonePad.value);
const inBoxRef = ref();
const inInputRef = ref();
const panelRef = ref();
const tempInputValue = ref();
const tempParsed = computed(() => {
var _a;
const [hour, minute, second] = ((_a = tempInputValue.value) == null ? void 0 : _a.split(":").map((v) => Number.parseInt(v))) ?? [];
return { hour, minute, second };
});
const tempSelectedHour = computed(() => Number.isNaN(tempParsed.value.hour) ? null : tempParsed.value.hour);
const tempSelectedMinute = computed(() => Number.isNaN(tempParsed.value.minute) ? null : tempParsed.value.minute);
const { hourOptions, minuteOptions, secondOptions, disabledHourOptions, disabledMinuteOptions, disabledSecondOptions } = useTimePickerOptions({
hourStep,
minuteStep,
secondStep,
disabledHours: propsRefs.disabledHours,
disabledMinutes: propsRefs.disabledMinutes,
disabledSeconds: propsRefs.disabledSeconds,
minTime: propsRefs.minTime,
maxTime: propsRefs.maxTime,
selectedHour: tempSelectedHour,
selectedMinute: tempSelectedMinute
});
provide(timePickerInjectKey, {
...propsRefs,
computedOptions: { hourOptions, minuteOptions, secondOptions, disabledHourOptions }
});
const isTempInputValueValid = computed(() => {
const { hour, minute, second } = tempParsed.value;
if (!isValidTimeUnit(hour, hourOptions.value, disabledHourOptions.value)) return false;
if (!isValidTimeUnit(minute, minuteOptions.value, disabledMinuteOptions.value)) return false;
if (!isValidTimeUnit(second, secondOptions.value, disabledSecondOptions.value)) return false;
return true;
});
watch(
modelValue,
(newValue) => {
tempInputValue.value = newValue ?? "";
},
{ immediate: true }
);
watch(isFocus, (newVal, oldVal) => {
var _a;
if (!newVal && oldVal) {
tempInputValue.value = modelValue.value ?? "";
if (!isResponding.value) {
(_a = panelRef.value) == null ? void 0 : _a.close();
}
}
});
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: () => {
formOnBlur();
},
disabled: isResponding
});
const getNearestAvailableTime = () => {
const now = dayjs();
return findNearestTime({
hourOptions: hourOptions.value,
minuteOptions: minuteOptions.value,
secondOptions: secondOptions.value,
minTime: props.minTime,
maxTime: props.maxTime,
disabledHours: props.disabledHours,
disabledMinutes: props.disabledMinutes,
disabledSeconds: props.disabledSeconds,
target: { hour: now.hour(), minute: now.minute(), second: now.second() },
format: format.value
});
};
let skipOpenPanel = false;
const onFocus = (e) => {
var _a;
if (readonly.value) return;
if (!skipOpenPanel) {
let valueToOpen = tempInputValue.value;
if (!valueToOpen) {
const nearest = getNearestAvailableTime();
if (nearest) {
tempInputValue.value = nearest;
valueToOpen = nearest;
}
}
(_a = panelRef.value) == null ? void 0 : _a.open(valueToOpen);
}
skipOpenPanel = false;
if (isFocus.value) return;
formOnFocus(e);
};
let prevValue = modelValue.value;
let internalChangePending = false;
watch(modelValue, (_, old) => {
if (internalChangePending) {
internalChangePending = false;
} else {
prevValue = old;
}
});
const handleChange = () => {
var _a;
internalChangePending = true;
modelValue.value = isTempInputValueValid.value ? tempInputValue.value : (_a = panelRef.value) == null ? void 0 : _a.getValue();
emits("change", modelValue.value, prevValue);
prevValue = modelValue.value;
notifyChange();
};
const handleInput = useDebounceFn(async () => {
var _a;
await nextTick();
if (isTempInputValueValid.value) {
(_a = panelRef.value) == null ? void 0 : _a.setValue(tempInputValue.value);
}
});
const blurAndClose = () => {
var _a, _b;
(_a = inInputRef.value) == null ? void 0 : _a.blur();
(_b = panelRef.value) == null ? void 0 : _b.close();
formOnBlur();
};
const cancelEdit = () => {
tempInputValue.value = modelValue.value ?? "";
blurAndClose();
};
const handlePanelChange = (newVal) => {
tempInputValue.value = newVal;
};
const onPressEnter = () => {
if (!isTempInputValueValid.value) {
cancelEdit();
return;
}
handleChange();
blurAndClose();
formOnPressEnter();
};
const handleConfirm = () => {
handleChange();
formOnBlur();
formOnPressEnter();
};
const handleCancel = () => {
cancelEdit();
};
const onClear = (e) => {
e == null ? void 0 : e.stopPropagation();
const oldVal = prevValue;
internalChangePending = true;
modelValue.value = void 0;
tempInputValue.value = "";
prevValue = void 0;
emits("change", void 0, oldVal);
formOnClear(e);
notifyChange();
blurAndClose();
};
__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 = inInputRef.value) == null ? void 0 : _a.focus();
},
/**
* @zh-CN 使输入框失去焦点,校验合法则应用当前值,否则回滚
* @en-US Blur the input. Applies the current value if valid, otherwise rolls back.
*/
blur: () => {
if (isTempInputValueValid.value) {
handleChange();
blurAndClose();
} else {
cancelEdit();
}
},
/**
* @zh-CN 清除输入值
* @en-US Clear the input value
*/
clear: () => {
var _a;
return (_a = inInputRef.value) == null ? void 0 : _a.clear();
},
/**
* @zh-CN 获取输入框 DOM 元素
* @en-US Get the input DOM element
*/
inputEl: () => {
var _a;
return (_a = inInputRef.value) == null ? void 0 : _a.inputEl;
}
});
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: unref(isFocus)
}, { class: ["o-time-picker", "o-input"] }),
createSlots({
default: withCtx(() => {
var _a;
return [
createVNode(unref(_sfc_main$2), {
ref_key: "inInputRef",
ref: inInputRef,
modelValue: tempInputValue.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => tempInputValue.value = $event),
class: normalizeClass(["o-input-wrap", { "o-input-wrap-focused": unref(isFocus), "o-input-wrap-touch": isResponding.value }]),
disabled: unref(disabled),
clearable: props.clearable && !!tempInputValue.value,
placeholder: props.placeholder ?? unref(t)("timePicker.placeholder"),
"input-id": unref(inputId),
"max-length": ((_a = unref(format)) == null ? void 0 : _a.length) ?? 8,
"input-on-outlimit": false,
"show-length": "never",
readonly: unref(readonly),
"no-keyboard": "",
onKeydown: withKeys(handleCancel, ["esc"]),
onInput: unref(handleInput),
onFocus,
onClear,
onPressEnter
}, createSlots({
extra: withCtx(() => {
var _a2;
return [
!unref(disabled) && !unref(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: handlePanelChange,
onCancel: handleCancel,
onConfirm: handleConfirm
}, {
shortcut: withCtx(({ setValue: panelSetValue, emitChange }) => [
renderSlot(_ctx.$slots, "shortcut", {
setValue: panelSetValue,
emitChange
})
]),
_: 3
/* FORWARDED */
}, 8, ["target", "option-title"])) : createCommentVNode("v-if", true)
];
}),
_: 2
/* DYNAMIC */
}, [
!isResponding.value || !tempInputValue.value ? {
name: "suffix",
fn: withCtx(() => [
createVNode(unref(IconTime))
]),
key: "0"
} : void 0
]), 1032, ["modelValue", "class", "disabled", "clearable", "placeholder", "input-id", "max-length", "readonly", "onInput"])
];
}),
_: 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
/* FULL_PROPS, DYNAMIC_SLOTS */
);
};
}
});
export {
_sfc_main as default
};