expanded margin of position spots on gatekeeper; cleaned up #id_tray scripts & styles
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
This commit is contained in:
@@ -71,29 +71,36 @@ var Tray = (function () {
|
||||
if (_isLandscape()) {
|
||||
// Landscape: the wrap slides on the Y axis.
|
||||
// Structure (column-reverse): tray above, handle below.
|
||||
// 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.
|
||||
// Wrap height is fixed to gearBtnTop so the handle bottom always
|
||||
// meets the gear button when open. Tray is flex:1 and fills the rest.
|
||||
// Open: wrap top = 0 (pinned to viewport top).
|
||||
// Closed: wrap top = -(gearBtnTop - handleH) = tray fully above viewport.
|
||||
var gearBtn = document.getElementById('id_gear_btn');
|
||||
var gearBtnTop = window.innerHeight;
|
||||
if (gearBtn) {
|
||||
gearBtnTop = Math.round(gearBtn.getBoundingClientRect().top);
|
||||
}
|
||||
var handleH = (_btn && _btn.offsetHeight) || 48;
|
||||
var wrapH = (_wrap && _wrap.offsetHeight) || (handleH + 280);
|
||||
|
||||
// Pin wrap height so handle bottom = gear btn top when open.
|
||||
if (_wrap) _wrap.style.height = gearBtnTop + 'px';
|
||||
|
||||
// Open: wrap pinned to viewport top.
|
||||
_minTop = 0;
|
||||
|
||||
// Closed: tray hidden above viewport, handle visible at y=0.
|
||||
_maxTop = -(wrapH - handleH);
|
||||
|
||||
// Open: handle bottom at gear btn top.
|
||||
_minTop = gearBtnTop - wrapH;
|
||||
_maxTop = -(gearBtnTop - handleH);
|
||||
} else {
|
||||
// Portrait: slide on X axis.
|
||||
var rightPx = parseInt(getComputedStyle(_wrap).right, 10);
|
||||
if (isNaN(rightPx)) rightPx = 0;
|
||||
// Wrap width is pinned to viewportW (JS) so its right edge only
|
||||
// reaches the viewport boundary when left = 0 (fully open).
|
||||
// This mirrors landscape: the open edge appears only at the last moment.
|
||||
// Open: left = 0 → wrap right = viewportW exactly.
|
||||
// Closed: left = viewportW - handleW → tray fully off-screen right.
|
||||
var handleW = _btn.offsetWidth || 48;
|
||||
if (_wrap) _wrap.style.width = window.innerWidth + 'px';
|
||||
_minLeft = 0;
|
||||
_maxLeft = window.innerWidth - rightPx - handleW;
|
||||
_maxLeft = window.innerWidth - handleW;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,21 +329,39 @@ var Tray = (function () {
|
||||
_btn.addEventListener('click', _onBtnClick);
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
// Always close on resize: bounds change invalidates current position.
|
||||
// Cancel any in-flight close animation, then force-close state.
|
||||
_cancelPendingHide();
|
||||
_open = false;
|
||||
if (_btn) _btn.classList.remove('open');
|
||||
if (_wrap) _wrap.classList.remove('wobble', 'snap', 'tray-dragging');
|
||||
|
||||
if (_isLandscape()) {
|
||||
// Ensure tray is visible before measuring bounds.
|
||||
if (_tray) _tray.style.display = 'grid';
|
||||
if (_wrap) { _wrap.style.left = ''; _wrap.style.bottom = ''; }
|
||||
if (_wrap) { _wrap.style.left = ''; _wrap.style.bottom = ''; _wrap.style.width = ''; }
|
||||
_computeBounds();
|
||||
_computeCellSize();
|
||||
if (!_open && _wrap) _wrap.style.top = _maxTop + 'px';
|
||||
// Snap to closed without transition (resize fires continuously).
|
||||
if (_wrap) {
|
||||
_wrap.classList.add('tray-dragging');
|
||||
_wrap.style.top = _maxTop + 'px';
|
||||
void _wrap.offsetWidth; // flush reflow so position lands before transition restored
|
||||
_wrap.classList.remove('tray-dragging');
|
||||
}
|
||||
} else {
|
||||
// Switching to portrait: hide tray if closed.
|
||||
if (!_open && _tray) _tray.style.display = 'none';
|
||||
if (_wrap) _wrap.style.top = '';
|
||||
if (_tray) _tray.style.display = 'none';
|
||||
if (_wrap) { _wrap.style.top = ''; _wrap.style.height = ''; }
|
||||
_computeBounds();
|
||||
_applyVerticalBounds();
|
||||
_computeCellSize();
|
||||
if (!_open && _wrap) _wrap.style.left = _maxLeft + 'px';
|
||||
// Snap to closed without transition.
|
||||
if (_wrap) {
|
||||
_wrap.classList.add('tray-dragging');
|
||||
_wrap.style.left = _maxLeft + 'px';
|
||||
void _wrap.offsetWidth; // flush reflow
|
||||
_wrap.classList.remove('tray-dragging');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -360,6 +385,8 @@ var Tray = (function () {
|
||||
_wrap.classList.remove('wobble', 'snap', 'tray-dragging');
|
||||
_wrap.style.left = '';
|
||||
_wrap.style.top = '';
|
||||
_wrap.style.height = '';
|
||||
_wrap.style.width = '';
|
||||
}
|
||||
if (_onDocMove) {
|
||||
document.removeEventListener('pointermove', _onDocMove);
|
||||
|
||||
@@ -358,5 +358,68 @@ describe("Tray", () => {
|
||||
const top = parseInt(wrap.style.top, 10);
|
||||
expect(top).toBeLessThan(0);
|
||||
});
|
||||
|
||||
// ── resize closes landscape tray ─────────────────────────────── //
|
||||
|
||||
describe("resize closes the tray", () => {
|
||||
it("closes when landscape tray is open", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
|
||||
it("removes .open from btn on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(btn.classList.contains("open")).toBe(false);
|
||||
});
|
||||
|
||||
it("resets wrap to closed top position on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(parseInt(wrap.style.top, 10)).toBeLessThan(0);
|
||||
});
|
||||
|
||||
it("does not re-open a closed tray on resize", () => {
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------- //
|
||||
// window resize — portrait //
|
||||
// ---------------------------------------------------------------------- //
|
||||
|
||||
describe("window resize (portrait)", () => {
|
||||
it("closes the tray when open", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
|
||||
it("removes .open from btn on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(btn.classList.contains("open")).toBe(false);
|
||||
});
|
||||
|
||||
it("hides the tray panel on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(tray.style.display).toBe("none");
|
||||
});
|
||||
|
||||
it("resets wrap to closed left position on resize", () => {
|
||||
Tray.open();
|
||||
expect(wrap.style.left).toBe("0px");
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(parseInt(wrap.style.left, 10)).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("does not re-open a closed tray on resize", () => {
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -296,6 +296,10 @@ html:has(.gate-overlay) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-container {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Narrow viewport — scale down, 2×3 slot grid (portrait mobile + narrow desktop)
|
||||
@@ -698,7 +702,8 @@ $inv-strip: 30px; // visible height of each stacked card after the first
|
||||
}
|
||||
|
||||
.form-container {
|
||||
h3 { font-size: 0.85rem; margin: 0.25rem 0; }
|
||||
margin-top: 0.75rem;
|
||||
h3 { font-size: 0.85rem; margin: 0.5rem 0; }
|
||||
|
||||
form { gap: 0.35rem; }
|
||||
|
||||
|
||||
@@ -26,10 +26,11 @@ $handle-r: 1rem;
|
||||
|
||||
#id_tray_wrap {
|
||||
position: fixed;
|
||||
// left set by JS: closed = vw - handle; open = 0
|
||||
// left set by JS: closed = vw - handleW; open = vw - wrapW
|
||||
// top/bottom set by JS from nav/footer measurements
|
||||
// right intentionally absent — wrap has fixed CSS width (handle + tray)
|
||||
// so the open edge only reaches the viewport boundary when fully open.
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 310;
|
||||
pointer-events: none;
|
||||
@@ -201,19 +202,6 @@ $handle-r: 1rem;
|
||||
&.wobble { animation: tray-wobble-landscape 0.45s ease; }
|
||||
&.snap { animation: tray-snap-landscape 0.30s ease; }
|
||||
|
||||
// Landscape: extend upward instead of rightward
|
||||
&::before {
|
||||
top: -9999px;
|
||||
bottom: auto;
|
||||
right: auto;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 9999px;
|
||||
border-top: none;
|
||||
border-bottom: none;
|
||||
border-left: 2.5rem solid rgba(var(--quaUser), 1);
|
||||
border-right: 2.5rem solid rgba(var(--quaUser), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#id_tray_handle {
|
||||
@@ -259,8 +247,8 @@ $handle-r: 1rem;
|
||||
inset -0.6rem 0 1.5rem -0.5rem rgba(0, 0, 0, 1), // right wall depth
|
||||
inset -0.6rem 0 1.5rem -0.5rem rgba(var(--quaUser), 0.5) // right wall depth (hue)
|
||||
;
|
||||
flex: none; // override portrait's flex:1 so height applies
|
||||
height: 80vh; // gives JS a real offsetHeight to compute bounds from
|
||||
flex: 1; // fill wrap height (JS sets wrap height = gearBtnTop)
|
||||
height: auto;
|
||||
min-height: unset;
|
||||
overflow: hidden; // clip #id_tray_grid to the felt interior
|
||||
}
|
||||
|
||||
@@ -358,5 +358,68 @@ describe("Tray", () => {
|
||||
const top = parseInt(wrap.style.top, 10);
|
||||
expect(top).toBeLessThan(0);
|
||||
});
|
||||
|
||||
// ── resize closes landscape tray ─────────────────────────────── //
|
||||
|
||||
describe("resize closes the tray", () => {
|
||||
it("closes when landscape tray is open", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
|
||||
it("removes .open from btn on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(btn.classList.contains("open")).toBe(false);
|
||||
});
|
||||
|
||||
it("resets wrap to closed top position on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(parseInt(wrap.style.top, 10)).toBeLessThan(0);
|
||||
});
|
||||
|
||||
it("does not re-open a closed tray on resize", () => {
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------- //
|
||||
// window resize — portrait //
|
||||
// ---------------------------------------------------------------------- //
|
||||
|
||||
describe("window resize (portrait)", () => {
|
||||
it("closes the tray when open", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
|
||||
it("removes .open from btn on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(btn.classList.contains("open")).toBe(false);
|
||||
});
|
||||
|
||||
it("hides the tray panel on resize", () => {
|
||||
Tray.open();
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(tray.style.display).toBe("none");
|
||||
});
|
||||
|
||||
it("resets wrap to closed left position on resize", () => {
|
||||
Tray.open();
|
||||
expect(wrap.style.left).toBe("0px");
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(parseInt(wrap.style.left, 10)).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("does not re-open a closed tray on resize", () => {
|
||||
window.dispatchEvent(new Event("resize"));
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user