SIMPLIFIED WIDGET ARCHITECTURE

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

Simplified Widget Architecture

Created: December 3, 2025 Status: 🎯 Proposed - Needs Review Goal: Simplify widget creation by making embeddables the atomic foundation


🎯 The Problem

The current widget system is confusing and complex:

  1. Multiple entry points: Widget Builder (Basic/Custom), Widget Manager, Variant Creator
  2. Unclear hierarchy: What's the difference between widget, variant, custom widget, embeddable?
  3. Broken variants: Core widgets don't show correct variants
  4. Disconnected from modules: Widgets and module embeddables feel like separate systems

💡 The Insight

"Start with embeddables as the atomic unit, then build widgets on top of them."

Embeddables are already defined in ModuleEmbeddableComponent - they ARE the smallest UI piece. Everything else should build upon them.


🏗️ Simplified Hierarchy

┌─────────────────────────────────────────────────────────────────┐
│                    ATOMIC LAYER (Foundation)                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   EMBEDDABLE COMPONENT                                          │
│   ├── Angular Standalone Component (.ts, .html, .scss)          │
│   ├── Input definitions (typed properties)                      │
│   ├── Output definitions (events)                               │
│   ├── Category (widget, form, list, card, etc.)                 │
│   └── Base styling                                              │
│                                                                 │
│   Example: ButtonEmbeddable, InputEmbeddable, CardEmbeddable    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                   CONFIGURATION LAYER (Widgets)                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   WIDGET = Embeddable + Configuration                           │
│   ├── References ONE embeddable                                 │
│   ├── Property Groups (organized input controls)                │
│   ├── Variants (saved configurations of the same embeddable)    │
│   ├── Default values                                            │
│   └── Persistence settings                                      │
│                                                                 │
│   Example: "Primary Button" = ButtonEmbeddable + blue styling   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                  COMPOSITION LAYER (Custom Widgets)             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   CUSTOM WIDGET = Multiple Embeddables + Layout + Logic         │
│   ├── Combines multiple embeddables                             │
│   ├── Defines layout/arrangement                                │
│   ├── Wires outputs to inputs (event connections)               │
│   └── Adds custom logic                                         │
│                                                                 │
│   Example: "Login Form" = InputEmbeddable + InputEmbeddable     │
│            + ButtonEmbeddable + validation logic                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    MODULE LAYER (Integration)                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   MODULE = Embeddables + Actions + State + Routes               │
│   ├── Declares embeddableComponents[] in manifest               │
│   ├── Defines public actions                                    │
│   ├── Manages state/collections                                 │
│   └── Provides routes                                           │
│                                                                 │
│   Example: UserManagementModule with UserCardEmbeddable,        │
│            UserListEmbeddable, UserFormEmbeddable               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

📐 Data Structures

1. Embeddable (Atomic Unit)

Already exists in ModuleEmbeddableComponent:

interface ModuleEmbeddableComponent {
  id: string;                       // 'button', 'input', 'card'
  componentType: Type<unknown>;     // Angular component class
  displayName: string;              // 'Button', 'Text Input'
  description?: string;
  category: 'widget' | 'form' | 'list' | 'card' | 'dashboard' | 'custom';
  inputs?: ModuleParameterMetadata[];  // Typed inputs
  outputs?: ModuleOutputMetadata[];    // Events
  thumbnail?: string;
}

2. Widget (Configuration Layer)

interface WidgetDefinition {
  id: string;                       // 'primary-button-widget'
  name: string;                     // 'Primary Button'
  description?: string;
  
  // REFERENCE to embeddable (not duplication!)
  embeddableId: string;             // 'button'
  
  // Configuration on top of embeddable
  propertyGroups: PropertyGroup[];  // Organized input controls
  defaultValues: Record<string, unknown>;
  
  // Variants are SAVED CONFIGURATIONS (not separate components)
  variants: WidgetVariant[];
  
  // Metadata
  icon?: string;
  thumbnail?: string;
  tags?: string[];
}

interface WidgetVariant {
  id: string;                       // 'variant-primary-blue'
  name: string;                     // 'Primary Blue'
  description?: string;
  thumbnail?: string;
  
  // Just the property values that differ from default
  propertyOverrides: Record<string, unknown>;
}

interface PropertyGroup {
  id: string;
  title: string;                    // 'Appearance', 'Behavior'
  icon?: string;
  collapsed?: boolean;
  
  // Which inputs from the embeddable belong here
  properties: string[];             // ['color', 'size', 'variant']
}

3. Custom Widget (Composition)

interface CustomWidgetDefinition {
  id: string;
  name: string;
  description?: string;
  
  // Composition of embeddables
  children: EmbeddableInstance[];
  
  // Layout configuration
  layout: {
    type: 'stack' | 'row' | 'grid' | 'absolute';
    gap?: number;
    padding?: number;
  };
  
  // Event wiring
  connections: EventConnection[];
  
  // Exposed inputs/outputs (what the custom widget exposes externally)
  inputs: ModuleParameterMetadata[];
  outputs: ModuleOutputMetadata[];
}

interface EmbeddableInstance {
  instanceId: string;               // Unique in this composition
  embeddableId: string;             // Reference to embeddable
  position?: { x: number; y: number };
  size?: { width: number; height: number };
  propertyValues: Record<string, unknown>;
}

