last works on x-chart components, adding separated features ...

This commit is contained in:
2026-08-31 16:31:31 +03:30
parent 864fa79454
commit 76dc91fb4b
2 changed files with 906 additions and 200 deletions
@@ -6,31 +6,43 @@ import {
} from '@angular/core'; } from '@angular/core';
import { import {
nameof, nameof,
toArray,
XMimeType, XMimeType,
downloadFile, downloadFile,
generateUuid, generateUuid,
getXColorValue, getXColorValue,
XOneOrManyType,
XColorIdentifier,
downloadFileByUrl, downloadFileByUrl,
isNullOrUndefined, isNullOrUndefined,
XResourceIdentifier,
isNullOrEmptyString, isNullOrEmptyString,
XColorWithBrightness, XColorWithBrightness,
downloadArrayBufferedFile, downloadArrayBufferedFile,
} from 'x-framework-core'; } from 'x-framework-core';
import { import {
isChartType, isChartType,
XChartXAxis,
XChartYAxis,
XChartAction, XChartAction,
XChartTooltip,
XChartOptions, XChartOptions,
XChartEventOpts, XChartEventOpts,
XChartTextAlign, XChartTextAlign,
XChartSeriesType, XChartSeriesType,
isChartTextAlign, isChartTextAlign,
XChartAnnotations,
XChartActionBarItem, XChartActionBarItem,
XChartDrilldownEvent, XChartDrilldownEvent,
XChartAnnotationLabel,
XChartAnnotationStyle,
XChartMarkerAnnotations,
XChartTooltipAnnotations,
XChartContainerComponent, XChartContainerComponent,
XChartStoryboardBeatInfo, XChartStoryboardBeatInfo,
} from './x-chart.typings'; } from './x-chart.typings';
import ApexCharts from 'apexcharts'; import ApexCharts from 'apexcharts';
import { Subject, Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { import {
XActionBarItem, XActionBarItem,
XActionBarItemSlot, XActionBarItemSlot,
@@ -189,6 +201,58 @@ export class XChartComponent extends XChartContainerComponent {
zoomStep = await this.getValueAsync(this.zoomStep); zoomStep = await this.getValueAsync(this.zoomStep);
} }
//
// Toolbar ...
isChanged = changeKeys.includes(nameof<XChartComponent>('tooltip'));
if (isChanged) {
//
if (isNullOrUndefined(this.tooltip)) {
this.tooltip = this.propertyProvider.tooltip;
}
//
await this.handleChartUpdate();
}
//
// XAxis ...
isChanged = changeKeys.includes(nameof<XChartComponent>('xAxis'));
if (isChanged) {
//
if (isNullOrUndefined(this.xAxis)) {
this.xAxis = this.propertyProvider.xAxis;
}
//
await this.handleChartUpdate();
}
//
// YAxis ...
isChanged = changeKeys.includes(nameof<XChartComponent>('yAxis'));
if (isChanged) {
//
if (isNullOrUndefined(this.yAxis)) {
this.yAxis = this.propertyProvider.yAxis;
}
//
await this.handleChartUpdate();
}
//
// Annotations ...
isChanged = changeKeys.includes(nameof<XChartComponent>('annotations'));
if (isChanged) {
//
if (isNullOrUndefined(this.annotations)) {
this.annotations = this.propertyProvider.annotations;
}
//
await this.handleChartUpdate();
}
// //
// Chart Title ... // Chart Title ...
isChanged = changeKeys.includes(nameof<XChartComponent>('title')); isChanged = changeKeys.includes(nameof<XChartComponent>('title'));
@@ -692,7 +756,7 @@ export class XChartComponent extends XChartContainerComponent {
printWindow.document.close(); printWindow.document.close();
} }
} }
} catch (error) {} } catch (error) { }
} }
async handleToggleFullscreen() { async handleToggleFullscreen() {
@@ -724,8 +788,18 @@ export class XChartComponent extends XChartContainerComponent {
const type = await this.getValueAsync(this.type); const type = await this.getValueAsync(this.type);
const chartId = await this.getValueAsync(this.uuid); const chartId = await this.getValueAsync(this.uuid);
const series = await this.getValueAsync(this.series); const series = await this.getValueAsync(this.series);
//
const cOptions = await this.getValueAsync(this.options); const cOptions = await this.getValueAsync(this.options);
//
const xAxis = await this.getPreparedXAxis(cOptions?.xaxis);
const yAxis = await this.getPreparedYAxis(cOptions?.yaxis);
//
const tooltip = await this.getPreparedTooltip();
const annotations = await this.getPreparedAnnotations();
// //
const title = await this.getResourceValueAsync(this.title); const title = await this.getResourceValueAsync(this.title);
const titleAlignProp = await this.getValueAsync(this.titleAlign); const titleAlignProp = await this.getValueAsync(this.titleAlign);
@@ -798,7 +872,15 @@ export class XChartComponent extends XChartContainerComponent {
// //
this.CHART_OPTIONS = { this.CHART_OPTIONS = {
...cOptions, ...cOptions,
series: series || cOptions.series, series:
{
...cOptions?.series,
...series as any
},
annotations: {
...cOptions?.annotations,
...annotations
},
title: { title: {
...cOptions.title, ...cOptions.title,
text: title, text: title,
@@ -1371,10 +1453,19 @@ export class XChartComponent extends XChartContainerComponent {
palette: cOptions.theme?.palette || 'palette1', palette: cOptions.theme?.palette || 'palette1',
}, },
colors: cOptions.colors || [primaryColorValue], colors: cOptions.colors || [primaryColorValue],
xaxis: {
...xAxis
},
yaxis: [
...yAxis
],
grid: { grid: {
...cOptions.grid, ...cOptions.grid,
borderColor: gridColorValue, borderColor: gridColorValue,
}, },
tooltip: {
...tooltip
},
dataLabels: { dataLabels: {
...cOptions.dataLabels, ...cOptions.dataLabels,
}, },
@@ -2100,8 +2191,7 @@ export class XChartComponent extends XChartContainerComponent {
// //
const offset = 24; const offset = 24;
height = `${ height = `${window.innerHeight -
window.innerHeight -
offset - offset -
(actionBarHeight + (actionBarHeight +
(cardImageElement?.clientHeight || 0) + (cardImageElement?.clientHeight || 0) +
@@ -2238,5 +2328,583 @@ export class XChartComponent extends XChartContainerComponent {
this.CARD_ACTION_PROVIDER_SUBSCRIPTION.unsubscribe(); this.CARD_ACTION_PROVIDER_SUBSCRIPTION.unsubscribe();
this.CARD_ACTION_PROVIDER_SUBSCRIPTION = null; this.CARD_ACTION_PROVIDER_SUBSCRIPTION = null;
} }
//
//#region Preparers ...
private async getPreparedTooltip() {
//
let result: XChartTooltip = undefined;
//
if (!isNullOrUndefined(this.tooltip)) {
//
result = await this.getValueAsync(this.tooltip);
//
result = await this.normalizeChartTooltip(result);
}
//
return result;;
}
private async getPreparedXAxis(v?: XChartXAxis) {
//
let result: XChartXAxis = undefined;
//
if (!isNullOrUndefined(this.xAxis)) {
//
result = {
...(await this.getValueAsync(this.xAxis))
};
}
//
if (!isNullOrUndefined(v)) {
//
result = {
...v,
...result
};
}
//
if (!isNullOrUndefined(result)) {
//
result = {
...result,
}
//
if (!isNullOrUndefined(result.labels)) {
//
result.labels = {
...result.labels,
}
//
if (!isNullOrUndefined(result.labels.style)) {
//
result.labels.style = {
...result.labels.style,
colors: toArray(result.labels.style.colors).map(c => this.normalizeColor(c))
}
}
}
//
if (!isNullOrUndefined(result.group)) {
//
result.group = {
...result.group
}
//
if (!isNullOrUndefined(result.group.style)) {
//
result.group.style = {
...result.group.style,
colors: toArray(result.group.style.colors).map(x => this.normalizeColor(x))
}
}
}
//
if (!isNullOrUndefined(result.axisBorder)) {
//
result.axisBorder = {
...result.axisBorder,
color: this.normalizeColor(result.axisBorder.color)
}
}
//
if (!isNullOrUndefined(result.axisTicks)) {
//
result.axisTicks = {
...result.axisTicks,
color: this.normalizeColor(result.axisTicks.color)
}
}
//
if (!isNullOrUndefined(result.title)) {
//
result.title = {
...result.title,
text: (await this.normalizeText(result.title.text) as string)
}
//
if (!isNullOrUndefined(result.title.style)) {
//
result.title.style = {
...result.title.style,
color: this.normalizeColor(result.title.style.color)
}
}
}
//
if (!isNullOrUndefined(result.crosshairs)) {
//
result.crosshairs = {
...result.crosshairs
}
//
if (!isNullOrUndefined(result.crosshairs.stroke)) {
//
result.crosshairs.stroke = {
...result.crosshairs.stroke,
color: this.normalizeColor(result.crosshairs.stroke.color)
}
}
//
if (!isNullOrUndefined(result.crosshairs.fill)) {
//
result.crosshairs.fill = {
...result.crosshairs.fill,
color: this.normalizeColor(result.crosshairs.fill.color)
}
//
if (!isNullOrUndefined(result.crosshairs.fill.gradient)) {
//
result.crosshairs.fill.gradient = {
...result.crosshairs.fill.gradient,
colorTo: this.normalizeColor(result.crosshairs.fill.gradient.colorTo),
colorFrom: this.normalizeColor(result.crosshairs.fill.gradient.colorFrom),
}
}
}
}
}
//
return result;
}
private async getPreparedYAxis(v?: XOneOrManyType<XChartYAxis>) {
//
let result: XChartYAxis[] = undefined;
//
if (!isNullOrUndefined(v)) {
//
result = [
...toArray(v)
];
}
//
if (!isNullOrUndefined(this.yAxis)) {
//
result = [
...(this.hasChild(result) ? result : []),
...await this.getArrayValueAsync(this.yAxis)
];
//
const mappers = result.map(async x => {
//
if (!isNullOrUndefined(x.labels)) {
//
x.labels = {
...x.labels
};
//
if (!isNullOrUndefined(x.labels.style)) {
//
x.labels.style = {
...x.labels.style,
colors: toArray(x.labels.style.colors).map(c => this.normalizeColor(c))
}
}
}
//
if (!isNullOrUndefined(x.axisBorder)) {
//
x.axisBorder = {
...x.axisBorder,
color: this.normalizeColor(x.axisBorder.color)
}
}
//
if (!isNullOrUndefined(x.axisTicks)) {
//
x.axisTicks = {
...x.axisTicks,
color: this.normalizeColor(x.axisTicks.color)
}
}
//
if (!isNullOrUndefined(x.title)) {
//
x.title = {
...x.title,
text: (await this.normalizeText(x.title.text) as string)
}
//
if (!isNullOrUndefined(x.title.style)) {
//
x.title.style = {
...x.title.style,
color: this.normalizeColor(x.title.style.color)
}
}
}
//
if (!isNullOrUndefined(x.crosshairs)) {
//
x.crosshairs = {
...x.crosshairs,
}
//
if (!isNullOrUndefined(x.crosshairs.stroke)) {
//
x.crosshairs.stroke = {
...x.crosshairs.stroke,
color: this.normalizeColor(x.crosshairs.stroke.color)
}
}
}
//
return x;
});
//
const items = await Promise.all(mappers);
//
result = [
...result,
...items
];
}
//
return result;
}
private async getPreparedAnnotations() {
//
let result: XChartAnnotations = undefined;
//
if (!isNullOrUndefined(this.annotations)) {
//
result = await this.getValueAsync(this.annotations);
//
// Try to Normalize Colors and Resources ...
//
// Texts ...
if (this.hasChild(result.texts)) {
//
const mappers = result.texts.map(async x => {
//
// Normalizing Text ...
x.text = (await this.normalizeText(x.text)) as string;
//
// Normalizing Colors ...
x.foreColor = this.normalizeColor(x.foreColor);
x.borderColor = this.normalizeColor(x.borderColor);
x.backgroundColor = this.normalizeColor(x.backgroundColor);
//
return x;
});
const items = await Promise.all(mappers);
result.texts = [
...items
];
}
//
// Y Axis ...
if (this.hasChild(result.yaxis)) {
//
const mappers = result.yaxis.map(async x => {
//
// Normalizing Text ...
x.label = await this.normalizeLabel(x.label);
//
// Normalizing Colors ...
x.fillColor = this.normalizeColor(x.fillColor);
x.borderColor = this.normalizeColor(x.borderColor);
//
return x;
});
const items = await Promise.all(mappers);
result.yaxis = [
...items
];
}
//
// X Axis ...
if (this.hasChild(result.xaxis)) {
//
const mappers = result.xaxis.map(async x => {
//
// Normalizing Text ...
x.label = await this.normalizeLabel(x.label);
//
// Normalizing Colors ...
x.fillColor = this.normalizeColor(x.fillColor);
x.borderColor = this.normalizeColor(x.borderColor);
//
return x;
});
const items = await Promise.all(mappers);
result.yaxis = [
...items
];
}
//
// Points ...
if (this.hasChild(result.points)) {
//
const mappers = result.points.map(async x => {
//
x.marker = this.normalizeMarker(x.marker);
x.label = await this.normalizeLabel(x.label);
x.tooltip = await this.normalizeTooltip(x.tooltip);
//
return x;
});
const items = await Promise.all(mappers);
result.points = [
...items
];
}
//
// Images ...
if (this.hasChild(result.images)) {
//
result.images = [
...result.images
];
}
}
//
return result;
}
//#endregion
//
//#region Normalizers ...
private async normalizeText(
v: XOneOrManyType<XResourceIdentifier>,
forceLocale: boolean = false
) {
//
let result: XOneOrManyType<string> = undefined;
//
if (!isNullOrUndefined(v)) {
//
let items = toArray(v);
if (this.hasChild(items)) {
//
const mappers = items.map(async x => {
//
let r: string = await this.getResourceValueAsync(x);
//
if (forceLocale && !isNullOrEmptyString(r)) {
r = this.applyLocale(r);
}
//
return r;
});
//
var results = await Promise.all(mappers);
//
if (this.hasChild(results)) {
//
if (results.length == 1) {
result = results[0];
} else {
result = [
...results
];
}
}
}
}
//
return result;
}
private normalizeColor(v: XColorIdentifier) {
//
let result = '';
//
if (!isNullOrUndefined(v)) {
//
const titleColorValue = getXColorValue(v);
//
result = titleColorValue || v;
}
//
return result;
}
private normalizeMarker(v: XChartMarkerAnnotations) {
//
let result: XChartMarkerAnnotations = undefined;
//
if (!isNullOrUndefined(v)) {
//
result = {
...v,
fillColor: this.normalizeColor(v.fillColor),
strokeColor: this.normalizeColor(v.strokeColor),
}
}
//
return result;
}
private async normalizeStyle(v: XChartAnnotationStyle) {
//
let result: XChartAnnotationStyle = undefined;
//
if (!isNullOrUndefined(v)) {
//
result = {
...v,
//
color: this.normalizeColor(v.color),
background: this.normalizeColor(v.background),
};
}
//
return result;
}
private async normalizeLabel(v: XChartAnnotationLabel) {
//
let result: XChartAnnotationLabel = undefined;
//
if (!isNullOrUndefined(v)) {
//
result = {
...v,
//
text: await this.normalizeText(v.text),
//
style: await this.normalizeStyle(v.style),
//
borderColor: this.normalizeColor(v.borderColor),
};
}
//
return result;
}
private async normalizeTooltip(v: XChartTooltipAnnotations) {
//
let result: XChartTooltipAnnotations = undefined;
//
if (!isNullOrUndefined(v)) {
//
result = {
...v,
//
text: await this.normalizeText(v.text),
}
}
//
return result;
}
private async normalizeChartTooltip(v: XChartTooltip) {
//
let result: XChartTooltip = undefined;
//
if (!isNullOrUndefined(v)) {
//
result = {
...v,
};
//
// Style ...
if (!isNullOrUndefined(v.style)) {
//
result.style = {
...v.style,
background: this.normalizeColor(v.style.background)
};
}
//
// Z ...
if (!isNullOrUndefined(v.z)) {
//
result.z = {
...v.z,
title: (await this.normalizeText(v.z.title) as string)
}
}
//
// Marker ...
if (!isNullOrUndefined(v.marker)) {
//
result.marker = {
...v.marker,
fillColors: v.marker.fillColors?.map(fc => this.normalizeColor(fc))
}
}
}
//
return result;
}
//#endregion
//#endregion //#endregion
} }
@@ -101,21 +101,7 @@ export type XChartXAxisAnnotations = XCahrtAxisBaseAnnotations & {
x2?: null | number | string; x2?: null | number | string;
}; };
export type XChartPointAnnotations = { export type XChartMarkerAnnotations = {
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?: {
size?: number; size?: number;
fillColor?: string; fillColor?: string;
strokeColor?: string; strokeColor?: string;
@@ -124,20 +110,9 @@ export type XChartPointAnnotations = {
offsetX?: number; offsetX?: number;
offsetY?: number; offsetY?: number;
cssClass?: string; cssClass?: string;
}; };
label?: XChartAnnotationLabel;
image?: { export type XChartTooltipAnnotations = {
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?: {
enabled?: boolean; enabled?: boolean;
/** /**
* Static tooltip content (HTML allowed; an array is joined with line * Static tooltip content (HTML allowed; an array is joined with line
@@ -159,7 +134,36 @@ export type XChartPointAnnotations = {
theme?: 'light' | 'dark'; theme?: 'light' | 'dark';
offsetX?: number; offsetX?: number;
offsetY?: 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 * Render arbitrary SVG markup at the annotation's position. Deprecated in
* favor of `image`/`marker`, but still supported. * favor of `image`/`marker`, but still supported.
@@ -3225,7 +3229,7 @@ export enum XChartAction {
export type XChartActionIdentifier = XChartAction | string; export type XChartActionIdentifier = XChartAction | string;
export interface XChartActionModel extends XBaseActionModel<XChartActionIdentifier> {} export interface XChartActionModel extends XBaseActionModel<XChartActionIdentifier> { }
export type XChartSeriesType = XChartAxisChartSeries | XChartNonAxisChartSeries; export type XChartSeriesType = XChartAxisChartSeries | XChartNonAxisChartSeries;
@@ -3323,6 +3327,14 @@ export interface IXChartContainer extends TXChartContainer {
series: XStandardType<XChartSeriesType>; series: XStandardType<XChartSeriesType>;
type: XStandardType<XChartTypeIdentifier>; type: XStandardType<XChartTypeIdentifier>;
//
xAxis: XStandardType<XChartXAxis>;
yAxis: XStandardType<XOneOrManyType<XChartYAxis>>;
//
tooltip: XStandardType<XChartTooltip>;
annotations: XStandardType<XChartAnnotations>;
// //
title: XStandardType<XResourceIdentifier>; title: XStandardType<XResourceIdentifier>;
titleAlign: XStandardType<XChartTextAlignIdentifier>; titleAlign: XStandardType<XChartTextAlignIdentifier>;
@@ -3460,8 +3472,7 @@ export interface IXChartContainer extends TXChartContainer {
}) })
export class XChartContainer export class XChartContainer
extends XBaseContainer extends XBaseContainer
implements Readonly<IXChartContainer> implements Readonly<IXChartContainer> {
{
// //
readonly actionProvider = new Subject<XChartActionModel>(); readonly actionProvider = new Subject<XChartActionModel>();
readonly cssClass: XStandardType<XOneOrManyType<string>>; readonly cssClass: XStandardType<XOneOrManyType<string>>;
@@ -3473,6 +3484,14 @@ export class XChartContainer
readonly series: XStandardType<XChartSeriesType>; readonly series: XStandardType<XChartSeriesType>;
readonly type: XStandardType<XChartTypeIdentifier> = XChartType.Line; readonly type: XStandardType<XChartTypeIdentifier> = XChartType.Line;
//
readonly xAxis: XStandardType<XChartXAxis>;
readonly yAxis: XStandardType<XOneOrManyType<XChartYAxis>>;
//
readonly tooltip: XStandardType<XChartTooltip>;
readonly annotations: XStandardType<XChartAnnotations>;
// //
readonly title: XStandardType<XResourceIdentifier>; readonly title: XStandardType<XResourceIdentifier>;
readonly titleAlign: XStandardType<XChartTextAlignIdentifier> = readonly titleAlign: XStandardType<XChartTextAlignIdentifier> =
@@ -3671,8 +3690,7 @@ export class XChartContainer
@Component({ template: `` }) @Component({ template: `` })
export abstract class XChartContainerComponent export abstract class XChartContainerComponent
extends XBaseContainerComponent extends XBaseContainerComponent
implements IXChartContainer implements IXChartContainer {
{
// //
//#region Props ... //#region Props ...
@Input() @Input()
@@ -3698,6 +3716,18 @@ export abstract class XChartContainerComponent
@Input() @Input()
zoomStep: XStandardType<number> = this.propertyProvider.zoomStep; zoomStep: XStandardType<number> = this.propertyProvider.zoomStep;
@Input()
xAxis: XStandardType<XChartXAxis> = this.propertyProvider.xAxis;
@Input()
yAxis: XStandardType<XOneOrManyType<XChartYAxis>> = this.propertyProvider.yAxis;
@Input()
tooltip: XStandardType<XChartTooltip> = this.propertyProvider.tooltip;
@Input()
annotations: XStandardType<XChartAnnotations> = this.propertyProvider.annotations;
@Input() @Input()
defaultActionBarItemColor: XStandardType<XColorIdentifier> = defaultActionBarItemColor: XStandardType<XColorIdentifier> =
this.propertyProvider.defaultActionBarItemColor; this.propertyProvider.defaultActionBarItemColor;
@@ -4034,6 +4064,14 @@ export abstract class XChartContainerComponent
series: this.getValue(container ? container.series : this.series), series: this.getValue(container ? container.series : this.series),
zoomStep: this.getValue(container ? container.zoomStep : this.zoomStep), zoomStep: this.getValue(container ? container.zoomStep : this.zoomStep),
//
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), title: this.getValue(container ? container.title : this.title),
titleAlign: this.getValue( titleAlign: this.getValue(