upp-grid
A configurable grid layout component that displays items in either GRID or LIST view mode. It persists its view mode and thumbnail size through the application's configService, so user preferences survive navigation. Supports an empty-state slot and works together with upp-grid-item to propagate view mode changes to child components (e.g., upp-thumb).
When to Use
- You need a catalog or product listing that can switch between grid and list layouts.
- You want view mode preferences to persist across sessions via configuration.
- You need an empty-state placeholder when there are no items to display.
- You are building a responsive item browser with adjustable thumbnail sizes.
Demo
Source Code
- HTML
- TypeScript
- SCSS
<h2>upp-grid</h2>
<p class="demo-description">Product catalog using <code>upp-grid</code> with view mode, scale, and empty state.</p>
<div class="demo-controls">
<ion-button size="small" (click)="toggleViewMode()">
<ion-icon [name]="viewMode === 'GRID' ? 'grid-outline' : 'list-outline'" slot="start"></ion-icon>
{{ viewMode }}
</ion-button>
<ion-button size="small" (click)="toggleSize()">
<ion-icon [name]="gridSize === 'SMALL' ? 'expand-outline' : 'contract-outline'" slot="start"></ion-icon>
{{ gridSize }}
</ion-button>
<ion-button size="small" [color]="showItems ? 'medium' : 'success'" (click)="toggleItems()">
<ion-icon [name]="showItems ? 'eye-off-outline' : 'eye-outline'" slot="start"></ion-icon>
{{ showItems ? 'Hide Products' : 'Show Products' }}
</ion-button>
</div>
<div class="demo-section demo-section--scrollable">
<div class="grid-container">
<upp-grid name="DEMO_CATALOG" [size]="gridSize">
<upp-grid-empty>
<div class="empty-state">
<ion-icon name="storefront-outline" class="empty-icon"></ion-icon>
<p>No products found</p>
</div>
</upp-grid-empty>
<ng-container *ngIf="showItems">
<upp-grid-item *ngFor="let p of products">
<upp-thumb
[title]="p.name"
[color]="p.color"
[icon]="p.icon"
[detail]="p.price"
[foot]="p.stock">
</upp-thumb>
</upp-grid-item>
</ng-container>
</upp-grid>
</div>
</div>
import { Component } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { configService, Configuration } from '@unpispas/upp-base';
import { UppGridConfiguration } from '@unpispas/upp-wdgt';
interface Product {
name: string;
color: string;
icon: string;
price: string;
stock: string;
}
@Component({
selector: 'demo-upp-grid',
templateUrl: './demo-upp-grid.html',
styleUrls: ['../demo-common.scss', './demo-upp-grid.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DemoUppGridComponent {
showItems = true;
products: Product[] = [
{ name: 'Espresso', color: 'primary', icon: 'cafe-outline', price: '€2.50', stock: 'In stock' },
{ name: 'Latte', color: 'success', icon: 'cafe-outline', price: '€3.80', stock: 'In stock' },
{ name: 'Cappuccino', color: 'warning', icon: 'cafe-outline', price: '€3.50', stock: 'Low' },
{ name: 'Tea', color: 'danger', icon: 'leaf-outline', price: '€2.00', stock: 'In stock' },
{ name: 'Juice', color: 'tertiary', icon: 'nutrition-outline', price: '€4.20', stock: 'Low' },
{ name: 'Water', color: 'medium', icon: 'water-outline', price: '€1.50', stock: 'In stock' },
];
private _config: Configuration;
constructor(private config: configService, private change: ChangeDetectorRef) {
this._config = this.config.getConfiguration('DEMO_CATALOG');
this._config.set(UppGridConfiguration.viewmode, 'GRID');
this._config.set(UppGridConfiguration.thumbsize, 10);
}
get viewMode(): string {
return String(this._config.get(UppGridConfiguration.viewmode) || 'GRID');
}
get gridSize(): 'SMALL' | 'LARGE' {
const ts = Number(this._config.get(UppGridConfiguration.thumbsize)) || 10;
return ts >= 15 ? 'LARGE' : 'SMALL';
}
toggleViewMode() {
const next = this.viewMode === 'GRID' ? 'LIST' : 'GRID';
this._config.set(UppGridConfiguration.viewmode, next);
this._config.commit();
this.change.markForCheck();
}
toggleSize() {
const current = Number(this._config.get(UppGridConfiguration.thumbsize)) || 10;
const next = current >= 15 ? 10 : 15;
this._config.set(UppGridConfiguration.thumbsize, next);
this._config.commit();
this.change.markForCheck();
}
toggleItems() {
this.showItems = !this.showItems;
this.change.markForCheck();
}
}
:host {
display: block;
padding: 16px;
}
.demo-section--scrollable {
overflow: auto;
max-width: 100%;
}
.grid-container {
width: 100%;
min-width: 0;
min-height: 300px;
overflow: auto;
border: 1px solid var(--ion-color-light-shade, #d7d8da);
border-radius: 10px;
padding: 10px;
}
.empty-state {
text-align: center;
padding: 48px 16px;
color: var(--ion-color-medium, #888);
}
.empty-icon {
font-size: 48px;
margin-bottom: 8px;
}
.empty-state p {
margin: 0;
font-size: 14px;
}
API Reference
upp-grid
Selector: upp-grid
| Type | Name | Default | Description |
|---|---|---|---|
@Input() | name: string | null | null | Configuration name. Required to load and persist view mode and thumbnail size via configService. |
@Input() | size: 'SMALL' | 'LARGE' | 'SMALL' | Card size. SMALL renders square cards, LARGE renders rectangular cards. |
Computed properties:
| Property | Type | Description |
|---|---|---|
ViewMode | 'GRID' | 'LIST' | Current view mode, read from configuration. |
ScaleMode | number | Scaling factor derived from thumbsize configuration value (value / 10). |
IsEmpty | boolean | true when no upp-grid-item children are present. |
UppGridConfiguration (enum)
Keys used in the configuration object:
| Key | Description | Default |
|---|---|---|
viewmode | Display mode: 'GRID' or 'LIST'. | 'GRID' |
thumbsize | Thumbnail size factor (integer, where 10 = 100% scale). | 10 |
upp-grid-item
Selector: upp-grid-item
Wraps each item inside the grid. Propagates the parent grid's view mode to any child components that extend ViewModeDirective (such as upp-thumb).
upp-grid-empty
Selector: upp-grid-empty
Content projection slot displayed when the grid has no items. Place any custom empty-state markup inside this component.