Limpiar eventos cuando el elemento o controlador que los escucha desaparece

Squashed commit of the following:

commit 482eea28821868f03ace33562e7bd34ab9a4478f
Merge: 5f48528 1c128f2
Author: f <f@sutty.nl>
Date:   Thu Nov 25 18:31:35 2021 -0300

    Merge branch 'master' into limpiar-eventos

commit 5f48528c28b0709bd859a4dc52a830f60bfedc6e
Author: f <f@sutty.nl>
Date:   Thu Nov 25 18:23:23 2021 -0300

    pretty

commit 70d05bc90a6cb64d1c4bfc39f48388af3fbc3c18
Merge: c4f33c0 ff1bc21
Author: Nulo <nulo@sutty.nl>
Date:   Thu Oct 28 16:46:31 2021 -0300

    Merge branch 'master' into limpiar-eventos

commit c4f33c084058002a10fc0ec2137ffe045826cfd2
Author: f <f@sutty.nl>
Date:   Thu Oct 28 14:52:41 2021 -0300

    limpiar eventos
This commit is contained in:
Cat /dev/Nulo 2021-11-25 21:40:44 +00:00
parent 1c128f2a65
commit 3a0c41b736
12 changed files with 306 additions and 174 deletions

View file

@ -4,21 +4,29 @@ export default class extends CartBaseController {
static targets = ["form", "username"]; static targets = ["form", "username"];
connect() { connect() {
this.focusout_event = this._focusout_event.bind(this);
if (!this.hasUsernameTarget) return; if (!this.hasUsernameTarget) return;
if (!this.hasFormTarget) return; if (!this.hasFormTarget) return;
this.formTarget.addEventListener("focusout", (event) => { this.formTarget.addEventListener("focusout", this.focusout_event);
if (!this.formTarget.checkValidity()) { }
this.formTarget.classList.add("was-validated");
return;
}
this.formTarget.classList.remove("was-validated"); disconnect() {
this.formTarget.removeEventListener("focusout", this.focusout_event);
}
const username = this.usernameTarget.value.trim(); _focusout_event(event) {
if (username.length === 0) return; if (!this.formTarget.checkValidity()) {
this.formTarget.classList.add("was-validated");
return;
}
this.email = username; this.formTarget.classList.remove("was-validated");
});
const username = this.usernameTarget.value.trim();
if (username.length === 0) return;
this.email = username;
} }
} }

View file

@ -22,53 +22,61 @@ export default class extends CartBaseController {
connect() { connect() {
if (!this.hasQuantityTarget) return; if (!this.hasQuantityTarget) return;
this.change_event = this._change_event.bind(this);
/* /*
* When the quantity selector changes, we update the order to have * When the quantity selector changes, we update the order to have
* that amount of items. * that amount of items.
* *
* TODO: Go back to previous amount if there's not enough. * TODO: Go back to previous amount if there's not enough.
*/ */
this.quantityTarget.addEventListener("change", async (event) => { this.quantityTarget.addEventListener("change", this.change_event);
const quantity = event.target.value; }
if (quantity < 1) return; disconnect() {
this.quantityTarget.removeEventListener("change", this.change_event);
}
const orderToken = await this.tokenGetOrCreate(); async _change_event(event) {
const product = this.product; const quantity = event.target.value;
if (!product) return; if (quantity < 1) return;
event.target.disabled = true; const orderToken = await this.tokenGetOrCreate();
const product = this.product;
const response = await this.spree.cart.setQuantity( if (!product) return;
{ orderToken },
{
line_item_id: product.line_item.id,
quantity,
include: "line_items",
}
);
event.target.disabled = false; event.target.disabled = true;
event.target.focus();
// If we're failing here it could be due to a missing order, so we const response = await this.spree.cart.setQuantity(
// ask the user to decide what they want to do about it { orderToken },
if (response.isFail()) { {
this.handleFailure(response); line_item_id: product.line_item.id,
return; quantity,
include: "line_items",
} }
);
this.cart = response; event.target.disabled = false;
this.subtotalUpdate(); event.target.focus();
this.counterUpdate();
await this.itemStore();
if (!this.hasSubtotalTarget) return; // If we're failing here it could be due to a missing order, so we
// ask the user to decide what they want to do about it
if (response.isFail()) {
this.handleFailure(response);
return;
}
this.subtotalTarget.innerText = this.cart = response;
product.line_item.attributes.discounted_amount; this.subtotalUpdate();
}); this.counterUpdate();
await this.itemStore();
if (!this.hasSubtotalTarget) return;
this.subtotalTarget.innerText =
product.line_item.attributes.discounted_amount;
} }
subtotalUpdate() { subtotalUpdate() {

View file

@ -9,20 +9,31 @@ export default class extends CartBaseController {
return; return;
} }
window.addEventListener( this.cart_count_event = this._cart_count_event.bind(this);
"cart:counter", this.storage_event = this._storage_event.bind(this);
(event) => (this.counter = event.detail.item_count)
); window.addEventListener("cart:counter", this.cart_count_event);
window.addEventListener("storage", (event) => { window.addEventListener("storage", this.storage_event);
if (event.key == "cart:counter") this.counter = event.newValue;
});
if (!this.cart) return; if (!this.cart) return;
this.counter = this.cart.data.attributes.item_count; this.counter = this.cart.data.attributes.item_count;
} }
disconnect() {
window.removeEventListener("cart:counter", this.cart_count_event);
window.removeEventListener("storage", this.storage_event);
}
set counter(quantity) { set counter(quantity) {
this.counterTarget.innerText = quantity; this.counterTarget.innerText = quantity;
} }
_cart_count_event(event) {
this.counter = event.detail.item_count;
}
_storage_event(event) {
if (event.key == "cart:counter") this.counter = event.newValue;
}
} }

