@primer/components 0.0.0-202192231947 → 0.0.0-202192543046

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 (177) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/codemods/deprecateUtilityComponents.js +1 -1
  3. package/dist/browser.esm.js +880 -770
  4. package/dist/browser.esm.js.map +1 -1
  5. package/dist/browser.umd.js +883 -773
  6. package/dist/browser.umd.js.map +1 -1
  7. package/lib/ActionList/Item.js +4 -4
  8. package/lib/ActionList/List.d.ts +2 -1
  9. package/lib/AnchoredOverlay/AnchoredOverlay.d.ts +6 -3
  10. package/lib/AnchoredOverlay/AnchoredOverlay.js +11 -3
  11. package/lib/Autocomplete/Autocomplete.d.ts +306 -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 +294 -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/BaseStyles.js +1 -1
  24. package/lib/BorderBox.js +1 -1
  25. package/lib/Button/Button.js +1 -1
  26. package/lib/Button/ButtonInvisible.js +1 -1
  27. package/lib/Button/ButtonTableList.js +1 -1
  28. package/lib/Caret.js +2 -2
  29. package/lib/DatePicker/DatePicker.js +14 -8
  30. package/lib/DatePicker/DatePickerAnchor.js +7 -2
  31. package/lib/DatePicker/DatePickerOverlay.d.ts +3 -0
  32. package/lib/DatePicker/DatePickerOverlay.js +39 -0
  33. package/lib/DatePicker/DatePickerPanel.js +92 -9
  34. package/lib/DatePicker/Day.js +2 -1
  35. package/lib/DatePicker/Month.js +16 -6
  36. package/lib/DatePicker/useDatePicker.d.ts +18 -2
  37. package/lib/DatePicker/useDatePicker.js +155 -51
  38. package/lib/Dialog.js +1 -1
  39. package/lib/FilteredActionList/FilteredActionList.js +5 -31
  40. package/lib/Flash.js +16 -16
  41. package/lib/Label.js +1 -1
  42. package/lib/Overlay.d.ts +1 -0
  43. package/lib/Overlay.js +3 -1
  44. package/lib/Pagination/Pagination.js +1 -1
  45. package/lib/ProgressBar.js +1 -1
  46. package/lib/SelectMenu/SelectMenu.d.ts +12 -10
  47. package/lib/StateLabel.js +13 -19
  48. package/lib/TextInput.d.ts +5 -13
  49. package/lib/TextInput.js +4 -46
  50. package/lib/TextInputWithTokens.d.ts +325 -0
  51. package/lib/TextInputWithTokens.js +245 -0
  52. package/lib/Token/AvatarToken.d.ts +7 -0
  53. package/lib/Token/AvatarToken.js +64 -0
  54. package/lib/Token/IssueLabelToken.d.ts +14 -0
  55. package/lib/Token/IssueLabelToken.js +144 -0
  56. package/lib/Token/Token.d.ts +15 -0
  57. package/lib/Token/Token.js +94 -0
  58. package/lib/Token/TokenBase.d.ts +31 -0
  59. package/lib/Token/TokenBase.js +108 -0
  60. package/lib/Token/_RemoveTokenButton.d.ts +12 -0
  61. package/lib/Token/_RemoveTokenButton.js +77 -0
  62. package/lib/Token/_TokenTextContainer.d.ts +3 -0
  63. package/lib/Token/_TokenTextContainer.js +17 -0
  64. package/lib/Token/index.d.ts +3 -0
  65. package/lib/Token/index.js +31 -0
  66. package/lib/_TextInputWrapper.d.ts +10 -0
  67. package/lib/_TextInputWrapper.js +51 -0
  68. package/lib/_UnstyledTextInput.d.ts +2 -0
  69. package/lib/_UnstyledTextInput.js +20 -0
  70. package/lib/behaviors/scrollIntoViewingArea.d.ts +1 -0
  71. package/lib/behaviors/scrollIntoViewingArea.js +39 -0
  72. package/lib/hooks/useOpenAndCloseFocus.d.ts +2 -1
  73. package/lib/hooks/useOpenAndCloseFocus.js +7 -2
  74. package/lib/hooks/useOverlay.d.ts +2 -1
  75. package/lib/hooks/useOverlay.js +4 -2
  76. package/lib/index.d.ts +5 -0
  77. package/lib/index.js +36 -0
  78. package/lib/theme-preval.js +372 -3102
  79. package/lib/utils/testing.d.ts +51 -494
  80. package/lib/utils/{types.d.ts → types/AriaRole.d.ts} +0 -13
  81. package/lib/utils/{types.js → types/AriaRole.js} +0 -0
  82. package/lib/utils/types/ComponentProps.d.ts +9 -0
  83. package/lib/utils/types/ComponentProps.js +1 -0
  84. package/lib/utils/types/Flatten.d.ts +4 -0
  85. package/lib/utils/types/Flatten.js +1 -0
  86. package/lib/utils/types/MandateProps.d.ts +3 -0
  87. package/lib/utils/types/MandateProps.js +1 -0
  88. package/lib/utils/types/Merge.d.ts +19 -0
  89. package/lib/utils/types/Merge.js +1 -0
  90. package/lib/utils/types/index.d.ts +5 -0
  91. package/lib/utils/types/index.js +70 -0
  92. package/lib-esm/ActionList/Item.js +4 -4
  93. package/lib-esm/ActionList/List.d.ts +2 -1
  94. package/lib-esm/AnchoredOverlay/AnchoredOverlay.d.ts +6 -3
  95. package/lib-esm/AnchoredOverlay/AnchoredOverlay.js +11 -3
  96. package/lib-esm/Autocomplete/Autocomplete.d.ts +306 -0
  97. package/lib-esm/Autocomplete/Autocomplete.js +123 -0
  98. package/lib-esm/Autocomplete/AutocompleteContext.d.ts +17 -0
  99. package/lib-esm/Autocomplete/AutocompleteContext.js +2 -0
  100. package/lib-esm/Autocomplete/AutocompleteInput.d.ts +294 -0
  101. package/lib-esm/Autocomplete/AutocompleteInput.js +138 -0
  102. package/lib-esm/Autocomplete/AutocompleteMenu.d.ts +72 -0
  103. package/lib-esm/Autocomplete/AutocompleteMenu.js +205 -0
  104. package/lib-esm/Autocomplete/AutocompleteOverlay.d.ts +20 -0
  105. package/lib-esm/Autocomplete/AutocompleteOverlay.js +62 -0
  106. package/lib-esm/Autocomplete/index.d.ts +2 -0
  107. package/lib-esm/Autocomplete/index.js +1 -0
  108. package/lib-esm/BaseStyles.js +1 -1
  109. package/lib-esm/BorderBox.js +1 -1
  110. package/lib-esm/Button/Button.js +1 -1
  111. package/lib-esm/Button/ButtonInvisible.js +1 -1
  112. package/lib-esm/Button/ButtonTableList.js +1 -1
  113. package/lib-esm/Caret.js +2 -2
  114. package/lib-esm/DatePicker/DatePicker.js +13 -6
  115. package/lib-esm/DatePicker/DatePickerAnchor.js +7 -2
  116. package/lib-esm/DatePicker/DatePickerOverlay.d.ts +3 -0
  117. package/lib-esm/DatePicker/DatePickerOverlay.js +24 -0
  118. package/lib-esm/DatePicker/DatePickerPanel.js +86 -10
  119. package/lib-esm/DatePicker/Day.js +2 -1
  120. package/lib-esm/DatePicker/Month.js +15 -6
  121. package/lib-esm/DatePicker/useDatePicker.d.ts +18 -2
  122. package/lib-esm/DatePicker/useDatePicker.js +156 -52
  123. package/lib-esm/Dialog.js +1 -1
  124. package/lib-esm/FilteredActionList/FilteredActionList.js +3 -31
  125. package/lib-esm/Flash.js +16 -16
  126. package/lib-esm/Label.js +1 -1
  127. package/lib-esm/Overlay.d.ts +1 -0
  128. package/lib-esm/Overlay.js +3 -1
  129. package/lib-esm/Pagination/Pagination.js +1 -1
  130. package/lib-esm/ProgressBar.js +1 -1
  131. package/lib-esm/SelectMenu/SelectMenu.d.ts +12 -10
  132. package/lib-esm/StateLabel.js +13 -19
  133. package/lib-esm/TextInput.d.ts +5 -13
  134. package/lib-esm/TextInput.js +4 -37
  135. package/lib-esm/TextInputWithTokens.d.ts +325 -0
  136. package/lib-esm/TextInputWithTokens.js +220 -0
  137. package/lib-esm/Token/AvatarToken.d.ts +7 -0
  138. package/lib-esm/Token/AvatarToken.js +43 -0
  139. package/lib-esm/Token/IssueLabelToken.d.ts +14 -0
  140. package/lib-esm/Token/IssueLabelToken.js +124 -0
  141. package/lib-esm/Token/Token.d.ts +15 -0
  142. package/lib-esm/Token/Token.js +73 -0
  143. package/lib-esm/Token/TokenBase.d.ts +31 -0
  144. package/lib-esm/Token/TokenBase.js +87 -0
  145. package/lib-esm/Token/_RemoveTokenButton.d.ts +12 -0
  146. package/lib-esm/Token/_RemoveTokenButton.js +60 -0
  147. package/lib-esm/Token/_TokenTextContainer.d.ts +3 -0
  148. package/lib-esm/Token/_TokenTextContainer.js +6 -0
  149. package/lib-esm/Token/index.d.ts +3 -0
  150. package/lib-esm/Token/index.js +3 -0
  151. package/lib-esm/_TextInputWrapper.d.ts +10 -0
  152. package/lib-esm/_TextInputWrapper.js +31 -0
  153. package/lib-esm/_UnstyledTextInput.d.ts +2 -0
  154. package/lib-esm/_UnstyledTextInput.js +7 -0
  155. package/lib-esm/behaviors/scrollIntoViewingArea.d.ts +1 -0
  156. package/lib-esm/behaviors/scrollIntoViewingArea.js +30 -0
  157. package/lib-esm/hooks/useOpenAndCloseFocus.d.ts +2 -1
  158. package/lib-esm/hooks/useOpenAndCloseFocus.js +7 -2
  159. package/lib-esm/hooks/useOverlay.d.ts +2 -1
  160. package/lib-esm/hooks/useOverlay.js +4 -2
  161. package/lib-esm/index.d.ts +5 -0
  162. package/lib-esm/index.js +3 -0
  163. package/lib-esm/theme-preval.js +372 -3102
  164. package/lib-esm/utils/testing.d.ts +51 -494
  165. package/lib-esm/utils/{types.d.ts → types/AriaRole.d.ts} +0 -13
  166. package/lib-esm/utils/{types.js → types/AriaRole.js} +0 -0
  167. package/lib-esm/utils/types/ComponentProps.d.ts +9 -0
  168. package/lib-esm/utils/types/ComponentProps.js +1 -0
  169. package/lib-esm/utils/types/Flatten.d.ts +4 -0
  170. package/lib-esm/utils/types/Flatten.js +1 -0
  171. package/lib-esm/utils/types/MandateProps.d.ts +3 -0
  172. package/lib-esm/utils/types/MandateProps.js +1 -0
  173. package/lib-esm/utils/types/Merge.d.ts +19 -0
  174. package/lib-esm/utils/types/Merge.js +1 -0
  175. package/lib-esm/utils/types/index.d.ts +5 -0
  176. package/lib-esm/utils/types/index.js +5 -0
  177. package/package.json +10 -9
