Files
OpenNoodl/dev-docs/tasks/phase-2/TASK-004B-componentsPanel-react-migration/NOTES.md
2025-12-23 09:39:33 +01:00

6.0 KiB

TASK-005 Working Notes

  • 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 panels
  • views/panels/SearchPanel/SearchPanel.tsx - Modern React panel example
  • views/panels/VersionControlPanel/VersionControlPanel.tsx - Another React panel
  • views/PopupLayer/PopupMenu.tsx - Context menu component

Key Decisions

Decision 1: State Management Approach

Options considered:

  1. useState + useEffect for ProjectModel subscription
  2. useModernModel hook (existing pattern)
  3. 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:

  1. Reuse ComponentsPanelFolder class
  2. Create new TreeNode interface
  3. Flat array with parent references

Decision: [TBD during implementation]

Reasoning: [TBD]


Decision 3: Drag-Drop Implementation

Options considered:

  1. Native HTML5 drag-drop with PopupLayer
  2. @dnd-kit library
  3. 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 nodeGraphEditor reference 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:

  1. Create React component with same props
  2. Use useModernModel for model subscriptions
  3. Replace data-click handlers with onClick props
  4. Replace data-class bindings with conditional classNames
  5. Replace $(selector) queries with refs or state
  6. Port CSS to CSS modules

Location: Sidebar panels


Pattern: [TBD]

Context: [TBD during implementation]

Pattern: [TBD]

Location: [TBD]