@bundle-stats/ui 4.22.3 → 4.23.0-beta.2

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 (198) hide show
  1. package/lib/app/app.js +3 -0
  2. package/lib/app/app.js.map +1 -1
  3. package/lib/app/app.module.css +12 -2
  4. package/lib/chart-colors.json +58 -44
  5. package/lib/components/delta/delta.js +3 -13
  6. package/lib/components/delta/delta.js.map +1 -1
  7. package/lib/components/delta/delta.module.css +13 -12
  8. package/lib/components/delta/index.js.map +1 -1
  9. package/lib/components/entry-info/entry-info.js +1 -1
  10. package/lib/components/entry-info/entry-info.js.map +1 -1
  11. package/lib/components/entry-info/entry-info.module.css +1 -1
  12. package/lib/components/index.js +3 -1
  13. package/lib/components/index.js.map +1 -1
  14. package/lib/components/insight-icon/insight-icon.module.css +6 -6
  15. package/lib/components/job-name/job-name.js +1 -2
  16. package/lib/components/job-name/job-name.js.map +1 -1
  17. package/lib/components/metric-change-legend/index.js +18 -0
  18. package/lib/components/metric-change-legend/index.js.map +1 -0
  19. package/lib/components/metric-change-legend/metric-change-legend.js +91 -0
  20. package/lib/components/metric-change-legend/metric-change-legend.js.map +1 -0
  21. package/lib/components/metric-change-legend/metric-change-legend.module.css +80 -0
  22. package/lib/components/metric-run-info/metric-run-info.js +22 -7
  23. package/lib/components/metric-run-info/metric-run-info.js.map +1 -1
  24. package/lib/components/metrics-display-selector/metrics-display-selector.js +7 -9
  25. package/lib/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  26. package/lib/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  27. package/lib/components/metrics-table-header/metrics-table-header.js +6 -6
  28. package/lib/components/metrics-table-header/metrics-table-header.js.map +1 -1
  29. package/lib/components/metrics-table-header/metrics-table-header.module.css +8 -12
  30. package/lib/components/metrics-table-options/metrics-table-options.js +2 -3
  31. package/lib/components/metrics-table-options/metrics-table-options.js.map +1 -1
  32. package/lib/components/metrics-table-title/metrics-table-title.js +1 -1
  33. package/lib/components/metrics-table-title/metrics-table-title.js.map +1 -1
  34. package/lib/components/metrics-treemap/metrics-treemap.constants.js +3 -1
  35. package/lib/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
  36. package/lib/components/metrics-treemap/metrics-treemap.js +68 -57
  37. package/lib/components/metrics-treemap/metrics-treemap.js.map +1 -1
  38. package/lib/components/metrics-treemap/metrics-treemap.module.css +9 -16
  39. package/lib/components/metrics-treemap/metrics-treemap.utils.js +72 -16
  40. package/lib/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  41. package/lib/components/run-info/run-info.js +26 -14
  42. package/lib/components/run-info/run-info.js.map +1 -1
  43. package/lib/components/run-info/run-info.module.css +35 -26
  44. package/lib/components/summary/summary.js +9 -10
  45. package/lib/components/summary/summary.js.map +1 -1
  46. package/lib/components/summary/summary.module.css +60 -10
  47. package/lib/constants.js +13 -1
  48. package/lib/constants.js.map +1 -1
  49. package/lib/css/variables.css +71 -58
  50. package/lib/ui/button/button.js +2 -2
  51. package/lib/ui/button/button.js.map +1 -1
  52. package/lib/ui/dialog/dialog.js +4 -2
  53. package/lib/ui/dialog/dialog.js.map +1 -1
  54. package/lib/ui/dialog/dialog.module.css +22 -0
  55. package/lib/ui/dropdown/dropdown.js +13 -4
  56. package/lib/ui/dropdown/dropdown.js.map +1 -1
  57. package/lib/ui/dropdown/dropdown.module.css +51 -6
  58. package/lib/ui/filters/filters.js +22 -11
  59. package/lib/ui/filters/filters.js.map +1 -1
  60. package/lib/ui/filters/filters.module.css +0 -14
  61. package/lib/ui/hover-card/hover-card.js +7 -1
  62. package/lib/ui/hover-card/hover-card.js.map +1 -1
  63. package/lib/ui/hover-card/hover-card.module.css +14 -0
  64. package/lib/ui/table/table.module.css +0 -1
  65. package/lib/ui/tooltip/tooltip.js +6 -1
  66. package/lib/ui/tooltip/tooltip.js.map +1 -1
  67. package/lib/ui/tooltip/tooltip.module.css +11 -0
  68. package/lib-esm/app/app.js +4 -1
  69. package/lib-esm/app/app.js.map +1 -1
  70. package/lib-esm/app/app.module.css +12 -2
  71. package/lib-esm/chart-colors.json +58 -44
  72. package/lib-esm/components/delta/delta.js +3 -13
  73. package/lib-esm/components/delta/delta.js.map +1 -1
  74. package/lib-esm/components/delta/delta.module.css +13 -12
  75. package/lib-esm/components/delta/index.js.map +1 -1
  76. package/lib-esm/components/entry-info/entry-info.js +1 -1
  77. package/lib-esm/components/entry-info/entry-info.js.map +1 -1
  78. package/lib-esm/components/entry-info/entry-info.module.css +1 -1
  79. package/lib-esm/components/index.js +1 -0
  80. package/lib-esm/components/index.js.map +1 -1
  81. package/lib-esm/components/insight-icon/insight-icon.module.css +6 -6
  82. package/lib-esm/components/job-name/job-name.js +1 -2
  83. package/lib-esm/components/job-name/job-name.js.map +1 -1
  84. package/lib-esm/components/metric-change-legend/index.js +2 -0
  85. package/lib-esm/components/metric-change-legend/index.js.map +1 -0
  86. package/lib-esm/components/metric-change-legend/metric-change-legend.js +84 -0
  87. package/lib-esm/components/metric-change-legend/metric-change-legend.js.map +1 -0
  88. package/lib-esm/components/metric-change-legend/metric-change-legend.module.css +80 -0
  89. package/lib-esm/components/metric-run-info/metric-run-info.js +22 -7
  90. package/lib-esm/components/metric-run-info/metric-run-info.js.map +1 -1
  91. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js +7 -9
  92. package/lib-esm/components/metrics-display-selector/metrics-display-selector.js.map +1 -1
  93. package/lib-esm/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  94. package/lib-esm/components/metrics-table-header/metrics-table-header.js +6 -6
  95. package/lib-esm/components/metrics-table-header/metrics-table-header.js.map +1 -1
  96. package/lib-esm/components/metrics-table-header/metrics-table-header.module.css +8 -12
  97. package/lib-esm/components/metrics-table-options/metrics-table-options.js +3 -4
  98. package/lib-esm/components/metrics-table-options/metrics-table-options.js.map +1 -1
  99. package/lib-esm/components/metrics-table-title/metrics-table-title.js +1 -1
  100. package/lib-esm/components/metrics-table-title/metrics-table-title.js.map +1 -1
  101. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js +2 -0
  102. package/lib-esm/components/metrics-treemap/metrics-treemap.constants.js.map +1 -1
  103. package/lib-esm/components/metrics-treemap/metrics-treemap.js +74 -63
  104. package/lib-esm/components/metrics-treemap/metrics-treemap.js.map +1 -1
  105. package/lib-esm/components/metrics-treemap/metrics-treemap.module.css +9 -16
  106. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js +71 -17
  107. package/lib-esm/components/metrics-treemap/metrics-treemap.utils.js.map +1 -1
  108. package/lib-esm/components/run-info/run-info.js +26 -14
  109. package/lib-esm/components/run-info/run-info.js.map +1 -1
  110. package/lib-esm/components/run-info/run-info.module.css +35 -26
  111. package/lib-esm/components/summary/summary.js +10 -11
  112. package/lib-esm/components/summary/summary.js.map +1 -1
  113. package/lib-esm/components/summary/summary.module.css +60 -10
  114. package/lib-esm/constants.js +12 -0
  115. package/lib-esm/constants.js.map +1 -1
  116. package/lib-esm/css/variables.css +71 -58
  117. package/lib-esm/ui/button/button.js +2 -2
  118. package/lib-esm/ui/button/button.js.map +1 -1
  119. package/lib-esm/ui/dialog/dialog.js +4 -2
  120. package/lib-esm/ui/dialog/dialog.js.map +1 -1
  121. package/lib-esm/ui/dialog/dialog.module.css +22 -0
  122. package/lib-esm/ui/dropdown/dropdown.js +11 -3
  123. package/lib-esm/ui/dropdown/dropdown.js.map +1 -1
  124. package/lib-esm/ui/dropdown/dropdown.module.css +51 -6
  125. package/lib-esm/ui/filters/filters.js +23 -12
  126. package/lib-esm/ui/filters/filters.js.map +1 -1
  127. package/lib-esm/ui/filters/filters.module.css +0 -14
  128. package/lib-esm/ui/hover-card/hover-card.js +7 -1
  129. package/lib-esm/ui/hover-card/hover-card.js.map +1 -1
  130. package/lib-esm/ui/hover-card/hover-card.module.css +14 -0
  131. package/lib-esm/ui/table/table.module.css +0 -1
  132. package/lib-esm/ui/tooltip/tooltip.js +6 -1
  133. package/lib-esm/ui/tooltip/tooltip.js.map +1 -1
  134. package/lib-esm/ui/tooltip/tooltip.module.css +11 -0
  135. package/package.json +16 -16
  136. package/src/app/app.jsx +6 -1
  137. package/src/app/app.module.css +12 -2
  138. package/src/chart-colors.json +58 -44
  139. package/src/components/delta/delta.module.css +13 -12
  140. package/src/components/delta/delta.tsx +32 -0
  141. package/src/components/entry-info/entry-info.module.css +1 -1
  142. package/src/components/entry-info/entry-info.tsx +2 -2
  143. package/src/components/index.ts +3 -0
  144. package/src/components/insight-icon/insight-icon.module.css +6 -6
  145. package/src/components/job-name/job-name.jsx +2 -3
  146. package/src/components/metric-change-legend/index.ts +1 -0
  147. package/src/components/metric-change-legend/metric-change-legend.module.css +80 -0
  148. package/src/components/metric-change-legend/metric-change-legend.stories.tsx +13 -0
  149. package/src/components/metric-change-legend/metric-change-legend.tsx +148 -0
  150. package/src/components/metric-run-info/metric-run-info.tsx +32 -17
  151. package/src/components/metrics-display-selector/metrics-display-selector.module.css +0 -25
  152. package/src/components/metrics-display-selector/metrics-display-selector.tsx +25 -37
  153. package/src/components/metrics-table-header/metrics-table-header.module.css +8 -12
  154. package/src/components/metrics-table-header/metrics-table-header.tsx +5 -6
  155. package/src/components/metrics-table-options/metrics-table-options.tsx +5 -6
  156. package/src/components/metrics-table-title/metrics-table-title.jsx +1 -1
  157. package/src/components/metrics-treemap/metrics-treemap.constants.ts +8 -0
  158. package/src/components/metrics-treemap/metrics-treemap.module.css +9 -16
  159. package/src/components/metrics-treemap/metrics-treemap.tsx +160 -135
  160. package/src/components/metrics-treemap/metrics-treemap.utils.ts +141 -27
  161. package/src/components/run-info/run-info.module.css +35 -26
  162. package/src/components/run-info/run-info.stories.tsx +16 -10
  163. package/src/components/run-info/run-info.tsx +132 -73
  164. package/src/components/summary/summary.module.css +60 -10
  165. package/src/components/summary/summary.tsx +47 -50
  166. package/src/constants.ts +13 -0
  167. package/src/css/variables.css +71 -58
  168. package/src/ui/button/button.tsx +2 -1
  169. package/src/ui/dialog/dialog.module.css +22 -0
  170. package/src/ui/dialog/dialog.tsx +9 -2
  171. package/src/ui/dropdown/dropdown.module.css +51 -6
  172. package/src/ui/dropdown/dropdown.tsx +17 -2
  173. package/src/ui/filters/filters.module.css +0 -14
  174. package/src/ui/filters/filters.tsx +111 -47
  175. package/src/ui/hover-card/hover-card.module.css +14 -0
  176. package/src/ui/hover-card/hover-card.tsx +7 -1
  177. package/src/ui/table/table.module.css +0 -1
  178. package/src/ui/tooltip/tooltip.module.css +11 -0
  179. package/src/ui/tooltip/tooltip.tsx +6 -1
  180. package/types/components/delta/delta.d.ts +7 -21
  181. package/types/components/delta/index.d.ts +1 -1
  182. package/types/components/index.d.ts +3 -0
  183. package/types/components/metric-change-legend/index.d.ts +1 -0
  184. package/types/components/metric-change-legend/metric-change-legend.d.ts +4 -0
  185. package/types/components/metric-run-info/metric-run-info.d.ts +3 -6
  186. package/types/components/metrics-treemap/metrics-treemap.constants.d.ts +2 -0
  187. package/types/components/metrics-treemap/metrics-treemap.utils.d.ts +11 -5
  188. package/types/components/run-info/run-info.d.ts +16 -7
  189. package/types/constants.d.ts +12 -0
  190. package/types/ui/dialog/dialog.d.ts +3 -2
  191. package/types/ui/dropdown/dropdown.d.ts +4 -1
  192. package/types/ui/filters/filters.d.ts +2 -1
  193. package/vitest.config.ts +4 -0
  194. package/lib/components/metrics-table-options/metrics-table-options.module.css +0 -15
  195. package/lib-esm/components/metrics-table-options/metrics-table-options.module.css +0 -15
  196. package/src/components/delta/delta.jsx +0 -22
  197. package/src/components/metrics-table-options/metrics-table-options.module.css +0 -15
  198. /package/src/components/delta/{index.js → index.ts} +0 -0
