import { CartBaseController } from "./cart_base_controller"; /* * Populates a state field where users can type to filter and select * from a predefined list. It waits for an `cart:country:update` event * to become populated. */ export default class extends CartBaseController { // All are required! static targets = ["id", "list", "name"]; connect() { window.addEventListener("cart:country:update", async (event) => { if (this.data.get("group") !== event.detail.group) return; this.idTarget.value = ""; this.nameTarget.value = ""; this.listTarget.innerHTML = ""; const statesRequired = event.detail.data.statesRequired == "true"; this.nameTarget.disabled = !statesRequired; this.nameTarget.required = statesRequired; if (!statesRequired) return; const states = await this.states(event.detail.iso); const site = window.site; states.forEach((state) => { let option = document.createElement("option"); option.value = state.attributes.name; option.dataset.id = state.id; this.listTarget.appendChild(option); }); this.nameTarget.pattern = states.map((x) => x.attributes.name).join("|"); this.nameTarget.addEventListener("input", (event) => this.nameTarget.setCustomValidity("") ); this.nameTarget.addEventListener("invalid", (event) => this.nameTarget.setCustomValidity(site.i18n.states.validation) ); if (event.detail.selectedState) { this.nameTarget.value = event.detail.selectedState; this.nameTarget.dispatchEvent(new Event("change")); } }); // When the input changes we update the actual value and also the // state list via an Event this.nameTarget.addEventListener("change", (event) => { const options = Array.from(this.listTarget.options); const option = options.find((x) => x.value == this.nameTarget.value); // TODO: If no option is found, mark the field as invalid if (!option) return; this.idTarget.value = option.dataset.id; this.idTarget.dispatchEvent(new Event("change")); }); } /* * Fetch the state list from storage or from API using a country ISO * code */ async states(countryIso) { const stateId = `states:${countryIso}`; let states = JSON.parse(this.storageTemp.getItem(stateId)); if (states) return states; // There's no state query, but we can fetch the country and include // its states. const response = await this.spree.countries.show(countryIso, { include: "states", }); // TODO: Show error message if (response.isFail()) { this.handleFailure(response); return {}; } states = response.success().included; // Order alphabetically by name states.sort((x, y) => x.attributes.name > y.attributes.name); this.storageTemp.setItem(stateId, JSON.stringify(states)); return states; } }