@primer/components 0.0.0-202195195330 → 0.0.0-202195195659

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 (54) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/dist/browser.esm.js +260 -261
  3. package/dist/browser.esm.js.map +1 -1
  4. package/dist/browser.umd.js +332 -333
  5. package/dist/browser.umd.js.map +1 -1
  6. package/lib/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  7. package/lib/AnchoredOverlay/AnchoredOverlay.js +11 -3
  8. package/lib/Autocomplete/Autocomplete.d.ts +40 -0
  9. package/lib/Autocomplete/Autocomplete.js +68 -0
  10. package/lib/Autocomplete/AutocompleteContext.d.ts +17 -0
  11. package/lib/Autocomplete/AutocompleteContext.js +11 -0
  12. package/lib/Autocomplete/AutocompleteInput.d.ts +9 -0
  13. package/lib/Autocomplete/AutocompleteInput.js +141 -0
  14. package/lib/Autocomplete/AutocompleteMenu.d.ts +71 -0
  15. package/lib/Autocomplete/AutocompleteMenu.js +220 -0
  16. package/lib/Autocomplete/AutocompleteOverlay.d.ts +17 -0
  17. package/lib/Autocomplete/AutocompleteOverlay.js +69 -0
  18. package/lib/Autocomplete/index.d.ts +2 -0
  19. package/lib/Autocomplete/index.js +15 -0
  20. package/lib/FilteredActionList/FilteredActionList.js +5 -31
  21. package/lib/Overlay.d.ts +2 -1
  22. package/lib/Overlay.js +10 -5
  23. package/lib/hooks/useOverlay.d.ts +2 -1
  24. package/lib/hooks/useOverlay.js +11 -6
  25. package/lib/index.d.ts +1 -0
  26. package/lib/index.js +8 -0
  27. package/lib/utils/scrollIntoViewingArea.d.ts +1 -0
  28. package/lib/utils/scrollIntoViewingArea.js +39 -0
  29. package/lib/utils/types.d.ts +3 -0
  30. package/lib-esm/AnchoredOverlay/AnchoredOverlay.d.ts +2 -1
  31. package/lib-esm/AnchoredOverlay/AnchoredOverlay.js +11 -3
  32. package/lib-esm/Autocomplete/Autocomplete.d.ts +40 -0
  33. package/lib-esm/Autocomplete/Autocomplete.js +47 -0
  34. package/lib-esm/Autocomplete/AutocompleteContext.d.ts +17 -0
  35. package/lib-esm/Autocomplete/AutocompleteContext.js +2 -0
  36. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +9 -0
  37. package/lib-esm/Autocomplete/AutocompleteInput.js +122 -0
  38. package/lib-esm/Autocomplete/AutocompleteMenu.d.ts +71 -0
  39. package/lib-esm/Autocomplete/AutocompleteMenu.js +201 -0
  40. package/lib-esm/Autocomplete/AutocompleteOverlay.d.ts +17 -0
  41. package/lib-esm/Autocomplete/AutocompleteOverlay.js +51 -0
  42. package/lib-esm/Autocomplete/index.d.ts +2 -0
  43. package/lib-esm/Autocomplete/index.js +1 -0
  44. package/lib-esm/FilteredActionList/FilteredActionList.js +3 -31
  45. package/lib-esm/Overlay.d.ts +2 -1
  46. package/lib-esm/Overlay.js +8 -5
  47. package/lib-esm/hooks/useOverlay.d.ts +2 -1
  48. package/lib-esm/hooks/useOverlay.js +11 -6
  49. package/lib-esm/index.d.ts +1 -0
  50. package/lib-esm/index.js +1 -0
  51. package/lib-esm/utils/scrollIntoViewingArea.d.ts +1 -0
  52. package/lib-esm/utils/scrollIntoViewingArea.js +30 -0
  53. package/lib-esm/utils/types.d.ts +3 -0
  54. package/package.json +1 -1
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _react = _interopRequireWildcard(require("react"));
9
+
10
+ var _hooks = require("../hooks");
11
+
12
+ var _Overlay = _interopRequireDefault(require("../Overlay"));
13
+
14
+ var _AutocompleteContext = require("./AutocompleteContext");
15
+
16
+ var _useCombinedRefs = require("../hooks/useCombinedRefs");
17
+
18
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
+
20
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
21
+
22
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
23
+
24
+ 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); }
25
+
26
+ // TODO: consider making 'aria-labelledby' required
27
+ const AutocompleteOverlay = ({
28
+ menuAnchorRef,
29
+ overlayProps,
30
+ children
31
+ }) => {
32
+ const {
33
+ inputRef,
34
+ scrollContainerRef,
35
+ selectedItemLength,
36
+ setShowMenu,
37
+ showMenu = false
38
+ } = (0, _react.useContext)(_AutocompleteContext.AutocompleteContext);
39
+ const {
40
+ floatingElementRef,
41
+ position
42
+ } = (0, _hooks.useAnchoredPosition)({
43
+ side: 'outside-bottom',
44
+ align: 'start',
45
+ anchorElementRef: menuAnchorRef ? menuAnchorRef : inputRef
46
+ }, [showMenu, selectedItemLength]);
47
+ const combinedOverlayRef = (0, _useCombinedRefs.useCombinedRefs)(scrollContainerRef, floatingElementRef);
48
+ const closeOptionList = (0, _react.useCallback)(() => {
49
+ setShowMenu && setShowMenu(false);
50
+ }, [setShowMenu]);
51
+ return /*#__PURE__*/_react.default.createElement(_Overlay.default, _extends({
52
+ returnFocusRef: inputRef,
53
+ preventFocusOnOpen: true,
54
+ onClickOutside: closeOptionList,
55
+ onEscape: closeOptionList,
56
+ ref: combinedOverlayRef,
57
+ top: position === null || position === void 0 ? void 0 : position.top,
58
+ left: position === null || position === void 0 ? void 0 : position.left,
59
+ visibility: showMenu ? 'visible' : 'hidden',
60
+ sx: {
61
+ overflow: 'auto'
62
+ }
63
+ }, overlayProps), children);
64
+ };
65
+
66
+ AutocompleteOverlay.displayName = "AutocompleteOverlay";
67
+ AutocompleteOverlay.displayName = 'AutocompleteOverlay';
68
+ var _default = AutocompleteOverlay;
69
+ exports.default = _default;
@@ -0,0 +1,2 @@
1
+ export { default } from './Autocomplete';
2
+ export type { AutocompleteMenuProps, AutocompleteInputProps, AutocompleteOverlayProps } from './Autocomplete';
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "default", {
7
+ enumerable: true,
8
+ get: function () {
9
+ return _Autocomplete.default;
10
+ }
11
+ });
12
+
13
+ var _Autocomplete = _interopRequireDefault(require("./Autocomplete"));
14
+
15
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -7,6 +7,8 @@ exports.FilteredActionList = FilteredActionList;
7
7
 
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
 
