@backstage/ui 0.15.0 → 0.16.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 (64) hide show
  1. package/CHANGELOG.md +243 -0
  2. package/dist/components/Combobox/Combobox.esm.js +150 -52
  3. package/dist/components/Combobox/Combobox.esm.js.map +1 -1
  4. package/dist/components/Combobox/Combobox.module.css.esm.js +2 -2
  5. package/dist/components/Combobox/ComboboxItem.esm.js +76 -0
  6. package/dist/components/Combobox/ComboboxItem.esm.js.map +1 -0
  7. package/dist/components/Combobox/ComboboxListBox.esm.js +215 -17
  8. package/dist/components/Combobox/ComboboxListBox.esm.js.map +1 -1
  9. package/dist/components/Combobox/definition.esm.js +62 -3
  10. package/dist/components/Combobox/definition.esm.js.map +1 -1
  11. package/dist/components/Combobox/useAsyncComboboxState.esm.js +133 -0
  12. package/dist/components/Combobox/useAsyncComboboxState.esm.js.map +1 -0
  13. package/dist/components/Header/Header.module.css.esm.js +2 -2
  14. package/dist/components/Header/HeaderNav.esm.js +0 -1
  15. package/dist/components/Header/HeaderNav.esm.js.map +1 -1
  16. package/dist/components/NumberField/NumberField.esm.js +113 -0
  17. package/dist/components/NumberField/NumberField.esm.js.map +1 -0
  18. package/dist/components/NumberField/NumberField.module.css.esm.js +8 -0
  19. package/dist/components/NumberField/NumberField.module.css.esm.js.map +1 -0
  20. package/dist/components/NumberField/definition.esm.js +34 -0
  21. package/dist/components/NumberField/definition.esm.js.map +1 -0
  22. package/dist/components/Select/Select.esm.js +87 -19
  23. package/dist/components/Select/Select.esm.js.map +1 -1
  24. package/dist/components/Select/Select.module.css.esm.js +2 -2
  25. package/dist/components/Select/SelectContent.esm.js +70 -18
  26. package/dist/components/Select/SelectContent.esm.js.map +1 -1
  27. package/dist/components/Select/SelectItem.esm.js +76 -0
  28. package/dist/components/Select/SelectItem.esm.js.map +1 -0
  29. package/dist/components/Select/SelectListBox.esm.js +175 -19
  30. package/dist/components/Select/SelectListBox.esm.js.map +1 -1
  31. package/dist/components/Select/SelectTrigger.esm.js +1 -1
  32. package/dist/components/Select/SelectTrigger.esm.js.map +1 -1
  33. package/dist/components/Select/definition.esm.js +72 -9
  34. package/dist/components/Select/definition.esm.js.map +1 -1
  35. package/dist/components/Skeleton/Skeleton.module.css.esm.js +2 -2
  36. package/dist/components/Skeleton/definition.esm.js +1 -0
  37. package/dist/components/Skeleton/definition.esm.js.map +1 -1
  38. package/dist/components/Switch/Switch.esm.js +17 -5
  39. package/dist/components/Switch/Switch.esm.js.map +1 -1
  40. package/dist/components/Switch/Switch.module.css.esm.js +2 -2
  41. package/dist/components/Switch/definition.esm.js +1 -0
  42. package/dist/components/Switch/definition.esm.js.map +1 -1
  43. package/dist/components/Table/Table.module.css.esm.js +2 -2
  44. package/dist/components/Table/components/Table.esm.js +60 -57
  45. package/dist/components/Table/components/Table.esm.js.map +1 -1
  46. package/dist/components/Table/definition.esm.js +2 -1
  47. package/dist/components/Table/definition.esm.js.map +1 -1
  48. package/dist/components/TablePagination/TablePagination.esm.js +4 -1
  49. package/dist/components/TablePagination/TablePagination.esm.js.map +1 -1
  50. package/dist/components/Tabs/TabsIndicators.esm.js +155 -108
  51. package/dist/components/Tabs/TabsIndicators.esm.js.map +1 -1
  52. package/dist/css/styles.css +184 -68
  53. package/dist/hooks/useCollectionAdapter.esm.js +67 -0
  54. package/dist/hooks/useCollectionAdapter.esm.js.map +1 -0
  55. package/dist/hooks/useDelayedVisibility.esm.js +17 -0
  56. package/dist/hooks/useDelayedVisibility.esm.js.map +1 -0
  57. package/dist/hooks/useTrackedSelectionKeys.esm.js +23 -0
  58. package/dist/hooks/useTrackedSelectionKeys.esm.js.map +1 -0
  59. package/dist/index.d.ts +805 -77
  60. package/dist/index.esm.js +7 -2
  61. package/dist/index.esm.js.map +1 -1
  62. package/dist/utils/selectableCollection.esm.js +75 -0
  63. package/dist/utils/selectableCollection.esm.js.map +1 -0
  64. package/package.json +3 -3
@@ -85,150 +85,266 @@
85
85
  --bui-radius-6: calc(1.25rem);
86
86
  --bui-radius-full: 9999px;
87
87
 
88
+ /* Animations */
89
+ --bui-animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
90
+
88
91
  /* Base Colors */
89
92
  --bui-black: #000000;
90
93
  --bui-white: #ffffff;
94
+ --bui-ring: #1f5493;
95
+ --bui-scrollbar: #a0a0a03b;
96
+ --bui-scrollbar-thumb: #a0a0a0;
97
+
98
+ /* Gray scale */
99
+ --bui-gray-1: #fafafa;
100
+ --bui-gray-2: #f5f5f5;
101
+ --bui-gray-3: #e5e5e5;
102
+ --bui-gray-4: #d4d4d4;
103
+ --bui-gray-5: #a1a1a1;
104
+ --bui-gray-6: #737373;
105
+ --bui-gray-7: #525252;
106
+ --bui-gray-8: #404040;
107
+ --bui-gray-9: #262626;
108
+ --bui-gray-10: #171717;
109
+ --bui-gray-11: #0a0a0a;
110
+
111
+ /* Neutral background colors */
112
+ --bui-bg-app: #f5f5f5;
113
+ --bui-bg-neutral-1: var(--bui-white);
114
+ --bui-bg-neutral-2: #f7f7f7;
115
+ --bui-bg-neutral-3: var(--bui-white);
116
+ --bui-bg-neutral-4: #f7f7f7;
91
117
 
92
- /* Solid background colors */
118
+ /* Foreground colors */
119
+ --bui-fg-primary: var(--bui-black);
120
+ --bui-fg-secondary: #696969;
121
+ --bui-fg-disabled: #aaaaaa;
122
+ --bui-fg-positive: #1abc54;
123
+ --bui-fg-negative: #ee3a4c;
124
+ --bui-fg-warning: #f18900;
125
+ --bui-fg-announcement: #2a86f3;
126
+
127
+ /* Border colors */
128
+ --bui-border-1: var(--bui-gray-3);
129
+ --bui-border-2: var(--bui-gray-6);
130
+
131
+ /* Accent */
132
+ --bui-accent-bg: #1f5493;
133
+ --bui-accent-bg-hover: #163a66;
134
+ --bui-accent-bg-disabled: #163a66;
135
+ --bui-accent-fg: #ffffff;
136
+ --bui-accent-fg-disabled: #98a8bc;
137
+
138
+ /* Announcement */
139
+ --bui-announcement-bg: #0d72ea;
140
+ --bui-announcement-bg-hover: #2b88f5;
141
+ --bui-announcement-bg-disabled: #0d72ea;
142
+ --bui-announcement-bg-subdued: #c8e0fc;
143
+ --bui-announcement-bg-subdued-hover: #e0edfd;
144
+ --bui-announcement-bg-subdued-disabled: #c8e0fc;
145
+ --bui-announcement-border: #549ef4;
146
+ --bui-announcement-fg: #ffffff;
147
+ --bui-announcement-fg-disabled: #a4ccfb;
148
+ --bui-announcement-fg-subdued: #173da6;
149
+ --bui-announcement-fg-subdued-disabled: #7391e3;
150
+
151
+ /* Warning */
152
+ --bui-warning-bg: #ff9400;
153
+ --bui-warning-bg-hover: #ffb656;
154
+ --bui-warning-bg-disabled: #ff9400;
155
+ --bui-warning-bg-subdued: #ffedd5;
156
+ --bui-warning-bg-subdued-hover: #f8ddb9;
157
+ --bui-warning-bg-subdued-disabled: #f8ddb9;
158
+ --bui-warning-border: #ff9400;
159
+ --bui-warning-fg: #000000;
160
+ --bui-warning-fg-disabled: #aa4d00;
161
+ --bui-warning-fg-subdued: #aa4d00;
162
+ --bui-warning-fg-subdued-disabled: #e3a572;
163
+
164
+ /* Negative */
165
+ --bui-negative-bg: #dc2626;
166
+ --bui-negative-bg-hover: #f44b4b;
167
+ --bui-negative-bg-disabled: #ff8686;
168
+ --bui-negative-bg-subdued: #ffcbcb;
169
+ --bui-negative-bg-subdued-hover: #f7b6b6;
170
+ --bui-negative-bg-subdued-disabled: #f7b6b6;
171
+ --bui-negative-border: #d06565;
172
+ --bui-negative-fg: #ffffff;
173
+ --bui-negative-fg-disabled: #f7b6b6;
174
+ --bui-negative-fg-subdued: #991919;
175
+ --bui-negative-fg-subdued-disabled: #bb7272;
176
+
177
+ /* Positive */
178
+ --bui-positive-bg: #1ed760;
179
+ --bui-positive-bg-hover: #3be477;
180
+ --bui-positive-bg-disabled: #1abc54;
181
+ --bui-positive-bg-subdued: #96f0b6;
182
+ --bui-positive-bg-subdued-hover: #c5f7d7;
183
+ --bui-positive-bg-subdued-disabled: #96f0b6;
184
+ --bui-positive-border: #54a671;
185
+ --bui-positive-fg: #000000;
186
+ --bui-positive-fg-disabled: #0c632b;
187
+ --bui-positive-fg-subdued: #0c632b;
188
+ --bui-positive-fg-subdued-disabled: #4b9666;
189
+
190
+ /* Deprecated tokens */
93
191
  --bui-bg-solid: #1f5493;
