README
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
Widget System
Infraestructura UI para sistema de widgets genéricos embeddables
📋 Overview
@gograb-space/widget-system proporciona la infraestructura de UI para renderizar y gestionar widgets embeddables en GoGrab Space. Este sistema se integra completamente con la arquitectura existente sin duplicar funcionalidad.
🎯 Propósito
- Grid layout responsive para widgets con posicionamiento dinámico
- Error boundaries para aislamiento de fallos en widgets
- Base components opcionales con helpers de integración
- Lifecycle management estandarizado para widgets
- Loading/error states consistentes
⚠️ Importante: Integración con Infraestructura Existente
Este sistema NO duplica funcionalidad existente. Usa:
- ✅
ResolvedWidgetConfig(de@gograb-space/module-config) - Configuración de widgets - ✅
WidgetRegistryEntry(de@gograb-space/module-config) - Metadata de widgets - ✅
ModuleEmbeddableComponent(de@gograb-space/module-base) - Contratos de widgets - ✅
ComponentRegistryService(de@gograb-space/module-base) - Registry de components - ✅
AppConfigService(de@gograb-space/module-config) - Dashboard config - ✅
EventBusService(de@gograb-space/event-system) - Comunicación entre widgets
🏗️ Arquitectura
┌─────────────────────────────────────────────────────────┐
│ EXISTING (No Changes) │
├─────────────────────────────────────────────────────────┤
│ module-base → ModuleEmbeddableComponent │
│ module-config → ResolvedWidgetConfig, WidgetRegistryEntry│
│ event-system → EventBusService │
└─────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ widget-system (NEW - UI Infrastructure) │
├─────────────────────────────────────────────────────────┤
│ • WidgetGridComponent (grid layout) │
│ • WidgetContainerComponent (error boundary) │
│ • BaseWidgetComponent (optional helpers) │
│ • WidgetLayoutService (responsive breakpoints) │
└─────────────────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ gograb-admin (Usage) │
├─────────────────────────────────────────────────────────┤
│ <lib-gs-widget-grid [widgets]="enabledWidgets()" /> │
└─────────────────────────────────────────────────────────┘
📦 Componentes Principales
WidgetGridComponent
Grid layout responsive que renderiza widgets usando CSS Grid.
<lib-gs-widget-grid
[widgets]="enabledWidgets()"
[gridConfig]="gridConfig" />
Features:
- CSS Grid layout con columnas configurables
- Responsive breakpoints (mobile/tablet/desktop)
- Usa
position: {x, y, width, height}deResolvedWidgetConfig
WidgetContainerComponent
Wrapper que proporciona error boundary y loading states.
<lib-gs-widget-container [widgetConfig]="widget" />
Features:
- Error boundary (widget errors no rompen dashboard)
- Loading placeholder durante carga
- Error display con retry capability
BaseWidgetComponent (Opcional)
Clase base abstracta para widgets con helpers de integración.
@Component({...})
export class MyWidget extends BaseWidgetComponent {
onWidgetInit(): void {
// Widget initialization
}
}
Features:
- Helpers para event-system integration
- Lifecycle hooks estandarizados
- Error handling helpers
🚀 Quick Start
1. Instalación
# widget-system será una librería Nx
npx nx g @nx/angular:lib widget-system --directory=libs/core
2. Importar en Dashboard
import { WidgetGridComponent } from '@libs-core/widget-system';
@Component({
imports: [WidgetGridComponent],
template: `
<lib-gs-widget-grid [widgets]="enabledWidgets()" />
`
})
export class DashboardComponent {
private appConfig = inject(AppConfigService);
enabledWidgets = computed(() =>
this.appConfig.dashboardConfig()?.widgets ?? []
);
}
3. Crear Widget (Opcional: Extender Base)
import { BaseWidgetComponent } from '@libs-core/widget-system';
@Component({
selector: 'my-custom-widget',
standalone: true
})
export class CustomWidgetComponent extends BaseWidgetComponent {
onWidgetInit(): void {
// Subscribe to events
this.subscribeToWidgetEvent('data.updated', (data) => {
this.handleDataUpdate(data);
});
}
onWidgetDestroy(): void {
// Cleanup handled by DestroyRef
}
}
📝 Usage Example
Complete Dashboard Integration
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [WidgetGridComponent],
template: `
<lib-gs-widget-grid
[widgets]="enabledWidgets()"
[gridConfig]="gridConfig" />
`
})
export class DashboardComponent {
private appConfig = inject(AppConfigService);
// Grid configuration
gridConfig: GridConfig = {
columns: 12,
gap: 16,
rowHeight: 100,
breakpoints: {
mobile: { columns: 1, gap: 8, rowHeight: 'auto' },
tablet: { columns: 6, gap: 12, rowHeight: 100 },
desktop: { columns: 12, gap: 16, rowHeight: 100 }
}
};
// Widgets from AppConfigService (ResolvedWidgetConfig[])
enabledWidgets = computed(() =>
this.appConfig.dashboardConfig()?.widgets.filter(w => w.enabled) ?? []
);
}
🔧 Configuration
GridConfig
interface GridConfig {
columns: number; // Base columns (e.g., 12)
gap: number; // Gap between widgets (px)
rowHeight: number | 'auto'; // Row height
breakpoints: {
mobile: GridBreakpointConfig;
tablet: GridBreakpointConfig;
desktop: GridBreakpointConfig;
};
}
Widget Positioning (from ResolvedWidgetConfig)
// Widgets ya tienen position en ResolvedWidgetConfig
interface ResolvedWidgetConfig {
position: {
x: number; // Grid column start (0-indexed)
y: number; // Grid row start (0-indexed)
width: number; // Span columns
height: number; // Span rows
};
}
🎨 Styling
Widget-system usa design tokens de @gograb-space/ui-framework:
.widget-container {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--spacing-md);
}
🧪 Testing
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { WidgetGridComponent } from '@libs-core/widget-system';
describe('WidgetGridComponent', () => {
let component: WidgetGridComponent;
let fixture: ComponentFixture<WidgetGridComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [WidgetGridComponent]
}).compileComponents();
fixture = TestBed.createComponent(WidgetGridComponent);
component = fixture.componentInstance;
});
it('should render enabled widgets', () => {
const mockWidgets: ResolvedWidgetConfig[] = [
{
id: 'widget-1',
widgetTypeId: 'user-stats',
displayName: 'User Stats',
position: { x: 0, y: 0, width: 4, height: 2 },
enabled: true,
componentType: MockWidgetComponent
}
];
component.widgets = signal(mockWidgets);
fixture.detectChanges();
expect(component.enabledWidgets().length).toBe(1);
});
});
📚 Documentation
- Requirements - Functional and non-functional requirements
- Design - Architecture and technical decisions
- Tasks - Implementation roadmap
🔗 Related Libraries
@gograb-space/module-base- Module contracts and embeddable component definitions@gograb-space/module-config- Widget configuration and dashboard config@gograb-space/event-system- Widget communication@gograb-space/ui-framework- Design tokens and base components@gograb-space/log-system- Logging infrastructure
📦 Dependencies
{
"dependencies": {
"@gograb-space/module-base": "workspace:*",
"@gograb-space/module-config": "workspace:*",
"@gograb-space/event-system": "workspace:*",
"@gograb-space/log-system": "workspace:*",
"@gograb-space/utilities": "workspace:*",
"@angular/cdk": "^20.x"
}
}
🚀 Roadmap
MVP (Current)
- ✅ Grid layout component
- ✅ Widget container with error boundary
- ✅ Responsive breakpoints
- ✅ Base widget component (optional)
Post-MVP
- ⏳ Drag-and-drop rearrangement (Angular CDK)
- ⏳ Widget resize handles
- ⏳ Widget maximize/fullscreen
- ⏳ Lazy loading optimization
- ⏳ Widget templates/presets
📄 License
Proprietary - GoGrab Team