@workday/canvas-kit-react 9.0.0-alpha.419-next.21 → 9.0.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 (255) hide show
  1. package/breadcrumbs/lib/BreadcrumbsItem.tsx +1 -1
  2. package/collection/index.ts +2 -0
  3. package/collection/lib/ListBox.tsx +12 -10
  4. package/collection/lib/useBaseListModel.tsx +6 -4
  5. package/collection/lib/useCursorListModel.tsx +1 -1
  6. package/collection/lib/useListActiveDescendant.ts +24 -0
  7. package/collection/lib/useListItemAllowChildStrings.ts +2 -1
  8. package/collection/lib/useListItemRegister.tsx +10 -3
  9. package/collection/lib/useListItemSelect.tsx +1 -1
  10. package/collection/lib/useListKeyboardHandler.ts +28 -0
  11. package/collection/lib/useListLoader.ts +116 -54
  12. package/collection/lib/useOverflowListItemMeasure.tsx +2 -2
  13. package/collection/lib/useOverflowListMeasure.ts +11 -13
  14. package/collection/lib/useOverflowListTarget.tsx +28 -30
  15. package/collection/lib/useSelectionListModel.tsx +1 -0
  16. package/combobox/LICENSE +52 -0
  17. package/combobox/README.md +7 -0
  18. package/combobox/index.ts +8 -0
  19. package/combobox/lib/Combobox.tsx +32 -0
  20. package/combobox/lib/ComboboxCard.tsx +33 -0
  21. package/combobox/lib/ComboboxInput.tsx +104 -0
  22. package/combobox/lib/ComboboxMenu.tsx +67 -0
  23. package/combobox/lib/ComboboxMenuItem.tsx +74 -0
  24. package/combobox/lib/ComboboxMenuList.tsx +45 -0
  25. package/combobox/lib/useComboboxInputOpenWithArrowKeys.ts +21 -0
  26. package/combobox/lib/useComboboxLoader.ts +72 -0
  27. package/combobox/lib/useComboboxModel.tsx +85 -0
  28. package/combobox/package.json +6 -0
  29. package/common/lib/utils/components.ts +15 -16
  30. package/common/lib/utils/models.ts +2 -2
  31. package/dist/commonjs/action-bar/lib/ActionBar.d.ts +2 -2
  32. package/dist/commonjs/action-bar/lib/ActionBarList.d.ts +1 -1
  33. package/dist/commonjs/action-bar/lib/useActionBarModel.d.ts +2 -2
  34. package/dist/commonjs/breadcrumbs/lib/Breadcrumbs.d.ts +2 -2
  35. package/dist/commonjs/breadcrumbs/lib/BreadcrumbsMenu.d.ts +1 -1
  36. package/dist/commonjs/breadcrumbs/lib/hooks/useBreadcrumbsModel.d.ts +2 -2
  37. package/dist/commonjs/collection/index.d.ts +2 -0
  38. package/dist/commonjs/collection/index.d.ts.map +1 -1
  39. package/dist/commonjs/collection/index.js +2 -0
  40. package/dist/commonjs/collection/lib/ListBox.d.ts +7 -6
  41. package/dist/commonjs/collection/lib/ListBox.d.ts.map +1 -1
  42. package/dist/commonjs/collection/lib/ListBox.js +5 -3
  43. package/dist/commonjs/collection/lib/useBaseListModel.d.ts.map +1 -1
  44. package/dist/commonjs/collection/lib/useBaseListModel.js +4 -4
  45. package/dist/commonjs/collection/lib/useCursorListModel.d.ts +0 -34
  46. package/dist/commonjs/collection/lib/useCursorListModel.d.ts.map +1 -1
  47. package/dist/commonjs/collection/lib/useCursorListModel.js +1 -1
  48. package/dist/commonjs/collection/lib/useListActiveDescendant.d.ts +68 -0
  49. package/dist/commonjs/collection/lib/useListActiveDescendant.d.ts.map +1 -0
  50. package/dist/commonjs/collection/lib/useListActiveDescendant.js +25 -0
  51. package/dist/commonjs/collection/lib/useListItemAllowChildStrings.d.ts +2 -1
  52. package/dist/commonjs/collection/lib/useListItemAllowChildStrings.d.ts.map +1 -1
  53. package/dist/commonjs/collection/lib/useListItemAllowChildStrings.js +2 -1
  54. package/dist/commonjs/collection/lib/useListItemRegister.d.ts.map +1 -1
  55. package/dist/commonjs/collection/lib/useListItemRegister.js +5 -1
  56. package/dist/commonjs/collection/lib/useListKeyboardHandler.d.ts +66 -0
  57. package/dist/commonjs/collection/lib/useListKeyboardHandler.d.ts.map +1 -0
  58. package/dist/commonjs/collection/lib/useListKeyboardHandler.js +27 -0
  59. package/dist/commonjs/collection/lib/useListLoader.d.ts +46 -7
  60. package/dist/commonjs/collection/lib/useListLoader.d.ts.map +1 -1
  61. package/dist/commonjs/collection/lib/useListLoader.js +61 -35
  62. package/dist/commonjs/collection/lib/useOverflowListMeasure.d.ts +1 -1
  63. package/dist/commonjs/collection/lib/useOverflowListMeasure.d.ts.map +1 -1
  64. package/dist/commonjs/collection/lib/useOverflowListTarget.d.ts +5 -2
  65. package/dist/commonjs/collection/lib/useOverflowListTarget.d.ts.map +1 -1
  66. package/dist/commonjs/collection/lib/useOverflowListTarget.js +1 -1
  67. package/dist/commonjs/collection/lib/useSelectionListModel.d.ts.map +1 -1
  68. package/dist/commonjs/collection/lib/useSelectionListModel.js +1 -0
  69. package/dist/commonjs/combobox/index.d.ts +9 -0
  70. package/dist/commonjs/combobox/index.d.ts.map +1 -0
  71. package/dist/commonjs/combobox/index.js +19 -0
  72. package/dist/commonjs/combobox/lib/Combobox.d.ts +2075 -0
  73. package/dist/commonjs/combobox/lib/Combobox.d.ts.map +1 -0
  74. package/dist/commonjs/combobox/lib/Combobox.js +29 -0
  75. package/dist/commonjs/combobox/lib/ComboboxCard.d.ts +166 -0
  76. package/dist/commonjs/combobox/lib/ComboboxCard.d.ts.map +1 -0
  77. package/dist/commonjs/combobox/lib/ComboboxCard.js +25 -0
  78. package/dist/commonjs/combobox/lib/ComboboxInput.d.ts +186 -0
  79. package/dist/commonjs/combobox/lib/ComboboxInput.d.ts.map +1 -0
  80. package/dist/commonjs/combobox/lib/ComboboxInput.js +84 -0
  81. package/dist/commonjs/combobox/lib/ComboboxMenu.d.ts +428 -0
  82. package/dist/commonjs/combobox/lib/ComboboxMenu.d.ts.map +1 -0
  83. package/dist/commonjs/combobox/lib/ComboboxMenu.js +63 -0
  84. package/dist/commonjs/combobox/lib/ComboboxMenuItem.d.ts +178 -0
  85. package/dist/commonjs/combobox/lib/ComboboxMenuItem.d.ts.map +1 -0
  86. package/dist/commonjs/combobox/lib/ComboboxMenuItem.js +50 -0
  87. package/dist/commonjs/combobox/lib/ComboboxMenuList.d.ts +164 -0
  88. package/dist/commonjs/combobox/lib/ComboboxMenuList.d.ts.map +1 -0
  89. package/dist/commonjs/combobox/lib/ComboboxMenuList.js +31 -0
  90. package/dist/commonjs/combobox/lib/useComboboxInputOpenWithArrowKeys.d.ts +84 -0
  91. package/dist/commonjs/combobox/lib/useComboboxInputOpenWithArrowKeys.d.ts.map +1 -0
  92. package/dist/commonjs/combobox/lib/useComboboxInputOpenWithArrowKeys.js +20 -0
  93. package/dist/commonjs/combobox/lib/useComboboxLoader.d.ts +28 -0
  94. package/dist/commonjs/combobox/lib/useComboboxLoader.d.ts.map +1 -0
  95. package/dist/commonjs/combobox/lib/useComboboxLoader.js +65 -0
  96. package/dist/commonjs/combobox/lib/useComboboxModel.d.ts +1998 -0
  97. package/dist/commonjs/combobox/lib/useComboboxModel.d.ts.map +1 -0
  98. package/dist/commonjs/combobox/lib/useComboboxModel.js +82 -0
  99. package/dist/commonjs/common/lib/utils/components.d.ts +9 -23
  100. package/dist/commonjs/common/lib/utils/components.d.ts.map +1 -1
  101. package/dist/commonjs/common/lib/utils/components.js +2 -2
  102. package/dist/commonjs/common/lib/utils/models.js +2 -2
  103. package/dist/commonjs/index.d.ts +1 -0
  104. package/dist/commonjs/index.d.ts.map +1 -1
  105. package/dist/commonjs/index.js +1 -0
  106. package/dist/commonjs/menu/index.d.ts +1 -1
  107. package/dist/commonjs/menu/index.d.ts.map +1 -1
  108. package/dist/commonjs/menu/index.js +2 -1
  109. package/dist/commonjs/menu/lib/Menu.d.ts +7 -5
  110. package/dist/commonjs/menu/lib/Menu.d.ts.map +1 -1
  111. package/dist/commonjs/menu/lib/Menu.js +6 -4
  112. package/dist/commonjs/menu/lib/MenuContextTarget.d.ts +2 -2
  113. package/dist/commonjs/menu/lib/MenuItem.d.ts +7 -1
  114. package/dist/commonjs/menu/lib/MenuItem.d.ts.map +1 -1
  115. package/dist/commonjs/menu/lib/MenuItem.js +9 -11
  116. package/dist/commonjs/menu/lib/MenuList.d.ts +3 -3
  117. package/dist/commonjs/menu/lib/MenuList.d.ts.map +1 -1
  118. package/dist/commonjs/menu/lib/MenuList.js +3 -2
  119. package/dist/commonjs/menu/lib/MenuPopper.d.ts +1 -1
  120. package/dist/commonjs/menu/lib/MenuTarget.d.ts +2 -2
  121. package/dist/commonjs/menu/lib/useMenuModel.d.ts +2 -2
  122. package/dist/commonjs/menu/lib/useMenuModel.d.ts.map +1 -1
  123. package/dist/commonjs/menu/lib/useMenuModel.js +3 -5
  124. package/dist/commonjs/popup/lib/hooks/usePopupPopper.d.ts +1 -1
  125. package/dist/commonjs/popup/lib/hooks/usePopupTarget.d.ts +1 -1
  126. package/dist/commonjs/popup/lib/hooks/usePopupTargetContext.d.ts +1 -1
  127. package/dist/commonjs/tabs/lib/Tabs.d.ts +18 -120
  128. package/dist/commonjs/tabs/lib/Tabs.d.ts.map +1 -1
  129. package/dist/commonjs/tabs/lib/TabsItem.d.ts +0 -1
  130. package/dist/commonjs/tabs/lib/TabsItem.d.ts.map +1 -1
  131. package/dist/commonjs/tabs/lib/TabsItem.js +0 -1
  132. package/dist/commonjs/tabs/lib/TabsList.d.ts +1 -1
  133. package/dist/commonjs/tabs/lib/TabsMenuPopper.d.ts +1 -1
  134. package/dist/commonjs/tabs/lib/TabsPanel.d.ts.map +1 -1
  135. package/dist/commonjs/tabs/lib/useTabsModel.d.ts +8 -20
  136. package/dist/commonjs/tabs/lib/useTabsModel.d.ts.map +1 -1
  137. package/dist/commonjs/tabs/lib/useTabsModel.js +1 -0
  138. package/dist/es6/action-bar/lib/ActionBar.d.ts +2 -2
  139. package/dist/es6/action-bar/lib/ActionBarList.d.ts +1 -1
  140. package/dist/es6/action-bar/lib/useActionBarModel.d.ts +2 -2
  141. package/dist/es6/breadcrumbs/lib/Breadcrumbs.d.ts +2 -2
  142. package/dist/es6/breadcrumbs/lib/BreadcrumbsMenu.d.ts +1 -1
  143. package/dist/es6/breadcrumbs/lib/hooks/useBreadcrumbsModel.d.ts +2 -2
  144. package/dist/es6/collection/index.d.ts +2 -0
  145. package/dist/es6/collection/index.d.ts.map +1 -1
  146. package/dist/es6/collection/index.js +2 -0
  147. package/dist/es6/collection/lib/ListBox.d.ts +7 -6
  148. package/dist/es6/collection/lib/ListBox.d.ts.map +1 -1
  149. package/dist/es6/collection/lib/ListBox.js +6 -4
  150. package/dist/es6/collection/lib/useBaseListModel.d.ts.map +1 -1
  151. package/dist/es6/collection/lib/useBaseListModel.js +4 -4
  152. package/dist/es6/collection/lib/useCursorListModel.d.ts +0 -34
  153. package/dist/es6/collection/lib/useCursorListModel.d.ts.map +1 -1
  154. package/dist/es6/collection/lib/useCursorListModel.js +1 -1
  155. package/dist/es6/collection/lib/useListActiveDescendant.d.ts +68 -0
  156. package/dist/es6/collection/lib/useListActiveDescendant.d.ts.map +1 -0
  157. package/dist/es6/collection/lib/useListActiveDescendant.js +22 -0
  158. package/dist/es6/collection/lib/useListItemAllowChildStrings.d.ts +2 -1
  159. package/dist/es6/collection/lib/useListItemAllowChildStrings.d.ts.map +1 -1
  160. package/dist/es6/collection/lib/useListItemAllowChildStrings.js +2 -1
  161. package/dist/es6/collection/lib/useListItemRegister.d.ts.map +1 -1
  162. package/dist/es6/collection/lib/useListItemRegister.js +5 -1
  163. package/dist/es6/collection/lib/useListKeyboardHandler.d.ts +66 -0
  164. package/dist/es6/collection/lib/useListKeyboardHandler.d.ts.map +1 -0
  165. package/dist/es6/collection/lib/useListKeyboardHandler.js +24 -0
  166. package/dist/es6/collection/lib/useListLoader.d.ts +46 -7
  167. package/dist/es6/collection/lib/useListLoader.d.ts.map +1 -1
  168. package/dist/es6/collection/lib/useListLoader.js +61 -35
  169. package/dist/es6/collection/lib/useOverflowListMeasure.d.ts +1 -1
  170. package/dist/es6/collection/lib/useOverflowListMeasure.d.ts.map +1 -1
  171. package/dist/es6/collection/lib/useOverflowListTarget.d.ts +5 -2
  172. package/dist/es6/collection/lib/useOverflowListTarget.d.ts.map +1 -1
  173. package/dist/es6/collection/lib/useOverflowListTarget.js +1 -1
  174. package/dist/es6/collection/lib/useSelectionListModel.d.ts.map +1 -1
  175. package/dist/es6/collection/lib/useSelectionListModel.js +1 -0
  176. package/dist/es6/combobox/index.d.ts +9 -0
  177. package/dist/es6/combobox/index.d.ts.map +1 -0
  178. package/dist/es6/combobox/index.js +8 -0
  179. package/dist/es6/combobox/lib/Combobox.d.ts +2075 -0
  180. package/dist/es6/combobox/lib/Combobox.d.ts.map +1 -0
  181. package/dist/es6/combobox/lib/Combobox.js +23 -0
  182. package/dist/es6/combobox/lib/ComboboxCard.d.ts +166 -0
  183. package/dist/es6/combobox/lib/ComboboxCard.d.ts.map +1 -0
  184. package/dist/es6/combobox/lib/ComboboxCard.js +19 -0
  185. package/dist/es6/combobox/lib/ComboboxInput.d.ts +186 -0
  186. package/dist/es6/combobox/lib/ComboboxInput.d.ts.map +1 -0
  187. package/dist/es6/combobox/lib/ComboboxInput.js +78 -0
  188. package/dist/es6/combobox/lib/ComboboxMenu.d.ts +428 -0
  189. package/dist/es6/combobox/lib/ComboboxMenu.d.ts.map +1 -0
  190. package/dist/es6/combobox/lib/ComboboxMenu.js +57 -0
  191. package/dist/es6/combobox/lib/ComboboxMenuItem.d.ts +178 -0
  192. package/dist/es6/combobox/lib/ComboboxMenuItem.d.ts.map +1 -0
  193. package/dist/es6/combobox/lib/ComboboxMenuItem.js +44 -0
  194. package/dist/es6/combobox/lib/ComboboxMenuList.d.ts +164 -0
  195. package/dist/es6/combobox/lib/ComboboxMenuList.d.ts.map +1 -0
  196. package/dist/es6/combobox/lib/ComboboxMenuList.js +25 -0
  197. package/dist/es6/combobox/lib/useComboboxInputOpenWithArrowKeys.d.ts +84 -0
  198. package/dist/es6/combobox/lib/useComboboxInputOpenWithArrowKeys.d.ts.map +1 -0
  199. package/dist/es6/combobox/lib/useComboboxInputOpenWithArrowKeys.js +17 -0
  200. package/dist/es6/combobox/lib/useComboboxLoader.d.ts +28 -0
  201. package/dist/es6/combobox/lib/useComboboxLoader.d.ts.map +1 -0
  202. package/dist/es6/combobox/lib/useComboboxLoader.js +58 -0
  203. package/dist/es6/combobox/lib/useComboboxModel.d.ts +1998 -0
  204. package/dist/es6/combobox/lib/useComboboxModel.d.ts.map +1 -0
  205. package/dist/es6/combobox/lib/useComboboxModel.js +76 -0
  206. package/dist/es6/common/lib/utils/components.d.ts +9 -23
  207. package/dist/es6/common/lib/utils/components.d.ts.map +1 -1
  208. package/dist/es6/common/lib/utils/components.js +2 -2
  209. package/dist/es6/common/lib/utils/models.js +2 -2
  210. package/dist/es6/index.d.ts +1 -0
  211. package/dist/es6/index.d.ts.map +1 -1
  212. package/dist/es6/index.js +1 -0
  213. package/dist/es6/menu/index.d.ts +1 -1
  214. package/dist/es6/menu/index.d.ts.map +1 -1
  215. package/dist/es6/menu/index.js +1 -1
  216. package/dist/es6/menu/lib/Menu.d.ts +7 -5
  217. package/dist/es6/menu/lib/Menu.d.ts.map +1 -1
  218. package/dist/es6/menu/lib/Menu.js +6 -4
  219. package/dist/es6/menu/lib/MenuContextTarget.d.ts +2 -2
  220. package/dist/es6/menu/lib/MenuItem.d.ts +7 -1
  221. package/dist/es6/menu/lib/MenuItem.d.ts.map +1 -1
  222. package/dist/es6/menu/lib/MenuItem.js +8 -10
  223. package/dist/es6/menu/lib/MenuList.d.ts +3 -3
  224. package/dist/es6/menu/lib/MenuList.d.ts.map +1 -1
  225. package/dist/es6/menu/lib/MenuList.js +4 -3
  226. package/dist/es6/menu/lib/MenuPopper.d.ts +1 -1
  227. package/dist/es6/menu/lib/MenuTarget.d.ts +2 -2
  228. package/dist/es6/menu/lib/useMenuModel.d.ts +2 -2
  229. package/dist/es6/menu/lib/useMenuModel.d.ts.map +1 -1
  230. package/dist/es6/menu/lib/useMenuModel.js +4 -6
  231. package/dist/es6/popup/lib/hooks/usePopupPopper.d.ts +1 -1
  232. package/dist/es6/popup/lib/hooks/usePopupTarget.d.ts +1 -1
  233. package/dist/es6/popup/lib/hooks/usePopupTargetContext.d.ts +1 -1
  234. package/dist/es6/tabs/lib/Tabs.d.ts +18 -120
  235. package/dist/es6/tabs/lib/Tabs.d.ts.map +1 -1
  236. package/dist/es6/tabs/lib/TabsItem.d.ts +0 -1
  237. package/dist/es6/tabs/lib/TabsItem.d.ts.map +1 -1
  238. package/dist/es6/tabs/lib/TabsItem.js +0 -1
  239. package/dist/es6/tabs/lib/TabsList.d.ts +1 -1
  240. package/dist/es6/tabs/lib/TabsMenuPopper.d.ts +1 -1
  241. package/dist/es6/tabs/lib/TabsPanel.d.ts.map +1 -1
  242. package/dist/es6/tabs/lib/useTabsModel.d.ts +8 -20
  243. package/dist/es6/tabs/lib/useTabsModel.d.ts.map +1 -1
  244. package/dist/es6/tabs/lib/useTabsModel.js +1 -0
  245. package/index.ts +1 -0
  246. package/menu/index.ts +1 -1
  247. package/menu/lib/Menu.tsx +6 -4
  248. package/menu/lib/MenuItem.tsx +9 -12
  249. package/menu/lib/MenuList.tsx +16 -10
  250. package/menu/lib/useMenuModel.tsx +4 -7
  251. package/modal/lib/hooks/useModalHeading.ts +2 -2
  252. package/package.json +3 -3
  253. package/tabs/lib/TabsItem.tsx +10 -13
  254. package/tabs/lib/TabsPanel.tsx +1 -5
  255. package/tabs/lib/useTabsModel.tsx +1 -0
@@ -38,7 +38,7 @@ export const useBreadcrumbsItem = composeHooks(
38
38
  createElemPropsHook(useBreadcrumbsModel)(
39
39
  (
40
40
  {state},
41
- _?: React.Ref<HTMLElement>,
41
+ _,
42
42
  elemProps: {
43
43
  'data-id'?: string;
44
44
  item?: {id: string};
@@ -8,9 +8,11 @@ export * from './lib/useListRenderItem';
8
8
  export * from './lib/useListResetCursorOnBlur';
9
9
  export * from './lib/useListItemRovingFocus';
10
10
  export * from './lib/useListItemSelect';
11
+ export * from './lib/useListKeyboardHandler';
11
12
  export * from './lib/useListLoader';
12
13
  export * from './lib/useListModel';
13
14
  export * from './lib/useGridModel';
15
+ export * from './lib/useListActiveDescendant';
14
16
  export * from './lib/useListItemAllowChildStrings';
15
17
  export {ListBox, ListBoxProps} from './lib/ListBox';
16
18
  export {
@@ -3,22 +3,23 @@ import {
3
3
  createContainer,
4
4
  createElemPropsHook,
5
5
  createSubcomponent,
6
+ ExtractProps,
6
7
  } from '@workday/canvas-kit-react/common';
7
- import {Box, BoxProps} from '@workday/canvas-kit-react/layout';
8
+ import {Box, Flex} from '@workday/canvas-kit-react/layout';
8
9
 
9
10
  import {useListModel} from './useListModel';
10
11
  import {useListRenderItems} from './useListRenderItem';
11
12
  import {useListItemRegister} from './useListItemRegister';
12
13
 
13
- export interface ListBoxProps extends Omit<BoxProps, 'children'> {
14
- children?: React.ReactNode | ((item: any) => React.ReactNode);
14
+ export interface ListBoxProps<T = any> extends Omit<ExtractProps<typeof Flex, never>, 'children'> {
15
+ children?: React.ReactNode | ((item: T, index: number) => React.ReactNode);
15
16
  }
16
17
 
17
18
  export const ListBoxItem = createSubcomponent('li')({
18
19
  displayName: 'Item',
19
20
  modelHook: useListModel,
20
21
  elemPropsHook: useListItemRegister,
21
- })<BoxProps>((elemProps, Element) => {
22
+ })<ExtractProps<typeof Flex, never>>((elemProps, Element) => {
22
23
  return <Box as={Element} {...elemProps} />;
23
24
  });
24
25
 
@@ -64,20 +65,21 @@ export const ListBox = createContainer('ul')({
64
65
  */
65
66
  Item: ListBoxItem,
66
67
  },
67
- })<ListBoxProps>(({height, maxHeight, ...elemProps}, Element, model) => {
68
+ })<ListBoxProps>(({height, maxHeight, marginY, ...elemProps}, Element, model) => {
69
+ // We're moving `marginY` to the container to not interfere with the virtualization size. We set
70
+ // the `marginY` on the Flex to `0` to avoid inaccurate scrollbars
71
+
68
72
  // TODO figure out what style props should go to which `Box`
69
73
  return (
70
74
  <Box
71
75
  ref={model.state.containerRef}
72
- height={
73
- height || model.state.isVirtualized ? model.state.UNSTABLE_virtual.totalSize : undefined
74
- }
76
+ marginY={marginY}
75
77
  maxHeight={maxHeight}
76
78
  overflowY={model.state.orientation === 'vertical' ? 'auto' : undefined}
77
79
  >
78
- <Box as={Element} {...elemProps}>
80
+ <Flex as={Element} flexDirection="column" {...elemProps} marginY={0}>
79
81
  {useListRenderItems(model, elemProps.children)}
80
- </Box>
82
+ </Flex>
81
83
  </Box>
82
84
  );
83
85
  });
@@ -6,11 +6,11 @@ import {useUniqueId, createModelHook, Generic} from '@workday/canvas-kit-react/c
6
6
  export type Orientation = 'horizontal' | 'vertical';
7
7
 
8
8
  export const defaultGetId = (item: any): string => {
9
- return item.id || '';
9
+ return typeof item === 'string' ? item : item.id || '';
10
10
  };
11
11
 
12
12
  export const defaultGetTextValue = (item: any): string => {
13
- return item.text || '';
13
+ return typeof item === 'string' ? item : item.text || '';
14
14
  };
15
15
 
16
16
  export interface Item<T> {
@@ -84,13 +84,15 @@ export const useBaseListModel = createModelHook({
84
84
  * JSX, the list will create an internal array of items where `id` is the only property and the
85
85
  * default `getId` will return the desired result.
86
86
  */
87
- getId: (item: Generic) => item.id as string,
87
+ getId: (item: Generic) =>
88
+ typeof item === 'string' ? item : item === undefined ? '' : (item.id as string),
88
89
  /**
89
90
  * Optional function to return the text representation of an item. If not provided, the default
90
91
  * function will return the `text` property of the object of each item or an empty string if
91
92
  * there is no `text` property. If you did not provide `items`, do not override this function.
92
93
  */
93
- getTextValue: (item: Generic) => (item.text || '') as string,
94
+ getTextValue: (item: Generic) =>
95
+ (typeof item === 'string' ? item : item === undefined ? '' : item.text || '') as string,
94
96
  /**
95
97
  * Array of all ids which are currently disabled. This is used for navigation to skip over items
96
98
  * which are not focusable.
@@ -184,7 +184,7 @@ export const getWrappingOffsetItem = (offset: number) => (
184
184
  }
185
185
  }
186
186
 
187
- if (state.nonInteractiveIds.includes(items[nextIndex].id) && tries > 0) {
187
+ if (items.length > 1 && state.nonInteractiveIds.includes(items[nextIndex].id) && tries > 0) {
188
188
  // The next item is disabled, try again, but only if we haven't already tried everything.
189
189
  // Avoid an infinite loop with `tries`
190
190
  return getWrappingOffsetItem(offset)(nextIndex, {state}, tries - 1);
@@ -0,0 +1,24 @@
1
+ import {createElemPropsHook, slugify} from '@workday/canvas-kit-react/common';
2
+
3
+ import {useCursorListModel} from './useCursorListModel';
4
+
5
+ /**
6
+ * This elemProps hook is used for cursor navigation by using [Active
7
+ * Descendant](https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_focus_activedescendant).
8
+ * `aria-activedescendant` tells assistive technology which element has "focus" within the component
9
+ * while this single element maintains focus.
10
+ *
11
+ * ```ts
12
+ * const useMyItem = composeHooks(
13
+ * useListActiveDescendant, // adds `aria-activedescendant` support
14
+ * useListItemRegister
15
+ * );
16
+ ```
17
+ */
18
+ export const useListActiveDescendant = createElemPropsHook(useCursorListModel)(model => {
19
+ return {
20
+ 'aria-activedescendant': model.state.cursorId
21
+ ? slugify(`${model.state.id}-${model.state.cursorId}`)
22
+ : undefined,
23
+ };
24
+ });
@@ -8,12 +8,13 @@ import {useListModel} from '@workday/canvas-kit-react/collection';
8
8
  * attribute if one hasn't been defined by the application.
9
9
  *
10
10
  * An example might look like:
11
+ *
11
12
  * ```tsx
12
13
  * const useMyListItem = composeHooks(
13
14
  * // any other hooks here
14
15
  * useListItemSelect,
15
16
  * useListItemRegister,
16
- * useListItemAllowChildStrings
17
+ * useListItemAllowChildStrings // always the last in the list
17
18
  * )
18
19
  *
19
20
  *
@@ -28,9 +28,10 @@ import {useListModel} from './useListModel';
28
28
  export const useListItemRegister = createElemPropsHook(useListModel)(
29
29
  (
30
30
  {state, events},
31
- ref?: React.Ref<HTMLElement>,
31
+ ref,
32
32
  elemProps: {
33
33
  'data-id'?: string;
34
+ 'data-text'?: string;
34
35
  children?: React.ReactNode;
35
36
  index?: number;
36
37
  disabled?: boolean;
@@ -39,7 +40,9 @@ export const useListItemRegister = createElemPropsHook(useListModel)(
39
40
  } = {}
40
41
  ) => {
41
42
  const [localId, setLocalId] = React.useState(elemProps['data-id'] || elemProps.item?.id || '');
42
- const {localRef, elementRef} = useLocalRef(useForkRef(ref, elemProps.virtual?.measureRef));
43
+ const {localRef, elementRef} = useLocalRef(
44
+ useForkRef(ref as React.Ref<HTMLElement>, elemProps.virtual?.measureRef)
45
+ );
43
46
 
44
47
  // if the list is virtual, force the correct styling. Without this, weird things happen...
45
48
  const style: CSSProperties = elemProps.virtual
@@ -72,7 +75,11 @@ export const useListItemRegister = createElemPropsHook(useListModel)(
72
75
  item: {
73
76
  id: itemId,
74
77
  },
75
- textValue: typeof elemProps.children === 'string' ? elemProps.children : '',
78
+ textValue: elemProps['data-text']
79
+ ? elemProps['data-text']
80
+ : typeof elemProps.children === 'string'
81
+ ? elemProps.children
82
+ : '',
76
83
  });
77
84
  setLocalId(itemId);
78
85
 
@@ -17,7 +17,7 @@ import {useListModel} from './useListModel';
17
17
  * ```
18
18
  */
19
19
  export const useListItemSelect = createElemPropsHook(useListModel)(
20
- ({state, events}, _?: React.Ref<HTMLElement>, elemProps: {'data-id'?: string} = {}) => {
20
+ ({state, events}, _, elemProps: {'data-id'?: string} = {}) => {
21
21
  const name = elemProps['data-id'] || '';
22
22
  const onClick = (event: React.MouseEvent<HTMLElement>) => {
23
23
  if (
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import {useIsRTL, createElemPropsHook} from '@workday/canvas-kit-react/common';
3
+
4
+ import {useCursorListModel} from './useCursorListModel';
5
+ import {keyboardEventToCursorEvents} from './keyUtils';
6
+
7
+ /**
8
+ * This elemProps hook is used to attach keyboard events to handle keyboard navigation
9
+ *
10
+ * ```ts
11
+ * const useMyItem = composeHooks(
12
+ * useListKeyboardHandler, // adds keyboard support support
13
+ * useListItemRegister
14
+ * );
15
+ ```
16
+ */
17
+ export const useListKeyboardHandler = createElemPropsHook(useCursorListModel)(model => {
18
+ const isRTL = useIsRTL();
19
+
20
+ return {
21
+ onKeyDown(event: React.KeyboardEvent) {
22
+ const handled = keyboardEventToCursorEvents(event, model, isRTL);
23
+ if (handled) {
24
+ event.preventDefault();
25
+ }
26
+ },
27
+ };
28
+ });
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
+ import {Model} from '@workday/canvas-kit-react/common';
4
+
3
5
  import {useListModel} from './useListModel';
4
6
 
5
7
  export interface LoadReturn<T> {
@@ -13,9 +15,46 @@ export interface LoadParams {
13
15
  filter: CollectionLoader['filter'];
14
16
  sorter: CollectionLoader['sorter'];
15
17
  }
16
- export interface AsyncCollectionConfig<T> {
18
+ export interface AsyncCollectionConfig<T, M extends Model<any, any>> {
19
+ /**
20
+ * The desired results per page.
21
+ */
17
22
  pageSize: number;
18
- total: number;
23
+ /**
24
+ * The total items in the collection. If you do not have this information before making an API
25
+ * call, leave it blank and it will default to `0` and be updated when `load` comes back with
26
+ * results.
27
+ * @default 0
28
+ */
29
+ total?: number;
30
+ /**
31
+ * This function acts like a guard similar to model events. If you provide this function and it
32
+ * returns `false`, the `load` call from the loader will be cancelled. Be aware, this function can
33
+ * cause a loader to not function properly. You will need to call `loader.load(pageNumber)` on
34
+ * your own if a valid load event is cancelled. An example of using this function is in
35
+ * {@link useComboboxLoader} where the load calls are only allowed when the menu is open.
36
+ */
37
+ shouldLoad?(
38
+ /**
39
+ * Parameters that are being sent to the `load` function
40
+ */
41
+ params: LoadParams,
42
+ /**
43
+ * State coming from the model
44
+ */
45
+ prevState: M['state']
46
+ ): boolean;
47
+ /**
48
+ * A `load` callback function provides an easier way to hook up loader requests for data to an
49
+ * API. It will be called when the user is ready for more of the virtual data set. The `load`
50
+ * function will be called when:
51
+ * - The list UI initially loads. It will request as many pages as necessary to render enough
52
+ * items in the viewport
53
+ * - The user is scrolling the list and has run out of cached data
54
+ * - The user is using the keyboard to navigate the list and has run out of cached data
55
+ * - A filter or sorter value has changed and the cached data is now invalid
56
+ * - `loader.load` was called by the developer, manually requesting a page number
57
+ */
19
58
  load(params: LoadParams): LoadReturn<T> | Promise<LoadReturn<T>>;
20
59
  }
21
60
 
@@ -25,6 +64,7 @@ export interface CollectionLoader {
25
64
  sorter: string | Record<string, string>;
26
65
  updateSorter(value: string): void;
27
66
  isLoading: boolean;
67
+ load(pageNumber?: number): void;
28
68
  }
29
69
 
30
70
  /**
@@ -57,7 +97,7 @@ function load<T>(
57
97
  filter: CollectionLoader['filter'];
58
98
  sorter: CollectionLoader['sorter'];
59
99
  },
60
- loadCb: AsyncCollectionConfig<T>['load'],
100
+ loadCb: AsyncCollectionConfig<T, any>['load'],
61
101
  loadingCache: Record<number, boolean>
62
102
  ): Promise<Updater<T>> {
63
103
  if (loadingCache[params.pageNumber]) {
@@ -122,11 +162,12 @@ export function getPagesToLoad<T>(
122
162
  const resetItems = (total: number) => Array(total).fill(undefined);
123
163
 
124
164
  /**
125
- * Create a data loader and a model. The model should be passed to a collection component. The
126
- * loader can be used to manipulate filters, sorters, and clear cache.
165
+ * Create a data loader and a model. The list loader should be used on virtual data sets with
166
+ * possibly large amounts of data. The model should be passed to a collection component. The loader
167
+ * can be used to manipulate filters, sorters, and clear cache.
127
168
  *
128
- * A simple loader using [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
129
- * could look like the following:
169
+ * A simple loader using [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) could
170
+ * look like the following:
130
171
  *
131
172
  * ```ts
132
173
  * const {model, loader} = useListLoader(
@@ -151,14 +192,14 @@ const resetItems = (total: number) => Array(total).fill(undefined);
151
192
  export function useListLoader<
152
193
  T,
153
194
  // I cannot get Typescript to accept models that extend from `useListModel` to be considered valid
154
- M extends ((...args: any[]) => any) & Omit<typeof useListModel, 'TConfig' | 'Context'>
195
+ M extends ((...args: any[]) => any) & Omit<typeof useListModel, 'Context'>
155
196
  >(
156
- config: AsyncCollectionConfig<T> & typeof useListModel.TConfig,
197
+ config: AsyncCollectionConfig<T, ReturnType<M>> & M['TConfig'],
157
198
  modelHook: M
158
199
  ): {model: ReturnType<M>; loader: CollectionLoader} {
159
- const [total, setTotal] = React.useState(config.total);
200
+ const [total, setTotal] = React.useState(config.total || 0);
160
201
  const [items, setItems] = React.useState<T[]>(() => {
161
- return resetItems(config.total);
202
+ return resetItems(config.total || 0);
162
203
  });
163
204
 
164
205
  // keep track of pages that are currently loading
@@ -182,33 +223,6 @@ export function useListLoader<
182
223
  [total]
183
224
  );
184
225
 
185
- const updateFilter = (filter: CollectionLoader['filter']) => {
186
- loadingRef.current = {};
187
- setFilter(filter);
188
- const params = {
189
- pageNumber: 1,
190
- pageSize: config.pageSize,
191
- filter,
192
- sorter,
193
- };
194
- load(params, loadRef.current, loadingRef.current).then(updateItems);
195
- };
196
-
197
- const updateSorter = (sorter: CollectionLoader['sorter']) => {
198
- loadingRef.current = {};
199
- setSorter(sorter);
200
- load(
201
- {
202
- pageNumber: 1,
203
- pageSize: config.pageSize,
204
- filter,
205
- sorter,
206
- },
207
- loadRef.current,
208
- loadingRef.current
209
- ).then(updateItems);
210
- };
211
-
212
226
  // Our `useEffect` functions use `config.load`, but we don't want to rerun the effects if the user
213
227
  // sends a different load function every render
214
228
  const loadRef = React.useRef(config.load);
@@ -216,25 +230,29 @@ export function useListLoader<
216
230
 
217
231
  const itemsRef = React.useRef(items);
218
232
  itemsRef.current = items;
233
+
234
+ const shouldLoadRef = React.useRef(config.shouldLoad);
235
+ shouldLoadRef.current = config.shouldLoad;
236
+
219
237
  const requestAnimationFrameRef = React.useRef(0);
220
238
 
221
239
  const shouldLoadIndex = (
222
240
  navigationMethod: Exclude<keyof ReturnType<typeof useListModel>['navigation'], 'getItem'>,
223
241
  eventKey: keyof ReturnType<typeof useListModel>['events']
224
242
  ) => {
225
- return (data: any) => {
243
+ return (data: any, prevState: any) => {
226
244
  const index = model.navigation[navigationMethod](model.state.cursorIndexRef.current, model);
245
+ const params = {
246
+ pageNumber: Math.floor(index / config.pageSize) + 1,
247
+ pageSize: config.pageSize,
248
+ filter,
249
+ sorter,
250
+ };
227
251
  if (!items[index]) {
228
- load(
229
- {
230
- pageNumber: Math.floor(index / config.pageSize) + 1,
231
- pageSize: config.pageSize,
232
- filter,
233
- sorter,
234
- },
235
- loadRef.current,
236
- loadingRef.current
237
- )
252
+ if (config.shouldLoad && !config.shouldLoad(params, prevState)) {
253
+ return false;
254
+ }
255
+ load(params, loadRef.current, loadingRef.current)
238
256
  .then(updateItems)
239
257
  .then(() => {
240
258
  cancelAnimationFrame(requestAnimationFrameRef.current);
@@ -262,6 +280,40 @@ export function useListLoader<
262
280
 
263
281
  const {virtualItems} = model.state.UNSTABLE_virtual;
264
282
 
283
+ const {state} = model;
284
+ const stateRef = React.useRef(state);
285
+ stateRef.current = state;
286
+
287
+ const updateFilter = (filter: CollectionLoader['filter']) => {
288
+ loadingRef.current = {};
289
+ setFilter(filter);
290
+ const params = {
291
+ pageNumber: 1,
292
+ pageSize: config.pageSize,
293
+ filter,
294
+ sorter,
295
+ };
296
+ if (config.shouldLoad && !config.shouldLoad(params, state)) {
297
+ return;
298
+ }
299
+ load(params, loadRef.current, loadingRef.current).then(updateItems);
300
+ };
301
+
302
+ const updateSorter = (sorter: CollectionLoader['sorter']) => {
303
+ loadingRef.current = {};
304
+ setSorter(sorter);
305
+ const params = {
306
+ pageNumber: 1,
307
+ pageSize: config.pageSize,
308
+ filter,
309
+ sorter,
310
+ };
311
+ if (config.shouldLoad && !config.shouldLoad(params, state)) {
312
+ return;
313
+ }
314
+ load(params, loadRef.current, loadingRef.current).then(updateItems);
315
+ };
316
+
265
317
  // Our only signal to trigger loading is if our virtual indexes are too close to boundaries.
266
318
  React.useEffect(() => {
267
319
  if (!virtualItems.length) {
@@ -280,11 +332,12 @@ export function useListLoader<
280
332
  );
281
333
 
282
334
  pagesToLoad.forEach(pageNumber => {
283
- load(
284
- {pageNumber, pageSize: config.pageSize, filter, sorter},
285
- loadRef.current,
286
- loadingRef.current
287
- ).then(updateItems);
335
+ const params = {pageNumber, pageSize: config.pageSize, filter, sorter};
336
+ const shouldLoad = shouldLoadRef.current;
337
+ if (shouldLoad && !shouldLoad(params, stateRef.current)) {
338
+ return;
339
+ }
340
+ load(params, loadRef.current, loadingRef.current).then(updateItems);
288
341
  });
289
342
 
290
343
  return () => {
@@ -292,6 +345,14 @@ export function useListLoader<
292
345
  };
293
346
  }, [virtualItems, config.pageSize, filter, sorter, updateItems]);
294
347
 
348
+ const loaderLoad: CollectionLoader['load'] = (pageNumber = 1) => {
349
+ return load<T>(
350
+ {pageNumber, pageSize: config.pageSize, filter, sorter},
351
+ loadRef.current,
352
+ loadingRef.current
353
+ ).then(updateItems);
354
+ };
355
+
295
356
  // Typescript won't allow me to say the included model extends `useListModel` for some reason, so
296
357
  // we have no way to type check it. Without the constraint, Typescript doesn't know how type it
297
358
  // properly, so we have an `as any`.
@@ -308,6 +369,7 @@ export function useListLoader<
308
369
  (loading, result) => loading || result,
309
370
  false
310
371
  ),
372
+ load: loaderLoad,
311
373
  },
312
374
  };
313
375
  }
@@ -19,8 +19,8 @@ const hiddenStyles = {
19
19
  * ```
20
20
  */
21
21
  export const useOverflowListItemMeasure = createElemPropsHook(useOverflowListModel)(
22
- (model, ref?: React.Ref<HTMLElement>, elemProps: {'data-id'?: string} = {}) => {
23
- const {elementRef, localRef} = useLocalRef(ref);
22
+ (model, ref, elemProps: {'data-id'?: string} = {}) => {
23
+ const {elementRef, localRef} = useLocalRef(ref as React.Ref<HTMLElement>);
24
24
  const name = elemProps['data-id'] || '';
25
25
 
26
26
  useMountLayout(() => {
@@ -8,17 +8,15 @@ import {useOverflowListModel} from './useOverflowListModel';
8
8
  * This elemProps hook measures a list and reports it to an `OverflowListModel`. This is used in
9
9
  * overflow detection.
10
10
  */
11
- export const useOverflowListMeasure = createElemPropsHook(useOverflowListModel)(
12
- (model, ref?: React.Ref<HTMLElement>) => {
13
- const localRef = React.useRef(null);
14
- const {ref: resizeRef} = useResizeObserver({
15
- ref: localRef,
16
- onResize: model.events.setContainerWidth,
17
- });
18
- const elementRef = useForkRef(ref, resizeRef);
11
+ export const useOverflowListMeasure = createElemPropsHook(useOverflowListModel)((model, ref) => {
12
+ const localRef = React.useRef(null);
13
+ const {ref: resizeRef} = useResizeObserver({
14
+ ref: localRef,
15
+ onResize: model.events.setContainerWidth,
16
+ });
17
+ const elementRef = useForkRef(ref, resizeRef);
19
18
 
20
- return {
21
- ref: elementRef,
22
- };
23
- }
24
- );
19
+ return {
20
+ ref: elementRef,
21
+ };
22
+ });
@@ -14,33 +14,31 @@ const hiddenStyle = {
14
14
  * This elemProps hook measures an overflow list target and reports it to an `OverflowListModel`.
15
15
  * This is used in overflow detection.
16
16
  */
17
- export const useOverflowListTarget = createElemPropsHook(useOverflowListModel)(
18
- (model, ref?: React.Ref<HTMLButtonElement>) => {
19
- const {elementRef, localRef} = useLocalRef(ref);
20
- // track first render to force correct size calculations
21
- const firstRender = React.useRef(true);
22
-
23
- useMountLayout(() => {
24
- firstRender.current = false;
25
- if (localRef.current) {
26
- const styles = getComputedStyle(localRef.current);
27
-
28
- model.events.setOverflowTargetWidth({
29
- width:
30
- localRef.current.offsetWidth +
31
- parseFloat(styles.marginLeft) +
32
- parseFloat(styles.marginRight),
33
- });
34
- }
35
- });
36
-
37
- const isHidden = !model.state.hiddenIds.length;
38
-
39
- return {
40
- ref: elementRef,
41
- 'aria-hidden': isHidden,
42
- tabIndex: isHidden ? -1 : 0,
43
- style: isHidden ? hiddenStyle : {},
44
- };
45
- }
46
- );
17
+ export const useOverflowListTarget = createElemPropsHook(useOverflowListModel)((model, ref) => {
18
+ const {elementRef, localRef} = useLocalRef(ref as React.Ref<HTMLElement>);
19
+ // track first render to force correct size calculations
20
+ const firstRender = React.useRef(true);
21
+
22
+ useMountLayout(() => {
23
+ firstRender.current = false;
24
+ if (localRef.current) {
25
+ const styles = getComputedStyle(localRef.current);
26
+
27
+ model.events.setOverflowTargetWidth({
28
+ width:
29
+ localRef.current.offsetWidth +
30
+ parseFloat(styles.marginLeft) +
31
+ parseFloat(styles.marginRight),
32
+ });
33
+ }
34
+ });
35
+
36
+ const isHidden = !model.state.hiddenIds.length;
37
+
38
+ return {
39
+ ref: elementRef,
40
+ 'aria-hidden': isHidden,
41
+ tabIndex: isHidden ? -1 : 0,
42
+ style: isHidden ? hiddenStyle : undefined,
43
+ };
44
+ });
@@ -71,6 +71,7 @@ export const useSelectionListModel = createModelHook({
71
71
  selection: singleSelectionManager,
72
72
  },
73
73
  requiredConfig: useCursorListModel.requiredConfig,
74
+ contextOverride: useCursorListModel.Context,
74
75
  })(config => {
75
76
  const cursor = useCursorListModel(config);
76
77
  const [selectedIds, setSelectedIds] = React.useState(config.initialSelectedIds);