@workday/canvas-kit-react 10.0.0-alpha.540-next.0 → 10.0.0-alpha.546-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/collection/index.ts +1 -0
  2. package/collection/lib/useBaseListModel.tsx +3 -3
  3. package/collection/lib/useListItemRegister.tsx +1 -1
  4. package/collection/lib/useListRenderItem.tsx +2 -2
  5. package/combobox/index.ts +11 -4
  6. package/combobox/lib/Combobox.tsx +1 -1
  7. package/combobox/lib/ComboboxCard.tsx +1 -1
  8. package/combobox/lib/ComboboxInput.tsx +2 -90
  9. package/combobox/lib/ComboboxMenu.tsx +4 -3
  10. package/combobox/lib/ComboboxMenuItem.tsx +9 -8
  11. package/combobox/lib/ComboboxMenuList.tsx +1 -1
  12. package/combobox/lib/ComboboxPopper.tsx +20 -0
  13. package/combobox/lib/hooks/index.ts +9 -0
  14. package/combobox/lib/hooks/useComboboxInput.ts +88 -0
  15. package/combobox/lib/hooks/useComboboxKeyboardTypeAhead.ts +127 -0
  16. package/combobox/lib/hooks/useComboboxListKeyboardHandler.ts +23 -0
  17. package/combobox/lib/{useComboboxModel.tsx → hooks/useComboboxModel.tsx} +6 -2
  18. package/combobox/lib/hooks/useComboboxMoveCursorToSelected.tsx +27 -0
  19. package/combobox/lib/hooks/useComboboxResetCursorToSelected.tsx +18 -0
  20. package/combobox/lib/hooks/useSetPopupWidth.ts +16 -0
  21. package/common/lib/utils/components.ts +284 -274
  22. package/common/lib/utils/models.ts +67 -84
  23. package/dist/commonjs/collection/index.d.ts +1 -0
  24. package/dist/commonjs/collection/index.d.ts.map +1 -1
  25. package/dist/commonjs/collection/index.js +3 -1
  26. package/dist/commonjs/collection/lib/useBaseListModel.d.ts +1 -1
  27. package/dist/commonjs/collection/lib/useBaseListModel.d.ts.map +1 -1
  28. package/dist/commonjs/collection/lib/useBaseListModel.js +2 -2
  29. package/dist/commonjs/collection/lib/useListItemRegister.js +2 -2
  30. package/dist/commonjs/collection/lib/useListRenderItem.js +2 -2
  31. package/dist/commonjs/combobox/index.d.ts +1 -4
  32. package/dist/commonjs/combobox/index.d.ts.map +1 -1
  33. package/dist/commonjs/combobox/index.js +11 -9
  34. package/dist/commonjs/combobox/lib/Combobox.d.ts +9 -3
  35. package/dist/commonjs/combobox/lib/Combobox.d.ts.map +1 -1
  36. package/dist/commonjs/combobox/lib/Combobox.js +2 -2
  37. package/dist/commonjs/combobox/lib/ComboboxCard.js +3 -3
  38. package/dist/commonjs/combobox/lib/ComboboxInput.d.ts +1 -106
  39. package/dist/commonjs/combobox/lib/ComboboxInput.d.ts.map +1 -1
  40. package/dist/commonjs/combobox/lib/ComboboxInput.js +4 -71
  41. package/dist/commonjs/combobox/lib/ComboboxMenu.d.ts +9 -3
  42. package/dist/commonjs/combobox/lib/ComboboxMenu.d.ts.map +1 -1
  43. package/dist/commonjs/combobox/lib/ComboboxMenu.js +5 -4
  44. package/dist/commonjs/combobox/lib/ComboboxMenuItem.d.ts +7 -1
  45. package/dist/commonjs/combobox/lib/ComboboxMenuItem.d.ts.map +1 -1
  46. package/dist/commonjs/combobox/lib/ComboboxMenuItem.js +7 -7
  47. package/dist/commonjs/combobox/lib/ComboboxMenuList.js +2 -2
  48. package/dist/commonjs/combobox/lib/ComboboxPopper.d.ts +97 -0
  49. package/dist/commonjs/combobox/lib/ComboboxPopper.d.ts.map +1 -0
  50. package/dist/commonjs/{select/lib/SelectOption.js → combobox/lib/ComboboxPopper.js} +12 -8
  51. package/dist/commonjs/combobox/lib/hooks/index.d.ts +10 -0
  52. package/dist/commonjs/combobox/lib/hooks/index.d.ts.map +1 -0
  53. package/dist/commonjs/combobox/lib/hooks/index.js +21 -0
  54. package/dist/commonjs/combobox/lib/hooks/useComboboxInput.d.ts +107 -0
  55. package/dist/commonjs/combobox/lib/hooks/useComboboxInput.d.ts.map +1 -0
  56. package/dist/commonjs/combobox/lib/hooks/useComboboxInput.js +74 -0
  57. package/dist/{es6/combobox/lib → commonjs/combobox/lib/hooks}/useComboboxInputOpenWithArrowKeys.d.ts +6 -6
  58. package/dist/commonjs/combobox/lib/hooks/useComboboxInputOpenWithArrowKeys.d.ts.map +1 -0
  59. package/dist/commonjs/combobox/lib/hooks/useComboboxKeyboardTypeAhead.d.ts +101 -0
  60. package/dist/commonjs/combobox/lib/hooks/useComboboxKeyboardTypeAhead.d.ts.map +1 -0
  61. package/dist/commonjs/combobox/lib/hooks/useComboboxKeyboardTypeAhead.js +109 -0
  62. package/dist/commonjs/combobox/lib/hooks/useComboboxListKeyboardHandler.d.ts +84 -0
  63. package/dist/commonjs/combobox/lib/hooks/useComboboxListKeyboardHandler.d.ts.map +1 -0
  64. package/dist/commonjs/combobox/lib/hooks/useComboboxListKeyboardHandler.js +22 -0
  65. package/dist/commonjs/combobox/lib/hooks/useComboboxLoader.d.ts.map +1 -0
  66. package/dist/commonjs/combobox/lib/{useComboboxModel.d.ts → hooks/useComboboxModel.d.ts} +196 -196
  67. package/dist/commonjs/combobox/lib/hooks/useComboboxModel.d.ts.map +1 -0
  68. package/dist/commonjs/combobox/lib/{useComboboxModel.js → hooks/useComboboxModel.js} +3 -2
  69. package/dist/commonjs/combobox/lib/hooks/useComboboxMoveCursorToSelected.d.ts +82 -0
  70. package/dist/commonjs/combobox/lib/hooks/useComboboxMoveCursorToSelected.d.ts.map +1 -0
  71. package/dist/commonjs/combobox/lib/hooks/useComboboxMoveCursorToSelected.js +33 -0
  72. package/dist/commonjs/combobox/lib/hooks/useComboboxResetCursorToSelected.d.ts +82 -0
  73. package/dist/commonjs/combobox/lib/hooks/useComboboxResetCursorToSelected.d.ts.map +1 -0
  74. package/dist/commonjs/combobox/lib/hooks/useComboboxResetCursorToSelected.js +23 -0
  75. package/dist/commonjs/combobox/lib/hooks/useSetPopupWidth.d.ts +82 -0
  76. package/dist/commonjs/combobox/lib/hooks/useSetPopupWidth.d.ts.map +1 -0
  77. package/dist/commonjs/combobox/lib/hooks/useSetPopupWidth.js +22 -0
  78. package/dist/commonjs/common/lib/utils/components.d.ts.map +1 -1
  79. package/dist/commonjs/common/lib/utils/models.d.ts +4 -4
  80. package/dist/commonjs/common/lib/utils/models.d.ts.map +1 -1
  81. package/dist/commonjs/common/lib/utils/models.js +7 -8
  82. package/dist/commonjs/form-field/lib/FormField.d.ts.map +1 -1
  83. package/dist/commonjs/form-field/lib/FormField.js +3 -2
  84. package/dist/commonjs/menu/lib/MenuPopper.d.ts.map +1 -1
  85. package/dist/commonjs/menu/lib/MenuPopper.js +10 -1
  86. package/dist/commonjs/popup/lib/PopupPopper.d.ts.map +1 -1
  87. package/dist/commonjs/popup/lib/PopupPopper.js +10 -1
  88. package/dist/commonjs/select/index.d.ts +1 -1
  89. package/dist/commonjs/select/index.d.ts.map +1 -1
  90. package/dist/commonjs/select/index.js +1 -1
  91. package/dist/commonjs/select/lib/Select.d.ts +2634 -18
  92. package/dist/commonjs/select/lib/Select.d.ts.map +1 -1
  93. package/dist/commonjs/select/lib/Select.js +141 -84
  94. package/dist/commonjs/select/lib/hooks/index.d.ts +4 -0
  95. package/dist/commonjs/select/lib/hooks/index.d.ts.map +1 -0
  96. package/dist/commonjs/select/lib/hooks/index.js +15 -0
  97. package/dist/commonjs/select/lib/hooks/useSelectCard.d.ts +83 -0
  98. package/dist/commonjs/select/lib/hooks/useSelectCard.d.ts.map +1 -0
  99. package/dist/commonjs/select/lib/hooks/useSelectCard.js +13 -0
  100. package/dist/commonjs/select/lib/hooks/useSelectInput.d.ts +112 -0
  101. package/dist/commonjs/select/lib/hooks/useSelectInput.d.ts.map +1 -0
  102. package/dist/commonjs/select/lib/hooks/useSelectInput.js +49 -0
  103. package/dist/commonjs/select/lib/hooks/useSelectModel.d.ts +1779 -0
  104. package/dist/commonjs/select/lib/hooks/useSelectModel.d.ts.map +1 -0
  105. package/dist/commonjs/select/lib/hooks/useSelectModel.js +32 -0
  106. package/dist/es6/collection/index.d.ts +1 -0
  107. package/dist/es6/collection/index.d.ts.map +1 -1
  108. package/dist/es6/collection/index.js +1 -0
  109. package/dist/es6/collection/lib/useBaseListModel.d.ts +1 -1
  110. package/dist/es6/collection/lib/useBaseListModel.d.ts.map +1 -1
  111. package/dist/es6/collection/lib/useBaseListModel.js +2 -2
  112. package/dist/es6/collection/lib/useListItemRegister.js +2 -2
  113. package/dist/es6/collection/lib/useListRenderItem.js +2 -2
  114. package/dist/es6/combobox/index.d.ts +1 -4
  115. package/dist/es6/combobox/index.d.ts.map +1 -1
  116. package/dist/es6/combobox/index.js +1 -4
  117. package/dist/es6/combobox/lib/Combobox.d.ts +9 -3
  118. package/dist/es6/combobox/lib/Combobox.d.ts.map +1 -1
  119. package/dist/es6/combobox/lib/Combobox.js +1 -1
  120. package/dist/es6/combobox/lib/ComboboxCard.js +1 -1
  121. package/dist/es6/combobox/lib/ComboboxInput.d.ts +1 -106
  122. package/dist/es6/combobox/lib/ComboboxInput.d.ts.map +1 -1
  123. package/dist/es6/combobox/lib/ComboboxInput.js +2 -69
  124. package/dist/es6/combobox/lib/ComboboxMenu.d.ts +9 -3
  125. package/dist/es6/combobox/lib/ComboboxMenu.d.ts.map +1 -1
  126. package/dist/es6/combobox/lib/ComboboxMenu.js +4 -3
  127. package/dist/es6/combobox/lib/ComboboxMenuItem.d.ts +7 -1
  128. package/dist/es6/combobox/lib/ComboboxMenuItem.d.ts.map +1 -1
  129. package/dist/es6/combobox/lib/ComboboxMenuItem.js +7 -7
  130. package/dist/es6/combobox/lib/ComboboxMenuList.js +1 -1
  131. package/dist/es6/combobox/lib/ComboboxPopper.d.ts +97 -0
  132. package/dist/es6/combobox/lib/ComboboxPopper.d.ts.map +1 -0
  133. package/dist/es6/combobox/lib/ComboboxPopper.js +12 -0
  134. package/dist/es6/combobox/lib/hooks/index.d.ts +10 -0
  135. package/dist/es6/combobox/lib/hooks/index.d.ts.map +1 -0
  136. package/dist/es6/combobox/lib/hooks/index.js +9 -0
  137. package/dist/es6/combobox/lib/hooks/useComboboxInput.d.ts +107 -0
  138. package/dist/es6/combobox/lib/hooks/useComboboxInput.d.ts.map +1 -0
  139. package/dist/es6/combobox/lib/hooks/useComboboxInput.js +68 -0
  140. package/dist/{commonjs/combobox/lib → es6/combobox/lib/hooks}/useComboboxInputOpenWithArrowKeys.d.ts +6 -6
  141. package/dist/es6/combobox/lib/hooks/useComboboxInputOpenWithArrowKeys.d.ts.map +1 -0
  142. package/dist/es6/combobox/lib/hooks/useComboboxKeyboardTypeAhead.d.ts +101 -0
  143. package/dist/es6/combobox/lib/hooks/useComboboxKeyboardTypeAhead.d.ts.map +1 -0
  144. package/dist/es6/combobox/lib/hooks/useComboboxKeyboardTypeAhead.js +103 -0
  145. package/dist/es6/combobox/lib/hooks/useComboboxListKeyboardHandler.d.ts +84 -0
  146. package/dist/es6/combobox/lib/hooks/useComboboxListKeyboardHandler.d.ts.map +1 -0
  147. package/dist/es6/combobox/lib/hooks/useComboboxListKeyboardHandler.js +19 -0
  148. package/dist/es6/combobox/lib/hooks/useComboboxLoader.d.ts.map +1 -0
  149. package/dist/es6/combobox/lib/{useComboboxModel.d.ts → hooks/useComboboxModel.d.ts} +196 -196
  150. package/dist/es6/combobox/lib/hooks/useComboboxModel.d.ts.map +1 -0
  151. package/dist/es6/combobox/lib/{useComboboxModel.js → hooks/useComboboxModel.js} +3 -2
  152. package/dist/es6/combobox/lib/hooks/useComboboxMoveCursorToSelected.d.ts +82 -0
  153. package/dist/es6/combobox/lib/hooks/useComboboxMoveCursorToSelected.d.ts.map +1 -0
  154. package/dist/es6/combobox/lib/hooks/useComboboxMoveCursorToSelected.js +27 -0
  155. package/dist/es6/combobox/lib/hooks/useComboboxResetCursorToSelected.d.ts +82 -0
  156. package/dist/es6/combobox/lib/hooks/useComboboxResetCursorToSelected.d.ts.map +1 -0
  157. package/dist/es6/combobox/lib/hooks/useComboboxResetCursorToSelected.js +17 -0
  158. package/dist/es6/combobox/lib/hooks/useSetPopupWidth.d.ts +82 -0
  159. package/dist/es6/combobox/lib/hooks/useSetPopupWidth.d.ts.map +1 -0
  160. package/dist/es6/combobox/lib/hooks/useSetPopupWidth.js +16 -0
  161. package/dist/es6/common/lib/utils/components.d.ts.map +1 -1
  162. package/dist/es6/common/lib/utils/models.d.ts +4 -4
  163. package/dist/es6/common/lib/utils/models.d.ts.map +1 -1
  164. package/dist/es6/common/lib/utils/models.js +7 -8
  165. package/dist/es6/form-field/lib/FormField.d.ts.map +1 -1
  166. package/dist/es6/form-field/lib/FormField.js +3 -2
  167. package/dist/es6/menu/lib/MenuPopper.d.ts.map +1 -1
  168. package/dist/es6/menu/lib/MenuPopper.js +10 -1
  169. package/dist/es6/popup/lib/PopupPopper.d.ts.map +1 -1
  170. package/dist/es6/popup/lib/PopupPopper.js +10 -1
  171. package/dist/es6/select/index.d.ts +1 -1
  172. package/dist/es6/select/index.d.ts.map +1 -1
  173. package/dist/es6/select/index.js +1 -1
  174. package/dist/es6/select/lib/Select.d.ts +2634 -18
  175. package/dist/es6/select/lib/Select.d.ts.map +1 -1
  176. package/dist/es6/select/lib/Select.js +139 -66
  177. package/dist/es6/select/lib/hooks/index.d.ts +4 -0
  178. package/dist/es6/select/lib/hooks/index.d.ts.map +1 -0
  179. package/dist/es6/select/lib/hooks/index.js +3 -0
  180. package/dist/es6/select/lib/hooks/useSelectCard.d.ts +83 -0
  181. package/dist/es6/select/lib/hooks/useSelectCard.d.ts.map +1 -0
  182. package/dist/es6/select/lib/hooks/useSelectCard.js +10 -0
  183. package/dist/es6/select/lib/hooks/useSelectInput.d.ts +112 -0
  184. package/dist/es6/select/lib/hooks/useSelectInput.d.ts.map +1 -0
  185. package/dist/es6/select/lib/hooks/useSelectInput.js +46 -0
  186. package/dist/es6/select/lib/hooks/useSelectModel.d.ts +1779 -0
  187. package/dist/es6/select/lib/hooks/useSelectModel.d.ts.map +1 -0
  188. package/dist/es6/select/lib/hooks/useSelectModel.js +29 -0
  189. package/form-field/lib/FormField.tsx +8 -2
  190. package/menu/lib/MenuPopper.tsx +14 -1
  191. package/package.json +4 -4
  192. package/popup/lib/Popper.tsx +1 -1
  193. package/popup/lib/PopupPopper.tsx +17 -1
  194. package/select/index.ts +1 -1
  195. package/select/lib/Select.tsx +175 -111
  196. package/select/lib/hooks/index.ts +3 -0
  197. package/select/lib/hooks/useSelectCard.ts +11 -0
  198. package/select/lib/hooks/useSelectInput.ts +74 -0
  199. package/select/lib/hooks/useSelectModel.tsx +30 -0
  200. package/dist/commonjs/combobox/lib/useComboboxInputOpenWithArrowKeys.d.ts.map +0 -1
  201. package/dist/commonjs/combobox/lib/useComboboxLoader.d.ts.map +0 -1
  202. package/dist/commonjs/combobox/lib/useComboboxModel.d.ts.map +0 -1
  203. package/dist/commonjs/select/lib/SelectOption.d.ts +0 -20
  204. package/dist/commonjs/select/lib/SelectOption.d.ts.map +0 -1
  205. package/dist/es6/combobox/lib/useComboboxInputOpenWithArrowKeys.d.ts.map +0 -1
  206. package/dist/es6/combobox/lib/useComboboxLoader.d.ts.map +0 -1
  207. package/dist/es6/combobox/lib/useComboboxModel.d.ts.map +0 -1
  208. package/dist/es6/select/lib/SelectOption.d.ts +0 -20
  209. package/dist/es6/select/lib/SelectOption.d.ts.map +0 -1
  210. package/dist/es6/select/lib/SelectOption.js +0 -7
  211. package/select/lib/SelectOption.tsx +0 -33
  212. /package/combobox/lib/{useComboboxInputOpenWithArrowKeys.ts → hooks/useComboboxInputOpenWithArrowKeys.ts} +0 -0
  213. /package/combobox/lib/{useComboboxLoader.ts → hooks/useComboboxLoader.ts} +0 -0
  214. /package/dist/commonjs/combobox/lib/{useComboboxInputOpenWithArrowKeys.js → hooks/useComboboxInputOpenWithArrowKeys.js} +0 -0
  215. /package/dist/commonjs/combobox/lib/{useComboboxLoader.d.ts → hooks/useComboboxLoader.d.ts} +0 -0
  216. /package/dist/commonjs/combobox/lib/{useComboboxLoader.js → hooks/useComboboxLoader.js} +0 -0
  217. /package/dist/es6/combobox/lib/{useComboboxInputOpenWithArrowKeys.js → hooks/useComboboxInputOpenWithArrowKeys.js} +0 -0
  218. /package/dist/es6/combobox/lib/{useComboboxLoader.d.ts → hooks/useComboboxLoader.d.ts} +0 -0
  219. /package/dist/es6/combobox/lib/{useComboboxLoader.js → hooks/useComboboxLoader.js} +0 -0
