Files
frontend-svelte/src/shared/ui/SidebarContainer/SidebarContainer.svelte
T
Ilia Mashkov 5b7ec03973 refactor: sweep call sites onto design-system utilities + bug fixes
Replace inline class clusters with the design-system utilities and
tokens established in the prior two commits. No behavior changes
intended beyond two real bug fixes.

Bug fixes:
- SampleList.svelte: 'border-border-subtle bg-background-40' was a
  silent no-op (both classes mis-spelled). Now 'border-subtle
  bg-background/40' applies as intended.
- FontList.svelte: 'h-[44px]' → 'h-11' (44px = 2.75rem = spacing-11,
  no need for arbitrary value).

Sweeps:
- TypographyMenu: popover + floating bar now use surface-popover /
  surface-floating + shadow-popover.
- FontList + FilterGroup: tertiary list buttons use the new
  Button layout="block-list-row" variant; skeleton fills use
  the skeleton-fill utility.
- Footer / BreadcrumbHeader: surface-floating absorbs the
  bg-surface/blur/border cluster. Footer bumped to z-20 with a
  comment explaining the stacking against SidebarContainer (z-40/50).
- FontSampler: surface-card + hover shadow-stamp-card token.
- SliderArea: surface-canvas, flex-center, shadow-floating-panel
  tokens (light + dark variants).
- Sidebar / Header / ButtonGroup / Layout / SidebarContainer:
  bg-surface dark:bg-dark-bg → surface-canvas (8 sites);
  SidebarContainer mobile panel uses shadow-overlay.
- Loader / Thumb: flex items-center justify-center → flex-center;
  Thumb durations → duration-fast.
- ComboControl: trigger uses surface-card-elevated when open,
  popover uses surface-card-elevated, label cluster → text-label-mono,
  flex-center for the trigger interior.
- Slider: shadow-sm → shadow-rest, duration-150 → duration-fast.
- text-secondary → text-subtle across Input, Slider, ComboControl
  (matches the rename in the styles commit).
- Link: reverted earlier surface-floating attempt — Link's original
  bg-surface/80 backdrop-blur pattern was thinner than surface-floating
  (no border, smaller blur), and the Footer was overlaying its own
  border-subtle on top, fighting the utility. Kept the original style.
2026-05-25 10:20:40 +03:00

101 lines
2.9 KiB
Svelte

<!--
Component: SidebarContainer
Wraps <Sidebar> and handles show/hide behaviour for both breakpoints.
-->
<script lang="ts">
import type { ResponsiveManager } from '$shared/lib';
import { cn } from '$shared/lib';
import type { Snippet } from 'svelte';
import { getContext } from 'svelte';
import { cubicOut } from 'svelte/easing';
import {
fade,
fly,
} from 'svelte/transition';
interface Props {
/**
* Sidebar open state
*/
isOpen: boolean;
/**
* Sidebar snippet
*/
sidebar?: Snippet<[{ onClose: () => void }]>;
/**
* CSS classes
*/
class?: string;
}
let {
isOpen = $bindable(false),
sidebar,
class: className,
}: Props = $props();
const responsive = getContext<ResponsiveManager>('responsive');
function close() {
isOpen = false;
}
</script>
{#if responsive.isMobile}
<!--
── MOBILE: fixed overlay ─────────────────────────────────────────────
Only rendered when open. Both backdrop and panel use Svelte transitions
so they animate in and out independently.
-->
{#if isOpen}
<!-- Backdrop -->
<div
class="fixed inset-0 bg-black/40 backdrop-blur-sm z-40"
transition:fade={{ duration: 200 }}
onclick={close}
aria-hidden="true"
>
</div>
<!-- Panel -->
<div
class="fixed left-0 top-0 bottom-0 w-80 z-50 shadow-overlay"
in:fly={{ x: -320, duration: 300, easing: cubicOut }}
out:fly={{ x: -320, duration: 250, easing: cubicOut }}
>
{#if sidebar}
{@render sidebar({ onClose: close })}
{/if}
</div>
{/if}
{:else}
<!--
── DESKTOP: collapsible column ───────────────────────────────────────
Always in the DOM — width transitions between 320px and 0.
overflow-hidden clips the w-80 inner div during the collapse.
transition-[width] is on the outer shell.
duration-300 + ease-out approximates the spring(300, 30) feel.
The inner div stays w-80 so Sidebar layout never reflows mid-animation.
-->
<div
class={cn(
'shrink-0 z-30 h-full relative',
'overflow-hidden',
'will-change-[width]',
'border-r border-subtle',
'surface-canvas',
isOpen ? 'w-80 opacity-100' : 'w-0 opacity-0',
'transition-[width,opacity] duration-slow ease-out',
className,
)}
>
<!-- Fixed-width inner so content never reflows during width animation -->
<div class="w-80 h-full">
{#if sidebar}
{@render sidebar({ onClose: close })}
{/if}
</div>
</div>
{/if}