@@ -0,0 +1,205 @@
1
+ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { ActionList } from '../ActionList';
3
+ import { useFocusZone } from '../hooks/useFocusZone';
4
+ import { Box, Spinner } from '../';
5
+ import { AutocompleteContext } from './AutocompleteContext';
6
+ import { PlusIcon } from '@primer/octicons-react';
7
+ import { uniqueId } from '../utils/uniqueId';
8
+ import { scrollIntoViewingArea } from '../behaviors/scrollIntoViewingArea';
9
+
10
+ const getDefaultSortFn = isItemSelectedFn => (itemIdA, itemIdB) => isItemSelectedFn(itemIdA) === isItemSelectedFn(itemIdB) ? 0 : isItemSelectedFn(itemIdA) ? -1 : 1;
11
+
12
+ function getDefaultItemFilter(filterValue) {
13
+ return function (item, _i) {
14
+ var _item$text;
15
+
16
+ return Boolean((_item$text = item.text) === null || _item$text === void 0 ? void 0 : _item$text.toLowerCase().startsWith(filterValue.toLowerCase()));
17
+ };
18
+ }
19
+
20
+ function getDefaultOnSelectionChange(setInputValueFn) {
21
+ return function (itemOrItems) {
22
+ const {
23
+ text = ''
24
+ } = Array.isArray(itemOrItems) ? itemOrItems.slice(-1)[0] : itemOrItems;
25
+ setInputValueFn(text);
26
+ };
27
+ }
28
+
29
+ const isItemSelected = (itemId, selectedItemIds) => selectedItemIds.includes(itemId);
30
+
31
+ function getItemById(itemId, items) {
32
+ return items.find(item => item.id === itemId);
33
+ } // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+
35
+
36
+ function AutocompleteMenu(props) {
37
+ const autocompleteContext = useContext(AutocompleteContext);
38
+
39
+ if (autocompleteContext === null) {
40
+ throw new Error('AutocompleteContext returned null values');
41
+ }
42
+
43
+ const {
44
+ activeDescendantRef,
45
+ id,
46
+ inputRef,
47
+ inputValue = '',
48
+ scrollContainerRef,
49
+ setAutocompleteSuggestion,
50
+ setShowMenu,
51
+ setInputValue,
52
+ setIsMenuDirectlyActivated,
53
+ setSelectedItemLength,
54
+ showMenu
55
+ } = autocompleteContext;
56
+ const {
57
+ items,
58
+ selectedItemIds,
59
+ sortOnCloseFn,
60
+ emptyStateText,
61
+ addNewItem,
62
+ loading,
63
+ selectionVariant,
64
+ filterFn,
65
+ 'aria-labelledby': ariaLabelledBy,
66
+ onOpenChange,
67
+ onSelectedChange,
68
+ customScrollContainerRef
69
+ } = props;
70
+ const listContainerRef = useRef(null);
71
+ const [highlightedItem, setHighlightedItem] = useState();
72
+ const [sortedItemIds, setSortedItemIds] = useState(items.map(({
73
+ id: itemId
74
+ }) => itemId));
75
+ const selectableItems = useMemo(() => items.map(selectableItem => {
76
+ return { ...selectableItem,
77
+ role: 'option',
78
+ id: selectableItem.id,
79
+ selected: selectionVariant === 'multiple' ? selectedItemIds.includes(selectableItem.id) : undefined,
80
+ onAction: item => {
81
+ const otherSelectedItemIds = selectedItemIds.filter(selectedItemId => selectedItemId !== item.id);
82
+ const newSelectedItemIds = selectedItemIds.includes(item.id) ? otherSelectedItemIds : [...otherSelectedItemIds, item.id];
83
+ const onSelectedChangeFn = onSelectedChange ? onSelectedChange : getDefaultOnSelectionChange(setInputValue);
84
+ onSelectedChangeFn(newSelectedItemIds.map(newSelectedItemId => getItemById(newSelectedItemId, items)));
85
+
86
+ if (selectionVariant === 'multiple') {
87
+ setInputValue('');
88
+ setAutocompleteSuggestion('');
89
+ } else {
90
+ var _inputRef$current;
91
+
92
+ setShowMenu(false);
93
+ (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.setSelectionRange(inputRef.current.value.length, inputRef.current.value.length);
94
+ }
95
+ }
96
+ };
97
+ }), [items, selectedItemIds, inputRef, onSelectedChange, selectionVariant, setAutocompleteSuggestion, setInputValue, setShowMenu]);
98
+ const itemSortOrderData = useMemo(() => sortedItemIds.reduce((acc, curr, i) => {
99
+ acc[curr] = i;
100
+ return acc;
101
+ }, {}), [sortedItemIds]);
102
+ const sortedAndFilteredItemsToRender = useMemo(() => selectableItems.filter(filterFn ? filterFn : getDefaultItemFilter(inputValue)).sort((a, b) => itemSortOrderData[a.id] - itemSortOrderData[b.id]), [selectableItems, itemSortOrderData, filterFn, inputValue]);
103
+ const allItemsToRender = useMemo(() => [// sorted and filtered selectable items
104
+ ...sortedAndFilteredItemsToRender, // menu item used for creating a token from whatever is in the text input
105
+ ...(addNewItem ? [{ ...addNewItem,
106
+ leadingVisual: () => /*#__PURE__*/React.createElement(PlusIcon, null),
107
+ onAction: item => {
108
+ // TODO: make it possible to pass a leadingVisual when using `addNewItem`
109
+ addNewItem.handleAddItem({ ...item,
110
+ id: item.id || uniqueId(),
111
+ leadingVisual: undefined
112
+ });
113
+
114
+ if (selectionVariant === 'multiple') {
115
+ setInputValue('');
116
+ setAutocompleteSuggestion('');
117
+ }
118
+ }
119
+ }] : [])], [sortedAndFilteredItemsToRender, addNewItem, setAutocompleteSuggestion, selectionVariant, setInputValue]);
120
+ useFocusZone({
121
+ containerRef: listContainerRef,
122
+ focusOutBehavior: 'wrap',
123
+ focusableElementFilter: element => {
124
+ return !(element instanceof HTMLInputElement);
125
+ },
126
+ activeDescendantFocus: inputRef,
127
+ onActiveDescendantChanged: (current, _previous, directlyActivated) => {
128
+ activeDescendantRef.current = current || null;
129
+
130
+ if (current) {
131
+ const selectedItem = selectableItems.find(item => item.id.toString() === current.getAttribute('data-id'));
132
+ setHighlightedItem(selectedItem);
133
+ setIsMenuDirectlyActivated(directlyActivated);
134
+ }
135
+
136
+ if (current && customScrollContainerRef && customScrollContainerRef.current && directlyActivated) {
137
+ scrollIntoViewingArea(current, customScrollContainerRef.current);
138
+ } else if (current && scrollContainerRef.current && directlyActivated) {
139
+ scrollIntoViewingArea(current, scrollContainerRef.current);
140
+ }
141
+ }
142
+ }, [loading]);
143
+ useEffect(() => {
144
+ var _highlightedItem$text;
145
+
146
+ if (highlightedItem !== null && highlightedItem !== void 0 && (_highlightedItem$text = highlightedItem.text) !== null && _highlightedItem$text !== void 0 && _highlightedItem$text.startsWith(inputValue) && !selectedItemIds.includes(highlightedItem.id)) {
147
+ setAutocompleteSuggestion(highlightedItem.text);
148
+ } else {
149
+ setAutocompleteSuggestion('');
150
+ }
151
+ }, [highlightedItem, inputValue, selectedItemIds, setAutocompleteSuggestion]);
152
+ useEffect(() => {
153
+ const itemIdSortResult = [...sortedItemIds].sort(sortOnCloseFn ? sortOnCloseFn : getDefaultSortFn(itemId => isItemSelected(itemId, selectedItemIds)));
154
+ const sortResultMatchesState = itemIdSortResult.length === sortedItemIds.length && itemIdSortResult.every((element, index) => element === sortedItemIds[index]);
155
+
156
+ if (showMenu === false && !sortResultMatchesState) {
157
+ setSortedItemIds(itemIdSortResult);
158
+ }
159
+
160
+ onOpenChange && onOpenChange(Boolean(showMenu));
161
+ }, [showMenu, onOpenChange, selectedItemIds, sortOnCloseFn, sortedItemIds]);
162
+ useEffect(() => {
163
+ if (selectedItemIds.length) {
164
+ setSelectedItemLength(selectedItemIds.length);
165
+ }
166
+ }, [selectedItemIds, setSelectedItemLength]);
167
+ return /*#__PURE__*/React.createElement(Box, {
168
+ sx: !showMenu ? {
169
+ // visually hides this label for sighted users
170
+ position: 'absolute',
171
+ width: '1px',
172
+ height: '1px',
173
+ padding: '0',
174
+ margin: '-1px',
175
+ overflow: 'hidden',
176
+ clip: 'rect(0, 0, 0, 0)',
177
+ whiteSpace: 'nowrap',
178
+ borderWidth: '0'
179
+ } : {}
180
+ }, loading ? /*#__PURE__*/React.createElement(Box, {
181
+ p: 3,
182
+ display: "flex",
183
+ justifyContent: "center"
184
+ }, /*#__PURE__*/React.createElement(Spinner, null)) : /*#__PURE__*/React.createElement("div", {
185
+ ref: listContainerRef
186
+ }, allItemsToRender.length ? /*#__PURE__*/React.createElement(ActionList, {
187
+ selectionVariant: "multiple" // have to typecast to `ItemProps` because we have an extra property
188
+ // on `items` for Autocomplete: `metadata`
189
+ ,
190
+ items: allItemsToRender,
191
+ role: "listbox",
192
+ id: `${id}-listbox`,
193
+ "aria-labelledby": ariaLabelledBy
194
+ }) : /*#__PURE__*/React.createElement(Box, {
195
+ p: 3
196
+ }, emptyStateText)));
197
+ }
198
+
199
+ AutocompleteMenu.displayName = "AutocompleteMenu";
200
+ AutocompleteMenu.defaultProps = {
201
+ emptyStateText: 'No selectable options',
202
+ selectionVariant: 'single'
203
+ };
204
+ AutocompleteMenu.displayName = 'AutocompleteMenu';
205
+ export default AutocompleteMenu;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { OverlayProps } from '../Overlay';
3
+ import { ComponentProps } from '../utils/types';
4
+ declare type AutocompleteOverlayInternalProps = {
5
+ /**
6
+ * The ref of the element that the position of the menu is based on. By default, the menu is positioned based on the text input
7
+ */
8
+ menuAnchorRef?: React.RefObject<HTMLElement>;
9
+ /**
10
+ * Props to be spread on the internal `Overlay` component.
11
+ */
12
+ overlayProps?: Partial<OverlayProps>;
13
+ children?: React.ReactNode;
14
+ } & Pick<React.AriaAttributes, 'aria-labelledby'>;
15
+ declare function AutocompleteOverlay({ menuAnchorRef, overlayProps, children }: AutocompleteOverlayInternalProps): JSX.Element | null;
16
+ declare namespace AutocompleteOverlay {
17
+ var displayName: string;
18
+ }
19
+ export declare type AutocompleteOverlayProps = ComponentProps<typeof AutocompleteOverlay>;
20
+ export default AutocompleteOverlay;
@@ -0,0 +1,62 @@
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 } from 'react';
4
+ import { useAnchoredPosition } from '../hooks';
5
+ import Overlay from '../Overlay';
6
+ import { AutocompleteContext } from './AutocompleteContext';
7
+ import { useCombinedRefs } from '../hooks/useCombinedRefs';
8
+
9
+ // TODO: consider making 'aria-labelledby' required
10
+ function AutocompleteOverlay({
11
+ menuAnchorRef,
12
+ overlayProps,
13
+ children
14
+ }) {
15
+ const autocompleteContext = useContext(AutocompleteContext);
16
+
17
+ if (autocompleteContext === null) {
18
+ throw new Error('AutocompleteContext returned null values');
19
+ }
20
+
21
+ const {
22
+ inputRef,
23
+ scrollContainerRef,
24
+ selectedItemLength,
25
+ setShowMenu,
26
+ showMenu = false
27
+ } = autocompleteContext;
28
+ const {
29
+ floatingElementRef,
30
+ position
31
+ } = useAnchoredPosition({
32
+ side: 'outside-bottom',
33
+ align: 'start',
34
+ anchorElementRef: menuAnchorRef ? menuAnchorRef : inputRef
35
+ }, [showMenu, selectedItemLength]);
36
+ const combinedOverlayRef = useCombinedRefs(scrollContainerRef, floatingElementRef);
37
+ const closeOptionList = useCallback(() => {
38
+ setShowMenu(false);
39
+ }, [setShowMenu]);
40
+
41
+ if (typeof window === 'undefined') {
42
+ return null;
43
+ }
44
+
45
+ return /*#__PURE__*/React.createElement(Overlay, _extends({
46
+ returnFocusRef: inputRef,
47
+ preventFocusOnOpen: true,
48
+ onClickOutside: closeOptionList,
49
+ onEscape: closeOptionList,
50
+ ref: combinedOverlayRef,
51
+ top: position === null || position === void 0 ? void 0 : position.top,
52
+ left: position === null || position === void 0 ? void 0 : position.left,
53
+ visibility: showMenu ? 'visible' : 'hidden',
54
+ sx: {
55
+ overflow: 'auto'
56
+ }
57
+ }, overlayProps), children);
58
+ }
59
+
60
+ AutocompleteOverlay.displayName = "AutocompleteOverlay";
61
+ AutocompleteOverlay.displayName = 'AutocompleteOverlay';
62
+ export default AutocompleteOverlay;
@@ -0,0 +1,2 @@
1
+ export { default } from './Autocomplete';
2
+ export type { AutocompleteMenuProps, AutocompleteInputProps, AutocompleteOverlayProps } from './Autocomplete';
@@ -0,0 +1 @@
1
+ export { default } from './Autocomplete';
@@ -42,7 +42,7 @@ function BaseStyles(props) {
42
42
 
43
43
  BaseStyles.displayName = "BaseStyles";
44
44
  BaseStyles.defaultProps = {
45
- color: 'text.primary',
45
+ color: 'fg.default',
46
46
  fontFamily: 'normal',
47
47
  lineHeight: 'default'
48
48
  };
@@ -11,7 +11,7 @@ const BorderBox = styled(Box).withConfig({
11
11
  BorderBox.defaultProps = {
12
12
  borderWidth: '1px',
13
13
  borderStyle: 'solid',
14
- borderColor: 'border.primary',
14
+ borderColor: 'border.default',
15
15
  borderRadius: 2
16
16
  };
17
17
  export default BorderBox;
@@ -5,5 +5,5 @@ import ButtonBase, { buttonSystemProps } from './ButtonBase';
5
5
  const Button = styled(ButtonBase).withConfig({
6
6
  displayName: "Button",
7
7
  componentId: "xjtz72-0"
8
- })(["color:", ";background-color:", ";border:1px solid ", ";box-shadow:", ",", "};&:hover{background-color:", ";border-color:", ";}&:focus{border-color:", ";box-shadow:", ";}&:active{background-color:", ";box-shadow:", ";}&:disabled{color:", ";background-color:", ";border-color:", ";}", ";", ";"], get('colors.btn.text'), get('colors.btn.bg'), get('colors.btn.border'), get('shadows.btn.shadow'), get('shadows.btn.insetShadow'), get('colors.btn.hoverBg'), get('colors.btn.hoverBorder'), get('colors.btn.focusBorder'), get('shadows.btn.focusShadow'), get('colors.btn.selectedBg'), get('shadows.btn.shadowActive'), get('colors.fg.muted'), get('colors.btn.bg'), get('colors.btn.border'), buttonSystemProps, sx);
8
+ })(["color:", ";background-color:", ";border:1px solid ", ";box-shadow:", ",", "};&:hover{background-color:", ";border-color:", ";}&:focus{border-color:", ";box-shadow:", ";}&:active{background-color:", ";box-shadow:", ";}&:disabled{color:", ";background-color:", ";border-color:", ";}", ";", ";"], get('colors.btn.text'), get('colors.btn.bg'), get('colors.btn.border'), get('shadows.btn.shadow'), get('shadows.btn.insetShadow'), get('colors.btn.hoverBg'), get('colors.btn.hoverBorder'), get('colors.btn.focusBorder'), get('shadows.btn.focusShadow'), get('colors.btn.selectedBg'), get('shadows.btn.shadowActive'), get('colors.primer.fg.disabled'), get('colors.btn.bg'), get('colors.btn.border'), buttonSystemProps, sx);
9
9
  export default Button;
@@ -5,5 +5,5 @@ import ButtonBase, { buttonSystemProps } from './ButtonBase';
5
5
  const ButtonInvisible = styled(ButtonBase).withConfig({
6
6
  displayName: "ButtonInvisible",
7
7
  componentId: "sc-1195tpn-0"
8
- })(["color:", ";background-color:transparent;border:0;border-radius:", ";box-shadow:none;&:disabled{color:", ";}&:focus{box-shadow:", ";}", ";", ""], get('colors.accent.fg'), get('radii.2'), get('colors.fg.muted'), get('shadows.btn.focusShadow'), buttonSystemProps, sx);
8
+ })(["color:", ";background-color:transparent;border:0;border-radius:", ";box-shadow:none;&:disabled{color:", ";}&:focus{box-shadow:", ";}&:hover{background-color:", ";}&:active{background-color:", ";}", ";", ""], get('colors.accent.fg'), get('radii.2'), get('colors.primer.fg.disabled'), get('shadows.btn.focusShadow'), get('colors.btn.hoverBg'), get('colors.btn.selectedBg'), buttonSystemProps, sx);
9
9
  export default ButtonInvisible;
