New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

column-fitter

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

column-fitter

This is an Angular Module containing Components/Services using Material

latest
npmnpm
Version
15.0.9
Version published
Weekly downloads
0
-100%
Maintainers
1
Weekly downloads
 
Created
Source

Column Fitter Component

Overview

The column-fitter library provides a responsive grid layout system that automatically adjusts the number of columns based on the detected device size. It integrates with the screen-observer package to monitor device changes and dynamically updates CSS Grid layouts for optimal viewing across different screen sizes.

Core Capabilities

📱 Responsive Grid Layout System

  • Device Detection: Automatically detects device type using screen-observer service
  • Dynamic Column Adjustment: Updates column count based on current device (mobile, tablet, mini, desktop)
  • CSS Grid Integration: Uses modern CSS Grid with auto-fit and minmax for flexible layouts
  • Flexible Configuration: Support for both fixed columns and device-specific column settings
  • Responsive Behavior: Seamlessly adapts between different screen sizes
  • Performance Optimized: Uses RxJS distinctUntilChanged to prevent unnecessary updates

🔧 Features

Device Size Detection - Automatic detection via screen-observer integration
Dynamic Grid Updates - Real-time column count adjustment
CSS Grid Foundation - Modern CSS Grid with repeat() and auto-fit
Flexible Configuration - Fixed numbers or device-specific settings
Customizable Styling - Configurable gap, margins, padding, and colors
Performance Optimized - Efficient change detection and updates
Type-Safe Configuration - Strong typing with Column and DeviceSizes models
Demo Component - Interactive demo showcasing all features

Key Benefits

FeatureDescription
Automatic ResponsivenessNo manual media queries needed
Device-Aware LayoutsOptimized layouts for each device type
Modern CSS GridLeverages CSS Grid for superior performance
Type-Safe ConfigurationFull TypeScript support with device models
Seamless IntegrationWorks with existing screen-observer implementations

Demo Component (ColumnFitterDemoComponent)

The demo component showcases responsive grid layouts using a bookmarks list example.

Usage

To use the demo component in your application:

<app-column-fitter-demo></app-column-fitter-demo>

Demo Features

  • Bookmarks List: Displays a list of classic books in responsive grid
  • Device-Specific Columns:
    • Mobile: 1 column
    • Tablet: 4 columns
    • Mini: 2 columns
    • Desktop: Auto-fit with minmax
  • Real-time Updates: Grid layout updates as you resize the browser
  • Visual Feedback: Console logging of device changes

Summary

The column-fitter library provides a modern, responsive grid system that automatically adapts column layouts based on device detection, making it perfect for creating responsive applications without manual media query management.

Quick Start Guide

Installation & Setup (2 minutes)

1. Import Module

// app.module.ts
import { ColumnFitterModule } from 'column-fitter';

@NgModule({
  imports: [
    ColumnFitterModule
  ]
})
export class AppModule { }

2. Dependencies

The package requires the screen-observer package for device detection:

npm install screen-observer

Quick Examples

Example 1: Fixed Column Layout

import { Component } from '@angular/core';

@Component({
  selector: 'app-fixed-grid',
  template: `
    <app-column-fitter
      [columns]="4"
      [gap]="'1rem'"
      [padding]="'1rem'"
      [backgroundColor]="'#f5f5f5'">
      
      <div class="grid-item" *ngFor="let item of items">
        {{ item.name }}
      </div>
      
    </app-column-fitter>
  `
})
export class FixedGridComponent {
  items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' },
    { id: 4, name: 'Item 4' },
    { id: 5, name: 'Item 5' },
    { id: 6, name: 'Item 6' }
  ];
}

Example 2: Device-Specific Columns

import { Component } from '@angular/core';
import { Column, DeviceSizes } from 'column-fitter';

@Component({
  selector: 'app-responsive-grid',
  template: `
    <app-column-fitter
      [columns]="responsiveColumns"
      [gap]="'1.5rem'"
      [margin]="'1rem'"
      [minWidth]="'250px'"
      [backgroundColor]="'#ffffff'"
      [padding]="'1rem'">
      
      <div class="product-card" *ngFor="let product of products">
        <h3>{{ product.name }}</h3>
        <p>{{ product.description }}</p>
        <span class="price">{{ product.price | currency }}</span>
      </div>
      
    </app-column-fitter>
  `,
  styles: [`
    .product-card {
      padding: 1rem;
      border: 1px solid #ddd;
      border-radius: 8px;
      background: white;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    
    .price {
      font-weight: bold;
      color: #2196f3;
    }
  `]
})
export class ResponsiveGridComponent {
  responsiveColumns: Column[] = [
    { device: DeviceSizes.MOBILE, columns: 1 },
    { device: DeviceSizes.MINI, columns: 2 },
    { device: DeviceSizes.TABLET, columns: 3 },
    { device: DeviceSizes.DESKTOP, columns: 4 }
  ];
  
  products = [
    { id: 1, name: 'Product 1', description: 'Description 1', price: 29.99 },
    { id: 2, name: 'Product 2', description: 'Description 2', price: 39.99 },
    { id: 3, name: 'Product 3', description: 'Description 3', price: 49.99 },
    { id: 4, name: 'Product 4', description: 'Description 4', price: 59.99 },
    { id: 5, name: 'Product 5', description: 'Description 5', price: 69.99 },
    { id: 6, name: 'Product 6', description: 'Description 6', price: 79.99 }
  ];
}
import { Component } from '@angular/core';
import { Column, DeviceSizes } from 'column-fitter';

@Component({
  selector: 'app-image-gallery',
  template: `
    <app-column-fitter
      [columns]="galleryColumns"
      [gap]="'0.5rem'"
      [padding]="'0.5rem'"
      [backgroundColor]="'#000'"
      [minWidth]="'200px'">
      
      <div class="gallery-item" *ngFor="let image of images">
        <img [src]="image.url" [alt]="image.alt" />
        <div class="overlay">
          <h4>{{ image.title }}</h4>
        </div>
      </div>
      
    </app-column-fitter>
  `,
  styles: [`
    .gallery-item {
      position: relative;
      overflow: hidden;
      border-radius: 4px;
      aspect-ratio: 1;
    }
    
    .gallery-item img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      transition: transform 0.3s ease;
    }
    
    .gallery-item:hover img {
      transform: scale(1.1);
    }
    
    .overlay {
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      background: linear-gradient(transparent, rgba(0,0,0,0.8));
      color: white;
      padding: 1rem;
      transform: translateY(100%);
      transition: transform 0.3s ease;
    }
    
    .gallery-item:hover .overlay {
      transform: translateY(0);
    }
  `]
})
export class ImageGalleryComponent {
  galleryColumns: Column[] = [
    { device: DeviceSizes.MOBILE, columns: 2 },
    { device: DeviceSizes.MINI, columns: 3 },
    { device: DeviceSizes.TABLET, columns: 4 },
    { device: DeviceSizes.DESKTOP, columns: 6 }
  ];
  
  images = [
    { id: 1, url: 'https://picsum.photos/300/300?random=1', alt: 'Random 1', title: 'Image 1' },
    { id: 2, url: 'https://picsum.photos/300/300?random=2', alt: 'Random 2', title: 'Image 2' },
    { id: 3, url: 'https://picsum.photos/300/300?random=3', alt: 'Random 3', title: 'Image 3' },
    { id: 4, url: 'https://picsum.photos/300/300?random=4', alt: 'Random 4', title: 'Image 4' },
    { id: 5, url: 'https://picsum.photos/300/300?random=5', alt: 'Random 5', title: 'Image 5' },
    { id: 6, url: 'https://picsum.photos/300/300?random=6', alt: 'Random 6', title: 'Image 6' }
  ];
}

Example 4: Dashboard Cards

import { Component } from '@angular/core';
import { Column, DeviceSizes } from 'column-fitter';

