Responsive System

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

Widget Responsive System

Overview

The responsive system allows widgets to have different properties (position, size, styles) for each breakpoint. This facilitates the creation of interfaces that automatically adapt to different screen sizes without the need to duplicate widgets.

Components of the responsive system

The responsive system mainly consists of:

  1. ResponsiveWidgetProps: Directive that provides helper methods to access responsive properties.
  2. ResponsiveDraggable: Directive that implements fully responsive drag functionality.
  3. ResponsiveWidgetService: Service for manipulating responsive widget properties.

Data Structure

A responsive widget uses a special format for its properties:

// Non-responsive widget (classic format)
{
  position: { x: 100, y: 200 },
  size: { width: 300, height: 150 }
}

// Responsive widget
{
  position: {
    base: { x: 100, y: 200 },
    md: { x: 50, y: 100 },
    lg: { x: 20, y: 50 }
  },
  size: {
    base: { width: 300, height: 150 },
    md: { width: 500, height: 250 },
    lg: { width: 700, height: 350 }
  }
}

Breakpoints

The system defines a set of standard breakpoints:

Name Min Width Description
base 0px Base/mobile (default)
sm 640px Small screens
md 768px Medium screens (tablet)
lg 1024px Large screens (laptop)
xl 1280px Extra large screens (desktop)
2xl 1536px Extra extra large screens

Utility Functions

Getting Value for Current Breakpoint

import { ResponsiveWidgetService } from '@widgets-editor/core';

@Component({...})
export class MyComponent {
  constructor(private responsiveWidgetService: ResponsiveWidgetService) {}

  getWidgetSize(widget: Widget): Size {
    return this.responsiveWidgetService.getValueForBreakpoint(widget.size);
  }
}

Updating Value for Specific Breakpoint

updateWidgetPosition(widget: Widget, breakpoint: BreakpointKey, x: number, y: number): Widget {
  return this.responsiveWidgetService.setWidgetPropertyForBreakpoint(
    widget,
    'position',
    { x, y },
    breakpoint
  );
}

Integration with Directives

The responsive system is fully integrated with the widget directives, making it easy to work with responsive properties.

Example with ResponsiveWidgetProps

<div
  libResponsiveWidgetProps
  [widget]="widget"
  [activeBreakpoint]="currentBreakpoint"
  #widgetProps="responsiveWidgetProps"
>
  <div [ngStyle]="widgetProps.getComputedStyles()">Widget content</div>
</div>

Example with ResponsiveDraggable

<div
  libResponsiveDraggable
  [widget]="widget"
  [activeBreakpoint]="currentBreakpoint"
  (widgetChanged)="onWidgetChanged($event)"
>
  Draggable content
</div>

Full Example

Below is a complete example showing how to implement a responsive widget:

import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
  ResponsiveWidgetPropsDirective,
  ResponsiveDraggableDirective,
} from '@widgets-editor/shared-ui';
import { BreakpointKey, Widget } from '@widgets-editor/core';

@Component({
  selector: 'app-responsive-widget',
  standalone: true,
  imports: [CommonModule, ResponsiveWidgetPropsDirective, ResponsiveDraggableDirective],
  template: `
    <div
      class="widget-container"
      libResponsiveWidgetProps
      [widget]="widget"
      [activeBreakpoint]="activeBreakpoint"
      #widgetProps="responsiveWidgetProps"
      libResponsiveDraggable
      [widget]="widget"
      [activeBreakpoint]="activeBreakpoint"
      (widgetChanged)="onWidgetChanged($event)"
    >
      <div class="header">
        <span>{{ widget.name }}</span>
      </div>

      <div
        class="content"
        [ngStyle]="{
          'font-size': widgetProps.getStyleProp('fontSize') + 'px',
          color: widgetProps.getStyleProp('color'),
          'background-color': widgetProps.getStyleProp('backgroundColor')
        }"
      >
        {{ widget.content }}
      </div>
    </div>
  `,
  styles: [
    `
      .widget-container {
        position: absolute;
        border: 1px solid #ccc;
        border-radius: 4px;
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        overflow: hidden;
      }

      .header {
        padding: 8px;
        background-color: #f5f5f5;
        border-bottom: 1px solid #ddd;
        cursor: move;
      }

      .content {
        padding: 16px;
      }
    `,
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ResponsiveWidgetComponent {
  @Input() widget!: Widget;
  @Input() activeBreakpoint: BreakpointKey = 'base';
  @Output() widgetUpdate = new EventEmitter<Widget>();

  onWidgetChanged(updatedWidget: Widget): void {
    this.widget = updatedWidget;
    this.widgetUpdate.emit(updatedWidget);
  }
}

Switching Between Breakpoints

To enable users to switch between breakpoints in the editor, you can use the ResponsiveToolbarComponent:

import { Component, signal } from '@angular/core';
import { BreakpointKey } from '@widgets-editor/core';

@Component({
  selector: 'app-responsive-toolbar',
  standalone: true,
  template: `
    <div class="responsive-toolbar">
      <button
        *ngFor="let bp of breakpoints"
        [class.active]="bp === activeBreakpoint()"
        (click)="setActiveBreakpoint(bp)"
      >
        {{ bp }}
      </button>
    </div>
  `,
})
export class ResponsiveToolbarComponent {
  private _activeBreakpoint = signal<BreakpointKey>('base');
  activeBreakpoint = this._activeBreakpoint.asReadonly();

  breakpoints: BreakpointKey[] = ['base', 'sm', 'md', 'lg', 'xl', '2xl'];

  setActiveBreakpoint(bp: BreakpointKey): void {
    this._activeBreakpoint.set(bp);
    // Additional logic to update UI or notify other components
  }
}

Best Practices

  1. Always define base properties: Every responsive property should have a base value.
  2. Use relative units when possible: Prefer percentage widths for horizontal dimensions.
  3. Test on real devices: Always test your responsive layouts on actual devices.
  4. Minimize overrides: Only override properties that need to change at each breakpoint.
  5. Choose correct breakpoints: Focus on content adaptations, not specific device sizes.

Further Reading