diff --git a/agent.md b/agent.md index b96b3ae..37c4d76 100644 --- a/agent.md +++ b/agent.md @@ -36,6 +36,7 @@ This project replaces the Excel/VBA workflow named `Catalog maker - 20` with a N - Run `npm run css:build` after styling changes; `public/styles.css` is generated output. - Prefer Tailwind utility classes and Tailwind-style component patterns for buttons, panels, dialogs and responsive layouts. - 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. - Use Playwright to inspect the rendered result after every visual change at desktop and narrow viewport sizes. ## TypeScript workflow diff --git a/public/app.js b/public/app.js index 5c67de1..befc612 100644 --- a/public/app.js +++ b/public/app.js @@ -22,6 +22,10 @@ const findSourcesButton = document.querySelector("#findSourcesButton"); const getPicturesButton = document.querySelector("#getPicturesButton"); const parserDrawerToggle = document.querySelector("#parserDrawerToggle"); const parserDrawer = document.querySelector("#parserDrawer"); +const sidebarDrawer = document.querySelector("#sidebarDrawer"); +const sidebarOpenButton = document.querySelector("#sidebarOpenButton"); +const sidebarCloseButton = document.querySelector("#sidebarCloseButton"); +const sidebarOverlay = document.querySelector("#sidebarOverlay"); const sourceModal = document.querySelector("#sourceModal"); const sourceTitle = document.querySelector("#sourceTitle"); const sourceList = document.querySelector("#sourceList"); @@ -140,6 +144,13 @@ loadProductButton.addEventListener("click", loadProductInfoForCurrentProduct); findSourcesButton.addEventListener("click", findSourcesForCurrentProduct); getPicturesButton.addEventListener("click", getPicturesForCurrentProduct); parserDrawerToggle.addEventListener("click", toggleParserDrawer); +sidebarOpenButton.addEventListener("click", openSidebar); +sidebarCloseButton.addEventListener("click", closeSidebar); +sidebarOverlay.addEventListener("click", closeSidebar); +document.addEventListener("keydown", (event) => { + if (event.key === "Escape") + closeSidebar(); +}); previousProductButton.addEventListener("click", () => { if (!manufacturerSelect.value || currentPosition <= 1) return; @@ -183,6 +194,8 @@ for (const input of localDbInputs) input.addEventListener("change", saveLocalDbSettings); testLocalDbButton.addEventListener("click", testLocalDbConnection); loadDatabaseMode(); +syncSidebarForViewport(); +window.matchMedia("(min-width: 981px)").addEventListener("change", syncSidebarForViewport); setTimeout(() => { if (manufacturerSelect.value) { loadMappingSources(); @@ -756,6 +769,26 @@ function toggleParserDrawer() { parserDrawerToggle.setAttribute("aria-expanded", String(!isOpen)); parserDrawerToggle.querySelector(".drawer-chevron").textContent = isOpen ? "⌄" : "⌃"; } +function syncSidebarForViewport() { + if (window.matchMedia("(min-width: 981px)").matches) + openSidebar(); + else + closeSidebar(); +} +function openSidebar() { + sidebarDrawer.classList.add("is-open"); + sidebarOverlay.hidden = false; + sidebarOpenButton.hidden = true; + sidebarOpenButton.setAttribute("aria-expanded", "true"); + sidebarDrawer.setAttribute("aria-hidden", "false"); +} +function closeSidebar() { + sidebarDrawer.classList.remove("is-open"); + sidebarOverlay.hidden = true; + sidebarOpenButton.hidden = false; + sidebarOpenButton.setAttribute("aria-expanded", "false"); + sidebarDrawer.setAttribute("aria-hidden", "true"); +} function loadTheme() { const isDark = localStorage.getItem("catalog-maker:theme") === "dark"; document.documentElement.classList.toggle("dark", isDark); diff --git a/public/app.ts b/public/app.ts index a25448f..1143f64 100644 --- a/public/app.ts +++ b/public/app.ts @@ -22,6 +22,10 @@ const findSourcesButton = document.querySelector("#findSourcesButton"); const getPicturesButton = document.querySelector("#getPicturesButton"); const parserDrawerToggle = document.querySelector("#parserDrawerToggle"); const parserDrawer = document.querySelector("#parserDrawer"); +const sidebarDrawer = document.querySelector("#sidebarDrawer"); +const sidebarOpenButton = document.querySelector("#sidebarOpenButton"); +const sidebarCloseButton = document.querySelector("#sidebarCloseButton"); +const sidebarOverlay = document.querySelector("#sidebarOverlay"); const sourceModal = document.querySelector("#sourceModal"); const sourceTitle = document.querySelector("#sourceTitle"); const sourceList = document.querySelector("#sourceList"); @@ -147,6 +151,12 @@ loadProductButton.addEventListener("click", loadProductInfoForCurrentProduct); findSourcesButton.addEventListener("click", findSourcesForCurrentProduct); getPicturesButton.addEventListener("click", getPicturesForCurrentProduct); parserDrawerToggle.addEventListener("click", toggleParserDrawer); +sidebarOpenButton.addEventListener("click", openSidebar); +sidebarCloseButton.addEventListener("click", closeSidebar); +sidebarOverlay.addEventListener("click", closeSidebar); +document.addEventListener("keydown", (event) => { + if (event.key === "Escape") closeSidebar(); +}); previousProductButton.addEventListener("click", () => { if (!manufacturerSelect.value || currentPosition <= 1) return; @@ -185,6 +195,8 @@ for (const input of localDbInputs) input.addEventListener("change", saveLocalDbS testLocalDbButton.addEventListener("click", testLocalDbConnection); loadDatabaseMode(); +syncSidebarForViewport(); +window.matchMedia("(min-width: 981px)").addEventListener("change", syncSidebarForViewport); setTimeout(() => { if (manufacturerSelect.value) { @@ -837,6 +849,27 @@ function toggleParserDrawer() { parserDrawerToggle.querySelector(".drawer-chevron").textContent = isOpen ? "⌄" : "⌃"; } +function syncSidebarForViewport() { + if (window.matchMedia("(min-width: 981px)").matches) openSidebar(); + else closeSidebar(); +} + +function openSidebar() { + sidebarDrawer.classList.add("is-open"); + sidebarOverlay.hidden = false; + sidebarOpenButton.hidden = true; + sidebarOpenButton.setAttribute("aria-expanded", "true"); + sidebarDrawer.setAttribute("aria-hidden", "false"); +} + +function closeSidebar() { + sidebarDrawer.classList.remove("is-open"); + sidebarOverlay.hidden = true; + sidebarOpenButton.hidden = false; + sidebarOpenButton.setAttribute("aria-expanded", "false"); + sidebarDrawer.setAttribute("aria-hidden", "true"); +} + function loadTheme() { const isDark = localStorage.getItem("catalog-maker:theme") === "dark"; document.documentElement.classList.toggle("dark", isDark); @@ -1177,4 +1210,4 @@ async function loadProductInfoForCurrentProduct() { } } -export {}; \ No newline at end of file +export {}; diff --git a/public/icons.svg b/public/icons.svg index 518008f..d818abb 100644 --- a/public/icons.svg +++ b/public/icons.svg @@ -12,4 +12,5 @@ + diff --git a/public/index.html b/public/index.html index b8edfac..9816ce1 100644 --- a/public/index.html +++ b/public/index.html @@ -10,7 +10,19 @@
-