@@ -32,22 +32,21 @@
32
32
  --line-height-number: 1.2em;
33
33
 
34
34
  /*
35
- * Color scheme:
36
- * https://coolors.co/287acc-da444e-55b950-efcc1a-192339
37
- * http://mcg.mbitson.com/#!?mcgpalette0=%23da444e&mcgpalette1=%2355b950&mcgpalette2=%23ebc013&mcgpalette3=%232a2933&mcgpalette4=%23287acc&themename=bundle-stats
38
- * https://hihayk.github.io/scale/#4/5/50/90/-51/0/0/14/353138/53/49/56/white
35
+ * Color scheme: https://coolors.co/287acc-da444e-55b950-e5c110-192339-353538
39
36
  */
40
- --color-gray-25: #f9f9f9;
41
- --color-gray-50: #e5e5e7;
42
- --color-gray-100: #d2d2d4;
43
- --color-gray-200: #959499;
44
- --color-gray-300: #6a6970;
45
- --color-gray-400: #4a4952;
46
- --color-gray-500: #2a2933;
47
- --color-gray-600: #25242e;
48
- --color-gray-700: #1f1f27;
49
- --color-gray-800: #191920;
50
- --color-gray-900: #0f0f14;
37
+
38
+ --color-gray-25: #f9f9f9;
39
+ /** https://hihayk.github.io/scale/#4/5/60/90/0/-0/0/0/3F3F47/63/63/71/white */
40
+ --color-gray-50: #ececed;
41
+ --color-gray-100: #c9c9cc;
42
+ --color-gray-200: #a7a7aa;
43
+ --color-gray-300: #848489;
44
+ --color-gray-400: #626268;
45
+ --color-gray-500: #3f3f47;
46
+ --color-gray-600: #36363c;
47
+ --color-gray-700: #2c2c32;
48
+ --color-gray-800: #232327;
49
+ --color-gray-900: #19191c;
51
50
 
52
51
  --color-gray-ultra-light: var(--color-gray-200);
53
52
  --color-gray-light: var(--color-gray-300);
@@ -56,16 +55,17 @@
56
55
  --color-gray-ultra-dark: var(--color-gray-900);
57
56
 
58
57
  --color-red-25: #fdf5f6;
59
- --color-red-50: #fbe9ea;
60
- --color-red-100: #f4c7ca;
61
- --color-red-200: #eda2a7;
62
- --color-red-300: #e57c83;
63
- --color-red-400: #e06069;
58
+ /* https://hihayk.github.io/scale/#4/5/60/90/0/-0/0/100/DA444E/218/68/78/white */
59
+ --color-red-50: #fbeced;
60
+ --color-red-100: #f5cbcd;
61
+ --color-red-200: #eea9ae;
62
+ --color-red-300: #e7878e;
63
+ --color-red-400: #e1666e;
64
64
  --color-red-500: #da444e;
65
- --color-red-600: #d63e47;
66
- --color-red-700: #d0353d;
67
- --color-red-800: #cb2d35;
68
- --color-red-900: #c21f25;
65
+ --color-red-600: #c92a35;
66
+ --color-red-700: #b31520;
67
+ --color-red-800: #8c0710;
68
+ --color-red-900: #660005;
69
69
  --color-red-ultra-light: var(--color-red-100);
70
70
  --color-red-light: var(--color-red-300);
71
71
  --color-red: var(--color-red-500);
@@ -73,16 +73,17 @@
73
73
  --color-red-ultra-dark: var(--color-red-900);
74
74
 
75
75
  --color-blue-25: #f3f8fd;
76
- --color-blue-50: #e5eff9;
77
- --color-blue-100: #bfd7f0;
78
- --color-blue-200: #94bde6;
79
- --color-blue-300: #69a2db;
80
- --color-blue-400: #488ed4;
76
+ /* https://hihayk.github.io/scale/#4/5/60/90/0/-0/0/100/287ACC/40/122/204/white */
77
+ --color-blue-50: #eaf2fa;
78
+ --color-blue-100: #c3daf1;
79
+ --color-blue-200: #9cc2e8;
80
+ --color-blue-300: #75aade;
81
+ --color-blue-400: #4f92d5;
81
82
  --color-blue-500: #287acc;
82
- --color-blue-600: #2472c7;
83
- --color-blue-700: #1e67c0;
84
- --color-blue-800: #185db9;
85
- --color-blue-900: #0f4aad;
83
+ --color-blue-600: #1168bf;
84
+ --color-blue-700: #0055ac;
85
+ --color-blue-800: #00438c;
86
+ --color-blue-900: #003166;
86
87
 
87
88
  --color-blue-ultra-light: var(--color-blue-100);
88
89
  --color-blue-light: var(--color-blue-300);
@@ -91,16 +92,17 @@
91
92
  --color-blue-ultra-dark: var(--color-blue-900);
92
93
 
93
94
  --color-green-25: #f6fbf6;
94
- --color-green-50: #ebf7ea;
95
- --color-green-100: #cceacb;
96
- --color-green-200: #aadca8;
97
- --color-green-300: #88ce85;
98
- --color-green-400: #6fc46a;
99
- --color-green-500: #55b950;
100
- --color-green-600: #4eb249;
101
- --color-green-700: #44aa40;
102
- --color-green-800: #3ba237;
103
- --color-green-900: #2a9327;
95
+ /* https://hihayk.github.io/scale/#4/5/60/90/0/-0/0/100/55b950/85/185/80/white */
96
+ --color-green-50: #eef8ee;
97
+ --color-green-100: #cfebce;
98
+ --color-green-200: #b1dfaf;
99
+ --color-green-300: #92d28f;
100
+ --color-green-400: #74c670;
101
+ --color-green-500: #55B950;
102
+ --color-green-600: #3ea839;
103
+ --color-green-700: #2b9426;
104
+ --color-green-800: #1b7b16;
105
+ --color-green-900: #0f5f0b;
104
106
 
105
107
  --color-green-ultra-light: var(--color-green-100);
106
108
  --color-green-light: var(--color-green-300);
@@ -109,18 +111,18 @@
109
111
  --color-green-ultra-dark: var(--color-green-900);
110
112
 
111
113
  --color-yellow-25: #fffdf2;
112
- --color-yellow-50: #fdf7e3;
113
- --color-yellow-100: #f9ecb8;
114
- --color-yellow-200: #f5e089;
115
- --color-yellow-300: #f1d35a;
116
- --color-yellow-400: #eec936;
117
- --color-yellow-500: #ebc013;
118
- --color-yellow-600: #e9ba11;
119
- --color-yellow-700: #e5b20e;
120
- --color-yellow-800: #e2aa0b;
121
- --color-yellow-900: #dd9c06;
122
-
123
- --color-yellow-light-background: var(--color-yellow-25);
114
+ /* https://hihayk.github.io/scale/#4/5/40/80/0/-0/0/0/E5C110/229/193/16/white */
115
+ --color-yellow-50: #faf3cf;
116
+ --color-yellow-100: #f6e9a9;
117
+ --color-yellow-200: #f2df83;
118
+ --color-yellow-300: #edd55d;
119
+ --color-yellow-400: #e9cb36;
120
+ --color-yellow-500: #e5c110;
121
+ --color-yellow-600: #ceae0e;
122
+ --color-yellow-700: #b79a0d;
123
+ --color-yellow-800: #a0870b;
124
+ --color-yellow-900: #89740a;
125
+
124
126
  --color-yellow-ultra-light: var(--color-yellow-100);
