yearAndMonthFoundation.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { setMonth, setYear } from 'date-fns';
  2. import BaseFoundation, { DefaultAdapter } from '../base/foundation';
  3. import { PresetPosition } from './foundation';
  4. import { ArrayElement } from '../utils/type';
  5. import { strings } from './constants';
  6. import { PanelType } from './monthsGridFoundation';
  7. import copy from 'fast-copy';
  8. import { TZDate } from '@date-fns/tz';
  9. type Type = ArrayElement<typeof strings.TYPE_SET>;
  10. export interface YearAndMonthFoundationProps {
  11. currentYear: { left: number; right: number };
  12. currentMonth: { left: number; right: number };
  13. onSelect?: (obj: { currentMonth: { left: number; right: number }; currentYear: { left: number; right: number } }) => void;
  14. onBackToMain?: () => void;
  15. locale?: any;
  16. localeCode?: string;
  17. monthCycled?: boolean;
  18. yearCycled?: boolean;
  19. noBackBtn?: boolean;
  20. disabledDate?: (date: TZDate) => boolean;
  21. density?: string;
  22. presetPosition?: PresetPosition;
  23. renderQuickControls?: any;
  24. renderDateInput?: any;
  25. type?: Type;
  26. yearAndMonthOpts?: any;
  27. startYear?: number;
  28. endYear?: number;
  29. timeZone?: number | string
  30. }
  31. export interface YearAndMonthFoundationState {
  32. years: Array<{ value: number; year: number }>;
  33. months: Array<{ value: number; month: number }>;
  34. currentYear: { left: number; right: number };
  35. currentMonth: { left: number; right: number }
  36. }
  37. export interface YearAndMonthAdapter extends DefaultAdapter<YearAndMonthFoundationProps, YearAndMonthFoundationState> {
  38. setCurrentYear: (currentYear: { left: number; right: number }, cb?: () => void) => void;
  39. setCurrentMonth: (currentMonth: { left: number; right: number }) => void;
  40. setCurrentYearAndMonth: (currentYear: { left: number; right: number }, currentMonth: { left: number; right: number }) => void;
  41. notifySelectYear: (year: { left: number; right: number }) => void;
  42. notifySelectMonth: (month: { left: number; right: number }) => void;
  43. notifySelectYearAndMonth: (year: { left: number; right: number }, month: { left: number; right: number }) => void;
  44. notifyBackToMain: () => void
  45. }
  46. export interface MonthScrollItem {
  47. [k: string]: any;
  48. month: number;
  49. value: string;
  50. disabled: boolean
  51. }
  52. export interface YearScrollItem {
  53. [k: string]: any;
  54. year: number;
  55. value: number;
  56. disabled: boolean
  57. }
  58. export default class YearAndMonthFoundation extends BaseFoundation<YearAndMonthAdapter> {
  59. constructor(adapter: YearAndMonthAdapter) {
  60. super({ ...adapter });
  61. }
  62. init() {}
  63. destroy() {}
  64. selectYear(item: YearScrollItem, panelType?: PanelType) {
  65. // const year = item.value;
  66. const { currentYear, currentMonth } = this.getStates();
  67. const { type } = this.getProps();
  68. const left = strings.PANEL_TYPE_LEFT;
  69. const right = strings.PANEL_TYPE_RIGHT;
  70. const year = copy(currentYear);
  71. year[panelType] = item.value;
  72. // make sure the right panel time is always less than the left panel time
  73. if (type === 'monthRange') {
  74. const isSameYearIllegalDate = year[left] === year[right] && currentMonth[left] > currentMonth[right];
  75. if ((panelType === left && item.value > year[right]) || (panelType === left && isSameYearIllegalDate)) {
  76. // 1. select left year and left year > right year
  77. // 2. select left year, left year = right year, but left date > right date
  78. year[right] = item.value + 1;
  79. } else if (panelType === right && isSameYearIllegalDate) {
  80. // 1. select right year, left year = right year, but left date > right date
  81. year[left] = item.value - 1;
  82. }
  83. }
  84. this._adapter.setCurrentYear(year, () => this.autoSelectMonth(item, panelType, year));
  85. this._adapter.notifySelectYear(year);
  86. }
  87. selectMonth(item: MonthScrollItem, panelType?: PanelType) {
  88. const { currentMonth, currentYear } = this.getStates();
  89. const { type } = this.getProps();
  90. const left = strings.PANEL_TYPE_LEFT;
  91. const right = strings.PANEL_TYPE_RIGHT;
  92. const month = copy(currentMonth);
  93. month[panelType] = item.month;
  94. // Make sure the time on the right panel is always greater than or equal to the time on the left panel
  95. if (type === 'monthRange' && panelType === left && currentYear[left] === currentYear[right] && item.value > month[right]) {
  96. month[right] = item.month ;
  97. }
  98. this._adapter.setCurrentMonth(month);
  99. this._adapter.notifySelectMonth(month);
  100. }
  101. /**
  102. * After selecting a year, if the currentMonth is disabled, automatically select a non-disabled month
  103. */
  104. autoSelectMonth(item: YearScrollItem, panelType: PanelType, year: { left: number; right: number }) {
  105. const { disabledDate } = this._adapter.getProps();
  106. const { months, currentMonth } = this._adapter.getStates();
  107. const oppositeType = panelType === strings.PANEL_TYPE_LEFT ? 'right' : 'left';
  108. const currentDate = setYear(Date.now(), item.year);
  109. const isCurrentMonthDisabled = disabledDate(setMonth(currentDate, currentMonth[panelType] - 1));
  110. // whether the date on the opposite is legal
  111. const isOppositeMonthDisabled = disabledDate(setMonth(setYear(Date.now(), year[oppositeType]), currentMonth[oppositeType] - 1));
  112. if (!isCurrentMonthDisabled && !isOppositeMonthDisabled) {
  113. // all panel Date is legal
  114. return;
  115. }
  116. let finalYear = year;
  117. let finalMonth = currentMonth;
  118. if (isCurrentMonthDisabled) {
  119. const currentIndex = months.findIndex(({ month }) => month === currentMonth[panelType]);
  120. let validMonth: typeof months[number];
  121. // First look in the back, if you can't find it in the back, then look in the front
  122. validMonth = months.slice(currentIndex).find(({ month }) => !disabledDate(setMonth(currentDate, month - 1)));
  123. if (!validMonth) {
  124. validMonth = months.slice(0, currentIndex).find(({ month }) => !disabledDate(setMonth(currentDate, month - 1)));
  125. }
  126. if (validMonth && !isOppositeMonthDisabled) {
  127. // only currentPanel Date is illegal
  128. // just need to modify the month of the current panel
  129. finalMonth[panelType] = validMonth.month;
  130. } else if (validMonth && isOppositeMonthDisabled) {
  131. // all panel Date is illegal
  132. // change the value to the legal value calculated by the current panel
  133. finalYear = { 'left': item.year, 'right': item.year };
  134. finalMonth = { 'left': validMonth.month, 'right': validMonth.month };
  135. }
  136. } else if (!isCurrentMonthDisabled && isOppositeMonthDisabled) {
  137. // only opposite panel Date is illegal
  138. // change the value to the legal value in the current panel
  139. finalYear = { 'left': item.year, 'right': item.year };
  140. finalMonth = { 'left': currentMonth[panelType], 'right': currentMonth[panelType] };
  141. }
  142. this._adapter.setCurrentYearAndMonth(finalYear, finalMonth);
  143. this._adapter.notifySelectYearAndMonth(finalYear, finalMonth);
  144. }
  145. backToMain() {
  146. this._adapter.notifyBackToMain();
  147. }
  148. }