@foxeltech/angular-ui 0.7.44 → 0.7.75

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.
Files changed (49) hide show
  1. package/components.css +320 -18
  2. package/familjen-grotesk/familjen-grotesk-400-latin-ext.woff2 +0 -0
  3. package/familjen-grotesk/familjen-grotesk-400-latin.woff2 +0 -0
  4. package/familjen-grotesk/familjen-grotesk-400-vietnamese.woff2 +0 -0
  5. package/familjen-grotesk/familjen-grotesk-500-latin-ext.woff2 +0 -0
  6. package/familjen-grotesk/familjen-grotesk-500-latin.woff2 +0 -0
  7. package/familjen-grotesk/familjen-grotesk-500-vietnamese.woff2 +0 -0
  8. package/familjen-grotesk/familjen-grotesk-600-latin-ext.woff2 +0 -0
  9. package/familjen-grotesk/familjen-grotesk-600-latin.woff2 +0 -0
  10. package/familjen-grotesk/familjen-grotesk-600-vietnamese.woff2 +0 -0
  11. package/familjen-grotesk/familjen-grotesk-700-latin-ext.woff2 +0 -0
  12. package/familjen-grotesk/familjen-grotesk-700-latin.woff2 +0 -0
  13. package/familjen-grotesk/familjen-grotesk-700-vietnamese.woff2 +0 -0
  14. package/fesm2022/foxeltech-angular-ui-chart.mjs +197 -24
  15. package/fesm2022/foxeltech-angular-ui-chart.mjs.map +1 -1
  16. package/fesm2022/foxeltech-angular-ui.mjs +504 -108
  17. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  18. package/fonts.css +104 -3
  19. package/package.json +1 -1
  20. package/src/lib/styles/components.css +320 -18
  21. package/src/lib/styles/fonts.css +104 -3
  22. package/src/lib/styles/tailwind.css +12 -0
  23. package/src/lib/styles/theme.css +160 -114
  24. package/src/lib/styles/tokens.css +25 -0
  25. package/src/lib/styles/utilities.css +12 -0
  26. package/src/lib/ui/components/button/button.component.html +2 -0
  27. package/src/lib/ui/components/drawer/drawer.component.scss +3 -1
  28. package/src/lib/ui/components/facets-rail/facets-rail.component.html +1 -6
  29. package/src/lib/ui/components/master-detail/master-detail.component.css +65 -13
  30. package/src/lib/ui/components/master-detail/master-detail.component.html +26 -8
  31. package/src/lib/ui/components/route-skeleton/route-skeleton.component.css +38 -0
  32. package/src/lib/ui/components/route-skeleton/route-skeleton.component.html +184 -0
  33. package/src/lib/ui/components/section-card/section-card.component.html +1 -5
  34. package/src/lib/ui/components/skeleton-dashboard/skeleton-dashboard.component.html +24 -0
  35. package/src/lib/ui/components/skeleton-detail/skeleton-detail.component.html +8 -8
  36. package/src/lib/ui/components/skeleton-facets/skeleton-facets.component.html +15 -0
  37. package/src/lib/ui/components/skeleton-frame/skeleton-frame.component.html +59 -0
  38. package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +6 -6
  39. package/src/lib/ui/components/switch/switch.component.html +1 -1
  40. package/src/lib/ui/components/toast/toast.component.html +6 -6
  41. package/tailwind.css +12 -0
  42. package/theme.css +160 -114
  43. package/tokens.css +25 -0
  44. package/types/foxeltech-angular-ui-chart.d.ts +76 -1
  45. package/types/foxeltech-angular-ui.d.ts +278 -33
  46. package/utilities.css +12 -0
  47. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.html +0 -21
  48. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.scss +0 -0
  49. package/src/lib/ui/components/tab-component/tab.component.html +0 -1
package/theme.css CHANGED
@@ -26,12 +26,12 @@
26
26
  --og-fs-4xl: 32px;
27
27
  --og-space-1: 4px;
28
28
  --og-space-2: 8px;
29
- --og-space-3: 12px;
30
- --og-space-4: 16px;
31
- --og-space-5: 20px;
32
- --og-space-6: 26px;
33
- --og-space-7: 32px;
34
- --og-space-8: 40px;
29
+ --og-space-3: 8px;
30
+ --og-space-4: 10px;
31
+ --og-space-5: 12px;
32
+ --og-space-6: 13px;
33
+ --og-space-7: 15px;
34
+ --og-space-8: 18px;
35
35
  --og-track-h: 5px;
36
36
  --og-rule: 1px;
37
37
  --og-tracking-label: 0.08em;
@@ -48,11 +48,11 @@
48
48
  --og-text-body: 60 63 68; /* #3c3f44 */
49
49
  --og-text-muted: 107 112 117; /* #6b7075 */
50
50
  --og-text-faint: 155 160 166; /* #9ba0a6 */
51
- --og-accent: 124 58 237; /* #7c3aed — violet V1 */
52
- --og-accent-fg: 124 58 237; /* #7c3aed */
51
+ --og-accent: 74 125 189; /* #4a7dbd */
52
+ --og-accent-fg: 64 105 156; /* #40699c */
53
53
  --og-accent-contrast: 255 255 255; /* #ffffff */
54
- --og-accent-soft-bg: 242 235 253; /* #f2ebfd */
55
- --og-accent-soft-fg: 102 48 194; /* #6630c2 */
54
+ --og-accent-soft-bg: 231 238 247; /* #e7eef7 */
55
+ --og-accent-soft-fg: 53 97 156; /* #35619c */
56
56
  --og-positive: 63 125 85; /* #3f7d55 */
57
57
  --og-positive-soft-bg: 230 241 234; /* #e6f1ea */
58
58
  --og-negative: 192 69 47; /* #c0452f */
@@ -66,15 +66,15 @@
66
66
  --og-radius-card: 9px;
67
67
  --og-radius-control: 7px;
68
68
  --og-radius-pill: 999px;
69
- --og-font-body: "Archivo", system-ui, sans-serif;
70
- --og-font-display: "Archivo", system-ui, sans-serif;
71
- --og-font-mono: "JetBrains Mono", ui-monospace, monospace;
72
- --og-font-prose: "Source Serif 4", Georgia, serif;
73
- --og-font-figure: var(--og-font-body);
69
+ --og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
70
+ --og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
71
+ --og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
72
+ --og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
73
+ --og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
74
74
  /* derived (script-generated, KHÔNG có trong design gốc) */
75
75
  --og-derived-critical: 169 0 42; /* #a9002a */
76
76
  --og-derived-critical-soft-bg: 239 194 191; /* #efc2bf */
77
- --og-derived-gradient-accent-end: 79 142 247; /* #4f8ef7 — gradient end V1 */
77
+ --og-derived-gradient-accent-end: 98 150 215; /* #6296d7 */
78
78
  --og-derived-gradient-positive-end: 87 149 108; /* #57956c */
79
79
  --shadow-color: 0 0 0;
80
80
  /* severity tones — bộ LEGACY (vivid), không lấy từ design V2 */
@@ -84,6 +84,11 @@
84
84
  --og-tone-danger: 255 122 0;
85
85
  --og-tone-information: 23 127 240;
86
86
  --og-tone-discovery: 156 129 255;
87
+ /* severity ramp cho CHART — literal đo trên demo, không phải token design */
88
+ --og-sev-critical: 127 29 29; /* #7f1d1d */
89
+ --og-sev-danger: 220 38 38; /* #dc2626 */
90
+ --og-sev-warning: 245 158 11; /* #f59e0b */
91
+ --og-sev-success: 22 163 74; /* #16a34a */
87
92
 
88
93
  /* ---- LEGACY ALIASES (@deprecated — gỡ khi kết thúc campaign V2) ----
89
94
  Các biến cũ trỏ vào --og-* qua var() nên chỉ cần khai báo MỘT lần:
@@ -135,11 +140,11 @@
135
140
  --og-text-body: 60 63 68; /* #3c3f44 */
136
141
  --og-text-muted: 107 112 117; /* #6b7075 */
137
142
  --og-text-faint: 155 160 166; /* #9ba0a6 */
138
- --og-accent: 124 58 237; /* #7c3aed — violet V1 */
139
- --og-accent-fg: 124 58 237; /* #7c3aed */
143
+ --og-accent: 74 125 189; /* #4a7dbd */
144
+ --og-accent-fg: 64 105 156; /* #40699c */
140
145
  --og-accent-contrast: 255 255 255; /* #ffffff */
141
- --og-accent-soft-bg: 242 235 253; /* #f2ebfd */
142
- --og-accent-soft-fg: 102 48 194; /* #6630c2 */
146
+ --og-accent-soft-bg: 231 238 247; /* #e7eef7 */
147
+ --og-accent-soft-fg: 53 97 156; /* #35619c */
143
148
  --og-positive: 63 125 85; /* #3f7d55 */
144
149
  --og-positive-soft-bg: 230 241 234; /* #e6f1ea */
145
150
  --og-negative: 192 69 47; /* #c0452f */
@@ -153,15 +158,15 @@
153
158
  --og-radius-card: 9px;
154
159
  --og-radius-control: 7px;
155
160
  --og-radius-pill: 999px;
156
- --og-font-body: "Archivo", system-ui, sans-serif;
157
- --og-font-display: "Archivo", system-ui, sans-serif;
158
- --og-font-mono: "JetBrains Mono", ui-monospace, monospace;
159
- --og-font-prose: "Source Serif 4", Georgia, serif;
160
- --og-font-figure: var(--og-font-body);
161
+ --og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
162
+ --og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
163
+ --og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
164
+ --og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
165
+ --og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
161
166
  /* derived (script-generated, KHÔNG có trong design gốc) */
162
167
  --og-derived-critical: 169 0 42; /* #a9002a */
163
168
  --og-derived-critical-soft-bg: 239 194 191; /* #efc2bf */
164
- --og-derived-gradient-accent-end: 79 142 247; /* #4f8ef7 — gradient end V1 */
169
+ --og-derived-gradient-accent-end: 98 150 215; /* #6296d7 */
165
170
  --og-derived-gradient-positive-end: 87 149 108; /* #57956c */
166
171
  --shadow-color: 0 0 0;
167
172
  /* severity tones — bộ LEGACY (vivid), không lấy từ design V2 */
@@ -171,48 +176,56 @@
171
176
  --og-tone-danger: 255 122 0;
172
177
  --og-tone-information: 23 127 240;
173
178
  --og-tone-discovery: 156 129 255;
179
+ /* severity ramp cho CHART — literal đo trên demo, không phải token design */
180
+ --og-sev-critical: 127 29 29; /* #7f1d1d */
181
+ --og-sev-danger: 220 38 38; /* #dc2626 */
182
+ --og-sev-warning: 245 158 11; /* #f59e0b */
183
+ --og-sev-success: 22 163 74; /* #16a34a */
174
184
  }
175
185
 
176
186
  :root[data-og-theme='charcoal'] {
177
- --og-page-bg: 14 14 16; /* #0e0e10 */
178
- --og-surface: 26 23 23; /* #1a1717 */
179
- --og-surface-raised: 33 28 27; /* #211c1b */
180
- --og-surface-sunken: 40 34 33; /* #282221 */
181
- --og-border: 48 41 40; /* #302928 */
182
- --og-border-subtle: 36 31 30; /* #241f1e */
183
- --og-border-strong: 74 63 61; /* #4a3f3d */
184
- --og-text: 240 239 236; /* #f0efec */
185
- --og-text-body: 214 212 207; /* #d6d4cf */
186
- --og-text-muted: 139 138 132; /* #8b8a84 */
187
- --og-text-faint: 110 109 104; /* #6e6d68 */
188
- --og-accent: 255 107 74; /* #ff6b4a */
189
- --og-accent-fg: 255 107 74; /* #ff6b4a */
190
- --og-accent-contrast: 46 21 13; /* #2e150d */
191
- --og-accent-soft-bg: 46 29 24; /* #2e1d18 */
192
- --og-accent-soft-fg: 255 143 117; /* #ff8f75 */
193
- --og-positive: 95 191 122; /* #5fbf7a */
194
- --og-positive-soft-bg: 29 47 40; /* #1d2f28 */
195
- --og-negative: 224 118 106; /* #e0766a */
196
- --og-negative-soft-bg: 51 35 35; /* #332323 */
197
- --og-warning: 217 146 78; /* #d9924e */
198
- --og-warning-soft-bg: 51 40 15; /* #33280f */
199
- --og-neutral-soft-bg: 40 34 33; /* #282221 */
200
- --og-neutral-soft-fg: 179 176 169; /* #b3b0a9 */
201
- --og-track: 46 39 38; /* #2e2726 */
187
+ --og-page-bg: 26 24 17; /* #1a1811 */
188
+ --og-surface: 34 31 22; /* #221f16 */
189
+ --og-surface-raised: 42 38 25; /* #2a2619 */
190
+ --og-surface-sunken: 23 21 9; /* #171509 */
191
+ --og-border: 58 54 38; /* #3a3626 */
192
+ --og-border-subtle: 44 41 25; /* #2c2919 */
193
+ --og-border-strong: 76 71 50; /* #4c4732 */
194
+ --og-text: 226 221 203; /* #e2ddcb */
195
+ --og-text-body: 193 188 166; /* #c1bca6 */
196
+ --og-text-muted: 147 141 118; /* #938d76 */
197
+ --og-text-faint: 107 102 83; /* #6b6653 */
198
+ --og-accent: 143 200 102; /* #8fc866 */
199
+ --og-accent-fg: 164 217 126; /* #a4d97e */
200
+ --og-accent-contrast: 23 21 9; /* #171509 */
201
+ --og-accent-soft-bg: 38 48 26; /* #26301a */
202
+ --og-accent-soft-fg: 164 217 126; /* #a4d97e */
203
+ --og-positive: 132 200 98; /* #84c862 */
204
+ --og-positive-soft-bg: 34 48 15; /* #22300f */
205
+ --og-negative: 229 130 142; /* #e5828e */
206
+ --og-negative-soft-bg: 63 30 36; /* #3f1e24 */
207
+ --og-warning: 214 178 90; /* #d6b25a */
208
+ --og-warning-soft-bg: 51 42 18; /* #332a12 */
209
+ --og-neutral-soft-bg: 42 39 25; /* #2a2719 */
210
+ --og-neutral-soft-fg: 164 158 134; /* #a49e86 */
211
+ --og-track: 48 44 30; /* #302c1e */
202
212
  --og-radius-frame: 10px;
203
213
  --og-radius-card: 9px;
204
214
  --og-radius-control: 7px;
205
215
  --og-radius-pill: 999px;
206
- --og-font-body: "Archivo", system-ui, sans-serif;
207
- --og-font-display: "Archivo", system-ui, sans-serif;
208
- --og-font-mono: "IBM Plex Mono", ui-monospace, monospace;
209
- --og-font-prose: "Source Serif 4", Georgia, serif;
210
- --og-font-figure: var(--og-font-body);
216
+ --og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
217
+ --og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
218
+ --og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
219
+ --og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
220
+ --og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
221
+ --og-positive-soft-fg: 164 217 126; /* #a4d97e */
222
+ --og-negative-soft-fg: 238 163 171; /* #eea3ab */
223
+ --og-warning-soft-fg: 224 194 116; /* #e0c274 */
211
224
  /* derived (script-generated, KHÔNG có trong design gốc) */
212
- --og-derived-critical: 246 107 130; /* #f66b82 */
213
- --og-derived-critical-soft-bg: 86 49 52; /* #563134 */
214
- --og-derived-gradient-accent-end: 255 140 106; /* #ff8c6a */
215
- --og-derived-gradient-positive-end: 121 217 147; /* #79d993 */
225
+ --og-derived-critical: 254 119 154; /* #fe779a */
226
+ --og-derived-critical-soft-bg: 97 46 57; /* #612e39 */
227
+ --og-derived-gradient-accent-end: 174 233 134; /* #aee986 */
228
+ --og-derived-gradient-positive-end: 157 226 123; /* #9de27b */
216
229
  --shadow-color: 255 255 255;
217
230
  /* severity tones — bộ LEGACY (vivid), không lấy từ design V2 */
218
231
  --og-tone-success: 76 175 80;
@@ -221,6 +234,11 @@
221
234
  --og-tone-danger: 249 133 7;
222
235
  --og-tone-information: 62 175 240;
223
236
  --og-tone-discovery: 119 113 227;
237
+ /* severity ramp cho CHART — literal đo trên demo, không phải token design */
238
+ --og-sev-critical: 207 103 96; /* #cf6760 — #7f1d1d nâng L cho nền tối */
239
+ --og-sev-danger: 246 68 61; /* #f6443d */
240
+ --og-sev-warning: 245 158 11; /* #f59e0b */
241
+ --og-sev-success: 22 163 74; /* #16a34a */
224
242
  }
225
243
 
226
244
  :root[data-og-theme='ivory'] {
@@ -253,11 +271,11 @@
253
271
  --og-radius-card: 14px;
254
272
  --og-radius-control: 999px;
255
273
  --og-radius-pill: 999px;
256
- --og-font-body: "Archivo", system-ui, sans-serif;
257
- --og-font-display: "Newsreader", Georgia, serif;
258
- --og-font-mono: "JetBrains Mono", ui-monospace, monospace;
259
- --og-font-prose: "Source Serif 4", Georgia, serif;
260
- --og-font-figure: var(--og-font-display);
274
+ --og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
275
+ --og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
276
+ --og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
277
+ --og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
278
+ --og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
261
279
  /* derived (script-generated, KHÔNG có trong design gốc) */
262
280
  --og-derived-critical: 154 11 57; /* #9a0b39 */
263
281
  --og-derived-critical-soft-bg: 232 192 193; /* #e8c0c1 */
@@ -271,6 +289,11 @@
271
289
  --og-tone-danger: 255 122 0;
272
290
  --og-tone-information: 23 127 240;
273
291
  --og-tone-discovery: 156 129 255;
292
+ /* severity ramp cho CHART — literal đo trên demo, không phải token design */
293
+ --og-sev-critical: 127 29 29; /* #7f1d1d */
294
+ --og-sev-danger: 220 38 38; /* #dc2626 */
295
+ --og-sev-warning: 245 158 11; /* #f59e0b */
296
+ --og-sev-success: 22 163 74; /* #16a34a */
274
297
  }
275
298
 
276
299
  :root[data-og-theme='studio'] {
@@ -303,11 +326,11 @@
303
326
  --og-radius-card: 11px;
304
327
  --og-radius-control: 9px;
305
328
  --og-radius-pill: 999px;
306
- --og-font-body: "Archivo", system-ui, sans-serif;
307
- --og-font-display: "Archivo", system-ui, sans-serif;
308
- --og-font-mono: "IBM Plex Mono", ui-monospace, monospace;
309
- --og-font-prose: "Source Serif 4", Georgia, serif;
310
- --og-font-figure: var(--og-font-body);
329
+ --og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
330
+ --og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
331
+ --og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
332
+ --og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
333
+ --og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
311
334
  /* derived (script-generated, KHÔNG có trong design gốc) */
312
335
  --og-derived-critical: 238 108 128; /* #ee6c80 */
313
336
  --og-derived-critical-soft-bg: 85 48 49; /* #553031 */
@@ -321,6 +344,11 @@
321
344
  --og-tone-danger: 249 133 7;
322
345
  --og-tone-information: 62 175 240;
323
346
  --og-tone-discovery: 119 113 227;
347
+ /* severity ramp cho CHART — literal đo trên demo, không phải token design */
348
+ --og-sev-critical: 207 103 96; /* #cf6760 — #7f1d1d nâng L cho nền tối */
349
+ --og-sev-danger: 246 68 61; /* #f6443d */
350
+ --og-sev-warning: 245 158 11; /* #f59e0b */
351
+ --og-sev-success: 22 163 74; /* #16a34a */
324
352
  }
325
353
 
326
354
  :root[data-og-theme='blueprint'] {
@@ -353,11 +381,11 @@
353
381
  --og-radius-card: 0px;
354
382
  --og-radius-control: 0px;
355
383
  --og-radius-pill: 0px;
356
- --og-font-body: "IBM Plex Mono", ui-monospace, monospace;
357
- --og-font-display: "Newsreader", Georgia, serif;
358
- --og-font-mono: "IBM Plex Mono", ui-monospace, monospace;
359
- --og-font-prose: "Source Serif 4", Georgia, serif;
360
- --og-font-figure: var(--og-font-mono);
384
+ --og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
385
+ --og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
386
+ --og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
387
+ --og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
388
+ --og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
361
389
  /* derived (script-generated, KHÔNG có trong design gốc) */
362
390
  --og-derived-critical: 216 93 120; /* #d85d78 */
363
391
  --og-derived-critical-soft-bg: 73 39 39; /* #492727 */
@@ -371,6 +399,11 @@
371
399
  --og-tone-danger: 249 133 7;
372
400
  --og-tone-information: 62 175 240;
373
401
  --og-tone-discovery: 119 113 227;
402
+ /* severity ramp cho CHART — literal đo trên demo, không phải token design */
403
+ --og-sev-critical: 207 103 96; /* #cf6760 — #7f1d1d nâng L cho nền tối */
404
+ --og-sev-danger: 246 68 61; /* #f6443d */
405
+ --og-sev-warning: 245 158 11; /* #f59e0b */
406
+ --og-sev-success: 22 163 74; /* #16a34a */
374
407
  }
375
408
 
376
409
  :root[data-og-theme='terminal'] {
@@ -403,11 +436,11 @@
403
436
  --og-radius-card: 0px;
404
437
  --og-radius-control: 0px;
405
438
  --og-radius-pill: 0px;
406
- --og-font-body: "JetBrains Mono", ui-monospace, monospace;
407
- --og-font-display: "JetBrains Mono", ui-monospace, monospace;
408
- --og-font-mono: "JetBrains Mono", ui-monospace, monospace;
409
- --og-font-prose: "Source Serif 4", Georgia, serif;
410
- --og-font-figure: var(--og-font-mono);
439
+ --og-font-body: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
440
+ --og-font-display: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
441
+ --og-font-mono: 'JetBrains Mono',ui-monospace,monospace;
442
+ --og-font-prose: 'Familjen Grotesk',ui-sans-serif,system-ui,sans-serif;
443
+ --og-font-figure: 'JetBrains Mono',ui-monospace,monospace;
411
444
  /* derived (script-generated, KHÔNG có trong design gốc) */
412
445
  --og-derived-critical: 169 0 42; /* #a9002a */
413
446
  --og-derived-critical-soft-bg: 236 191 187; /* #ecbfbb */
@@ -421,6 +454,11 @@
421
454
  --og-tone-danger: 255 122 0;
422
455
  --og-tone-information: 23 127 240;
423
456
  --og-tone-discovery: 156 129 255;
457
+ /* severity ramp cho CHART — literal đo trên demo, không phải token design */
458
+ --og-sev-critical: 127 29 29; /* #7f1d1d */
459
+ --og-sev-danger: 220 38 38; /* #dc2626 */
460
+ --og-sev-warning: 245 158 11; /* #f59e0b */
461
+ --og-sev-success: 22 163 74; /* #16a34a */
424
462
  }
