@automattic/newspack-blocks 4.31.1 → 4.32.0-alpha.2

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 (97) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/composer.lock +73 -994
  3. package/dist/blocks/checkout-button/block.json +3 -0
  4. package/dist/carousel/view-rtl.css +1 -1
  5. package/dist/carousel/view.asset.php +1 -1
  6. package/dist/carousel/view.css +1 -1
  7. package/dist/editor-rtl.css +20 -16
  8. package/dist/editor.asset.php +1 -1
  9. package/dist/editor.css +20 -16
  10. package/dist/editor.js +32 -28
  11. package/dist/homepage-articles/view-rtl.css +1 -1
  12. package/dist/homepage-articles/view.asset.php +1 -1
  13. package/dist/homepage-articles/view.css +1 -1
  14. package/dist/modal.asset.php +1 -1
  15. package/dist/modal.js +1 -1
  16. package/dist/modalCheckout-rtl.css +1 -1
  17. package/dist/modalCheckout.asset.php +1 -1
  18. package/dist/modalCheckout.css +1 -1
  19. package/dist/modalCheckout.js +1 -1
  20. package/dist/tiersBased.asset.php +1 -1
  21. package/dist/tiersBased.js +1 -1
  22. package/includes/class-modal-checkout.php +354 -5
  23. package/includes/modal-checkout/class-checkout-data.php +30 -6
  24. package/languages/newspack-blocks-de_DE-37552bb09e2a9fceec1970e3c6d46557.json +1 -1
  25. package/languages/newspack-blocks-de_DE-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  26. package/languages/newspack-blocks-de_DE-5c566531829f64486c13d7d8df1d6151.json +1 -0
  27. package/languages/newspack-blocks-de_DE-7c29764d5ef91723f5ce454f66431b9e.json +1 -1
  28. package/languages/newspack-blocks-de_DE-eeb28f99adedd5fad3081d2756d6f4a4.json +1 -1
  29. package/languages/newspack-blocks-de_DE-fef17c08abbcd3d3c4cdc0fd13a9bff5.json +1 -0
  30. package/languages/newspack-blocks-de_DE.po +427 -322
  31. package/languages/newspack-blocks-es_ES-37552bb09e2a9fceec1970e3c6d46557.json +1 -1
  32. package/languages/newspack-blocks-es_ES-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  33. package/languages/newspack-blocks-es_ES-5c566531829f64486c13d7d8df1d6151.json +1 -0
  34. package/languages/newspack-blocks-es_ES-7c29764d5ef91723f5ce454f66431b9e.json +1 -1
  35. package/languages/newspack-blocks-es_ES-eeb28f99adedd5fad3081d2756d6f4a4.json +1 -1
  36. package/languages/newspack-blocks-es_ES-fef17c08abbcd3d3c4cdc0fd13a9bff5.json +1 -0
  37. package/languages/newspack-blocks-es_ES.po +425 -322
  38. package/languages/newspack-blocks-fr_BE-37552bb09e2a9fceec1970e3c6d46557.json +1 -1
  39. package/languages/newspack-blocks-fr_BE-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  40. package/languages/newspack-blocks-fr_BE-5c566531829f64486c13d7d8df1d6151.json +1 -0
  41. package/languages/newspack-blocks-fr_BE-7c29764d5ef91723f5ce454f66431b9e.json +1 -1
  42. package/languages/newspack-blocks-fr_BE-eeb28f99adedd5fad3081d2756d6f4a4.json +1 -1
  43. package/languages/newspack-blocks-fr_BE-fef17c08abbcd3d3c4cdc0fd13a9bff5.json +1 -0
  44. package/languages/newspack-blocks-fr_BE.po +427 -322
  45. package/languages/newspack-blocks-nb_NO-37552bb09e2a9fceec1970e3c6d46557.json +1 -1
  46. package/languages/newspack-blocks-nb_NO-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  47. package/languages/newspack-blocks-nb_NO-5c566531829f64486c13d7d8df1d6151.json +1 -0
  48. package/languages/newspack-blocks-nb_NO-7c29764d5ef91723f5ce454f66431b9e.json +1 -1
  49. package/languages/newspack-blocks-nb_NO-eeb28f99adedd5fad3081d2756d6f4a4.json +1 -1
  50. package/languages/newspack-blocks-nb_NO-fef17c08abbcd3d3c4cdc0fd13a9bff5.json +1 -0
  51. package/languages/newspack-blocks-nb_NO.po +426 -322
  52. package/languages/newspack-blocks-pt_PT-37552bb09e2a9fceec1970e3c6d46557.json +1 -1
  53. package/languages/newspack-blocks-pt_PT-53e2a1d5945b8d2b1c35e81ae1e532f3.json +1 -1
  54. package/languages/newspack-blocks-pt_PT-5c566531829f64486c13d7d8df1d6151.json +1 -0
  55. package/languages/newspack-blocks-pt_PT-7c29764d5ef91723f5ce454f66431b9e.json +1 -1
  56. package/languages/newspack-blocks-pt_PT-eeb28f99adedd5fad3081d2756d6f4a4.json +1 -1
  57. package/languages/newspack-blocks-pt_PT-fef17c08abbcd3d3c4cdc0fd13a9bff5.json +1 -0
  58. package/languages/newspack-blocks-pt_PT.po +426 -322
  59. package/languages/newspack-blocks.pot +506 -379
  60. package/newspack-blocks.php +2 -2
  61. package/package.json +3 -2
  62. package/src/blocks/carousel/edit.js +1 -1
  63. package/src/blocks/carousel/view.scss +5 -1
  64. package/src/blocks/checkout-button/block.json +3 -0
  65. package/src/blocks/checkout-button/edit.js +32 -5
  66. package/src/blocks/checkout-button/view.php +37 -0
  67. package/src/blocks/donate/consts.ts +2 -1
  68. package/src/blocks/donate/frequency-slugs.js +10 -0
  69. package/src/blocks/donate/tiers-based/view.ts +8 -0
  70. package/src/blocks/homepage-articles/edit.tsx +1 -1
  71. package/src/blocks/homepage-articles/editor.scss +2 -1
  72. package/src/blocks/homepage-articles/view.scss +15 -5
  73. package/src/components/autocomplete-tokenfield.js +21 -7
  74. package/src/components/autocomplete-tokenfield.scss +1 -1
  75. package/src/components/autocomplete-tokenfield.test.js +58 -0
  76. package/src/components/query-controls.js +28 -6
  77. package/src/components/query-controls.test.js +56 -0
  78. package/src/components/reorder-modal.js +382 -0
  79. package/src/components/reorder-modal.scss +87 -0
  80. package/src/components/reorder-modal.test.js +487 -0
  81. package/src/components/specific-posts-control.js +91 -0
  82. package/src/components/specific-posts-control.scss +5 -0
  83. package/src/components/specific-posts-control.test.js +173 -0
  84. package/src/modal-checkout/analytics/ga4/utils/index.js +1 -0
  85. package/src/modal-checkout/checkout-button-trigger.js +29 -2
  86. package/src/modal-checkout/checkout-button-trigger.test.js +37 -0
  87. package/src/modal-checkout/checkout.scss +28 -0
  88. package/src/modal-checkout/donate-trigger.js +134 -0
  89. package/src/modal-checkout/donate-trigger.test.js +233 -0
  90. package/src/modal-checkout/index.js +91 -0
  91. package/src/modal-checkout/modal.js +129 -45
  92. package/src/modal-checkout/utils.test.js +49 -0
  93. package/src/shared/js/tiers-based-ready.js +26 -0
  94. package/vendor/autoload.php +1 -1
  95. package/vendor/composer/autoload_real.php +4 -4
  96. package/vendor/composer/autoload_static.php +2 -2
  97. package/vendor/composer/installed.php +2 -2
@@ -0,0 +1,382 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import classnames from 'classnames';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __, _n, sprintf } from '@wordpress/i18n';
10
+ import { speak } from '@wordpress/a11y';
11
+ import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
12
+ import {
13
+ Button,
14
+ Modal,
15
+ Notice,
16
+ Spinner,
17
+ __experimentalConfirmDialog as ConfirmDialog, // eslint-disable-line @wordpress/no-unsafe-wp-apis
18
+ } from '@wordpress/components';
19
+ import { Icon, chevronDown, chevronUp, close, dragHandle } from '@wordpress/icons';
20
+ import { Card } from '@wordpress/ui';
21
+
22
+ /**
23
+ * Internal dependencies
24
+ */
25
+ import './reorder-modal.scss';
26
+
27
+ export const moveItem = ( items, from, to ) => {
28
+ if ( from === to || from < 0 || to < 0 || from >= items.length || to >= items.length ) {
29
+ return items;
30
+ }
31
+ const next = [ ...items ];
32
+ const [ moved ] = next.splice( from, 1 );
33
+ next.splice( to, 0, moved );
34
+ return next;
35
+ };
36
+
37
+ const OVERLAY_SELECTOR = '.components-modal__screen-overlay';
38
+
39
+ // The overlay carries the dismissal handling and the drop target, and `Modal`
40
+ // forwards its ref to it today. Deriving it from whatever node arrives keeps
41
+ // both working if core ever forwards that ref somewhere else.
42
+ export const findOverlay = node =>
43
+ node?.closest?.( OVERLAY_SELECTOR ) ||
44
+ node?.querySelector?.( OVERLAY_SELECTOR ) ||
45
+ node?.ownerDocument?.querySelector( OVERLAY_SELECTOR ) ||
46
+ null;
47
+
48
+ // Disabled controls stay focusable here, so they report `aria-disabled` rather
49
+ // than the native attribute.
50
+ const isAvailable = button => !! button && ! button.matches( ':disabled, [aria-disabled="true"]' );
51
+
52
+ // Speech input matches on the visible label, so it has to open the accessible
53
+ // name. Composing both from one translated string keeps that true in every locale.
54
+ const nameWithTitle = ( label, title ) =>
55
+ sprintf(
56
+ /* translators: keep %1$s first: it repeats the button's visible label, which speech input matches on. 1: the control's visible label. 2: title of the content being moved. */
57
+ __( '%1$s: %2$s', 'newspack-blocks' ),
58
+ label,
59
+ title
60
+ );
61
+
62
+ const ReorderModal = ( { title, ids, fetchItems, onSave, onClose } ) => {
63
+ const moveUpLabel = __( 'Move Up', 'newspack-blocks' );
64
+ const moveDownLabel = __( 'Move Down', 'newspack-blocks' );
65
+ const [ items, setItems ] = useState( null );
66
+ const [ dragIndex, setDragIndex ] = useState( null );
67
+ const [ hasFetchError, setHasFetchError ] = useState( false );
68
+ const [ isConfirmingDiscard, setIsConfirmingDiscard ] = useState( false );
69
+ const listRef = useRef( null );
70
+ const overlayRef = useRef( null );
71
+ const pendingFocus = useRef( null );
72
+ const closeRef = useRef( null );
73
+ const preDragItems = useRef( null );
74
+ const dragBlocked = useRef( false );
75
+ const captureOverlay = useCallback( node => {
76
+ overlayRef.current = findOverlay( node );
77
+ }, [] );
78
+
79
+ // The order the modal opened with. Snapshotted so a change to `ids` from
80
+ // elsewhere in the editor cannot desynchronise it from `items`.
81
+ const openedWith = useRef( ids );
82
+
83
+ const isDirty = !! items && items.some( ( item, index ) => item.id !== openedWith.current[ index ] );
84
+
85
+ useEffect( () => {
86
+ let cancelled = false;
87
+ const openIds = openedWith.current;
88
+ const withLabels = labels => openIds.map( id => ( { id, label: labels[ id ] || __( '(no title)', 'newspack-blocks' ) } ) );
89
+
90
+ fetchItems( openIds )
91
+ .then( results => {
92
+ if ( cancelled ) {
93
+ return;
94
+ }
95
+ const labels = {};
96
+ results.forEach( ( { value, label } ) => {
97
+ labels[ value ] = label;
98
+ } );
99
+ const loaded = withLabels( labels );
100
+ setItems( loaded );
101
+ speak(
102
+ sprintf(
103
+ /* translators: %d: number of items in the list. */
104
+ _n( '%d item ready to reorder.', '%d items ready to reorder.', loaded.length, 'newspack-blocks' ),
105
+ loaded.length
106
+ )
107
+ );
108
+ } )
109
+ .catch( error => {
110
+ // eslint-disable-next-line no-console
111
+ console.error( 'Newspack Blocks: could not prepare the content to reorder.', error );
112
+ if ( ! cancelled ) {
113
+ setHasFetchError( true );
114
+ // Only to leave the loading state: the error view lists nothing.
115
+ setItems( [] );
116
+ }
117
+ } );
118
+
119
+ return () => {
120
+ cancelled = true;
121
+ };
122
+ }, [] ); // eslint-disable-line react-hooks/exhaustive-deps
123
+
124
+ // A chevron that reaches an end of the list stops accepting moves, so send
125
+ // focus to the row's other chevron.
126
+ useEffect( () => {
127
+ const pending = pendingFocus.current;
128
+ pendingFocus.current = null;
129
+ if ( ! pending || ! listRef.current ) {
130
+ return;
131
+ }
132
+ const row = listRef.current.querySelector( `[data-item-id="${ pending.id }"]` );
133
+ if ( ! row ) {
134
+ return;
135
+ }
136
+ const preferred = row.querySelector( `[data-direction="${ pending.direction }"]` );
137
+ const fallback = row.querySelector( `[data-direction="${ 'up' === pending.direction ? 'down' : 'up' }"]` );
138
+ if ( isAvailable( preferred ) ) {
139
+ preferred.focus();
140
+ } else if ( isAvailable( fallback ) ) {
141
+ fallback.focus();
142
+ }
143
+ }, [ items ] );
144
+
145
+ const moveTo = ( from, to, direction ) => {
146
+ const next = moveItem( items, from, to );
147
+ if ( next === items ) {
148
+ return;
149
+ }
150
+ pendingFocus.current = { id: items[ from ].id, direction };
151
+ setItems( next );
152
+ speak(
153
+ sprintf(
154
+ /* translators: 1: title of the moved item. 2: its new position. 3: total number of items. */
155
+ __( '%1$s moved to position %2$d of %3$d.', 'newspack-blocks' ),
156
+ items[ from ].label,
157
+ to + 1,
158
+ items.length
159
+ )
160
+ );
161
+ };
162
+
163
+ const requestClose = () => {
164
+ if ( isDirty ) {
165
+ setIsConfirmingDiscard( true );
166
+ return;
167
+ }
168
+ onClose();
169
+ };
170
+
171
+ useEffect( () => {
172
+ closeRef.current = requestClose;
173
+ }, [ requestClose ] );
174
+
175
+ const handleKeyDown = event => {
176
+ if ( 'Escape' !== event.key || event.defaultPrevented ) {
177
+ return;
178
+ }
179
+ event.preventDefault();
180
+ requestClose();
181
+ };
182
+
183
+ // `Modal` plays its exit animation before it reports a close request, which
184
+ // would fade the modal out and back in whenever the confirmation vetoes one.
185
+ // Its own dismissal handlers are off, so drive the overlay press here.
186
+ useEffect( () => {
187
+ const overlay = overlayRef.current;
188
+ if ( ! overlay ) {
189
+ return;
190
+ }
191
+ let pressedOverlay = false;
192
+ const onPointerDown = event => {
193
+ pressedOverlay = event.target === overlay;
194
+ if ( pressedOverlay ) {
195
+ event.preventDefault();
196
+ }
197
+ };
198
+ const onPointerUp = event => {
199
+ const dismisses = pressedOverlay && 0 === event.button && event.target === overlay;
200
+ pressedOverlay = false;
201
+ if ( dismisses ) {
202
+ closeRef.current();
203
+ }
204
+ };
205
+ overlay.addEventListener( 'pointerdown', onPointerDown );
206
+ overlay.addEventListener( 'pointerup', onPointerUp );
207
+ return () => {
208
+ overlay.removeEventListener( 'pointerdown', onPointerDown );
209
+ overlay.removeEventListener( 'pointerup', onPointerUp );
210
+ };
211
+ }, [] );
212
+
213
+ // Rows are the only drop targets, and the frame's own padding is not one, so a
214
+ // release a few pixels wide of the list would report a cancelled drag and undo
215
+ // the reorder. Accepting the drop across the whole dialog leaves "cancelled"
216
+ // meaning only Escape or a release outside it.
217
+ useEffect( () => {
218
+ const frame = overlayRef.current?.querySelector( '.components-modal__frame' );
219
+ if ( ! frame ) {
220
+ return;
221
+ }
222
+ const accept = event => event.preventDefault();
223
+ frame.addEventListener( 'dragover', accept );
224
+ frame.addEventListener( 'drop', accept );
225
+ return () => {
226
+ frame.removeEventListener( 'dragover', accept );
227
+ frame.removeEventListener( 'drop', accept );
228
+ };
229
+ }, [] );
230
+
231
+ const handleDragOver = ( event, index ) => {
232
+ event.preventDefault();
233
+ if ( null === dragIndex ) {
234
+ return;
235
+ }
236
+ event.dataTransfer.dropEffect = 'move';
237
+ if ( dragIndex === index ) {
238
+ return;
239
+ }
240
+ setItems( moveItem( items, dragIndex, index ) );
241
+ setDragIndex( index );
242
+ };
243
+
244
+ return (
245
+ <>
246
+ <Modal
247
+ ref={ captureOverlay }
248
+ title={ title }
249
+ onRequestClose={ requestClose }
250
+ onKeyDown={ handleKeyDown }
251
+ isDismissible={ false }
252
+ shouldCloseOnEsc={ false }
253
+ shouldCloseOnClickOutside={ false }
254
+ headerActions={ <Button size="compact" icon={ close } label={ __( 'Close', 'newspack-blocks' ) } onClick={ requestClose } /> }
255
+ size="medium"
256
+ className="newspack-blocks-reorder-modal"
257
+ >
258
+ <div className="newspack-blocks-reorder-modal__body" aria-busy={ ! items }>
259
+ { ! items && (
260
+ <div className="newspack-blocks-reorder-modal__loading">
261
+ <Spinner />
262
+ <span className="screen-reader-text">{ __( 'Loading content…', 'newspack-blocks' ) }</span>
263
+ </div>
264
+ ) }
265
+ { items && hasFetchError && (
266
+ <>
267
+ <Notice status="error" isDismissible={ false } className="newspack-blocks-reorder-modal__notice">
268
+ { __( 'The content could not be loaded, so the order cannot be saved.', 'newspack-blocks' ) }
269
+ </Notice>
270
+ <div className="newspack-blocks-reorder-modal__footer">
271
+ <Button variant="tertiary" onClick={ requestClose }>
272
+ { __( 'Cancel', 'newspack-blocks' ) }
273
+ </Button>
274
+ </div>
275
+ </>
276
+ ) }
277
+ { items && ! hasFetchError && (
278
+ <>
279
+ { /* `list-style: none` drops list semantics in Safari, so the role is restated. */ }
280
+ { /* eslint-disable-next-line jsx-a11y/no-redundant-roles */ }
281
+ <ul className="newspack-blocks-reorder-modal__list" role="list" ref={ listRef }>
282
+ { items.map( ( item, index ) => (
283
+ <Card.Root
284
+ key={ item.id }
285
+ render={ <li /> }
286
+ data-item-id={ item.id }
287
+ className={ classnames( 'newspack-blocks-reorder-modal__item', {
288
+ 'is-dragging': dragIndex === index,
289
+ } ) }
290
+ draggable
291
+ // `dragstart` always fires at the draggable row, never at the
292
+ // chevron inside it, so the grab point is recorded separately.
293
+ onPointerDown={ event => {
294
+ dragBlocked.current = !! event.target.closest?.( 'button' );
295
+ } }
296
+ onDragStart={ event => {
297
+ if ( dragBlocked.current ) {
298
+ event.preventDefault();
299
+ return;
300
+ }
301
+ preDragItems.current = items;
302
+ // Firefox and Safari will not start a drag until the payload is set.
303
+ event.dataTransfer.setData( 'text/plain', String( item.id ) );
304
+ event.dataTransfer.effectAllowed = 'move';
305
+ setDragIndex( index );
306
+ } }
307
+ onDragOver={ event => handleDragOver( event, index ) }
308
+ onDragEnd={ event => {
309
+ // A cancelled drag reports no drop effect: put the order back.
310
+ if ( 'none' === event.dataTransfer.dropEffect && preDragItems.current ) {
311
+ setItems( preDragItems.current );
312
+ }
313
+ preDragItems.current = null;
314
+ setDragIndex( null );
315
+ } }
316
+ onDrop={ event => event.preventDefault() }
317
+ >
318
+ <span className="newspack-blocks-reorder-modal__grip" aria-hidden="true">
319
+ <Icon icon={ dragHandle } />
320
+ </span>
321
+ <span className="newspack-blocks-reorder-modal__move">
322
+ <Button
323
+ icon={ chevronUp }
324
+ size="small"
325
+ data-direction="up"
326
+ disabled={ 0 === index }
327
+ accessibleWhenDisabled
328
+ label={ moveUpLabel }
329
+ aria-label={ nameWithTitle( moveUpLabel, item.label ) }
330
+ onClick={ () => moveTo( index, index - 1, 'up' ) }
331
+ />
332
+ <Button
333
+ icon={ chevronDown }
334
+ size="small"
335
+ data-direction="down"
336
+ disabled={ index === items.length - 1 }
337
+ accessibleWhenDisabled
338
+ label={ moveDownLabel }
339
+ aria-label={ nameWithTitle( moveDownLabel, item.label ) }
340
+ onClick={ () => moveTo( index, index + 1, 'down' ) }
341
+ />
342
+ </span>
343
+ <Card.Content className="newspack-blocks-reorder-modal__title" title={ item.label }>
344
+ { item.label }
345
+ </Card.Content>
346
+ </Card.Root>
347
+ ) ) }
348
+ </ul>
349
+ <div className="newspack-blocks-reorder-modal__footer">
350
+ <Button variant="tertiary" onClick={ requestClose }>
351
+ { __( 'Cancel', 'newspack-blocks' ) }
352
+ </Button>
353
+ <Button
354
+ variant="primary"
355
+ disabled={ ! isDirty }
356
+ accessibleWhenDisabled
357
+ onClick={ () => onSave( items.map( item => item.id ) ) }
358
+ >
359
+ { __( 'Save', 'newspack-blocks' ) }
360
+ </Button>
361
+ </div>
362
+ </>
363
+ ) }
364
+ </div>
365
+ </Modal>
366
+ { isConfirmingDiscard && (
367
+ <ConfirmDialog
368
+ isOpen
369
+ contentLabel={ __( 'Discard the new order?', 'newspack-blocks' ) }
370
+ confirmButtonText={ __( 'Discard', 'newspack-blocks' ) }
371
+ cancelButtonText={ __( 'Keep editing', 'newspack-blocks' ) }
372
+ onConfirm={ onClose }
373
+ onCancel={ () => setIsConfirmingDiscard( false ) }
374
+ >
375
+ { __( 'The order you set will be lost.', 'newspack-blocks' ) }
376
+ </ConfirmDialog>
377
+ ) }
378
+ </>
379
+ );
380
+ };
381
+
382
+ export default ReorderModal;
@@ -0,0 +1,87 @@
1
+ @use "~@wordpress/base-styles/colors" as wp-colors;
2
+ @use "~@wordpress/base-styles/mixins" as wp-mixins;
3
+ @use "~@wordpress/base-styles/variables" as wp-vars;
4
+
5
+ .newspack-blocks-reorder-modal {
6
+ &__body {
7
+ display: flex;
8
+ flex-direction: column;
9
+ min-height: calc(#{wp-vars.$grid-unit} * 23);
10
+ }
11
+
12
+ &__loading {
13
+ align-items: center;
14
+ display: flex;
15
+ flex: 1;
16
+ justify-content: center;
17
+ }
18
+
19
+ &__notice {
20
+ margin: 0 0 wp-vars.$grid-unit-10;
21
+ }
22
+
23
+ &__list {
24
+ display: flex;
25
+ flex-direction: column;
26
+ gap: wp-vars.$grid-unit-10;
27
+ list-style: none;
28
+ margin: 0;
29
+ padding: 0;
30
+ }
31
+
32
+ &__item {
33
+ --newspack-blocks-reorder-divider: #{wp-colors.$gray-100};
34
+ --wp-ui-card-padding: #{wp-vars.$grid-unit-15};
35
+
36
+ align-items: stretch;
37
+ cursor: grab;
38
+ flex-direction: row;
39
+ margin: 0;
40
+
41
+ &:focus-within,
42
+ &:hover {
43
+ --newspack-blocks-reorder-divider: #{wp-colors.$gray-300};
44
+
45
+ border-color: wp-colors.$gray-300;
46
+ }
47
+
48
+ &.is-dragging {
49
+ cursor: grabbing;
50
+ opacity: 0.5;
51
+ }
52
+ }
53
+
54
+ &__grip {
55
+ align-items: center;
56
+ color: wp-colors.$gray-700;
57
+ display: flex;
58
+ padding-left: wp-vars.$grid-unit-05;
59
+ }
60
+
61
+ &__move {
62
+ align-items: center;
63
+ border-right: 1px solid var(--newspack-blocks-reorder-divider);
64
+ display: flex;
65
+ flex-direction: column;
66
+ padding: wp-vars.$grid-unit-05;
67
+ }
68
+
69
+ &__title {
70
+ @include wp-mixins.heading-large();
71
+
72
+ align-self: center;
73
+ flex: 1;
74
+ min-width: 0;
75
+ overflow: hidden;
76
+ text-overflow: ellipsis;
77
+ white-space: nowrap;
78
+ }
79
+
80
+ &__footer {
81
+ display: flex;
82
+ gap: wp-vars.$grid-unit-10;
83
+ justify-content: flex-end;
84
+ margin-top: auto;
85
+ padding-top: wp-vars.$grid-unit-30;
86
+ }
87
+ }