jelhan/croodle

View on GitHub
app/components/create-options-dates.js

Summary

Maintainability
A
3 hrs
Test Coverage
import classic from 'ember-classic-decorator';
import { action, computed } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import { isArray } from '@ember/array';
import { isPresent } from '@ember/utils';
import moment from 'moment';

@classic
export default class CreateOptionsDates extends Component {
  @service('store')
  store;

  @computed('options.[]')
  get selectedDays() {
    return this.options
      // should be unique
      .uniqBy('day')
      // raw dates
      .map(({ date }) => date)
      // filter out invalid
      .filter(moment.isMoment)
      .toArray();
  }

  @computed('calendarCenter')
  get calendarCenterNext() {
    return moment(this.calendarCenter).add(1, 'months');
  }

  @action
  daysSelected({ moment: newMoments }) {
    let { options } = this;

    if (!isArray(newMoments)) {
      // special case: all options are unselected
      options.clear();
      return;
    }

    // array of options that represent days missing in updated selection
    let removedOptions = options.filter((option) => {
      return !newMoments.find((newMoment) => newMoment.format('YYYY-MM-DD') === option.day);
    });

    // array of moments that aren't represented yet by an option
    let addedMoments = newMoments.filter((moment) => {
      return !options.find((option) => moment.format('YYYY-MM-DD') === option.day);
    });

    // remove options that represent deselected days
    options.removeObjects(removedOptions);

    // add options for newly selected days
    let newOptions = addedMoments.map((moment) => {
      return this.store.createFragment('option', {
        title: moment.format('YYYY-MM-DD'),
      })
    });
    newOptions.forEach((newOption) => {
      // options must be insert into options array at correct position
      let insertBefore = options.find(({ date }) => {
        if (!moment.isMoment(date)) {
          // ignore options that do not represent a valid date
          return false;
        }

        return date.isAfter(newOption.date);
      });
      let position = isPresent(insertBefore) ? options.indexOf(insertBefore) : options.length;
      options.insertAt(position, newOption);
    });
  }

  @action
  updateCalenderCenter(diff) {
    this.calendarCenter.add(diff, 'months');
    this.notifyPropertyChange('calenderCenter');
  }

  init() {
    super.init(arguments);

    let { selectedDays } = this;
    this.set('calendarCenter', selectedDays.length >= 1 ? selectedDays[0] : moment());
  }
}