@wordpress/block-editor 9.0.0 → 9.1.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 (235) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +1 -0
  3. package/build/components/block-alignment-matrix-control/index.js +1 -6
  4. package/build/components/block-alignment-matrix-control/index.js.map +1 -1
  5. package/build/components/block-content-overlay/index.js +4 -82
  6. package/build/components/block-content-overlay/index.js.map +1 -1
  7. package/build/components/block-draggable/draggable-chip.native.js +64 -0
  8. package/build/components/block-draggable/draggable-chip.native.js.map +1 -0
  9. package/build/components/block-draggable/dropping-insertion-point.native.js +157 -0
  10. package/build/components/block-draggable/dropping-insertion-point.native.js.map +1 -0
  11. package/build/components/block-draggable/index.native.js +484 -0
  12. package/build/components/block-draggable/index.native.js.map +1 -0
  13. package/build/components/block-draggable/use-scroll-when-dragging.native.js +130 -0
  14. package/build/components/block-draggable/use-scroll-when-dragging.native.js.map +1 -0
  15. package/build/components/block-list/block-list-context.native.js +195 -0
  16. package/build/components/block-list/block-list-context.native.js.map +1 -0
  17. package/build/components/block-list/block-list-item-cell.native.js +67 -0
  18. package/build/components/block-list/block-list-item-cell.native.js.map +1 -0
  19. package/build/components/block-list/block-list-item.native.js +12 -9
  20. package/build/components/block-list/block-list-item.native.js.map +1 -1
  21. package/build/components/block-list/block.native.js +26 -5
  22. package/build/components/block-list/block.native.js.map +1 -1
  23. package/build/components/block-list/index.native.js +75 -23
  24. package/build/components/block-list/index.native.js.map +1 -1
  25. package/build/components/block-list/use-block-props/index.js +8 -4
  26. package/build/components/block-list/use-block-props/index.js.map +1 -1
  27. package/build/components/block-list/use-in-between-inserter.js +1 -1
  28. package/build/components/block-list/use-in-between-inserter.js.map +1 -1
  29. package/build/components/block-mobile-toolbar/index.native.js +9 -3
  30. package/build/components/block-mobile-toolbar/index.native.js.map +1 -1
  31. package/build/components/block-mover/index.native.js +17 -4
  32. package/build/components/block-mover/index.native.js.map +1 -1
  33. package/build/components/block-popover/inbetween.js +10 -2
  34. package/build/components/block-popover/inbetween.js.map +1 -1
  35. package/build/components/block-popover/index.js +4 -16
  36. package/build/components/block-popover/index.js.map +1 -1
  37. package/build/components/block-preview/index.js +1 -1
  38. package/build/components/block-preview/index.js.map +1 -1
  39. package/build/components/block-tools/selected-block-popover.js +1 -29
  40. package/build/components/block-tools/selected-block-popover.js.map +1 -1
  41. package/build/components/border-radius-control/input-controls.js +10 -3
  42. package/build/components/border-radius-control/input-controls.js.map +1 -1
  43. package/build/components/colors-gradients/dropdown.js +149 -44
  44. package/build/components/colors-gradients/dropdown.js.map +1 -1
  45. package/build/components/iframe/index.js +51 -50
  46. package/build/components/iframe/index.js.map +1 -1
  47. package/build/components/image-editor/use-save-image.js +3 -1
  48. package/build/components/image-editor/use-save-image.js.map +1 -1
  49. package/build/components/index.js +5 -14
  50. package/build/components/index.js.map +1 -1
  51. package/build/components/inserter/index.native.js +1 -1
  52. package/build/components/inserter/index.native.js.map +1 -1
  53. package/build/components/link-control/constants.js +11 -1
  54. package/build/components/link-control/constants.js.map +1 -1
  55. package/build/components/link-control/search-results.js +4 -3
  56. package/build/components/link-control/search-results.js.map +1 -1
  57. package/build/components/link-control/use-search-handler.js +4 -4
  58. package/build/components/link-control/use-search-handler.js.map +1 -1
  59. package/build/components/list-view/drop-indicator.js +0 -1
  60. package/build/components/list-view/drop-indicator.js.map +1 -1
  61. package/build/components/navigable-toolbar/index.js +12 -2
  62. package/build/components/navigable-toolbar/index.js.map +1 -1
  63. package/build/components/rich-text/format-toolbar-container.js +0 -1
  64. package/build/components/rich-text/format-toolbar-container.js.map +1 -1
  65. package/build/components/rich-text/index.js +1 -1
  66. package/build/components/rich-text/index.js.map +1 -1
  67. package/build/components/url-input/index.js +4 -1
  68. package/build/components/url-input/index.js.map +1 -1
  69. package/build/components/use-block-drop-zone/index.native.js +167 -0
  70. package/build/components/use-block-drop-zone/index.native.js.map +1 -0
  71. package/build/components/use-on-block-drop/index.native.js +95 -0
  72. package/build/components/use-on-block-drop/index.native.js.map +1 -0
  73. package/build/components/warning/index.js +6 -1
  74. package/build/components/warning/index.js.map +1 -1
  75. package/build/hooks/anchor.js.map +1 -1
  76. package/build/hooks/border.js +2 -7
  77. package/build/hooks/border.js.map +1 -1
  78. package/build/hooks/color-panel.js +14 -7
  79. package/build/hooks/color-panel.js.map +1 -1
  80. package/build/hooks/style.js +14 -13
  81. package/build/hooks/style.js.map +1 -1
  82. package/build/hooks/typography.js +6 -2
  83. package/build/hooks/typography.js.map +1 -1
  84. package/build-module/components/block-alignment-matrix-control/index.js +1 -6
  85. package/build-module/components/block-alignment-matrix-control/index.js.map +1 -1
  86. package/build-module/components/block-content-overlay/index.js +3 -78
  87. package/build-module/components/block-content-overlay/index.js.map +1 -1
  88. package/build-module/components/block-draggable/draggable-chip.native.js +50 -0
  89. package/build-module/components/block-draggable/draggable-chip.native.js.map +1 -0
  90. package/build-module/components/block-draggable/dropping-insertion-point.native.js +137 -0
  91. package/build-module/components/block-draggable/dropping-insertion-point.native.js.map +1 -0
  92. package/build-module/components/block-draggable/index.native.js +449 -0
  93. package/build-module/components/block-draggable/index.native.js.map +1 -0
  94. package/build-module/components/block-draggable/use-scroll-when-dragging.native.js +120 -0
  95. package/build-module/components/block-draggable/use-scroll-when-dragging.native.js.map +1 -0
  96. package/build-module/components/block-list/block-list-context.native.js +179 -0
  97. package/build-module/components/block-list/block-list-context.native.js.map +1 -0
  98. package/build-module/components/block-list/block-list-item-cell.native.js +59 -0
  99. package/build-module/components/block-list/block-list-item-cell.native.js.map +1 -0
  100. package/build-module/components/block-list/block-list-item.native.js +12 -9
  101. package/build-module/components/block-list/block-list-item.native.js.map +1 -1
  102. package/build-module/components/block-list/block.native.js +25 -5
  103. package/build-module/components/block-list/block.native.js.map +1 -1
  104. package/build-module/components/block-list/index.native.js +72 -23
  105. package/build-module/components/block-list/index.native.js.map +1 -1
  106. package/build-module/components/block-list/use-block-props/index.js +9 -5
  107. package/build-module/components/block-list/use-block-props/index.js.map +1 -1
  108. package/build-module/components/block-list/use-in-between-inserter.js +1 -1
  109. package/build-module/components/block-list/use-in-between-inserter.js.map +1 -1
  110. package/build-module/components/block-mobile-toolbar/index.native.js +8 -3
  111. package/build-module/components/block-mobile-toolbar/index.native.js.map +1 -1
  112. package/build-module/components/block-mover/index.native.js +18 -5
  113. package/build-module/components/block-mover/index.native.js.map +1 -1
  114. package/build-module/components/block-popover/inbetween.js +10 -2
  115. package/build-module/components/block-popover/inbetween.js.map +1 -1
  116. package/build-module/components/block-popover/index.js +4 -15
  117. package/build-module/components/block-popover/index.js.map +1 -1
  118. package/build-module/components/block-preview/index.js +1 -1
  119. package/build-module/components/block-preview/index.js.map +1 -1
  120. package/build-module/components/block-tools/selected-block-popover.js +2 -29
  121. package/build-module/components/block-tools/selected-block-popover.js.map +1 -1
  122. package/build-module/components/border-radius-control/input-controls.js +11 -4
  123. package/build-module/components/border-radius-control/input-controls.js.map +1 -1
  124. package/build-module/components/colors-gradients/dropdown.js +151 -46
  125. package/build-module/components/colors-gradients/dropdown.js.map +1 -1
  126. package/build-module/components/iframe/index.js +52 -51
  127. package/build-module/components/iframe/index.js.map +1 -1
  128. package/build-module/components/image-editor/use-save-image.js +2 -1
  129. package/build-module/components/image-editor/use-save-image.js.map +1 -1
  130. package/build-module/components/index.js +1 -2
  131. package/build-module/components/index.js.map +1 -1
  132. package/build-module/components/inserter/index.native.js +1 -1
  133. package/build-module/components/inserter/index.native.js.map +1 -1
  134. package/build-module/components/link-control/constants.js +5 -0
  135. package/build-module/components/link-control/constants.js.map +1 -1
  136. package/build-module/components/link-control/search-results.js +3 -4
  137. package/build-module/components/link-control/search-results.js.map +1 -1
  138. package/build-module/components/link-control/use-search-handler.js +5 -5
  139. package/build-module/components/link-control/use-search-handler.js.map +1 -1
  140. package/build-module/components/list-view/drop-indicator.js +0 -1
  141. package/build-module/components/list-view/drop-indicator.js.map +1 -1
  142. package/build-module/components/navigable-toolbar/index.js +12 -2
  143. package/build-module/components/navigable-toolbar/index.js.map +1 -1
  144. package/build-module/components/rich-text/format-toolbar-container.js +0 -1
  145. package/build-module/components/rich-text/format-toolbar-container.js.map +1 -1
  146. package/build-module/components/rich-text/index.js +1 -1
  147. package/build-module/components/rich-text/index.js.map +1 -1
  148. package/build-module/components/url-input/index.js +4 -1
  149. package/build-module/components/url-input/index.js.map +1 -1
  150. package/build-module/components/use-block-drop-zone/index.native.js +148 -0
  151. package/build-module/components/use-block-drop-zone/index.native.js.map +1 -0
  152. package/build-module/components/use-on-block-drop/index.native.js +83 -0
  153. package/build-module/components/use-on-block-drop/index.native.js.map +1 -0
  154. package/build-module/components/warning/index.js +6 -1
  155. package/build-module/components/warning/index.js.map +1 -1
  156. package/build-module/hooks/anchor.js.map +1 -1
  157. package/build-module/hooks/border.js +2 -7
  158. package/build-module/hooks/border.js.map +1 -1
  159. package/build-module/hooks/color-panel.js +11 -6
  160. package/build-module/hooks/color-panel.js.map +1 -1
  161. package/build-module/hooks/style.js +15 -14
  162. package/build-module/hooks/style.js.map +1 -1
  163. package/build-module/hooks/typography.js +6 -2
  164. package/build-module/hooks/typography.js.map +1 -1
  165. package/build-style/style-rtl.css +60 -174
  166. package/build-style/style.css +60 -174
  167. package/package.json +28 -28
  168. package/src/components/block-alignment-matrix-control/index.js +1 -5
  169. package/src/components/block-content-overlay/index.js +8 -95
  170. package/src/components/block-content-overlay/style.scss +2 -11
  171. package/src/components/block-draggable/draggable-chip.native.js +49 -0
  172. package/src/components/block-draggable/dropping-insertion-point.native.js +181 -0
  173. package/src/components/block-draggable/dropping-insertion-point.native.scss +8 -0
  174. package/src/components/block-draggable/index.native.js +458 -0
  175. package/src/components/block-draggable/style.native.scss +19 -0
  176. package/src/components/block-draggable/use-scroll-when-dragging.native.js +135 -0
  177. package/src/components/block-list/block-list-context.native.js +175 -0
  178. package/src/components/block-list/block-list-item-cell.native.js +49 -0
  179. package/src/components/block-list/block-list-item.native.js +7 -11
  180. package/src/components/block-list/block.native.js +36 -8
  181. package/src/components/block-list/index.native.js +54 -13
  182. package/src/components/block-list/test/block-list-context.native.js +253 -0
  183. package/src/components/block-list/test/fixtures/block-list-context.native.js +79 -0
  184. package/src/components/block-list/use-block-props/index.js +10 -5
  185. package/src/components/block-list/use-in-between-inserter.js +1 -1
  186. package/src/components/block-mobile-toolbar/index.native.js +8 -1
  187. package/src/components/block-mover/index.native.js +22 -6
  188. package/src/components/block-mover/test/__snapshots__/index.native.js.snap +6 -0
  189. package/src/components/block-popover/inbetween.js +9 -1
  190. package/src/components/block-popover/index.js +1 -16
  191. package/src/components/block-popover/style.scss +1 -0
  192. package/src/components/block-preview/index.js +1 -4
  193. package/src/components/block-switcher/style.scss +2 -39
  194. package/src/components/block-tools/selected-block-popover.js +1 -36
  195. package/src/components/block-tools/style.scss +1 -12
  196. package/src/components/border-radius-control/input-controls.js +16 -8
  197. package/src/components/border-radius-control/style.scss +3 -2
  198. package/src/components/colors-gradients/dropdown.js +156 -62
  199. package/src/components/colors-gradients/style.scss +51 -23
  200. package/src/components/duotone-control/style.scss +1 -7
  201. package/src/components/iframe/index.js +62 -54
  202. package/src/components/image-editor/use-save-image.js +2 -1
  203. package/src/components/index.js +1 -2
  204. package/src/components/inserter/index.native.js +1 -1
  205. package/src/components/inserter/style.scss +2 -1
  206. package/src/components/link-control/constants.js +11 -0
  207. package/src/components/link-control/search-results.js +4 -5
  208. package/src/components/link-control/use-search-handler.js +11 -5
  209. package/src/components/list-view/drop-indicator.js +0 -1
  210. package/src/components/list-view/style.scss +2 -1
  211. package/src/components/navigable-toolbar/index.js +12 -2
  212. package/src/components/preview-options/style.scss +0 -4
  213. package/src/components/rich-text/format-toolbar-container.js +0 -1
  214. package/src/components/rich-text/index.js +1 -1
  215. package/src/components/rich-text/style.scss +2 -8
  216. package/src/components/url-input/index.js +3 -1
  217. package/src/components/use-block-drop-zone/index.native.js +173 -0
  218. package/src/components/use-on-block-drop/index.native.js +119 -0
  219. package/src/components/warning/index.js +47 -42
  220. package/src/components/warning/test/__snapshots__/index.js.snap +15 -6
  221. package/src/components/warning/test/index.js +1 -1
  222. package/src/hooks/anchor.js +1 -1
  223. package/src/hooks/border.js +2 -11
  224. package/src/hooks/border.scss +0 -48
  225. package/src/hooks/color-panel.js +13 -9
  226. package/src/hooks/color.scss +0 -62
  227. package/src/hooks/style.js +25 -39
  228. package/src/hooks/typography.js +2 -0
  229. package/src/style.scss +0 -1
  230. package/build/components/colors-gradients/tools-panel-color-dropdown.js +0 -89
  231. package/build/components/colors-gradients/tools-panel-color-dropdown.js.map +0 -1
  232. package/build-module/components/colors-gradients/tools-panel-color-dropdown.js +0 -75
  233. package/build-module/components/colors-gradients/tools-panel-color-dropdown.js.map +0 -1
  234. package/src/components/block-alignment-matrix-control/style.scss +0 -10
  235. package/src/components/colors-gradients/tools-panel-color-dropdown.js +0 -85
