BooleanControl Primitive
Complete API reference for BooleanControl - the primitive for building custom boolean (on/off) controls.
BooleanControl is the primitive component for building custom boolean (on/off) controls. It handles interaction logic, parameter management, and layout while you define the visual appearance.
API Reference
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
view | ControlComponent<P> | — | Yes | The visualization component that implements ControlComponentView |
viewProps | P | — | No | Props specific to the view component |
value | boolean | — | Yes | Current value of the control |
onChange | (event: AudioControlEvent<boolean>) => void | — | No | Handler for value changes |
onClick | React.MouseEventHandler | — | No | Click event handler (for non-editable views) |
latch | boolean | false | No | Whether the control operates in latch (toggle) or momentary mode |
parameter | BooleanParameter | — | No | Audio Parameter definition (overrides ad-hoc props) |
label | string | — | No | Label displayed below the component |
viewBoxWidthUnits | number | — | No | Override viewBox width from view component |
viewBoxHeightUnits | number | — | No | Override viewBox height from view component |
labelHeightUnits | number | — | No | Override label height from view component |
displayMode | "scaleToFit" | "fill" | scaleToFit | No | How the component scales within its container |
labelMode | "visible" | "hidden" | "none" | visible | No | Label display mode |
labelPosition | "above" | "below" | below | No | Label position relative to control |
labelAlign | "start" | "center" | "end" | center | No | Label horizontal alignment |
className | string | — | No | Additional CSS classes |
style | React.CSSProperties | — | No | Additional inline styles |
View Component Contract
Your view component must implement the ControlComponentView interface:
interface ControlComponentView {
viewBox: { width: number; height: number };
labelHeightUnits?: number; // Default: 20
interaction?: {
mode: "drag" | "wheel" | "both";
direction: "vertical" | "horizontal" | "circular" | "both";
};
}View Props
Your view component receives these props:
type ControlComponentViewProps<P = Record<string, unknown>> = {
normalizedValue: number; // 0.0 (false) or 1.0 (true)
children?: React.ReactNode; // For center content (HTML overlay)
className?: string;
style?: React.CSSProperties;
// ... any custom props from viewProps
} & P;Note: normalizedValue is always 0.0 (false) or 1.0 (true) for boolean controls.
Basic Usage
import { BooleanControl, ControlComponentViewProps } from "@cutoff/audio-ui-react";
import { useState } from "react";
function SimpleSwitchView({ normalizedValue }: ControlComponentViewProps) {
const isOn = normalizedValue > 0.5;
return (
<g>
<rect
x="20"
y="40"
width="60"
height="20"
rx="10"
fill={isOn ? "currentColor" : "#ccc"}
/>
<circle
cx={isOn ? 70 : 30}
cy="50"
r="8"
fill="white"
/>
</g>
);
}
SimpleSwitchView.viewBox = { width: 100, height: 100 };
export default function BasicUsageExample() {
const [value, setValue] = useState(false);
return (
<BooleanControl
view={SimpleSwitchView}
value={value}
onChange={(e) => setValue(e.value)}
latch={true}
label="Power"
/>
);
}Latch vs Momentary Modes
Latch Mode (Toggle)
In latch mode, the control toggles between on and off states:
<BooleanControl
view={MyView}
value={isOn}
onChange={(e) => setIsOn(e.value)}
latch={true}
label="Power"
/>Behavior:
- Click inside → toggles state
- Drag out while pressed → no change
- Drag back in while pressed → toggles again
- Works even when press starts outside the button
Momentary Mode
In momentary mode, the control is only active while being pressed:
<BooleanControl
view={MyView}
value={isPressed}
onChange={(e) => setIsPressed(e.value)}
latch={false}
label="Record"
/>Behavior:
- Press inside → turns on
- Drag out while pressed → turns off
- Drag back in while pressed → turns on again
- Works even when press starts outside the button
Drag-In/Drag-Out Behavior
BooleanControl supports hardware-like drag-in/drag-out interactions, enabling step sequencer patterns:
// Multiple buttons can be activated with a single drag gesture
<div style={{ display: "flex", gap: "10px" }}>
{[0, 1, 2, 3, 4, 5, 6, 7].map((step) => (
<BooleanControl
key={step}
view={StepButtonView}
value={steps[step]}
onChange={(e) => setSteps((prev) => ({ ...prev, [step]: e.value }))}
latch={true}
label={`Step ${step + 1}`}
/>
))}
</div>How it works:
- Global pointer tracking (not just on button element)
mouseenter/mouseleaveevents detect boundary crossing while pressed- Enables classic step sequencer interactions
Parameter Integration
Ad-Hoc Mode
Provide latch directly:
<BooleanControl
view={MyView}
value={isOn}
onChange={(e) => setIsOn(e.value)}
latch={true}
label="Power"
/>Parameter Model Mode
Provide a full BooleanParameter:
import { createBooleanParameter } from "@cutoff/audio-ui-react";
const powerParam = createBooleanParameter({
paramId: "power",
label: "Power",
latch: true,
defaultValue: false,
});
<BooleanControl
view={MyView}
parameter={powerParam}
value={isOn}
onChange={(e) => setIsOn(e.value)}
/>Interaction Modes
Editable Control
When onChange is provided, the control is editable:
<BooleanControl
view={MyView}
value={isOn}
onChange={(e) => setIsOn(e.value)} // Makes it editable
latch={true}
/>Supported interactions:
- Mouse click
- Touch tap
- Keyboard (Space/Enter)
- Drag-in/drag-out (momentary mode)
Clickable View
When only onClick is provided (no onChange), the control is a clickable view:
<BooleanControl
view={MyView}
value={false} // Value doesn't change
onClick={() => handleAction()} // Triggers action
/>Behavior:
- Triggers
onClickon mouse click - Touch events handled for mobile compatibility
- Value remains unchanged
Both
When both onChange and onClick are provided:
<BooleanControl
view={MyView}
value={isOn}
onChange={(e) => setIsOn(e.value)} // Handles value changes
onClick={() => handleClick()} // Also triggers on mouse click
latch={true}
/>Behavior:
onChangehandles touch events and value changesonClicktriggers on mouse clicks- Both can fire for the same interaction
Custom View Props
Pass custom props to your view component:
function CustomView({
normalizedValue,
customColor,
customSize,
}: ControlComponentViewProps<{
customColor: string;
customSize: number;
}>) {
const isOn = normalizedValue > 0.5;
return (
<g>
<circle
cx="50"
cy="50"
r={customSize}
fill={isOn ? customColor : "#ccc"}
/>
</g>
);
}
CustomView.viewBox = { width: 100, height: 100 };
<BooleanControl
view={CustomView}
viewProps={{
customColor: "#3b82f6",
customSize: 40,
}}
value={value}
onChange={(e) => setValue(e.value)}
/>Center Content (HTML Overlay)
Render HTML content as an overlay:
<BooleanControl
view={MyView}
value={isOn}
onChange={(e) => setIsOn(e.value)}
htmlOverlay={
<div style={{ fontSize: "16px", fontWeight: 700 }}>
{isOn ? "ON" : "OFF"}
</div>
}
/>Or use the children prop:
<BooleanControl
view={MyView}
value={isOn}
onChange={(e) => setIsOn(e.value)}
>
{isOn ? <OnIcon /> : <OffIcon />}
</BooleanControl>Keyboard Support
Automatic keyboard support:
- Space/Enter: Activates the control (toggles in latch mode, presses in momentary mode)
- Tab: Focus management
<BooleanControl
view={MyView}
value={isOn}
onChange={(e) => setIsOn(e.value)}
latch={true}
// Keyboard support is automatic
/>Advanced Usage Patterns
Step Sequencer Pattern
Use drag-in/drag-out to create step sequencer interactions:
import { BooleanControl } from "@cutoff/audio-ui-react";
import { useState } from "react";
export default function StepSequencerExample() {
const [steps, setSteps] = useState<boolean[]>([
false, false, false, false,
false, false, false, false,
]);
return (
<div style={{ display: "flex", gap: "4px" }}>
{steps.map((active, index) => (
<BooleanControl
key={index}
view={StepButtonView}
value={active}
onChange={(e) => {
const newSteps = [...steps];
newSteps[index] = e.value;
setSteps(newSteps);
}}
latch={true}
/>
))}
</div>
);
}Toggle Group
Multiple buttons where only one can be active:
import { BooleanControl } from "@cutoff/audio-ui-react";
import { useState } from "react";
export default function ToggleGroupExample() {
const [active, setActive] = useState<string | null>(null);
const options = ["Option 1", "Option 2", "Option 3"];
return (
<div style={{ display: "flex", gap: "10px" }}>
{options.map((option) => (
<BooleanControl
key={option}
view={ToggleButtonView}
value={active === option}
onChange={(e) => {
if (e.value) {
setActive(option);
} else {
setActive(null);
}
}}
latch={true}
label={option}
/>
))}
</div>
);
}Momentary Action Button
Button that triggers an action but doesn't maintain state:
<BooleanControl
view={ActionButtonView}
value={false} // Always false
onChange={(e) => {
if (e.value) {
handleAction();
}
}}
latch={false} // Momentary
label="Trigger"
/>Performance Considerations
- Global event listeners: Only attached during active drag sessions
- Memoization: Both BooleanControl and view components are memoized
- Pointer tracking: Efficient global pointer state management
- Re-renders: Only re-renders when value or props change
Type Exports
import {
BooleanControl,
BooleanControlComponentProps,
ControlComponentView,
ControlComponentViewProps,
} from "@cutoff/audio-ui-react";Examples
See the View System and View Primitives guide for complete examples of building custom boolean controls with BooleanControl.
Next Steps
- Learn about ContinuousControl Primitive for continuous value controls
- Explore DiscreteControl Primitive for discrete controls
- Check out View Primitives for building custom visualizations