Compare commits

..

No commits in common. "77ff55353c36ebd55fc033bf38d101749246aa1d" and "a184c7557584e103e80876bad67e9d6f95f9aa2b" have entirely different histories.

5 changed files with 24 additions and 133 deletions

View file

@ -1,6 +1,6 @@
// When the species face picker changes, update and submit the main picker form. // When the species *face* picker changes, update and submit the main picker form.
document.addEventListener("change", (e) => { document.addEventListener("click", (e) => {
if (!e.target.matches("species-face-picker")) return; if (!e.target.matches(".species-face-picker input[type=radio]")) return;
try { try {
const mainPicker = document.querySelector("#item-preview .species-color-picker"); const mainPicker = document.querySelector("#item-preview .species-color-picker");
@ -14,43 +14,8 @@ document.addEventListener("change", (e) => {
} }
}); });
class SpeciesFacePicker extends HTMLElement { // Now that the face picker is ready to go, mark it as usable.
connectedCallback() { for (const options of document.querySelectorAll(".species-face-picker-options")) {
this.addEventListener("click", this.#handleClick); options.removeAttribute("inert");
} options.removeAttribute("aria-hidden");
get value() {
return this.querySelector("input[type=radio]:checked")?.value;
}
#handleClick(e) {
if (e.target.matches("input[type=radio]")) {
this.dispatchEvent(new Event("change", {bubbles: true}));
}
}
} }
class SpeciesFacePickerOptions extends HTMLElement {
static observedAttributes = ["inert", "aria-hidden"];
connectedCallback() {
// Once this component is loaded, we stop being inert and aria-hidden. We're ready!
this.#activate();
}
attributeChangedCallback() {
// If a Turbo Frame tries to morph us into being inert again, activate again!
// (It's important that the server's HTML always return `inert`, for progressive
// enhancement; and it's important to morph this element, so radio focus state
// is preserved. To thread that needle, we have to monitor and remove!)
this.#activate();
}
#activate() {
this.removeAttribute("inert");
this.removeAttribute("aria-hidden");
}
}
customElements.define("species-face-picker", SpeciesFacePicker);
customElements.define("species-face-picker-options", SpeciesFacePickerOptions);

View file

@ -161,46 +161,9 @@ body.items-show
border-color: $error-border-color border-color: $error-border-color
color: $error-color color: $error-color
species-face-picker .species-face-picker
display: block
position: relative position: relative
species-face-picker-options
display: flex
flex-wrap: wrap
img
width: 50px
height: 50px
transition: all 0.2s
// Calm down the default color, just a smidge! There's a lot of color
// on this page already, y'know?
opacity: .9
filter: saturate(90%)
label
display: flex
overflow: hidden
transition: all 0.2s
position: relative
line-height: 1
// NOTE: The box-shadows here were copy-pasted from Impress 2020, which uses
// Chakra UI's styling system to generate them! (The colors are from their
// color palette, too.)
&:has(input:checked)
border-radius: 6px
z-index: 1
background: #9AE6B4
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1),0 10px 10px -5px rgba(0, 0, 0, 0.04), #2F855A 0 0 2px 2px
transform: scale(1.1)
&:has(input:focus)
background: #BEE3F8
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1),0 10px 10px -5px rgba(0, 0, 0, 0.04), #4299e1 0 0 0 3px
transform: scale(1.2)
input[type=radio] input[type=radio]
position: absolute position: absolute
left: -10000px left: -10000px
@ -209,30 +172,21 @@ body.items-show
height: 1px height: 1px
overflow: hidden overflow: hidden
&:checked + img &:not(:checked) + img
opacity: 1 opacity: .5
filter: saturate(110%)
&:disabled + img
opacity: .6
filter: saturate(0%)
label:has(input[type=radio]:disabled)
cursor: not-allowed
noscript noscript
position: absolute position: absolute
inset: 0 inset: 0
background: rgba(white, .75) background: rgba(white, .75)
z-index: 1 z-index: 1
cursor: auto
display: flex display: flex
align-items: center align-items: center
justify-content: center justify-content: center
text-align: center text-align: center
&:has(species-face-picker-options[inert]) &:has(.species-face-picker-options[inert])
cursor: wait cursor: wait
.item-zones-info .item-zones-info

View file

