@foxeltech/angular-ui 0.3.1 → 0.7.44

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 (193) 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 +12 -12
  14. package/dialogs.css +2 -2
  15. package/fesm2022/foxeltech-angular-ui-chart.mjs +560 -0
  16. package/fesm2022/foxeltech-angular-ui-chart.mjs.map +1 -0
  17. package/fesm2022/foxeltech-angular-ui-flow.mjs +211 -0
  18. package/fesm2022/foxeltech-angular-ui-flow.mjs.map +1 -0
  19. package/fesm2022/foxeltech-angular-ui-rich-text.mjs +156 -0
  20. package/fesm2022/foxeltech-angular-ui-rich-text.mjs.map +1 -0
  21. package/fesm2022/foxeltech-angular-ui.mjs +1580 -1409
  22. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  23. package/fonts.css +462 -138
  24. package/fx-icons.svg +97 -0
  25. package/ibm-plex-mono/ibm-plex-mono-400-cyrillic-ext.woff2 +0 -0
  26. package/ibm-plex-mono/ibm-plex-mono-400-cyrillic.woff2 +0 -0
  27. package/ibm-plex-mono/ibm-plex-mono-400-latin-ext.woff2 +0 -0
  28. package/ibm-plex-mono/ibm-plex-mono-400-latin.woff2 +0 -0
  29. package/ibm-plex-mono/ibm-plex-mono-400-vietnamese.woff2 +0 -0
  30. package/ibm-plex-mono/ibm-plex-mono-500-cyrillic-ext.woff2 +0 -0
  31. package/ibm-plex-mono/ibm-plex-mono-500-cyrillic.woff2 +0 -0
  32. package/ibm-plex-mono/ibm-plex-mono-500-latin-ext.woff2 +0 -0
  33. package/ibm-plex-mono/ibm-plex-mono-500-latin.woff2 +0 -0
  34. package/ibm-plex-mono/ibm-plex-mono-500-vietnamese.woff2 +0 -0
  35. package/ibm-plex-mono/ibm-plex-mono-600-cyrillic-ext.woff2 +0 -0
  36. package/ibm-plex-mono/ibm-plex-mono-600-cyrillic.woff2 +0 -0
  37. package/ibm-plex-mono/ibm-plex-mono-600-latin-ext.woff2 +0 -0
  38. package/ibm-plex-mono/ibm-plex-mono-600-latin.woff2 +0 -0
  39. package/ibm-plex-mono/ibm-plex-mono-600-vietnamese.woff2 +0 -0
  40. package/jetbrains-mono/jetbrains-mono-400-cyrillic-ext.woff2 +0 -0
  41. package/jetbrains-mono/jetbrains-mono-400-cyrillic.woff2 +0 -0
  42. package/jetbrains-mono/jetbrains-mono-400-greek.woff2 +0 -0
  43. package/jetbrains-mono/jetbrains-mono-400-latin-ext.woff2 +0 -0
  44. package/jetbrains-mono/jetbrains-mono-400-latin.woff2 +0 -0
  45. package/jetbrains-mono/jetbrains-mono-400-vietnamese.woff2 +0 -0
  46. package/jetbrains-mono/jetbrains-mono-500-cyrillic-ext.woff2 +0 -0
  47. package/jetbrains-mono/jetbrains-mono-500-cyrillic.woff2 +0 -0
  48. package/jetbrains-mono/jetbrains-mono-500-greek.woff2 +0 -0
  49. package/jetbrains-mono/jetbrains-mono-500-latin-ext.woff2 +0 -0
  50. package/jetbrains-mono/jetbrains-mono-500-latin.woff2 +0 -0
  51. package/jetbrains-mono/jetbrains-mono-500-vietnamese.woff2 +0 -0
  52. package/jetbrains-mono/jetbrains-mono-600-cyrillic-ext.woff2 +0 -0
  53. package/jetbrains-mono/jetbrains-mono-600-cyrillic.woff2 +0 -0
  54. package/jetbrains-mono/jetbrains-mono-600-greek.woff2 +0 -0
  55. package/jetbrains-mono/jetbrains-mono-600-latin-ext.woff2 +0 -0
  56. package/jetbrains-mono/jetbrains-mono-600-latin.woff2 +0 -0
  57. package/jetbrains-mono/jetbrains-mono-600-vietnamese.woff2 +0 -0
  58. package/jetbrains-mono/jetbrains-mono-700-cyrillic-ext.woff2 +0 -0
  59. package/jetbrains-mono/jetbrains-mono-700-cyrillic.woff2 +0 -0
  60. package/jetbrains-mono/jetbrains-mono-700-greek.woff2 +0 -0
  61. package/jetbrains-mono/jetbrains-mono-700-latin-ext.woff2 +0 -0
  62. package/jetbrains-mono/jetbrains-mono-700-latin.woff2 +0 -0
  63. package/jetbrains-mono/jetbrains-mono-700-vietnamese.woff2 +0 -0
  64. package/newsreader/newsreader-400-latin-ext.woff2 +0 -0
  65. package/newsreader/newsreader-400-latin.woff2 +0 -0
  66. package/newsreader/newsreader-400-vietnamese.woff2 +0 -0
  67. package/newsreader/newsreader-500-latin-ext.woff2 +0 -0
  68. package/newsreader/newsreader-500-latin.woff2 +0 -0
  69. package/newsreader/newsreader-500-vietnamese.woff2 +0 -0
  70. package/newsreader/newsreader-600-latin-ext.woff2 +0 -0
  71. package/newsreader/newsreader-600-latin.woff2 +0 -0
  72. package/newsreader/newsreader-600-vietnamese.woff2 +0 -0
  73. package/package.json +18 -9
  74. package/source-serif-4/source-serif-4-400-cyrillic-ext.woff2 +0 -0
  75. package/source-serif-4/source-serif-4-400-cyrillic.woff2 +0 -0
  76. package/source-serif-4/source-serif-4-400-greek.woff2 +0 -0
  77. package/source-serif-4/source-serif-4-400-latin-ext.woff2 +0 -0
  78. package/source-serif-4/source-serif-4-400-latin.woff2 +0 -0
  79. package/source-serif-4/source-serif-4-400-vietnamese.woff2 +0 -0
  80. package/source-serif-4/source-serif-4-600-cyrillic-ext.woff2 +0 -0
  81. package/source-serif-4/source-serif-4-600-cyrillic.woff2 +0 -0
  82. package/source-serif-4/source-serif-4-600-greek.woff2 +0 -0
  83. package/source-serif-4/source-serif-4-600-latin-ext.woff2 +0 -0
  84. package/source-serif-4/source-serif-4-600-latin.woff2 +0 -0
  85. package/source-serif-4/source-serif-4-600-vietnamese.woff2 +0 -0
  86. package/src/lib/styles/components.css +12 -12
  87. package/src/lib/styles/dialogs.css +2 -2
  88. package/src/lib/styles/fonts.css +462 -138
  89. package/src/lib/styles/tailwind.css +1 -2
  90. package/src/lib/styles/theme.css +596 -69
  91. package/src/lib/styles/tokens.css +56 -19
  92. package/src/lib/styles/utilities.css +4 -1
  93. package/src/lib/ui/components/bar-list/bar-list.component.html +29 -0
  94. package/src/lib/ui/components/button/button.component.html +20 -18
  95. package/src/lib/ui/components/checkbox/checkbox.component.html +3 -3
  96. package/src/lib/ui/components/circle-progress-bar/circle-progress-bar.component.html +2 -2
  97. package/src/lib/ui/components/code-block/code-block.component.html +8 -0
  98. package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +49 -27
  99. package/src/lib/ui/components/datetime-picker/datetime-picker.component.scss +25 -137
  100. package/src/lib/ui/components/dnd-upload/dnd-upload.component.html +3 -3
  101. package/src/lib/ui/components/drawer/drawer.component.html +36 -13
  102. package/src/lib/ui/components/drawer/drawer.component.scss +104 -18
  103. package/src/lib/ui/components/empty-state/empty-state.component.html +2 -2
  104. package/src/lib/ui/components/facets-rail/facets-rail.component.html +42 -0
  105. package/src/lib/ui/components/filter-pills/filter-pills.component.html +16 -0
  106. package/src/lib/ui/components/hero-icon/hero-icon.component.html +10 -9
  107. package/src/lib/ui/components/info-field/info-field.component.html +1 -1
  108. package/src/lib/ui/components/input/input.component.html +6 -6
  109. package/src/lib/ui/components/kanban-board/kanban-board.component.html +1 -1
  110. package/src/lib/ui/components/kanban-board/kanban-board.component.scss +9 -9
  111. package/src/lib/ui/components/list-row/list-row.component.html +20 -0
  112. package/src/lib/ui/components/loading-panel/loading-panel.component.html +1 -1
  113. package/src/lib/ui/components/master-detail/master-detail.component.css +22 -0
  114. package/src/lib/ui/components/master-detail/master-detail.component.html +42 -10
  115. package/src/lib/ui/components/menu/menu.component.html +27 -0
  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 +4 -4
  120. package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +4 -4
  121. package/src/lib/ui/components/sample-frame/sample-frame.component.html +18 -0
  122. package/src/lib/ui/components/score-badge/score-badge.component.html +7 -0
  123. package/src/lib/ui/components/screenshot-frame/screenshot-frame.component.html +40 -0
  124. package/src/lib/ui/components/search-bar/search-bar.component.html +1 -1
  125. package/src/lib/ui/components/section-card/section-card.component.html +19 -6
  126. package/src/lib/ui/components/segmented/segmented.component.html +42 -0
  127. package/src/lib/ui/components/segmented/segmented.component.scss +12 -0
  128. package/src/lib/ui/components/select/select.component.html +6 -6
  129. package/src/lib/ui/components/skeleton-detail/skeleton-detail.component.html +2 -2
  130. package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +1 -1
  131. package/src/lib/ui/components/skeleton-table-loading/skeleton-table-loading.component.html +5 -5
  132. package/src/lib/ui/components/slider/slider.component.html +8 -8
  133. package/src/lib/ui/components/sparkbar/sparkbar.component.html +14 -0
  134. package/src/lib/ui/components/stage-dots/stage-dots.component.html +13 -0
  135. package/src/lib/ui/components/stat-cards/stat-cards.component.html +45 -12
  136. package/src/lib/ui/components/status-dot/status-dot.component.html +13 -0
  137. package/src/lib/ui/components/switch/switch.component.html +1 -1
  138. package/src/lib/ui/components/tab-group/tab-group.component.html +41 -12
  139. package/src/lib/ui/components/tab-group/tab-group.component.scss +41 -1
  140. package/src/lib/ui/components/table-cell/table-cell.component.html +2 -2
  141. package/src/lib/ui/components/tag/tag.component.html +4 -4
  142. package/src/lib/ui/components/theme-picker/theme-picker.component.html +44 -0
  143. package/src/lib/ui/components/toast/toast.component.html +7 -7
  144. package/src/lib/ui/components/toast-container/toast-container.component.html +1 -0
  145. package/src/lib/ui/components/tree-diagram/tree-diagram.component.html +5 -5
  146. package/tailwind.css +1 -2
  147. package/theme.css +596 -69
  148. package/tokens.css +56 -19
  149. package/types/foxeltech-angular-ui-chart.d.ts +65 -0
  150. package/types/foxeltech-angular-ui-flow.d.ts +96 -0
  151. package/types/foxeltech-angular-ui-rich-text.d.ts +35 -0
  152. package/types/foxeltech-angular-ui.d.ts +1018 -395
  153. package/utilities.css +4 -1
  154. package/BeVietnamPro/BeVietnamPro-Bold.woff2 +0 -0
  155. package/BeVietnamPro/BeVietnamPro-ExtraLight.woff2 +0 -0
  156. package/BeVietnamPro/BeVietnamPro-Light.woff2 +0 -0
  157. package/BeVietnamPro/BeVietnamPro-Medium.woff2 +0 -0
  158. package/BeVietnamPro/BeVietnamPro-Regular.woff2 +0 -0
  159. package/BeVietnamPro/BeVietnamPro-SemiBold.woff2 +0 -0
  160. package/BeVietnamPro/BeVietnamPro-Thin.woff2 +0 -0
  161. package/SpaceMono-Regular.woff2 +0 -0
  162. package/dm-sans/DMSans-Bold.woff2 +0 -0
  163. package/dm-sans/DMSans-Light.woff2 +0 -0
  164. package/dm-sans/DMSans-Medium.woff2 +0 -0
  165. package/dm-sans/DMSans-Regular.woff2 +0 -0
  166. package/dm-sans/DMSans-SemiBold.woff2 +0 -0
  167. package/dm-sans/DMSans-Thin.woff2 +0 -0
  168. package/orbitron/Orbitron-Bold.woff2 +0 -0
  169. package/orbitron/Orbitron-Medium.woff2 +0 -0
  170. package/orbitron/Orbitron-Regular.woff2 +0 -0
  171. package/orbitron/Orbitron-SemiBold.woff2 +0 -0
  172. package/roboto/Roboto-Black.woff2 +0 -0
  173. package/roboto/Roboto-BlackItalic.woff2 +0 -0
  174. package/roboto/Roboto-Bold.woff2 +0 -0
  175. package/roboto/Roboto-BoldCondensed.woff2 +0 -0
  176. package/roboto/Roboto-BoldCondensedItalic.woff2 +0 -0
  177. package/roboto/Roboto-BoldItalic.woff2 +0 -0
  178. package/roboto/Roboto-Condensed.woff2 +0 -0
  179. package/roboto/Roboto-CondensedItalic.woff2 +0 -0
  180. package/roboto/Roboto-Italic.woff2 +0 -0
  181. package/roboto/Roboto-Light.woff2 +0 -0
  182. package/roboto/Roboto-LightItalic.woff2 +0 -0
  183. package/roboto/Roboto-Medium.woff2 +0 -0
  184. package/roboto/Roboto-MediumItalic.woff2 +0 -0
  185. package/roboto/Roboto-Regular.woff2 +0 -0
  186. package/roboto/Roboto-Thin.woff2 +0 -0
  187. package/roboto/Roboto-ThinItalic.woff2 +0 -0
  188. package/src/lib/ui/components/chart/chart.component.html +0 -20
  189. package/src/lib/ui/components/chart/chart.component.scss +0 -49
  190. package/src/lib/ui/components/flow-connection/flow-connection.component.html +0 -10
  191. package/src/lib/ui/components/flow-connection/flow-connection.component.scss +0 -0
  192. package/src/lib/ui/components/rich-text-area/rich-text-area.component.html +0 -3
  193. package/src/lib/ui/components/rich-text-area/rich-text-area.component.scss +0 -18
@@ -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,15 @@
54
122
  }
55
123
  }
56
124
 
125
+ .drawer-section-label {
126
+ @apply px-large pt-4 pb-1 text-[11px] font-semibold uppercase text-faint select-none;
127
+ letter-spacing: var(--og-tracking-label);
128
+ }
129
+
130
+ .drawer-section-rule {
131
+ @apply mx-3 my-3 border-t border-border-default;
132
+ }
133
+
57
134
  .drawer-item-wrapper {
58
135
  @apply relative;
59
136
  }
@@ -62,15 +139,35 @@
62
139
  @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
140
  transition: all 200ms ease;
64
141
 
142
+ /* Hover = glass mờ hơn: chỉ translucent, chưa có viền/shadow, để không
143
+ nhầm với hàng đang chọn. */
65
144
  &:hover {
66
- @apply bg-bg-hover text-text-primary scale-95;
145
+ @apply text-text-primary scale-95;
146
+ background: rgb(var(--og-surface-raised) / 0.5);
67
147
  }
68
148
 
149
+ /* Hàng đang chọn = GLASS, KHÔNG dùng màu primary (anh Tú chốt 31/08):
150
+ một tấm surface trong suốt nổi trên nền secondary của drawer + viền
151
+ hairline + shadow nhẹ. Chữ/icon giữ màu text thường.
152
+ - surface-RAISED (không phải surface) vì ở charcoal raised (#211c1b)
153
+ sáng hơn page-bg (#0e0e10) nên tấm kính vẫn nổi; ở porcelain cả hai
154
+ đều là trắng nên không khác gì.
155
+ - Viền vẽ bằng inset box-shadow chứ không phải `border`: .drawer-item
156
+ khai border-none, thêm border thật sẽ đẩy layout 1px.
157
+ - backdrop-blur chỉ có tác dụng khi nền sau có hoạ tiết/gradient; nền
158
+ phẳng thì hiệu ứng đến từ độ trong + viền + shadow. */
69
159
  &.active {
70
- @apply bg-gradient-primary text-white font-medium;
160
+ @apply text-text-primary font-medium;
161
+ background: rgb(var(--og-surface-raised) / 0.8);
162
+ backdrop-filter: blur(12px);
163
+ /* shadow viết literal chứ không dùng var(--shadow-sm): biến đó do @theme
164
+ của Tailwind sinh, không chắc có mặt trong scope của component SCSS. */
165
+ box-shadow:
166
+ inset 0 0 0 1px rgb(var(--og-border)),
167
+ 0 1px 2px 0 rgb(var(--shadow-color) / 0.05);
71
168
 
72
- .item-icon {
73
- @apply text-white;
169
+ &:hover {
170
+ background: rgb(var(--og-surface-raised) / 0.95);
74
171
  }
75
172
  }
76
173
 
@@ -114,21 +211,10 @@
114
211
 
115
212
  .drawer-version {
116
213
  @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;
214
+ font-family: var(--og-font-mono);
118
215
  }
119
216
 
120
217
  .drawer-version-dot {
121
218
  @apply block w-1.5 h-1.5 rounded-full bg-text-placeholder;
122
219
  }
123
220
 
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
- }
@@ -1,9 +1,9 @@
1
1
  <div
