datepicker.js

  1. /**
  2. * @param {HTMLElement} element DOM element for component instantiation and scope
  3. * @param {Object} options
  4. * @param {String} options.datepickerFormat Format for dates
  5. */
  6. export class Datepicker {
  7. /**
  8. * @static
  9. * Shorthand for instance creation and initialisation.
  10. *
  11. * @param {HTMLElement} root DOM element for component instantiation and scope
  12. *
  13. * @return {Datepicker} An instance of Datepicker.
  14. */
  15. static autoInit(root, { DATEPICKER: defaultOptions = {} } = {}) {
  16. const datepicker = new Datepicker(root, defaultOptions);
  17. datepicker.init();
  18. root.ECLDatepicker = datepicker;
  19. return datepicker;
  20. }
  21. constructor(
  22. element,
  23. {
  24. format = '',
  25. theme = 'ecl-datepicker-theme',
  26. yearRange = 40,
  27. reposition = false,
  28. i18n = {
  29. previousMonth: 'Previous Month',
  30. nextMonth: 'Next Month',
  31. months: [
  32. 'January',
  33. 'February',
  34. 'March',
  35. 'April',
  36. 'May',
  37. 'June',
  38. 'July',
  39. 'August',
  40. 'September',
  41. 'October',
  42. 'November',
  43. 'December',
  44. ],
  45. weekdays: [
  46. 'Sunday',
  47. 'Monday',
  48. 'Tuesday',
  49. 'Wednesday',
  50. 'Thursday',
  51. 'Friday',
  52. 'Saturday',
  53. ],
  54. weekdaysShort: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
  55. },
  56. showDaysInNextAndPreviousMonths = true,
  57. enableSelectionDaysInNextAndPreviousMonths = true,
  58. } = {},
  59. ) {
  60. // Check element
  61. if (!element || element.nodeType !== Node.ELEMENT_NODE) {
  62. throw new TypeError(
  63. 'DOM element should be given to initialize this widget.',
  64. );
  65. }
  66. this.element = element;
  67. // Options
  68. this.picker = null;
  69. this.format = format;
  70. this.theme = theme;
  71. this.yearRange = yearRange;
  72. this.i18n = i18n;
  73. this.showDaysInNextAndPreviousMonths = showDaysInNextAndPreviousMonths;
  74. this.enableSelectionDaysInNextAndPreviousMonths =
  75. enableSelectionDaysInNextAndPreviousMonths;
  76. this.reposition = reposition;
  77. this.direction = 'ltr';
  78. }
  79. /**
  80. * Initialise component.
  81. */
  82. init() {
  83. if (typeof window.Pikaday === 'undefined') {
  84. throw new TypeError(
  85. 'Pikaday is not available. Make sure to include Pikaday in your project if you want to use the ECL datepicker',
  86. );
  87. }
  88. if (!ECL) {
  89. throw new TypeError('Called init but ECL is not present');
  90. }
  91. ECL.components = ECL.components || new Map();
  92. this.direction = getComputedStyle(this.element).direction;
  93. const options = {
  94. field: this.element,
  95. yearRange: this.yearRange,
  96. firstDay: 1,
  97. i18n: this.i18n,
  98. theme: this.theme,
  99. reposition: this.reposition,
  100. isRTL: this.direction === 'rtl',
  101. position: this.direction === 'rtl' ? 'bottom right' : 'bottom left',
  102. showDaysInNextAndPreviousMonths: this.showDaysInNextAndPreviousMonths,
  103. enableSelectionDaysInNextAndPreviousMonths:
  104. this.enableSelectionDaysInNextAndPreviousMonths,
  105. };
  106. if (this.format !== '') {
  107. options.format = this.format;
  108. } else {
  109. options.toString = (date) => {
  110. const day = `0${date.getDate()}`.slice(-2);
  111. const month = `0${date.getMonth() + 1}`.slice(-2);
  112. const year = date.getFullYear();
  113. return `${day}-${month}-${year}`;
  114. };
  115. }
  116. // eslint-disable-next-line no-undef
  117. this.picker = new Pikaday({
  118. ...options,
  119. onOpen() {
  120. this.direction = getComputedStyle(this.el).direction;
  121. // Extend picker size on mobile
  122. const vw = Math.max(
  123. document.documentElement.clientWidth || 0,
  124. window.innerWidth || 0,
  125. );
  126. const elRect = this.el.getBoundingClientRect();
  127. if (this.direction === 'rtl') {
  128. const pickerMargin = vw - elRect.right;
  129. if (vw < 768) {
  130. this.el.style.left = `${pickerMargin}px`;
  131. } else {
  132. this.el.style.left = 'auto';
  133. }
  134. } else {
  135. const pickerMargin = elRect.left;
  136. if (vw < 768) {
  137. this.el.style.right = `${pickerMargin}px`;
  138. } else {
  139. this.el.style.right = 'auto';
  140. }
  141. }
  142. },
  143. });
  144. // Set ecl initialized attribute
  145. this.element.setAttribute('data-ecl-auto-initialized', 'true');
  146. ECL.components.set(this.element, this);
  147. return this.picker;
  148. }
  149. /**
  150. * Destroy component.
  151. */
  152. destroy() {
  153. if (this.picker) {
  154. this.picker.destroy();
  155. this.picker = null;
  156. }
  157. if (this.element) {
  158. this.element.removeAttribute('data-ecl-auto-initialized');
  159. ECL.components.delete(this.element);
  160. }
  161. }
  162. }
  163. export default Datepicker;