@@ -15,6 +15,7 @@ export * from './lib/useGridModel';
15
15
  export * from './lib/useListActiveDescendant';
16
16
  export * from './lib/useListItemAllowChildStrings';
17
17
  export {ListBox, ListBoxProps} from './lib/ListBox';
18
+ export {keyboardEventToCursorEvents} from './lib/keyUtils';
18
19
  export {
19
20
  singleSelectionManager,
20
21
  multiSelectionManager,
@@ -10,7 +10,7 @@ export const defaultGetId = (item: any): string => {
10
10
  };
11
11
 
12
12
  export const defaultGetTextValue = (item: any): string => {
13
- return typeof item === 'string' ? item : item.text || '';
13
+ return typeof item === 'string' ? item : item.text || item.id || '';
14
14
  };
15
15
 
16
16
  export interface Item<T> {
@@ -20,7 +20,7 @@ export interface Item<T> {
20
20
  /**
21
21
  * Used by components that allow jumping to an item based on typing
22
22
  */
23
- textValue?: string;
23
+ textValue: string;
24
24
  }
25
25
 
26
26
  // interface Collection<T> {
@@ -124,7 +124,7 @@ export const useBaseListModel = createModelHook({
124
124
  const getTextValueRef = React.useRef(defaultGetTextValue);
125
125
 
126
126
  const getId = config.getId || defaultGetId;
127
- const getTextValue = config.getTextValue || defaultGetTextValue;
127
+ const getTextValue = config.getTextValue || config.getId || defaultGetTextValue;
128
128
  getIdRef.current = getId;
129
129
  getTextValueRef.current = getTextValue;
130
130
 
@@ -94,7 +94,7 @@ export const useListItemRegister = createElemPropsHook(useListModel)(
94
94
  disabled: elemProps.disabled || state.nonInteractiveIds.includes(localId) ? true : undefined,
95
95
  item: null, // remove `item` from prop list
96
96
  virtual: null, // remove `virtual` from prop list
97
- 'aria-setsize': elemProps.virtual?.size,
97
+ 'aria-setsize': elemProps.virtual ? state.UNSTABLE_virtual.totalSize : undefined,
98
98
  'aria-posinset': elemProps.virtual ? elemProps.item!.index + 1 : undefined,
99
99
  style,
100
100
  id: slugify(`${state.id}-${localId}`),
@@ -34,7 +34,7 @@ export function useListRenderItems<T>(
34
34
  // about capital letters in attributes. React thinks this will be applied to the DOM
35
35
  // element even though we remove it later...
36
36
  virtual,
37
- key: item.id,
37
+ key: item.id || item.index,
38
38
  item,
39
39
  });
40
40
  }
@@ -44,7 +44,7 @@ export function useListRenderItems<T>(
44
44
  const child = children(item.value, item.index);
45
45
  if (React.isValidElement(child)) {
46
46
  return React.cloneElement(child, {
47
- key: item.id,
47
+ key: item.id || item.index,
48
48
  item: item.value,
49
49
  });
50
50
  }
package/combobox/index.ts CHANGED
@@ -2,7 +2,14 @@ export {Combobox} from './lib/Combobox';
2
2
  export {useComboboxCard} from './lib/ComboboxCard';
3
3
  export {useComboboxMenuItem} from './lib/ComboboxMenuItem';
4
4
  export {useComboboxMenuList} from './lib/ComboboxMenuList';
5
- export {useComboboxInput} from './lib/ComboboxInput';
6
- export {useComboboxLoader} from './lib/useComboboxLoader';
7
- export {useComboboxModel} from './lib/useComboboxModel';
8
- export {useComboboxInputOpenWithArrowKeys as useComboboxOpenWithArrowKeys} from './lib/useComboboxInputOpenWithArrowKeys';
5
+ export {
6
+ useComboboxLoader,
7
+ useComboboxModel,
8
+ useComboboxInputOpenWithArrowKeys,
9
+ useComboboxKeyboardTypeAhead,
10
+ useSetPopupWidth,
11
+ useComboboxMoveCursorToSelected,
12
+ useComboboxInput,
13
+ useComboboxListKeyboardHandler,
14
+ useComboboxResetCursorToSelected,
15
+ } from './lib/hooks';
@@ -2,7 +2,7 @@ import React from 'react';
2
2
 
3
3
  import {createContainer} from '@workday/canvas-kit-react/common';
4
4
 
5
- import {useComboboxModel} from './useComboboxModel';
5
+ import {useComboboxModel} from './hooks';
6
6
  import {ComboboxInput} from './ComboboxInput';
7
7
  import {ComboboxMenu} from './ComboboxMenu';
8
8
  export interface ComboboxProps {
@@ -7,7 +7,7 @@ import {
7
7
  } from '@workday/canvas-kit-react/common';
8
8
  import {Menu} from '@workday/canvas-kit-react/menu';
9
9
 
10
- import {useComboboxModel} from './useComboboxModel';
10
+ import {useComboboxModel} from './hooks';
11
11
 
12
12
  export interface ComboboxCardProps extends ExtractProps<typeof Menu.Card> {}
13
13
 
@@ -1,97 +1,9 @@
1
1
  import React from 'react';
2
2
 
3
- import {
4
- composeHooks,
5
- createElemPropsHook,
6
- createSubcomponent,
7
- dispatchInputEvent,
8
- ExtractProps,
9
- useForkRef,
10
- } from '@workday/canvas-kit-react/common';
3
+ import {createSubcomponent, ExtractProps} from '@workday/canvas-kit-react/common';
11
4
  import {TextInput} from '@workday/canvas-kit-react/text-input';
12
- import {usePopupTarget} from '@workday/canvas-kit-react/popup';
13
- import {
14
- useListActiveDescendant,
15
- useListKeyboardHandler,
16
- } from '@workday/canvas-kit-react/collection';
17
5
 
18
- import {useComboboxModel} from './useComboboxModel';
19
- import {useComboboxInputOpenWithArrowKeys} from './useComboboxInputOpenWithArrowKeys';
20
-
21
- /**
22
- * Adds all attributes necessary to start with a {@link ComboboxInput Combobox.Input}. It opens the
23
- * menu with arrow keys, uses {@link useListActiveDescendant}, and handles keyboard arrows to
24
- * navigate items of the menu. You may also compose this hook to add more specific behaviors for
25
- * your {@link ComboboxInput Combobox.Input}.
26
- */
27
- export const useComboboxInput = composeHooks(
28
- createElemPropsHook(useComboboxModel)((model, ref) => {
29
- const elementRef = useForkRef(ref, model.state.inputRef);
30
-
31
- React.useEffect(() => {
32
- if (model.state.cursorId && model.state.visibility === 'visible') {
33
- const item = model.navigation.getItem(model.state.cursorId, model);
34
- if (model.state.isVirtualized && item) {
35
- model.state.UNSTABLE_virtual.scrollToIndex(item.index);
36
- } else {
37
- const listboxId = model.state.inputRef.current?.getAttribute('aria-controls');
38
- if (listboxId) {
39
- const menuItem = document.querySelector(
40
- `[id="${listboxId}"] [data-id="${model.state.cursorId}"]`
41
- );
42
- if (menuItem) {
43
- menuItem.scrollIntoView({block: 'nearest'});
44
- }
45
- }
46
- }
47
- }
48
-
49
- // we only want to run this effect if the cursor changes and not any other time
50
- // eslint-disable-next-line react-hooks/exhaustive-deps
51
- }, [model.state.cursorId]);
52
-
53
- return {
54
- onKeyDown(event: React.KeyboardEvent) {
55
- if (event.key === 'Escape') {
56
- dispatchInputEvent(event.currentTarget as HTMLElement, '');
57
- }
58
- if (event.key === 'Enter' && !event.metaKey && model.state.visibility === 'visible') {
59
- const element = document.querySelector(`[data-id="${model.state.cursorId}"]`);
60
- if (element && element?.getAttribute('aria-disabled') !== 'true') {
61
- model.events.select({id: model.state.cursorId});
62
- if (model.state.mode === 'single') {
63
- model.events.hide(event);
64
- }
65
- }
66
-
67
- // We don't want to submit forms while the combobox is open
68
- event.preventDefault();
69
- }
70
- },
71
- onBlur(event: React.FocusEvent) {
72
- model.events.hide(event);
73
- },
74
- onChange: model.onChange,
75
- onClick(event: React.MouseEvent) {
76
- if (model.state.visibility === 'hidden') {
77
- model.events.setWidth(event.currentTarget.clientWidth);
78
- }
79
- },
80
- value: model.state.value,
81
- role: 'combobox',
82
- 'aria-haspopup': true,
83
- 'aria-expanded': model.state.visibility === 'visible',
84
- 'aria-autocomplete': 'list',
85
- 'aria-controls': `${model.state.id}-list`,
86
- id: model.state.id,
87
- ref: elementRef,
88
- } as const;
89
- }),
90
- useComboboxInputOpenWithArrowKeys,
91
- useListActiveDescendant,
92
- useListKeyboardHandler,
93
- usePopupTarget
94
- );
6
+ import {useComboboxModel, useComboboxInput} from './hooks';
95
7
 
96
8
  export const ComboboxInput = createSubcomponent(TextInput)({
97
9
  modelHook: useComboboxModel,
@@ -3,10 +3,11 @@ import React from 'react';
3
3
  import {createSubcomponent} from '@workday/canvas-kit-react/common';
4
4
  import {Menu} from '@workday/canvas-kit-react/menu';
5
5
 
6
- import {useComboboxModel} from './useComboboxModel';
6
+ import {useComboboxModel} from './hooks';
7
7
  import {ComboboxMenuList} from './ComboboxMenuList';
8
8
  import {ComboboxMenuItem} from './ComboboxMenuItem';
9
9
  import {ComboboxCard} from './ComboboxCard';
10
+ import {ComboboxMenuPopper} from './ComboboxPopper';
10
11
 
11
12
  export interface ComboboxMenuProps {
12
13
  /**
@@ -23,9 +24,9 @@ export const ComboboxMenu = createSubcomponent()({
23
24
  * {@link ComboboxInput Combobox.Input}. It renders a `div` element that is portalled to the
24
25
  * `document.body` which is controlled by the {@link PopupStack}. The `PopupStack` is not part
25
26
  * of React. This means no extra props given to this component will be forwarded to the `div`
26
- * element, but the `ref` will be forwarded.
27
+ * element, but the `ref` will be forwarded. Also fallback placements for the popper with be either `top` or `bottom`.
27
28
  */
28
- Popper: Menu.Popper,
29
+ Popper: ComboboxMenuPopper,
29
30
  /**
30
31
  * The combobox menu list follows the Collections API. A list can either contain static items or
31
32
  * a render prop and `items` to the model.
@@ -4,15 +4,17 @@ import {
4
4
  composeHooks,
5
5
  createElemPropsHook,
6
6
  createSubcomponent,
7
+ styled,
7
8
  } from '@workday/canvas-kit-react/common';
8
9
  import {StyledMenuItem, useMenuModel} from '@workday/canvas-kit-react/menu';
10
+ import {SystemIcon} from '@workday/canvas-kit-react/icon';
9
11
  import {
10
12
  useListItemAllowChildStrings,
11
13
  useListItemRegister,
12
14
  } from '@workday/canvas-kit-react/collection';
13
15
  import {OverflowTooltip} from '@workday/canvas-kit-react/tooltip';
14
16
 
15
- import {useComboboxModel} from './useComboboxModel';
17
+ import {useComboboxModel} from './hooks';
16
18
 
17
19
  export interface ComboboxMenuItemProps {
18
20
  children: React.ReactNode;
@@ -26,17 +28,13 @@ export interface ComboboxMenuItemProps {
26
28
  * added when the item has "focus" (when the cursor is on the item).
27
29
  */
28
30
  export const useComboboxMenuItem = composeHooks(
29
- createElemPropsHook(useMenuModel)((model, _, elemProps: {'data-id'?: string} = {}) => {
31
+ createElemPropsHook(useMenuModel)((model, ref, elemProps: {'data-id'?: string} = {}) => {
30
32
  const id = elemProps['data-id'] || '';
33
+
31
34
  const onMouseDown = (event: React.MouseEvent<HTMLElement>) => {
32
- if (
33
- model.state.nonInteractiveIds.includes(id) ||
34
- event.currentTarget.hasAttribute('aria-disabled')
35
- ) {
36
- return;
37
- }
38
35
  if (event.currentTarget.getAttribute('aria-disabled') !== 'true') {
39
36
  model.events.select({id});
37
+
40
38
  if (model.state.mode === 'single') {
41
39
  model.events.hide(event);
42
40
  }
@@ -63,6 +61,9 @@ export const useComboboxMenuItem = composeHooks(
63
61
  export const ComboboxMenuItem = createSubcomponent('li')({
64
62
  modelHook: useComboboxModel,
65
63
  elemPropsHook: useComboboxMenuItem,
64
+ subComponents: {
65
+ Icon: styled(SystemIcon)({alignSelf: 'start'}),
66
+ },
66
67
  })<ComboboxMenuItemProps>(({children, ...elemProps}, Element) => {
67
68
  return (
68
69
  <OverflowTooltip placement="left">
@@ -5,7 +5,7 @@ import {createElemPropsHook, createSubcomponent} from '@workday/canvas-kit-react
5
5
  import {useMenuModel} from '@workday/canvas-kit-react/menu';
6
6
  import {ListBox, ListBoxProps} from '@workday/canvas-kit-react/collection';
7
7
 
8
- import {useComboboxModel} from './useComboboxModel';
8
+ import {useComboboxModel} from './hooks';
9
9
 
10
10
  export interface ComboboxMenuListProps<T = any> extends ListBoxProps<T> {}
11
11
 
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+
3
+ import {createSubcomponent, ExtractProps} from '@workday/canvas-kit-react/common';
4
+ import {usePopupPopper, Popper} from '@workday/canvas-kit-react/popup';
5
+
6
+ import {Menu, useMenuModel} from '@workday/canvas-kit-react/menu';
7
+
8
+ export const useMenuPopper = usePopupPopper;
9
+
10
+ export const ComboboxMenuPopper = createSubcomponent('div')({
11
+ displayName: 'Combobox.Menu.Popper',
12
+ modelHook: useMenuModel,
13
+ elemPropsHook: useMenuPopper,
14
+ })<ExtractProps<typeof Popper>>(({children, ...elemProps}) => {
15
+ return (
16
+ <Menu.Popper fallbackPlacements={['top', 'bottom']} {...elemProps}>
17
+ {children}
18
+ </Menu.Popper>
19
+ );
20
+ });
@@ -0,0 +1,9 @@
1
+ export * from './useComboboxInputOpenWithArrowKeys';
2
+ export * from './useComboboxLoader';
3
+ export * from './useComboboxModel';
4
+ export * from './useComboboxMoveCursorToSelected';
5
+ export * from './useSetPopupWidth';
6
+ export * from './useComboboxKeyboardTypeAhead';
7
+ export * from './useComboboxListKeyboardHandler';
8
+ export * from './useComboboxInput';
9
+ export * from './useComboboxResetCursorToSelected';
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+
3
+ import {composeHooks, createElemPropsHook, useForkRef} from '@workday/canvas-kit-react/common';
4
+ import {usePopupTarget} from '@workday/canvas-kit-react/popup';
5
+
6
+ import {useListActiveDescendant} from '@workday/canvas-kit-react/collection';
7
+
8
+ import {
9
+ useComboboxModel,
10
+ useComboboxInputOpenWithArrowKeys,
11
+ useSetPopupWidth,
12
+ useComboboxListKeyboardHandler,
13
+ } from './index';
14
+
15
+ /**
16
+ * `useComboboxInput` Adds all attributes necessary to start with a {@link ComboboxInput Combobox.Input}. It opens the
17
+ * menu with arrow keys, uses {@link useListActiveDescendant}, and handles keyboard arrows to
18
+ * navigate items of the menu. You may also compose this hook to add more specific behaviors for
19
+ * your {@link ComboboxInput Combobox.Input}.
20
+ */
21
+ export const useComboboxInput = composeHooks(
22
+ createElemPropsHook(useComboboxModel)((model, ref) => {
23
+ const elementRef = useForkRef(ref, model.state.inputRef);
24
+
25
+ React.useEffect(() => {
26
+ if (model.state.cursorId && model.state.visibility === 'visible') {
27
+ const item = model.navigation.getItem(model.state.cursorId, model);
28
+ if (model.state.isVirtualized && item) {
29
+ model.state.UNSTABLE_virtual.scrollToIndex(item.index);
30
+ } else {
31
+ const listboxId = model.state.inputRef.current?.getAttribute('aria-controls');
32
+ if (listboxId) {
33
+ const menuItem = document.querySelector(
34
+ `[id="${listboxId}"] [data-id="${model.state.cursorId}"]`
35
+ );
36
+ if (menuItem) {
37
+ requestAnimationFrame(() => {
38
+ menuItem.scrollIntoView({block: 'nearest'});
39
+ });
40
+ }
41
+ }
42
+ }
43
+ }
44
+
45
+ // we only want to run this effect if the cursor, visibility and selectedIds change and not any other time
46
+ // eslint-disable-next-line react-hooks/exhaustive-deps
47
+ }, [model.state.cursorId, model.state.selectedIds, model.state.visibility]);
48
+
49
+ return {
50
+ onKeyDown(event: React.KeyboardEvent) {
51
+ if (event.key === 'Enter' && !event.metaKey && model.state.visibility === 'visible') {
52
+ const element = document.querySelector(`[data-id="${model.state.cursorId}"]`);
53
+ if (element && element?.getAttribute('aria-disabled') !== 'true') {
54
+ model.events.select({id: model.state.cursorId});
55
+ if (model.state.mode === 'single') {
56
+ model.events.hide(event);
57
+ }
58
+ }
59
+
60
+ // We don't want to submit forms while the combobox is open
61
+ event.preventDefault();
62
+ }
63
+ },
64
+ onBlur(event: React.FocusEvent) {
65
+ model.events.hide(event);
66
+ },
67
+ onChange: model.onChange,
68
+ onClick(event: React.MouseEvent) {
69
+ if (model.state.visibility === 'hidden') {
70
+ model.events.setWidth(event.currentTarget.clientWidth);
71
+ }
72
+ },
73
+ value: model.state.value,
74
+ role: 'combobox',
75
+ 'aria-haspopup': true,
76
+ 'aria-expanded': model.state.visibility === 'visible',
77
+ 'aria-autocomplete': 'list',
78
+ 'aria-controls': `${model.state.id}-list`,
79
+ id: model.state.id,
80
+ ref: elementRef,
81
+ } as const;
82
+ }),
83
+ useSetPopupWidth,
84
+ useComboboxInputOpenWithArrowKeys,
85
+ useListActiveDescendant,
86
+ useComboboxListKeyboardHandler,
87
+ usePopupTarget
88
+ );
@@ -0,0 +1,127 @@
1
+ import React from 'react';
2
+ import {createElemPropsHook} from '@workday/canvas-kit-react/common';
3
+ import {useComboboxModel} from './useComboboxModel';
4
+
5
+ /**
6
+ * `useComboboxKeyboardTypeAhead` handles type-ahead. It will try and find a matching item in the list. It also returns the keys types so far.
7
+ * This hook is best used when composing hooks and in conjunction with `useComboboxInput`.
8
+ *
9
+ * ```tsx
10
+ * // Example for a `Select` input
11
+ * export const useSelectInput = composeHooks(
12
+ * createElemPropsHook(useComboboxModel)((model, ref, elemProps: {keySofar?: string} = {}) => {
13
+ * return {
14
+ * onKeyDown(event: React.KeyboardEvent) {
15
+ * // onKeyDown logic ...
16
+ * },
17
+ * } as const;
18
+ * }),
19
+ * useComboboxKeyboardTypeAhead,
20
+ * useComboboxInput
21
+ * );
22
+ * ```
23
+ */
24
+ export const useComboboxKeyboardTypeAhead = createElemPropsHook(useComboboxModel)(model => {
25
+ const keySofar = React.useRef('');
26
+ const timer = React.useRef<ReturnType<typeof setTimeout>>();
27
+ const [keyTypedSofar, setKeyTypedSofar] = React.useState(keySofar.current);
28
+
29
+ const startClearKeysSoFarTimer = () => {
30
+ if (timer.current) {
31
+ clearTimeout(timer.current);
32
+ }
33
+ timer.current = setTimeout(() => {
34
+ keySofar.current = '';
35
+ setKeyTypedSofar(keySofar.current);
36
+ }, 500);
37
+ };
38
+
39
+ // Based on the the key typed, this will try and find an index in the array where it matches the string
40
+ // so if a user types `de` it will try and find an index in the array whose id matches `de` like denver
41
+ const getIndexByStartString = (
42
+ startIndex: number,
43
+ startString: string,
44
+ endIndex: number = model.state.items.length,
45
+ ignoreDisabled: boolean = true
46
+ ): number => {
47
+ for (let i = startIndex; i < endIndex; i++) {
48
+ const label = model.state.items[i].textValue.toLowerCase();
49
+ if (
50
+ label.startsWith(startString.toLowerCase()) &&
51
+ (!ignoreDisabled ||
52
+ (ignoreDisabled && !model.state.nonInteractiveIds.includes(model.state.items[i].id)))
53
+ ) {
54
+ return i;
55
+ }
56
+ }
57
+ return -1;
58
+ };
59
+
60
+ const currentItem = model.navigation.getItem(model.state.cursorId, model);
61
+ const cursorFocusedIndex = currentItem.index;
62
+
63
+ const handleKeyboardTypeAhead = (key: string, numOptions: number) => {
64
+ // If the starting point is beyond the list of options, reset it
65
+ // to the beginning of the list
66
+ const startNumber = keySofar.current.length === 0 ? cursorFocusedIndex + 1 : cursorFocusedIndex;
67
+
68
+ const start = startNumber === numOptions ? 0 : startNumber;
69
+
70
+ // Keeps track of the current key types and adds to it
71
+ // if you type `de` vs `d` for denver
72
+ keySofar.current += key;
73
+ setKeyTypedSofar(keySofar.current);
74
+ startClearKeysSoFarTimer();
75
+
76
+ // First, look for a match from start to end
77
+ let matchIndex = getIndexByStartString(start, keySofar.current);
78
+ getIndexByStartString(start, keySofar.current);
79
+
80
+ // If a match isn't found between start and end, wrap the search
81
+ // around and search again from the beginning (0) to start
82
+ if (matchIndex === -1) {
83
+ matchIndex = getIndexByStartString(0, keySofar.current, start);
84
+ }
85
+
86
+ // A match was found...
87
+ if (matchIndex > -1) {
88
+ model.events.goTo({id: model.state.items[matchIndex].id});
89
+ if (model.state.visibility === 'hidden') {
90
+ // If the menu is closed, fire the change event
91
+ // go to that item and select based on its id
92
+ model.events.select({id: model.state.items[matchIndex].id});
93
+ }
94
+ }
95
+ };
96
+
97
+ return {
98
+ onKeyDown(event: React.KeyboardEvent) {
99
+ // Call type ahead excluding backspace
100
+ if (event.key.length === 1 && event.key.match(/\S/)) {
101
+ handleKeyboardTypeAhead(event.key, model.state.items.length);
102
+ }
103
+
104
+ // If spacebar is typed
105
+ if (
106
+ (event.key === 'Spacebar' || event.key === ' ') &&
107
+ model.state.visibility === 'visible' &&
108
+ keySofar.current !== ''
109
+ ) {
110
+ // If the user is in the middle of typing a string, treat
111
+ // space key as type-ahead rather than option selection
112
+ handleKeyboardTypeAhead(' ', model.state.items.length);
113
+ }
114
+
115
+ if (
116
+ (event.key === 'Spacebar' || event.key === ' ') &&
117
+ model.state.visibility === 'hidden' &&
118
+ keySofar.current !== ''
119
+ ) {
120
+ // If the user is in the middle of typing a string, treat
121
+ // space key as type-ahead rather than option selection
122
+ handleKeyboardTypeAhead(' ', model.state.items.length);
123
+ }
124
+ },
125
+ keySofar: keyTypedSofar,
126
+ };
127
+ });
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import {createElemPropsHook, useIsRTL} from '@workday/canvas-kit-react/common';
3
+ import {keyboardEventToCursorEvents} from '@workday/canvas-kit-react/collection';
4
+
5
+ import {useComboboxModel} from './index';
6
+
7
+ /**
8
+ * `useComboboxListKeyboardHandler` handle calling keyboard events like ArrowUp and ArrowDown only when the menu is visible.
9
+ */
10
+ export const useComboboxListKeyboardHandler = createElemPropsHook(useComboboxModel)(model => {
11
+ const isRTL = useIsRTL();
12
+
13
+ return {
14
+ onKeyDown(event: React.KeyboardEvent) {
15
+ if (model.state.visibility === 'visible') {
16
+ const handled = keyboardEventToCursorEvents(event, model, isRTL);
17
+ if (handled) {
18
+ event.preventDefault();
19
+ }
20
+ }
21
+ },
22
+ };
23
+ });
@@ -43,21 +43,25 @@ export const useComboboxModel = createModelHook({
43
43
  defaultConfig: {
44
44
  ...useInputModel.defaultConfig,
45
45
  ...useMenuModel.defaultConfig,
46
+ shouldVirtualize: true,
46
47
  },
48
+
47
49
  requiredConfig: {
48
50
  ...useInputModel.requiredConfig,
49
51
  ...useMenuModel.requiredConfig,
50
52
  },
51
53
  })(config => {
52
54
  const input = useInputModel(config);
55
+
53
56
  const menu = useMenuModel(
54
57
  useMenuModel.mergeConfig(config, {
55
- shouldVirtualize: true,
56
58
  onSelect({id}) {
57
- dispatchInputEvent(menu.state.targetRef.current, id);
59
+ const textValue = menu.navigation.getItem(id, menu).textValue || id;
60
+ dispatchInputEvent(menu.state.targetRef.current, textValue);
58
61
  },
59
62
  })
60
63
  );
64
+
61
65
  const [width, setWidth] = React.useState(0);
62
66
 
63
67
  const state = {
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import {createElemPropsHook} from '@workday/canvas-kit-react/common';
3
+ import {useComboboxModel} from './useComboboxModel';
4
+
5
+ /**
6
+ * If there is nothing selected, move cursor to the first item, otherwise, move the cursor to the selected item
7
+ */
8
+ export const useComboboxMoveCursorToSelected = createElemPropsHook(useComboboxModel)(model => {
9
+ React.useLayoutEffect(() => {
10
+ const cursorItemId = model.state.items.find(item => item.id === model.state.cursorId)?.id;
11
+ // If there is no selected item and items exists we want to set the cursor to the first item in the array
12
+ if (!model.state.selectedIds.length && model.state.items.length && !cursorItemId) {
13
+ model.events.goTo({id: model.state.items[0].id});
14
+ } else if (model.state.selectedIds.length && !cursorItemId) {
15
+ // If the user wants an item selected by default by passing `initialSelectedId` we select that item
16
+ model.events.goTo({id: model.state.selectedIds[0]});
17
+ }
18
+ }, [
19
+ model.state.selectedIds,
20
+ model.events,
21
+ model.state.items,
22
+ model.state.cursorId,
23
+ model.state.visibility,
24
+ ]);
25
+
26
+ return {};
27
+ });
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import {createElemPropsHook} from '@workday/canvas-kit-react/common';
3
+ import {useComboboxModel} from './useComboboxModel';
4
+
5
+ /**
6
+ * Reset the cursor to the selected item when the Menu is closed
7
+ */
8
+ export const useComboboxResetCursorToSelected = createElemPropsHook(useComboboxModel)(model => {
9
+ const visible = model.state.visibility !== 'hidden';
10
+ React.useEffect(() => {
11
+ // when closed, the cursor should reset to the selected item if something is selected
12
+ if (!visible && model.state.selectedIds.length) {
13
+ model.events.goTo({id: model.state.selectedIds[0]});
14
+ }
15
+ // eslint-disable-next-line react-hooks/exhaustive-deps
16
+ }, [visible, model.events, model.state.selectedIds]);
17
+ return {};
18
+ });
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import {createElemPropsHook} from '@workday/canvas-kit-react/common';
3
+ import {useComboboxModel} from './useComboboxModel';
4
+
5
+ /**
6
+ * Sets the width of the Menu to the target element when the menu is visible
7
+ */
8
+ export const useSetPopupWidth = createElemPropsHook(useComboboxModel)(model => {
9
+ const visible = model.state.visibility !== 'hidden';
10
+ React.useLayoutEffect(() => {
11
+ if (visible) {
12
+ model.events.setWidth(model.state.targetRef.current?.clientWidth || 0);
13
+ }
14
+ }, [visible, model.events, model.state.targetRef]);
15
+ return {};
16
+ });