@kosmeo-ui/react 0.1.1 → 0.2.0

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 (187) hide show
  1. package/dist/components/buttons/button/index.d.ts +2 -0
  2. package/dist/components/buttons/button/index.d.ts.map +1 -1
  3. package/dist/components/buttons/button/index.js +2 -2
  4. package/dist/components/buttons/button/index.js.map +1 -1
  5. package/dist/components/buttons/button/styles.css +6 -4
  6. package/dist/components/buttons/rowActions/styles.css +7 -0
  7. package/dist/components/data-display/addressBlock/styles.css +2 -4
  8. package/dist/components/data-display/dataTable/styles.css +15 -0
  9. package/dist/components/data-display/infiniteScroll/filters/searchFilter.d.ts.map +1 -1
  10. package/dist/components/data-display/infiniteScroll/filters/searchFilter.js +4 -35
  11. package/dist/components/data-display/infiniteScroll/filters/searchFilter.js.map +1 -1
  12. package/dist/components/data-display/infiniteScroll/filters/styles.css +4 -0
  13. package/dist/components/data-display/infiniteScroll/header/deleteButton.d.ts.map +1 -1
  14. package/dist/components/data-display/infiniteScroll/header/deleteButton.js +1 -1
  15. package/dist/components/data-display/infiniteScroll/header/deleteButton.js.map +1 -1
  16. package/dist/components/data-display/infiniteScroll/header/index.d.ts.map +1 -1
  17. package/dist/components/data-display/infiniteScroll/header/index.js +1 -1
  18. package/dist/components/data-display/infiniteScroll/header/index.js.map +1 -1
  19. package/dist/components/data-display/infiniteScroll/table/index.d.ts.map +1 -1
  20. package/dist/components/data-display/infiniteScroll/table/index.js +2 -2
  21. package/dist/components/data-display/infiniteScroll/table/index.js.map +1 -1
  22. package/dist/components/data-display/infiniteScroll/tags/index.d.ts.map +1 -1
  23. package/dist/components/data-display/infiniteScroll/tags/index.js +1 -6
  24. package/dist/components/data-display/infiniteScroll/tags/index.js.map +1 -1
  25. package/dist/components/data-display/infiniteScroll/tags/listFilterTag.d.ts.map +1 -1
  26. package/dist/components/data-display/infiniteScroll/tags/listFilterTag.js +3 -3
  27. package/dist/components/data-display/infiniteScroll/tags/listFilterTag.js.map +1 -1
  28. package/dist/components/data-display/infiniteScroll/tags/rangeFilterTag.d.ts.map +1 -1
  29. package/dist/components/data-display/infiniteScroll/tags/rangeFilterTag.js +3 -13
  30. package/dist/components/data-display/infiniteScroll/tags/rangeFilterTag.js.map +1 -1
  31. package/dist/components/data-display/infiniteScroll/tags/selectFilterTag.d.ts.map +1 -1
  32. package/dist/components/data-display/infiniteScroll/tags/selectFilterTag.js +3 -5
  33. package/dist/components/data-display/infiniteScroll/tags/selectFilterTag.js.map +1 -1
  34. package/dist/components/data-display/infiniteScroll/tags/styles.css +1 -29
  35. package/dist/components/data-display/recoveryCodeGrid/styles.css +1 -2
  36. package/dist/components/data-display/statCard/styles.css +1 -1
  37. package/dist/components/data-display/table/styles.css +12 -1
  38. package/dist/components/data-display/tagCard/index.d.ts.map +1 -1
  39. package/dist/components/data-display/tagCard/index.js +1 -1
  40. package/dist/components/data-display/tagCard/index.js.map +1 -1
  41. package/dist/components/feedback/badge/styles.css +8 -6
  42. package/dist/components/feedback/emptyState/styles.css +2 -4
  43. package/dist/components/feedback/progressBar/index.d.ts.map +1 -1
  44. package/dist/components/feedback/progressBar/index.js +7 -2
  45. package/dist/components/feedback/progressBar/index.js.map +1 -1
  46. package/dist/components/feedback/progressBar/styles.css +15 -6
  47. package/dist/components/feedback/spinner/styles.css +0 -13
  48. package/dist/components/forms/fieldShell/styles.css +1 -1
  49. package/dist/components/forms/formActions/styles.css +1 -1
  50. package/dist/components/forms/infoRow/styles.css +1 -2
  51. package/dist/components/forms/requiredAsterisk/styles.css +1 -1
  52. package/dist/components/index.d.ts +2 -0
  53. package/dist/components/index.d.ts.map +1 -1
  54. package/dist/components/index.js +1 -0
  55. package/dist/components/index.js.map +1 -1
  56. package/dist/components/inputs/checkbox/index.d.ts +6 -1
  57. package/dist/components/inputs/checkbox/index.d.ts.map +1 -1
  58. package/dist/components/inputs/checkbox/index.js +9 -2
  59. package/dist/components/inputs/checkbox/index.js.map +1 -1
  60. package/dist/components/inputs/checkbox/styles.css +8 -0
  61. package/dist/components/inputs/colorPicker/styles.css +1 -1
  62. package/dist/components/inputs/dateInput/index.d.ts +3 -3
  63. package/dist/components/inputs/dateInput/index.d.ts.map +1 -1
  64. package/dist/components/inputs/dateInput/index.js +13 -3
  65. package/dist/components/inputs/dateInput/index.js.map +1 -1
  66. package/dist/components/inputs/dropdown/index.d.ts +2 -0
  67. package/dist/components/inputs/dropdown/index.d.ts.map +1 -1
  68. package/dist/components/inputs/dropdown/index.js +1 -1
  69. package/dist/components/inputs/dropdown/index.js.map +1 -1
  70. package/dist/components/inputs/dropdownArrowButton/index.d.ts.map +1 -1
  71. package/dist/components/inputs/dropdownArrowButton/index.js +1 -1
  72. package/dist/components/inputs/dropdownArrowButton/index.js.map +1 -1
  73. package/dist/components/inputs/dropdownArrowButton/styles.css +1 -1
  74. package/dist/components/inputs/input/index.d.ts +5 -1
  75. package/dist/components/inputs/input/index.d.ts.map +1 -1
  76. package/dist/components/inputs/input/index.js +25 -4
  77. package/dist/components/inputs/input/index.js.map +1 -1
  78. package/dist/components/inputs/input/styles.css +46 -2
  79. package/dist/components/inputs/multiSelect/index.d.ts.map +1 -1
  80. package/dist/components/inputs/multiSelect/index.js +1 -1
  81. package/dist/components/inputs/multiSelect/index.js.map +1 -1
  82. package/dist/components/inputs/multiSelect/styles.css +1 -1
  83. package/dist/components/inputs/phoneInput/index.d.ts.map +1 -1
  84. package/dist/components/inputs/phoneInput/index.js +1 -1
  85. package/dist/components/inputs/phoneInput/index.js.map +1 -1
  86. package/dist/components/inputs/richTextEditor/index.d.ts.map +1 -1
  87. package/dist/components/inputs/richTextEditor/index.js +14 -3
  88. package/dist/components/inputs/richTextEditor/index.js.map +1 -1
  89. package/dist/components/layout/borderedSection/index.d.ts +3 -1
  90. package/dist/components/layout/borderedSection/index.d.ts.map +1 -1
  91. package/dist/components/layout/borderedSection/index.js +2 -2
  92. package/dist/components/layout/borderedSection/index.js.map +1 -1
  93. package/dist/components/layout/card/styles.css +22 -0
  94. package/dist/components/layout/sectionHeader/styles.css +1 -2
  95. package/dist/components/navigation/pagination/index.d.ts +23 -0
  96. package/dist/components/navigation/pagination/index.d.ts.map +1 -0
  97. package/dist/components/navigation/pagination/index.js +29 -0
  98. package/dist/components/navigation/pagination/index.js.map +1 -0
  99. package/dist/components/navigation/pagination/styles.css +113 -0
  100. package/dist/components/navigation/pagination/utils.d.ts +9 -0
  101. package/dist/components/navigation/pagination/utils.d.ts.map +1 -0
  102. package/dist/components/navigation/pagination/utils.js +25 -0
  103. package/dist/components/navigation/pagination/utils.js.map +1 -0
  104. package/dist/components/navigation/sidebar/styles.css +9 -1
  105. package/dist/components/navigation/tabButton/styles.css +6 -6
  106. package/dist/components/navigation/tabs/styles.css +2 -2
  107. package/dist/components/overlays/confirmationModal/index.d.ts +5 -0
  108. package/dist/components/overlays/confirmationModal/index.d.ts.map +1 -1
  109. package/dist/components/overlays/confirmationModal/index.js +23 -4
  110. package/dist/components/overlays/confirmationModal/index.js.map +1 -1
  111. package/dist/components/overlays/confirmationModal/styles.css +3 -0
  112. package/dist/components/overlays/modal/index.d.ts +2 -0
  113. package/dist/components/overlays/modal/index.d.ts.map +1 -1
  114. package/dist/components/overlays/modal/index.js +13 -4
  115. package/dist/components/overlays/modal/index.js.map +1 -1
  116. package/dist/hooks/index.d.ts +1 -0
  117. package/dist/hooks/index.d.ts.map +1 -1
  118. package/dist/hooks/index.js +1 -0
  119. package/dist/hooks/index.js.map +1 -1
  120. package/dist/hooks/usePaginated.d.ts +43 -0
  121. package/dist/hooks/usePaginated.d.ts.map +1 -0
  122. package/dist/hooks/usePaginated.js +60 -0
  123. package/dist/hooks/usePaginated.js.map +1 -0
  124. package/dist/styles.css +37 -14
  125. package/package.json +1 -1
  126. package/src/components/buttons/button/index.tsx +14 -5
  127. package/src/components/buttons/button/styles.css +6 -4
  128. package/src/components/buttons/rowActions/styles.css +7 -0
  129. package/src/components/data-display/addressBlock/styles.css +2 -4
  130. package/src/components/data-display/dataTable/styles.css +15 -0
  131. package/src/components/data-display/infiniteScroll/filters/searchFilter.tsx +16 -65
  132. package/src/components/data-display/infiniteScroll/filters/styles.css +4 -0
  133. package/src/components/data-display/infiniteScroll/header/deleteButton.tsx +1 -0
  134. package/src/components/data-display/infiniteScroll/header/index.tsx +4 -0
  135. package/src/components/data-display/infiniteScroll/table/index.tsx +2 -0
  136. package/src/components/data-display/infiniteScroll/tags/index.tsx +1 -11
  137. package/src/components/data-display/infiniteScroll/tags/listFilterTag.tsx +12 -16
  138. package/src/components/data-display/infiniteScroll/tags/rangeFilterTag.tsx +4 -22
  139. package/src/components/data-display/infiniteScroll/tags/selectFilterTag.tsx +4 -11
  140. package/src/components/data-display/infiniteScroll/tags/styles.css +1 -29
  141. package/src/components/data-display/recoveryCodeGrid/styles.css +1 -2
  142. package/src/components/data-display/statCard/styles.css +1 -1
  143. package/src/components/data-display/table/styles.css +12 -1
  144. package/src/components/data-display/tagCard/index.tsx +5 -3
  145. package/src/components/feedback/badge/styles.css +8 -6
  146. package/src/components/feedback/emptyState/styles.css +2 -4
  147. package/src/components/feedback/progressBar/index.tsx +14 -8
  148. package/src/components/feedback/progressBar/styles.css +15 -6
  149. package/src/components/feedback/spinner/styles.css +0 -13
  150. package/src/components/forms/fieldShell/styles.css +1 -1
  151. package/src/components/forms/formActions/styles.css +1 -1
  152. package/src/components/forms/infoRow/styles.css +1 -2
  153. package/src/components/forms/requiredAsterisk/styles.css +1 -1
  154. package/src/components/index.ts +2 -0
  155. package/src/components/inputs/checkbox/index.tsx +24 -4
  156. package/src/components/inputs/checkbox/styles.css +8 -0
  157. package/src/components/inputs/colorPicker/styles.css +1 -1
  158. package/src/components/inputs/dateInput/index.tsx +13 -4
  159. package/src/components/inputs/dropdown/index.tsx +4 -0
  160. package/src/components/inputs/dropdownArrowButton/index.tsx +1 -0
  161. package/src/components/inputs/dropdownArrowButton/styles.css +1 -1
  162. package/src/components/inputs/input/index.tsx +47 -4
  163. package/src/components/inputs/input/styles.css +46 -2
  164. package/src/components/inputs/multiSelect/index.tsx +1 -0
  165. package/src/components/inputs/multiSelect/styles.css +1 -1
  166. package/src/components/inputs/phoneInput/index.tsx +6 -2
  167. package/src/components/inputs/richTextEditor/index.tsx +27 -3
  168. package/src/components/layout/borderedSection/index.tsx +4 -1
  169. package/src/components/layout/card/styles.css +22 -0
  170. package/src/components/layout/sectionHeader/styles.css +1 -2
  171. package/src/components/navigation/pagination/index.tsx +134 -0
  172. package/src/components/navigation/pagination/styles.css +113 -0
  173. package/src/components/navigation/pagination/utils.ts +32 -0
  174. package/src/components/navigation/sidebar/styles.css +9 -1
  175. package/src/components/navigation/tabButton/styles.css +6 -6
  176. package/src/components/navigation/tabs/styles.css +2 -2
  177. package/src/components/overlays/confirmationModal/index.tsx +37 -1
  178. package/src/components/overlays/confirmationModal/styles.css +3 -0
  179. package/src/components/overlays/modal/index.tsx +23 -4
  180. package/src/hooks/index.ts +4 -0
  181. package/src/hooks/usePaginated.ts +123 -0
  182. package/src/styles.css +37 -14
  183. package/dist/components/data-display/infiniteScroll/tags/searchFilterTag.d.ts +0 -10
  184. package/dist/components/data-display/infiniteScroll/tags/searchFilterTag.d.ts.map +0 -1
  185. package/dist/components/data-display/infiniteScroll/tags/searchFilterTag.js +0 -22
  186. package/dist/components/data-display/infiniteScroll/tags/searchFilterTag.js.map +0 -1
  187. package/src/components/data-display/infiniteScroll/tags/searchFilterTag.tsx +0 -41
package/dist/styles.css CHANGED
@@ -81,12 +81,14 @@ h6 {
81
81
  --primary-color-active: #141f6c;
82
82
  --primary-color-background: #3d52db;
83
83
  --primary-color-background-light: #f0f2ff;
84
+ /* Primary as text (links, active tabs) on page and card surfaces. */
85
+ --primary-color-text: #17257d;
84
86
 
85
- /* Secondary Colors */
86
- --secondary-color: #858585;
87
- --secondary-color-hover: #8c8c8c;
88
- --secondary-color-active: #808080;
89
- --secondary-color-background: #a6a6a6;
87
+ /* Secondary Colors — dark enough for white text (4.5:1) */
88
+ --secondary-color: #6b6b6b;
89
+ --secondary-color-hover: #757575;
90
+ --secondary-color-active: #616161;
91
+ --secondary-color-background: #6b6b6b;
90
92
 
91
93
  /* Light Colors */
92
94
  --light-color: #fafafa;
@@ -101,18 +103,22 @@ h6 {
101
103
  --warning-color-background: #ffd24d;
102
104
  --warning-color-background-light: #fff8e1;
103
105
  --warning-color-text: #92400e;
106
+ /* Text on a warning fill. */
107
+ --warning-color-contrast: #333;
104
108
 
105
109
  /* Danger Colors */
106
110
  --danger-color: #dc3545;
107
111
  --danger-color-hover: #e23243;
108
112
  --danger-color-active: #d92638;
109
- --danger-color-background: #e46774;
113
+ --danger-color-background: #c42b3b;
114
+ /* Danger as text (field errors) on page and card surfaces. */
115
+ --danger-color-text: #b92535;
110
116
 
111
117
  /* Success Colors */
112
118
  --success-color: #198754;
113
119
  --success-color-hover: #1c975d;
114
120
  --success-color-active: #188150;
115
- --success-color-background: #24c278;
121
+ --success-color-background: #157347;
116
122
  --success-color-background-light: #e8f5e8;
117
123
 
118
124
  /* Background Colors */
@@ -136,7 +142,7 @@ h6 {
136
142
  --text-color: #333;
137
143
  --text-color-secondary: #666;
138
144
  --text-color-light: white;
139
- --text-color-link: #007bff;
145
+ --text-color-link: #0062cc;
140
146
  --text-size-sm: 0.875rem;
141
147
  --text-size-md: 1rem;
142
148
  --text-size-lg: 1.125rem;
@@ -161,11 +167,12 @@ h6 {
161
167
  --primary-color-active: #3d4f99;
162
168
  --primary-color-background: #4a5fb5;
163
169
  --primary-color-background-light: #1c2240;
170
+ --primary-color-text: #9aabf2;
164
171
 
165
172
  /* Secondary Colors */
166
- --secondary-color: #8a8f99;
167
- --secondary-color-hover: #9aa0aa;
168
- --secondary-color-active: #7a7f88;
173
+ --secondary-color: #646a75;
174
+ --secondary-color-hover: #6e7480;
175
+ --secondary-color-active: #5a606a;
169
176
  --secondary-color-background: #4a4f5a;
170
177
 
171
178
  /* Light Colors — surface-toned (used for "light" variant buttons/pills) */
@@ -181,17 +188,19 @@ h6 {
181
188
  --warning-color-background: #4d3a00;
182
189
  --warning-color-background-light: #2e2200;
183
190
  --warning-color-text: #f5c542;
191
+ --warning-color-contrast: #15181f;
184
192
 
185
193
  /* Danger Colors */
186
194
  --danger-color: #c94250;
187
195
  --danger-color-hover: #d35261;
188
196
  --danger-color-active: #b53847;
189
197
  --danger-color-background: #4a1820;
198
+ --danger-color-text: #ff8f9a;
190
199
 
191
200
  /* Success Colors */
192
- --success-color: #2a9560;
193
- --success-color-hover: #32a66c;
194
- --success-color-active: #248355;
201
+ --success-color: #1f7a4e;
202
+ --success-color-hover: #238556;
203
+ --success-color-active: #1b6d45;
195
204
  --success-color-background: #133e2c;
196
205
  --success-color-background-light: #0d2419;
197
206
 
@@ -384,3 +393,17 @@ code {
384
393
  .k-flex-wrap {
385
394
  flex-wrap: wrap;
386
395
  }
396
+
397
+ /* Visually hidden, still read by screen readers. Global so any component can
398
+ use it without depending on another component's stylesheet. */
399
+ .k-sr-only {
400
+ position: absolute;
401
+ width: 0.0625rem;
402
+ height: 0.0625rem;
403
+ padding: 0;
404
+ margin: -0.0625rem;
405
+ overflow: hidden;
406
+ clip: rect(0, 0, 0, 0);
407
+ white-space: nowrap;
408
+ border: 0;
409
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kosmeo-ui/react",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "React components themed with CSS variables, light and dark",
5
5
  "keywords": [
6
6
  "react",
@@ -33,6 +33,8 @@ export interface ButtonProps extends PropsWithSize, PropsWithVariant {
33
33
  fullWidth?: boolean;
34
34
  /** Accessible label. Required for icon-only buttons. */
35
35
  "aria-label"?: string;
36
+ /** Marks a toggle button as on or off. */
37
+ "aria-pressed"?: boolean;
36
38
  /** ID of element describing this button (for additional context). */
37
39
  "aria-describedby"?: string;
38
40
  /** ID of element labelling this button. */
@@ -61,6 +63,8 @@ const Button = (props: ButtonProps) => {
61
63
  aria-label={props["aria-label"]}
62
64
  aria-describedby={props["aria-describedby"]}
63
65
  aria-labelledby={props["aria-labelledby"]}
66
+ aria-busy={isLoading || undefined}
67
+ aria-pressed={props["aria-pressed"]}
64
68
  title={props.title}
65
69
  className={`k-button k-button-${variant} k-button-${size} k-button-${align} ${
66
70
  shadow ? "k-button-shadow" : ""
@@ -68,11 +72,16 @@ const Button = (props: ButtonProps) => {
68
72
  onClick={props.onClick}
69
73
  >
70
74
  {isLoading ? (
71
- <FontAwesomeIcon
72
- icon={faCircleNotch}
73
- className="k-button-spinner"
74
- spin
75
- />
75
+ <>
76
+ <FontAwesomeIcon
77
+ icon={faCircleNotch}
78
+ className="k-button-spinner"
79
+ spin
80
+ aria-hidden="true"
81
+ />
82
+ {/* Keeps the button's accessible name while the spinner shows. */}
83
+ <span className="k-sr-only">{props.children}</span>
84
+ </>
76
85
  ) : (
77
86
  props.children
78
87
  )}
@@ -151,7 +151,7 @@
151
151
 
152
152
  .k-button-warning {
153
153
  background-color: var(--warning-color);
154
- color: var(--text-color);
154
+ color: var(--warning-color-contrast);
155
155
  }
156
156
  .k-button-warning:not(:disabled):hover {
157
157
  background-color: var(--warning-color-hover);
@@ -165,7 +165,7 @@
165
165
 
166
166
  .k-button-link {
167
167
  background-color: transparent;
168
- color: var(--primary-color);
168
+ color: var(--primary-color-text);
169
169
  padding: 0;
170
170
  border-radius: 0;
171
171
  font-weight: 500;
@@ -174,12 +174,14 @@
174
174
  box-shadow: none;
175
175
  }
176
176
  .k-button-link:not(:disabled):hover {
177
- color: var(--primary-color-hover);
177
+ color: var(--primary-color-text);
178
+ /* One text color keeps contrast in both themes; hover shows as a heavier underline. */
179
+ text-decoration-thickness: 0.125rem;
178
180
  background-color: transparent;
179
181
  box-shadow: none;
180
182
  }
181
183
  .k-button-link:active {
182
- color: var(--primary-color-active);
184
+ color: var(--primary-color-text);
183
185
  background-color: transparent;
184
186
  box-shadow: none;
185
187
  }
@@ -2,6 +2,13 @@
2
2
  display: flex;
3
3
  }
4
4
 
5
+ /* Never squeeze an action below its natural width: in a narrow table cell a
6
+ shrunk icon button collapses its icon to 0px (svg max-width: 100%). The
7
+ column grows to fit instead. */
8
+ .k-row-actions > * {
9
+ flex-shrink: 0;
10
+ }
11
+
5
12
  .k-row-actions-start {
6
13
  justify-content: flex-start;
7
14
  }
@@ -10,8 +10,7 @@
10
10
  font-weight: 600;
11
11
  text-transform: uppercase;
12
12
  letter-spacing: 0.04em;
13
- color: var(--text-color);
14
- opacity: 0.65;
13
+ color: var(--text-color-secondary);
15
14
  }
16
15
 
17
16
  .k-address-block-body {
@@ -30,8 +29,7 @@
30
29
 
31
30
  .k-address-block-contact {
32
31
  font-size: 0.8125rem;
33
- color: var(--text-color);
34
- opacity: 0.7;
32
+ color: var(--text-color-secondary);
35
33
  margin-top: 0.125rem;
36
34
  }
37
35
 
@@ -4,6 +4,21 @@
4
4
  width: 3rem;
5
5
  }
6
6
 
7
+ /* Even padding, overriding the table's wider first-column inset, so the
8
+ checkbox stays centered. */
9
+ .k-table th.k-data-table-leading,
10
+ .k-table td.k-data-table-leading {
11
+ padding-left: 0.5rem;
12
+ padding-right: 0.5rem;
13
+ }
14
+
15
+ /* text-align doesn't move flex items, and Checkbox's container is a
16
+ full-width flex column, so center flex content on both axes too. */
17
+ .k-data-table-leading > * {
18
+ justify-content: center;
19
+ align-items: center;
20
+ }
21
+
7
22
  .k-data-table-spacer {
8
23
  padding: 0;
9
24
  border: 0;
@@ -1,9 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
4
- import { useCallback, useEffect, useMemo, useState } from "react";
5
- import Dropdown from "../../../inputs/dropdown/index.js";
6
- import Button from "../../../buttons/button/index.js";
4
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
7
5
  import Input from "../../../inputs/input/index.js";
8
6
  import "./styles.css";
9
7
  import {
@@ -16,68 +14,21 @@ interface InfiniteScrollSearchFilterProps {
16
14
  onFilterChange: (filter: InfiniteScrollMenuGeneratedFilter) => void;
17
15
  }
18
16
 
19
- const SearchFilter = (props: InfiniteScrollSearchFilterProps) => {
20
- const [value, setValue] = useState<string>("");
21
- const [prevValue, setPrevValue] = useState<string>("");
22
- const [isOpen, setIsOpen] = useState(false);
23
-
24
- useEffect(() => {
25
- if (
26
- props.filter.value === prevValue ||
27
- (props.filter.value === undefined && prevValue === "")
28
- )
29
- return;
30
- setValue(props.filter.value || "");
31
- setPrevValue(props.filter.value || "");
32
- return;
33
- }, [props.filter.value, value, prevValue]);
34
-
35
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
36
- setValue(e.target.value);
37
- };
38
-
39
- const handleSubmit = useCallback(() => {
40
- setPrevValue(value);
41
- props.onFilterChange({ ...props.filter, value: value });
42
- setIsOpen(false);
43
- }, [value, props]);
44
-
45
- const handleKeydown = useCallback(
46
- (e: React.KeyboardEvent<HTMLInputElement>) => {
47
- if (e.key !== "Enter") return;
48
- e.preventDefault();
49
- handleSubmit();
50
- },
51
- [handleSubmit]
52
- );
53
-
54
- const allowSubmit = useMemo(() => {
55
- return value !== prevValue;
56
- }, [value, prevValue]);
57
-
58
- return (
59
- <Dropdown
60
- label={props.filter.title}
61
- icon={faMagnifyingGlass}
62
- align="left"
17
+ // Typing updates the pending filter, like the other filters; Apply applies it.
18
+ const SearchFilter = (props: InfiniteScrollSearchFilterProps) => (
19
+ <div className="k-search-filter">
20
+ <Input
63
21
  size="sm"
64
- variant="secondary"
65
- isOpen={isOpen}
66
- onOpenChange={setIsOpen}
67
- >
68
- <div className="k-filter-menu">
69
- <Input
70
- size="sm"
71
- value={value}
72
- onChange={handleChange}
73
- onKeyDown={handleKeydown}
74
- />
75
- <Button size="sm" disabled={!allowSubmit} onClick={handleSubmit}>
76
- Add Filter
77
- </Button>
78
- </div>
79
- </Dropdown>
80
- );
81
- };
22
+ label={props.filter.title}
23
+ placeholder="Search…"
24
+ prefix={<FontAwesomeIcon icon={faMagnifyingGlass} />}
25
+ value={props.filter.value ?? ""}
26
+ onChange={(e) =>
27
+ props.onFilterChange({ ...props.filter, value: e.target.value })
28
+ }
29
+ onClear={() => props.onFilterChange({ ...props.filter, value: "" })}
30
+ />
31
+ </div>
32
+ );
82
33
 
83
34
  export default SearchFilter;
@@ -26,6 +26,10 @@
26
26
  min-width: 15rem;
27
27
  }
28
28
 
29
+ .k-search-filter {
30
+ width: 14rem;
31
+ }
32
+
29
33
  .k-range-filter-menu-column {
30
34
  display: flex;
31
35
  flex-direction: column;
@@ -42,6 +42,7 @@ const DeleteButton = <TData extends object>(
42
42
  onClick={handleShowBulkDeleteModal}
43
43
  disabled={!props.selectedRows.size && !props.allSelected}
44
44
  icon={faTrash}
45
+ aria-label="Delete selected"
45
46
  />
46
47
  <ConfirmationModal
47
48
  show={showBulkDeleteModal}
@@ -47,6 +47,7 @@ const Header = <TData extends object>(props: HeaderProps<TData>) => {
47
47
  size="sm"
48
48
  variant="secondary"
49
49
  onClick={props.onRefetch}
50
+ aria-label="Refresh"
50
51
  icon={faRotate}
51
52
  />
52
53
  {props.onDownload && (
@@ -54,6 +55,7 @@ const Header = <TData extends object>(props: HeaderProps<TData>) => {
54
55
  size="sm"
55
56
  variant="secondary"
56
57
  onClick={props.onDownload}
58
+ aria-label="Download"
57
59
  icon={faDownload}
58
60
  />
59
61
  )}
@@ -62,6 +64,7 @@ const Header = <TData extends object>(props: HeaderProps<TData>) => {
62
64
  size="sm"
63
65
  variant="success"
64
66
  onClick={props.onAddNew}
67
+ aria-label="Add new"
65
68
  icon={faPlus}
66
69
  />
67
70
  )}
@@ -74,6 +77,7 @@ const Header = <TData extends object>(props: HeaderProps<TData>) => {
74
77
  {props.optionsDropdown && (
75
78
  <Dropdown
76
79
  icon={faEllipsisVertical}
80
+ aria-label="More options"
77
81
  size="sm"
78
82
  variant="secondary"
79
83
  align="left"
@@ -32,12 +32,14 @@ const InfiniteScrollTable = <TData extends object>(
32
32
  <Checkbox
33
33
  checked={props.allSelected}
34
34
  onChange={props.onSelectAll}
35
+ aria-label="Select all rows"
35
36
  />
36
37
  ),
37
38
  cell: (row) => (
38
39
  <Checkbox
39
40
  checked={props.selectedRows.has(row.index) || props.allSelected}
40
41
  onChange={() => props.onRowSelect(row.index)}
42
+ aria-label={`Select row ${row.index + 1}`}
41
43
  />
42
44
  ),
43
45
  };
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
 
3
3
  import { InfiniteScrollMenuGeneratedFilter } from "../types.js";
4
- import SearchFilterTag from "./searchFilterTag.js";
5
4
  import SelectFilterTag from "./selectFilterTag.js";
6
5
  import RangeFilterTag from "./rangeFilterTag.js";
7
6
  import ListFilterTag from "./listFilterTag.js";
@@ -13,6 +12,7 @@ interface InfiniteScrollMenuTagsProps {
13
12
  }
14
13
 
15
14
  const Tags = (props: InfiniteScrollMenuTagsProps) => {
15
+ // SEARCH filters get no tag: their search box already shows the value.
16
16
  const filterTags = props.filters.reduce((tags, f) => {
17
17
  if (f.type === "SELECT") {
18
18
  const selectedFilters = f.options?.filter((o) => o.selected);
@@ -51,16 +51,6 @@ const Tags = (props: InfiniteScrollMenuTagsProps) => {
51
51
  />
52
52
  );
53
53
  }
54
- } else if (f.type === "SEARCH") {
55
- if (!f.value) return tags;
56
- tags.push(
57
- <SearchFilterTag
58
- key={f.filterId}
59
- filter={f}
60
- filters={props.filters}
61
- handleNewFilters={props.handleNewFilters}
62
- />
63
- );
64
54
  } else if (f.type === "LIST") {
65
55
  if (!f.list) return tags;
66
56
  tags.push(
@@ -1,16 +1,15 @@
1
1
  "use client";
2
2
 
3
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
3
  import IconButton from "../../../buttons/iconButton/index.js";
5
4
  import type { TableColumn } from "../../dataTable/types.js";
6
5
  import Table from "../../../data-display/table/index.js";
7
6
  import { useCallback, useMemo, useState } from "react";
7
+ import Badge from "../../../feedback/badge/index.js";
8
8
  import Modal from "../../../overlays/modal/index.js";
9
9
  import Input from "../../../inputs/input/index.js";
10
10
  import Row from "../../../layout/row/index.js";
11
11
  import "./styles.css";
12
12
  import {
13
- faList,
14
13
  faPenToSquare,
15
14
  faPlus,
16
15
  faXmark,
@@ -111,20 +110,17 @@ const ListFilterTag = (props: InfiniteScrollListFilterTagProps) => {
111
110
  );
112
111
 
113
112
  return (
114
- <div className="k-tag-container">
115
- <p className="k-tag-title">{props.filter.title}</p>
116
- <div className="k-tag-actions">
117
- <FontAwesomeIcon icon={faList} />
118
- <p className="k-tag-value">{tagValue}</p>
119
- <div className="k-lft-buttons">
120
- <IconButton
121
- icon={faPenToSquare}
122
- onClick={() => setShow(!show)}
123
- size="sm"
124
- />
125
- <IconButton icon={faXmark} onClick={handleRemove} size="sm" />
126
- </div>
127
- </div>
113
+ <div className="k-lft-buttons">
114
+ <Badge onRemove={handleRemove}>
115
+ {props.filter.title}: {tagValue}
116
+ </Badge>
117
+ <IconButton
118
+ icon={faPenToSquare}
119
+ onClick={() => setShow(!show)}
120
+ size="sm"
121
+ variant="light"
122
+ aria-label={`Edit ${props.filter.title}`}
123
+ />
128
124
  <Modal show={show} onClose={handleClose} title={props.filter.title}>
129
125
  <div className="k-lft-modal-body">
130
126
  <Input
@@ -1,16 +1,9 @@
1
1
  "use client";
2
2
 
3
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
- import IconButton from "../../../buttons/iconButton/index.js";
3
+ import Badge from "../../../feedback/badge/index.js";
5
4
  import { useCallback, useMemo } from "react";
6
5
  import { DateTime } from "luxon";
7
6
  import "./styles.css";
8
- import {
9
- faCalendarDays,
10
- faFont,
11
- faHashtag,
12
- faXmark,
13
- } from "@fortawesome/free-solid-svg-icons";
14
7
  import {
15
8
  GeneratedRangeFilter,
16
9
  InfiniteScrollMenuGeneratedFilter,
@@ -48,12 +41,6 @@ const RangeFilterTag = (props: InfiniteScrollRangeFilterTagProps) => {
48
41
  else if (filter.rangeType === "STRING") return value;
49
42
  }, [filter, filterValue, filter.rangeType]);
50
43
 
51
- const iconToUse = useMemo(() => {
52
- if (isDate) return faCalendarDays;
53
- else if (filter.rangeType === "NUMBER") return faHashtag;
54
- else if (filter.rangeType === "STRING") return faFont;
55
- }, [filter.rangeType]);
56
-
57
44
  const handleRemove = useCallback(() => {
58
45
  const newFilters = props.filters.map((f) => {
59
46
  if (f.filterId !== props.filter.filterId) return f;
@@ -63,14 +50,9 @@ const RangeFilterTag = (props: InfiniteScrollRangeFilterTagProps) => {
63
50
  }, [props.filters, filterValue]);
64
51
 
65
52
  return (
66
- <div className="k-tag-container">
67
- <p className="k-tag-title">{tagTitle}</p>
68
- <div className="k-tag-actions">
69
- {iconToUse && <FontAwesomeIcon icon={iconToUse} />}
70
- <p className="k-tag-value">{tagValue}</p>
71
- <IconButton icon={faXmark} onClick={handleRemove} size="sm" />
72
- </div>
73
- </div>
53
+ <Badge onRemove={handleRemove}>
54
+ {tagTitle}: {tagValue}
55
+ </Badge>
74
56
  );
75
57
  };
76
58
 
@@ -1,8 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { faCheckSquare, faXmark } from "@fortawesome/free-solid-svg-icons";
4
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
5
- import IconButton from "../../../buttons/iconButton/index.js";
3
+ import Badge from "../../../feedback/badge/index.js";
6
4
  import { useCallback } from "react";
7
5
  import "../styles.css";
8
6
  import {
@@ -32,14 +30,9 @@ const SelectFilterTag = (props: InfiniteScrollSelectFilterTagProps) => {
32
30
  }, [props]);
33
31
 
34
32
  return (
35
- <div className="k-tag-container">
36
- <p className="k-tag-title">{props.filter.title}</p>
37
- <div className="k-tag-actions">
38
- <FontAwesomeIcon icon={faCheckSquare} />
39
- <p className="k-tag-value">{props.filterOption.name}</p>
40
- <IconButton onClick={handleRemove} size="sm" icon={faXmark} />
41
- </div>
42
- </div>
33
+ <Badge onRemove={handleRemove}>
34
+ {props.filter.title}: {props.filterOption.name}
35
+ </Badge>
43
36
  );
44
37
  };
45
38
 
@@ -2,42 +2,14 @@
2
2
  display: flex;
3
3
  gap: 0.5rem;
4
4
  flex-wrap: wrap;
5
- }
6
-
7
- .k-tag-container {
8
- display: flex;
9
- flex-direction: column;
10
- justify-content: center;
11
- background-color: var(--primary-color);
12
- border-radius: 0.375rem;
13
- color: var(--text-color-light);
14
- padding-left: 0.5rem;
15
- }
16
-
17
- .k-tag-title {
18
- margin: 0;
19
- margin-top: 0.25rem;
20
- font-size: 0.75rem;
21
- }
22
-
23
- .k-tag-actions {
24
- display: flex;
25
- flex-direction: row;
26
5
  align-items: center;
27
- gap: 0.5rem;
28
- margin-bottom: 0.25rem;
29
- margin-top: -0.25rem;
30
- }
31
-
32
- .k-tag-value {
33
- margin: 0;
34
- font-size: 0.875rem;
35
6
  }
36
7
 
37
8
  .k-lft-buttons {
38
9
  display: flex;
39
10
  flex-direction: row;
40
11
  align-items: center;
12
+ gap: 0.25rem;
41
13
  }
42
14
 
43
15
  .k-lft-modal-body {
@@ -34,8 +34,7 @@
34
34
 
35
35
  .k-recovery-code-grid-index {
36
36
  font-size: 0.75rem;
37
- color: var(--text-color);
38
- opacity: 0.55;
37
+ color: var(--text-color-secondary);
39
38
  min-width: 1.5rem;
40
39
  text-align: right;
41
40
  }
@@ -92,5 +92,5 @@
92
92
  }
93
93
 
94
94
  .k-stat-card-interactive:hover .k-stat-card-trailing {
95
- color: var(--primary-color);
95
+ color: var(--primary-color-text);
96
96
  }
@@ -8,7 +8,18 @@
8
8
 
9
9
  .k-table th,
10
10
  .k-table td {
11
- padding: 0.5rem;
11
+ padding: 0.5rem 0.75rem;
12
+ }
13
+
14
+ /* Keep text clear of the table's outer edges. */
15
+ .k-table th:first-child,
16
+ .k-table td:first-child {
17
+ padding-left: 1rem;
18
+ }
19
+
20
+ .k-table th:last-child,
21
+ .k-table td:last-child {
22
+ padding-right: 1rem;
12
23
  }
13
24
 
14
25
  .k-table th {
@@ -21,9 +21,11 @@ const TagCard = (props: TagCardProps) => {
21
21
  {props.tags.length ? (
22
22
  <ul className="k-tag-card-list">
23
23
  {props.tags.map((t) => (
24
- <Badge key={t} variant={color} size="md" pill>
25
- {t}
26
- </Badge>
24
+ <li key={t}>
25
+ <Badge variant={color} size="md" pill>
26
+ {t}
27
+ </Badge>
28
+ </li>
27
29
  ))}
28
30
  </ul>
29
31
  ) : (