125
127
  --color-yellow-light: var(--color-yellow-300);
126
128
  --color-yellow: var(--color-yellow-500);
@@ -130,11 +132,13 @@
130
132
  --color-dark: #111;
131
133
  --color-light: #fff;
132
134
 
133
- /* Functional color variables */
134
135
  --color-branding-light: #2b76d4;
135
136
  --color-branding: #1a477f;
136
137
  --color-branding-dark: #09182a;
138
+ }
137
139
 
140
+ body {
141
+ /* Functional color variables */
138
142
  --color-text-ultra-light: var(--color-gray-ultra-light);
139
143
  --color-text-light: var(--color-gray-light);
140
144
  --color-text: var(--color-gray);
@@ -183,8 +187,8 @@
183
187
  --color-backdrop: rgba(0, 0, 0, 0.05);
184
188
  --color-shadow: rgba(0, 0, 0, 0.25);
185
189
 
186
- --transition-duration-in: 0.2s;
187
- --transition-duration-out: 0.17s;
190
+ --transition-duration-in: 0.16s;
191
+ --transition-duration-out: 0.10s;
188
192
  --transition-in: all var(--transition-duration-in) ease-in;
189
193
  --transition-out: all var(--transition-duration-out) ease-out;
190
194
 
@@ -219,3 +223,12 @@
219
223
  --entry-info-width: 64rem;
220
224
  --entry-info-top: var(--header-height);
221
225
  }
226
+
227
+ @media (prefers-reduced-motion: reduce) {
228
+ * {
229
+ animation-duration: 0s !important;
230
+ animation-delay: 0s !important;
231
+ transition-duration: 0s !important;
232
+ transition-delay: 0s !important;
233
+ }
234
+ }
@@ -41,6 +41,7 @@ interface ButtonProps<T extends React.ElementType> {
41
41
 
42
42
  const ButtonComponent = <T extends React.ElementType = 'button'>(
43
43
  props: ButtonProps<T> & Omit<React.ComponentPropsWithoutRef<T>, keyof ButtonProps<T>>,
44
+ ref: React.Ref<HTMLButtonElement>,
44
45
  ) => {
45
46
  const {
46
47
  className = '',
@@ -86,7 +87,7 @@ const ButtonComponent = <T extends React.ElementType = 'button'>(
86
87
  );
87
88
 
88
89
  return (
89
- <Component {...restProps} className={rootClassName}>
90
+ <Component {...restProps} ref={ref} className={rootClassName}>
90
91
  {glyph && <Icon glyph={glyph} className={css.glyph} />}
91
92
  {children && <span className={css.content}>{children}</span>}
92
93
  {rightGlyph && <Icon glyph={rightGlyph} className={css.glyph} />}
@@ -3,6 +3,17 @@
3
3
  align-items: center;
4
4
  justify-content: center;
5
5
  z-index: var(--layer-xxhigh);
6
+ opacity: 0;
7
+ transition: var(--transition-out);
8
+ }
9
+
10
+ .backdrop[data-enter] {
11
+ opacity: 1;
12
+ transition: var(--transition-in);
13
+ }
14
+
15
+ .backdrop[data-leave] {
16
+ pointer-events: none;
6
17
  }
7
18
 
8
19
  .backdrop::before {
@@ -27,6 +38,17 @@
27
38
  background: var(--color-background);
28
39
  box-shadow: 0 0 var(--space-medium) var(--color-shadow);
29
40
  z-index: var(--layer-xxhigh);
41
+ opacity: 0;
42
+ transition: var(--transition-out);
43
+ }
44
+
45
+ .dialog[data-enter] {
46
+ opacity: 1;
47
+ transition: var(--transition-in);
48
+ }
49
+
50
+ .digalog[data-leave] {
51
+ pointer-events: none;
30
52
  }
31
53
 
32
54
  .header {
@@ -1,13 +1,20 @@
1
1
  import type { ComponentProps, ReactNode } from 'react';
2
2
  import React from 'react';
3
3
  import cx from 'classnames';
4
- import { Dialog as AriakitDialog, DialogDismiss as AriakitDialogDismiss } from 'ariakit/dialog';
4
+ import {
5
+ Dialog as AriakitDialog,
6
+ DialogDismiss as AriakitDialogDismiss,
7
+ useDialogState as useAriakitDialogState,
8
+ type DialogStateProps,
9
+ } from 'ariakit/dialog';
5
10
 
6
11
  import { Stack } from '../../layout/stack';
7
12
  import { BUTTON_GLYPHS, Button } from '../button';
8
13
  import css from './dialog.module.css';
9
14
 
10
- export { useDialogState } from 'ariakit/dialog';
15
+ export const useDialogState = (options: DialogStateProps = {}) => {
16
+ return useAriakitDialogState({ animated: true, ...options });
17
+ };
11
18
 
12
19
  type AriakitDialogProps = ComponentProps<typeof AriakitDialog>;
13
20
 
@@ -13,9 +13,23 @@
13
13
  border-radius: var(--radius-small);
14
14
  box-shadow: var(--shadow-layer);
15
15
  background: var(--color-background);
16
- min-width: 12em;
16
+ min-width: 16em;
17
17
  z-index: var(--layer-high);
18
- padding: calc(var(--space-xxxsmall) / 2);
18
+ padding: var(--space-xxxsmall);
19
+ color: var(--color-text-light);
20
+ font-size: var(--size-small);
21
+ line-height: var(--line-height);
22
+ opacity: 0;
23
+ transition: var(--transition-out);
24
+ }
25
+
26
+ .dropdown[data-enter] {
27
+ opacity: 1;
28
+ transition: var(--transition-in);
29
+ }
30
+
31
+ .dropdown[data-leave] {
32
+ pointer-events: none;
19
33
  }
20
34
 
21
35
  .dropdown:focus {
@@ -29,12 +43,9 @@
29
43
  outline: none;
30
44
  border: 0;
31
45
  border-radius: var(--radius-small);
32
- padding: calc(var(--space-xxxsmall) + var(--space-xxxsmall) / 2);
46
+ padding: var(--space-xxxsmall);
33
47
  background: none;
34
48
  text-align: left;
35
- color: var(--color-text-light);
36
- font-size: var(--size-small);
37
- line-height: var(--line-height);
38
49
  text-decoration: none;
39
50
  cursor: default;
40
51
  }
@@ -58,3 +69,37 @@
58
69
  background: var(--color-highlight-info);
59
70
  color: var(--color-primary-dark);
60
71
  }
72
+
73
+
74
+ .group {
75
+ position: relative;
76
+ padding: calc(var(--space-xxxsmall) / 2 + 1px) 0 calc(var(--space-xxxsmall) / 2);
77
+ }
78
+
79
+ .group::before {
80
+ content: " ";
81
+ display: block;
82
+ position: absolute;
83
+ left: var(--space-xxxsmall);
84
+ right: var(--space-xxxsmall);
85
+ top: 0px;
86
+ height: 1px;
87
+ border: none;
88
+ background: var(--color-outline);
89
+ }
90
+
91
+ .group:first-child {
92
+ padding-top: 0;
93
+ }
94
+
95
+ .group:first-child::before {
96
+ display: none;
97
+ }
98
+
99
+ .group:last-child {
100
+ padding-bottom: 0;
101
+ }
102
+
103
+ .group:empty {
104
+ display: none;
105
+ }
@@ -1,9 +1,19 @@
1
- import React from 'react';
1
+ import React, { ComponentProps } from 'react';
2
2
  import cx from 'classnames';
3
3
  import { Menu, MenuButton, MenuItem, MenuStateProps, useMenuState } from 'ariakit/menu';
4
+ import isNull from 'lodash/isNull';
5
+ import isUndefined from 'lodash/isUndefined';
4
6
 
5
7
  import { Button, BUTTON_SIZE } from '../button';
6
8
  import css from './dropdown.module.css';
9
+ import { Icon } from '../icon';
10
+
11
+ export type DropdownGroupProps = ComponentProps<'div'>;
12
+
13
+ export const DropdownGroup = (props: DropdownGroupProps) => {
14
+ const { className, ...restProps } = props;
15
+ return <div className={cx(css.group, className)} {...restProps} />;
16
+ };
7
17
 
8
18
  interface DropdownItemProps {
9
19
  isActive?: boolean;
@@ -26,6 +36,7 @@ interface DropdownProps {
26
36
  placement?: MenuStateProps['placement'];
27
37
  gutter?: MenuStateProps['gutter'];
28
38
  shift?: MenuStateProps['shift'];
39
+ showChevron?: boolean;
29
40
  }
30
41
 
31
42
  export const Dropdown = (props: DropdownProps & React.ComponentProps<'div'>) => {
@@ -35,6 +46,7 @@ export const Dropdown = (props: DropdownProps & React.ComponentProps<'div'>) =>
35
46
  label = null,
36
47
  ariaLabel = '',
37
48
  glyph = '',
49
+ showChevron: initialShowChevron,
38
50
  disabled = false,
39
51
  placement,
40
52
  gutter = 4,
@@ -43,7 +55,8 @@ export const Dropdown = (props: DropdownProps & React.ComponentProps<'div'>) =>
43
55
  } = props;
44
56
 
45
57
  const dropdownAriaLabel = ariaLabel || (typeof label === 'string' ? label : '');
46
- const menuState = useMenuState({ placement, gutter, shift });
58
+ const menuState = useMenuState({ animated: true, placement, gutter, shift });
59
+ const showChevron = isUndefined(initialShowChevron) ? !isNull(label) : initialShowChevron;
47
60
 
48
61
  return (
49
62
  <>
@@ -52,8 +65,10 @@ export const Dropdown = (props: DropdownProps & React.ComponentProps<'div'>) =>
52
65
  outline
53
66
  size={BUTTON_SIZE.SMALL}
54
67
  glyph={glyph}
68
+ rightGlyph={showChevron ? Icon.ICONS.CHEVRON_DOWN : undefined}
55
69
  disabled={disabled}
56
70
  state={menuState}
71
+ toggleOnClick
57
72
  tabIndex={null}
58
73
  className={cx(css.button, className)}
59
74
  >
@@ -101,23 +101,9 @@
101
101
  text-transform: lowercase;
102
102
  }
103
103
 
104
- .filterGroupSearchWrapper {
105
- padding-bottom: var(--space-xxxsmall);
106
- margin-bottom: var(--space-xxxsmall);
107
- border-bottom: 1px solid var(--color-outline);
108
- }
109
-
110
104
  .filterGroupSearchNotFound {
111
105
  padding: var(--space-xxsmall) 0;
112
106
  text-align: center;
113
107
  font-size: var(--size-small);
114
108
  color: var(--color-text-light);
115
109
  }
116
-
117
- .filterGroupActions {
118
- margin-top: var(--space-xxxsmall);
119
- padding-top: var(--space-xxxsmall);
120
- padding-bottom: var(--space-xxxsmall);
121
- margin-bottom: calc(0px - var(--space-xxxsmall));
122
- border-top: 1px solid var(--color-outline);
123
- }
@@ -6,7 +6,7 @@ import { FlexStack } from '../../layout/flex-stack';
6
6
  import { Stack } from '../../layout/stack';
7
7
  import { Button } from '../button';
8
8
  import { ControlGroup } from '../control-group';
9
- import { Dropdown, DropdownItem } from '../dropdown';
9
+ import { Dropdown, DropdownGroup, DropdownItem } from '../dropdown';
10
10
  import { InputSearch } from '../input-search';
11
11
  import * as I18N from './filters.i18n';
12
12
  import { getGroupFiltersLabelSuffix, LABELS } from './filters.utils';
@@ -40,12 +40,86 @@ const Filter = (props: FilterBooleanProps) => {
40
40
  );
41
41
  };
42
42
 
43
+ const getGroupItemKey = (groupKey: string, itemKey: string): string => `${groupKey}.${itemKey}`;
44
+
45
+ type ToggleFilters = (newFilters: Record<string, boolean>) => void;
46
+
47
+ type GetOnGroupCheck = (value: boolean, overrides?: Record<string, boolean>) => () => void;
48
+
49
+ interface FilterDropdownItemProps {
50
+ className?: string;
51
+ id: string;
52
+ label: string;
53
+ checked: boolean;
54
+ disabled?: boolean;
55
+ onChange: FilterBooleanProps['onChange'];
56
+ toggleFilters: ToggleFilters;
57
+ getOnGroupCheck: GetOnGroupCheck;
58
+ }
59
+
60
+ const FilterDropdownItem = (props: FilterDropdownItemProps) => {
61
+ const { className, id, label, checked, disabled, onChange, toggleFilters, getOnGroupCheck } =
62
+ props;
63
+
64
+ /**
65
+ * Toggle the filter when the menu item itself is activated: ariakit fires a click on the menu
66
+ * item when pressing Enter/Space, and clicking the item padding targets it directly. Clicks on
67
+ * the nested label/checkbox and on the only button are handled by their own handlers.
68
+ */
69
+ const onItemClick = useCallback(
70
+ (event: React.MouseEvent<HTMLDivElement>) => {
71
+ if (disabled || event.target !== event.currentTarget) {
72
+ return;
73
+ }
74
+
75
+ toggleFilters({ [id]: !checked });
76
+ },
77
+ [disabled, toggleFilters, id, checked],
78
+ );
79
+
80
+ /**
81
+ * Check only the current filter of the group
82
+ */
83
+ const onOnlyClick = useCallback(
84
+ () => getOnGroupCheck(false, { [id]: true })(),
85
+ [getOnGroupCheck, id],
86
+ );
87
+
88
+ return (
89
+ <DropdownItem
90
+ onClick={onItemClick}
91
+ hideOnClick={false}
92
+ className={cx(css.filterGroupItem, className)}
93
+ >
94
+ <Filter
95
+ label={label}
96
+ name={id}
97
+ onChange={onChange}
98
+ checked={checked}
99
+ disabled={disabled}
100
+ className={css.filterGroupItemFilter}
101
+ />
102
+ <Button
103
+ kind="info"
104
+ solid
105
+ size="small"
106
+ type="button"
107
+ onClick={onOnlyClick}
108
+ disabled={disabled}
109
+ className={css.filterGroupOnlyButton}
110
+ >
111
+ {I18N.ONLY}
112
+ </Button>
113
+ </DropdownItem>
114
+ );
115
+ };
116
+
43
117
  interface FilterGroupProps extends React.ComponentProps<'div'> {
44
118
  groupKey: string;
45
119
  data: FilterGroupFieldData;
46
120
  values: Record<string, boolean>;
47
121
  onCheckboxChange: FilterBooleanProps['onChange'];
48
- toggleFilters: (newFilters: Record<string, boolean>) => void;
122
+ toggleFilters: ToggleFilters;
49
123
  }
50
124
 
51
125
  const FilterGroup = (props: FilterGroupProps) => {
@@ -70,22 +144,24 @@ const FilterGroup = (props: FilterGroupProps) => {
70
144
  </>
71
145
  );
72
146
 
73
- const getOnGroupCheck =
74
- (value: boolean, overrides = {}) =>
75
- () => {
76
- const newFilters = groupItems.reduce(
77
- (agg, { key: itemKey }) => ({
78
- ...agg,
79
- [`${groupKey}.${itemKey}`]: value,
80
- }),
81
- {},
82
- );
83
-
84
- toggleFilters({
85
- ...newFilters,
86
- ...overrides,
87
- });
88
- };
147
+ const getOnGroupCheck: GetOnGroupCheck = useCallback(
148
+ (value, overrides = {}) =>
149
+ () => {
150
+ const newFilters = groupItems.reduce(
151
+ (agg, { key: itemKey }) => ({
152
+ ...agg,
153
+ [getGroupItemKey(groupKey, itemKey)]: value,
154
+ }),
155
+ {},
156
+ );
157
+
158
+ toggleFilters({
159
+ ...newFilters,
160
+ ...overrides,
161
+ });
162
+ },
163
+ [groupItems, groupKey, toggleFilters],
164
+ );
89
165
 
90
166
  const filteredGroupItems = useMemo(() => {
91
167
  if (!search) {
@@ -102,16 +178,16 @@ const FilterGroup = (props: FilterGroupProps) => {
102
178
  ariaLabel={`${groupLabel}: ${filterSuffix}`}
103
179
  >
104
180
  {groupItems.length > 10 && (
105
- <div className={css.filterGroupSearchWrapper}>
181
+ <DropdownGroup>
106
182
  <InputSearch
107
183
  defaultValue={search}
108
184
  onChange={setSearch}
109
185
  placeholder={I18N.GROUP_SEARCH}
110
186
  debounceWait={0}
111
187
  />
112
- </div>
188
+ </DropdownGroup>
113
189
  )}
114
- <div className={css.filterGroupItems}>
190
+ <DropdownGroup className={css.filterGroupItems}>
115
191
  {filteredGroupItems.length === 0 && (
116
192
  <Stack className={css.filterGroupSearchNotFound}>
117
193
  <p>{I18N.GROUP_NOT_FOUND}</p>
@@ -129,36 +205,24 @@ const FilterGroup = (props: FilterGroupProps) => {
129
205
  </Stack>
130
206
  )}
131
207
  {filteredGroupItems.map(({ key: itemKey, ...itemData }) => {
132
- const id = [groupKey, itemKey].join('.');
133
- const getOnOnlyClick = () => getOnGroupCheck(false, { [id]: true });
208
+ const id = getGroupItemKey(groupKey, itemKey);
134
209
 
135
210
  return (
136
- <DropdownItem key={id} className={css.filterGroupItem}>
137
- <Filter
138
- label={itemData.label}
139
- name={id}
140
- onChange={onCheckboxChange}
141
- checked={values[id]}
142
- disabled={itemData.disabled}
143
- className={css.filterGroupItemFilter}
144
- />
145
- <Button
146
- kind="info"
147
- solid
148
- size="small"
149
- type="button"
150
- onClick={getOnOnlyClick()}
151
- disabled={itemData.disabled}
152
- className={css.filterGroupOnlyButton}
153
- >
154
- {I18N.ONLY}
155
- </Button>
156
- </DropdownItem>
211
+ <FilterDropdownItem
212
+ key={id}
213
+ id={id}
214
+ label={itemData.label}
215
+ checked={values[id]}
216
+ disabled={itemData.disabled}
217
+ onChange={onCheckboxChange}
218
+ toggleFilters={toggleFilters}
219
+ getOnGroupCheck={getOnGroupCheck}
220
+ />
157
221
  );
158
222
  })}
159
- </div>
223
+ </DropdownGroup>
160
224
  {filteredGroupItems.length !== 0 && (
161
- <div className={css.filterGroupActions}>
225
+ <DropdownGroup className={css.filterGroupActions}>
162
226
  {areAllGroupItemsChecked ? (
163
227
  <DropdownItem id="clear-all" onClick={getOnGroupCheck(false)} role="button">
164
228
  {I18N.CLEAR}
@@ -168,7 +232,7 @@ const FilterGroup = (props: FilterGroupProps) => {
168
232
  {I18N.CHECK}
169
233
  </DropdownItem>
170
234
  )}
171
- </div>
235
+ </DropdownGroup>
172
236
  )}
173
237
  </Dropdown>
174
238
  );
@@ -5,11 +5,14 @@
5
5
  .anchor {
6
6
  display: inline-block;
7
7
  color: inherit;
8
+ cursor: default;
8
9
  text-decoration: none;
10
+ transition: var(--transition-out);
9
11
  }
10
12
 
11
13
  .anchor:hover {
12
14
  color: var(--color-primary);
15
+ transition: var(--transition-in);
13
16
  }
14
17
 
15
18
  .anchor:focus {
@@ -27,4 +30,15 @@
27
30
  will-change: filter;
28
31
  background: var(--color-light);
29
32
  font-size: var(--size-small);
33
+ opacity: 0;
34
+ transition: var(--transition-out);
35
+ }
36
+
37
+ .hoverCard[data-enter] {
38
+ opacity: 1;
39
+ transition: var(--transition-in);
40
+ }
41
+
42
+ .hoverCard[data-leave] {
43
+ pointer-events: none;
30
44
  }
@@ -31,7 +31,13 @@ export const HoverCard = (props: HoverCardProps) => {
31
31
  children,
32
32
  } = props;
33
33
 
34
- const state = useHovercardState({ gutter: 8, timeout: 300, placement: 'top' });
34
+ const state = useHovercardState({
35
+ animated: true,
36
+ gutter: 8,
37
+ timeout: 300,
38
+ overflowPadding: 48,
39
+ placement: 'top',
40
+ });
35
41
  const hovercardProps = useHovercard({ state, portal: true });
36
42
 
37
43
  // Fallback to span if no href