mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-11 23:02:56 +01:00
518 lines
14 KiB
Markdown
518 lines
14 KiB
Markdown
# TASK-004B: ComponentsPanel React Migration
|
|
|
|
## ✅ CURRENT STATUS: COMPLETE
|
|
|
|
**Last Updated:** December 26, 2025
|
|
**Status:** ✅ COMPLETE - All features working, ready for production
|
|
**Completion:** 100% (All functionality implemented and tested)
|
|
|
|
### Quick Summary
|
|
|
|
- ✅ Full React migration from legacy jQuery/underscore.js
|
|
- ✅ All features working: tree rendering, context menus, drag-drop, rename
|
|
- ✅ Direct ProjectModel subscription pattern (events working correctly)
|
|
- ✅ Root folder display issue fixed (no unnamed folder)
|
|
- ✅ Components like "App" immediately visible on load
|
|
- ✅ Zero jQuery dependencies, proper TypeScript throughout
|
|
|
|
**Migration Complete!** The panel is now fully modernized and ready for future enhancements (TASK-004 badges/filters).
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
Migrate the ComponentsPanel from the legacy jQuery/underscore.js View pattern to a modern React component. This eliminates tech debt, enables the migration badges/filters feature from TASK-004, and establishes a clean pattern for migrating remaining legacy panels.
|
|
|
|
**Phase:** 2 (Runtime Migration System)
|
|
**Priority:** HIGH (blocks TASK-004 parts 2 & 3)
|
|
**Effort:** 6-8 hours (Original estimate - actual time ~12 hours due to caching issues)
|
|
**Risk:** Medium → HIGH (Webpack caching complications)
|
|
|
|
---
|
|
|
|
## Background
|
|
|
|
### Current State
|
|
|
|
`ComponentsPanel.ts` is a ~800 line legacy View class that uses:
|
|
|
|
- jQuery for DOM manipulation and event handling
|
|
- Underscore.js HTML templates (`componentspanel.html`) with `data-*` attribute bindings
|
|
- Manual DOM updates via `scheduleRender()` pattern
|
|
- Complex drag-and-drop via PopupLayer integration
|
|
- Deep integration with ProjectModel, NodeGraphEditor, and sheets system
|
|
|
|
### Why Migrate Now?
|
|
|
|
1. **Blocks TASK-004**: Adding migration status badges and filters to a jQuery template creates a Frankenstein component mixing React dialogs into jQuery views
|
|
2. **Philosophy alignment**: "When we touch a component, we clean it properly"
|
|
3. **Pattern establishment**: This migration creates a template for other legacy panels
|
|
4. **Maintainability**: React components are easier to test, extend, and debug
|
|
|
|
### Prior Art
|
|
|
|
Several patterns already exist in the codebase:
|
|
|
|
- `ReactView` wrapper class for hybrid components
|
|
- `SidePanel.tsx` - the container that hosts sidebar panels (already React)
|
|
- `SidebarModel` registration pattern supports both legacy Views and React components
|
|
- `UndoQueuePanel` example in `docs/sidebar.md` shows the migration pattern
|
|
|
|
---
|
|
|
|
## Goals
|
|
|
|
1. **Full React rewrite** of ComponentsPanel with zero jQuery dependencies
|
|
2. **Feature parity** with existing functionality (drag-drop, folders, context menus, rename-in-place)
|
|
3. **Clean integration** with existing SidebarModel registration
|
|
4. **Prepare for badges/filters** - structure component to easily add TASK-004 features
|
|
5. **TypeScript throughout** - proper typing, no TSFixme
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
### Component Structure
|
|
|
|
```
|
|
ComponentsPanel/
|
|
├── ComponentsPanel.tsx # Main container, registered with SidebarModel
|
|
├── ComponentsPanel.module.scss # Scoped styles
|
|
├── components/
|
|
│ ├── ComponentTree.tsx # Recursive tree renderer
|
|
│ ├── ComponentItem.tsx # Single component row
|
|
│ ├── FolderItem.tsx # Folder row with expand/collapse
|
|
│ ├── SheetSelector.tsx # Sheet tabs (if showSheetList option)
|
|
│ └── AddComponentMenu.tsx # "+" button dropdown
|
|
├── hooks/
|
|
│ ├── useComponentsPanel.ts # Main state management hook
|
|
│ ├── useDragDrop.ts # Drag-drop logic
|
|
│ └── useRenameMode.ts # Inline rename handling
|
|
├── types.ts # TypeScript interfaces
|
|
└── index.ts # Exports
|
|
```
|
|
|
|
### State Management
|
|
|
|
Use React hooks with ProjectModel as source of truth:
|
|
|
|
- `useModernModel` hook to subscribe to ProjectModel events
|
|
- Local state for UI concerns (expanded folders, selection, rename mode)
|
|
- Derive tree structure from ProjectModel on each render
|
|
|
|
### Drag-Drop Strategy
|
|
|
|
Two options to evaluate:
|
|
|
|
**Option A: Native HTML5 Drag-Drop**
|
|
|
|
- Lighter weight, no dependencies
|
|
- Already used elsewhere in codebase via PopupLayer
|
|
- Requires manual drop zone management
|
|
|
|
**Option B: @dnd-kit library**
|
|
|
|
- Already planned as dependency for DASH-003 (Project Organisation)
|
|
- Better accessibility, smoother animations
|
|
- More code but cleaner abstractions
|
|
|
|
**Recommendation**: Start with Option A to maintain existing PopupLayer integration patterns. Can upgrade to dnd-kit later if needed.
|
|
|
|
---
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1: Foundation (1-2 hours)
|
|
|
|
Create the component structure and basic rendering without interactivity.
|
|
|
|
**Files to create:**
|
|
|
|
- `ComponentsPanel.tsx` - Shell component
|
|
- `ComponentsPanel.module.scss` - Base styles (port from existing CSS)
|
|
- `types.ts` - TypeScript interfaces
|
|
- `hooks/useComponentsPanel.ts` - State hook skeleton
|
|
|
|
**Tasks:**
|
|
|
|
1. Create directory structure
|
|
2. Define TypeScript interfaces for component/folder items
|
|
3. Create basic ComponentsPanel that renders static tree
|
|
4. Register with SidebarModel (replacing legacy panel)
|
|
5. Verify it mounts without errors
|
|
|
|
**Success criteria:**
|
|
|
|
- Panel appears in sidebar
|
|
- Shows hardcoded component list
|
|
- No console errors
|
|
|
|
### Phase 2: Tree Rendering (1-2 hours)
|
|
|
|
Implement proper tree structure from ProjectModel.
|
|
|
|
**Files to create:**
|
|
|
|
- `components/ComponentTree.tsx`
|
|
- `components/ComponentItem.tsx`
|
|
- `components/FolderItem.tsx`
|
|
|
|
**Tasks:**
|
|
|
|
1. Subscribe to ProjectModel with useModernModel
|
|
2. Build folder/component tree structure (port logic from `addComponentToFolderStructure`)
|
|
3. Implement recursive tree rendering
|
|
4. Add expand/collapse for folders
|
|
5. Implement component selection
|
|
6. Add proper icons (home, page, cloud function, visual)
|
|
|
|
**Success criteria:**
|
|
|
|
- Tree matches current panel exactly
|
|
- Folders expand/collapse
|
|
- Selection highlights correctly
|
|
- Icons display correctly
|
|
|
|
### Phase 3: Context Menus (1 hour)
|
|
|
|
Port context menu functionality.
|
|
|
|
**Files to create:**
|
|
|
|
- `components/AddComponentMenu.tsx`
|
|
|
|
**Tasks:**
|
|
|
|
1. Implement header "+" button menu using existing PopupMenu
|
|
2. Implement component right-click context menu
|
|
3. Implement folder right-click context menu
|
|
4. Wire up all actions (rename, duplicate, delete, make home)
|
|
|
|
**Success criteria:**
|
|
|
|
- All context menu items work
|
|
- Actions perform correctly (components created, renamed, deleted)
|
|
- Undo/redo works for all actions
|
|
|
|
### Phase 4: Drag-Drop (2 hours)
|
|
|
|
Port the drag-drop system.
|
|
|
|
**Files to create:**
|
|
|
|
- `hooks/useDragDrop.ts`
|
|
|
|
**Tasks:**
|
|
|
|
1. Create drag-drop hook using PopupLayer.startDragging pattern
|
|
2. Implement drag initiation on component/folder rows
|
|
3. Implement drop zones on folders and between items
|
|
4. Port drop validation logic (`getAcceptableDropType`)
|
|
5. Port drop execution logic (`dropOn`)
|
|
6. Handle cross-sheet drops
|
|
|
|
**Success criteria:**
|
|
|
|
- Components can be dragged to folders
|
|
- Folders can be dragged to folders
|
|
- Invalid drops show appropriate feedback
|
|
- Drop creates undo action
|
|
|
|
### Phase 5: Inline Rename (1 hour)
|
|
|
|
Port rename-in-place functionality.
|
|
|
|
**Files to create:**
|
|
|
|
- `hooks/useRenameMode.ts`
|
|
|
|
**Tasks:**
|
|
|
|
1. Create rename mode state management
|
|
2. Implement inline input field rendering
|
|
3. Handle Enter to confirm, Escape to cancel
|
|
4. Validate name uniqueness
|
|
5. Handle focus management
|
|
|
|
**Success criteria:**
|
|
|
|
- Double-click or menu triggers rename
|
|
- Input shows with current name selected
|
|
- Enter saves, Escape cancels
|
|
- Invalid names show error
|
|
|
|
### Phase 6: Sheet Selector (30 min)
|
|
|
|
Port sheet/tab functionality (if `showSheetList` option is true).
|
|
|
|
**Files to create:**
|
|
|
|
- `components/SheetSelector.tsx`
|
|
|
|
**Tasks:**
|
|
|
|
1. Render sheet tabs
|
|
2. Handle sheet switching
|
|
3. Respect `hideSheets` option
|
|
|
|
**Success criteria:**
|
|
|
|
- Sheets display correctly
|
|
- Switching sheets filters component list
|
|
- Hidden sheets don't appear
|
|
|
|
### Phase 7: Polish & Integration (1 hour)
|
|
|
|
Final cleanup and TASK-004 preparation.
|
|
|
|
**Tasks:**
|
|
|
|
1. Remove old ComponentsPanel.ts and template
|
|
2. Update any imports/references
|
|
3. Add data attributes for testing
|
|
4. Prepare component structure for badges/filters (TASK-004)
|
|
5. Write migration notes for other legacy panels
|
|
|
|
**Success criteria:**
|
|
|
|
- No references to old files
|
|
- All tests pass
|
|
- Ready for TASK-004 badge implementation
|
|
|
|
---
|
|
|
|
## Files to Modify
|
|
|
|
### Create (New)
|
|
|
|
```
|
|
packages/noodl-editor/src/editor/src/views/panels/ComponentsPanel/
|
|
├── ComponentsPanel.tsx
|
|
├── ComponentsPanel.module.scss
|
|
├── components/
|
|
│ ├── ComponentTree.tsx
|
|
│ ├── ComponentItem.tsx
|
|
│ ├── FolderItem.tsx
|
|
│ ├── SheetSelector.tsx
|
|
│ └── AddComponentMenu.tsx
|
|
├── hooks/
|
|
│ ├── useComponentsPanel.ts
|
|
│ ├── useDragDrop.ts
|
|
│ └── useRenameMode.ts
|
|
├── types.ts
|
|
└── index.ts
|
|
```
|
|
|
|
### Modify
|
|
|
|
```
|
|
packages/noodl-editor/src/editor/src/router.setup.ts
|
|
- Update ComponentsPanel import to new location
|
|
- Verify SidebarModel.register call works with React component
|
|
|
|
packages/noodl-editor/src/editor/src/models/sidebar/sidebarmodel.tsx
|
|
- May need adjustment if React components need different handling
|
|
```
|
|
|
|
### Delete (After Migration Complete)
|
|
|
|
```
|
|
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentsPanel.ts
|
|
packages/noodl-editor/src/editor/src/templates/componentspanel.html
|
|
```
|
|
|
|
### Keep (Reference/Integration)
|
|
|
|
```
|
|
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentsPanelFolder.ts
|
|
- Data structure class, can be reused or ported to types.ts
|
|
|
|
packages/noodl-editor/src/editor/src/views/panels/componentspanel/ComponentTemplates.ts
|
|
- Template definitions, used by AddComponentMenu
|
|
```
|
|
|
|
---
|
|
|
|
## Technical Notes
|
|
|
|
### SidebarModel Registration
|
|
|
|
Current registration in `router.setup.ts`:
|
|
|
|
```typescript
|
|
SidebarModel.instance.register({
|
|
id: 'components',
|
|
name: 'Components',
|
|
order: 1,
|
|
icon: IconName.Components,
|
|
onOpen: () => {
|
|
/* ... */
|
|
},
|
|
panelProps: {
|
|
options: {
|
|
showSheetList: true,
|
|
hideSheets: ['__cloud__']
|
|
}
|
|
},
|
|
panel: ComponentsPanel // Currently legacy View class
|
|
});
|
|
```
|
|
|
|
React components can be registered directly - see how `SidePanel.tsx` handles this with `SidebarModel.instance.getPanelComponent()`.
|
|
|
|
### ProjectModel Integration
|
|
|
|
Key events to subscribe to:
|
|
|
|
- `componentAdded` - New component created
|
|
- `componentRemoved` - Component deleted
|
|
- `componentRenamed` - Component name changed
|
|
- `rootComponentChanged` - Home component changed
|
|
|
|
Use `useModernModel(ProjectModel.instance, [...events])` pattern.
|
|
|
|
### Existing Patterns to Follow
|
|
|
|
Look at these files for patterns:
|
|
|
|
- `SearchPanel.tsx` - Modern React sidebar panel
|
|
- `VersionControlPanel.tsx` - Another React sidebar panel
|
|
- `useModernModel` hook - Model subscription pattern
|
|
- `PopupMenu` component - For context menus
|
|
|
|
### CSS Migration
|
|
|
|
Port styles from:
|
|
|
|
- `packages/noodl-editor/src/editor/src/styles/componentspanel.css`
|
|
|
|
To CSS modules in `ComponentsPanel.module.scss`.
|
|
|
|
---
|
|
|
|
## Testing Checklist
|
|
|
|
### Basic Rendering
|
|
|
|
- [ ] Panel appears in sidebar when Components icon clicked
|
|
- [ ] Components display with correct names
|
|
- [ ] Folders display with correct names
|
|
- [ ] Nested structure renders correctly
|
|
- [ ] Icons display correctly (home, page, cloud, visual, folder)
|
|
|
|
### Selection
|
|
|
|
- [ ] Clicking component selects it
|
|
- [ ] Clicking folder selects it
|
|
- [ ] Selection opens component in node graph editor
|
|
- [ ] Only one item selected at a time
|
|
|
|
### Folders
|
|
|
|
- [ ] Clicking caret expands/collapses folder
|
|
- [ ] Folder state persists during session
|
|
- [ ] Empty folders display correctly
|
|
- [ ] "Folder components" (folders that are also components) work
|
|
|
|
### Context Menus
|
|
|
|
- [ ] "+" button shows add menu
|
|
- [ ] Component context menu shows all options
|
|
- [ ] Folder context menu shows all options
|
|
- [ ] "Make home" option works
|
|
- [ ] "Rename" option works
|
|
- [ ] "Duplicate" option works
|
|
- [ ] "Delete" option works (with confirmation)
|
|
|
|
### Drag-Drop
|
|
|
|
- [ ] Can drag component to folder
|
|
- [ ] Can drag folder to folder
|
|
- [ ] Cannot drag folder into its own children
|
|
- [ ] Drop indicator shows correctly
|
|
- [ ] Invalid drops show feedback
|
|
- [ ] Undo works after drop
|
|
|
|
### Rename
|
|
|
|
- [ ] Double-click enables rename
|
|
- [ ] Context menu "Rename" enables rename
|
|
- [ ] Enter confirms rename
|
|
- [ ] Escape cancels rename
|
|
- [ ] Tab moves to next item (optional)
|
|
- [ ] Invalid names show error
|
|
|
|
### Sheets
|
|
|
|
- [ ] Sheet tabs display (if enabled)
|
|
- [ ] Clicking sheet filters component list
|
|
- [ ] Hidden sheets don't appear
|
|
|
|
### Integration
|
|
|
|
- [ ] Warnings icon appears for components with warnings
|
|
- [ ] Selection syncs with node graph editor
|
|
- [ ] New component appears immediately after creation
|
|
- [ ] Deleted component disappears immediately
|
|
|
|
---
|
|
|
|
## Risks & Mitigations
|
|
|
|
### Risk: Drag-drop edge cases
|
|
|
|
**Mitigation**: Port logic directly from existing implementation, test thoroughly
|
|
|
|
### Risk: Performance with large component trees
|
|
|
|
**Mitigation**: Use React.memo for tree items, virtualize if needed (future)
|
|
|
|
### Risk: Breaking existing functionality
|
|
|
|
**Mitigation**: Test all features before removing old code, keep old files until verified
|
|
|
|
### Risk: Subtle event timing issues
|
|
|
|
**Mitigation**: Use same ProjectModel subscription pattern as other panels
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
1. **Feature parity**: All existing functionality works identically
|
|
2. **No regressions**: Existing projects work correctly
|
|
3. **Clean code**: No jQuery, no TSFixme, proper TypeScript
|
|
4. **Ready for TASK-004**: Easy to add migration badges/filters
|
|
5. **Pattern established**: Can be used as template for other panel migrations
|
|
|
|
---
|
|
|
|
## Future Enhancements (Out of Scope)
|
|
|
|
- Virtualized rendering for huge component trees
|
|
- Keyboard navigation (arrow keys)
|
|
- Multi-select for bulk operations
|
|
- Search/filter within panel (separate from SearchPanel)
|
|
- Drag to reorder (not just move to folder)
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
**Blocked by:** None
|
|
|
|
**Blocks:**
|
|
|
|
- TASK-004 Parts 2 & 3 (Migration Status Badges & Filters)
|
|
|
|
---
|
|
|
|
## References
|
|
|
|
- Current implementation: `views/panels/componentspanel/ComponentsPanel.ts`
|
|
- Template: `templates/componentspanel.html`
|
|
- Styles: `styles/componentspanel.css`
|
|
- Folder model: `views/panels/componentspanel/ComponentsPanelFolder.ts`
|
|
- Sidebar docs: `packages/noodl-editor/docs/sidebar.md`
|
|
- SidePanel container: `views/SidePanel/SidePanel.tsx`
|