Variant System Implementation Roadmap

Step-by-step guide to implement the variant management system

Variant System Implementation Roadmap

Quick implementation guide for the variant management system.


🎯 Goal

Transform the widget gallery/demo into a powerful variant creation and management system where:


📦 Phase 1: Variant Storage Service (Foundation)

1.1 Create Variant Storage Service

File: libs/libs-widgets/widgets-manager/src/lib/services/variant-storage.service.ts

@Injectable({ providedIn: 'root' })
export class VariantStorageService {
  private storage = inject(GgStorage);
  private logger = inject(LoggerService);
  
  /**
   * Get all variants for a widget type (merged: global + universe + space)
   */
  async getAllVariants(params: {
    widgetType: string;
    universeId: string;
    spaceId?: string;
  }): Promise<WidgetVariant[]> {
    const variants: WidgetVariant[] = [];
    
    // 1. Global variants (from registry)
    const globalVariants = CORE_VARIANTS_REGISTRY[params.widgetType] || [];
    variants.push(...globalVariants);
    
    // 2. Universe variants
    const universeKey = `universes/${params.universeId}/widgets/variants/${params.widgetType}`;
    const universeVariants = await this.storage.get<WidgetVariant[]>(universeKey) || [];
    variants.push(...universeVariants);
    
    // 3. Space variants (if provided)
    if (params.spaceId) {
      const spaceKey = `spaces/${params.spaceId}/widgets/variants/${params.widgetType}`;
      const spaceVariants = await this.storage.get<WidgetVariant[]>(spaceKey) || [];
      variants.push(...spaceVariants);
    }
    
    return variants;
  }
  
  /**
   * Save a new variant (user-created only)
   */
  async saveVariant(variant: WidgetVariant): Promise<void> {
    if (variant.author === 'gograb-team') {
      throw new Error('Cannot modify global variants');
    }
    
    const key = variant.scope === 'universe'
      ? `universes/${variant.universeId}/widgets/variants/${variant.baseWidget}`
      : `spaces/${variant.spaceId}/widgets/variants/${variant.baseWidget}`;
    
    // Get existing variants
    const existingVariants = await this.storage.get<WidgetVariant[]>(key) || [];
    
    // Check for duplicate ID
    const index = existingVariants.findIndex(v => v.id === variant.id);
    if (index >= 0) {
      // Update existing
      existingVariants[index] = variant;
    } else {
      // Add new
      existingVariants.push(variant);
    }
    
    // Save back
    await this.storage.put(key, existingVariants);
    
    this.logger.info('[VariantStorage] Variant saved', { variantId: variant.id, scope: variant.scope });
  }
  
  /**
   * Delete a variant (user-created only)
   */
  async deleteVariant(params: {
    variantId: string;
    widgetType: string;
    scope: 'universe' | 'space';
    universeId?: string;
    spaceId?: string;
  }): Promise<void> {
    const key = params.scope === 'universe'
      ? `universes/${params.universeId}/widgets/variants/${params.widgetType}`
      : `spaces/${params.spaceId}/widgets/variants/${params.widgetType}`;
    
    const variants = await this.storage.get<WidgetVariant[]>(key) || [];
    const filtered = variants.filter(v => v.id !== params.variantId);
    
    await this.storage.put(key, filtered);
    
    this.logger.info('[VariantStorage] Variant deleted', { variantId: params.variantId });
  }
}

1.2 Define Variant Types

File: libs/libs-widgets/widgets-manager/src/lib/types/widget-variant.types.ts

export interface WidgetVariant {
  // Identity
  id: string;
  baseWidget: string; // 'button', 'input', 'card', etc.
  name: string;
  description: string;
  
  // Ownership & Scoping
  author: 'gograb-team' | string; // userId
  scope: 'global' | 'universe' | 'space';
  universeId?: string;
  spaceId?: string;
  
  // Category
  category: 'functional' | 'style' | 'layout' | 'theme';
  
  // Configuration
  config: {
    properties: Record<string, any>;
    styles?: Record<string, string>; // CSS variables
    template?: string;
  };
  
  // Metadata
  preview?: string; // HTML preview
  thumbnail?: string;
  tags?: string[];
  isPublic: boolean;
  isFavorite?: boolean;
  
  // Analytics
  usageCount?: number;
  lastUsedAt?: Date;
  
  // Timestamps
  createdAt: Date;
  updatedAt: Date;
}

📚 Phase 2: Global Variants Registry

2.1 Create Core Variants Registry

File: libs/libs-widgets/widgets/src/lib/registries/core-variants.registry.ts

Define all GoGrab team global variants for each Core UI widget.

import { WidgetVariant } from '@libs-widgets/widgets-manager/types';

// Button variants
const BUTTON_VARIANTS: WidgetVariant[] = [
  {
    id: 'primary',
    baseWidget: 'button',
    name: 'Primary Button',
    description: 'Main call-to-action with emphasis',
    author: 'gograb-team',
    scope: 'global',
    category: 'style',
    config: {
      properties: { variant: 'primary', size: 'medium' },
      styles: {
        '--btn-bg': 'var(--ux-primary)',
        '--btn-color': 'var(--ux-on-primary)'
      }
    },
    preview: '<button class="btn btn-primary">Click Me</button>',
    tags: ['cta', 'primary'],
    isPublic: true,
    createdAt: new Date('2025-01-01'),
    updatedAt: new Date('2025-01-01')
  },
  // ... more variants
];