@@ -0,0 +1,49 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { View } from 'react-native';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { dragHandle } from '@wordpress/icons';
10
+ import { usePreferredColorSchemeStyle } from '@wordpress/compose';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import BlockIcon from '../block-icon';
16
+ import styles from './style.scss';
17
+
18
+ const shadowStyle = {
19
+ shadowColor: '#000',
20
+ shadowOffset: {
21
+ width: 0,
22
+ height: 2,
23
+ },
24
+ shadowOpacity: 0.25,
25
+ shadowRadius: 3.84,
26
+
27
+ elevation: 5,
28
+ };
29
+
30
+ /**
31
+ * Block draggable chip component
32
+ *
33
+ * @param {Object} props Component props.
34
+ * @param {Object} [props.icon] Block icon.
35
+ * @return {JSX.Element} Chip component.
36
+ */
37
+ export default function BlockDraggableChip( { icon } ) {
38
+ const containerStyle = usePreferredColorSchemeStyle(
39
+ styles[ 'draggable-chip__container' ],
40
+ styles[ 'draggable-chip__container--dark' ]
41
+ );
42
+
43
+ return (
44
+ <View style={ [ containerStyle, shadowStyle ] }>
45
+ <BlockIcon icon={ dragHandle } />
46
+ { icon && <BlockIcon icon={ icon } /> }
47
+ </View>
48
+ );
49
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import Animated, {
5
+ useSharedValue,
6
+ useAnimatedStyle,
7
+ withTiming,
8
+ useAnimatedReaction,
9
+ runOnJS,
10
+ } from 'react-native-reanimated';
11
+ import {
12
+ useSafeAreaInsets,
13
+ useSafeAreaFrame,
14
+ } from 'react-native-safe-area-context';
15
+
16
+ /**
17
+ * WordPress dependencies
18
+ */
19
+ import { useSelect } from '@wordpress/data';
20
+ import { generateHapticFeedback } from '@wordpress/react-native-bridge';
21
+
22
+ /**
23
+ * Internal dependencies
24
+ */
25
+ import { store as blockEditorStore } from '../../store';
26
+ import { useBlockListContext } from '../block-list/block-list-context';
27
+ import styles from './dropping-insertion-point.scss';
28
+
29
+ /**
30
+ * Dropping zone indicator component.
31
+ *
32
+ * This component shows where a block can be dropped when it's being dragged.
33
+ *
34
+ * @param {Object} props Component props.
35
+ * @param {Object} props.scroll Scroll offset object.
36
+ * @param {Object} props.currentYPosition Current Y coordinate position when dragging.
37
+ * @param {import('react-native-reanimated').SharedValue} props.isDragging Whether or not dragging has started.
38
+ * @param {import('react-native-reanimated').SharedValue} props.targetBlockIndex Current block target index.
39
+ *
40
+ * @return {JSX.Element} The component to be rendered.
41
+ */
42
+ export default function DroppingInsertionPoint( {
43
+ scroll,
44
+ currentYPosition,
45
+ isDragging,
46
+ targetBlockIndex,
47
+ } ) {
48
+ const {
49
+ getBlockOrder,
50
+ isBlockBeingDragged,
51
+ isDraggingBlocks,
52
+ getPreviousBlockClientId,
53
+ getNextBlockClientId,
54
+ } = useSelect( blockEditorStore );
55
+
56
+ const { blocksLayouts, findBlockLayoutByClientId } = useBlockListContext();
57
+ const { top, bottom } = useSafeAreaInsets();
58
+ const { height } = useSafeAreaFrame();
59
+ const safeAreaOffset = top + bottom;
60
+ const maxHeight =
61
+ height -
62
+ ( safeAreaOffset + styles[ 'dropping-insertion-point' ].height );
63
+
64
+ const blockYPosition = useSharedValue( 0 );
65
+ const opacity = useSharedValue( 0 );
66
+
67
+ useAnimatedReaction(
68
+ () => isDragging.value,
69
+ ( value ) => {
70
+ if ( ! value ) {
71
+ opacity.value = 0;
72
+ blockYPosition.value = 0;
73
+ }
74
+ }
75
+ );
76
+
77
+ function getSelectedBlockIndicatorPosition( positions ) {
78
+ const currentYPositionWithScroll =
79
+ currentYPosition.value + scroll.offsetY.value;
80
+ const midpoint = ( positions.top + positions.bottom ) / 2;
81
+
82
+ return midpoint < currentYPositionWithScroll
83
+ ? positions.bottom
84
+ : positions.top;
85
+ }
86
+
87
+ function setIndicatorPosition( index ) {
88
+ const insertionPointIndex = index;
89
+ const order = getBlockOrder();
90
+ const isDraggingAnyBlocks = isDraggingBlocks();
91
+
92
+ if (
93
+ ! isDraggingAnyBlocks ||
94
+ insertionPointIndex === null ||
95
+ ! order.length
96
+ ) {
97
+ return;
98
+ }
99
+
100
+ let previousClientId = order[ insertionPointIndex - 1 ];
101
+ let nextClientId = order[ insertionPointIndex ];
102
+
103
+ while ( isBlockBeingDragged( previousClientId ) ) {
104
+ previousClientId = getPreviousBlockClientId( previousClientId );
105
+ }
106
+
107
+ while ( isBlockBeingDragged( nextClientId ) ) {
108
+ nextClientId = getNextBlockClientId( nextClientId );
109
+ }
110
+
111
+ const previousElement = previousClientId
112
+ ? findBlockLayoutByClientId(
113
+ blocksLayouts.current,
114
+ previousClientId
115
+ )
116
+ : null;
117
+ const nextElement = nextClientId
118
+ ? findBlockLayoutByClientId( blocksLayouts.current, nextClientId )
119
+ : null;
120
+
121
+ const previousElementPosition = previousElement
122
+ ? previousElement.y + previousElement.height
123
+ : 0;
124
+ const nextElementPosition = nextElement ? nextElement.y : 0;
125
+
126
+ const elementsPositions = {
127
+ top: Math.floor(
128
+ previousElement ? previousElementPosition : nextElementPosition
129
+ ),
130
+ bottom: Math.floor(
131
+ nextElement ? nextElementPosition : previousElementPosition
132
+ ),
133
+ };
134
+
135
+ const nextPosition =
136
+ elementsPositions.top !== elementsPositions.bottom
137
+ ? getSelectedBlockIndicatorPosition( elementsPositions )
138
+ : elementsPositions.top;
139
+
140
+ if ( nextPosition && blockYPosition.value !== nextPosition ) {
141
+ opacity.value = 0;
142
+ blockYPosition.value = nextPosition;
143
+ opacity.value = withTiming( 1 );
144
+ generateHapticFeedback();
145
+ }
146
+ }
147
+
148
+ useAnimatedReaction(
149
+ () => targetBlockIndex.value,
150
+ ( value, previous ) => {
151
+ if ( value !== previous ) {
152
+ runOnJS( setIndicatorPosition )( value );
153
+ }
154
+ }
155
+ );
156
+
157
+ const animatedStyles = useAnimatedStyle( () => {
158
+ const translationY = blockYPosition.value - scroll.offsetY.value;
159
+ // Prevents overflowing behind the header/footer
160
+ const shouldHideIndicator =
161
+ translationY < 0 || translationY > maxHeight;
162
+
163
+ return {
164
+ opacity: shouldHideIndicator ? 0 : opacity.value,
165
+ transform: [
166
+ {
167
+ translateY: translationY,
168
+ },
169
+ ],
170
+ };
171
+ } );
172
+
173
+ const insertionPointStyles = [
174
+ styles[ 'dropping-insertion-point' ],
175
+ animatedStyles,
176
+ ];
177
+
178
+ return (
179
+ <Animated.View pointerEvents="none" style={ insertionPointStyles } />
180
+ );
181
+ }
@@ -0,0 +1,8 @@
1
+ .dropping-insertion-point {
2
+ position: absolute;
3
+ left: $dashed-border-space;
4
+ right: $dashed-border-space;
5
+ height: 3;
6
+ background-color: $blue-wordpress;
7
+ z-index: 1;
8
+ }
@@ -0,0 +1,458 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { AccessibilityInfo } from 'react-native';
5
+ import {
6
+ useSafeAreaInsets,
7
+ useSafeAreaFrame,
8
+ } from 'react-native-safe-area-context';
9
+ import Animated, {
10
+ runOnJS,
11
+ runOnUI,
12
+ useAnimatedRef,
13
+ useAnimatedStyle,
14
+ useSharedValue,
15
+ withDelay,
16
+ withTiming,
17
+ ZoomInEasyDown,
18
+ } from 'react-native-reanimated';
19
+
20
+ /**
21
+ * WordPress dependencies
22
+ */
23
+ import { Draggable, DraggableTrigger } from '@wordpress/components';
24
+ import { select, useSelect, useDispatch } from '@wordpress/data';
25
+ import {
26
+ useCallback,
27
+ useEffect,
28
+ useRef,
29
+ useState,
30
+ Platform,
31
+ } from '@wordpress/element';
32
+ import { getBlockType } from '@wordpress/blocks';
33
+ import { generateHapticFeedback } from '@wordpress/react-native-bridge';
34
+ import RCTAztecView from '@wordpress/react-native-aztec';
35
+
36
+ /**
37
+ * Internal dependencies
38
+ */
39
+ import useScrollWhenDragging from './use-scroll-when-dragging';
40
+ import DraggableChip from './draggable-chip';
41
+ import { store as blockEditorStore } from '../../store';
42
+ import { useBlockListContext } from '../block-list/block-list-context';
43
+ import DroppingInsertionPoint from './dropping-insertion-point';
44
+ import useBlockDropZone from '../use-block-drop-zone';
45
+ import styles from './style.scss';
46
+
47
+ const CHIP_OFFSET_TO_TOUCH_POSITION = 32;
48
+ const BLOCK_OPACITY_ANIMATION_CONFIG = { duration: 350 };
49
+ const BLOCK_OPACITY_ANIMATION_DELAY = 250;
50
+ const DEFAULT_LONG_PRESS_MIN_DURATION = 500;
51
+ const DEFAULT_IOS_LONG_PRESS_MIN_DURATION =
52
+ DEFAULT_LONG_PRESS_MIN_DURATION - 50;
53
+
54
+ /**
55
+ * Block draggable wrapper component
56
+ *
57
+ * This component handles all the interactions for dragging blocks.
58
+ * It relies on the block list and its context for dragging, hence it
59
+ * should be rendered between the `BlockListProvider` component and the
60
+ * block list rendering. It also requires listening to scroll events,
61
+ * therefore for this purpose, it returns the `onScroll` event handler
62
+ * that should be attached to the list that renders the blocks.
63
+ *
64
+ *
65
+ * @param {Object} props Component props.
66
+ * @param {JSX.Element} props.children Children to be rendered.
67
+ * @param {boolean} props.isRTL Check if current locale is RTL.
68
+ *
69
+ * @return {Function} Render function that passes `onScroll` event handler.
70
+ */
71
+ const BlockDraggableWrapper = ( { children, isRTL } ) => {
72
+ const [ draggedBlockIcon, setDraggedBlockIcon ] = useState();
73
+
74
+ const {
75
+ selectBlock,
76
+ startDraggingBlocks,
77
+ stopDraggingBlocks,
78
+ } = useDispatch( blockEditorStore );
79
+
80
+ const { scrollRef } = useBlockListContext();
81
+ const animatedScrollRef = useAnimatedRef();
82
+ const { left, right } = useSafeAreaInsets();
83
+ const { width } = useSafeAreaFrame();
84
+ const safeAreaOffset = left + right;
85
+ const contentWidth = width - safeAreaOffset;
86
+ animatedScrollRef( scrollRef );
87
+
88
+ const scroll = {
89
+ offsetY: useSharedValue( 0 ),
90
+ };
91
+ const chip = {
92
+ x: useSharedValue( 0 ),
93
+ y: useSharedValue( 0 ),
94
+ width: useSharedValue( 0 ),
95
+ height: useSharedValue( 0 ),
96
+ };
97
+ const currentYPosition = useSharedValue( 0 );
98
+ const isDragging = useSharedValue( false );
99
+
100
+ const [
101
+ startScrolling,
102
+ scrollOnDragOver,
103
+ stopScrolling,
104
+ draggingScrollHandler,
105
+ ] = useScrollWhenDragging();
106
+
107
+ const scrollHandler = ( event ) => {
108
+ 'worklet';
109
+ const { contentOffset } = event;
110
+ scroll.offsetY.value = contentOffset.y;
111
+
112
+ draggingScrollHandler( event );
113
+ };
114
+
115
+ const {
116
+ onBlockDragOver,
117
+ onBlockDragEnd,
118
+ onBlockDrop,
119
+ targetBlockIndex,
120
+ } = useBlockDropZone();
121
+
122
+ // Stop dragging blocks if the block draggable is unmounted.
123
+ useEffect( () => {
124
+ return () => {
125
+ if ( isDragging.value ) {
126
+ stopDraggingBlocks();
127
+ }
128
+ };
129
+ }, [] );
130
+
131
+ const setDraggedBlockIconByClientId = ( clientId ) => {
132
+ const blockName = select( blockEditorStore ).getBlockName( clientId );
133
+ const blockIcon = getBlockType( blockName )?.icon;
134
+ if ( blockIcon ) {
135
+ setDraggedBlockIcon( blockIcon );
136
+ }
137
+ };
138
+
139
+ const onStartDragging = ( { clientId, position } ) => {
140
+ if ( clientId ) {
141
+ startDraggingBlocks( [ clientId ] );
142
+ setDraggedBlockIconByClientId( clientId );
143
+ runOnUI( startScrolling )( position.y );
144
+ generateHapticFeedback();
145
+ } else {
146
+ // We stop dragging if no block is found.
147
+ runOnUI( stopDragging )();
148
+ }
149
+ };
150
+
151
+ const onStopDragging = ( { clientId } ) => {
152
+ if ( clientId ) {
153
+ onBlockDrop( {
154
+ // Dropping is only allowed at root level
155
+ srcRootClientId: '',
156
+ srcClientIds: [ clientId ],
157
+ type: 'block',
158
+ } );
159
+ selectBlock( clientId );
160
+ setDraggedBlockIcon( undefined );
161
+ }
162
+ onBlockDragEnd();
163
+ stopDraggingBlocks();
164
+ };
165
+
166
+ const onChipLayout = ( { nativeEvent: { layout } } ) => {
167
+ if ( layout.width > 0 ) {
168
+ chip.width.value = layout.width;
169
+ }
170
+ if ( layout.height > 0 ) {
171
+ chip.height.value = layout.height;
172
+ }
173
+ };
174
+
175
+ const startDragging = ( { x, y, id } ) => {
176
+ 'worklet';
177
+ const dragPosition = { x, y };
178
+ chip.x.value = dragPosition.x;
179
+ chip.y.value = dragPosition.y;
180
+ currentYPosition.value = dragPosition.y;
181
+
182
+ isDragging.value = true;
183
+
184
+ runOnJS( onStartDragging )( { clientId: id, position: dragPosition } );
185
+ };
186
+
187
+ const updateDragging = ( { x, y } ) => {
188
+ 'worklet';
189
+ const dragPosition = { x, y };
190
+ chip.x.value = dragPosition.x;
191
+ chip.y.value = dragPosition.y;
192
+ currentYPosition.value = dragPosition.y;
193
+
194
+ runOnJS( onBlockDragOver )( { x, y: y + scroll.offsetY.value } );
195
+
196
+ // Update scrolling velocity
197
+ scrollOnDragOver( dragPosition.y );
198
+ };
199
+
200
+ const stopDragging = ( { id } ) => {
201
+ 'worklet';
202
+ isDragging.value = false;
203
+
204
+ stopScrolling();
205
+ runOnJS( onStopDragging )( { clientId: id } );
206
+ };
207
+
208
+ const chipDynamicStyles = useAnimatedStyle( () => {
209
+ const chipOffset = chip.width.value / 2;
210
+ const translateX = ! isRTL
211
+ ? chip.x.value - chipOffset
212
+ : -( contentWidth - ( chip.x.value + chipOffset ) );
213
+
214
+ return {
215
+ transform: [
216
+ {
217
+ translateX,
218
+ },
219
+ {
220
+ translateY:
221
+ chip.y.value -
222
+ chip.height.value -
223
+ CHIP_OFFSET_TO_TOUCH_POSITION,
224
+ },
225
+ ],
226
+ };
227
+ } );
228
+ const chipStyles = [
229
+ chipDynamicStyles,
230
+ styles[ 'draggable-chip__wrapper' ],
231
+ ];
232
+
233
+ const exitingAnimation = ( { currentHeight, currentWidth } ) => {
234
+ 'worklet';
235
+ const translateX = ! isRTL ? 0 : currentWidth * -1;
236
+ const duration = 150;
237
+ const animations = {
238
+ transform: [
239
+ {
240
+ translateY: withTiming( currentHeight, {
241
+ duration,
242
+ } ),
243
+ },
244
+ {
245
+ translateX: withTiming( translateX, {
246
+ duration,
247
+ } ),
248
+ },
249
+ { scale: withTiming( 0, { duration } ) },
250
+ ],
251
+ };
252
+ const initialValues = {
253
+ transform: [ { translateY: 0 }, { translateX }, { scale: 1 } ],
254
+ };
255
+ return {
256
+ initialValues,
257
+ animations,
258
+ };
259
+ };
260
+
261
+ return (
262
+ <>
263
+ <DroppingInsertionPoint
264
+ scroll={ scroll }
265
+ currentYPosition={ currentYPosition }
266
+ isDragging={ isDragging }
267
+ targetBlockIndex={ targetBlockIndex }
268
+ />
269
+ <Draggable
270
+ onDragStart={ startDragging }
271
+ onDragOver={ updateDragging }
272
+ onDragEnd={ stopDragging }
273
+ >
274
+ { children( { onScroll: scrollHandler } ) }
275
+ </Draggable>
276
+ <Animated.View
277
+ onLayout={ onChipLayout }
278
+ style={ chipStyles }
279
+ pointerEvents="none"
280
+ >
281
+ { draggedBlockIcon && (
282
+ <Animated.View
283
+ entering={ ZoomInEasyDown.duration( 200 ) }
284
+ exiting={ exitingAnimation }
285
+ >
286
+ <DraggableChip icon={ draggedBlockIcon } />
287
+ </Animated.View>
288
+ ) }
289
+ </Animated.View>
290
+ </>
291
+ );
292
+ };
293
+
294
+ /**
295
+ * Block draggable component
296
+ *
297
+ * This component serves for animating the block when it is being dragged.
298
+ * Hence, it should be wrapped around the rendering of a block.
299
+ *
300
+ * @param {Object} props Component props.
301
+ * @param {JSX.Element} props.children Children to be rendered.
302
+ * @param {string} props.clientId Client id of the block.
303
+ * @param {string} [props.draggingClientId] Client id to use for dragging. If not defined, the value from `clientId` will be used.
304
+ * @param {boolean} [props.enabled] Enables the draggable trigger.
305
+ *
306
+ * @return {Function} Render function which includes the parameter `isDraggable` to determine if the block can be dragged.
307
+ */
308
+ const BlockDraggable = ( {
309
+ clientId,
310
+ children,
311
+ draggingClientId,
312
+ enabled = true,
313
+ } ) => {
314
+ const wasBeingDragged = useRef( false );
315
+ const [ isEditingText, setIsEditingText ] = useState( false );
316
+ const [ isScreenReaderEnabled, setIsScreenReaderEnabled ] = useState(
317
+ false
318
+ );
319
+
320
+ const draggingAnimation = {
321
+ opacity: useSharedValue( 1 ),
322
+ };
323
+
324
+ const startDraggingBlock = () => {
325
+ draggingAnimation.opacity.value = withTiming(
326
+ 0.4,
327
+ BLOCK_OPACITY_ANIMATION_CONFIG
328
+ );
329
+ };
330
+
331
+ const stopDraggingBlock = () => {
332
+ draggingAnimation.opacity.value = withDelay(
333
+ BLOCK_OPACITY_ANIMATION_DELAY,
334
+ withTiming( 1, BLOCK_OPACITY_ANIMATION_CONFIG )
335
+ );
336
+ };
337
+
338
+ const { isDraggable, isBeingDragged, isBlockSelected } = useSelect(
339
+ ( _select ) => {
340
+ const {
341
+ getBlockRootClientId,
342
+ getTemplateLock,
343
+ isBlockBeingDragged,
344
+ getSelectedBlockClientId,
345
+ } = _select( blockEditorStore );
346
+ const rootClientId = getBlockRootClientId( clientId );
347
+ const templateLock = rootClientId
348
+ ? getTemplateLock( rootClientId )
349
+ : null;
350
+ const selectedBlockClientId = getSelectedBlockClientId();
351
+
352
+ return {
353
+ isBeingDragged: isBlockBeingDragged( clientId ),
354
+ isDraggable: 'all' !== templateLock,
355
+ isBlockSelected:
356
+ selectedBlockClientId && selectedBlockClientId === clientId,
357
+ };
358
+ },
359
+ [ clientId ]
360
+ );
361
+
362
+ useEffect( () => {
363
+ if ( isBeingDragged !== wasBeingDragged.current ) {
364
+ if ( isBeingDragged ) {
365
+ startDraggingBlock();
366
+ } else {
367
+ stopDraggingBlock();
368
+ }
369
+ }
370
+ wasBeingDragged.current = isBeingDragged;
371
+ }, [ isBeingDragged ] );
372
+
373
+ const onFocusChangeAztec = useCallback( ( { isFocused } ) => {
374
+ setIsEditingText( isFocused );
375
+ }, [] );
376
+
377
+ useEffect( () => {
378
+ let mounted = true;
379
+
380
+ const isAnyAztecInputFocused = RCTAztecView.InputState.isFocused();
381
+ if ( isAnyAztecInputFocused ) {
382
+ setIsEditingText( isAnyAztecInputFocused );
383
+ }
384
+
385
+ RCTAztecView.InputState.addFocusChangeListener( onFocusChangeAztec );
386
+
387
+ const screenReaderChangedListener = AccessibilityInfo.addEventListener(
388
+ 'screenReaderChanged',
389
+ setIsScreenReaderEnabled
390
+ );
391
+ AccessibilityInfo.isScreenReaderEnabled().then(
392
+ ( screenReaderEnabled ) => {
393
+ if ( mounted ) {
394
+ setIsScreenReaderEnabled( screenReaderEnabled );
395
+ }
396
+ }
397
+ );
398
+
399
+ return () => {
400
+ mounted = false;
401
+
402
+ RCTAztecView.InputState.removeFocusChangeListener(
403
+ onFocusChangeAztec
404
+ );
405
+
406
+ screenReaderChangedListener.remove();
407
+ };
408
+ }, [] );
409
+
410
+ const onLongPressDraggable = useCallback( () => {
411
+ // Ensure that no text input is focused when starting the dragging gesture in order to prevent conflicts with text editing.
412
+ RCTAztecView.InputState.blurCurrentFocusedElement();
413
+ }, [] );
414
+
415
+ const animatedWrapperStyles = useAnimatedStyle( () => {
416
+ return {
417
+ opacity: draggingAnimation.opacity.value,
418
+ };
419
+ } );
420
+ const wrapperStyles = [
421
+ animatedWrapperStyles,
422
+ styles[ 'draggable-wrapper__container' ],
423
+ ];
424
+
425
+ const canDragBlock =
426
+ enabled &&
427
+ ! isScreenReaderEnabled &&
428
+ ( ! isBlockSelected || ! isEditingText );
429
+
430
+ if ( ! isDraggable ) {
431
+ return children( { isDraggable: false } );
432
+ }
433
+
434
+ return (
435
+ <DraggableTrigger
436
+ id={ draggingClientId || clientId }
437
+ enabled={ enabled && canDragBlock }
438
+ minDuration={ Platform.select( {
439
+ // On iOS, using a lower min duration than the default
440
+ // value prevents the long-press gesture from being
441
+ // triggered in underneath elements. This is required to
442
+ // prevent enabling text editing when dragging is available.
443
+ ios: canDragBlock
444
+ ? DEFAULT_IOS_LONG_PRESS_MIN_DURATION
445
+ : DEFAULT_LONG_PRESS_MIN_DURATION,
446
+ android: DEFAULT_LONG_PRESS_MIN_DURATION,
447
+ } ) }
448
+ onLongPress={ onLongPressDraggable }
449
+ >
450
+ <Animated.View style={ wrapperStyles }>
451
+ { children( { isDraggable: true } ) }
452
+ </Animated.View>
453
+ </DraggableTrigger>
454
+ );
455
+ };
456
+
457
+ export { BlockDraggableWrapper };
458
+ export default BlockDraggable;
@@ -0,0 +1,19 @@
1
+ .draggable-wrapper__container {
2
+ flex: 1;
3
+ }
4
+
5
+ .draggable-chip__wrapper {
6
+ position: absolute;
7
+ z-index: 10;
8
+ }
9
+
10
+ .draggable-chip__container {
11
+ flex-direction: row;
12
+ padding: 16px;
13
+ background-color: $gray-0;
14
+ border-radius: 8px;
15
+ }
16
+
17
+ .draggable-chip__container--dark {
18
+ background-color: $app-background-dark-alt;
19
+ }