mid layout change

This commit is contained in:
2026-03-13 18:41:44 -05:00
parent c72da2fad5
commit b52564d44d
2 changed files with 36 additions and 41 deletions
+10 -2
View File
@@ -208,7 +208,15 @@ const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
onSaveChanges={() => appStore.isLatestVersion() ? saveChangesToCloud() : saveAsNewestToCloud()} onSaveChanges={() => appStore.isLatestVersion() ? saveChangesToCloud() : saveAsNewestToCloud()}
/> />
<div class="flex flex-1 relative items-start"> <div
style={{
display: 'grid',
'grid-template-columns': isSidebarCollapsed() ? '0px 1fr' : '18rem 1fr',
transition: 'grid-template-columns 500ms ease',
'align-items': 'start',
flex: '1',
}}
>
<BuilderSidebar <BuilderSidebar
isCollapsed={isSidebarCollapsed()} isCollapsed={isSidebarCollapsed()}
unassignedItems={unassignedItems()} unassignedItems={unassignedItems()}
@@ -219,7 +227,7 @@ const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
onAddScope={addScope} onAddScope={addScope}
/> />
<main onClick={clearSelection} class="flex-1 min-w-0 p-4 md:p-8 bg-zinc-50/50 space-y-8 min-h-screen"> <main onClick={clearSelection} class="min-w-0 p-4 md:p-8 bg-zinc-50/50 space-y-8 min-h-screen">
<ProjectDetailsForm /> <ProjectDetailsForm />
<div class="bg-muted/30 border border-border rounded-3xl p-6 mb-8"> <div class="bg-muted/30 border border-border rounded-3xl p-6 mb-8">
@@ -22,14 +22,20 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
}; };
return ( return (
/*
* The sidebar sits inside a CSS grid column (in EstimateBuilder.tsx).
* It fills its column completely. When the column width transitions to 0,
* the sidebar content is clipped by overflow-hidden on this element.
* This means the main content genuinely reclaims the space.
*/
<aside <aside
class={`h-[calc(100vh-var(--spacing-header))] sticky top-header bg-background border-r border-border flex flex-col transition-all duration-500 ease-in-out relative group/sidebar class="h-[calc(100vh-var(--spacing-header))] sticky top-[var(--spacing-header)] bg-background border-r border-border flex flex-col overflow-hidden"
${props.isCollapsed ? 'w-0 opacity-0 -translate-x-full overflow-hidden' : 'w-72 opacity-100'}`} style={{ "min-width": 0 }}
> >
<div class="flex-1 overflow-y-auto px-4 py-8"> <div class="flex-1 overflow-y-auto overflow-x-hidden px-4 py-8 w-72">
<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]">Estimate Navigator</span> <span class="text-[10px] font-black text-muted-foreground uppercase tracking-[0.2em] whitespace-nowrap">Estimate Navigator</span>
<button <button
onClick={props.onAddScope} onClick={props.onAddScope}
class="p-1.5 bg-muted hover:bg-primary/10 text-muted-foreground hover:text-primary rounded-lg transition-all" class="p-1.5 bg-muted hover:bg-primary/10 text-muted-foreground hover:text-primary rounded-lg transition-all"
@@ -53,18 +59,15 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
const headerOffset = 180; const headerOffset = 180;
const elementPosition = el.getBoundingClientRect().top; const elementPosition = el.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - headerOffset; const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
window.scrollTo({ window.scrollTo({ top: offsetPosition, behavior: 'smooth' });
top: offsetPosition,
behavior: 'smooth'
});
} }
}} }}
class={`group flex items-center justify-between px-3 py-3 rounded-xl text-xs font-black uppercase tracking-wider transition-all cursor-pointer border-2 ${props.hoveredScopeId === 'unassigned' ? 'bg-primary/10 border-primary scale-[1.02] shadow-premium-lg shadow-primary/10 text-primary' : (props.unassignedItems.length > 0 ? 'bg-muted/30 text-foreground border-border/50 hover:bg-muted/50' : 'text-muted-foreground border-transparent hover:bg-accent')}`} class={`group flex items-center justify-between px-3 py-3 rounded-xl text-xs font-black uppercase tracking-wider transition-all cursor-pointer border-2 ${props.hoveredScopeId === 'unassigned' ? 'bg-primary/10 border-primary scale-[1.02] shadow-premium text-primary' : (props.unassignedItems.length > 0 ? 'bg-muted/30 text-foreground border-border/50 hover:bg-muted/50' : 'text-muted-foreground border-transparent hover:bg-accent')}`}
> >
<span class="flex items-center gap-2"> <span class="flex items-center gap-2 whitespace-nowrap">
<ListTree class="w-4 h-4" /> Unassigned <ListTree class="w-4 h-4 shrink-0" /> Unassigned
</span> </span>
<span class={`px-2 py-0.5 rounded-lg text-[10px] font-black border transition-colors ${props.hoveredScopeId === 'unassigned' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background text-primary border-border'}`}> <span class={`px-2 py-0.5 rounded-lg text-[10px] font-black border transition-colors shrink-0 ${props.hoveredScopeId === 'unassigned' ? 'bg-primary text-primary-foreground border-primary' : 'bg-background text-primary border-border'}`}>
{props.unassignedItems.length} {props.unassignedItems.length}
</span> </span>
</div> </div>
@@ -76,28 +79,22 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
{(scope) => ( {(scope) => (
<div class="space-y-1"> <div class="space-y-1">
<div <div
onDragOver={(e) => { onDragOver={(e) => { e.preventDefault(); props.setHoveredScopeId(scope.id); }}
e.preventDefault();
props.setHoveredScopeId(scope.id);
}}
onDragLeave={() => props.setHoveredScopeId(null)} onDragLeave={() => props.setHoveredScopeId(null)}
onDrop={(e) => props.onDrop(e, scope.id)} onDrop={(e) => props.onDrop(e, scope.id)}
class={`group flex items-center justify-between px-4 py-3 rounded-xl text-sm font-bold transition-all cursor-pointer border-2 ${props.hoveredScopeId === scope.id ? 'bg-primary/10 border-primary scale-[1.02] shadow-premium-lg shadow-primary/10 text-primary-foreground' : 'text-muted-foreground border-transparent hover:bg-card hover:shadow-premium hover:border-border'}`} class={`group flex items-center justify-between px-4 py-3 rounded-xl text-sm font-bold transition-all cursor-pointer border-2 ${props.hoveredScopeId === scope.id ? 'bg-primary/10 border-primary scale-[1.02] shadow-premium text-primary' : 'text-muted-foreground border-transparent hover:bg-card hover:shadow-premium hover:border-border'}`}
onClick={() => { onClick={() => {
const el = document.getElementById(`scope-${scope.id}`); const el = document.getElementById(`scope-${scope.id}`);
if (el) { if (el) {
const headerOffset = 180; const headerOffset = 180;
const elementPosition = el.getBoundingClientRect().top; const elementPosition = el.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - headerOffset; const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
window.scrollTo({ window.scrollTo({ top: offsetPosition, behavior: 'smooth' });
top: offsetPosition,
behavior: 'smooth'
});
} }
}} }}
> >
<span class="truncate pr-2">{scope.name}</span> <span class="truncate pr-2">{scope.name}</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2 shrink-0">
<span class={`px-2 py-0.5 rounded-lg text-[10px] font-black border transition-colors ${props.hoveredScopeId === scope.id ? 'bg-primary text-primary-foreground border-primary' : 'bg-muted text-muted-foreground border-border'}`}> <span class={`px-2 py-0.5 rounded-lg text-[10px] font-black border transition-colors ${props.hoveredScopeId === scope.id ? 'bg-primary text-primary-foreground border-primary' : 'bg-muted text-muted-foreground border-border'}`}>
{items.filter(i => i.scopeId === scope.id).length} {items.filter(i => i.scopeId === scope.id).length}
</span> </span>
@@ -110,16 +107,9 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
<For each={subScopes.filter(ss => ss.scopeId === scope.id)}> <For each={subScopes.filter(ss => ss.scopeId === scope.id)}>
{(subScope) => ( {(subScope) => (
<div <div
onDragOver={(e) => { onDragOver={(e) => { e.preventDefault(); e.stopPropagation(); props.setHoveredScopeId(subScope.id); }}
e.preventDefault();
e.stopPropagation();
props.setHoveredScopeId(subScope.id);
}}
onDragLeave={() => props.setHoveredScopeId(null)} onDragLeave={() => props.setHoveredScopeId(null)}
onDrop={(e) => { onDrop={(e) => { e.stopPropagation(); props.onDrop(e, scope.id, subScope.id); }}
e.stopPropagation();
props.onDrop(e, scope.id, subScope.id);
}}
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
const el = document.getElementById(`subscope-${subScope.id}`); const el = document.getElementById(`subscope-${subScope.id}`);
@@ -127,16 +117,13 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
const headerOffset = 180; const headerOffset = 180;
const elementPosition = el.getBoundingClientRect().top; const elementPosition = el.getBoundingClientRect().top;
const offsetPosition = elementPosition + window.pageYOffset - headerOffset; const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
window.scrollTo({ window.scrollTo({ top: offsetPosition, behavior: 'smooth' });
top: offsetPosition,
behavior: 'smooth'
});
} }
}} }}
class={`flex items-center justify-between px-3 py-1.5 rounded-lg text-[11px] font-black uppercase tracking-wider transition-all cursor-pointer border ${props.hoveredScopeId === subScope.id ? 'bg-primary/5 border-primary/30 text-primary scale-[1.02]' : 'text-muted-foreground/60 border-transparent hover:bg-muted hover:text-foreground'}`} class={`flex items-center justify-between px-3 py-1.5 rounded-lg text-[11px] font-black uppercase tracking-wider transition-all cursor-pointer border ${props.hoveredScopeId === subScope.id ? 'bg-primary/5 border-primary/30 text-primary scale-[1.02]' : 'text-muted-foreground/60 border-transparent hover:bg-muted hover:text-foreground'}`}
> >
<span class="truncate">{subScope.name}</span> <span class="truncate">{subScope.name}</span>
<span class="text-[10px] opacity-60 font-medium"> <span class="text-[10px] opacity-60 font-medium shrink-0">
{items.filter(i => i.subScopeId === subScope.id).length} {items.filter(i => i.subScopeId === subScope.id).length}
</span> </span>
</div> </div>
@@ -150,9 +137,9 @@ export const BuilderSidebar: Component<BuilderSidebarProps> = (props) => {
</div> </div>
{/* Sidebar Footer */} {/* Sidebar Footer */}
<div class="p-4 border-t border-border bg-muted/30"> <div class="p-4 border-t border-border bg-muted/30 rounded-2xl mt-4">
<div class="bg-card rounded-2xl p-4 border border-border shadow-sm"> <div class="bg-card rounded-2xl p-4 border border-border shadow-sm">
<div class="text-[10px] font-black text-muted-foreground uppercase tracking-widest mb-1">Current Total</div> <div class="text-[10px] font-black text-muted-foreground uppercase tracking-widest mb-1 whitespace-nowrap">Current Total</div>
<div class="text-xl font-black text-foreground">${formatNumber(props.grandTotals.grandTotal)}</div> <div class="text-xl font-black text-foreground">${formatNumber(props.grandTotals.grandTotal)}</div>
</div> </div>
</div> </div>