Compare commits

..

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

14 changed files with 38 additions and 154 deletions

View file

@ -3,36 +3,17 @@ document.addEventListener("change", (e) => {
if (!e.target.matches("species-face-picker")) return;
try {
const mainPickerForm = document.querySelector(
"#item-preview species-color-picker form");
const mainPicker = document.querySelector("#item-preview .species-color-picker");
const mainSpeciesField =
mainPickerForm.querySelector("[name='preview[species_id]']");
mainPicker.querySelector("[name='preview[species_id]']");
mainSpeciesField.value = e.target.value;
mainPickerForm.requestSubmit(); // `submit` doesn't get captured by Turbo!
mainPicker.requestSubmit(); // `submit` doesn't get captured by Turbo!
} catch (error) {
e.preventDefault();
console.error("Couldn't update species picker: ", error);
}
});
class SpeciesColorPicker extends HTMLElement {
#internals;
constructor() {
super();
this.#internals = this.attachInternals();
}
connectedCallback() {
// Listen for changes to auto-submit the form, then tell CSS about it!
this.addEventListener("change", this.#handleChange);
this.#internals.states.add("auto-loading");
}
#handleChange(e) {
this.querySelector("form").requestSubmit();
}
}
class SpeciesFacePicker extends HTMLElement {
connectedCallback() {
this.addEventListener("click", this.#handleClick);
@ -71,6 +52,5 @@ class SpeciesFacePickerOptions extends HTMLElement {
}
}
customElements.define("species-color-picker", SpeciesColorPicker);
customElements.define("species-face-picker", SpeciesFacePicker);
customElements.define("species-face-picker-options", SpeciesFacePickerOptions);

View file

@ -165,12 +165,9 @@ class OutfitLayer extends HTMLElement {
#sendMessageToIframe(message) {
// If we have no frame or it hasn't loaded, ignore this message.
if (this.iframe == null) {
return;
}
if (this.iframe.contentWindow == null) {
if (this.iframe?.contentWindow == null) {
console.debug(
`Ignoring message, frame not loaded yet: `,
`Ignoring message, frame not loaded: `,
this.iframe,
message,
);

View file

@ -5,15 +5,9 @@ body.items-index, body.items-show, body.items-needed, body.item_trades
text-align: center
.item-search-form
display: flex
gap: .5em
justify-content: center
input[type=text]
font-size: 125%
width: 15em
flex: 0 1 auto
input[type=text]
font-size: 125%
width: 15em
h1
margin-bottom: 1em

View file

@ -1,12 +0,0 @@
body.use-responsive-design
#container
max-width: 100%
padding-inline: 1rem
box-sizing: border-box
#home-link
margin-left: 1rem
padding-inline: 0
#userbar
margin-right: 1rem

View file

@ -4,7 +4,6 @@
@import partials/clean/mixins
@import layout
@import responsive
@import partials/jquery.jgrowl

View file

@ -46,7 +46,7 @@ body.items-show
border: 1px solid $module-border-color
border-radius: 1em
overflow: hidden
margin: 0 auto
margin: 0 auto .75em
// There's no useful text in here, but double-clicking the play/pause
// button can cause a weird selection state. Disable text selection.
@ -151,42 +151,22 @@ body.items-show
.error-indicator
display: block
species-color-picker
.species-color-picker
.error-icon
cursor: help
margin-right: .25em
form[data-is-valid="false"]
&[data-is-valid="false"]
select
border-color: $error-border-color
color: $error-color
// If JS is enabled, but auto-loading isn't ready yet (script loading or
// failed?), hide the submit button for .75sec, to give it time to load.
@media (scripting: enabled)
input[type=submit]
position: absolute
margin-left: .5em
opacity: 0
animation: fade-in .25s forwards
animation-delay: .75s
// Once the auto-loading behavior is ready, remove the submit button.
&:state(auto-loading)
input[type=submit]
display: none
species-face-picker
display: block
position: relative
max-height: 200px // 4 rows of 50px images, and padding will offer a hint of below
padding: 10px // leave enough room for the zoomed-in selected face
margin-top: -10px
overflow: auto
species-face-picker-options
display: flex
justify-content: center
flex-wrap: wrap
img
@ -256,6 +236,8 @@ body.items-show
cursor: wait
.item-zones-info
margin-top: .5em
h3
display: inline
font: inherit
@ -279,35 +261,3 @@ body.items-show
.zone-species-info
font-style: italic
text-decoration: underline dotted
#item-preview
display: flex
flex-direction: column
gap: .75em
@media (min-width: 600px)
display: grid
grid-template-areas: "viewer faces" "picker zones"
gap: .5em
outfit-viewer
grid-area: viewer
width: 350px
height: 350px
species-color-picker
grid-area: picker
species-face-picker
grid-area: faces
max-height: 350px
margin: -10px
.item-zones-info
grid-area: zones
@keyframes fade-in
from
opacity: 0
to
opacity: 1

View file

@ -35,7 +35,6 @@
text-align: left
display: flex
align-items: center
flex-wrap: wrap
gap: 1em
abbr
@ -128,7 +127,6 @@
.item-subpages-nav
display: flex
align-items: flex-end
gap: 1em
.preview-link
margin-right: auto
@ -169,4 +167,4 @@
background: $background-color
padding-bottom: calc(.5em + 1px)
font-weight: bold
margin-bottom: -1px
margin-bottom: -1px

View file

@ -95,7 +95,7 @@ class ItemsController < ApplicationController
sort_by { |z, a| z.label }
@preview_pet_type_options = PetType.where(color: @preview_outfit.color).
includes(:species).merge(Species.alphabetical)
joins(:species).merge(Species.alphabetical)
end
format.gif do

View file

@ -1,6 +1,6 @@
module ApplicationHelper
include FragmentLocalization
def absolute_url(path_or_url)
if path_or_url.include?('://') # already an absolute URL
path_or_url
@ -231,15 +231,6 @@ module ApplicationHelper
@hide_title_header = true
end
def use_responsive_design
@use_responsive_design = true
add_body_class "use-responsive-design"
end
def use_responsive_design?
@use_responsive_design || false
end
def signed_in_meta_tag
%(<meta name="user-signed-in" content="#{user_signed_in?}">).html_safe
end

View file

@ -533,10 +533,6 @@ class Item < ApplicationRecord
end
def appearances
@appearances ||= build_appearances
end
def build_appearances
all_swf_assets = swf_assets.to_a
# If there are no assets yet, there are no appearances.
@ -555,10 +551,10 @@ class Item < ApplicationRecord
# Otherwise, create an appearance for each real (nonzero) body ID. We don't
# generally expect body_id = 0 and body_id != 0 to mix, but if they do,
# uhh, let's merge the body_id = 0 ones in?
species_by_body_id = Species.with_body_ids(swf_assets_by_body_id.keys)
swf_assets_by_body_id.map do |body_id, body_specific_assets|
swf_assets_for_body = body_specific_assets + swf_assets_for_all_bodies
body = Appearance::Body.new(body_id, species_by_body_id[body_id])
species = Species.with_body_id(body_id).first!
body = Appearance::Body.new(body_id, species)
Appearance.new(self, body, swf_assets_for_body)
end
end

View file

@ -3,6 +3,11 @@ class Species < ApplicationRecord
has_many :alt_styles
scope :alphabetical, -> { order(:name) }
scope :with_body_id, -> body_id {
pt = PetType.arel_table
joins(:pet_types).where(pt[:body_id].eq(body_id)).limit(1)
}
def as_json(options={})
super({only: [:id, :name], methods: [:human_name]}.merge(options))
@ -15,15 +20,4 @@ class Species < ApplicationRecord
I18n.translate('species.default_human_name')
end
end
# Given a list of body IDs, return a hash from body ID to Species.
# (We assume that each body ID belongs to just one species; if not, which
# species we return for that body ID is undefined.)
def self.with_body_ids(body_ids)
species_ids_by_body_id = PetType.where(body_id: body_ids).distinct.
pluck(:body_id, :species_id).to_h
species_by_id = Species.where(id: species_ids_by_body_id.values).
to_h { |s| [s.id, s] }
species_ids_by_body_id.transform_values { |id| species_by_id[id] }
end
end

View file

@ -1,6 +1,5 @@
- title @item.name
- canonical_path @item
- use_responsive_design
= render partial: "item_header",
locals: {item: @item, trades: @trades, current_subpage: "preview",
@ -19,20 +18,20 @@
.error-indicator
💥 We couldn't load all of this outfit. Try again?
%species-color-picker
= form_for item_path(@item), method: :get, data: {"is-valid": @preview_error.nil?} do |f|
- if @preview_error == :pet_type_does_not_exist
%span.error-icon{title: "We haven't seen this kind of pet before."} ⚠️
- elsif @preview_error == :no_item_data
%span.error-icon{title: "We haven't seen this item on this pet before."} ⚠️
= form_for item_path(@item), method: :get, class: "species-color-picker",
data: {"is-valid": @preview_error.nil?} do |f|
- if @preview_error == :pet_type_does_not_exist
%span.error-icon{title: "We haven't seen this kind of pet before."} ⚠️
- elsif @preview_error == :no_item_data
%span.error-icon{title: "We haven't seen this item on this pet before."} ⚠️
= select_tag "preview[color_id]",
options_from_collection_for_select(Color.funny.alphabetical,
"id", "human_name", @selected_preview_pet_type.color_id)
= select_tag "preview[species_id]",
options_from_collection_for_select(Species.alphabetical,
"id", "human_name", @selected_preview_pet_type.species_id)
= submit_tag "Go", name: nil
= select_tag "preview[color_id]",
options_from_collection_for_select(Color.funny.alphabetical,
"id", "human_name", @selected_preview_pet_type.color_id)
= select_tag "preview[species_id]",
options_from_collection_for_select(Species.alphabetical,
"id", "human_name", @selected_preview_pet_type.species_id)
= submit_tag "Go", name: nil
%species-face-picker
%noscript

View file

@ -13,8 +13,6 @@
%link{href: image_path('favicon.png'), rel: 'icon'}
= yield :stylesheets
= stylesheet_link_tag "application"
- if use_responsive_design?
%meta{name: "viewport", content: "width=device-width, initial-scale=1"}
= yield :meta
= open_graph_tags
= csrf_meta_tag

View file

@ -7,7 +7,7 @@
= image_tag 'https://images.neopets.com/items/mall_floatingneggfaerie.gif'
%span= t 'infinite_closet'
- content_for :content do
= form_tag items_path, method: :get, class: "item-search-form" do
= form_tag items_path, :method => :get do
= text_field_tag :q, @query.to_s
= submit_tag t('.search'), :name => nil
= yield