IMPLEMENTATION ROADMAP

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Canvas V3 Platform-Agnostic Implementation Roadmap

✅ Phase 1: Type System & Architecture (COMPLETED)

What We Built:

  1. Comprehensive Type Definitions (canvas.types.ts)

    • LayoutTemplate: 6 root-level templates (split-sidebar, header-content-footer, dashboard, 2-col, 3-col, custom)
    • TemplateRegion: Grid areas with named regions
    • TemplateConfig: Configuration for each template
    • FlexPreset: 5 nested content layouts (stack, row, cards, center, custom)
  2. Template Definitions with exact Flutter mappings:

    LAYOUT_TEMPLATES = {
      'split-sidebar': {
        gridColumns: '300px 1fr',
        flutterStructure: 'Row(children: [Container(width: 300), Expanded(...)])',
        // ...
      }
    }
    
  3. Helper Functions:

    • createTemplateSection() - Root-level templates
    • createFlexSection() - Nested content
    • createDefaultSection() - Backwards compatibility
  4. Documentation:

    • PLATFORM_AGNOSTIC_LAYOUTS.md - Complete platform mapping guide
    • Web (CSS Grid) ↔ Flutter (Row/Column/Expanded) equivalents

⏳ Phase 2: Web Implementation (NEXT)

Tasks:

1. Update Canvas Builder Component

File: canvas-builder.component.ts

// Add template mode
readonly canvasMode = signal<'select' | 'template'>('select');

// Update root section creation
initializeCanvas(): void {
  const root = createTemplateSection(
    'root',
    'Root Container',
    'two-column-equal' // Default template
  );
  this.rootSection.set(root);
}

2. Create Template Renderer Component

File: canvas-template-renderer.component.ts

@Component({
  selector: 'lib-canvas-template-renderer',
  template: `
    <div class="template-container" [ngStyle]="templateStyles()">
      @for (region of section().regions; track region.name) {
        <div 
          class="template-region"
          [attr.data-region]="region.name"
          [ngStyle]="regionStyles(region)">
          
          <!-- Nested content inside region -->
          @for (child of region.children; track child.id) {
            <lib-canvas-section [section]="child" />
          }
        </div>
      }
    </div>
  `
})
export class CanvasTemplateRendererComponent {
  readonly section = input.required<CanvasSection>();
  
  readonly templateStyles = computed(() => {
    const section = this.section();
    const template = LAYOUT_TEMPLATES[section.template!];
    
    return {
      display: 'grid',
      gridTemplateColumns: template.gridColumns,
      gridTemplateRows: template.gridRows,
      gridTemplateAreas: template.gridAreas,
      gap: section.templateConfig?.gap || '0px',
      width: '100%',
      minHeight: '100vh'
    };
  });
  
  regionStyles(region: TemplateRegion): any {
    return {
      gridArea: region.name,
      width: region.width || 'auto',
      height: region.height || 'auto'
    };
  }
}

3. Update Visual Layout Selector

File: layout-selector.component.ts

// Change from LAYOUT_PRESETS to LAYOUT_TEMPLATES
readonly templateOptions = Object.entries(LAYOUT_TEMPLATES).map(([key, value]) => ({
  id: key as LayoutTemplate,
  name: value.name,
  description: value.description,
  icon: value.icon,
  responsive: value.responsive,
  preview: value.gridAreas // Use for SVG preview
}));

4. Update Canvas Section Component

File: canvas-section.component.html

<!-- Template mode: render grid -->
@if (section().layout === 'template') {
  <lib-canvas-template-renderer [section]="section()" />
}

<!-- Flexbox mode: render children -->
@else {
  <div class="section-children-container" [ngStyle]="childrenContainerStyles()">
    <!-- ... existing flexbox logic ... -->
  </div>
}

⏳ Phase 3: Flutter Implementation

Tasks:

1. Create Flutter Model Classes

File: lib/models/canvas_section.dart

enum LayoutTemplate {
  splitSidebar,
  headerContentFooter,
  dashboard,
  twoColumnEqual,
  threeColumnEqual,
  custom
}

class CanvasSection {
  final String id;
  final String label;
  final SectionType type;
  final LayoutType layout;
  
  // Template properties
  final LayoutTemplate? template;
  final TemplateConfig? templateConfig;
  final List<TemplateRegion>? regions;
  
  // Flex properties
  final FlexPreset? preset;
  final FlexConfig? flex;
  
  // Content
  final List<CanvasSection> children;
  final ContentConfig? content;
  
  CanvasSection({
    required this.id,
    required this.label,
    required this.type,
    required this.layout,
    this.template,
    this.templateConfig,
    this.regions,
    this.preset,
    this.flex,
    required this.children,
    this.content,
  });
  
  factory CanvasSection.fromJson(Map<String, dynamic> json) {
    // Parse JSON from web
  }
}

2. Create Template Renderer Widget

File: lib/widgets/template_renderer.dart

class TemplateRenderer extends StatelessWidget {
  final CanvasSection section;
  
  const TemplateRenderer({required this.section});
  
  @override
  Widget build(BuildContext context) {
    switch (section.template) {
      case LayoutTemplate.splitSidebar:
        return _buildSplitSidebar();
      case LayoutTemplate.headerContentFooter:
        return _buildHeaderContentFooter();
      case LayoutTemplate.twoColumnEqual:
        return _buildTwoColumnEqual();
      // ... other templates
      default:
        return Container();
    }
  }
  
  Widget _buildSplitSidebar() {
    final sidebar = section.regions?.firstWhere((r) => r.name == 'sidebar');
    final main = section.regions?.firstWhere((r) => r.name == 'main');
    
    return Row(
      children: [
        Container(
          width: parseWidth(section.templateConfig?.sidebarWidth ?? '300px'),
          child: _buildRegion(sidebar),
        ),
        Expanded(
          child: _buildRegion(main),
        ),
      ],
    );
  }
  
  Widget _buildTwoColumnEqual() {
    final left = section.regions?[0];
    final right = section.regions?[1];
    
    return Row(
      children: [
        Expanded(flex: 1, child: _buildRegion(left)),
        SizedBox(width: parseGap(section.templateConfig?.gap ?? '16px')),
        Expanded(flex: 1, child: _buildRegion(right)),
      ],
    );
  }
  
  Widget _buildRegion(TemplateRegion? region) {
    if (region == null) return Container();
    
    // Render nested content inside region
    return Column(
      children: region.children.map((child) {
        return FlexRenderer(section: child); // Nested flexbox content
      }).toList(),
    );
  }
}

3. Create Flex Renderer Widget

File: lib/widgets/flex_renderer.dart

class FlexRenderer extends StatelessWidget {
  final CanvasSection section;
  
  const FlexRenderer({required this.section});
  
  @override
  Widget build(BuildContext context) {
    final flex = section.flex;
    if (flex == null) return Container();
    
    final children = section.children.map((child) {
      return FlexRenderer(section: child); // Recursive
    }).toList();
    
    if (flex.direction == 'column') {
      return Column(
        mainAxisAlignment: parseJustifyContent(flex.justifyContent),
        crossAxisAlignment: parseAlignItems(flex.alignItems),
        children: children,
      );
    } else {
      return Row(
        mainAxisAlignment: parseJustifyContent(flex.justifyContent),
        crossAxisAlignment: parseAlignItems(flex.alignItems),
        children: children,
      );
    }
  }
}

⏳ Phase 4: Testing & Validation

Test Cases:

  1. Split Sidebar

    • Web: CSS Grid 300px 1fr
    • Flutter: Row(children: [Container(width: 300), Expanded(...)])
    • ✅ Sidebar fixed at 300px
    • ✅ Main area flexes to fill
  2. Two Column Equal

    • Web: CSS Grid 1fr 1fr
    • Flutter: Row(children: [Expanded(flex: 1), Expanded(flex: 1)])
    • ✅ Both columns take exactly 50%
  3. Header/Content/Footer

    • Web: CSS Grid auto 1fr auto
    • Flutter: Column(children: [header, Expanded(child: content), footer])
    • ✅ Header auto height
    • ✅ Content fills remaining
    • ✅ Footer auto height
  4. Nested Content

    • Template region contains flexbox sections
    • ✅ Template → Flex nesting works
    • ✅ JSON serialization round-trip
    • ✅ Visual match between Web & Flutter

📋 Implementation Checklist

Web (Angular)

Flutter (Separate Task)

Documentation


🎯 Success Criteria

✅ 1:1 Parity: Same JSON produces identical layouts on Web and Flutter
✅ Type-Safe: Full TypeScript/Dart type coverage
✅ Performant: No layout jank, smooth rendering
✅ Intuitive: Visual template selector, no code required
✅ Maintainable: Single source of truth (LAYOUT_TEMPLATES)
✅ Extensible: Easy to add new templates


Next Immediate Step: Implement Phase 2 (Web) - Start with canvas-template-renderer.component.ts