@foxeltech/angular-ui 0.4.0 → 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 (189) hide show
  1. package/archivo/archivo-400-latin-ext.woff2 +0 -0
  2. package/archivo/archivo-400-latin.woff2 +0 -0
  3. package/archivo/archivo-400-vietnamese.woff2 +0 -0
  4. package/archivo/archivo-500-latin-ext.woff2 +0 -0
  5. package/archivo/archivo-500-latin.woff2 +0 -0
  6. package/archivo/archivo-500-vietnamese.woff2 +0 -0
  7. package/archivo/archivo-600-latin-ext.woff2 +0 -0
  8. package/archivo/archivo-600-latin.woff2 +0 -0
  9. package/archivo/archivo-600-vietnamese.woff2 +0 -0
  10. package/archivo/archivo-700-latin-ext.woff2 +0 -0
  11. package/archivo/archivo-700-latin.woff2 +0 -0
  12. package/archivo/archivo-700-vietnamese.woff2 +0 -0
  13. package/components.css +332 -30
  14. package/dialogs.css +2 -2
  15. package/familjen-grotesk/familjen-grotesk-400-latin-ext.woff2 +0 -0
  16. package/familjen-grotesk/familjen-grotesk-400-latin.woff2 +0 -0
  17. package/familjen-grotesk/familjen-grotesk-400-vietnamese.woff2 +0 -0
  18. package/familjen-grotesk/familjen-grotesk-500-latin-ext.woff2 +0 -0
  19. package/familjen-grotesk/familjen-grotesk-500-latin.woff2 +0 -0
  20. package/familjen-grotesk/familjen-grotesk-500-vietnamese.woff2 +0 -0
  21. package/familjen-grotesk/familjen-grotesk-600-latin-ext.woff2 +0 -0
  22. package/familjen-grotesk/familjen-grotesk-600-latin.woff2 +0 -0
  23. package/familjen-grotesk/familjen-grotesk-600-vietnamese.woff2 +0 -0
  24. package/familjen-grotesk/familjen-grotesk-700-latin-ext.woff2 +0 -0
  25. package/familjen-grotesk/familjen-grotesk-700-latin.woff2 +0 -0
  26. package/familjen-grotesk/familjen-grotesk-700-vietnamese.woff2 +0 -0
  27. package/fesm2022/foxeltech-angular-ui-chart.mjs +381 -40
  28. package/fesm2022/foxeltech-angular-ui-chart.mjs.map +1 -1
  29. package/fesm2022/foxeltech-angular-ui-flow.mjs +176 -3
  30. package/fesm2022/foxeltech-angular-ui-flow.mjs.map +1 -1
  31. package/fesm2022/foxeltech-angular-ui-rich-text.mjs +2 -2
  32. package/fesm2022/foxeltech-angular-ui-rich-text.mjs.map +1 -1
  33. package/fesm2022/foxeltech-angular-ui.mjs +1327 -151
  34. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  35. package/fonts.css +558 -133
  36. package/ibm-plex-mono/ibm-plex-mono-400-cyrillic-ext.woff2 +0 -0
  37. package/ibm-plex-mono/ibm-plex-mono-400-cyrillic.woff2 +0 -0
  38. package/ibm-plex-mono/ibm-plex-mono-400-latin-ext.woff2 +0 -0
  39. package/ibm-plex-mono/ibm-plex-mono-400-latin.woff2 +0 -0
  40. package/ibm-plex-mono/ibm-plex-mono-400-vietnamese.woff2 +0 -0
  41. package/ibm-plex-mono/ibm-plex-mono-500-cyrillic-ext.woff2 +0 -0
  42. package/ibm-plex-mono/ibm-plex-mono-500-cyrillic.woff2 +0 -0
  43. package/ibm-plex-mono/ibm-plex-mono-500-latin-ext.woff2 +0 -0
  44. package/ibm-plex-mono/ibm-plex-mono-500-latin.woff2 +0 -0
  45. package/ibm-plex-mono/ibm-plex-mono-500-vietnamese.woff2 +0 -0
  46. package/ibm-plex-mono/ibm-plex-mono-600-cyrillic-ext.woff2 +0 -0
  47. package/ibm-plex-mono/ibm-plex-mono-600-cyrillic.woff2 +0 -0
  48. package/ibm-plex-mono/ibm-plex-mono-600-latin-ext.woff2 +0 -0
  49. package/ibm-plex-mono/ibm-plex-mono-600-latin.woff2 +0 -0
  50. package/ibm-plex-mono/ibm-plex-mono-600-vietnamese.woff2 +0 -0
  51. package/jetbrains-mono/jetbrains-mono-400-cyrillic-ext.woff2 +0 -0
  52. package/jetbrains-mono/jetbrains-mono-400-cyrillic.woff2 +0 -0
  53. package/jetbrains-mono/jetbrains-mono-400-greek.woff2 +0 -0
  54. package/jetbrains-mono/jetbrains-mono-400-latin-ext.woff2 +0 -0
  55. package/jetbrains-mono/jetbrains-mono-400-latin.woff2 +0 -0
  56. package/jetbrains-mono/jetbrains-mono-400-vietnamese.woff2 +0 -0
  57. package/jetbrains-mono/jetbrains-mono-500-cyrillic-ext.woff2 +0 -0
  58. package/jetbrains-mono/jetbrains-mono-500-cyrillic.woff2 +0 -0
  59. package/jetbrains-mono/jetbrains-mono-500-greek.woff2 +0 -0
  60. package/jetbrains-mono/jetbrains-mono-500-latin-ext.woff2 +0 -0
  61. package/jetbrains-mono/jetbrains-mono-500-latin.woff2 +0 -0
  62. package/jetbrains-mono/jetbrains-mono-500-vietnamese.woff2 +0 -0
  63. package/jetbrains-mono/jetbrains-mono-600-cyrillic-ext.woff2 +0 -0
  64. package/jetbrains-mono/jetbrains-mono-600-cyrillic.woff2 +0 -0
  65. package/jetbrains-mono/jetbrains-mono-600-greek.woff2 +0 -0
  66. package/jetbrains-mono/jetbrains-mono-600-latin-ext.woff2 +0 -0
  67. package/jetbrains-mono/jetbrains-mono-600-latin.woff2 +0 -0
  68. package/jetbrains-mono/jetbrains-mono-600-vietnamese.woff2 +0 -0
  69. package/jetbrains-mono/jetbrains-mono-700-cyrillic-ext.woff2 +0 -0
  70. package/jetbrains-mono/jetbrains-mono-700-cyrillic.woff2 +0 -0
  71. package/jetbrains-mono/jetbrains-mono-700-greek.woff2 +0 -0
  72. package/jetbrains-mono/jetbrains-mono-700-latin-ext.woff2 +0 -0
  73. package/jetbrains-mono/jetbrains-mono-700-latin.woff2 +0 -0
  74. package/jetbrains-mono/jetbrains-mono-700-vietnamese.woff2 +0 -0
  75. package/newsreader/newsreader-400-latin-ext.woff2 +0 -0
  76. package/newsreader/newsreader-400-latin.woff2 +0 -0
  77. package/newsreader/newsreader-400-vietnamese.woff2 +0 -0
  78. package/newsreader/newsreader-500-latin-ext.woff2 +0 -0
  79. package/newsreader/newsreader-500-latin.woff2 +0 -0
  80. package/newsreader/newsreader-500-vietnamese.woff2 +0 -0
  81. package/newsreader/newsreader-600-latin-ext.woff2 +0 -0
  82. package/newsreader/newsreader-600-latin.woff2 +0 -0
  83. package/newsreader/newsreader-600-vietnamese.woff2 +0 -0
  84. package/package.json +5 -2
  85. package/source-serif-4/source-serif-4-400-cyrillic-ext.woff2 +0 -0
  86. package/source-serif-4/source-serif-4-400-cyrillic.woff2 +0 -0
  87. package/source-serif-4/source-serif-4-400-greek.woff2 +0 -0
  88. package/source-serif-4/source-serif-4-400-latin-ext.woff2 +0 -0
  89. package/source-serif-4/source-serif-4-400-latin.woff2 +0 -0
  90. package/source-serif-4/source-serif-4-400-vietnamese.woff2 +0 -0
  91. package/source-serif-4/source-serif-4-600-cyrillic-ext.woff2 +0 -0
  92. package/source-serif-4/source-serif-4-600-cyrillic.woff2 +0 -0
  93. package/source-serif-4/source-serif-4-600-greek.woff2 +0 -0
  94. package/source-serif-4/source-serif-4-600-latin-ext.woff2 +0 -0
  95. package/source-serif-4/source-serif-4-600-latin.woff2 +0 -0
  96. package/source-serif-4/source-serif-4-600-vietnamese.woff2 +0 -0
  97. package/src/lib/styles/components.css +332 -30
  98. package/src/lib/styles/dialogs.css +2 -2
  99. package/src/lib/styles/fonts.css +558 -133
  100. package/src/lib/styles/tailwind.css +13 -1
  101. package/src/lib/styles/theme.css +642 -69
  102. package/src/lib/styles/tokens.css +81 -19
  103. package/src/lib/styles/utilities.css +16 -1
  104. package/src/lib/ui/components/bar-list/bar-list.component.html +29 -0
  105. package/src/lib/ui/components/button/button.component.html +9 -5
  106. package/src/lib/ui/components/checkbox/checkbox.component.html +1 -1
  107. package/src/lib/ui/components/code-block/code-block.component.html +8 -0
  108. package/src/lib/ui/components/drawer/drawer.component.html +35 -12
  109. package/src/lib/ui/components/drawer/drawer.component.scss +106 -18
  110. package/src/lib/ui/components/facets-rail/facets-rail.component.html +37 -0
  111. package/src/lib/ui/components/filter-pills/filter-pills.component.html +16 -0
  112. package/src/lib/ui/components/kanban-board/kanban-board.component.scss +9 -9
  113. package/src/lib/ui/components/list-row/list-row.component.html +20 -0
  114. package/src/lib/ui/components/master-detail/master-detail.component.css +79 -5
  115. package/src/lib/ui/components/master-detail/master-detail.component.html +57 -7
  116. package/src/lib/ui/components/pending-badge/pending-badge.component.html +15 -0
  117. package/src/lib/ui/components/pipeline-stepper/pipeline-stepper.component.html +45 -0
  118. package/src/lib/ui/components/project-card/project-card.component.html +102 -0
  119. package/src/lib/ui/components/radio-button/radio-button.component.html +1 -1
  120. package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +1 -1
  121. package/src/lib/ui/components/route-skeleton/route-skeleton.component.css +38 -0
  122. package/src/lib/ui/components/route-skeleton/route-skeleton.component.html +184 -0
  123. package/src/lib/ui/components/sample-frame/sample-frame.component.html +18 -0
  124. package/src/lib/ui/components/score-badge/score-badge.component.html +7 -0
  125. package/src/lib/ui/components/screenshot-frame/screenshot-frame.component.html +40 -0
  126. package/src/lib/ui/components/section-card/section-card.component.html +15 -6
  127. package/src/lib/ui/components/segmented/segmented.component.html +42 -0
  128. package/src/lib/ui/components/segmented/segmented.component.scss +12 -0
  129. package/src/lib/ui/components/skeleton-dashboard/skeleton-dashboard.component.html +24 -0
  130. package/src/lib/ui/components/skeleton-detail/skeleton-detail.component.html +8 -8
  131. package/src/lib/ui/components/skeleton-facets/skeleton-facets.component.html +15 -0
  132. package/src/lib/ui/components/skeleton-frame/skeleton-frame.component.html +59 -0
  133. package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +6 -6
  134. package/src/lib/ui/components/slider/slider.component.html +2 -2
  135. package/src/lib/ui/components/sparkbar/sparkbar.component.html +14 -0
  136. package/src/lib/ui/components/stage-dots/stage-dots.component.html +13 -0
  137. package/src/lib/ui/components/stat-cards/stat-cards.component.html +44 -11
  138. package/src/lib/ui/components/status-dot/status-dot.component.html +13 -0
  139. package/src/lib/ui/components/switch/switch.component.html +1 -1
  140. package/src/lib/ui/components/tab-group/tab-group.component.html +37 -8
  141. package/src/lib/ui/components/tab-group/tab-group.component.scss +41 -1
  142. package/src/lib/ui/components/tag/tag.component.html +2 -2
  143. package/src/lib/ui/components/theme-picker/theme-picker.component.html +44 -0
  144. package/src/lib/ui/components/toast/toast.component.html +6 -6
  145. package/src/lib/ui/components/tree-diagram/tree-diagram.component.html +3 -3
  146. package/tailwind.css +13 -1
  147. package/theme.css +642 -69
  148. package/tokens.css +81 -19
  149. package/types/foxeltech-angular-ui-chart.d.ts +90 -3
  150. package/types/foxeltech-angular-ui-flow.d.ts +83 -1
  151. package/types/foxeltech-angular-ui.d.ts +909 -48
  152. package/utilities.css +16 -1
  153. package/BeVietnamPro/BeVietnamPro-Bold.woff2 +0 -0
  154. package/BeVietnamPro/BeVietnamPro-ExtraLight.woff2 +0 -0
  155. package/BeVietnamPro/BeVietnamPro-Light.woff2 +0 -0
  156. package/BeVietnamPro/BeVietnamPro-Medium.woff2 +0 -0
  157. package/BeVietnamPro/BeVietnamPro-Regular.woff2 +0 -0
  158. package/BeVietnamPro/BeVietnamPro-SemiBold.woff2 +0 -0
  159. package/BeVietnamPro/BeVietnamPro-Thin.woff2 +0 -0
  160. package/SpaceMono-Regular.woff2 +0 -0
  161. package/dm-sans/DMSans-Bold.woff2 +0 -0
  162. package/dm-sans/DMSans-Light.woff2 +0 -0
  163. package/dm-sans/DMSans-Medium.woff2 +0 -0
  164. package/dm-sans/DMSans-Regular.woff2 +0 -0
  165. package/dm-sans/DMSans-SemiBold.woff2 +0 -0
  166. package/dm-sans/DMSans-Thin.woff2 +0 -0
  167. package/orbitron/Orbitron-Bold.woff2 +0 -0
  168. package/orbitron/Orbitron-Medium.woff2 +0 -0
  169. package/orbitron/Orbitron-Regular.woff2 +0 -0
  170. package/orbitron/Orbitron-SemiBold.woff2 +0 -0
  171. package/roboto/Roboto-Black.woff2 +0 -0
  172. package/roboto/Roboto-BlackItalic.woff2 +0 -0
  173. package/roboto/Roboto-Bold.woff2 +0 -0
  174. package/roboto/Roboto-BoldCondensed.woff2 +0 -0
  175. package/roboto/Roboto-BoldCondensedItalic.woff2 +0 -0
  176. package/roboto/Roboto-BoldItalic.woff2 +0 -0
  177. package/roboto/Roboto-Condensed.woff2 +0 -0
  178. package/roboto/Roboto-CondensedItalic.woff2 +0 -0
  179. package/roboto/Roboto-Italic.woff2 +0 -0
  180. package/roboto/Roboto-Light.woff2 +0 -0
  181. package/roboto/Roboto-LightItalic.woff2 +0 -0
  182. package/roboto/Roboto-Medium.woff2 +0 -0
  183. package/roboto/Roboto-MediumItalic.woff2 +0 -0
  184. package/roboto/Roboto-Regular.woff2 +0 -0
  185. package/roboto/Roboto-Thin.woff2 +0 -0
  186. package/roboto/Roboto-ThinItalic.woff2 +0 -0
  187. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.html +0 -21
  188. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.scss +0 -0
  189. package/src/lib/ui/components/tab-component/tab.component.html +0 -1
@@ -3,16 +3,60 @@
3
3
  * Replaces the old JS preset (tailwind.config.js / lodash.merge sharing).
4
4
  *
5
5
  * Colour tokens point at the runtime RGB-triplet variables from theme.css
6
- * (dark default on :root, light via .light_theme) so theme switching keeps
7
- * working; opacity modifiers like bg-critical/10 are handled by v4's
8
- * color-mix() and need no <alpha-value> plumbing.
6
+ * (OgeniumDashboards --og-* per [data-og-theme] on <html>, porcelain default)
7
+ * so theme switching keeps working; opacity modifiers like bg-critical/10 are
8
+ * handled by v4's color-mix() and need no <alpha-value> plumbing.
9
9
  */
10
10
  /* `inline` is load-bearing: without it the rgb(var(--x)) values are baked
11
- into :root once, so .light_theme's runtime triplet overrides never reach
11
+ into :root once, so [data-og-theme]'s runtime triplet overrides never reach
12
12
  the utilities. inline makes every utility carry rgb(var(--x)) itself and
13
13
  resolve at the element that uses it — same behavior as v3. */
14
14
  @theme inline {
15
- /* Colours (names match the v3 preset so every existing class keeps working) */
15
+ /* ---- OG palette use these in new/restyled markup ---- */
16
+ --color-page: rgb(var(--og-page-bg));
17
+ --color-surface: rgb(var(--og-surface));
18
+ --color-surface-raised: rgb(var(--og-surface-raised));
19
+ --color-surface-sunken: rgb(var(--og-surface-sunken));
20
+ --color-border: rgb(var(--og-border));
21
+ --color-border-subtle: rgb(var(--og-border-subtle));
22
+ --color-strong: rgb(var(--og-text));
23
+ --color-body: rgb(var(--og-text-body));
24
+ --color-muted: rgb(var(--og-text-muted));
25
+ --color-faint: rgb(var(--og-text-faint));
26
+ --color-accent: rgb(var(--og-accent));
27
+ --color-accent-fg: rgb(var(--og-accent-fg));
28
+ --color-accent-contrast: rgb(var(--og-accent-contrast));
29
+ --color-accent-soft: rgb(var(--og-accent-soft-bg));
30
+ --color-accent-soft-fg: rgb(var(--og-accent-soft-fg));
31
+ --color-positive: rgb(var(--og-positive));
32
+ --color-positive-soft: rgb(var(--og-positive-soft-bg));
33
+ --color-negative: rgb(var(--og-negative));
34
+ --color-negative-soft: rgb(var(--og-negative-soft-bg));
35
+ --color-warning-soft: rgb(var(--og-warning-soft-bg));
36
+ --color-neutral-soft: rgb(var(--og-neutral-soft-bg));
37
+ --color-neutral-soft-fg: rgb(var(--og-neutral-soft-fg));
38
+ --color-track: rgb(var(--og-track));
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
+
57
+ /* ---- Legacy colour names (@deprecated — remove after the V2 campaign;
58
+ they resolve through the alias layer in theme.css, so every existing
59
+ class already renders the OG palette) ---- */
16
60
  --color-gradient-primary-start: rgb(var(--gradient-primary-start));
17
61
  --color-gradient-primary-end: rgb(var(--gradient-primary-end));
18
62
  --color-gradient-success-start: rgb(var(--gradient-success-start));
@@ -46,6 +90,14 @@
46
90
  --color-button-primary: rgb(var(--button-primary));
47
91
 
48
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:`. */
49
101
  --breakpoint-sm: 480px;
50
102
  --breakpoint-md: 720px;
51
103
  --breakpoint-lg: 1080px;
@@ -61,14 +113,22 @@
61
113
  --shadow-2xl: 0 25px 50px -12px rgb(var(--shadow-color) / 0.25);
62
114
  --inset-shadow-sm: inset 0 2px 4px 0 rgb(var(--shadow-color) / 0.05);
63
115
 
64
- /* v3 default border radii, incl. DEFAULT so bare `rounded` keeps working */
65
- --radius-sm: 0.125rem;
66
- --radius: 0.25rem;
67
- --radius-md: 0.375rem;
68
- --radius-lg: 0.5rem;
69
- --radius-xl: 0.75rem;
70
- --radius-2xl: 1rem;
71
- --radius-3xl: 1.5rem;
116
+ /* Radius is a THEME variable in V2 (blueprint/terminal are square, ivory
117
+ controls are pills) — never hardcode border-radius in components. */
118
+ --radius-frame: var(--og-radius-frame);
119
+ --radius-card: var(--og-radius-card);
120
+ --radius-control: var(--og-radius-control);
121
+ --radius-pill: var(--og-radius-pill);
122
+ /* Legacy fixed scale remapped onto the theme radii so blueprint/terminal go
123
+ square with zero template edits. `rounded-full` stays Tailwind-native
124
+ 9999px — avatars/dots must remain circular in every theme. */
125
+ --radius-sm: var(--og-radius-control);
126
+ --radius: var(--og-radius-control);
127
+ --radius-md: var(--og-radius-control);
128
+ --radius-lg: var(--og-radius-card);
129
+ --radius-xl: var(--og-radius-card);
130
+ --radius-2xl: var(--og-radius-card);
131
+ --radius-3xl: var(--og-radius-frame);
72
132
 
73
133
  /* Named spacing scale (gap-small, p-semi, mb-large, …) */
74
134
  --spacing-xs: 0.125rem;
@@ -79,12 +139,14 @@
79
139
  --spacing-xl: 1.25rem;
80
140
  --spacing-2xl: 1.5rem;
81
141
 
82
- /* Font families */
83
- --font-spmono: 'SpaceMonoRegular', monospace;
84
- --font-dmsans: 'DMSans', sans-serif;
85
- --font-orbitron: 'Orbitron', sans-serif;
86
- --font-bevn: 'BeVietnamPro', sans-serif;
87
- --font-roboto: 'Roboto', sans-serif;
142
+ /* Font families — theme variables (blueprint/terminal switch to mono).
143
+ V2 set: Archivo (body/display), JetBrains Mono / IBM Plex Mono (mono),
144
+ Source Serif 4 (prose), Newsreader (ivory/blueprint display). */
145
+ --font-body: var(--og-font-body);
146
+ --font-display: var(--og-font-display);
147
+ --font-mono: var(--og-font-mono);
148
+ --font-prose: var(--og-font-prose);
149
+ --font-figure: var(--og-font-figure);
88
150
 
89
151
  /* Font-size overrides (v3 preset values) */
90
152
  --text-xs: 0.75rem;
@@ -8,13 +8,28 @@
8
8
  }
9
9
 
10
10
  @utility btn-common {
11
- @apply flex text-sm h-10 font-semibold px-xl py-normal items-center justify-center rounded-lg shadow-sm transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed;
11
+ /* Compact size (anh 29/08): h-8 + 13px label the pre-V2 h-10 read too
12
+ large against the V2 density. leading-none: arbitrary text-[13px] inherits
13
+ lh 1.5 and Archivo's uneven leading floats the ink ~1px above center. */
14
+ @apply flex text-[13px] leading-none h-8 font-semibold px-large py-small items-center justify-center rounded-lg shadow-sm transition-all duration-200 disabled:opacity-50 disabled:pointer-events-none disabled:cursor-not-allowed;
12
15
  }
13
16
 
14
17
  @utility btn-menu-common {
15
18
  @apply flex flex-row items-center px-semi;
16
19
  }
17
20
 
21
+ /* Nhãn "eyebrow" (tiêu đề panel, nhóm facet) — spec CUỐI của demo sau khi
22
+ lớp override đè lên base: font MONO, 11.5px, weight 600, letter-spacing
23
+ 0.02em và KHÔNG viết hoa. Base DS viết hoa + tracking 0.08em, nhưng
24
+ override đặt `text-transform: none` nên demo hiển thị chữ thường. */
25
+ @utility txt-eyebrow {
26
+ font-family: var(--og-font-mono);
27
+ font-size: var(--og-fs-xs);
28
+ font-weight: 600;
29
+ letter-spacing: 0.02em;
30
+ color: rgb(var(--og-text-muted));
31
+ }
32
+
18
33
  @utility card-common {
19
34
  @apply text-left px-xl py-large border border-border-default rounded-lg shadow-sm transition-colors duration-500 ease-in-out;
20
35
  }
@@ -0,0 +1,29 @@
1
+ <div class="flex flex-col gap-small">
2
+ @for (item of items(); track item.label) {
3
+ <button
4
+ type="button"
5
+ class="text-left rounded-control px-normal py-small -mx-normal transition-colors"
6
+ [class.hover:bg-surface-sunken]="interactive()"
7
+ [class.cursor-default]="!interactive()"
8
+ [tabindex]="interactive() ? 0 : -1"
9
+ (click)="onSelect(item)"
10
+ >
11
+ <div class="flex items-baseline justify-between gap-normal">
12
+ <span class="txt-body-medium-01 truncate">{{ item.label }}</span>
13
+ <span class="text-sm font-semibold text-strong shrink-0" style="font-family: var(--og-font-figure)">
14
+ {{ item.valueLabel ?? item.value }}
15
+ </span>
16
+ </div>
17
+ @if (item.meta) {
18
+ <div class="txt-utility-description truncate">{{ item.meta }}</div>
19
+ }
20
+ <div class="mt-xs h-[var(--og-track-h)] rounded-pill bg-track overflow-hidden">
21
+ <div
22
+ class="h-full rounded-pill transition-all duration-500"
23
+ [ngClass]="barClass(item)"
24
+ [style.width.%]="widthOf(item)"
25
+ ></div>
26
+ </div>
27
+ </button>
28
+ }
29
+ </div>
@@ -7,6 +7,7 @@
7
7
  'btn-default': buttonVariant() === 'default',
8
8
  'btn-primary': buttonVariant() === 'primary',
9
9
  'btn-success': buttonVariant() === 'success',
10
+ 'btn-cta': buttonVariant() === 'cta',
10
11
  'btn-alternative': buttonVariant() === 'alternative',
11
12
  }"
12
13
  [disabled]="disabled() || loading()"
@@ -18,8 +19,8 @@
18
19
  [attr.fill]="
19
20
  buttonVariant() !== 'alternative' ? 'rgb(var(--text-primary))' : 'rgb(var(--primary))'
20
21
  "
21
- width="20"
22
- height="20"
22
+ width="16"
23
+ height="16"
23
24
  viewBox="0 0 24 24"
24
25
  xmlns="http://www.w3.org/2000/svg"
25
26
  >
@@ -219,17 +220,20 @@
219
220
  @if (icon) {
220
221
  <fx-ui-hero-icon
221
222
  [icon]="icon"
222
- [size]="20"
223
+ [size]="16"
223
224
  class="flex"
224
225
  [ngClass]="{
225
226
  'text-text-inverse': buttonVariant() === 'default',
226
227
  'text-text-primary': buttonVariant() === 'alternative',
227
- 'text-white': buttonVariant() === 'primary' || buttonVariant() === 'success',
228
+ 'text-accent-contrast': buttonVariant() === 'primary',
229
+ 'text-surface': buttonVariant() === 'success',
230
+ 'text-white': buttonVariant() === 'cta',
228
231
  }"
229
232
  ></fx-ui-hero-icon>
230
233
  }
231
234
  @if (label) {
232
- <div>{{ label }}</div>
235
+ <!-- text-box: optical centering — Archivo's leading floats ink up otherwise -->
236
+ <div class="[text-box:trim-both_cap_alphabetic]">{{ label }}</div>
233
237
  }
234
238
  </div>
235
239
  </ng-container>
@@ -25,7 +25,7 @@
25
25
  <span class="absolute inset-0 flex items-center justify-center">
26
26
  <svg
27
27
  xmlns="http://www.w3.org/2000/svg"
28
- class="text-white w-4 h-4 transition-opacity duration-150"
28
+ class="text-accent-contrast w-4 h-4 transition-opacity duration-150"
29
29
  viewBox="0 0 24 24"
30
30
  fill="none"
31
31
  stroke="currentColor"
@@ -0,0 +1,8 @@
1
+ @if (label()) {
2
+ <div class="txt-utility-caption font-bold text-text-secondary mb-normal">{{ label() }}</div>
3
+ }
4
+ <pre
5
+ class="m-0 p-large rounded-control border border-border bg-surface overflow-auto txt-body-compact-01 text-strong"
6
+ [style.max-height]="maxHeight() || null"
7
+ style="font-family: var(--og-font-mono); white-space: pre; tab-size: 2"
8
+ >{{ code() }}</pre>
@@ -1,18 +1,19 @@
1
1
  <div class="drawer-host-wrapper">
2
- <a
3
- href="/"
4
- class="h-[60px]"
5
- [ngClass]="{ 'flex items-center justify-center': !isExpanded, 'p-4': isExpanded }"
6
- >
2
+ <a href="/" class="drawer-brand" [class.is-collapsed]="!isExpanded">
7
3
  @if (logo) {
8
- <img
9
- [src]="logo"
10
- alt="Home"
11
- class="h-[20px] w-auto transition-all duration-300 ease-in-out"
12
- [ngClass]="{ 'scale-90': !isExpanded, 'ml-5': isExpanded }"
13
- />
4
+ <img [src]="logo" alt="Home" class="drawer-brand-mark" />
14
5
  } @else {
15
- <div class="h-[30px] w-auto transition-all duration-300 ease-in-out"></div>
6
+ <div class="drawer-brand-mark"></div>
7
+ }
8
+ @if (isExpanded && (logoText || logoSubtext)) {
9
+ <span class="drawer-brand-copy">
10
+ @if (logoText) {
11
+ <span class="drawer-brand-text">{{ logoText }}</span>
12
+ }
13
+ @if (logoSubtext) {
14
+ <span class="drawer-brand-subtext">{{ logoSubtext }}</span>
15
+ }
16
+ </span>
16
17
  }
17
18
  </a>
18
19
  <div class="drawer-container" [@drawerExpand]="getDrawerState()">
@@ -25,6 +26,20 @@
25
26
  }
26
27
  </nav>
27
28
 
29
+ <!-- V2: bottom section (Administration group, user block, …) -->
30
+ <div class="mt-auto">
31
+ @if (footerItems().length) {
32
+ <nav class="drawer-footer-nav">
33
+ @for (item of footerItems(); track item) {
34
+ <ng-container
35
+ *ngTemplateOutlet="drawerItem; context: { $implicit: item, level: 0 }"
36
+ ></ng-container>
37
+ }
38
+ </nav>
39
+ }
40
+ <ng-content select="[drawerFooter]"></ng-content>
41
+ </div>
42
+
28
43
  <!-- Version -->
29
44
  @if (version) {
30
45
  <div class="drawer-version" [matTooltip]="!isExpanded ? version : ''" matTooltipPosition="right">
@@ -59,6 +74,13 @@
59
74
 
60
75
  <!-- Recursive Template for Drawer Items -->
61
76
  <ng-template #drawerItem let-item let-level="level">
77
+ @if (item.heading) {
78
+ @if (isExpanded) {
79
+ <div class="drawer-section-label">{{ item.label }}</div>
80
+ } @else {
81
+ <div class="drawer-section-rule"></div>
82
+ }
83
+ } @else {
62
84
  <div class="drawer-item-wrapper">
63
85
  <!-- Item Button/Link -->
64
86
  @if (item.route && !item.children) {
@@ -125,4 +147,5 @@
125
147
  </div>
126
148
  }
127
149
  </div>
150
+ }
128
151
  </ng-template>
@@ -6,11 +6,75 @@
6
6
  }
7
7
 
8
8
  .drawer-host-wrapper {
9
- @apply relative h-full border-r border-border-default;
9
+ /* Column flex: logo (fixed) + container (flex-1). The container was h-full
10
+ UNDER a 60px logo -> the drawer overflowed the viewport by 60px and the
11
+ footer (account block) sagged below the fold; opening its menu then
12
+ scroll-jumped the page. */
13
+ /* Nền SECONDARY (--og-page-bg) đặt ở WRAPPER chứ không ở .drawer-container:
14
+ hàng brand nằm ngoài container, nếu tô ở container thì 60px trên cùng
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
+ 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;
18
+ }
19
+
20
+ /* 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). */
24
+ .drawer-brand {
25
+ @apply h-[60px] shrink-0 flex items-center gap-3 no-underline overflow-hidden;
26
+ padding-left: 24px;
27
+ padding-right: 8px;
28
+ transition: padding 300ms cubic-bezier(0.4, 0, 0.2, 1);
29
+ /* max-width là BẮT BUỘC: .drawer-host-wrapper là flex column có width auto
30
+ nên bề rộng của nó = max bề rộng các con. Chỉ .drawer-container được
31
+ animate 70/260px, còn hàng brand thì tự do — một tagline dài (VD "Binary
32
+ Exploit Intelligence Platform") sẽ NỚI RỘNG cả drawer thay vì xuống dòng.
33
+ Hai con số dưới đây phải khớp trigger drawerExpand trong drawer.component.ts. */
34
+ max-width: 260px;
35
+
36
+ /* Thu gọn: drawer rộng 70px, icon nav (20px @ 24px) có tâm 34px ≈ tâm
37
+ drawer 35px → căn giữa là khớp, không cần trừ bì. */
38
+ &.is-collapsed {
39
+ @apply justify-center;
40
+ padding-left: 0;
41
+ padding-right: 0;
42
+ max-width: 70px;
43
+ }
44
+ }
45
+
46
+ .drawer-brand-mark {
47
+ @apply h-[20px] w-auto shrink-0;
48
+ }
49
+
50
+ /* Cột chữ: title + subtitle xếp dọc. min-w-0 để truncate hoạt động khi
51
+ drawer bị bóp — thiếu nó thì chữ đẩy tràn ra ngoài thay vì cắt. */
52
+ .drawer-brand-copy {
53
+ @apply flex flex-col min-w-0 leading-tight;
54
+ }
55
+
56
+ /* Wordmark: dùng CHỮ THẬT chứ không phải ảnh text_logo.png — ảnh đó là
57
+ navy đặc nên chìm hẳn trên theme dark; chữ ăn --og-text nên đúng cả 6. */
58
+ .drawer-brand-text {
59
+ @apply truncate text-[15px] font-semibold text-text-primary select-none;
60
+ font-family: var(--og-font-display);
61
+ letter-spacing: -0.01em;
62
+ }
63
+
64
+ /* Subtitle: 11px muted, cùng cỡ với .drawer-version và caption
65
+ ADMINISTRATION nên không tự sinh ra một bậc typography mới. */
66
+ /* line-clamp-2 chứ không phải truncate: tagline dài (VD "Binary Exploit
67
+ Intelligence Platform") không lọt một dòng trong cột chữ ~188px của drawer
68
+ 260px, truncate sẽ cắt mất chữ. Hai dòng vẫn nằm gọn trong 60px của
69
+ .drawer-brand (18.75 + 1 + 27.5 = 47px). */
70
+ .drawer-brand-subtext {
71
+ @apply line-clamp-2 text-[11px] text-text-placeholder select-none mt-[1px];
72
+ letter-spacing: var(--og-tracking-label);
10
73
  }
11
74
 
12
75
  .drawer-container {
13
- @apply px-2 h-full bg-bg-primary flex flex-col overflow-auto;
76
+ /* Nền do .drawer-host-wrapper lo container để trong suốt. */
77
+ @apply px-2 flex-1 min-h-0 flex flex-col overflow-hidden;
14
78
  transition: width 300ms cubic-bezier(0.4, 0, 0.2, 1);
15
79
  }
16
80
 
@@ -33,6 +97,10 @@
33
97
  }
34
98
  }
35
99
 
100
+ .drawer-footer-nav {
101
+ @apply border-t border-border-default pt-2 pb-1 px-0;
102
+ }
103
+
36
104
  .drawer-nav {
37
105
  @apply flex-1 overflow-y-auto overflow-x-hidden py-2;
38
106
 
@@ -54,6 +122,17 @@
54
122
  }
55
123
  }
56
124
 
125
+ .drawer-section-label {
126
+ /* Demo (.og-sidebar__section sau override): 10.5px, text-faint, giãn chữ
127
+ .08em và KHÔNG viết hoa — override đặt `text-transform: none`. */
128
+ @apply px-large pt-4 pb-1 text-[10.5px]/[1.4] font-semibold text-faint select-none;
129
+ letter-spacing: var(--og-tracking-label);
130
+ }
131
+
132
+ .drawer-section-rule {
133
+ @apply mx-3 my-3 border-t border-border-default;
134
+ }
135
+
57
136
  .drawer-item-wrapper {
58
137
  @apply relative;
59
138
  }
@@ -62,15 +141,35 @@
62
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;
63
142
  transition: all 200ms ease;
64
143
 
144
+ /* Hover = glass mờ hơn: chỉ translucent, chưa có viền/shadow, để không
145
+ nhầm với hàng đang chọn. */
65
146
  &:hover {
66
- @apply bg-bg-hover text-text-primary scale-95;
147
+ @apply text-text-primary scale-95;
148
+ background: rgb(var(--og-surface-raised) / 0.5);
67
149
  }
68
150
 
151
+ /* Hàng đang chọn = GLASS, KHÔNG dùng màu primary (anh Tú chốt 31/08):
152
+ một tấm surface trong suốt nổi trên nền secondary của drawer + viền
153
+ hairline + shadow nhẹ. Chữ/icon giữ màu text thường.
154
+ - surface-RAISED (không phải surface) vì ở charcoal raised (#211c1b)
155
+ sáng hơn page-bg (#0e0e10) nên tấm kính vẫn nổi; ở porcelain cả hai
156
+ đều là trắng nên không khác gì.
157
+ - Viền vẽ bằng inset box-shadow chứ không phải `border`: .drawer-item
158
+ khai border-none, thêm border thật sẽ đẩy layout 1px.
159
+ - backdrop-blur chỉ có tác dụng khi nền sau có hoạ tiết/gradient; nền
160
+ phẳng thì hiệu ứng đến từ độ trong + viền + shadow. */
69
161
  &.active {
70
- @apply bg-gradient-primary text-white font-medium;
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);
71
170
 
72
- .item-icon {
73
- @apply text-white;
171
+ &:hover {
172
+ background: rgb(var(--og-surface-raised) / 0.95);
74
173
  }
75
174
  }
76
175
 
@@ -114,21 +213,10 @@
114
213
 
115
214
  .drawer-version {
116
215
  @apply flex items-center justify-center py-3 px-4 text-text-placeholder text-[11px] border-t border-border-default select-none;
117
- font-family: 'Roboto', monospace;
216
+ font-family: var(--og-font-mono);
118
217
  }
119
218
 
120
219
  .drawer-version-dot {
121
220
  @apply block w-1.5 h-1.5 rounded-full bg-text-placeholder;
122
221
  }
123
222
 
124
- /* Collapsed mode: center the icons */
125
- .drawer-container[style*='width: 64px'] {
126
- .drawer-item {
127
- @apply justify-center;
128
- padding-left: 16px !important;
129
- }
130
-
131
- .item-content {
132
- @apply justify-center;
133
- }
134
- }
@@ -0,0 +1,37 @@
1
+ <div class="flex flex-col gap-large">
2
+ @if (hasSelection()) {
3
+ <button type="button" class="txt-link self-end" (click)="reset()">
4
+ {{ resetLabel() }}
5
+ </button>
6
+ }
7
+ @for (group of groups(); track group.id) {
8
+ <div role="group" [attr.aria-label]="group.label">
9
+ <div class="txt-eyebrow mb-small">{{ group.label }}</div>
10
+ <div class="flex flex-col gap-xs">
11
+ @for (option of group.options; track option.value) {
12
+ <button
13
+ type="button"
14
+ (click)="toggle(group, option)"
15
+ [attr.aria-pressed]="isSelected(group, option)"
16
+ class="flex items-center gap-normal px-normal py-small rounded-control text-left transition-colors"
17
+ [ngClass]="
18
+ isSelected(group, option)
19
+ ? 'bg-accent-soft text-accent-soft-fg font-medium'
20
+ : 'text-body hover:bg-surface-sunken'
21
+ "
22
+ >
23
+ @if (option.tone) {
24
+ <fx-ui-status-dot [tone]="option.tone" [size]="7" />
25
+ }
26
+ <span class="text-sm truncate flex-1">{{ option.label }}</span>
27
+ @if (option.count !== undefined) {
28
+ <span class="txt-tag-label text-faint" style="font-family: var(--og-font-figure)">
29
+ {{ option.count }}
30
+ </span>
31
+ }
32
+ </button>
33
+ }
34
+ </div>
35
+ </div>
36
+ }
37
+ </div>
@@ -0,0 +1,16 @@
1
+ <div class="flex flex-wrap items-center gap-small">
2
+ @for (option of options(); track option.value) {
3
+ <button
4
+ type="button"
5
+ [attr.aria-pressed]="isActive(option)"
6
+ class="flex items-center gap-small px-semi py-xs rounded-full border txt-utility-caption font-bold cursor-pointer transition-colors"
7
+ [ngClass]="pillClass(option)"
8
+ (click)="valueChange.emit(option.value)"
9
+ >
10
+ {{ option.label }}
11
+ @if (option.count !== null && option.count !== undefined) {
12
+ <span [ngClass]="countClass(option)" style="font-family: var(--og-font-figure)">{{ option.count }}</span>
13
+ }
14
+ </button>
15
+ }
16
+ </div>
@@ -10,20 +10,20 @@
10
10
  }
11
11
 
12
12
  .kanban-column {
13
- @apply flex flex-col min-w-[300px] bg-gray-50 dark:bg-gray-800 rounded-lg;
13
+ @apply flex flex-col min-w-[300px] bg-surface-sunken rounded-lg;
14
14
  flex-shrink: 0;
15
15
  }
16
16
 
17
17
  .kanban-column-header {
18
- @apply flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700;
18
+ @apply flex items-center justify-between p-4 border-b border-border;
19
19
  }
20
20
 
21
21
  .kanban-column-title {
22
- @apply text-lg font-semibold text-gray-900 dark:text-gray-100 m-0;
22
+ @apply text-lg font-semibold text-strong m-0;
23
23
  }
24
24
 
25
25
  .kanban-column-count {
26
- @apply px-2 py-1 text-sm font-medium bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-full;
26
+ @apply px-2 py-1 text-sm font-medium bg-neutral-soft text-neutral-soft-fg rounded-full;
27
27
  }
28
28
 
29
29
  .kanban-column-content {
@@ -32,7 +32,7 @@
32
32
  }
33
33
 
34
34
  .kanban-item {
35
- @apply bg-white dark:bg-gray-700 rounded-lg shadow-sm border border-gray-200 dark:border-gray-600;
35
+ @apply bg-surface-raised rounded-lg shadow-sm border border-border;
36
36
  @apply transition-shadow duration-200;
37
37
  cursor: move;
38
38
 
@@ -42,18 +42,18 @@
42
42
  }
43
43
 
44
44
  .kanban-item-default {
45
- @apply p-4 text-gray-900 dark:text-gray-100;
45
+ @apply p-4 text-strong;
46
46
  }
47
47
 
48
48
  .kanban-item-placeholder {
49
- @apply bg-gray-200 dark:bg-gray-600 rounded-lg border-2 border-dashed border-gray-400 dark:border-gray-500;
49
+ @apply bg-neutral-soft rounded-lg border-2 border-dashed border-border-strong;
50
50
  @apply opacity-50;
51
51
  min-height: 60px;
52
52
  }
53
53
 
54
54
  .kanban-column-empty {
55
- @apply flex items-center justify-center h-32 text-gray-400 dark:text-gray-500 text-sm;
56
- @apply border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg;
55
+ @apply flex items-center justify-center h-32 text-faint text-sm;
56
+ @apply border-2 border-dashed border-border rounded-lg;
57
57
  }
58
58
 
59
59
  // CDK Drag and Drop Styles
@@ -0,0 +1,20 @@
1
+ <div class="flex items-center gap-normal min-w-0">
2
+ @if (dotTone() !== undefined) {
3
+ <fx-ui-status-dot [tone]="dotTone() ?? null" [pulse]="dotPulse()" />
4
+ }
5
+ <span class="txt-body-medium-01 truncate flex-1">{{ title() }}</span>
6
+ @for (badge of badges(); track badge.label) {
7
+ <span
8
+ class="txt-tag-label px-normal py-[1px] rounded-pill whitespace-nowrap"
9
+ [ngClass]="badgeClass(badge)"
10
+ >
11
+ {{ badge.label }}
12
+ </span>
13
+ }
14
+ </div>
15
+ @if (meta()) {
16
+ <div class="txt-utility-description mt-xs truncate" [class.ml-large]="dotTone() !== undefined">
17
+ {{ meta() }}
18
+ </div>
19
+ }
20
+ <ng-content></ng-content>