@foxeltech/angular-ui 0.7.106 → 0.7.108

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxeltech/angular-ui",
3
- "version": "0.7.106",
3
+ "version": "0.7.108",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -849,3 +849,186 @@ input[type='password'] {
849
849
  .fx-rs-busy > *:not(fx-ui-route-skeleton) {
850
850
  display: none !important;
851
851
  }
852
+
853
+ /* ============================================================================
854
+ Grid tables, badges and status dots to the reference HTML (Binary
855
+ Intelligence Dashboard, porcelain) — anh Tú 15/09, moved into the lib in
856
+ 0.7.107 from three identical copies in analyze/knowledge/host styles.scss.
857
+
858
+ The reference draws its lists as CSS-grid rows, not <table>: a sunken 10px
859
+ header in 10.5px/600 tracked faint, 11px rows split by hairlines, hover to
860
+ surface-sunken, a trailing caret that turns accent and slides 2px. Columns
861
+ come from the consumer as `--fx-cols` on `.fx-grid-table`.
862
+
863
+ Inside layer(components) like the rest of this file, so a Tailwind utility
864
+ on the same element still wins.
865
+ ============================================================================ */
866
+ .fx-grid-table {
867
+ border: 1px solid rgb(var(--og-border));
868
+ border-radius: var(--og-radius-card);
869
+ background: rgb(var(--og-surface));
870
+ overflow: hidden;
871
+ }
872
+ .fx-grid-table__head,
873
+ .fx-grid-table__row {
874
+ display: grid;
875
+ grid-template-columns: var(--fx-cols);
876
+ gap: 12px;
877
+ align-items: center;
878
+ padding: 10px 12px;
879
+ }
880
+ .fx-grid-table__head {
881
+ border-bottom: 1px solid rgb(var(--og-border));
882
+ background: rgb(var(--og-surface-sunken));
883
+ font-size: var(--og-fs-2xs);
884
+ font-weight: 600;
885
+ letter-spacing: var(--og-tracking-label);
886
+ color: rgb(var(--og-text-faint));
887
+ }
888
+ .fx-grid-table__row {
889
+ appearance: none;
890
+ width: 100%;
891
+ padding: 11px 12px;
892
+ border: 0;
893
+ border-bottom: 1px solid rgb(var(--og-border-subtle));
894
+ background: transparent;
895
+ text-align: left;
896
+ cursor: pointer;
897
+ font: inherit;
898
+ color: inherit;
899
+ transition: background-color 130ms ease;
900
+ }
901
+ .fx-grid-table__row:last-child {
902
+ border-bottom: 0;
903
+ }
904
+ .fx-grid-table__row:hover {
905
+ background: rgb(var(--og-surface-sunken));
906
+ }
907
+ .fx-grid-table__row--on {
908
+ background: rgb(var(--og-accent-soft-bg));
909
+ }
910
+ .fx-grid-table__row:focus-visible {
911
+ outline: 2px solid rgb(var(--og-accent));
912
+ outline-offset: -2px;
913
+ }
914
+ .fx-grid-caret {
915
+ font-size: 15px;
916
+ line-height: 1;
917
+ color: rgb(var(--og-text-faint));
918
+ justify-self: end;
919
+ transition:
920
+ color 130ms ease,
921
+ transform 130ms ease;
922
+ }
923
+ .fx-grid-table__row:hover .fx-grid-caret {
924
+ color: rgb(var(--og-accent));
925
+ transform: translateX(2px);
926
+ }
927
+ .fx-grid-table__num {
928
+ font-family: var(--og-font-mono);
929
+ font-size: var(--og-fs-sm);
930
+ color: rgb(var(--og-text-body));
931
+ text-align: right;
932
+ font-variant-numeric: tabular-nums;
933
+ }
934
+ .fx-grid-table__head .fx-grid-table__num {
935
+ font-family: inherit;
936
+ font-size: inherit;
937
+ color: inherit;
938
+ }
939
+ .fx-grid-table__strong {
940
+ font-size: var(--og-fs-sm);
941
+ font-weight: 600;
942
+ color: rgb(var(--og-text));
943
+ }
944
+ .fx-grid-table__muted {
945
+ font-size: var(--og-fs-xs);
946
+ color: rgb(var(--og-text-muted));
947
+ }
948
+ .fx-grid-table__mono {
949
+ font-family: var(--og-font-mono);
950
+ font-size: var(--og-fs-xs);
951
+ color: rgb(var(--og-text-faint));
952
+ }
953
+ .fx-grid-empty {
954
+ padding: 15px;
955
+ text-align: center;
956
+ color: rgb(var(--og-text-muted));
957
+ font-size: var(--og-fs-sm);
958
+ border: 1px dashed rgb(var(--og-border));
959
+ border-radius: var(--og-radius-card);
960
+ }
961
+
962
+ /* `.og-badge`: 3px 10px, pill, 11.5px, soft tint by tone; outline = the
963
+ neutral "Low" band; solid = Critical. Tone names are the SEMANTIC tokens
964
+ (`--og-negative`, not the chart ramp) — see the note above `.tone-tint-*`. */
965
+ .fx-badge {
966
+ display: inline-flex;
967
+ align-items: center;
968
+ gap: 5px;
969
+ padding: 3px 10px;
970
+ border-radius: var(--og-radius-pill);
971
+ font-size: var(--og-fs-xs);
972
+ line-height: 1.5;
973
+ white-space: nowrap;
974
+ font-weight: 500;
975
+ }
976
+ .fx-badge--negative {
977
+ background: rgb(var(--og-negative-soft-bg));
978
+ color: rgb(var(--og-negative));
979
+ }
980
+ .fx-badge--warning {
981
+ background: rgb(var(--og-warning-soft-bg));
982
+ color: rgb(var(--og-warning));
983
+ }
984
+ .fx-badge--positive {
985
+ background: rgb(var(--og-positive-soft-bg));
986
+ color: rgb(var(--og-positive));
987
+ }
988
+ .fx-badge--neutral {
989
+ background: rgb(var(--og-neutral-soft-bg));
990
+ color: rgb(var(--og-neutral-soft-fg));
991
+ }
992
+ .fx-badge--accent {
993
+ background: rgb(var(--og-accent-soft-bg));
994
+ color: rgb(var(--og-accent-soft-fg));
995
+ }
996
+ .fx-badge--outline {
997
+ background: none;
998
+ border: 1px solid rgb(var(--og-border));
999
+ color: rgb(var(--og-text-muted));
1000
+ }
1001
+ .fx-badge--solid {
1002
+ background: rgb(var(--og-text));
1003
+ color: rgb(var(--og-surface));
1004
+ }
1005
+
1006
+ /* `.og-statusdot`: a 7px mark and the state's name. */
1007
+ .fx-statusdot {
1008
+ display: inline-flex;
1009
+ align-items: center;
1010
+ gap: 6px;
1011
+ font-size: var(--og-fs-sm);
1012
+ color: rgb(var(--og-text-body));
1013
+ white-space: nowrap;
1014
+ }
1015
+ .fx-statusdot::before {
1016
+ content: '';
1017
+ width: 7px;
1018
+ height: 7px;
1019
+ border-radius: 50%;
1020
+ background: rgb(var(--og-text-faint));
1021
+ flex: none;
1022
+ }
1023
+ .fx-statusdot--positive::before {
1024
+ background: rgb(var(--og-positive));
1025
+ }
1026
+ .fx-statusdot--negative::before {
1027
+ background: rgb(var(--og-negative));
1028
+ }
1029
+ .fx-statusdot--warning::before {
1030
+ background: rgb(var(--og-warning));
1031
+ }
1032
+ .fx-statusdot--accent::before {
1033
+ background: rgb(var(--og-accent));
1034
+ }
@@ -89,7 +89,7 @@
89
89
  class="drawer-item"
90
90
  [class.active]="isActiveRoute(item.route)"
91
91
  [class.has-children]="item.children && item.children.length > 0"
92
- [style.paddingLeft.px]="isExpanded ? 16 + level * 16 : 16"
92
+ [style.paddingLeft.px]="isExpanded ? 8 + level * 16 : 16"
93
93
  [matTooltip]="!isExpanded ? item.label : ''"
94
94
  matTooltipPosition="right"
95
95
  matTooltipClass="text-text-primary text-xs font-semibold"
@@ -110,7 +110,7 @@
110
110
  class="drawer-item"
111
111
  [class.active]="isActiveRoute(item.route)"
112
112
  [class.has-children]="item.children && item.children.length > 0"
113
- [style.paddingLeft.px]="isExpanded ? 16 + level * 16 : 16"
113
+ [style.paddingLeft.px]="isExpanded ? 8 + level * 16 : 16"
114
114
  [matTooltip]="!isExpanded ? item.label : ''"
115
115
  matTooltipPosition="right"
116
116
  matTooltipClass="text-text-primary text-sm font-semibold"
@@ -14,16 +14,24 @@
14
14
  hàng brand nằm ngoài container, nếu tô ở container thì 60px trên cùng
15
15
  lộ nền của trang. Sáng: drawer xám, nội dung trắng; tối: drawer tối hơn
16
16
  surface — đúng quy ước sidebar của cả hai chiều. */
17
- @apply relative h-full border-r border-border-default flex flex-col bg-page;
17
+ /* 0.7.107 (anh 15/09, reference HTML "Binary Intelligence Dashboard"):
18
+ the demo's `.og-sidebar` is `--og-surface-sunken` (#fbfbfa on porcelain)
19
+ with a `--og-border-subtle` right edge — not the page's `--og-page-bg`
20
+ (#f2f2f1). Was overridden from platform-host's styles.scss with doubled
21
+ selectors; it lives here now so all five apps agree. */
22
+ @apply relative h-full flex flex-col;
23
+ background: rgb(var(--og-surface-sunken));
24
+ border-right: 1px solid rgb(var(--og-border-subtle));
18
25
  }
