vue3-month-range-picker
Version:
這是一個非常簡易而且只有選擇月份範圍的 Vue3 組件,支持更換月份格式。
165 lines (164 loc) • 7.48 kB
JavaScript
!function(){"use strict";try{if("undefined"!=typeof document){var d=document.createElement("style");d.appendChild(document.createTextNode(".month-range-picker[data-v-cad7382f]{position:relative}.month-range-picker .input-field input[data-v-cad7382f]{padding:8px;border:1px solid #ddd;border-radius:5px;cursor:pointer;width:100%;background:#fff;text-align:left}.months-panel[data-v-cad7382f]{max-width:250px;display:flex;flex-wrap:wrap;margin-top:5px;border:1px solid #ddd;padding:12px;border-radius:5px;position:absolute;background:#fff;z-index:10;box-shadow:0 2px 5px #0003;color:#000}.month[data-v-cad7382f]{width:calc(33.3333333333% - 20px);text-align:center;padding:4px 10px;margin:0;cursor:pointer;border:0}.month.start-month[data-v-cad7382f]{background-color:#007bff;color:#fff;border-top-left-radius:4px;border-bottom-left-radius:4px}.month.end-month[data-v-cad7382f]{background-color:#007bff;color:#fff;border-top-right-radius:4px;border-bottom-right-radius:4px}.month.in-range[data-v-cad7382f],.month.hover-range[data-v-cad7382f]{background-color:#e0efff}.month[data-v-cad7382f]:hover{background-color:#007bff;color:#fff;border-radius:4px}")),document.head.appendChild(d)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}}();
import { defineComponent, ref, watch, toRefs, onMounted, onBeforeUnmount, openBlock, createElementBlock, withModifiers, createElementVNode, withDirectives, isRef, vModelText, unref, Fragment, renderList, normalizeClass, toDisplayString, createCommentVNode } from "vue";
const _hoisted_1 = { class: "input-field" };
const _hoisted_2 = ["placeholder"];
const _hoisted_3 = {
key: 0,
class: "months-panel"
};
const _hoisted_4 = ["onClick", "onMouseover"];
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "MonthRangePicker",
props: {
placeholder: { type: String, default: "" },
startMonth: { type: String, default: "" },
endMonth: { type: String, default: "" },
monthFormat: {
type: Array,
default: () => ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
}
},
emits: ["update:startMonth", "update:endMonth"],
setup(__props, { emit: __emit }) {
const emit = __emit;
const props = __props;
let isClickingButton = ref(false);
let isPanelOpen = ref(false);
let isHovering = ref(false);
let hoverMonth = ref(null);
let displayText = ref("");
let months = ref([
{ id: "01", name: "01" },
{ id: "02", name: "02" },
{ id: "03", name: "03" },
{ id: "04", name: "04" },
{ id: "05", name: "05" },
{ id: "06", name: "06" },
{ id: "07", name: "07" },
{ id: "08", name: "08" },
{ id: "09", name: "09" },
{ id: "10", name: "10" },
{ id: "11", name: "11" },
{ id: "12", name: "12" }
]);
const updateDisplayText = () => {
const startMonthObj = months.value.find((month) => month.id === props.startMonth);
const endMonthObj = months.value.find((month) => month.id === props.endMonth);
if (startMonthObj && endMonthObj) {
displayText.value = `${startMonthObj.name} - ${endMonthObj.name}`;
} else if (startMonthObj) {
displayText.value = startMonthObj.name;
} else {
displayText.value = props.placeholder;
}
};
const openPanel = () => {
isClickingButton.value = true;
isPanelOpen.value = true;
setTimeout(() => isClickingButton.value = false, 200);
document.addEventListener("click", handleGlobalClick);
};
const handleGlobalClick = () => {
setTimeout(() => {
if (!isClickingButton.value) {
isPanelOpen.value = false;
}
}, 200);
};
const isInRange = (month) => props.startMonth && props.endMonth && month > props.startMonth && month < props.endMonth;
const isInHoverRange = (month) => props.startMonth && !props.endMonth && month > props.startMonth && month <= hoverMonth.value;
const selectMonth = (month) => {
isClickingButton.value = true;
setTimeout(() => isClickingButton.value = false, 200);
if (!props.startMonth || props.startMonth && props.endMonth) {
emit("update:startMonth", month.id);
emit("update:endMonth", void 0);
} else if (!props.endMonth) {
if (month.id >= props.startMonth) {
emit("update:endMonth", month.id);
} else {
emit("update:startMonth", month.id);
emit("update:endMonth", props.startMonth);
}
closePanel();
}
};
const closePanel = () => {
isPanelOpen.value = false;
removeGlobalClickListener();
};
const removeGlobalClickListener = () => {
document.removeEventListener("click", handleGlobalClick);
};
const handleHover = (month) => {
isHovering.value = true;
hoverMonth.value = month;
};
const clearHover = () => {
isHovering.value = false;
hoverMonth.value = null;
};
watch(() => props.monthFormat, () => {
months.value = months.value.map((m, index) => ({
...m,
name: String(props.monthFormat[index] || m.name)
// 確保 name 為 string
}));
}, { immediate: true });
const { startMonth, endMonth } = toRefs(props);
watch([startMonth, endMonth], ([newStart, newEnd], [oldStart, oldEnd]) => updateDisplayText());
onMounted(() => updateDisplayText());
onBeforeUnmount(() => {
document.removeEventListener("click", handleGlobalClick);
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", {
class: "month-range-picker",
onClick: _cache[1] || (_cache[1] = withModifiers(() => {
}, ["stop"]))
}, [
createElementVNode("div", _hoisted_1, [
withDirectives(createElementVNode("input", {
type: "button",
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(displayText) ? displayText.value = $event : displayText = $event),
placeholder: __props.placeholder,
onClick: openPanel,
onBlur: handleGlobalClick
}, null, 40, _hoisted_2), [
[vModelText, unref(displayText)]
])
]),
unref(isPanelOpen) ? (openBlock(), createElementBlock("div", _hoisted_3, [
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(months), (month) => {
return openBlock(), createElementBlock("div", {
key: month.id,
class: normalizeClass([
"month",
{
"start-month": month.id === props.startMonth,
"end-month": month.id === props.endMonth,
"in-range": isInRange(month.id),
"hover-range": unref(isHovering) && isInHoverRange(month.id)
}
]),
onClick: ($event) => selectMonth(month),
onMouseover: ($event) => handleHover(month.id),
onMouseleave: clearHover
}, toDisplayString(month.name), 43, _hoisted_4);
}), 128))
])) : createCommentVNode("", true)
]);
};
}
});
const _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const MonthRangePicker = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-cad7382f"]]);
export {
MonthRangePicker as default
};