@wordpress/block-editor 13.0.3 → 13.2.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 (141) hide show
  1. package/CHANGELOG.md +21 -17
  2. package/README.md +1 -1
  3. package/build/components/block-lock/modal.js +67 -67
  4. package/build/components/block-lock/modal.js.map +1 -1
  5. package/build/components/block-mover/index.js +12 -6
  6. package/build/components/block-mover/index.js.map +1 -1
  7. package/build/components/child-layout-control/index.js +185 -127
  8. package/build/components/child-layout-control/index.js.map +1 -1
  9. package/build/components/date-format-picker/index.js +18 -10
  10. package/build/components/date-format-picker/index.js.map +1 -1
  11. package/build/components/grid/grid-item-movers.js +97 -0
  12. package/build/components/grid/grid-item-movers.js.map +1 -0
  13. package/build/components/{grid-visualizer → grid}/grid-item-resizer.js +18 -56
  14. package/build/components/grid/grid-item-resizer.js.map +1 -0
  15. package/build/components/grid/grid-visualizer.js +225 -0
  16. package/build/components/grid/grid-visualizer.js.map +1 -0
  17. package/build/components/{grid-visualizer → grid}/index.js +14 -0
  18. package/build/components/grid/index.js.map +1 -0
  19. package/build/components/grid/use-get-number-of-blocks-before-cell.js +40 -0
  20. package/build/components/grid/use-get-number-of-blocks-before-cell.js.map +1 -0
  21. package/build/components/grid/use-grid-layout-sync.js +162 -0
  22. package/build/components/grid/use-grid-layout-sync.js.map +1 -0
  23. package/build/components/grid/utils.js +145 -0
  24. package/build/components/grid/utils.js.map +1 -0
  25. package/build/components/inner-blocks/index.js +1 -1
  26. package/build/components/inner-blocks/index.js.map +1 -1
  27. package/build/components/inserter/block-types-tab.native.js +1 -1
  28. package/build/components/inserter/block-types-tab.native.js.map +1 -1
  29. package/build/components/link-control/link-preview.js +12 -1
  30. package/build/components/link-control/link-preview.js.map +1 -1
  31. package/build/components/provider/use-block-sync.js +1 -7
  32. package/build/components/provider/use-block-sync.js.map +1 -1
  33. package/build/components/rich-text/event-listeners/input-rules.js +1 -0
  34. package/build/components/rich-text/event-listeners/input-rules.js.map +1 -1
  35. package/build/components/rich-text/index.native.js +14 -4
  36. package/build/components/rich-text/index.native.js.map +1 -1
  37. package/build/components/rich-text/native/index.native.js +20 -4
  38. package/build/components/rich-text/native/index.native.js.map +1 -1
  39. package/build/hooks/block-style-variation.js +2 -2
  40. package/build/hooks/block-style-variation.js.map +1 -1
  41. package/build/hooks/layout-child.js +29 -21
  42. package/build/hooks/layout-child.js.map +1 -1
  43. package/build/layouts/grid.js +24 -47
  44. package/build/layouts/grid.js.map +1 -1
  45. package/build/store/private-actions.js +0 -34
  46. package/build/store/private-actions.js.map +1 -1
  47. package/build-module/components/block-lock/modal.js +67 -67
  48. package/build-module/components/block-lock/modal.js.map +1 -1
  49. package/build-module/components/block-mover/index.js +12 -6
  50. package/build-module/components/block-mover/index.js.map +1 -1
  51. package/build-module/components/child-layout-control/index.js +185 -127
  52. package/build-module/components/child-layout-control/index.js.map +1 -1
  53. package/build-module/components/date-format-picker/index.js +19 -11
  54. package/build-module/components/date-format-picker/index.js.map +1 -1
  55. package/build-module/components/grid/grid-item-movers.js +90 -0
  56. package/build-module/components/grid/grid-item-movers.js.map +1 -0
  57. package/build-module/components/{grid-visualizer → grid}/grid-item-resizer.js +13 -51
  58. package/build-module/components/grid/grid-item-resizer.js.map +1 -0
  59. package/build-module/components/grid/grid-visualizer.js +217 -0
  60. package/build-module/components/grid/grid-visualizer.js.map +1 -0
  61. package/build-module/components/grid/index.js +5 -0
  62. package/build-module/components/grid/index.js.map +1 -0
  63. package/build-module/components/grid/use-get-number-of-blocks-before-cell.js +33 -0
  64. package/build-module/components/grid/use-get-number-of-blocks-before-cell.js.map +1 -0
  65. package/build-module/components/grid/use-grid-layout-sync.js +155 -0
  66. package/build-module/components/grid/use-grid-layout-sync.js.map +1 -0
  67. package/build-module/components/grid/utils.js +131 -0
  68. package/build-module/components/grid/utils.js.map +1 -0
  69. package/build-module/components/inner-blocks/index.js +1 -1
  70. package/build-module/components/inner-blocks/index.js.map +1 -1
  71. package/build-module/components/inserter/block-types-tab.native.js +1 -1
  72. package/build-module/components/inserter/block-types-tab.native.js.map +1 -1
  73. package/build-module/components/link-control/link-preview.js +14 -1
  74. package/build-module/components/link-control/link-preview.js.map +1 -1
  75. package/build-module/components/provider/use-block-sync.js +1 -7
  76. package/build-module/components/provider/use-block-sync.js.map +1 -1
  77. package/build-module/components/rich-text/event-listeners/input-rules.js +1 -1
  78. package/build-module/components/rich-text/event-listeners/input-rules.js.map +1 -1
  79. package/build-module/components/rich-text/index.native.js +15 -5
  80. package/build-module/components/rich-text/index.native.js.map +1 -1
  81. package/build-module/components/rich-text/native/index.native.js +20 -4
  82. package/build-module/components/rich-text/native/index.native.js.map +1 -1
  83. package/build-module/hooks/block-style-variation.js +2 -2
  84. package/build-module/hooks/block-style-variation.js.map +1 -1
  85. package/build-module/hooks/layout-child.js +27 -19
  86. package/build-module/hooks/layout-child.js.map +1 -1
  87. package/build-module/layouts/grid.js +24 -47
  88. package/build-module/layouts/grid.js.map +1 -1
  89. package/build-module/store/private-actions.js +0 -33
  90. package/build-module/store/private-actions.js.map +1 -1
  91. package/build-style/style-rtl.css +38 -16
  92. package/build-style/style.css +38 -16
  93. package/package.json +31 -31
  94. package/src/components/block-lock/modal.js +95 -82
  95. package/src/components/block-lock/style.scss +11 -1
  96. package/src/components/block-mover/index.js +37 -24
  97. package/src/components/child-layout-control/index.js +224 -159
  98. package/src/components/date-format-picker/index.js +25 -13
  99. package/src/components/grid/grid-item-movers.js +128 -0
  100. package/src/components/{grid-visualizer → grid}/grid-item-resizer.js +14 -52
  101. package/src/components/grid/grid-visualizer.js +267 -0
  102. package/src/components/grid/index.js +4 -0
  103. package/src/components/grid/style.scss +63 -0
  104. package/src/components/grid/use-get-number-of-blocks-before-cell.js +30 -0
  105. package/src/components/grid/use-grid-layout-sync.js +167 -0
  106. package/src/components/grid/utils.js +178 -0
  107. package/src/components/inner-blocks/index.js +3 -1
  108. package/src/components/inserter/block-types-tab.native.js +2 -1
  109. package/src/components/link-control/link-preview.js +18 -1
  110. package/src/components/provider/use-block-sync.js +0 -6
  111. package/src/components/rich-text/event-listeners/input-rules.js +1 -1
  112. package/src/components/rich-text/index.native.js +14 -8
  113. package/src/components/rich-text/native/index.native.js +20 -1
  114. package/src/hooks/block-style-variation.js +2 -2
  115. package/src/hooks/layout-child.js +34 -14
  116. package/src/layouts/grid.js +54 -62
  117. package/src/store/private-actions.js +0 -29
  118. package/src/style.scss +1 -1
  119. package/build/components/grid-visualizer/grid-item-resizer.js.map +0 -1
  120. package/build/components/grid-visualizer/grid-visualizer.js +0 -92
  121. package/build/components/grid-visualizer/grid-visualizer.js.map +0 -1
  122. package/build/components/grid-visualizer/index.js.map +0 -1
  123. package/build/components/grid-visualizer/utils.js +0 -10
  124. package/build/components/grid-visualizer/utils.js.map +0 -1
  125. package/build/store/undo-ignore.js +0 -11
  126. package/build/store/undo-ignore.js.map +0 -1
  127. package/build-module/components/grid-visualizer/grid-item-resizer.js.map +0 -1
  128. package/build-module/components/grid-visualizer/grid-visualizer.js +0 -84
  129. package/build-module/components/grid-visualizer/grid-visualizer.js.map +0 -1
  130. package/build-module/components/grid-visualizer/index.js +0 -3
  131. package/build-module/components/grid-visualizer/index.js.map +0 -1
  132. package/build-module/components/grid-visualizer/utils.js +0 -4
  133. package/build-module/components/grid-visualizer/utils.js.map +0 -1
  134. package/build-module/store/undo-ignore.js +0 -5
  135. package/build-module/store/undo-ignore.js.map +0 -1
  136. package/src/components/grid-visualizer/grid-visualizer.js +0 -101
  137. package/src/components/grid-visualizer/index.js +0 -2
  138. package/src/components/grid-visualizer/style.scss +0 -34
  139. package/src/components/grid-visualizer/utils.js +0 -5
  140. package/src/store/undo-ignore.js +0 -4
  141. /package/src/components/font-sizes/{README.MD → README.md} +0 -0
@@ -9,11 +9,17 @@ import { useState, useEffect } from '@wordpress/element';
9
9
  */
10
10
  import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
11
11
  import BlockPopoverCover from '../block-popover/cover';
12
- import { getComputedCSS } from './utils';
12
+ import { getComputedCSS, getGridTracks, getClosestTrack } from './utils';
13
13
 
14
- export function GridItemResizer( { clientId, bounds, onChange } ) {
14
+ export function GridItemResizer( {
15
+ clientId,
16
+ bounds,
17
+ onChange,
18
+ parentLayout,
19
+ } ) {
15
20
  const blockElement = useBlockElement( clientId );
16
21
  const rootBlockElement = blockElement?.parentElement;
22
+ const { columnCount } = parentLayout;
17
23
 
18
24
  if ( ! blockElement || ! rootBlockElement ) {
19
25
  return null;
@@ -26,6 +32,9 @@ export function GridItemResizer( { clientId, bounds, onChange } ) {
26
32
  blockElement={ blockElement }
27
33
  rootBlockElement={ rootBlockElement }
28
34
  onChange={ onChange }
35
+ isManualGrid={
36
+ !! columnCount && window.__experimentalEnableGridInteractivity
37
+ }
29
38
  />
30
39
  );
31
40
  }
@@ -36,6 +45,7 @@ function GridItemResizerInner( {
36
45
  blockElement,
37
46
  rootBlockElement,
38
47
  onChange,
48
+ isManualGrid,
39
49
  } ) {
40
50
  const [ resizeDirection, setResizeDirection ] = useState( null );
41
51
  const [ enableSide, setEnableSide ] = useState( {
@@ -171,59 +181,11 @@ function GridItemResizerInner( {
171
181
  onChange( {
172
182
  columnSpan: columnEnd - columnStart + 1,
173
183
  rowSpan: rowEnd - rowStart + 1,
184
+ columnStart: isManualGrid ? columnStart : undefined,
185
+ rowStart: isManualGrid ? rowStart : undefined,
174
186
  } );
175
187
  } }
176
188
  />
177
189
  </BlockPopoverCover>
178
190
  );
179
191
  }
180
-
181
- /**
182
- * Given a grid-template-columns or grid-template-rows CSS property value, gets the start and end
183
- * position in pixels of each grid track.
184
- *
185
- * https://css-tricks.com/snippets/css/complete-guide-grid/#aa-grid-track
186
- *
187
- * @param {string} template The grid-template-columns or grid-template-rows CSS property value.
188
- * Only supports fixed sizes in pixels.
189
- * @param {number} gap The gap between grid tracks in pixels.
190
- *
191
- * @return {Array<{start: number, end: number}>} An array of objects with the start and end
192
- * position in pixels of each grid track.
193
- */
194
- function getGridTracks( template, gap ) {
195
- const tracks = [];
196
- for ( const size of template.split( ' ' ) ) {
197
- const previousTrack = tracks[ tracks.length - 1 ];
198
- const start = previousTrack ? previousTrack.end + gap : 0;
199
- const end = start + parseFloat( size );
200
- tracks.push( { start, end } );
201
- }
202
- return tracks;
203
- }
204
-
205
- /**
206
- * Given an array of grid tracks and a position in pixels, gets the index of the closest track to
207
- * that position.
208
- *
209
- * https://css-tricks.com/snippets/css/complete-guide-grid/#aa-grid-track
210
- *
211
- * @param {Array<{start: number, end: number}>} tracks An array of objects with the start and end
212
- * position in pixels of each grid track.
213
- * @param {number} position The position in pixels.
214
- * @param {string} edge The edge of the track to compare the
215
- * position to. Either 'start' or 'end'.
216
- *
217
- * @return {number} The index of the closest track to the position. 0-based, unlike CSS grid which
218
- * is 1-based.
219
- */
220
- function getClosestTrack( tracks, position, edge = 'start' ) {
221
- return tracks.reduce(
222
- ( closest, track, index ) =>
223
- Math.abs( track[ edge ] - position ) <
224
- Math.abs( tracks[ closest ][ edge ] - position )
225
- ? index
226
- : closest,
227
- 0
228
- );
229
- }
@@ -0,0 +1,267 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import clsx from 'clsx';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useState, useEffect, forwardRef } from '@wordpress/element';
10
+ import { useSelect, useDispatch } from '@wordpress/data';
11
+ import { __experimentalUseDropZone as useDropZone } from '@wordpress/compose';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
17
+ import BlockPopoverCover from '../block-popover/cover';
18
+ import { range, GridRect, getGridInfo } from './utils';
19
+ import { store as blockEditorStore } from '../../store';
20
+ import { useGetNumberOfBlocksBeforeCell } from './use-get-number-of-blocks-before-cell';
21
+
22
+ export function GridVisualizer( { clientId, contentRef, parentLayout } ) {
23
+ const isDistractionFree = useSelect(
24
+ ( select ) =>
25
+ select( blockEditorStore ).getSettings().isDistractionFree,
26
+ []
27
+ );
28
+ const gridElement = useBlockElement( clientId );
29
+
30
+ if ( isDistractionFree || ! gridElement ) {
31
+ return null;
32
+ }
33
+
34
+ const isManualGrid =
35
+ parentLayout?.columnCount &&
36
+ window.__experimentalEnableGridInteractivity;
37
+ return (
38
+ <GridVisualizerGrid
39
+ clientId={ clientId }
40
+ gridElement={ gridElement }
41
+ isManualGrid={ isManualGrid }
42
+ ref={ contentRef }
43
+ />
44
+ );
45
+ }
46
+
47
+ const GridVisualizerGrid = forwardRef(
48
+ ( { clientId, gridElement, isManualGrid }, ref ) => {
49
+ const [ gridInfo, setGridInfo ] = useState( () =>
50
+ getGridInfo( gridElement )
51
+ );
52
+ const [ isDroppingAllowed, setIsDroppingAllowed ] = useState( false );
53
+ const [ highlightedRect, setHighlightedRect ] = useState( null );
54
+
55
+ useEffect( () => {
56
+ const observers = [];
57
+ for ( const element of [ gridElement, ...gridElement.children ] ) {
58
+ const observer = new window.ResizeObserver( () => {
59
+ setGridInfo( getGridInfo( gridElement ) );
60
+ } );
61
+ observer.observe( element );
62
+ observers.push( observer );
63
+ }
64
+ return () => {
65
+ for ( const observer of observers ) {
66
+ observer.disconnect();
67
+ }
68
+ };
69
+ }, [ gridElement ] );
70
+
71
+ useEffect( () => {
72
+ function onGlobalDrag() {
73
+ setIsDroppingAllowed( true );
74
+ }
75
+ function onGlobalDragEnd() {
76
+ setIsDroppingAllowed( false );
77
+ }
78
+ document.addEventListener( 'drag', onGlobalDrag );
79
+ document.addEventListener( 'dragend', onGlobalDragEnd );
80
+ return () => {
81
+ document.removeEventListener( 'drag', onGlobalDrag );
82
+ document.removeEventListener( 'dragend', onGlobalDragEnd );
83
+ };
84
+ }, [] );
85
+
86
+ return (
87
+ <BlockPopoverCover
88
+ className={ clsx( 'block-editor-grid-visualizer', {
89
+ 'is-dropping-allowed': isDroppingAllowed,
90
+ } ) }
91
+ clientId={ clientId }
92
+ __unstablePopoverSlot="block-toolbar"
93
+ >
94
+ <div
95
+ ref={ ref }
96
+ className="block-editor-grid-visualizer__grid"
97
+ style={ gridInfo.style }
98
+ >
99
+ { isManualGrid
100
+ ? range( 1, gridInfo.numRows ).map( ( row ) =>
101
+ range( 1, gridInfo.numColumns ).map(
102
+ ( column ) => (
103
+ <GridVisualizerCell
104
+ key={ `${ row }-${ column }` }
105
+ color={ gridInfo.currentColor }
106
+ >
107
+ <GridVisualizerDropZone
108
+ column={ column }
109
+ row={ row }
110
+ gridClientId={ clientId }
111
+ gridInfo={ gridInfo }
112
+ highlightedRect={
113
+ highlightedRect
114
+ }
115
+ setHighlightedRect={
116
+ setHighlightedRect
117
+ }
118
+ />
119
+ </GridVisualizerCell>
120
+ )
121
+ )
122
+ )
123
+ : Array.from(
124
+ { length: gridInfo.numItems },
125
+ ( _, i ) => (
126
+ <GridVisualizerCell
127
+ key={ i }
128
+ color={ gridInfo.currentColor }
129
+ />
130
+ )
131
+ ) }
132
+ </div>
133
+ </BlockPopoverCover>
134
+ );
135
+ }
136
+ );
137
+
138
+ function GridVisualizerCell( { color, children } ) {
139
+ return (
140
+ <div
141
+ className="block-editor-grid-visualizer__cell"
142
+ style={ {
143
+ boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${ color } 20%, #0000)`,
144
+ } }
145
+ >
146
+ { children }
147
+ </div>
148
+ );
149
+ }
150
+
151
+ function GridVisualizerDropZone( {
152
+ column,
153
+ row,
154
+ gridClientId,
155
+ gridInfo,
156
+ highlightedRect,
157
+ setHighlightedRect,
158
+ } ) {
159
+ const { getBlockAttributes } = useSelect( blockEditorStore );
160
+ const {
161
+ updateBlockAttributes,
162
+ moveBlocksToPosition,
163
+ __unstableMarkNextChangeAsNotPersistent,
164
+ } = useDispatch( blockEditorStore );
165
+
166
+ const getNumberOfBlocksBeforeCell = useGetNumberOfBlocksBeforeCell(
167
+ gridClientId,
168
+ gridInfo.numColumns
169
+ );
170
+
171
+ const ref = useDropZoneWithValidation( {
172
+ validateDrag( srcClientId ) {
173
+ const attributes = getBlockAttributes( srcClientId );
174
+ const rect = new GridRect( {
175
+ columnStart: column,
176
+ rowStart: row,
177
+ columnSpan: attributes.style?.layout?.columnSpan,
178
+ rowSpan: attributes.style?.layout?.rowSpan,
179
+ } );
180
+ const isInBounds = new GridRect( {
181
+ columnSpan: gridInfo.numColumns,
182
+ rowSpan: gridInfo.numRows,
183
+ } ).containsRect( rect );
184
+ return isInBounds;
185
+ },
186
+ onDragEnter( srcClientId ) {
187
+ const attributes = getBlockAttributes( srcClientId );
188
+ setHighlightedRect(
189
+ new GridRect( {
190
+ columnStart: column,
191
+ rowStart: row,
192
+ columnSpan: attributes.style?.layout?.columnSpan,
193
+ rowSpan: attributes.style?.layout?.rowSpan,
194
+ } )
195
+ );
196
+ },
197
+ onDragLeave() {
198
+ // onDragEnter can be called before onDragLeave if the user moves
199
+ // their mouse quickly, so only clear the highlight if it was set
200
+ // by this cell.
201
+ setHighlightedRect( ( prevHighlightedRect ) =>
202
+ prevHighlightedRect?.columnStart === column &&
203
+ prevHighlightedRect?.rowStart === row
204
+ ? null
205
+ : prevHighlightedRect
206
+ );
207
+ },
208
+ onDrop( srcClientId ) {
209
+ setHighlightedRect( null );
210
+ const attributes = getBlockAttributes( srcClientId );
211
+ updateBlockAttributes( srcClientId, {
212
+ style: {
213
+ ...attributes.style,
214
+ layout: {
215
+ ...attributes.style?.layout,
216
+ columnStart: column,
217
+ rowStart: row,
218
+ },
219
+ },
220
+ } );
221
+ __unstableMarkNextChangeAsNotPersistent();
222
+ moveBlocksToPosition(
223
+ [ srcClientId ],
224
+ gridClientId,
225
+ gridClientId,
226
+ getNumberOfBlocksBeforeCell( column, row )
227
+ );
228
+ },
229
+ } );
230
+
231
+ const isHighlighted = highlightedRect?.contains( column, row ) ?? false;
232
+
233
+ return (
234
+ <div
235
+ ref={ ref }
236
+ className={ clsx( 'block-editor-grid-visualizer__drop-zone', {
237
+ 'is-highlighted': isHighlighted,
238
+ } ) }
239
+ />
240
+ );
241
+ }
242
+
243
+ function useDropZoneWithValidation( {
244
+ validateDrag,
245
+ onDragEnter,
246
+ onDragLeave,
247
+ onDrop,
248
+ } ) {
249
+ const { getDraggedBlockClientIds } = useSelect( blockEditorStore );
250
+ return useDropZone( {
251
+ onDragEnter() {
252
+ const [ srcClientId ] = getDraggedBlockClientIds();
253
+ if ( srcClientId && validateDrag( srcClientId ) ) {
254
+ onDragEnter( srcClientId );
255
+ }
256
+ },
257
+ onDragLeave() {
258
+ onDragLeave();
259
+ },
260
+ onDrop() {
261
+ const [ srcClientId ] = getDraggedBlockClientIds();
262
+ if ( srcClientId && validateDrag( srcClientId ) ) {
263
+ onDrop( srcClientId );
264
+ }
265
+ },
266
+ } );
267
+ }
@@ -0,0 +1,4 @@
1
+ export { GridVisualizer } from './grid-visualizer';
2
+ export { GridItemResizer } from './grid-item-resizer';
3
+ export { GridItemMovers } from './grid-item-movers';
4
+ export { useGridLayoutSync } from './use-grid-layout-sync';
@@ -0,0 +1,63 @@
1
+ .block-editor-grid-visualizer {
2
+ // Specificity to override the z-index and pointer-events set by .components-popover.
3
+ &.block-editor-grid-visualizer.block-editor-grid-visualizer {
4
+ z-index: z-index(".block-editor-grid-visualizer");
5
+
6
+ .components-popover__content * {
7
+ pointer-events: none;
8
+ }
9
+
10
+ &.is-dropping-allowed {
11
+ .block-editor-grid-visualizer__drop-zone {
12
+ pointer-events: all;
13
+ }
14
+ }
15
+ }
16
+ }
17
+
18
+ .block-editor-grid-visualizer__grid {
19
+ display: grid;
20
+ }
21
+
22
+ .block-editor-grid-visualizer__cell {
23
+ align-items: center;
24
+ display: flex;
25
+ justify-content: center;
26
+ }
27
+
28
+ .block-editor-grid-visualizer__drop-zone {
29
+ background: rgba($gray-400, 0.1);
30
+ border: $border-width dotted $gray-300;
31
+ width: 100%;
32
+ height: 100%;
33
+
34
+ // Make drop zone 8x8 at minimum so that it's easier to drag into. This will overflow the parent.
35
+ min-width: $grid-unit-10;
36
+ min-height: $grid-unit-10;
37
+
38
+ &.is-highlighted {
39
+ background: var(--wp-admin-theme-color);
40
+ }
41
+ }
42
+
43
+ .block-editor-grid-item-resizer {
44
+ // Specificity to override the z-index and pointer-events set by .components-popover.
45
+ &.block-editor-grid-item-resizer.block-editor-grid-item-resizer {
46
+ z-index: z-index(".block-editor-grid-visualizer");
47
+
48
+ .components-popover__content * {
49
+ pointer-events: none;
50
+ }
51
+ }
52
+ }
53
+
54
+ .block-editor-grid-item-resizer__box {
55
+ border: $border-width solid var(--wp-admin-theme-color);
56
+
57
+ .components-resizable-box__handle {
58
+ // Specificity to override the pointer-events set by .components-popover.
59
+ &.components-resizable-box__handle.components-resizable-box__handle {
60
+ pointer-events: all;
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { store as blockEditorStore } from '../../store';
10
+
11
+ export function useGetNumberOfBlocksBeforeCell( gridClientId, numColumns ) {
12
+ const { getBlockOrder, getBlockAttributes } = useSelect( blockEditorStore );
13
+
14
+ const getNumberOfBlocksBeforeCell = ( column, row ) => {
15
+ const targetIndex = ( row - 1 ) * numColumns + column - 1;
16
+
17
+ let count = 0;
18
+ for ( const clientId of getBlockOrder( gridClientId ) ) {
19
+ const { columnStart, rowStart } =
20
+ getBlockAttributes( clientId ).style?.layout ?? {};
21
+ const cellIndex = ( rowStart - 1 ) * numColumns + columnStart - 1;
22
+ if ( cellIndex < targetIndex ) {
23
+ count++;
24
+ }
25
+ }
26
+ return count;
27
+ };
28
+
29
+ return getNumberOfBlocksBeforeCell;
30
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useDispatch, useSelect } from '@wordpress/data';
5
+ import { useEffect } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { store as blockEditorStore } from '../../store';
11
+ import { GridRect } from './utils';
12
+
13
+ export function useGridLayoutSync( { clientId: gridClientId } ) {
14
+ const { gridLayout, blockOrder } = useSelect(
15
+ ( select ) => {
16
+ const { getBlockAttributes, getBlockOrder } =
17
+ select( blockEditorStore );
18
+ return {
19
+ gridLayout: getBlockAttributes( gridClientId ).layout ?? {},
20
+ blockOrder: getBlockOrder( gridClientId ),
21
+ };
22
+ },
23
+ [ gridClientId ]
24
+ );
25
+
26
+ const { getBlockAttributes } = useSelect( blockEditorStore );
27
+ const { updateBlockAttributes, __unstableMarkNextChangeAsNotPersistent } =
28
+ useDispatch( blockEditorStore );
29
+
30
+ useEffect( () => {
31
+ const updates = {};
32
+
33
+ const { columnCount, rowCount = 2 } = gridLayout;
34
+ const isManualGrid = !! columnCount;
35
+
36
+ if ( isManualGrid ) {
37
+ const rects = [];
38
+ let cellsTaken = 0;
39
+
40
+ // Respect the position of blocks that already have a columnStart and rowStart value.
41
+ for ( const clientId of blockOrder ) {
42
+ const attributes = getBlockAttributes( clientId );
43
+ const {
44
+ columnStart,
45
+ rowStart,
46
+ columnSpan = 1,
47
+ rowSpan = 1,
48
+ } = attributes.style?.layout || {};
49
+ cellsTaken += columnSpan * rowSpan;
50
+ if ( ! columnStart || ! rowStart ) {
51
+ continue;
52
+ }
53
+ rects.push(
54
+ new GridRect( {
55
+ columnStart,
56
+ rowStart,
57
+ columnSpan,
58
+ rowSpan,
59
+ } )
60
+ );
61
+ }
62
+
63
+ // Ensure there's enough rows to fit all blocks.
64
+ const minimumNeededRows = Math.ceil( cellsTaken / columnCount );
65
+ if ( rowCount < minimumNeededRows ) {
66
+ updates[ gridClientId ] = {
67
+ layout: {
68
+ ...gridLayout,
69
+ rowCount: minimumNeededRows,
70
+ },
71
+ };
72
+ }
73
+
74
+ // When in manual mode, ensure that every block has a columnStart and rowStart value.
75
+ for ( const clientId of blockOrder ) {
76
+ const attributes = getBlockAttributes( clientId );
77
+ const { columnStart, rowStart, columnSpan, rowSpan } =
78
+ attributes.style?.layout || {};
79
+ if ( columnStart && rowStart ) {
80
+ continue;
81
+ }
82
+ const [ newColumnStart, newRowStart ] = getFirstEmptyCell(
83
+ rects,
84
+ columnCount,
85
+ minimumNeededRows,
86
+ columnSpan,
87
+ rowSpan
88
+ );
89
+ rects.push(
90
+ new GridRect( {
91
+ columnStart: newColumnStart,
92
+ rowStart: newRowStart,
93
+ columnSpan,
94
+ rowSpan,
95
+ } )
96
+ );
97
+ updates[ clientId ] = {
98
+ style: {
99
+ ...attributes.style,
100
+ layout: {
101
+ ...attributes.style?.layout,
102
+ columnStart: newColumnStart,
103
+ rowStart: newRowStart,
104
+ },
105
+ },
106
+ };
107
+ }
108
+ } else {
109
+ // When in auto mode, remove all of the columnStart and rowStart values.
110
+ for ( const clientId of blockOrder ) {
111
+ const attributes = getBlockAttributes( clientId );
112
+ const { columnStart, rowStart, ...layout } =
113
+ attributes.style?.layout || {};
114
+ // Only update attributes if columnStart or rowStart are set.
115
+ if ( columnStart || rowStart ) {
116
+ updates[ clientId ] = {
117
+ style: {
118
+ ...attributes.style,
119
+ layout,
120
+ },
121
+ };
122
+ }
123
+ }
124
+ }
125
+
126
+ if ( Object.keys( updates ).length ) {
127
+ __unstableMarkNextChangeAsNotPersistent();
128
+ updateBlockAttributes(
129
+ Object.keys( updates ),
130
+ updates,
131
+ /* uniqueByBlock: */ true
132
+ );
133
+ }
134
+ }, [
135
+ // Actual deps to sync:
136
+ gridClientId,
137
+ gridLayout,
138
+ blockOrder,
139
+ // Needed for linter:
140
+ __unstableMarkNextChangeAsNotPersistent,
141
+ getBlockAttributes,
142
+ updateBlockAttributes,
143
+ ] );
144
+ }
145
+
146
+ function getFirstEmptyCell(
147
+ rects,
148
+ columnCount,
149
+ rowCount,
150
+ columnSpan = 1,
151
+ rowSpan = 1
152
+ ) {
153
+ for ( let row = 1; row <= rowCount; row++ ) {
154
+ for ( let column = 1; column <= columnCount; column++ ) {
155
+ const rect = new GridRect( {
156
+ columnStart: column,
157
+ rowStart: row,
158
+ columnSpan,
159
+ rowSpan,
160
+ } );
161
+ if ( ! rects.some( ( r ) => r.intersectsRect( rect ) ) ) {
162
+ return [ column, row ];
163
+ }
164
+ }
165
+ }
166
+ return [ 1, 1 ];
167
+ }