/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import AddressOption from "../components/address-option.js";
import PaymentStateSubscriberMixin from "../mixins/PaymentStateSubscriberMixin.js";
import RichSelect from "../components/rich-select.js";
/**
*
* Container around (eventually providing add/edit links) with
* listening to savedAddresses.
*/
export default class AddressPicker extends PaymentStateSubscriberMixin(HTMLElement) {
static get observedAttributes() {
return ["address-fields"];
}
constructor() {
super();
this.dropdown = new RichSelect();
this.dropdown.addEventListener("change", this);
}
connectedCallback() {
this.appendChild(this.dropdown);
super.connectedCallback();
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.render(this.requestStore.getState());
}
}
/**
* De-dupe and filter addresses for the given set of fields that will be visible
*
* @param {object} addresses
* @param {array?} fieldNames - optional list of field names that be used when
* de-duping and excluding entries
* @returns {object} filtered copy of given addresses
*/
filterAddresses(addresses, fieldNames = [
"address-level1",
"address-level2",
"country",
"name",
"postal-code",
"street-address",
]) {
let uniques = new Set();
let result = {};
for (let [guid, address] of Object.entries(addresses)) {
let addressCopy = {};
let isMatch = false;
// exclude addresses that are missing all of the requested fields
for (let name of fieldNames) {
if (address[name]) {
isMatch = true;
addressCopy[name] = address[name];
}
}
if (isMatch) {
let key = JSON.stringify(addressCopy);
// exclude duplicated addresses
if (!uniques.has(key)) {
uniques.add(key);
result[guid] = address;
}
}
}
return result;
}
render(state) {
let {savedAddresses} = state;
let desiredOptions = [];
let fieldNames;
if (this.hasAttribute("address-fields")) {
let names = this.getAttribute("address-fields").split(/\s+/);
if (names.length) {
fieldNames = names;
}
}
let filteredAddresses = this.filterAddresses(savedAddresses, fieldNames);
for (let [guid, address] of Object.entries(filteredAddresses)) {
let optionEl = this.dropdown.getOptionByValue(guid);
if (!optionEl) {
optionEl = new AddressOption();
optionEl.value = guid;
}
for (let key of AddressOption.recordAttributes) {
let val = address[key];
if (val) {
optionEl.setAttribute(key, val);
} else {
optionEl.removeAttribute(key);
}
}
desiredOptions.push(optionEl);
}
let el = null;
while ((el = this.dropdown.popupBox.querySelector(":scope > address-option"))) {
el.remove();
}
for (let option of desiredOptions) {
this.dropdown.popupBox.appendChild(option);
}
// Update selectedness after the options are updated
let selectedAddressGUID = state[this.selectedStateKey];
let optionWithGUID = this.dropdown.getOptionByValue(selectedAddressGUID);
this.dropdown.selectedOption = optionWithGUID;
if (selectedAddressGUID && !optionWithGUID) {
throw new Error(`${this.selectedStateKey} option ${selectedAddressGUID}` +
`does not exist in options`);
}
}
get selectedStateKey() {
return this.getAttribute("selected-state-key");
}
handleEvent(event) {
switch (event.type) {
case "change": {
this.onChange(event);
break;
}
}
}
onChange(event) {
let select = event.target;
let selectedKey = this.selectedStateKey;
if (selectedKey) {
this.requestStore.setState({
[selectedKey]: select.selectedOption && select.selectedOption.guid,
});
}
}
}
customElements.define("address-picker", AddressPicker);