refactor(helpers): modernize reactive helpers and add tests

This commit is contained in:
Ilia Mashkov
2026-03-02 22:18:59 +03:00
parent 594af924c7
commit ac73fd5044
12 changed files with 1117 additions and 185 deletions

View File

@@ -1,61 +1,83 @@
/**
* 3D perspective animation state manager
*
* Manages smooth transitions between "front" (interactive) and "back" (background)
* visual states using Svelte springs. Used for creating depth-based UI effects
* like settings panels, modal transitions, and spatial navigation.
*
* @example
* ```svelte
* <script lang="ts">
* import { createPerspectiveManager } from '$shared/lib/helpers';
*
* const perspective = createPerspectiveManager({
* depthStep: 100,
* scaleStep: 0.5,
* blurStep: 4
* });
* </script>
*
* <div
* style="transform: scale({perspective.isBack ? 0.5 : 1});
* filter: blur({perspective.isBack ? 4 : 0}px)"
* >
* <button on:click={perspective.toggle}>Toggle View</button>
* </div>
* ```
*/
import { Spring } from 'svelte/motion';
/**
* Configuration options for perspective effects
*/
export interface PerspectiveConfig {
/**
* How many px to move back per level
*/
/** Z-axis translation per level in pixels */
depthStep?: number;
/**
* Scale reduction per level
*/
/** Scale reduction per level (0-1) */
scaleStep?: number;
/**
* Blur amount per level
*/
/** Blur amount per level in pixels */
blurStep?: number;
/**
* Opacity reduction per level
*/
/** Opacity reduction per level (0-1) */
opacityStep?: number;
/**
* Parallax intensity per level
*/
/** Parallax movement intensity per level */
parallaxIntensity?: number;
/**
* Horizontal offset for each plan (x-axis positioning)
* Positive = right, Negative = left
*/
/** Horizontal offset - positive for right, negative for left */
horizontalOffset?: number;
/**
* Layout mode: 'center' (default) or 'split' for Swiss-style side-by-side
*/
/** Layout mode: 'center' for centered, 'split' for side-by-side */
layoutMode?: 'center' | 'split';
}
/**
* Manages perspective state with a simple boolean flag.
* Manages perspective state with spring-based transitions
*
* Drastically simplified from the complex camera/index system.
* Just manages whether content is in "back" or "front" state.
* Simplified from a complex camera system to just track back/front state.
* The spring value animates between 0 (front) and 1 (back) for smooth
* visual transitions of scale, blur, opacity, and position.
*
* @example
* ```typescript
* ```ts
* const perspective = createPerspectiveManager({
* depthStep: 100,
* scaleStep: 0.5,
* blurStep: 4,
* blurStep: 4
* });
*
* // Toggle back/front
* perspective.toggle();
* // Check state (reactive)
* console.log(perspective.isBack); // false
*
* // Check state
* const isBack = perspective.isBack; // reactive boolean
* // Toggle with animation
* perspective.toggle(); // Smoothly animates to back position
*
* // Direct control
* perspective.setBack(); // Go to back
* perspective.setFront(); // Go to front
* ```
*/
export class PerspectiveManager {
/**
* Spring for smooth back/front transitions
* Spring animation state
* Animates between 0 (front) and 1 (back) with configurable physics
*/
spring = new Spring(0, {
stiffness: 0.2,
@@ -63,20 +85,30 @@ export class PerspectiveManager {
});
/**
* Reactive boolean: true when in back position (blurred, scaled down)
* Reactive state: true when in back position
*
* Content should appear blurred, scaled down, and less interactive
* when this is true. Derived from spring value > 0.5.
*/
isBack = $derived(this.spring.current > 0.5);
/**
* Reactive boolean: true when in front position (fully visible, interactive)
* Reactive state: true when in front position
*
* Content should be fully visible, sharp, and interactive
* when this is true. Derived from spring value < 0.5.
*/
isFront = $derived(this.spring.current < 0.5);
/**
* Configuration values for style computation
* Internal configuration with defaults applied
*/
private config: Required<PerspectiveConfig>;
/**
* Creates a new perspective manager
* @param config - Configuration for visual effects
*/
constructor(config: PerspectiveConfig = {}) {
this.config = {
depthStep: config.depthStep ?? 100,
@@ -90,8 +122,10 @@ export class PerspectiveManager {
}
/**
* Toggle between front (0) and back (1) positions.
* Smooth spring animation handles the transition.
* Toggle between front and back positions
*
* Uses spring animation for smooth transition. Toggles based on
* current state - if spring < 0.5 goes to 1, otherwise goes to 0.
*/
toggle = () => {
const target = this.spring.current < 0.5 ? 1 : 0;
@@ -99,31 +133,40 @@ export class PerspectiveManager {
};
/**
* Force to back position
* Force to back position (blurred, scaled down)
*/
setBack = () => {
this.spring.target = 1;
};
/**
* Force to front position
* Force to front position (fully visible, interactive)
*/
setFront = () => {
this.spring.target = 0;
};
/**
* Get configuration for style computation
* @internal
* Get current configuration
* @internal Used by components to compute styles
*/
getConfig = () => this.config;
}
/**
* Factory function to create a PerspectiveManager instance.
* Factory function to create a perspective manager
*
* @param config - Configuration options
* @param config - Configuration options for visual effects
* @returns Configured PerspectiveManager instance
*
* @example
* ```ts
* const perspective = createPerspectiveManager({
* scaleStep: 0.6,
* blurStep: 8,
* layoutMode: 'split'
* });
* ```
*/
export function createPerspectiveManager(config: PerspectiveConfig = {}) {
return new PerspectiveManager(config);