10
+ var _ssr = require("@react-aria/ssr");
11
+
10
12
  var _TextInput = _interopRequireDefault(require("../TextInput"));
11
13
 
12
14
  var _Box = _interopRequireDefault(require("../Box"));
@@ -27,7 +29,7 @@ var _useProvidedRefOrCreate = require("../hooks/useProvidedRefOrCreate");
27
29
 
28
30
  var _useScrollFlash = _interopRequireDefault(require("../hooks/useScrollFlash"));
29
31
 
30
- var _ssr = require("@react-aria/ssr");
32
+ var _scrollIntoViewingArea = require("../utils/scrollIntoViewingArea");
31
33
 
32
34
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
35
 
@@ -37,34 +39,6 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
37
39
 
38
40
  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); }
39
41
 
40
- function scrollIntoViewingArea(child, container, margin = 8, behavior = 'smooth') {
41
- const {
42
- top: childTop,
43
- bottom: childBottom
44
- } = child.getBoundingClientRect();
45
- const {
46
- top: containerTop,
47
- bottom: containerBottom
48
- } = container.getBoundingClientRect();
49
- const isChildTopAboveViewingArea = childTop < containerTop + margin;
50
- const isChildBottomBelowViewingArea = childBottom > containerBottom - margin;
51
-
52
- if (isChildTopAboveViewingArea) {
53
- const scrollHeightToChildTop = childTop - containerTop + container.scrollTop;
54
- container.scrollTo({
55
- behavior,
56
- top: scrollHeightToChildTop - margin
57
- });
58
- } else if (isChildBottomBelowViewingArea) {
59
- const scrollHeightToChildBottom = childBottom - containerBottom + container.scrollTop;
60
- container.scrollTo({
61
- behavior,
62
- top: scrollHeightToChildBottom + margin
63
- });
64
- } // either completely in view or outside viewing area on both ends, don't scroll
65
-
66
- }
67
-
68
42
  const StyledHeader = _styledComponents.default.div.withConfig({
69
43
  displayName: "FilteredActionList__StyledHeader",
70
44
  componentId: "yg3jkv-0"
@@ -111,7 +85,7 @@ function FilteredActionList({
111
85
  activeDescendantRef.current = current;
112
86
 
113
87
  if (current && scrollContainerRef.current && directlyActivated) {
114
- scrollIntoViewingArea(current, scrollContainerRef.current);
88
+ (0, _scrollIntoViewingArea.scrollIntoViewingArea)(current, scrollContainerRef.current);
115
89
  }
116
90
  }
117
91
  }, [// List ref isn't set while loading. Need to re-bind focus zone when it changes
@@ -119,7 +93,7 @@ function FilteredActionList({
119
93
  (0, _react.useEffect)(() => {
120
94
  // if items changed, we want to instantly move active descendant into view
121
95
  if (activeDescendantRef.current && scrollContainerRef.current) {
122
- scrollIntoViewingArea(activeDescendantRef.current, scrollContainerRef.current, undefined, 'auto');
96
+ (0, _scrollIntoViewingArea.scrollIntoViewingArea)(activeDescendantRef.current, scrollContainerRef.current, 'vertical', undefined, undefined, 'auto');
123
97
  }
124
98
  }, [items]);
125
99
  (0, _useScrollFlash.default)(scrollContainerRef);
package/lib/Overlay.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ComponentProps } from './utils/types';
4
4
  import { TouchOrMouseEvent } from './hooks';
5
5
  import { SxProp } from './sx';
6
6
  import { AnchorSide } from './behaviors/anchoredPosition';
7
- declare type StyledOverlayProps = {
7
+ export declare type StyledOverlayProps = {
8
8
  width?: keyof typeof widthMap;
9
9
  height?: keyof typeof heightMap;
10
10
  maxHeight?: keyof Omit<typeof heightMap, 'auto' | 'initial'>;
@@ -40,6 +40,7 @@ export declare type OverlayProps = {
40
40
  top: number;
41
41
  left: number;
42
42
  portalContainerName?: string;
43
+ preventFocusOnOpen?: boolean;
43
44
  } & Omit<ComponentProps<typeof StyledOverlay>, 'visibility' | keyof SystemPositionProps>;
44
45
  /**
45
46
  * An `Overlay` is a flexible floating surface, used to display transient content such as menus,
package/lib/Overlay.js CHANGED
@@ -113,6 +113,7 @@ const Overlay = /*#__PURE__*/_react.default.forwardRef(({
113
113
  left,
114
114
  anchorSide,
115
115
  portalContainerName,
116
+ preventFocusOnOpen,
116
117
  ...rest
117
118
  }, forwardedRef) => {
118
119
  const overlayRef = (0, _react.useRef)(null);
@@ -128,7 +129,8 @@ const Overlay = /*#__PURE__*/_react.default.forwardRef(({
128
129
  onEscape,
129
130
  ignoreClickRefs,
130
131
  onClickOutside,
131
- initialFocusRef
132
+ initialFocusRef,
133
+ preventFocusOnOpen
132
134
  });
133
135
  (0, _react.useEffect)(() => {
134
136
  var _combinedRef$current;
@@ -157,9 +159,8 @@ const Overlay = /*#__PURE__*/_react.default.forwardRef(({
157
159
  easing: slideAnimationEasing
158
160
  });
159
161
  }, [anchorSide, slideAnimationDistance, slideAnimationEasing, visibility]);
160
- return /*#__PURE__*/_react.default.createElement(_Portal.default, {
161
- containerName: portalContainerName
162
- }, /*#__PURE__*/_react.default.createElement(StyledOverlay, _extends({
162
+
163
+ const styledOverlay = /*#__PURE__*/_react.default.createElement(StyledOverlay, _extends({
163
164
  height: height,
164
165
  role: role
165
166
  }, rest, {
@@ -170,7 +171,11 @@ const Overlay = /*#__PURE__*/_react.default.forwardRef(({
170
171
  ...rest.style,
171
172
  '--styled-overlay-visibility': visibility
172
173
  }
173
- })));
174
+ }));
175
+
176
+ return /*#__PURE__*/_react.default.createElement(_Portal.default, {
177
+ containerName: portalContainerName
178
+ }, styledOverlay);
174
179
  });
175
180
 
176
181
  Overlay.defaultProps = {
@@ -7,8 +7,9 @@ export declare type UseOverlaySettings = {
7
7
  onEscape: (e: KeyboardEvent) => void;
8
8
  onClickOutside: (e: TouchOrMouseEvent) => void;
9
9
  overlayRef?: React.RefObject<HTMLDivElement>;
10
+ preventFocusOnOpen?: boolean;
10
11
  };
11
12
  export declare type OverlayReturnProps = {
12
13
  ref: React.RefObject<HTMLDivElement>;
13
14
  };
14
- export declare const useOverlay: ({ overlayRef: _overlayRef, returnFocusRef, initialFocusRef, onEscape, ignoreClickRefs, onClickOutside }: UseOverlaySettings) => OverlayReturnProps;
15
+ export declare const useOverlay: ({ overlayRef: _overlayRef, returnFocusRef, initialFocusRef, onEscape, ignoreClickRefs, onClickOutside, preventFocusOnOpen }: UseOverlaySettings) => OverlayReturnProps;
@@ -19,14 +19,19 @@ const useOverlay = ({
19
19
  initialFocusRef,
20
20
  onEscape,
21
21
  ignoreClickRefs,
22
- onClickOutside
22
+ onClickOutside,
23
+ preventFocusOnOpen
23
24
  }) => {
24
25
  const overlayRef = (0, _useProvidedRefOrCreate.useProvidedRefOrCreate)(_overlayRef);
25
- (0, _useOpenAndCloseFocus.useOpenAndCloseFocus)({
26
- containerRef: overlayRef,
27
- returnFocusRef,
28
- initialFocusRef
29
- });
26
+
27
+ if (!preventFocusOnOpen) {
28
+ (0, _useOpenAndCloseFocus.useOpenAndCloseFocus)({
29
+ containerRef: overlayRef,
30
+ returnFocusRef,
31
+ initialFocusRef
32
+ });
33
+ }
34
+
30
35
  (0, _useOnOutsideClick.useOnOutsideClick)({
31
36
  containerRef: overlayRef,
32
37
  ignoreClickRefs,
package/lib/index.d.ts CHANGED
@@ -24,6 +24,7 @@ export { useConfirm } from './Dialog/ConfirmationDialog';
24
24
  export { ActionList } from './ActionList';
25
25
  export { ActionMenu } from './ActionMenu';
26
26
  export type { ActionMenuProps } from './ActionMenu';
27
+ export { default as Autocomplete } from './Autocomplete';
27
28
  export { default as Avatar } from './Avatar';
28
29
  export type { AvatarProps } from './Avatar';
29
30
  export { default as AvatarPair } from './AvatarPair';
package/lib/index.js CHANGED
@@ -153,6 +153,12 @@ Object.defineProperty(exports, "ActionMenu", {
153
153
  return _ActionMenu.ActionMenu;
154
154
  }
155
155
  });
156
+ Object.defineProperty(exports, "Autocomplete", {
157
+ enumerable: true,
158
+ get: function () {
159
+ return _Autocomplete.default;
160
+ }
161
+ });
156
162
  Object.defineProperty(exports, "Avatar", {
157
163
  enumerable: true,
158
164
  get: function () {
@@ -532,6 +538,8 @@ var _ActionList = require("./ActionList");
532
538
 
533
539
  var _ActionMenu = require("./ActionMenu");
534
540
 
541
+ var _Autocomplete = _interopRequireDefault(require("./Autocomplete"));
542
+
535
543
  var _Avatar = _interopRequireDefault(require("./Avatar"));
536
544
 
537
545
  var _AvatarPair = _interopRequireDefault(require("./AvatarPair"));
@@ -0,0 +1 @@
1
+ export declare const scrollIntoViewingArea: (child: HTMLElement, container: HTMLElement, direction?: 'horizontal' | 'vertical', startMargin?: number, endMargin?: number, behavior?: ScrollBehavior) => void;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.scrollIntoViewingArea = void 0;
7
+
8
+ const scrollIntoViewingArea = (child, container, direction = 'vertical', startMargin = 8, endMargin = 0, behavior = 'smooth') => {
9
+ const startSide = direction === 'vertical' ? 'top' : 'left';
10
+ const endSide = direction === 'vertical' ? 'bottom' : 'right';
11
+ const scrollSide = direction === 'vertical' ? 'scrollTop' : 'scrollLeft';
12
+ const {
13
+ [startSide]: childStart,
14
+ [endSide]: childEnd
15
+ } = child.getBoundingClientRect();
16
+ const {
17
+ [startSide]: containerStart,
18
+ [endSide]: containerEnd
19
+ } = container.getBoundingClientRect();
20
+ const isChildStartAboveViewingArea = childStart < containerStart + endMargin;
21
+ const isChildBottomBelowViewingArea = childEnd > containerEnd - startMargin;
22
+
23
+ if (isChildStartAboveViewingArea) {
24
+ const scrollHeightToChildStart = childStart - containerStart + container[scrollSide];
25
+ container.scrollTo({
26
+ behavior,
27
+ [startSide]: scrollHeightToChildStart - endMargin
28
+ });
29
+ } else if (isChildBottomBelowViewingArea) {
30
+ const scrollHeightToChildBottom = childEnd - containerEnd + container[scrollSide];
31
+ container.scrollTo({
32
+ behavior,
33
+ [startSide]: scrollHeightToChildBottom + startMargin
34
+ });
35
+ } // either completely in view or outside viewing area on both ends, don't scroll
36
+
37
+ };
38
+
39
+ exports.scrollIntoViewingArea = scrollIntoViewingArea;
@@ -12,3 +12,6 @@ export declare type ComponentProps<T> = T extends React.ComponentType<infer Prop
12
12
  */
13
13
  export declare type Flatten<T extends unknown> = T extends (infer U)[] ? U : never;
14
14
  export declare type AriaRole = 'alert' | 'alertdialog' | 'application' | 'article' | 'banner' | 'button' | 'cell' | 'checkbox' | 'columnheader' | 'combobox' | 'complementary' | 'contentinfo' | 'definition' | 'dialog' | 'directory' | 'document' | 'feed' | 'figure' | 'form' | 'grid' | 'gridcell' | 'group' | 'heading' | 'img' | 'link' | 'list' | 'listbox' | 'listitem' | 'log' | 'main' | 'marquee' | 'math' | 'menu' | 'menubar' | 'menuitem' | 'menuitemcheckbox ' | 'menuitemradio' | 'navigation' | 'none' | 'note' | 'option' | 'presentation' | 'progressbar' | 'radio' | 'radiogroup' | 'region' | 'row' | 'rowgroup' | 'rowheader' | 'scrollbar' | 'search' | 'searchbox' | 'separator' | 'slider' | 'spinbutton' | 'status' | 'switch' | 'tab' | 'table' | 'tablist' | 'tabpanel' | 'term' | 'textbox' | 'timer' | 'toolbar' | 'tooltip' | 'tree' | 'treegrid' | 'treeitem';
15
+ export declare type MandateProps<T extends {}, K extends keyof T> = Omit<T, K> & {
16
+ [MK in K]-?: NonNullable<T[MK]>;
17
+ };
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { OverlayProps } from '../Overlay';
3
3
  import { FocusTrapHookSettings } from '../hooks/useFocusTrap';
4
4
  import { FocusZoneHookSettings } from '../hooks/useFocusZone';
5
+ import { PositionSettings } from '../behaviors/anchoredPosition';
5
6
  interface AnchoredOverlayPropsWithAnchor {
6
7
  /**
7
8
  * A custom function component used to render the anchor element.
@@ -52,7 +53,7 @@ interface AnchoredOverlayBaseProps extends Pick<OverlayProps, 'height' | 'width'
52
53
  */
53
54
  focusZoneSettings?: Partial<FocusZoneHookSettings>;
54
55
  }
55
- export declare type AnchoredOverlayProps = AnchoredOverlayBaseProps & (AnchoredOverlayPropsWithAnchor | AnchoredOverlayPropsWithoutAnchor);
56
+ export declare type AnchoredOverlayProps = AnchoredOverlayBaseProps & (AnchoredOverlayPropsWithAnchor | AnchoredOverlayPropsWithoutAnchor) & Partial<Pick<PositionSettings, 'align' | 'side'>>;
56
57
  /**
57
58
  * An `AnchoredOverlay` provides an anchor that will open a floating overlay positioned relative to the anchor.
58
59
  * The overlay can be opened and navigated using keyboard or mouse.
@@ -22,7 +22,9 @@ export const AnchoredOverlay = ({
22
22
  width,
23
23
  overlayProps,
24
24
  focusTrapSettings,
25
- focusZoneSettings
25
+ focusZoneSettings,
26
+ side,
27
+ align
26
28
  }) => {
27
29
  const anchorRef = useProvidedRefOrCreate(externalAnchorRef);
28
30
  const [overlayRef, updateOverlayRef] = useRenderForcingRef();
@@ -52,7 +54,9 @@ export const AnchoredOverlay = ({
52
54
  position
53
55
  } = useAnchoredPosition({
54
56
  anchorElementRef: anchorRef,
55
- floatingElementRef: overlayRef
57
+ floatingElementRef: overlayRef,
58
+ side,
59
+ align
56
60
  }, [overlayRef.current]);
57
61
  useEffect(() => {
58
62
  // ensure overlay ref gets cleared when closed, so position can reset between closing/re-opening
@@ -93,4 +97,8 @@ export const AnchoredOverlay = ({
93
97
  anchorSide: position === null || position === void 0 ? void 0 : position.anchorSide
94
98
  }, overlayProps), children) : null);
95
99
  };
96
- AnchoredOverlay.displayName = 'AnchoredOverlay';
100
+ AnchoredOverlay.displayName = 'AnchoredOverlay';
101
+ AnchoredOverlay.defaultProps = {
102
+ side: 'outside-bottom',
103
+ align: 'start'
104
+ };
@@ -0,0 +1,40 @@
1
+ import React from 'react';
2
+ import { ComponentProps } from '../utils/types';
3
+ import AutocompleteMenu from './AutocompleteMenu';
4
+ declare const Autocomplete: React.FC<{
5
+ id?: string;
6
+ }>;
7
+ export declare type AutocompleteProps = ComponentProps<typeof Autocomplete>;
8
+ export type { AutocompleteInputProps } from './AutocompleteInput';
9
+ export type { AutocompleteMenuProps } from './AutocompleteMenu';
10
+ export type { AutocompleteOverlayProps } from './AutocompleteOverlay';
11
+ declare const _default: React.FC<{
12
+ id?: string | undefined;
13
+ }> & {
14
+ AutocompleteContext: React.Context<{
15
+ activeDescendantRef?: React.MutableRefObject<HTMLElement | null> | undefined;
16
+ autocompleteSuggestion?: string | undefined;
17
+ id?: string | undefined;
18
+ inputRef?: React.MutableRefObject<HTMLInputElement | null> | undefined;
19
+ inputValue?: string | undefined;
20
+ isMenuDirectlyActivated?: boolean | undefined;
21
+ scrollContainerRef?: React.MutableRefObject<HTMLElement | null> | undefined;
22
+ selectedItemLength?: number | undefined;
23
+ setAutocompleteSuggestion?: React.Dispatch<React.SetStateAction<string>> | undefined;
24
+ setInputValue?: React.Dispatch<React.SetStateAction<string>> | undefined;
25
+ setIsMenuDirectlyActivated?: React.Dispatch<React.SetStateAction<boolean>> | undefined;
26
+ setSelectedItemLength?: React.Dispatch<React.SetStateAction<number | undefined>> | undefined;
27
+ setShowMenu?: React.Dispatch<React.SetStateAction<boolean>> | undefined;
28
+ showMenu?: boolean | undefined;
29
+ }>;
30
+ Input: import("@radix-ui/react-polymorphic").ForwardRefComponent<"input", {
31
+ as?: React.ComponentType<any> | undefined;
32
+ }>;
33
+ Menu: typeof AutocompleteMenu;
34
+ Overlay: React.FC<{
35
+ menuAnchorRef?: React.RefObject<HTMLElement> | undefined;
36
+ overlayProps?: Partial<import("..").OverlayProps> | undefined;
37
+ children?: React.ReactNode;
38
+ } & Pick<React.AriaAttributes, "aria-labelledby">>;
39
+ };
40
+ export default _default;
@@ -0,0 +1,47 @@
1
+ import React, { useRef, useState } from 'react';
2
+ import { uniqueId } from '../utils/uniqueId';
3
+ import { AutocompleteContext } from './AutocompleteContext';
4
+ import AutocompleteInput from './AutocompleteInput';
5
+ import AutocompleteMenu from './AutocompleteMenu';
6
+ import AutocompleteOverlay from './AutocompleteOverlay';
7
+
8
+ const Autocomplete = ({
9
+ children,
10
+ id: idProp
11
+ }) => {
12
+ const activeDescendantRef = useRef(null);
13
+ const scrollContainerRef = useRef(null);
14
+ const inputRef = useRef(null);
15
+ const [inputValue, setInputValue] = useState('');
16
+ const [showMenu, setShowMenu] = useState(false);
17
+ const [autocompleteSuggestion, setAutocompleteSuggestion] = useState('');
18
+ const [isMenuDirectlyActivated, setIsMenuDirectlyActivated] = useState(false);
19
+ const [selectedItemLength, setSelectedItemLength] = useState();
20
+ const id = idProp || uniqueId();
21
+ return /*#__PURE__*/React.createElement(AutocompleteContext.Provider, {
22
+ value: {
23
+ activeDescendantRef,
24
+ autocompleteSuggestion,
25
+ id,
26
+ inputRef,
27
+ inputValue,
28
+ isMenuDirectlyActivated,
29
+ scrollContainerRef,
30
+ selectedItemLength,
31
+ setAutocompleteSuggestion,
32
+ setInputValue,
33
+ setIsMenuDirectlyActivated,
34
+ setShowMenu,
35
+ setSelectedItemLength,
36
+ showMenu
37
+ }
38
+ }, children);
39
+ };
40
+
41
+ Autocomplete.displayName = "Autocomplete";
42
+ export default Object.assign(Autocomplete, {
43
+ AutocompleteContext,
44
+ Input: AutocompleteInput,
45
+ Menu: AutocompleteMenu,
46
+ Overlay: AutocompleteOverlay
47
+ });
@@ -0,0 +1,17 @@
1
+ /// <reference types="react" />
2
+ export declare const AutocompleteContext: import("react").Context<{
3
+ activeDescendantRef?: import("react").MutableRefObject<HTMLElement | null> | undefined;
4
+ autocompleteSuggestion?: string | undefined;
5
+ id?: string | undefined;
6
+ inputRef?: import("react").MutableRefObject<HTMLInputElement | null> | undefined;
7
+ inputValue?: string | undefined;
8
+ isMenuDirectlyActivated?: boolean | undefined;
9
+ scrollContainerRef?: import("react").MutableRefObject<HTMLElement | null> | undefined;
10
+ selectedItemLength?: number | undefined;
11
+ setAutocompleteSuggestion?: import("react").Dispatch<import("react").SetStateAction<string>> | undefined;
12
+ setInputValue?: import("react").Dispatch<import("react").SetStateAction<string>> | undefined;
13
+ setIsMenuDirectlyActivated?: import("react").Dispatch<import("react").SetStateAction<boolean>> | undefined;
14
+ setSelectedItemLength?: import("react").Dispatch<import("react").SetStateAction<number | undefined>> | undefined;
15
+ setShowMenu?: import("react").Dispatch<import("react").SetStateAction<boolean>> | undefined;
16
+ showMenu?: boolean | undefined;
17
+ }>;
@@ -0,0 +1,2 @@
1
+ import { createContext } from 'react';
2
+ export const AutocompleteContext = /*#__PURE__*/createContext({});
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type * as Polymorphic from '@radix-ui/react-polymorphic';
3
+ import { ComponentProps } from '../utils/types';
4
+ declare type InternalAutocompleteInputProps = {
5
+ as?: React.ComponentType<any>;
6
+ };
7
+ declare const AutocompleteInput: Polymorphic.ForwardRefComponent<"input", InternalAutocompleteInputProps>;
8
+ export declare type AutocompleteInputProps = ComponentProps<typeof AutocompleteInput>;
9
+ export default AutocompleteInput;
@@ -0,0 +1,122 @@
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, useContext, useEffect, useState } from 'react';
4
+ import { AutocompleteContext } from './AutocompleteContext';
5
+ import TextInput from '../TextInput';
6
+ import { useCombinedRefs } from '../hooks/useCombinedRefs';
7
+ const AutocompleteInput = /*#__PURE__*/React.forwardRef(({
8
+ as: Component = TextInput,
9
+ onFocus,
10
+ onBlur,
11
+ onChange,
12
+ onKeyDown,
13
+ onKeyPress,
14
+ value,
15
+ ...props
16
+ }, forwardedRef) => {
17
+ const {
18
+ activeDescendantRef,
19
+ autocompleteSuggestion = '',
20
+ id,
21
+ inputRef,
22
+ inputValue = '',
23
+ isMenuDirectlyActivated,
24
+ setInputValue,
25
+ setShowMenu,
26
+ showMenu
27
+ } = useContext(AutocompleteContext);
28
+ const combinedInputRef = useCombinedRefs(inputRef, forwardedRef);
29
+ const [highlightRemainingText, setHighlightRemainingText] = useState(true);
30
+ const handleInputFocus = useCallback(event => {
31
+ onFocus && onFocus(event);
32
+ setShowMenu && setShowMenu(true);
33
+ }, [onFocus, setShowMenu]);
34
+ const handleInputBlur = useCallback(event => {
35
+ onBlur && onBlur(event); // HACK: wait a tick and check the focused element before hiding the autocomplete menu
36
+ // this prevents the menu from hiding when the user is clicking an option in the Autoselect.Menu,
37
+ // but still hides the menu when the user blurs the input by tabbing out or clicking somewhere else on the page
38
+
39
+ setTimeout(() => {
40
+ if (document.activeElement !== combinedInputRef.current) {
41
+ setShowMenu && setShowMenu(false);
42
+ }
43
+ }, 0);
44
+ }, [onBlur, setShowMenu]);
45
+ const handleInputChange = useCallback(event => {
46
+ onChange && onChange(event);
47
+ setInputValue && setInputValue(event.currentTarget.value);
48
+
49
+ if (!showMenu) {
50
+ setShowMenu && setShowMenu(true);
51
+ }
52
+ }, [onChange, setInputValue, setShowMenu, showMenu]);
53
+ const handleInputKeyDown = useCallback(event => {
54
+ var _inputRef$current;
55
+
56
+ if (event.key === 'Backspace') {
57
+ setHighlightRemainingText(false);
58
+ }
59
+
60
+ if (event.key === 'Escape' && inputRef !== null && inputRef !== void 0 && (_inputRef$current = inputRef.current) !== null && _inputRef$current !== void 0 && _inputRef$current.value) {
61
+ setInputValue && setInputValue('');
62
+ inputRef.current.value = '';
63
+ }
64
+ }, [inputRef, setInputValue, setHighlightRemainingText]);
65
+ const handleInputKeyUp = useCallback(event => {
66
+ if (event.key === 'Backspace') {
67
+ setHighlightRemainingText(true);
68
+ }
69
+ }, [setHighlightRemainingText]);
70
+ const onInputKeyPress = useCallback(event => {
71
+ if (showMenu && activeDescendantRef && event.key === 'Enter' && activeDescendantRef.current) {
72
+ event.preventDefault();
73
+ event.nativeEvent.stopImmediatePropagation(); // Forward Enter key press to active descendant so that item gets activated
74
+
75
+ const activeDescendantEvent = new KeyboardEvent(event.type, event.nativeEvent);
76
+ activeDescendantRef.current.dispatchEvent(activeDescendantEvent);
77
+ }
78
+ }, [activeDescendantRef, showMenu]);
79
+ useEffect(() => {
80
+ if (!(inputRef !== null && inputRef !== void 0 && inputRef.current)) {
81
+ return;
82
+ } // resets input value to being empty after a selection has been made
83
+
84
+
85
+ if (!autocompleteSuggestion) {
86
+ inputRef.current.value = inputValue;
87
+ } // TODO: fix bug where this function prevents `onChange` from being triggered if the highlighted item text
88
+ // is the same as what I'm typing
89
+ // e.g.: typing 'tw' highights 'two', but when I 'two', the text input change does not get triggered
90
+
91
+
92
+ if (highlightRemainingText && autocompleteSuggestion && (inputValue || isMenuDirectlyActivated)) {
93
+ inputRef.current.value = autocompleteSuggestion;
94
+
95
+ if (autocompleteSuggestion.toLowerCase().indexOf(inputValue.toLowerCase()) === 0) {
96
+ inputRef.current.setSelectionRange(inputValue.length, autocompleteSuggestion.length);
97
+ }
98
+ }
99
+ }, [autocompleteSuggestion, inputValue]);
100
+ useEffect(() => {
101
+ if (value) {
102
+ setInputValue && setInputValue(value.toString());
103
+ }
104
+ }, [value]);
105
+ return /*#__PURE__*/React.createElement(Component, _extends({
106
+ onFocus: handleInputFocus,
107
+ onBlur: handleInputBlur,
108
+ onChange: handleInputChange,
109
+ onKeyDown: handleInputKeyDown,
110
+ onKeyPress: onInputKeyPress,
111
+ onKeyUp: handleInputKeyUp,
112
+ ref: combinedInputRef,
113
+ "aria-controls": `${id}-listbox`,
114
+ "aria-autocomplete": "both",
115
+ role: "combobox",
116
+ "aria-expanded": showMenu,
117
+ "aria-haspopup": "listbox",
118
+ "aria-owns": `${id}-listbox`,
119
+ autocomplete: "off"
120
+ }, props));
121
+ });
122
+ export default AutocompleteInput;