@Component({
  selector: 'app-dashboard',
  template: `
    <div class="dashboard-container">
      <h2>Dashboard</h2>
      
      <app-column-fitter
        [columns]="dashboardColumns"
        [gap]="'1rem'"
        [padding'"
        [background]="'1remColor]="'#f8f9fa'"
        [minWidth]="'300px'">
        
        <div class="stat-card" *ngFor="let stat of statistics">
          <div class="stat-icon">
            <mat-icon>{{ stat.icon }}</mat-icon>
          </div>
          <div class="stat-content">
            <h3>{{ stat.value }}</h3>
            <p>{{ stat.label }}</p>
          </div>
        </div>
        
      </app-column-fitter>
    </div>
  `,
  styles: [`
    .dashboard-container {
      padding: 2rem;
    }
    
    .dashboard-container h2 {
      margin-bottom: 2rem;
      color: #333;
    }
    
    .stat-card {
      background: white;
      padding: 1.5rem;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      display: flex;
      align-items: center;
      gap: 1rem;
    }
    
    .stat-icon {
      width: 48px;
      height: 48px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      background: #e3f2fd;
      color: #1976d2;
    }
    
    .stat-content h3 {
      margin: 0;
      font-size: 1.5rem;
      font-weight: bold;
      color: #333;
    }
    
    .stat-content p {
      margin: 0;
      color: #666;
      font-size: 0.9rem;
    }
  `]
})
export class DashboardComponent {
  dashboardColumns: Column[] = [
    { device: DeviceSizes.MOBILE, columns: 1 },
    { device: DeviceSizes.TABLET, columns: 2 },
    { device: DeviceSizes.MINI, columns: 2 },
    { device: DeviceSizes.DESKTOP, columns: 4 }
  ];
  
  statistics = [
    { id: 1, icon: 'people', value: '1,234', label: 'Total Users' },
    { id: 2, icon: 'shopping_cart', value: '$12,345', label: 'Revenue' },
    { id: 3, icon: 'trending_up', value: '98.5%', label: 'Growth Rate' },
    { id: 4, icon: 'assignment', value: '567', label: 'Tasks Completed' },
    { id: 5, icon: 'star', value: '4.8/5', label: 'Customer Rating' },
    { id: 6, icon: 'notifications', value: '23', label: 'Pending Alerts' }
  ];
}

Component API

Inputs

InputTypeDescriptionDefault
paddingstringPadding for the grid container (CSS padding value)''
marginstringMargin for the grid container (CSS margin value)''
backgroundColorstringBackground color for the grid container''
minWidthstringMinimum width for auto-fit columns (CSS length value)''
gapstringGap between grid items (CSS gap value)'1rem'
columnsnumber | Column[]Column configuration - fixed number or device-specific array0

Dynamic Properties

PropertyTypeDescription
gridColumnsstringCurrent CSS grid-template-columns value
hasColumnsbooleanWhether valid column configuration exists
subscriptionsSubscriptionRxJS subscription management

Model Structures

DeviceSizes Enum

export enum DeviceSizes {
  DESKTOP = 'desktop',    // Desktop/large screens
  TABLET = 'tablet',      // Tablet devices
  MINI = 'mini',          // Small tablets/large phones
  MOBILE = 'mobile'       // Mobile phones
}

Column Interface

export interface ColumnInterface {
  device: DeviceSizes;    // Target device type
  columns: number;        // Number of columns for this device
}

Column Class

export class Column implements ColumnInterface {
  constructor(
    public device = DeviceSizes.DESKTOP,
    public columns = 0,
  ) {}

  static adapt(item?: any): Column {
    return new Column(
      item?.device,
      item?.columns
    );
  }
}

Usage Examples

// Device-specific column configurations
const responsiveColumns: Column[] = [
  new Column(DeviceSizes.MOBILE, 1),    // 1 column on mobile
  new Column(DeviceSizes.MINI, 2),      // 2 columns on mini devices
  new Column(DeviceSizes.TABLET, 3),    // 3 columns on tablets
  new Column(DeviceSizes.DESKTOP, 4)    // 4 columns on desktop
];

// Using adapt method
const adaptedColumns = [
  Column.adapt({ device: DeviceSizes.MOBILE, columns: 1 }),
  Column.adapt({ device: DeviceSizes.TABLET, columns: 3 })
];

// Mixed configuration
const mixedColumns: (number | Column[])[] = [
  3, // Fixed 3 columns for all devices
  // OR
  [
    { device: DeviceSizes.MOBILE, columns: 1 },
    { device: DeviceSizes.TABLET, columns: 2 },
    { device: DeviceSizes.DESKTOP, columns: 4 }
  ]
];

Grid Layout Logic

CSS Grid Generation

The component automatically generates CSS Grid templates based on the configuration:

Fixed Column Mode

// Input: columns = 3
// Output: gridColumns = 'repeat(3, 1fr)'

// Input: columns = 0 (disabled)
// Output: gridColumns = 'repeat(auto-fit, minmax(250px, 1fr))'

Device-Specific Mode

