Refactor item page outfit-layer to use Web Components
Instead of doing all this listening to Turbo events etc to know when outfit layers might have changed, making it a custom element and wiring in the behavior to its actual lifecycle makes it always Just Work!
This commit is contained in:
parent
857812610a
commit
3c415e9cd3
4 changed files with 27 additions and 36 deletions
23
app/assets/javascripts/outfit-viewer.js
Normal file
23
app/assets/javascripts/outfit-viewer.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
class OutfitLayer extends HTMLElement {
|
||||
connectedCallback() {
|
||||
setTimeout(() => this.#initializeImage(), 0);
|
||||
}
|
||||
|
||||
#initializeImage() {
|
||||
this.image = this.querySelector("img");
|
||||
if (!this.image) {
|
||||
throw new Error(`<outfit-layer> must contain an <img> tag`);
|
||||
}
|
||||
|
||||
this.image.addEventListener("load", () => this.#setStatus("loaded"));
|
||||
this.image.addEventListener("error", () => this.#setStatus("error"));
|
||||
|
||||
this.#setStatus(this.image.complete ? "loaded" : "loading");
|
||||
}
|
||||
|
||||
#setStatus(newStatus) {
|
||||
this.setAttribute("status", newStatus);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("outfit-layer", OutfitLayer);
|
|
@ -49,7 +49,7 @@ body.items-show
|
|||
|
||||
margin: 0 auto .75em
|
||||
|
||||
.outfit-layer
|
||||
outfit-layer
|
||||
display: block
|
||||
position: absolute
|
||||
inset: 0
|
||||
|
@ -58,7 +58,7 @@ body.items-show
|
|||
width: 100%
|
||||
height: 100%
|
||||
|
||||
&:has(.outfit-layer[data-status="loading"])
|
||||
&:has(outfit-layer[status="loading"])
|
||||
background: gray
|
||||
|
||||
.species-color-picker
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// When we load in a new preview, set the status on the images. We use this in
|
||||
// our CSS to show a loading state when needed.
|
||||
function setImageStatuses() {
|
||||
for (const layer of document.querySelectorAll(".outfit-layer")) {
|
||||
const isLoaded = layer.querySelector("img").complete;
|
||||
layer.setAttribute("data-status", isLoaded ? "loaded" : "loading");
|
||||
}
|
||||
}
|
||||
document.addEventListener("turbo:frame-render", () => setImageStatuses());
|
||||
document.addEventListener("turbo:load", () => setImageStatuses());
|
||||
|
||||
// When a preview image loads or fails, update its status. (Note that `load`
|
||||
// does not fire for images that were loaded from cache, which is why we need
|
||||
// both this and `setImageStatuses` when rendering new images!)
|
||||
document.addEventListener(
|
||||
"load",
|
||||
({ target }) => {
|
||||
if (target.matches(".outfit-layer img")) {
|
||||
target.closest(".outfit-layer").setAttribute("data-status", "loaded");
|
||||
}
|
||||
},
|
||||
{ capture: true },
|
||||
);
|
||||
document.addEventListener(
|
||||
"error",
|
||||
({ target }) => {
|
||||
if (target.matches(".outfit-layer img")) {
|
||||
target.closest(".outfit-layer").setAttribute("data-status", "error");
|
||||
}
|
||||
},
|
||||
{ capture: true },
|
||||
);
|
|
@ -16,7 +16,7 @@
|
|||
= turbo_frame_tag "item-preview" do
|
||||
.outfit-viewer
|
||||
- @preview_outfit.visible_layers.each do |swf_asset|
|
||||
.outfit-layer
|
||||
%outfit-layer
|
||||
= image_tag swf_asset.image_url, alt: ""
|
||||
|
||||
= form_for item_path(@item), method: :get, class: "species-color-picker",
|
||||
|
@ -43,5 +43,5 @@
|
|||
%footer= t '.contributors.footer'
|
||||
|
||||
- content_for :javascripts do
|
||||
= javascript_include_tag 'item-page', async: true
|
||||
= javascript_include_tag 'outfit-viewer', async: true
|
||||
|
||||
|
|
Loading…
Reference in a new issue