Add helpful layer interactions on pet appearance edit page

When you hover the row for a layer in the table, it highlights the
corresponding layer in the outfit viewer. And when you click anywhere
in the row, it opens the first link (usually the PNG image).
This commit is contained in:
Emi Matchu 2024-12-07 10:41:52 -08:00
parent d92e3288ab
commit b3f3b39aa0
4 changed files with 70 additions and 5 deletions

View file

@ -0,0 +1,42 @@
class SupportOutfitViewer extends HTMLElement {
#internals = this.attachInternals();
connectedCallback() {
this.addEventListener("mouseenter", this.#onMouseEnter, { capture: true });
this.addEventListener("mouseleave", this.#onMouseLeave, { capture: true });
this.addEventListener("click", this.#onClick);
this.#internals.states.add("ready");
}
// When a row is hovered, highlight its corresponding outfit viewer layer.
#onMouseEnter(e) {
if (!e.target.matches("tr")) return;
const id = e.target.querySelector("[data-field=id]").innerText;
const layer = this.querySelector(
`outfit-viewer [data-asset-id="${CSS.escape(id)}"]`,
);
layer.setAttribute("highlighted", "");
}
// When a row is unhovered, unhighlight its corresponding outfit viewer layer.
#onMouseLeave(e) {
if (!e.target.matches("tr")) return;
const id = e.target.querySelector("[data-field=id]").innerText;
const layer = this.querySelector(
`outfit-viewer [data-asset-id="${CSS.escape(id)}"]`,
);
layer.removeAttribute("highlighted");
}
// When clicking a row, redirect the click to the first link.
#onClick(e) {
const row = e.target.closest("tr");
if (row == null) return;
row.querySelector("[data-field=links] a").click();
}
}
customElements.define("support-outfit-viewer", SupportOutfitViewer);

View file

@ -108,3 +108,18 @@ outfit-viewer
&:has(outfit-layer:state(loading))
+outfit-viewer-loading
// If a layer has the `[highlighted]` attribute, it's brought to the front,
// and other layers are grayed out and blurred. We use this in the support
// outfit viewer, when you hover over a layer.
&:has(outfit-layer[highlighted])
outfit-layer[highlighted]
z-index: 999
// Filter everything behind the bottom-most highlighted layer, using a
// backdrop filter. This gives us the best visual consistency by applying
// effects to the entire backdrop, instead of each layer and then
// re-compositing them.
backdrop-filter: grayscale(1) brightness(1.5) blur(1px)
& ~ outfit-layer[highlighted]
backdrop-filter: none

View file

@ -18,14 +18,22 @@ support-outfit-viewer
border-radius: .5em
th, td
border: 1px solid $soft-border-color
border: 1px solid $module-border-color
font-size: .85em
padding: .25em .5em
text-align: left
> tbody
> tr > :nth-child(3) // links column
[data-field=links]
ul
list-style-type: none
display: flex
gap: .5em
// Once the component is ready, add some hints about potential interactions.
&:state(ready)
> table
> tbody > tr
cursor: zoom-in
&:hover
background: $module-bg-color

View file

@ -9,12 +9,12 @@
%tbody
- outfit.visible_layers.each do |swf_asset|
%tr
%th{scope: "row"}
%th{scope: "row", "data-field": "id"}
= swf_asset.id
%td
= swf_asset.zone.label
(##{swf_asset.zone.id})
%td
%td{"data-field": "links"}
%ul
- if swf_asset.image_url?
%li= link_to "PNG", swf_asset.image_url, target: "_blank"