// Device detection logic
if (device === 'desktop' && found(DeviceSizes.DESKTOP)) {
  const cols = found(DeviceSizes.DESKTOP) as Column;
  return `repeat(${cols.columns}, 1fr)`;
}

// Fallback for unmatched devices
return `repeat(auto-fit, minmax(${this.minWidth}, 1fr))`;

Device Detection Flow

  • Screen Observer Integration: Subscribes to screenObserverService.device$
  • Change Detection: Uses RxJS distinctUntilChanged() to prevent unnecessary updates
  • Column Calculation: Calls getGridTemplateColumns() with current device
  • Grid Update: Updates gridColumns property with new CSS Grid value

Module Configuration

ColumnFitterModule

No Global Configuration Required

The ColumnFitterModule does not provide a forRoot() method or global configuration options. All configuration is done at the component level through input properties.

Module Structure

@NgModule({
  declarations: [
    ColumnFitterComponent,
    ColumnFitterDemoComponent
  ],
  imports: [
    // Dependencies are imported by the consuming application
    // screen-observer must be installed separately
  ],
  exports: [
    ColumnFitterComponent,
    ColumnFitterDemoComponent
  ]
})
export class ColumnFitterModule { }

Dependencies

  • screen-observer: Device detection service (must be installed separately)
  • @angular/core: Core Angular functionality
  • rxjs: Reactive programming utilities for device change detection

Styling and Customization

CSS Grid Styling

The component uses CSS Grid with the following base styles:

:host {
  display: block;
}

.grid-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 1rem;
}

Custom Styling Examples

Custom Grid Appearance

// Enhanced grid styling
:host ::ng-deep .grid-container {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 12px;
  padding: 2rem;
  
  .grid-item {
    background: white;
    border-radius: 8px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
    
    &:hover {
      transform: translateY(-2px);
      box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
    }
  }
}

Responsive Gap Adjustment

// Dynamic gaps based on device
:host ::ng-deep .grid-container {
  gap: var(--grid-gap, 1rem);
  
  @media (max-width: 768px) {
    --grid-gap: 0.5rem;
  }
  
  @media (min-width: 1200px) {
    --grid-gap: 1.5rem;
  }
}

Advanced Layout Patterns

Masonry-Style Layout

// CSS Grid with masonry-like behavior
.masonry-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  grid-auto-rows: 200px;
  gap: 1rem;
  
  .masonry-item {
    grid-row-end: span var(--row-span, 1);
    
    &.large {
      --row-span: 2;
    }
    
    &.wide {
      grid-column-end: span 2;
    }
  }
}

Card-Based Layout

// Card layout with consistent height
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
  align-items: stretch;
  
  .card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: white;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    
    .card-image {
      height: 200px;
      overflow: hidden;
      
      img {
        width: 100%;
        height: 100%;
        object-fit: cover;
      }
    }
    
    .card-content {
      padding: 1.5rem;
      flex: 1;
      display: flex;
      flex-direction: column;
    }
  }
}

Integration Examples

With Angular Material

import { Component } from '@angular/core';
import { Column, DeviceSizes } from 'column-fitter';

@Component({
  selector: 'app-material-grid',
  template: `
    <app-column-fitter
      [columns]="materialColumns"
      [gap]="'1rem'"
      [padding]="'1rem'">
      
      <mat-card class="grid-card" *ngFor="let item of materialItems">
        <mat-card-header>
          <mat-card-title>{{ item.title }}</mat-card-title>
          <mat-card-subtitle>{{ item.subtitle }}</mat-card-subtitle>
        </mat-card-header>
        
        <img mat-card-image [src]="item.image" [alt]="item.title">
        
        <mat-card-content>
          <p>{{ item.description }}</p>
        </mat-card-content>
        
        <mat-card-actions>
          <button mat-button>LIKE</button>
          <button mat-button>SHARE</button>
        </mat-card-actions>
      </mat-card>
      
    </app-column-fitter>
  `
})
export class MaterialGridComponent {
  materialColumns: Column[] = [
    { device: DeviceSizes.MOBILE, columns: 1 },
    { device: DeviceSizes.TABLET, columns: 2 },
    { device: DeviceSizes.DESKTOP, columns: 3 }
  ];
  
  materialItems = [
    {
      title: 'Card 1',
      subtitle: 'Subtitle 1',
      description: 'Description for card 1',
      image: 'https://picsum.photos/400/200?random=1'
    },
    // ... more items
  ];
}