@@ -4,5 +4,5 @@ import sx from '../sx';
4
4
  const ButtonTableList = styled.summary.withConfig({
5
5
  displayName: "ButtonTableList",
6
6
  componentId: "sc-1m4q8ia-0"
7
- })(["display:inline-block;padding:0;font-size:", ";color:", ";text-decoration:none;white-space:nowrap;cursor:pointer;user-select:none;background-color:transparent;border:0;appearance:none;&:hover{text-decoration:underline;}&:disabled{&,&:hover{color:rgba(", ",0.5);cursor:default;}}&:after{display:inline-block;margin-left:", ";width:0;height:0;vertical-align:-2px;content:'';border:4px solid transparent;border-top-color:currentcolor;}", " ", " ", " ", ";"], get('fontSizes.1'), get('colors.fg.muted'), get('colors.fg.muted'), get('space.1'), COMMON, TYPOGRAPHY, LAYOUT, sx);
7
+ })(["display:inline-block;padding:0;font-size:", ";color:", ";text-decoration:none;white-space:nowrap;cursor:pointer;user-select:none;background-color:transparent;border:0;appearance:none;&:hover{text-decoration:underline;}&:disabled{&,&:hover{color:", ";cursor:default;}}&:after{display:inline-block;margin-left:", ";width:0;height:0;vertical-align:-2px;content:'';border:4px solid transparent;border-top-color:currentcolor;}", " ", " ", " ", ";"], get('fontSizes.1'), get('colors.fg.muted'), get('colors.primer.fg.disabled'), get('space.1'), COMMON, TYPOGRAPHY, LAYOUT, sx);
8
8
  export default ButtonTableList;
