Home Reference Source

src/components/controls/DatePicker/utils.js

import moment from 'moment';
import { flattenDeep, keys, map, maxBy } from 'lodash';

/**
 * Дата (date) была после конца месяца другой даты(displayedMonth) или нет
 * @param date
 * @param displayedMonth
 */
export function isDateFromNextMonth(date, displayedMonth) {
  return (
    date.year() > displayedMonth.year() ||
    (date.year() === displayedMonth.year() &&
      date.month() > displayedMonth.month())
  );
}

/**
 * Дата (date) была до начала месяца другой даты(displayedMonth) или нет
 * @param date
 * @param displayedMonth
 */
export function isDateFromPrevMonth(date, displayedMonth) {
  return (
    date.year() < displayedMonth.year() ||
    (date.year() === displayedMonth.year() &&
      date.month() < displayedMonth.month())
  );
}

/**
 * Возвращает массив из недель(каждая неделя - массив из дней)
 * @param firstDay
 */
export function weeks(firstDay) {
  const lastDay = firstDay.clone().add(35, 'days');
  const day = firstDay.clone();
  let weeks = [];
  while (day <= lastDay) {
    let week = [];
    for (let i = 0; i < 7; i++) {
      week.push(day.clone());
      if (i === 6) {
        weeks.push(week);
      }
      day.add(1, 'days');
    }
  }
  return weeks;
}

/**
 * добавить часы и минуты к дате
 * @param date
 * @param hours
 * @param mins
 */
export function addTime(date, hours, mins, secs) {
  return date
    .clone()
    .add(hours, 'h')
    .add(mins, 'm')
    .add(secs, 's');
}

/**
 * добавть локаль
 * @param date
 * @param locale
 */
export function withLocale(date, locale) {
  return date.clone().locale(locale);
}

/**
 * преобразовать дату к moment-объекту
 * @param value
 * @param dateFormat
 */
export function parseDate(value, dateFormat) {
  if (value instanceof Date) {
    value = moment(value);
  } else if (typeof value === 'string') {
    value = moment(value, dateFormat);
    if (!value.isValid()) {
      console.log('Invalid date');
    }
  }
  return value;
}

/**
 * Привести по пропсам к нужному формату
 * @param val
 * @param defaultTime
 * @param dateFormat
 * @param locale
 * @param defaultName
 */
export function mapToValue(val, defaultTime, dateFormat, locale, defaultName) {
  let res = {};
  if (Array.isArray(val)) {
    val.map(input => {
      if (!input.value) {
        res[input.name] = null;
      } else {
        const value = addTime(
          withLocale(parseDate(input.value, dateFormat), locale).startOf('day'),
          defaultTime[input.name].hours,
          defaultTime[input.name].mins,
          defaultTime[input.name].seconds
        );
        res[input.name] = value;
      }
    });
    return res;
  }
  if (!val) return { [defaultName]: null };
  const value = addTime(
    withLocale(parseDate(val, dateFormat), locale).startOf('day'),
    defaultTime[defaultName].hours,
    defaultTime[defaultName].mins,
    defaultTime[defaultName].seconds
  );
  res[defaultName] = value;
  return res;
}

/**
 * Установаить дефолтное время
 * @param val
 * @param defaultTime
 * @param defaultName
 */
export function mapToDefaultTime(
  val,
  defaultTime,
  defaultName,
  timeFormat = 'HH:mm:ss',
  format
) {
  if (Array.isArray(val)) {
    let res = {};
    val.map(input => {
      res[input.name] = {
        hours:
          (input.value && moment(input.value, format).hour()) ||
          moment(input.defaultTime || '00:00', timeFormat).hour() ||
          0,
        mins:
          (input.value && moment(input.value, format).minute()) ||
          moment(input.defaultTime || '00:00', timeFormat).minute() ||
          0,
        seconds:
          (input.value && moment(input.value, format).second()) ||
          moment(input.defaultTime || '00:00', timeFormat).second() ||
          0,
        hasDefaultTime: false,
      };

      if (
        res[input.name].hours ||
        res[input.name].mins ||
        res[input.name].seconds ||
        timeFormat
      ) {
        res[input.name].hasDefaultTime = true;
      }
    });

    return res;
  }

  if (val) {
    return {
      [defaultName]: {
        hours: moment(val, format).hour(),
        mins: moment(val, format).minute(),
        seconds: moment(val, format).second(),
        hasDefaultTime: true,
      },
    };
  }

  let ress = {
    [defaultName]: {
      hours: moment(defaultTime, timeFormat).hour(),
      mins: moment(defaultTime, timeFormat).minute(),
      seconds: moment(defaultTime, timeFormat).second(),
      hasDefaultTime: false,
    },
  };

  if (
    ress[defaultName].hours ||
    ress[defaultName].mins ||
    ress[defaultName].seconds
  ) {
    ress[defaultName].hasDefaultTime = true;
  }

  return ress;
}

