-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathLiveSlider.svelte
More file actions
109 lines (97 loc) · 2.79 KB
/
LiveSlider.svelte
File metadata and controls
109 lines (97 loc) · 2.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<script lang="ts">
import type { LiveShockerState } from '$lib/state/live-control-state.svelte';
interface Props {
liveState: LiveShockerState;
maxIntensity?: number;
onRelease?: () => void;
}
let { liveState, maxIntensity = 100, onRelease }: Props = $props();
let container: HTMLDivElement | undefined = $state();
let y = $state(1);
let intensity = $derived(Math.round((1 - y) * maxIntensity));
function startDrag(event: PointerEvent) {
if (!container) return;
liveState.isDragging = true;
container.setPointerCapture(event.pointerId);
updatePosition(event);
}
function onPointerMove(event: PointerEvent) {
if (!liveState.isDragging || !container) return;
updatePosition(event);
}
function stopDrag() {
liveState.isDragging = false;
y = 1;
liveState.intensity = 0;
onRelease?.();
}
function updatePosition(event: PointerEvent) {
if (!container) return;
const rect = container.getBoundingClientRect();
y = Math.min(1, Math.max(0, (event.clientY - rect.top) / rect.height));
liveState.intensity = intensity;
}
const STEP = 0.05;
function onKeydown(event: KeyboardEvent) {
let handled = true;
switch (event.key) {
case 'ArrowUp':
case 'ArrowRight':
y = Math.max(0, y - STEP);
break;
case 'ArrowDown':
case 'ArrowLeft':
y = Math.min(1, y + STEP);
break;
case 'Home':
y = 0;
break;
case 'End':
y = 1;
break;
default:
handled = false;
}
if (handled) {
event.preventDefault();
liveState.intensity = intensity;
}
}
</script>
<div class="relative h-full w-full p-4 select-none">
<div
bind:this={container}
class="border-border relative h-full w-full cursor-pointer overflow-hidden rounded-md border"
onpointerdown={startDrag}
onpointermove={onPointerMove}
onpointerup={stopDrag}
onpointercancel={stopDrag}
onkeydown={onKeydown}
role="slider"
aria-valuenow={intensity}
aria-valuemin={0}
aria-valuemax={maxIntensity}
aria-label="Live intensity"
tabindex="0"
>
<!-- Fill from bottom -->
<div
class="bg-muted pointer-events-none absolute bottom-0 left-0 w-full transition-none"
style="height: {(1 - y) * 100}%"
></div>
<!-- Handle -->
<div
class="pointer-events-none absolute -translate-x-1/2 -translate-y-1/2 rounded-full transition-none
{liveState.isDragging
? 'border-border bg-primary h-12 w-12 border'
: 'bg-primary h-10 w-10 border-2 border-transparent'}"
style="left: 50%; top: {y * 100}%"
>
<span
class="text-primary-foreground flex h-full items-center justify-center text-sm font-medium"
>
{intensity}%
</span>
</div>
</div>
</div>