styles related to #id_tray & apparatus separated out into _tray.scss; new tray.js computes the cell size of the tray grid for item organization; room.html now sports the grid as a separate div so as not to interfere w. tray styling or size; new tests in FTs.test_room_tray

This commit is contained in:
Disco DeDisco
2026-03-29 13:36:44 -04:00
parent 5f643350c5
commit 39db59c71a
6 changed files with 455 additions and 256 deletions

View File

@@ -9,6 +9,7 @@ var Tray = (function () {
var _wrap = null;
var _btn = null;
var _tray = null;
var _grid = null;
// Portrait bounds (X axis)
var _minLeft = 0;
@@ -39,11 +40,38 @@ var Tray = (function () {
return window.innerWidth > window.innerHeight;
}
// Compute the square cell size from the tray's interior dimension and set
// --tray-cell-size on #id_tray so SCSS grid tracks pick it up.
// Portrait: divide height / 8. Landscape: divide width / 8.
// In portrait the tray may be display:none; we show it with visibility:hidden
// briefly so clientHeight returns a real value, then restore display:none.
function _computeCellSize() {
if (!_tray) return;
var size;
if (_isLandscape()) {
size = Math.floor(_tray.clientWidth / 8);
} else {
var wasHidden = (_tray.style.display === 'none' || !_tray.style.display);
if (wasHidden) {
_tray.style.visibility = 'hidden';
_tray.style.display = 'grid';
}
size = Math.floor(_tray.clientHeight / 8);
if (wasHidden) {
_tray.style.display = 'none';
_tray.style.visibility = '';
}
}
if (size > 0) {
_tray.style.setProperty('--tray-cell-size', size + 'px');
}
}
function _computeBounds() {
if (_isLandscape()) {
// Landscape: the wrap slides on the Y axis.
// Structure (column-reverse): tray above, handle below.
// Tray is always display:block in landscape — wrap top hides/reveals it.
// Tray has an explicit CSS height (80vh) so offsetHeight is real.
// Closed: wrap top = -(trayH) so tray is above viewport, handle at y=0.
// Open: wrap top = gearBtnTop - wrapH so handle bottom = gear btn top.
var gearBtn = document.getElementById('id_gear_btn');
@@ -52,7 +80,6 @@ var Tray = (function () {
gearBtnTop = Math.round(gearBtn.getBoundingClientRect().top);
}
var handleH = (_btn && _btn.offsetHeight) || 48;
// Tray is display:block so offsetHeight includes it; fall back to 280.
var wrapH = (_wrap && _wrap.offsetHeight) || (handleH + 280);
// Closed: tray hidden above viewport, handle visible at y=0.
@@ -96,7 +123,7 @@ var Tray = (function () {
_open = true;
// Portrait only: toggle tray display.
// Landscape: tray is always display:block; wrap position controls visibility.
if (!_isLandscape() && _tray) _tray.style.display = 'block';
if (!_isLandscape() && _tray) _tray.style.display = 'grid';
if (_btn) _btn.classList.add('open');
if (_wrap) {
_wrap.classList.remove('tray-dragging');
@@ -156,7 +183,7 @@ var Tray = (function () {
if (!_wrap) return;
// Portrait: show tray so it peeks in during the translateX animation,
// then re-hide it if the tray is still closed when the animation ends.
if (!_isLandscape() && _tray) _tray.style.display = 'block';
if (!_isLandscape() && _tray) _tray.style.display = 'grid';
_wrap.classList.add('wobble');
_wrap.addEventListener('animationend', function handler() {
_wrap.classList.remove('wobble');
@@ -185,19 +212,22 @@ var Tray = (function () {
_wrap = document.getElementById('id_tray_wrap');
_btn = document.getElementById('id_tray_btn');
_tray = document.getElementById('id_tray');
_grid = document.getElementById('id_tray_grid');
if (!_btn) return;
if (_isLandscape()) {
// Show tray before measuring so offsetHeight includes it.
if (_tray) _tray.style.display = 'block';
if (_tray) _tray.style.display = 'grid';
_computeBounds();
// Clear portrait's inline left/bottom so media-query CSS applies.
if (_wrap) { _wrap.style.left = ''; _wrap.style.bottom = ''; }
if (_wrap) _wrap.style.top = _maxTop + 'px';
_computeCellSize();
} else {
// Clear landscape's inline top so portrait CSS applies.
if (_wrap) _wrap.style.top = '';
_applyVerticalBounds();
_computeCellSize(); // wrap has correct height after _applyVerticalBounds
_computeBounds();
if (_wrap) _wrap.style.left = _maxLeft + 'px';
}
@@ -244,7 +274,7 @@ var Tray = (function () {
if (newLeft < _maxLeft) {
if (!_open) {
_open = true;
if (_tray) _tray.style.display = 'block';
if (_tray) _tray.style.display = 'grid';
if (_btn) _btn.classList.add('open');
}
} else {
@@ -294,9 +324,10 @@ var Tray = (function () {
window.addEventListener('resize', function () {
if (_isLandscape()) {
// Ensure tray is visible before measuring bounds.
if (_tray) _tray.style.display = 'block';
if (_tray) _tray.style.display = 'grid';
if (_wrap) { _wrap.style.left = ''; _wrap.style.bottom = ''; }
_computeBounds();
_computeCellSize();
if (!_open && _wrap) _wrap.style.top = _maxTop + 'px';
} else {
// Switching to portrait: hide tray if closed.
@@ -304,6 +335,7 @@ var Tray = (function () {
if (_wrap) _wrap.style.top = '';
_computeBounds();
_applyVerticalBounds();
_computeCellSize();
if (!_open && _wrap) _wrap.style.left = _maxLeft + 'px';
}
});
@@ -319,7 +351,10 @@ var Tray = (function () {
_dragHandled = false;
_landscapeOverride = null;
// Restore portrait default (display:none); landscape init() will show it.
if (_tray) _tray.style.display = 'none';
if (_tray) {
_tray.style.display = 'none';
_tray.style.removeProperty('--tray-cell-size');
}
if (_btn) _btn.classList.remove('open');
if (_wrap) {
_wrap.classList.remove('wobble', 'snap', 'tray-dragging');
@@ -344,6 +379,7 @@ var Tray = (function () {
_wrap = null;
_btn = null;
_tray = null;
_grid = null;
}
if (document.readyState === 'loading') {