Skip to content

Commit 3cbd731

Browse files
committed
feat: migrate to svelte 5
1 parent 437fc73 commit 3cbd731

File tree

5 files changed

+93
-54
lines changed

5 files changed

+93
-54
lines changed

bun.lockb

607 Bytes
Binary file not shown.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "svelte-knobs",
33
"description": "Svelte component library for building customizable knob controls.",
4-
"version": "0.1.1",
4+
"version": "0.2.0",
55
"repository": {
66
"url": "https://github.com/eye-wave/svelte-knobs"
77
},
@@ -41,8 +41,8 @@
4141
"devDependencies": {
4242
"@sveltejs/adapter-static": "^3.0.5",
4343
"@sveltejs/kit": "^2.7.2",
44-
"@sveltejs/package": "^2.3.5",
45-
"@sveltejs/vite-plugin-svelte": "^3.1.2",
44+
"@sveltejs/package": "^2.3.6",
45+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
4646
"@types/eslint": "^9.6.1",
4747
"eslint": "^9.13.0",
4848
"eslint-config-prettier": "^9.1.0",
@@ -52,11 +52,11 @@
5252
"prettier-plugin-svelte": "^3.2.7",
5353
"publint": "^0.2.11",
5454
"shiki": "^1.22.0",
55-
"svelte": "^4.2.19",
55+
"svelte": "^5.1.0",
5656
"svelte-check": "^4.0.5",
5757
"typescript": "^5.6.3",
5858
"typescript-eslint": "^8.11.0",
59-
"vite": "^5.4.9"
59+
"vite": "^5.4.10"
6060
},
6161
"svelte": "./dist/index.js",
6262
"types": "./dist/index.d.ts",

src/lib/Knob.svelte

Lines changed: 67 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,63 @@
11
<script lang="ts">
2+
import { normalize, format, unnormalizeToString, unnormalizeToNumber } from './params.js';
23
import { spring } from 'svelte/motion';
3-
import { createEventDispatcher } from 'svelte';
44
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 = [];
265
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+
}
3126
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);
3358
let startY: number;
3459
let startValue: number;
3560
36-
const dispatch = createEventDispatcher();
37-
3861
// This is needed in case some snap value is very close to the min or max range
3962
// preventing the user from selecting that value
4063
function completeFixedSnapValues(snapValues: number[]) {
@@ -50,14 +73,20 @@
5073
return clone;
5174
}
5275
53-
$: fixedSnapValues = completeFixedSnapValues(snapValues);
76+
const fixedSnapValues = $derived(completeFixedSnapValues(snapValues));
5477
5578
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5679
const rotationDegrees = spring(normalize(value as any, param as any) * 270 - 135, { stiffness });
5780
5881
// 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+
});
6190
6291
function handleMouseDown(event: MouseEvent) {
6392
isDragging = true;
@@ -83,7 +112,7 @@
83112
84113
if (value !== newValue) {
85114
value = newValue;
86-
dispatch('change', { value });
115+
onChange?.(value);
87116
}
88117
89118
return;
@@ -109,7 +138,7 @@
109138
110139
if (value !== newValue) {
111140
value = newValue;
112-
dispatch('change', { value });
141+
onChange?.(value);
113142
}
114143
}
115144
@@ -151,16 +180,13 @@
151180
152181
return paths.join(' ');
153182
}
154-
155-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
156-
$: formatted = isDragging ? format(value as any, param as any, decimalDigits) : '';
157183
</script>
158184

159-
<svelte:window on:mousemove={handleMouseMove} on:mouseup={handleMouseUp} />
185+
<svelte:window onmousemove={handleMouseMove} onmouseup={handleMouseUp} />
160186

161-
<div class="container" style={$$props.style}>
187+
<div class="container" {style}>
162188
<svg
163-
class={$$props.class}
189+
class={className}
164190
role="slider"
165191
tabindex="0"
166192
aria-valuenow={normalizedValue}
@@ -170,7 +196,7 @@
170196
stroke-linecap="round"
171197
stroke-linejoin="round"
172198
stroke-width={lineWidth}
173-
on:mousedown={handleMouseDown}
199+
onmousedown={handleMouseDown}
174200
>
175201
<circle cx={center} cy={center} r={circleRadius} fill={bgColor}></circle>
176202
{#if snapValues.length > 0 || param.type === 'enum-param'}

src/routes/+page.svelte

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
<div class="example">
4646
<h2>Logarithmic</h2>
4747
<Knob param={freqParam} bind:value={freqValue} label="Frequency" unit="hz" />
48-
<Knob param={gainParam} bind:value={gainValue} label="Gain" unit="dB" />
48+
<Knob param={gainParam} bind:value={gainValue} label="Gain" unit="dB" decimalDigits={1} />
4949
<Knob param={qParam} bind:value={qValue} label="Q" unit="dB" decimalDigits={2} />
5050

5151
<p>A knob with logarithmic scaling (default base is 10).</p>
@@ -122,16 +122,25 @@
122122
<div class="example">
123123
<h2>Colors</h2>
124124

125-
<Knob param={basicParam} value={24} label="Svelte theme" arcColor="#ff3e00" />
125+
<Knob param={basicParam} value={24} label="Svelte theme" colors={{ arc: '#ff3e00' }} />
126126
<Knob
127127
param={basicParam}
128128
value={48}
129129
label="Light theme"
130-
arcColor="#4292d3"
131-
bgColor="#eef"
130+
colors={{
131+
arc: '#4292d3',
132+
bg: '#eef'
133+
}}
132134
style="color:#000"
133135
/>
134-
<Knob param={basicParam} value={64} label="Disabled color" disabledColor="#aaa" disabled />
136+
<Knob
137+
onChange={console.log}
138+
param={basicParam}
139+
value={64}
140+
label="Disabled color"
141+
colors={{ disabled: '#ccc' }}
142+
disabled
143+
/>
135144

136145
<p>
137146
Of course, <code>{'<Knob />'}</code> colors can be customized to look however you want.

src/routes/CopyPaste.svelte

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
<script lang="ts">
2+
import type { Snippet } from 'svelte';
3+
24
let ref: HTMLPreElement;
3-
let copied = false;
5+
let copied = $state(false);
46
let timeoutId = -1;
57
8+
let { children }: { children: Snippet } = $props();
9+
610
function copyToClipboard() {
711
const code = ref.textContent?.trim();
812
if (code === undefined) return;
@@ -18,7 +22,7 @@
1822
</script>
1923

2024
<div class="code-container">
21-
<button on:click={copyToClipboard} class="copy-button" aria-label="Copy code to clipboard">
25+
<button onclick={copyToClipboard} class="copy-button" aria-label="Copy code to clipboard">
2226
{#if copied}
2327
<svg
2428
xmlns="http://www.w3.org/2000/svg"
@@ -50,7 +54,7 @@
5054
{/if}
5155
</button>
5256
<pre bind:this={ref}>
53-
<slot />
57+
{@render children?.()}
5458
</pre>
5559
</div>
5660

0 commit comments

Comments
 (0)