Skip to content

Commit 34054e6

Browse files
feat: Ui Enhancements introduce two Ui changes, Filter section toggle and document section Resizer
2 parents 789af06 + 6f24121 commit 34054e6

File tree

4 files changed

+305
-18
lines changed

4 files changed

+305
-18
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
.panel-resizer {
2+
width: 8px;
3+
background: #f0f0f0;
4+
border-left: 1px solid #ddd;
5+
border-right: 1px solid #ddd;
6+
cursor: col-resize;
7+
position: relative;
8+
display: flex;
9+
align-items: center;
10+
justify-content: center;
11+
transition: background-color 0.2s ease;
12+
user-select: none;
13+
flex-shrink: 0;
14+
15+
&:hover {
16+
background: #e0e0e0;
17+
}
18+
19+
&.dragging {
20+
background: #d0d0d0;
21+
cursor: col-resize;
22+
}
23+
}
24+
25+
.resizer-handle {
26+
height: 60px;
27+
width: 100%;
28+
display: flex;
29+
align-items: center;
30+
justify-content: center;
31+
pointer-events: none;
32+
}
33+
34+
.resizer-dots {
35+
display: flex;
36+
flex-direction: column;
37+
gap: 3px;
38+
align-items: center;
39+
}
40+
41+
.dot {
42+
width: 3px;
43+
height: 3px;
44+
background-color: #999;
45+
border-radius: 50%;
46+
}
47+
48+
.panel-resizer:hover .dot {
49+
background-color: #666;
50+
}
51+
52+
.panel-resizer.dragging .dot {
53+
background-color: #333;
54+
}
55+
56+
/* Global cursor override when dragging */
57+
body.resizing {
58+
cursor: col-resize !important;
59+
user-select: none !important;
60+
}
61+
62+
body.resizing * {
63+
pointer-events: none !important;
64+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import React, { useState, useRef, useEffect } from 'react';
2+
3+
interface PanelResizerProps {
4+
onResize?: (leftWidth: number, rightWidth: number) => void;
5+
minLeftWidth?: number;
6+
minRightWidth?: number;
7+
initialLeftWidth?: number;
8+
}
9+
10+
export const PanelResizer: React.FC<PanelResizerProps> = ({
11+
onResize,
12+
minLeftWidth = 200,
13+
minRightWidth = 200,
14+
initialLeftWidth = 50
15+
}) => {
16+
const [isDragging, setIsDragging] = useState(false);
17+
const resizerRef = useRef<HTMLDivElement>(null);
18+
19+
useEffect(() => {
20+
const handleMouseMove = (e: MouseEvent) => {
21+
if (!isDragging || !resizerRef.current) return;
22+
23+
const container = resizerRef.current.parentElement;
24+
if (!container) return;
25+
26+
const containerRect = container.getBoundingClientRect();
27+
const newLeftWidth = ((e.clientX - containerRect.left) / containerRect.width) * 100;
28+
const newRightWidth = 100 - newLeftWidth;
29+
30+
// Apply minimum width constraints
31+
const minLeftPercent = (minLeftWidth / containerRect.width) * 100;
32+
const minRightPercent = (minRightWidth / containerRect.width) * 100;
33+
34+
if (newLeftWidth >= minLeftPercent && newRightWidth >= minRightPercent) {
35+
onResize?.(newLeftWidth, newRightWidth);
36+
}
37+
};
38+
39+
const handleMouseUp = () => {
40+
setIsDragging(false);
41+
document.body.style.cursor = '';
42+
document.body.style.userSelect = '';
43+
};
44+
45+
if (isDragging) {
46+
document.body.style.cursor = 'col-resize';
47+
document.body.style.userSelect = 'none';
48+
document.addEventListener('mousemove', handleMouseMove);
49+
document.addEventListener('mouseup', handleMouseUp);
50+
}
51+
52+
return () => {
53+
document.removeEventListener('mousemove', handleMouseMove);
54+
document.removeEventListener('mouseup', handleMouseUp);
55+
};
56+
}, [isDragging, onResize, minLeftWidth, minRightWidth]);
57+
58+
const handleMouseDown = () => {
59+
setIsDragging(true);
60+
};
61+
62+
const resizerStyle: React.CSSProperties = {
63+
width: '8px',
64+
background: isDragging ? '#d0d0d0' : '#f0f0f0',
65+
borderLeft: '1px solid #ddd',
66+
borderRight: '1px solid #ddd',
67+
cursor: 'col-resize',
68+
position: 'relative',
69+
display: 'flex',
70+
alignItems: 'center',
71+
justifyContent: 'center',
72+
transition: 'background-color 0.2s ease',
73+
userSelect: 'none',
74+
flexShrink: 0,
75+
};
76+
77+
const handleStyle: React.CSSProperties = {
78+
height: '60px',
79+
width: '100%',
80+
display: 'flex',
81+
alignItems: 'center',
82+
justifyContent: 'center',
83+
pointerEvents: 'none',
84+
};
85+
86+
const dotsStyle: React.CSSProperties = {
87+
display: 'flex',
88+
flexDirection: 'column',
89+
gap: '3px',
90+
alignItems: 'center',
91+
};
92+
93+
const dotStyle: React.CSSProperties = {
94+
width: '3px',
95+
height: '3px',
96+
backgroundColor: isDragging ? '#333' : '#999',
97+
borderRadius: '50%',
98+
};
99+
100+
return (
101+
<div
102+
ref={resizerRef}
103+
style={resizerStyle}
104+
onMouseDown={handleMouseDown}
105+
onMouseEnter={(e) => {
106+
if (!isDragging) {
107+
(e.target as HTMLElement).style.background = '#e0e0e0';
108+
}
109+
}}
110+
onMouseLeave={(e) => {
111+
if (!isDragging) {
112+
(e.target as HTMLElement).style.background = '#f0f0f0';
113+
}
114+
}}
115+
>
116+
<div style={handleStyle}>
117+
<div style={dotsStyle}>
118+
<div style={dotStyle}></div>
119+
<div style={dotStyle}></div>
120+
<div style={dotStyle}></div>
121+
</div>
122+
</div>
123+
</div>
124+
);
125+
};

App/frontend-app/src/pages/home/home.module.scss

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,64 @@
3737
.filters-panel {
3838
width: 15% !important;
3939
min-width: 229px;
40+
transition: width 0.3s ease-in-out;
4041
}
4142

4243
.documents-panel {
4344
width: 34% !important;
4445
min-width: 410px;
46+
transition: width 0.3s ease-in-out;
4547
}
4648

4749
.chat-panel {
4850
width: 51% !important;
51+
min-width: 300px;
52+
transition: width 0.3s ease-in-out;
4953
}
54+
55+
.filters-panel-collapsed {
56+
width: 50px !important;
57+
min-width: 50px;
58+
transition: width 0.3s ease-in-out;
59+
}
60+
}
61+
62+
/* Resizable panels styling */
63+
.resizable-container {
64+
display: flex;
65+
flex-direction: row;
66+
flex-grow: 1;
67+
overflow: hidden;
68+
}
69+
70+
.resizable-panel {
71+
display: flex;
72+
flex-direction: column;
73+
min-width: 200px;
74+
overflow: hidden;
75+
}
76+
77+
/* Filter panel transitions */
78+
.filter-panel-enter {
79+
opacity: 0;
80+
transform: translateX(-100%);
81+
}
82+
83+
.filter-panel-enter-active {
84+
opacity: 1;
85+
transform: translateX(0);
86+
transition: opacity 300ms, transform 300ms;
87+
}
88+
89+
.filter-panel-exit {
90+
opacity: 1;
91+
transform: translateX(0);
92+
}
93+
94+
.filter-panel-exit-active {
95+
opacity: 0;
96+
transform: translateX(-100%);
97+
transition: opacity 300ms, transform 300ms;
5098
}
5199

