Initial release: dreamknob library + showcase
React + TypeScript knob/fader library with a headless interaction core (rotary, relative-drag and track modes, wheel, keyboard, a11y), SVG primitives, nine prebuilt skins, seven-segment display engine, and a Vite showcase with gallery, live playground and API docs.
This commit is contained in:
commit
60a02cb1db
46 changed files with 6186 additions and 0 deletions
94
packages/dreamknob/src/skins/SteppedKnob.tsx
Normal file
94
packages/dreamknob/src/skins/SteppedKnob.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import * as React from 'react';
|
||||
import { Knob } from '../components/Knob';
|
||||
import { Pointer } from '../primitives/Pointer';
|
||||
import { Ticks } from '../primitives/Ticks';
|
||||
import { KnobLabel } from '../primitives/Text';
|
||||
import { MONO_FONT, UI_FONT, type SkinProps } from './shared';
|
||||
|
||||
export interface SteppedKnobProps extends SkinProps {
|
||||
/** Position names, e.g. ['LP','BP','HP']. Sets the number of detents. */
|
||||
positions?: readonly string[];
|
||||
/** Number of detents when `positions` is not given. */
|
||||
steps?: number;
|
||||
color?: string;
|
||||
tickColor?: string;
|
||||
faceColor?: string;
|
||||
textColor?: string;
|
||||
labelColor?: string;
|
||||
}
|
||||
|
||||
/** Detented selector knob — mode/range switches with hard stops. */
|
||||
export const SteppedKnob: React.FC<SteppedKnobProps> = ({
|
||||
size = 90,
|
||||
positions,
|
||||
steps,
|
||||
color = '#ffd23e',
|
||||
tickColor = 'rgba(255,255,255,0.18)',
|
||||
faceColor = '#1c1d22',
|
||||
textColor = 'rgba(255,255,255,0.92)',
|
||||
labelColor = 'rgba(255,255,255,0.45)',
|
||||
label,
|
||||
showValue = true,
|
||||
className,
|
||||
style,
|
||||
...core
|
||||
}) => {
|
||||
const detents = positions ? positions.length : steps ?? 5;
|
||||
const min = core.min ?? 0;
|
||||
const max = core.max ?? (positions ? positions.length - 1 : 100);
|
||||
|
||||
return (
|
||||
<Knob size={size} className={className} style={style} {...core} min={min} max={max} steps={detents}>
|
||||
{ctx => {
|
||||
const index = Math.round(ctx.normalized * (detents - 1));
|
||||
return (
|
||||
<>
|
||||
<Ticks
|
||||
count={detents}
|
||||
radius={size / 2 - 1}
|
||||
length={size * 0.07}
|
||||
width={Math.max(1.5, size * 0.022)}
|
||||
color={tickColor}
|
||||
getTickProps={i => (i === index ? { stroke: color } : undefined)}
|
||||
/>
|
||||
<circle
|
||||
cx={ctx.center}
|
||||
cy={ctx.center}
|
||||
r={size / 2 - size * 0.14}
|
||||
fill={faceColor}
|
||||
stroke="rgba(255,255,255,0.1)"
|
||||
strokeWidth="1"
|
||||
/>
|
||||
<Pointer
|
||||
type="triangle"
|
||||
radius={size / 2 - size * 0.15}
|
||||
length={size * 0.14}
|
||||
width={size * 0.09}
|
||||
color={color}
|
||||
/>
|
||||
{showValue && (
|
||||
<text
|
||||
x={ctx.center}
|
||||
y={ctx.center}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="central"
|
||||
fontSize={size * (positions ? 0.15 : 0.17)}
|
||||
fill={textColor}
|
||||
fontFamily={MONO_FONT}
|
||||
fontWeight={600}
|
||||
style={{ pointerEvents: 'none' }}
|
||||
>
|
||||
{positions ? positions[index] : ctx.value.toFixed(ctx.decimals)}
|
||||
</text>
|
||||
)}
|
||||
{label && (
|
||||
<KnobLabel color={labelColor} fontFamily={UI_FONT} dy={size * 0.45} fontSize={size * 0.095}>
|
||||
{label}
|
||||
</KnobLabel>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Knob>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue