← back to Quadrille Showroom

public/js/furniture-styles.js

117 lines

/* ============================================================================
 * furniture-styles.js — The Quadrille House: FURNITURE STYLE SELECTOR.
 *
 * A bottom-left chip-tray with the four procedural furniture styles
 * (Contemporary / Traditional / Transitional / Brutalist). Clicking a chip calls
 * window._qh.swapFurniture(styleKey, { includeExtras }). A "+ Decor" toggle opts
 * the set into its optional decor extras (onload stays minimal = table + chair).
 * The chosen style persists in localStorage (via swapFurniture) so it survives a
 * reload; the tray reflects the active style read back from window._qh.
 *
 * Matches the hamburger-dock visual language (gold #c9a96e on smoked glass,
 * Georgia eyebrow). Its own #qh-style-tray id is NOT in dock.js's stow lists, so
 * it stays permanently visible like the guided pager.
 * ========================================================================== */
(function () {
  'use strict';

  var LS_EXTRAS = 'qh_furniture_extras_ui';   // remembers ONLY the toggle's UI state
  var extrasOn = false;

  function injectStyles() {
    if (document.getElementById('qh-style-styles')) return;
    var st = document.createElement('style');
    st.id = 'qh-style-styles';
    st.textContent = [
      '#qh-style-tray{position:fixed;left:16px;bottom:16px;z-index:8800;',
        'display:flex;flex-direction:column;gap:7px;padding:10px 11px;',
        'background:rgba(14,14,20,0.90);border:1px solid rgba(201,169,110,0.30);border-radius:14px;',
        'backdrop-filter:blur(12px);box-shadow:0 18px 50px -12px rgba(0,0,0,0.8);',
        'font-family:Georgia,serif;max-width:250px;}',
      '.qh-style-eyebrow{font-size:10px;letter-spacing:1.5px;text-transform:uppercase;',
        'color:#8c8678;font-weight:700;padding:0 2px 1px;}',
      '.qh-style-chips{display:flex;flex-wrap:wrap;gap:6px;}',
      '.qh-style-chip{cursor:pointer;padding:7px 12px;border-radius:9px;',
        'background:rgba(255,255,255,0.04);border:1px solid rgba(201,169,110,0.20);',
        'color:#e8e2d4;font-size:12.5px;font-weight:600;letter-spacing:.2px;',
        'transition:background .12s,border-color .12s,color .12s;white-space:nowrap;}',
      '.qh-style-chip:hover{background:rgba(201,169,110,0.18);border-color:#c9a96e;}',
      '.qh-style-chip.active{background:rgba(201,169,110,0.28);border-color:#c9a96e;color:#fbf4e3;}',
      '.qh-style-decor{display:flex;align-items:center;justify-content:space-between;gap:8px;',
        'margin-top:1px;padding:6px 10px;border-radius:9px;cursor:pointer;',
        'background:rgba(255,255,255,0.03);border:1px solid rgba(201,169,110,0.16);',
        'color:#cfc7b4;font-size:11.5px;font-weight:600;letter-spacing:.3px;',
        'transition:background .12s,border-color .12s,color .12s;}',
      '.qh-style-decor:hover{border-color:#c9a96e;color:#f0e6cf;}',
      '.qh-style-decor.on{background:rgba(201,169,110,0.22);border-color:#c9a96e;color:#fbf4e3;}',
      '.qh-style-decor .qh-decor-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto;',
        'background:rgba(201,169,110,0.35);transition:background .12s;}',
      '.qh-style-decor.on .qh-decor-dot{background:#c9a96e;}'
    ].join('\n');
    document.head.appendChild(st);
  }

  var tray;

  function reflect() {
    if (!tray || !window._qh) return;
    var active = window._qh.furnitureStyle;
    tray.querySelectorAll('.qh-style-chip').forEach(function (c) {
      c.classList.toggle('active', c.getAttribute('data-style') === active);
    });
    var decor = tray.querySelector('.qh-style-decor');
    if (decor) decor.classList.toggle('on', extrasOn);
  }

  function build() {
    var order = (window._qh && window._qh.FURNITURE_STYLE_ORDER) || [];
    injectStyles();
    tray = document.createElement('div');
    tray.id = 'qh-style-tray';

    var html = '<div class="qh-style-eyebrow">Furniture Style</div><div class="qh-style-chips">';
    order.forEach(function (key) {
      var label = window._qh.furnitureStyleLabel ? window._qh.furnitureStyleLabel(key) : key;
      html += '<button class="qh-style-chip" data-style="' + key + '">' + label + '</button>';
    });
    html += '</div>' +
      '<div class="qh-style-decor" role="button" tabindex="0">' +
        '<span>Add Decor</span><span class="qh-decor-dot"></span></div>';
    tray.innerHTML = html;
    document.body.appendChild(tray);

    tray.querySelector('.qh-style-chips').addEventListener('click', function (e) {
      var chip = e.target.closest('.qh-style-chip'); if (!chip) return;
      window._qh.swapFurniture(chip.getAttribute('data-style'), { includeExtras: extrasOn });
      reflect();
    });

    var decor = tray.querySelector('.qh-style-decor');
    function toggleDecor() {
      extrasOn = !extrasOn;
      try { localStorage.setItem(LS_EXTRAS, extrasOn ? '1' : '0'); } catch (e) {}
      window._qh.swapFurniture(window._qh.furnitureStyle, { includeExtras: extrasOn });
      reflect();
    }
    decor.addEventListener('click', toggleDecor);
    decor.addEventListener('keydown', function (e) {
      if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleDecor(); }
    });

    reflect();
  }

  function boot(tries) {
    tries = tries || 0;
    // Wait until showroom.js has booted its furniture API + built the first set.
    if (!window._qh || !window._qh.FURNITURE_STYLE_ORDER || !window._qh.furniturePresent) {
      if (tries > 200) return;                       // give up after ~12s
      return setTimeout(function () { boot(tries + 1); }, 60);
    }
    build();
  }

  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function () { boot(0); });
  else boot(0);
})();