Skip to content

Commit daeda2d

Browse files
committed
fix reactivity bug
1 parent d10409f commit daeda2d

File tree

8 files changed

+66
-71
lines changed

8 files changed

+66
-71
lines changed

docs/src/examples/ImageKnob.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
let value = $state(0.0);
55
</script>
66

7-
<ImageKnob bind:value src="/PurpleKnob2.webp" width={90} height={90} />
7+
<ImageKnob bind:value src="/web-knobs/PurpleKnob2.webp" width={90} height={90} />

docs/src/pages/index.astro

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ const logos = [
112112
const [arcFull, arc, logo] = el.querySelectorAll('svg path')!;
113113
const val = Math.random();
114114

115-
arcFull.setAttribute('d', describeArc(12, 12, 10, 1, -135, 135));
115+
arcFull?.setAttribute('d', describeArc(12, 12, 10, 1, -135, 135));
116116

117117
draw(val);
118118
function draw(v: number) {
119119
const angle = valueToAngle(v, 0, 180);
120120

121-
arc.setAttribute('d', describeArc(12, 12, 10, v, -135, 135));
122-
logo.setAttribute(
121+
arc?.setAttribute('d', describeArc(12, 12, 10, v, -135, 135));
122+
logo?.setAttribute(
123123
'transform',
124124
`translate(12 12) scale(0.5) translate(-12 -12) rotate(${angle} 12 12)`
125125
);

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "@web-knobs/core",
4-
"version": "0.0.1",
4+
"version": "0.0.2",
55
"type": "module",
66
"exports": {
77
".": {

packages/core/src/components/image-knob.ts

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
type DraggableApi,
55
type DraggableOptions
66
} from '../draggable';
7-
import { addReactive, type WithSilent } from '../helpers';
7+
import { addReactive } from '../helpers';
88

99
export type ImageKnobReactive = {
1010
/**
@@ -46,8 +46,7 @@ export type ImageKnobOptions = DraggableOptions & {
4646
onHeightChange?: (v: number) => void;
4747
} & ImageKnobReactive;
4848

49-
type ImageStateRaw = Required<ImageKnobReactive>;
50-
type ImageState = WithSilent<ImageStateRaw>;
49+
type ImageState = Required<ImageKnobReactive>;
5150

5251
export function createImageKnob<E extends HTMLElement>(
5352
container: E,
@@ -67,36 +66,48 @@ export function createImageKnob<E extends HTMLElement>(
6766
}
6867
};
6968

69+
const engine = createDraggable(container, {
70+
...options,
71+
onValueChange(v) {
72+
options.onValueChange?.(v);
73+
draw(v);
74+
}
75+
}) as ImageKnobApi;
76+
7077
addReactive(state, 'src', options.src, (src) => {
7178
options.onSrcChange?.(src);
7279

7380
image.src = src;
7481
container.style.backgroundImage = `url(${src})`;
7582
});
7683
addReactive(state, 'numberOfFrames', null, options.onNumberOfFramesChange);
77-
addReactive(state, 'width', options.width, (width) => {
78-
options.onWidthChange?.(width);
79-
80-
container.style.width = width + 'px';
81-
draw(engine.__state.value);
82-
});
83-
addReactive(state, 'height', options.height, (height) => {
84-
options.onHeightChange?.(height);
85-
86-
container.style.width = height + 'px';
87-
draw(engine.__state.value);
88-
});
84+
addReactive(
85+
state,
86+
'width',
87+
options.width,
88+
(width) => {
89+
options.onWidthChange?.(width);
90+
91+
container.style.width = width + 'px';
92+
draw(engine.__state.value);
93+
},
94+
false
95+
);
96+
addReactive(
97+
state,
98+
'height',
99+
options.height,
100+
(height) => {
101+
options.onHeightChange?.(height);
102+
103+
container.style.width = height + 'px';
104+
draw(engine.__state.value);
105+
},
106+
false
107+
);
89108

90109
Object.freeze(state);
91110

92-
const engine = createDraggable(container, {
93-
...options,
94-
onValueChange(v) {
95-
options.onValueChange?.(v);
96-
draw(v);
97-
}
98-
}) as ImageKnobApi;
99-
100111
container.style.width = options.width + 'px';
101112
container.style.height = options.height + 'px';
102113

@@ -111,10 +122,10 @@ export function createImageKnob<E extends HTMLElement>(
111122
}
112123
});
113124

114-
engine.setSrc = (v) => state.srcSilent(v);
115-
engine.setNumberOfFrames = (v) => state.numberOfFramesSilent(v);
116-
engine.setWidth = (v) => state.widthSilent(v);
117-
engine.setHeight = (v) => state.heightSilent(v);
125+
engine.setSrc = (v) => (state.src = v);
126+
engine.setNumberOfFrames = (v) => (state.numberOfFrames = v);
127+
engine.setWidth = (v) => (state.width = v);
128+
engine.setHeight = (v) => (state.height = v);
118129

119130
return engine;
120131
}

packages/core/src/components/svg-knob.ts

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import {
44
type DraggableApi,
55
type DraggableOptions
66
} from '../draggable';
7-
import {
8-
addReactive,
9-
describeArc,
10-
polarToCartesian,
11-
valueToAngle,
12-
type WithSilent
13-
} from '../helpers';
7+
import { addReactive, describeArc, polarToCartesian, valueToAngle } from '../helpers';
148

159
export const DEFAULT_SIZE = 80;
1610
export const DEFAULT_BG_COLOR = '#333';
@@ -85,8 +79,7 @@ export type SvgKnobOptions = DraggableOptions & {
8579
onSnapPointLengthChange?: (v: number) => void;
8680
} & SvgKnobReactive;
8781

88-
type SvgStateRaw = Required<SvgKnobReactive>;
89-
type SvgState = WithSilent<SvgStateRaw>;
82+
type SvgState = Required<SvgKnobReactive>;
9083

9184
export function createSvgKnob<E extends HTMLElement>(
9285
container: E,
@@ -327,15 +320,15 @@ export function createSvgKnob<E extends HTMLElement>(
327320
}
328321
});
329322

330-
engine.setArcRadius = (v) => state.arcRadiusSilent(v);
331-
engine.setBgColor = (v) => state.bgColorSilent(v);
332-
engine.setCircleRadius = (v) => state.circleRadiusSilent(v);
333-
engine.setDisabledColor = (v) => state.disabledColorSilent(v);
334-
engine.setMaxAngle = (v) => state.maxAngleSilent(v);
335-
engine.setMinAngle = (v) => state.minAngleSilent(v);
336-
engine.setPointerLength = (v) => state.pointerLengthSilent(v);
337-
engine.setSize = (v) => state.sizeSilent(v);
338-
engine.setSnapPointLength = (v) => state.snapPointLengthSilent(v);
323+
engine.setArcRadius = (v) => (state.arcRadius = v);
324+
engine.setBgColor = (v) => (state.bgColor = v);
325+
engine.setCircleRadius = (v) => (state.circleRadius = v);
326+
engine.setDisabledColor = (v) => (state.disabledColor = v);
327+
engine.setMaxAngle = (v) => (state.maxAngle = v);
328+
engine.setMinAngle = (v) => (state.minAngle = v);
329+
engine.setPointerLength = (v) => (state.pointerLength = v);
330+
engine.setSize = (v) => (state.size = v);
331+
engine.setSnapPointLength = (v) => (state.snapPointLength = v);
339332

340333
return engine;
341334
}

packages/core/src/draggable.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { clamp } from './helpers';
2-
import { addReactive, type WithSilent } from './helpers/reactive';
2+
import { addReactive } from './helpers/reactive';
33

44
export const DEFAULT_KNOB_VALUE = 0.5;
55
export const DEFAULT_KNOB_STEP = 0.05;
@@ -49,8 +49,7 @@ export type DraggableOptions = {
4949
onWeightChange?: (v: number) => void;
5050
} & DraggableReactive;
5151

52-
type DragStateRaw = Required<DraggableReactive>;
53-
type DragState = WithSilent<DragStateRaw>;
52+
type DragState = Required<DraggableReactive>;
5453

5554
/** Creates a Knob API inside an existing HTML element */
5655
export function createDraggable<E extends HTMLElement>(
@@ -233,14 +232,14 @@ export function createDraggable<E extends HTMLElement>(
233232
return s;
234233
},
235234
destroy,
236-
setValue: (v) => s.valueSilent(v),
237-
setDisabled: (v) => s.disabledSilent(v),
238-
setDefaultValue: (v) => s.defaultValueSilent(v),
239-
setInvertWheel: (v) => s.invertWheelSilent(v),
240-
setStep: (v) => s.stepSilent(v),
241-
setSnapPoints: (v) => s.snapPointsSilent(v),
242-
setSnapThreshold: (v) => s.snapThresholdSilent(v),
243-
setWeight: (v) => s.weightSilent(v)
235+
setValue: (v) => (s.value = v),
236+
setDisabled: (v) => (s.disabled = v),
237+
setDefaultValue: (v) => (s.defaultValue = v),
238+
setInvertWheel: (v) => (s.invertWheel = v),
239+
setStep: (v) => (s.step = v),
240+
setSnapPoints: (v) => (s.snapPoints = v),
241+
setSnapThreshold: (v) => (s.snapThreshold = v),
242+
setWeight: (v) => (s.weight = v)
244243
};
245244
}
246245

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
export type WithSilent<T> = T & {
2-
[K in keyof T as `${Extract<K, string>}Silent`]: (v: T[K]) => void;
3-
};
4-
51
export function addReactive<T>(
62
// this 'any' is safe, special types are overriding it later in the code
73
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -18,17 +14,13 @@ export function addReactive<T>(
1814
return value;
1915
},
2016
set(v: T) {
17+
if (value != v) callback?.(v);
2118
value = v;
22-
callback?.(v);
2319
},
2420
configurable: false,
2521
enumerable: false
2622
});
2723

28-
api[fieldName + 'Silent'] = (v: T) => {
29-
value = v;
30-
};
31-
3224
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
3325
instantCall && callback?.(init);
3426
}

packages/web-knobs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@eyewave/web-knobs",
3-
"version": "0.0.3",
3+
"version": "0.0.4",
44
"license": "MIT",
55
"description": " Cross-framework component library for building audio software on the web.",
66
"repository": {

0 commit comments

Comments
 (0)