94
192
  --bui-bg-solid-hover: #163a66;
95
193
  --bui-bg-solid-pressed: #0f2b4e;
96
194
  --bui-bg-solid-disabled: #163a66;
97
-
98
- /* Neutral background colors */
99
- --bui-bg-app: #f8f8f8;
100
-
101
- --bui-bg-neutral-1: #fff;
102
195
  --bui-bg-neutral-1-hover: oklch(0% 0 0 / 6%);
103
196
  --bui-bg-neutral-1-pressed: oklch(0% 0 0 / 12%);
104
197
  --bui-bg-neutral-1-disabled: oklch(0% 0 0 / 6%);
105
-
106
- --bui-bg-neutral-2: oklch(0% 0 0 / 6%);
107
198
  --bui-bg-neutral-2-hover: oklch(0% 0 0 / 12%);
108
199
  --bui-bg-neutral-2-pressed: oklch(0% 0 0 / 16%);
109
200
  --bui-bg-neutral-2-disabled: oklch(0% 0 0 / 6%);
110
-
111
- --bui-bg-neutral-3: oklch(0% 0 0 / 6%);
112
201
  --bui-bg-neutral-3-hover: oklch(0% 0 0 / 12%);
113
202
  --bui-bg-neutral-3-pressed: oklch(0% 0 0 / 16%);
114
203
  --bui-bg-neutral-3-disabled: oklch(0% 0 0 / 6%);
115
-
116
- --bui-bg-neutral-4: oklch(0% 0 0 / 6%);
117
204
  --bui-bg-neutral-4-hover: oklch(0% 0 0 / 12%);
118
205
  --bui-bg-neutral-4-pressed: oklch(0% 0 0 / 16%);
119
206
  --bui-bg-neutral-4-disabled: oklch(0% 0 0 / 6%);
120
-
121
- /* Status background colors */
122
207
  --bui-bg-danger: #ffe2e2;
123
208
  --bui-bg-warning: #ffedd5;
124
209
  --bui-bg-success: #dcfce7;
125
210
  --bui-bg-info: #dbeafe;
126
-
127
- /* Foreground colors */
128
- --bui-fg-primary: var(--bui-black);
129
- --bui-fg-secondary: oklch(0% 0 0 / 50%);
130
- --bui-fg-disabled: oklch(0% 0 0 / 28%);
131
211
  --bui-fg-solid: var(--bui-white);
132
212
  --bui-fg-solid-disabled: #98a8bc;
