// import Flatpickr import Flatpickr from "stimulus-flatpickr"; import ShortcutButtonsPlugin from "shortcut-buttons-flatpickr"; import labelPlugin from "flatpickr/dist/plugins/labelPlugin/labelPlugin"; export default class extends Flatpickr { /* * defaultDate (optional): "startOfDay" | "endOfDay" */ static values = { enableTime: Boolean, mode: String, defaultDate: String }; static targets = ["start", "end"]; initialize() { const datetimepicker = this.enableTimeValue === true; const mode = this.modeValue === "range" ? "range" : "single"; // configure flatpickr options (locale set dynamically in connect()) this.config = { altInput: true, altFormat: datetimepicker ? Spree.translations.flatpickr_datetime_format : Spree.translations.flatpickr_date_format, dateFormat: datetimepicker ? "Y-m-d H:i" : "Y-m-d", enableTime: datetimepicker, time_24hr: datetimepicker, plugins: this.plugins(mode, datetimepicker), mode, }; } async connect() { const locale = await this.importFlatpickrLocale(I18n.base_locale); this.config = { ...this.config, locale, }; super.connect(); window.addEventListener("flatpickr:change", this.onChangeEvent); window.addEventListener("flatpickr:clear", this.clear); } disconnect() { super.disconnect(); window.removeEventListener("flatpickr:change", this.onChangeEvent); window.removeEventListener("flatpickr:clear", this.clear); } clear = (e) => { this.fp.setDate(null); }; open() { this.fp.element.dispatchEvent(new Event("focus")); if (!this.fp.selectedDates.length) { this.setDefaultDateValue(); } } onChangeEvent = (e) => { if (this.modeValue === "range" && this.hasStartTarget && this.hasEndTarget) { // date range mode if (e.detail) { this.startTarget.value = e.detail.startDate; this.endTarget.value = e.detail.endDate; } this.fp.setDate([this.startTarget.value, this.endTarget.value]); } else if (e.detail.date) { // single date mode this.fp.setDate(e.detail.date); } }; change(selectedDates, dateStr, instance) { if (this.hasStartTarget && this.hasEndTarget && this.modeValue === "range") { this.startTarget.value = selectedDates[0] ? this.fp.formatDate(selectedDates[0], this.config.dateFormat) : ""; this.endTarget.value = selectedDates[1] ? this.fp.formatDate(selectedDates[1], this.config.dateFormat) : ""; // Also, send event to be sure that ng-model is well updated // Send event only if range il valid, ie. start and end are not empty if (this.startTarget.value && this.endTarget.value) { this.startTarget.dispatchEvent(new Event("change")); this.endTarget.dispatchEvent(new Event("change")); } } } close() { // Send a change event to the input element to trigger the ng-change this.hasEndTarget && this.endTarget.dispatchEvent(new Event("change")); } // private plugins = (mode, datetimepicker) => { const buttons = [{ label: Spree.translations.close }]; if (mode === "single") { buttons.unshift({ label: datetimepicker ? Spree.translations.now : Spree.translations.today, }); } return [ ShortcutButtonsPlugin({ button: buttons, onClick: this.onClickButtons, }), labelPlugin({}), ]; }; onClickButtons = (index, fp) => { // Memorize index used for the 'Close' and 'Today|Now' buttons // it has index of 1 in case of single mode (ie. can set Today or Now date) // it has index of 0 in case of range mode (no Today or Now button) const closeButtonIndex = this.modeValue === "range" ? 0 : 1; const todayButtonIndex = this.modeValue === "range" ? null : 0; switch (index) { case todayButtonIndex: fp.setDate(new Date(), true); break; case closeButtonIndex: fp.close(); break; } }; setDefaultDateValue() { if (this.defaultDateValue === "startOfDay") { this.fp.setDate(moment().startOf("day").format()); } else if (this.defaultDateValue === "endOfDay") { /* * We use "startOf('day')" of tomorrow in order to not lose * the records between [23:59:00 ~ 23:59:59] of today */ this.fp.setDate(moment().add(1, "days").startOf("day").format()); } } async importFlatpickrLocale(localeCode) { // null tells flatpickr to fall back to its built-in english locale if (!localeCode || localeCode === "en") return null; try { const localeModule = await import(`flatpickr/dist/l10n/${localeCode}.js`); return localeModule.default?.[localeCode] ?? null; } catch { return null; } } }