Wardrobe V2 initial proof-of-concept
This commit is contained in:
parent
4e2c99d4dd
commit
276cc1b5ea
5 changed files with 212 additions and 1 deletions
6
app/assets/javascripts/outfits/new_v2.js
Normal file
6
app/assets/javascripts/outfits/new_v2.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
// Wardrobe v2 - Simple Rails+Turbo outfit editor
|
||||||
|
//
|
||||||
|
// This page uses Turbo Frames for instant updates when changing species/color.
|
||||||
|
// The outfit_viewer Web Component handles the pet rendering.
|
||||||
|
|
||||||
|
console.log("Wardrobe v2 loaded!");
|
||||||
106
app/assets/stylesheets/outfits/new_v2.sass
Normal file
106
app/assets/stylesheets/outfits/new_v2.sass
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
body.wardrobe-v2
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
height: 100vh
|
||||||
|
overflow: hidden
|
||||||
|
font-family: sans-serif
|
||||||
|
|
||||||
|
.wardrobe-container
|
||||||
|
display: flex
|
||||||
|
height: 100vh
|
||||||
|
background: #000
|
||||||
|
|
||||||
|
@media (max-width: 800px)
|
||||||
|
flex-direction: column
|
||||||
|
|
||||||
|
.outfit-preview-section
|
||||||
|
flex: 1
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
background: #000
|
||||||
|
position: relative
|
||||||
|
min-height: 400px
|
||||||
|
|
||||||
|
outfit-viewer
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.no-preview-message
|
||||||
|
color: white
|
||||||
|
text-align: center
|
||||||
|
padding: 2rem
|
||||||
|
font-size: 1.2rem
|
||||||
|
|
||||||
|
.outfit-controls-section
|
||||||
|
width: 400px
|
||||||
|
background: #fff
|
||||||
|
padding: 2rem
|
||||||
|
overflow-y: auto
|
||||||
|
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.3)
|
||||||
|
|
||||||
|
@media (max-width: 800px)
|
||||||
|
width: 100%
|
||||||
|
max-height: 40vh
|
||||||
|
|
||||||
|
h1
|
||||||
|
margin-top: 0
|
||||||
|
font-size: 1.75rem
|
||||||
|
color: #448844
|
||||||
|
|
||||||
|
h2
|
||||||
|
font-size: 1.25rem
|
||||||
|
color: #448844
|
||||||
|
margin-top: 2rem
|
||||||
|
|
||||||
|
.species-color-picker
|
||||||
|
margin: 1.5rem 0
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
margin-bottom: 1rem
|
||||||
|
|
||||||
|
label
|
||||||
|
display: block
|
||||||
|
font-weight: bold
|
||||||
|
margin-bottom: 0.5rem
|
||||||
|
color: #333
|
||||||
|
|
||||||
|
select
|
||||||
|
width: 100%
|
||||||
|
padding: 0.5rem
|
||||||
|
font-size: 1rem
|
||||||
|
border: 1px solid #ccc
|
||||||
|
border-radius: 4px
|
||||||
|
font-family: inherit
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
outline: none
|
||||||
|
border-color: #448844
|
||||||
|
|
||||||
|
.current-selection
|
||||||
|
padding: 1rem
|
||||||
|
background: #f0f0f0
|
||||||
|
border-radius: 4px
|
||||||
|
margin: 1rem 0
|
||||||
|
|
||||||
|
p
|
||||||
|
margin: 0
|
||||||
|
color: #666
|
||||||
|
|
||||||
|
strong
|
||||||
|
color: #000
|
||||||
|
|
||||||
|
.worn-items
|
||||||
|
margin-top: 2rem
|
||||||
|
|
||||||
|
ul
|
||||||
|
list-style: none
|
||||||
|
padding: 0
|
||||||
|
margin: 0.5rem 0
|
||||||
|
|
||||||
|
li
|
||||||
|
padding: 0.5rem
|
||||||
|
background: #f9f9f9
|
||||||
|
margin-bottom: 0.5rem
|
||||||
|
border-radius: 4px
|
||||||
|
color: #333
|
||||||
|
|
@ -68,7 +68,7 @@ class OutfitsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
@species_count = Species.count
|
@species_count = Species.count
|
||||||
|
|
||||||
@latest_contribution = Contribution.recent.first
|
@latest_contribution = Contribution.recent.first
|
||||||
Contribution.preload_contributeds_and_parents([@latest_contribution].compact)
|
Contribution.preload_contributeds_and_parents([@latest_contribution].compact)
|
||||||
|
|
||||||
|
|
@ -77,6 +77,36 @@ class OutfitsController < ApplicationController
|
||||||
@campaign = Fundraising::Campaign.current rescue nil
|
@campaign = Fundraising::Campaign.current rescue nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new_v2
|
||||||
|
@colors = Color.alphabetical
|
||||||
|
@species = Species.alphabetical
|
||||||
|
|
||||||
|
# Get selected species and color from params, or default to Blue Acara
|
||||||
|
species_id = params[:species] || Species.find_by_name("Acara")&.id
|
||||||
|
color_id = params[:color] || Color.find_by_name("Blue")&.id
|
||||||
|
|
||||||
|
# Find the pet type (species+color combination)
|
||||||
|
@selected_species = Species.find_by(id: species_id)
|
||||||
|
@selected_color = Color.find_by(id: color_id)
|
||||||
|
@pet_type = PetType.find_by(species_id: species_id, color_id: color_id) if @selected_species && @selected_color
|
||||||
|
|
||||||
|
# Load items from the objects[] parameter
|
||||||
|
item_ids = params[:objects] || []
|
||||||
|
items = Item.where(id: item_ids)
|
||||||
|
|
||||||
|
# Build the outfit
|
||||||
|
@outfit = Outfit.new(
|
||||||
|
pet_state: @pet_type&.canonical_pet_state,
|
||||||
|
worn_items: items,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Preload the manifests for all visible layers, so they load efficiently
|
||||||
|
# in parallel rather than sequentially when rendering
|
||||||
|
SwfAsset.preload_manifests(@outfit.visible_layers) if @outfit.pet_state
|
||||||
|
|
||||||
|
render layout: false
|
||||||
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@outfit = Outfit.find(params[:id])
|
@outfit = Outfit.find(params[:id])
|
||||||
|
|
||||||
|
|
|
||||||
68
app/views/outfits/new_v2.html.haml
Normal file
68
app/views/outfits/new_v2.html.haml
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
- title "Wardrobe v2"
|
||||||
|
|
||||||
|
!!! 5
|
||||||
|
%html
|
||||||
|
%head
|
||||||
|
%meta{charset: 'utf-8'}
|
||||||
|
%meta{name: 'viewport', content: 'width=device-width, initial-scale=1'}
|
||||||
|
%title= yield :title
|
||||||
|
%link{href: image_path('favicon.png'), rel: 'icon'}
|
||||||
|
= stylesheet_link_tag "application/hanger-spinner"
|
||||||
|
= stylesheet_link_tag "application/outfit-viewer"
|
||||||
|
= page_stylesheet_link_tag "outfits/new_v2"
|
||||||
|
= csrf_meta_tags
|
||||||
|
= javascript_include_tag "application", async: true
|
||||||
|
= javascript_include_tag "idiomorph", async: true
|
||||||
|
= javascript_include_tag "outfit-viewer", async: true
|
||||||
|
= javascript_include_tag "outfits/new_v2", async: true
|
||||||
|
%body.wardrobe-v2
|
||||||
|
= turbo_frame_tag "outfit-editor" do
|
||||||
|
.wardrobe-container
|
||||||
|
.outfit-preview-section
|
||||||
|
- if @outfit.pet_state
|
||||||
|
= outfit_viewer @outfit
|
||||||
|
- elsif @pet_type.nil?
|
||||||
|
.no-preview-message
|
||||||
|
%p
|
||||||
|
We haven't seen this kind of pet before! Try a different species/color
|
||||||
|
combination.
|
||||||
|
- else
|
||||||
|
.no-preview-message
|
||||||
|
%p Loading...
|
||||||
|
|
||||||
|
.outfit-controls-section
|
||||||
|
%h1 Customize your pet
|
||||||
|
|
||||||
|
.species-color-picker
|
||||||
|
= form_with url: wardrobe_v2_path, method: :get do |f|
|
||||||
|
.form-group
|
||||||
|
= label_tag :species, "Species:"
|
||||||
|
= select_tag :species,
|
||||||
|
options_from_collection_for_select(@species, "id", "human_name",
|
||||||
|
@selected_species&.id),
|
||||||
|
onchange: "this.form.requestSubmit()"
|
||||||
|
|
||||||
|
.form-group
|
||||||
|
= label_tag :color, "Color:"
|
||||||
|
= select_tag :color,
|
||||||
|
options_from_collection_for_select(@colors, "id", "human_name",
|
||||||
|
@selected_color&.id),
|
||||||
|
onchange: "this.form.requestSubmit()"
|
||||||
|
|
||||||
|
-# Preserve item IDs in the URL
|
||||||
|
- if params[:objects].present?
|
||||||
|
- params[:objects].each do |item_id|
|
||||||
|
= hidden_field_tag "objects[]", item_id
|
||||||
|
|
||||||
|
- if @outfit.pet_state
|
||||||
|
.current-selection
|
||||||
|
%p
|
||||||
|
Currently showing:
|
||||||
|
%strong= "#{@selected_color.human_name} #{@selected_species.human_name}"
|
||||||
|
|
||||||
|
- if @outfit.worn_items.any?
|
||||||
|
.worn-items
|
||||||
|
%h2 Items (#{@outfit.worn_items.size})
|
||||||
|
%ul
|
||||||
|
- @outfit.worn_items.each do |item|
|
||||||
|
%li= item.name
|
||||||
|
|
@ -10,6 +10,7 @@ OpenneoImpressItems::Application.routes.draw do
|
||||||
# TODO: It's a bit silly that outfits/new points to outfits#edit.
|
# TODO: It's a bit silly that outfits/new points to outfits#edit.
|
||||||
# Should we refactor the controller/view structure here?
|
# Should we refactor the controller/view structure here?
|
||||||
get '/outfits/new', to: 'outfits#edit', as: :wardrobe
|
get '/outfits/new', to: 'outfits#edit', as: :wardrobe
|
||||||
|
get '/outfits/new/v2', to: 'outfits#new_v2', as: :wardrobe_v2
|
||||||
get '/wardrobe' => redirect('/outfits/new')
|
get '/wardrobe' => redirect('/outfits/new')
|
||||||
get '/start/:color_name/:species_name' => 'outfits#start'
|
get '/start/:color_name/:species_name' => 'outfits#start'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue