Skip to content

Scrub Input System

Figma-style drag-to-adjust number inputs for the creative builder. Available on the theming-system-redesign branch.

Overview

Number inputs in the builder configuration panel support two scrubbing interactions:

  1. Input scrubbing — click and drag horizontally on any unfocused number input to adjust its value
  2. Label scrubbing — click and drag on a property label (e.g., "Width", "X") to adjust the associated value

Both methods support keyboard modifiers for precision control.

Keyboard Modifiers

ModifierEffectExample
NoneDefault step (usually 1)100 → 101
Shift10× step100 → 110
Alt/Option0.1× step (min 0.1)1.5 → 1.6
↑ / ↓ (focused)±1 stepArrow keys in focused input
Shift + ↑/↓±10 step
Alt + ↑/↓±0.1 step

Architecture

useScrubInput Composable

src/composables/useScrubInput.ts

The core implementation. Attaches to any HTML element and emits value changes on horizontal drag.

Key behaviors:

  • Throttles emissions at 32ms for smooth UI updates
  • Adds html.scrubbing-active class globally to prevent cursor jitter during drag
  • Handles floating-point precision with decimalPlaces() and roundToStep() helpers
  • Respects min/max bounds when provided
  • Cleans up all event listeners on destroy, including mid-drag cleanup

Usage:

ts
import { useScrubInput } from '@/composables/useScrubInput'

// In setup or mounted:
useScrubInput(elementRef, {
  value: () => currentValue,
  step: 1,
  min: 0,
  max: 100,
  onUpdate: (newValue) => emit('input', newValue),
})

InputField.vue Integration

src/components/common/InputField.vue

Number-type InputField instances automatically get:

  • ew-resize cursor on hover (when unfocused)
  • Drag-to-scrub behavior on the input element itself
  • Keyboard ↑/↓ handling with Shift/Alt modifiers
  • Hidden native browser number spinners
  • Visual feedback: accent color border during active scrub

OptionRow.vue Integration

src/pages/Chatbots/components/OptionRow/OptionRow.vue

The property row wrapper supports an optional scrub prop:

vue
<OptionRow title="Width" scrub @scrub="onWidthChange">
  <InputField type="number" :value="width" />
</OptionRow>

When scrub is enabled, the title label becomes draggable. Visual feedback: cursor changes to ew-resize, border color changes during drag.

Live Preview During Scrub

To avoid janky rebuilds during fast scrubbing, the system uses a direct CSS update path:

  1. During scrub drag, block updates go directly to the creative iframe via DataStore reactive properties
  2. The iframe applies CSS changes immediately without a full creative rebuild
  3. On scrub end (mouseup), a normal block update is sent which triggers a proper rebuild

This means the user sees smooth, real-time visual feedback while dragging.

Rollout

Scrub is enabled on these builder sections:

SectionProperties
SizeSectionWidth, Height
MaxSizeSectionMax Width, Max Height
RotationSectionRotation angle
ScaleSectionScale factor
FontSizeSectionFont size
GapSectionGap spacing
DelaySectionAnimation delay
DurationSectionAnimation duration
IterationsSectionAnimation iterations
FilterSectionBlur, Brightness, Contrast, Grayscale, Hue Rotate, Invert, Opacity, Saturate, Sepia

Discoverability

The feature is discoverable through:

  • Cursor changing to on hover over number inputs and scrub-enabled labels
  • Visual highlight (accent border) during active drag
  • Consistent behavior across all number properties in the builder

Internal documentation