Appearance
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:
- Input scrubbing — click and drag horizontally on any unfocused number input to adjust its value
- 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
| Modifier | Effect | Example |
|---|---|---|
| None | Default step (usually 1) | 100 → 101 |
| Shift | 10× step | 100 → 110 |
| Alt/Option | 0.1× step (min 0.1) | 1.5 → 1.6 |
| ↑ / ↓ (focused) | ±1 step | Arrow 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-activeclass globally to prevent cursor jitter during drag - Handles floating-point precision with
decimalPlaces()androundToStep()helpers - Respects
min/maxbounds 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-resizecursor 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:
- During scrub drag, block updates go directly to the creative iframe via
DataStorereactive properties - The iframe applies CSS changes immediately without a full creative rebuild
- 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:
| Section | Properties |
|---|---|
SizeSection | Width, Height |
MaxSizeSection | Max Width, Max Height |
RotationSection | Rotation angle |
ScaleSection | Scale factor |
FontSizeSection | Font size |
GapSection | Gap spacing |
DelaySection | Animation delay |
DurationSection | Animation duration |
IterationsSection | Animation iterations |
FilterSection | Blur, 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