Responsive Widgets Implementation Guide

Complete guide for adapting widgets to work with the responsive system

Responsive Widgets Implementation Guide

This document describes how to adapt existing widgets to work with the new responsive system.

Overview

The responsive system allows widgets to have different properties (position, size, styles) for each breakpoint. To facilitate this adaptation, we have created a ResponsiveWidgetPropsDirective that provides helper methods to access these properties.

Steps to Adapt a Widget

1. Import Required Components

In the widget component (.ts file):

import {
  ResponsiveWidgetPropsDirective,
  // Other required directives...
} from 'libs/shared-ui/src/lib/directives';
import {
  BreakpointKey,
  ResponsiveWidgetService,
  // Other models and services...
} from '@libs-foundational/core-models'; 

2. Add the Directive to Component Imports

@Component({
  // ...
  imports: [
    // Existing imports...
    ResponsiveWidgetPropsDirective,
  ],
  // ...
})

3. Add the activeBreakpoint Input

export class MyWidgetComponent implements OnInit {
  // Existing inputs...
  @Input() activeBreakpoint: BreakpointKey = 'base';

  // Rest of component...
}

4. Modify the HTML Template

Add the directive and its bindings:

<div class="widget-container" <!-- Other attributes and events... -->
  libResponsiveWidgetProps [widget]="widget" [activeBreakpoint]="activeBreakpoint"
  #widgetProps="responsiveWidgetProps">

  <!-- Widget content... -->
</div>

5. Replace Direct Property Access with Directive Methods

In the HTML template, replace:

[resizeMinWidth]="widget?.size?.minWidth || 20"

With:

[resizeMinWidth]="widgetProps.getSizeProp('minWidth') || 20"

6. Update Manipulation Methods

For manipulating widgets (drag, resize, etc.), use ResponsiveWidgetService:

onDragEnd(event: any): void {
  if (this.isPreview) return;

  const { x, y } = event;

  const responsiveWidgetService = inject(ResponsiveWidgetService);

  if (responsiveWidgetService.isResponsiveWidget(this.widget)) {
    // For responsive widgets, update position for current breakpoint
    let updatedWidget = responsiveWidgetService.setWidgetPropertyForBreakpoint(
      this.widget,
      'position.x',
      Math.round(x / 8) * 8,
      this.activeBreakpoint
    );

    // More updates...

    this.widgetChange.emit(updatedWidget);
  } else {
    // For non-responsive widgets, direct update
    const updatedPosition = {
      x: Math.round(x / 8) * 8,
      y: Math.round(y / 8) * 8,
      constrainToParent: (this.widget.position as Position).constrainToParent,
    };

    this.updateWidget({
      position: updatedPosition,
    });
  }
}

Visualization and Style Calculation

To generate visual styles for a widget:

readonly containerStyles = computed(() => {
  const widgetSize = this.responsiveWidgetService.getWidgetSize(
    this.widget,
    this.activeBreakpoint
  );
  const widgetPosition = this.responsiveWidgetService.getWidgetPosition(
    this.widget,
    this.activeBreakpoint
  );

  return {
    width: `${widgetSize.width}px`,
    height: `${widgetSize.height}px`,
    position: 'absolute',
    left: `${widgetPosition.x}px`,
    top: `${widgetPosition.y}px`,
    // Other styles...
  };
});

Available Helper Methods

The ResponsiveWidgetPropsDirective provides these helper methods:

Size Properties

// Get size properties for current breakpoint
widgetProps.getSizeProp('width')
widgetProps.getSizeProp('height')
widgetProps.getSizeProp('minWidth')
widgetProps.getSizeProp('minHeight')
widgetProps.getSizeProp('maxWidth')
widgetProps.getSizeProp('maxHeight')

Position Properties

// Get position properties for current breakpoint
widgetProps.getPositionProp('x')
widgetProps.getPositionProp('y')
widgetProps.getPositionProp('constrainToParent')

Style Properties

// Get style properties for current breakpoint
widgetProps.getStyleProp('backgroundColor')
widgetProps.getStyleProp('borderRadius')
widgetProps.getStyleProp('opacity')
// ... any custom style property

Complete Property Objects

// Get complete objects for current breakpoint
widgetProps.getSize()      // Returns complete Size object
widgetProps.getPosition()  // Returns complete Position object
widgetProps.getStyles()    // Returns complete styles object

Responsive Widget Service Methods

The ResponsiveWidgetService provides these key methods:

Widget Type Detection

// Check if widget supports responsive properties
responsiveWidgetService.isResponsiveWidget(widget)

Property Getters

// Get properties for specific breakpoint
responsiveWidgetService.getWidgetSize(widget, breakpoint)
responsiveWidgetService.getWidgetPosition(widget, breakpoint)
responsiveWidgetService.getWidgetStyles(widget, breakpoint)

Property Setters

// Set properties for specific breakpoint
responsiveWidgetService.setWidgetPropertyForBreakpoint(
  widget,
  'size.width',
  newValue,
  breakpoint
)

responsiveWidgetService.setWidgetPropertyForBreakpoint(
  widget,
  'position.x',
  newValue,
  breakpoint
)

Breakpoint System

The responsive system supports these breakpoints:

Adapted Widgets

The following widgets have already been adapted to the responsive system:

All other widgets should follow this guide to adapt to the new responsive system.

Best Practices

1. Always Check Widget Type

if (responsiveWidgetService.isResponsiveWidget(this.widget)) {
  // Use responsive methods
} else {
  // Use traditional methods
}

2. Use Computed Styles

readonly containerStyles = computed(() => {
  // Calculate styles based on current breakpoint
  return this.calculateResponsiveStyles();
});

3. Handle Breakpoint Changes

// Listen for breakpoint changes
effect(() => {
  const breakpoint = this.activeBreakpoint();
  // Update widget display based on new breakpoint
  this.updateWidgetDisplay();
});

4. Preserve Backward Compatibility

// Always support both responsive and non-responsive widgets
const size = responsiveWidgetService.isResponsiveWidget(this.widget)
  ? responsiveWidgetService.getWidgetSize(this.widget, this.activeBreakpoint)
  : this.widget.size;

Testing Responsive Widgets

1. Test All Breakpoints

2. Test Property Updates

3. Test Backward Compatibility

Troubleshooting

Common Issues

1. Widget Not Responding to Breakpoint Changes

Solution: Ensure activeBreakpoint input is properly bound and the directive is correctly applied.

2. Properties Not Updating

Solution: Check that you're using the directive methods (widgetProps.getSizeProp()) instead of direct property access.

3. Styles Not Applying

Solution: Verify that computed styles are properly reactive and use the responsive service methods.

Migration Checklist

Summary

The responsive widget system provides a powerful way to create widgets that adapt to different screen sizes and breakpoints. By following this guide, you can ensure your widgets work seamlessly across all device types while maintaining backward compatibility with existing non-responsive widgets.

The key is to use the provided directive and service methods instead of direct property access, and always check whether a widget supports responsive properties before applying responsive logic.