package/lib-esm/Caret.js CHANGED
@@ -106,8 +106,8 @@ function Caret(props) {
106
106
  Caret.displayName = "Caret";
107
107
  Caret.locations = ['top', 'top-left', 'top-right', 'right', 'right-top', 'right-bottom', 'bottom', 'bottom-left', 'bottom-right', 'left', 'left-top', 'left-bottom'];
108
108
  Caret.defaultProps = {
109
- bg: 'bg.canvas',
110
- borderColor: 'border.primary',
109
+ bg: 'canvas.default',
110
+ borderColor: 'border.default',
111
111
  borderWidth: 1
112
112
  };
113
113
  export default Caret;
@@ -1,13 +1,15 @@
1
1
  import React, { useRef, useState } from 'react';
2
- import { AnchoredOverlay } from '../AnchoredOverlay';
3
2
  import { DatePickerAnchor } from './DatePickerAnchor';
4
- import { DatePickerPanel } from './DatePickerPanel';
5
3
  import { DatePickerProvider } from './useDatePicker';
4
+ import { DatePickerOverlay } from './DatePickerOverlay';
6
5
  export const DatePicker = ({
7
6
  anchorVariant,
8
7
  anchorRef: externalAnchorRef,
8
+ confirmation,
9
9
  focusTrapSettings,
10
10
  focusZoneSettings,
11
+ maxDate,
12
+ minDate,
11
13
  onOpen: onOpenExternal,
12
14
  onClose: onCloseExternal,
13
15
  open,
@@ -15,14 +17,19 @@ export const DatePicker = ({
15
17
  renderAnchor,
16
18
  selection,
17
19
  value,
18
- view
20
+ view,
21
+ weekStartsOn
19
22
  }) => {
20
23
  const buttonRef = useRef(null);
21
24
  const [isOpen, setIsOpen] = useState(false);
22
25
  const datePickerConfiguration = {
23
26
  anchorVariant,
27
+ confirmation,
28
+ maxDate,
29
+ minDate,
24
30
  selection,
25
- view
31
+ view,
32
+ weekStartsOn
26
33
  };
27
34
 
28
35
  const onOpen = gesture => {
@@ -52,7 +59,7 @@ export const DatePicker = ({
52
59
  }, /*#__PURE__*/React.createElement(DatePickerAnchor, {
53
60
  ref: buttonRef,
54
61
  onAction: toggleIsOpen
55
- }), /*#__PURE__*/React.createElement(AnchoredOverlay, {
62
+ }), /*#__PURE__*/React.createElement(DatePickerOverlay, {
56
63
  anchorRef: externalAnchorRef !== null && externalAnchorRef !== void 0 ? externalAnchorRef : buttonRef,
57
64
  renderAnchor: renderAnchor,
58
65
  open: open !== null && open !== void 0 ? open : isOpen,
@@ -61,6 +68,6 @@ export const DatePicker = ({
61
68
  overlayProps: overlayProps,
62
69
  focusTrapSettings: focusTrapSettings,
63
70
  focusZoneSettings: focusZoneSettings
64
- }, /*#__PURE__*/React.createElement(DatePickerPanel, null)));
71
+ }));
65
72
  };
66
73
  DatePicker.displayName = "DatePicker";
@@ -10,7 +10,7 @@ import TextInput from '../TextInput';
10
10
  const DatePickerAnchorButton = styled(Button).withConfig({
11
11
  displayName: "DatePickerAnchor__DatePickerAnchorButton",
12
12
  componentId: "sc-8gpb9d-0"
13
- })(["align-items:center;display:flex;flex-direction:row;justify-content:space-between;& ", "{margin-left:", ";}"], Text, get('space.2'));
13
+ })(["align-items:center;display:flex;flex-direction:row;justify-content:space-between;max-width:350px;overflow:hidden;& ", "{margin-left:", ";}"], Text, get('space.2'));
14
14
  export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
15
15
  onAction
16
16
  }, ref) => {
@@ -54,5 +54,10 @@ export const DatePickerAnchor = /*#__PURE__*/React.forwardRef(({
54
54
  sx: {
55
55
  my: '2px'
56
56
  }
57
- }), anchorVariant !== 'icon-only' && /*#__PURE__*/React.createElement(Text, null, formattedDate));
57
+ }), anchorVariant !== 'icon-only' && /*#__PURE__*/React.createElement(Text, {
58
+ sx: {
59
+ overflow: 'hidden',
60
+ textOverflow: 'ellipsis'
61
+ }
62
+ }, formattedDate));
58
63
  });
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { AnchoredOverlayProps } from '../AnchoredOverlay';
3
+ export declare const DatePickerOverlay: React.FC<AnchoredOverlayProps>;
@@ -0,0 +1,24 @@
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 from 'react';
4
+ import useDatePicker from './useDatePicker';
5
+ import { AnchoredOverlay } from '../AnchoredOverlay';
6
+ import { DatePickerPanel } from './DatePickerPanel';
7
+ export const DatePickerOverlay = ({
8
+ onClose,
9
+ ...rest
10
+ }) => {
11
+ const {
12
+ revertValue
13
+ } = useDatePicker();
14
+
15
+ const onOverlayClose = gesture => {
16
+ revertValue();
17
+ onClose === null || onClose === void 0 ? void 0 : onClose(gesture);
18
+ };
19
+
20
+ return /*#__PURE__*/React.createElement(AnchoredOverlay, _extends({
21
+ onClose: onOverlayClose
22
+ }, rest), /*#__PURE__*/React.createElement(DatePickerPanel, null));
23
+ };
24
+ DatePickerOverlay.displayName = "DatePickerOverlay";
@@ -1,24 +1,100 @@
1
- import { addMonths } from 'date-fns';
2
- import React from 'react';
1
+ import { addMonths, subMonths } from 'date-fns';
2
+ import React, { useMemo } from 'react';
3
3
  import Box from '../Box';
