campaign_builder_controller.js
2 years ago
chart_controller.js
2 years ago
chart_geo_controller.js
2 years ago
chart_interval_controller.js
2 years ago
clipboard_controller.js
2 years ago
copy_report_controller.js
2 years ago
create_report_controller.js
2 years ago
dates_controller.js
2 years ago
delete_data_controller.js
2 years ago
delete_report_controller.js
2 years ago
easepick_controller.js
2 years ago
export_reports_controller.js
2 years ago
filters_controller.js
2 years ago
group_controller.js
2 years ago
import_reports_controller.js
2 years ago
migration_redirect_controller.js
2 years ago
modal_controller.js
2 years ago
plugin_group_options_controller.js
2 years ago
pruner_controller.js
2 years ago
quick_stats_controller.js
2 years ago
real_time_controller.js
2 years ago
rename_report_controller.js
2 years ago
report_controller.js
2 years ago
reset_analytics_controller.js
2 years ago
save_report_controller.js
2 years ago
select_input_controller.js
2 years ago
set_favorite_report_controller.js
2 years ago
sort_controller.js
2 years ago
sortable_reports_controller.js
2 years ago
table_columns_controller.js
2 years ago
dates_controller.js
182 lines
| 1 | import {Controller} from "@hotwired/stimulus" |
| 2 | import * as easepick from '@easepick/bundle' |
| 3 | |
| 4 | /** |
| 5 | * This controller is designed to work with either a relative range or an |
| 6 | * exact range. |
| 7 | * |
| 8 | * You can use a relative range by supplying a relativeRangeId value. |
| 9 | * |
| 10 | * You can use an exact range by supplying an exactStart and exactEnd value. |
| 11 | */ |
| 12 | export default class extends Controller { |
| 13 | static values = { |
| 14 | relativeRangeId: String, |
| 15 | exactStart: String, |
| 16 | exactEnd: String, |
| 17 | firstDayOfWeek: Number, // 0 is Sunday, 1 is Monday, etc... |
| 18 | cssUrl: String, |
| 19 | format: String |
| 20 | } |
| 21 | static targets = ["modal", "modalButton", "easepick", "apply", "relativeRange"] |
| 22 | |
| 23 | easepicker = undefined |
| 24 | exactStart = undefined |
| 25 | exactEnd = undefined |
| 26 | relativeRangeId = undefined |
| 27 | |
| 28 | connect() { |
| 29 | this.setFromValues() |
| 30 | this.enableEasepick() |
| 31 | this.updateUserInterface() |
| 32 | document.addEventListener('click', this.maybeClose) |
| 33 | } |
| 34 | |
| 35 | disconnect() { |
| 36 | document.removeEventListener('click', this.maybeClose) |
| 37 | } |
| 38 | |
| 39 | setFromValues() { |
| 40 | // If any values are empty strings, set them equal to undefined instead |
| 41 | this.exactStart = this.exactStartValue === "" ? undefined : new easepick.DateTime(this.exactStartValue) |
| 42 | this.exactEnd = this.exactEndValue === "" ? undefined : new easepick.DateTime(this.exactEndValue) |
| 43 | this.relativeRangeId = this.relativeRangeIdValue === "" ? undefined : this.relativeRangeIdValue |
| 44 | } |
| 45 | |
| 46 | maybeClose = (event) => { |
| 47 | const isOpen = this.modalTarget.classList.contains('show') |
| 48 | const isInComponent = this.element.contains(event.target) |
| 49 | |
| 50 | |
| 51 | if (isOpen && !isInComponent) { |
| 52 | this.closeModal() |
| 53 | } |
| 54 | } |
| 55 | |
| 56 | toggleModal(e) { |
| 57 | e.preventDefault(); |
| 58 | const isOpen = this.modalTarget.classList.contains('show') |
| 59 | isOpen ? this.closeModal() : this.openModal() |
| 60 | } |
| 61 | |
| 62 | apply() { |
| 63 | let detail = {} |
| 64 | |
| 65 | if (this.relativeRangeId) { |
| 66 | detail = {relativeRangeId: this.relativeRangeId} |
| 67 | } else { |
| 68 | detail = { |
| 69 | exactStart: this.exactStart.format('YYYY-MM-DD'), |
| 70 | exactEnd: this.exactEnd.format('YYYY-MM-DD') |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | document.dispatchEvent( |
| 75 | new CustomEvent('iawp:changeDates', { |
| 76 | detail |
| 77 | }) |
| 78 | ) |
| 79 | |
| 80 | // Update values so future resets reset to the last applied state |
| 81 | this.relativeRangeIdValue = detail.relativeRangeId |
| 82 | this.exactStartValue = detail.exactStart |
| 83 | this.exactEndValue = detail.exactEnd |
| 84 | |
| 85 | // Todo - This should happen in a central place from report_controller |
| 86 | this.closeModal() |
| 87 | } |
| 88 | |
| 89 | openModal() { |
| 90 | this.modalTarget.classList.add('show') |
| 91 | this.modalButtonTarget.classList.add('open') |
| 92 | this.easepickTarget.nextSibling.classList.add('show'); |
| 93 | document.getElementById('iawp-layout').classList.add('modal-open'); |
| 94 | } |
| 95 | |
| 96 | closeModal() { |
| 97 | this.modalTarget.classList.remove('show') |
| 98 | this.modalButtonTarget.classList.remove('open') |
| 99 | document.getElementById('iawp-layout').classList.remove('modal-open'); |
| 100 | this.setFromValues() |
| 101 | this.updateUserInterface() |
| 102 | setTimeout(() => { |
| 103 | this.easepickTarget.nextSibling.classList.remove('show'); |
| 104 | }, 200) |
| 105 | } |
| 106 | |
| 107 | relativeRangeSelected = (e) => { |
| 108 | this.exactStart = undefined |
| 109 | this.exactEnd = undefined |
| 110 | this.relativeRangeId = e.target.dataset.relativeRangeId |
| 111 | this.updateUserInterface() |
| 112 | } |
| 113 | |
| 114 | exactRangeSelected = (e) => { |
| 115 | this.exactStart = e.detail.start |
| 116 | this.exactEnd = e.detail.end |
| 117 | this.relativeRangeId = undefined |
| 118 | this.updateUserInterface() |
| 119 | } |
| 120 | |
| 121 | updateUserInterface() { |
| 122 | const isExact = this.exactStart && this.exactEnd && !this.relativeRangeId |
| 123 | |
| 124 | // Select the correct relative range button, if any |
| 125 | this.relativeRangeTargets.forEach((button) => { |
| 126 | const match = button.dataset.relativeRangeId === this.relativeRangeId |
| 127 | button.classList.toggle('active', !match) |
| 128 | button.classList.toggle('active', match) |
| 129 | }) |
| 130 | |
| 131 | if (isExact) { |
| 132 | this.easepicker.setDateRange(this.exactStart, this.exactEnd) |
| 133 | this.applyTarget.textContent = iawpText.exactDates |
| 134 | } else { |
| 135 | const [start, end] = this.relativeRangeDates() |
| 136 | this.easepicker.setDateRange(start, end) |
| 137 | this.applyTarget.textContent = iawpText.relativeDates |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | relativeRangeDates() { |
| 142 | const button = this.relativeRangeTargets.find( |
| 143 | (button) => button.dataset.relativeRangeId === this.relativeRangeId |
| 144 | ) |
| 145 | const start = new easepick.DateTime(button.dataset.relativeRangeStart); |
| 146 | const end = new easepick.DateTime(button.dataset.relativeRangeEnd); |
| 147 | return [start, end] |
| 148 | } |
| 149 | |
| 150 | enableEasepick() { |
| 151 | let start, end |
| 152 | |
| 153 | if (this.relativeRangeId) { |
| 154 | [start, end] = this.relativeRangeDates() |
| 155 | } else { |
| 156 | start = this.exactStart |
| 157 | end = this.exactEnd |
| 158 | } |
| 159 | |
| 160 | this.easepicker = new easepick.create({ |
| 161 | element: this.easepickTarget, |
| 162 | css: [this.cssUrlValue], |
| 163 | inline: true, |
| 164 | firstDay: this.firstDayOfWeekValue, |
| 165 | format: this.formatValue, // Todo - Does this work? |
| 166 | calendars: 2, |
| 167 | grid: 2, |
| 168 | autoApply: true, |
| 169 | plugins: [ |
| 170 | easepick.RangePlugin |
| 171 | ], |
| 172 | RangePlugin: { |
| 173 | startDate: start.format(this.formatValue), |
| 174 | endDate: end.format(this.formatValue), |
| 175 | }, |
| 176 | setup: (picker) => { |
| 177 | picker.on('select', this.exactRangeSelected) |
| 178 | this.easepickTarget.nextSibling.classList.add('inline') |
| 179 | } |
| 180 | }) |
| 181 | } |
| 182 | } |