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 uses a 140px track floor; LARGE uses 280px. |
Column tracks (layout contract)
size | grid-template-columns |
|---|---|
SMALL (default) | repeat(auto-fill, minmax(140px, 1fr)) |
LARGE | repeat(auto-fill, minmax(280px, 1fr)) |
Expected behaviour:
- As many columns as fit at the floor size (
auto-fill). - Each column grows with
1frso the row fills the container width — no trailing empty strip on the right. - Resizing the container changes how many thumbs fit. That is intentional (catalog, CART selector, place picker, tables, offers, …).
Do not pin a fixed column count from consumers (repeat(N, …)). Column count follows container width and size; consumers only choose SMALL / LARGE.
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.