With Dynamic Content

import { Component } from '@angular/core';
import { Column, DeviceSizes } from 'column-fitter';

@Component({
  selector: 'app-dynamic-content',
  template: `
    <div class="controls">
      <button (click)="addItem()">Add Item</button>
      <button (click)="removeItem()">Remove Item</button>
      <select [(ngModel)]="selectedLayout" (change)="changeLayout()">
        <option value="mobile1">Mobile: 1 Col</option>
        <option value="tablet3">Tablet: 3 Col</option>
        <option value="desktop4">Desktop: 4 Col</option>
      </select>
    </div>
    
    <app-column-fitter
      [columns]="currentColumns"
      [gap]="'1rem'"
      [padding]="'1rem'">
      
      <div class="dynamic-item" *ngFor="let item of dynamicItems; trackBy: trackById">
        <h3>{{ item.title }}</h3>
        <p>{{ item.content }}</p>
        <small>ID: {{ item.id }}</small>
      </div>
      
    </app-column-fitter>
  `
})
export class DynamicContentComponent {
  currentColumns: Column[] = [
    { device: DeviceSizes.MOBILE, columns: 1 },
    { device: DeviceSizes.TABLET, columns: 3 },
    { device: DeviceSizes.DESKTOP, columns: 4 }
  ];
  
  selectedLayout = 'tablet3';
  dynamicItems = [
    { id: 1, title: 'Item 1', content: 'Content 1' },
    { id: 2, title: 'Item 2', content: 'Content 2' },
    { id: 3, title: 'Item 3', content: 'Content 3' }
  ];
  
  addItem() {
    const newItem = {
      id: Date.now(),
      title: `Item ${this.dynamicItems.length + 1}`,
      content: `Content ${this.dynamicItems.length + 1}`
    };
    this.dynamicItems = [...this.dynamicItems, newItem];
  }
  
  removeItem() {
    if (this.dynamicItems.length > 0) {
      this.dynamicItems = this.dynamicItems.slice(0, -1);
    }
  }
  
  changeLayout() {
    switch (this.selectedLayout) {
      case 'mobile1':
        this.currentColumns = [
          { device: DeviceSizes.MOBILE, columns: 1 },
          { device: DeviceSizes.TABLET, columns: 1 },
          { device: DeviceSizes.DESKTOP, columns: 1 }
        ];
        break;
      case 'tablet3':
        this.currentColumns = [
          { device: DeviceSizes.MOBILE, columns: 1 },
          { device: DeviceSizes.TABLET, columns: 3 },
          { device: DeviceSizes.DESKTOP, columns: 3 }
        ];
        break;
      case 'desktop4':
        this.currentColumns = [
          { device: DeviceSizes.MOBILE, columns: 1 },
          { device: DeviceSizes.TABLET, columns: 2 },
          { device: DeviceSizes.DESKTOP, columns: 4 }
        ];
        break;
    }
  }
  
  trackById(index: number, item: any): any {
    return item.id;
  }
}

Performance Optimization

Change Detection

The component uses several performance optimizations:

  • distinctUntilChanged(): Prevents duplicate device updates
  • Efficient Grid Calculation: Only recalculates when device actually changes
  • Subscription Management: Properly cleans up RxJS subscriptions

Memory Management

ngOnDestroy() {
  // Clean up subscriptions to prevent memory leaks
  this.subscriptions.unsubscribe();
}

Large Dataset Handling

// Use trackBy for large lists
@Component({
  template: `
    <app-column-fitter [columns]="columns">
      <div *ngFor="let item of largeDataset; trackBy: trackById">
        {{ item.name }}
      </div>
    </app-column-fitter>
  `
})
export class LargeDatasetComponent {
  trackById(index: number, item: any): any {
    return item.id; // Use unique identifier
  }
}

Testing

Unit Testing Example

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ColumnFitterComponent } from './column-fitter.component';
import { Column, DeviceSizes } from './models/column.model';
import { ScreenObserverService } from 'screen-observer';

