@foxeltech/angular-ui 0.7.101 → 0.7.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -68,6 +68,24 @@ class ChartComponent {
|
|
|
68
68
|
pendingFrames = new Set();
|
|
69
69
|
isMobile = false;
|
|
70
70
|
containerWidth = 0;
|
|
71
|
+
containerHeight = 0;
|
|
72
|
+
/**
|
|
73
|
+
* Mốc (performance.now) mà animation "xuất hiện" của lần setOption gần nhất
|
|
74
|
+
* chắc chắn đã xong. ECharts mặc định animationDuration 1000ms; trong khoảng
|
|
75
|
+
* này KHÔNG được `resize()` — resize nhảy thẳng tới trạng thái cuối và giết
|
|
76
|
+
* animation (đo 05/09: chart PM bị cắt ở ~800ms khi thanh cuộn xuất hiện
|
|
77
|
+
* lúc lưới card nạp xong làm bề rộng đổi 720→710).
|
|
78
|
+
*/
|
|
79
|
+
appearUntil = 0;
|
|
80
|
+
static APPEAR_MS = 1100;
|
|
81
|
+
deferTimer;
|
|
82
|
+
/**
|
|
83
|
+
* Thay đổi kích thước ghi nhận TRONG cửa sổ appear, chờ áp sau. `measure()`
|
|
84
|
+
* đã cập nhật containerWidth/Height ngay lúc đo, nên lần gọi hoãn không thể
|
|
85
|
+
* dựa vào "có đổi không" nữa — phải nhớ bằng cờ.
|
|
86
|
+
*/
|
|
87
|
+
pendingResize = false;
|
|
88
|
+
pendingMobile = false;
|
|
71
89
|
constructor(ref, cdr) {
|
|
72
90
|
this.ref = ref;
|
|
73
91
|
this.cdr = cdr;
|
|
@@ -78,24 +96,27 @@ class ChartComponent {
|
|
|
78
96
|
this.intersectionObserver = new IntersectionObserver(([entry]) => {
|
|
79
97
|
if (entry.isIntersecting && !this.canInit && !this.destroyed) {
|
|
80
98
|
this.canInit = true;
|
|
81
|
-
this.
|
|
99
|
+
this.measure();
|
|
82
100
|
this.rebuildChart();
|
|
83
101
|
this.cdr.detectChanges();
|
|
84
102
|
}
|
|
85
103
|
}, { threshold: 0.1 });
|
|
86
104
|
this.intersectionObserver.observe(this.ref.nativeElement);
|
|
87
|
-
// Resize-safe (tabs, flex, async layout)
|
|
105
|
+
// Resize-safe (tabs, flex, async layout). Đây là ĐƯỜNG RESIZE DUY NHẤT:
|
|
106
|
+
// `autoResize` của NgxEchartsDirective đã tắt trong template — nó
|
|
107
|
+
// resize() mù sau throttle 100ms, kể cả về 0×H khi phần tử display:none
|
|
108
|
+
// (skeleton route ẩn nội dung cũ) rồi resize lại khi hiện → animation
|
|
109
|
+
// xuất hiện bị giết (đo 05/09 khi đổi project trên chip breadcrumb).
|
|
88
110
|
this.resizeObserver = new ResizeObserver(() => {
|
|
89
111
|
if (!this.canInit || this.destroyed)
|
|
90
112
|
return;
|
|
91
|
-
// forceMediaRecalc tự đo lại và chỉ dựng lại khi kích thước đổi.
|
|
92
113
|
this.forceMediaRecalc();
|
|
93
114
|
});
|
|
94
115
|
this.resizeObserver.observe(this.ref.nativeElement);
|
|
95
116
|
}
|
|
96
117
|
ngOnChanges() {
|
|
97
118
|
if (this.canInit && !this.destroyed) {
|
|
98
|
-
this.
|
|
119
|
+
this.measure();
|
|
99
120
|
this.rebuildChart();
|
|
100
121
|
}
|
|
101
122
|
}
|
|
@@ -108,6 +129,9 @@ class ChartComponent {
|
|
|
108
129
|
for (const id of this.pendingFrames)
|
|
109
130
|
cancelAnimationFrame(id);
|
|
110
131
|
this.pendingFrames.clear();
|
|
132
|
+
if (this.deferTimer !== undefined)
|
|
133
|
+
clearTimeout(this.deferTimer);
|
|
134
|
+
this.deferTimer = undefined;
|
|
111
135
|
this.resizeObserver?.disconnect();
|
|
112
136
|
this.intersectionObserver?.disconnect();
|
|
113
137
|
this.resizeObserver = undefined;
|
|
@@ -211,63 +235,91 @@ class ChartComponent {
|
|
|
211
235
|
chart.setOption(options, true);
|
|
212
236
|
}
|
|
213
237
|
}
|
|
238
|
+
// Lần vẽ đầu (directive setOption lúc init) cũng có animation xuất hiện.
|
|
239
|
+
this.markAppear();
|
|
214
240
|
this.chartInit.emit(chart);
|
|
215
241
|
this.schedule(() => this.forceMediaRecalc());
|
|
216
242
|
}
|
|
217
243
|
/* ================= CORE FIX ================= */
|
|
218
244
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* ghi chú ở `forceMediaRecalc`.
|
|
245
|
+
* Đo host và ghi lại; trả về CÁI GÌ đổi so với lần đo trước.
|
|
246
|
+
* Khi bị ẩn (0×0) thì KHÔNG ghi — giữ kích thước hiển thị cuối, để lúc hiện
|
|
247
|
+
* lại cùng cỡ thì coi như không đổi (không resize, không cắt animation).
|
|
223
248
|
*/
|
|
224
|
-
|
|
225
|
-
const { width } = this.ref.nativeElement.getBoundingClientRect();
|
|
226
|
-
if (width <= 0)
|
|
227
|
-
return false;
|
|
249
|
+
measure() {
|
|
250
|
+
const { width, height } = this.ref.nativeElement.getBoundingClientRect();
|
|
251
|
+
if (width <= 0 || height <= 0)
|
|
252
|
+
return { size: false, mobile: false };
|
|
228
253
|
const isMobile = width < this.mobileWidth();
|
|
229
|
-
const
|
|
254
|
+
const size = width !== this.containerWidth || height !== this.containerHeight;
|
|
255
|
+
const mobile = isMobile !== this.isMobile;
|
|
230
256
|
this.containerWidth = width;
|
|
257
|
+
this.containerHeight = height;
|
|
231
258
|
this.isMobile = isMobile;
|
|
232
|
-
return
|
|
259
|
+
return { size, mobile };
|
|
260
|
+
}
|
|
261
|
+
/** Animation xuất hiện vừa bắt đầu — khoá resize tới khi nó xong. */
|
|
262
|
+
markAppear() {
|
|
263
|
+
this.appearUntil = performance.now() + ChartComponent.APPEAR_MS;
|
|
264
|
+
}
|
|
265
|
+
/** Pie/sunburst đặt legend + số giữa theo px của containerWidth → phải dựng lại. */
|
|
266
|
+
needsLayoutRebuild() {
|
|
267
|
+
const type = this.detectType();
|
|
268
|
+
return type === 'pie' || type === 'sunburst';
|
|
269
|
+
}
|
|
270
|
+
deferResize(ms) {
|
|
271
|
+
if (this.deferTimer !== undefined)
|
|
272
|
+
clearTimeout(this.deferTimer);
|
|
273
|
+
this.deferTimer = setTimeout(() => {
|
|
274
|
+
this.deferTimer = undefined;
|
|
275
|
+
if (!this.destroyed)
|
|
276
|
+
this.forceMediaRecalc();
|
|
277
|
+
}, ms);
|
|
233
278
|
}
|
|
234
279
|
/**
|
|
235
|
-
*
|
|
236
|
-
* **animation lúc chart xuất hiện bị giết**.
|
|
237
|
-
*
|
|
238
|
-
* Đường đi: `onChartInit` → schedule(forceMediaRecalc) → chạy ở frame KẾ
|
|
239
|
-
* TIẾP. Nếu chỗ này vô điều kiện `rebuildChart()` (clear + setOption notMerge)
|
|
240
|
-
* thì nó xoá sạch và vẽ lại từ đầu ~16ms sau lần vẽ đầu, trong khi animation
|
|
241
|
-
* xuất hiện dài ~1000ms ⇒ người dùng thấy chart hiện ra đứng im.
|
|
242
|
-
*
|
|
243
|
-
* Đo được: lần xuất hiện đầu 1/12 frame khác nhau (đứng yên) trong khi vẽ lại
|
|
244
|
-
* bằng `theme-changed` cho 9/12 → animation của ECharts vẫn bật, chỉ lần đầu
|
|
245
|
-
* bị cắt.
|
|
280
|
+
* Phản ứng với kích thước host (RO, sau init, sau khi hiện lại).
|
|
246
281
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
*
|
|
282
|
+
* Ba quy tắc, đều đo được (0.7.75 và 05/09):
|
|
283
|
+
* 1. **Ẩn (0×H) thì không làm gì** — không resize canvas về 0. Chart hiện
|
|
284
|
+
* lại cùng cỡ thì animation đã bắt đầu lúc ẩn (setOption khi resolver về,
|
|
285
|
+
* ngay trước NavigationEnd) chạy tiếp bình thường.
|
|
286
|
+
* 2. **`resize()` nhảy thẳng tới trạng thái cuối** ⇒ trong cửa sổ appear
|
|
287
|
+
* (`appearUntil`) thì HOÃN, đo lại và xử lý sau khi animation xong. Không
|
|
288
|
+
* dựng lại (clear + setOption) lúc đó vì đó là "vẽ 2 lần" theo cách khác.
|
|
289
|
+
* 3. Ngoài cửa sổ: `resize()` trước, rồi chỉ dựng lại khi preset đổi (mobile)
|
|
290
|
+
* hoặc loại chart đặt layout theo px (pie/sunburst). Thứ tự này quan
|
|
291
|
+
* trọng: resize SAU rebuild sẽ cắt ngay animation vừa bắt đầu.
|
|
252
292
|
*/
|
|
253
293
|
forceMediaRecalc(retry = 0) {
|
|
254
294
|
if (!this.liveChart)
|
|
255
295
|
return;
|
|
256
296
|
const { width, height } = this.ref.nativeElement.getBoundingClientRect();
|
|
257
|
-
if (
|
|
258
|
-
|
|
297
|
+
if (width === 0 || height === 0) {
|
|
298
|
+
// Chưa layout xong (ngay sau init) thì thử lại vài frame; đang ẩn thì thôi.
|
|
299
|
+
if (retry < 10)
|
|
300
|
+
this.schedule(() => this.forceMediaRecalc(retry + 1));
|
|
259
301
|
return;
|
|
260
302
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (
|
|
303
|
+
const change = this.measure();
|
|
304
|
+
const size = change.size || this.pendingResize;
|
|
305
|
+
const mobile = change.mobile || this.pendingMobile;
|
|
306
|
+
if (!size && !mobile)
|
|
307
|
+
return;
|
|
308
|
+
const remaining = this.appearUntil - performance.now();
|
|
309
|
+
if (remaining > 0) {
|
|
310
|
+
this.pendingResize = size;
|
|
311
|
+
this.pendingMobile = mobile;
|
|
312
|
+
this.deferResize(remaining + 30);
|
|
268
313
|
return;
|
|
269
|
-
|
|
270
|
-
this.
|
|
314
|
+
}
|
|
315
|
+
this.pendingResize = false;
|
|
316
|
+
this.pendingMobile = false;
|
|
317
|
+
const chart = this.liveChart;
|
|
318
|
+
if (!chart)
|
|
319
|
+
return;
|
|
320
|
+
chart.resize();
|
|
321
|
+
if (mobile || this.needsLayoutRebuild())
|
|
322
|
+
this.rebuildChart();
|
|
271
323
|
}
|
|
272
324
|
rebuildChart() {
|
|
273
325
|
if (this.destroyed)
|
|
@@ -295,6 +347,7 @@ class ChartComponent {
|
|
|
295
347
|
this.hasOptions = true;
|
|
296
348
|
chart.clear();
|
|
297
349
|
chart.setOption(options, true);
|
|
350
|
+
this.markAppear();
|
|
298
351
|
}
|
|
299
352
|
checkEmpty() {
|
|
300
353
|
const type = this.detectType();
|
|
@@ -717,11 +770,11 @@ class ChartComponent {
|
|
|
717
770
|
});
|
|
718
771
|
}
|
|
719
772
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ChartComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
720
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ChartComponent, isStandalone: true, selector: "fx-ui-chart", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, palette: { classPropertyName: "palette", publicName: "palette", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, pieLegendAlign: { classPropertyName: "pieLegendAlign", publicName: "pieLegendAlign", isSignal: true, isRequired: false, transformFunction: null }, pieLegendPosition: { classPropertyName: "pieLegendPosition", publicName: "pieLegendPosition", isSignal: true, isRequired: false, transformFunction: null }, showPieLabel: { classPropertyName: "showPieLabel", publicName: "showPieLabel", isSignal: true, isRequired: false, transformFunction: null }, showPieLegend: { classPropertyName: "showPieLegend", publicName: "showPieLegend", isSignal: true, isRequired: false, transformFunction: null }, pieCenterLabel: { classPropertyName: "pieCenterLabel", publicName: "pieCenterLabel", isSignal: true, isRequired: false, transformFunction: null }, mobileWidth: { classPropertyName: "mobileWidth", publicName: "mobileWidth", isSignal: true, isRequired: false, transformFunction: null }, labelFontFamily: { classPropertyName: "labelFontFamily", publicName: "labelFontFamily", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { chartClick: "chartClick", chartInit: "chartInit" }, host: { listeners: { "window:theme-changed": "onThemeChange()" } }, providers: [provideEchartsCore({ echarts: () => import('echarts') })], usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-wrapper\" [style.height]=\"height()\">\n @if (isEmpty) {\n <div class=\"chart-empty\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"chart-empty-icon\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z\" />\n </svg>\n <span class=\"chart-empty-text\">{{ emptyText() }}</span>\n </div>\n } @else if (hasOptions) {\n <div\n echarts\n [options]=\"chartOptions\"\n [loading]=\"loading()\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height()\"\n ></div>\n } @else {\n <!-- Until echarts is loaded + initialised (or the element scrolls into view)\n the panel keeps the chart's footprint with a quiet pulsing sketch\n instead of an empty box (anh T\u00FA 04/09: \"the page is too blank\"). -->\n <div class=\"chart-placeholder\" aria-hidden=\"true\">\n @for (h of placeholderBars; track $index) {\n <span class=\"chart-placeholder__bar\" [style.height.%]=\"h\"></span>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.chart-wrapper{position:relative;width:100%;height:100%}.chart-container{width:100%;height:100%}.chart-loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--bg-primary-hover)/.6);font-weight:500;font-size:1.1rem;z-index:2;border-radius:4px}.chart-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:12px;color:rgba(var(--text-secondary)/1)}.chart-empty-icon{width:48px;height:48px;opacity:.4}.chart-empty-text{font-size:14px;font-weight:500}.chart-placeholder{position:absolute;inset:0;display:flex;align-items:flex-end;gap:6px;padding:12px 8px 22px;animation:chart-placeholder-pulse 1.6s ease-in-out infinite}.chart-placeholder__bar{flex:1;border-radius:3px 3px 0 0;background:rgb(var(--og-track))}@keyframes chart-placeholder-pulse{0%,to{opacity:1}50%{opacity:.45}}@media(prefers-reduced-motion:reduce){.chart-placeholder{animation:none}}\n"], dependencies: [{ kind: "directive", type: NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
773
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: ChartComponent, isStandalone: true, selector: "fx-ui-chart", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, palette: { classPropertyName: "palette", publicName: "palette", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, pieLegendAlign: { classPropertyName: "pieLegendAlign", publicName: "pieLegendAlign", isSignal: true, isRequired: false, transformFunction: null }, pieLegendPosition: { classPropertyName: "pieLegendPosition", publicName: "pieLegendPosition", isSignal: true, isRequired: false, transformFunction: null }, showPieLabel: { classPropertyName: "showPieLabel", publicName: "showPieLabel", isSignal: true, isRequired: false, transformFunction: null }, showPieLegend: { classPropertyName: "showPieLegend", publicName: "showPieLegend", isSignal: true, isRequired: false, transformFunction: null }, pieCenterLabel: { classPropertyName: "pieCenterLabel", publicName: "pieCenterLabel", isSignal: true, isRequired: false, transformFunction: null }, mobileWidth: { classPropertyName: "mobileWidth", publicName: "mobileWidth", isSignal: true, isRequired: false, transformFunction: null }, labelFontFamily: { classPropertyName: "labelFontFamily", publicName: "labelFontFamily", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { chartClick: "chartClick", chartInit: "chartInit" }, host: { listeners: { "window:theme-changed": "onThemeChange()" } }, providers: [provideEchartsCore({ echarts: () => import('echarts') })], usesOnChanges: true, ngImport: i0, template: "<div class=\"chart-wrapper\" [style.height]=\"height()\">\n @if (isEmpty) {\n <div class=\"chart-empty\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"chart-empty-icon\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z\" />\n </svg>\n <span class=\"chart-empty-text\">{{ emptyText() }}</span>\n </div>\n } @else if (hasOptions) {\n <div\n echarts\n [options]=\"chartOptions\"\n [autoResize]=\"false\"\n [loading]=\"loading()\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height()\"\n ></div>\n } @else {\n <!-- Until echarts is loaded + initialised (or the element scrolls into view)\n the panel keeps the chart's footprint with a quiet pulsing sketch\n instead of an empty box (anh T\u00FA 04/09: \"the page is too blank\"). -->\n <div class=\"chart-placeholder\" aria-hidden=\"true\">\n @for (h of placeholderBars; track $index) {\n <span class=\"chart-placeholder__bar\" [style.height.%]=\"h\"></span>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.chart-wrapper{position:relative;width:100%;height:100%}.chart-container{width:100%;height:100%}.chart-loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--bg-primary-hover)/.6);font-weight:500;font-size:1.1rem;z-index:2;border-radius:4px}.chart-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:12px;color:rgba(var(--text-secondary)/1)}.chart-empty-icon{width:48px;height:48px;opacity:.4}.chart-empty-text{font-size:14px;font-weight:500}.chart-placeholder{position:absolute;inset:0;display:flex;align-items:flex-end;gap:6px;padding:12px 8px 22px;animation:chart-placeholder-pulse 1.6s ease-in-out infinite}.chart-placeholder__bar{flex:1;border-radius:3px 3px 0 0;background:rgb(var(--og-track))}@keyframes chart-placeholder-pulse{0%,to{opacity:1}50%{opacity:.45}}@media(prefers-reduced-motion:reduce){.chart-placeholder{animation:none}}\n"], dependencies: [{ kind: "directive", type: NgxEchartsDirective, selector: "echarts, [echarts]", inputs: ["options", "theme", "initOpts", "merge", "autoResize", "loading", "loadingType", "loadingOpts"], outputs: ["chartInit", "optionsError", "chartClick", "chartDblClick", "chartMouseDown", "chartMouseMove", "chartMouseUp", "chartMouseOver", "chartMouseOut", "chartGlobalOut", "chartContextMenu", "chartHighlight", "chartDownplay", "chartSelectChanged", "chartLegendSelectChanged", "chartLegendSelected", "chartLegendUnselected", "chartLegendLegendSelectAll", "chartLegendLegendInverseSelect", "chartLegendScroll", "chartDataZoom", "chartDataRangeSelected", "chartGraphRoam", "chartGeoRoam", "chartTreeRoam", "chartTimelineChanged", "chartTimelinePlayChanged", "chartRestore", "chartDataViewChanged", "chartMagicTypeChanged", "chartGeoSelectChanged", "chartGeoSelected", "chartGeoUnselected", "chartAxisAreaSelected", "chartBrush", "chartBrushEnd", "chartBrushSelected", "chartGlobalCursorTaken", "chartRendered", "chartFinished"], exportAs: ["echarts"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
721
774
|
}
|
|
722
775
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ChartComponent, decorators: [{
|
|
723
776
|
type: Component,
|
|
724
|
-
args: [{ selector: 'fx-ui-chart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgxEchartsDirective, InputComponent], providers: [provideEchartsCore({ echarts: () => import('echarts') })], template: "<div class=\"chart-wrapper\" [style.height]=\"height()\">\n @if (isEmpty) {\n <div class=\"chart-empty\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"chart-empty-icon\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z\" />\n </svg>\n <span class=\"chart-empty-text\">{{ emptyText() }}</span>\n </div>\n } @else if (hasOptions) {\n <div\n echarts\n [options]=\"chartOptions\"\n [loading]=\"loading()\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height()\"\n ></div>\n } @else {\n <!-- Until echarts is loaded + initialised (or the element scrolls into view)\n the panel keeps the chart's footprint with a quiet pulsing sketch\n instead of an empty box (anh T\u00FA 04/09: \"the page is too blank\"). -->\n <div class=\"chart-placeholder\" aria-hidden=\"true\">\n @for (h of placeholderBars; track $index) {\n <span class=\"chart-placeholder__bar\" [style.height.%]=\"h\"></span>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.chart-wrapper{position:relative;width:100%;height:100%}.chart-container{width:100%;height:100%}.chart-loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--bg-primary-hover)/.6);font-weight:500;font-size:1.1rem;z-index:2;border-radius:4px}.chart-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:12px;color:rgba(var(--text-secondary)/1)}.chart-empty-icon{width:48px;height:48px;opacity:.4}.chart-empty-text{font-size:14px;font-weight:500}.chart-placeholder{position:absolute;inset:0;display:flex;align-items:flex-end;gap:6px;padding:12px 8px 22px;animation:chart-placeholder-pulse 1.6s ease-in-out infinite}.chart-placeholder__bar{flex:1;border-radius:3px 3px 0 0;background:rgb(var(--og-track))}@keyframes chart-placeholder-pulse{0%,to{opacity:1}50%{opacity:.45}}@media(prefers-reduced-motion:reduce){.chart-placeholder{animation:none}}\n"] }]
|
|
777
|
+
args: [{ selector: 'fx-ui-chart', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgxEchartsDirective, InputComponent], providers: [provideEchartsCore({ echarts: () => import('echarts') })], template: "<div class=\"chart-wrapper\" [style.height]=\"height()\">\n @if (isEmpty) {\n <div class=\"chart-empty\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"chart-empty-icon\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z\" />\n </svg>\n <span class=\"chart-empty-text\">{{ emptyText() }}</span>\n </div>\n } @else if (hasOptions) {\n <div\n echarts\n [options]=\"chartOptions\"\n [autoResize]=\"false\"\n [loading]=\"loading()\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height()\"\n ></div>\n } @else {\n <!-- Until echarts is loaded + initialised (or the element scrolls into view)\n the panel keeps the chart's footprint with a quiet pulsing sketch\n instead of an empty box (anh T\u00FA 04/09: \"the page is too blank\"). -->\n <div class=\"chart-placeholder\" aria-hidden=\"true\">\n @for (h of placeholderBars; track $index) {\n <span class=\"chart-placeholder__bar\" [style.height.%]=\"h\"></span>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;width:100%}.chart-wrapper{position:relative;width:100%;height:100%}.chart-container{width:100%;height:100%}.chart-loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(var(--bg-primary-hover)/.6);font-weight:500;font-size:1.1rem;z-index:2;border-radius:4px}.chart-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:12px;color:rgba(var(--text-secondary)/1)}.chart-empty-icon{width:48px;height:48px;opacity:.4}.chart-empty-text{font-size:14px;font-weight:500}.chart-placeholder{position:absolute;inset:0;display:flex;align-items:flex-end;gap:6px;padding:12px 8px 22px;animation:chart-placeholder-pulse 1.6s ease-in-out infinite}.chart-placeholder__bar{flex:1;border-radius:3px 3px 0 0;background:rgb(var(--og-track))}@keyframes chart-placeholder-pulse{0%,to{opacity:1}50%{opacity:.45}}@media(prefers-reduced-motion:reduce){.chart-placeholder{animation:none}}\n"] }]
|
|
725
778
|
}], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], palette: [{ type: i0.Input, args: [{ isSignal: true, alias: "palette", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], pieLegendAlign: [{ type: i0.Input, args: [{ isSignal: true, alias: "pieLegendAlign", required: false }] }], pieLegendPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "pieLegendPosition", required: false }] }], showPieLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPieLabel", required: false }] }], showPieLegend: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPieLegend", required: false }] }], pieCenterLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pieCenterLabel", required: false }] }], mobileWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "mobileWidth", required: false }] }], labelFontFamily: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelFontFamily", required: false }] }], emptyText: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyText", required: false }] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }], chartInit: [{ type: i0.Output, args: ["chartInit"] }], onThemeChange: [{
|
|
726
779
|
type: HostListener,
|
|
727
780
|
args: ['window:theme-changed']
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foxeltech-angular-ui-chart.mjs","sources":["../../../projects/ui/chart/src/chart/chart.component.ts","../../../projects/ui/chart/src/chart/chart.component.html","../../../projects/ui/chart/src/preload-echarts.ts","../../../projects/ui/chart/src/foxeltech-angular-ui-chart.ts"],"sourcesContent":["import {\n AfterViewInit,\n ChangeDetectorRef,\n Component,\n ElementRef,\n HostListener,\n OnChanges,\n OnDestroy,\n ChangeDetectionStrategy,\n input,\n output\n} from '@angular/core';\nimport { ECharts, EChartsOption } from 'echarts';\nimport { FxUtils, InputComponent } from '@foxeltech/angular-ui';\nimport { NgxEchartsDirective, provideEchartsCore } from 'ngx-echarts';\n\n\n// 'radar' was advertised here but has no builder in the registry — it\n// silently rendered an empty chart. Re-add when implemented.\n// V2 presets: 'line' (risk score by release), 'area-trend' (weekly trend with\n// accent fill + gray comparison line). Both need an explicit [type] — 'auto'\n// cannot tell them apart from 'bar'.\nexport type IChartType = 'bar' | 'pie' | 'sunburst' | 'line' | 'area-trend' | 'auto';\n\n@Component({\n selector: 'fx-ui-chart',\n templateUrl: './chart.component.html',\n styleUrl: './chart.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgxEchartsDirective, InputComponent],\n // Self-contained: echarts is provided (and lazy-loaded) right here, so\n // consumers just import ChartComponent — no forRoot wiring in the app.\n providers: [provideEchartsCore({ echarts: () => import('echarts') })],\n})\nexport class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {\n /* ================= INPUTS ================= */\n\n readonly type = input<IChartType>('auto');\n readonly data = input<any>();\n readonly options = input<EChartsOption>();\n readonly palette = input<string[]>();\n readonly height = input('400px');\n readonly loading = input(false);\n\n // pie controls\n readonly pieLegendAlign = input<string | number>('10%');\n readonly pieLegendPosition = input<string | number>('middle');\n readonly showPieLabel = input(true);\n /** Hide the built-in legend (panels that render their own legend beside the\n donut); the donut then centres in the container. */\n readonly showPieLegend = input(true);\n /** Caption under the donut's centre total (design: \"42 / confirmed\"). */\n readonly pieCenterLabel = input('');\n readonly mobileWidth = input(350);\n /** Override font for canvas text; empty = follow the theme's --og-font-figure. */\n readonly labelFontFamily = input('');\n readonly emptyText = input('No data available');\n\n /* ================= OUTPUTS ================= */\n\n readonly chartClick = output<any>();\n readonly chartInit = output<ECharts>();\n\n /* ================= INTERNAL ================= */\n\n chartOptions: EChartsOption = {};\n isEmpty = false;\n /**\n * ⚠️ Cổng cho `<div echarts>` trong template. Trước đây div render NGAY với\n * `chartOptions = {}`, nên NgxEchartsDirective khởi tạo chart bằng options\n * RỖNG; options thật gán vào giữa lúc `await createChart()` của lần đó ⇒\n * onOptionsChange() thấy `chart == null` và createChart() LẦN HAI trên cùng\n * DOM node ⇒ \"[ECharts] There is a chart instance already initialized on the\n * dom.\". Chỉ dựng directive khi đã có options thật ⇒ đúng một lần init.\n */\n hasOptions = false;\n /** Heights (%) of the pre-init placeholder bars — a fixed, calm silhouette. */\n readonly placeholderBars = [34, 52, 44, 66, 58, 78, 70, 88, 62, 74];\n labelFontSize = 12;\n\n private chartInstance?: ECharts;\n private resizeObserver?: ResizeObserver;\n private intersectionObserver?: IntersectionObserver;\n\n private canInit = false;\n private destroyed = false;\n /**\n * ⚠️ Đang chờ init đầu tiên của directive hoàn tất?\n *\n * NgxEchartsDirective đăng ký `changeFilter.notFirstAndEmpty('options', …)`:\n * lần gán `options` ĐẦU bị bỏ qua (ngAfterViewInit lo), lần gán THỨ HAI gọi\n * onOptionsChange(). Mà onOptionsChange() chỉ createChart() khi `this.chart`\n * còn null — và nó null suốt thời gian `await createChart()` của lần đầu.\n * Nên nếu ta gán `chartOptions` (object mới) trong khoảng await đó thì\n * init() chạy HAI LẦN trên cùng DOM node ⇒\n * \"[ECharts] There is a chart instance already initialized on the dom.\"\n *\n * Cách chặn: trong khoảng chờ, giữ options ở `deferredOptions` chứ không đụng\n * vào property đang bind; init xong thì áp một lần qua instance.\n */\n private awaitingFirstInit = false;\n private deferredOptions?: EChartsOption;\n private pendingFrames = new Set<number>();\n private isMobile = false;\n private containerWidth = 0;\n\n constructor(\n private ref: ElementRef<HTMLElement>,\n private cdr: ChangeDetectorRef,\n ) {}\n\n /* ================= LIFECYCLE ================= */\n\n ngAfterViewInit(): void {\n // Only initialize when visible (tab-safe)\n this.intersectionObserver = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting && !this.canInit && !this.destroyed) {\n this.canInit = true;\n this.updateResponsiveState();\n this.rebuildChart();\n this.cdr.detectChanges();\n }\n },\n { threshold: 0.1 },\n );\n\n this.intersectionObserver.observe(this.ref.nativeElement);\n\n // Resize-safe (tabs, flex, async layout)\n this.resizeObserver = new ResizeObserver(() => {\n if (!this.canInit || this.destroyed) return;\n // forceMediaRecalc tự đo lại và chỉ dựng lại khi kích thước đổi.\n this.forceMediaRecalc();\n });\n\n this.resizeObserver.observe(this.ref.nativeElement);\n }\n\n ngOnChanges(): void {\n if (this.canInit && !this.destroyed) {\n this.updateResponsiveState();\n this.rebuildChart();\n }\n }\n\n ngOnDestroy(): void {\n // NgxEchartsDirective tự dispose instance của nó, nên KHÔNG dispose ở đây\n // (sẽ dispose trùng). Việc phải làm là ngắt mọi đường còn chạm được vào\n // instance sau khi nó đã bị dispose: callback rAF đang chờ + tham chiếu\n // `chartInstance` mà forceMediaRecalc/rebuildChart vẫn dùng.\n this.destroyed = true;\n\n for (const id of this.pendingFrames) cancelAnimationFrame(id);\n this.pendingFrames.clear();\n\n this.resizeObserver?.disconnect();\n this.intersectionObserver?.disconnect();\n this.resizeObserver = undefined;\n this.intersectionObserver = undefined;\n\n this.chartInstance = undefined;\n this.awaitingFirstInit = false;\n this.deferredOptions = undefined;\n this.sweepOrphanInstance();\n }\n\n /**\n * ⚠️ RACE TRONG NGX-ECHARTS v20 — nguồn của\n * \"[ECharts] There is a chart instance already initialized on the dom.\"\n *\n * NgxEchartsDirective.onOptionsChange() làm:\n * this.chart = await this.createChart(); // ← destroy có thể rơi vào đây\n * this.chart$.next(this.chart);\n * this.chartInit.emit(this.chart);\n *\n * Destroy đúng lúc đang await thì ngOnDestroy → dispose() thấy `this.chart == null`\n * nên KHÔNG dispose gì. Sau đó await resolve, gán một instance ECharts còn sống cho\n * directive đã chết → instance MỒ CÔI, vẫn nằm trong registry của echarts gắn với\n * DOM node đó. Angular tái dùng node ⇒ init() lần sau báo \"already initialized\".\n *\n * Không thể bắt qua (chartInit): Angular v20 TỪ CHỐI emit trên OutputRef đã destroy\n * (chỉ log NG0953, listener không hề được gọi). Nên đường duy nhất là hỏi thẳng\n * echarts xem node còn instance nào không, ở các macrotask SAU destroy.\n */\n private sweepOrphanInstance(): void {\n const host = this.ref.nativeElement.querySelector<HTMLElement>('.fx-chart');\n if (!host) return;\n\n // Chỉ nạp echarts nếu nó đã ở trong module cache — không tự kéo bundle về\n // chỉ để đi dọn.\n let attempt = 0;\n const sweep = () => {\n import('echarts').then(({ getInstanceByDom }) => {\n const orphan = getInstanceByDom(host);\n if (orphan) {\n if (!orphan.isDisposed()) orphan.dispose();\n return;\n }\n // createChart() phải chờ dynamic import nên instance mồ côi có thể xuất\n // hiện muộn hơn một macrotask; thử lại vài nhịp rồi dừng.\n if (++attempt < 5) setTimeout(sweep, 50 * attempt);\n });\n };\n\n setTimeout(sweep);\n }\n\n /** rAF có thể huỷ được khi destroy — tránh chạm instance đã dispose. */\n private schedule(fn: () => void): void {\n if (this.destroyed) return;\n const id = requestAnimationFrame(() => {\n this.pendingFrames.delete(id);\n if (this.destroyed) return;\n fn();\n });\n this.pendingFrames.add(id);\n }\n\n /** Instance còn dùng được? (đã dispose thì mọi API echarts đều ném lỗi) */\n private get liveChart(): ECharts | undefined {\n const chart = this.chartInstance;\n if (!chart || this.destroyed || chart.isDisposed()) return undefined;\n return chart;\n }\n\n @HostListener('window:theme-changed')\n onThemeChange(): void {\n if (!this.canInit || this.destroyed) return;\n this.schedule(() => this.rebuildChart());\n }\n\n /* ================= EVENTS ================= */\n\n onChartClick(event: any) {\n this.chartClick.emit(event);\n }\n\n // ⚠️ Phải để `any`: NgxEchartsDirective emit `EChartsType` từ\n // 'echarts/types/dist/shared', TS coi nó KHÔNG tương thích với `ECharts`\n // của 'echarts' (private `_ssr` khai báo riêng) → template typecheck fail.\n onChartInit(raw: any) {\n const chart = raw as ECharts;\n // Phòng thân: bình thường Angular đã chặn emit sau destroy (NG0953) nên\n // nhánh này không tới; nhưng nếu tới thì tuyệt đối không được gán lại\n // `chartInstance` sau ngOnDestroy. Việc thu dọn instance mồ côi do\n // sweepOrphanInstance() lo — xem ghi chú ở đó.\n if (this.destroyed) {\n if (!chart.isDisposed()) chart.dispose();\n return;\n }\n\n this.chartInstance = chart;\n this.awaitingFirstInit = false;\n\n if (this.deferredOptions) {\n const options = this.deferredOptions;\n this.deferredOptions = undefined;\n this.chartOptions = options;\n if (!chart.isDisposed()) {\n chart.clear();\n chart.setOption(options, true);\n }\n }\n\n this.chartInit.emit(chart);\n\n this.schedule(() => this.forceMediaRecalc());\n }\n\n /* ================= CORE FIX ================= */\n\n /**\n * @returns true nếu trạng thái responsive THỰC SỰ đổi (nên dựng lại options).\n *\n * Trả về boolean là điều then chốt cho animation lúc chart xuất hiện — xem\n * ghi chú ở `forceMediaRecalc`.\n */\n private updateResponsiveState(): boolean {\n const { width } = this.ref.nativeElement.getBoundingClientRect();\n if (width <= 0) return false;\n\n const isMobile = width < this.mobileWidth();\n const changed = width !== this.containerWidth || isMobile !== this.isMobile;\n this.containerWidth = width;\n this.isMobile = isMobile;\n return changed;\n }\n\n /**\n * ⚠️ CHỈ dựng lại options khi kích thước thật sự đổi — nếu không thì\n * **animation lúc chart xuất hiện bị giết**.\n *\n * Đường đi: `onChartInit` → schedule(forceMediaRecalc) → chạy ở frame KẾ\n * TIẾP. Nếu chỗ này vô điều kiện `rebuildChart()` (clear + setOption notMerge)\n * thì nó xoá sạch và vẽ lại từ đầu ~16ms sau lần vẽ đầu, trong khi animation\n * xuất hiện dài ~1000ms ⇒ người dùng thấy chart hiện ra đứng im.\n *\n * Đo được: lần xuất hiện đầu 1/12 frame khác nhau (đứng yên) trong khi vẽ lại\n * bằng `theme-changed` cho 9/12 → animation của ECharts vẫn bật, chỉ lần đầu\n * bị cắt.\n *\n * Trước Pha 0 lỗi này bị che: lần init đầu dùng `chartOptions = {}` nên\n * animation xuất hiện xảy ra ở lần cập nhật SAU, tức sau khi forceMediaRecalc\n * đã chạy xong. Cổng `hasOptions` đưa options thật về ngay lần init nên lộ ra.\n *\n * `resize()` thì luôn gọi được — nó không cắt animation.\n */\n private forceMediaRecalc(retry = 0) {\n if (!this.liveChart) return;\n\n const { width, height } = this.ref.nativeElement.getBoundingClientRect();\n\n if ((width === 0 || height === 0) && retry < 10) {\n this.schedule(() => this.forceMediaRecalc(retry + 1));\n return;\n }\n\n // ⚠️ `resize()` cũng phải CÓ ĐIỀU KIỆN. ECharts nhảy thẳng tới trạng thái\n // cuối khi resize, nên gọi nó ở frame ngay sau init sẽ CẮT animation lúc\n // chart xuất hiện — đo được: 3 lần đổi khung trong 200ms rồi đứng im, dù\n // đã đặt animationDuration 3000ms.\n // Không cần resize ở lần đầu: ECharts tự lấy kích thước khi init, và vòng\n // retry ở trên đã lo ca width/height = 0.\n if (!this.updateResponsiveState()) return;\n\n this.rebuildChart();\n this.liveChart?.resize();\n }\n\n private rebuildChart(): void {\n if (this.destroyed) return;\n\n this.isEmpty = this.checkEmpty();\n\n if (this.isEmpty) {\n this.liveChart?.clear();\n return;\n }\n\n const options = this.buildOptions();\n const chart = this.liveChart;\n\n if (!chart) {\n if (this.awaitingFirstInit) {\n // Đang trong khoảng await của lần init đầu — KHÔNG gán vào property\n // đang bind, xem ghi chú ở `awaitingFirstInit`.\n this.deferredOptions = options;\n return;\n }\n this.awaitingFirstInit = true;\n this.chartOptions = options;\n this.hasOptions = true;\n return;\n }\n\n this.chartOptions = options;\n this.hasOptions = true;\n chart.clear();\n chart.setOption(options, true);\n }\n\n private checkEmpty(): boolean {\n const type = this.detectType();\n\n if (type === 'bar') {\n const data = this.data();\n const categories = data?.categories;\n const values = data?.values;\n return !categories?.length || !values?.length;\n }\n\n if (type === 'line') {\n const data = this.data();\n return !data?.categories?.length || !data?.values?.length;\n }\n\n if (type === 'area-trend') {\n const data = this.data();\n return !data?.categories?.length || !data?.current?.length;\n }\n\n if (type === 'pie') {\n const data = this.data();\n if (!Array.isArray(data) || data.length === 0) return true;\n const total = data.reduce((sum: number, d: any) => sum + (d.value || 0), 0);\n return total === 0;\n }\n\n return false;\n }\n\n /* ================= REGISTRY ================= */\n\n private registry: Record<string, (data: any, palette: string[]) => EChartsOption> = {\n bar: (data, colors) => ({\n color: colors,\n tooltip: { trigger: 'axis' },\n grid: { left: '5%', right: '5%', bottom: '10%', containLabel: true },\n xAxis: {\n type: 'category',\n data: data?.categories || [],\n axisLabel: {\n interval: 0,\n width: 80,\n overflow: 'truncate',\n ellipsis: '…',\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n axisLine: {\n lineStyle: {\n color: FxUtils.convertColorFromVariable('--border-strong'),\n },\n },\n },\n yAxis: {\n type: 'value',\n axisLabel: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n axisLine: {\n lineStyle: {\n color: FxUtils.convertColorFromVariable('--border-strong'),\n },\n },\n splitLine: {\n lineStyle: {\n color: FxUtils.convertColorFromVariable('--border-default'),\n },\n },\n },\n series: [\n {\n type: 'bar',\n data: data?.values || [],\n barMaxWidth: 70,\n itemStyle: {\n color: (p: any) => colors[p.dataIndex % colors.length],\n },\n label: {\n show: true,\n position: 'top',\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-primary'),\n },\n },\n ],\n }),\n\n pie: (data, colors) => {\n const total = data.reduce((sum: number, d: any) => sum + d.value, 0);\n const layout = this.calcPieLayout(data);\n\n return {\n color: colors,\n tooltip: { trigger: 'item' },\n\n legend: {\n show: this.showPieLegend(),\n type: 'scroll' as const,\n orient: layout.legendOrient,\n icon: 'circle',\n itemGap: 12,\n\n right: layout.legendRight,\n top: layout.legendTop,\n left: layout.legendLeft,\n bottom: layout.legendBottom,\n width: layout.legendWidth,\n\n formatter: (name: string) => {\n const item = data.find((d: any) => d.name === name);\n return item ? `${name}: ${item.value}` : name;\n },\n\n textStyle: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: 13,\n fontWeight: 500,\n color: FxUtils.convertColorFromVariable('--text-primary'),\n overflow: 'truncate',\n },\n },\n\n series: [\n {\n type: 'pie',\n radius: ['40%', '70%'],\n center: layout.pieCenter,\n data,\n\n label: {\n show: this.showPieLabel(),\n position: 'outside',\n formatter: '{d}%',\n fontSize: this.labelFontSize,\n fontFamily: this.resolvedFontFamily(),\n color: FxUtils.convertColorFromVariable('--text-primary'),\n textBorderWidth: 0,\n textBorderColor: 'transparent',\n },\n\n labelLine: {\n show: this.showPieLabel(),\n length: 7,\n length2: 7,\n },\n },\n ],\n\n // Centre figure: positioned by x/y in pixels — graphic left/top\n // percentages place the element's bounding-box EDGE, which shifted the\n // total right/down by half its own size (mis-centred, caption clipped).\n graphic: (() => {\n const cx = (this.containerWidth || 400) * (parseFloat(layout.pieCenter[0]) / 100);\n const ch = this.ref.nativeElement.clientHeight || 300;\n const cy = ch * (parseFloat(layout.pieCenter[1]) / 100);\n return [\n {\n type: 'text' as const,\n // Shift the total up when a caption sits under it.\n x: cx,\n y: cy + (this.pieCenterLabel() ? -9 : 0),\n style: {\n text: total.toString(),\n fontSize: 24,\n fontWeight: 600,\n fontFamily: this.resolvedFontFamily(),\n fill: FxUtils.convertColorFromVariable('--text-primary'),\n align: 'center' as const,\n verticalAlign: 'middle' as const,\n },\n },\n ...(this.pieCenterLabel()\n ? [\n {\n type: 'text' as const,\n x: cx,\n y: cy + 12,\n style: {\n text: this.pieCenterLabel(),\n fontSize: 11,\n fontFamily: this.resolvedFontFamily(),\n fill: FxUtils.convertColorFromVariable('--text-secondary'),\n align: 'center' as const,\n verticalAlign: 'middle' as const,\n },\n },\n ]\n : []),\n ];\n })(),\n };\n },\n // V2 preset: single line, accent-coloured — \"risk score by release\".\n line: (data) => ({\n tooltip: { trigger: 'axis' },\n grid: { left: '3%', right: '4%', bottom: '8%', top: '8%', containLabel: true },\n xAxis: this.categoryAxis(data?.categories || []),\n yAxis: this.valueAxis(),\n series: [\n {\n type: 'line',\n data: data?.values || [],\n smooth: false,\n symbol: 'circle',\n symbolSize: 7,\n lineStyle: { width: 2, color: FxUtils.convertColorFromVariable('--og-accent', 1, { legacyCommaSyntax: true }) },\n itemStyle: { color: FxUtils.convertColorFromVariable('--og-accent', 1, { legacyCommaSyntax: true }) },\n label: {\n show: true,\n position: 'top',\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-primary'),\n },\n },\n ],\n }),\n\n // V2 preset: weekly trend — accent gradient fill + gray comparison line.\n // data: { categories: string[], current: number[], previous?: number[] }\n 'area-trend': (data) => {\n // legacyCommaSyntax: zrender's colour parser chokes on CSS4 space syntax.\n const accent = FxUtils.convertColorFromVariable('--og-accent', 1, { legacyCommaSyntax: true });\n const accentSoft = FxUtils.convertColorFromVariable('--og-accent', 0.25, { legacyCommaSyntax: true });\n const accentFade = FxUtils.convertColorFromVariable('--og-accent', 0.02, { legacyCommaSyntax: true });\n const series: any[] = [\n {\n type: 'line',\n name: data?.currentLabel || 'Current',\n data: data?.current || [],\n smooth: true,\n symbol: 'none',\n lineStyle: { width: 2, color: accent },\n itemStyle: { color: accent },\n areaStyle: {\n color: {\n type: 'linear',\n x: 0, y: 0, x2: 0, y2: 1,\n colorStops: [\n { offset: 0, color: accentSoft },\n { offset: 1, color: accentFade },\n ],\n },\n },\n },\n ];\n if (data?.previous?.length) {\n series.push({\n type: 'line',\n name: data?.previousLabel || 'Previous',\n data: data.previous,\n smooth: true,\n symbol: 'none',\n lineStyle: {\n width: 1.5,\n type: 'dashed',\n color: FxUtils.convertColorFromVariable('--text-placeholder', 1, { legacyCommaSyntax: true }),\n },\n itemStyle: { color: FxUtils.convertColorFromVariable('--text-placeholder', 1, { legacyCommaSyntax: true }) },\n });\n }\n return {\n tooltip: { trigger: 'axis' },\n grid: { left: '3%', right: '4%', bottom: '8%', top: '10%', containLabel: true },\n xAxis: { ...this.categoryAxis(data?.categories || []), boundaryGap: false },\n yAxis: this.valueAxis(),\n series,\n };\n },\n\n sunburst: (data, colors) => ({\n color: colors,\n series: [\n {\n type: 'sunburst',\n data,\n radius: ['15%', '90%'],\n itemStyle: {\n borderWidth: 1,\n borderColor: FxUtils.convertColorFromVariable('--border-default'),\n },\n label: {\n rotate: 'tangential',\n fontSize: this.labelFontSize,\n fontFamily: this.resolvedFontFamily(),\n color: FxUtils.convertColorFromVariable('--text-primary'),\n textBorderWidth: 0,\n textBorderColor: 'transparent',\n },\n },\n ],\n }),\n };\n\n /* ================= PIE LAYOUT ================= */\n\n private calcPieLayout(data: any[]) {\n const cw = this.containerWidth || 400;\n\n if (!this.showPieLegend()) {\n return {\n legendOrient: 'vertical' as const,\n legendLeft: undefined as string | undefined,\n legendRight: undefined as string | undefined,\n legendTop: undefined as string | undefined,\n legendBottom: undefined as number | undefined,\n legendWidth: undefined as number | undefined,\n pieCenter: ['50%', '50%'],\n graphicLeft: '50%',\n graphicTop: '50%',\n };\n }\n\n if (this.isMobile) {\n return {\n legendOrient: 'horizontal' as const,\n legendLeft: 'center' as string | undefined,\n legendRight: undefined as string | undefined,\n legendTop: undefined as string | undefined,\n legendBottom: 0 as number | undefined,\n legendWidth: undefined as number | undefined,\n pieCenter: ['50%', '40%'],\n graphicLeft: '50%',\n graphicTop: '40%',\n };\n }\n\n // Estimate legend pixel width from longest formatted label\n const CHAR_WIDTH = 7.5; // approx px per char at fontSize 13\n const ICON_GAP = 30; // legend icon + padding\n const longestLabel = data.reduce((max: number, d: any) => {\n return Math.max(max, `${d.name}: ${d.value}`.length);\n }, 0);\n const rawLegendPx = longestLabel * CHAR_WIDTH + ICON_GAP;\n\n // Clamp: min 100px, max 45% of container\n const legendPx = Math.max(100, Math.min(rawLegendPx, cw * 0.45));\n const legendPct = (legendPx / cw) * 100;\n\n // Pie center: middle of the remaining space after legend\n const pieCenterX = Math.round((100 - legendPct) / 2);\n\n return {\n legendOrient: 'vertical' as const,\n legendLeft: undefined,\n legendRight: '2%',\n legendTop: 'middle' as string | undefined,\n legendBottom: undefined,\n legendWidth: Math.round(legendPx),\n pieCenter: [`${pieCenterX}%`, '50%'],\n graphicLeft: `${pieCenterX}%`,\n graphicTop: '50%',\n };\n }\n\n /* ================= BUILD ================= */\n\n private detectType(): string {\n const type = this.type();\n if (type !== 'auto') return type;\n const data = this.data();\n if (data?.children) return 'sunburst';\n if (data?.categories && data?.values) return 'bar';\n if (Array.isArray(data)) return 'pie';\n return 'bar';\n }\n\n private buildOptions(): EChartsOption {\n const type = this.detectType();\n const palette = this.resolvePalette();\n const base = this.registry[type]?.(this.data(), palette) || {};\n\n return {\n ...base,\n ...this.options(),\n };\n }\n\n private categoryAxis(categories: string[]) {\n return {\n type: 'category' as const,\n data: categories,\n axisLabel: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n axisLine: {\n lineStyle: { color: FxUtils.convertColorFromVariable('--border-strong') },\n },\n };\n }\n\n private valueAxis() {\n return {\n type: 'value' as const,\n axisLabel: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n splitLine: {\n lineStyle: { color: FxUtils.convertColorFromVariable('--border-default') },\n },\n };\n }\n\n /** Canvas không ăn var() — resolve font stack thật tại render; chart đã\n rebuild theo event theme-changed nên tự đổi font khi đổi theme\n (blueprint/terminal dùng mono qua --og-font-figure). */\n private resolvedFontFamily(): string {\n const override = this.labelFontFamily();\n if (override) return override;\n // Dev-mode CSS pretty-printer bẻ dòng giá trị dài — normalize whitespace\n // trước khi chuỗi này vào ctx.font của canvas.\n const figure = getComputedStyle(document.body)\n .getPropertyValue('--og-font-figure')\n .replace(/\\s+/g, ' ')\n .trim();\n return figure || 'Archivo, system-ui, sans-serif';\n }\n\n private resolvePalette(): string[] {\n const defaults = [\n 'rgba(var(--success) / 1)',\n 'rgba(var(--information) / 1)',\n 'rgba(var(--critical) / 1)',\n 'rgba(var(--danger) / 1)',\n 'rgba(var(--warning) / 1)',\n 'rgba(var(--discovery) / 1)',\n ];\n\n const paletteValue = this.palette();\n const palette = paletteValue?.length ? paletteValue : defaults;\n\n return palette.map((raw) => {\n if (!raw.includes('var(')) return raw;\n\n const cssVar = raw.match(/var\\((.*?)\\)/)?.[1];\n const rgb = getComputedStyle(document.body)\n .getPropertyValue(cssVar || '')\n .trim()\n .replace(/\\s+/g, ',');\n return `rgba(${rgb},1)`;\n });\n }\n}\n","<div class=\"chart-wrapper\" [style.height]=\"height()\">\n @if (isEmpty) {\n <div class=\"chart-empty\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"chart-empty-icon\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z\" />\n </svg>\n <span class=\"chart-empty-text\">{{ emptyText() }}</span>\n </div>\n } @else if (hasOptions) {\n <div\n echarts\n [options]=\"chartOptions\"\n [loading]=\"loading()\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height()\"\n ></div>\n } @else {\n <!-- Until echarts is loaded + initialised (or the element scrolls into view)\n the panel keeps the chart's footprint with a quiet pulsing sketch\n instead of an empty box (anh Tú 04/09: \"the page is too blank\"). -->\n <div class=\"chart-placeholder\" aria-hidden=\"true\">\n @for (h of placeholderBars; track $index) {\n <span class=\"chart-placeholder__bar\" [style.height.%]=\"h\"></span>\n }\n </div>\n }\n</div>\n","let pending: Promise<unknown> | undefined;\n\n/**\n * Warm the echarts chunk before any chart is on screen.\n *\n * `fx-ui-chart` loads echarts lazily (`provideEchartsCore({ echarts: () =>\n * import('echarts') })`), so the FIRST chart of a session pays the chunk\n * download right when the user is looking at an empty panel. Call this once\n * from an idle callback when the app's env injector is created (e.g. a\n * `provideEnvironmentInitializer` in the remote's root route) and the dashboard\n * charts appear on the frame they mount. Same module URL as the directive's\n * import ⇒ one shared instance, no double load.\n */\nexport function preloadEcharts(): Promise<unknown> {\n return (pending ??= import('echarts'));\n}\n\n/** Schedule `preloadEcharts()` when the main thread is idle (SSR/tests: no-op). */\nexport function preloadEchartsWhenIdle(timeoutMs = 3000): void {\n if (typeof window === 'undefined') return;\n const w = window as Window & { requestIdleCallback?: (cb: () => void, o?: { timeout: number }) => number };\n if (typeof w.requestIdleCallback === 'function') {\n w.requestIdleCallback(() => void preloadEcharts(), { timeout: timeoutMs });\n } else {\n setTimeout(() => void preloadEcharts(), 1500);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"foxeltech-angular-ui-chart.mjs","sources":["../../../projects/ui/chart/src/chart/chart.component.ts","../../../projects/ui/chart/src/chart/chart.component.html","../../../projects/ui/chart/src/preload-echarts.ts","../../../projects/ui/chart/src/foxeltech-angular-ui-chart.ts"],"sourcesContent":["import {\n AfterViewInit,\n ChangeDetectorRef,\n Component,\n ElementRef,\n HostListener,\n OnChanges,\n OnDestroy,\n ChangeDetectionStrategy,\n input,\n output\n} from '@angular/core';\nimport { ECharts, EChartsOption } from 'echarts';\nimport { FxUtils, InputComponent } from '@foxeltech/angular-ui';\nimport { NgxEchartsDirective, provideEchartsCore } from 'ngx-echarts';\n\n\n// 'radar' was advertised here but has no builder in the registry — it\n// silently rendered an empty chart. Re-add when implemented.\n// V2 presets: 'line' (risk score by release), 'area-trend' (weekly trend with\n// accent fill + gray comparison line). Both need an explicit [type] — 'auto'\n// cannot tell them apart from 'bar'.\nexport type IChartType = 'bar' | 'pie' | 'sunburst' | 'line' | 'area-trend' | 'auto';\n\n@Component({\n selector: 'fx-ui-chart',\n templateUrl: './chart.component.html',\n styleUrl: './chart.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [NgxEchartsDirective, InputComponent],\n // Self-contained: echarts is provided (and lazy-loaded) right here, so\n // consumers just import ChartComponent — no forRoot wiring in the app.\n providers: [provideEchartsCore({ echarts: () => import('echarts') })],\n})\nexport class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {\n /* ================= INPUTS ================= */\n\n readonly type = input<IChartType>('auto');\n readonly data = input<any>();\n readonly options = input<EChartsOption>();\n readonly palette = input<string[]>();\n readonly height = input('400px');\n readonly loading = input(false);\n\n // pie controls\n readonly pieLegendAlign = input<string | number>('10%');\n readonly pieLegendPosition = input<string | number>('middle');\n readonly showPieLabel = input(true);\n /** Hide the built-in legend (panels that render their own legend beside the\n donut); the donut then centres in the container. */\n readonly showPieLegend = input(true);\n /** Caption under the donut's centre total (design: \"42 / confirmed\"). */\n readonly pieCenterLabel = input('');\n readonly mobileWidth = input(350);\n /** Override font for canvas text; empty = follow the theme's --og-font-figure. */\n readonly labelFontFamily = input('');\n readonly emptyText = input('No data available');\n\n /* ================= OUTPUTS ================= */\n\n readonly chartClick = output<any>();\n readonly chartInit = output<ECharts>();\n\n /* ================= INTERNAL ================= */\n\n chartOptions: EChartsOption = {};\n isEmpty = false;\n /**\n * ⚠️ Cổng cho `<div echarts>` trong template. Trước đây div render NGAY với\n * `chartOptions = {}`, nên NgxEchartsDirective khởi tạo chart bằng options\n * RỖNG; options thật gán vào giữa lúc `await createChart()` của lần đó ⇒\n * onOptionsChange() thấy `chart == null` và createChart() LẦN HAI trên cùng\n * DOM node ⇒ \"[ECharts] There is a chart instance already initialized on the\n * dom.\". Chỉ dựng directive khi đã có options thật ⇒ đúng một lần init.\n */\n hasOptions = false;\n /** Heights (%) of the pre-init placeholder bars — a fixed, calm silhouette. */\n readonly placeholderBars = [34, 52, 44, 66, 58, 78, 70, 88, 62, 74];\n labelFontSize = 12;\n\n private chartInstance?: ECharts;\n private resizeObserver?: ResizeObserver;\n private intersectionObserver?: IntersectionObserver;\n\n private canInit = false;\n private destroyed = false;\n /**\n * ⚠️ Đang chờ init đầu tiên của directive hoàn tất?\n *\n * NgxEchartsDirective đăng ký `changeFilter.notFirstAndEmpty('options', …)`:\n * lần gán `options` ĐẦU bị bỏ qua (ngAfterViewInit lo), lần gán THỨ HAI gọi\n * onOptionsChange(). Mà onOptionsChange() chỉ createChart() khi `this.chart`\n * còn null — và nó null suốt thời gian `await createChart()` của lần đầu.\n * Nên nếu ta gán `chartOptions` (object mới) trong khoảng await đó thì\n * init() chạy HAI LẦN trên cùng DOM node ⇒\n * \"[ECharts] There is a chart instance already initialized on the dom.\"\n *\n * Cách chặn: trong khoảng chờ, giữ options ở `deferredOptions` chứ không đụng\n * vào property đang bind; init xong thì áp một lần qua instance.\n */\n private awaitingFirstInit = false;\n private deferredOptions?: EChartsOption;\n private pendingFrames = new Set<number>();\n private isMobile = false;\n private containerWidth = 0;\n private containerHeight = 0;\n /**\n * Mốc (performance.now) mà animation \"xuất hiện\" của lần setOption gần nhất\n * chắc chắn đã xong. ECharts mặc định animationDuration 1000ms; trong khoảng\n * này KHÔNG được `resize()` — resize nhảy thẳng tới trạng thái cuối và giết\n * animation (đo 05/09: chart PM bị cắt ở ~800ms khi thanh cuộn xuất hiện\n * lúc lưới card nạp xong làm bề rộng đổi 720→710).\n */\n private appearUntil = 0;\n private static readonly APPEAR_MS = 1100;\n private deferTimer?: ReturnType<typeof setTimeout>;\n /**\n * Thay đổi kích thước ghi nhận TRONG cửa sổ appear, chờ áp sau. `measure()`\n * đã cập nhật containerWidth/Height ngay lúc đo, nên lần gọi hoãn không thể\n * dựa vào \"có đổi không\" nữa — phải nhớ bằng cờ.\n */\n private pendingResize = false;\n private pendingMobile = false;\n\n constructor(\n private ref: ElementRef<HTMLElement>,\n private cdr: ChangeDetectorRef,\n ) {}\n\n /* ================= LIFECYCLE ================= */\n\n ngAfterViewInit(): void {\n // Only initialize when visible (tab-safe)\n this.intersectionObserver = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting && !this.canInit && !this.destroyed) {\n this.canInit = true;\n this.measure();\n this.rebuildChart();\n this.cdr.detectChanges();\n }\n },\n { threshold: 0.1 },\n );\n\n this.intersectionObserver.observe(this.ref.nativeElement);\n\n // Resize-safe (tabs, flex, async layout). Đây là ĐƯỜNG RESIZE DUY NHẤT:\n // `autoResize` của NgxEchartsDirective đã tắt trong template — nó\n // resize() mù sau throttle 100ms, kể cả về 0×H khi phần tử display:none\n // (skeleton route ẩn nội dung cũ) rồi resize lại khi hiện → animation\n // xuất hiện bị giết (đo 05/09 khi đổi project trên chip breadcrumb).\n this.resizeObserver = new ResizeObserver(() => {\n if (!this.canInit || this.destroyed) return;\n this.forceMediaRecalc();\n });\n\n this.resizeObserver.observe(this.ref.nativeElement);\n }\n\n ngOnChanges(): void {\n if (this.canInit && !this.destroyed) {\n this.measure();\n this.rebuildChart();\n }\n }\n\n ngOnDestroy(): void {\n // NgxEchartsDirective tự dispose instance của nó, nên KHÔNG dispose ở đây\n // (sẽ dispose trùng). Việc phải làm là ngắt mọi đường còn chạm được vào\n // instance sau khi nó đã bị dispose: callback rAF đang chờ + tham chiếu\n // `chartInstance` mà forceMediaRecalc/rebuildChart vẫn dùng.\n this.destroyed = true;\n\n for (const id of this.pendingFrames) cancelAnimationFrame(id);\n this.pendingFrames.clear();\n if (this.deferTimer !== undefined) clearTimeout(this.deferTimer);\n this.deferTimer = undefined;\n\n this.resizeObserver?.disconnect();\n this.intersectionObserver?.disconnect();\n this.resizeObserver = undefined;\n this.intersectionObserver = undefined;\n\n this.chartInstance = undefined;\n this.awaitingFirstInit = false;\n this.deferredOptions = undefined;\n this.sweepOrphanInstance();\n }\n\n /**\n * ⚠️ RACE TRONG NGX-ECHARTS v20 — nguồn của\n * \"[ECharts] There is a chart instance already initialized on the dom.\"\n *\n * NgxEchartsDirective.onOptionsChange() làm:\n * this.chart = await this.createChart(); // ← destroy có thể rơi vào đây\n * this.chart$.next(this.chart);\n * this.chartInit.emit(this.chart);\n *\n * Destroy đúng lúc đang await thì ngOnDestroy → dispose() thấy `this.chart == null`\n * nên KHÔNG dispose gì. Sau đó await resolve, gán một instance ECharts còn sống cho\n * directive đã chết → instance MỒ CÔI, vẫn nằm trong registry của echarts gắn với\n * DOM node đó. Angular tái dùng node ⇒ init() lần sau báo \"already initialized\".\n *\n * Không thể bắt qua (chartInit): Angular v20 TỪ CHỐI emit trên OutputRef đã destroy\n * (chỉ log NG0953, listener không hề được gọi). Nên đường duy nhất là hỏi thẳng\n * echarts xem node còn instance nào không, ở các macrotask SAU destroy.\n */\n private sweepOrphanInstance(): void {\n const host = this.ref.nativeElement.querySelector<HTMLElement>('.fx-chart');\n if (!host) return;\n\n // Chỉ nạp echarts nếu nó đã ở trong module cache — không tự kéo bundle về\n // chỉ để đi dọn.\n let attempt = 0;\n const sweep = () => {\n import('echarts').then(({ getInstanceByDom }) => {\n const orphan = getInstanceByDom(host);\n if (orphan) {\n if (!orphan.isDisposed()) orphan.dispose();\n return;\n }\n // createChart() phải chờ dynamic import nên instance mồ côi có thể xuất\n // hiện muộn hơn một macrotask; thử lại vài nhịp rồi dừng.\n if (++attempt < 5) setTimeout(sweep, 50 * attempt);\n });\n };\n\n setTimeout(sweep);\n }\n\n /** rAF có thể huỷ được khi destroy — tránh chạm instance đã dispose. */\n private schedule(fn: () => void): void {\n if (this.destroyed) return;\n const id = requestAnimationFrame(() => {\n this.pendingFrames.delete(id);\n if (this.destroyed) return;\n fn();\n });\n this.pendingFrames.add(id);\n }\n\n /** Instance còn dùng được? (đã dispose thì mọi API echarts đều ném lỗi) */\n private get liveChart(): ECharts | undefined {\n const chart = this.chartInstance;\n if (!chart || this.destroyed || chart.isDisposed()) return undefined;\n return chart;\n }\n\n @HostListener('window:theme-changed')\n onThemeChange(): void {\n if (!this.canInit || this.destroyed) return;\n this.schedule(() => this.rebuildChart());\n }\n\n /* ================= EVENTS ================= */\n\n onChartClick(event: any) {\n this.chartClick.emit(event);\n }\n\n // ⚠️ Phải để `any`: NgxEchartsDirective emit `EChartsType` từ\n // 'echarts/types/dist/shared', TS coi nó KHÔNG tương thích với `ECharts`\n // của 'echarts' (private `_ssr` khai báo riêng) → template typecheck fail.\n onChartInit(raw: any) {\n const chart = raw as ECharts;\n // Phòng thân: bình thường Angular đã chặn emit sau destroy (NG0953) nên\n // nhánh này không tới; nhưng nếu tới thì tuyệt đối không được gán lại\n // `chartInstance` sau ngOnDestroy. Việc thu dọn instance mồ côi do\n // sweepOrphanInstance() lo — xem ghi chú ở đó.\n if (this.destroyed) {\n if (!chart.isDisposed()) chart.dispose();\n return;\n }\n\n this.chartInstance = chart;\n this.awaitingFirstInit = false;\n\n if (this.deferredOptions) {\n const options = this.deferredOptions;\n this.deferredOptions = undefined;\n this.chartOptions = options;\n if (!chart.isDisposed()) {\n chart.clear();\n chart.setOption(options, true);\n }\n }\n // Lần vẽ đầu (directive setOption lúc init) cũng có animation xuất hiện.\n this.markAppear();\n\n this.chartInit.emit(chart);\n\n this.schedule(() => this.forceMediaRecalc());\n }\n\n /* ================= CORE FIX ================= */\n\n /**\n * Đo host và ghi lại; trả về CÁI GÌ đổi so với lần đo trước.\n * Khi bị ẩn (0×0) thì KHÔNG ghi — giữ kích thước hiển thị cuối, để lúc hiện\n * lại cùng cỡ thì coi như không đổi (không resize, không cắt animation).\n */\n private measure(): { size: boolean; mobile: boolean } {\n const { width, height } = this.ref.nativeElement.getBoundingClientRect();\n if (width <= 0 || height <= 0) return { size: false, mobile: false };\n\n const isMobile = width < this.mobileWidth();\n const size = width !== this.containerWidth || height !== this.containerHeight;\n const mobile = isMobile !== this.isMobile;\n this.containerWidth = width;\n this.containerHeight = height;\n this.isMobile = isMobile;\n return { size, mobile };\n }\n\n /** Animation xuất hiện vừa bắt đầu — khoá resize tới khi nó xong. */\n private markAppear(): void {\n this.appearUntil = performance.now() + ChartComponent.APPEAR_MS;\n }\n\n /** Pie/sunburst đặt legend + số giữa theo px của containerWidth → phải dựng lại. */\n private needsLayoutRebuild(): boolean {\n const type = this.detectType();\n return type === 'pie' || type === 'sunburst';\n }\n\n private deferResize(ms: number): void {\n if (this.deferTimer !== undefined) clearTimeout(this.deferTimer);\n this.deferTimer = setTimeout(() => {\n this.deferTimer = undefined;\n if (!this.destroyed) this.forceMediaRecalc();\n }, ms);\n }\n\n /**\n * Phản ứng với kích thước host (RO, sau init, sau khi hiện lại).\n *\n * Ba quy tắc, đều đo được (0.7.75 và 05/09):\n * 1. **Ẩn (0×H) thì không làm gì** — không resize canvas về 0. Chart hiện\n * lại cùng cỡ thì animation đã bắt đầu lúc ẩn (setOption khi resolver về,\n * ngay trước NavigationEnd) chạy tiếp bình thường.\n * 2. **`resize()` nhảy thẳng tới trạng thái cuối** ⇒ trong cửa sổ appear\n * (`appearUntil`) thì HOÃN, đo lại và xử lý sau khi animation xong. Không\n * dựng lại (clear + setOption) lúc đó vì đó là \"vẽ 2 lần\" theo cách khác.\n * 3. Ngoài cửa sổ: `resize()` trước, rồi chỉ dựng lại khi preset đổi (mobile)\n * hoặc loại chart đặt layout theo px (pie/sunburst). Thứ tự này quan\n * trọng: resize SAU rebuild sẽ cắt ngay animation vừa bắt đầu.\n */\n private forceMediaRecalc(retry = 0) {\n if (!this.liveChart) return;\n\n const { width, height } = this.ref.nativeElement.getBoundingClientRect();\n\n if (width === 0 || height === 0) {\n // Chưa layout xong (ngay sau init) thì thử lại vài frame; đang ẩn thì thôi.\n if (retry < 10) this.schedule(() => this.forceMediaRecalc(retry + 1));\n return;\n }\n\n const change = this.measure();\n const size = change.size || this.pendingResize;\n const mobile = change.mobile || this.pendingMobile;\n if (!size && !mobile) return;\n\n const remaining = this.appearUntil - performance.now();\n if (remaining > 0) {\n this.pendingResize = size;\n this.pendingMobile = mobile;\n this.deferResize(remaining + 30);\n return;\n }\n this.pendingResize = false;\n this.pendingMobile = false;\n\n const chart = this.liveChart;\n if (!chart) return;\n chart.resize();\n if (mobile || this.needsLayoutRebuild()) this.rebuildChart();\n }\n\n private rebuildChart(): void {\n if (this.destroyed) return;\n\n this.isEmpty = this.checkEmpty();\n\n if (this.isEmpty) {\n this.liveChart?.clear();\n return;\n }\n\n const options = this.buildOptions();\n const chart = this.liveChart;\n\n if (!chart) {\n if (this.awaitingFirstInit) {\n // Đang trong khoảng await của lần init đầu — KHÔNG gán vào property\n // đang bind, xem ghi chú ở `awaitingFirstInit`.\n this.deferredOptions = options;\n return;\n }\n this.awaitingFirstInit = true;\n this.chartOptions = options;\n this.hasOptions = true;\n return;\n }\n\n this.chartOptions = options;\n this.hasOptions = true;\n chart.clear();\n chart.setOption(options, true);\n this.markAppear();\n }\n\n private checkEmpty(): boolean {\n const type = this.detectType();\n\n if (type === 'bar') {\n const data = this.data();\n const categories = data?.categories;\n const values = data?.values;\n return !categories?.length || !values?.length;\n }\n\n if (type === 'line') {\n const data = this.data();\n return !data?.categories?.length || !data?.values?.length;\n }\n\n if (type === 'area-trend') {\n const data = this.data();\n return !data?.categories?.length || !data?.current?.length;\n }\n\n if (type === 'pie') {\n const data = this.data();\n if (!Array.isArray(data) || data.length === 0) return true;\n const total = data.reduce((sum: number, d: any) => sum + (d.value || 0), 0);\n return total === 0;\n }\n\n return false;\n }\n\n /* ================= REGISTRY ================= */\n\n private registry: Record<string, (data: any, palette: string[]) => EChartsOption> = {\n bar: (data, colors) => ({\n color: colors,\n tooltip: { trigger: 'axis' },\n grid: { left: '5%', right: '5%', bottom: '10%', containLabel: true },\n xAxis: {\n type: 'category',\n data: data?.categories || [],\n axisLabel: {\n interval: 0,\n width: 80,\n overflow: 'truncate',\n ellipsis: '…',\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n axisLine: {\n lineStyle: {\n color: FxUtils.convertColorFromVariable('--border-strong'),\n },\n },\n },\n yAxis: {\n type: 'value',\n axisLabel: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n axisLine: {\n lineStyle: {\n color: FxUtils.convertColorFromVariable('--border-strong'),\n },\n },\n splitLine: {\n lineStyle: {\n color: FxUtils.convertColorFromVariable('--border-default'),\n },\n },\n },\n series: [\n {\n type: 'bar',\n data: data?.values || [],\n barMaxWidth: 70,\n itemStyle: {\n color: (p: any) => colors[p.dataIndex % colors.length],\n },\n label: {\n show: true,\n position: 'top',\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-primary'),\n },\n },\n ],\n }),\n\n pie: (data, colors) => {\n const total = data.reduce((sum: number, d: any) => sum + d.value, 0);\n const layout = this.calcPieLayout(data);\n\n return {\n color: colors,\n tooltip: { trigger: 'item' },\n\n legend: {\n show: this.showPieLegend(),\n type: 'scroll' as const,\n orient: layout.legendOrient,\n icon: 'circle',\n itemGap: 12,\n\n right: layout.legendRight,\n top: layout.legendTop,\n left: layout.legendLeft,\n bottom: layout.legendBottom,\n width: layout.legendWidth,\n\n formatter: (name: string) => {\n const item = data.find((d: any) => d.name === name);\n return item ? `${name}: ${item.value}` : name;\n },\n\n textStyle: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: 13,\n fontWeight: 500,\n color: FxUtils.convertColorFromVariable('--text-primary'),\n overflow: 'truncate',\n },\n },\n\n series: [\n {\n type: 'pie',\n radius: ['40%', '70%'],\n center: layout.pieCenter,\n data,\n\n label: {\n show: this.showPieLabel(),\n position: 'outside',\n formatter: '{d}%',\n fontSize: this.labelFontSize,\n fontFamily: this.resolvedFontFamily(),\n color: FxUtils.convertColorFromVariable('--text-primary'),\n textBorderWidth: 0,\n textBorderColor: 'transparent',\n },\n\n labelLine: {\n show: this.showPieLabel(),\n length: 7,\n length2: 7,\n },\n },\n ],\n\n // Centre figure: positioned by x/y in pixels — graphic left/top\n // percentages place the element's bounding-box EDGE, which shifted the\n // total right/down by half its own size (mis-centred, caption clipped).\n graphic: (() => {\n const cx = (this.containerWidth || 400) * (parseFloat(layout.pieCenter[0]) / 100);\n const ch = this.ref.nativeElement.clientHeight || 300;\n const cy = ch * (parseFloat(layout.pieCenter[1]) / 100);\n return [\n {\n type: 'text' as const,\n // Shift the total up when a caption sits under it.\n x: cx,\n y: cy + (this.pieCenterLabel() ? -9 : 0),\n style: {\n text: total.toString(),\n fontSize: 24,\n fontWeight: 600,\n fontFamily: this.resolvedFontFamily(),\n fill: FxUtils.convertColorFromVariable('--text-primary'),\n align: 'center' as const,\n verticalAlign: 'middle' as const,\n },\n },\n ...(this.pieCenterLabel()\n ? [\n {\n type: 'text' as const,\n x: cx,\n y: cy + 12,\n style: {\n text: this.pieCenterLabel(),\n fontSize: 11,\n fontFamily: this.resolvedFontFamily(),\n fill: FxUtils.convertColorFromVariable('--text-secondary'),\n align: 'center' as const,\n verticalAlign: 'middle' as const,\n },\n },\n ]\n : []),\n ];\n })(),\n };\n },\n // V2 preset: single line, accent-coloured — \"risk score by release\".\n line: (data) => ({\n tooltip: { trigger: 'axis' },\n grid: { left: '3%', right: '4%', bottom: '8%', top: '8%', containLabel: true },\n xAxis: this.categoryAxis(data?.categories || []),\n yAxis: this.valueAxis(),\n series: [\n {\n type: 'line',\n data: data?.values || [],\n smooth: false,\n symbol: 'circle',\n symbolSize: 7,\n lineStyle: { width: 2, color: FxUtils.convertColorFromVariable('--og-accent', 1, { legacyCommaSyntax: true }) },\n itemStyle: { color: FxUtils.convertColorFromVariable('--og-accent', 1, { legacyCommaSyntax: true }) },\n label: {\n show: true,\n position: 'top',\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-primary'),\n },\n },\n ],\n }),\n\n // V2 preset: weekly trend — accent gradient fill + gray comparison line.\n // data: { categories: string[], current: number[], previous?: number[] }\n 'area-trend': (data) => {\n // legacyCommaSyntax: zrender's colour parser chokes on CSS4 space syntax.\n const accent = FxUtils.convertColorFromVariable('--og-accent', 1, { legacyCommaSyntax: true });\n const accentSoft = FxUtils.convertColorFromVariable('--og-accent', 0.25, { legacyCommaSyntax: true });\n const accentFade = FxUtils.convertColorFromVariable('--og-accent', 0.02, { legacyCommaSyntax: true });\n const series: any[] = [\n {\n type: 'line',\n name: data?.currentLabel || 'Current',\n data: data?.current || [],\n smooth: true,\n symbol: 'none',\n lineStyle: { width: 2, color: accent },\n itemStyle: { color: accent },\n areaStyle: {\n color: {\n type: 'linear',\n x: 0, y: 0, x2: 0, y2: 1,\n colorStops: [\n { offset: 0, color: accentSoft },\n { offset: 1, color: accentFade },\n ],\n },\n },\n },\n ];\n if (data?.previous?.length) {\n series.push({\n type: 'line',\n name: data?.previousLabel || 'Previous',\n data: data.previous,\n smooth: true,\n symbol: 'none',\n lineStyle: {\n width: 1.5,\n type: 'dashed',\n color: FxUtils.convertColorFromVariable('--text-placeholder', 1, { legacyCommaSyntax: true }),\n },\n itemStyle: { color: FxUtils.convertColorFromVariable('--text-placeholder', 1, { legacyCommaSyntax: true }) },\n });\n }\n return {\n tooltip: { trigger: 'axis' },\n grid: { left: '3%', right: '4%', bottom: '8%', top: '10%', containLabel: true },\n xAxis: { ...this.categoryAxis(data?.categories || []), boundaryGap: false },\n yAxis: this.valueAxis(),\n series,\n };\n },\n\n sunburst: (data, colors) => ({\n color: colors,\n series: [\n {\n type: 'sunburst',\n data,\n radius: ['15%', '90%'],\n itemStyle: {\n borderWidth: 1,\n borderColor: FxUtils.convertColorFromVariable('--border-default'),\n },\n label: {\n rotate: 'tangential',\n fontSize: this.labelFontSize,\n fontFamily: this.resolvedFontFamily(),\n color: FxUtils.convertColorFromVariable('--text-primary'),\n textBorderWidth: 0,\n textBorderColor: 'transparent',\n },\n },\n ],\n }),\n };\n\n /* ================= PIE LAYOUT ================= */\n\n private calcPieLayout(data: any[]) {\n const cw = this.containerWidth || 400;\n\n if (!this.showPieLegend()) {\n return {\n legendOrient: 'vertical' as const,\n legendLeft: undefined as string | undefined,\n legendRight: undefined as string | undefined,\n legendTop: undefined as string | undefined,\n legendBottom: undefined as number | undefined,\n legendWidth: undefined as number | undefined,\n pieCenter: ['50%', '50%'],\n graphicLeft: '50%',\n graphicTop: '50%',\n };\n }\n\n if (this.isMobile) {\n return {\n legendOrient: 'horizontal' as const,\n legendLeft: 'center' as string | undefined,\n legendRight: undefined as string | undefined,\n legendTop: undefined as string | undefined,\n legendBottom: 0 as number | undefined,\n legendWidth: undefined as number | undefined,\n pieCenter: ['50%', '40%'],\n graphicLeft: '50%',\n graphicTop: '40%',\n };\n }\n\n // Estimate legend pixel width from longest formatted label\n const CHAR_WIDTH = 7.5; // approx px per char at fontSize 13\n const ICON_GAP = 30; // legend icon + padding\n const longestLabel = data.reduce((max: number, d: any) => {\n return Math.max(max, `${d.name}: ${d.value}`.length);\n }, 0);\n const rawLegendPx = longestLabel * CHAR_WIDTH + ICON_GAP;\n\n // Clamp: min 100px, max 45% of container\n const legendPx = Math.max(100, Math.min(rawLegendPx, cw * 0.45));\n const legendPct = (legendPx / cw) * 100;\n\n // Pie center: middle of the remaining space after legend\n const pieCenterX = Math.round((100 - legendPct) / 2);\n\n return {\n legendOrient: 'vertical' as const,\n legendLeft: undefined,\n legendRight: '2%',\n legendTop: 'middle' as string | undefined,\n legendBottom: undefined,\n legendWidth: Math.round(legendPx),\n pieCenter: [`${pieCenterX}%`, '50%'],\n graphicLeft: `${pieCenterX}%`,\n graphicTop: '50%',\n };\n }\n\n /* ================= BUILD ================= */\n\n private detectType(): string {\n const type = this.type();\n if (type !== 'auto') return type;\n const data = this.data();\n if (data?.children) return 'sunburst';\n if (data?.categories && data?.values) return 'bar';\n if (Array.isArray(data)) return 'pie';\n return 'bar';\n }\n\n private buildOptions(): EChartsOption {\n const type = this.detectType();\n const palette = this.resolvePalette();\n const base = this.registry[type]?.(this.data(), palette) || {};\n\n return {\n ...base,\n ...this.options(),\n };\n }\n\n private categoryAxis(categories: string[]) {\n return {\n type: 'category' as const,\n data: categories,\n axisLabel: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n axisLine: {\n lineStyle: { color: FxUtils.convertColorFromVariable('--border-strong') },\n },\n };\n }\n\n private valueAxis() {\n return {\n type: 'value' as const,\n axisLabel: {\n fontFamily: this.resolvedFontFamily(),\n fontSize: this.labelFontSize,\n color: FxUtils.convertColorFromVariable('--text-secondary'),\n },\n splitLine: {\n lineStyle: { color: FxUtils.convertColorFromVariable('--border-default') },\n },\n };\n }\n\n /** Canvas không ăn var() — resolve font stack thật tại render; chart đã\n rebuild theo event theme-changed nên tự đổi font khi đổi theme\n (blueprint/terminal dùng mono qua --og-font-figure). */\n private resolvedFontFamily(): string {\n const override = this.labelFontFamily();\n if (override) return override;\n // Dev-mode CSS pretty-printer bẻ dòng giá trị dài — normalize whitespace\n // trước khi chuỗi này vào ctx.font của canvas.\n const figure = getComputedStyle(document.body)\n .getPropertyValue('--og-font-figure')\n .replace(/\\s+/g, ' ')\n .trim();\n return figure || 'Archivo, system-ui, sans-serif';\n }\n\n private resolvePalette(): string[] {\n const defaults = [\n 'rgba(var(--success) / 1)',\n 'rgba(var(--information) / 1)',\n 'rgba(var(--critical) / 1)',\n 'rgba(var(--danger) / 1)',\n 'rgba(var(--warning) / 1)',\n 'rgba(var(--discovery) / 1)',\n ];\n\n const paletteValue = this.palette();\n const palette = paletteValue?.length ? paletteValue : defaults;\n\n return palette.map((raw) => {\n if (!raw.includes('var(')) return raw;\n\n const cssVar = raw.match(/var\\((.*?)\\)/)?.[1];\n const rgb = getComputedStyle(document.body)\n .getPropertyValue(cssVar || '')\n .trim()\n .replace(/\\s+/g, ',');\n return `rgba(${rgb},1)`;\n });\n }\n}\n","<div class=\"chart-wrapper\" [style.height]=\"height()\">\n @if (isEmpty) {\n <div class=\"chart-empty\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"chart-empty-icon\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 0 1 3 19.875v-6.75ZM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V8.625ZM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 0 1-1.125-1.125V4.125Z\" />\n </svg>\n <span class=\"chart-empty-text\">{{ emptyText() }}</span>\n </div>\n } @else if (hasOptions) {\n <div\n echarts\n [options]=\"chartOptions\"\n [autoResize]=\"false\"\n [loading]=\"loading()\"\n (chartInit)=\"onChartInit($event)\"\n (chartClick)=\"onChartClick($event)\"\n class=\"fx-chart\"\n [style.height]=\"height()\"\n ></div>\n } @else {\n <!-- Until echarts is loaded + initialised (or the element scrolls into view)\n the panel keeps the chart's footprint with a quiet pulsing sketch\n instead of an empty box (anh Tú 04/09: \"the page is too blank\"). -->\n <div class=\"chart-placeholder\" aria-hidden=\"true\">\n @for (h of placeholderBars; track $index) {\n <span class=\"chart-placeholder__bar\" [style.height.%]=\"h\"></span>\n }\n </div>\n }\n</div>\n","let pending: Promise<unknown> | undefined;\n\n/**\n * Warm the echarts chunk before any chart is on screen.\n *\n * `fx-ui-chart` loads echarts lazily (`provideEchartsCore({ echarts: () =>\n * import('echarts') })`), so the FIRST chart of a session pays the chunk\n * download right when the user is looking at an empty panel. Call this once\n * from an idle callback when the app's env injector is created (e.g. a\n * `provideEnvironmentInitializer` in the remote's root route) and the dashboard\n * charts appear on the frame they mount. Same module URL as the directive's\n * import ⇒ one shared instance, no double load.\n */\nexport function preloadEcharts(): Promise<unknown> {\n return (pending ??= import('echarts'));\n}\n\n/** Schedule `preloadEcharts()` when the main thread is idle (SSR/tests: no-op). */\nexport function preloadEchartsWhenIdle(timeoutMs = 3000): void {\n if (typeof window === 'undefined') return;\n const w = window as Window & { requestIdleCallback?: (cb: () => void, o?: { timeout: number }) => number };\n if (typeof w.requestIdleCallback === 'function') {\n w.requestIdleCallback(() => void preloadEcharts(), { timeout: timeoutMs });\n } else {\n setTimeout(() => void preloadEcharts(), 1500);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/lib/styles/theme.css
CHANGED
|
@@ -504,143 +504,3 @@
|
|
|
504
504
|
--border-secondary-hover: var(--og-accent);
|
|
505
505
|
--bg-secondary-hover: var(--og-neutral-soft-bg);
|
|
506
506
|
}
|
|
507
|
-
|
|
508
|
-
/* ---- STALE-HOST FALLBACK (transitional) -----------------------------
|
|
509
|
-
Host CŨ không set data-og-theme, chỉ toggle .light_theme trên body.
|
|
510
|
-
Map light→porcelain (đã là :root default), dark→charcoal — cả hai kèm
|
|
511
|
-
alias ở body-level để đè literal của stylesheet host cũ. Remote chạy
|
|
512
|
-
standalone không set theme cũng rơi vào nhánh charcoal (khớp hành vi
|
|
513
|
-
dark-default cũ). */
|
|
514
|
-
html:not([data-og-theme]) body:not(.light_theme) {
|
|
515
|
-
--og-page-bg: 26 24 17; /* #1a1811 */
|
|
516
|
-
--og-surface: 34 31 22; /* #221f16 */
|
|
517
|
-
--og-surface-raised: 42 38 25; /* #2a2619 */
|
|
518
|
-
--og-surface-sunken: 23 21 9; /* #171509 */
|
|
519
|
-
--og-border: 58 54 38; /* #3a3626 */
|
|
520
|
-
--og-border-subtle: 44 41 25; /* #2c2919 */
|
|
521
|
-
--og-border-strong: 76 71 50; /* #4c4732 */
|
|
522
|
-
--og-text: 226 221 203; /* #e2ddcb */
|
|
523
|
-
--og-text-body: 193 188 166; /* #c1bca6 */
|
|
524
|
-
--og-text-muted: 147 141 118; /* #938d76 */
|
|
525
|
-
--og-text-faint: 107 102 83; /* #6b6653 */
|
|
526
|
-
--og-accent: 143 200 102; /* #8fc866 */
|
|
527
|
-
--og-accent-fg: 164 217 126; /* #a4d97e */
|
|
528
|
-
--og-accent-contrast: 23 21 9; /* #171509 */
|
|
529
|
-
--og-accent-soft-bg: 38 48 26; /* #26301a */
|
|
530
|
-
--og-accent-soft-fg: 164 217 126; /* #a4d97e */
|
|
531
|
-
--og-positive: 132 200 98; /* #84c862 */
|
|
532
|
-
--og-positive-soft-bg: 34 48 15; /* #22300f */
|
|
533
|
-
--og-negative: 229 130 142; /* #e5828e */
|
|
534
|
-
--og-negative-soft-bg: 63 30 36; /* #3f1e24 */
|
|
535
|
-
--og-warning: 214 178 90; /* #d6b25a */
|
|
536
|
-
--og-warning-soft-bg: 51 42 18; /* #332a12 */
|
|
537
|
-
--og-neutral-soft-bg: 42 39 25; /* #2a2719 */
|
|
538
|
-
--og-neutral-soft-fg: 164 158 134; /* #a49e86 */
|
|
539
|
-
--og-track: 48 44 30; /* #302c1e */
|
|
540
|
-
--og-radius-frame: 10px;
|
|
541
|
-
--og-radius-card: 9px;
|
|
542
|
-
--og-radius-control: 7px;
|
|
543
|
-
--og-radius-pill: 999px;
|
|
544
|
-
--og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
|
|
545
|
-
--og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
|
|
546
|
-
--og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
|
|
547
|
-
--og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
|
|
548
|
-
--og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
|
|
549
|
-
--og-positive-soft-fg: 164 217 126; /* #a4d97e */
|
|
550
|
-
--og-negative-soft-fg: 238 163 171; /* #eea3ab */
|
|
551
|
-
--og-warning-soft-fg: 224 194 116; /* #e0c274 */
|
|
552
|
-
/* derived (script-generated, KHÔNG có trong design gốc) */
|
|
553
|
-
--og-derived-critical: 254 119 154; /* #fe779a */
|
|
554
|
-
--og-derived-critical-soft-bg: 97 46 57; /* #612e39 */
|
|
555
|
-
--og-derived-gradient-accent-end: 174 233 134; /* #aee986 */
|
|
556
|
-
--og-derived-gradient-positive-end: 157 226 123; /* #9de27b */
|
|
557
|
-
--shadow-color: 255 255 255;
|
|
558
|
-
/* severity tones — bộ LEGACY (vivid), không lấy từ design V2 */
|
|
559
|
-
--og-tone-success: 76 175 80;
|
|
560
|
-
--og-tone-warning: 255 188 59;
|
|
561
|
-
--og-tone-critical: 236 25 56;
|
|
562
|
-
--og-tone-danger: 249 133 7;
|
|
563
|
-
--og-tone-information: 62 175 240;
|
|
564
|
-
--og-tone-discovery: 119 113 227;
|
|
565
|
-
/* severity ramp cho CHART — literal đo trên demo, không phải token design */
|
|
566
|
-
--og-sev-critical: 207 103 96; /* #cf6760 — #7f1d1d nâng L cho nền tối */
|
|
567
|
-
--og-sev-danger: 246 68 61; /* #f6443d */
|
|
568
|
-
--og-sev-warning: 245 158 11; /* #f59e0b */
|
|
569
|
-
--og-sev-success: 22 163 74; /* #16a34a */
|
|
570
|
-
|
|
571
|
-
/* ---- LEGACY ALIASES (@deprecated — gỡ khi kết thúc campaign V2) ----
|
|
572
|
-
Các biến cũ trỏ vào --og-* qua var() nên chỉ cần khai báo MỘT lần:
|
|
573
|
-
var() resolve tại element sử dụng, giá trị og đúng theo theme đang active. */
|
|
574
|
-
--gradient-primary-start: var(--og-accent);
|
|
575
|
-
--gradient-primary-end: var(--og-derived-gradient-accent-end);
|
|
576
|
-
--gradient-success-start: var(--og-positive);
|
|
577
|
-
--gradient-success-end: var(--og-derived-gradient-positive-end);
|
|
578
|
-
--bg-primary: var(--og-surface);
|
|
579
|
-
--bg-canvas: var(--og-page-bg);
|
|
580
|
-
--primary: var(--og-accent);
|
|
581
|
-
--success: var(--og-tone-success);
|
|
582
|
-
--warning: var(--og-tone-warning);
|
|
583
|
-
--critical: var(--og-tone-critical);
|
|
584
|
-
--danger: var(--og-tone-danger);
|
|
585
|
-
--discovery: var(--og-tone-discovery);
|
|
586
|
-
--information: var(--og-tone-information);
|
|
587
|
-
--text-primary: var(--og-text);
|
|
588
|
-
--text-secondary: var(--og-text-muted);
|
|
589
|
-
--border-default: var(--og-border);
|
|
590
|
-
--text-placeholder: var(--og-text-faint);
|
|
591
|
-
--step-bg: var(--og-text);
|
|
592
|
-
--text-link-primary: var(--og-accent-fg);
|
|
593
|
-
--bg-primary-hover: var(--og-surface-sunken);
|
|
594
|
-
--text-focus: var(--og-accent-contrast);
|
|
595
|
-
--bg-primary-neutral: var(--og-border-strong);
|
|
596
|
-
--text-selected-primary: var(--og-accent-fg);
|
|
597
|
-
--button-default: var(--og-text);
|
|
598
|
-
--text-inverse: var(--og-surface);
|
|
599
|
-
--bg-error-bold: var(--og-negative);
|
|
600
|
-
--text-error: var(--og-negative);
|
|
601
|
-
--border-interactive: var(--og-accent);
|
|
602
|
-
--border-selected: var(--og-accent);
|
|
603
|
-
--button-primary: var(--og-accent);
|
|
604
|
-
--border-strong: var(--og-border-strong);
|
|
605
|
-
--border-secondary-hover: var(--og-accent);
|
|
606
|
-
--bg-secondary-hover: var(--og-neutral-soft-bg);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
html:not([data-og-theme]) body.light_theme {
|
|
610
|
-
/* ---- LEGACY ALIASES (@deprecated — gỡ khi kết thúc campaign V2) ----
|
|
611
|
-
Các biến cũ trỏ vào --og-* qua var() nên chỉ cần khai báo MỘT lần:
|
|
612
|
-
var() resolve tại element sử dụng, giá trị og đúng theo theme đang active. */
|
|
613
|
-
--gradient-primary-start: var(--og-accent);
|
|
614
|
-
--gradient-primary-end: var(--og-derived-gradient-accent-end);
|
|
615
|
-
--gradient-success-start: var(--og-positive);
|
|
616
|
-
--gradient-success-end: var(--og-derived-gradient-positive-end);
|
|
617
|
-
--bg-primary: var(--og-surface);
|
|
618
|
-
--bg-canvas: var(--og-page-bg);
|
|
619
|
-
--primary: var(--og-accent);
|
|
620
|
-
--success: var(--og-tone-success);
|
|
621
|
-
--warning: var(--og-tone-warning);
|
|
622
|
-
--critical: var(--og-tone-critical);
|
|
623
|
-
--danger: var(--og-tone-danger);
|
|
624
|
-
--discovery: var(--og-tone-discovery);
|
|
625
|
-
--information: var(--og-tone-information);
|
|
626
|
-
--text-primary: var(--og-text);
|
|
627
|
-
--text-secondary: var(--og-text-muted);
|
|
628
|
-
--border-default: var(--og-border);
|
|
629
|
-
--text-placeholder: var(--og-text-faint);
|
|
630
|
-
--step-bg: var(--og-text);
|
|
631
|
-
--text-link-primary: var(--og-accent-fg);
|
|
632
|
-
--bg-primary-hover: var(--og-surface-sunken);
|
|
633
|
-
--text-focus: var(--og-accent-contrast);
|
|
634
|
-
--bg-primary-neutral: var(--og-border-strong);
|
|
635
|
-
--text-selected-primary: var(--og-accent-fg);
|
|
636
|
-
--button-default: var(--og-text);
|
|
637
|
-
--text-inverse: var(--og-surface);
|
|
638
|
-
--bg-error-bold: var(--og-negative);
|
|
639
|
-
--text-error: var(--og-negative);
|
|
640
|
-
--border-interactive: var(--og-accent);
|
|
641
|
-
--border-selected: var(--og-accent);
|
|
642
|
-
--button-primary: var(--og-accent);
|
|
643
|
-
--border-strong: var(--og-border-strong);
|
|
644
|
-
--border-secondary-hover: var(--og-accent);
|
|
645
|
-
--bg-secondary-hover: var(--og-neutral-soft-bg);
|
|
646
|
-
}
|
package/theme.css
CHANGED
|
@@ -504,143 +504,3 @@
|
|
|
504
504
|
--border-secondary-hover: var(--og-accent);
|
|
505
505
|
--bg-secondary-hover: var(--og-neutral-soft-bg);
|
|
506
506
|
}
|
|
507
|
-
|
|
508
|
-
/* ---- STALE-HOST FALLBACK (transitional) -----------------------------
|
|
509
|
-
Host CŨ không set data-og-theme, chỉ toggle .light_theme trên body.
|
|
510
|
-
Map light→porcelain (đã là :root default), dark→charcoal — cả hai kèm
|
|
511
|
-
alias ở body-level để đè literal của stylesheet host cũ. Remote chạy
|
|
512
|
-
standalone không set theme cũng rơi vào nhánh charcoal (khớp hành vi
|
|
513
|
-
dark-default cũ). */
|
|
514
|
-
html:not([data-og-theme]) body:not(.light_theme) {
|
|
515
|
-
--og-page-bg: 26 24 17; /* #1a1811 */
|
|
516
|
-
--og-surface: 34 31 22; /* #221f16 */
|
|
517
|
-
--og-surface-raised: 42 38 25; /* #2a2619 */
|
|
518
|
-
--og-surface-sunken: 23 21 9; /* #171509 */
|
|
519
|
-
--og-border: 58 54 38; /* #3a3626 */
|
|
520
|
-
--og-border-subtle: 44 41 25; /* #2c2919 */
|
|
521
|
-
--og-border-strong: 76 71 50; /* #4c4732 */
|
|
522
|
-
--og-text: 226 221 203; /* #e2ddcb */
|
|
523
|
-
--og-text-body: 193 188 166; /* #c1bca6 */
|
|
524
|
-
--og-text-muted: 147 141 118; /* #938d76 */
|
|
525
|
-
--og-text-faint: 107 102 83; /* #6b6653 */
|
|
526
|
-
--og-accent: 143 200 102; /* #8fc866 */
|
|
527
|
-
--og-accent-fg: 164 217 126; /* #a4d97e */
|
|
528
|
-
--og-accent-contrast: 23 21 9; /* #171509 */
|
|
529
|
-
--og-accent-soft-bg: 38 48 26; /* #26301a */
|
|
530
|
-
--og-accent-soft-fg: 164 217 126; /* #a4d97e */
|
|
531
|
-
--og-positive: 132 200 98; /* #84c862 */
|
|
532
|
-
--og-positive-soft-bg: 34 48 15; /* #22300f */
|
|
533
|
-
--og-negative: 229 130 142; /* #e5828e */
|
|
534
|
-
--og-negative-soft-bg: 63 30 36; /* #3f1e24 */
|
|
535
|
-
--og-warning: 214 178 90; /* #d6b25a */
|
|
536
|
-
--og-warning-soft-bg: 51 42 18; /* #332a12 */
|
|
537
|
-
--og-neutral-soft-bg: 42 39 25; /* #2a2719 */
|
|
538
|
-
--og-neutral-soft-fg: 164 158 134; /* #a49e86 */
|
|
539
|
-
--og-track: 48 44 30; /* #302c1e */
|
|
540
|
-
--og-radius-frame: 10px;
|
|
541
|
-
--og-radius-card: 9px;
|
|
542
|
-
--og-radius-control: 7px;
|
|
543
|
-
--og-radius-pill: 999px;
|
|
544
|
-
--og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
|
|
545
|
-
--og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
|
|
546
|
-
--og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
|
|
547
|
-
--og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
|
|
548
|
-
--og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
|
|
549
|
-
--og-positive-soft-fg: 164 217 126; /* #a4d97e */
|
|
550
|
-
--og-negative-soft-fg: 238 163 171; /* #eea3ab */
|
|
551
|
-
--og-warning-soft-fg: 224 194 116; /* #e0c274 */
|
|
552
|
-
/* derived (script-generated, KHÔNG có trong design gốc) */
|
|
553
|
-
--og-derived-critical: 254 119 154; /* #fe779a */
|
|
554
|
-
--og-derived-critical-soft-bg: 97 46 57; /* #612e39 */
|
|
555
|
-
--og-derived-gradient-accent-end: 174 233 134; /* #aee986 */
|
|
556
|
-
--og-derived-gradient-positive-end: 157 226 123; /* #9de27b */
|
|
557
|
-
--shadow-color: 255 255 255;
|
|
558
|
-
/* severity tones — bộ LEGACY (vivid), không lấy từ design V2 */
|
|
559
|
-
--og-tone-success: 76 175 80;
|
|
560
|
-
--og-tone-warning: 255 188 59;
|
|
561
|
-
--og-tone-critical: 236 25 56;
|
|
562
|
-
--og-tone-danger: 249 133 7;
|
|
563
|
-
--og-tone-information: 62 175 240;
|
|
564
|
-
--og-tone-discovery: 119 113 227;
|
|
565
|
-
/* severity ramp cho CHART — literal đo trên demo, không phải token design */
|
|
566
|
-
--og-sev-critical: 207 103 96; /* #cf6760 — #7f1d1d nâng L cho nền tối */
|
|
567
|
-
--og-sev-danger: 246 68 61; /* #f6443d */
|
|
568
|
-
--og-sev-warning: 245 158 11; /* #f59e0b */
|
|
569
|
-
--og-sev-success: 22 163 74; /* #16a34a */
|
|
570
|
-
|
|
571
|
-
/* ---- LEGACY ALIASES (@deprecated — gỡ khi kết thúc campaign V2) ----
|
|
572
|
-
Các biến cũ trỏ vào --og-* qua var() nên chỉ cần khai báo MỘT lần:
|
|
573
|
-
var() resolve tại element sử dụng, giá trị og đúng theo theme đang active. */
|
|
574
|
-
--gradient-primary-start: var(--og-accent);
|
|
575
|
-
--gradient-primary-end: var(--og-derived-gradient-accent-end);
|
|
576
|
-
--gradient-success-start: var(--og-positive);
|
|
577
|
-
--gradient-success-end: var(--og-derived-gradient-positive-end);
|
|
578
|
-
--bg-primary: var(--og-surface);
|
|
579
|
-
--bg-canvas: var(--og-page-bg);
|
|
580
|
-
--primary: var(--og-accent);
|
|
581
|
-
--success: var(--og-tone-success);
|
|
582
|
-
--warning: var(--og-tone-warning);
|
|
583
|
-
--critical: var(--og-tone-critical);
|
|
584
|
-
--danger: var(--og-tone-danger);
|
|
585
|
-
--discovery: var(--og-tone-discovery);
|
|
586
|
-
--information: var(--og-tone-information);
|
|
587
|
-
--text-primary: var(--og-text);
|
|
588
|
-
--text-secondary: var(--og-text-muted);
|
|
589
|
-
--border-default: var(--og-border);
|
|
590
|
-
--text-placeholder: var(--og-text-faint);
|
|
591
|
-
--step-bg: var(--og-text);
|
|
592
|
-
--text-link-primary: var(--og-accent-fg);
|
|
593
|
-
--bg-primary-hover: var(--og-surface-sunken);
|
|
594
|
-
--text-focus: var(--og-accent-contrast);
|
|
595
|
-
--bg-primary-neutral: var(--og-border-strong);
|
|
596
|
-
--text-selected-primary: var(--og-accent-fg);
|
|
597
|
-
--button-default: var(--og-text);
|
|
598
|
-
--text-inverse: var(--og-surface);
|
|
599
|
-
--bg-error-bold: var(--og-negative);
|
|
600
|
-
--text-error: var(--og-negative);
|
|
601
|
-
--border-interactive: var(--og-accent);
|
|
602
|
-
--border-selected: var(--og-accent);
|
|
603
|
-
--button-primary: var(--og-accent);
|
|
604
|
-
--border-strong: var(--og-border-strong);
|
|
605
|
-
--border-secondary-hover: var(--og-accent);
|
|
606
|
-
--bg-secondary-hover: var(--og-neutral-soft-bg);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
html:not([data-og-theme]) body.light_theme {
|
|
610
|
-
/* ---- LEGACY ALIASES (@deprecated — gỡ khi kết thúc campaign V2) ----
|
|
611
|
-
Các biến cũ trỏ vào --og-* qua var() nên chỉ cần khai báo MỘT lần:
|
|
612
|
-
var() resolve tại element sử dụng, giá trị og đúng theo theme đang active. */
|
|
613
|
-
--gradient-primary-start: var(--og-accent);
|
|
614
|
-
--gradient-primary-end: var(--og-derived-gradient-accent-end);
|
|
615
|
-
--gradient-success-start: var(--og-positive);
|
|
616
|
-
--gradient-success-end: var(--og-derived-gradient-positive-end);
|
|
617
|
-
--bg-primary: var(--og-surface);
|
|
618
|
-
--bg-canvas: var(--og-page-bg);
|
|
619
|
-
--primary: var(--og-accent);
|
|
620
|
-
--success: var(--og-tone-success);
|
|
621
|
-
--warning: var(--og-tone-warning);
|
|
622
|
-
--critical: var(--og-tone-critical);
|
|
623
|
-
--danger: var(--og-tone-danger);
|
|
624
|
-
--discovery: var(--og-tone-discovery);
|
|
625
|
-
--information: var(--og-tone-information);
|
|
626
|
-
--text-primary: var(--og-text);
|
|
627
|
-
--text-secondary: var(--og-text-muted);
|
|
628
|
-
--border-default: var(--og-border);
|
|
629
|
-
--text-placeholder: var(--og-text-faint);
|
|
630
|
-
--step-bg: var(--og-text);
|
|
631
|
-
--text-link-primary: var(--og-accent-fg);
|
|
632
|
-
--bg-primary-hover: var(--og-surface-sunken);
|
|
633
|
-
--text-focus: var(--og-accent-contrast);
|
|
634
|
-
--bg-primary-neutral: var(--og-border-strong);
|
|
635
|
-
--text-selected-primary: var(--og-accent-fg);
|
|
636
|
-
--button-default: var(--og-text);
|
|
637
|
-
--text-inverse: var(--og-surface);
|
|
638
|
-
--bg-error-bold: var(--og-negative);
|
|
639
|
-
--text-error: var(--og-negative);
|
|
640
|
-
--border-interactive: var(--og-accent);
|
|
641
|
-
--border-selected: var(--og-accent);
|
|
642
|
-
--button-primary: var(--og-accent);
|
|
643
|
-
--border-strong: var(--og-border-strong);
|
|
644
|
-
--border-secondary-hover: var(--og-accent);
|
|
645
|
-
--bg-secondary-hover: var(--og-neutral-soft-bg);
|
|
646
|
-
}
|
|
@@ -64,6 +64,24 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
|
|
|
64
64
|
private pendingFrames;
|
|
65
65
|
private isMobile;
|
|
66
66
|
private containerWidth;
|
|
67
|
+
private containerHeight;
|
|
68
|
+
/**
|
|
69
|
+
* Mốc (performance.now) mà animation "xuất hiện" của lần setOption gần nhất
|
|
70
|
+
* chắc chắn đã xong. ECharts mặc định animationDuration 1000ms; trong khoảng
|
|
71
|
+
* này KHÔNG được `resize()` — resize nhảy thẳng tới trạng thái cuối và giết
|
|
72
|
+
* animation (đo 05/09: chart PM bị cắt ở ~800ms khi thanh cuộn xuất hiện
|
|
73
|
+
* lúc lưới card nạp xong làm bề rộng đổi 720→710).
|
|
74
|
+
*/
|
|
75
|
+
private appearUntil;
|
|
76
|
+
private static readonly APPEAR_MS;
|
|
77
|
+
private deferTimer?;
|
|
78
|
+
/**
|
|
79
|
+
* Thay đổi kích thước ghi nhận TRONG cửa sổ appear, chờ áp sau. `measure()`
|
|
80
|
+
* đã cập nhật containerWidth/Height ngay lúc đo, nên lần gọi hoãn không thể
|
|
81
|
+
* dựa vào "có đổi không" nữa — phải nhớ bằng cờ.
|
|
82
|
+
*/
|
|
83
|
+
private pendingResize;
|
|
84
|
+
private pendingMobile;
|
|
67
85
|
constructor(ref: ElementRef<HTMLElement>, cdr: ChangeDetectorRef);
|
|
68
86
|
ngAfterViewInit(): void;
|
|
69
87
|
ngOnChanges(): void;
|
|
@@ -95,30 +113,29 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
|
|
|
95
113
|
onChartClick(event: any): void;
|
|
96
114
|
onChartInit(raw: any): void;
|
|
97
115
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* ghi chú ở `forceMediaRecalc`.
|
|
116
|
+
* Đo host và ghi lại; trả về CÁI GÌ đổi so với lần đo trước.
|
|
117
|
+
* Khi bị ẩn (0×0) thì KHÔNG ghi — giữ kích thước hiển thị cuối, để lúc hiện
|
|
118
|
+
* lại cùng cỡ thì coi như không đổi (không resize, không cắt animation).
|
|
102
119
|
*/
|
|
103
|
-
private
|
|
120
|
+
private measure;
|
|
121
|
+
/** Animation xuất hiện vừa bắt đầu — khoá resize tới khi nó xong. */
|
|
122
|
+
private markAppear;
|
|
123
|
+
/** Pie/sunburst đặt legend + số giữa theo px của containerWidth → phải dựng lại. */
|
|
124
|
+
private needsLayoutRebuild;
|
|
125
|
+
private deferResize;
|
|
104
126
|
/**
|
|
105
|
-
*
|
|
106
|
-
* **animation lúc chart xuất hiện bị giết**.
|
|
107
|
-
*
|
|
108
|
-
* Đường đi: `onChartInit` → schedule(forceMediaRecalc) → chạy ở frame KẾ
|
|
109
|
-
* TIẾP. Nếu chỗ này vô điều kiện `rebuildChart()` (clear + setOption notMerge)
|
|
110
|
-
* thì nó xoá sạch và vẽ lại từ đầu ~16ms sau lần vẽ đầu, trong khi animation
|
|
111
|
-
* xuất hiện dài ~1000ms ⇒ người dùng thấy chart hiện ra đứng im.
|
|
112
|
-
*
|
|
113
|
-
* Đo được: lần xuất hiện đầu 1/12 frame khác nhau (đứng yên) trong khi vẽ lại
|
|
114
|
-
* bằng `theme-changed` cho 9/12 → animation của ECharts vẫn bật, chỉ lần đầu
|
|
115
|
-
* bị cắt.
|
|
116
|
-
*
|
|
117
|
-
* Trước Pha 0 lỗi này bị che: lần init đầu dùng `chartOptions = {}` nên
|
|
118
|
-
* animation xuất hiện xảy ra ở lần cập nhật SAU, tức sau khi forceMediaRecalc
|
|
119
|
-
* đã chạy xong. Cổng `hasOptions` đưa options thật về ngay lần init nên lộ ra.
|
|
127
|
+
* Phản ứng với kích thước host (RO, sau init, sau khi hiện lại).
|
|
120
128
|
*
|
|
121
|
-
*
|
|
129
|
+
* Ba quy tắc, đều đo được (0.7.75 và 05/09):
|
|
130
|
+
* 1. **Ẩn (0×H) thì không làm gì** — không resize canvas về 0. Chart hiện
|
|
131
|
+
* lại cùng cỡ thì animation đã bắt đầu lúc ẩn (setOption khi resolver về,
|
|
132
|
+
* ngay trước NavigationEnd) chạy tiếp bình thường.
|
|
133
|
+
* 2. **`resize()` nhảy thẳng tới trạng thái cuối** ⇒ trong cửa sổ appear
|
|
134
|
+
* (`appearUntil`) thì HOÃN, đo lại và xử lý sau khi animation xong. Không
|
|
135
|
+
* dựng lại (clear + setOption) lúc đó vì đó là "vẽ 2 lần" theo cách khác.
|
|
136
|
+
* 3. Ngoài cửa sổ: `resize()` trước, rồi chỉ dựng lại khi preset đổi (mobile)
|
|
137
|
+
* hoặc loại chart đặt layout theo px (pie/sunburst). Thứ tự này quan
|
|
138
|
+
* trọng: resize SAU rebuild sẽ cắt ngay animation vừa bắt đầu.
|
|
122
139
|
*/
|
|
123
140
|
private forceMediaRecalc;
|
|
124
141
|
private rebuildChart;
|