133
-
134
- /* Foreground Statuses */
135
213
  --bui-fg-danger-on-bg: #991919;
136
214
  --bui-fg-warning-on-bg: #92310a;
137
215
  --bui-fg-success-on-bg: #116932;
138
216
  --bui-fg-info-on-bg: #173da6;
139
217
  --bui-fg-danger: #ec3b18;
140
- --bui-fg-warning: #ef7a32;
141
218
  --bui-fg-success: #1aaf4f;
142
219
  --bui-fg-info: #0d74ce;
143
-
144
- /* Border colors */
145
- --bui-border-1: #d5d5d5;
146
- --bui-border-2: #bababa;
147
220
  --bui-border-info: #7ea9d6;
148
221
  --bui-border-danger: #f87a7a;
149
222
  --bui-border-warning: #e36d05;
150
223
  --bui-border-success: #53db83;
151
-
152
- /* Special colors */
153
- --bui-ring: #1f5493;
154
- --bui-scrollbar: #a0a0a03b;
155
- --bui-scrollbar-thumb: #a0a0a0;
156
-
157
- /* Shadows */
158
224
  --bui-shadow: 0 10px 15px -3px rgba(0 0 0 / 0.1),
159
225
  0 4px 6px -4px rgba(0 0 0 / 0.1);
160
-
161
- --bui-animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
162
226
  }
163
227
 
164
228
  /* Dark theme tokens */
165
229
  [data-theme-mode='dark'] {
166
- /* Solid background colors */
167
- --bui-bg-solid: #9cc9ff;
168
- --bui-bg-solid-hover: #83b9fd;
169
- --bui-bg-solid-pressed: #83b9fd;
170
- --bui-bg-solid-disabled: #1b3d68;
230
+ /* Base colors */
231
+ --bui-ring: #1f5493;
232
+ --bui-scrollbar: #3636363a;
233
+ --bui-scrollbar-thumb: #575757;
171
234
 
172
235
  /* Neutral background colors */
173
236
  --bui-bg-app: #333333;
237
+ --bui-bg-neutral-1: #424141;
238
+ --bui-bg-neutral-2: #504f4f;
239
+ --bui-bg-neutral-3: #5c5c5c;
240
+ --bui-bg-neutral-4: #676767;
174
241
 
175
- --bui-bg-neutral-1: oklch(100% 0 0 / 10%);
242
+ /* Foreground colors */
243
+ --bui-fg-primary: var(--bui-white);
244
+ --bui-fg-secondary: #a3a3a3;
245
+ --bui-fg-disabled: #707070;
246
+ --bui-fg-positive: #21b656;
247
+ --bui-fg-negative: #ee3a4c;
248
+ --bui-fg-warning: #f18900;
249
+ --bui-fg-announcement: #2a86f3;
250
+
251
+ /* Border colors */
252
+ --bui-border-1: var(--bui-gray-6);
253
+ --bui-border-2: var(--bui-gray-5);
254
+
255
+ /* Accent */
256
+ --bui-accent-bg: #9cc9ff;
257
+ --bui-accent-bg-hover: #83b9fd;
258
+ --bui-accent-bg-disabled: #3b6293;
259
+ --bui-accent-fg: #101821;
260
+ --bui-accent-fg-disabled: #6191cc;
261
+
262
+ /* Announcement */
263
+ --bui-announcement-bg: #0d72ea;
264
+ --bui-announcement-bg-hover: #2b88f5;
265
+ --bui-announcement-bg-disabled: #0d72ea;
266
+ --bui-announcement-bg-subdued: #052a56;
267
+ --bui-announcement-bg-subdued-hover: #0c3d77;
268
+ --bui-announcement-bg-subdued-disabled: #052a56;
269
+ --bui-announcement-border: #2363af;
270
+ --bui-announcement-fg: #ffffff;
271
+ --bui-announcement-fg-disabled: #70b8ff;
272
+ --bui-announcement-fg-subdued: #70b8ff;
273
+ --bui-announcement-fg-subdued-disabled: #4275a6;
274
+
275
+ /* Warning */
276
+ --bui-warning-bg: #ffa42c;
277
+ --bui-warning-bg-hover: #ffb656;
278
+ --bui-warning-bg-disabled: #ffb656;
279
+ --bui-warning-bg-subdued: #491e00;
280
+ --bui-warning-bg-subdued-hover: #612901;
281
+ --bui-warning-bg-subdued-disabled: #582400;
282
+ --bui-warning-border: #7c4903;
283
+ --bui-warning-fg: #000000;
284
+ --bui-warning-fg-disabled: #aa4d00;
285
+ --bui-warning-fg-subdued: #f2ab4a;
286
+ --bui-warning-fg-subdued-disabled: #aa4d00;
287
+
288
+ /* Negative */
289
+ --bui-negative-bg: #dc2626;
290
+ --bui-negative-bg-hover: #f44b4b;
291
+ --bui-negative-bg-disabled: #dc2626;
292
+ --bui-negative-bg-subdued: #350708;
293
+ --bui-negative-bg-subdued-hover: #5e1a1b;
294
+ --bui-negative-bg-subdued-disabled: #5e1a1b;
295
+ --bui-negative-border: #6f101c;
296
+ --bui-negative-fg: #ffffff;
297
+ --bui-negative-fg-disabled: #f7b6b6;
298
+ --bui-negative-fg-subdued: #fca5a5;
299
+ --bui-negative-fg-subdued-disabled: #bb7272;
300
+
301
+ /* Positive */
302
+ --bui-positive-bg: #1ed760;
303
+ --bui-positive-bg-hover: #3be477;
304
+ --bui-positive-bg-disabled: #1abc54;
305
+ --bui-positive-bg-subdued: #073116;
306
+ --bui-positive-bg-subdued-hover: #0b431f;
307
+ --bui-positive-bg-subdued-disabled: #073116;
308
+ --bui-positive-border: #136d33;
309
+ --bui-positive-fg: #000000;
310
+ --bui-positive-fg-disabled: #0c632b;
311
+ --bui-positive-fg-subdued: #8ddeaa;
312
+ --bui-positive-fg-subdued-disabled: #509c6b;
313
+
314
+ /* Deprecated tokens */
315
+ --bui-bg-solid: #9cc9ff;
316
+ --bui-bg-solid-hover: #83b9fd;
317
+ --bui-bg-solid-pressed: #83b9fd;
318
+ --bui-bg-solid-disabled: #1b3d68;
176
319
  --bui-bg-neutral-1-hover: oklch(100% 0 0 / 14%);
177
320
  --bui-bg-neutral-1-pressed: oklch(100% 0 0 / 20%);
178
321
  --bui-bg-neutral-1-disabled: oklch(100% 0 0 / 10%);
179
-
180
- --bui-bg-neutral-2: oklch(100% 0 0 / 6%);
181
322
  --bui-bg-neutral-2-hover: oklch(100% 0 0 / 10%);
182
323
  --bui-bg-neutral-2-pressed: oklch(100% 0 0 / 16%);
183
324
  --bui-bg-neutral-2-disabled: oklch(100% 0 0 / 6%);
184
-
185
- --bui-bg-neutral-3: oklch(100% 0 0 / 8%);
186
325
  --bui-bg-neutral-3-hover: oklch(100% 0 0 / 12%);
187
326
  --bui-bg-neutral-3-pressed: oklch(100% 0 0 / 20%);
188
327
  --bui-bg-neutral-3-disabled: oklch(100% 0 0 / 8%);
189
-
190
- --bui-bg-neutral-4: oklch(100% 0 0 / 8%);
191
328
  --bui-bg-neutral-4-hover: oklch(100% 0 0 / 12%);
192
329
  --bui-bg-neutral-4-pressed: oklch(100% 0 0 / 20%);
193
330
  --bui-bg-neutral-4-disabled: oklch(100% 0 0 / 8%);
194
-
195
- /* Status background colors */
196
331
  --bui-bg-danger: #300c0c;
197
332
  --bui-bg-warning: #302008;
198
333
  --bui-bg-success: #042713;
199
334
  --bui-bg-info: #132049;
200
-
201
- /* Foreground colors */
202
- --bui-fg-primary: var(--bui-white);
203
- --bui-fg-secondary: oklch(100% 0 0 / 50%);
204
- --bui-fg-disabled: oklch(100% 0 0 / 28%);
205
335
  --bui-fg-solid: #101821;
206
336
  --bui-fg-solid-disabled: #6191cc;
207
-
208
- /* Foreground statuses */
209
337
  --bui-fg-danger-on-bg: #fca5a5;
210
338
  --bui-fg-warning-on-bg: #fdba74;
211
339
  --bui-fg-success-on-bg: #86efac;
212
340
  --bui-fg-info-on-bg: #a3cfff;
213
341
  --bui-fg-danger: #ff5a30;
214
- --bui-fg-warning: #ffa057;
215
342
  --bui-fg-success: #1ed760;
216
343
  --bui-fg-info: #70b8ff;
217
-
218
- /* Border colors */
219
- --bui-border-1: #434343;
220
- --bui-border-2: #585858;
221
344
  --bui-border-info: #7ea9d6;
222
345
  --bui-border-danger: #f87a7a;
223
346
  --bui-border-warning: #e36d05;
224
347
  --bui-border-success: #53db83;
225
-
226
- /* Special colors */
227
- --bui-ring: #1f5493;
228
- --bui-scrollbar: #3636363a;
229
- --bui-scrollbar-thumb: #575757;
230
-
231
- /* Shadows */
232
348
  --bui-shadow: none;
233
349
  }
234
350
  }
@@ -0,0 +1,67 @@
1
+ import { useRef } from 'react';
2
+ import { isAsyncListSource, getItemKeys } from '../utils/selectableCollection.esm.js';
3
+
4
+ function useCollectionAdapter({
5
+ items,
6
+ selectedKeys = [],
7
+ search,
8
+ loading,
9
+ retainSelectedItems = true
10
+ }) {
11
+ const retainedItems = useRef(/* @__PURE__ */ new Map());
12
+ let asyncSource;
13
+ let sourceItems;
14
+ if (isAsyncListSource(items)) {
15
+ asyncSource = items;
16
+ sourceItems = [...items.items];
17
+ } else {
18
+ sourceItems = Array.from(items);
19
+ }
20
+ const selectedIdSet = new Set(selectedKeys);
21
+ const searchProps = typeof search === "object" ? search : void 0;
22
+ const isServerSearch = searchProps?.mode === "server";
23
+ for (const key of retainedItems.current.keys()) {
24
+ if (!selectedIdSet.has(key)) {
25
+ retainedItems.current.delete(key);
26
+ }
27
+ }
28
+ for (const item of sourceItems) {
29
+ if (selectedIdSet.has(item.id)) {
30
+ retainedItems.current.set(item.id, item);
31
+ }
32
+ }
33
+ const sourceIds = getItemKeys(sourceItems);
34
+ const shouldRetainSelectedItems = isServerSearch && retainSelectedItems;
35
+ let canonicalItems = sourceItems;
36
+ let visibleIds;
37
+ if (shouldRetainSelectedItems) {
38
+ const missingSelectedItems = Array.from(retainedItems.current.entries()).filter(([id]) => !sourceIds.has(id)).map(([, item]) => item);
39
+ canonicalItems = [...sourceItems, ...missingSelectedItems];
40
+ visibleIds = sourceIds;
41
+ }
42
+ let resolvedLoading = loading;
43
+ if (asyncSource) {
44
+ resolvedLoading = {
45
+ state: asyncSource.loadingState,
46
+ onLoadMore: asyncSource.loadMore
47
+ };
48
+ }
49
+ let inputValue = searchProps?.inputValue;
50
+ let onInputChange = searchProps?.onInputChange;
51
+ if (asyncSource && isServerSearch) {
52
+ inputValue = asyncSource.filterText;
53
+ onInputChange = asyncSource.setFilterText;
54
+ }
55
+ return {
56
+ canonicalItems,
57
+ visibleIds,
58
+ loading: resolvedLoading,
59
+ isStale: sourceItems.length > 0 && (resolvedLoading?.state === "filtering" || resolvedLoading?.state === "sorting"),
60
+ inputValue,
61
+ defaultInputValue: searchProps?.defaultInputValue,
62
+ onInputChange
63
+ };
64
+ }
65
+
66
+ export { useCollectionAdapter };
67
+ //# sourceMappingURL=useCollectionAdapter.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCollectionAdapter.esm.js","sources":["../../src/hooks/useCollectionAdapter.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useRef } from 'react';\nimport type { Key } from 'react-aria-components';\nimport type {\n AsyncListSource,\n CollectionItem,\n LoadingConfig,\n} from '../types/selectableCollection';\nimport { getItemKeys, isAsyncListSource } from '../utils/selectableCollection';\n\ntype CollectionAdapterSearch =\n | true\n | {\n mode?: 'client' | 'server';\n inputValue?: string;\n defaultInputValue?: string;\n onInputChange?: (value: string) => void;\n };\n\ntype UseCollectionAdapterProps<T extends CollectionItem> = {\n items: Iterable<T> | AsyncListSource<T>;\n selectedKeys?: Iterable<Key>;\n search?: CollectionAdapterSearch;\n loading?: LoadingConfig;\n retainSelectedItems?: boolean;\n};\n\n/** @internal */\nexport type CollectionAdapterResult<T extends CollectionItem> = {\n canonicalItems: T[];\n visibleIds?: Set<Key>;\n loading?: LoadingConfig;\n isStale: boolean;\n inputValue?: string;\n defaultInputValue?: string;\n onInputChange?: (value: string) => void;\n};\n\n/** @internal */\nexport function useCollectionAdapter<T extends CollectionItem>({\n items,\n selectedKeys = [],\n search,\n loading,\n retainSelectedItems = true,\n}: UseCollectionAdapterProps<T>): CollectionAdapterResult<T> {\n const retainedItems = useRef(new Map<Key, T>());\n let asyncSource: AsyncListSource<T> | undefined;\n let sourceItems: T[];\n\n if (isAsyncListSource(items)) {\n asyncSource = items;\n sourceItems = [...items.items];\n } else {\n sourceItems = Array.from(items);\n }\n\n const selectedIdSet = new Set(selectedKeys);\n const searchProps = typeof search === 'object' ? search : undefined;\n const isServerSearch = searchProps?.mode === 'server';\n\n for (const key of retainedItems.current.keys()) {\n if (!selectedIdSet.has(key)) {\n retainedItems.current.delete(key);\n }\n }\n\n for (const item of sourceItems) {\n if (selectedIdSet.has(item.id)) {\n retainedItems.current.set(item.id, item);\n }\n }\n\n const sourceIds = getItemKeys(sourceItems);\n const shouldRetainSelectedItems = isServerSearch && retainSelectedItems;\n let canonicalItems = sourceItems;\n let visibleIds: Set<Key> | undefined;\n\n if (shouldRetainSelectedItems) {\n const missingSelectedItems = Array.from(retainedItems.current.entries())\n .filter(([id]) => !sourceIds.has(id))\n .map(([, item]) => item);\n\n canonicalItems = [...sourceItems, ...missingSelectedItems];\n visibleIds = sourceIds;\n }\n\n let resolvedLoading = loading;\n if (asyncSource) {\n resolvedLoading = {\n state: asyncSource.loadingState,\n onLoadMore: asyncSource.loadMore,\n };\n }\n\n let inputValue = searchProps?.inputValue;\n let onInputChange = searchProps?.onInputChange;\n if (asyncSource && isServerSearch) {\n inputValue = asyncSource.filterText;\n onInputChange = asyncSource.setFilterText;\n }\n\n return {\n canonicalItems,\n visibleIds,\n loading: resolvedLoading,\n isStale:\n sourceItems.length > 0 &&\n (resolvedLoading?.state === 'filtering' ||\n resolvedLoading?.state === 'sorting'),\n inputValue,\n defaultInputValue: searchProps?.defaultInputValue,\n onInputChange,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { useState, useEffect } from 'react';
2
+
3
+ function useDelayedVisibility(isVisible, delayMs) {
4
+ const [isDelayedVisible, setIsDelayedVisible] = useState(false);
5
+ useEffect(() => {
6
+ if (!isVisible) {
7
+ setIsDelayedVisible(false);
8
+ return void 0;
9
+ }
10
+ const timer = setTimeout(() => setIsDelayedVisible(true), delayMs);
11
+ return () => clearTimeout(timer);
12
+ }, [delayMs, isVisible]);
13
+ return isVisible && isDelayedVisible;
14
+ }
15
+
16
+ export { useDelayedVisibility };
17
+ //# sourceMappingURL=useDelayedVisibility.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDelayedVisibility.esm.js","sources":["../../src/hooks/useDelayedVisibility.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useEffect, useState } from 'react';\n\n/** @internal */\nexport function useDelayedVisibility(isVisible: boolean, delayMs: number) {\n const [isDelayedVisible, setIsDelayedVisible] = useState(false);\n\n useEffect(() => {\n if (!isVisible) {\n setIsDelayedVisible(false);\n return undefined;\n }\n\n const timer = setTimeout(() => setIsDelayedVisible(true), delayMs);\n return () => clearTimeout(timer);\n }, [delayMs, isVisible]);\n\n return isVisible && isDelayedVisible;\n}\n"],"names":[],"mappings":";;AAmBO,SAAS,oBAAA,CAAqB,WAAoB,OAAA,EAAiB;AACxE,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAAS,KAAK,CAAA;AAE9D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,SAAA,EAAW;AACd,MAAA,mBAAA,CAAoB,KAAK,CAAA;AACzB,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,mBAAA,CAAoB,IAAI,GAAG,OAAO,CAAA;AACjE,IAAA,OAAO,MAAM,aAAa,KAAK,CAAA;AAAA,EACjC,CAAA,EAAG,CAAC,OAAA,EAAS,SAAS,CAAC,CAAA;AAEvB,EAAA,OAAO,SAAA,IAAa,gBAAA;AACtB;;;;"}
@@ -0,0 +1,23 @@
1
+ import { useState, useCallback } from 'react';
2
+
3
+ function useTrackedSelectionKeys({
4
+ selectedKeys,
5
+ defaultSelectedKeys,
6
+ onSelectionChange
7
+ }) {
8
+ const [uncontrolledSelectedKeys, setUncontrolledSelectedKeys] = useState(() => defaultSelectedKeys ?? /* @__PURE__ */ new Set());
9
+ const handleSelectionChange = useCallback(
10
+ (keys) => {
11
+ setUncontrolledSelectedKeys(keys);
12
+ onSelectionChange?.(keys);
13
+ },
14
+ [onSelectionChange]
15
+ );
16
+ return {
17
+ selectedKeys: selectedKeys ?? uncontrolledSelectedKeys,
18
+ onSelectionChange: handleSelectionChange
19
+ };
20
+ }
21
+
22
+ export { useTrackedSelectionKeys };
23
+ //# sourceMappingURL=useTrackedSelectionKeys.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTrackedSelectionKeys.esm.js","sources":["../../src/hooks/useTrackedSelectionKeys.ts"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useCallback, useState } from 'react';\nimport type { Key, Selection } from 'react-aria-components';\n\ntype UseTrackedSelectionKeysProps = {\n selectedKeys?: Selection;\n defaultSelectedKeys?: Selection;\n onSelectionChange?: (keys: Selection) => void;\n};\n\nexport function useTrackedSelectionKeys({\n selectedKeys,\n defaultSelectedKeys,\n onSelectionChange,\n}: UseTrackedSelectionKeysProps) {\n const [uncontrolledSelectedKeys, setUncontrolledSelectedKeys] =\n useState<Selection>(() => defaultSelectedKeys ?? new Set<Key>());\n\n const handleSelectionChange = useCallback(\n (keys: Selection) => {\n setUncontrolledSelectedKeys(keys);\n onSelectionChange?.(keys);\n },\n [onSelectionChange],\n );\n\n return {\n selectedKeys: selectedKeys ?? uncontrolledSelectedKeys,\n onSelectionChange: handleSelectionChange,\n };\n}\n"],"names":[],"mappings":";;AAyBO,SAAS,uBAAA,CAAwB;AAAA,EACtC,YAAA;AAAA,EACA,mBAAA;AAAA,EACA;AACF,CAAA,EAAiC;AAC/B,EAAA,MAAM,CAAC,0BAA0B,2BAA2B,CAAA,GAC1D,SAAoB,MAAM,mBAAA,oBAAuB,IAAI,GAAA,EAAU,CAAA;AAEjE,EAAA,MAAM,qBAAA,GAAwB,WAAA;AAAA,IAC5B,CAAC,IAAA,KAAoB;AACnB,MAAA,2BAAA,CAA4B,IAAI,CAAA;AAChC,MAAA,iBAAA,GAAoB,IAAI,CAAA;AAAA,IAC1B,CAAA;AAAA,IACA,CAAC,iBAAiB;AAAA,GACpB;AAEA,EAAA,OAAO;AAAA,IACL,cAAc,YAAA,IAAgB,wBAAA;AAAA,IAC9B,iBAAA,EAAmB;AAAA,GACrB;AACF;;;;"}