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
72
packages/dreamknob/src/components/Knob.tsx
Normal file
72
packages/dreamknob/src/components/Knob.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import * as React from 'react';
|
||||
import { KnobContextProvider, type KnobRenderContext } from '../core/context';
|
||||
import type { KnobCoreProps } from '../core/types';
|
||||
import { useKnob } from '../hooks/useKnob';
|
||||
|
||||
export interface KnobProps extends KnobCoreProps {
|
||||
/** Square canvas size in px. Default: 80. */
|
||||
size?: number;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
/** SVG children (Arc, Pointer, Ticks, ...) or a render function receiving the live state. */
|
||||
children?: React.ReactNode | ((ctx: KnobRenderContext) => React.ReactNode);
|
||||
/** Extra content rendered inside the wrapper but outside the SVG (e.g. HTML labels). */
|
||||
overlay?: React.ReactNode | ((ctx: KnobRenderContext) => React.ReactNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Headless-but-visual knob container: wires up the full interaction model
|
||||
* (rotary/relative/track drag, wheel, keyboard, double-click reset, a11y)
|
||||
* and provides a render context for composable SVG primitives.
|
||||
*/
|
||||
export const Knob: React.FC<KnobProps> = ({
|
||||
size = 80,
|
||||
className,
|
||||
style,
|
||||
children,
|
||||
overlay,
|
||||
...core
|
||||
}) => {
|
||||
const knob = useKnob(core);
|
||||
const ctx: KnobRenderContext = {
|
||||
value: knob.value,
|
||||
normalized: knob.normalized,
|
||||
angle: knob.angle,
|
||||
isDragging: knob.isDragging,
|
||||
min: knob.min,
|
||||
max: knob.max,
|
||||
decimals: knob.decimals,
|
||||
angleOffset: knob.angleOffset,
|
||||
angleRange: knob.angleRange,
|
||||
size,
|
||||
center: size / 2,
|
||||
};
|
||||
|
||||
return (
|
||||
<KnobContextProvider value={ctx}>
|
||||
<div
|
||||
{...knob.bind}
|
||||
className={className}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
position: 'relative',
|
||||
width: size,
|
||||
height: size,
|
||||
...knob.bind.style,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox={`0 0 ${size} ${size}`}
|
||||
style={{ display: 'block', overflow: 'visible' }}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{typeof children === 'function' ? children(ctx) : children}
|
||||
</svg>
|
||||
{typeof overlay === 'function' ? overlay(ctx) : overlay}
|
||||
</div>
|
||||
</KnobContextProvider>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue