UNPKG

@opentiny/vue-renderless

Version:

An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.

510 lines (509 loc) 19.4 kB
import "../chunk-G2ADBYYC.js"; import { lastMonth, nextMonth } from "@opentiny/utils"; import { getDirection } from "@opentiny/utils"; const normalConfig = (config, state) => { let { disabled, holiday, workday, mark } = config || {}; const weekFirst = getWeekFirst(state, { config }); disabled = typeof disabled === "function" ? disabled : () => false; holiday = typeof holiday === "function" ? holiday : () => true; workday = typeof workday === "function" ? workday : () => true; mark = typeof mark === "function" ? mark : () => ""; return { weekFirst, disabled, holiday, workday, mark }; }; const normalDate = (date) => { date = !date ? /* @__PURE__ */ new Date() : !(date instanceof Date) ? new Date(date) : date; date = date.toString() === "Invalid Date" ? /* @__PURE__ */ new Date() : date; return date; }; const splitDate = (date) => { const today = /* @__PURE__ */ new Date(); const todayFullYear = today.getFullYear(); const todayMonth = today.getMonth() + 1; const todayDate = today.getDate(); const currentFullYear = date.getFullYear(); const currentMonth = date.getMonth() + 1; const currentDate = date.getDate(); return { todayFullYear, todayMonth, todayDate, currentFullYear, currentMonth, currentDate }; }; const getBuildDay = (args) => (year, month, date) => { const { pad0: pad02, isCurrent, holiday, workday, mark, type } = args; const { currentDate, isToday, disabled, dayOfWeek } = args; const dateStr = `${year}-${pad02(month)}-${pad02(date)}`; const d = new Date(dateStr); const isCurMon = isCurrent(year, month); const isSat = d.getDay() === dayOfWeek - 1; const isSun = d.getDay() === 0; let isWorkday, isHoliday; if (isSat || isSun) { isHoliday = holiday(d); isWorkday = !isHoliday; } else { isWorkday = workday(d); isHoliday = !isWorkday; } const markMsg = mark(d); const marked = !!markMsg; const day = { dateStr, marked, markMsg, isWorkday, isHoliday, isSat, isSun, isCurMon }; day.isCur = isCurMon && date === currentDate && type === "cur"; day.isToday = isToday(year, month, date); day.disabled = disabled(d); day.dateArr = [year, month, date]; day.type = type; return day; }; const getPadCalendarDays = ({ calendarDays, buildDay }) => (flag, count, cur) => { const sign = flag === "s" ? -1 : 1; Array.from({ length: count }).forEach((v, i) => { const d = new Date(cur.getTime() + (i + 1) * sign * 864e5); const year = d.getFullYear(); const month = d.getMonth() + 1; const date = d.getDate(); calendarDays[flag === "s" ? "unshift" : "push"](buildDay(year, month, date)); }); }; const equalArr = (arr1, arr2) => Array.isArray(arr1) && Array.isArray(arr2) && arr1.join(",") === arr2.join(","); const setDayRow = ({ calendarDays, state }) => calendarDays.forEach((day, i) => { day.row = Math.floor(i / state.dayOfWeek); if (day.isCur) { state.activeRow = day.row; state.activeDate = day.dateStr; } }); const pad0 = (str, n = 2) => String(str).padStart(n, "0"); const getDate = (state, type, date) => { let resDate = /* @__PURE__ */ new Date(); if (type === "last") { const { year, month } = lastMonth(state.currentYear, state.currentMonth); resDate = /* @__PURE__ */ new Date(`${year}-${month}-01`); } else if (type === "next") { const { year, month } = nextMonth(state.currentYear, state.currentMonth); resDate = /* @__PURE__ */ new Date(`${year}-${month}-01`); } else { resDate = normalDate(date); } return resDate; }; const getCalendarDays = (state) => (date, config, type = "cur") => { date = getDate(state, type, date); let { weekFirst, disabled, holiday, workday, mark } = normalConfig(config, state); const { todayFullYear, todayMonth, todayDate, currentFullYear, currentMonth, currentDate } = splitDate(date); const isToday = (year, month, date2) => year === todayFullYear && month === todayMonth && date2 === todayDate; const isCurrent = (year, month) => year === currentFullYear && month === currentMonth; const february = currentFullYear % 4 === 0 && currentFullYear % 100 !== 0 || currentFullYear % 400 === 0 ? 29 : 28; const monthDays = [-1, 31, february, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; const f = (arr) => arr[0]; const l = (arr) => arr[arr.length - 1]; const { dayOfWeek } = state; const args = { pad0, isCurrent, currentFullYear, currentMonth, holiday, workday, mark, type }; Object.assign(args, { currentDate, isToday, disabled, dayOfWeek }); const buildDay = getBuildDay(args); const calendarDays = Array.from({ length: monthDays[currentMonth] }).map( (v, i) => buildDay(currentFullYear, currentMonth, i + 1) ); const weekDays = Array.from({ length: dayOfWeek }).map((v, i) => (weekFirst + i) % dayOfWeek); const monthDaysFirst = new Date(f(calendarDays).dateStr).getDay(); const monthDaysLast = new Date(l(calendarDays).dateStr).getDay(); const padCalendarDays = getPadCalendarDays({ calendarDays, buildDay }); let count = monthDaysFirst !== f(weekDays) ? weekDays.indexOf(monthDaysFirst) : dayOfWeek; padCalendarDays("s", count, new Date(f(calendarDays).dateStr)); count = monthDaysLast !== l(weekDays) ? dayOfWeek - 1 - weekDays.indexOf(monthDaysLast) : dayOfWeek; padCalendarDays("e", count, new Date(l(calendarDays).dateStr)); setCalendarDays(state, type, calendarDays); if (type !== "cur") return; setDayRow({ calendarDays, state }); if (!equalArr(state.weekDays, weekDays)) { state.weekDays = weekDays; } state.currentYear = currentFullYear; state.currentMonth = currentMonth; state.currentDate = currentDate; state.cascaderCurrent = [currentFullYear, currentMonth]; }; const setCalendarDays = (state, type, calendarDays) => { if (type === "last") { state.lastCalendarDays = calendarDays; } else if (type === "next") { state.nextCalendarDays = calendarDays; } else { state.calendarDays = calendarDays; } }; const getPrevWeek = ({ props, emit, state, api }) => () => { let newPrevDate = new Date(state.weekDates[0].dateStr); newPrevDate.setDate(newPrevDate.getDate() - 7); state.weekDates = api.getAllDatesOfCurrWeek(new Date(newPrevDate)); setActiveDate(state, props, emit); }; const getNextWeek = ({ props, emit, state, api }) => () => { let newNextDate = new Date(state.weekDates[6].dateStr); newNextDate.setDate(newNextDate.getDate() + 7); state.weekDates = api.getAllDatesOfCurrWeek(new Date(newNextDate)); setActiveDate(state, props, emit); }; const setActiveDate = (state, props, emit) => { state.weekDates.forEach((day2) => day2.isCur = false); const day = new Date(props.modelValue).getDay(); const dayIdx = state.weekDays.indexOf(day); const curDate = state.weekDates[dayIdx]; curDate.isCur = true; emit("update:modelValue", curDate.dateStr); state.activeDate = curDate.dateStr; state.currentDate = curDate.dateArr[2]; state.activeRow = curDate.row; }; const getAllDatesOfCurrWeek = ({ state, props }) => (date) => { const weekFirst = getWeekFirst(state, props); const weekDates = []; let weekDate = new Date(date); const calDate = weekDate.getDate() - weekDate.getDay() + weekFirst; const weekFirstDate = calDate > weekDate.getDate() ? calDate - 7 : calDate; weekDate.setDate(weekFirstDate); const threeMonthDays = [...state.calendarDays, ...state.lastCalendarDays, ...state.nextCalendarDays]; const len = threeMonthDays.length; for (let i = 0; i < 7; i++) { const date2 = formatDate(new Date(weekDate)); let findDate = false; for (let j = 0; j < len; j++) { if (threeMonthDays[j].dateStr === date2) { threeMonthDays[j].type = "cur"; weekDates.push(threeMonthDays[j]); findDate = true; break; } } if (!findDate) { const newDate = new Date(weekDate); weekDates.push({ dateStr: date2, dateArr: [newDate.getFullYear(), newDate.getMonth() + 1, newDate.getDate()] }); } weekDate.setDate(weekDate.getDate() + 1); } return weekDates; }; const getWeekOfDate = ({ api }) => (type, date) => { const aWeekTimestamp = 7 * 24 * 60 * 60 * 1e3; let nextDate = +new Date(date); if (type === "prev") { nextDate = +new Date(date) - aWeekTimestamp; } else { nextDate = +new Date(date) + aWeekTimestamp; } return api.getAllDatesOfCurrWeek(new Date(nextDate)); }; const showWeekChange = ({ state }) => (value) => { if (value) { getWeekDatesByActiveRow(state); } else { state.calendarDays.forEach((date) => { if (state.activeDate === date.dateStr) { date.isCur = true; } else { date.isCur = false; } }); } }; const formatDate = (date) => { const newDate = new Date(date); return newDate.getFullYear() + "-" + pad0(newDate.getMonth() + 1) + "-" + pad0(newDate.getDate()); }; const getWeekDatesByActiveRow = (state) => { state.weekDates = state.calendarDays.filter((day) => day.row === state.activeRow); if (!state.weekDates.length) { state.activeRow = 0; state.weekDates = state.calendarDays.filter((day) => day.row === state.activeRow); } }; const computedCurrentRow = (state) => () => { const { calendarDays } = state; if (Array.isArray(calendarDays) && calendarDays.length) { const currentDay = calendarDays.find((day) => day.isCur); if (currentDay) { return currentDay.row; } } return null; }; const computedData = (state) => () => { if (state.showWeek) { return [state.prevWeekDates, state.weekDates, state.nextWeekDates]; } else { return [state.lastCalendarDays, state.calendarDays, state.nextCalendarDays]; } }; const computedFilteredCalendarDays = (state) => () => { const { calendarDays, currentRow, visibleRows } = state; if (Array.isArray(calendarDays) && calendarDays.length && currentRow !== null) { const rows = [...visibleRows, currentRow]; return calendarDays.filter((day) => ~rows.indexOf(day.row)); } return []; }; const handleDraggerClick = (state, emit) => () => { if (state.dragging) return; state.showWeek = !state.showWeek; emit("expand", !state.showWeek); }; const handleClickDay = ({ api, emit, props, state }) => (day) => { if (day.isCur || day.disabled) return; if (typeof day.dateStr === "string") { const isCurrent = day.dateArr[0] === +state.currentYear && day.dateArr[1] === +state.currentMonth; if (!isCurrent) { api.getCalendarDays(day.dateStr, props.config, "cur"); api.getCalendarDays(day.dateStr, props.config, "last"); api.getCalendarDays(day.dateStr, props.config, "next"); } const showCalendarDays = [...state.calendarDays, ...state.weekDates]; state.activeRow = day.row; showCalendarDays.forEach((date) => { if (day.dateStr === date.dateStr) { date.isCur = true; state.activeRow = date.row; } else { date.isCur = false; } }); state.activeDate = day.dateStr; state.currentDate = day.dateArr[2]; emit("update:modelValue", day.dateStr); } }; const calcCalendarItemHeight = ({ state, vm }) => () => { const calendarPanel = vm.$refs.calendarPanel; if (calendarPanel && calendarPanel.childNodes instanceof NodeList && calendarPanel.childNodes.length > 1) { setTimeout(() => { state.itemHeight = calendarPanel.childNodes[1].offsetHeight; }); } }; const computedTotalRows = (state) => () => { const len = state.dragging ? state.calendarDaysCopy.length : calendarDays.length; const { calendarDays, dayOfWeek } = state; const total = Math.floor(len / dayOfWeek); return total; }; const dragStart = ({ state, clientX, clientY, vm }) => { state.showRows = 0; state.calendarDaysCopy = state.calendarDays.slice(0); state.calendarPanelHeight = vm.$refs.calendarPanel.offsetHeight; state.dragPosStart = state.dragPos = { clientX, clientY }; }; const dragEnd = ({ state, vm, clientY, emit }) => { const dy = clientY - state.dragPosStart.clientY; if (dy === 0) return; state.dragPosStart = state.dragPos = {}; if (state.showWeek) { if (state.showRows >= 2) { state.showWeek = false; } } else { if (state.showRows <= 5) { state.showWeek = true; } } emit("expand", !state.showWeek); state.weekDates = state.copyWeekDates; state.calendarDays = state.calendarDaysCopy; vm.$refs.calendarPanel.style.height = "auto"; state.showRows = 0; }; const dragMove = ({ state, vm, clientX, clientY }) => { const { dragPos: lastPos, itemHeight } = state; const totalRows = 6; if (lastPos.clientY === void 0 || lastPos.clientX === void 0) { state.dragging = false; return; } const minH = itemHeight; const maxH = itemHeight * totalRows; state.dragPos = { clientX, clientY }; state.calendarPanelHeight += clientY - lastPos.clientY; let h = state.calendarPanelHeight; h = h < minH ? minH : h > maxH ? maxH : h; state.showRows = Math.floor(h / itemHeight); vm.$refs.calendarPanel.style.height = `${h}px`; if (state.showWeek) { if (state.showRows === 1) { state.weekDates = [...state.copyWeekDates]; } if (state.showRows === 2) { state.weekDates = [...state.prevWeekDates, ...state.copyWeekDates]; } if (state.showRows === 3) { state.weekDates = [...state.prevWeekDates, ...state.copyWeekDates, ...state.nextWeekDates]; } } else { getShowedCalendarDays(state); } }; const getShowedCalendarDays = (state) => { if (state.showRows === 0) return; state.calendarDays = state.calendarDaysCopy.slice(0, state.showRows * 7); }; const handleDraggerMousedown = ({ api, state, vm }) => (e) => { const { clientX, clientY } = e; dragStart({ state, vm, clientX, clientY }); document.addEventListener("mouseup", api.handleMouseup); document.addEventListener("mousemove", api.throttledHandleMousemove); }; const handleMouseup = ({ api, state, vm, emit }) => (e) => { setTimeout(() => state.dragging = false); document.removeEventListener("mouseup", api.handleMouseup); document.removeEventListener("mousemove", api.throttledHandleMousemove); dragEnd({ state, vm, clientY: e.clientY, emit }); }; const handleMousemove = ({ state, vm }) => (e) => { state.dragging = true; const { clientX, clientY } = e; dragMove({ state, vm, clientX, clientY }); }; const handleDraggerTouchstart = ({ state, vm }) => (e) => { const { clientX, clientY } = e.changedTouches[0]; state.touching = true; dragStart({ state, vm, clientX, clientY }); }; const handleDraggerTouchend = ({ state, vm, emit }) => (e) => { const { clientY } = e.changedTouches[0]; state.touching = false; dragEnd({ state, vm, clientY, emit }); }; const handleDraggerTouchmove = ({ state, vm }) => (e) => { const { clientX, clientY } = e.changedTouches[0]; dragMove({ state, vm, clientX, clientY }); }; const setCascaderVisible = (state) => (value) => state.cascaderVisible = !!value; const handleCascaderChange = ({ api, emit, props, state }) => (val) => { const dateStr = getDateStr(state, val[0], val[1]); api.getCalendarDays(dateStr, props.config, "cur"); api.getCalendarDays(dateStr, props.config, "last"); api.getCalendarDays(dateStr, props.config, "next"); emit("update:modelValue", dateStr); getWeekDatesByActiveRow(state); }; const computeCascaderOptions = (t) => () => [ { range: [2e3, 2050], optionMethod: (i) => ({ id: i, label: t("ui.calendarBar.year").replace("%s", String(i)) }) }, { range: [1, 12], optionMethod: (i) => ({ id: i, label: t(`ui.calendarBar.month.${i}`) }) } ]; const i18nYearMonth = ({ state, t }) => () => { const { currentYear, currentMonth } = state; let template = t("ui.calendarBar.yearMonth"); const monthAbbr = t(`ui.calendarBar.monthAbbr.${currentMonth}`); template = template.replace("%y", currentYear); template = template.replace("%m", monthAbbr); return template; }; const touchmove = ({ state }) => (event) => { const touch = event.touches[0]; state.deltaPos.Y = touch.clientY - state.startPos.Y; state.deltaPos.X = touch.clientX - state.startPos.X; state.offsetPos.Y = Math.abs(state.deltaPos.Y); state.offsetPos.X = Math.abs(state.deltaPos.X); state.direction = getDirection(state.offsetPos.X, state.offsetPos.Y); if (state.direction === "vertical") return; state.delta = state.deltaPos.X; }; const touchstart = ({ state }) => (event) => { event.stopPropagation(); resetTouchStatus(state); state.duration = 300; state.moving = true; state.touchTime = Date.now(); state.startPos.X = event.touches[0].clientX; state.startPos.Y = event.touches[0].clientY; }; const touchend = ({ state, api, vm }) => () => { const weekPanel = vm.$refs.weekPanel; const width = weekPanel && weekPanel.offsetWidth; const speed = state.delta / (Date.now() - state.touchTime); const isShouldMove = Math.abs(speed) > 0.3 || Math.abs(state.delta) > +(width / 3).toFixed(2); if (isShouldMove) { const delta = state.delta; state.duration = 300; state.delta = delta > 0 ? width : -width; setTimeout(() => { if (state.showWeek) { delta > 0 ? api.getPrevWeek() : api.getNextWeek(); if (state.weekDates[3].dateArr[0] !== +state.currentYear || state.weekDates[3].dateArr[1] !== +state.currentMonth) { api.updateCalendarDays(delta); } } else { api.updateCalendarDays(delta); } state.duration = 0; state.delta = 0; }, 300); } else { state.delta = 0; } state.moving = false; }; const updateCalendarDays = ({ props, state, api, emit }) => (delta) => { let { year, month } = delta > 0 ? lastMonth(state.currentYear, state.currentMonth) : nextMonth(state.currentYear, state.currentMonth); const dateStr = getDateStr(state, year, month); api.getCalendarDays(dateStr, props.config, "cur"); api.getCalendarDays(dateStr, props.config, "last"); api.getCalendarDays(dateStr, props.config, "next"); !state.showWeek && emit("update:modelValue", dateStr); }; function getDateStr(state, year, month) { const { currentDate } = state; let dateStr = `${year}-${pad0(month)}-${pad0(currentDate)}`; const numberCurrentDate = Number(currentDate); const { currentFullYear: curYear, currentMonth: curMonth, currentDate: curDate } = splitDate(new Date(dateStr)); if (curYear !== year || curMonth !== month || curDate !== numberCurrentDate) { dateStr = `${year}-${pad0(month)}-01`; } return dateStr; } function getWeekFirst(state, props) { let weekFirst = props.config.weekFirst || 0; const minWeekFirst = 0; const maxWeekFirst = state.dayOfWeek - 1; weekFirst = typeof weekFirst === "number" && weekFirst >= minWeekFirst && weekFirst <= maxWeekFirst ? Math.round(weekFirst) : minWeekFirst; return weekFirst; } function resetTouchStatus(state) { state.direction = ""; state.delta = 0; state.deltaPos.X = 0; state.deltaPos.Y = 0; state.offsetPos.X = 0; state.offsetPos.Y = 0; } export { calcCalendarItemHeight, computeCascaderOptions, computedCurrentRow, computedData, computedFilteredCalendarDays, computedTotalRows, getAllDatesOfCurrWeek, getCalendarDays, getNextWeek, getPrevWeek, getWeekOfDate, handleCascaderChange, handleClickDay, handleDraggerClick, handleDraggerMousedown, handleDraggerTouchend, handleDraggerTouchmove, handleDraggerTouchstart, handleMousemove, handleMouseup, i18nYearMonth, setCascaderVisible, showWeekChange, touchend, touchmove, touchstart, updateCalendarDays };