Tier 3: bubbles, animation, wrap mode, XYPad, AlphaDisplay, PushButton
- valueBubble/bubbleFormat: floating readout above knobs while dragging
(Knob-based skins + ImageKnob)
- animateChanges: rAF ease-out tween of the pointer on programmatic
value changes; user gestures snap; prefers-reduced-motion disables
- wrap: endless encoder mode — values and relative drags roll around
min↔max; pairs with angleOffset={0} angleRange={360}
- XYPad: two-parameter pad with absolute drag, arrow keys (Shift
coarse), Escape cancel, double-click reset, grid/crosshair, focus
ring, hidden form inputs, change meta
- AlphaDisplay: fourteen-segment alphanumeric LED (A-Z, 0-9, symbols,
decimal points, chars padding/align, glow/skew)
- PushButton: studio panel button with LED strip — toggle or momentary,
controlled/uncontrolled, aria-pressed, hidden form input
- Docs: five new gallery cards incl. animated preset demo, API rows,
README
This commit is contained in:
parent
0aee48b749
commit
e9e52e43ed
12 changed files with 936 additions and 8 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
AlphaDisplay,
|
||||
Arc,
|
||||
DreamknobProvider,
|
||||
Fader,
|
||||
|
|
@ -13,14 +14,57 @@ import {
|
|||
Meter,
|
||||
NeonKnob,
|
||||
Pointer,
|
||||
PushButton,
|
||||
RubberKnob,
|
||||
SegmentDisplay,
|
||||
SteppedKnob,
|
||||
Ticks,
|
||||
VintageKnob,
|
||||
XYPad,
|
||||
logTaper,
|
||||
} from 'dreamknob';
|
||||
|
||||
const PRESETS = [
|
||||
{ name: 'INIT PATCH', cutoff: 50 },
|
||||
{ name: 'WARM PAD', cutoff: 22 },
|
||||
{ name: 'ACID 303', cutoff: 84 },
|
||||
];
|
||||
|
||||
const PresetDemo: React.FC = () => {
|
||||
const [preset, setPreset] = React.useState(0);
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 14 }}>
|
||||
<div style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
|
||||
<FlatKnob
|
||||
size={84}
|
||||
value={PRESETS[preset].cutoff}
|
||||
onChange={() => undefined}
|
||||
readOnly
|
||||
animateChanges={{ duration: 350 }}
|
||||
color="#3df2ad"
|
||||
label="Cutoff"
|
||||
aria-label="Preset cutoff"
|
||||
/>
|
||||
<AlphaDisplay value={PRESETS[preset].name} chars={10} height={16} color="#3df2ad" />
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
{PRESETS.map((p, i) => (
|
||||
<PushButton
|
||||
key={p.name}
|
||||
pressed={preset === i}
|
||||
onChange={on => on && setPreset(i)}
|
||||
color="#3df2ad"
|
||||
size={34}
|
||||
aria-label={`Preset ${p.name}`}
|
||||
>
|
||||
{String.fromCharCode(65 + i)}
|
||||
</PushButton>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/** Draw a film-strip of a simple hardware knob at runtime (stand-in for a KnobMan PNG). */
|
||||
const useGeneratedStrip = (frames = 31, fs = 120): string | null => {
|
||||
const [src, setSrc] = React.useState<string | null>(null);
|
||||
|
|
@ -340,6 +384,59 @@ export const Gallery: React.FC = () => (
|
|||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="Animated presets + buttons" desc="animateChanges tweens programmatic changes; PushButton latches or is momentary." tag="animateChanges · <PushButton />">
|
||||
<PresetDemo />
|
||||
</Card>
|
||||
|
||||
<Card title="XY pad" desc="Two parameters at once — cutoff/resonance, vector mixing, FX morphing." tag="<XYPad />">
|
||||
<XYPad
|
||||
width={190}
|
||||
height={140}
|
||||
x={{ min: 20, max: 20000, step: 1, defaultValue: 800 }}
|
||||
y={{ min: 0, max: 100, step: 0.5, defaultValue: 30 }}
|
||||
formatX={v => (v >= 1000 ? `${(v / 1000).toFixed(1)}k` : `${Math.round(v)}`)}
|
||||
color="#e44cff"
|
||||
label="Filter"
|
||||
aria-label="Filter XY pad"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="Endless encoder" desc="wrap makes the value roll around min↔max — full-turn phase and offset dials." tag="wrap · angleRange={360}">
|
||||
<NeonKnob
|
||||
size={92}
|
||||
min={0}
|
||||
max={360}
|
||||
step={1}
|
||||
defaultValue={90}
|
||||
wrap
|
||||
angleOffset={0}
|
||||
angleRange={360}
|
||||
unit="°"
|
||||
color="#4cc2ff"
|
||||
label="Phase"
|
||||
aria-label="Phase encoder"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="Drag bubble" desc="A floating readout follows the gesture — keep the knob face clean." tag="valueBubble">
|
||||
<MetalKnob
|
||||
size={92}
|
||||
defaultValue={64}
|
||||
step={1}
|
||||
valueBubble
|
||||
bubbleFormat={v => `${v} %`}
|
||||
label="Blend"
|
||||
aria-label="Bubble demo"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card title="Alpha display" desc="Fourteen-segment alphanumeric LED — preset names, modes, messages." tag="<AlphaDisplay />">
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, alignItems: 'center' }}>
|
||||
<AlphaDisplay value="DREAMKNOB" height={20} />
|
||||
<AlphaDisplay value="LP-24 SAT+2" height={16} color="#4cc2ff" />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card title="Compose your own" desc="Primitives + headless core = any design you want." tag="<Knob> + primitives">
|
||||
<Knob size={96} defaultValue={42} aria-label="Composed demo">
|
||||
<Ticks count={28} radius={47} length={5} width={1.5} color="rgba(255,255,255,0.12)" activeColor="#ffd23e" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue