Keep sidebar controls in dedicated left rail
This commit is contained in:
@@ -38,6 +38,7 @@ This project replaces the Excel/VBA workflow named `Catalog maker - 20` with a N
|
|||||||
- Whenever a reusable pattern appears, prefer Tailwind component patterns (`@layer components` with `@apply`) over handwritten legacy CSS; keep one-off layout details as utility classes where practical.
|
- Whenever a reusable pattern appears, prefer Tailwind component patterns (`@layer components` with `@apply`) over handwritten legacy CSS; keep one-off layout details as utility classes where practical.
|
||||||
- Whenever possible, use Tailwind UI / Tailwind Plus application components and patterns for shells, multi-column layouts, sidebars, drawers, dialogs, navigation and forms; adapt them to this project's dense catalog workflow instead of inventing a separate visual pattern.
|
- Whenever possible, use Tailwind UI / Tailwind Plus application components and patterns for shells, multi-column layouts, sidebars, drawers, dialogs, navigation and forms; adapt them to this project's dense catalog workflow instead of inventing a separate visual pattern.
|
||||||
- Multi-column panels must use a shared collapse pattern: collapsing a panel removes its grid track, lets the adjacent workspace expand, preserves an accessible expand/collapse control, and stacks predictably on mobile.
|
- Multi-column panels must use a shared collapse pattern: collapsing a panel removes its grid track, lets the adjacent workspace expand, preserves an accessible expand/collapse control, and stacks predictably on mobile.
|
||||||
|
- When the left sidebar is closed on desktop, its open control keeps a dedicated narrow rail; it must not be positioned inside the secondary product-details panel. On mobile, the rail collapses and the control may overlay the stacked layout.
|
||||||
- Use Playwright to inspect the rendered result after every visual change at desktop and narrow viewport sizes.
|
- Use Playwright to inspect the rendered result after every visual change at desktop and narrow viewport sizes.
|
||||||
|
|
||||||
## TypeScript workflow
|
## TypeScript workflow
|
||||||
|
|||||||
+4
-2
@@ -53,6 +53,7 @@ const darkContrastSelect = document.querySelector("#darkContrastSelect");
|
|||||||
const catalogScreen = document.querySelector(".catalog-screen");
|
const catalogScreen = document.querySelector(".catalog-screen");
|
||||||
const secondaryPanelToggle = document.querySelector("#secondaryPanelToggle");
|
const secondaryPanelToggle = document.querySelector("#secondaryPanelToggle");
|
||||||
const mainWorkspaceToggle = document.querySelector("#mainWorkspaceToggle");
|
const mainWorkspaceToggle = document.querySelector("#mainWorkspaceToggle");
|
||||||
|
let sidebarUserClosed = false;
|
||||||
const nativeFetch = window.fetch.bind(window);
|
const nativeFetch = window.fetch.bind(window);
|
||||||
window.fetch = (input, init = {}) => {
|
window.fetch = (input, init = {}) => {
|
||||||
const url = typeof input === "string" ? input : input.url;
|
const url = typeof input === "string" ? input : input.url;
|
||||||
@@ -225,7 +226,6 @@ lightContrastSelect.addEventListener("change", saveAppearance);
|
|||||||
darkContrastSelect.addEventListener("change", saveAppearance);
|
darkContrastSelect.addEventListener("change", saveAppearance);
|
||||||
loadDatabaseMode();
|
loadDatabaseMode();
|
||||||
syncSidebarForViewport();
|
syncSidebarForViewport();
|
||||||
window.matchMedia("(min-width: 981px)").addEventListener("change", syncSidebarForViewport);
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (manufacturerSelect.value) {
|
if (manufacturerSelect.value) {
|
||||||
loadMappingSources();
|
loadMappingSources();
|
||||||
@@ -852,12 +852,13 @@ function toggleParserDrawer() {
|
|||||||
parserDrawerToggle.querySelector(".drawer-chevron").textContent = isOpen ? "⌄" : "⌃";
|
parserDrawerToggle.querySelector(".drawer-chevron").textContent = isOpen ? "⌄" : "⌃";
|
||||||
}
|
}
|
||||||
function syncSidebarForViewport() {
|
function syncSidebarForViewport() {
|
||||||
if (window.matchMedia("(min-width: 981px)").matches)
|
if (window.matchMedia("(min-width: 981px)").matches && !sidebarUserClosed)
|
||||||
openSidebar();
|
openSidebar();
|
||||||
else
|
else
|
||||||
closeSidebar();
|
closeSidebar();
|
||||||
}
|
}
|
||||||
function openSidebar() {
|
function openSidebar() {
|
||||||
|
sidebarUserClosed = false;
|
||||||
sidebarDrawer.classList.add("is-open");
|
sidebarDrawer.classList.add("is-open");
|
||||||
sidebarOverlay.hidden = false;
|
sidebarOverlay.hidden = false;
|
||||||
sidebarOpenButton.hidden = true;
|
sidebarOpenButton.hidden = true;
|
||||||
@@ -866,6 +867,7 @@ function openSidebar() {
|
|||||||
catalogScreen.classList.remove("sidebar-collapsed");
|
catalogScreen.classList.remove("sidebar-collapsed");
|
||||||
}
|
}
|
||||||
function closeSidebar() {
|
function closeSidebar() {
|
||||||
|
sidebarUserClosed = true;
|
||||||
sidebarDrawer.classList.remove("is-open");
|
sidebarDrawer.classList.remove("is-open");
|
||||||
sidebarOverlay.hidden = true;
|
sidebarOverlay.hidden = true;
|
||||||
sidebarOpenButton.hidden = false;
|
sidebarOpenButton.hidden = false;
|
||||||
|
|||||||
+4
-2
@@ -53,6 +53,7 @@ const darkContrastSelect = document.querySelector("#darkContrastSelect");
|
|||||||
const catalogScreen = document.querySelector(".catalog-screen");
|
const catalogScreen = document.querySelector(".catalog-screen");
|
||||||
const secondaryPanelToggle = document.querySelector("#secondaryPanelToggle");
|
const secondaryPanelToggle = document.querySelector("#secondaryPanelToggle");
|
||||||
const mainWorkspaceToggle = document.querySelector("#mainWorkspaceToggle");
|
const mainWorkspaceToggle = document.querySelector("#mainWorkspaceToggle");
|
||||||
|
let sidebarUserClosed = false;
|
||||||
|
|
||||||
const nativeFetch = window.fetch.bind(window);
|
const nativeFetch = window.fetch.bind(window);
|
||||||
window.fetch = (input, init = {}) => {
|
window.fetch = (input, init = {}) => {
|
||||||
@@ -225,7 +226,6 @@ darkContrastSelect.addEventListener("change", saveAppearance);
|
|||||||
|
|
||||||
loadDatabaseMode();
|
loadDatabaseMode();
|
||||||
syncSidebarForViewport();
|
syncSidebarForViewport();
|
||||||
window.matchMedia("(min-width: 981px)").addEventListener("change", syncSidebarForViewport);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (manufacturerSelect.value) {
|
if (manufacturerSelect.value) {
|
||||||
@@ -940,11 +940,12 @@ function toggleParserDrawer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncSidebarForViewport() {
|
function syncSidebarForViewport() {
|
||||||
if (window.matchMedia("(min-width: 981px)").matches) openSidebar();
|
if (window.matchMedia("(min-width: 981px)").matches && !sidebarUserClosed) openSidebar();
|
||||||
else closeSidebar();
|
else closeSidebar();
|
||||||
}
|
}
|
||||||
|
|
||||||
function openSidebar() {
|
function openSidebar() {
|
||||||
|
sidebarUserClosed = false;
|
||||||
sidebarDrawer.classList.add("is-open");
|
sidebarDrawer.classList.add("is-open");
|
||||||
sidebarOverlay.hidden = false;
|
sidebarOverlay.hidden = false;
|
||||||
sidebarOpenButton.hidden = true;
|
sidebarOpenButton.hidden = true;
|
||||||
@@ -954,6 +955,7 @@ function openSidebar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function closeSidebar() {
|
function closeSidebar() {
|
||||||
|
sidebarUserClosed = true;
|
||||||
sidebarDrawer.classList.remove("is-open");
|
sidebarDrawer.classList.remove("is-open");
|
||||||
sidebarOverlay.hidden = true;
|
sidebarOverlay.hidden = true;
|
||||||
sidebarOpenButton.hidden = false;
|
sidebarOpenButton.hidden = false;
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -24,6 +24,7 @@ const config = loadConfig();
|
|||||||
const mimeTypes = new Map([
|
const mimeTypes = new Map([
|
||||||
[".html", "text/html; charset=utf-8"],
|
[".html", "text/html; charset=utf-8"],
|
||||||
[".css", "text/css; charset=utf-8"],
|
[".css", "text/css; charset=utf-8"],
|
||||||
|
[".js", "text/javascript; charset=utf-8"],
|
||||||
[".ts", "text/javascript; charset=utf-8"],
|
[".ts", "text/javascript; charset=utf-8"],
|
||||||
[".json", "application/json; charset=utf-8"],
|
[".json", "application/json; charset=utf-8"],
|
||||||
[".svg", "image/svg+xml"],
|
[".svg", "image/svg+xml"],
|
||||||
|
|||||||
+2
-1
@@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
.catalog-screen { @apply relative grid min-h-screen bg-catalog-surface pl-72; --secondary-track: minmax(240px,292px); --workspace-track: minmax(0,1fr); grid-template-columns: var(--secondary-track) var(--workspace-track); }
|
.catalog-screen { @apply relative grid min-h-screen bg-catalog-surface pl-72; --secondary-track: minmax(240px,292px); --workspace-track: minmax(0,1fr); grid-template-columns: var(--secondary-track) var(--workspace-track); }
|
||||||
.catalog-screen.sidebar-collapsed { @apply pl-0; }
|
.catalog-screen.sidebar-collapsed { @apply pl-11; }
|
||||||
.catalog-screen.secondary-collapsed { --secondary-track: 44px; }
|
.catalog-screen.secondary-collapsed { --secondary-track: 44px; }
|
||||||
.catalog-screen.workspace-collapsed { --workspace-track: 44px; }
|
.catalog-screen.workspace-collapsed { --workspace-track: 44px; }
|
||||||
.sidebar-drawer { @apply fixed inset-y-0 left-0 z-40 flex w-72 min-h-screen flex-col gap-1.5 overflow-y-auto bg-slate-900 p-1.5 text-[11px] text-slate-200 shadow-xl transition-transform duration-200 ease-out; }
|
.sidebar-drawer { @apply fixed inset-y-0 left-0 z-40 flex w-72 min-h-screen flex-col gap-1.5 overflow-y-auto bg-slate-900 p-1.5 text-[11px] text-slate-200 shadow-xl transition-transform duration-200 ease-out; }
|
||||||
@@ -186,6 +186,7 @@
|
|||||||
.picture-candidate span { @apply min-w-0 truncate; }
|
.picture-candidate span { @apply min-w-0 truncate; }
|
||||||
@media (max-width: 980px) {
|
@media (max-width: 980px) {
|
||||||
.catalog-screen { @apply flex flex-col pl-0; }
|
.catalog-screen { @apply flex flex-col pl-0; }
|
||||||
|
.catalog-screen.sidebar-collapsed { @apply pl-0; }
|
||||||
.sidebar-drawer { @apply w-[min(18rem,calc(100vw-1rem))] min-h-0; }
|
.sidebar-drawer { @apply w-[min(18rem,calc(100vw-1rem))] min-h-0; }
|
||||||
.sidebar-drawer:not([aria-hidden="true"]) { @apply max-h-screen; }
|
.sidebar-drawer:not([aria-hidden="true"]) { @apply max-h-screen; }
|
||||||
.sidebar-drawer[aria-hidden="true"] { @apply -translate-x-full; }
|
.sidebar-drawer[aria-hidden="true"] { @apply -translate-x-full; }
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ test("workspace columns collapse and expand without leaving a gap", async ({ pag
|
|||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
await page.locator("#sidebarCloseButton").click();
|
await page.locator("#sidebarCloseButton").click();
|
||||||
await expect(page.locator(".catalog-screen")).toHaveClass(/sidebar-collapsed/);
|
await expect(page.locator(".catalog-screen")).toHaveClass(/sidebar-collapsed/);
|
||||||
|
const secondaryPanel = await page.locator(".secondary-panel").boundingBox();
|
||||||
|
expect(secondaryPanel?.x).toBeGreaterThanOrEqual(44);
|
||||||
|
|
||||||
await page.locator("#secondaryPanelToggle").click();
|
await page.locator("#secondaryPanelToggle").click();
|
||||||
await expect(page.locator(".catalog-screen")).toHaveClass(/secondary-collapsed/);
|
await expect(page.locator(".catalog-screen")).toHaveClass(/secondary-collapsed/);
|
||||||
|
|||||||
Reference in New Issue
Block a user