@primer/components 30.3.0-rc.2010c7d4 → 30.3.0-rc.9dbc85a9

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 (97) hide show
  1. package/CHANGELOG.md +4 -2
  2. package/dist/browser.esm.js +717 -718
  3. package/dist/browser.esm.js.map +1 -1
  4. package/dist/browser.umd.js +320 -321
  5. package/dist/browser.umd.js.map +1 -1
  6. package/docs/content/Autocomplete.mdx +627 -0
  7. package/docs/content/TextInputTokens.mdx +89 -0
  8. package/docs/src/@primer/gatsby-theme-doctocat/nav.yml +2 -0
  9. package/lib/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  10. package/lib/AnchoredOverlay/AnchoredOverlay.js +11 -3
  11. package/lib/Autocomplete/Autocomplete.d.ts +304 -0
  12. package/lib/Autocomplete/Autocomplete.js +145 -0
  13. package/lib/Autocomplete/AutocompleteContext.d.ts +17 -0
  14. package/lib/Autocomplete/AutocompleteContext.js +11 -0
  15. package/lib/Autocomplete/AutocompleteInput.d.ts +292 -0
  16. package/lib/Autocomplete/AutocompleteInput.js +157 -0
  17. package/lib/Autocomplete/AutocompleteMenu.d.ts +72 -0
  18. package/lib/Autocomplete/AutocompleteMenu.js +224 -0
  19. package/lib/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  20. package/lib/Autocomplete/AutocompleteOverlay.js +80 -0
  21. package/lib/Autocomplete/index.d.ts +2 -0
  22. package/lib/Autocomplete/index.js +15 -0
  23. package/lib/FilteredActionList/FilteredActionList.js +5 -31
  24. package/lib/Overlay.d.ts +1 -0
  25. package/lib/Overlay.js +3 -1
  26. package/lib/__tests__/Autocomplete.test.d.ts +1 -0
  27. package/lib/__tests__/Autocomplete.test.js +528 -0
  28. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  29. package/lib/__tests__/behaviors/scrollIntoViewingArea.test.js +226 -0
  30. package/lib/behaviors/scrollIntoViewingArea.d.ts +1 -0
  31. package/lib/behaviors/scrollIntoViewingArea.js +39 -0
  32. package/lib/hooks/useOpenAndCloseFocus.d.ts +2 -1
  33. package/lib/hooks/useOpenAndCloseFocus.js +7 -2
  34. package/lib/hooks/useOverlay.d.ts +2 -1
  35. package/lib/hooks/useOverlay.js +4 -2
  36. package/lib/index.d.ts +2 -0
  37. package/lib/index.js +8 -0
  38. package/lib/stories/Autocomplete.stories.js +608 -0
  39. package/lib/utils/types/MandateProps.d.ts +3 -0
  40. package/lib/utils/types/MandateProps.js +1 -0
  41. package/lib/utils/types/index.d.ts +1 -0
  42. package/lib/utils/types/index.js +13 -0
  43. package/lib-esm/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  44. package/lib-esm/AnchoredOverlay/AnchoredOverlay.js +11 -3
  45. package/lib-esm/Autocomplete/Autocomplete.d.ts +304 -0
  46. package/lib-esm/Autocomplete/Autocomplete.js +123 -0
  47. package/lib-esm/Autocomplete/AutocompleteContext.d.ts +17 -0
  48. package/lib-esm/Autocomplete/AutocompleteContext.js +2 -0
  49. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +292 -0
  50. package/lib-esm/Autocomplete/AutocompleteInput.js +138 -0
  51. package/lib-esm/Autocomplete/AutocompleteMenu.d.ts +72 -0
  52. package/lib-esm/Autocomplete/AutocompleteMenu.js +205 -0
  53. package/lib-esm/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  54. package/lib-esm/Autocomplete/AutocompleteOverlay.js +62 -0
  55. package/lib-esm/Autocomplete/index.d.ts +2 -0
  56. package/lib-esm/Autocomplete/index.js +1 -0
  57. package/lib-esm/FilteredActionList/FilteredActionList.js +3 -31
  58. package/lib-esm/Overlay.d.ts +1 -0
  59. package/lib-esm/Overlay.js +3 -1
  60. package/lib-esm/__tests__/Autocomplete.test.d.ts +1 -0
  61. package/lib-esm/__tests__/Autocomplete.test.js +494 -0
  62. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.d.ts +1 -0
  63. package/lib-esm/__tests__/behaviors/scrollIntoViewingArea.test.js +224 -0
  64. package/lib-esm/behaviors/scrollIntoViewingArea.d.ts +1 -0
  65. package/lib-esm/behaviors/scrollIntoViewingArea.js +30 -0
  66. package/lib-esm/hooks/useOpenAndCloseFocus.d.ts +2 -1
  67. package/lib-esm/hooks/useOpenAndCloseFocus.js +7 -2
  68. package/lib-esm/hooks/useOverlay.d.ts +2 -1
  69. package/lib-esm/hooks/useOverlay.js +4 -2
  70. package/lib-esm/index.d.ts +2 -0
  71. package/lib-esm/index.js +1 -0
  72. package/lib-esm/stories/Autocomplete.stories.js +549 -0
  73. package/lib-esm/utils/types/MandateProps.d.ts +3 -0
  74. package/lib-esm/utils/types/MandateProps.js +1 -0
  75. package/lib-esm/utils/types/index.d.ts +1 -0
  76. package/lib-esm/utils/types/index.js +2 -1
  77. package/package.json +1 -1
  78. package/src/AnchoredOverlay/AnchoredOverlay.tsx +14 -3
  79. package/src/Autocomplete/Autocomplete.tsx +103 -0
  80. package/src/Autocomplete/AutocompleteContext.tsx +19 -0
  81. package/src/Autocomplete/AutocompleteInput.tsx +179 -0
  82. package/src/Autocomplete/AutocompleteMenu.tsx +341 -0
  83. package/src/Autocomplete/AutocompleteOverlay.tsx +68 -0
  84. package/src/Autocomplete/index.ts +2 -0
  85. package/src/FilteredActionList/FilteredActionList.tsx +10 -25
  86. package/src/Overlay.tsx +4 -1
  87. package/src/__tests__/Autocomplete.test.tsx +444 -0
  88. package/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +3414 -0
  89. package/src/__tests__/behaviors/scrollIntoViewingArea.test.ts +195 -0
  90. package/src/behaviors/scrollIntoViewingArea.ts +27 -0
  91. package/src/hooks/useOpenAndCloseFocus.ts +7 -2
  92. package/src/hooks/useOverlay.tsx +4 -2
  93. package/src/index.ts +2 -0
  94. package/src/stories/Autocomplete.stories.tsx +572 -0
  95. package/src/utils/types/MandateProps.ts +19 -0
  96. package/src/utils/types/index.ts +1 -0
  97. package/stats.html +1 -1
@@ -0,0 +1,549 @@
1
+ function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
+
3
+ import React, { useCallback, useRef, useState } from 'react';
4
+ import { BaseStyles, Box, Text, TextInput, ThemeProvider } from '..';
5
+ import TextInputTokens from '../TextInputWithTokens';
6
+ import Autocomplete from '../Autocomplete/Autocomplete';
7
+ import { AnchoredOverlay } from '../AnchoredOverlay';
8
+ import { ButtonInvisible } from '../Button';
9
+ const items = [{
10
+ text: 'zero',
11
+ id: 0
12
+ }, {
13
+ text: 'one',
14
+ id: 1
15
+ }, {
16
+ text: 'two',
17
+ id: 2
18
+ }, {
19
+ text: 'three',
20
+ id: 3
21
+ }, {
22
+ text: 'four',
23
+ id: 4
24
+ }, {
25
+ text: 'five',
26
+ id: 5
27
+ }, {
28
+ text: 'six',
29
+ id: 6
30
+ }, {
31
+ text: 'seven',
32
+ id: 7
33
+ }, {
34
+ text: 'twenty',
35
+ id: 20
36
+ }, {
37
+ text: 'twentyone',
38
+ id: 21
39
+ }];
40
+ const mockTokens = [{
41
+ text: 'zero',
42
+ id: 0
43
+ }, {
44
+ text: 'one',
45
+ id: 1
46
+ }, {
47
+ text: 'three',
48
+ id: 3
49
+ }, {
50
+ text: 'four',
51
+ id: 4
52
+ }];
53
+ export default {
54
+ title: 'Forms/Autocomplete',
55
+ decorators: [Story => {
56
+ const [lastKey, setLastKey] = useState('none');
57
+ const reportKey = useCallback(event => {
58
+ setLastKey(event.key);
59
+ }, []);
60
+ return /*#__PURE__*/React.createElement(ThemeProvider, null, /*#__PURE__*/React.createElement(BaseStyles, null, /*#__PURE__*/React.createElement(Box, {
61
+ onKeyDownCapture: reportKey
62
+ }, /*#__PURE__*/React.createElement(Box, {
63
+ position: "absolute",
64
+ right: 5,
65
+ top: 2
66
+ }, "Last key pressed: ", lastKey), /*#__PURE__*/React.createElement(Box, {
67
+ paddingTop: 5
68
+ }, /*#__PURE__*/React.createElement(Story, null)))));
69
+ }]
70
+ };
71
+ export const SingleSelect = () => {
72
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
73
+ as: "label",
74
+ display: "block",
75
+ htmlFor: "autocompleteInput",
76
+ id: "autocompleteLabel"
77
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
78
+ id: "autocompleteInput"
79
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
80
+ items: items,
81
+ selectedItemIds: [],
82
+ "aria-labelledby": "autocompleteLabel"
83
+ }))));
84
+ };
85
+ export const MultiSelect = () => {
86
+ const [selectedItemIds, setSelectedItemIds] = useState([]);
87
+
88
+ const onSelectedChange = newlySelectedItems => {
89
+ if (!Array.isArray(newlySelectedItems)) {
90
+ return;
91
+ }
92
+
93
+ setSelectedItemIds(newlySelectedItems.map(item => item.id));
94
+ };
95
+
96
+ const getItemById = id => items.find(item => item.id === id);
97
+
98
+ return /*#__PURE__*/React.createElement(Box, {
99
+ display: "flex",
100
+ sx: {
101
+ gap: '1em'
102
+ }
103
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Box, {
104
+ as: "label",
105
+ display: "block",
106
+ htmlFor: "autocompleteInput",
107
+ id: "autocompleteLabel"
108
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
109
+ id: "autocompleteInput"
110
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
111
+ items: items,
112
+ selectedItemIds: selectedItemIds,
113
+ "aria-labelledby": "autocompleteLabel",
114
+ onSelectedChange: onSelectedChange,
115
+ selectionVariant: "multiple"
116
+ })))), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", null, "Selected items:"), /*#__PURE__*/React.createElement(Box, {
117
+ as: "ul",
118
+ my: 0
119
+ }, selectedItemIds.map(selectedItemId => {
120
+ var _getItemById;
121
+
122
+ return /*#__PURE__*/React.createElement("li", {
123
+ key: selectedItemId
124
+ }, (_getItemById = getItemById(selectedItemId)) === null || _getItemById === void 0 ? void 0 : _getItemById.text);
125
+ }))));
126
+ };
127
+ MultiSelect.displayName = "MultiSelect";
128
+ export const MultiSelectWithTokenInput = () => {
129
+ const [tokens, setTokens] = useState(mockTokens);
130
+ const selectedTokenIds = tokens.map(token => token.id);
131
+ const [selectedItemIds, setSelectedItemIds] = useState(selectedTokenIds);
132
+
133
+ const onTokenRemove = tokenId => {
134
+ setTokens(tokens.filter(token => token.id !== tokenId));
135
+ setSelectedItemIds(selectedItemIds.filter(id => id !== tokenId));
136
+ };
137
+
138
+ const onSelectedChange = newlySelectedItems => {
139
+ if (!Array.isArray(newlySelectedItems)) {
140
+ return;
141
+ }
142
+
143
+ setSelectedItemIds(newlySelectedItems.map(item => item.id));
144
+
145
+ if (newlySelectedItems.length < selectedItemIds.length) {
146
+ const newlySelectedItemIds = newlySelectedItems.map(({
147
+ id
148
+ }) => id);
149
+ const removedItemIds = selectedTokenIds.filter(id => !newlySelectedItemIds.includes(id));
150
+
151
+ for (const removedItemId of removedItemIds) {
152
+ onTokenRemove(removedItemId);
153
+ }
154
+
155
+ return;
156
+ }
157
+
158
+ setTokens(newlySelectedItems.map(({
159
+ id,
160
+ text
161
+ }) => ({
162
+ id,
163
+ text
164
+ })));
165
+ };
166
+
167
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
168
+ as: "label",
169
+ display: "block",
170
+ htmlFor: "autocompleteInput",
171
+ id: "autocompleteLabel"
172
+ }, "Pick options"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
173
+ as: TextInputTokens,
174
+ tokens: tokens,
175
+ onTokenRemove: onTokenRemove,
176
+ id: "autocompleteInput"
177
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
178
+ items: items,
179
+ selectedItemIds: selectedItemIds,
180
+ onSelectedChange: onSelectedChange,
181
+ selectionVariant: "multiple",
182
+ "aria-labelledby": "autocompleteLabel"
183
+ }))));
184
+ };
185
+ export const MultiSelectAddNewItem = () => {
186
+ const [localItemsState, setLocalItemsState] = useState(items);
187
+ const [filterVal, setFilterVal] = useState('');
188
+ const [tokens, setTokens] = useState(mockTokens);
189
+ const selectedTokenIds = tokens.map(token => token.id);
190
+ const [selectedItemIds, setSelectedItemIds] = useState(selectedTokenIds);
191
+
192
+ const onTokenRemove = tokenId => {
193
+ setTokens(tokens.filter(token => token.id !== tokenId));
194
+ setSelectedItemIds(selectedItemIds.filter(id => id !== tokenId));
195
+ };
196
+
197
+ const onSelectedChange = newlySelectedItems => {
198
+ if (!Array.isArray(newlySelectedItems)) {
199
+ return;
200
+ }
201
+
202
+ setSelectedItemIds(newlySelectedItems.map(item => item.id));
203
+
204
+ if (newlySelectedItems.length < selectedItemIds.length) {
205
+ const newlySelectedItemIds = newlySelectedItems.map(({
206
+ id
207
+ }) => id);
208
+ const removedItemIds = selectedTokenIds.filter(id => !newlySelectedItemIds.includes(id));
209
+
210
+ for (const removedItemId of removedItemIds) {
211
+ onTokenRemove(removedItemId);
212
+ }
213
+
214
+ return;
215
+ }
216
+
217
+ setTokens(newlySelectedItems.map(({
218
+ id,
219
+ text
220
+ }) => ({
221
+ id,
222
+ text
223
+ })));
224
+ };
225
+
226
+ const onItemSelect = item => {
227
+ onSelectedChange([...selectedItemIds.map(id => items.find(selectedItem => selectedItem.id === id)), item]);
228
+
229
+ if (!localItemsState.some(localItem => localItem.id === item.id)) {
230
+ setLocalItemsState([...localItemsState, item]);
231
+ }
232
+ };
233
+
234
+ const handleChange = e => {
235
+ setFilterVal(e.currentTarget.value);
236
+ };
237
+
238
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
239
+ as: "label",
240
+ display: "block",
241
+ htmlFor: "autocompleteInput",
242
+ id: "autocompleteLabel"
243
+ }, "Pick options"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
244
+ as: TextInputTokens,
245
+ tokens: tokens,
246
+ onTokenRemove: onTokenRemove,
247
+ onChange: handleChange,
248
+ id: "autocompleteInput"
249
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
250
+ addNewItem: filterVal && !localItemsState.map(localItem => localItem.text).includes(filterVal) ? {
251
+ text: `Add '${filterVal}'`,
252
+ handleAddItem: item => {
253
+ onItemSelect({ ...item,
254
+ text: filterVal,
255
+ selected: true
256
+ });
257
+ setFilterVal('');
258
+ }
259
+ } : undefined,
260
+ items: localItemsState,
261
+ selectedItemIds: selectedItemIds,
262
+ onSelectedChange: onSelectedChange,
263
+ selectionVariant: "multiple",
264
+ "aria-labelledby": "autocompleteLabel"
265
+ }))));
266
+ };
267
+ export const CustomEmptyStateMessage = () => {
268
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
269
+ as: "label",
270
+ display: "block",
271
+ htmlFor: "autocompleteInput",
272
+ id: "autocompleteLabel"
273
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
274
+ id: "autocompleteInput"
275
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
276
+ items: items,
277
+ selectedItemIds: [],
278
+ "aria-labelledby": "autocompleteLabel",
279
+ emptyStateText: "Sorry, no matches"
280
+ }))));
281
+ };
282
+ export const CustomSearchFilter = () => {
283
+ const [filterVal, setFilterVal] = useState('');
284
+
285
+ const handleChange = e => {
286
+ setFilterVal(e.currentTarget.value);
287
+ };
288
+
289
+ const customFilterFn = item => item.text.includes(filterVal);
290
+
291
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
292
+ as: "label",
293
+ display: "block",
294
+ htmlFor: "autocompleteInput",
295
+ id: "autocompleteLabel"
296
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
297
+ id: "autocompleteInput",
298
+ onChange: handleChange
299
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
300
+ items: items,
301
+ selectedItemIds: [],
302
+ "aria-labelledby": "autocompleteLabel",
303
+ filterFn: customFilterFn
304
+ }))), /*#__PURE__*/React.createElement(Text, {
305
+ fontSize: 0,
306
+ display: "block",
307
+ color: "fg.subtle",
308
+ mt: 2
309
+ }, "Items in dropdown are filtered if their text has no part that matches the input value"));
310
+ };
311
+ export const CustomSortAfterMenuClose = () => {
312
+ const [selectedItemIds, setSelectedItemIds] = useState([]);
313
+
314
+ const isItemSelected = itemId => selectedItemIds.includes(itemId);
315
+
316
+ const onSelectedChange = newlySelectedItems => {
317
+ if (!Array.isArray(newlySelectedItems)) {
318
+ return;
319
+ }
320
+
321
+ setSelectedItemIds(newlySelectedItems.map(item => item.id));
322
+ };
323
+
324
+ const customSortFn = (itemIdA, itemIdB) => isItemSelected(itemIdA) === isItemSelected(itemIdB) ? 0 : isItemSelected(itemIdA) ? 1 : -1;
325
+
326
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
327
+ as: "label",
328
+ display: "block",
329
+ htmlFor: "autocompleteInput",
330
+ id: "autocompleteLabel"
331
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
332
+ id: "autocompleteInput"
333
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
334
+ items: items,
335
+ selectedItemIds: selectedItemIds,
336
+ "aria-labelledby": "autocompleteLabel",
337
+ onSelectedChange: onSelectedChange,
338
+ sortOnCloseFn: customSortFn,
339
+ selectionVariant: "multiple"
340
+ }))), /*#__PURE__*/React.createElement(Text, {
341
+ fontSize: 0,
342
+ display: "block",
343
+ color: "fg.subtle",
344
+ mt: 2
345
+ }, "When the dropdown closes, selected items are sorted to the end"));
346
+ };
347
+ export const WithCallbackWhenOverlayOpenStateChanges = () => {
348
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
349
+
350
+ const onOpenChange = isOpen => {
351
+ setIsMenuOpen(isOpen);
352
+ };
353
+
354
+ return /*#__PURE__*/React.createElement(Box, {
355
+ display: "flex",
356
+ sx: {
357
+ gap: '1em'
358
+ }
359
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Box, {
360
+ as: "label",
361
+ display: "block",
362
+ htmlFor: "autocompleteInput",
363
+ id: "autocompleteLabel"
364
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
365
+ id: "autocompleteInput"
366
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
367
+ items: items,
368
+ selectedItemIds: [],
369
+ "aria-labelledby": "autocompleteLabel",
370
+ onOpenChange: onOpenChange
371
+ })))), /*#__PURE__*/React.createElement("div", null, "The menu is ", /*#__PURE__*/React.createElement("strong", null, isMenuOpen ? 'opened' : 'closed')));
372
+ };
373
+ WithCallbackWhenOverlayOpenStateChanges.displayName = "WithCallbackWhenOverlayOpenStateChanges";
374
+ export const AsyncLoadingOfItems = () => {
375
+ const [loadedItems, setLoadedItems] = useState([]);
376
+
377
+ const onOpenChange = () => {
378
+ setTimeout(() => {
379
+ setLoadedItems(items);
380
+ }, 1500);
381
+ };
382
+
383
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
384
+ as: "label",
385
+ display: "block",
386
+ htmlFor: "autocompleteInput",
387
+ id: "autocompleteLabel"
388
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
389
+ id: "autocompleteInput"
390
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, null, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
391
+ items: loadedItems,
392
+ selectedItemIds: [],
393
+ "aria-labelledby": "autocompleteLabel",
394
+ onOpenChange: onOpenChange,
395
+ loading: loadedItems.length === 0
396
+ }))));
397
+ };
398
+ export const RenderingTheMenuOutsideAnOverlay = () => {
399
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
400
+ as: "label",
401
+ display: "block",
402
+ htmlFor: "autocompleteInput",
403
+ id: "autocompleteLabel"
404
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
405
+ id: "autocompleteInput"
406
+ }), /*#__PURE__*/React.createElement(Autocomplete.Menu, {
407
+ items: items,
408
+ selectedItemIds: [],
409
+ "aria-labelledby": "autocompleteLabel"
410
+ })));
411
+ };
412
+ export const CustomOverlayMenuAnchor = () => {
413
+ const menuAnchorRef = useRef(null);
414
+ const anchorWrapperStyles = {
415
+ display: 'flex',
416
+ alignItems: 'center',
417
+ flexGrow: 1,
418
+ flexShrink: 0,
419
+ flexBasis: '25%',
420
+ border: '1px solid black',
421
+ padding: '1em'
422
+ };
423
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
424
+ as: "label",
425
+ htmlFor: "autocompleteInput",
426
+ id: "autocompleteLabel"
427
+ }, "Pick labels"), /*#__PURE__*/React.createElement(Box, _extends({}, anchorWrapperStyles, {
428
+ ref: menuAnchorRef
429
+ }), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
430
+ as: TextInput,
431
+ id: "autocompleteInput",
432
+ sx: {
433
+ border: '0',
434
+ padding: '0',
435
+ boxShadow: 'none',
436
+ ':focus-within': {
437
+ border: '0',
438
+ boxShadow: 'none'
439
+ }
440
+ }
441
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, {
442
+ menuAnchorRef: menuAnchorRef
443
+ }, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
444
+ items: items,
445
+ selectedItemIds: [],
446
+ "aria-labelledby": "autocompleteLabel"
447
+ })))), /*#__PURE__*/React.createElement(Text, {
448
+ fontSize: 0,
449
+ display: "block",
450
+ color: "fg.subtle",
451
+ mt: 2
452
+ }, "The overlay menu's position is anchored to the div with the black border instead of to the text input"));
453
+ };
454
+ export const WithCustomOverlayProps = () => {
455
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, {
456
+ as: "label",
457
+ display: "block",
458
+ htmlFor: "autocompleteInput",
459
+ id: "autocompleteLabel"
460
+ }, "Pick an option"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Autocomplete.Input, {
461
+ id: "autocompleteInput"
462
+ }), /*#__PURE__*/React.createElement(Autocomplete.Overlay, {
463
+ overlayProps: {
464
+ width: 'large',
465
+ height: 'xsmall'
466
+ }
467
+ }, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
468
+ items: items,
469
+ selectedItemIds: [],
470
+ "aria-labelledby": "autocompleteLabel"
471
+ }))));
472
+ };
473
+ export const InOverlayWithCustomScrollContainerRef = () => {
474
+ const scrollContainerRef = useRef(null);
475
+ const inputRef = useRef(null);
476
+ const [isOpen, setIsOpen] = useState(false);
477
+
478
+ const handleOpen = () => {
479
+ setIsOpen(true);
480
+ inputRef.current && inputRef.current.focus();
481
+ };
482
+
483
+ return /*#__PURE__*/React.createElement(AnchoredOverlay, {
484
+ open: isOpen,
485
+ onOpen: handleOpen,
486
+ onClose: () => setIsOpen(false),
487
+ width: "large",
488
+ height: "xsmall",
489
+ focusTrapSettings: {
490
+ initialFocusRef: inputRef
491
+ },
492
+ side: "inside-top",
493
+ renderAnchor: props => /*#__PURE__*/React.createElement(ButtonInvisible, props, "open overlay")
494
+ }, /*#__PURE__*/React.createElement(Box, {
495
+ as: "label",
496
+ display: "block",
497
+ htmlFor: "autocompleteInput",
498
+ id: "autocompleteLabel",
499
+ sx: {
500
+ // visually hides this label for sighted users
501
+ position: 'absolute',
502
+ width: '1px',
503
+ height: '1px',
504
+ padding: '0',
505
+ margin: '-1px',
506
+ overflow: 'hidden',
507
+ clip: 'rect(0, 0, 0, 0)',
508
+ whiteSpace: 'nowrap',
509
+ borderWidth: '0'
510
+ }
511
+ }, "Pick options"), /*#__PURE__*/React.createElement(Autocomplete, null, /*#__PURE__*/React.createElement(Box, {
512
+ display: "flex",
513
+ flexDirection: "column",
514
+ height: "100%"
515
+ }, /*#__PURE__*/React.createElement(Box, {
516
+ paddingX: "3",
517
+ paddingY: "1",
518
+ borderWidth: 0,
519
+ borderBottomWidth: 1,
520
+ borderColor: "border.default",
521
+ borderStyle: "solid"
522
+ }, /*#__PURE__*/React.createElement(Autocomplete.Input, {
523
+ block: true,
524
+ as: TextInput,
525
+ ref: inputRef,
526
+ id: "autocompleteInput",
527
+ sx: {
528
+ display: 'flex',
529
+ border: '0',
530
+ padding: '0',
531
+ boxShadow: 'none',
532
+ ':focus-within': {
533
+ border: '0',
534
+ boxShadow: 'none'
535
+ }
536
+ }
537
+ })), /*#__PURE__*/React.createElement(Box, {
538
+ overflow: "auto",
539
+ flexGrow: 1,
540
+ ref: scrollContainerRef
541
+ }, /*#__PURE__*/React.createElement(Autocomplete.Menu, {
542
+ items: items,
543
+ selectedItemIds: [] // onSelectedChange={onSelectedChange}
544
+ ,
545
+ customScrollContainerRef: scrollContainerRef,
546
+ "aria-labelledby": "autocompleteLabel"
547
+ })))));
548
+ };
549
+ InOverlayWithCustomScrollContainerRef.displayName = "InOverlayWithCustomScrollContainerRef";
@@ -0,0 +1,3 @@
1
+ export declare type MandateProps<T extends unknown, K extends keyof T> = Omit<T, K> & {
2
+ [MK in K]-?: NonNullable<T[MK]>;
3
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -2,3 +2,4 @@ export * from './AriaRole';
2
2
  export * from './ComponentProps';
3
3
  export * from './Flatten';
4
4
  export * from './Merge';
5
+ export * from './MandateProps';
@@ -1,4 +1,5 @@
1
1
  export * from './AriaRole';
2
2
  export * from './ComponentProps';
3
3
  export * from './Flatten';
4
- export * from './Merge';
4
+ export * from './Merge';
5
+ export * from './MandateProps';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primer/components",
3
- "version": "30.3.0-rc.2010c7d4",
3
+ "version": "30.3.0-rc.9dbc85a9",
4
4
  "description": "Primer react components",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib-esm/index.js",
@@ -4,6 +4,7 @@ import {FocusTrapHookSettings, useFocusTrap} from '../hooks/useFocusTrap'
4
4
  import {FocusZoneHookSettings, useFocusZone} from '../hooks/useFocusZone'
5
5
  import {useAnchoredPosition, useProvidedRefOrCreate, useRenderForcingRef} from '../hooks'
6
6
  import {useSSRSafeId} from '@react-aria/ssr'
7
+ import {PositionSettings} from '../behaviors/anchoredPosition'
7
8
 
8
9
  interface AnchoredOverlayPropsWithAnchor {
9
10
  /**
@@ -69,7 +70,8 @@ interface AnchoredOverlayBaseProps extends Pick<OverlayProps, 'height' | 'width'
69
70
  }
70
71
 
71
72
  export type AnchoredOverlayProps = AnchoredOverlayBaseProps &
72
- (AnchoredOverlayPropsWithAnchor | AnchoredOverlayPropsWithoutAnchor)
73
+ (AnchoredOverlayPropsWithAnchor | AnchoredOverlayPropsWithoutAnchor) &
74
+ Partial<Pick<PositionSettings, 'align' | 'side'>>
73
75
 
74
76
  /**
75
77
  * An `AnchoredOverlay` provides an anchor that will open a floating overlay positioned relative to the anchor.
@@ -86,7 +88,9 @@ export const AnchoredOverlay: React.FC<AnchoredOverlayProps> = ({
86
88
  width,
87
89
  overlayProps,
88
90
  focusTrapSettings,
89
- focusZoneSettings
91
+ focusZoneSettings,
92
+ side,
93
+ align
90
94
  }) => {
91
95
  const anchorRef = useProvidedRefOrCreate(externalAnchorRef)
92
96
  const [overlayRef, updateOverlayRef] = useRenderForcingRef<HTMLDivElement>()
@@ -123,7 +127,9 @@ export const AnchoredOverlay: React.FC<AnchoredOverlayProps> = ({
123
127
  const {position} = useAnchoredPosition(
124
128
  {
125
129
  anchorElementRef: anchorRef,
126
- floatingElementRef: overlayRef
130
+ floatingElementRef: overlayRef,
131
+ side,
132
+ align
127
133
  },
128
134
  [overlayRef.current]
129
135
  )
@@ -178,3 +184,8 @@ export const AnchoredOverlay: React.FC<AnchoredOverlayProps> = ({
178
184
  }
179
185
 
180
186
  AnchoredOverlay.displayName = 'AnchoredOverlay'
187
+
188
+ AnchoredOverlay.defaultProps = {
189
+ side: 'outside-bottom',
190
+ align: 'start'
191
+ }