4
4
  import { Month } from './Month';
5
5
  import styled from 'styled-components';
6
6
  import { get } from '../constants';
7
7
  import useDatePicker from './useDatePicker';
8
+ import { ChevronLeftIcon, ChevronRightIcon } from '@primer/octicons-react';
9
+ import StyledOcticon from '../StyledOcticon';
10
+ import Button, { ButtonPrimary } from '../Button';
8
11
  const DatePickerPanelContainer = styled(Box).withConfig({
9
12
  displayName: "DatePickerPanel__DatePickerPanelContainer",
10
13
  componentId: "sc-19upxpo-0"
11
- })(["align-items:flex-start;display:flex;flex-direction:row;gap:", ";padding:", ";"], get('space.6'), get('space.3'));
14
+ })(["align-items:stretch;display:flex;flex-direction:column;"]);
15
+ const DatePickerPanelMonths = styled(Box).withConfig({
16
+ displayName: "DatePickerPanel__DatePickerPanelMonths",
17
+ componentId: "sc-19upxpo-1"
18
+ })(["align-items:flex-start;display:flex;flex-direction:row;gap:", ";padding:", ";position:relative;"], get('space.6'), get('space.3'));
19
+ const DatePickerPanelFooter = styled(Box).withConfig({
20
+ displayName: "DatePickerPanel__DatePickerPanelFooter",
21
+ componentId: "sc-19upxpo-2"
22
+ })(["align-items:flex-start;border-top:1px solid;border-top-color:", ";display:flex;gap:", ";padding-top:12px;padding-bottom:12px;padding-left:", ";padding-right:", ";flex-direction:row;justify-content:space-between;position:relative;"], get('colors.border.default'), get('space.6'), get('space.3'), get('space.3'));
23
+ const ArrowButton = styled(Button).withConfig({
24
+ displayName: "DatePickerPanel__ArrowButton",
25
+ componentId: "sc-19upxpo-3"
26
+ })(["position:absolute;width:40px;height:28px;top:12px;", ";"], props => `${props.side}: ${get('space.3')(props)}`);
12
27
  export const DatePickerPanel = () => {
13
28
  const {
14
- configuration
29
+ configuration,
30
+ saveValue,
31
+ revertValue,
32
+ currentViewingDate,
33
+ goToMonth,
34
+ nextMonth,
35
+ previousMonth
15
36
  } = useDatePicker();
16
- return /*#__PURE__*/React.createElement(DatePickerPanelContainer, null, /*#__PURE__*/React.createElement(Month, {
17
- month: new Date().getMonth(),
18
- year: new Date().getFullYear()
37
+ const previousDisabled = useMemo(() => {
38
+ const {
39
+ minDate
40
+ } = configuration;
41
+ if (!minDate) return false;
42
+ const previous = subMonths(currentViewingDate, 1);
43
+
44
+ if (minDate.getFullYear() >= previous.getFullYear() && minDate.getMonth() > previous.getMonth()) {
45
+ return true;
46
+ }
47
+
48
+ return false;
49
+ }, [configuration, currentViewingDate]);
50
+ const nextDisabled = useMemo(() => {
51
+ const {
52
+ maxDate,
53
+ view
54
+ } = configuration;
55
+ if (!maxDate) return false;
56
+ const next = addMonths(currentViewingDate, view === '2-month' ? 2 : 1);
57
+
58
+ if (maxDate.getFullYear() <= next.getFullYear() && maxDate.getMonth() < next.getMonth()) {
59
+ return true;
60
+ }
61
+
62
+ return false;
63
+ }, [configuration, currentViewingDate]);
64
+ return /*#__PURE__*/React.createElement(DatePickerPanelContainer, null, /*#__PURE__*/React.createElement(DatePickerPanelMonths, null, /*#__PURE__*/React.createElement(ArrowButton, {
65
+ variant: "small",
66
+ side: "left",
67
+ onClick: previousMonth,
68
+ disabled: previousDisabled
69
+ }, /*#__PURE__*/React.createElement(StyledOcticon, {
70
+ icon: ChevronLeftIcon,
71
+ color: "fg.muted"
72
+ })), /*#__PURE__*/React.createElement(Month, {
73
+ month: currentViewingDate.getMonth(),
74
+ year: currentViewingDate.getFullYear()
19
75
  }), configuration.view === '2-month' && /*#__PURE__*/React.createElement(Month, {
20
- month: addMonths(new Date(), 1).getMonth(),
21
- year: addMonths(new Date(), 1).getFullYear()
22
- }));
76
+ month: addMonths(currentViewingDate, 1).getMonth(),
77
+ year: addMonths(currentViewingDate, 1).getFullYear()
78
+ }), /*#__PURE__*/React.createElement(ArrowButton, {
79
+ variant: "small",
80
+ side: "right",
81
+ onClick: nextMonth,
82
+ disabled: nextDisabled
83
+ }, /*#__PURE__*/React.createElement(StyledOcticon, {
84
+ icon: ChevronRightIcon,
85
+ color: "fg.muted"
86
+ }))), /*#__PURE__*/React.createElement(DatePickerPanelFooter, null, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Button, {
87
+ variant: "small",
88
+ sx: {
89
+ mr: 1
90
+ },
91
+ onClick: () => revertValue()
92
+ }, "Reset"), /*#__PURE__*/React.createElement(Button, {
93
+ variant: "small",
94
+ onClick: () => goToMonth(new Date())
95
+ }, "Today")), configuration.confirmation && /*#__PURE__*/React.createElement(ButtonPrimary, {
96
+ variant: "small",
97
+ onClick: () => saveValue()
98
+ }, "Apply")));
23
99
  };
