Compare commits
5 commits
0a9193aed7
...
81a58f8656
Author | SHA1 | Date | |
---|---|---|---|
81a58f8656 | |||
76d2cc6c21 | |||
e9145251a9 | |||
3c415e9cd3 | |||
857812610a |
6 changed files with 45 additions and 48 deletions
31
app/assets/javascripts/outfit-viewer.js
Normal file
31
app/assets/javascripts/outfit-viewer.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
class OutfitLayer extends HTMLElement {
|
||||||
|
#internals;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.#internals = this.attachInternals();
|
||||||
|
}
|
||||||
|
|
||||||
|
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.#internals.states.clear();
|
||||||
|
this.#internals.states.add(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:state(loading))
|
||||||
background: gray
|
background: gray
|
||||||
|
|
||||||
.species-color-picker
|
.species-color-picker
|
||||||
|
|
|
@ -244,15 +244,5 @@ module ItemsHelper
|
||||||
def item_header_user_lists_form_state
|
def item_header_user_lists_form_state
|
||||||
cookies.fetch("DTIItemPageUserListsFormState", "closed")
|
cookies.fetch("DTIItemPageUserListsFormState", "closed")
|
||||||
end
|
end
|
||||||
|
|
||||||
def outfit_viewer_layers(swf_assets)
|
|
||||||
swf_assets.map { |a| outfit_viewer_layer(a) }.join("\n").html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
def outfit_viewer_layer(swf_asset)
|
|
||||||
content_tag :div, class: "outfit-layer" do
|
|
||||||
image_tag swf_asset.image_url, alt: ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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 },
|
|
||||||
);
|
|
9
app/views/items/_outfit_viewer.html.haml
Normal file
9
app/views/items/_outfit_viewer.html.haml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.outfit-viewer
|
||||||
|
- outfit.visible_layers.each do |swf_asset|
|
||||||
|
%outfit-layer{
|
||||||
|
data: {
|
||||||
|
"asset-id": swf_asset.id,
|
||||||
|
"zone": swf_asset.zone.label,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
= image_tag swf_asset.image_url, alt: ""
|
|
@ -14,8 +14,7 @@
|
||||||
sorry!
|
sorry!
|
||||||
|
|
||||||
= turbo_frame_tag "item-preview" do
|
= turbo_frame_tag "item-preview" do
|
||||||
.outfit-viewer
|
= render partial: "outfit_viewer", locals: {outfit: @preview_outfit}
|
||||||
= outfit_viewer_layers @preview_outfit.visible_layers
|
|
||||||
|
|
||||||
= form_for item_path(@item), method: :get, class: "species-color-picker",
|
= form_for item_path(@item), method: :get, class: "species-color-picker",
|
||||||
data: {"is-valid": @preview_error.nil?} do |f|
|
data: {"is-valid": @preview_error.nil?} do |f|
|
||||||
|
@ -40,6 +39,6 @@
|
||||||
%li= link_to(contributor.name, user_contributions_path(contributor)) + format_contribution_count(count)
|
%li= link_to(contributor.name, user_contributions_path(contributor)) + format_contribution_count(count)
|
||||||
%footer= t '.contributors.footer'
|
%footer= t '.contributors.footer'
|
||||||
|
|
||||||
- content_for :javascripts_body do
|
- content_for :javascripts do
|
||||||
= javascript_include_tag 'item-page', defer: true
|
= javascript_include_tag 'outfit-viewer', async: true
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue