@foxeltech/angular-ui 0.7.106 → 0.7.124

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.
package/components.css CHANGED
@@ -631,6 +631,10 @@ input[type='password'] {
631
631
  0 1px 2px rgb(0 0 0 / 0.15);
632
632
  }
633
633
  .btn-cta:hover {
634
+ /* The edge darkens a step past the fill — reference `.bi-newbtn:hover`
635
+ (measured 16/09). Without it the button only changed fill and shadow, so
636
+ the hover read softer than the design's. */
637
+ border-color: var(--color-cta-hover-border);
634
638
  box-shadow:
635
639
  inset 0 1px 0 rgb(255 255 255 / 0.18),
636
640
  0 2px 6px rgb(0 0 0 / 0.2);
@@ -849,3 +853,200 @@ input[type='password'] {
849
853
  .fx-rs-busy > *:not(fx-ui-route-skeleton) {
850
854
  display: none !important;
851
855
  }
856
+
857
+ /* ============================================================================
858
+ Grid tables, badges and status dots to the reference HTML (Binary
859
+ Intelligence Dashboard, porcelain) — anh Tú 15/09, moved into the lib in
860
+ 0.7.107 from three identical copies in analyze/knowledge/host styles.scss.
861
+
862
+ The reference draws its lists as CSS-grid rows, not <table>: a sunken 10px
863
+ header in 10.5px/600 tracked faint, 11px rows split by hairlines, hover to
864
+ surface-sunken, a trailing caret that turns accent and slides 2px. Columns
865
+ come from the consumer as `--fx-cols` on `.fx-grid-table`.
866
+
867
+ Inside layer(components) like the rest of this file, so a Tailwind utility
868
+ on the same element still wins.
869
+ ============================================================================ */
870
+ .fx-grid-table {
871
+ border: 1px solid rgb(var(--og-border));
872
+ border-radius: var(--og-radius-card);
873
+ background: rgb(var(--og-surface));
874
+ overflow: hidden;
875
+ }
876
+ .fx-grid-table__head,
877
+ .fx-grid-table__row {
878
+ display: grid;
879
+ grid-template-columns: var(--fx-cols);
880
+ gap: 12px;
881
+ align-items: center;
882
+ padding: 10px 12px;
883
+ }
884
+ .fx-grid-table__head {
885
+ border-bottom: 1px solid rgb(var(--og-border));
886
+ background: rgb(var(--og-surface-sunken));
887
+ font-size: var(--og-fs-2xs);
888
+ font-weight: 600;
889
+ letter-spacing: var(--og-tracking-label);
890
+ color: rgb(var(--og-text-faint));
891
+ }
892
+ .fx-grid-table__row {
893
+ appearance: none;
894
+ width: 100%;
895
+ padding: 11px 12px;
896
+ border: 0;
897
+ border-bottom: 1px solid rgb(var(--og-border-subtle));
898
+ background: transparent;
899
+ text-align: left;
900
+ cursor: pointer;
901
+ font: inherit;
902
+ color: inherit;
903
+ transition: background-color 130ms ease;
904
+ }
905
+ .fx-grid-table__row:last-child {
906
+ border-bottom: 0;
907
+ }
908
+ .fx-grid-table__row:hover {
909
+ background: rgb(var(--og-surface-sunken));
910
+ }
911
+ .fx-grid-table__row--on {
912
+ background: rgb(var(--og-accent-soft-bg));
913
+ }
914
+ .fx-grid-table__row:focus-visible {
915
+ outline: 2px solid rgb(var(--og-accent));
916
+ outline-offset: -2px;
917
+ }
918
+ .fx-grid-caret {
919
+ font-size: 15px;
920
+ line-height: 1;
921
+ color: rgb(var(--og-text-faint));
922
+ justify-self: end;
923
+ transition:
924
+ color 130ms ease,
925
+ transform 130ms ease;
926
+ }
927
+ .fx-grid-table__row:hover .fx-grid-caret {
928
+ color: rgb(var(--og-accent));
929
+ transform: translateX(2px);
930
+ }
931
+ .fx-grid-table__num {
932
+ font-family: var(--og-font-mono);
933
+ font-size: var(--og-fs-sm);
934
+ color: rgb(var(--og-text-body));
935
+ text-align: right;
936
+ font-variant-numeric: tabular-nums;
937
+ }
938
+ .fx-grid-table__head .fx-grid-table__num {
939
+ font-family: inherit;
940
+ font-size: inherit;
941
+ color: inherit;
942
+ }
943
+ .fx-grid-table__strong {
944
+ font-size: var(--og-fs-sm);
945
+ font-weight: 600;
946
+ color: rgb(var(--og-text));
947
+ }
948
+ .fx-grid-table__muted {
949
+ font-size: var(--og-fs-xs);
950
+ color: rgb(var(--og-text-muted));
951
+ }
952
+ .fx-grid-table__mono {
953
+ font-family: var(--og-font-mono);
954
+ font-size: var(--og-fs-xs);
955
+ color: rgb(var(--og-text-faint));
956
+ }
957
+ .fx-grid-empty {
958
+ padding: 15px;
959
+ text-align: center;
960
+ color: rgb(var(--og-text-muted));
961
+ font-size: var(--og-fs-sm);
962
+ border: 1px dashed rgb(var(--og-border));
963
+ border-radius: var(--og-radius-card);
964
+ }
965
+
966
+ /* `.og-badge`: 3px 10px, pill, 11.5px, soft tint by tone; outline = the
967
+ neutral "Low" band; solid = Critical. Tone names are the SEMANTIC tokens
968
+ (`--og-negative`, not the chart ramp) — see the note above `.tone-tint-*`. */
969
+ .fx-badge {
970
+ display: inline-flex;
971
+ align-items: center;
972
+ gap: 5px;
973
+ padding: 3px 10px;
974
+ border-radius: var(--og-radius-pill);
975
+ font-size: var(--og-fs-xs);
976
+ line-height: 1.5;
977
+ white-space: nowrap;
978
+ /* 400, per the reference's risk badge ("High · 82", measured 16/09). At 500
979
+ it sat a step heavier than every other 11.5px label around it. */
980
+ font-weight: 400;
981
+ }
982
+ .fx-badge--negative {
983
+ background: rgb(var(--og-negative-soft-bg));
984
+ color: rgb(var(--og-negative));
985
+ }
986
+ .fx-badge--warning {
987
+ background: rgb(var(--og-warning-soft-bg));
988
+ color: rgb(var(--og-warning));
989
+ }
990
+ .fx-badge--positive {
991
+ background: rgb(var(--og-positive-soft-bg));
992
+ color: rgb(var(--og-positive));
993
+ }
994
+ .fx-badge--neutral {
995
+ background: rgb(var(--og-neutral-soft-bg));
996
+ color: rgb(var(--og-neutral-soft-fg));
997
+ }
998
+ .fx-badge--accent {
999
+ background: rgb(var(--og-accent-soft-bg));
1000
+ color: rgb(var(--og-accent-soft-fg));
1001
+ }
1002
+ .fx-badge--outline {
1003
+ background: none;
1004
+ border: 1px solid rgb(var(--og-border));
1005
+ color: rgb(var(--og-text-muted));
1006
+ }
1007
+ .fx-badge--solid {
1008
+ background: rgb(var(--og-text));
1009
+ color: rgb(var(--og-surface));
1010
+ }
1011
+
1012
+ /* `.og-statusdot`: a 7px mark and the state's name. */
1013
+ .fx-statusdot {
1014
+ display: inline-flex;
1015
+ align-items: center;
1016
+ gap: 6px;
1017
+ font-size: var(--og-fs-sm);
1018
+ color: rgb(var(--og-text-body));
1019
+ white-space: nowrap;
1020
+ }
1021
+ .fx-statusdot::before {
1022
+ content: '';
1023
+ width: 7px;
1024
+ height: 7px;
1025
+ border-radius: 50%;
1026
+ background: rgb(var(--og-text-faint));
1027
+ flex: none;
1028
+ }
1029
+ .fx-statusdot--positive::before {
1030
+ background: rgb(var(--og-positive));
1031
+ }
1032
+ .fx-statusdot--negative::before {
1033
+ background: rgb(var(--og-negative));
1034
+ }
1035
+ .fx-statusdot--warning::before {
1036
+ background: rgb(var(--og-warning));
1037
+ }
1038
+ .fx-statusdot--accent::before {
1039
+ background: rgb(var(--og-accent));
1040
+ }
1041
+
1042
+ /* A tooltip must never take the pointer. Material gives its overlay pane
1043
+ `pointer-events: auto`, and with the drawer COLLAPSED a nav item's tooltip
1044
+ opens at x≈70..110 while the collapse/expand button sits at 58..82 — the
1045
+ tooltip covered the button's right half and swallowed the click, which is
1046
+ why the toggle "sometimes" did nothing (MEASURED 16/09). Nothing in a
1047
+ tooltip is interactive, so this is safe everywhere. */
1048
+ .mat-mdc-tooltip-panel,
1049
+ .mat-mdc-tooltip,
1050
+ .mat-mdc-tooltip-panel > * {
1051
+ pointer-events: none !important;
1052
+ }
@@ -28,6 +28,14 @@ class ChartComponent {
28
28
  * rather than an annotation.
29
29
  */
30
30
  pieCenterValueSize = input(24, ...(ngDevMode ? [{ debugName: "pieCenterValueSize" }] : /* istanbul ignore next */ []));
31
+ /**
32
+ * The donut's [inner, outer] radius — ECharts' own vocabulary, so either a
33
+ * percentage of the shorter side or a pixel count. The default is what the
34
+ * ring has always been. The reference dashboard draws a 140px ring with a
35
+ * 22px stroke, i.e. `[48, 70]` at `height="140px"` — the panel that wants it
36
+ * says so; the other pies keep their proportions.
37
+ */
38
+ pieRadius = input(['40%', '70%'], ...(ngDevMode ? [{ debugName: "pieRadius" }] : /* istanbul ignore next */ []));
31
39
  mobileWidth = input(350, ...(ngDevMode ? [{ debugName: "mobileWidth" }] : /* istanbul ignore next */ []));
32
40
  /** Override font for canvas text; empty = follow the theme's --og-font-figure. */
33
41
  labelFontFamily = input('', ...(ngDevMode ? [{ debugName: "labelFontFamily" }] : /* istanbul ignore next */ []));
@@ -94,6 +102,8 @@ class ChartComponent {
94
102
  */
95
103
  appearUntil = 0;
96
104
  static APPEAR_MS = 1100;
105
+ /** Quiet time a host size has to hold before a resize is acted on. */
106
+ static RESIZE_SETTLE_MS = 120;
97
107
  deferTimer;
98
108
  /**
99
109
  * Thay đổi kích thước ghi nhận TRONG cửa sổ appear, chờ áp sau. `measure()`
@@ -123,10 +133,15 @@ class ChartComponent {
123
133
  // resize() mù sau throttle 100ms, kể cả về 0×H khi phần tử display:none
124
134
  // (skeleton route ẩn nội dung cũ) rồi resize lại khi hiện → animation
125
135
  // xuất hiện bị giết (đo 05/09 khi đổi project trên chip breadcrumb).
136
+ // Coalesced: a host that is being ANIMATED (the drawer's 300ms width
137
+ // tween, a panel sliding open) fires one RO event per frame, and acting
138
+ // on each one resized + rebuilt the pie ~18 times and replayed its appear
139
+ // animation twice (anh Tú 15/09, donut on sidebar toggle). Wait until the
140
+ // size has been still for RESIZE_SETTLE_MS, then act once.
126
141
  this.resizeObserver = new ResizeObserver(() => {
127
142
  if (!this.canInit || this.destroyed)
128
143
  return;
129
- this.forceMediaRecalc();
144
+ this.deferResize(ChartComponent.RESIZE_SETTLE_MS);
130
145
  });
131
146
  this.resizeObserver.observe(this.ref.nativeElement);
132
147
  }
@@ -285,10 +300,18 @@ class ChartComponent {
285
300
  markAppear() {
286
301
  this.appearUntil = performance.now() + ChartComponent.APPEAR_MS;
287
302
  }
288
- /** Pie/sunburst đặt legend + số giữa theo px của containerWidth → phải dựng lại. */
303
+ /**
304
+ * Sunburst, and a pie WITH its built-in legend, place things in px of
305
+ * containerWidth → must rebuild on resize. A legend-less pie centres its
306
+ * ring at 50%/50% and its total in a `left: 'center' / top: 'middle'`
307
+ * graphic group, so `resize()` alone re-centres it — no rebuild, no
308
+ * second appear animation (15/09, donut on sidebar toggle).
309
+ */
289
310
  needsLayoutRebuild() {
290
311
  const type = this.detectType();
291
- return type === 'pie' || type === 'sunburst';
312
+ if (type === 'sunburst')
313
+ return true;
314
+ return type === 'pie' && this.showPieLegend();
292
315
  }
293
316
  deferResize(ms) {
294
317
  if (this.deferTimer !== undefined)
@@ -341,10 +364,19 @@ class ChartComponent {
341
364
  if (!chart)
342
365
  return;
343
366
  chart.resize();
344
- if (mobile || this.needsLayoutRebuild())
367
+ // A rebuild forced by GEOMETRY draws the same data at a new size: no
368
+ // appear animation, or the panel visibly redraws (that was the "renders
369
+ // twice" of 15/09). Only a preset change (mobile) keeps the animation.
370
+ if (mobile)
345
371
  this.rebuildChart();
372
+ else if (this.needsLayoutRebuild())
373
+ this.rebuildChart(true);
346
374
  }
347
- rebuildChart() {
375
+ /**
376
+ * @param silent redraw for a new size only — same data, no appear animation,
377
+ * and the appear window is not re-armed.
378
+ */
379
+ rebuildChart(silent = false) {
348
380
  if (this.destroyed)
349
381
  return;
350
382
  this.isEmpty = this.checkEmpty();
@@ -352,7 +384,7 @@ class ChartComponent {
352
384
  this.liveChart?.clear();
353
385
  return;
354
386
  }
355
- const options = this.buildOptions();
387
+ const options = silent ? { ...this.buildOptions(), animation: false } : this.buildOptions();
356
388
  const chart = this.liveChart;
357
389
  if (!chart) {
358
390
  if (this.awaitingFirstInit) {
@@ -370,7 +402,8 @@ class ChartComponent {
370
402
  this.hasOptions = true;
371
403
  chart.clear();
372
404
  chart.setOption(options, true);
373
- this.markAppear();
405
+ if (!silent)
406
+ this.markAppear();
374
407
  }
375
408
  checkEmpty() {
376
409
  const type = this.detectType();
@@ -489,7 +522,7 @@ class ChartComponent {
489
522
  series: [
490
523
  {
491
524
  type: 'pie',
492
- radius: ['40%', '70%'],
525
+ radius: this.pieRadius(),
493
526
  center: layout.pieCenter,
494
527
  data,
495
528
  label: {
@@ -509,46 +542,62 @@ class ChartComponent {
509
542
  },
510
543
  },
511
544
  ],
512
- // Centre figure: positioned by x/y in pixels graphic left/top
513
- // percentages place the element's bounding-box EDGE, which shifted the
514
- // total right/down by half its own size (mis-centred, caption clipped).
545
+ // Centre figure. With the built-in legend the ring sits off-centre
546
+ // (layout.pieCenter in px of containerWidth), so the texts are placed
547
+ // by x/y in pixels graphic left/top PERCENTAGES place the element's
548
+ // bounding-box EDGE and mis-centred the total. Without the legend the
549
+ // ring is at 50%/50% and the pair goes in a group with the KEYWORDS
550
+ // left: 'center' / top: 'middle', which ECharts re-lays out on
551
+ // `resize()` — so a legend-less donut never needs a rebuild to stay
552
+ // centred (15/09).
515
553
  graphic: (() => {
516
- const cx = (this.containerWidth || 400) * (parseFloat(layout.pieCenter[0]) / 100);
517
- const ch = this.ref.nativeElement.clientHeight || 300;
518
- const cy = ch * (parseFloat(layout.pieCenter[1]) / 100);
519
554
  const centre = this.pieCentreAnchors();
520
- return [
521
- {
555
+ const font = this.resolvedFontFamily();
556
+ const totalText = {
557
+ type: 'text',
558
+ style: {
559
+ text: total.toString(),
560
+ fontSize: this.pieCenterValueSize(),
561
+ fontWeight: 600,
562
+ fontFamily: font,
563
+ fill: FxUtils.convertColorFromVariable('--text-primary'),
564
+ align: 'center',
565
+ verticalAlign: 'middle',
566
+ },
567
+ };
568
+ const captionText = this.pieCenterLabel()
569
+ ? {
522
570
  type: 'text',
523
- x: cx,
524
- y: cy + centre.total,
525
571
  style: {
526
- text: total.toString(),
527
- fontSize: this.pieCenterValueSize(),
528
- fontWeight: 600,
529
- fontFamily: this.resolvedFontFamily(),
530
- fill: FxUtils.convertColorFromVariable('--text-primary'),
572
+ text: this.pieCenterLabel(),
573
+ fontSize: 11,
574
+ fontFamily: font,
575
+ fill: FxUtils.convertColorFromVariable('--text-secondary'),
531
576
  align: 'center',
532
577
  verticalAlign: 'middle',
533
578
  },
534
- },
535
- ...(this.pieCenterLabel()
536
- ? [
537
- {
538
- type: 'text',
539
- x: cx,
540
- y: cy + centre.caption,
541
- style: {
542
- text: this.pieCenterLabel(),
543
- fontSize: 11,
544
- fontFamily: this.resolvedFontFamily(),
545
- fill: FxUtils.convertColorFromVariable('--text-secondary'),
546
- align: 'center',
547
- verticalAlign: 'middle',
548
- },
549
- },
550
- ]
551
- : []),
579
+ }
580
+ : undefined;
581
+ if (!this.showPieLegend()) {
582
+ return [
583
+ {
584
+ type: 'group',
585
+ left: 'center',
586
+ top: 'middle',
587
+ silent: true,
588
+ children: [
589
+ { ...totalText, x: 0, y: centre.total },
590
+ ...(captionText ? [{ ...captionText, x: 0, y: centre.caption }] : []),
591
+ ],
592
+ },
593
+ ];
594
+ }
595
+ const cx = (this.containerWidth || 400) * (parseFloat(layout.pieCenter[0]) / 100);
596
+ const ch = this.ref.nativeElement.clientHeight || 300;
597
+ const cy = ch * (parseFloat(layout.pieCenter[1]) / 100);
598
+ return [
599
+ { ...totalText, x: cx, y: cy + centre.total },
600
+ ...(captionText ? [{ ...captionText, x: cx, y: cy + centre.caption }] : []),
552
601
  ];
553
602
  })(),
554
603
  };
@@ -623,9 +672,33 @@ class ChartComponent {
623
672
  itemStyle: { color: FxUtils.convertColorFromVariable('--text-placeholder', 1, { legacyCommaSyntax: true }) },
624
673
  });
625
674
  }
675
+ const faint = FxUtils.convertColorFromVariable('--og-text-faint', 1, { legacyCommaSyntax: true });
626
676
  return {
627
677
  tooltip: { trigger: 'axis' },
628
- grid: { left: '3%', right: '4%', bottom: '8%', top: '10%', containLabel: true },
678
+ // The reference labels the two lines under the chart ("Analyses run /
679
+ // Findings opened"); the series already carry those names, they were
680
+ // simply never drawn. Only shown when there ARE two lines — a lone
681
+ // series needs no key. NOTE the row sits BELOW the date axis here,
682
+ // where the reference draws it just above: ECharts pins axis labels to
683
+ // the grid edge, so the two cannot swap without hand-placing both.
684
+ legend: {
685
+ show: series.length > 1,
686
+ data: series.map((serie) => serie.name),
687
+ left: '3%',
688
+ bottom: 0,
689
+ icon: 'roundRect',
690
+ itemWidth: 12,
691
+ itemHeight: 2,
692
+ itemGap: 14,
693
+ textStyle: { fontSize: 10.5, color: faint, fontFamily: this.resolvedFontFamily() },
694
+ },
695
+ grid: {
696
+ left: '3%',
697
+ right: '4%',
698
+ bottom: series.length > 1 ? 28 : '8%',
699
+ top: '10%',
700
+ containLabel: true,
701
+ },
629
702
  xAxis: { ...this.categoryAxis(data?.categories || []), boundaryGap: false },
630
703
  yAxis: this.valueAxis(),
631
704
  series,
@@ -858,12 +931,12 @@ class ChartComponent {
858
931
  });
859
932
  }
860
933
  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 });
861
- 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 }, pieCenterValueSize: { classPropertyName: "pieCenterValueSize", publicName: "pieCenterValueSize", 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 }, showPointLabels: { classPropertyName: "showPointLabels", publicName: "showPointLabels", 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 });
934
+ 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 }, pieCenterValueSize: { classPropertyName: "pieCenterValueSize", publicName: "pieCenterValueSize", isSignal: true, isRequired: false, transformFunction: null }, pieRadius: { classPropertyName: "pieRadius", publicName: "pieRadius", 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 }, showPointLabels: { classPropertyName: "showPointLabels", publicName: "showPointLabels", 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 });
862
935
  }
863
936
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: ChartComponent, decorators: [{
864
937
  type: Component,
865
938
  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"] }]
866
- }], 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 }] }], pieCenterValueSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pieCenterValueSize", 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 }] }], showPointLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPointLabels", required: false }] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }], chartInit: [{ type: i0.Output, args: ["chartInit"] }], onThemeChange: [{
939
+ }], 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 }] }], pieCenterValueSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pieCenterValueSize", required: false }] }], pieRadius: [{ type: i0.Input, args: [{ isSignal: true, alias: "pieRadius", 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 }] }], showPointLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPointLabels", required: false }] }], chartClick: [{ type: i0.Output, args: ["chartClick"] }], chartInit: [{ type: i0.Output, args: ["chartInit"] }], onThemeChange: [{
867
940
  type: HostListener,
868
941
  args: ['window:theme-changed']
869
942
  }] } });