|
2 | 2 | import { normalize, format, unnormalizeToString, unnormalizeToNumber } from './params.js'; |
3 | 3 | import { spring } from 'svelte/motion'; |
4 | 4 | import type { EnumParam, FloatParam } from './params.js'; |
| 5 | + import { clamp } from './helpers/clamp.js'; |
5 | 6 |
|
6 | 7 | interface Props { |
7 | 8 | style?: string; |
|
18 | 19 | snapValues?: Array<number>; |
19 | 20 | snapThreshold?: number; |
20 | 21 | disabled?: boolean; |
| 22 | + draggable?: boolean; |
21 | 23 | colors?: { |
22 | 24 | arc?: string; |
23 | 25 | bg?: string; |
|
40 | 42 | snapValues = [], |
41 | 43 | snapThreshold = 0.1, |
42 | 44 | disabled = false, |
| 45 | + draggable = true, |
43 | 46 | colors = {} |
44 | 47 | }: Props = $props(); |
45 | 48 |
|
|
76 | 79 | } |
77 | 80 |
|
78 | 81 | const fixedSnapValues = $derived(completeFixedSnapValues(snapValues)); |
| 82 | + const rotationDegrees = spring(normalize(value, param) * 270 - 135, { stiffness }); |
| 83 | + const normalizedValue = $derived(normalize(value, param)); |
79 | 84 |
|
80 | | - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
81 | | - const rotationDegrees = spring(normalize(value as any, param as any) * 270 - 135, { stiffness }); |
| 85 | + const formatted = $derived(isDragging ? format(value, param, decimalDigits) : ''); |
82 | 86 |
|
83 | | - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
84 | | - const normalizedValue = $derived(normalize(value as any, param as any)); |
| 87 | + function toMobile(handler: ({ clientY }: MouseEvent) => boolean | void) { |
| 88 | + return (event: TouchEvent) => { |
| 89 | + const touch = event.touches?.[0]; |
| 90 | + if (touch === undefined) return; |
85 | 91 |
|
86 | | - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
87 | | - const formatted = $derived(isDragging ? format(value as any, param as any, decimalDigits) : ''); |
| 92 | + const clientY = touch.clientY; |
88 | 93 |
|
89 | | - $effect(() => { |
90 | | - rotationDegrees.set(normalizedValue * 270 - 135); |
91 | | - }); |
| 94 | + // eslint-disable-next-line @typescript-eslint/no-unused-expressions |
| 95 | + handler({ clientY } as MouseEvent) && event.preventDefault(); |
| 96 | + }; |
| 97 | + } |
92 | 98 |
|
93 | | - function handleMouseDown(event: MouseEvent) { |
| 99 | + function handleMouseDown({ clientY }: MouseEvent) { |
| 100 | + if (!draggable) return; |
94 | 101 | isDragging = true; |
95 | | - startY = event.clientY; |
| 102 | + startY = clientY; |
96 | 103 | startValue = normalizedValue; |
| 104 | +
|
| 105 | + return true; |
97 | 106 | } |
98 | 107 |
|
99 | | - function handleMouseMove(event: MouseEvent) { |
| 108 | + function handleMouseMove({ clientY }: MouseEvent) { |
| 109 | + if (!draggable) return; |
100 | 110 | if (disabled) return; |
101 | 111 | if (!isDragging) return; |
102 | | - const deltaY = startY - event.clientY; |
| 112 | + const deltaY = startY - clientY; |
103 | 113 | const deltaValue = deltaY / 200; |
104 | | - setValue(Math.max(0, Math.min(1, startValue + deltaValue))); |
| 114 | + setValue(clamp(startValue + deltaValue, 0, 1)); |
| 115 | +
|
| 116 | + return true; |
105 | 117 | } |
106 | 118 |
|
107 | 119 | function handleMouseUp() { |
|
115 | 127 | (param as EnumParam<string[]>).variants?.[0]; |
116 | 128 | if (val === undefined) return; |
117 | 129 |
|
118 | | - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
119 | | - setValue(Math.max(0, Math.min(1, normalize(val as any, param as any)))); |
| 130 | + setValue(clamp(normalize(val, param), 0, 1)); |
120 | 131 | } |
121 | 132 |
|
| 133 | + const handleTouchStart = toMobile(handleMouseDown); |
| 134 | + const handleTouchMove = toMobile(handleMouseMove); |
| 135 | +
|
122 | 136 | $effect(() => { |
| 137 | + rotationDegrees.set(normalizedValue * 270 - 135); |
| 138 | +
|
123 | 139 | // this was easier in svelte 4 :/ |
124 | 140 | window.addEventListener('touchmove', handleTouchMove, { passive: false }); |
125 | 141 | return () => window.removeEventListener('touchmove', handleTouchMove); |
126 | 142 | }); |
127 | 143 |
|
128 | | - function handleTouchStart(event: TouchEvent) { |
129 | | - isDragging = true; |
130 | | - const touch = event.touches?.[0]; |
131 | | - if (touch === undefined) return; |
132 | | - startY = touch.clientY; |
133 | | - startValue = normalizedValue; |
134 | | - } |
135 | | -
|
136 | | - function handleTouchMove(event: TouchEvent) { |
137 | | - if (!isDragging) return; |
138 | | -
|
139 | | - event.preventDefault(); |
140 | | - if (disabled) return; |
141 | | -
|
142 | | - const touch = event.touches?.[0]; |
143 | | - if (touch === undefined) return; |
144 | | -
|
145 | | - const deltaY = startY - touch.clientY; |
146 | | - const deltaValue = deltaY / 200; |
147 | | - setValue(Math.max(0, Math.min(1, startValue + deltaValue))); |
148 | | - } |
149 | | -
|
150 | 144 | function setValue(newNormalizedValue: number) { |
151 | 145 | if (param.type === 'enum-param') { |
152 | 146 | const newValue = unnormalizeToString(newNormalizedValue, param); |
|
210 | 204 |
|
211 | 205 | const values = param.type === 'enum-param' ? param.variants : snapValues; |
212 | 206 | for (let snapValue of values) { |
213 | | - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
214 | | - const normalizedSnapValue = normalize(snapValue as any, param as any); |
| 207 | + const normalizedSnapValue = normalize(snapValue, param); |
215 | 208 | const angle = normalizedSnapValue * 270 - 135; |
216 | 209 | const [x1, y1] = polarToCartesian(center, center, arcRadius, angle); |
217 | 210 | const [x2, y2] = polarToCartesian(center, center, size * 0.46, angle); |
|
0 commit comments