newspack-components 4.10.0 → 4.11.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # newspack-components [4.11.0](https://github.com/Automattic/newspack-workspace/compare/newspack-components@4.10.0...newspack-components@4.11.0) (2026-10-14)
2
+
3
+
4
+ ### Features
5
+
6
+ * **newsletters:** show premium lists in the subscription form ([#1291](https://github.com/Automattic/newspack-workspace/issues/1291)) ([456d762](https://github.com/Automattic/newspack-workspace/commit/456d762ba465aafc6e5be08ed47836acafaad1a6))
7
+
1
8
  # newspack-components [4.10.0](https://github.com/Automattic/newspack-workspace/compare/newspack-components@4.9.2...newspack-components@4.10.0) (2026-10-12)
2
9
 
3
10
 
package/DEVELOPMENT.md CHANGED
@@ -93,6 +93,7 @@ When building a screen, use the **spacing scale** (8px unit: 16, 24, 32, 48, 64)
93
93
  - **`FormTokenField`** - Token input field for tags/categories; prefer the Newspack component over the core `FormTokenField` because it also supports a `description` prop for help text like other controls.
94
94
  - **`ImageUpload`** - Image upload and selection component
95
95
  - **`RadioControl`** - Radio button group control
96
+ - **`ReorderModal`** - Modal for putting a short list of items in order, by dragging or with move buttons; applies on Save
96
97
  - **`TextControl`** - Text input with Newspack styling and required field support
97
98
 
98
99
  ### Content Components
@@ -11,7 +11,7 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
11
11
  var _components = require("@wordpress/components");
12
12
  var _element = require("@wordpress/element");
13
13
  var _ui = require("@wordpress/ui");
14
- var _ = require("../");
14
+ var _card = _interopRequireDefault(require("../card"));
15
15
  require("./style.scss");
16
16
  var _classnames = _interopRequireDefault(require("classnames"));
17
17
  var _jsxRuntime = require("react/jsx-runtime");
@@ -388,7 +388,7 @@ var CardSortableList = function CardSortableList(_ref) {
388
388
  var _item$badge, _item$badge$intent;
389
389
  var onDraggableStart = _ref2.onDraggableStart,
390
390
  onDraggableEnd = _ref2.onDraggableEnd;
391
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_.Card, {
391
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_card["default"], {
392
392
  isSmall: true,
393
393
  draggable: true,
394
394
  onDragStart: onDraggableStart,
@@ -413,7 +413,10 @@ var CardSortableList = function CardSortableList(_ref) {
413
413
  }, item.onToggleChange !== undefined && {
414
414
  actionType: 'toggle',
415
415
  isActive: item.toggleChecked,
416
- onToggle: item.onToggleChange
416
+ onToggle: item.onToggleChange,
417
+ // Names the toggle for assistive tech. The card hides this
418
+ // label, since the header already shows the title.
419
+ title: item.title
417
420
  }), item.actions !== undefined && {
418
421
  actions: item.actions
419
422
  })
@@ -47,4 +47,25 @@ describe('CardSortableList', function () {
47
47
  // An unguarded Badge would still emit an empty span, so assert on element children.
48
48
  expect(container.querySelector('h3').children).toHaveLength(0);
49
49
  });
50
+ it('names each toggle after its item', function () {
51
+ (0, _react.render)(/*#__PURE__*/(0, _jsxRuntime.jsx)(_["default"], {
52
+ items: [{
53
+ id: 1,
54
+ title: 'Homepage',
55
+ toggleChecked: true,
56
+ onToggleChange: function onToggleChange() {}
57
+ }, {
58
+ id: 2,
59
+ title: 'About',
60
+ toggleChecked: false,
61
+ onToggleChange: function onToggleChange() {}
62
+ }]
63
+ }));
64
+ expect(_react.screen.getByRole('checkbox', {
65
+ name: 'Homepage'
66
+ })).toBeChecked();
67
+ expect(_react.screen.getByRole('checkbox', {
68
+ name: 'About'
69
+ })).not.toBeChecked();
70
+ });
50
71
  });
@@ -6,6 +6,14 @@ $drop-duration: 400ms; /* must match DROP_ANIMATION_DURATION in index.tsx */
6
6
  &__item {
7
7
  cursor: grab;
8
8
 
9
+ /*
10
+ * Keep a card's title in the case it was written in. The block editor
11
+ * sidebar uppercases every h3 (`.interface-complementary-area h3`).
12
+ */
13
+ .newspack-card--core__header-content h3 {
14
+ text-transform: none;
15
+ }
16
+
9
17
  /*
10
18
  * Animate items sliding to fill/make space around the dragged item.
11
19
  */
package/dist/cjs/index.js CHANGED
@@ -250,6 +250,12 @@ Object.defineProperty(exports, "RadioControl", {
250
250
  return _radioControl["default"];
251
251
  }
252
252
  });
253
+ Object.defineProperty(exports, "ReorderModal", {
254
+ enumerable: true,
255
+ get: function get() {
256
+ return _reorderModal["default"];
257
+ }
258
+ });
253
259
  Object.defineProperty(exports, "Router", {
254
260
  enumerable: true,
255
261
  get: function get() {
@@ -436,6 +442,7 @@ var _pluginToggle = _interopRequireDefault(require("./plugin-toggle"));
436
442
  var _popover = _interopRequireDefault(require("./popover"));
437
443
  var _progressBar = _interopRequireDefault(require("./progress-bar"));
438
444
  var _radioControl = _interopRequireDefault(require("./radio-control"));
445
+ var _reorderModal = _interopRequireDefault(require("./reorder-modal"));
439
446
  var _sectionHeader = _interopRequireDefault(require("./section-header"));
440
447
  var _selectControl = _interopRequireDefault(require("./select-control"));
441
448
  var _settings = _interopRequireDefault(require("./settings"));
@@ -0,0 +1,521 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.moveItem = exports.findOverlay = exports["default"] = void 0;
8
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
10
+ var _classnames = _interopRequireDefault(require("classnames"));
11
+ var _i18n = require("@wordpress/i18n");
12
+ var _a11y = require("@wordpress/a11y");
13
+ var _element = require("@wordpress/element");
14
+ var _compose = require("@wordpress/compose");
15
+ var _components = require("@wordpress/components");
16
+ var _icons = require("@wordpress/icons");
17
+ var _ui = require("@wordpress/ui");
18
+ require("./style.scss");
19
+ var _jsxRuntime = require("react/jsx-runtime");
20
+ /**
21
+ * External dependencies
22
+ */
23
+
24
+ /**
25
+ * WordPress dependencies
26
+ */
27
+
28
+ /**
29
+ * Internal dependencies
30
+ */
31
+
32
+ var moveItem = exports.moveItem = function moveItem(items, from, to) {
33
+ if (from === to || from < 0 || to < 0 || from >= items.length || to >= items.length) {
34
+ return items;
35
+ }
36
+ var next = (0, _toConsumableArray2["default"])(items);
37
+ var _next$splice = next.splice(from, 1),
38
+ _next$splice2 = (0, _slicedToArray2["default"])(_next$splice, 1),
39
+ moved = _next$splice2[0];
40
+ next.splice(to, 0, moved);
41
+ return next;
42
+ };
43
+ var OVERLAY_SELECTOR = '.components-modal__screen-overlay';
44
+
45
+ // The overlay carries the dismissal handling and the drop target, and `Modal`
46
+ // forwards its ref to it today. Deriving it from whatever node arrives keeps
47
+ // both working if core ever forwards that ref somewhere else.
48
+ var findOverlay = exports.findOverlay = function findOverlay(node) {
49
+ var _node$closest, _node$querySelector, _node$ownerDocument;
50
+ return (node === null || node === void 0 || (_node$closest = node.closest) === null || _node$closest === void 0 ? void 0 : _node$closest.call(node, OVERLAY_SELECTOR)) || (node === null || node === void 0 || (_node$querySelector = node.querySelector) === null || _node$querySelector === void 0 ? void 0 : _node$querySelector.call(node, OVERLAY_SELECTOR)) || (node === null || node === void 0 || (_node$ownerDocument = node.ownerDocument) === null || _node$ownerDocument === void 0 ? void 0 : _node$ownerDocument.querySelector(OVERLAY_SELECTOR)) || null;
51
+ };
52
+
53
+ // Disabled controls stay focusable here, so they report `aria-disabled` rather
54
+ // than the native attribute.
55
+ var isAvailable = function isAvailable(button) {
56
+ return !!button && !button.matches(':disabled, [aria-disabled="true"]');
57
+ };
58
+
59
+ // Speech input matches on the visible label, so it has to open the accessible
60
+ // name. Composing both from one translated string keeps that true in every locale.
61
+ var nameWithTitle = function nameWithTitle(label, title) {
62
+ return (0, _i18n.sprintf)(/* 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. */
63
+ (0, _i18n.__)('%1$s: %2$s', 'newspack-plugin'), label, title);
64
+ };
65
+
66
+ /**
67
+ * Put a short list of items in order and, optionally, choose which are on.
68
+ * Changes apply on Save.
69
+ *
70
+ * @param {Object} props Component props.
71
+ * @param {string} props.title Modal title.
72
+ * @param {Array} props.ids Item IDs, in their current order.
73
+ * @param {Function} props.fetchItems Resolves the IDs to `{ value, label, badge? }`, where `badge` is `{ label, tooltip? }`.
74
+ * @param {Function} props.onSave Called with the new order and, when `enabledIds` is given, the enabled IDs: those
75
+ * on, in that order, then any from `enabledIds` the modal had no row for, kept as they were.
76
+ * @param {Function} props.onClose Called when the modal closes without saving.
77
+ * @param {Array} props.enabledIds Optional. Gives each row an on/off toggle, starting on for these IDs. Save is held while none is on.
78
+ * @param {string} props.description Optional. Introduces the list, above it.
79
+ * @param {string} props.noneOnHelp Why Save is held while no toggle is on.
80
+ */
81
+ var _ReorderModal = function ReorderModal(_ref) {
82
+ var title = _ref.title,
83
+ ids = _ref.ids,
84
+ fetchItems = _ref.fetchItems,
85
+ onSave = _ref.onSave,
86
+ onClose = _ref.onClose,
87
+ enabledIds = _ref.enabledIds,
88
+ description = _ref.description,
89
+ _ref$noneOnHelp = _ref.noneOnHelp,
90
+ noneOnHelp = _ref$noneOnHelp === void 0 ? (0, _i18n.__)('Turn on at least one item to save.', 'newspack-plugin') : _ref$noneOnHelp;
91
+ var hasToggles = Array.isArray(enabledIds);
92
+ var instanceId = (0, _compose.useInstanceId)(_ReorderModal, 'newspack-reorder-modal');
93
+ var moveUpLabel = (0, _i18n.__)('Move Up', 'newspack-plugin');
94
+ var moveDownLabel = (0, _i18n.__)('Move Down', 'newspack-plugin');
95
+ var _useState = (0, _element.useState)(null),
96
+ _useState2 = (0, _slicedToArray2["default"])(_useState, 2),
97
+ items = _useState2[0],
98
+ setItems = _useState2[1];
99
+ var _useState3 = (0, _element.useState)(null),
100
+ _useState4 = (0, _slicedToArray2["default"])(_useState3, 2),
101
+ dragIndex = _useState4[0],
102
+ setDragIndex = _useState4[1];
103
+ var _useState5 = (0, _element.useState)(false),
104
+ _useState6 = (0, _slicedToArray2["default"])(_useState5, 2),
105
+ hasFetchError = _useState6[0],
106
+ setHasFetchError = _useState6[1];
107
+ var _useState7 = (0, _element.useState)(false),
108
+ _useState8 = (0, _slicedToArray2["default"])(_useState7, 2),
109
+ isConfirmingDiscard = _useState8[0],
110
+ setIsConfirmingDiscard = _useState8[1];
111
+ var listRef = (0, _element.useRef)(null);
112
+ var overlayRef = (0, _element.useRef)(null);
113
+ var pendingFocus = (0, _element.useRef)(null);
114
+ var closeRef = (0, _element.useRef)(null);
115
+ var preDragItems = (0, _element.useRef)(null);
116
+ var dragBlocked = (0, _element.useRef)(false);
117
+ var captureOverlay = (0, _element.useCallback)(function (node) {
118
+ overlayRef.current = findOverlay(node);
119
+ }, []);
120
+
121
+ // The order the modal opened with. Snapshotted so a change to `ids` from
122
+ // elsewhere in the editor cannot desynchronise it from `items`.
123
+ var openedWith = (0, _element.useRef)(ids);
124
+ var openedEnabled = (0, _element.useRef)(hasToggles ? enabledIds : []);
125
+ var _useState9 = (0, _element.useState)(function () {
126
+ return new Set(openedEnabled.current);
127
+ }),
128
+ _useState0 = (0, _slicedToArray2["default"])(_useState9, 2),
129
+ enabled = _useState0[0],
130
+ setEnabled = _useState0[1];
131
+ var isReordered = !!items && items.some(function (item, index) {
132
+ return item.id !== openedWith.current[index];
133
+ });
134
+ var isToggled = enabled.size !== openedEnabled.current.length || openedEnabled.current.some(function (id) {
135
+ return !enabled.has(id);
136
+ });
137
+ var isDirty = isReordered || hasToggles && isToggled;
138
+ // Enabled IDs without a row can't be switched off here, so Save keeps them.
139
+ var enabledWithoutRow = openedEnabled.current.filter(function (id) {
140
+ return !openedWith.current.includes(id);
141
+ });
142
+ var isNoneOn = hasToggles && 0 === enabled.size && 0 === enabledWithoutRow.length;
143
+ var toggleItem = function toggleItem(id) {
144
+ return setEnabled(function (current) {
145
+ var next = new Set(current);
146
+ if (next.has(id)) {
147
+ next["delete"](id);
148
+ } else {
149
+ next.add(id);
150
+ }
151
+ return next;
152
+ });
153
+ };
154
+ (0, _element.useEffect)(function () {
155
+ var cancelled = false;
156
+ var openIds = openedWith.current;
157
+ var withLabels = function withLabels(labels, badges) {
158
+ return openIds.map(function (id) {
159
+ return {
160
+ id: id,
161
+ label: labels[id] || (0, _i18n.__)('(no title)', 'newspack-plugin'),
162
+ badge: badges[id]
163
+ };
164
+ });
165
+ };
166
+ fetchItems(openIds).then(function (results) {
167
+ if (cancelled) {
168
+ return;
169
+ }
170
+ var labels = {};
171
+ var badges = {};
172
+ results.forEach(function (_ref2) {
173
+ var value = _ref2.value,
174
+ label = _ref2.label,
175
+ badge = _ref2.badge;
176
+ labels[value] = label;
177
+ badges[value] = badge;
178
+ });
179
+ var loaded = withLabels(labels, badges);
180
+ setItems(loaded);
181
+ (0, _a11y.speak)((0, _i18n.sprintf)(/* translators: %d: number of items in the list. */
182
+ (0, _i18n._n)('%d item ready to reorder.', '%d items ready to reorder.', loaded.length, 'newspack-plugin'), loaded.length));
183
+ })["catch"](function (error) {
184
+ // eslint-disable-next-line no-console
185
+ console.error('Newspack: could not prepare the items to reorder.', error);
186
+ if (!cancelled) {
187
+ setHasFetchError(true);
188
+ // Only to leave the loading state: the error view lists nothing.
189
+ setItems([]);
190
+ }
191
+ });
192
+ return function () {
193
+ cancelled = true;
194
+ };
195
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
196
+
197
+ // A chevron that reaches an end of the list stops accepting moves, so send
198
+ // focus to the row's other chevron.
199
+ (0, _element.useEffect)(function () {
200
+ var pending = pendingFocus.current;
201
+ pendingFocus.current = null;
202
+ if (!pending || !listRef.current) {
203
+ return;
204
+ }
205
+ var row = listRef.current.querySelector("[data-item-id=\"".concat(pending.id, "\"]"));
206
+ if (!row) {
207
+ return;
208
+ }
209
+ var preferred = row.querySelector("[data-direction=\"".concat(pending.direction, "\"]"));
210
+ var fallback = row.querySelector("[data-direction=\"".concat('up' === pending.direction ? 'down' : 'up', "\"]"));
211
+ if (isAvailable(preferred)) {
212
+ preferred.focus();
213
+ } else if (isAvailable(fallback)) {
214
+ fallback.focus();
215
+ }
216
+ }, [items]);
217
+ var moveTo = function moveTo(from, to, direction) {
218
+ var next = moveItem(items, from, to);
219
+ if (next === items) {
220
+ return;
221
+ }
222
+ pendingFocus.current = {
223
+ id: items[from].id,
224
+ direction: direction
225
+ };
226
+ setItems(next);
227
+ (0, _a11y.speak)((0, _i18n.sprintf)(/* translators: 1: title of the moved item. 2: its new position. 3: total number of items. */
228
+ (0, _i18n.__)('%1$s moved to position %2$d of %3$d.', 'newspack-plugin'), items[from].label, to + 1, items.length));
229
+ };
230
+ var requestClose = function requestClose() {
231
+ if (isDirty) {
232
+ setIsConfirmingDiscard(true);
233
+ return;
234
+ }
235
+ onClose();
236
+ };
237
+ (0, _element.useEffect)(function () {
238
+ closeRef.current = requestClose;
239
+ }, [requestClose]);
240
+ var handleKeyDown = function handleKeyDown(event) {
241
+ if ('Escape' !== event.key || event.defaultPrevented) {
242
+ return;
243
+ }
244
+ event.preventDefault();
245
+ requestClose();
246
+ };
247
+
248
+ // `Modal` plays its exit animation before it reports a close request, which
249
+ // would fade the modal out and back in whenever the confirmation vetoes one.
250
+ // Its own dismissal handlers are off, so drive the overlay press here.
251
+ (0, _element.useEffect)(function () {
252
+ var overlay = overlayRef.current;
253
+ if (!overlay) {
254
+ return;
255
+ }
256
+ var pressedOverlay = false;
257
+ var onPointerDown = function onPointerDown(event) {
258
+ pressedOverlay = event.target === overlay;
259
+ if (pressedOverlay) {
260
+ event.preventDefault();
261
+ }
262
+ };
263
+ var onPointerUp = function onPointerUp(event) {
264
+ var dismisses = pressedOverlay && 0 === event.button && event.target === overlay;
265
+ pressedOverlay = false;
266
+ if (dismisses) {
267
+ closeRef.current();
268
+ }
269
+ };
270
+ overlay.addEventListener('pointerdown', onPointerDown);
271
+ overlay.addEventListener('pointerup', onPointerUp);
272
+ return function () {
273
+ overlay.removeEventListener('pointerdown', onPointerDown);
274
+ overlay.removeEventListener('pointerup', onPointerUp);
275
+ };
276
+ }, []);
277
+
278
+ // Rows are the only drop targets, and the frame's own padding is not one, so a
279
+ // release a few pixels wide of the list would report a cancelled drag and undo
280
+ // the reorder. Accepting the drop across the whole dialog leaves "cancelled"
281
+ // meaning only Escape or a release outside it.
282
+ (0, _element.useEffect)(function () {
283
+ var _overlayRef$current;
284
+ var frame = (_overlayRef$current = overlayRef.current) === null || _overlayRef$current === void 0 ? void 0 : _overlayRef$current.querySelector('.components-modal__frame');
285
+ if (!frame) {
286
+ return;
287
+ }
288
+ var accept = function accept(event) {
289
+ return event.preventDefault();
290
+ };
291
+ frame.addEventListener('dragover', accept);
292
+ frame.addEventListener('drop', accept);
293
+ return function () {
294
+ frame.removeEventListener('dragover', accept);
295
+ frame.removeEventListener('drop', accept);
296
+ };
297
+ }, []);
298
+ var handleDragOver = function handleDragOver(event, index) {
299
+ event.preventDefault();
300
+ if (null === dragIndex) {
301
+ return;
302
+ }
303
+ event.dataTransfer.dropEffect = 'move';
304
+ if (dragIndex === index) {
305
+ return;
306
+ }
307
+ setItems(moveItem(items, dragIndex, index));
308
+ setDragIndex(index);
309
+ };
310
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
311
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Modal, {
312
+ ref: captureOverlay,
313
+ title: title,
314
+ onRequestClose: requestClose,
315
+ onKeyDown: handleKeyDown,
316
+ isDismissible: false,
317
+ shouldCloseOnEsc: false,
318
+ shouldCloseOnClickOutside: false,
319
+ headerActions: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
320
+ size: "compact",
321
+ icon: _icons.close,
322
+ label: (0, _i18n.__)('Close', 'newspack-plugin'),
323
+ onClick: requestClose
324
+ }),
325
+ size: "medium",
326
+ className: "newspack-reorder-modal",
327
+ aria: {
328
+ describedby: description ? "".concat(instanceId, "-description") : undefined
329
+ },
330
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
331
+ className: "newspack-reorder-modal__body",
332
+ "aria-busy": !items,
333
+ children: [description && /*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
334
+ id: "".concat(instanceId, "-description"),
335
+ className: "newspack-reorder-modal__description",
336
+ children: description
337
+ }), !items && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
338
+ className: "newspack-reorder-modal__loading",
339
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Spinner, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
340
+ className: "screen-reader-text",
341
+ children: (0, _i18n.__)('Loading content…', 'newspack-plugin')
342
+ })]
343
+ }), items && hasFetchError && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
344
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Notice, {
345
+ status: "error",
346
+ isDismissible: false,
347
+ className: "newspack-reorder-modal__notice",
348
+ children: (0, _i18n.__)('The content could not be loaded, so the order cannot be saved.', 'newspack-plugin')
349
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
350
+ className: "newspack-reorder-modal__footer",
351
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
352
+ variant: "tertiary",
353
+ onClick: requestClose,
354
+ children: (0, _i18n.__)('Cancel', 'newspack-plugin')
355
+ })
356
+ })]
357
+ }), items && !hasFetchError && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
358
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("ul", {
359
+ className: "newspack-reorder-modal__list",
360
+ role: "list",
361
+ ref: listRef,
362
+ children: items.map(function (item, index) {
363
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Card.Root, {
364
+ render: /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {}),
365
+ "data-item-id": item.id,
366
+ className: (0, _classnames["default"])('newspack-reorder-modal__item', {
367
+ 'is-dragging': dragIndex === index,
368
+ 'is-off': hasToggles && !enabled.has(item.id)
369
+ }),
370
+ draggable: true
371
+ // `dragstart` always fires at the draggable row, never at the
372
+ // chevron inside it, so the grab point is recorded separately.
373
+ ,
374
+ onPointerDown: function onPointerDown(event) {
375
+ var _event$target$closest, _event$target;
376
+ dragBlocked.current = !!((_event$target$closest = (_event$target = event.target).closest) !== null && _event$target$closest !== void 0 && _event$target$closest.call(_event$target, 'button, input'));
377
+ },
378
+ onDragStart: function onDragStart(event) {
379
+ if (dragBlocked.current) {
380
+ event.preventDefault();
381
+ return;
382
+ }
383
+ preDragItems.current = items;
384
+ // Firefox and Safari will not start a drag until the payload is set.
385
+ event.dataTransfer.setData('text/plain', String(item.id));
386
+ event.dataTransfer.effectAllowed = 'move';
387
+ setDragIndex(index);
388
+ },
389
+ onDragOver: function onDragOver(event) {
390
+ return handleDragOver(event, index);
391
+ },
392
+ onDragEnd: function onDragEnd(event) {
393
+ // A cancelled drag reports no drop effect: put the order back.
394
+ if ('none' === event.dataTransfer.dropEffect && preDragItems.current) {
395
+ setItems(preDragItems.current);
396
+ }
397
+ preDragItems.current = null;
398
+ setDragIndex(null);
399
+ },
400
+ onDrop: function onDrop(event) {
401
+ return event.preventDefault();
402
+ },
403
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
404
+ className: "newspack-reorder-modal__grip",
405
+ "aria-hidden": "true",
406
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
407
+ icon: _icons.dragHandle
408
+ })
409
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
410
+ className: "newspack-reorder-modal__move",
411
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
412
+ icon: _icons.chevronUp,
413
+ size: "small",
414
+ "data-direction": "up",
415
+ disabled: 0 === index,
416
+ accessibleWhenDisabled: true,
417
+ label: moveUpLabel,
418
+ "aria-label": nameWithTitle(moveUpLabel, item.label),
419
+ onClick: function onClick() {
420
+ return moveTo(index, index - 1, 'up');
421
+ }
422
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
423
+ icon: _icons.chevronDown,
424
+ size: "small",
425
+ "data-direction": "down",
426
+ disabled: index === items.length - 1,
427
+ accessibleWhenDisabled: true,
428
+ label: moveDownLabel,
429
+ "aria-label": nameWithTitle(moveDownLabel, item.label),
430
+ onClick: function onClick() {
431
+ return moveTo(index, index + 1, 'down');
432
+ }
433
+ })]
434
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ui.Card.Content, {
435
+ className: "newspack-reorder-modal__content",
436
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
437
+ className: "newspack-reorder-modal__title",
438
+ title: item.label,
439
+ children: item.label
440
+ }), item.badge && /*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
441
+ className: "newspack-reorder-modal__badge",
442
+ children: [item.badge.tooltip ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Tooltip, {
443
+ text: item.badge.tooltip,
444
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
445
+ "aria-hidden": "true",
446
+ tabIndex: -1,
447
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Badge, {
448
+ intent: "draft",
449
+ children: item.badge.label
450
+ })
451
+ })
452
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
453
+ "aria-hidden": "true",
454
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ui.Badge, {
455
+ intent: "draft",
456
+ children: item.badge.label
457
+ })
458
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.VisuallyHidden, {
459
+ id: "".concat(instanceId, "-badge-").concat(item.id),
460
+ children: item.badge.tooltip ? "".concat(item.badge.label, ": ").concat(item.badge.tooltip) : item.badge.label
461
+ })]
462
+ })]
463
+ }), hasToggles && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
464
+ className: "newspack-reorder-modal__toggle",
465
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.FormToggle, {
466
+ checked: enabled.has(item.id),
467
+ onChange: function onChange() {
468
+ return toggleItem(item.id);
469
+ },
470
+ "aria-label": item.label,
471
+ "aria-describedby": item.badge ? "".concat(instanceId, "-badge-").concat(item.id) : undefined
472
+ })
473
+ })]
474
+ }, item.id);
475
+ })
476
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
477
+ className: "newspack-reorder-modal__footer",
478
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
479
+ variant: "tertiary",
480
+ onClick: requestClose,
481
+ children: (0, _i18n.__)('Cancel', 'newspack-plugin')
482
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
483
+ variant: "primary",
484
+ disabled: !isDirty || isNoneOn,
485
+ accessibleWhenDisabled: true
486
+ // A button with visible children shows a tooltip only when asked and labelled.
487
+ ,
488
+ label: (0, _i18n.__)('Save', 'newspack-plugin'),
489
+ showTooltip: isDirty && isNoneOn,
490
+ description: isDirty && isNoneOn ? noneOnHelp : undefined,
491
+ onClick: function onClick() {
492
+ var order = items.map(function (item) {
493
+ return item.id;
494
+ });
495
+ if (hasToggles) {
496
+ onSave(order, [].concat((0, _toConsumableArray2["default"])(order.filter(function (id) {
497
+ return enabled.has(id);
498
+ })), (0, _toConsumableArray2["default"])(enabledWithoutRow)));
499
+ } else {
500
+ onSave(order);
501
+ }
502
+ },
503
+ children: (0, _i18n.__)('Save', 'newspack-plugin')
504
+ })]
505
+ })]
506
+ })]
507
+ })
508
+ }), isConfirmingDiscard && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalConfirmDialog, {
509
+ isOpen: true,
510
+ contentLabel: hasToggles ? (0, _i18n.__)('Discard the changes?', 'newspack-plugin') : (0, _i18n.__)('Discard the new order?', 'newspack-plugin'),
511
+ confirmButtonText: (0, _i18n.__)('Discard', 'newspack-plugin'),
512
+ cancelButtonText: (0, _i18n.__)('Keep editing', 'newspack-plugin'),
513
+ onConfirm: onClose,
514
+ onCancel: function onCancel() {
515
+ return setIsConfirmingDiscard(false);
516
+ },
517
+ children: hasToggles ? (0, _i18n.__)('The changes you made will be lost.', 'newspack-plugin') : (0, _i18n.__)('The order you set will be lost.', 'newspack-plugin')
518
+ })]
519
+ });
520
+ };
521
+ var _default = exports["default"] = _ReorderModal;