19
26
 
20
27
  /* Brand row — phải thẳng hàng với icon của nav item.
21
- Icon nav nằm ở 24px tính từ mép drawer: 8px của .drawer-container (px-2)
22
- + 16px padding-left inline của .drawer-item. Brand nằm NGOÀI container
23
- nên phải tự cộng đủ 24px (trước đây p-4 + ml-5 = 36px → lệch 12px). */
28
+ Icon nav nằm ở 16px tính từ mép drawer (0.7.107, theo reference: 8px của
29
+ .drawer-container (px-2) + 8px padding-left inline của .drawer-item
30
+ trước đó 16px, tức 24px tổng). Brand nằm NGOÀI container nên phải tự
31
+ cộng đủ 16px. */
24
32
  .drawer-brand {
25
33
  @apply h-[60px] shrink-0 flex items-center gap-3 no-underline overflow-hidden;
26
- padding-left: 24px;
34
+ padding-left: 16px;
27
35
  padding-right: 8px;
28
36
  transition: padding 300ms cubic-bezier(0.4, 0, 0.2, 1);
29
37
  /* max-width là BẮT BUỘC: .drawer-host-wrapper là flex column có width auto
@@ -138,14 +146,25 @@
138
146
  }
139
147
 
140
148
  .drawer-item {
141
- @apply flex items-center mt-1 w-full min-h-8 py-2 px-large border-none bg-transparent cursor-pointer no-underline text-text-primary relative rounded;
142
- transition: all 200ms ease;
143
-
144
- /* Hover = glass mờ hơn: chỉ translucent, chưa viền/shadow, để không
145
- nhầm với hàng đang chọn. */
149
+ /* Row geometry to the reference (0.7.107): 6px 8px padding, 30px min
150
+ height, 12.5px, 7px control radius, 1px between rows, text-body at rest.
151
+ The container's px-2 supplies the 8px inset from the drawer's edge, so
152
+ the row itself carries no horizontal margin the reference's rows sit
153
+ 8px in, not 16. */
154
+ @apply flex items-center w-full border-none bg-transparent cursor-pointer no-underline relative;
155
+ margin-top: 1px;
156
+ min-height: 30px;
157
+ padding: 6px 8px;
158
+ font-size: var(--og-fs-sm);
159
+ color: rgb(var(--og-text-body));
160
+ border-radius: var(--og-radius-control);
161
+ transition: background-color 130ms ease, color 130ms ease;
162
+
163
+ /* Hover: an 8% tint of the text colour — the reference does not move or
164
+ scale the row. */
146
165
  &:hover {
147
- @apply text-text-primary scale-95;
148
- background: rgb(var(--og-surface-raised) / 0.5);
166
+ color: rgb(var(--og-text));
167
+ background: color-mix(in srgb, rgb(var(--og-text)) 8%, transparent);
149
168
  }
150
169
 
151
170
  /* Hàng đang chọn = GLASS, KHÔNG dùng màu primary (anh Tú chốt 31/08):
@@ -158,25 +177,24 @@
158
177
  khai border-none, thêm border thật sẽ đẩy layout 1px.
159
178
  - backdrop-blur chỉ có tác dụng khi nền sau có hoạ tiết/gradient; nền
160
179
  phẳng thì hiệu ứng đến từ độ trong + viền + shadow. */
180
+ /* Active (0.7.107, reference `.og-sidebar__item.is-active`): a 6% tint of
181
+ the text colour with a 7% inset hairline, weight 500. Replaces the glass
182
+ tile (surface-raised/0.8 + blur + shadow) of 31/08 — the reference's
183
+ selected row is a flat tint, and the tint is token-driven so charcoal
184
+ gets a light-on-dark ring where porcelain gets dark-on-light.
185
+ Viền vẽ bằng inset box-shadow chứ không phải `border`: .drawer-item
186
+ khai border-none, thêm border thật sẽ đẩy layout 1px. */
161
187
  &.active {
162
- @apply text-text-primary font-medium;
163
- background: rgb(var(--og-surface-raised) / 0.8);
164
- backdrop-filter: blur(12px);
165
- /* shadow viết literal chứ không dùng var(--shadow-sm): biến đó do @theme
166
- của Tailwind sinh, không chắc có mặt trong scope của component SCSS. */
167
- box-shadow:
168
- inset 0 0 0 1px rgb(var(--og-border)),
169
- 0 1px 2px 0 rgb(var(--shadow-color) / 0.05);
188
+ color: rgb(var(--og-text));
189
+ font-weight: 500;
190
+ background: color-mix(in srgb, rgb(var(--og-text)) 6%, transparent);
191
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, rgb(var(--og-text)) 7%, transparent);
170
192
 
171
193
  &:hover {
172
- background: rgb(var(--og-surface-raised) / 0.95);
194
+ background: color-mix(in srgb, rgb(var(--og-text)) 9%, transparent);
173
195
  }
174
196
  }
175
197
 
176
- &:active {
177
- transform: scale(0.98);
178
- }
179
-
180
198
  &:focus-visible {
181
199
  @apply outline-2 outline-primary outline-offset-2;
182
200
  }
@@ -195,7 +213,9 @@
195
213
  }
196
214
 
197
215
  .item-label {
198
- @apply flex-1 whitespace-nowrap overflow-hidden text-ellipsis text-[13px] leading-5 text-left;
216
+ /* Size comes from .drawer-item (12.5px, 0.7.107) — no second scale here. */
217
+ @apply flex-1 whitespace-nowrap overflow-hidden text-ellipsis leading-5 text-left;
218
+ font-size: inherit;
199
219
  }
200
220
 
201
221
  .chevron-icon {
@@ -26,6 +26,14 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
26
26
  * rather than an annotation.
27
27
  */
28
28
  readonly pieCenterValueSize: _angular_core.InputSignal<number>;
29
+ /**
30
+ * The donut's [inner, outer] radius — ECharts' own vocabulary, so either a
31
+ * percentage of the shorter side or a pixel count. The default is what the
32
+ * ring has always been. The reference dashboard draws a 140px ring with a
33
+ * 22px stroke, i.e. `[48, 70]` at `height="140px"` — the panel that wants it
34
+ * says so; the other pies keep their proportions.
35
+ */
36
+ readonly pieRadius: _angular_core.InputSignal<[string | number, string | number]>;
29
37
  readonly mobileWidth: _angular_core.InputSignal<number>;
30
38
  /** Override font for canvas text; empty = follow the theme's --og-font-figure. */
31
39
  readonly labelFontFamily: _angular_core.InputSignal<string>;
@@ -89,6 +97,8 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
89
97
  */
90
98
  private appearUntil;
91
99
  private static readonly APPEAR_MS;
100
+ /** Quiet time a host size has to hold before a resize is acted on. */
101
+ private static readonly RESIZE_SETTLE_MS;
92
102
  private deferTimer?;
93
103
  /**
94
104
  * Thay đổi kích thước ghi nhận TRONG cửa sổ appear, chờ áp sau. `measure()`
@@ -135,7 +145,13 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
135
145
  private measure;
136
146
  /** Animation xuất hiện vừa bắt đầu — khoá resize tới khi nó xong. */
137
147
  private markAppear;
138
- /** Pie/sunburst đặt legend + số giữa theo px của containerWidth → phải dựng lại. */
148
+ /**
149
+ * Sunburst, and a pie WITH its built-in legend, place things in px of
150
+ * containerWidth → must rebuild on resize. A legend-less pie centres its
151
+ * ring at 50%/50% and its total in a `left: 'center' / top: 'middle'`
152
+ * graphic group, so `resize()` alone re-centres it — no rebuild, no
153
+ * second appear animation (15/09, donut on sidebar toggle).
154
+ */
139
155
  private needsLayoutRebuild;
140
156
  private deferResize;
141
157
  /**
@@ -153,6 +169,10 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
153
169
  * trọng: resize SAU rebuild sẽ cắt ngay animation vừa bắt đầu.
154
170
  */
155
171
  private forceMediaRecalc;
172
+ /**
173
+ * @param silent redraw for a new size only — same data, no appear animation,
174
+ * and the appear window is not re-armed.
175
+ */
156
176
  private rebuildChart;
157
177
  private checkEmpty;
158
178
  private registry;
@@ -195,7 +215,7 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
195
215
  private resolvedFontFamily;
196
216
  private resolvePalette;
197
217
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ChartComponent, never>;
198
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ChartComponent, "fx-ui-chart", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "palette": { "alias": "palette"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "pieLegendAlign": { "alias": "pieLegendAlign"; "required": false; "isSignal": true; }; "pieLegendPosition": { "alias": "pieLegendPosition"; "required": false; "isSignal": true; }; "showPieLabel": { "alias": "showPieLabel"; "required": false; "isSignal": true; }; "showPieLegend": { "alias": "showPieLegend"; "required": false; "isSignal": true; }; "pieCenterLabel": { "alias": "pieCenterLabel"; "required": false; "isSignal": true; }; "pieCenterValueSize": { "alias": "pieCenterValueSize"; "required": false; "isSignal": true; }; "mobileWidth": { "alias": "mobileWidth"; "required": false; "isSignal": true; }; "labelFontFamily": { "alias": "labelFontFamily"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "showPointLabels": { "alias": "showPointLabels"; "required": false; "isSignal": true; }; }, { "chartClick": "chartClick"; "chartInit": "chartInit"; }, never, never, true, never>;
218
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ChartComponent, "fx-ui-chart", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "palette": { "alias": "palette"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "pieLegendAlign": { "alias": "pieLegendAlign"; "required": false; "isSignal": true; }; "pieLegendPosition": { "alias": "pieLegendPosition"; "required": false; "isSignal": true; }; "showPieLabel": { "alias": "showPieLabel"; "required": false; "isSignal": true; }; "showPieLegend": { "alias": "showPieLegend"; "required": false; "isSignal": true; }; "pieCenterLabel": { "alias": "pieCenterLabel"; "required": false; "isSignal": true; }; "pieCenterValueSize": { "alias": "pieCenterValueSize"; "required": false; "isSignal": true; }; "pieRadius": { "alias": "pieRadius"; "required": false; "isSignal": true; }; "mobileWidth": { "alias": "mobileWidth"; "required": false; "isSignal": true; }; "labelFontFamily": { "alias": "labelFontFamily"; "required": false; "isSignal": true; }; "emptyText": { "alias": "emptyText"; "required": false; "isSignal": true; }; "showPointLabels": { "alias": "showPointLabels"; "required": false; "isSignal": true; }; }, { "chartClick": "chartClick"; "chartInit": "chartInit"; }, never, never, true, never>;
199
219
  }
200
220
 
201
221
  /**