# TASK-008: ComponentsPanel Menu Enhancements & Sheet System ## 🟡 CURRENT STATUS: IN PROGRESS (Phase 2 Complete) **Last Updated:** December 27, 2025 **Status:** 🟡 IN PROGRESS **Completion:** 50% ### Quick Summary Implement the remaining ComponentsPanel features discovered during TASK-004B research: - ✅ Enhanced context menus with "Create" submenus - COMPLETE - ✅ Sheet system backend (detection, filtering, management) - COMPLETE - ⏳ Sheet selector UI with dropdown - NEXT - ⏳ Sheet management actions wired up - PENDING **Predecessor:** TASK-004B (ComponentsPanel React Migration) - COMPLETE ✅ ### Completed Phases **Phase 1: Enhanced Context Menus** ✅ - Create menu items in component/folder right-click menus - All component templates + folder creation accessible **Phase 2: Sheet System Backend** ✅ (December 27, 2025) - Sheet detection from `#`-prefixed folders - `useComponentsPanel` now exports: `sheets`, `currentSheet`, `selectSheet` - Tree filtering by selected sheet - `useSheetManagement` hook with full CRUD operations - All operations with undo support **TASK-008C: Drag-Drop System** ✅ - All 7 drop combinations working - Root drop zone implemented --- ## Overview TASK-004B successfully migrated the ComponentsPanel to React, but several features from the legacy implementation were intentionally deferred. This task completes the ComponentsPanel by adding: 1. **Enhanced Context Menus**: Add "Create" submenus to component and folder right-click menus 2. **Sheet System UI**: Implement dropdown selector for managing component sheets 3. **Sheet Management**: Full CRUD operations for sheets with undo support **Phase:** 2 (Runtime Migration System) **Priority:** MEDIUM (UX enhancement, not blocking) **Effort:** 8-12 hours **Risk:** Low (foundation already stable) --- ## Background ### What Are Sheets? Sheets are a way to organize components into top-level groups: - **Sheet Folders**: Top-level folders with names starting with `#` (e.g., `#CloudFunctions`, `#Pages`) - **Default Sheet**: All components not in a `#` folder - **Special Sheets**: Some sheets can be hidden (e.g., `__cloud__` sheet) ### Current State After TASK-004B completion, the React ComponentsPanel has: **✅ Working:** - Basic tree rendering with folders/components - Component selection and navigation - Expand/collapse folders - Basic context menus (Make Home, Rename, Duplicate, Delete) - Drag-drop for organizing components - Root folder transparency (no unnamed folder) **❌ Missing:** - "Create" submenus in context menus - Sheet selector UI (currently no way to see/switch sheets) - Sheet creation/deletion/rename - Visual indication of current sheet ### Legacy Implementation The legacy `ComponentsPanel.ts.legacy` shows: - Full context menu system with "Create" submenus - Sheet selector bar with tabs - Sheet management actions (add, rename, delete) - Sheet drag-drop support --- ## Goals 1. **Enhanced Context Menus** - Add "Create" submenus with all component types + folder 2. **Sheet Dropdown UI** - Replace legacy tab bar with modern dropdown selector 3. **Sheet Management** - Full create/rename/delete with undo support 4. **Sheet Filtering** - Show only components in selected sheet 5. **TypeScript Throughout** - Proper typing, no TSFixme --- ## Architecture ### Component Structure ``` ComponentsPanel/ ├── ComponentsPanelReact.tsx # Add sheet selector UI ├── components/ │ ├── ComponentTree.tsx # Enhance context menus │ ├── ComponentItem.tsx # Update menu items │ ├── FolderItem.tsx # Update menu items │ └── SheetSelector.tsx # NEW: Dropdown for sheets ├── hooks/ │ ├── useComponentsPanel.ts # Add sheet filtering │ ├── useComponentActions.ts # Add sheet actions │ └── useSheetManagement.ts # NEW: Sheet operations └── types.ts # Add sheet types ``` ### State Management **Sheet State (in useComponentsPanel):** - `currentSheet: ComponentsPanelFolder | null` - Active sheet - `sheets: ComponentsPanelFolder[]` - All available sheets - `selectSheet(sheet)` - Switch to a sheet - `filterBySheet(sheet)` - Filter tree to show only sheet components **Sheet Actions (in useSheetManagement):** - `createSheet(name)` - Create new sheet with undo - `renameSheet(sheet, newName)` - Rename sheet with undo - `deleteSheet(sheet)` - Delete sheet with confirmation + undo - `moveToSheet(item, targetSheet)` - Move component/folder to sheet --- ## Implementation Phases ### Phase 1: Enhanced Context Menus (2-3 hours) Add "Create" submenus to existing context menus. **Files to Modify:** - `components/ComponentItem.tsx` - Add "Create" submenu before divider - `components/FolderItem.tsx` - Add "Create" submenu before divider - `hooks/useComponentActions.ts` - Already has `handleAddComponent` and `handleAddFolder` **Tasks:** 1. **Check PopupMenu Submenu Support** - Read PopupMenu source to see if nested menus are supported - If not, may need to enhance PopupMenu or use alternative approach 2. **Add "Create" Submenu to Component Context Menu** - Position: After "Make Home", before "Rename" - Items: - Page (template) - Visual Component (template) - Logic Component (template) - Cloud Function (template) - Divider - Folder - Each item calls `handleAddComponent(template, parentPath)` 3. **Add "Create" Submenu to Folder Context Menu** - Same items as component menu - Parent path is folder path 4. **Wire Up Template Selection** - Get templates from `ComponentTemplates.instance.getTemplates()` - Filter by runtime type (browser vs cloud) - Pass correct parent path to popup **Success Criteria:** - [ ] Component right-click shows "Create" submenu - [ ] Folder right-click shows "Create" submenu - [ ] All 4 component templates + folder appear in submenu - [ ] Clicking template opens creation popup at correct path - [ ] All operations support undo/redo ### Phase 2: Sheet System Backend (2 hours) Implement sheet detection and filtering logic. **Files to Create:** - `hooks/useSheetManagement.ts` - Sheet operations hook **Files to Modify:** - `hooks/useComponentsPanel.ts` - Add sheet filtering **Tasks:** 1. **Sheet Detection in useComponentsPanel** ```typescript // Identify sheets from projectFolder.folders const sheets = useMemo(() => { const allSheets = [{ name: 'Default', folder: projectFolder, isDefault: true }]; projectFolder.folders .filter((f) => f.name.startsWith('#')) .forEach((f) => { allSheets.push({ name: f.name.substring(1), // Remove # prefix folder: f, isDefault: false }); }); // Filter out hidden sheets return allSheets.filter((s) => !hideSheets?.includes(s.name)); }, [projectFolder, hideSheets]); ``` 2. **Current Sheet State** ```typescript const [currentSheet, setCurrentSheet] = useState(() => { // Default to first non-hidden sheet return sheets[0] || null; }); ``` 3. **Sheet Filtering** ```typescript const filteredTreeData = useMemo(() => { if (!currentSheet) return treeData; if (currentSheet.isDefault) { // Show components not in any # folder return filterNonSheetComponents(treeData); } else { // Show only components in this sheet's folder return filterSheetComponents(treeData, currentSheet.folder); } }, [treeData, currentSheet]); ``` 4. **Create useSheetManagement Hook** - `createSheet(name)` - Create `#SheetName` folder - `renameSheet(sheet, newName)` - Rename folder with component path updates - `deleteSheet(sheet)` - Delete folder and all components (with confirmation) - All operations use `UndoQueue.pushAndDo()` pattern **Success Criteria:** - [ ] Sheets correctly identified from folder structure - [ ] Current sheet state maintained - [ ] Tree data filtered by selected sheet - [ ] Sheet CRUD operations with undo support ### Phase 3: Sheet Selector UI (2-3 hours) Create dropdown component for sheet selection. **Files to Create:** - `components/SheetSelector.tsx` - Dropdown component - `components/SheetSelector.module.scss` - Styles **Component Structure:** ```typescript interface SheetSelectorProps { sheets: Sheet[]; currentSheet: Sheet | null; onSelectSheet: (sheet: Sheet) => void; onCreateSheet: () => void; onRenameSheet: (sheet: Sheet) => void; onDeleteSheet: (sheet: Sheet) => void; } export function SheetSelector({ sheets, currentSheet, onSelectSheet, onCreateSheet, onRenameSheet, onDeleteSheet }: SheetSelectorProps) { // Dropdown implementation } ``` **UI Design:** ``` ┌─────────────────────────────────┐ │ Components ▼ [Default] │ ← Header with dropdown ├─────────────────────────────────┤ │ Click dropdown: │ │ ┌─────────────────────────────┐ │ │ │ ● Default │ │ │ │ Pages │ │ │ │ Components │ │ │ │ ──────────────── │ │ │ │ + Add Sheet │ │ │ └─────────────────────────────┘ │ └─────────────────────────────────┘ ``` **Tasks:** 1. **Create SheetSelector Component** - Button showing current sheet name with dropdown icon - Click opens dropdown menu - List of all sheets with selection indicator - "Add Sheet" button at bottom 2. **Sheet List Item with Actions** - Sheet name - Three-dot menu for rename/delete - Cannot delete "Default" sheet - Click sheet name to switch 3. **Integrate into ComponentsPanelReact** ```tsx
Components
``` 4. **Style the Dropdown** - Match existing ComponentsPanel styling - Smooth open/close animation - Proper z-index layering **Success Criteria:** - [ ] Dropdown button shows current sheet name - [ ] Clicking opens sheet list - [ ] Sheet list shows all non-hidden sheets - [ ] "Add Sheet" button at bottom - [ ] Three-dot menu on each sheet (except Default) - [ ] Clicking sheet switches view ### Phase 4: Sheet Management Actions (1-2 hours) Wire up all sheet management actions. **Files to Modify:** - `ComponentsPanelReact.tsx` - Wire up SheetSelector callbacks **Tasks:** 1. **Create Sheet Action** ```typescript const handleCreateSheet = useCallback(() => { const popup = new PopupLayer.StringInputPopup({ label: 'New sheet name', okLabel: 'Create', cancelLabel: 'Cancel', onOk: (name: string) => { if (!name || name.trim() === '') { ToastLayer.showError('Sheet name cannot be empty'); return; } createSheet(name); PopupLayer.instance.hidePopup(); } }); popup.render(); PopupLayer.instance.showPopup({ content: popup, position: 'center' }); }, [createSheet]); ``` 2. **Rename Sheet Action** - Show StringInputPopup with current name - Validate name (non-empty, unique) - Call `renameSheet()` from useSheetManagement - Update displays new name immediately (via ProjectModel events) 3. **Delete Sheet Action** - Show confirmation dialog with component count - Call `deleteSheet()` from useSheetManagement - Switch to Default sheet after deletion 4. **Drag-Drop Between Sheets** (Optional Enhancement) - Extend useDragDrop to support sheet boundaries - Allow dropping on sheet name in dropdown - Move component/folder to target sheet **Success Criteria:** - [ ] "Add Sheet" creates new sheet with undo - [ ] Rename sheet updates all component paths - [ ] Delete sheet removes folder and components - [ ] All operations show in undo history - [ ] UI updates immediately after operations ### Phase 5: Integration & Testing (1 hour) Final integration and comprehensive testing. **Tasks:** 1. **Update TASK-004B Documentation** - Mark as "Feature Complete" (not just "Complete") - Add reference to TASK-008 for sheet system 2. **Test All Menu Features** - [ ] Component context menu "Create" submenu works - [ ] Folder context menu "Create" submenu works - [ ] All templates create components at correct path - [ ] Folder creation from context menu works 3. **Test All Sheet Features** - [ ] Sheet dropdown displays correctly - [ ] Switching sheets filters component list - [ ] Creating sheet adds to dropdown - [ ] Renaming sheet updates dropdown and paths - [ ] Deleting sheet removes from dropdown 4. **Test Edge Cases** - [ ] Hidden sheets don't appear in dropdown - [ ] Locked sheet mode prevents switching (for Cloud Functions panel) - [ ] Empty sheets show correctly - [ ] Deleting last component in sheet folder 5. **Test Undo/Redo** - [ ] Create sheet → undo removes it - [ ] Rename sheet → undo reverts name - [ ] Delete sheet → undo restores it - [ ] Move to sheet → undo moves back **Success Criteria:** - [ ] All features working end-to-end - [ ] No console errors or warnings - [ ] Smooth UX with proper feedback - [ ] Documentation updated --- ## Technical Details ### PopupMenu Submenu Support The legacy implementation used nested PopupMenu items. Need to verify if current PopupMenu supports this: **Option A: Nested Menu Support** ```typescript { icon: IconName.Plus, label: 'Create', submenu: [ { icon: IconName.Page, label: 'Page', onClick: ... }, { icon: IconName.Component, label: 'Visual Component', onClick: ... }, // etc ] } ``` **Option B: Flat Menu with Dividers** ```typescript [ { icon: IconName.Plus, label: 'Create Page', onClick: ... }, { icon: IconName.Plus, label: 'Create Visual Component', onClick: ... }, { icon: IconName.Plus, label: 'Create Logic Component', onClick: ... }, { icon: IconName.Plus, label: 'Create Cloud Function', onClick: ... }, { type: 'divider' }, { icon: IconName.Plus, label: 'Create Folder', onClick: ... }, { type: 'divider' }, // existing items... ] ``` **Decision:** Check PopupMenu implementation first. If nested menus aren't supported, use Option B as it's simpler and still provides good UX. ### Sheet Folder Structure Sheets are implemented as top-level folders: ``` projectFolder (root) ├── #Pages/ ← Sheet: "Pages" │ ├── HomePage │ ├── AboutPage ├── #Components/ ← Sheet: "Components" │ ├── Header │ ├── Footer ├── #__cloud__/ ← Special hidden sheet │ ├── MyCloudFunction ├── App ← Default sheet ├── Settings ← Default sheet ``` **Key Points:** - Sheet names start with `#` in folder structure - Display names remove the `#` prefix - Default sheet = any component not in a `#` folder - Hidden sheets filtered by `hideSheets` option ### Sheet Filtering Algorithm ```typescript function filterBySheet(components, sheet) { if (sheet.isDefault) { // Show only components NOT in any sheet folder return components.filter((comp) => !comp.name.startsWith('/#')); } else { // Show only components in this sheet's folder const sheetPath = sheet.folder.getPath(); return components.filter((comp) => comp.name.startsWith(sheetPath)); } } ``` ### UndoQueue Pattern All sheet operations must use the proven pattern: ```typescript UndoQueue.instance.pushAndDo( new UndoActionGroup({ label: 'create sheet', do: () => { // Perform action }, undo: () => { // Revert action } }) ); ``` **NOT** the old broken pattern: ```typescript // ❌ DON'T DO THIS const undoGroup = new UndoActionGroup({ label: 'action' }); undoGroup.push({ do: ..., undo: ... }); UndoQueue.instance.push(undoGroup); undoGroup.do(); ``` --- ## Files to Modify ### Create (New) ``` packages/noodl-editor/src/editor/src/views/panels/ComponentsPanelNew/ ├── components/ │ ├── SheetSelector.tsx # NEW │ └── SheetSelector.module.scss # NEW └── hooks/ └── useSheetManagement.ts # NEW ``` ### Modify (Existing) ``` packages/noodl-editor/src/editor/src/views/panels/ComponentsPanelNew/ ├── ComponentsPanelReact.tsx # Add SheetSelector ├── components/ │ ├── ComponentItem.tsx # Enhance context menu │ └── FolderItem.tsx # Enhance context menu ├── hooks/ │ ├── useComponentsPanel.ts # Add sheet filtering │ └── useComponentActions.ts # Add sheet actions └── types.ts # Add Sheet types ``` --- ## Testing Checklist ### Context Menu Enhancements - [ ] Component right-click shows "Create" submenu - [ ] "Create" submenu shows all 4 templates + folder - [ ] Clicking template opens creation popup - [ ] Component created at correct path - [ ] Folder creation works from context menu - [ ] Folder right-click has same "Create" submenu - [ ] All operations support undo/redo ### Sheet Selector UI - [ ] Dropdown button appears in header - [ ] Dropdown shows current sheet name - [ ] Clicking opens sheet list - [ ] All non-hidden sheets appear in list - [ ] Current sheet has selection indicator - [ ] "Add Sheet" button at bottom - [ ] Three-dot menu on non-default sheets - [ ] Clicking sheet switches view ### Sheet Management - [ ] Create sheet opens input popup - [ ] New sheet appears in dropdown - [ ] Components filtered by selected sheet - [ ] Rename sheet updates name everywhere - [ ] Rename sheet updates component paths - [ ] Delete sheet shows confirmation - [ ] Delete sheet removes from dropdown - [ ] Delete sheet removes all components - [ ] Hidden sheets don't appear (e.g., **cloud**) ### Sheet Filtering - [ ] Default sheet shows non-sheet components - [ ] Named sheet shows only its components - [ ] Switching sheets updates tree immediately - [ ] Empty sheets show empty state - [ ] Component creation adds to current sheet ### Undo/Redo - [ ] Create sheet → undo removes it - [ ] Create sheet → undo → redo restores it - [ ] Rename sheet → undo reverts name - [ ] Delete sheet → undo restores sheet and components - [ ] Move to sheet → undo moves back ### Edge Cases - [ ] Cannot delete Default sheet - [ ] Cannot create sheet with empty name - [ ] Cannot create sheet with duplicate name - [ ] Locked sheet mode prevents switching - [ ] Hidden sheets stay hidden - [ ] Deleting last component doesn't break UI --- ## Risks & Mitigations ### Risk: PopupMenu doesn't support nested menus **Mitigation:** Use flat menu structure with dividers. Still provides good UX. ### Risk: Sheet filtering breaks component selection **Mitigation:** Test extensively. Ensure ProjectModel events update sheet view correctly. ### Risk: Sheet delete is destructive **Mitigation:** Show confirmation with component count. Make undo work perfectly. --- ## Success Criteria 1. **Context Menus Enhanced**: "Create" submenus with all templates work perfectly 2. **Sheet UI Complete**: Dropdown selector with all management features 3. **Sheet Operations**: Full CRUD with undo support 4. **Feature Parity**: All legacy sheet features now in React 5. **Clean Code**: TypeScript throughout, no TSFixme 6. **Documentation**: Updated task status, learnings captured --- ## Future Enhancements (Out of Scope) - Drag-drop between sheets (drag component onto sheet name) - Sheet reordering - Sheet color coding - Sheet icons - Keyboard shortcuts for sheet switching - Sheet search/filter --- ## Dependencies **Blocked by:** None (TASK-004B complete) **Blocks:** None (UX enhancement) --- ## References - **TASK-004B**: ComponentsPanel React Migration (predecessor) - **Legacy Implementation**: `ComponentsPanel.ts.legacy` - Complete reference - **Current React**: `ComponentsPanelReact.tsx` - Foundation to build on - **Templates**: `ComponentTemplates.ts` - Template system - **Actions**: `useComponentActions.ts` - Action patterns - **Undo Pattern**: `dev-docs/reference/UNDO-QUEUE-PATTERNS.md` --- ## Notes for Implementation ### PopupMenu Investigation Before starting Phase 1, check: 1. Does PopupMenu support nested menus? 2. If yes, what's the API? 3. If no, is it easy to add or should we use flat menus? File to check: `packages/noodl-editor/src/editor/src/views/PopupLayer/PopupMenu.tsx` ### Sheet State Management Consider using a custom hook `useSheetState()` to encapsulate: - Current sheet selection - Sheet list with filtering - Sheet switching logic - Persistence (if needed) This keeps ComponentsPanelReact clean and focused. ### Component Path Updates When renaming sheets, ALL components in that sheet need path updates. This is similar to folder rename. Use the same pattern: ```typescript const componentsInSheet = ProjectModel.instance.getComponents().filter((c) => c.name.startsWith(oldSheetPath)); componentsInSheet.forEach((comp) => { const relativePath = comp.name.substring(oldSheetPath.length); const newName = newSheetPath + relativePath; ProjectModel.instance.renameComponent(comp, newName); }); ``` ### Hidden Sheets The `hideSheets` option is important for panels like the Cloud Functions panel. It might show: - `hideSheets: ['__cloud__']` - Don't show cloud functions in main panel OR it might be locked to ONLY cloud functions: - `lockCurrentSheetName: '__cloud__'` - Only show cloud functions Both patterns should work seamlessly. --- _Last Updated: December 26, 2025_