52100
@media (max-width: 1080px) {

App/frontend-app/src/pages/home/home.tsx

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import { SidecarCopilot } from "../../components/sidecarCopilot/sidecar";
3232
import homeStyles from "./home.module.scss";
3333
import { ChatRoom } from "../../components/chat/chatRoom";
3434
import UploadFilesButton from "../../components/uploadButton/uploadButton";
35+
import { PanelResizer } from "../../components/resizer/panelResizer";
36+
import { ChevronLeft24Regular, ChevronRight24Regular } from "@fluentui/react-icons";
3537

3638
const useStyles = makeStyles({
3739
dropdown: {
@@ -64,6 +66,9 @@ export function Home({ isSearchResultsPage }: HomeProps) {
6466
const [isLoading, setIsLoading] = useState<boolean>(false);
6567
const [searchParams, setSearchParams] = useSearchParams();
6668
const [areFiltersVisible, setAreFiltersVisible] = useState(true);
69+
const [isFilterPanelCollapsed, setIsFilterPanelCollapsed] = useState(false);
70+
const [documentsWidth, setDocumentsWidth] = useState(34); // Initial width percentage
71+
const [chatWidth, setChatWidth] = useState(51); // Initial width percentage
6772
const [showCopilot, setShowCopilot] = useState<boolean>(false);
6873
const [incomingFilter, setIncomingFilter] = useState(
6974
"(document/embedded eq false and document/translated eq false)"
@@ -236,6 +241,15 @@ export function Home({ isSearchResultsPage }: HomeProps) {
236241
setAreFiltersVisible((prevAreFiltersVisible) => !prevAreFiltersVisible);
237242
}
238243

244+
function toggleFilterPanel(): void {
245+
setIsFilterPanelCollapsed(!isFilterPanelCollapsed);
246+
}
247+
248+
const handlePanelResize = (leftWidth: number, rightWidth: number) => {
249+
setDocumentsWidth(leftWidth);
250+
setChatWidth(rightWidth);
251+
};
252+
239253
const handleSortSelected = (sort: string) => {
240254
setInOrderBy(sort);
241255
};
@@ -526,25 +540,50 @@ export function Home({ isSearchResultsPage }: HomeProps) {
526540

527541
<main className="w-full h-full flex flex-col">
528542
<div className="flex flex-col md:flex-row flex-grow">
529-
{/* Left Section: Filter */}
530-
<div className={`flex flex-col w-full bg-white shadow-md p-4 ${homeStyles["no-bottom-padding"]} ${homeStyles["filters-panel"]}`}>
531-
{/* Keep the FilterButton at the top */}
532-
<div className="mb-4">
533-
<FilterButton onFilterPress={onFilterPress} />
543+
{/* Left Section: Filter Panel with Toggle */}
544+
{!isFilterPanelCollapsed && (
545+
<div className={`flex flex-col w-full bg-white shadow-md p-4 ${homeStyles["no-bottom-padding"]} ${homeStyles["filters-panel"]} transition-all duration-300`}>
546+
{/* Filter Header with Toggle Button */}
547+
<div className="mb-4 flex items-center justify-between">
548+
<FilterButton onFilterPress={onFilterPress} />
549+
<Button
550+
appearance="subtle"
551+
icon={<ChevronLeft24Regular />}
552+
onClick={toggleFilterPanel}
553+
title="Collapse filters"
554+
size="small"
555+
/>
556+
</div>
557+
{areFiltersVisible && (
558+
<Filter
559+
onFilterChanged={onFilterChanged}
560+
prevSelectedFilters={persistedFilters}
561+
keywordFilterInfo={data?.keywordFilterInfo}
562+
clearFilters={clearFilters}
563+
onFilterCleared={handleFilterCleared}
564+
/>
565+
)}
534566
</div>
535-
{areFiltersVisible && (
536-
<Filter
537-
onFilterChanged={onFilterChanged}
538-
prevSelectedFilters={persistedFilters}
539-
keywordFilterInfo={data?.keywordFilterInfo}
540-
clearFilters={clearFilters}
541-
onFilterCleared={handleFilterCleared}
542-
/>
543-
)}
544-
</div>
567+
)}
545568

546-
{/* Right Section: Search Box and Fluent v2 Dropdown */}
547-
<div className={`flex flex-col w-full bg-white shadow-md p-4 ${homeStyles["no-bottom-padding"]} ${homeStyles["documents-panel"]}`}>
569+
{/* Collapsed Filter Toggle Button */}
570+
{isFilterPanelCollapsed && (
571+
<div className="flex flex-col bg-white shadow-md p-2 min-w-[50px] items-center">
572+
<Button
573+
appearance="subtle"
574+
icon={<ChevronRight24Regular />}
575+
onClick={toggleFilterPanel}
576+
title="Expand filters"
577+
size="small"
578+
/>
579+
</div>
580+
)}
581+
582+
{/* Documents Section */}
583+
<div
584+
className={`flex flex-col bg-white shadow-md p-4 ${homeStyles["no-bottom-padding"]} transition-all duration-300`}
585+
style={{ width: `${documentsWidth}%`, minWidth: '300px' }}
586+
>
548587
<div className="flex flex-row items-center space-x-2"> {/* Adjusted space between elements */}
549588
{/* Search Box */}
550589
<SearchBox
@@ -648,8 +687,19 @@ export function Home({ isSearchResultsPage }: HomeProps) {
648687
</div>
649688
</div>
650689

690+
{/* Panel Resizer */}
691+
<PanelResizer
692+
onResize={handlePanelResize}
693+
initialLeftWidth={documentsWidth}
694+
minLeftWidth={300}
695+
minRightWidth={300}
696+
/>
697+
651698
{/* Chat Room Section */}
652-
<div className={`flex flex-col w-full bg-white shadow-md ${homeStyles["chat-panel"]}`}>
699+
<div
700+
className={`flex flex-col bg-white shadow-md ${homeStyles["chat-panel"]} transition-all duration-300`}
701+
style={{ width: `${chatWidth}%`, minWidth: '300px' }}
702+
>
653703
<div className="flex flex-col h-full">
654704
<div className={`flex-grow overflow-y-auto ${homeStyles["no-bottom-padding"]}` } style={{ backgroundColor: '#f7f7f7', overflowX: 'hidden', overflowY: 'auto', display: 'flex', maxBlockSize: 'calc(100vh - 60px)'}}>
655705
<ChatRoom

0 commit comments

Comments
 (0)