// Input variants
const INPUT_VARIANTS: WidgetVariant[] = [
  {
    id: 'text',
    baseWidget: 'input',
    name: 'Text Input',
    description: 'Standard text input',
    author: 'gograb-team',
    scope: 'global',
    category: 'functional',
    config: {
      properties: { type: 'text', placeholder: 'Enter text...' }
    },
    preview: '<input type="text" class="form-input">',
    tags: ['text'],
    isPublic: true,
    createdAt: new Date('2025-01-01'),
    updatedAt: new Date('2025-01-01')
  },
  // ... email, password, number, etc.
];

// Export registry
export const CORE_VARIANTS_REGISTRY: Record<string, WidgetVariant[]> = {
  button: BUTTON_VARIANTS,
  input: INPUT_VARIANTS,
  select: [], // TODO
  checkbox: [], // TODO
  card: [], // TODO
  // ... etc
};

🎨 Phase 3: Variant Creator Component

3.1 Create Variant Creator Modal

File: libs/libs-widgets/widget-builder/src/lib/components/variant-creator/variant-creator.component.ts

Modal component for creating/editing variants with live preview.

Features:

Key Methods:


🎭 Phase 4: Update Core Component Detail Page

4.1 Add Variant Management to Component Detail

File: libs/libs-widgets/widget-showcase/src/lib/components/core-component-detail/core-component-detail.component.ts

Add:

  1. Variant storage service injection
  2. Load variants (global + user's) on init
  3. Separate sections: "GoGrab Variants" and "My Variants"
  4. "Create New Variant" button → Opens variant creator
  5. Variant actions: Edit, Duplicate, Delete, Favorite
  6. Click variant → Show in live preview

Template changes:

<!-- GoGrab Team Variants -->
<section class="variants-section">
  <h2>🌐 GoGrab Team Variants</h2>
  <div class="variants-grid">
    @for (variant of globalVariants(); track variant.id) {
      <div class="variant-card" (click)="selectVariant(variant.id)">
        <!-- ... variant display ... -->
      </div>
    }
  </div>
</section>

<!-- User's Custom Variants -->
<section class="variants-section">
  <div class="section-header">
    <h2>🎨 My Custom Variants</h2>
    <button class="btn-create" (click)="createNewVariant()">
      <span class="material-icons">add</span>
      Create New Variant
    </button>
  </div>
  
  <div class="variants-grid">
    @for (variant of userVariants(); track variant.id) {
      <div class="variant-card" (click)="selectVariant(variant.id)">
        <!-- ... variant display ... -->
        <div class="variant-actions">
          <button (click)="editVariant(variant.id)">✏️</button>
          <button (click)="duplicateVariant(variant.id)">📋</button>
          <button (click)="deleteVariant(variant.id)">🗑️</button>
          <button (click)="toggleFavorite(variant.id)">
            {{ variant.isFavorite ? '⭐' : '☆' }}
          </button>
        </div>
      </div>
    }
    
    @if (userVariants().length === 0) {
      <div class="empty-state">
        <p>No custom variants yet. Create your first one!</p>
      </div>
    }
  </div>
</section>

🎮 Phase 5: Widget Gallery as Playground

5.1 Transform Widget Demo into Interactive Playground

File: libs/libs-widgets/widget-showcase/src/lib/components/widget-demo/widget-demo.component.ts

Current: Static demo showing widget examples

New: Interactive playground for testing and creating variants

Features:

  1. Variant selector dropdown (try different variants)
  2. Live property editor panel
  3. Style customization panel
  4. "Save as New Variant" button
  5. Real-time preview updates
  6. Export code for current configuration

Layout:

┌─────────────────────────────────────────────────┐
│ 🎮 Button Widget Playground                     │
├──────────────────┬──────────────────────────────┤
│ Configuration    │   Live Preview               │
│                  │                              │
│ Variant:         │   ┌────────────────┐         │
│ [Primary ▼]      │   │   Click Me     │         │
│                  │   └────────────────┘         │
│ Properties:      │                              │
│ Size: [Medium▼]  │   Breakpoint: [Desktop ▼]   │
│ Text: [______]   │   Zoom: [100% ▼]            │
│                  │                              │
│ Styles:          │   [Copy Code]               │
│ BG: [#007bff]🎨  │   [Save as Variant]         │
│ Color: [#fff]🎨  │                              │
│                  │                              │
│ [Reset]          │                              │
└──────────────────┴──────────────────────────────┘

🔗 Phase 6: Widget Builder Integration

6.1 Add Variant Picker to Component Palette

When adding Core UI components to widget builder, show variant picker.

File: libs/libs-widgets/widget-builder/src/lib/components/component-palette/component-palette.component.ts

Add:


✅ Implementation Checklist

Foundation

UI Components

Core Component Detail Updates

Widget Gallery/Demo Updates

Widget Builder Integration

Testing & Polish

Documentation


🎯 Priority Order

Session 1 (Foundation):

  1. Create variant storage service
  2. Define variant types
  3. Create core variants registry (button + input)
  4. Test storage/retrieval

Session 2 (Variant Creator):

  1. Build variant creator component
  2. Add to core component detail page
  3. Test creation flow

Session 3 (Gallery Playground):

  1. Transform widget demo into playground
  2. Add interactive controls
  3. Add "Save as Variant"

Session 4 (Integration & Polish):

  1. Widget builder integration
  2. Testing across all flows
  3. Polish UI/UX
  4. Documentation

🚀 Quick Start

To begin implementation:

# 1. Create variant storage service
cd libs/libs-widgets/widgets-manager/src/lib/services
# Create variant-storage.service.ts

# 2. Create variant types
cd ../types
# Create widget-variant.types.ts

# 3. Create core variants registry
cd ../../../widgets/src/lib/registries
# Create core-variants.registry.ts

# 4. Start implementing!

Ready to transform widgets into a Ferrari-level reusable component system! 🏎️✨