diff --git a/app/assets/javascripts/items/show.js b/app/assets/javascripts/items/show.js index aff5f5dc..d5dfb960 100644 --- a/app/assets/javascripts/items/show.js +++ b/app/assets/javascripts/items/show.js @@ -24,24 +24,6 @@ document.addEventListener("turbo:frame-missing", (e) => { e.preventDefault(); }); -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() { @@ -109,7 +91,6 @@ class MeasuredContainer extends HTMLElement { } } -customElements.define("species-color-picker", SpeciesColorPicker); customElements.define("species-face-picker", SpeciesFacePicker); customElements.define("species-face-picker-options", SpeciesFacePickerOptions); customElements.define("measured-container", MeasuredContainer); diff --git a/app/assets/javascripts/species-color-picker.js b/app/assets/javascripts/species-color-picker.js new file mode 100644 index 00000000..6ed1355b --- /dev/null +++ b/app/assets/javascripts/species-color-picker.js @@ -0,0 +1,28 @@ +/** + * SpeciesColorPicker web component + * + * Progressive enhancement for species/color picker forms: + * - Auto-submits the form when species or color changes (if JS is enabled) + * - Shows a submit button as fallback (if JS is disabled or slow to load) + * - Uses Custom Element internals API to communicate state to CSS + */ +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(); + } +} + +customElements.define("species-color-picker", SpeciesColorPicker); diff --git a/app/assets/stylesheets/outfits/new_v2.css b/app/assets/stylesheets/outfits/new_v2.css index a8406865..c1c5ac91 100644 --- a/app/assets/stylesheets/outfits/new_v2.css +++ b/app/assets/stylesheets/outfits/new_v2.css @@ -59,8 +59,6 @@ body.wardrobe-v2 { transition: opacity 0.2s; form { - display: flex; - gap: 0.5rem; pointer-events: auto; /* Re-enable clicks on the form itself */ } @@ -97,6 +95,51 @@ body.wardrobe-v2 { color: white; } } + + /* Submit button: progressive enhancement pattern */ + /* If JS is disabled, the button is always visible */ + /* If JS is enabled but slow to load, fade in after 0.75s */ + /* Once the web component loads, hide the button completely */ + input[type="submit"] { + padding: 0.5rem 1rem; + font-size: 1rem; + border: 1px solid rgba(255, 255, 255, 0.3); + border-radius: 0.375rem; + background: rgba(0, 0, 0, 0.7); + color: white; + cursor: pointer; + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + + &:hover { + background-color: rgba(0, 0, 0, 0.8); + border-color: rgba(255, 255, 255, 0.5); + } + + &:focus { + outline: none; + border-color: rgba(255, 255, 255, 0.8); + box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1); + } + } + + /* If JS is enabled, hide the submit button initially with a delay */ + @media (scripting: enabled) { + input[type="submit"] { + position: absolute; + margin-left: 0.5em; + opacity: 0; + animation: fade-in 0.25s forwards; + animation-delay: 0.75s; + } + } + + /* Once auto-loading is ready, hide the submit button completely */ + &:state(auto-loading) { + input[type="submit"] { + display: none; + } + } } /* Show picker on hover (real hover only, not simulated touch hover) */ @@ -169,4 +212,14 @@ body.wardrobe-v2 { border-radius: 4px; color: #333; } +} + +@keyframes fade-in { + from { + opacity: 0; + } + + to { + opacity: 1; + } } \ No newline at end of file diff --git a/app/views/items/show.html.haml b/app/views/items/show.html.haml index 37879734..22ea1f39 100644 --- a/app/views/items/show.html.haml +++ b/app/views/items/show.html.haml @@ -131,4 +131,5 @@ - content_for :javascripts do = javascript_include_tag "idiomorph", async: true = javascript_include_tag "outfit-viewer", async: true + = javascript_include_tag "species-color-picker", async: true = javascript_include_tag "items/show", async: true diff --git a/app/views/outfits/new_v2.html.haml b/app/views/outfits/new_v2.html.haml index b384da3a..5f252eb0 100644 --- a/app/views/outfits/new_v2.html.haml +++ b/app/views/outfits/new_v2.html.haml @@ -14,6 +14,7 @@ = javascript_include_tag "application", async: true = javascript_include_tag "idiomorph", async: true = javascript_include_tag "outfit-viewer", async: true + = javascript_include_tag "species-color-picker", async: true = javascript_include_tag "outfits/new_v2", async: true %body.wardrobe-v2 = turbo_frame_tag "outfit-editor" do @@ -32,13 +33,12 @@ = select_tag :color, options_from_collection_for_select(@colors, "id", "human_name", @selected_color&.id), - onchange: "this.form.requestSubmit()", "aria-label": "Pet color" = select_tag :species, options_from_collection_for_select(@species, "id", "human_name", @selected_species&.id), - onchange: "this.form.requestSubmit()", "aria-label": "Pet species" + = submit_tag "Go", name: nil -# Preserve item IDs in the URL - if params[:objects].present?