Browse Source

add dynamic items per page depends on breakpoints

pull/15539/head
dpinkevych 5 months ago
committed by Andrew Shvayka
parent
commit
701214a156
  1. 3
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html
  2. 12
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.scss
  3. 82
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts

3
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.html

@ -16,6 +16,9 @@
--> -->
<div class="tb-iot-hub-browse flex h-full flex-col" [class.embedded]="embedded" [class.tabs-hidden]="hideTabs"> <div class="tb-iot-hub-browse flex h-full flex-col" [class.embedded]="embedded" [class.tabs-hidden]="hideTabs">
<!-- Hidden probe — we read its computed grid-template-columns to detect the current column count. -->
<div #cardGridProbe class="tb-iot-hub-card-grid tb-iot-hub-card-grid-probe" aria-hidden="true"></div>
<!-- Type tabs --> <!-- Type tabs -->
<nav mat-tab-nav-bar class="tb-iot-hub-tabs" [tabPanel]="tabPanel" [class.hidden]="hideTabs"> <nav mat-tab-nav-bar class="tb-iot-hub-tabs" [tabPanel]="tabPanel" [class.hidden]="hideTabs">
@for (tab of typeTabs; track tab.type) { @for (tab of typeTabs; track tab.type) {

12
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.scss

@ -383,6 +383,18 @@
&.tb-iot-hub-card-grid-compact { &.tb-iot-hub-card-grid-compact {
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
} }
// Probe: zero-height element used only to read the computed column count.
// Keeps display: grid and non-zero width so grid-template-columns resolves.
&.tb-iot-hub-card-grid-probe {
width: 100%;
height: 0;
padding: 0;
gap: 0;
overflow: hidden;
visibility: hidden;
pointer-events: none;
}
} }
// Empty state text + no-data image // Empty state text + no-data image

82
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'; import { AfterViewInit, Component, ElementRef, EventEmitter, Input, NgZone, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { debounceTime, takeUntil } from 'rxjs/operators'; import { debounceTime, takeUntil } from 'rxjs/operators';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
@ -43,7 +43,7 @@ interface SortOption {
styleUrls: ['./iot-hub-browse.component.scss'], styleUrls: ['./iot-hub-browse.component.scss'],
host: { '[class.embedded]': 'embedded' } host: { '[class.embedded]': 'embedded' }
}) })
export class TbIotHubBrowseComponent implements OnInit, OnDestroy { export class TbIotHubBrowseComponent implements OnInit, AfterViewInit, OnDestroy {
readonly ItemType = ItemType; readonly ItemType = ItemType;
@ -88,8 +88,21 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
filterDrawerOpened = false; filterDrawerOpened = false;
textSearch = ''; textSearch = '';
pageSizeOptions = [12, 24, 48];
_activeType: ItemType = ItemType.WIDGET; _activeType: ItemType = ItemType.WIDGET;
@ViewChild('cardGridProbe', { static: true }) cardGridProbe!: ElementRef<HTMLElement>;
probeCols = 4;
get cols(): number {
return this.isCompactType ? Math.max(1, this.probeCols - 1) : this.probeCols;
}
get pageSizeOptions(): number[] {
return this.cols >= 5 ? [15, 30, 60] : [12, 24, 48];
}
private resizeObserver?: ResizeObserver;
private resize$ = new Subject<void>();
activeCategories = new Set<string>(); activeCategories = new Set<string>();
activeUseCases = new Set<string>(); activeUseCases = new Set<string>();
activeCfTypes = new Set<string>(); activeCfTypes = new Set<string>();
@ -137,7 +150,8 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
private iotHubActions: IotHubActionsService, private iotHubActions: IotHubActionsService,
private translate: TranslateService, private translate: TranslateService,
private router: Router, private router: Router,
private route: ActivatedRoute private route: ActivatedRoute,
private zone: NgZone
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
@ -166,14 +180,73 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
} else { } else {
this.loadInstalledItemCounts(); this.loadInstalledItemCounts();
} }
}
ngAfterViewInit(): void {
this.probeCols = this.measureProbeCols();
this.adjustPageSize();
this.resize$.pipe(
debounceTime(150),
takeUntil(this.destroy$)
).subscribe(() => this.handleResize());
this.resizeObserver = new ResizeObserver(() => {
this.zone.run(() => this.resize$.next());
});
this.resizeObserver.observe(this.cardGridProbe.nativeElement);
this.loadItems(); this.loadItems();
} }
ngOnDestroy(): void { ngOnDestroy(): void {
this.resizeObserver?.disconnect();
this.destroy$.next(); this.destroy$.next();
this.destroy$.complete(); this.destroy$.complete();
} }
private measureProbeCols(): number {
const el = this.cardGridProbe?.nativeElement;
if (!el) {
return this.probeCols;
}
// Force layout so `grid-template-columns` resolves to pixel tracks, not `repeat(...)`.
void el.offsetWidth;
const tracks = getComputedStyle(el).gridTemplateColumns;
if (!tracks || tracks === 'none') {
return 1;
}
if (tracks.startsWith('repeat(')) {
const match = tracks.match(/^repeat\(\s*(\d+)\s*,/);
return match ? parseInt(match[1], 10) : 1;
}
return Math.max(1, tracks.trim().split(/\s+/).filter(t => t.length > 0).length);
}
private adjustPageSize(): void {
const options = this.pageSizeOptions;
if (options.includes(this.pageSize)) {
return;
}
const oldPageSize = this.pageSize;
const oldPageIndex = this.pageIndex;
this.pageSize = options.reduce((best, cur) =>
Math.abs(cur - oldPageSize) < Math.abs(best - oldPageSize) ? cur : best);
this.pageIndex = Math.floor((oldPageIndex * oldPageSize) / this.pageSize);
}
private handleResize(): void {
const newProbeCols = this.measureProbeCols();
if (newProbeCols === this.probeCols) {
return;
}
const oldOptions = this.pageSizeOptions;
this.probeCols = newProbeCols;
const newOptions = this.pageSizeOptions;
if (oldOptions[0] === newOptions[0]) {
return;
}
this.adjustPageSize();
this.loadItems();
}
onSearchChange(value: string): void { onSearchChange(value: string): void {
this.textSearch = value; this.textSearch = value;
this.searchSubject.next(value); this.searchSubject.next(value);
@ -185,6 +258,7 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
this.filterSearch = {}; this.filterSearch = {};
this.loadFilterInfo(); this.loadFilterInfo();
this.pageIndex = 0; this.pageIndex = 0;
this.adjustPageSize();
if (type === ItemType.WIDGET) { if (type === ItemType.WIDGET) {
this.loadInstalledWidgets(); this.loadInstalledWidgets();
} else if (type === ItemType.SOLUTION_TEMPLATE) { } else if (type === ItemType.SOLUTION_TEMPLATE) {

Loading…
Cancel
Save