Skip to content

Commit 08f1c7c

Browse files
committed
feat: 0.3.0
- added draggable prop to knob - refactored knob - refactored core - updated docs
1 parent 5c1f3cf commit 08f1c7c

16 files changed

+82
-82
lines changed

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,29 @@ npm install svelte-knobs
2525

2626
#### Basic Knob
2727

28-
```typescript
29-
import { createFloatParam, createRange } from 'svelte-knobs';
28+
```svelte
29+
<script lang="ts">
30+
import { createFloatParam, Knob } from '$lib';
3031
31-
const basicParam = createFloatParam(createRange('lin', 0, 100));
32-
let basicValue = 0;
33-
```
32+
const basicParam = createFloatParam('lin', 0, 100);
33+
let basicValue = 0;
34+
</script>
3435
35-
```svelte
3636
<Knob param={basicParam} bind:value={basicValue} label="Volume" unit="%" />
3737
```
3838

3939
A basic knob control with linear scaling.
4040

4141
#### Logarithmic Knob
4242

43-
```typescript
44-
import { createFloatParam, createRange } from 'svelte-knobs';
43+
```svelte
44+
<script lang="ts">
45+
import { createFloatParam } from 'svelte-knobs';
4546
46-
const logParam = createFloatParam(createRange('log', 20, 20_000));
47-
let logValue = 20;
48-
```
47+
const logParam = createFloatParam('log', 20, 20_000);
48+
let logValue = 20;
49+
</script>
4950
50-
```svelte
5151
<Knob param={logParam} bind:value={logValue} label="Frequency" unit="Hz" />
5252
```
5353

@@ -69,7 +69,7 @@ Set specific snap points and adjust snapping sensitivity using `snapThreshold`.
6969
```typescript
7070
import { createFloatParam, createRange } from 'svelte-knobs';
7171

72-
const snapParam = createFloatParam(createRange('lin', 0, 2000));
72+
const snapParam = createFloatParam('lin', 0, 2000);
7373
const snapPoints = [0, 500, 1000, 1500, 2000];
7474

7575
let snapValue = 0;

bun.lockb

5.81 KB
Binary file not shown.

package.json

Lines changed: 6 additions & 6 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.2.2",
4+
"version": "0.3.0",
55
"repository": {
66
"url": "https://github.com/eye-wave/svelte-knobs"
77
},
@@ -40,24 +40,24 @@
4040
},
4141
"devDependencies": {
4242
"@sveltejs/adapter-static": "^3.0.6",
43-
"@sveltejs/kit": "^2.7.3",
43+
"@sveltejs/kit": "^2.7.4",
4444
"@sveltejs/package": "^2.3.7",
4545
"@sveltejs/vite-plugin-svelte": "^4.0.0",
4646
"@types/eslint": "^9.6.1",
47-
"eslint": "^9.13.0",
47+
"eslint": "^9.14.0",
4848
"eslint-config-prettier": "^9.1.0",
4949
"eslint-plugin-svelte": "^2.46.0",
5050
"globals": "^15.11.0",
5151
"lint-staged": "^15.2.10",
5252
"prettier": "^3.3.3",
5353
"prettier-plugin-svelte": "^3.2.7",
5454
"publint": "^0.2.12",
55-
"shiki": "^1.22.1",
55+
"shiki": "^1.22.2",
5656
"simple-git-hooks": "^2.11.1",
57-
"svelte": "^5.1.3",
57+
"svelte": "^5.1.9",
5858
"svelte-check": "^4.0.5",
5959
"typescript": "^5.6.3",
60-
"typescript-eslint": "^8.11.0",
60+
"typescript-eslint": "^8.12.2",
6161
"vite": "^5.4.10"
6262
},
6363
"svelte": "./dist/index.js",

src-build/print-component.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const printComponent = () => {
3939

4040
return `{@html \`${html}\`}`;
4141
} catch (err) {
42-
console.error(err)
42+
console.error(err);
4343
return match;
4444
}
4545
});

