7 Commits
8 changed files with 1567 additions and 340 deletions
@@ -0,0 +1,4 @@
export interface XSizeModel {
width?: number;
height?: number;
}
@@ -9,7 +9,7 @@
[class.fullscreen]="getValue(isFullscreen) | async"
>
<!-- 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 -->
<section
*ngIf="notEmptyValue(avatarUrl) | async"
@@ -57,6 +57,7 @@
<!-- Card Image -->
<img
mat-card-image
#cardImage
class="x-image"
[src]="getValue(imageUrl) | async"
*ngIf="notEmptyValue(imageUrl) | async"
@@ -64,18 +65,18 @@
/>
<!-- 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>
</mat-card-content>
<!-- 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>
</mat-card-actions>
<!-- Card Footer -->
<mat-card-footer
#footer
#cardFooter
class="x-footer"
*ngIf="getValue(showFooter) | async"
>
@@ -19,7 +19,6 @@
&:fullscreen {
border-radius: 0;
background-color: var(--full-screen-bg, #ffffff);
width: 100vw;
height: 100vh;
@@ -54,7 +53,7 @@
height: var(--height, inherit);
}
.x-card.page-card {
.x-card.page-card:not(.fullscreen) {
@include push-auto();
@include applyWidthHeight();
@include m-tb($d-l);
@@ -5,8 +5,18 @@ import {
} from './x-card.typings';
import { Subject, Subscription } from 'rxjs';
import { concatMap, map } from 'rxjs/operators';
import { nameof, isNullOrEmptyString } from 'x-framework-core';
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { XSizeModel } from '../classes/x-size.model.';
import {
nameof,
isNullOrEmptyString,
isNullOrUndefined,
} from 'x-framework-core';
import {
Component,
ChangeDetectionStrategy,
ElementRef,
ViewChild,
} from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
@@ -22,22 +32,107 @@ export class XCardComponent extends XCardContainerComponent {
private ACTION_PROVIDER_SUBSCRIPTION: Subscription;
//
// Full Screen ...
private IS_FULLSCREEN: boolean = false;
public set isFullscreen(v: boolean) {
//
const isChanged = v != this.IS_FULLSCREEN;
//#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;
//
this.IS_FULLSCREEN = v;
@ViewChild('cardImage', { static: false })
set cardImage(v: ElementRef<HTMLElement>) {
this.CARD_IMAGE = v;
}
public get isFullscreen() {
return this.IS_FULLSCREEN;
@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 ...
//
private IS_FULLSCREEN: boolean = false;
set isFullscreen(v: boolean) {
//
const isChanged = v != this.IS_FULLSCREEN;
if (isChanged) {
//
this.actionProvider.next({
action: XCardAction.FullScreenToggled,
payload: v,
});
}
//
this.IS_FULLSCREEN = v;
}
get isFullscreen() {
return this.IS_FULLSCREEN;
}
//#endregion
//#endregion
//
//#region LifeCycles ...
onInit() {
super.onInit();
//
// Listen for fullscreen change events (handles ESC key and browser UI)
document.addEventListener('fullscreenchange', this.fullscreenChangeHandler);
document.addEventListener(
'webkitfullscreenchange',
this.fullscreenChangeHandler,
);
document.addEventListener(
'mozfullscreenchange',
this.fullscreenChangeHandler,
);
document.addEventListener(
'MSFullscreenChange',
this.fullscreenChangeHandler,
);
}
onChange(changeKeys: string[]) {
super.onChange(changeKeys);
@@ -58,6 +153,24 @@ export class XCardComponent extends XCardContainerComponent {
//
this.unregisterActionProvider();
//
document.removeEventListener(
'fullscreenchange',
this.fullscreenChangeHandler,
);
document.removeEventListener(
'webkitfullscreenchange',
this.fullscreenChangeHandler,
);
document.removeEventListener(
'mozfullscreenchange',
this.fullscreenChangeHandler,
);
document.removeEventListener(
'MSFullscreenChange',
this.fullscreenChangeHandler,
);
}
registerViewHandlers() {
@@ -149,6 +262,124 @@ export class XCardComponent extends XCardContainerComponent {
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
//
@@ -211,6 +442,22 @@ export class XCardComponent extends XCardContainerComponent {
}
//#endregion
//
//#region Fullscreen ...
private fullscreenChangeHandler = () => {
//
const isFullscreen = !!(
document.fullscreenElement ||
(document as any).webkitFullscreenElement ||
(document as any).mozFullScreenElement ||
(document as any).msFullscreenElement
);
//
this.isFullscreen = isFullscreen;
this.detectChanges();
};
private canUseFullscreen(): boolean {
return !!(
document.fullscreenEnabled ||
@@ -220,4 +467,5 @@ export class XCardComponent extends XCardContainerComponent {
);
}
//#endregion
//#endregion
}
@@ -33,16 +33,19 @@ import { X_FRAMEWORK_COMPONENTS_CONFIG } from '../tokens/x-injectable-tokens';
import { XFrameworkComponentsConfig } from '../config/x-framework-components.config';
export enum XCardAction {
//
Refresh = 'refresh',
//
ExitFullScreen = 'exit_full_screen',
EnterFullScreen = 'enter_full_screen',
ToggleFullScreen = 'toggle_full_screen',
//
FullScreenToggled = 'full_screen_toggled',
}
export type XCardActionIdentifier = XCardAction | string;
export interface XCardActionModel
extends XBaseActionModel<XCardActionIdentifier> {}
export interface XCardActionModel extends XBaseActionModel<XCardActionIdentifier> {}
//
//#region Container ...
@@ -190,7 +193,7 @@ export abstract class XCardContainerComponent
public ruler: ViewportRuler,
public zone: NgZone,
public changeDetector: ChangeDetectorRef,
protected propertyProvider: XCardContainer
protected propertyProvider: XCardContainer,
) {
super(
config,
@@ -199,7 +202,7 @@ export abstract class XCardContainerComponent
renderer,
ruler,
zone,
changeDetector
changeDetector,
);
}
//#endregion
@@ -207,37 +210,37 @@ export abstract class XCardContainerComponent
//
//#region Abstract ...
public extractProps(
container?: IXCardContainer
container?: IXCardContainer,
): Observable<IXCardContainer> {
return forkJoin({
//
cssClass: this.getArrayValue(
container ? container.cssClass : this.cssClass
container ? container.cssClass : this.cssClass,
),
//
color: this.getValue(container ? container.color : this.color),
foregroundColor: this.getValue(
container ? container.foregroundColor : this.foregroundColor
container ? container.foregroundColor : this.foregroundColor,
),
//
showHeader: this.getValue(
container ? container.showHeader : this.showHeader
container ? container.showHeader : this.showHeader,
),
title: this.getValue(container ? container.title : this.title),
subTitle: this.getValue(container ? container.subTitle : this.subTitle),
avatarUrl: this.getValue(
container ? container.avatarUrl : this.avatarUrl
container ? container.avatarUrl : this.avatarUrl,
),
avatarTemplate: this.getValue(
container ? container.avatarTemplate : this.avatarTemplate
container ? container.avatarTemplate : this.avatarTemplate,
),
//
imageUrl: this.getValue(container ? container.imageUrl : this.imageUrl),
imageDescription: this.getValue(
container ? container.imageDescription : this.imageDescription
container ? container.imageDescription : this.imageDescription,
),
//
@@ -245,13 +248,13 @@ export abstract class XCardContainerComponent
//
showContent: this.getValue(
container ? container.showContent : this.showContent
container ? container.showContent : this.showContent,
),
showActions: this.getValue(
container ? container.showActions : this.showActions
container ? container.showActions : this.showActions,
),
showFooter: this.getValue(
container ? container.showFooter : this.showFooter
container ? container.showFooter : this.showFooter,
),
}).pipe(
map((data) => {
@@ -264,7 +267,7 @@ export abstract class XCardContainerComponent
? container.actionProvider
: this.actionProvider,
};
})
}),
);
}
//#endregion
@@ -288,8 +291,7 @@ type TXComponentCardContainer = {
};
export interface IXComponentCardContainer
extends TXComponentCardContainer,
IXBaseComponentContainer {
extends TXComponentCardContainer, IXBaseComponentContainer {
isInPage: XStandardType<boolean>;
wrapWithCard: XStandardType<boolean>;
}
@@ -403,7 +405,7 @@ export abstract class XComponentCardContainerComponent
public ruler: ViewportRuler,
public zone: NgZone,
public changeDetector: ChangeDetectorRef,
protected propertyProvider: XComponentCardContainer
protected propertyProvider: XComponentCardContainer,
) {
super(
config,
@@ -412,7 +414,7 @@ export abstract class XComponentCardContainerComponent
renderer,
ruler,
zone,
changeDetector
changeDetector,
);
}
//#endregion
@@ -420,34 +422,34 @@ export abstract class XComponentCardContainerComponent
//
//#region Abstract ...
public extractProps(
container?: IXComponentCardContainer
container?: IXComponentCardContainer,
): Observable<IXComponentCardContainer> {
return forkJoin({
//
isInPage: this.getValue(container ? container.isInPage : this.isInPage),
wrapWithCard: this.getValue(
container ? container.wrapWithCard : this.wrapWithCard
container ? container.wrapWithCard : this.wrapWithCard,
),
cardCssClass: this.getValue(
container ? container.cardCssClass : this.cardCssClass
container ? container.cardCssClass : this.cardCssClass,
),
cardColor: this.getValue(
container ? container.cardColor : this.cardColor
container ? container.cardColor : this.cardColor,
),
cardForegroundColor: this.getValue(
container ? container.cardForegroundColor : this.cardForegroundColor
container ? container.cardForegroundColor : this.cardForegroundColor,
),
cardTitle: this.getValue(
container ? container.cardTitle : this.cardTitle
container ? container.cardTitle : this.cardTitle,
),
cardSubTitle: this.getValue(
container ? container.cardSubTitle : this.cardSubTitle
container ? container.cardSubTitle : this.cardSubTitle,
),
cardShowFooter: this.getValue(
container ? container.cardShowFooter : this.cardShowFooter
container ? container.cardShowFooter : this.cardShowFooter,
),
cardShowActions: this.getValue(
container ? container.cardShowActions : this.cardShowActions
container ? container.cardShowActions : this.cardShowActions,
),
}).pipe(
map((data) => {
@@ -460,7 +462,7 @@ export abstract class XComponentCardContainerComponent
? container.cardActionProvider
: this.cardActionProvider,
};
})
}),
);
}
//#endregion
@@ -1,8 +1,10 @@
<!-- Component Template -->
<ng-container *ngIf="getValue(wrapWithCard) | async; else componentsRef">
<x-card
#chartCard
class="x-card"
[showActions]="true"
[actionProvider]="cardActionProvider"
[color]="getValue(cardColor) | async"
[ngClass]="getValue(cssClass) | async"
[isInPage]="getValue(isInPage) | async"
@@ -41,6 +43,7 @@
>
<!-- Toolbar -->
<x-action-bar
#chartActionBar
[color]="actionBarColor"
[cssClass]="actionBarCssClass"
[actionProvider]="actionBarActionProvider"
File diff suppressed because it is too large Load Diff
@@ -13,6 +13,7 @@ import {
import {
XStandardType,
isValueInEnum,
getXColorValue,
XOneOrManyType,
XColorIdentifier,
XResourceIdentifier,
@@ -101,21 +102,7 @@ export type XChartXAxisAnnotations = XCahrtAxisBaseAnnotations & {
x2?: null | number | string;
};
export type XChartPointAnnotations = {
id?: number | string;
x?: number | string;
y?: null | number;
yAxisIndex?: number;
seriesIndex?: number;
/**
* Ink Layer (#7): make this point annotation draggable. Overrides
* `chart.ink.enabled`. Requires the `ink` feature.
*/
draggable?: boolean;
mouseEnter?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
mouseLeave?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
click?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
marker?: {
export type XChartMarkerAnnotations = {
size?: number;
fillColor?: string;
strokeColor?: string;
@@ -125,19 +112,8 @@ export type XChartPointAnnotations = {
offsetY?: number;
cssClass?: string;
};
label?: XChartAnnotationLabel;
image?: {
path?: string;
width?: number;
height?: number;
offsetX?: number;
offsetY?: number;
};
/**
* Show a hover tooltip over the annotation marker, like a regular data
* point. Useful for surfacing more detail than fits in the label.
*/
tooltip?: {
export type XChartTooltipAnnotations = {
enabled?: boolean;
/**
* Static tooltip content (HTML allowed; an array is joined with line
@@ -160,6 +136,35 @@ export type XChartPointAnnotations = {
offsetX?: number;
offsetY?: number;
};
export type XChartPointAnnotations = {
id?: number | string;
x?: number | string;
y?: null | number;
yAxisIndex?: number;
seriesIndex?: number;
/**
* Ink Layer (#7): make this point annotation draggable. Overrides
* `chart.ink.enabled`. Requires the `ink` feature.
*/
draggable?: boolean;
mouseEnter?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
mouseLeave?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
click?: (annotation: XChartPointAnnotations, e: MouseEvent) => void;
marker?: XChartMarkerAnnotations;
label?: XChartAnnotationLabel;
image?: {
path?: string;
width?: number;
height?: number;
offsetX?: number;
offsetY?: number;
};
/**
* Show a hover tooltip over the annotation marker, like a regular data
* point. Useful for surfacing more detail than fits in the label.
*/
tooltip?: XChartTooltipAnnotations;
/**
* Render arbitrary SVG markup at the annotation's position. Deprecated in
* favor of `image`/`marker`, but still supported.
@@ -2407,6 +2412,8 @@ export type XChartTooltip = {
};
};
export type XChartColorType = Array<string | ((opts: XChartColorFormatterOpts) => string)>;
export interface XChartOptions {
annotations?: XChartAnnotations;
chart?: XChart;
@@ -2415,7 +2422,7 @@ export interface XChartOptions {
* named) or a function returning one per-datapoint. The list cycles when
* there are more series than colors.
*/
colors?: Array<string | ((opts: XChartColorFormatterOpts) => string)>;
colors?: XChartColorType;
dataLabels?: XChartDataLabels;
/** Opt-in drilldown navigation. Requires `import 'apexcharts/features/drilldown'`. */
drilldown?: XChartDrilldown;
@@ -3323,6 +3330,18 @@ export interface IXChartContainer extends TXChartContainer {
series: XStandardType<XChartSeriesType>;
type: XStandardType<XChartTypeIdentifier>;
//
stroke: XStandardType<XChartStroke>;
colors: XStandardType<XChartColorType>;
//
xAxis: XStandardType<XChartXAxis>;
yAxis: XStandardType<XOneOrManyType<XChartYAxis>>;
//
tooltip: XStandardType<XChartTooltip>;
annotations: XStandardType<XChartAnnotations>;
//
title: XStandardType<XResourceIdentifier>;
titleAlign: XStandardType<XChartTextAlignIdentifier>;
@@ -3460,8 +3479,7 @@ export interface IXChartContainer extends TXChartContainer {
})
export class XChartContainer
extends XBaseContainer
implements Readonly<IXChartContainer>
{
implements Readonly<IXChartContainer> {
//
readonly actionProvider = new Subject<XChartActionModel>();
readonly cssClass: XStandardType<XOneOrManyType<string>>;
@@ -3471,7 +3489,22 @@ export class XChartContainer
readonly options: XStandardType<XChartOptions>;
readonly zoomStep: XStandardType<number> = 0.125;
readonly series: XStandardType<XChartSeriesType>;
//
readonly stroke: XStandardType<XChartStroke> = {
width: 3,
curve: 'smooth'
};
readonly type: XStandardType<XChartTypeIdentifier> = XChartType.Line;
readonly colors: XStandardType<XChartColorType> = [getXColorValue(XColorWithBrightness.PrimaryShade)];
//
readonly xAxis: XStandardType<XChartXAxis>;
readonly yAxis: XStandardType<XOneOrManyType<XChartYAxis>>;
//
readonly tooltip: XStandardType<XChartTooltip>;
readonly annotations: XStandardType<XChartAnnotations>;
//
readonly title: XStandardType<XResourceIdentifier>;
@@ -3671,8 +3704,7 @@ export class XChartContainer
@Component({ template: `` })
export abstract class XChartContainerComponent
extends XBaseContainerComponent
implements IXChartContainer
{
implements IXChartContainer {
//
//#region Props ...
@Input()
@@ -3698,6 +3730,24 @@ export abstract class XChartContainerComponent
@Input()
zoomStep: XStandardType<number> = this.propertyProvider.zoomStep;
@Input()
xAxis: XStandardType<XChartXAxis> = this.propertyProvider.xAxis;
@Input()
yAxis: XStandardType<XOneOrManyType<XChartYAxis>> = this.propertyProvider.yAxis;
@Input()
colors: XStandardType<XChartColorType> = this.propertyProvider.colors;
@Input()
stroke: XStandardType<XChartStroke> = this.propertyProvider.stroke;
@Input()
tooltip: XStandardType<XChartTooltip> = this.propertyProvider.tooltip;
@Input()
annotations: XStandardType<XChartAnnotations> = this.propertyProvider.annotations;
@Input()
defaultActionBarItemColor: XStandardType<XColorIdentifier> =
this.propertyProvider.defaultActionBarItemColor;
@@ -4030,10 +4080,22 @@ export abstract class XChartContainerComponent
//
type: this.getValue(container ? container.type : this.type),
options: this.getValue(container ? container.options : this.options),
series: this.getValue(container ? container.series : this.series),
options: this.getValue(container ? container.options : this.options),
zoomStep: this.getValue(container ? container.zoomStep : this.zoomStep),
//
stroke: this.getValue(container ? container.stroke : this.stroke),
colors: this.getArrayValue(container ? container.colors : this.colors),
//
xAxis: this.getValue(container ? container.xAxis : this.xAxis),
yAxis: this.getValue(container ? container.yAxis : this.yAxis),
//
tooltip: this.getValue(container ? container.tooltip : this.tooltip),
annotations: this.getValue(container ? container.annotations : this.annotations),
//
title: this.getValue(container ? container.title : this.title),
titleAlign: this.getValue(