|
1 | 1 | <script lang="ts">
|
| 2 | + import { normalize, format, unnormalizeToString, unnormalizeToNumber } from './params.js'; |
2 | 3 | import { spring } from 'svelte/motion';
|
3 |
| - import { createEventDispatcher } from 'svelte'; |
4 | 4 | import type { EnumParam, FloatParam } from './params.js';
|
5 |
| - import { normalize, format, unnormalizeToString, unnormalizeToNumber } from './params.js'; |
6 |
| -
|
7 |
| - const size = 80; |
8 |
| -
|
9 |
| - export let label = ''; |
10 |
| - export let unit = ''; |
11 |
| - export let param: FloatParam | EnumParam<readonly string[]>; |
12 |
| - export let value: number | string; |
13 |
| - export let stiffness = 0.5; |
14 |
| - export let decimalDigits = 0; |
15 |
| - export let snapValues: number[] = []; |
16 |
| - export let snapThreshold = 0.1; |
17 |
| - export let disabled = false; |
18 |
| -
|
19 |
| - export let arcColor = '#ae98db'; |
20 |
| - export let bgColor = '#444'; |
21 |
| - export let disabledColor = '#777'; |
22 |
| -
|
23 |
| - $: arcColor2 = disabled ? disabledColor : arcColor; |
24 |
| -
|
25 |
| - $: if (param.type === 'enum-param') snapValues = []; |
26 | 5 |
|
27 |
| - $: center = size / 2; |
28 |
| - $: arcRadius = size * 0.4; |
29 |
| - $: circleRadius = size * 0.32; |
30 |
| - $: lineWidth = size * 0.04; |
| 6 | + interface Props { |
| 7 | + style?: string; |
| 8 | + class?: string; |
| 9 | + label?: string; |
| 10 | + unit?: string; |
| 11 | + size?: number; |
| 12 | + onChange?: (value: number | string) => void; |
| 13 | + param: FloatParam | EnumParam<readonly string[]>; |
| 14 | + value: number | string; |
| 15 | + stiffness?: number; |
| 16 | + decimalDigits?: number; |
| 17 | + snapValues?: Array<number>; |
| 18 | + snapThreshold?: number; |
| 19 | + disabled?: boolean; |
| 20 | + colors?: { |
| 21 | + arc?: string; |
| 22 | + bg?: string; |
| 23 | + disabled?: string; |
| 24 | + }; |
| 25 | + } |
31 | 26 |
|
32 |
| - let isDragging = false; |
| 27 | + let { |
| 28 | + style, |
| 29 | + class: className, |
| 30 | + label = '', |
| 31 | + unit = '', |
| 32 | + size = 80, |
| 33 | + onChange, |
| 34 | + value = $bindable(), |
| 35 | + param, |
| 36 | + stiffness = 0.5, |
| 37 | + decimalDigits = 0, |
| 38 | + snapValues = [], |
| 39 | + snapThreshold = 0.1, |
| 40 | + disabled = false, |
| 41 | + colors = {} |
| 42 | + }: Props = $props(); |
| 43 | +
|
| 44 | + const { |
| 45 | + arc: arcColor = '#ae98db', |
| 46 | + bg: bgColor = '#444', |
| 47 | + disabled: disabledColor = '#777' |
| 48 | + } = colors; |
| 49 | +
|
| 50 | + const arcColor2 = $derived(disabled ? disabledColor : arcColor); |
| 51 | +
|
| 52 | + const center = $derived(size / 2); |
| 53 | + const arcRadius = $derived(size * 0.4); |
| 54 | + const circleRadius = $derived(size * 0.32); |
| 55 | + const lineWidth = $derived(size * 0.04); |
| 56 | +
|
| 57 | + let isDragging = $state(false); |
33 | 58 | let startY: number;
|
34 | 59 | let startValue: number;
|
35 | 60 |
|
36 |
| - const dispatch = createEventDispatcher(); |
37 |
| -
|
38 | 61 | // This is needed in case some snap value is very close to the min or max range
|
39 | 62 | // preventing the user from selecting that value
|
40 | 63 | function completeFixedSnapValues(snapValues: number[]) {
|
|
50 | 73 | return clone;
|
51 | 74 | }
|
52 | 75 |
|
53 |
| - $: fixedSnapValues = completeFixedSnapValues(snapValues); |
| 76 | + const fixedSnapValues = $derived(completeFixedSnapValues(snapValues)); |
54 | 77 |
|
55 | 78 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
56 | 79 | const rotationDegrees = spring(normalize(value as any, param as any) * 270 - 135, { stiffness });
|
57 | 80 |
|
58 | 81 | // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
59 |
| - $: normalizedValue = normalize(value as any, param as any); |
60 |
| - $: rotationDegrees.set(normalizedValue * 270 - 135); |
| 82 | + const normalizedValue = $derived(normalize(value as any, param as any)); |
| 83 | +
|
| 84 | + // eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 85 | + const formatted = $derived(isDragging ? format(value as any, param as any, decimalDigits) : ''); |
| 86 | +
|
| 87 | + $effect(() => { |
| 88 | + rotationDegrees.set(normalizedValue * 270 - 135); |
| 89 | + }); |
61 | 90 |
|
62 | 91 | function handleMouseDown(event: MouseEvent) {
|
63 | 92 | isDragging = true;
|
|
83 | 112 |
|
84 | 113 | if (value !== newValue) {
|
85 | 114 | value = newValue;
|
86 |
| - dispatch('change', { value }); |
| 115 | + onChange?.(value); |
87 | 116 | }
|
88 | 117 |
|
89 | 118 | return;
|
|
109 | 138 |
|
110 | 139 | if (value !== newValue) {
|
111 | 140 | value = newValue;
|
112 |
| - dispatch('change', { value }); |
| 141 | + onChange?.(value); |
113 | 142 | }
|
114 | 143 | }
|
115 | 144 |
|
|
151 | 180 |
|
152 | 181 | return paths.join(' ');
|
153 | 182 | }
|
154 |
| -
|
155 |
| - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
156 |
| - $: formatted = isDragging ? format(value as any, param as any, decimalDigits) : ''; |
157 | 183 | </script>
|
158 | 184 |
|
159 |
| -<svelte:window on:mousemove={handleMouseMove} on:mouseup={handleMouseUp} /> |
| 185 | +<svelte:window onmousemove={handleMouseMove} onmouseup={handleMouseUp} /> |
160 | 186 |
|
161 |
| -<div class="container" style={$$props.style}> |
| 187 | +<div class="container" {style}> |
162 | 188 | <svg
|
163 |
| - class={$$props.class} |
| 189 | + class={className} |
164 | 190 | role="slider"
|
165 | 191 | tabindex="0"
|
166 | 192 | aria-valuenow={normalizedValue}
|
|
170 | 196 | stroke-linecap="round"
|
171 | 197 | stroke-linejoin="round"
|
172 | 198 | stroke-width={lineWidth}
|
173 |
| - on:mousedown={handleMouseDown} |
| 199 | + onmousedown={handleMouseDown} |
174 | 200 | >
|
175 | 201 | <circle cx={center} cy={center} r={circleRadius} fill={bgColor}></circle>
|
176 | 202 | {#if snapValues.length > 0 || param.type === 'enum-param'}
|
|
0 commit comments