@wordpress/components 23.7.0 → 23.9.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 (274) hide show
  1. package/CHANGELOG.md +52 -6
  2. package/build/checkbox-control/index.js +2 -2
  3. package/build/checkbox-control/index.js.map +1 -1
  4. package/build/color-palette/index.native.js +12 -0
  5. package/build/color-palette/index.native.js.map +1 -1
  6. package/build/custom-gradient-picker/index.native.js +3 -1
  7. package/build/custom-gradient-picker/index.native.js.map +1 -1
  8. package/build/custom-gradient-picker/serializer.js +0 -4
  9. package/build/custom-gradient-picker/serializer.js.map +1 -1
  10. package/build/draggable/index.js +6 -1
  11. package/build/draggable/index.js.map +1 -1
  12. package/build/drop-zone/index.js +8 -8
  13. package/build/drop-zone/index.js.map +1 -1
  14. package/build/index.js.map +1 -1
  15. package/build/mobile/bottom-sheet/cell.native.js +6 -6
  16. package/build/mobile/bottom-sheet/cell.native.js.map +1 -1
  17. package/build/mobile/color-settings/palette.screen.native.js +0 -8
  18. package/build/mobile/color-settings/palette.screen.native.js.map +1 -1
  19. package/build/mobile/global-styles-context/utils.native.js +21 -4
  20. package/build/mobile/global-styles-context/utils.native.js.map +1 -1
  21. package/build/mobile/keyboard-aware-flat-list/index.android.js +0 -4
  22. package/build/mobile/keyboard-aware-flat-list/index.android.js.map +1 -1
  23. package/build/mobile/keyboard-aware-flat-list/index.ios.js +100 -55
  24. package/build/mobile/keyboard-aware-flat-list/index.ios.js.map +1 -1
  25. package/build/mobile/keyboard-aware-flat-list/use-keyboard-offset.native.js +82 -0
  26. package/build/mobile/keyboard-aware-flat-list/use-keyboard-offset.native.js.map +1 -0
  27. package/build/mobile/keyboard-aware-flat-list/use-scroll-to-text-input.native.js +85 -0
  28. package/build/mobile/keyboard-aware-flat-list/use-scroll-to-text-input.native.js.map +1 -0
  29. package/build/mobile/keyboard-aware-flat-list/use-text-input-caret-position.native.js +44 -0
  30. package/build/mobile/keyboard-aware-flat-list/use-text-input-caret-position.native.js.map +1 -0
  31. package/build/mobile/keyboard-aware-flat-list/use-text-input-offset.native.js +53 -0
  32. package/build/mobile/keyboard-aware-flat-list/use-text-input-offset.native.js.map +1 -0
  33. package/build/mobile/segmented-control/index.native.js +4 -2
  34. package/build/mobile/segmented-control/index.native.js.map +1 -1
  35. package/build/navigator/navigator-provider/component.js +4 -2
  36. package/build/navigator/navigator-provider/component.js.map +1 -1
  37. package/build/navigator/navigator-screen/component.js +4 -3
  38. package/build/navigator/navigator-screen/component.js.map +1 -1
  39. package/build/popover/index.js +1 -8
  40. package/build/popover/index.js.map +1 -1
  41. package/build/private-apis.js +4 -1
  42. package/build/private-apis.js.map +1 -1
  43. package/build/query-controls/author-select.js +2 -1
  44. package/build/query-controls/author-select.js.map +1 -1
  45. package/build/query-controls/category-select.js +3 -1
  46. package/build/query-controls/category-select.js.map +1 -1
  47. package/build/query-controls/index.js +7 -1
  48. package/build/query-controls/index.js.map +1 -1
  49. package/build/sandbox/index.native.js +55 -29
  50. package/build/sandbox/index.native.js.map +1 -1
  51. package/build/slot-fill/index.js +20 -7
  52. package/build/slot-fill/index.js.map +1 -1
  53. package/build/spinner/styles.js +4 -4
  54. package/build/spinner/styles.js.map +1 -1
  55. package/build/tree-grid/index.js +3 -3
  56. package/build/tree-grid/index.js.map +1 -1
  57. package/build/view/component.js +1 -2
  58. package/build/view/component.js.map +1 -1
  59. package/build-module/checkbox-control/index.js +2 -2
  60. package/build-module/checkbox-control/index.js.map +1 -1
  61. package/build-module/color-palette/index.native.js +13 -1
  62. package/build-module/color-palette/index.native.js.map +1 -1
  63. package/build-module/custom-gradient-picker/index.native.js +3 -1
  64. package/build-module/custom-gradient-picker/index.native.js.map +1 -1
  65. package/build-module/custom-gradient-picker/serializer.js +0 -4
  66. package/build-module/custom-gradient-picker/serializer.js.map +1 -1
  67. package/build-module/draggable/index.js +6 -1
  68. package/build-module/draggable/index.js.map +1 -1
  69. package/build-module/drop-zone/index.js +8 -8
  70. package/build-module/drop-zone/index.js.map +1 -1
  71. package/build-module/index.js.map +1 -1
  72. package/build-module/mobile/bottom-sheet/cell.native.js +6 -5
  73. package/build-module/mobile/bottom-sheet/cell.native.js.map +1 -1
  74. package/build-module/mobile/color-settings/palette.screen.native.js +0 -8
  75. package/build-module/mobile/color-settings/palette.screen.native.js.map +1 -1
  76. package/build-module/mobile/global-styles-context/utils.native.js +21 -3
  77. package/build-module/mobile/global-styles-context/utils.native.js.map +1 -1
  78. package/build-module/mobile/keyboard-aware-flat-list/index.android.js +0 -4
  79. package/build-module/mobile/keyboard-aware-flat-list/index.android.js.map +1 -1
  80. package/build-module/mobile/keyboard-aware-flat-list/index.ios.js +97 -54
  81. package/build-module/mobile/keyboard-aware-flat-list/index.ios.js.map +1 -1
  82. package/build-module/mobile/keyboard-aware-flat-list/use-keyboard-offset.native.js +73 -0
  83. package/build-module/mobile/keyboard-aware-flat-list/use-keyboard-offset.native.js.map +1 -0
  84. package/build-module/mobile/keyboard-aware-flat-list/use-scroll-to-text-input.native.js +76 -0
  85. package/build-module/mobile/keyboard-aware-flat-list/use-scroll-to-text-input.native.js.map +1 -0
  86. package/build-module/mobile/keyboard-aware-flat-list/use-text-input-caret-position.native.js +33 -0
  87. package/build-module/mobile/keyboard-aware-flat-list/use-text-input-caret-position.native.js.map +1 -0
  88. package/build-module/mobile/keyboard-aware-flat-list/use-text-input-offset.native.js +40 -0
  89. package/build-module/mobile/keyboard-aware-flat-list/use-text-input-offset.native.js.map +1 -0
  90. package/build-module/mobile/segmented-control/index.native.js +4 -2
  91. package/build-module/mobile/segmented-control/index.native.js.map +1 -1
  92. package/build-module/navigator/navigator-provider/component.js +4 -2
  93. package/build-module/navigator/navigator-provider/component.js.map +1 -1
  94. package/build-module/navigator/navigator-screen/component.js +4 -3
  95. package/build-module/navigator/navigator-screen/component.js.map +1 -1
  96. package/build-module/popover/index.js +1 -8
  97. package/build-module/popover/index.js.map +1 -1
  98. package/build-module/private-apis.js +3 -1
  99. package/build-module/private-apis.js.map +1 -1
  100. package/build-module/query-controls/author-select.js +2 -1
  101. package/build-module/query-controls/author-select.js.map +1 -1
  102. package/build-module/query-controls/category-select.js +3 -1
  103. package/build-module/query-controls/category-select.js.map +1 -1
  104. package/build-module/query-controls/index.js +7 -2
  105. package/build-module/query-controls/index.js.map +1 -1
  106. package/build-module/sandbox/index.native.js +56 -31
  107. package/build-module/sandbox/index.native.js.map +1 -1
  108. package/build-module/slot-fill/index.js +16 -6
  109. package/build-module/slot-fill/index.js.map +1 -1
  110. package/build-module/spinner/styles.js +4 -4
  111. package/build-module/spinner/styles.js.map +1 -1
  112. package/build-module/tree-grid/index.js +3 -3
  113. package/build-module/tree-grid/index.js.map +1 -1
  114. package/build-module/view/component.js +1 -2
  115. package/build-module/view/component.js.map +1 -1
  116. package/build-style/style-rtl.css +26 -16
  117. package/build-style/style.css +26 -16
  118. package/build-types/angle-picker-control/styles/angle-picker-control-styles.d.ts +1 -1
  119. package/build-types/border-box-control/border-box-control/hook.d.ts +2 -2
  120. package/build-types/border-box-control/border-box-control-linked-button/hook.d.ts +2 -2
  121. package/build-types/border-box-control/border-box-control-split-controls/hook.d.ts +2 -2
  122. package/build-types/border-box-control/border-box-control-visualizer/hook.d.ts +2 -2
  123. package/build-types/border-control/border-control/hook.d.ts +2 -2
  124. package/build-types/border-control/border-control-dropdown/hook.d.ts +2 -2
  125. package/build-types/border-control/border-control-style-picker/hook.d.ts +2 -2
  126. package/build-types/box-control/styles/box-control-styles.d.ts +5 -5
  127. package/build-types/button/deprecated.d.ts +8 -8
  128. package/build-types/card/card/hook.d.ts +2 -2
  129. package/build-types/card/card-body/hook.d.ts +2 -2
  130. package/build-types/card/card-divider/hook.d.ts +2 -2
  131. package/build-types/card/card-footer/hook.d.ts +2 -2
  132. package/build-types/card/card-header/hook.d.ts +2 -2
  133. package/build-types/card/card-media/hook.d.ts +2 -2
  134. package/build-types/checkbox-control/index.d.ts.map +1 -1
  135. package/build-types/color-palette/styles.d.ts +1 -1
  136. package/build-types/color-picker/styles.d.ts +7 -7
  137. package/build-types/combobox-control/styles.d.ts +1 -1
  138. package/build-types/custom-gradient-picker/serializer.d.ts +1 -5
  139. package/build-types/custom-gradient-picker/serializer.d.ts.map +1 -1
  140. package/build-types/custom-gradient-picker/types.d.ts +0 -2
  141. package/build-types/custom-gradient-picker/types.d.ts.map +1 -1
  142. package/build-types/date-time/date/styles.d.ts +2 -2
  143. package/build-types/date-time/date-time/styles.d.ts +1 -1
  144. package/build-types/date-time/time/styles.d.ts +8 -8
  145. package/build-types/draggable/index.d.ts +1 -1
  146. package/build-types/draggable/index.d.ts.map +1 -1
  147. package/build-types/draggable/stories/index.d.ts +8 -0
  148. package/build-types/draggable/stories/index.d.ts.map +1 -1
  149. package/build-types/draggable/types.d.ts +7 -0
  150. package/build-types/draggable/types.d.ts.map +1 -1
  151. package/build-types/drop-zone/index.d.ts.map +1 -1
  152. package/build-types/elevation/hook.d.ts +2 -2
  153. package/build-types/external-link/styles/external-link-styles.d.ts +1 -1
  154. package/build-types/flex/flex/hook.d.ts +2 -2
  155. package/build-types/flex/flex-block/hook.d.ts +2 -2
  156. package/build-types/flex/flex-item/hook.d.ts +2 -2
  157. package/build-types/focal-point-picker/styles/focal-point-picker-style.d.ts +2 -2
  158. package/build-types/form-token-field/styles.d.ts +1 -1
  159. package/build-types/grid/hook.d.ts +2 -2
  160. package/build-types/h-stack/hook.d.ts +2 -2
  161. package/build-types/heading/hook.d.ts +2 -2
  162. package/build-types/index.d.ts +129 -0
  163. package/build-types/index.d.ts.map +1 -0
  164. package/build-types/input-control/styles/input-control-styles.d.ts +2 -2
  165. package/build-types/item-group/item/hook.d.ts +2 -2
  166. package/build-types/item-group/item-group/hook.d.ts +2 -2
  167. package/build-types/navigation/styles/navigation-styles.d.ts +2 -2
  168. package/build-types/navigator/navigator-back-button/component.d.ts +1 -1
  169. package/build-types/navigator/navigator-back-button/hook.d.ts +3 -3
  170. package/build-types/navigator/navigator-button/component.d.ts +1 -1
  171. package/build-types/navigator/navigator-button/hook.d.ts +3 -3
  172. package/build-types/navigator/navigator-provider/component.d.ts.map +1 -1
  173. package/build-types/navigator/navigator-screen/component.d.ts +1 -1
  174. package/build-types/navigator/navigator-screen/component.d.ts.map +1 -1
  175. package/build-types/navigator/navigator-to-parent-button/component.d.ts +1 -1
  176. package/build-types/navigator/stories/index.d.ts +1 -0
  177. package/build-types/navigator/stories/index.d.ts.map +1 -1
  178. package/build-types/navigator/types.d.ts +2 -2
  179. package/build-types/navigator/types.d.ts.map +1 -1
  180. package/build-types/number-control/index.d.ts +2 -2
  181. package/build-types/number-control/stories/index.d.ts +2 -2
  182. package/build-types/palette-edit/styles.d.ts +18 -12
  183. package/build-types/palette-edit/styles.d.ts.map +1 -1
  184. package/build-types/popover/index.d.ts +1 -1
  185. package/build-types/popover/index.d.ts.map +1 -1
  186. package/build-types/popover/stories/e2e/index.d.ts +1 -1
  187. package/build-types/private-apis.d.ts +2 -3
  188. package/build-types/private-apis.d.ts.map +1 -1
  189. package/build-types/query-controls/author-select.d.ts.map +1 -1
  190. package/build-types/query-controls/category-select.d.ts.map +1 -1
  191. package/build-types/query-controls/index.d.ts.map +1 -1
  192. package/build-types/range-control/index.d.ts +1 -1
  193. package/build-types/range-control/styles/range-control-styles.d.ts +2 -2
  194. package/build-types/resizable-box/index.d.ts +1 -1
  195. package/build-types/resizable-box/resize-tooltip/index.d.ts +1 -1
  196. package/build-types/resizable-box/stories/index.d.ts +2 -2
  197. package/build-types/scrollable/hook.d.ts +2 -2
  198. package/build-types/search-control/index.d.ts +1 -1
  199. package/build-types/search-control/stories/index.d.ts +2 -2
  200. package/build-types/slot-fill/index.d.ts +13 -1
  201. package/build-types/slot-fill/index.d.ts.map +1 -1
  202. package/build-types/spacer/hook.d.ts +2 -2
  203. package/build-types/spinner/index.d.ts +1 -1
  204. package/build-types/spinner/styles.d.ts.map +1 -1
  205. package/build-types/surface/hook.d.ts +2 -2
  206. package/build-types/text/hook.d.ts +2 -2
  207. package/build-types/text-control/index.d.ts +1 -1
  208. package/build-types/toolbar/toolbar-button/index.d.ts +8 -8
  209. package/build-types/tools-panel/tools-panel/hook.d.ts +2 -2
  210. package/build-types/tools-panel/tools-panel-header/hook.d.ts +2 -2
  211. package/build-types/tools-panel/tools-panel-item/hook.d.ts +2 -2
  212. package/build-types/tree-grid/index.d.ts.map +1 -1
  213. package/build-types/truncate/hook.d.ts +2 -2
  214. package/build-types/ui/context/wordpress-component.d.ts +1 -1
  215. package/build-types/ui/context/wordpress-component.d.ts.map +1 -1
  216. package/build-types/ui/control-group/hook.d.ts +2 -2
  217. package/build-types/ui/control-label/hook.d.ts +2 -2
  218. package/build-types/ui/form-group/form-group.d.ts +2 -2
  219. package/build-types/ui/form-group/use-form-group.d.ts +2 -2
  220. package/build-types/unit-control/index.d.ts +1 -1
  221. package/build-types/unit-control/styles/unit-control-styles.d.ts +2 -2
  222. package/build-types/v-stack/hook.d.ts +2 -2
  223. package/build-types/view/component.d.ts +1 -1
  224. package/build-types/view/component.d.ts.map +1 -1
  225. package/package.json +22 -22
  226. package/src/autocomplete/README.md +4 -2
  227. package/src/checkbox-control/index.tsx +6 -2
  228. package/src/color-palette/index.native.js +20 -1
  229. package/src/color-picker/test/index.tsx +99 -99
  230. package/src/custom-gradient-picker/index.native.js +1 -1
  231. package/src/custom-gradient-picker/serializer.ts +2 -6
  232. package/src/custom-gradient-picker/types.ts +0 -18
  233. package/src/dimension-control/README.md +1 -1
  234. package/src/draggable/README.md +8 -1
  235. package/src/draggable/index.tsx +6 -1
  236. package/src/draggable/stories/index.tsx +69 -33
  237. package/src/draggable/types.ts +7 -0
  238. package/src/drop-zone/index.tsx +12 -8
  239. package/src/drop-zone/style.scss +1 -1
  240. package/src/{index.js → index.ts} +1 -0
  241. package/src/mobile/bottom-sheet/cell.native.js +4 -5
  242. package/src/mobile/color-settings/palette.screen.native.js +0 -7
  243. package/src/mobile/global-styles-context/utils.native.js +18 -3
  244. package/src/mobile/keyboard-aware-flat-list/index.android.js +0 -4
  245. package/src/mobile/keyboard-aware-flat-list/index.ios.js +118 -67
  246. package/src/mobile/keyboard-aware-flat-list/test/use-keyboard-offset.native.js +203 -0
  247. package/src/mobile/keyboard-aware-flat-list/test/use-scroll-to-text-input.native.js +140 -0
  248. package/src/mobile/keyboard-aware-flat-list/test/use-text-input-caret-position.native.js +82 -0
  249. package/src/mobile/keyboard-aware-flat-list/test/use-text-input-offset.native.js +147 -0
  250. package/src/mobile/keyboard-aware-flat-list/use-keyboard-offset.native.js +87 -0
  251. package/src/mobile/keyboard-aware-flat-list/use-scroll-to-text-input.native.js +105 -0
  252. package/src/mobile/keyboard-aware-flat-list/use-text-input-caret-position.native.js +36 -0
  253. package/src/mobile/keyboard-aware-flat-list/use-text-input-offset.native.js +54 -0
  254. package/src/mobile/segmented-control/index.native.js +2 -2
  255. package/src/modal/style.scss +20 -12
  256. package/src/navigator/navigator-provider/component.tsx +2 -0
  257. package/src/navigator/navigator-screen/component.tsx +5 -2
  258. package/src/navigator/stories/index.tsx +68 -0
  259. package/src/navigator/test/index.tsx +52 -0
  260. package/src/navigator/types.ts +2 -1
  261. package/src/popover/index.tsx +2 -15
  262. package/src/{private-apis.js → private-apis.ts} +2 -0
  263. package/src/query-controls/author-select.tsx +1 -0
  264. package/src/query-controls/category-select.tsx +1 -0
  265. package/src/query-controls/index.tsx +4 -2
  266. package/src/sandbox/index.native.js +78 -37
  267. package/src/slot-fill/index.js +14 -6
  268. package/src/snackbar/style.scss +2 -1
  269. package/src/spinner/styles.ts +2 -0
  270. package/src/tree-grid/index.tsx +7 -2
  271. package/src/ui/context/wordpress-component.ts +1 -1
  272. package/src/view/component.tsx +2 -2
  273. package/tsconfig.json +3 -4
  274. package/tsconfig.tsbuildinfo +1 -1