interface EventConnection {
  from: { instanceId: string; output: string };
  to: { instanceId: string; input: string } | { action: string };
}

🛠️ Simplified Workflow

Creating an Embeddable (Foundation)

Who: Developers/Advanced Users Tool: Code or Visual Embeddable Builder Output: Angular component + metadata

// 1. Create the Angular component
@Component({
  selector: 'app-rating-stars',
  standalone: true,
  template: `<div class="stars">⭐</div>`,
})
export class RatingStarsComponent {
  @Input() value = 0;
  @Input() max = 5;
  @Input() readonly = false;
  @Output() valueChange = new EventEmitter<number>();
}

// 2. Define the embeddable metadata
const ratingEmbeddable: ModuleEmbeddableComponent = {
  id: 'rating-stars',
  componentType: RatingStarsComponent,
  displayName: 'Rating Stars',
  category: 'widget',
  inputs: [
    { name: 'value', type: 'number', defaultValue: 0 },
    { name: 'max', type: 'number', defaultValue: 5 },
    { name: 'readonly', type: 'boolean', defaultValue: false }
  ],
  outputs: [
    { name: 'valueChange', type: 'number' }
  ]
};

Creating a Widget (Configuration)

Who: Space builders Tool: Widget Builder (simplified) Output: Configured embeddable with variants

┌─────────────────────────────────────────────────────┐
│  CREATE WIDGET                                      │
├─────────────────────────────────────────────────────┤
│                                                     │
│  1. Select Base Embeddable:                         │
│     ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐               │
│     │ ★  │ │ 🔘 │ │ 📝 │ │ 📦 │               │
│     │Stars│ │Button│ │Input│ │ Card │               │
│     └─────┘ └─────┘ └─────┘ └─────┘               │
│                                                     │
│  2. Configure Properties:                           │
│     Max Stars: [5]                                  │
│     Readonly:  [ ] No                               │
│     Size:      [Medium ▾]                           │
│                                                     │
│  3. Save as Variant:                                │
│     [💾 Save "5-Star Rating"]                       │
│                                                     │
│  4. Create More Variants:                           │
│     • 5-Star Rating (default)                       │
│     • 10-Star Rating (max=10)                       │
│     • Read-Only Display (readonly=true)            │
│                                                     │
└─────────────────────────────────────────────────────┘

Creating a Custom Widget (Composition)

Who: Advanced space builders Tool: Custom Widget Composer Output: Composed widget from multiple embeddables

┌─────────────────────────────────────────────────────┐
│  COMPOSE CUSTOM WIDGET                              │
├─────────────────────────────────────────────────────┤
│                                                     │
│  ┌─────────────────────────────────────────────┐   │
│  │  Embeddables        │    Canvas             │   │
│  │  ─────────────      │    ───────            │   │
│  │  ★ Stars            │   ┌───────────────┐   │   │
│  │  📝 Input           │   │ Product Name  │   │   │
│  │  🖼️ Image           │   │ [Text Input]  │   │   │
│  │  📦 Card            │   ├───────────────┤   │   │
│  │                     │   │ [Image]       │   │   │
│  │  Drag to canvas →   │   │ 🖼️ Product    │   │   │
│  │                     │   ├───────────────┤   │   │
│  │                     │   │ Rating: ★★★★☆ │   │   │
│  │                     │   │ [Stars]       │   │   │
│  │                     │   └───────────────┘   │   │
│  └─────────────────────────────────────────────┘   │
│                                                     │
│  [💾 Save as "Product Card Widget"]                 │
│                                                     │
└─────────────────────────────────────────────────────┘

🔄 Module Integration

All widgets/embeddables integrate with modules via the manifest:

const productModule: ModuleManifest = {
  id: 'product-catalog',
  name: 'Product Catalog',
  version: '1.0.0',
  
  // Embeddables this module provides
  embeddableComponents: [
    productCardEmbeddable,      // Basic product card
    productListEmbeddable,      // List of products
    productFormEmbeddable,      // Edit product form
    ratingStarsEmbeddable,      // Rating widget
  ],
  
  publicActions: [...],
  routes: [...],
};

🎯 Benefits of This Architecture

  1. Clear Hierarchy: Embeddable → Widget → Custom Widget → Module
  2. No Duplication: Widgets REFERENCE embeddables, don't duplicate code
  3. Variants are Simple: Just saved property configurations
  4. Module-First: Everything aligns with ModuleEmbeddableComponent
  5. Composable: Custom widgets compose embeddables
  6. Type-Safe: Inputs/outputs are strongly typed

📋 Implementation Tasks

Phase 1: Core Embeddable Registry

Phase 2: Simplified Widget Builder

Phase 3: Custom Widget Composer

Phase 4: Variant System Fix


🚫 What We Remove

  1. Separate "widget types": Everything is an embeddable
  2. Duplicate registries: One registry for embeddables
  3. Complex "Basic vs Custom" modes: Just "Configure" vs "Compose"
  4. Code generation in Widget Builder: That's for modules

✅ What We Keep

  1. ModuleEmbeddableComponent: The atomic unit
  2. Property groups: For organizing inputs
  3. Variants: As saved configurations
  4. Module integration: Embeddables in manifests
  5. Visual composer: For custom widgets

Next Step: Review and approve this architecture, then begin Phase 1 implementation.