describe('ColumnFitterComponent', () => {
  let component: ColumnFitterComponent;
  let fixture: ComponentFixture<ColumnFitterComponent>;
  let mockScreenObserverService: jasmine.SpyObj<ScreenObserverService>;

  beforeEach(async () => {
    const screenObserverSpy = jasmine.createSpyObj('ScreenObserverService', ['device$']);
    
    await TestBed.configureTestingModule({
      declarations: [ ColumnFitterComponent ],
      providers: [
        { provide: ScreenObserverService, useValue: screenObserverSpy }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(ColumnFitterComponent);
    component = fixture.componentInstance;
    mockScreenObserverService = TestBed.inject(ScreenObserverService) as jasmine.SpyObj<ScreenObserverService>;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should set fixed columns correctly', () => {
    component.columns = 3;
    fixture.detectChanges();
    
    const result = component.getGridTemplateColumns('desktop');
    expect(result).toBe('repeat(3, 1fr)');
  });

  it('should handle device-specific columns', () => {
    component.columns = [
      { device: DeviceSizes.MOBILE, columns: 1 },
      { device: DeviceSizes.TABLET, columns: 3 },
      { device: DeviceSizes.DESKTOP, columns: 4 }
    ];
    
    fixture.detectChanges();
    
    expect(component.getGridTemplateColumns('mobile')).toBe('repeat(1, 1fr)');
    expect(component.getGridTemplateColumns('tablet')).toBe('repeat(3, 1fr)');
    expect(component.getGridTemplateColumns('desktop')).toBe('repeat(4, 1fr)');
  });

  it('should fallback to auto-fit when no matching device', () => {
    component.columns = [
      { device: DeviceSizes.MOBILE, columns: 1 }
    ];
    component.minWidth = '200px';
    
    fixture.detectChanges();
    
    const result = component.getGridTemplateColumns('desktop');
    expect(result).toBe('repeat(auto-fit, minmax(200px, 1fr))');
  });

  it('should handle disabled state (columns = 0)', () => {
    component.columns = 0;
    component.minWidth = '250px';
    
    fixture.detectChanges();
    
    const result = component.getGridTemplateColumns('desktop');
    expect(result).toBe('repeat(auto-fit, minmax(250px, 1fr))');
  });
});

Troubleshooting

Common Issues

  • No columns showing: Ensure screen-observer package is installed and configured
  • Layout not updating: Check that device$ observable is emitting values
  • Styling issues: Verify CSS Grid is supported in target browsers
  • Performance issues: Consider using OnPush change detection for large datasets

Debug Mode

@Component({
  template: `
    <div class="debug-info">
      Current Device: {{ currentDevice }}<br>
      Grid Columns: {{ gridColumns }}<br>
      Has Columns: {{ hasColumns }}<br>
      Columns Config: {{ columns | json }}
    </div>
    
    <app-column-fitter
      [columns]="columns"
      [gap]="gap"
      [minWidth]="minWidth">
      <!-- Content -->
    </app-column-fitter>
  `
})
export class DebugColumnFitterComponent {
  currentDevice = '';
  gridColumns = '';
  hasColumns = false;
  columns: any = [];
  gap = '1rem';
  minWidth = '250px';
  
  constructor() {
    // Add debugging logic
  }
}

Performance Monitoring

ngOnInit() {
  const start = performance.now();
  
  this.subscriptions.add(
    this.screenObserverService.device$.subscribe((screen: string) => {
      const updateStart = performance.now();
      this.gridColumns = this.getGridTemplateColumns(screen);
      const updateEnd = performance.now();
      
      console.log(`Grid update took ${updateEnd - updateStart}ms for device: ${screen}`);
    })
  );
  
  const initEnd = performance.now();
  console.log(`ColumnFitter initialization took ${initEnd - start}ms`);
}

Browser Support

CSS Grid Support

The component requires CSS Grid support, which is available in:

  • Chrome: 57+ (March 2017)
  • Firefox: 52+ (March 2017)
  • Safari: 10.1+ (March 2017)
  • Edge: 16+ (October 2017)

Fallback for Older Browsers

// CSS Grid fallback using Flexbox
.grid-container {
  display: flex;
  flex-wrap: wrap;
  margin: -0.5rem;
  
  .grid-item {
    flex: 1 1 250px; /* Minimum width of 250px */
    margin: 0.5rem;
    
    @supports (display: grid) {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
      margin: 0;
    }
  }
}

Progressive Enhancement

// JavaScript fallback for older browsers
if (!CSS.supports('display', 'grid')) {
  // Apply Flexbox fallback
  this.applyFlexboxFallback();
}

FAQs

Package last updated on 28 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts