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:
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 regionsTemplateConfig: Configuration for each templateFlexPreset: 5 nested content layouts (stack, row, cards, center, custom)
Template Definitions with exact Flutter mappings:
LAYOUT_TEMPLATES = { 'split-sidebar': { gridColumns: '300px 1fr', flutterStructure: 'Row(children: [Container(width: 300), Expanded(...)])', // ... } }Helper Functions:
createTemplateSection()- Root-level templatescreateFlexSection()- Nested contentcreateDefaultSection()- Backwards compatibility
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:
Split Sidebar
- Web: CSS Grid
300px 1fr - Flutter:
Row(children: [Container(width: 300), Expanded(...)]) - ✅ Sidebar fixed at 300px
- ✅ Main area flexes to fill
- Web: CSS Grid
Two Column Equal
- Web: CSS Grid
1fr 1fr - Flutter:
Row(children: [Expanded(flex: 1), Expanded(flex: 1)]) - ✅ Both columns take exactly 50%
- Web: CSS Grid
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
- Web: CSS Grid
Nested Content
- Template region contains flexbox sections
- ✅ Template → Flex nesting works
- ✅ JSON serialization round-trip
- ✅ Visual match between Web & Flutter
📋 Implementation Checklist
Web (Angular)
- Update
canvas-builder.component.tswith template mode - Create
canvas-template-renderer.component.ts - Update
layout-selector.component.tsto use LAYOUT_TEMPLATES - Update
canvas-section.component.htmlto detect template vs flex - Add CSS Grid styles for templates
- Test all 6 templates in browser
- Verify persistence (save/load)
- Verify undo/redo with templates
Flutter (Separate Task)
- Create Dart model classes
- Create
TemplateRendererwidget - Create
FlexRendererwidget - Implement parsing helpers (parseWidth, parseGap, etc.)
- Test all 6 templates on iOS/Android
- Verify JSON deserialization
- Visual comparison Web vs Flutter
Documentation
- API documentation for each template
- Flutter implementation guide
- Migration guide (old preset system → new template system)
- AI training 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