Docs/playground accuracy (from a full docs-vs-source audit): - Playground: drag-feel select covers pickup/relative rotary modes, is disabled for faders, and generated code no longer emits props the target component does not accept - API/README claims corrected: Shift-fine wheel scope, wheelStep and decimals defaults, trackInset row, data-part and ref-forwarding scope, VintageKnob readout exception, Meter peakHold tag, stale component lists, aria-valuetext wording, also-exported list API consistency fixes surfaced by the audit and the tutorial: - Keyboard gestures now fire onChangeStart (bracket parity with drag/ wheel - fixes gesture-scoped undo patterns) - name (hidden form input) added to Fader, LEDFader, ImageKnob - PushButton gets a themed keyboard-only focus ring + data-focus-visible New docs content: - Getting Started: 7-step guide with live examples and a gotchas list - Advanced guide: 8-step channel-strip tutorial ending in a live, themed, gesture-undoable strip with meter (plus headless demo) - Synth section: playable Web Audio synth - osc/filter/env/LFO/master all dreamknob controls, momentary-pad keyboard, analyser-driven Meter
155 lines
5.8 KiB
TypeScript
155 lines
5.8 KiB
TypeScript
import React from 'react';
|
||
import { FlatKnob, logTaper } from 'dreamknob';
|
||
import { CodeBlock } from '../components/CodeBlock';
|
||
|
||
const Step: React.FC<{ n: number; title: string; children: React.ReactNode }> = ({
|
||
n,
|
||
title,
|
||
children,
|
||
}) => (
|
||
<div style={{ marginBottom: 34 }}>
|
||
<h3 className="api-h" style={{ marginTop: 0 }}>
|
||
<span style={{ color: 'var(--accent)', fontFamily: 'var(--font-mono)', marginRight: 10 }}>
|
||
{String(n).padStart(2, '0')}
|
||
</span>
|
||
{title}
|
||
</h3>
|
||
{children}
|
||
</div>
|
||
);
|
||
|
||
export const GettingStarted: React.FC = () => (
|
||
<section className="block" id="start">
|
||
<div className="container prose">
|
||
<div className="section-kicker">Getting started</div>
|
||
<h2 className="section-title">Your first knob in five minutes</h2>
|
||
<p className="section-sub">
|
||
Everything below is copy-pasteable. The only rule to remember: every control needs
|
||
an <code>aria-label</code> (or a visible label wired via <code>aria-labelledby</code>).
|
||
</p>
|
||
|
||
<Step n={1} title="Install">
|
||
<CodeBlock code={`npm install dreamknob\n# peer deps: react >= 18, react-dom >= 18`} />
|
||
<p className="api-note" style={{ marginTop: 10 }}>
|
||
Zero runtime dependencies, ESM + CJS, full TypeScript types. No CSS file to
|
||
import — everything is SVG and inline styles.
|
||
</p>
|
||
</Step>
|
||
|
||
<Step n={2} title="Drop in a knob (uncontrolled)">
|
||
<p className="api-note" style={{ marginBottom: 10 }}>
|
||
Pass <code>defaultValue</code> and the knob manages its own state.{' '}
|
||
<code>defaultValue</code> is also the double-click reset target.
|
||
</p>
|
||
<CodeBlock
|
||
code={`import { FlatKnob } from 'dreamknob'
|
||
|
||
<FlatKnob
|
||
size={90}
|
||
min={0} max={100} step={1}
|
||
defaultValue={50}
|
||
label="Level" unit="%"
|
||
aria-label="Level"
|
||
/>`}
|
||
/>
|
||
<div className="preview" style={{ minHeight: 150, marginTop: 12 }}>
|
||
<FlatKnob size={90} min={0} max={100} step={1} defaultValue={50} label="Level" unit="%" aria-label="Level" />
|
||
</div>
|
||
</Step>
|
||
|
||
<Step n={3} title="Read the value (controlled)">
|
||
<p className="api-note" style={{ marginBottom: 10 }}>
|
||
For app state, pass <code>value</code> + <code>onChange</code>. The second
|
||
argument tells you what caused the change — useful later for undo/automation.
|
||
</p>
|
||
<CodeBlock
|
||
code={`const [gain, setGain] = useState(-6)
|
||
|
||
<FlatKnob
|
||
value={gain}
|
||
onChange={(v, meta) => setGain(v)} // meta.source: 'drag' | 'wheel' | 'keyboard' | 'reset' | 'api'
|
||
min={-60} max={12} step={0.5}
|
||
unit=" dB"
|
||
aria-label="Gain"
|
||
/>`}
|
||
/>
|
||
</Step>
|
||
|
||
<Step n={4} title="Pick the feel">
|
||
<p className="api-note" style={{ marginBottom: 10 }}>
|
||
The default is grab-and-rotate (the pointer angle drives the value). Prefer the
|
||
up/down plugin feel, or MIDI-style pickup? One prop each. Users always get
|
||
scroll-wheel, arrow keys, Shift-fine, Escape-cancel and double-click reset for
|
||
free.
|
||
</p>
|
||
<CodeBlock
|
||
code={`<FlatKnob interaction="vertical" ... /> // drag up/down like a plugin
|
||
<FlatKnob rotaryMode="pickup" ... /> // never jumps when grabbed off-position
|
||
<FlatKnob rotaryMode="relative" ... /> // angle deltas, no jump either`}
|
||
/>
|
||
</Step>
|
||
|
||
<Step n={5} title="Get the numbers right">
|
||
<p className="api-note" style={{ marginBottom: 10 }}>
|
||
<code>step</code> snaps and sets display precision (float-safe — no{' '}
|
||
<code>0.30000000000000004</code>). For audio ranges, use a taper:{' '}
|
||
<code>logTaper</code> gives equal ratios per turn, so 20 Hz–20 kHz feels right.
|
||
<code>format</code> controls display without touching the emitted value.
|
||
</p>
|
||
<CodeBlock
|
||
code={`import { FlatKnob, logTaper } from 'dreamknob'
|
||
|
||
<FlatKnob
|
||
min={20} max={20000}
|
||
taper={logTaper} // min must be > 0 for log
|
||
defaultValue={440}
|
||
decimals={0}
|
||
format={v => v >= 1000 ? \`\${(v / 1000).toFixed(1)}k\` : \`\${Math.round(v)}\`}
|
||
label="Freq"
|
||
aria-label="Frequency"
|
||
/>`}
|
||
/>
|
||
<div className="preview" style={{ minHeight: 150, marginTop: 12 }}>
|
||
<FlatKnob
|
||
size={90}
|
||
min={20}
|
||
max={20000}
|
||
taper={logTaper}
|
||
defaultValue={440}
|
||
decimals={0}
|
||
color="#3df2ad"
|
||
format={v => (v >= 1000 ? `${(v / 1000).toFixed(1)}k` : `${Math.round(v)}`)}
|
||
label="Freq"
|
||
aria-label="Frequency example"
|
||
/>
|
||
</div>
|
||
</Step>
|
||
|
||
<Step n={6} title="Theme once, restyle everything">
|
||
<CodeBlock
|
||
code={`import { DreamknobProvider } from 'dreamknob'
|
||
|
||
<DreamknobProvider base="dark" theme={{ accent: '#ff4d6b' }}>
|
||
{/* every knob/fader/meter below inherits the tokens */}
|
||
</DreamknobProvider>`}
|
||
/>
|
||
<p className="api-note" style={{ marginTop: 10 }}>
|
||
Use <code>base="light"</code> on light backgrounds. Per-instance color props
|
||
always win over the theme.
|
||
</p>
|
||
</Step>
|
||
|
||
<Step n={7} title="Gotchas checklist">
|
||
<p className="api-note">
|
||
① Always pass <code>aria-label</code>. ② <code>logTaper</code> needs{' '}
|
||
<code>min > 0</code> (it falls back to linear otherwise). ③ Controlled mode
|
||
means <em>you</em> own the value — if you don't call <code>setState</code> in{' '}
|
||
<code>onChange</code>, the knob won't move. ④ Hovering captures the scroll
|
||
wheel by default; pass <code>wheelRequiresFocus</code> if your UI scrolls. ⑤
|
||
The knob is a <code>div</code>, not an input — use <code>name="…"</code> if you
|
||
need it in a plain <code><form></code> post.
|
||
</p>
|
||
</Step>
|
||
</div>
|
||
</section>
|
||
);
|