View file

@ -7,10 +7,18 @@ export default class extends CartBaseController {
static targets = ["couponCodeInvalid", "preDiscount", "total"]; static targets = ["couponCodeInvalid", "preDiscount", "total"];
connect() { connect() {
this.couponCode.addEventListener("input", (event) => { this.input_event = this._input_event.bind(this);
this.couponCode.parentElement.classList.remove("was-validated");
this.couponCode.setCustomValidity(""); this.couponCode.addEventListener("input", this.input_event);
}); }
disconnect() {
this.couponCode.removeEventListener("input", this.input_event);
}
_input_event(event) {
this.couponCode.parentElement.classList.remove("was-validated");
this.couponCode.setCustomValidity("");
} }
get couponCode() { get couponCode() {

View file

@ -10,6 +10,8 @@ export default class extends CartBaseController {
const orderToken = this.token; const orderToken = this.token;
const response = await this.spree.checkout.paymentMethods({ orderToken }); const response = await this.spree.checkout.paymentMethods({ orderToken });
this.change_event = this._change_event.bind(this);
if (response.isFail()) { if (response.isFail()) {
this.handleFailure(response); this.handleFailure(response);
return; return;
@ -31,10 +33,22 @@ export default class extends CartBaseController {
if (!this.hasSubmitTarget) return; if (!this.hasSubmitTarget) return;
this.formTarget.elements.forEach((p) => this.formTarget.elements.forEach((p) =>
p.addEventListener("change", (e) => (this.submitTarget.disabled = false)) p.addEventListener("change", this.change_event)
); );
} }
disconnect() {
if (!this.hasSubmitTarget) return;
this.formTarget.elements.forEach((p) =>
p.removeEventListener("change", this.change_event)
);
}
_change_event(event) {
this.submitTarget.disabled = false;
}
async pay(event) { async pay(event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();

View file

@ -4,9 +4,17 @@ export default class extends CartBaseController {
static targets = ["methods", "rates", "form"]; static targets = ["methods", "rates", "form"];
connect() { connect() {
this.formTarget.addEventListener("formdata", (event) => this.formdata_event = this._formdata_event.bind(this);
this.processShippingAddress(event.formData)
); this.formTarget.addEventListener("formdata", this.formdata_event);
}
disconnect() {
this.formTarget.removeEventListener("formdata", this.formdata_event);
}
_formdata_event(event) {
this.processShippingAddress(event.formData);
} }
async rates(event) { async rates(event) {

View file

@ -23,42 +23,18 @@ export default class extends CartBaseController {
this.listTarget.appendChild(option); this.listTarget.appendChild(option);
}); });
const site = window.site; 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 // Only allow names on this list
this.nameTarget.pattern = countries.map((x) => x.attributes.name).join("|"); this.nameTarget.pattern = countries.map((x) => x.attributes.name).join("|");
this.nameTarget.addEventListener("input", (event) => this.nameTarget.addEventListener("input", this.input_event);
this.nameTarget.setCustomValidity("") this.nameTarget.addEventListener("invalid", this.invalid_event);
);
this.nameTarget.addEventListener("invalid", (event) =>
this.nameTarget.setCustomValidity(site.i18n.countries.validation)
);
// When the input changes we update the actual value and also the // When the input changes we update the actual value and also the
// state list via an Event // state list via an Event
this.nameTarget.addEventListener("change", (event) => { this.nameTarget.addEventListener("change", this.change_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;
});
// The input is disabled at this point // The input is disabled at this point
this.nameTarget.disabled = false; this.nameTarget.disabled = false;
@ -67,6 +43,45 @@ export default class extends CartBaseController {
this.nameTarget.dispatchEvent(new CustomEvent("change")); 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 * Sends a `cart:country:update` event so other controllers can
* subscribe to changes. * subscribe to changes.

View file

@ -8,18 +8,26 @@ export default class extends Controller {
static targets = ["content"]; static targets = ["content"];
connect() { connect() {
window.addEventListener("toast", (event) => { this.toast_event = this._toast_event.bind(this);
this.contentTarget.innerText = event.detail.content;
this.set(true);
if (this.interval) { window.addEventListener("toast", this.toast_event);
clearTimeout(this.interval); }
}
this.interval = setTimeout(() => { disconnect() {
this.set(false); window.removeEventListener("toast", this.toast_event);
this.interval = null; }
}, 3000);
}); _toast_event(event) {
this.contentTarget.innerText = event.detail.content;
this.set(true);
if (this.interval) {
clearTimeout(this.interval);
}
this.interval = setTimeout(() => {
this.set(false);
this.interval = null;
}, 3000);
} }
set(show) { set(show) {

View file

@ -4,9 +4,17 @@ export default class extends Controller {
static targets = ["item"]; static targets = ["item"];
connect() { connect() {
window.addEventListener("scroll:section", (event) => this.scroll_section_event = this._scroll_section_event.bind(this);
this.update(event.detail.id)
); window.addEventListener("scroll:section", this.scroll_section_event);
}
disconnect() {
window.removeEventListener("scroll:section", this.scroll_section_event);
}
_scroll_section_event(event) {
this.update(event.detail.id);
} }
get items() { get items() {

View file

@ -15,28 +15,40 @@ export default class extends CartBaseController {
this.render({ products, site }); this.render({ products, site });
this.subtotalUpdate(); this.subtotalUpdate();
this.itemCountUpdate(); this.itemCountUpdate();
this.subscribe();
this.storage_event = this._storage_event.bind(this);
this.cart_subtotal_update_event =
this._cart_subtotal_update_event.bind(this);
window.addEventListener("storage", this.storage_event);
window.addEventListener(
"cart:subtotal:update",
this.cart_subtotal_update_event
);
} }
/* disconnect() {
* Subscribe to change on the storage to update the cart. window.removeEventListener("storage", this.storage_event);
*/ window.removeEventListener(
subscribe() { "cart:subtotal:update",
window.addEventListener("storage", async (event) => { this.cart_subtotal_update_event
if (!event.key?.startsWith("cart:item:")) return; );
}
const products = this.products; async _storage_event(event) {
const site = window.site; if (!event.key?.startsWith("cart:item:")) return;
this.render({ products, site }); const products = this.products;
this.subtotalUpdate(); const site = window.site;
this.itemCountUpdate();
});
window.addEventListener("cart:subtotal:update", (event) => { this.render({ products, site });
this.itemCountUpdate(); this.subtotalUpdate();
this.subtotalUpdate(); this.itemCountUpdate();
}); }
_cart_subtotal_update_event(event) {
this.itemCountUpdate();
this.subtotalUpdate();
} }
/* /*

View file

@ -175,24 +175,38 @@ export default class extends Controller {
}; };
connect() { connect() {
window.addEventListener("cart:country:update", (event) => { this.cart_country_update_event = this._cart_country_update_event.bind(this);
if (this.data.get("group") !== event.detail.group) return;
const zipcodeRequired = event.detail.data.zipcodeRequired == "true"; window.addEventListener(
"cart:country:update",
this.cart_country_update_event
);
}
this.codeTarget.value = ""; disconnect() {
this.codeTarget.disabled = !zipcodeRequired; window.removeEventListener(
this.codeTarget.required = zipcodeRequired; "cart:country:update",
this.cart_country_update_event
);
}
if (!zipcodeRequired) return; _cart_country_update_event(event) {
if (this.data.get("group") !== event.detail.group) return;
this.codeTarget.pattern = const zipcodeRequired = event.detail.data.zipcodeRequired == "true";
this.postal_codes[event.detail.iso.toLowerCase()] || ".*";
if (event.detail.selectedZipcode) { this.codeTarget.value = "";
this.codeTarget.value = event.detail.selectedZipcode; this.codeTarget.disabled = !zipcodeRequired;
this.codeTarget.dispatchEvent(new Event("change")); this.codeTarget.required = zipcodeRequired;
}
}); if (!zipcodeRequired) return;
this.codeTarget.pattern =
this.postal_codes[event.detail.iso.toLowerCase()] || ".*";
if (event.detail.selectedZipcode) {
this.codeTarget.value = event.detail.selectedZipcode;
this.codeTarget.dispatchEvent(new Event("change"));
}
} }
} }

View file

@ -10,57 +10,75 @@ export default class extends CartBaseController {
static targets = ["id", "list", "name"]; static targets = ["id", "list", "name"];
connect() { connect() {
window.addEventListener("cart:country:update", async (event) => { this.cart_country_update_event = this._cart_country_update_event.bind(this);
if (this.data.get("group") !== event.detail.group) return; this.change_event = this._change_event.bind(this);
this.idTarget.value = ""; window.addEventListener(
this.nameTarget.value = ""; "cart:country:update",
this.listTarget.innerHTML = ""; this.cart_country_update_event
);
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 // When the input changes we update the actual value and also the
// state list via an Event // state list via an Event
this.nameTarget.addEventListener("change", (event) => { this.nameTarget.addEventListener("change", this.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 disconnect() {
if (!option) return; window.removeEventListener(
"cart:country:update",
this.cart_country_update_event
);
this.nameTarget.removeEventListener("change", this.change_event);
}
this.idTarget.value = option.dataset.id; async _cart_country_update_event(event) {
this.idTarget.dispatchEvent(new Event("change")); 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"));
}
}
_change_event(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"));
} }
/* /*