24
100
  DatePickerPanel.displayName = "DatePickerPanel";
@@ -101,7 +101,7 @@ const DayComponent = styled(DayBaseComponent).attrs(props => ({
101
101
  })).withConfig({
102
102
  displayName: "Day__DayComponent",
103
103
  componentId: "sc-1japneh-1"
104
- })(["background-color:", ";border-radius:", ";transition:0.2s background-color ease;& ", "{align-self:center;color:", ";display:flex;font-family:", ";font-size:", ";justify-self:center;user-select:none;transition:0.2s color ease;}&:hover{background-color:", ";cursor:pointer;transition:0.05s background-color ease;& ", "{color:", ";transition:0.1s color ease;}}&:active{background-color:", ";box-shadow:inset ", ";transition:0.1s background-color ease,0.1s box-shadow ease,0.1s color ease;& ", "{color:", ";transition:0.1s color ease;}}"], props => props.background, props => props.borderRadius, Text, props => props.textColor, get('fonts.mono'), get('fontSizes.0'), props => props.backgroundHover, Text, props => props.textColorHover, props => props.backgroundPressed, get('shadows.shadow.medium'), Text, props => props.textColorPressed);
104
+ })(["background-color:", ";border-radius:", ";transition:0.1s background-color ease;& ", "{align-self:center;color:", ";display:flex;font-family:", ";font-size:", ";justify-self:center;user-select:none;transition:0.1s color ease;}&:hover{background-color:", ";cursor:pointer;transition:0.05s background-color ease;& ", "{color:", ";transition:0.1s color ease;}}&:active{background-color:", ";box-shadow:inset ", ";transition:0.1s background-color ease,0.1s box-shadow ease,0.1s color ease;& ", "{color:", ";transition:0.1s color ease;}}"], props => props.background, props => props.borderRadius, Text, props => props.textColor, get('fonts.mono'), get('fontSizes.0'), props => props.backgroundHover, Text, props => props.textColorHover, props => props.backgroundPressed, get('shadows.shadow.medium'), Text, props => props.textColorPressed);
105
105
  export const Day = ({
106
106
  date,
107
107
  onAction
@@ -138,6 +138,7 @@ export const Day = ({
138
138
  disabled: disabled,
139
139
  selected: selected,
140
140
  onClick: clickHandler,
141
+ onMouseEnter: () => onDayFocus(date),
141
142
  onFocus: () => onDayFocus(date),
142
143
  onBlur: () => onDayBlur(date),
143
144
  onKeyPress: keyPressHandler
@@ -5,6 +5,7 @@ import Box from '../Box';
5
5
  import Text from '../Text';
6
6
  import { get } from '../constants';
7
7
  import { BlankDay, Day } from './Day';
8
+ import useDatePicker from './useDatePicker';
8
9
  const MonthComponent = styled(Box).withConfig({
9
10
  displayName: "Month__MonthComponent",
10
11
  componentId: "l6j7o0-0"
@@ -21,17 +22,23 @@ export const Month = ({
21
22
  month,
22
23
  year
23
24
  }) => {
25
+ const {
26
+ configuration
27
+ } = useDatePicker();
24
28
  const [selectedDay, setSelectedDay] = useState(null);
25
29
  const getTitle = useMemo(() => `${format(new Date(year, month), 'MMMM yyyy')}`, [month, year]);
26
30
  const weekdayHeaders = useMemo(() => {
27
31
  const now = new Date(year, month);
32
+ const weekOptions = {
33
+ weekStartsOn: configuration.weekStartsOn === 'Sunday' ? 0 : 1
34
+ };
28
35
  return eachDayOfInterval({
29
- start: startOfWeek(now),
30
- end: endOfWeek(now)
36
+ start: startOfWeek(now, weekOptions),
37
+ end: endOfWeek(now, weekOptions)
31
38
  }).map(d => /*#__PURE__*/React.createElement(WeekdayHeader, {
32
39
  key: `weekday-${d}-header`
33
40
  }, format(d, 'EEEEEE')));
34
- }, [month, year]);
41
+ }, [configuration.weekStartsOn, month, year]);
35
42
 
36
43
  const dayAction = date => {
37
44
  setSelectedDay(date);
@@ -40,8 +47,9 @@ export const Month = ({
40
47
  const dayComponents = useMemo(() => {
41
48
  const components = [];
42
49
  const firstDay = new Date(year, month, 1);
50
+ const preBlanks = configuration.weekStartsOn === 'Sunday' ? firstDay.getDay() : (firstDay.getDay() + 6) % 7;
43
51
 
44
- for (let i = 0; i < firstDay.getDay(); i++) {
52
+ for (let i = 0; i < preBlanks; i++) {
45
53
  components.push( /*#__PURE__*/React.createElement(BlankDay, {
46
54
  key: `month-pre-blank-${i}`
47
55
  }));
@@ -58,15 +66,16 @@ export const Month = ({
58
66
  }
59
67
 
60
68
  const lastDay = lastDayOfMonth(firstDay);
69
+ const postBlanks = configuration.weekStartsOn === 'Sunday' ? lastDay.getDay() : (lastDay.getDay() + 6) % 7;
61
70
 
62
- for (let i = 6; i > lastDay.getDay(); i--) {
71
+ for (let i = 6; i > postBlanks; i--) {
63
72
  components.push( /*#__PURE__*/React.createElement(BlankDay, {
64
73
  key: `month-post-blank-${i}`
65
74
  }));
66
75
  }
67
76
 
68
77
  return components;
69
- }, [month, selectedDay, year]);
78
+ }, [configuration.weekStartsOn, month, selectedDay, year]);
70
79
  return /*#__PURE__*/React.createElement(MonthComponent, {
71
80
  role: "grid"
72
81
  }, /*#__PURE__*/React.createElement(MonthTitle, null, getTitle), weekdayHeaders, dayComponents);