sidebar improvements
This commit is contained in:
@@ -219,6 +219,7 @@ const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
|
|||||||
>
|
>
|
||||||
<BuilderSidebar
|
<BuilderSidebar
|
||||||
isCollapsed={isSidebarCollapsed()}
|
isCollapsed={isSidebarCollapsed()}
|
||||||
|
onToggleSidebar={() => setIsSidebarCollapsed(!isSidebarCollapsed())}
|
||||||
unassignedItems={unassignedItems()}
|
unassignedItems={unassignedItems()}
|
||||||
grandTotals={grandTotals()}
|
grandTotals={grandTotals()}
|
||||||
hoveredScopeId={hoveredScopeId()}
|
hoveredScopeId={hoveredScopeId()}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createSignal, onCleanup, Show } from 'solid-js';
|
import { createSignal, onCleanup, Show } from 'solid-js';
|
||||||
import type { Component } from 'solid-js';
|
import type { Component } from 'solid-js';
|
||||||
import { PanelLeftOpen, PanelLeftClose, Calculator, Plus, Upload, Table, Download, History, Save, ChevronDown, MoreHorizontal } from 'lucide-solid';
|
import { Calculator, Plus, Upload, Table, Download, History, Save, ChevronDown, MoreHorizontal } from 'lucide-solid';
|
||||||
import { appStore } from '../../store/appStore';
|
import { appStore } from '../../store/appStore';
|
||||||
|
|
||||||
interface BuilderHeaderProps {
|
interface BuilderHeaderProps {
|
||||||
@@ -34,13 +34,6 @@ export const BuilderHeader: Component<BuilderHeaderProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div class="min-h-[4rem] py-3 px-6 border-b border-border bg-background/50 flex flex-wrap gap-y-4 items-center justify-between sticky top-0 z-50 backdrop-blur-md">
|
<div class="min-h-[4rem] py-3 px-6 border-b border-border bg-background/50 flex flex-wrap gap-y-4 items-center justify-between sticky top-0 z-50 backdrop-blur-md">
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<button
|
|
||||||
onClick={props.onToggleSidebar}
|
|
||||||
class="p-2 hover:bg-accent rounded-xl transition-colors text-muted-foreground shrink-0"
|
|
||||||
title={props.isSidebarCollapsed ? "Expand Sidebar" : "Collapse Sidebar"}
|
|
||||||
>
|
|
||||||
{props.isSidebarCollapsed ? <PanelLeftOpen class="w-5 h-5" /> : <PanelLeftClose class="w-5 h-5" />}
|
|
||||||
</button>
|
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="p-2 bg-primary rounded-xl text-primary-foreground shadow-md shadow-primary/20 shrink-0">
|
<div class="p-2 bg-primary rounded-xl text-primary-foreground shadow-md shadow-primary/20 shrink-0">
|
||||||
<Calculator class="w-5 h-5" />
|
<Calculator class="w-5 h-5" />
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import type { Component } from 'solid-js';
|
import type { Component } from 'solid-js';
|
||||||
import { For } from 'solid-js';
|
import { For } from 'solid-js';
|
||||||
import { Plus, ListTree, ChevronRight } from 'lucide-solid';
|
import { Plus, ListTree, ChevronRight, PanelLeftOpen, PanelLeftClose } from 'lucide-solid';
|
||||||
import { appStore } from '../../store/appStore';
|
import { appStore } from '../../store/appStore';
|
||||||
import type { EstimateItem } from '../../types';
|
import type { EstimateItem } from '../../types';
|
||||||
|
|
||||||
interface BuilderSidebarProps {
|
interface BuilderSidebarProps {
|
||||||
isCollapsed: boolean;
|
isCollapsed: boolean;
|
||||||
|
onToggleSidebar: () => void;
|
||||||
unassignedItems: EstimateItem[];
|
unassignedItems: EstimateItem[];
|
||||||
grandTotals: { grandTotal: number };
|
grandTotals: { grandTotal: number };
|
||||||
hoveredScopeId: string | null;
|
hoveredScopeId: string | null;
|
||||||
@@ -24,15 +25,40 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
/*
|
/*
|
||||||
* The sidebar sits inside a CSS grid column (in EstimateBuilder.tsx).
|
* The sidebar sits inside a CSS grid column (in EstimateBuilder.tsx).
|
||||||
* It fills its column completely. When the column width transitions to 0,
|
* We use overflow-visible on the aside so the toggle button can remain
|
||||||
* the sidebar content is clipped by overflow-hidden on this element.
|
* visible and clickable even when the grid column width is 0px.
|
||||||
* This means the main content genuinely reclaims the space.
|
|
||||||
*/
|
*/
|
||||||
<aside
|
<aside
|
||||||
class="h-[calc(100vh-var(--spacing-header))] sticky top-[var(--spacing-header)] bg-background border-r border-border flex flex-col overflow-hidden"
|
class="h-[calc(100vh-var(--spacing-header))] sticky top-[var(--spacing-header)] bg-background border-r border-border flex flex-col overflow-visible z-40 transition-colors"
|
||||||
style={{ "min-width": 0 }}
|
style={{ "min-width": 0 }}
|
||||||
>
|
>
|
||||||
<div class="flex-1 overflow-y-auto overflow-x-hidden px-4 py-8 w-72">
|
{/*
|
||||||
|
* Sticky Toggle Button
|
||||||
|
* Anchored to the right edge (100% width) of the sidebar column.
|
||||||
|
* As the column width animates, the button follows perfectly.
|
||||||
|
* We use translateX to adjust horizontal offset from the edge.
|
||||||
|
*/}
|
||||||
|
<button
|
||||||
|
onClick={props.onToggleSidebar}
|
||||||
|
class="absolute top-4 z-50 w-10 h-10 bg-background hover:bg-accent rounded-xl transition-all duration-500 ease-in-out shadow-premium border border-border text-muted-foreground hover:text-primary active:scale-95 flex items-center justify-center cursor-pointer"
|
||||||
|
style={{
|
||||||
|
left: '100%',
|
||||||
|
transform: props.isCollapsed
|
||||||
|
? 'translateX(1.25rem)' // When closed (0px width), sit 20px off the left edge
|
||||||
|
: 'translateX(-0.5rem)' // When open, hang 80% out to the right (overlap only 8px)
|
||||||
|
}}
|
||||||
|
title={props.isCollapsed ? "Expand Sidebar" : "Collapse Sidebar"}
|
||||||
|
>
|
||||||
|
{props.isCollapsed ? <PanelLeftOpen class="w-5 h-5" /> : <PanelLeftClose class="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/*
|
||||||
|
* Sidebar Content Wrapper
|
||||||
|
* We apply the overflow-hidden here so the content doesn't leak out
|
||||||
|
* when the grid column is 0px.
|
||||||
|
*/}
|
||||||
|
<div class={`flex-1 overflow-y-auto overflow-x-hidden transition-all duration-500 flex flex-col ${props.isCollapsed ? 'w-0 opacity-0 pointer-events-none' : 'w-72 opacity-100'}`}>
|
||||||
|
<div class="flex-1 px-4 py-8">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<div class="flex items-center justify-between mb-4 px-2">
|
<div class="flex items-center justify-between mb-4 px-2">
|
||||||
<span class="text-[10px] font-black text-muted-foreground uppercase tracking-[0.2em] whitespace-nowrap">Estimate Navigator</span>
|
<span class="text-[10px] font-black text-muted-foreground uppercase tracking-[0.2em] whitespace-nowrap">Estimate Navigator</span>
|
||||||
@@ -144,6 +170,7 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user