2
2
  class="flex flex-col items-center justify-center gap-small w-full"
3
- [style.minHeight]="height"
3
+ [style.minHeight]="height()"
4
4
  >
5
5
  @if (icon) {
6
6
  <fx-ui-hero-icon class="text-text-placeholder" [icon]="icon" [size]="24"></fx-ui-hero-icon>
7
7
  }
8
- <div class="txt-default text-text-placeholder">{{ message }}</div>
8
+ <div class="txt-default text-text-placeholder">{{ message() }}</div>
9
9
  </div>
@@ -0,0 +1,42 @@
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
10
+ class="txt-table-header uppercase mb-small"
11
+ [style.letter-spacing]="'var(--og-tracking-label)'"
12
+ >
13
+ {{ group.label }}
14
+ </div>
15
+ <div class="flex flex-col gap-xs">
16
+ @for (option of group.options; track option.value) {
17
+ <button
18
+ type="button"
19
+ (click)="toggle(group, option)"
20
+ [attr.aria-pressed]="isSelected(group, option)"
21
+ class="flex items-center gap-normal px-normal py-small rounded-control text-left transition-colors"
22
+ [ngClass]="
23
+ isSelected(group, option)
24
+ ? 'bg-accent-soft text-accent-soft-fg font-medium'
25
+ : 'text-body hover:bg-surface-sunken'
26
+ "
27
+ >
28
+ @if (option.tone) {
29
+ <fx-ui-status-dot [tone]="option.tone" [size]="7" />
30
+ }
31
+ <span class="text-sm truncate flex-1">{{ option.label }}</span>
32
+ @if (option.count !== undefined) {
33
+ <span class="txt-tag-label text-faint" style="font-family: var(--og-font-figure)">
34
+ {{ option.count }}
35
+ </span>
36
+ }
37
+ </button>
38
+ }
39
+ </div>
40
+ </div>
41
+ }
42
+ </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>
@@ -1,9 +1,10 @@
1
- <ng-heroicons
2
- [stroke]="stroke"
3
- [icon]="icon"
4
- [size]="size"
5
- [solid]="solid"
6
- [outline]="outline"
7
- [color]="color"
8
- [class]="class"
9
- />
1
+ <svg
2
+ [attr.width]="size() || 24"
3
+ [attr.height]="size() || 24"
4
+ [attr.stroke-width]="solid() ? null : stroke()"
5
+ [style.color]="color() || null"
6
+ [class]="class()"
7
+ aria-hidden="true"
8
+ >
9
+ <use [attr.href]="href" />
10
+ </svg>
@@ -1,4 +1,4 @@
1
1
  <div class="flex flex-col gap-small min-w-0">
2
- <span class="txt-field-label !mb-0">{{ label }}</span>
2
+ <span class="txt-field-label !mb-0">{{ label() }}</span>
3
3
  <span class="txt-default" [ngClass]="valueClass">{{ displayValue }}</span>
4
4
  </div>
@@ -1,8 +1,8 @@
1
- <div class="mb-4 text-left w-full text-text-primary" [class]="class">
1
+ <div class="mb-4 text-left w-full text-text-primary" [class]="class()">
2
2
  @if (label) {
3
3
  <label class="block txt-field-label" [attr.for]="fieldId">
4
4
  {{ label }}
5
- @if (required) {
5
+ @if (required()) {
6
6
  <span class="txt-required" aria-hidden="true">*</span>
7
7
  }
8
8
  @if (tooltip) {
@@ -24,16 +24,16 @@
24
24
  [appTrimOnBlurCollapse]="true"
25
25
  [id]="fieldId"
26
26
  [type]="displayType"
27
- [placeholder]="placeholder"
27
+ [placeholder]="placeholder()"
28
28
  [disabled]="disabled"
29
29
  [value]="value"
30
30
  (input)="onInput($event)"
31
31
  (blur)="onBlur()"
32
32
  (focus)="onFocus()"
33
- [attr.maxlength]="maxlength"
33
+ [attr.maxlength]="maxlength()"
34
34
  [attr.aria-invalid]="invalid || null"
35
35
  [attr.aria-describedby]="invalid ? fieldId + '-error' : null"
36
- [attr.aria-required]="required ? true : null"
36
+ [attr.aria-required]="required() ? true : null"
37
37
  [class]="type === 'otp' ? 'input-otp' : 'input-default'"
38
38
  [class.input-invalid]="invalid"
39
39
  [class.pr-8]="type === 'password' || suffixIcon"
@@ -48,7 +48,7 @@
48
48
  [attr.aria-label]="
49
49
  type === 'password'
50
50
  ? (showPassword ? 'Hide password' : 'Show password')
51
- : (suffixAriaLabel || null)
51
+ : (suffixAriaLabel() || null)
52
52
  "
53
53
  [attr.aria-pressed]="type === 'password' ? showPassword : null"
54
54
  >
@@ -1,6 +1,6 @@
1
1
  <div class="kanban-board">
2
2
  <div class="kanban-columns">
3
- @for (column of columns; track column.id) {
3
+ @for (column of columns(); track column.id) {
4
4
  <div class="kanban-column">
5
5
  <div class="kanban-column-header">
6
6
  <h3 class="kanban-column-title">{{ column.title }}</h3>
@@ -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>
@@ -1,5 +1,5 @@
1
1
  <!-- Full-screen glass overlay -->
2
- @if (show) {
2
+ @if (show()) {
3
3
  <div
4
4
  class="fixed inset-0 z-50 flex items-center justify-center"
5
5
  aria-hidden="false"
@@ -9,3 +9,25 @@
9
9
  flex-shrink: 0;
10
10
  }
11
11
  }
12
+
13
+ /* Facets rail: full-width when stacked; fixed narrow column on lg+. */
14
+ .fx-md-facets {
15
+ width: 100%;
16
+ }
17
+
18
+ @media (min-width: 1080px) {
19
+ .fx-md-facets {
20
+ width: 230px;
21
+ flex-shrink: 0;
22
+ }
23
+ }
24
+
25
+ /* prevnext style: the Material paginator stays as the paging engine but is
26
+ visually removed (display:none would break its measurements on re-show). */
27
+ .fx-md-paginator-hidden {
28
+ position: absolute !important;
29
+ width: 1px;
30
+ height: 1px;
31
+ overflow: hidden;
32
+ clip-path: inset(50%);
33
+ }
@@ -1,13 +1,19 @@
1
- <div class="flex flex-col lg:flex-row items-start gap-xl" [style.--fx-md-list-width]="listWidth">
1
+ <div class="flex flex-col lg:flex-row items-start gap-xl" [style.--fx-md-list-width]="listWidth()">
2
+ <!-- Facets rail (V2 3-column lookup) — wrapper always rendered so ng-content
3
+ stays unconditional; hidden when the host projects nothing. -->
4
+ <div class="fx-md-facets card-common bg-surface" [class.hidden]="!hasFacets">
5
+ <ng-content select="[mdFacets]"></ng-content>
6
+ </div>
7
+
2
8
  <!-- Master: list pane -->
3
9
  <div class="fx-md-list card-common">
4
10
  <ng-content select="[mdHeader]"></ng-content>
5
11
  <ng-content select="[mdFilters]"></ng-content>
6
12
  <div
7
13
  class="flex flex-col gap-normal overflow-auto pr-xs"
8
- [style.maxHeight]="maxListHeight"
14
+ [style.maxHeight]="maxListHeight()"
9
15
  role="listbox"
10
- [attr.aria-label]="listLabel || null"
16
+ [attr.aria-label]="listLabel() || null"
11
17
  >
12
18
  @if (table.loading) {
13
19
  <fx-ui-skeleton-list [rows]="table.pageSize"></fx-ui-skeleton-list>
@@ -24,18 +30,18 @@
24
30
  class="text-left rounded-lg border p-semi transition-colors"
25
31
  [ngClass]="
26
32
  isSelected(row)
27
- ? 'border-gradient-primary-start bg-gradient-primary-start/5'
33
+ ? 'border-accent bg-accent-soft/60'
28
34
  : 'border-border-default hover:border-border-interactive bg-bg-primary'
29
35
  "
30
36
  >
31
37
  <ng-container
32
- [ngTemplateOutlet]="rowTemplate"
38
+ [ngTemplateOutlet]="rowTemplate()"
33
39
  [ngTemplateOutletContext]="{ $implicit: row, selected: isSelected(row) }"
34
40
  ></ng-container>
35
41
  </button>
36
42
  }
37
43
  @if (!table.dataSource.data.length) {
38
- <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
44
+ <fx-ui-empty-state [message]="emptyMessage()"></fx-ui-empty-state>
39
45
  }
40
46
  }
41
47
  </div>
@@ -44,26 +50,52 @@
44
50
  wires page events — a binding here would double-fetch every change. -->
45
51
  <mat-paginator
46
52
  class="mt-normal"
53
+ [class.fx-md-paginator-hidden]="paginationStyle() === 'prevnext'"
47
54
  [length]="table.total"
48
55
  [pageSize]="table.pageSize"
49
- [pageSizeOptions]="pageSizeOptions"
56
+ [pageSizeOptions]="pageSizeOptions()"
50
57
  ></mat-paginator>
58
+ @if (paginationStyle() === 'prevnext') {
59
+ <div class="flex items-center justify-between mt-normal">
60
+ <span class="txt-utility-description" style="font-family: var(--og-font-figure)">
61
+ {{ rangeText() }}
62
+ </span>
63
+ <div class="flex gap-small">
64
+ <button
65
+ type="button"
66
+ class="btn-alternative !h-8 !px-semi text-sm"
67
+ [disabled]="!paginator().hasPreviousPage()"
68
+ (click)="paginator().previousPage()"
69
+ >
70
+ {{ prevLabel() }}
71
+ </button>
72
+ <button
73
+ type="button"
74
+ class="btn-alternative !h-8 !px-semi text-sm"
75
+ [disabled]="!paginator().hasNextPage()"
76
+ (click)="paginator().nextPage()"
77
+ >
78
+ {{ nextLabel() }}
79
+ </button>
80
+ </div>
81
+ </div>
82
+ }
51
83
  </div>
52
84
 
53
85
  <!-- Detail pane -->
54
86
  <div class="w-full lg:flex-1 min-w-0">
55
87
  @if (table.selected) {
56
88
  @if (table.detailBusy) {
57
- <fx-ui-skeleton-detail [cards]="skeletonCards"></fx-ui-skeleton-detail>
89
+ <fx-ui-skeleton-detail [cards]="skeletonCards()"></fx-ui-skeleton-detail>
58
90
  } @else {
59
91
  <ng-container
60
- [ngTemplateOutlet]="detailTemplate"
92
+ [ngTemplateOutlet]="detailTemplate()"
61
93
  [ngTemplateOutletContext]="{ $implicit: table.detail, selected: table.selected }"
62
94
  ></ng-container>
63
95
  }
64
96
  } @else {
65
97
  <div class="card-common flex items-center justify-center h-[300px]">
66
- <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
98
+ <fx-ui-empty-state [message]="emptyMessage()"></fx-ui-empty-state>
67
99
  </div>
68
100
  }
69
101
  </div>
@@ -0,0 +1,27 @@
1
+ <button
2
+ type="button"
3
+ class="inline-flex items-center justify-center rounded-lg p-1 hover:bg-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
4
+ [matMenuTriggerFor]="menu"
5
+ [disabled]="disabled()"
6
+ [attr.aria-label]="ariaLabel()"
7
+ >
8
+ <ng-content></ng-content>
9
+ </button>
10
+
11
+ <mat-menu #menu="matMenu">
12
+ @for (item of items(); track item.label) {
13
+ <button
14
+ mat-menu-item
15
+ type="button"
16
+ [disabled]="item.disabled"
17
+ (click)="select.emit(item)"
18
+ >
19
+ <span class="flex items-center gap-normal" [class]="toneClass(item)">
20
+ @if (item.icon) {
21
+ <fx-ui-hero-icon [icon]="item.icon" [size]="18"></fx-ui-hero-icon>
22
+ }
23
+ <span>{{ item.label }}</span>
24
+ </span>
25
+ </button>
26
+ }
27
+ </mat-menu>
@@ -0,0 +1,15 @@
1
+ @if (variant() === 'dot') {
2
+ <span
3
+ class="inline-block w-[7px] h-[7px] rounded-full bg-warning/70 align-middle"
4
+ [attr.title]="tooltip"
5
+ [attr.aria-label]="tooltip"
6
+ ></span>
7
+ } @else {
8
+ <span
9
+ class="inline-flex items-center gap-xs px-small py-[1px] rounded-pill border border-dashed border-warning/60 bg-warning/10 text-warning txt-tag-label whitespace-nowrap align-middle"
10
+ [attr.title]="tooltip"
11
+ >
12
+ <fx-ui-hero-icon icon="clock" [size]="11" class="flex" />
13
+ {{ label() }}
14
+ </span>
15
+ }
@@ -0,0 +1,45 @@
1
+ <ol class="flex items-start overflow-x-auto pb-small" role="list">
2
+ @for (stage of stages(); track stage.id; let i = $index; let last = $last) {
3
+ <li class="flex items-start" [class.flex-1]="!last" [attr.aria-current]="stateOf(stage) === 'active' ? 'step' : null">
4
+ <button
5
+ type="button"
6
+ class="flex flex-col items-center gap-xs min-w-20 px-xs"
7
+ [class.cursor-pointer]="canClick(stage)"
8
+ [class.cursor-default]="!canClick(stage)"
9
+ [tabindex]="canClick(stage) ? 0 : -1"
10
+ (click)="onStageClick(stage, i)"
11
+ >
12
+ <span
13
+ class="w-8 h-8 rounded-full border-2 flex items-center justify-center text-sm font-semibold transition-colors"
14
+ [ngClass]="circleClass(stage)"
15
+ >
16
+ @switch (stateOf(stage)) {
17
+ @case ('done') {
18
+ <fx-ui-hero-icon icon="check" [size]="16" class="flex" />
19
+ }
20
+ @case ('error') {
21
+ <fx-ui-hero-icon icon="exclamation-triangle" [size]="16" class="flex" />
22
+ }
23
+ @default {
24
+ {{ i + 1 }}
25
+ }
26
+ }
27
+ </span>
28
+ <span class="text-xs text-center whitespace-nowrap" [ngClass]="labelClass(stage)">
29
+ {{ stage.label }}
30
+ </span>
31
+ @if (stage.description) {
32
+ <span class="txt-utility-description text-center whitespace-nowrap">
33
+ {{ stage.description }}
34
+ </span>
35
+ }
36
+ </button>
37
+ @if (!last) {
38
+ <div class="flex-1 h-[2px] mt-4 mx-xs rounded-pill min-w-6" [ngClass]="connectorClass(stage)"></div>
39
+ }
40
+ </li>
41
+ }
42
+ </ol>
43
+ <div class="mt-large">
44
+ <ng-content></ng-content>
45
+ </div>