File

projects/angularx-flatpickr/src/lib/flatpickr.directive.ts

Extends

FlatPickrOutputOptions

Index

Properties

Properties

dayElement
dayElement: HTMLElement
Type : HTMLElement
import {
  Directive,
  ElementRef,
  AfterViewInit,
  Input,
  Output,
  EventEmitter,
  OnChanges,
  SimpleChanges,
  OnDestroy,
  forwardRef,
  HostListener,
  Renderer2,
  PLATFORM_ID,
  Inject,
} from '@angular/core';
import {
  FlatpickrDefaults,
  DisableEnableDate,
  FlatpickrDefaultsInterface,
} from './flatpickr-defaults.service';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import flatpickr from 'flatpickr';
import { isPlatformBrowser } from '@angular/common';

export interface FlatPickrOutputOptions {
  selectedDates: Date[];
  dateString: string;
  instance: any;
}

export interface FlatPickrDayCreateOutputOptions
  extends FlatPickrOutputOptions {
  dayElement: HTMLElement;
}

export const FLATPICKR_CONTROL_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => FlatpickrDirective),
  multi: true,
};

@Directive({
  selector: '[mwlFlatpickr]',
  providers: [FLATPICKR_CONTROL_VALUE_ACCESSOR],
  exportAs: 'mwlFlatpickr',
  standalone: true,
})
export class FlatpickrDirective
  implements AfterViewInit, OnChanges, OnDestroy, ControlValueAccessor
{
  /**
   * Object-options that can be user for multiple instances of Flatpickr.
   * Option from this object is applied only if specific option is not specified.
   * Example:
   * ```typescript
   * options: FlatpickrDefaultsInterface = {
   *      altFormat: 'd/m/Y',   // will be ignored since altFormat is provided via specific attribute
   *      altInput: true        // will be used since specific attribute is not provided
   * };
   * ```
   * ```html
   * <input
   *   class="form-control"
   *   type="text"
   *   mwlFlatpickr
   *   [options]="options"
   *   altFormat="d/m/Y">
   * ```
   */
  @Input() options: FlatpickrDefaultsInterface = {};

  /**
   * Exactly the same as date format, but for the altInput field.
   */
  @Input() altFormat: string;

  /**
   * 	Show the user a readable date (as per altFormat), but return something totally different to the server.
   */
  @Input() altInput: boolean;

  /**
   * This class will be added to the input element created by the altInput option.
   * Note that `altInput` already inherits classes from the original input.
   */
  @Input() altInputClass: string;

  /**
   * Allows the user to enter a date directly input the input field. By default, direct entry is disabled.
   */
  @Input() allowInput: boolean;

  /**
   * Allows the preloading of an invalid date. When disabled, the field will be cleared if the provided date is invalid
   */
  @Input() allowInvalidPreload: boolean;

  /**
   * Instead of `body`, appends the calendar to the specified node instead.
   */
  @Input() appendTo: HTMLElement;

  /**
   * Defines how the date will be formatted in the aria-label for calendar days, using the same tokens as dateFormat. If you change this, you should choose a value that will make sense if a screen reader reads it out loud.
   */
  @Input() ariaDateFormat?: string;

  /**
   * Whether clicking on the input should open the picker.
   * You could disable this if you wish to open the calendar manually `with.open()`.
   */
  @Input() clickOpens: boolean;

  /**
   * A string of characters which are used to define how the date will be displayed in the input box.
   * The supported characters are defined in the table below.
   */
  @Input() dateFormat: string;

  /**
   * Initial value of the hour element.
   */
  @Input() defaultHour?: number;
  /**
   * Initial value of the minute element.
   */
  @Input() defaultMinute?: number;

  /**
   * Initial value of the seconds element.
   */
  @Input() defaultSeconds?: number;

  /**
   * See <a href="https://chmln.github.io/flatpickr/examples/#disabling-specific-dates">disabling dates</a>.
   */
  @Input() disable: DisableEnableDate[];

  /**
   * Set disableMobile to true to always use the non-native picker.
   * By default, Flatpickr utilizes native datetime widgets unless certain options (e.g. disable) are used.
   */
  @Input() disableMobile: boolean;

  /**
   * See <a href="https://chmln.github.io/flatpickr/examples/#disabling-all-dates-except-select-few">enabling dates</a>.
   */
  @Input() enable: DisableEnableDate[];

  /**
   * Enables time picker.
   */
  @Input() enableTime: boolean;

  /**
   * Enables seconds in the time picker.
   */
  @Input() enableSeconds: boolean;

  /**
   * Allows using a custom date formatting function instead of the built-in handling for date formats using dateFormat, altFormat, etc.
   */
  @Input() formatDate?: (value: any) => string;

  /**
   * Adjusts the step for the hour input (incl. scrolling).
   */
  @Input() hourIncrement: number;

  /**
   * Displays the calendar inline.
   */
  @Input() inline: boolean;

  /**
   * The maximum date that a user can pick to (inclusive).
   */
  @Input() maxDate: string | Date;

  /**
   * The minimum date that a user can start picking from (inclusive).
   */
  @Input() minDate: string | Date;

  /**
   * The maximum time that a user can pick to (inclusive).
   */
  @Input() maxTime: string;

  /**
   * The minimum time that a user can start picking from (inclusive).
   */
  @Input() minTime: string;

  /**
   * Adjusts the step for the minute input (incl. scrolling).
   */
  @Input() minuteIncrement: number;

  /**
   * Select a single date, multiple dates or a date range.
   */
  @Input() mode: 'single' | 'multiple' | 'range';

  /**
   * HTML for the arrow icon, used to switch months.
   */
  @Input() nextArrow: string;

  /**
   * Hides the day selection in calendar. Use it along with `enableTime` to create a time picker.
   */
  @Input() noCalendar: boolean;

  /**
   * Provide a date for 'today', which will be used instead of "new Date()"
   */
  @Input() now?: Date | string | number;

  /**
   * Function that expects a date string and must return a Date object.
   */
  @Input() parseDate: (str: string) => Date;

  /**
   * HTML for the left arrow icon.
   */
  @Input() prevArrow: string;

  /**
   * Show the month using the shorthand version (ie, Sep instead of September).
   */
  @Input() shorthandCurrentMonth: boolean;

  /**
   * The number of months shown.
   */
  @Input() showMonths: number;

  /**
   * Position the calendar inside the wrapper and next to the input element. (Leave `false` unless you know what you're doing).
   */
  @Input() static: boolean;

  /**
   * Displays time picker in 24 hour mode without AM/PM selection when enabled.
   */
  @Input() time24hr: boolean;

  /**
   * Enables display of week numbers in calendar.
   */
  @Input() weekNumbers: boolean;

  /**
   * You may override the function that extracts the week numbers from a Date by supplying a getWeek function.
   * It takes in a date as a parameter and should return a corresponding string that you want to appear left of every week.
   */
  @Input() getWeek: (date: Date) => string;

  /**
   * Custom elements and input groups.
   */
  @Input() wrap: boolean;

  /**
   * Array of plugin instances to use.
   */
  @Input() plugins: any[];

  /**
   * The locale object or string to use for the locale.
   */
  @Input() locale: object | string;

  /**
   * Auto convert the ngModel value from a string to a date / array of dates / from - to date object depending on the `mode`
   */
  @Input() convertModelValue: boolean;

  /**
   * How the month should be displayed in the header of the calendar.
   */
  @Input() monthSelectorType: 'static' | 'dropdown';

  /**
   * Array of HTML elements that should not close the picker on click.
   */
  @Input() ignoredFocusElements: HTMLElement[] = [];

  /**
   * Gets triggered once the calendar is in a ready state
   */
  @Output()
  flatpickrReady: EventEmitter<FlatPickrOutputOptions> = new EventEmitter();

  /**
   * Gets triggered when the user selects a date, or changes the time on a selected date.
   */
  @Output()
  flatpickrChange: EventEmitter<FlatPickrOutputOptions> = new EventEmitter();

  /**
   * Gets triggered when the input value is updated with a new date string.
   */
  @Output()
  flatpickrValueUpdate: EventEmitter<FlatPickrOutputOptions> =
    new EventEmitter();

  /**
   * Gets triggered when the calendar is opened.
   */
  @Output()
  flatpickrOpen: EventEmitter<FlatPickrOutputOptions> = new EventEmitter();

  /**
   * Gets triggered when the calendar is closed.
   */
  @Output()
  flatpickrClose: EventEmitter<FlatPickrOutputOptions> = new EventEmitter();

  /**
   * Gets triggered when the month is changed, either by the user or programmatically.
   */
  @Output()
  flatpickrMonthChange: EventEmitter<FlatPickrOutputOptions> =
    new EventEmitter();

  /**
   * Gets triggered when the year is changed, either by the user or programmatically.
   */
  @Output()
  flatpickrYearChange: EventEmitter<FlatPickrOutputOptions> =
    new EventEmitter();

  /**
   * Take full control of every date cell with this output
   */
  @Output()
  flatpickrDayCreate: EventEmitter<FlatPickrDayCreateOutputOptions> =
    new EventEmitter();

  /**
   * The flatpickr instance where you can call methods like toggle(), open(), close() etc
   */
  instance: flatpickr.Instance;

  private isDisabled = false;
  private initialValue: any;

  onChangeFn: (value: any) => void = () => {};

  @HostListener('blur')
  onTouchedFn = () => {};

  constructor(
    private elm: ElementRef,
    private defaults: FlatpickrDefaults,
    private renderer: Renderer2,
    @Inject(PLATFORM_ID) private platformId: Object,
  ) {}

  ngAfterViewInit(): void {
    const options: any = {
      altFormat: this.altFormat,
      altInput: this.altInput,
      altInputClass: this.altInputClass,
      allowInput: this.allowInput,
      allowInvalidPreload: this.allowInvalidPreload,
      appendTo: this.appendTo,
      ariaDateFormat: this.ariaDateFormat,
      clickOpens: this.clickOpens,
      dateFormat: this.dateFormat,
      defaultHour: this.defaultHour,
      defaultMinute: this.defaultMinute,
      defaultSeconds: this.defaultSeconds,
      disable: this.disable,
      disableMobile: this.disableMobile,
      enable: this.enable,
      enableTime: this.enableTime,
      enableSeconds: this.enableSeconds,
      formatDate: this.formatDate,
      hourIncrement: this.hourIncrement,
      defaultDate: this.initialValue,
      inline: this.inline,
      maxDate: this.maxDate,
      minDate: this.minDate,
      maxTime: this.maxTime,
      minTime: this.minTime,
      minuteIncrement: this.minuteIncrement,
      mode: this.mode,
      nextArrow: this.nextArrow,
      noCalendar: this.noCalendar,
      now: this.now,
      parseDate: this.parseDate,
      prevArrow: this.prevArrow,
      shorthandCurrentMonth: this.shorthandCurrentMonth,
      showMonths: this.showMonths,
      monthSelectorType: this.monthSelectorType,
      static: this.static,
      time24hr: this.time24hr,
      weekNumbers: this.weekNumbers,
      getWeek: this.getWeek,
      wrap: this.wrap,
      plugins: this.plugins,
      locale: this.locale,
      ignoredFocusElements: this.ignoredFocusElements,
      onChange: (selectedDates: Date[], dateString: string, instance: any) => {
        this.flatpickrChange.emit({ selectedDates, dateString, instance });
      },
      onOpen: (selectedDates: Date[], dateString: string, instance: any) => {
        this.flatpickrOpen.emit({ selectedDates, dateString, instance });
      },
      onClose: (selectedDates: Date[], dateString: string, instance: any) => {
        this.flatpickrClose.emit({ selectedDates, dateString, instance });
      },
      onMonthChange: (
        selectedDates: Date[],
        dateString: string,
        instance: any,
      ) => {
        this.flatpickrMonthChange.emit({ selectedDates, dateString, instance });
      },
      onYearChange: (
        selectedDates: Date[],
        dateString: string,
        instance: any,
      ) => {
        this.flatpickrYearChange.emit({ selectedDates, dateString, instance });
      },
      onReady: (selectedDates: Date[], dateString: string, instance: any) => {
        this.flatpickrReady.emit({ selectedDates, dateString, instance });
      },
      onValueUpdate: (
        selectedDates: Date[],
        dateString: string,
        instance: any,
      ) => {
        this.flatpickrValueUpdate.emit({ selectedDates, dateString, instance });
      },
      onDayCreate: (
        selectedDates: Date[],
        dateString: string,
        instance: any,
        dayElement: HTMLElement,
      ) => {
        this.flatpickrDayCreate.emit({
          selectedDates,
          dateString,
          instance,
          dayElement,
        });
      },
    };

    Object.keys(options).forEach((key) => {
      if (typeof options[key] === 'undefined') {
        if (typeof (this.options as any)[key] !== 'undefined') {
          options[key] = (this.options as any)[key];
        } else {
          options[key] = (this.defaults as any)[key];
        }
      }
    });

    // @ts-ignore
    options.time_24hr = options.time24hr;

    // workaround bug in flatpickr 4.6 where it doesn't copy the classes across
    // TODO - remove once fix in https://github.com/flatpickr/flatpickr/issues/1860 is released
    options.altInputClass =
      (options.altInputClass || '') + ' ' + this.elm.nativeElement.className;

    if (!options.enable) {
      delete options.enable;
    }

    if (isPlatformBrowser(this.platformId)) {
      this.instance = flatpickr(
        this.elm.nativeElement,
        options,
      ) as flatpickr.Instance;
      this.setDisabledState(this.isDisabled);
    }
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (this.instance) {
      Object.keys(changes).forEach((inputKey) => {
        this.instance.set(inputKey as any, (this as any)[inputKey]);
      });
    }
  }

  ngOnDestroy(): void {
    if (this.instance) {
      this.instance.destroy();
    }
  }

  writeValue(value: any): void {
    let convertedValue: any = value;
    if (this.convertModelValue && this.mode === 'range' && value) {
      convertedValue = [value.from, value.to];
    }

    if (this.instance) {
      this.instance.setDate(convertedValue);
    } else {
      // flatpickr hasn't been initialised yet, store the value for later use
      this.initialValue = convertedValue;
    }
  }

  registerOnChange(fn: any): void {
    this.onChangeFn = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouchedFn = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.isDisabled = isDisabled;
    if (this.instance) {
      if (this.isDisabled) {
        this.renderer.setProperty(this.instance._input, 'disabled', 'disabled');
      } else {
        this.renderer.removeAttribute(this.instance._input, 'disabled');
      }
    }
  }

  @HostListener('input')
  inputChanged(): void {
    const value: string = this.elm.nativeElement.value;
    if (this.convertModelValue && typeof value === 'string') {
      switch (this.mode) {
        case 'multiple':
          const dates: Array<Date | undefined> = value
            .split('; ')
            .map((str) =>
              this.instance.parseDate(
                str,
                this.instance.config.dateFormat,
                !this.instance.config.enableTime,
              ),
            );
          this.onChangeFn(dates);
          break;

        case 'range':
          const [from, to] = value
            .split(this.instance.l10n.rangeSeparator)
            .map((str) =>
              this.instance.parseDate(
                str,
                this.instance.config.dateFormat,
                !this.instance.config.enableTime,
              ),
            );
          this.onChangeFn({ from, to });
          break;

        case 'single':
        default:
          this.onChangeFn(
            this.instance.parseDate(
              value,
              this.instance.config.dateFormat,
              !this.instance.config.enableTime,
            ),
          );
      }
    } else {
      this.onChangeFn(value);
    }
  }
}

results matching ""

    No results matching ""