last works for handle card sizing ...

This commit is contained in:
2026-08-29 17:22:55 +03:30
parent 921b9e09f8
commit 30f944123f
5 changed files with 231 additions and 61 deletions
@@ -0,0 +1,4 @@
export interface XSizeModel {
width?: number;
height?: number;
}
@@ -9,7 +9,7 @@
[class.fullscreen]="getValue(isFullscreen) | async" [class.fullscreen]="getValue(isFullscreen) | async"
> >
<!-- Card Header --> <!-- Card Header -->
<mat-card-header class="x-header" *ngIf="getValue(showHeader) | async"> <mat-card-header #cardHeader class="x-header" *ngIf="getValue(showHeader) | async">
<!-- Avatar Image --> <!-- Avatar Image -->
<section <section
*ngIf="notEmptyValue(avatarUrl) | async" *ngIf="notEmptyValue(avatarUrl) | async"
@@ -57,6 +57,7 @@
<!-- Card Image --> <!-- Card Image -->
<img <img
mat-card-image mat-card-image
#cardImage
class="x-image" class="x-image"
[src]="getValue(imageUrl) | async" [src]="getValue(imageUrl) | async"
*ngIf="notEmptyValue(imageUrl) | async" *ngIf="notEmptyValue(imageUrl) | async"
@@ -64,18 +65,18 @@
/> />
<!-- Card Content --> <!-- Card Content -->
<mat-card-content class="x-content" *ngIf="getValue(showContent) | async"> <mat-card-content #cardContent class="x-content" *ngIf="getValue(showContent) | async">
<ng-content></ng-content> <ng-content></ng-content>
</mat-card-content> </mat-card-content>
<!-- Card Actions --> <!-- Card Actions -->
<mat-card-actions class="x-actions" *ngIf="getValue(showActions) | async"> <mat-card-actions #cardActions class="x-actions" *ngIf="getValue(showActions) | async">
<ng-content select="[actions]"></ng-content> <ng-content select="[actions]"></ng-content>
</mat-card-actions> </mat-card-actions>
<!-- Card Footer --> <!-- Card Footer -->
<mat-card-footer <mat-card-footer
#footer #cardFooter
class="x-footer" class="x-footer"
*ngIf="getValue(showFooter) | async" *ngIf="getValue(showFooter) | async"
> >
@@ -5,8 +5,9 @@ import {
} from './x-card.typings'; } from './x-card.typings';
import { Subject, Subscription } from 'rxjs'; import { Subject, Subscription } from 'rxjs';
import { concatMap, map } from 'rxjs/operators'; import { concatMap, map } from 'rxjs/operators';
import { nameof, isNullOrEmptyString } from 'x-framework-core'; import { XSizeModel } from '../classes/x-size.model.';
import { Component, ChangeDetectionStrategy } from '@angular/core'; import { nameof, isNullOrEmptyString, isNullOrUndefined } from 'x-framework-core';
import { Component, ChangeDetectionStrategy, ElementRef, ViewChild } from '@angular/core';
@Component({ @Component({
// tslint:disable-next-line:component-selector // tslint:disable-next-line:component-selector
@@ -21,17 +22,71 @@ export class XCardComponent extends XCardContainerComponent {
// //
private ACTION_PROVIDER_SUBSCRIPTION: Subscription; private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
//
//#region Card Elements ....
private CARD_IMAGE: ElementRef<HTMLElement> = undefined;
private CARD_HEADER: ElementRef<HTMLElement> = undefined;
private CARD_FOOTER: ElementRef<HTMLElement> = undefined;
private CARD_CONTENT: ElementRef<HTMLElement> = undefined;
private CARD_ACTIONS: ElementRef<HTMLElement> = undefined;
@ViewChild('cardImage', { static: false })
set cardImage(v: ElementRef<HTMLElement>) {
this.CARD_IMAGE = v;
}
@ViewChild('cardHeader', { static: false })
set cardHeader(v: ElementRef<HTMLElement>) {
this.CARD_HEADER = v;
}
@ViewChild('cardFooter', { static: false })
set cardFooter(v: ElementRef<HTMLElement>) {
this.CARD_FOOTER = v;
}
@ViewChild('cardContent', { static: false })
set cardContent(v: ElementRef<HTMLElement>) {
this.CARD_CONTENT = v;
}
@ViewChild('cardActions', { static: false })
set cardActions(v: ElementRef<HTMLElement>) {
this.CARD_ACTIONS = v;
}
get cardImage() {
return this.CARD_IMAGE;
}
get cardHeader() {
return this.CARD_HEADER;
}
get cardFooter() {
return this.CARD_FOOTER;
}
get cardContent() {
return this.CARD_CONTENT;
}
get cardActions() {
return this.CARD_ACTIONS;
}
//#endregion
// //
//#region Full Screen ... //#region Full Screen ...
private IS_FULLSCREEN: boolean = false; private IS_FULLSCREEN: boolean = false;
public set isFullscreen(v: boolean) { set isFullscreen(v: boolean) {
// //
const isChanged = v != this.IS_FULLSCREEN; const isChanged = v != this.IS_FULLSCREEN;
// //
this.IS_FULLSCREEN = v; this.IS_FULLSCREEN = v;
} }
public get isFullscreen() { get isFullscreen() {
return this.IS_FULLSCREEN; return this.IS_FULLSCREEN;
} }
//#endregion //#endregion
@@ -167,6 +222,109 @@ export class XCardComponent extends XCardContainerComponent {
await this.enterFullscreen(); await this.enterFullscreen();
} }
} }
//
//#region Size Calculating ...
public calculateImageSize() {
//
let result: XSizeModel = undefined;
//
// Validate ...
if (isNullOrUndefined(this.CARD_IMAGE) || isNullOrUndefined(this.CARD_IMAGE.nativeElement)) {
return result;
}
//
result = {
width: this.CARD_IMAGE.nativeElement.offsetWidth,
height: this.CARD_IMAGE.nativeElement.offsetHeight
};
//
return result;
}
public calculateHeaderSize() {
//
let result: XSizeModel = undefined;
//
// Validate ...
if (isNullOrUndefined(this.CARD_HEADER) || isNullOrUndefined(this.CARD_HEADER.nativeElement)) {
return result;
}
//
result = {
width: this.CARD_HEADER.nativeElement.offsetWidth,
height: this.CARD_HEADER.nativeElement.offsetHeight
};
//
return result;
}
public calculateFooterSize() {
//
let result: XSizeModel = undefined;
//
// Validate ...
if (isNullOrUndefined(this.CARD_FOOTER) || isNullOrUndefined(this.CARD_FOOTER.nativeElement)) {
return result;
}
//
result = {
width: this.CARD_FOOTER.nativeElement.offsetWidth,
height: this.CARD_FOOTER.nativeElement.offsetHeight
};
//
return result;
}
public calculateContentSize() {
//
let result: XSizeModel = undefined;
//
// Validate ...
if (isNullOrUndefined(this.CARD_CONTENT) || isNullOrUndefined(this.CARD_CONTENT.nativeElement)) {
return result;
}
//
result = {
width: this.CARD_CONTENT.nativeElement.offsetWidth,
height: this.CARD_CONTENT.nativeElement.offsetHeight
};
//
return result;
}
public calculateActionsSize() {
//
let result: XSizeModel = undefined;
//
// Validate ...
if (isNullOrUndefined(this.CARD_ACTIONS) || isNullOrUndefined(this.CARD_ACTIONS.nativeElement)) {
return result;
}
//
result = {
width: this.CARD_ACTIONS.nativeElement.offsetWidth,
height: this.CARD_ACTIONS.nativeElement.offsetHeight
};
//
return result;
}
//#endregion
//#endregion //#endregion
// //
@@ -1,6 +1,7 @@
<!-- Component Template --> <!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef"> <ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card <x-card
#chartCard
class="x-card" class="x-card"
[showActions]="true" [showActions]="true"
[color]="getValue(cardColor) | async" [color]="getValue(cardColor) | async"
@@ -30,7 +30,7 @@ import {
XChartContainerComponent, XChartContainerComponent,
XChartStoryboardBeatInfo, XChartStoryboardBeatInfo,
} from './x-chart.typings'; } from './x-chart.typings';
import { Subscription } from 'rxjs'; import { fromEvent, Subject, Subscription } from 'rxjs';
import ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
import { import {
XActionBarItem, XActionBarItem,
@@ -39,6 +39,8 @@ import {
} from '../x-action-bar/x-action-bar.typings'; } from '../x-action-bar/x-action-bar.typings';
import { XIconNames } from '../x-icon/x-icon.typings'; import { XIconNames } from '../x-icon/x-icon.typings';
import { XCardAction } from '../x-card/x-card.typings'; import { XCardAction } from '../x-card/x-card.typings';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { XCardComponent } from '../x-card/x-card.component';
@Component({ @Component({
selector: 'x-chart', selector: 'x-chart',
@@ -50,10 +52,10 @@ export class XChartComponent extends XChartContainerComponent {
// //
//#region Props ... //#region Props ...
// //
private CHART_CONTAINER: ElementRef | undefined; private CHART_CONTAINER: ElementRef = undefined;
@ViewChild('chartContainer', { static: false }) @ViewChild('chartContainer', { static: false })
set chartContainer(el: ElementRef | undefined) { set chartContainer(el: ElementRef) {
// //
if (isNullOrUndefined(el)) { if (isNullOrUndefined(el)) {
// //
@@ -71,17 +73,19 @@ export class XChartComponent extends XChartContainerComponent {
} }
// //
@HostListener('resize', ['$event']) private CHART_CARD: ElementRef<XCardComponent> = undefined;
onResize(event: any) {
// @ViewChild('chartCard', { static: false })
if (isNullOrUndefined(this.chartInstance)) { set chartCard(el: ElementRef<XCardComponent>) {
return; this.CHART_CARD = el;
}
get chartCard() {
return this.CHART_CARD;;
} }
// //
// TODO: Complete this ... private RESIZE_SUBJECT = new Subject<Event>();
// this.chartInstance.updateOptions();
}
// //
private CHART_OPTIONS: XChartOptions = null; private CHART_OPTIONS: XChartOptions = null;
@@ -105,6 +109,7 @@ export class XChartComponent extends XChartContainerComponent {
// //
await this.initializeComponent(); await this.initializeComponent();
this.registerResizeHandler();
} }
async onChange(changeKeys: string[]) { async onChange(changeKeys: string[]) {
@@ -337,6 +342,10 @@ export class XChartComponent extends XChartContainerComponent {
onDestroy() { onDestroy() {
super.onDestroy(); super.onDestroy();
//
this.RESIZE_SUBJECT.next();
this.RESIZE_SUBJECT.complete();
// //
this.destroyComponent(); this.destroyComponent();
this.unregisterActionProvider(); this.unregisterActionProvider();
@@ -658,7 +667,7 @@ export class XChartComponent extends XChartContainerComponent {
printWindow.document.close(); printWindow.document.close();
} }
} }
} catch (error) {} } catch (error) { }
} }
async handleToggleFullscreen() { async handleToggleFullscreen() {
@@ -672,47 +681,8 @@ export class XChartComponent extends XChartContainerComponent {
} }
// //
// Handle Fullscreen Toggle using Card ...
this.cardActionProvider.next({ action: XCardAction.ToggleFullScreen }); this.cardActionProvider.next({ action: XCardAction.ToggleFullScreen });
//
return;
//
const chartWrapper = this.chartContainer.nativeElement;
const doc = document as any;
//
// Check if the chart wrapper is currently in fullscreen mode
const isFullscreen =
doc.fullscreenElement === chartWrapper ||
doc.msFullscreenElement === chartWrapper ||
doc.mozFullScreenElement === chartWrapper ||
doc.webkitFullscreenElement === chartWrapper;
if (!isFullscreen) {
//
// Enter Fullscreen
if (chartWrapper.requestFullscreen) {
await chartWrapper.requestFullscreen();
} else if (chartWrapper.webkitRequestFullscreen) {
await chartWrapper.webkitRequestFullscreen();
} else if (chartWrapper.mozRequestFullScreen) {
await chartWrapper.mozRequestFullScreen();
} else if (chartWrapper.msRequestFullscreen) {
await chartWrapper.msRequestFullscreen();
}
} else {
//
// Exit Fullscreen
if (doc.exitFullscreen) {
await doc.exitFullscreen();
} else if (doc.webkitExitFullscreen) {
await doc.webkitExitFullscreen();
} else if (doc.mozCancelFullScreen) {
await doc.mozCancelFullScreen();
} else if (doc.msExitFullscreen) {
await doc.msExitFullscreen();
}
}
} }
//#endregion //#endregion
@@ -2075,6 +2045,42 @@ export class XChartComponent extends XChartContainerComponent {
this.chartInstance = null; this.chartInstance = null;
} }
private registerResizeHandler() {
//
fromEvent(window, 'resize')
.pipe(
debounceTime(200),
takeUntil(this.RESIZE_SUBJECT)
)
.subscribe(() => {
//
if (isNullOrUndefined(this.chartInstance) || isNullOrUndefined(this.chartCard) || isNullOrUndefined(this.chartCard.nativeElement) || !this.chartCard.nativeElement.isFullscreen) {
return;
}
//
var height = this.chartCard.nativeElement.calculateContentSize();
//
// Reading Height ...
this.CHART_OPTIONS = {
...this.CHART_OPTIONS,
chart: {
height: `${height}px`
}
};
//
// TODO:
// Only Apply when Full Screen Mode ...
// Add FullScreen Event Handler in XCard Component ...
console.log('chart resize: ', height);
//
this.chartInstance.updateOptions(this.CHART_OPTIONS, false, false);
});
}
private registerActionProvider() { private registerActionProvider() {
// //
this.unregisterActionProvider(); this.unregisterActionProvider();