feature/ux-improvements #26

Merged
ilia merged 73 commits from feature/ux-improvements into main 2026-02-18 14:43:05 +00:00
Showing only changes of commit 5d2c05e192 - Show all commits

View File

@@ -0,0 +1,83 @@
<!--
Component: PerspectivePlan
Wrapper that applies perspective transformations based on back/front state.
Style computation moved from manager to component for simpler architecture.
-->
<script lang="ts">
import type { PerspectiveManager } from '$shared/lib';
import { cn } from '$shared/shadcn/utils/shadcn-utils';
import { type Snippet } from 'svelte';
interface Props {
/**
* Perspective manager
*/
manager: PerspectiveManager;
/**
* Additional classes
*/
class?: string;
/**
* Children
*/
children: Snippet<[{ className?: string }]>;
/**
* Constrain plan to a horizontal region
* 'left' | 'right' | 'full' (default)
*/
region?: 'left' | 'right' | 'full';
/**
* Width percentage when using left/right region (default 50)
*/
regionWidth?: number;
}
let { manager, children, class: className = '', region = 'full', regionWidth = 50 }: Props = $props();
const config = $derived(manager.getConfig());
// Computed style based on spring position (0 = front, 1 = back)
const style = $derived.by(() => {
const distance = manager.spring.current;
const baseX = config.horizontalOffset ?? 0;
// Back state: blurred, scaled down, pushed back
// Front state: fully visible, in focus
const scale = 1 - distance * (config.scaleStep ?? 0.5);
const blur = distance * (config.blurStep ?? 4);
const opacity = Math.max(0, 1 - distance * (config.opacityStep ?? 0.5));
const zIndex = 10;
const pointerEvents = distance < 0.4 ? 'auto' : ('none' as const);
return {
transform: `translate3d(${baseX}px, 0px, ${-distance * (config.depthStep ?? 100)}px) scale(${scale})`,
filter: `blur(${blur}px)`,
opacity,
pointerEvents,
zIndex,
};
});
// Calculate horizontal constraints based on region
const regionStyleStr = $derived(() => {
if (region === 'full') return '';
const side = region === 'left' ? 'left' : 'right';
return `position: absolute; ${side}: 0; width: ${regionWidth}%; top: 0; bottom: 0;`;
});
// Visibility: front = visible, back = hidden
const isVisible = $derived(manager.isFront);
</script>
<div
class={cn('will-change-transform', className)}
style:transform-style="preserve-3d"
style:transform={style?.transform}
style:filter={style?.filter}
style:opacity={style?.opacity}
style:pointer-events={style?.pointerEvents}
style:z-index={style?.zIndex}
style:custom={regionStyleStr()}
>
{@render children({ className: isVisible ? 'visible' : 'hidden' })}
</div>