CalendarMonth.mjs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. import { createVNode as _createVNode } from "vue";
  2. import { ref, computed, defineComponent } from "vue";
  3. import { pick, addUnit, numericProp, setScrollTop, createNamespace, makeRequiredProp } from "../utils/index.mjs";
  4. import { getMonthEndDay } from "../datetime-picker/utils.mjs";
  5. import { t, bem, compareDay, getPrevDay, getNextDay, formatMonthTitle } from "./utils.mjs";
  6. import { useRect, useToggle } from "@vant/use";
  7. import { useExpose } from "../composables/use-expose.mjs";
  8. import { useHeight } from "../composables/use-height.mjs";
  9. import CalendarDay from "./CalendarDay.mjs";
  10. const [name] = createNamespace("calendar-month");
  11. const calendarMonthProps = {
  12. date: makeRequiredProp(Date),
  13. type: String,
  14. color: String,
  15. minDate: makeRequiredProp(Date),
  16. maxDate: makeRequiredProp(Date),
  17. showMark: Boolean,
  18. rowHeight: numericProp,
  19. formatter: Function,
  20. lazyRender: Boolean,
  21. currentDate: [Date, Array],
  22. allowSameDay: Boolean,
  23. showSubtitle: Boolean,
  24. showMonthTitle: Boolean,
  25. firstDayOfWeek: Number
  26. };
  27. var stdin_default = defineComponent({
  28. name,
  29. props: calendarMonthProps,
  30. emits: ["click", "update-height"],
  31. setup(props, {
  32. emit,
  33. slots
  34. }) {
  35. const [visible, setVisible] = useToggle();
  36. const daysRef = ref();
  37. const monthRef = ref();
  38. const height = useHeight(monthRef);
  39. const title = computed(() => formatMonthTitle(props.date));
  40. const rowHeight = computed(() => addUnit(props.rowHeight));
  41. const offset = computed(() => {
  42. const realDay = props.date.getDay();
  43. if (props.firstDayOfWeek) {
  44. return (realDay + 7 - props.firstDayOfWeek) % 7;
  45. }
  46. return realDay;
  47. });
  48. const totalDay = computed(() => getMonthEndDay(props.date.getFullYear(), props.date.getMonth() + 1));
  49. const shouldRender = computed(() => visible.value || !props.lazyRender);
  50. const getTitle = () => title.value;
  51. const getMultipleDayType = (day) => {
  52. const isSelected = (date) => props.currentDate.some((item) => compareDay(item, date) === 0);
  53. if (isSelected(day)) {
  54. const prevDay = getPrevDay(day);
  55. const nextDay = getNextDay(day);
  56. const prevSelected = isSelected(prevDay);
  57. const nextSelected = isSelected(nextDay);
  58. if (prevSelected && nextSelected) {
  59. return "multiple-middle";
  60. }
  61. if (prevSelected) {
  62. return "end";
  63. }
  64. if (nextSelected) {
  65. return "start";
  66. }
  67. return "multiple-selected";
  68. }
  69. return "";
  70. };
  71. const getRangeDayType = (day) => {
  72. const [startDay, endDay] = props.currentDate;
  73. if (!startDay) {
  74. return "";
  75. }
  76. const compareToStart = compareDay(day, startDay);
  77. if (!endDay) {
  78. return compareToStart === 0 ? "start" : "";
  79. }
  80. const compareToEnd = compareDay(day, endDay);
  81. if (props.allowSameDay && compareToStart === 0 && compareToEnd === 0) {
  82. return "start-end";
  83. }
  84. if (compareToStart === 0) {
  85. return "start";
  86. }
  87. if (compareToEnd === 0) {
  88. return "end";
  89. }
  90. if (compareToStart > 0 && compareToEnd < 0) {
  91. return "middle";
  92. }
  93. return "";
  94. };
  95. const getDayType = (day) => {
  96. const {
  97. type,
  98. minDate,
  99. maxDate,
  100. currentDate
  101. } = props;
  102. if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) {
  103. return "disabled";
  104. }
  105. if (currentDate === null) {
  106. return "";
  107. }
  108. if (Array.isArray(currentDate)) {
  109. if (type === "multiple") {
  110. return getMultipleDayType(day);
  111. }
  112. if (type === "range") {
  113. return getRangeDayType(day);
  114. }
  115. } else if (type === "single") {
  116. return compareDay(day, currentDate) === 0 ? "selected" : "";
  117. }
  118. return "";
  119. };
  120. const getBottomInfo = (dayType) => {
  121. if (props.type === "range") {
  122. if (dayType === "start" || dayType === "end") {
  123. return t(dayType);
  124. }
  125. if (dayType === "start-end") {
  126. return `${t("start")}/${t("end")}`;
  127. }
  128. }
  129. };
  130. const renderTitle = () => {
  131. if (props.showMonthTitle) {
  132. return _createVNode("div", {
  133. "class": bem("month-title")
  134. }, [title.value]);
  135. }
  136. };
  137. const renderMark = () => {
  138. if (props.showMark && shouldRender.value) {
  139. return _createVNode("div", {
  140. "class": bem("month-mark")
  141. }, [props.date.getMonth() + 1]);
  142. }
  143. };
  144. const placeholders = computed(() => {
  145. const count = Math.ceil((totalDay.value + offset.value) / 7);
  146. return Array(count).fill({
  147. type: "placeholder"
  148. });
  149. });
  150. const days = computed(() => {
  151. const days2 = [];
  152. const year = props.date.getFullYear();
  153. const month = props.date.getMonth();
  154. for (let day = 1; day <= totalDay.value; day++) {
  155. const date = new Date(year, month, day);
  156. const type = getDayType(date);
  157. let config = {
  158. date,
  159. type,
  160. text: day,
  161. bottomInfo: getBottomInfo(type)
  162. };
  163. if (props.formatter) {
  164. config = props.formatter(config);
  165. }
  166. days2.push(config);
  167. }
  168. return days2;
  169. });
  170. const disabledDays = computed(() => days.value.filter((day) => day.type === "disabled"));
  171. const scrollToDate = (body, targetDate) => {
  172. if (daysRef.value) {
  173. const daysRect = useRect(daysRef.value);
  174. const totalRows = placeholders.value.length;
  175. const currentRow = Math.ceil((targetDate.getDate() + offset.value) / 7);
  176. const rowOffset = (currentRow - 1) * daysRect.height / totalRows;
  177. setScrollTop(body, daysRect.top + rowOffset + body.scrollTop - useRect(body).top);
  178. }
  179. };
  180. const renderDay = (item, index) => _createVNode(CalendarDay, {
  181. "item": item,
  182. "index": index,
  183. "color": props.color,
  184. "offset": offset.value,
  185. "rowHeight": rowHeight.value,
  186. "onClick": (item2) => emit("click", item2)
  187. }, pick(slots, ["top-info", "bottom-info"]));
  188. const renderDays = () => _createVNode("div", {
  189. "ref": daysRef,
  190. "role": "grid",
  191. "class": bem("days")
  192. }, [renderMark(), (shouldRender.value ? days : placeholders).value.map(renderDay)]);
  193. useExpose({
  194. getTitle,
  195. getHeight: () => height.value,
  196. setVisible,
  197. scrollToDate,
  198. disabledDays
  199. });
  200. return () => _createVNode("div", {
  201. "class": bem("month"),
  202. "ref": monthRef
  203. }, [renderTitle(), renderDays()]);
  204. }
  205. });
  206. export {
  207. stdin_default as default
  208. };