diff --git a/src/shared/ui/ComparisonSlider/ComparisonSlider.svelte b/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte similarity index 52% rename from src/shared/ui/ComparisonSlider/ComparisonSlider.svelte rename to src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte index 1692630..e259a97 100644 --- a/src/shared/ui/ComparisonSlider/ComparisonSlider.svelte +++ b/src/widgets/ComparisonSlider/ui/ComparisonSlider/ComparisonSlider.svelte @@ -10,35 +10,64 @@ - Performance optimized using offscreen canvas for measurements and transform-based animations. --> +import type { TypographyControl } from '$shared/lib'; +import { Input } from '$shared/shadcn/ui/input'; +import { cn } from '$shared/shadcn/utils/shadcn-utils'; +import { ComboControlV2 } from '$shared/ui'; +import AArrowUP from '@lucide/svelte/icons/a-arrow-up'; +import { Spring } from 'svelte/motion'; +import { slide } from 'svelte/transition'; + +interface Props { + wrapper?: HTMLDivElement | null; + sliderPos: number; + isDragging: boolean; + text: string; + containerWidth: number; + weightControl: TypographyControl; + sizeControl: TypographyControl; + heightControl: TypographyControl; +} + +let { + sliderPos, + isDragging, + wrapper = $bindable(null), + text = $bindable(), + containerWidth = 0, + weightControl, + sizeControl, + heightControl, +}: Props = $props(); + +let panelWidth = $state(0); +const margin = 24; +let side = $state<'left' | 'right'>('left'); + +// Unified active state for the entire wrapper +let isActive = $state(false); + +function handleWrapperClick() { + if (!isDragging) { + isActive = true; + } +} + +function handleClickOutside(e: MouseEvent) { + if (wrapper && !wrapper.contains(e.target as Node)) { + isActive = false; + } +} + +function handleInputFocus() { + isActive = true; +} + +function handleKeyDown(e: KeyboardEvent) { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleWrapperClick(); + } +} + +// Movement Logic +$effect(() => { + if (containerWidth === 0 || panelWidth === 0) return; + const sliderX = (sliderPos / 100) * containerWidth; + const buffer = 40; + const leftTrigger = margin + panelWidth + buffer; + const rightTrigger = containerWidth - (margin + panelWidth + buffer); + + if (side === 'left' && sliderX < leftTrigger) { + side = 'right'; + } else if (side === 'right' && sliderX > rightTrigger) { + side = 'left'; + } +}); + +// The "Dodge" +const xSpring = new Spring(0, { + stiffness: 0.14, // Lower is slower + damping: 0.5, // Settle +}); + +// The "Focus" +const ySpring = new Spring(0, { + stiffness: 0.32, + damping: 0.65, +}); + +// The "Rise" +const scaleSpring = new Spring(1, { + stiffness: 0.32, + damping: 0.65, +}); + +// The "Lean" +const rotateSpring = new Spring(0, { + stiffness: 0.12, + damping: 0.55, +}); + +$effect(() => { + const targetX = side === 'right' ? containerWidth - panelWidth - margin * 2 : 0; + if (containerWidth > 0 && panelWidth > 0 && !isActive) { + // On side change set the position and the rotation + xSpring.target = targetX; + rotateSpring.target = side === 'right' ? 3.5 : -3.5; + + setTimeout(() => { + rotateSpring.target = 0; + }, 600); + } +}); + +// Elevation and scale on focus and mouse over +$effect(() => { + if (isActive && !isDragging) { + // Lift up + ySpring.target = 8; + // Slightly bigger + scaleSpring.target = 1.1; + + rotateSpring.target = side === 'right' ? -1.1 : 1.1; + + setTimeout(() => { + rotateSpring.target = 0; + scaleSpring.target = 1.05; + }, 300); + } else { + ySpring.target = 0; + scaleSpring.target = 1; + rotateSpring.target = 0; + } +}); + +$effect(() => { + if (isDragging) { + isActive = false; + } +}); + +// Click outside handler +$effect(() => { + if (typeof window === 'undefined') return; + + document.addEventListener('click', handleClickOutside); + return () => document.removeEventListener('click', handleClickOutside); +}); + + +
+
+ +
+ +
+
+ +
+ + {#if isActive} +
+ + + +
+ {/if} +
+
+ + diff --git a/src/shared/ui/ComparisonSlider/components/Labels.svelte b/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/Labels.svelte similarity index 100% rename from src/shared/ui/ComparisonSlider/components/Labels.svelte rename to src/widgets/ComparisonSlider/ui/ComparisonSlider/components/Labels.svelte diff --git a/src/shared/ui/ComparisonSlider/components/SliderLine.svelte b/src/widgets/ComparisonSlider/ui/ComparisonSlider/components/SliderLine.svelte similarity index 100% rename from src/shared/ui/ComparisonSlider/components/SliderLine.svelte rename to src/widgets/ComparisonSlider/ui/ComparisonSlider/components/SliderLine.svelte diff --git a/src/widgets/ComparisonSlider/ui/index.ts b/src/widgets/ComparisonSlider/ui/index.ts new file mode 100644 index 0000000..ccad21a --- /dev/null +++ b/src/widgets/ComparisonSlider/ui/index.ts @@ -0,0 +1,3 @@ +import ComparisonSlider from './ComparisonSlider/ComparisonSlider.svelte'; + +export { ComparisonSlider };