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
|
margin: 0 auto .75em
|
||||||
|
|
||||||
.outfit-layer
|
outfit-layer
|
||||||
display: block
|
display: block
|
||||||
position: absolute
|
position: absolute
|
||||||
inset: 0
|
inset: 0
|
||||||
|
@ -58,7 +58,7 @@ body.items-show
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 100%
|
height: 100%
|
||||||
|
|
||||||
&:has(.outfit-layer[data-status="loading"])
|
&:has(outfit-layer[status="loading"])
|
||||||
background: gray
|
background: gray
|
||||||
|
|
||||||
.species-color-picker
|
.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
|
= turbo_frame_tag "item-preview" do
|
||||||
.outfit-viewer
|
.outfit-viewer
|
||||||
- @preview_outfit.visible_layers.each do |swf_asset|
|
- @preview_outfit.visible_layers.each do |swf_asset|
|
||||||
.outfit-layer
|
%outfit-layer
|
||||||
= image_tag swf_asset.image_url, alt: ""
|
= image_tag swf_asset.image_url, alt: ""
|
||||||
|
|
||||||
= form_for item_path(@item), method: :get, class: "species-color-picker",
|
= form_for item_path(@item), method: :get, class: "species-color-picker",
|
||||||
|
@ -43,5 +43,5 @@
|
||||||
%footer= t '.contributors.footer'
|
%footer= t '.contributors.footer'
|
||||||
|
|
||||||
- content_for :javascripts do
|
- content_for :javascripts do
|
||||||
= javascript_include_tag 'item-page', async: true
|
= javascript_include_tag 'outfit-viewer', async: true
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue