mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
## Overview First part of the move over to the new designs. This lays the groundwork (shared brand components, button/nav styling, theme tokens) and applies it across the editor and the processor. Later parts will build on top of it. ## What's changed **Branding** - Shared `Logo` and `BrandMark` components used everywhere, so the mark and wordmark are identical across the editor, processor, auth pages and the chat FAB. - The sidebar logo doubles as the editor to processor switcher, morphing into a chevron on hover. It only appears for users who can actually reach the processor. **Navigation and layout** - Both sidebars restructured onto the floating nav surface treatment, with rounded panels sitting on the app canvas. - The editor file sidebar is now three sections (controls, PDF Library, settings) and the workbench top bar and tools panel match. - Added a collapse toggle to both sidebars, with an animated expand and collapse and a tidy icon rail when collapsed. The processor did not have a desktop collapse before. **Components** - Buttons and action icons now share one styling system, so both react to the same tokens. - Secondary buttons in dark mode use a neutral fill and border instead of inheriting the primary colour. - Status badges default to a clean dot with no background, with a filled pill as the alternative. - Metric strips gained a row layout with an optional leading icon. **Theme** - Colour tokens consolidated. Literal colours live only in the palette file, everything else references the semantic `--c-*` tokens. - `saas-theme.css` removed and the parts that were genuinely needed moved into the shared theme, so all builds get them. - The colour linter enforces this across the app and runs in CI. ## Notes - Nothing functional should change here, it is styling plus the sidebar collapse feature. - Main has been merged in. The Sources and billing pages picked up changes from main during that merge and are worth a look alongside the new styling. --------- Co-authored-by: Reece Browne <74901996+reecebrowne@users.noreply.github.com>
91 lines
2.8 KiB
TypeScript
91 lines
2.8 KiB
TypeScript
import { useEffect, type ReactNode } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useLocation } from "react-router-dom";
|
|
import { ActionIcon } from "@app/ui";
|
|
import { Sidebar } from "@portal/components/Sidebar";
|
|
import { useUI } from "@portal/contexts/UIContext";
|
|
import { MenuIcon, SearchIcon } from "@portal/components/icons";
|
|
import { Logo } from "@app/ui/Logo";
|
|
import "@portal/components/AppShell.css";
|
|
|
|
/**
|
|
* Compact header shown only under the mobile breakpoint (CSS-hidden on
|
|
* desktop): hamburger opens the sidebar drawer, search opens the palette
|
|
* (there's no ⌘K on a phone).
|
|
*/
|
|
function MobileTopbar() {
|
|
const { t } = useTranslation();
|
|
const { mobileNavOpen, toggleMobileNav, openSearch } = useUI();
|
|
return (
|
|
<header className="portal-shell__topbar">
|
|
<ActionIcon
|
|
variant="tertiary"
|
|
size="lg"
|
|
aria-label={t("portal.shell.topbar.openNav")}
|
|
aria-expanded={mobileNavOpen}
|
|
onClick={toggleMobileNav}
|
|
>
|
|
<MenuIcon size={20} />
|
|
</ActionIcon>
|
|
<Logo
|
|
variant="iconAndText"
|
|
iconHeight="1.6rem"
|
|
textHeight="1.3rem"
|
|
className="portal-shell__topbar-wordmark"
|
|
/>
|
|
<ActionIcon
|
|
variant="tertiary"
|
|
size="lg"
|
|
aria-label={t("portal.shell.topbar.search")}
|
|
onClick={openSearch}
|
|
>
|
|
<SearchIcon size={19} />
|
|
</ActionIcon>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Two-column layout: fixed-width sidebar on the left, a scrolling main column on
|
|
* the right. Under the mobile breakpoint the sidebar becomes an off-canvas
|
|
* drawer behind a scrim, opened from the topbar hamburger. The Sidebar reads
|
|
* its state from context, so this shell stays prop-free.
|
|
*/
|
|
export function AppShell({ children }: { children: ReactNode }) {
|
|
const { mobileNavOpen, closeMobileNav } = useUI();
|
|
const { pathname } = useLocation();
|
|
|
|
// Navigating (tap on a nav row, back button, deep link) always dismisses the
|
|
// drawer. Depends on pathname only: the close fn's identity changes with any
|
|
// UI state, and re-running on that would instantly close a just-opened drawer.
|
|
useEffect(() => {
|
|
closeMobileNav();
|
|
}, [pathname]);
|
|
|
|
useEffect(() => {
|
|
if (!mobileNavOpen) return;
|
|
function onKey(e: KeyboardEvent) {
|
|
if (e.key === "Escape") closeMobileNav();
|
|
}
|
|
document.addEventListener("keydown", onKey);
|
|
return () => document.removeEventListener("keydown", onKey);
|
|
}, [mobileNavOpen, closeMobileNav]);
|
|
|
|
return (
|
|
<div className="portal-shell">
|
|
<Sidebar />
|
|
{mobileNavOpen && (
|
|
<div
|
|
className="portal-shell__scrim"
|
|
onClick={closeMobileNav}
|
|
aria-hidden
|
|
/>
|
|
)}
|
|
<div className="portal-shell__main">
|
|
<MobileTopbar />
|
|
<main className="portal-shell__view">{children}</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|