How We Build Selection Feature
The selection system is one of the most essential interactive features in any design tool. In our CDD-based architecture, we designed the selection logic to be decoupled, scalable, and context-driven—making it easy to evolve and extend, especially with AI agents and collaborative scenarios.
This document explains how we implemented selection in practice, and how the architecture promotes modularity and clear communication.
✅ Requirements and Design Considerations
Before implementation, we clarified the concrete requirements:
- Click on UI Panel to select a single element
- Click on Canvas to select a single element
- Shift + click to select multiple elements
- Drag to box-select a region
- Keyboard-driven selection (e.g., Cmd+A)
- Clear selection via click-away
- Clear selection via tool switch
- Update selection consistently across canvas, scene tree, and property panel
- Share selection state across modules without tight coupling
🗂️ Involved Repos
core
, reactive-events
, selection
, ui-context
, factroy
, input-system
, render
, ui
🔁 Event Flow Overview
Selection updates are driven by events and reactive flows. Here's a high-level view of how a box-selection happens:
🖼️ UI Rendering of Selection
The selection
state is bridged into the React UI through the ui-context
repository, which exposes a React context or hooks for components to consume.
factory
handles yjs changes for ui-context
and render
ui-context
listens for updates from the factory
selection yjs changes object and triggers re-renders of relevant UI parts, such as the selection highlights in the canvas or the selected items shown in the element panel.
The render
repo is responsible for the visual representation of the selection, including drawing the selection box and visual outlines.
🔽 Example Hook Usage
// In UI data providers
const useElementSelection = (): Set<string> =>
createStore(uiContext.elementSelection)
// In Element Panel
const elementSelection = useElementSelection()
<Element
elementId={elementId}
isSelected={elementSelection.has(elementId)}
/>
This pattern ensures a clean separation:
selection
manages the statefactory
handles selection changesui-context
provides reactive hooks and contextrender
handles the actual drawing
🧱 Decoupling and Modularity
The selection system separates state (selection
repo) from UI context (ui-context
) and rendering (render
) logic. This decoupling:
- Keeps feature logic focused and maintainable
- Makes it easier to swap or extend behavior without affecting unrelated modules
- Encourages collaboration, since each team can work within a clearly scoped repo
Even with minimal features, the modular structure reflects how CDD encourages clean boundaries and iterative growth.
📌 Summary
- The selection system is centered on a single reactive source of truth
- The system is extendable by design—new interactions, AI agents, and remote collaboration require no refactoring