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

NameTypeDefaultRequiredDescription
viewControlComponent<P>YesThe visualization component that implements ControlComponentView
viewPropsPNoProps specific to the view component
valuebooleanYesCurrent value of the control
onChange(event: AudioControlEvent<boolean>) => voidNoHandler for value changes
onClickReact.MouseEventHandlerNoClick event handler (for non-editable views)
latchbooleanfalseNoWhether the control operates in latch (toggle) or momentary mode
parameterBooleanParameterNoAudio Parameter definition (overrides ad-hoc props)
labelstringNoLabel displayed below the component
viewBoxWidthUnitsnumberNoOverride viewBox width from view component
viewBoxHeightUnitsnumberNoOverride viewBox height from view component
labelHeightUnitsnumberNoOverride label height from view component
displayMode"scaleToFit" | "fill"scaleToFitNoHow the component scales within its container
labelMode"visible" | "hidden" | "none"visibleNoLabel display mode
labelPosition"above" | "below"belowNoLabel position relative to control
labelAlign"start" | "center" | "end"centerNoLabel horizontal alignment
classNamestringNoAdditional CSS classes
styleReact.CSSPropertiesNoAdditional 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

BasicUsage.tsx
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:

LatchMode.tsx
<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:

MomentaryMode.tsx
<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:

DragInOut.tsx
// 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/mouseleave events detect boundary crossing while pressed
  • Enables classic step sequencer interactions

Parameter Integration

Ad-Hoc Mode

Provide latch directly:

AdHocMode.tsx
<BooleanControl
  view={MyView}
  value={isOn}
  onChange={(e) => setIsOn(e.value)}
  latch={true}
  label="Power"
/>

Parameter Model Mode

Provide a full BooleanParameter:

ParameterMode.tsx
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:

Editable.tsx
<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:

Clickable.tsx
<BooleanControl
  view={MyView}
  value={false} // Value doesn't change
  onClick={() => handleAction()} // Triggers action
/>

Behavior:

  • Triggers onClick on mouse click
  • Touch events handled for mobile compatibility
  • Value remains unchanged

Both

When both onChange and onClick are provided:

Both.tsx
<BooleanControl
  view={MyView}
  value={isOn}
  onChange={(e) => setIsOn(e.value)} // Handles value changes
  onClick={() => handleClick()} // Also triggers on mouse click
  latch={true}
/>

Behavior:

  • onChange handles touch events and value changes
  • onClick triggers on mouse clicks
  • Both can fire for the same interaction

Custom View Props

Pass custom props to your view component:

CustomViewProps.tsx
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:

CenterContent.tsx
<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:

ChildrenContent.tsx
<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
Keyboard.tsx
<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:

StepSequencer.tsx
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:

ToggleGroup.tsx
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:

MomentaryAction.tsx
<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