@ -33,9 +33,7 @@ module ItemsHelper
def standard_species_search_links def standard_species_search_links
all_species = Species.alphabetical.map(&:id) all_species = Species.alphabetical.map(&:id)
PetType.random_basic_per_species(all_species).map do |pet_type| PetType.random_basic_per_species(all_species).map do |pet_type|
human_name = pet_type.species.human_name image = pet_type_image(pet_type, :happy, :zoom)
image = pet_type_image pet_type, :happy, :zoom,
alt: human_name, title: human_name
query = "species:#{pet_type.species.name}" query = "species:#{pet_type.species.name}"
link_to(image, items_path(:q => query)) link_to(image, items_path(:q => query))
end.join.html_safe end.join.html_safe
@ -224,18 +222,6 @@ module ItemsHelper
cookies["DTIOutfitViewerIsPlaying"] == "true" cookies["DTIOutfitViewerIsPlaying"] == "true"
end end
def item_fits?(item, pet_type)
item.appearances.any? { |a| a.fits? pet_type }
end
def species_face_tooltip(pet_type, item)
if item_fits?(item, pet_type)
"#{pet_type.species.human_name}"
else
"#{pet_type.species.human_name}: No data yet"
end
end
def item_zone_partial_fit?(appearances_in_zone, all_appearances) def item_zone_partial_fit?(appearances_in_zone, all_appearances)
appearances_in_zone.size < all_appearances.size appearances_in_zone.size < all_appearances.size
end end
@ -244,13 +230,12 @@ module ItemsHelper
appearances_in_zone.map(&:species).uniq.map(&:human_name).sort.join(", ") appearances_in_zone.map(&:species).uniq.map(&:human_name).sort.join(", ")
end end
def pet_type_image(pet_type, emotion, size, **options) private
src = pet_type_image_url(pet_type, emotion:, size:)
srcset = if size == :face
[[pet_type_image_url(pet_type, emotion:, size: :face_2x), "2x"]]
end
image_tag(src, srcset:, **options) def pet_type_image(pet_type, emotion, size)
src = pet_type_image_url(pet_type, emotion:, size:)
human_name = pet_type.species.name.humanize
image_tag(src, :alt => human_name, :title => human_name)
end end
def item_header_user_lists_form_state def item_header_user_lists_form_state

View file

@ -502,7 +502,7 @@ class Item < ApplicationRecord
Appearance = Struct.new(:item, :body, :swf_assets) do Appearance = Struct.new(:item, :body, :swf_assets) do
include ActiveModel::Serializers::JSON include ActiveModel::Serializers::JSON
delegate :present?, :empty?, to: :swf_assets delegate :present?, :empty?, to: :swf_assets
delegate :species, :fits?, :fits_all?, to: :body delegate :species, to: :body
def attributes def attributes
{item:, body:, swf_assets:} {item:, body:, swf_assets:}
@ -522,14 +522,6 @@ class Item < ApplicationRecord
def attributes def attributes
{id:, species:} {id:, species:}
end end
def fits_all?
id == 0
end
def fits?(target)
fits_all? || target.body_id == id
end
end end
def appearances def appearances

View file

@ -33,24 +33,19 @@
"id", "human_name", @selected_preview_pet_type.species_id) "id", "human_name", @selected_preview_pet_type.species_id)
= submit_tag "Go", name: nil = submit_tag "Go", name: nil
%species-face-picker %form.species-face-picker
%noscript %noscript
This fancy species picker requires Javascript, but you can still use the This fancy species picker requires Javascript, but you can still use the
dropdowns instead! dropdowns instead!
%species-face-picker-options{ .species-face-picker-options{
inert: true, # waits for JS to remove "inert": true, # waits for JS to remove
"aria-hidden": true, # waits for JS to remove "aria-hidden": true, # waits for JS to remove
} }
- @preview_pet_type_options.each do |pet_type| - @preview_pet_type_options.each do |pet_type|
%label{ %label
title: species_face_tooltip(pet_type, @item),
}
= radio_button_tag "species_face_id", pet_type.species_id, = radio_button_tag "species_face_id", pet_type.species_id,
checked: pet_type == @preview_outfit.pet_type, checked: pet_type == @preview_outfit.pet_type
disabled: !item_fits?(@item, pet_type) = pet_type_image pet_type, :happy, :face
= pet_type_image pet_type,
item_fits?(@item, pet_type) ? :happy : :sad,
:face
.item-zones-info .item-zones-info
%section %section