import { CartBaseController } from "./cart_base_controller"; /* * Populates a country field where users can type to filter and select * from a predefined list. */ export default class extends CartBaseController { // All are required! static targets = ["id", "iso", "list", "name"]; async connect() { const countries = await this.countries(); countries.forEach((country) => { const option = document.createElement("option"); option.value = country.attributes.name; option.dataset.id = country.id; option.dataset.iso = country.attributes.iso; option.dataset.statesRequired = country.attributes.states_required; option.dataset.zipcodeRequired = country.attributes.zipcode_required; this.listTarget.appendChild(option); }); this.input_event = this._input_event.bind(this); this.invalid_event = this._invalid_event.bind(this); this.change_event = this._change_event.bind(this); // Only allow names on this list this.nameTarget.pattern = countries.map((x) => x.attributes.name).join("|"); this.nameTarget.addEventListener("input", this.input_event); this.nameTarget.addEventListener("invalid", this.invalid_event); // When the input changes we update the actual value and also the // state list via an Event this.nameTarget.addEventListener("change", this.change_event); // The input is disabled at this point this.nameTarget.disabled = false; // Load data if the input is autocompleted if (this.nameTarget.value.trim() !== "") this.nameTarget.dispatchEvent(new CustomEvent("change")); } disconnect() { this.nameTarget.removeEventListener("input", this.input_event); this.nameTarget.removeEventListener("invalid", this.invalid_event); this.nameTarget.removeEventListener("change", this.change_event); } _input_event(event) { this.nameTarget.setCustomValidity(""); } _invalid_event(event) { const site = window.site; this.nameTarget.setCustomValidity(site.i18n.countries.validation); } _change_event(event) { const value = this.nameTarget.value.trim(); if (value === "") return; const options = Array.from(this.nameTarget.list.options); const option = options.find((x) => x.value == value); // TODO: If no option is found, mark the field as invalid if (!option) return; this.idTarget.value = option.dataset.id; this.isoTarget.value = option.dataset.iso; this.idTarget.dispatchEvent(new Event("change")); this.isoTarget.dispatchEvent(new Event("change")); this.dispatchChangedEvent(option.dataset); // XXX: Prevent mixing data delete this.nameTarget.dataset.selectedState; delete this.nameTarget.dataset.selectedZipcode; } /* * Sends a `cart:country:update` event so other controllers can * subscribe to changes. */ dispatchChangedEvent(data = {}) { const event = new CustomEvent("cart:country:update", { detail: { id: this.idTarget.value, iso: this.isoTarget.value, group: this.data.get("group"), selectedState: this.nameTarget.dataset.selectedState, selectedZipcode: this.nameTarget.dataset.selectedZipcode, data, }, }); window.dispatchEvent(event); } /* * Fetch the country list from storage or from API */ async countries() { const countries = JSON.parse(this.storageTemp.getItem("countries")); if (countries) return countries; const response = await this.spree.countries.list(); // TODO: Show error message if (!response.success()) return; this.storageTemp.setItem( "countries", JSON.stringify(response.success().data) ); return response.success().data; } }