src/lib/Knob.svelte

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { normalize, format, unnormalizeToString, unnormalizeToNumber } from './params.js';
33
import { spring } from 'svelte/motion';
44
import type { EnumParam, FloatParam } from './params.js';
5+
import { clamp } from './helpers/clamp.js';
56
67
interface Props {
78
style?: string;
@@ -18,6 +19,7 @@
1819
snapValues?: Array<number>;
1920
snapThreshold?: number;
2021
disabled?: boolean;
22+
draggable?: boolean;
2123
colors?: {
2224
arc?: string;
2325
bg?: string;
@@ -40,6 +42,7 @@
4042
snapValues = [],
4143
snapThreshold = 0.1,
4244
disabled = false,
45+
draggable = true,
4346
colors = {}
4447
}: Props = $props();
4548
@@ -76,32 +79,41 @@
7679
}
7780
7881
const fixedSnapValues = $derived(completeFixedSnapValues(snapValues));
82+
const rotationDegrees = spring(normalize(value, param) * 270 - 135, { stiffness });
83+
const normalizedValue = $derived(normalize(value, param));
7984
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) : '');
8286
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;
8591
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;
8893
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+
}
9298
93-
function handleMouseDown(event: MouseEvent) {
99+
function handleMouseDown({ clientY }: MouseEvent) {
100+
if (!draggable) return;
94101
isDragging = true;
95-
startY = event.clientY;
102+
startY = clientY;
96103
startValue = normalizedValue;
104+
105+
return true;
97106
}
98107
99-
function handleMouseMove(event: MouseEvent) {
108+
function handleMouseMove({ clientY }: MouseEvent) {
109+
if (!draggable) return;
100110
if (disabled) return;
101111
if (!isDragging) return;
102-
const deltaY = startY - event.clientY;
112+
const deltaY = startY - clientY;
103113
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;
105117
}
106118
107119
function handleMouseUp() {
@@ -115,38 +127,20 @@
115127
(param as EnumParam<string[]>).variants?.[0];
116128
if (val === undefined) return;
117129
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));
120131
}
121132
133+
const handleTouchStart = toMobile(handleMouseDown);
134+
const handleTouchMove = toMobile(handleMouseMove);
135+
122136
$effect(() => {
137+
rotationDegrees.set(normalizedValue * 270 - 135);
138+
123139
// this was easier in svelte 4 :/
124140
window.addEventListener('touchmove', handleTouchMove, { passive: false });
125141
return () => window.removeEventListener('touchmove', handleTouchMove);
126142
});
127143
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-
150144
function setValue(newNormalizedValue: number) {
151145
if (param.type === 'enum-param') {
152146
const newValue = unnormalizeToString(newNormalizedValue, param);
@@ -210,8 +204,7 @@
210204
211205
const values = param.type === 'enum-param' ? param.variants : snapValues;
212206
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);
215208
const angle = normalizedSnapValue * 270 - 135;
216209
const [x1, y1] = polarToCartesian(center, center, arcRadius, angle);
217210
const [x2, y2] = polarToCartesian(center, center, size * 0.46, angle);

src/lib/helpers/clamp.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function clamp(value: number, min: number, max: number) {
2+
return Math.max(min, Math.min(max, value));
3+
}

src/lib/params.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ export type Param = EnumParam<readonly string[]> | FloatParam;
1313

1414
export function normalize<V extends readonly string[]>(value: string, param: EnumParam<V>): number;
1515
export function normalize(value: number, param: FloatParam): number;
16-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
17-
export function normalize(value: any, param: any): number {
16+
export function normalize(value: string | number, param: Param): number;
17+
export function normalize(value: string | number, param: Param): number {
1818
switch (param.type) {
1919
case 'enum-param': {
2020
if (typeof value !== 'string')
@@ -27,6 +27,7 @@ export function normalize(value: any, param: any): number {
2727
return FLOAT.normalize(value, param);
2828
}
2929
default:
30+
//@ts-expect-error just in case
3031
throw new TypeError(`Unsupported param type: ${param.type}`);
3132
}
3233
}
@@ -36,6 +37,7 @@ export function unnormalizeToNumber<V extends readonly string[]>(
3637
param: EnumParam<V>
3738
): number;
3839
export function unnormalizeToNumber(value: number, param: FloatParam): number;
40+
export function unnormalizeToNumber(value: number, param: Param): number;
3941
export function unnormalizeToNumber(value: number, param: Param): number {
4042
switch (param.type) {
4143
case 'enum-param':
@@ -53,7 +55,8 @@ export function unnormalizeToString<V extends readonly string[]>(
5355
param: EnumParam<V>
5456
): V[number];
5557
export function unnormalizeToString(value: number, param: FloatParam): string;
56-
export function unnormalizeToString(value: number, param: Param): unknown {
58+
export function unnormalizeToString(value: number, param: Param): string;
59+
export function unnormalizeToString(value: number, param: Param): string {
5760
switch (param.type) {
5861
case 'enum-param':
5962
return ENUM.unnormalizeToString(value, param);
@@ -70,6 +73,7 @@ export function format<V extends readonly string[]>(
7073
param: EnumParam<V>
7174
): V[number];
7275
export function format(value: number, param: FloatParam, precision?: number): number;
76+
export function format(value: unknown, param: Param, precision?: number): unknown;
7377
export function format(value: unknown, param: Param, precision?: number): unknown {
7478
switch (param.type) {
7579
case 'enum-param':

src/lib/params/float-param.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import { type Range } from './range.js';
1+
import { createRange, type Range } from './range.js';
22

33
export type FloatParam = {
44
type: 'float-param';
55
range: Range;
66
};
77

8-
export function createFloatParam(range: Range): FloatParam {
8+
export function createFloatParam(...args: Parameters<typeof createRange>): FloatParam {
99
return {
1010
type: 'float-param',
11-
range
11+
range: createRange(...args)
1212
};
1313
}
1414
export function normalize(value: number, param: FloatParam): number {

src/lib/params/range.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type RangeType = Range['type'];
1111

1212
export function createRange(type: 'lin', min: number, max: number): LinearRange;
1313
export function createRange(type: 'log', min: number, max: number, base?: number): LogRange;
14+
export function createRange(type: RangeType, min: number, max: number, a?: number): Range;
1415
export function createRange(type: RangeType, min: number, max: number, a?: number): Range {
1516
switch (type) {
1617
case 'lin':

src/routes/CopyPaste.svelte

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,11 @@
6262
:global(pre.shiki) {
6363
box-sizing: border-box;
6464
border-radius: 8px;
65-
overflow: scroll;
65+
overflow-x: scroll;
6666
padding: 1rem;
6767
}
6868
6969
:global(.shiki > code) {
70-
text-wrap: wrap;
7170
tab-size: 2;
7271
}
7372

0 commit comments

Comments
 (0)