From a53cce80b6f0ed7e4daf9d17ef2105a3cb3b5e0b Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Mon, 21 Jul 2025 16:48:24 +0100 Subject: [PATCH 1/9] V2 Navbar Styling --- frontend/index.html | 1 + .../src/components/shared/QuickAccessBar.css | 13 ++ .../src/components/shared/QuickAccessBar.tsx | 220 ++++++++++++++---- frontend/src/global.d.ts | 1 + frontend/src/pages/HomePage.tsx | 2 +- 5 files changed, 193 insertions(+), 44 deletions(-) create mode 100644 frontend/src/components/shared/QuickAccessBar.css diff --git a/frontend/index.html b/frontend/index.html index 0fc165c66..c8f825666 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,6 +3,7 @@ + void; @@ -16,6 +19,17 @@ interface QuickAccessBarProps { readerMode: boolean; } +interface ButtonConfig { + id: string; + name: string; + icon: React.ReactNode; + tooltip: string; + color: string; + isRound?: boolean; + size?: 'sm' | 'md' | 'lg' | 'xl'; + onClick: () => void; +} + const QuickAccessBar = ({ onToolsClick, onReaderToggle, @@ -26,54 +40,174 @@ const QuickAccessBar = ({ }: QuickAccessBarProps) => { const { isRainbowMode } = useRainbowThemeContext(); const [configModalOpen, setConfigModalOpen] = useState(false); + const [activeButton, setActiveButton] = useState('tools'); + + const buttonConfigs: ButtonConfig[] = [ + { + id: 'tools', + name: 'All Tools', + icon: , + tooltip: 'View all available tools', + color: '#1E88E5', + size: 'lg', + onClick: () => { + setActiveButton('tools'); + onReaderToggle(); + onToolsClick(); + } + }, + { + id: 'read', + name: 'Read', + icon: , + tooltip: 'Read documents', + color: '#4CAF50', + size: 'lg', + onClick: () => { + setActiveButton('read'); + onReaderToggle(); + } + }, + { + id: 'sign', + name: 'Sign', + icon: + + signature + , + tooltip: 'Sign your document', + color: '#3BA99C', + size: 'lg', + onClick: () => setActiveButton('sign') + }, + { + id: 'automate', + name: 'Automate', + icon: , + tooltip: 'Automate workflows', + color: '#A576E3', + size: 'lg', + onClick: () => setActiveButton('automate') + }, + { + id: 'files', + name: 'Files', + icon: , + tooltip: 'Manage files', + color: '', // the round icons are blue always, this logic lives in getButtonStyle + isRound: true, + size: 'lg', + onClick: () => setActiveButton('files') + }, + /* Access isn't going to be available yet */ + + /* + { + id: 'access', + name: 'Access', + icon: , + tooltip: 'Manage access and permissions', + color: '#00BCD4', + isRound: true, + size: 'lg', + onClick: () => setActiveButton('access') + }, + */ + { + id: 'activity', + name: 'Activity', + icon: + + vital_signs + , + tooltip: 'View activity and analytics', + color: '', + isRound: true, + size: 'lg', + onClick: () => setActiveButton('activity') + }, + { + id: 'config', + name: 'Config', + icon: , + tooltip: 'Configure settings', + color: '#9CA3AF', + size: 'lg', + onClick: () => { + setConfigModalOpen(true); + } + } + ]; + + const getButtonStyle = (config: ButtonConfig) => { + const isActive = activeButton === config.id; + if (config.isRound && isActive) { + return { + backgroundColor: '#D3E7F7', + color: '#0A8BFF', + borderRadius: '50%', + }; + } + return { + backgroundColor: isActive ? config.color : '#9CA3AF', + color: 'white', + border: 'none', + borderRadius: config.isRound ? '50%' : '8px', + }; + }; + + const getTextStyle = (config: ButtonConfig) => { + const isActive = activeButton === config.id; + return { + marginTop: '12px', + fontSize: '12px', + color: isActive ? 'var(--text-primary)' : 'var(--color-gray-700)', + fontWeight: isActive ? 'bold' : 'normal', + textRendering: 'optimizeLegibility' as const, + fontSynthesis: 'none' as const + }; + }; return (
- {/* All Tools Button */} -
- - - - Tools -
- - {/* Reader Mode Button */} -
- - - - Read -
- - {/* Spacer */} -
- - {/* Config Modal Button (for testing) */} -
- setConfigModalOpen(true)} - > - - - Config -
+ {buttonConfigs.map((config, index) => ( + + +
+ + + {config.icon} + + + + {config.name} + +
+
+ + {/* Add divider after Automate button (index 3) */} + {index === 3 && } + + {/* Add spacer before Config button (index 7) */} + {index === 5 &&
} + + ))} { - setReaderMode(!readerMode); + setReaderMode(true); }, [readerMode]); const handleViewChange = useCallback((view: string) => { From 7d09bf9e45ec995cdaaadb4aa2fa2c8dfa52783b Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Mon, 21 Jul 2025 22:10:23 +0100 Subject: [PATCH 2/9] forgot to push --- .../src/components/shared/QuickAccessBar.css | 14 ++ .../src/components/shared/QuickAccessBar.tsx | 132 ++++++++++++++++-- frontend/src/pages/HomePage.tsx | 10 +- frontend/src/styles/theme.css | 106 ++++++++++---- 4 files changed, 221 insertions(+), 41 deletions(-) diff --git a/frontend/src/components/shared/QuickAccessBar.css b/frontend/src/components/shared/QuickAccessBar.css index d429f9721..16f7def5e 100644 --- a/frontend/src/components/shared/QuickAccessBar.css +++ b/frontend/src/components/shared/QuickAccessBar.css @@ -10,4 +10,18 @@ justify-content: center; width: 32px; height: 32px; +} + +.fallbackDivider { + width: 60px; + height: 1px; + background-color: var(--color-gray-300); + margin: 8px 0; + border-radius: 1px; +} + +.mantineDivider { + width: 60px; + margin: 8px 0; + border-color: var(--color-gray-300); } \ No newline at end of file diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 1a4a3cab8..a12298b3c 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -5,6 +5,8 @@ import AppsIcon from "@mui/icons-material/AppsRounded"; import SettingsIcon from "@mui/icons-material/SettingsRounded"; import AutoAwesomeIcon from "@mui/icons-material/AutoAwesomeRounded"; import FolderIcon from "@mui/icons-material/FolderRounded"; +import PersonIcon from "@mui/icons-material/PersonRounded"; +import NotificationsIcon from "@mui/icons-material/NotificationsRounded"; import { useRainbowThemeContext } from "./RainbowThemeProvider"; import rainbowStyles from '../../styles/rainbow.module.css'; import AppConfigModal from './AppConfigModal'; @@ -30,6 +32,54 @@ interface ButtonConfig { onClick: () => void; } +function NavHeader() { + return ( + <> +
+ + + + + + + + + + +
+ {/* Divider after top icons */} + + + ); +} + const QuickAccessBar = ({ onToolsClick, onReaderToggle, @@ -46,7 +96,7 @@ const QuickAccessBar = ({ { id: 'tools', name: 'All Tools', - icon: , + icon: , tooltip: 'View all available tools', color: '#1E88E5', size: 'lg', @@ -141,16 +191,69 @@ const QuickAccessBar = ({ const getButtonStyle = (config: ButtonConfig) => { const isActive = activeButton === config.id; - if (config.isRound && isActive) { - return { - backgroundColor: '#D3E7F7', - color: '#0A8BFF', - borderRadius: '50%', - }; + + if (isActive) { + // Active state - use specific icon colors + if (config.id === 'tools') { + return { + backgroundColor: 'var(--icon-tools-bg)', + color: 'var(--icon-tools-color)', + border: 'none', + borderRadius: config.isRound ? '50%' : '8px', + }; + } + if (config.id === 'read') { + return { + backgroundColor: 'var(--icon-read-bg)', + color: 'var(--icon-read-color)', + border: 'none', + borderRadius: config.isRound ? '50%' : '8px', + }; + } + if (config.id === 'sign') { + return { + backgroundColor: 'var(--icon-sign-bg)', + color: 'var(--icon-sign-color)', + border: 'none', + borderRadius: config.isRound ? '50%' : '8px', + }; + } + if (config.id === 'automate') { + return { + backgroundColor: 'var(--icon-automate-bg)', + color: 'var(--icon-automate-color)', + border: 'none', + borderRadius: config.isRound ? '50%' : '8px', + }; + } + if (config.id === 'files') { + return { + backgroundColor: 'var(--icon-files-bg)', + color: 'var(--icon-files-color)', + borderRadius: '50%', + }; + } + if (config.id === 'activity') { + return { + backgroundColor: 'var(--icon-activity-bg)', + color: 'var(--icon-activity-color)', + borderRadius: '50%', + }; + } + if (config.id === 'config') { + return { + backgroundColor: 'var(--icon-config-bg)', + color: 'var(--icon-config-color)', + border: 'none', + borderRadius: config.isRound ? '50%' : '8px', + }; + } } + + // Inactive state - use consistent inactive colors return { - backgroundColor: isActive ? config.color : '#9CA3AF', - color: 'white', + backgroundColor: 'var(--icon-inactive-bg)', + color: 'var(--icon-inactive-color)', border: 'none', borderRadius: config.isRound ? '50%' : '8px', }; @@ -180,6 +283,7 @@ const QuickAccessBar = ({ }} > + {buttonConfigs.map((config, index) => ( @@ -202,7 +306,15 @@ const QuickAccessBar = ({ {/* Add divider after Automate button (index 3) */} - {index === 3 && } + {index === 3 && ( + + )} {/* Add spacer before Config button (index 7) */} {index === 5 &&
} diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 0503a7d6d..51cb0d8f6 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -97,7 +97,7 @@ export default function HomePage() { {/* Left: Tool Picker or Selected Tool Panel */}
{/* Top Controls */} Date: Tue, 22 Jul 2025 16:48:11 +0100 Subject: [PATCH 3/9] use rem styling, make navbar scrollable on overflow, extrapolate styling to be re-usable and remove redundant nav item colors, rely on tailwind theme instead --- frontend/index.html | 1 - frontend/package-lock.json | 7 ++ frontend/package.json | 1 + .../src/components/shared/QuickAccessBar.css | 42 +++++-- .../src/components/shared/QuickAccessBar.tsx | 106 ++++++++---------- frontend/src/index.css | 6 + 6 files changed, 94 insertions(+), 69 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index c8f825666..0fc165c66 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -3,7 +3,6 @@ - void; } +const actionIconStyle = { + backgroundColor: 'var(--icon-user-bg)', + color: 'var(--icon-user-color)', + borderRadius: '50%', + width: '1.5rem', + height: '1.5rem', +}; + function NavHeader() { return ( <> -
+
- + - +
@@ -72,7 +66,7 @@ function NavHeader() { @@ -98,8 +92,8 @@ const QuickAccessBar = ({ name: 'All Tools', icon: , tooltip: 'View all available tools', - color: '#1E88E5', size: 'lg', + isRound: false, onClick: () => { setActiveButton('tools'); onReaderToggle(); @@ -111,8 +105,8 @@ const QuickAccessBar = ({ name: 'Read', icon: , tooltip: 'Read documents', - color: '#4CAF50', size: 'lg', + isRound: false, onClick: () => { setActiveButton('read'); onReaderToggle(); @@ -122,12 +116,12 @@ const QuickAccessBar = ({ id: 'sign', name: 'Sign', icon: - + signature , tooltip: 'Sign your document', - color: '#3BA99C', size: 'lg', + isRound: false, onClick: () => setActiveButton('sign') }, { @@ -135,8 +129,8 @@ const QuickAccessBar = ({ name: 'Automate', icon: , tooltip: 'Automate workflows', - color: '#A576E3', size: 'lg', + isRound: false, onClick: () => setActiveButton('automate') }, { @@ -144,34 +138,18 @@ const QuickAccessBar = ({ name: 'Files', icon: , tooltip: 'Manage files', - color: '', // the round icons are blue always, this logic lives in getButtonStyle isRound: true, size: 'lg', onClick: () => setActiveButton('files') }, - /* Access isn't going to be available yet */ - - /* - { - id: 'access', - name: 'Access', - icon: , - tooltip: 'Manage access and permissions', - color: '#00BCD4', - isRound: true, - size: 'lg', - onClick: () => setActiveButton('access') - }, - */ { id: 'activity', name: 'Activity', icon: - + vital_signs , tooltip: 'View activity and analytics', - color: '', isRound: true, size: 'lg', onClick: () => setActiveButton('activity') @@ -181,7 +159,6 @@ const QuickAccessBar = ({ name: 'Config', icon: , tooltip: 'Configure settings', - color: '#9CA3AF', size: 'lg', onClick: () => { setConfigModalOpen(true); @@ -189,6 +166,13 @@ const QuickAccessBar = ({ } ]; + const ROUND_BORDER_RADIUS = '50%'; + const NOT_ROUND_BORDER_RADIUS = '8px'; + + const getBorderRadius = (config: ButtonConfig): string => { + return config.isRound ? ROUND_BORDER_RADIUS : NOT_ROUND_BORDER_RADIUS; + }; + const getButtonStyle = (config: ButtonConfig) => { const isActive = activeButton === config.id; @@ -199,7 +183,7 @@ const QuickAccessBar = ({ backgroundColor: 'var(--icon-tools-bg)', color: 'var(--icon-tools-color)', border: 'none', - borderRadius: config.isRound ? '50%' : '8px', + borderRadius: getBorderRadius(config), }; } if (config.id === 'read') { @@ -207,7 +191,7 @@ const QuickAccessBar = ({ backgroundColor: 'var(--icon-read-bg)', color: 'var(--icon-read-color)', border: 'none', - borderRadius: config.isRound ? '50%' : '8px', + borderRadius: getBorderRadius(config), }; } if (config.id === 'sign') { @@ -215,7 +199,7 @@ const QuickAccessBar = ({ backgroundColor: 'var(--icon-sign-bg)', color: 'var(--icon-sign-color)', border: 'none', - borderRadius: config.isRound ? '50%' : '8px', + borderRadius: getBorderRadius(config), }; } if (config.id === 'automate') { @@ -223,21 +207,21 @@ const QuickAccessBar = ({ backgroundColor: 'var(--icon-automate-bg)', color: 'var(--icon-automate-color)', border: 'none', - borderRadius: config.isRound ? '50%' : '8px', + borderRadius: getBorderRadius(config), }; } if (config.id === 'files') { return { backgroundColor: 'var(--icon-files-bg)', color: 'var(--icon-files-color)', - borderRadius: '50%', + borderRadius: ROUND_BORDER_RADIUS, }; } if (config.id === 'activity') { return { backgroundColor: 'var(--icon-activity-bg)', color: 'var(--icon-activity-color)', - borderRadius: '50%', + borderRadius: ROUND_BORDER_RADIUS, }; } if (config.id === 'config') { @@ -245,7 +229,7 @@ const QuickAccessBar = ({ backgroundColor: 'var(--icon-config-bg)', color: 'var(--icon-config-color)', border: 'none', - borderRadius: config.isRound ? '50%' : '8px', + borderRadius: getBorderRadius(config), }; } } @@ -255,15 +239,15 @@ const QuickAccessBar = ({ backgroundColor: 'var(--icon-inactive-bg)', color: 'var(--icon-inactive-color)', border: 'none', - borderRadius: config.isRound ? '50%' : '8px', + borderRadius: getBorderRadius(config), }; }; const getTextStyle = (config: ButtonConfig) => { const isActive = activeButton === config.id; return { - marginTop: '12px', - fontSize: '12px', + marginTop: '0.75rem', + fontSize: '0.75rem', color: isActive ? 'var(--text-primary)' : 'var(--color-gray-700)', fontWeight: isActive ? 'bold' : 'normal', textRendering: 'optimizeLegibility' as const, @@ -273,13 +257,19 @@ const QuickAccessBar = ({ return (
{ + // Prevent the wheel event from bubbling up to parent containers + e.stopPropagation(); }} > @@ -310,7 +300,7 @@ const QuickAccessBar = ({ diff --git a/frontend/src/index.css b/frontend/src/index.css index ec2585e8c..f7e5e0865 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,3 +1,9 @@ +@import 'material-symbols/rounded.css'; + +.material-symbols-rounded { + font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; +} + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', From 141dc8f8bb4a9be2b996ec9c5097cbc19030649a Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Tue, 22 Jul 2025 16:52:37 +0100 Subject: [PATCH 4/9] bad variable name --- frontend/src/components/shared/QuickAccessBar.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 8b52091cf..9e7908714 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -166,11 +166,11 @@ const QuickAccessBar = ({ } ]; - const ROUND_BORDER_RADIUS = '50%'; - const NOT_ROUND_BORDER_RADIUS = '8px'; + const CIRCULAR_BORDER_RADIUS = '50%'; + const ROUND_BORDER_RADIUS = '8px'; const getBorderRadius = (config: ButtonConfig): string => { - return config.isRound ? ROUND_BORDER_RADIUS : NOT_ROUND_BORDER_RADIUS; + return config.isRound ? CIRCULAR_BORDER_RADIUS : ROUND_BORDER_RADIUS; }; const getButtonStyle = (config: ButtonConfig) => { @@ -214,14 +214,14 @@ const QuickAccessBar = ({ return { backgroundColor: 'var(--icon-files-bg)', color: 'var(--icon-files-color)', - borderRadius: ROUND_BORDER_RADIUS, + borderRadius: CIRCULAR_BORDER_RADIUS, }; } if (config.id === 'activity') { return { backgroundColor: 'var(--icon-activity-bg)', color: 'var(--icon-activity-color)', - borderRadius: ROUND_BORDER_RADIUS, + borderRadius: CIRCULAR_BORDER_RADIUS, }; } if (config.id === 'config') { From 9f58dc69e84e20a9fd168e00f97182b9c3d529bb Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Tue, 22 Jul 2025 16:59:28 +0100 Subject: [PATCH 5/9] thin out some code --- .../src/components/shared/QuickAccessBar.tsx | 61 ++----------------- 1 file changed, 6 insertions(+), 55 deletions(-) diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 9e7908714..350e0f8e6 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -177,61 +177,12 @@ const QuickAccessBar = ({ const isActive = activeButton === config.id; if (isActive) { - // Active state - use specific icon colors - if (config.id === 'tools') { - return { - backgroundColor: 'var(--icon-tools-bg)', - color: 'var(--icon-tools-color)', - border: 'none', - borderRadius: getBorderRadius(config), - }; - } - if (config.id === 'read') { - return { - backgroundColor: 'var(--icon-read-bg)', - color: 'var(--icon-read-color)', - border: 'none', - borderRadius: getBorderRadius(config), - }; - } - if (config.id === 'sign') { - return { - backgroundColor: 'var(--icon-sign-bg)', - color: 'var(--icon-sign-color)', - border: 'none', - borderRadius: getBorderRadius(config), - }; - } - if (config.id === 'automate') { - return { - backgroundColor: 'var(--icon-automate-bg)', - color: 'var(--icon-automate-color)', - border: 'none', - borderRadius: getBorderRadius(config), - }; - } - if (config.id === 'files') { - return { - backgroundColor: 'var(--icon-files-bg)', - color: 'var(--icon-files-color)', - borderRadius: CIRCULAR_BORDER_RADIUS, - }; - } - if (config.id === 'activity') { - return { - backgroundColor: 'var(--icon-activity-bg)', - color: 'var(--icon-activity-color)', - borderRadius: CIRCULAR_BORDER_RADIUS, - }; - } - if (config.id === 'config') { - return { - backgroundColor: 'var(--icon-config-bg)', - color: 'var(--icon-config-color)', - border: 'none', - borderRadius: getBorderRadius(config), - }; - } + return { + backgroundColor: `var(--icon-${config.id}-bg)`, + color: `var(--icon-${config.id}-color)`, + border: 'none', + borderRadius: getBorderRadius(config), + }; } // Inactive state - use consistent inactive colors From 63e2791a8b83dd0e46f0beaa7acce29e06864372 Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Thu, 24 Jul 2025 16:55:11 +0100 Subject: [PATCH 6/9] stop using inline styling --- .../src/components/shared/QuickAccessBar.css | 110 ++++++++- .../src/components/shared/QuickAccessBar.tsx | 231 +++++++++++------- frontend/src/hooks/useIsOverflow.ts | 35 +++ 3 files changed, 281 insertions(+), 95 deletions(-) create mode 100644 frontend/src/hooks/useIsOverflow.ts diff --git a/frontend/src/components/shared/QuickAccessBar.css b/frontend/src/components/shared/QuickAccessBar.css index 6f4a89aeb..b484132b8 100644 --- a/frontend/src/components/shared/QuickAccessBar.css +++ b/frontend/src/components/shared/QuickAccessBar.css @@ -12,12 +12,118 @@ height: 32px; } -/* Scrollable navbar styling - scrollbars only show when scrolling */ +/* Action icon styles */ +.action-icon-style { + background-color: var(--icon-user-bg); + color: var(--icon-user-color); + border-radius: 50%; + width: 1.5rem; + height: 1.5rem; +} + +/* Main container styles */ +.quick-access-bar-main { + background-color: var(--bg-muted); + width: 5rem; + min-width: 5rem; + max-width: 5rem; + position: relative; + z-index: 10; +} + +/* Header padding */ +.quick-access-header { + padding: 1rem 0.5rem 0.5rem 0.5rem; +} + +/* Nav header divider */ +.nav-header-divider { + width: 3.75rem; + border-color: var(--color-gray-300); + margin-top: 0.5rem; + margin-bottom: 1rem; +} + +/* All tools text styles */ +.all-tools-text { + margin-top: 0.75rem; + font-size: 0.75rem; + text-rendering: optimizeLegibility; + font-synthesis: none; +} + +.all-tools-text.active { + color: var(--text-primary); + font-weight: bold; +} + +.all-tools-text.inactive { + color: var(--color-gray-700); + font-weight: normal; +} + +/* Overflow divider */ +.overflow-divider { + width: 3.75rem; + border-color: var(--color-gray-300); + margin: 0 0.5rem; +} + +/* Scrollable content area */ .quick-access-bar { overflow-x: auto; - overflow-y: hidden; + overflow-y: auto; scrollbar-gutter: stable both-edges; -webkit-overflow-scrolling: touch; + padding: 0 0.5rem 1rem 0.5rem; +} + +/* Scrollable content container */ +.scrollable-content { + display: flex; + flex-direction: column; + height: 100%; + min-height: 100%; +} + +/* Button text styles */ +.button-text { + margin-top: 0.75rem; + font-size: 0.75rem; + text-rendering: optimizeLegibility; + font-synthesis: none; +} + +.button-text.active { + color: var(--text-primary); + font-weight: bold; +} + +.button-text.inactive { + color: var(--color-gray-700); + font-weight: normal; +} + +/* Content divider */ +.content-divider { + width: 3.75rem; + border-color: var(--color-gray-300); +} + +/* Spacer */ +.spacer { + flex: 1; + margin-top: 1rem; +} + +/* Config button text */ +.config-button-text { + margin-top: 0.75rem; + font-size: 0.75rem; + color: var(--color-gray-700); + font-weight: normal; + text-rendering: optimizeLegibility; + font-synthesis: none; } /* Hide scrollbar by default, show on scroll (Webkit browsers - Chrome, Safari, Edge) */ diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 350e0f8e6..196fa01bb 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useRef } from "react"; import { ActionIcon, Stack, Tooltip, Divider } from "@mantine/core"; import MenuBookIcon from "@mui/icons-material/MenuBookRounded"; import AppsIcon from "@mui/icons-material/AppsRounded"; @@ -10,6 +10,7 @@ import NotificationsIcon from "@mui/icons-material/NotificationsRounded"; import { useRainbowThemeContext } from "./RainbowThemeProvider"; import rainbowStyles from '../../styles/rainbow.module.css'; import AppConfigModal from './AppConfigModal'; +import { useIsOverflow } from '../../hooks/useIsOverflow'; import './QuickAccessBar.css'; interface QuickAccessBarProps { @@ -31,15 +32,17 @@ interface ButtonConfig { onClick: () => void; } -const actionIconStyle = { - backgroundColor: 'var(--icon-user-bg)', - color: 'var(--icon-user-color)', - borderRadius: '50%', - width: '1.5rem', - height: '1.5rem', -}; - -function NavHeader() { +function NavHeader({ + activeButton, + setActiveButton, + onReaderToggle, + onToolsClick +}: { + activeButton: string; + setActiveButton: (id: string) => void; + onReaderToggle: () => void; + onToolsClick: () => void; +}) { return ( <>
@@ -47,7 +50,7 @@ function NavHeader() { @@ -56,7 +59,7 @@ function NavHeader() { @@ -65,11 +68,36 @@ function NavHeader() { {/* Divider after top icons */} + {/* All Tools button below divider */} + +
+ { + setActiveButton('tools'); + onReaderToggle(); + onToolsClick(); + }} + style={{ + backgroundColor: activeButton === 'tools' ? 'var(--icon-tools-bg)' : 'var(--icon-inactive-bg)', + color: activeButton === 'tools' ? 'var(--icon-tools-color)' : 'var(--icon-inactive-color)', + border: 'none', + borderRadius: '8px', + }} + className={activeButton === 'tools' ? 'activeIconScale' : ''} + > + + + + + + All Tools + +
+
); } @@ -85,21 +113,10 @@ const QuickAccessBar = ({ const { isRainbowMode } = useRainbowThemeContext(); const [configModalOpen, setConfigModalOpen] = useState(false); const [activeButton, setActiveButton] = useState('tools'); + const scrollableRef = useRef(null); + const isOverflow = useIsOverflow(scrollableRef); const buttonConfigs: ButtonConfig[] = [ - { - id: 'tools', - name: 'All Tools', - icon: , - tooltip: 'View all available tools', - size: 'lg', - isRound: false, - onClick: () => { - setActiveButton('tools'); - onReaderToggle(); - onToolsClick(); - } - }, { id: 'read', name: 'Read', @@ -194,74 +211,102 @@ const QuickAccessBar = ({ }; }; - const getTextStyle = (config: ButtonConfig) => { - const isActive = activeButton === config.id; - return { - marginTop: '0.75rem', - fontSize: '0.75rem', - color: isActive ? 'var(--text-primary)' : 'var(--color-gray-700)', - fontWeight: isActive ? 'bold' : 'normal', - textRendering: 'optimizeLegibility' as const, - fontSynthesis: 'none' as const - }; - }; - return (
{ - // Prevent the wheel event from bubbling up to parent containers - e.stopPropagation(); - }} + className={`h-screen flex flex-col w-20 quick-access-bar-main ${isRainbowMode ? rainbowStyles.rainbowPaper : ''}`} > - - - {buttonConfigs.map((config, index) => ( - - -
- - - {config.icon} - - - - {config.name} + {/* Fixed header outside scrollable area */} +
+ +
+ + {/* Conditional divider when overflowing */} + {isOverflow && ( + + )} + + {/* Scrollable content area */} +
{ + // Prevent the wheel event from bubbling up to parent containers + e.stopPropagation(); + }} + > +
+ {/* Top section with main buttons */} + + {buttonConfigs.slice(0, -1).map((config, index) => ( + + +
+ + + {config.icon} + + + + {config.name} + +
+
+ + {/* Add divider after Automate button (index 2) */} + {index === 2 && ( + + )} +
+ ))} +
+ + {/* Spacer to push Config button to bottom */} +
+ + {/* Config button at the bottom */} + +
+ { + setConfigModalOpen(true); + }} + style={{ + backgroundColor: 'var(--icon-inactive-bg)', + color: 'var(--icon-inactive-color)', + border: 'none', + borderRadius: '8px', + }} + > + + -
-
- - {/* Add divider after Automate button (index 3) */} - {index === 3 && ( - - )} - - {/* Add spacer before Config button (index 7) */} - {index === 5 &&
} - - ))} - + + + Config + +
+ +
+
, callback?: (isOverflow: boolean) => void) => { + const [isOverflow, setIsOverflow] = React.useState(undefined); + + React.useLayoutEffect(() => { + const { current } = ref; + + const trigger = () => { + if (!current) return; + + const hasOverflow = current.scrollHeight > current.clientHeight; + setIsOverflow(hasOverflow); + + if (callback) callback(hasOverflow); + }; + + if (current) { + if ('ResizeObserver' in window) { + const resizeObserver = new ResizeObserver(trigger); + resizeObserver.observe(current); + + // Cleanup function + return () => { + resizeObserver.disconnect(); + }; + } + + // Add a small delay to ensure the element is fully rendered + setTimeout(trigger, 0); + } + }, [callback, ref]); + + return isOverflow; +}; \ No newline at end of file From d7e0b506a8d8e90cc093709906ef781eeb030b14 Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Thu, 24 Jul 2025 17:17:04 +0100 Subject: [PATCH 7/9] remove fixed pixel sizes and inline styling --- .../src/components/shared/QuickAccessBar.css | 36 +++++++++++++++---- .../src/components/shared/QuickAccessBar.tsx | 20 +++++------ 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/frontend/src/components/shared/QuickAccessBar.css b/frontend/src/components/shared/QuickAccessBar.css index b484132b8..b1d22fcc3 100644 --- a/frontend/src/components/shared/QuickAccessBar.css +++ b/frontend/src/components/shared/QuickAccessBar.css @@ -8,8 +8,8 @@ display: flex; align-items: center; justify-content: center; - width: 32px; - height: 32px; + width: 2rem; + height: 2rem; } /* Action icon styles */ @@ -31,11 +31,30 @@ z-index: 10; } +/* Rainbow mode container */ +.quick-access-bar-main.rainbow-mode { + background-color: var(--bg-muted); + width: 5rem; + min-width: 5rem; + max-width: 5rem; + position: relative; + z-index: 10; +} + /* Header padding */ .quick-access-header { padding: 1rem 0.5rem 0.5rem 0.5rem; } +.nav-header { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + margin-bottom: 0; + gap: 0.5rem; +} + /* Nav header divider */ .nav-header-divider { width: 3.75rem; @@ -126,10 +145,15 @@ font-synthesis: none; } +/* Font size utility */ +.font-size-20 { + font-size: 20px; +} + /* Hide scrollbar by default, show on scroll (Webkit browsers - Chrome, Safari, Edge) */ .quick-access-bar::-webkit-scrollbar { - width: 8px; - height: 8px; + width: 0.5rem; + height: 0.5rem; background: transparent; } @@ -141,7 +165,7 @@ .quick-access-bar::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.2); - border-radius: 4px; + border-radius: 0.25rem; } .quick-access-bar::-webkit-scrollbar-track { @@ -152,4 +176,4 @@ .quick-access-bar { scrollbar-width: auto; scrollbar-color: rgba(0, 0, 0, 0.2) transparent; -} \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 196fa01bb..8bd4b7a6d 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -45,7 +45,7 @@ function NavHeader({ }) { return ( <> -
+
- + @@ -120,7 +120,7 @@ const QuickAccessBar = ({ { id: 'read', name: 'Read', - icon: , + icon: , tooltip: 'Read documents', size: 'lg', isRound: false, @@ -133,7 +133,7 @@ const QuickAccessBar = ({ id: 'sign', name: 'Sign', icon: - + signature , tooltip: 'Sign your document', @@ -144,7 +144,7 @@ const QuickAccessBar = ({ { id: 'automate', name: 'Automate', - icon: , + icon: , tooltip: 'Automate workflows', size: 'lg', isRound: false, @@ -153,7 +153,7 @@ const QuickAccessBar = ({ { id: 'files', name: 'Files', - icon: , + icon: , tooltip: 'Manage files', isRound: true, size: 'lg', @@ -163,7 +163,7 @@ const QuickAccessBar = ({ id: 'activity', name: 'Activity', icon: - + vital_signs , tooltip: 'View activity and analytics', @@ -174,7 +174,7 @@ const QuickAccessBar = ({ { id: 'config', name: 'Config', - icon: , + icon: , tooltip: 'Configure settings', size: 'lg', onClick: () => { @@ -213,7 +213,7 @@ const QuickAccessBar = ({ return (
{/* Fixed header outside scrollable area */}
@@ -297,7 +297,7 @@ const QuickAccessBar = ({ }} > - + From 8d96d0d31ad28e4ac264dd52e57138640a84632c Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Thu, 24 Jul 2025 21:21:46 +0100 Subject: [PATCH 8/9] add some comments to useIsOverflowing --- frontend/src/hooks/useIsOverflow.ts | 40 ++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/frontend/src/hooks/useIsOverflow.ts b/frontend/src/hooks/useIsOverflow.ts index fdf0340e5..9a2e381cd 100644 --- a/frontend/src/hooks/useIsOverflow.ts +++ b/frontend/src/hooks/useIsOverflow.ts @@ -1,31 +1,69 @@ import * as React from 'react'; + +/** + Hook to detect if an element's content overflows its container + + + Parameters: + - ref: React ref to the element to monitor + - callback: Optional callback function called when overflow state changes + + Returns: boolean | undefined - true if overflowing, false if not, undefined before first check + + Usage example: + + useEffect(() => { + if (isOverflow) { + // Do something + } + }, [isOverflow]); + + const scrollableRef = useRef(null); + const isOverflow = useIsOverflow(scrollableRef); + + Fallback example (for browsers without ResizeObserver): + + return ( +
+ {Content that might overflow} +
+ ); +*/ + + export const useIsOverflow = (ref: React.RefObject, callback?: (isOverflow: boolean) => void) => { + // State to track overflow status const [isOverflow, setIsOverflow] = React.useState(undefined); React.useLayoutEffect(() => { const { current } = ref; + // Function to check if element is overflowing const trigger = () => { if (!current) return; + // Compare scroll height (total content height) vs client height (visible height) const hasOverflow = current.scrollHeight > current.clientHeight; setIsOverflow(hasOverflow); + // Call optional callback with overflow state if (callback) callback(hasOverflow); }; if (current) { + // Use ResizeObserver for modern browsers (real-time detection) if ('ResizeObserver' in window) { const resizeObserver = new ResizeObserver(trigger); resizeObserver.observe(current); - // Cleanup function + // Cleanup function to disconnect observer return () => { resizeObserver.disconnect(); }; } + // Fallback for browsers without ResizeObserver support // Add a small delay to ensure the element is fully rendered setTimeout(trigger, 0); } From 7403dee98dabcc5cdbd251b76eaad4531d48beb6 Mon Sep 17 00:00:00 2001 From: EthanHealy01 Date: Thu, 24 Jul 2025 21:23:45 +0100 Subject: [PATCH 9/9] rename useIsOverflow hook and add comments to explain usage --- frontend/src/components/shared/QuickAccessBar.tsx | 4 ++-- frontend/src/hooks/{useIsOverflow.ts => useIsOverflowing.ts} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename frontend/src/hooks/{useIsOverflow.ts => useIsOverflowing.ts} (91%) diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 8bd4b7a6d..22a49617e 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -10,7 +10,7 @@ import NotificationsIcon from "@mui/icons-material/NotificationsRounded"; import { useRainbowThemeContext } from "./RainbowThemeProvider"; import rainbowStyles from '../../styles/rainbow.module.css'; import AppConfigModal from './AppConfigModal'; -import { useIsOverflow } from '../../hooks/useIsOverflow'; +import { useIsOverflowing } from '../../hooks/useIsOverflowing'; import './QuickAccessBar.css'; interface QuickAccessBarProps { @@ -114,7 +114,7 @@ const QuickAccessBar = ({ const [configModalOpen, setConfigModalOpen] = useState(false); const [activeButton, setActiveButton] = useState('tools'); const scrollableRef = useRef(null); - const isOverflow = useIsOverflow(scrollableRef); + const isOverflow = useIsOverflowing(scrollableRef); const buttonConfigs: ButtonConfig[] = [ { diff --git a/frontend/src/hooks/useIsOverflow.ts b/frontend/src/hooks/useIsOverflowing.ts similarity index 91% rename from frontend/src/hooks/useIsOverflow.ts rename to frontend/src/hooks/useIsOverflowing.ts index 9a2e381cd..b5e6d3962 100644 --- a/frontend/src/hooks/useIsOverflow.ts +++ b/frontend/src/hooks/useIsOverflowing.ts @@ -20,7 +20,7 @@ import * as React from 'react'; }, [isOverflow]); const scrollableRef = useRef(null); - const isOverflow = useIsOverflow(scrollableRef); + const isOverflow = useIsOverflowing(scrollableRef); Fallback example (for browsers without ResizeObserver): @@ -32,7 +32,7 @@ import * as React from 'react'; */ -export const useIsOverflow = (ref: React.RefObject, callback?: (isOverflow: boolean) => void) => { +export const useIsOverflowing = (ref: React.RefObject, callback?: (isOverflow: boolean) => void) => { // State to track overflow status const [isOverflow, setIsOverflow] = React.useState(undefined);