6.0 KiB
TASK-005 Working Notes
Quick Links
- Legacy implementation:
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentsPanel.ts - Template:
packages/noodl-editor/src/editor/src/templates/componentspanel.html - Styles:
packages/noodl-editor/src/editor/src/styles/componentspanel.css - Folder model:
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentsPanelFolder.ts - Templates:
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentTemplates.ts - Sidebar docs:
packages/noodl-editor/docs/sidebar.md
Reference Components
Good patterns to follow:
views/SidePanel/SidePanel.tsx- Container for sidebar panelsviews/panels/SearchPanel/SearchPanel.tsx- Modern React panel exampleviews/panels/VersionControlPanel/VersionControlPanel.tsx- Another React panelviews/PopupLayer/PopupMenu.tsx- Context menu component
Key Decisions
Decision 1: State Management Approach
Options considered:
- useState + useEffect for ProjectModel subscription
- useModernModel hook (existing pattern)
- New Zustand store
Decision: Use useModernModel hook
Reasoning: Matches existing patterns in codebase, already handles subscription cleanup, proven to work with ProjectModel.
Decision 2: Tree Structure Representation
Options considered:
- Reuse ComponentsPanelFolder class
- Create new TreeNode interface
- Flat array with parent references
Decision: [TBD during implementation]
Reasoning: [TBD]
Decision 3: Drag-Drop Implementation
Options considered:
- Native HTML5 drag-drop with PopupLayer
- @dnd-kit library
- react-dnd
Decision: Native HTML5 with PopupLayer (initially)
Reasoning: Maintains consistency with existing drag-drop patterns in codebase, no new dependencies. Can upgrade to dnd-kit later if needed for DASH-003.
Technical Discoveries
ProjectModel Events
Key events to subscribe to:
const events = [
'componentAdded',
'componentRemoved',
'componentRenamed',
'rootComponentChanged',
'projectLoaded'
];
ComponentsPanelFolder Structure
The folder structure is built dynamically from component names:
/Component1 → root folder
/Folder1/Component2 → Folder1 contains Component2
/Folder1/ → Folder1 (folder component - both folder AND component)
Key insight: A folder can also BE a component. This is the "folder component" pattern where folder.component is set.
Icon Type Detection
From ComponentIcon.ts:
export function getComponentIconType(component: ComponentModel): ComponentIconType {
// Cloud functions
if (isComponentModel_CloudRuntime(component)) {
return ComponentIconType.CloudFunction;
}
// Pages (visual with router)
if (hasRouterChildren(component)) {
return ComponentIconType.Page;
}
// Visual components
if (isVisualComponent(component)) {
return ComponentIconType.Visual;
}
// Default: logic
return ComponentIconType.Logic;
}
Sheet System
Sheets are special top-level folders that start with #:
/#__cloud__- Cloud functions sheet (often hidden)/#pages- Pages sheet/- Default sheet (root)
The hideSheets option filters these from display.
PopupLayer Drag-Drop Pattern
// Start drag
PopupLayer.instance.startDragging({
label: 'Component Name',
type: 'component',
component: componentModel,
folder: parentFolder
});
// During drag (on drop target)
PopupLayer.instance.isDragging(); // Check if drag active
PopupLayer.instance.dragItem; // Get current drag item
PopupLayer.instance.indicateDropType('move' | 'none');
// On drop
PopupLayer.instance.dragCompleted();
Gotchas Discovered
Gotcha 1: Folder Component Selection
When clicking a "folder component", the folder scope should be selected, not the component scope. See selectComponent() in original.
Gotcha 2: Sheet Auto-Selection
When a component is selected, its sheet should automatically become active. See selectSheet() calls.
Gotcha 3: Rename Input Focus
The rename input needs careful focus management - it should select all text on focus and prevent click-through issues.
Gotcha 4: Empty Folder Cleanup
When a folder becomes empty (no components, no subfolders), and it's a "folder component", it should revert to a regular component.
Useful Commands
# Find all usages of ComponentsPanel
grep -r "ComponentsPanel" packages/noodl-editor/src/ --include="*.ts" --include="*.tsx"
# Find ProjectModel event subscriptions
grep -r "ProjectModel.instance.on" packages/noodl-editor/src/editor/
# Find useModernModel usage examples
grep -r "useModernModel" packages/noodl-editor/src/editor/
# Find PopupLayer drag-drop usage
grep -r "startDragging" packages/noodl-editor/src/editor/
# Test build
cd packages/noodl-editor && npm run build
# Type check
cd packages/noodl-editor && npx tsc --noEmit
Debug Log
Add entries as you work through implementation
[Date/Time] - Phase 1: Foundation
- Trying: [what you're attempting]
- Result: [what happened]
- Next: [what to try next]
Questions to Resolve
- Does SidebarModel need changes to accept React functional components directly?
- Should we keep ComponentsPanelFolder.ts or inline the logic?
- How do we handle the
nodeGraphEditorreference passed via options? - What's the right pattern for context menu positioning?
Discoveries for LEARNINGS.md
Note patterns discovered that should be added to dev-docs/reference/LEARNINGS.md
Pattern: Migrating Legacy View to React
Context: Converting jQuery View classes to React components
Pattern:
- Create React component with same props
- Use useModernModel for model subscriptions
- Replace data-click handlers with onClick props
- Replace data-class bindings with conditional classNames
- Replace $(selector) queries with refs or state
- Port CSS to CSS modules
Location: Sidebar panels
Pattern: [TBD]
Context: [TBD during implementation]
Pattern: [TBD]
Location: [TBD]