zds-pickers 4.1.8 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +12 -38
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +4429 -5149
- package/dist/index.es.js.map +1 -1
- package/dist/other/SvgText.js +82 -19
- package/dist/pickers/Knob.js +2 -2
- package/dist/pickers/KnobPicker.js +3 -2
- package/dist/pickers/knobSkin10.js +31 -3
- package/dist/rotaryKnob/arcPath.js +39 -0
- package/dist/rotaryKnob/index.js +4 -1
- package/dist/types/other/SvgText.d.ts +4 -1
- package/dist/types/pickers/Knob.d.ts +2 -0
- package/dist/types/pickers/KnobPicker.d.ts +5 -2
- package/dist/types/pickers/knobSkin10.d.ts +11 -3
- package/dist/types/rotaryKnob/arcPath.d.ts +31 -0
- package/dist/types/rotaryKnob/index.d.ts +25 -21
- package/package.json +19 -15
package/dist/other/SvgText.js
CHANGED
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
const MEASUREMENT_ELEMENT_ID = '__react_svg_text_measurement_id';
|
|
4
|
+
/**
|
|
5
|
+
* The only computed properties that affect measured text geometry. Copying
|
|
6
|
+
* the full computed style (~300 properties) onto the measurement node per
|
|
7
|
+
* instance forced a style flush that dominated initial render in hosts
|
|
8
|
+
* with many SvgText instances.
|
|
9
|
+
*/
|
|
10
|
+
const FONT_PROPERTIES = [
|
|
11
|
+
'font-family',
|
|
12
|
+
'font-feature-settings',
|
|
13
|
+
'font-kerning',
|
|
14
|
+
'font-size',
|
|
15
|
+
'font-stretch',
|
|
16
|
+
'font-style',
|
|
17
|
+
'font-variant',
|
|
18
|
+
'font-weight',
|
|
19
|
+
'letter-spacing',
|
|
20
|
+
'text-transform',
|
|
21
|
+
'word-spacing',
|
|
22
|
+
];
|
|
23
|
+
const fontSignature = (style) => FONT_PROPERTIES.map(key => style.getPropertyValue(key)).join('|');
|
|
24
|
+
/**
|
|
25
|
+
* Word widths only depend on the word and the font it renders in, so they
|
|
26
|
+
* are cached module-wide by font signature. Text that repeats across
|
|
27
|
+
* instances (labels, numbers) measures once per font, ever.
|
|
28
|
+
*/
|
|
29
|
+
const fontMetricsCache = new Map();
|
|
30
|
+
const __resetFontMetricsCacheForTests = () => {
|
|
31
|
+
fontMetricsCache.clear();
|
|
32
|
+
};
|
|
4
33
|
const calculateWordsByLines = (text, wordWidths, maxWidth, maxHeight) => {
|
|
5
34
|
const { lineHeight, spaceWidth, wordsWithComputedWidth } = wordWidths;
|
|
6
35
|
return text.split(/\s+/).reduce((result, word) => {
|
|
@@ -27,19 +56,39 @@ const calculateWordsByLines = (text, wordWidths, maxWidth, maxHeight) => {
|
|
|
27
56
|
};
|
|
28
57
|
const calculateWordWidths = (style, textNode, text) => {
|
|
29
58
|
if (style && textNode) {
|
|
30
|
-
|
|
31
|
-
|
|
59
|
+
const signature = fontSignature(style);
|
|
60
|
+
let metrics = fontMetricsCache.get(signature);
|
|
32
61
|
const wordArray = [...new Set(String(text).split(/\s+/))];
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
62
|
+
const knownWidths = metrics?.widths;
|
|
63
|
+
const missingWords = knownWidths
|
|
64
|
+
? wordArray.filter(word => !knownWidths.has(word))
|
|
65
|
+
: wordArray;
|
|
66
|
+
if (!metrics || missingWords.length) {
|
|
67
|
+
for (const key of FONT_PROPERTIES) {
|
|
68
|
+
textNode.style.setProperty(key, style.getPropertyValue(key), style.getPropertyPriority(key));
|
|
69
|
+
}
|
|
70
|
+
if (!metrics) {
|
|
71
|
+
textNode.textContent = '\u00A0';
|
|
72
|
+
const spaceWidth = textNode?.getComputedTextLength?.() || 8;
|
|
73
|
+
const lineHeight = textNode.getBBox().height;
|
|
74
|
+
metrics = { widths: new Map(), spaceWidth, lineHeight };
|
|
75
|
+
fontMetricsCache.set(signature, metrics);
|
|
76
|
+
}
|
|
77
|
+
for (const word of missingWords) {
|
|
78
|
+
textNode.textContent = word;
|
|
79
|
+
metrics.widths.set(word, textNode.getBBox().width);
|
|
80
|
+
}
|
|
81
|
+
textNode.setAttribute('style', '');
|
|
82
|
+
}
|
|
83
|
+
const wordsWithComputedWidth = {};
|
|
84
|
+
for (const word of wordArray) {
|
|
85
|
+
wordsWithComputedWidth[word] = metrics.widths.get(word) ?? 0;
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
wordsWithComputedWidth,
|
|
89
|
+
spaceWidth: metrics.spaceWidth,
|
|
90
|
+
lineHeight: metrics.lineHeight,
|
|
91
|
+
};
|
|
43
92
|
}
|
|
44
93
|
return undefined;
|
|
45
94
|
};
|
|
@@ -67,14 +116,28 @@ const SvgText = (props) => {
|
|
|
67
116
|
if (el === null) {
|
|
68
117
|
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
69
118
|
svg.setAttribute('id', MEASUREMENT_ELEMENT_ID);
|
|
119
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
120
|
+
// Rendered but invisible: getBBox returns zeros under display:none,
|
|
121
|
+
// so park it offscreen instead. Being outside any host SVG also
|
|
122
|
+
// keeps per-word getBBox from forcing layout of live content.
|
|
123
|
+
svg.style.position = 'fixed';
|
|
124
|
+
svg.style.left = '-9999px';
|
|
125
|
+
svg.style.top = '0';
|
|
126
|
+
const textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
127
|
+
svg.appendChild(textEl);
|
|
70
128
|
document.body.appendChild(svg);
|
|
71
|
-
svg
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
129
|
+
// Measure against the <text> child — pointing at the <svg> container
|
|
130
|
+
// made every measurement return the svg's own box.
|
|
131
|
+
measureRef.current = textEl;
|
|
132
|
+
// Deliberately no cleanup: the element is a shared singleton and
|
|
133
|
+
// other mounted instances may still hold it as their measure target.
|
|
134
|
+
return;
|
|
76
135
|
}
|
|
77
|
-
|
|
136
|
+
// Hosts may provide either the <text> itself or a container around one.
|
|
137
|
+
measureRef.current =
|
|
138
|
+
el.tagName.toLowerCase() === 'text'
|
|
139
|
+
? el
|
|
140
|
+
: (el.querySelector('text') ?? el);
|
|
78
141
|
}, []);
|
|
79
142
|
useEffect(() => {
|
|
80
143
|
const wordWithWidths = calculateWordWidths(style, measureRef.current, text);
|
|
@@ -105,4 +168,4 @@ const SvgText = (props) => {
|
|
|
105
168
|
// textAnchor={textAnchor}
|
|
106
169
|
transform: transform, ...rest, children: _jsxs(_Fragment, { children: [displayedLines.map((line, idx) => (_jsx("tspan", { dx: x + dx, dy: idx === 0 ? startDy + dy : wordWidths?.lineHeight || 0, x: 0, children: line.words.join(' ') }, idx))), textLines.length && displayedLines.length !== textLines.length && (_jsxs(_Fragment, { children: [_jsx("tspan", { children: "..." }), _jsx("title", { children: text })] }))] }) }));
|
|
107
170
|
};
|
|
108
|
-
export {
|
|
171
|
+
export { __resetFontMetricsCacheForTests, calculateWordsByLines, calculateWordWidths, MEASUREMENT_ELEMENT_ID, SvgText, };
|
package/dist/pickers/Knob.js
CHANGED
|
@@ -5,7 +5,7 @@ import { RotaryKnob } from '../rotaryKnob';
|
|
|
5
5
|
import { assertRange } from '../utils';
|
|
6
6
|
import { knobSkin10 } from './knobSkin10';
|
|
7
7
|
const Knob = (props) => {
|
|
8
|
-
const { disabled = false, max = 127, min = 0, onChange, value: initialValue = 0, wheelEnabled = false, wheelSensitivity = 0.1, ...rest } = props;
|
|
8
|
+
const { centered = false, disabled = false, max = 127, min = 0, onChange, value: initialValue = 0, wheelEnabled = false, wheelSensitivity = 0.1, ...rest } = props;
|
|
9
9
|
const [value, setValue] = useStateWithDynamicDefault(initialValue);
|
|
10
10
|
const handleChange = (val) => {
|
|
11
11
|
if (disabled)
|
|
@@ -36,7 +36,7 @@ const Knob = (props) => {
|
|
|
36
36
|
onChange,
|
|
37
37
|
setValue,
|
|
38
38
|
]);
|
|
39
|
-
return (_jsx(RotaryKnob, { clampMax: 320, clampMin: 40, className: "zds-pickers__knob-container", disabled: disabled, onChange: handleChange, onWheel: wheelEnabled ? handleWheel : undefined, preciseMode: false, rotateDegrees: 180,
|
|
39
|
+
return (_jsx(RotaryKnob, { centered: centered, clampMax: 320, clampMin: 40, className: "zds-pickers__knob-container", disabled: disabled, onChange: handleChange, onWheel: wheelEnabled ? handleWheel : undefined, preciseMode: false, rotateDegrees: 180,
|
|
40
40
|
/**
|
|
41
41
|
* To see all skins:
|
|
42
42
|
* http://react-rotary-knob-skins-preview.surge.sh/
|
|
@@ -4,7 +4,7 @@ import { arraySequence } from '../utils';
|
|
|
4
4
|
import { Knob } from './Knob';
|
|
5
5
|
import { Select } from './Select';
|
|
6
6
|
const KnobPicker = forwardRef((props, ref) => {
|
|
7
|
-
const { disabled, highToLow, includeLabel, includePicker, knobProps, label, max = 127, min = 0, onChange, shrinkLabel, value = 0, wheelEnabled, wheelSensitivity = 0.1, ...rest } = props;
|
|
7
|
+
const { centered, disabled, highToLow, includeLabel, includePicker, knobProps, label, max = 127, min = 0, onChange, shrinkLabel, value = 0, wheelEnabled, wheelSensitivity = 0.1, ...rest } = props;
|
|
8
8
|
const options = useMemo(() => {
|
|
9
9
|
const result = arraySequence(max - min + 1)
|
|
10
10
|
.map(i => min + i)
|
|
@@ -17,7 +17,8 @@ const KnobPicker = forwardRef((props, ref) => {
|
|
|
17
17
|
onChange,
|
|
18
18
|
options,
|
|
19
19
|
ref,
|
|
20
|
-
value })), _jsx(Knob, {
|
|
20
|
+
value })), _jsx(Knob, { centered,
|
|
21
|
+
disabled,
|
|
21
22
|
max,
|
|
22
23
|
min,
|
|
23
24
|
onChange,
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { buildValueArcPath } from '../rotaryKnob/arcPath';
|
|
2
|
+
/** Value-arc geometry in the skin's fixed group: knob center and a radius in
|
|
3
|
+
* the dark bezel ring between the face plate (r≈86) and outer edge (r≈99). */
|
|
4
|
+
const ARC_CX = 100;
|
|
5
|
+
const ARC_CY = 100;
|
|
6
|
+
const ARC_R = 93;
|
|
1
7
|
const knobSkin10 = {
|
|
2
8
|
knobX: 71.44,
|
|
3
9
|
knobY: 71.44,
|
|
@@ -60,13 +66,15 @@ const knobSkin10 = {
|
|
|
60
66
|
<path d="M72.4209282,3.63066376 L88.6786876,16.869789 C81.585496,18.1548295 76.1662428,18.7973498 72.4209282,18.7973498 C68.6756135,18.7973498 63.2563604,18.1548295 56.1631688,16.869789 L72.4209282,3.63066376 Z" id="Rectangle" fill="#E6D7D7" transform="translate(72.420928, 11.214007) scale(1, -1) translate(-72.420928, -11.214007) "/>
|
|
61
67
|
</g>
|
|
62
68
|
|
|
69
|
+
<!-- Value arc: anchor->value fill in the bezel ring; path data is
|
|
70
|
+
computed per-render via updateAttributes (see below).
|
|
71
|
+
Consumers can restyle it via the knob-value-arc class. -->
|
|
72
|
+
<path id="valueArc" class="knob-value-arc" d="" stroke="#35619F" stroke-width="7" fill="none" opacity="0.85" stroke-linecap="round" pointer-events="none"/>
|
|
73
|
+
|
|
63
74
|
<!-- Fixed position marks (outside the rotating knob group) -->
|
|
64
75
|
<!-- 12:00 position mark (more prominent) -->
|
|
65
76
|
<line x1="100.0098534" y1="15" x2="100.0098534" y2="35" stroke="#CCCCCC" stroke-width="4" opacity="0.9"/>
|
|
66
77
|
|
|
67
|
-
<!-- Arc fill between end position marks -->
|
|
68
|
-
<!-- <path d="M 35 165 A 65 65 0 0 1 165 165" stroke="#666666" stroke-width="8" fill="none" opacity="0.3"/> -->
|
|
69
|
-
|
|
70
78
|
<!-- End position marks (0 and 127) -->
|
|
71
79
|
<!-- 0 position mark (40 degrees from top, clockwise) - at outer radius beyond knob edge -->
|
|
72
80
|
<!-- This should be at the bottom-left where the value indicator points when at 0 -->
|
|
@@ -106,6 +114,26 @@ const knobSkin10 = {
|
|
|
106
114
|
},
|
|
107
115
|
],
|
|
108
116
|
},
|
|
117
|
+
{
|
|
118
|
+
element: '#valueArc',
|
|
119
|
+
attrs: [
|
|
120
|
+
{
|
|
121
|
+
name: 'd',
|
|
122
|
+
value: (props, value) => buildValueArcPath({
|
|
123
|
+
value,
|
|
124
|
+
min: props.min ?? 0,
|
|
125
|
+
max: props.max ?? 127,
|
|
126
|
+
centered: props.centered ?? false,
|
|
127
|
+
cx: ARC_CX,
|
|
128
|
+
cy: ARC_CY,
|
|
129
|
+
r: ARC_R,
|
|
130
|
+
clampMin: props.clampMin ?? 40,
|
|
131
|
+
clampMax: props.clampMax ?? 320,
|
|
132
|
+
rotateDegrees: props.rotateDegrees ?? 180,
|
|
133
|
+
}),
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
},
|
|
109
137
|
],
|
|
110
138
|
};
|
|
111
139
|
export { knobSkin10 };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure geometry for the knob's value-arc fill.
|
|
3
|
+
*
|
|
4
|
+
* Angle convention matches RotaryKnob's convertValueToAngle: a value maps
|
|
5
|
+
* linearly onto [clampMin, clampMax] sweep degrees, then rotateDegrees is
|
|
6
|
+
* added (mod 360). The resulting angle is measured clockwise from 12 o'clock,
|
|
7
|
+
* because the skin's #knob indicator points up at rotation 0.
|
|
8
|
+
*
|
|
9
|
+
* With Knob's constants (clampMin 40, clampMax 320, rotateDegrees 180):
|
|
10
|
+
* min → 220° (bottom-left), max → 140° (bottom-right), midpoint → 0° (top).
|
|
11
|
+
*/
|
|
12
|
+
/** Below this many sweep degrees no arc is drawn (avoids zero-length paths). */
|
|
13
|
+
const MIN_ARC_DEGREES = 0.5;
|
|
14
|
+
const point = (cx, cy, r, clockwiseFromTopDeg) => {
|
|
15
|
+
const rad = (clockwiseFromTopDeg * Math.PI) / 180;
|
|
16
|
+
return [cx + r * Math.sin(rad), cy - r * Math.cos(rad)];
|
|
17
|
+
};
|
|
18
|
+
const fmt = (n) => String(Math.round(n * 1000) / 1000);
|
|
19
|
+
/**
|
|
20
|
+
* SVG path `d` for the arc from the anchor (min, or sweep midpoint when
|
|
21
|
+
* centered) to the current value. Empty string when there is nothing to draw.
|
|
22
|
+
*/
|
|
23
|
+
const buildValueArcPath = ({ value, min, max, centered = false, cx = 100, cy = 100, r = 93, clampMin = 40, clampMax = 320, rotateDegrees = 180, }) => {
|
|
24
|
+
if (max === min)
|
|
25
|
+
return '';
|
|
26
|
+
const ratio = Math.min(1, Math.max(0, (value - min) / (max - min)));
|
|
27
|
+
const sweepValue = clampMin + ratio * (clampMax - clampMin);
|
|
28
|
+
const sweepAnchor = centered ? (clampMin + clampMax) / 2 : clampMin;
|
|
29
|
+
const delta = sweepValue - sweepAnchor;
|
|
30
|
+
if (Math.abs(delta) < MIN_ARC_DEGREES)
|
|
31
|
+
return '';
|
|
32
|
+
const angleOf = (sweep) => (((sweep + rotateDegrees) % 360) + 360) % 360;
|
|
33
|
+
const [x1, y1] = point(cx, cy, r, angleOf(sweepAnchor));
|
|
34
|
+
const [x2, y2] = point(cx, cy, r, angleOf(sweepValue));
|
|
35
|
+
const largeArc = Math.abs(delta) > 180 ? 1 : 0;
|
|
36
|
+
const sweepFlag = delta > 0 ? 1 : 0;
|
|
37
|
+
return `M ${fmt(x1)} ${fmt(y1)} A ${fmt(r)} ${fmt(r)} 0 ${largeArc} ${sweepFlag} ${fmt(x2)} ${fmt(y2)}`;
|
|
38
|
+
};
|
|
39
|
+
export { buildValueArcPath };
|
package/dist/rotaryKnob/index.js
CHANGED
|
@@ -25,7 +25,10 @@ import { getAngleForPoint } from './utils';
|
|
|
25
25
|
* Generic knob component
|
|
26
26
|
*/
|
|
27
27
|
const RotaryKnob = (props) => {
|
|
28
|
-
const {
|
|
28
|
+
const {
|
|
29
|
+
// Consumed by skins via updateAttributes; destructured so it doesn't
|
|
30
|
+
// leak onto the container <div> through ...rest.
|
|
31
|
+
centered: _centered, clampMax = 360, clampMin = 0, defaultValue = 0, disabled = false, format = (val) => val.toFixed(0), max = 100, min = 0, onChange = () => { }, onEnd = () => { }, onStart = () => { }, preciseMode = true, rotateDegrees = 0, skin = defaultSkin, step = 1, style, unlockDistance = 100, value, ...rest } = props;
|
|
29
32
|
const [isControlled, setIsControlled] = useState(value !== undefined);
|
|
30
33
|
const container = useRef(null);
|
|
31
34
|
const inputRef = useRef(null);
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
declare const MEASUREMENT_ELEMENT_ID = "__react_svg_text_measurement_id";
|
|
2
|
+
declare const __resetFontMetricsCacheForTests: () => void;
|
|
2
3
|
type WordsByLine = {
|
|
3
4
|
words: string[];
|
|
4
5
|
width: number;
|
|
5
6
|
showLine: boolean;
|
|
6
7
|
};
|
|
8
|
+
declare const calculateWordsByLines: (text: string, wordWidths: WordWidths, maxWidth: number, maxHeight: number) => WordsByLine[];
|
|
7
9
|
type WordWidths = {
|
|
8
10
|
wordsWithComputedWidth: Record<string, number>;
|
|
9
11
|
spaceWidth: number;
|
|
10
12
|
lineHeight: number;
|
|
11
13
|
};
|
|
14
|
+
declare const calculateWordWidths: (style?: CSSStyleDeclaration, textNode?: SVGGraphicsElement | null, text?: string) => WordWidths | undefined;
|
|
12
15
|
type TextProps = Partial<HTMLOrSVGElement> & {
|
|
13
16
|
className?: string;
|
|
14
17
|
dx?: number;
|
|
@@ -23,5 +26,5 @@ type TextProps = Partial<HTMLOrSVGElement> & {
|
|
|
23
26
|
y?: number;
|
|
24
27
|
};
|
|
25
28
|
declare const SvgText: (props: TextProps) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
-
export {
|
|
29
|
+
export { __resetFontMetricsCacheForTests, calculateWordsByLines, calculateWordWidths, MEASUREMENT_ELEMENT_ID, SvgText, };
|
|
27
30
|
export type { TextProps, WordWidths, WordsByLine };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { GroupBase, SelectInstance } from 'react-select';
|
|
2
|
+
import type { KnobProps } from './Knob';
|
|
2
3
|
import type { Option, SelectProps } from './Select';
|
|
3
4
|
type KnobPickerProps = SelectProps<number> & {
|
|
5
|
+
centered?: boolean;
|
|
4
6
|
highToLow?: boolean;
|
|
5
7
|
includeLabel?: boolean;
|
|
6
8
|
includePicker?: boolean;
|
|
7
|
-
knobProps?: Partial<
|
|
9
|
+
knobProps?: Partial<KnobProps>;
|
|
8
10
|
max?: number;
|
|
9
11
|
min?: number;
|
|
10
12
|
wheelEnabled?: boolean;
|
|
@@ -39,10 +41,11 @@ declare const KnobPicker: import("react").ForwardRefExoticComponent<Partial<impo
|
|
|
39
41
|
shrinkLabel?: boolean;
|
|
40
42
|
value?: number | undefined;
|
|
41
43
|
} & {
|
|
44
|
+
centered?: boolean;
|
|
42
45
|
highToLow?: boolean;
|
|
43
46
|
includeLabel?: boolean;
|
|
44
47
|
includePicker?: boolean;
|
|
45
|
-
knobProps?: Partial<
|
|
48
|
+
knobProps?: Partial<KnobProps>;
|
|
46
49
|
max?: number;
|
|
47
50
|
min?: number;
|
|
48
51
|
wheelEnabled?: boolean;
|
|
@@ -1,14 +1,22 @@
|
|
|
1
|
+
import type { RotaryKnobProps } from '../rotaryKnob';
|
|
1
2
|
declare const knobSkin10: {
|
|
2
3
|
knobX: number;
|
|
3
4
|
knobY: number;
|
|
4
5
|
svg: string;
|
|
5
|
-
updateAttributes: {
|
|
6
|
+
updateAttributes: ({
|
|
6
7
|
element: string;
|
|
7
|
-
content: (_props:
|
|
8
|
+
content: (_props: RotaryKnobProps, value: number) => string;
|
|
8
9
|
attrs: {
|
|
9
10
|
name: string;
|
|
10
11
|
value: () => string;
|
|
11
12
|
}[];
|
|
12
|
-
}
|
|
13
|
+
} | {
|
|
14
|
+
element: string;
|
|
15
|
+
attrs: {
|
|
16
|
+
name: string;
|
|
17
|
+
value: (props: RotaryKnobProps, value: number) => string;
|
|
18
|
+
}[];
|
|
19
|
+
content?: undefined;
|
|
20
|
+
})[];
|
|
13
21
|
};
|
|
14
22
|
export { knobSkin10 };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure geometry for the knob's value-arc fill.
|
|
3
|
+
*
|
|
4
|
+
* Angle convention matches RotaryKnob's convertValueToAngle: a value maps
|
|
5
|
+
* linearly onto [clampMin, clampMax] sweep degrees, then rotateDegrees is
|
|
6
|
+
* added (mod 360). The resulting angle is measured clockwise from 12 o'clock,
|
|
7
|
+
* because the skin's #knob indicator points up at rotation 0.
|
|
8
|
+
*
|
|
9
|
+
* With Knob's constants (clampMin 40, clampMax 320, rotateDegrees 180):
|
|
10
|
+
* min → 220° (bottom-left), max → 140° (bottom-right), midpoint → 0° (top).
|
|
11
|
+
*/
|
|
12
|
+
type ValueArcOptions = {
|
|
13
|
+
value: number;
|
|
14
|
+
min: number;
|
|
15
|
+
max: number;
|
|
16
|
+
/** Anchor the arc at the sweep midpoint (12 o'clock) instead of min. */
|
|
17
|
+
centered?: boolean;
|
|
18
|
+
cx?: number;
|
|
19
|
+
cy?: number;
|
|
20
|
+
r?: number;
|
|
21
|
+
clampMin?: number;
|
|
22
|
+
clampMax?: number;
|
|
23
|
+
rotateDegrees?: number;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* SVG path `d` for the arc from the anchor (min, or sweep midpoint when
|
|
27
|
+
* centered) to the current value. Empty string when there is nothing to draw.
|
|
28
|
+
*/
|
|
29
|
+
declare const buildValueArcPath: ({ value, min, max, centered, cx, cy, r, clampMin, clampMax, rotateDegrees, }: ValueArcOptions) => string;
|
|
30
|
+
export { buildValueArcPath };
|
|
31
|
+
export type { ValueArcOptions };
|
|
@@ -2,28 +2,8 @@
|
|
|
2
2
|
* Show the rotation circle and marker
|
|
3
3
|
* dispatches drag events
|
|
4
4
|
*/
|
|
5
|
-
/**
|
|
6
|
-
* type definition for the skin system attribute modification element
|
|
7
|
-
*/
|
|
8
|
-
type AttributeSetValue = {
|
|
9
|
-
name: string;
|
|
10
|
-
value: (props: unknown, value: unknown) => string;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Type definition for the skin element manipulation block
|
|
14
|
-
*/
|
|
15
|
-
interface UpdateElement {
|
|
16
|
-
element: string;
|
|
17
|
-
content: (_props: React.CSSProperties, value: number) => string;
|
|
18
|
-
attrs: AttributeSetValue[];
|
|
19
|
-
}
|
|
20
|
-
interface Skin {
|
|
21
|
-
svg: string;
|
|
22
|
-
knobX: number;
|
|
23
|
-
knobY: number;
|
|
24
|
-
updateAttributes: UpdateElement[];
|
|
25
|
-
}
|
|
26
5
|
type KnobProps = Omit<React.ComponentProps<'div'>, 'onChange'> & {
|
|
6
|
+
centered?: boolean;
|
|
27
7
|
clampMax?: number;
|
|
28
8
|
clampMin?: number;
|
|
29
9
|
defaultValue?: number;
|
|
@@ -42,8 +22,32 @@ type KnobProps = Omit<React.ComponentProps<'div'>, 'onChange'> & {
|
|
|
42
22
|
unlockDistance?: number;
|
|
43
23
|
value?: number;
|
|
44
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* type definition for the skin system attribute modification element.
|
|
27
|
+
* Skins receive the knob's full props (min/max/centered/clamp*, etc.) plus
|
|
28
|
+
* the current value, so they can derive geometry like the value arc.
|
|
29
|
+
*/
|
|
30
|
+
type AttributeSetValue = {
|
|
31
|
+
name: string;
|
|
32
|
+
value: (props: KnobProps, value: number) => string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Type definition for the skin element manipulation block
|
|
36
|
+
*/
|
|
37
|
+
interface UpdateElement {
|
|
38
|
+
element: string;
|
|
39
|
+
content?: (props: KnobProps, value: number) => string;
|
|
40
|
+
attrs: AttributeSetValue[];
|
|
41
|
+
}
|
|
42
|
+
interface Skin {
|
|
43
|
+
svg: string;
|
|
44
|
+
knobX: number;
|
|
45
|
+
knobY: number;
|
|
46
|
+
updateAttributes: UpdateElement[];
|
|
47
|
+
}
|
|
45
48
|
/**
|
|
46
49
|
* Generic knob component
|
|
47
50
|
*/
|
|
48
51
|
declare const RotaryKnob: (props: KnobProps) => import("react/jsx-runtime").JSX.Element;
|
|
49
52
|
export { RotaryKnob };
|
|
53
|
+
export type { KnobProps as RotaryKnobProps, Skin, UpdateElement };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zds-pickers",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"main": "./dist/index.cjs.js",
|
|
5
5
|
"module": "./dist/index.es.js",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -21,25 +21,24 @@
|
|
|
21
21
|
},
|
|
22
22
|
"sideEffects": ["lib/soundfonts/*.js", "dist/soundfonts/*.js"],
|
|
23
23
|
"files": ["dist", "lib/soundfonts"],
|
|
24
|
-
"homepage": "https://github.com/
|
|
24
|
+
"homepage": "https://github.com/nebiru-software/zds-pickers#readme",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"author": "Darin Kadrioski <dkadrios@gmail.com>",
|
|
27
27
|
"bugs": {
|
|
28
|
-
"url": "https://github.com/
|
|
28
|
+
"url": "https://github.com/nebiru-software/zds-pickers/issues"
|
|
29
29
|
},
|
|
30
30
|
"description": "Picker controls that are common to Zendrum Studio apps",
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
33
|
-
"url": "git+https://github.com/
|
|
33
|
+
"url": "git+https://github.com/nebiru-software/zds-pickers.git"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@biomejs/biome": "1.9.4",
|
|
37
|
-
"@storybook/addon-docs": "
|
|
38
|
-
"@storybook/addon-
|
|
39
|
-
"@storybook/
|
|
40
|
-
"@storybook/
|
|
41
|
-
"@storybook/react": "
|
|
42
|
-
"@storybook/react-vite": "10.4.6",
|
|
37
|
+
"@storybook/addon-docs": "10.5.5",
|
|
38
|
+
"@storybook/addon-links": "10.5.5",
|
|
39
|
+
"@storybook/builder-vite": "10.5.5",
|
|
40
|
+
"@storybook/react": "10.5.5",
|
|
41
|
+
"@storybook/react-vite": "10.5.5",
|
|
43
42
|
"@types/d3-drag": "3.0.7",
|
|
44
43
|
"@types/d3-scale": "4.0.9",
|
|
45
44
|
"@types/d3-selection": "3.0.11",
|
|
@@ -49,19 +48,19 @@
|
|
|
49
48
|
"classnames": "2.5.1",
|
|
50
49
|
"globals": "15.15.0",
|
|
51
50
|
"immer": "10.1.1",
|
|
52
|
-
"nodemon": "3.1.
|
|
51
|
+
"nodemon": "3.1.14",
|
|
53
52
|
"prop-types": "15.8.1",
|
|
54
53
|
"rc-slider": "11.1.8",
|
|
55
54
|
"react": "18.3.1",
|
|
56
55
|
"react-dom": "18.3.1",
|
|
57
56
|
"react-select": "5.10.1",
|
|
58
|
-
"storybook": "
|
|
57
|
+
"storybook": "10.5.5",
|
|
59
58
|
"typescript": "5.8.2",
|
|
60
59
|
"vite": "^8.0.16",
|
|
60
|
+
"vitest": "4.1.10",
|
|
61
61
|
"zds-mappings": "1.4.9"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"caniuse-lite": "1.0.30001741",
|
|
65
64
|
"d3-drag": "3.0.0",
|
|
66
65
|
"d3-scale": "4.0.2",
|
|
67
66
|
"d3-selection": "3.0.0",
|
|
@@ -75,7 +74,7 @@
|
|
|
75
74
|
"react": ">=18.0.0",
|
|
76
75
|
"react-dom": ">=18.0.0",
|
|
77
76
|
"react-select": ">=5.10.0",
|
|
78
|
-
"zds-mappings": "1.4.9"
|
|
77
|
+
"zds-mappings": "^1.4.9"
|
|
79
78
|
},
|
|
80
79
|
"scripts": {
|
|
81
80
|
"dev": "vite",
|
|
@@ -85,6 +84,11 @@
|
|
|
85
84
|
"preview": "vite preview",
|
|
86
85
|
"sb": "storybook dev --quiet -p 6006 --no-open",
|
|
87
86
|
"sb:watch": "nodemon --watch .storybook -e ts,tsx,js,jsx --exec 'npm run sb'",
|
|
88
|
-
"prepublishOnly": "npm run build"
|
|
87
|
+
"prepublishOnly": "npm run build",
|
|
88
|
+
"lint:ci": "biome check",
|
|
89
|
+
"test": "vitest run"
|
|
90
|
+
},
|
|
91
|
+
"engines": {
|
|
92
|
+
"node": ">=22"
|
|
89
93
|
}
|
|
90
94
|
}
|