@@ -78,22 +78,15 @@ const PaletteScreen = () => {
78
78
  setCurrentValue( color );
79
79
  if ( isSolidSegment && onColorChange && onGradientChange ) {
80
80
  onColorChange( color );
81
- onGradientChange( '' );
82
81
  } else if ( isSolidSegment && onColorChange ) {
83
82
  onColorChange( color );
84
83
  } else if ( ! isSolidSegment && onGradientChange ) {
85
84
  onGradientChange( color );
86
- onColorChange( '' );
87
85
  }
88
86
  };
89
87
 
90
88
  function onClear() {
91
89
  setCurrentValue( undefined );
92
- if ( isSolidSegment ) {
93
- onColorChange( '' );
94
- } else {
95
- onGradientChange( '' );
96
- }
97
90
 
98
91
  if ( onColorCleared ) {
99
92
  onColorCleared();
@@ -2,7 +2,6 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { camelCase } from 'change-case';
5
- import { get } from 'lodash';
6
5
  import { Dimensions } from 'react-native';
7
6
 
8
7
  /**
@@ -191,6 +190,22 @@ export function getBlockTypography(
191
190
  return typographyStyles;
192
191
  }
193
192
 
193
+ /**
194
+ * Return a value from a certain path of the object.
195
+ * Path is specified as an array of properties, like: [ 'parent', 'child' ].
196
+ *
197
+ * @param {Object} object Input object.
198
+ * @param {Array} path Path to the object property.
199
+ * @return {*} Value of the object property at the specified path.
200
+ */
201
+ const getValueFromObjectPath = ( object, path ) => {
202
+ let value = object;
203
+ path.forEach( ( fieldName ) => {
204
+ value = value?.[ fieldName ];
205
+ } );
206
+ return value;
207
+ };
208
+
194
209
  export function parseStylesVariables( styles, mappedValues, customValues ) {
195
210
  let stylesBase = styles;
196
211
  const variables = [ 'preset', 'custom', 'var', 'fontSize' ];
@@ -231,11 +246,11 @@ export function parseStylesVariables( styles, mappedValues, customValues ) {
231
246
  customValuesData
232
247
  )
233
248
  ) {
234
- return get( customValuesData, path );
249
+ return getValueFromObjectPath( customValuesData, path );
235
250
  }
236
251
 
237
252
  // Check for camelcase properties.
238
- return get( customValuesData, [
253
+ return getValueFromObjectPath( customValuesData, [
239
254
  ...path.slice( 0, path.length - 1 ),
240
255
  camelCase( path[ path.length - 1 ] ),
241
256
  ] );
@@ -24,8 +24,4 @@ export const KeyboardAwareFlatList = ( { innerRef, onScroll, ...props } ) => {
24
24
  );
25
25
  };
26
26
 
27
- KeyboardAwareFlatList.handleCaretVerticalPositionChange = () => {
28
- // no need to handle on Android, it is system managed
29
- };
30
-
31
27
  export default KeyboardAwareFlatList;
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
5
- import { FlatList } from 'react-native';
6
- import fastDeepEqual from 'fast-deep-equal/es6';
4
+
5
+ import { ScrollView, FlatList, useWindowDimensions } from 'react-native';
7
6
  import Animated, {
8
7
  useAnimatedScrollHandler,
9
8
  useSharedValue,
@@ -12,36 +11,123 @@ import Animated, {
12
11
  /**
13
12
  * WordPress dependencies
14
13
  */
15
- import { memo, useCallback, useRef } from '@wordpress/element';
14
+ import { useCallback, useEffect, useRef } from '@wordpress/element';
15
+ import { useThrottle } from '@wordpress/compose';
16
16
 
17
- const List = memo( FlatList, fastDeepEqual );
18
- const AnimatedKeyboardAwareScrollView = Animated.createAnimatedComponent(
19
- KeyboardAwareScrollView
20
- );
17
+ /**
18
+ * Internal dependencies
19
+ */
20
+ import useTextInputOffset from './use-text-input-offset';
21
+ import useKeyboardOffset from './use-keyboard-offset';
22
+ import useScrollToTextInput from './use-scroll-to-text-input';
23
+ import useTextInputCaretPosition from './use-text-input-caret-position';
21
24
 
25
+ const AnimatedScrollView = Animated.createAnimatedComponent( ScrollView );
26
+
27
+ /**
28
+ * React component that provides a FlatList that is aware of the keyboard state and can scroll
29
+ * to the currently focused TextInput.
30
+ *
31
+ * @param {Object} props Component props.
32
+ * @param {number} props.extraScrollHeight Extra scroll height for the content.
33
+ * @param {Function} props.innerRef Function to pass the ScrollView ref to the parent component.
34
+ * @param {Function} props.onScroll Function to be called when the list is scrolled.
35
+ * @param {boolean} props.scrollEnabled Whether the list can be scrolled.
36
+ * @param {Object} props.scrollViewStyle Additional style for the ScrollView component.
37
+ * @param {boolean} props.shouldPreventAutomaticScroll Whether to prevent scrolling when there's a Keyboard offset set.
38
+ * @param {Object} props... Other props to pass to the FlatList component.
39
+ * @return {WPComponent} KeyboardAwareFlatList component.
40
+ */
22
41
  export const KeyboardAwareFlatList = ( {
23
42
  extraScrollHeight,
24
- shouldPreventAutomaticScroll,
25
43
  innerRef,
26
- autoScroll,
27
- scrollViewStyle,
28
- inputAccessoryViewHeight,
29
44
  onScroll,
30
- ...listProps
45
+ scrollEnabled,
46
+ scrollViewStyle,
47
+ shouldPreventAutomaticScroll,
48
+ ...props
31
49
  } ) => {
32
50
  const scrollViewRef = useRef();
33
- const keyboardWillShowIndicator = useRef();
51
+ const scrollViewMeasurements = useRef();
52
+ const scrollViewYOffset = useSharedValue( -1 );
53
+
54
+ const { height: windowHeight, width: windowWidth } = useWindowDimensions();
55
+ const isLandscape = windowWidth >= windowHeight;
56
+
57
+ const [ keyboardOffset ] = useKeyboardOffset(
58
+ scrollEnabled,
59
+ shouldPreventAutomaticScroll
60
+ );
61
+
62
+ const [ currentCaretData ] = useTextInputCaretPosition( scrollEnabled );
63
+
64
+ const [ getTextInputOffset ] = useTextInputOffset(
65
+ scrollEnabled,
66
+ scrollViewRef
67
+ );
68
+
69
+ const [ scrollToTextInputOffset ] = useScrollToTextInput(
70
+ extraScrollHeight,
71
+ keyboardOffset,
72
+ scrollEnabled,
73
+ scrollViewMeasurements,
74
+ scrollViewRef,
75
+ scrollViewYOffset
76
+ );
34
77
 
35
- const latestContentOffsetY = useSharedValue( -1 );
78
+ const onScrollToTextInput = useThrottle(
79
+ useCallback(
80
+ async ( caret ) => {
81
+ const textInputOffset = await getTextInputOffset( caret );
82
+ const hasTextInputOffset = textInputOffset !== null;
83
+
84
+ if ( hasTextInputOffset ) {
85
+ scrollToTextInputOffset( caret, textInputOffset );
86
+ }
87
+ },
88
+ [ getTextInputOffset, scrollToTextInputOffset ]
89
+ ),
90
+ 200,
91
+ { leading: false }
92
+ );
93
+
94
+ useEffect( () => {
95
+ onScrollToTextInput( currentCaretData );
96
+ }, [ currentCaretData, onScrollToTextInput ] );
97
+
98
+ // When the orientation changes, the ScrollView measurements
99
+ // need to be re-calculated.
100
+ useEffect( () => {
101
+ scrollViewMeasurements.current = null;
102
+ }, [ isLandscape ] );
36
103
 
37
104
  const scrollHandler = useAnimatedScrollHandler( {
38
105
  onScroll: ( event ) => {
39
106
  const { contentOffset } = event;
40
- latestContentOffsetY.value = contentOffset.y;
107
+ scrollViewYOffset.value = contentOffset.y;
41
108
  onScroll( event );
42
109
  },
43
110
  } );
44
111
 
112
+ const measureScrollView = useCallback( () => {
113
+ if ( scrollViewRef.current ) {
114
+ const scrollRef = scrollViewRef.current.getNativeScrollRef();
115
+
116
+ scrollRef.measureInWindow( ( _x, y, width, height ) => {
117
+ scrollViewMeasurements.current = { y, width, height };
118
+ } );
119
+ }
120
+ }, [] );
121
+
122
+ const onContentSizeChange = useCallback( () => {
123
+ onScrollToTextInput( currentCaretData );
124
+
125
+ // Sets the first values when the content size changes.
126
+ if ( ! scrollViewMeasurements.current ) {
127
+ measureScrollView();
128
+ }
129
+ }, [ measureScrollView, onScrollToTextInput, currentCaretData ] );
130
+
45
131
  const getRef = useCallback(
46
132
  ( ref ) => {
47
133
  scrollViewRef.current = ref;
@@ -49,63 +135,28 @@ export const KeyboardAwareFlatList = ( {
49
135
  },
50
136
  [ innerRef ]
51
137
  );
52
- const onKeyboardWillHide = useCallback( () => {
53
- keyboardWillShowIndicator.current = false;
54
- }, [] );
55
- const onKeyboardDidHide = useCallback( () => {
56
- setTimeout( () => {
57
- if (
58
- ! keyboardWillShowIndicator.current &&
59
- latestContentOffsetY.value !== -1 &&
60
- ! shouldPreventAutomaticScroll()
61
- ) {
62
- // Reset the content position if keyboard is still closed.
63
- scrollViewRef.current?.scrollToPosition(
64
- 0,
65
- latestContentOffsetY.value,
66
- true
67
- );
68
- }
69
- }, 50 );
70
- }, [ latestContentOffsetY, shouldPreventAutomaticScroll ] );
71
- const onKeyboardWillShow = useCallback( () => {
72
- keyboardWillShowIndicator.current = true;
73
- }, [] );
138
+
139
+ // Adds content insets when the keyboard is opened to have
140
+ // extra padding at the bottom.
141
+ const contentInset = { bottom: keyboardOffset };
142
+
143
+ const style = [ { flex: 1 }, scrollViewStyle ];
74
144
 
75
145
  return (
76
- <AnimatedKeyboardAwareScrollView
77
- style={ [ { flex: 1 }, scrollViewStyle ] }
78
- keyboardDismissMode="none"
79
- enableResetScrollToCoords={ false }
146
+ <AnimatedScrollView
147
+ automaticallyAdjustContentInsets={ false }
148
+ contentInset={ contentInset }
80
149
  keyboardShouldPersistTaps="handled"
81
- extraScrollHeight={ extraScrollHeight }
82
- extraHeight={ 0 }
83
- inputAccessoryViewHeight={ inputAccessoryViewHeight }
84
- enableAutomaticScroll={
85
- autoScroll === undefined ? false : autoScroll
86
- }
87
- ref={ getRef }
88
- onKeyboardWillHide={ onKeyboardWillHide }
89
- onKeyboardDidHide={ onKeyboardDidHide }
90
- onKeyboardWillShow={ onKeyboardWillShow }
91
- scrollEnabled={ listProps.scrollEnabled }
150
+ onContentSizeChange={ onContentSizeChange }
92
151
  onScroll={ scrollHandler }
152
+ ref={ getRef }
153
+ scrollEnabled={ scrollEnabled }
154
+ scrollEventThrottle={ 16 }
155
+ style={ style }
93
156
  >
94
- <List { ...listProps } />
95
- </AnimatedKeyboardAwareScrollView>
157
+ <FlatList { ...props } />
158
+ </AnimatedScrollView>
96
159
  );
97
160
  };
98
161
 
99
- KeyboardAwareFlatList.handleCaretVerticalPositionChange = (
100
- scrollView,
101
- targetId,
102
- caretY,
103
- previousCaretY
104
- ) => {
105
- if ( previousCaretY ) {
106
- // If this is not the first tap.
107
- scrollView.refreshScrollForField( targetId );
108
- }
109
- };
110
-
111
162
  export default KeyboardAwareFlatList;
@@ -0,0 +1,203 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { act, renderHook } from '@testing-library/react-native';
5
+ import { Keyboard } from 'react-native';
6
+ import RCTDeviceEventEmitter from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import useKeyboardOffset from '../use-keyboard-offset';
12
+
13
+ jest.useFakeTimers();
14
+
15
+ describe( 'useKeyboardOffset', () => {
16
+ beforeEach( () => {
17
+ Keyboard.removeAllListeners( 'keyboardDidShow' );
18
+ Keyboard.removeAllListeners( 'keyboardDidHide' );
19
+ Keyboard.removeAllListeners( 'keyboardWillShow' );
20
+ } );
21
+
22
+ it( 'returns the initial state', () => {
23
+ // Arrange
24
+ const { result } = renderHook( () => useKeyboardOffset( true ) );
25
+ const [ keyboardOffset ] = result.current;
26
+
27
+ // Assert
28
+ expect( keyboardOffset ).toBe( 0 );
29
+ } );
30
+
31
+ it( 'updates keyboard visibility and offset when the keyboard is shown', () => {
32
+ // Arrange
33
+ const { result } = renderHook( () => useKeyboardOffset( true ) );
34
+
35
+ // Act
36
+ act( () => {
37
+ RCTDeviceEventEmitter.emit( 'keyboardDidShow', {
38
+ endCoordinates: { height: 250 },
39
+ } );
40
+ } );
41
+
42
+ // Assert
43
+ const [ keyboardOffset ] = result.current;
44
+ expect( keyboardOffset ).toBe( 250 );
45
+ } );
46
+
47
+ it( 'updates keyboard visibility and offset when the keyboard is hidden', () => {
48
+ // Arrange
49
+ const shouldPreventAutomaticScroll = jest.fn().mockReturnValue( false );
50
+ const { result } = renderHook( () =>
51
+ useKeyboardOffset( true, shouldPreventAutomaticScroll )
52
+ );
53
+
54
+ // Act
55
+ act( () => {
56
+ RCTDeviceEventEmitter.emit( 'keyboardDidShow', {
57
+ endCoordinates: { height: 250 },
58
+ } );
59
+ } );
60
+
61
+ act( () => {
62
+ RCTDeviceEventEmitter.emit( 'keyboardDidHide' );
63
+ jest.runAllTimers();
64
+ } );
65
+
66
+ // Assert
67
+ const [ keyboardOffset ] = result.current;
68
+ expect( keyboardOffset ).toBe( 0 );
69
+ } );
70
+
71
+ it( 'removes all keyboard listeners when scrollEnabled changes to false', () => {
72
+ // Arrange
73
+ const { result, rerender } = renderHook(
74
+ ( { scrollEnabled } ) => useKeyboardOffset( scrollEnabled ),
75
+ {
76
+ initialProps: { scrollEnabled: true },
77
+ }
78
+ );
79
+ const [ keyboardOffset ] = result.current;
80
+
81
+ // Act
82
+ rerender( { scrollEnabled: false } );
83
+
84
+ // Assert
85
+ expect( keyboardOffset ).toBe( 0 );
86
+ expect( RCTDeviceEventEmitter.listenerCount( 'keyboardDidHide' ) ).toBe(
87
+ 0
88
+ );
89
+ expect( RCTDeviceEventEmitter.listenerCount( 'keyboardDidShow' ) ).toBe(
90
+ 0
91
+ );
92
+ } );
93
+
94
+ it( 'adds all keyboard listeners when scrollEnabled changes to true', () => {
95
+ // Arrange
96
+ const { result, rerender } = renderHook(
97
+ ( { scrollEnabled } ) => useKeyboardOffset( scrollEnabled ),
98
+ {
99
+ initialProps: { scrollEnabled: false },
100
+ }
101
+ );
102
+ // Act
103
+ act( () => {
104
+ rerender( { scrollEnabled: true } );
105
+ } );
106
+
107
+ act( () => {
108
+ RCTDeviceEventEmitter.emit( 'keyboardDidShow', {
109
+ endCoordinates: { height: 250 },
110
+ } );
111
+ } );
112
+
113
+ const [ keyboardOffset ] = result.current;
114
+
115
+ // Assert
116
+ expect( keyboardOffset ).toBe( 250 );
117
+ expect( RCTDeviceEventEmitter.listenerCount( 'keyboardDidShow' ) ).toBe(
118
+ 1
119
+ );
120
+ expect( RCTDeviceEventEmitter.listenerCount( 'keyboardDidHide' ) ).toBe(
121
+ 1
122
+ );
123
+ } );
124
+
125
+ it( 'does not set keyboard offset to 0 when keyboard is hidden and shouldPreventAutomaticScroll is true', () => {
126
+ // Arrange
127
+ const shouldPreventAutomaticScroll = jest.fn().mockReturnValue( true );
128
+ const { result } = renderHook( () =>
129
+ useKeyboardOffset( true, shouldPreventAutomaticScroll )
130
+ );
131
+
132
+ // Act
133
+ act( () => {
134
+ RCTDeviceEventEmitter.emit( 'keyboardDidShow', {
135
+ endCoordinates: { height: 250 },
136
+ } );
137
+ } );
138
+ act( () => {
139
+ RCTDeviceEventEmitter.emit( 'keyboardDidHide' );
140
+ jest.runAllTimers();
141
+ } );
142
+
143
+ // Assert
144
+ expect( result.current[ 0 ] ).toBe( 250 );
145
+ } );
146
+
147
+ it( 'handles updates to shouldPreventAutomaticScroll', () => {
148
+ // Arrange
149
+ const preventScrollTrue = jest.fn( () => true );
150
+ const preventScrollFalse = jest.fn( () => false );
151
+
152
+ // Act
153
+ const { result, rerender } = renderHook(
154
+ ( { shouldPreventAutomaticScroll } ) =>
155
+ useKeyboardOffset( true, shouldPreventAutomaticScroll ),
156
+ {
157
+ initialProps: {
158
+ shouldPreventAutomaticScroll: preventScrollFalse,
159
+ },
160
+ }
161
+ );
162
+
163
+ // Assert
164
+ expect( result.current[ 0 ] ).toBe( 0 );
165
+
166
+ // Act
167
+ act( () => {
168
+ RCTDeviceEventEmitter.emit( 'keyboardDidShow', {
169
+ endCoordinates: { height: 150 },
170
+ } );
171
+ } );
172
+
173
+ // Assert
174
+ expect( result.current[ 0 ] ).toBe( 150 );
175
+
176
+ // Act
177
+ act( () => {
178
+ rerender( { shouldPreventAutomaticScroll: preventScrollTrue } );
179
+ } );
180
+
181
+ act( () => {
182
+ RCTDeviceEventEmitter.emit( 'keyboardDidHide' );
183
+ jest.runAllTimers();
184
+ } );
185
+
186
+ // Assert
187
+ expect( result.current[ 0 ] ).toBe( 150 );
188
+
189
+ // Act
190
+ act( () => {
191
+ rerender( { shouldPreventAutomaticScroll: preventScrollFalse } );
192
+ } );
193
+
194
+ act( () => {
195
+ RCTDeviceEventEmitter.emit( 'keyboardDidShow', {
196
+ endCoordinates: { height: 250 },
197
+ } );
198
+ } );
199
+
200
+ // Assert
201
+ expect( result.current[ 0 ] ).toBe( 250 );
202
+ } );
203
+ } );
@@ -0,0 +1,140 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+
5
+ import { renderHook } from '@testing-library/react-native';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import useScrollToTextInput from '../use-scroll-to-text-input';
11
+
12
+ describe( 'useScrollToTextInput', () => {
13
+ it( 'scrolls up to the current TextInput offset', () => {
14
+ // Arrange
15
+ const currentCaretData = { caretHeight: 10 };
16
+ const extraScrollHeight = 50;
17
+ const keyboardOffset = 100;
18
+ const scrollEnabled = true;
19
+ const scrollViewRef = { current: { scrollTo: jest.fn() } };
20
+ const scrollViewMeasurements = { current: { height: 600 } };
21
+ const scrollViewYOffset = { value: 150 };
22
+ const textInputOffset = 50;
23
+
24
+ const { result } = renderHook( () =>
25
+ useScrollToTextInput(
26
+ extraScrollHeight,
27
+ keyboardOffset,
28
+ scrollEnabled,
29
+ scrollViewMeasurements,
30
+ scrollViewRef,
31
+ scrollViewYOffset
32
+ )
33
+ );
34
+
35
+ // Act
36
+ result.current[ 0 ]( currentCaretData, textInputOffset );
37
+
38
+ // Assert
39
+ expect( scrollViewRef.current.scrollTo ).toHaveBeenCalledWith( {
40
+ y: textInputOffset,
41
+ animated: true,
42
+ } );
43
+ } );
44
+
45
+ it( 'scrolls down to the current TextInput offset', () => {
46
+ // Arrange
47
+ const currentCaretData = { caretHeight: 10 };
48
+ const extraScrollHeight = 50;
49
+ const keyboardOffset = 100;
50
+ const scrollEnabled = true;
51
+ const scrollViewRef = { current: { scrollTo: jest.fn() } };
52
+ const scrollViewMeasurements = { current: { height: 600 } };
53
+ const scrollViewYOffset = { value: 250 };
54
+ const textInputOffset = 750;
55
+
56
+ const { result } = renderHook( () =>
57
+ useScrollToTextInput(
58
+ extraScrollHeight,
59
+ keyboardOffset,
60
+ scrollEnabled,
61
+ scrollViewMeasurements,
62
+ scrollViewRef,
63
+ scrollViewYOffset
64
+ )
65
+ );
66
+
67
+ // Act
68
+ result.current[ 0 ]( currentCaretData, textInputOffset );
69
+
70
+ // Assert
71
+ const expectedYOffset =
72
+ textInputOffset -
73
+ ( scrollViewMeasurements.current.height -
74
+ ( keyboardOffset +
75
+ extraScrollHeight +
76
+ currentCaretData.caretHeight ) );
77
+ expect( scrollViewRef.current.scrollTo ).toHaveBeenCalledWith( {
78
+ y: expectedYOffset,
79
+ animated: true,
80
+ } );
81
+ } );
82
+
83
+ it( 'does not scroll when the ScrollView ref is not available', () => {
84
+ // Arrange
85
+ const currentCaretData = { caretHeight: 10 };
86
+ const extraScrollHeight = 50;
87
+ const keyboardOffset = 100;
88
+ const scrollEnabled = true;
89
+ const scrollViewRef = { current: null };
90
+ const scrollViewMeasurements = { current: { height: 600 } };
91
+ const scrollViewYOffset = { value: 0 };
92
+ const textInputOffset = 50;
93
+
94
+ const { result } = renderHook( () =>
95
+ useScrollToTextInput(
96
+ extraScrollHeight,
97
+ keyboardOffset,
98
+ scrollEnabled,
99
+ scrollViewMeasurements,
100
+ scrollViewRef,
101
+ scrollViewYOffset
102
+ )
103
+ );
104
+
105
+ // Act
106
+ result.current[ 0 ]( currentCaretData, textInputOffset );
107
+
108
+ // Assert
109
+ expect( scrollViewRef.current ).toBeNull();
110
+ } );
111
+
112
+ it( 'does not scroll when the scroll is not enabled', () => {
113
+ // Arrange
114
+ const currentCaretData = { caretHeight: 10 };
115
+ const extraScrollHeight = 50;
116
+ const keyboardOffset = 100;
117
+ const scrollEnabled = false;
118
+ const scrollViewRef = { current: { scrollTo: jest.fn() } };
119
+ const scrollViewMeasurements = { current: { height: 600 } };
120
+ const scrollViewYOffset = { value: 0 };
121
+ const textInputOffset = 50;
122
+
123
+ const { result } = renderHook( () =>
124
+ useScrollToTextInput(
125
+ extraScrollHeight,
126
+ keyboardOffset,
127
+ scrollEnabled,
128
+ scrollViewMeasurements,
129
+ scrollViewRef,
130
+ scrollViewYOffset
131
+ )
132
+ );
133
+
134
+ // Act
135
+ result.current[ 0 ]( currentCaretData, textInputOffset );
136
+
137
+ // Assert
138
+ expect( scrollViewRef.current.scrollTo ).not.toHaveBeenCalled();
139
+ } );
140
+ } );