/**
 * узнать высоту DOM-элемента
 * @param el
 */
export function getAbsoluteHeight(el) {
  // Get the DOM Node if you pass in a string
  el = typeof el === 'string' ? document.querySelector(el) : el;

  let styles = window.getComputedStyle(el);
  let margin =
    parseFloat(styles['marginTop']) + parseFloat(styles['marginBottom']);

  return Math.ceil(el.offsetHeight + margin);
}

/**
 * узнать ширину DOM-элемента
 * @param el
 */
export function getAbsoluteWidth(el) {
  // Get the DOM Node if you pass in a string
  el = typeof el === 'string' ? document.querySelector(el) : el;

  let styles = window.getComputedStyle(el);
  let margin =
    parseFloat(styles['marginLeft']) + parseFloat(styles['marginRight']);

  return Math.ceil(el.offsetWidth + margin);
}

export function buildDateFormat(dateFormat, timeFormat, divider) {
  return (
    (dateFormat ? dateFormat : '') +
    (dateFormat && timeFormat ? divider : '') +
    (timeFormat ? timeFormat : '')
  );
}

/**
 * Вычисляем наибольшее свободное пространство рядом с элементом
 * Возвращаем одно из вариантов [left, top, right, bottom]
 * @param input - input dateInterval
 * @param popUp - popUp dateInterval
 * @param window - глобальный обьект window
 * @returns {*}
 */
export function calculateMaxFreeSpace(input, popUp, window) {
  const inputPosition = input.getBoundingClientRect();
  const popUpPosition = popUp.getBoundingClientRect();

  /* placements:
                        ^
                        |
                       top
                        |
  <------left----| DateInputGroup |-------right------->
                        |
                        |
                      bottom
                        |

   */

  const placements = {
    left: inputPosition.left,
    top: inputPosition.top,
    right: window.innerWidth - inputPosition.right,
    bottom: window.innerHeight - inputPosition.bottom,
  };

  // Не даем открыться вниз или вверх если попап выходит за рамки экрана.
  // Нужно из-за того что попап прибит при placement top и bottom к правому краю.
  if (inputPosition.width + inputPosition.left < popUpPosition.width) {
    placements.bottom = -1;
    placements.top = -1;
  }

  return maxBy(keys(placements), o => placements[o]);
}

export const replaceDictionary = tmp => {
  switch (tmp) {
    case 'DD':
      return [/[0-3]/, /\d/];
    case 'MM':
      return [/[0-1]/, /\d/];
    case 'YY':
      return [/\d/, /\d/];
    case 'YYYY':
      return [/[0-2]/, /\d/, /\d/, /\d/];
    case 'H':
    case 'HH':
      return [/[0-2]/, /\d/];
    case 'h':
    case 'hh':
      return [/[0-1]/, /\d/];
    case 'k':
    case 'kk':
      return [/[1-2]/, /\d/];
    case 's':
    case 'm':
    case 'ss':
    case 'mm':
      return [/[0-5]/, /\d/];
    default:
      return [/\d/];
  }
};

export const formatToMask = format => {
  const splitedFormat = format.split(/\b/gi);

  return flattenDeep(
    map(splitedFormat, item => {
      if (~item.search(/([A-Z]){1,}/gi)) {
        return replaceDictionary(item);
      }
      return item;
    })
  );
};

export const MODIFIERS = {
  preventOverflow: {
    boundariesElement: 'window',
  },
};
/**
 * Функция проверки находится ли дата в промежутке max и min
 * @param date
 * @param dateFormat
 * @param max
 * @param min
 * @returns {boolean}
 */
export const hasInsideMixMax = (date, { dateFormat, max, min }) => {
  if (!max && !min) return true;
  if (
    (!max && min && moment(min, dateFormat) <= moment(date, dateFormat)) ||
    (max && !min && moment(max, dateFormat) >= moment(date, dateFormat)) ||
    (max &&
      min &&
      moment(min, dateFormat) <= moment(date, dateFormat) &&
      moment(max, dateFormat) >= moment(date, dateFormat))
  ) {
    return true;
  }

  return false;
};