425
463
 
426
464
  /* ---- SKEW SHIELD (transitional) -------------------------------------
@@ -474,45 +512,48 @@
474
512
  standalone không set theme cũng rơi vào nhánh charcoal (khớp hành vi
475
513
  dark-default cũ). */
476
514
  html:not([data-og-theme]) body:not(.light_theme) {
477
- --og-page-bg: 14 14 16; /* #0e0e10 */
478
- --og-surface: 26 23 23; /* #1a1717 */
479
- --og-surface-raised: 33 28 27; /* #211c1b */
480
- --og-surface-sunken: 40 34 33; /* #282221 */
481
- --og-border: 48 41 40; /* #302928 */
482
- --og-border-subtle: 36 31 30; /* #241f1e */
483
- --og-border-strong: 74 63 61; /* #4a3f3d */
484
- --og-text: 240 239 236; /* #f0efec */
485
- --og-text-body: 214 212 207; /* #d6d4cf */
486
- --og-text-muted: 139 138 132; /* #8b8a84 */
487
- --og-text-faint: 110 109 104; /* #6e6d68 */
488
- --og-accent: 255 107 74; /* #ff6b4a */
489
- --og-accent-fg: 255 107 74; /* #ff6b4a */
490
- --og-accent-contrast: 46 21 13; /* #2e150d */
491
- --og-accent-soft-bg: 46 29 24; /* #2e1d18 */
492
- --og-accent-soft-fg: 255 143 117; /* #ff8f75 */
493
- --og-positive: 95 191 122; /* #5fbf7a */
494
- --og-positive-soft-bg: 29 47 40; /* #1d2f28 */
495
- --og-negative: 224 118 106; /* #e0766a */
496
- --og-negative-soft-bg: 51 35 35; /* #332323 */
497
- --og-warning: 217 146 78; /* #d9924e */
498
- --og-warning-soft-bg: 51 40 15; /* #33280f */
499
- --og-neutral-soft-bg: 40 34 33; /* #282221 */
500
- --og-neutral-soft-fg: 179 176 169; /* #b3b0a9 */
501
- --og-track: 46 39 38; /* #2e2726 */
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 */
502
540
  --og-radius-frame: 10px;
503
541
  --og-radius-card: 9px;
504
542
  --og-radius-control: 7px;
505
543
  --og-radius-pill: 999px;
506
- --og-font-body: "Archivo", system-ui, sans-serif;
507
- --og-font-display: "Archivo", system-ui, sans-serif;
508
- --og-font-mono: "IBM Plex Mono", ui-monospace, monospace;
509
- --og-font-prose: "Source Serif 4", Georgia, serif;
510
- --og-font-figure: var(--og-font-body);
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 */
511
552
  /* derived (script-generated, KHÔNG có trong design gốc) */
512
- --og-derived-critical: 246 107 130; /* #f66b82 */
513
- --og-derived-critical-soft-bg: 86 49 52; /* #563134 */
514
- --og-derived-gradient-accent-end: 255 140 106; /* #ff8c6a */
515
- --og-derived-gradient-positive-end: 121 217 147; /* #79d993 */
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 */
516
557
  --shadow-color: 255 255 255;
517
558
  /* severity tones — bộ LEGACY (vivid), không lấy từ design V2 */
518
559
  --og-tone-success: 76 175 80;
@@ -521,6 +562,11 @@ html:not([data-og-theme]) body:not(.light_theme) {
521
562
  --og-tone-danger: 249 133 7;
522
563
  --og-tone-information: 62 175 240;
523
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 */
524
570
 
525
571
  /* ---- LEGACY ALIASES (@deprecated — gỡ khi kết thúc campaign V2) ----
526
572
  Các biến cũ trỏ vào --og-* qua var() nên chỉ cần khai báo MỘT lần:
package/tokens.css CHANGED
@@ -37,6 +37,23 @@
37
37
  --color-neutral-soft-fg: rgb(var(--og-neutral-soft-fg));
38
38
  --color-track: rgb(var(--og-track));
39
39
 
40
+ /* ---- CTA (New Analysis / New Scan) ----
41
+ Demo dùng một màu CỐ ĐỊNH cho nút hành động chính (`.bi-newbtn`), không
42
+ đổi theo theme và KHÔNG phải accent — nên để literal ở đây thay vì thành
43
+ token theme. Nguồn: lớp override của design, #c2582a / hover #b34e24. */
44
+ /* ---- Severity ramp CHO CHART ----
45
+ Màu literal đo trên demo (Critical #7f1d1d · High #dc2626 · Medium
46
+ #f59e0b · Low #16a34a; bộ dark nâng L cho đủ tương phản). TÁCH khỏi
47
+ `--color-critical/danger/warning/success` — mấy tên đó trỏ vào bộ LEGACY
48
+ `--og-tone-*` dùng cho text/nền, không phải màu chart của design. */
49
+ --color-sev-critical: rgb(var(--og-sev-critical));
50
+ --color-sev-danger: rgb(var(--og-sev-danger));
51
+ --color-sev-warning: rgb(var(--og-sev-warning));
52
+ --color-sev-success: rgb(var(--og-sev-success));
53
+
54
+ --color-cta: #c2582a;
55
+ --color-cta-hover: #b34e24;
56
+
40
57
  /* ---- Legacy colour names (@deprecated — remove after the V2 campaign;
41
58
  they resolve through the alias layer in theme.css, so every existing
42
59
  class already renders the OG palette) ---- */
@@ -73,6 +90,14 @@
73
90
  --color-button-primary: rgb(var(--button-primary));
74
91
 
75
92
  /* Breakpoints (v3 custom screens) */
93
+ /* ⚠️ THANG NÀY KHÔNG PHẢI MẶC ĐỊNH CỦA TAILWIND (640/768/1024/1280).
94
+ `lg` ở đây là **1080px**, không phải 1024px — đã gây lỗi "màn lg mà render
95
+ layout sm" (03/09): mọi viewport 1024-1079 (rất dễ gặp khi OS scaling
96
+ 125-150% hoặc browser zoom) rơi xuống layout nhỏ. Anh Tú chốt GIỮ thang
97
+ này; đổi nó sẽ dịch ngưỡng của mọi `lg:`/`xl:` trong 5 app.
98
+ 👉 Layout nào cần biết "có đủ chỗ thật hay không" thì dùng CONTAINER QUERY
99
+ theo bề rộng của chính nó (xem master-detail.component.css), đừng bám `lg:`.
100
+ `xl` là 1920px nên màn 1900px KHÔNG BAO GIỜ đạt `xl:`. */
76
101
  --breakpoint-sm: 480px;
77
102
  --breakpoint-md: 720px;
78
103
  --breakpoint-lg: 1080px;
@@ -28,21 +28,96 @@ declare class ChartComponent implements OnChanges, AfterViewInit, OnDestroy {
28
28
  readonly chartInit: _angular_core.OutputEmitterRef<ECharts>;
29
29
  chartOptions: EChartsOption;
30
30
  isEmpty: boolean;
31
+ /**
32
+ * ⚠️ Cổng cho `<div echarts>` trong template. Trước đây div render NGAY với
33
+ * `chartOptions = {}`, nên NgxEchartsDirective khởi tạo chart bằng options
34
+ * RỖNG; options thật gán vào giữa lúc `await createChart()` của lần đó ⇒
35
+ * onOptionsChange() thấy `chart == null` và createChart() LẦN HAI trên cùng
36
+ * DOM node ⇒ "[ECharts] There is a chart instance already initialized on the
37
+ * dom.". Chỉ dựng directive khi đã có options thật ⇒ đúng một lần init.
38
+ */
39
+ hasOptions: boolean;
31
40
  labelFontSize: number;
32
41
  private chartInstance?;
33
42
  private resizeObserver?;
34
43
  private intersectionObserver?;
35
44
  private canInit;
45
+ private destroyed;
46
+ /**
47
+ * ⚠️ Đang chờ init đầu tiên của directive hoàn tất?
48
+ *
49
+ * NgxEchartsDirective đăng ký `changeFilter.notFirstAndEmpty('options', …)`:
50
+ * lần gán `options` ĐẦU bị bỏ qua (ngAfterViewInit lo), lần gán THỨ HAI gọi
51
+ * onOptionsChange(). Mà onOptionsChange() chỉ createChart() khi `this.chart`
52
+ * còn null — và nó null suốt thời gian `await createChart()` của lần đầu.
53
+ * Nên nếu ta gán `chartOptions` (object mới) trong khoảng await đó thì
54
+ * init() chạy HAI LẦN trên cùng DOM node ⇒
55
+ * "[ECharts] There is a chart instance already initialized on the dom."
56
+ *
57
+ * Cách chặn: trong khoảng chờ, giữ options ở `deferredOptions` chứ không đụng
58
+ * vào property đang bind; init xong thì áp một lần qua instance.
59
+ */
60
+ private awaitingFirstInit;
61
+ private deferredOptions?;
62
+ private pendingFrames;
36
63
  private isMobile;
37
64
  private containerWidth;
38
65
  constructor(ref: ElementRef<HTMLElement>, cdr: ChangeDetectorRef);
39
66
  ngAfterViewInit(): void;
40
67
  ngOnChanges(): void;
41
68
  ngOnDestroy(): void;
69
+ /**
70
+ * ⚠️ RACE TRONG NGX-ECHARTS v20 — nguồn của
71
+ * "[ECharts] There is a chart instance already initialized on the dom."
72
+ *
73
+ * NgxEchartsDirective.onOptionsChange() làm:
74
+ * this.chart = await this.createChart(); // ← destroy có thể rơi vào đây
75
+ * this.chart$.next(this.chart);
76
+ * this.chartInit.emit(this.chart);
77
+ *
78
+ * Destroy đúng lúc đang await thì ngOnDestroy → dispose() thấy `this.chart == null`
79
+ * nên KHÔNG dispose gì. Sau đó await resolve, gán một instance ECharts còn sống cho
80
+ * directive đã chết → instance MỒ CÔI, vẫn nằm trong registry của echarts gắn với
81
+ * DOM node đó. Angular tái dùng node ⇒ init() lần sau báo "already initialized".
82
+ *
83
+ * Không thể bắt qua (chartInit): Angular v20 TỪ CHỐI emit trên OutputRef đã destroy
84
+ * (chỉ log NG0953, listener không hề được gọi). Nên đường duy nhất là hỏi thẳng
85
+ * echarts xem node còn instance nào không, ở các macrotask SAU destroy.
86
+ */
87
+ private sweepOrphanInstance;
88
+ /** rAF có thể huỷ được khi destroy — tránh chạm instance đã dispose. */
89
+ private schedule;
90
+ /** Instance còn dùng được? (đã dispose thì mọi API echarts đều ném lỗi) */
91
+ private get liveChart();
42
92
  onThemeChange(): void;
43
93
  onChartClick(event: any): void;
44
- onChartInit(chart: any): void;
94
+ onChartInit(raw: any): void;
95
+ /**
96
+ * @returns true nếu trạng thái responsive THỰC SỰ đổi (nên dựng lại options).
97
+ *
98
+ * Trả về boolean là điều then chốt cho animation lúc chart xuất hiện — xem
99
+ * ghi chú ở `forceMediaRecalc`.
100
+ */
45
101
  private updateResponsiveState;
102
+ /**
103
+ * ⚠️ CHỈ dựng lại options khi kích thước thật sự đổi — nếu không thì
104
+ * **animation lúc chart xuất hiện bị giết**.
105
+ *
106
+ * Đường đi: `onChartInit` → schedule(forceMediaRecalc) → chạy ở frame KẾ
107
+ * TIẾP. Nếu chỗ này vô điều kiện `rebuildChart()` (clear + setOption notMerge)
108
+ * thì nó xoá sạch và vẽ lại từ đầu ~16ms sau lần vẽ đầu, trong khi animation
109
+ * xuất hiện dài ~1000ms ⇒ người dùng thấy chart hiện ra đứng im.
110
+ *
111
+ * Đo được: lần xuất hiện đầu 1/12 frame khác nhau (đứng yên) trong khi vẽ lại
112
+ * bằng `theme-changed` cho 9/12 → animation của ECharts vẫn bật, chỉ lần đầu
113
+ * bị cắt.
114
+ *
115
+ * Trước Pha 0 lỗi này bị che: lần init đầu dùng `chartOptions = {}` nên
116
+ * animation xuất hiện xảy ra ở lần cập nhật SAU, tức sau khi forceMediaRecalc
117
+ * đã chạy xong. Cổng `hasOptions` đưa options thật về ngay lần init nên lộ ra.
118
+ *
119
+ * `resize()` thì luôn gọi được — nó không cắt animation.
120
+ */
46
121
  private forceMediaRecalc;
47
122
  private rebuildChart;
48
123
  private checkEmpty;