upp-wdgt -- Overview
upp-wdgt is the widget library of the unpispas monorepo. It provides the complete set of reusable Angular components, directives, and services that form the visual building blocks used by the applications (unpispas-pos, upp-test) and the feature libraries (libs/feature/*).
Purpose and Design Philosophy
The library encapsulates every UI primitive the applications need -- layout shells, panels, headers, form controls, grids, media pickers, navigation tabs, and utility directives -- so that every application and feature shares a consistent look, feel, and behaviour.
Three principles guide the library's design:
-
Slot-based composition -- Components expose named content-projection slots (
<ng-content select="...">) rather than deeply nested configuration objects. You compose complex UIs by placing small, single-purpose components inside container components. For example,upp-panelhas slots for header, content, and footer;upp-thumbhas slots for top-left, top-right, and bottom overlays;upp-headerhas slots for title, buttons, and content. This keeps each component's API surface small while enabling rich layouts through composition. -
OnPush everywhere -- Every component uses
ChangeDetectionStrategy.OnPushto minimise unnecessary change detection cycles. Components communicate state changes through explicitmarkForCheck()calls and theViewRendererservice (see below), not through zone-triggered dirty checking. -
Grid-aware adaptability -- Components like
upp-thumbextendViewModeDirectiveso they automatically adapt their layout (GRID card vs LIST row) based on the parentupp-grid's view mode. This means a single template works for both grid and list presentations without*ngIfbranching in the consuming code.
Component Map
The library exports the following components, grouped by functional area. Each links to its detailed documentation page.
Application Shell
The top-level structure for the entire application.
| Component | Selector | Purpose |
|---|---|---|
UppApplicationComponent | upp-application | Root shell: sidebar menu + main content area |
UppApplicationMenuComponent | upp-application-menu | Sidebar menu slot |
UppApplicationMainComponent | upp-application-main | Main content area slot |
UppApplicationPageComponent | upp-application-page | Individual page within the main area |
See upp-application, upp-panel, upp-header.
Layout
Panels, headers, dropdowns, scrollable containers, and visibility control.
| Component | Selector | Purpose |
|---|---|---|
UppPanelComponent | upp-panel | Card-like container with header/content/footer slots |
UppPanelHeaderComponent | upp-panel-header | Panel header slot |
UppPanelContentComponent | upp-panel-content | Panel content slot |
UppPanelFooterComponent | upp-panel-footer | Panel footer slot |
UppHeaderComponent | upp-header | Page or section header with title, buttons, and content |
UppHeaderTitleComponent | upp-header-title | Header title slot |
UppHeaderButtonComponent | upp-header-button | Header action button slot |
UppHeaderContentComponent | upp-header-content | Header custom content slot |
UppDropdownComponent | upp-dropdown | Expandable/collapsible section |
UppDropdownTitleComponent | upp-dropdown-title | Dropdown title slot |
UppDropdownSplitComponent | upp-dropdown-split | Dropdown split-action slot |
UppDropdownContentComponent | upp-dropdown-content | Dropdown body slot |
UppScrollableComponent | upp-scrollable | Scrollable container with virtual-scroll-like optimisations |
UppVisibleControlComponent | upp-visible-control | Wraps uppVisible directive with automatic change-detection detach/reattach |
See upp-dropdown, upp-scrollable.
Grid
View-mode-aware grid with item and empty-state slots.
| Component | Selector | Purpose |
|---|---|---|
UppGridComponent | upp-grid | Grid/list container that propagates view mode to children |
UppGridItemComponent | upp-grid-item | Individual grid item slot |
UppGridEmptyComponent | upp-grid-empty | Empty-state content shown when no items exist |
ViewModeDirective | [uppViewMode] | Base directive for view-mode propagation (extended by UppThumbComponent) |
See upp-grid.
Media
Image display, cropping, and thumbnail cards.
| Component | Selector | Purpose |
|---|---|---|
UppImageComponent | upp-image | Image display with lazy loading, EXIF handling, crop modal, and form binding |
UppCropComponent | upp-crop-image | Standalone image cropper (wraps ngx-img-cropper) |
UppModalCropComponent | upp-modal-crop-image | Modal wrapper for the cropper (used by UppImageComponent) |
UppThumbComponent | upp-thumb | Thumbnail card with overlays, adapts to GRID/LIST mode |
UppThumbTopLeftComponent | upp-thumb-top-left | Overlay slot: top-left corner |
UppThumbTopRightComponent | upp-thumb-top-right | Overlay slot: top-right corner |
UppThumbBottomComponent | upp-thumb-bottom | Overlay slot: bottom edge |
See upp-image, upp-thumb, upp-crop.
Form Controls
Input fields, text areas, address fields, and search.
| Component | Selector | Purpose |
|---|---|---|
UppInputComponent | upp-input | Text input with label, error display, and form binding |
UppKbInputComponent | upp-kb-input | Keyboard-aware input variant |
UppErInputComponent | upp-er-input | Error-annotated input variant |
UppTextAreaComponent | upp-textarea | Multi-line text area with form binding |
UppKbTextAreaComponent | upp-kb-textarea | Keyboard-aware text area variant |
UppErTextAreaComponent | upp-er-textarea | Error-annotated text area variant |
UppAddressComponent | upp-address | Address input with Google Maps autocomplete |
UppMdAddressComponent | upp-md-address | Map-display address variant |
UppErAddressComponent | upp-er-address | Error-annotated address variant |
UppSearchComponent | upp-search | Search bar with debounced input |
See upp-input, upp-textarea, upp-search, upp-address.
Form Layout
Structured form containers for consistent form presentation.
| Component | Selector | Purpose |
|---|---|---|
UppFormComponent | upp-form | Form container with image, sections, and button bars |
UppFormImageComponent | upp-form-image | Image slot inside a form |
UppFormSectionComponent | upp-form-section | Labelled section divider |
UppFormActionComponent | upp-form-action | Action row inside a form |
UppFormWarningComponent | upp-form-warning | Warning message display |
UppFormButtonBarComponent | upp-form-button-bar | Fixed button bar at the form bottom |
UppFormBarButtonComponent | upp-form-bar-button | Individual button inside the bar |
UppFormButtonsComponent | upp-form-buttons | Inline button group |
See upp-form.
Select
Custom select/dropdown with list, items, and options.
| Component | Selector | Purpose |
|---|---|---|
UppSelectComponent | upp-select | Select container with search and selection management |
UppSelectItemComponent | upp-select-item | Individual selectable item |
UppSelectListComponent | upp-select-list | Scrollable option list |
UppSelectOptionComponent | upp-select-option | Option definition |
See upp-select.
Navigation
Tab-based content switching.
| Component | Selector | Purpose |
|---|---|---|
UppTabBarComponent | upp-tab-bar | Tab bar container managing selection state |
UppTabButtonComponent | upp-tab-button | Individual tab with icon and label |
See upp-tab-bar.
Directives
Utility directives for test automation, touch handling, and visibility detection.
| Directive | Selector | Purpose |
|---|---|---|
DataIdDirective | [uppDataid] | Normalised data-id attribute for test automation |
TouchDirective | [uppTouch] | Zone-optimised touch/click event handling |
VisibleDirective | [uppVisible] | Viewport visibility detection via IntersectionObserver |
See Directives.
Module: UppWdgtModule
All components, directives, and services are registered in a single Angular module.
import { UppWdgtModule } from '@unpispas/upp-wdgt';
@NgModule({
imports: [UppWdgtModule],
})
export class AppModule {}
Dependencies
UppWdgtModule imports the following modules:
| Module | Package | Purpose |
|---|---|---|
CommonModule | @angular/common | Standard Angular directives (*ngIf, *ngFor, etc.) |
IonicModule | @ionic/angular | Ionic UI primitives (ion-button, ion-icon, etc.) |
FormsModule | @angular/forms | Template-driven forms support |
ReactiveFormsModule | @angular/forms | Reactive forms -- also re-exported so consumers do not need to import it separately |
ImageCropperModule | ngx-img-cropper | Image cropping used by UppCropComponent and UppImageComponent |
GoogleMapsModule | @angular/google-maps | Google Maps integration for UppAddressComponent |
UppBaseModule | @unpispas/upp-base | Base services (platformService, viewService, configService, httpService, preloadService, languageService, etc.) |
Providers
| Provider | Description |
|---|---|
ViewRenderer | Batched change-detection service (see below) |
Schema
The module registers CUSTOM_ELEMENTS_SCHEMA to allow Ionic web-component selectors (e.g. ion-button, ion-icon) in templates without Angular treating them as unknown elements.
ViewRenderer Service
ViewRenderer is a lightweight injectable that optimises change detection by coalescing multiple markForCheck() calls into a single detectChanges() per microtask tick.
Why It Exists
In an OnPush component tree, multiple data changes within the same synchronous execution (e.g. processing a batch of observable emissions) can each call ChangeDetectorRef.markForCheck(). Without ViewRenderer, each call would schedule a separate check. ViewRenderer deduplicates these: it sets a flag on the first call and schedules a single detectChanges() via Promise.resolve().then(...). Subsequent calls within the same tick are no-ops. This reduces rendering work, especially in components that react to multiple streams.
Setup
Because ViewRenderer needs a ChangeDetectorRef (which is component-scoped, not injectable at the module level), you must provide it per-component:
import { ViewRenderer } from '@unpispas/upp-wdgt';
@Component({
selector: 'app-example',
providers: [
{
provide: ViewRenderer,
useFactory: (cdRef: ChangeDetectorRef) => new ViewRenderer(cdRef),
deps: [ChangeDetectorRef],
},
],
})
export class ExampleComponent {
constructor(private renderer: ViewRenderer) {}
update() {
this.renderer.markForCheck();
}
}
API
| Member | Type | Description |
|---|---|---|
cdref | ChangeDetectorRef (getter) | Access the underlying ChangeDetectorRef for cases where you need direct control. |
markForCheck(force?) | void | Schedule a change-detection cycle. When force is true, detectChanges() runs synchronously and immediately. When false (the default), it is deferred to the next microtask and deduplicated -- multiple calls within the same tick result in a single detectChanges(). |
Usage Example
export class ProductListComponent {
constructor(private renderer: ViewRenderer, private dataService: DataService) {}
ngOnInit() {
this.dataService.products$.subscribe(products => {
this.products = products;
this.renderer.markForCheck();
});
this.dataService.categories$.subscribe(categories => {
this.categories = categories;
this.renderer.markForCheck();
});
}
}
Even though both subscriptions fire in the same tick, only one detectChanges() executes.
Slot-Based Composition Pattern
The recurring composition pattern across all upp-wdgt components works as follows:
- Container component defines the layout structure and exposes named
<ng-content>slots using CSS selectors (e.g.select="upp-panel-header"). - Slot components are thin wrappers (
template: '<ng-content></ng-content>') whose only purpose is to serve as projection targets. They carry no logic. - Consumer code composes the UI by nesting slot components inside the container:
<upp-panel>
<upp-panel-header>Title here</upp-panel-header>
<upp-panel-content>Body here</upp-panel-content>
<upp-panel-footer>Actions here</upp-panel-footer>
</upp-panel>
This pattern is used by upp-panel, upp-header, upp-dropdown, upp-thumb, upp-form, and upp-application. Understanding it once means you can use any container component in the library.
Why Slots Instead of Inputs?
- Flexibility -- Slots accept arbitrary Angular templates, not just strings or simple values. A header slot can contain buttons, icons, badges, or custom components.
- Encapsulation -- The container component controls where each slot renders without exposing its internal DOM structure.
- Readability -- The consuming template reads like a declarative layout description rather than a configuration object.
Exported Surface
The public API is defined in libs/upp-wdgt/src/index.ts and re-exports every component, directive, and service:
- Directives --
DataIdDirective,TouchDirective,VisibleDirective - Components -- Application shell (
UppApplicationComponent, menu, main, page), visibility control, panels (panel, header, content, footer), headers (header, title, button, content), dropdown (dropdown, title, split, content), scrollable, grid (grid, item, empty), media (image, crop, modal crop, thumb with overlays), form controls (input, textarea, address, search), select (select, item, list, option), tab bar (tab-bar, tab-button), form layout (form, image, section, action, warning, button-bar, bar-button, buttons) - Services --
ViewRenderer
Theme and Styles
Global theme variables live in libs/upp-wdgt/src/styles/theme.scss. Each component also has its own .scss file co-located with the component source. The theme file should be included in the consuming application's styles array or imported from a global stylesheet.
Assets
Static assets (icons, images) that the widgets depend on are located under libs/upp-wdgt/src/assets/. Ensure the consuming application's build configuration includes this path in its assets array so that the assets are copied to the build output.