mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 23:32:55 +01:00
Working on the editor component tree
This commit is contained in:
@@ -0,0 +1,517 @@
|
||||
# TASK-005: ComponentsPanel React Migration
|
||||
|
||||
## ⚠️ CURRENT STATUS: BLOCKED
|
||||
|
||||
**Last Updated:** December 22, 2025
|
||||
**Status:** 🚫 BLOCKED - Webpack/Electron caching preventing testing
|
||||
**Completion:** ~85% (Backend works, UI update blocked)
|
||||
|
||||
**📖 See [STATUS-BLOCKED.md](./STATUS-BLOCKED.md) for complete details**
|
||||
|
||||
### Quick Summary
|
||||
|
||||
- ✅ Backend rename functionality works perfectly
|
||||
- ✅ Code fixes implemented correctly in source files
|
||||
- ❌ Webpack 5 persistent caching prevents new code from loading
|
||||
- ❌ UI doesn't update after rename because useEventListener never subscribes
|
||||
|
||||
**Next Action:** Requires dedicated investigation into webpack caching issue or alternative approach. See STATUS-BLOCKED.md for detailed analysis and potential solutions.
|
||||
|
||||
---
|
||||
|
||||
## 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`
|
||||
Reference in New Issue
Block a user