newspack-components 4.2.0 → 4.3.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/dist/cjs/action-card/index.js +21 -12
- package/dist/cjs/action-card/style.scss +25 -2
- package/dist/cjs/autocomplete-tokenfield/index.js +2 -2
- package/dist/cjs/autocomplete-with-latest-posts/index.js +289 -0
- package/dist/cjs/autocomplete-with-latest-posts/style.scss +150 -0
- package/dist/cjs/badge/index.js +32 -0
- package/dist/cjs/badge/style.scss +36 -0
- package/dist/cjs/button/index.js +2 -0
- package/dist/cjs/card/core-card.js +141 -0
- package/dist/cjs/card/index.js +33 -0
- package/dist/cjs/card/style-core.scss +227 -0
- package/dist/cjs/card/style.scss +2 -2
- package/dist/cjs/card-settings-group/index.js +49 -0
- package/dist/cjs/card-settings-group/style.scss +33 -0
- package/dist/cjs/card-sortable-list/index.js +390 -0
- package/dist/cjs/card-sortable-list/style.scss +48 -0
- package/dist/cjs/confirm-dialog/index.js +59 -0
- package/dist/cjs/divider/index.js +42 -0
- package/dist/cjs/divider/style.scss +51 -0
- package/dist/cjs/form-token-field/style.scss +0 -5
- package/dist/cjs/grid/index.js +2 -1
- package/dist/cjs/grid/style.scss +63 -9
- package/dist/cjs/index.js +42 -0
- package/dist/cjs/modal/index.js +11 -4
- package/dist/cjs/modal/style.scss +43 -13
- package/dist/cjs/newspack-icon/style.scss +5 -3
- package/dist/cjs/section-header/index.js +76 -17
- package/dist/cjs/section-header/style.scss +109 -4
- package/dist/cjs/style.scss +2 -0
- package/dist/cjs/waiting/index.js +3 -1
- package/dist/cjs/waiting/style.scss +4 -0
- package/dist/cjs/web-preview/index.js +98 -39
- package/dist/cjs/web-preview/style.scss +20 -53
- package/dist/cjs/with-wizard/index.js +1 -1
- package/dist/cjs/with-wizard/style.scss +24 -6
- package/dist/cjs/with-wizard-screen/style.scss +39 -10
- package/dist/cjs/wizard/components/WizardSnackbar.js +70 -0
- package/dist/cjs/wizard/components/style.scss +48 -0
- package/dist/cjs/wizard/index.js +97 -16
- package/dist/cjs/wizard/store/index.js +46 -0
- package/dist/esm/action-card/index.js +21 -12
- package/dist/esm/action-card/style.scss +25 -2
- package/dist/esm/autocomplete-tokenfield/index.js +2 -2
- package/dist/esm/autocomplete-with-latest-posts/index.js +281 -0
- package/dist/esm/autocomplete-with-latest-posts/style.scss +150 -0
- package/dist/esm/badge/index.js +24 -0
- package/dist/esm/badge/style.scss +36 -0
- package/dist/esm/button/index.js +2 -0
- package/dist/esm/card/core-card.js +133 -0
- package/dist/esm/card/index.js +33 -0
- package/dist/esm/card/style-core.scss +227 -0
- package/dist/esm/card/style.scss +2 -2
- package/dist/esm/card-settings-group/index.js +42 -0
- package/dist/esm/card-settings-group/style.scss +33 -0
- package/dist/esm/card-sortable-list/index.js +381 -0
- package/dist/esm/card-sortable-list/style.scss +48 -0
- package/dist/esm/confirm-dialog/index.js +49 -0
- package/dist/esm/divider/index.js +34 -0
- package/dist/esm/divider/style.scss +51 -0
- package/dist/esm/form-token-field/style.scss +0 -5
- package/dist/esm/grid/index.js +2 -1
- package/dist/esm/grid/style.scss +63 -9
- package/dist/esm/index.js +6 -0
- package/dist/esm/modal/index.js +11 -4
- package/dist/esm/modal/style.scss +43 -13
- package/dist/esm/newspack-icon/style.scss +5 -3
- package/dist/esm/section-header/index.js +75 -18
- package/dist/esm/section-header/style.scss +109 -4
- package/dist/esm/style.scss +2 -0
- package/dist/esm/waiting/index.js +3 -1
- package/dist/esm/waiting/style.scss +4 -0
- package/dist/esm/web-preview/index.js +101 -42
- package/dist/esm/web-preview/style.scss +20 -53
- package/dist/esm/with-wizard/index.js +1 -1
- package/dist/esm/with-wizard/style.scss +24 -6
- package/dist/esm/with-wizard-screen/style.scss +39 -10
- package/dist/esm/wizard/components/WizardSnackbar.js +64 -0
- package/dist/esm/wizard/components/style.scss +48 -0
- package/dist/esm/wizard/index.js +101 -20
- package/dist/esm/wizard/store/index.js +46 -0
- package/package.json +5 -6
- package/shared/author-context.js +26 -0
- package/shared/hooks/use-coauthors.js +220 -0
- package/shared/hooks/use-coauthors.test.js +624 -0
- package/shared/hooks/use-custom-byline.js +47 -0
- package/shared/hooks/use-custom-byline.test.js +133 -0
- package/shared/scss/_mixins.scss +46 -0
- package/src/action-card/action-card.d.ts +7 -3
- package/src/action-card/index.js +24 -11
- package/src/action-card/style.scss +25 -2
- package/src/autocomplete-tokenfield/index.js +2 -2
- package/src/autocomplete-with-latest-posts/index.js +290 -0
- package/src/autocomplete-with-latest-posts/style.scss +150 -0
- package/src/badge/index.tsx +24 -0
- package/src/badge/style.scss +36 -0
- package/src/button/index.tsx +3 -2
- package/src/card/core-card.js +147 -0
- package/src/card/index.js +39 -1
- package/src/card/style-core.scss +227 -0
- package/src/card/style.scss +2 -2
- package/src/card-settings-group/README.md +43 -0
- package/src/card-settings-group/index.tsx +53 -0
- package/src/card-settings-group/style.scss +33 -0
- package/src/card-sortable-list/README.md +51 -0
- package/src/card-sortable-list/index.tsx +412 -0
- package/src/card-sortable-list/style.scss +48 -0
- package/src/confirm-dialog/index.tsx +64 -0
- package/src/divider/README.md +25 -0
- package/src/divider/index.js +31 -0
- package/src/divider/style.scss +51 -0
- package/src/form-token-field/style.scss +0 -5
- package/src/grid/index.tsx +2 -1
- package/src/grid/style.scss +63 -9
- package/src/index.js +6 -0
- package/src/modal/index.js +12 -3
- package/src/modal/style.scss +43 -13
- package/src/newspack-icon/style.scss +5 -3
- package/src/section-header/index.js +72 -13
- package/src/section-header/style.scss +109 -4
- package/src/style.scss +2 -0
- package/src/waiting/index.js +2 -1
- package/src/waiting/style.scss +4 -0
- package/src/web-preview/index.js +90 -35
- package/src/web-preview/style.scss +20 -53
- package/src/with-wizard/index.js +1 -1
- package/src/with-wizard/style.scss +24 -6
- package/src/with-wizard-screen/style.scss +39 -10
- package/src/wizard/components/WizardSnackbar.js +58 -0
- package/src/wizard/components/style.scss +48 -0
- package/src/wizard/index.js +108 -14
- package/src/wizard/store/index.js +26 -0
|
@@ -36,13 +36,14 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
36
36
|
* @return {JSX.Element} ActionCard component.
|
|
37
37
|
*/const ActionCard = ({
|
|
38
38
|
badge,
|
|
39
|
-
badgeLevel
|
|
39
|
+
badgeLevel,
|
|
40
40
|
className,
|
|
41
41
|
checkbox,
|
|
42
42
|
children,
|
|
43
43
|
collapse,
|
|
44
44
|
disabled,
|
|
45
45
|
title,
|
|
46
|
+
heading = 2,
|
|
46
47
|
description,
|
|
47
48
|
handoff,
|
|
48
49
|
editLink,
|
|
@@ -67,11 +68,13 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
67
68
|
titleLink,
|
|
68
69
|
toggleChecked = false,
|
|
69
70
|
toggleOnChange,
|
|
71
|
+
togglePosition = 'leading',
|
|
70
72
|
hasGreyHeader,
|
|
71
73
|
hasWhiteHeader,
|
|
72
74
|
noBorder,
|
|
73
75
|
isPending,
|
|
74
76
|
expandable = false,
|
|
77
|
+
isExpanded,
|
|
75
78
|
isButtonEnabled = false,
|
|
76
79
|
// Draggable props. All are required to enable drag sorting.
|
|
77
80
|
draggable = false,
|
|
@@ -79,10 +82,15 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
79
82
|
dragWrapperRef,
|
|
80
83
|
onDragCallback
|
|
81
84
|
}) => {
|
|
82
|
-
const [expanded, setExpanded] = (0, _element.useState)(
|
|
85
|
+
const [expanded, setExpanded] = (0, _element.useState)(Boolean(isExpanded));
|
|
83
86
|
const [dragging, setDragging] = (0, _element.useState)(false);
|
|
84
87
|
const [targetIndex, setTargetIndex] = (0, _element.useState)(null);
|
|
85
88
|
const [dragRef, setDragRef] = (0, _element.useState)(null);
|
|
89
|
+
(0, _element.useEffect)(() => {
|
|
90
|
+
if (typeof isExpanded === 'boolean') {
|
|
91
|
+
setExpanded(isExpanded);
|
|
92
|
+
}
|
|
93
|
+
}, [isExpanded]);
|
|
86
94
|
(0, _element.useEffect)(() => {
|
|
87
95
|
if (dragWrapperRef && !dragRef) {
|
|
88
96
|
setDragRef(dragWrapperRef);
|
|
@@ -104,16 +112,19 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
104
112
|
onClick: () => toggleOnChange(!toggleChecked),
|
|
105
113
|
tabIndex: '0'
|
|
106
114
|
} : {};
|
|
115
|
+
const togglePositionClass = togglePosition === 'trailing' ? 'is-toggle-trailing' : 'is-toggle-leading';
|
|
107
116
|
const hasInternalLink = href && href.indexOf('http') !== 0;
|
|
108
117
|
const isDisplayingSecondaryAction = secondaryActionText && onSecondaryActionClick;
|
|
109
118
|
const badges = !Array.isArray(badge) && badge ? [badge] : badge;
|
|
110
|
-
const
|
|
119
|
+
const HeadingTag = `h${heading}`;
|
|
120
|
+
const cardContent = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
111
121
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
112
122
|
className: "newspack-action-card__region newspack-action-card__region-top",
|
|
113
123
|
children: [toggleOnChange && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToggleControl, {
|
|
114
124
|
checked: toggleChecked,
|
|
115
125
|
onChange: toggleOnChange,
|
|
116
|
-
disabled: disabled
|
|
126
|
+
disabled: disabled,
|
|
127
|
+
className: togglePositionClass
|
|
117
128
|
}), image && !toggleOnChange && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
118
129
|
className: "newspack-action-card__region newspack-action-card__region-left",
|
|
119
130
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("a", {
|
|
@@ -137,7 +148,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
137
148
|
columns: 1,
|
|
138
149
|
gutter: 8,
|
|
139
150
|
noMargin: true,
|
|
140
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(
|
|
151
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(HeadingTag, {
|
|
141
152
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("span", {
|
|
142
153
|
className: "newspack-action-card__title",
|
|
143
154
|
...titleProps,
|
|
@@ -214,10 +225,10 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
214
225
|
isWarning: true,
|
|
215
226
|
rawHTML: notificationHTML
|
|
216
227
|
})]
|
|
217
|
-
}), children && (expandable && expanded || !expandable)
|
|
228
|
+
}), children && (expandable && expanded || !expandable) ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
218
229
|
className: "newspack-action-card__region-children",
|
|
219
230
|
children: children
|
|
220
|
-
})]
|
|
231
|
+
}) : null]
|
|
221
232
|
});
|
|
222
233
|
if (draggable && dragRef?.current && typeof dragIndex === 'number' && onDragCallback && id) {
|
|
223
234
|
let wrapperRect = dragRef.current.getBoundingClientRect();
|
|
@@ -288,9 +299,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
288
299
|
onDragStart: onDraggableStart,
|
|
289
300
|
onDragEnd: onDraggableEnd,
|
|
290
301
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
|
|
291
|
-
icon: _icons.dragHandle
|
|
292
|
-
height: 18,
|
|
293
|
-
width: 18
|
|
302
|
+
icon: _icons.dragHandle
|
|
294
303
|
})
|
|
295
304
|
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
296
305
|
className: "movers",
|
|
@@ -306,7 +315,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
306
315
|
label: (0, _i18n.__)('Move one position down', 'newspack-plugin')
|
|
307
316
|
})]
|
|
308
317
|
})]
|
|
309
|
-
}),
|
|
318
|
+
}), cardContent]
|
|
310
319
|
})
|
|
311
320
|
})
|
|
312
321
|
});
|
|
@@ -316,7 +325,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
316
325
|
onClick: simple && onClick,
|
|
317
326
|
id: id !== null && id !== void 0 ? id : null,
|
|
318
327
|
noBorder: noBorder,
|
|
319
|
-
children:
|
|
328
|
+
children: cardContent
|
|
320
329
|
});
|
|
321
330
|
};
|
|
322
331
|
var _default = exports.default = ActionCard;
|
|
@@ -260,6 +260,16 @@
|
|
|
260
260
|
@media screen and (min-width: 744px) {
|
|
261
261
|
margin-bottom: 0;
|
|
262
262
|
margin-right: 24px;
|
|
263
|
+
|
|
264
|
+
&.is-toggle-trailing {
|
|
265
|
+
margin-left: 24px;
|
|
266
|
+
margin-right: 0;
|
|
267
|
+
order: 999;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
label {
|
|
272
|
+
display: none;
|
|
263
273
|
}
|
|
264
274
|
|
|
265
275
|
.components-base-control__field {
|
|
@@ -433,13 +443,21 @@
|
|
|
433
443
|
text-decoration: inherit;
|
|
434
444
|
}
|
|
435
445
|
|
|
436
|
-
.newspack-action-card__region-children {
|
|
437
|
-
border-top: 1px solid wp-colors.$gray-
|
|
446
|
+
> .newspack-action-card__region-children {
|
|
447
|
+
border-top: 1px solid wp-colors.$gray-300;
|
|
438
448
|
padding-top: 24px;
|
|
439
449
|
|
|
440
450
|
+ .newspack-action-card__region-children {
|
|
441
451
|
border-top: none;
|
|
442
452
|
}
|
|
453
|
+
|
|
454
|
+
> * + * {
|
|
455
|
+
margin-top: 24px !important;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.newspack-action-card__region-children:has(> div:not(:empty)) {
|
|
459
|
+
margin-top: 16px;
|
|
460
|
+
}
|
|
443
461
|
}
|
|
444
462
|
|
|
445
463
|
&.is-medium .newspack-action-card__region-children {
|
|
@@ -469,6 +487,11 @@
|
|
|
469
487
|
margin-left: 8px;
|
|
470
488
|
}
|
|
471
489
|
}
|
|
490
|
+
|
|
491
|
+
hr:not(.newspack-divider) {
|
|
492
|
+
background: wp-colors.$gray-100;
|
|
493
|
+
margin: 0;
|
|
494
|
+
}
|
|
472
495
|
}
|
|
473
496
|
|
|
474
497
|
.newspack-card {
|
|
@@ -143,7 +143,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
143
143
|
Object.keys(validValues).forEach(key => {
|
|
144
144
|
if (validValues[key] === label) {
|
|
145
145
|
// Preserve numeric or string type of values. Object.keys will convert numbers to strings.
|
|
146
|
-
const value =
|
|
146
|
+
const value = /^\d+$/.test(key.toString()) ? parseInt(key) : key.toString();
|
|
147
147
|
acc.push({
|
|
148
148
|
value,
|
|
149
149
|
label
|
|
@@ -153,7 +153,7 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
|
153
153
|
return acc;
|
|
154
154
|
}, []);
|
|
155
155
|
}
|
|
156
|
-
return labels.map(label => Object.keys(validValues).map(key =>
|
|
156
|
+
return labels.map(label => Object.keys(validValues).map(key => /^\d+$/.test(key.toString()) ? parseInt(key) : key.toString()).find(key => validValues[key] === label));
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
/**
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
var _debounce = _interopRequireDefault(require("lodash/debounce"));
|
|
9
|
+
var _apiFetch = _interopRequireDefault(require("@wordpress/api-fetch"));
|
|
10
|
+
var _components = require("@wordpress/components");
|
|
11
|
+
var _htmlEntities = require("@wordpress/html-entities");
|
|
12
|
+
var _element = require("@wordpress/element");
|
|
13
|
+
var _i18n = require("@wordpress/i18n");
|
|
14
|
+
var _url = require("@wordpress/url");
|
|
15
|
+
require("./style.scss");
|
|
16
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
+
/**
|
|
18
|
+
* External dependencies
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* WordPress dependencies
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const AutocompleteWithLatestPosts = ({
|
|
26
|
+
fetchSuggestions = false,
|
|
27
|
+
// If passed, will use this function to fetch suggestions data.
|
|
28
|
+
help = (0, _i18n.__)('Begin typing search term, click autocomplete result to select.', 'newspack-plugin'),
|
|
29
|
+
hideHelp = true,
|
|
30
|
+
// If true, all help text will be hidden.
|
|
31
|
+
hideFormTokenHelp = true,
|
|
32
|
+
// If true, hides FormTokenField's built-in help text.
|
|
33
|
+
label = (0, _i18n.__)('Search', 'newspack-plugin'),
|
|
34
|
+
maxItemsToSuggest = 0,
|
|
35
|
+
// If passed, will be used to determine "load more" state. Necessary if you want "load more" functionality when using a custom `fetchSuggestions` function.
|
|
36
|
+
multiSelect = false,
|
|
37
|
+
// If true, component can select multiple values at once.
|
|
38
|
+
onChange = false,
|
|
39
|
+
// Function to call when selections change.
|
|
40
|
+
selectedItems = [],
|
|
41
|
+
// Array of saved items.
|
|
42
|
+
suggestionsToFetch = 20 // Number of suggestions to fetch per query.
|
|
43
|
+
}) => {
|
|
44
|
+
const [isLoading, setIsLoading] = (0, _element.useState)(true);
|
|
45
|
+
const [isLoadingMore, setIsLoadingMore] = (0, _element.useState)(false);
|
|
46
|
+
const [suggestions, setSuggestions] = (0, _element.useState)([]);
|
|
47
|
+
const [maxSuggestions, setMaxSuggestions] = (0, _element.useState)(0);
|
|
48
|
+
const [searchSuggestions, setSearchSuggestions] = (0, _element.useState)([]);
|
|
49
|
+
const [validValues, setValidValues] = (0, _element.useState)({});
|
|
50
|
+
const [isSearching, setIsSearching] = (0, _element.useState)(false);
|
|
51
|
+
// Leaving this in here so we can expand to use different post types in the future; right now it defaults to 'post'.
|
|
52
|
+
const postTypeToSearch = 'post';
|
|
53
|
+
const classNames = ['newspack-autocomplete-with-latest-posts'];
|
|
54
|
+
if (hideFormTokenHelp) {
|
|
55
|
+
classNames.push('hide-form-token-help');
|
|
56
|
+
}
|
|
57
|
+
if (hideHelp) {
|
|
58
|
+
classNames.push('hide-help');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Debounced function to update search suggestions.
|
|
63
|
+
*/
|
|
64
|
+
const debouncedUpdateSuggestions = (0, _debounce.default)(input => {
|
|
65
|
+
if (!input || input.length < 2) {
|
|
66
|
+
setSearchSuggestions([]);
|
|
67
|
+
setIsSearching(false);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
setIsSearching(true);
|
|
71
|
+
handleFetchSuggestions(input, 0, postTypeToSearch).then(_suggestions => {
|
|
72
|
+
setSearchSuggestions(_suggestions);
|
|
73
|
+
// Update valid values for token conversion
|
|
74
|
+
const newValidValues = {
|
|
75
|
+
...validValues
|
|
76
|
+
};
|
|
77
|
+
_suggestions.forEach(suggestion => {
|
|
78
|
+
newValidValues[suggestion.value] = suggestion.label;
|
|
79
|
+
});
|
|
80
|
+
setValidValues(newValidValues);
|
|
81
|
+
}).finally(() => setIsSearching(false));
|
|
82
|
+
}, 500);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Fetch recent posts to show as suggestions.
|
|
86
|
+
*/
|
|
87
|
+
(0, _element.useEffect)(() => {
|
|
88
|
+
setIsLoading(true);
|
|
89
|
+
handleFetchSuggestions(null, 0, postTypeToSearch).then(_suggestions => {
|
|
90
|
+
if (0 < _suggestions.length) {
|
|
91
|
+
setSuggestions(_suggestions);
|
|
92
|
+
// Update valid values for token conversion
|
|
93
|
+
const newValidValues = {
|
|
94
|
+
...validValues
|
|
95
|
+
};
|
|
96
|
+
_suggestions.forEach(suggestion => {
|
|
97
|
+
newValidValues[suggestion.value] = suggestion.label;
|
|
98
|
+
});
|
|
99
|
+
setValidValues(newValidValues);
|
|
100
|
+
}
|
|
101
|
+
}).finally(() => setIsLoading(false));
|
|
102
|
+
}, []);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Fetch more suggestions.
|
|
106
|
+
*/
|
|
107
|
+
(0, _element.useEffect)(() => {
|
|
108
|
+
if (isLoadingMore) {
|
|
109
|
+
handleFetchSuggestions(null, suggestions.length, postTypeToSearch).then(_suggestions => {
|
|
110
|
+
if (0 < _suggestions.length) {
|
|
111
|
+
setSuggestions(suggestions.concat(_suggestions));
|
|
112
|
+
}
|
|
113
|
+
}).finally(() => setIsLoadingMore(false));
|
|
114
|
+
}
|
|
115
|
+
}, [isLoadingMore]);
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* If passed a `fetchSuggestions` prop, use that, otherwise, build it based on the selected post type.
|
|
119
|
+
*/
|
|
120
|
+
const handleFetchSuggestions = fetchSuggestions ? fetchSuggestions : async (search = null, offset = 0, searchSlug = null) => {
|
|
121
|
+
const postTypeSlug = searchSlug || postTypeToSearch;
|
|
122
|
+
const endpoint = 'post' === postTypeSlug || 'page' === postTypeSlug ? postTypeSlug + 's' // Default post type endpoints are plural.
|
|
123
|
+
: postTypeSlug; // Custom post type endpoints are singular.
|
|
124
|
+
const response = await (0, _apiFetch.default)({
|
|
125
|
+
parse: false,
|
|
126
|
+
path: (0, _url.addQueryArgs)('/wp/v2/' + endpoint, {
|
|
127
|
+
search,
|
|
128
|
+
offset,
|
|
129
|
+
per_page: suggestionsToFetch,
|
|
130
|
+
_fields: 'id,title'
|
|
131
|
+
})
|
|
132
|
+
});
|
|
133
|
+
const total = parseInt(response.headers.get('x-wp-total') || 0);
|
|
134
|
+
const posts = await response.json();
|
|
135
|
+
setMaxSuggestions(total);
|
|
136
|
+
|
|
137
|
+
// Format suggestions for FormTokenField display.
|
|
138
|
+
return posts.reduce((acc, post) => {
|
|
139
|
+
acc.push({
|
|
140
|
+
value: parseInt(post.id),
|
|
141
|
+
label: (0, _htmlEntities.decodeEntities)(post?.title.rendered) || (0, _i18n.__)('(no title)', 'newspack-plugin')
|
|
142
|
+
});
|
|
143
|
+
return acc;
|
|
144
|
+
}, []);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get labels for token values.
|
|
149
|
+
*/
|
|
150
|
+
const getLabelsForValues = values => {
|
|
151
|
+
return values.reduce((accumulator, value) => {
|
|
152
|
+
if (!value) {
|
|
153
|
+
return accumulator;
|
|
154
|
+
}
|
|
155
|
+
if (value.label) {
|
|
156
|
+
return [...accumulator, value.label];
|
|
157
|
+
}
|
|
158
|
+
return validValues[value] ? [...accumulator, validValues[value]] : accumulator;
|
|
159
|
+
}, []);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get values for token labels.
|
|
164
|
+
*/
|
|
165
|
+
const getValuesForLabels = labels => {
|
|
166
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
167
|
+
return labels.reduce((acc, label) => {
|
|
168
|
+
Object.keys(validValues).forEach(key => {
|
|
169
|
+
if (validValues[key] === label) {
|
|
170
|
+
const value = isNaN(parseInt(key)) ? key.toString() : parseInt(key);
|
|
171
|
+
acc.push({
|
|
172
|
+
value,
|
|
173
|
+
label
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return acc;
|
|
178
|
+
}, []);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Handle FormTokenField onChange.
|
|
183
|
+
*/
|
|
184
|
+
const handleTokenChange = tokenStrings => {
|
|
185
|
+
const newSelections = getValuesForLabels(tokenStrings);
|
|
186
|
+
|
|
187
|
+
// If only allowing one selection, just return the one selected item.
|
|
188
|
+
if (!multiSelect) {
|
|
189
|
+
const newSelection = newSelections[newSelections.length - 1]; // Get the last selected item
|
|
190
|
+
if (newSelection) {
|
|
191
|
+
return onChange([{
|
|
192
|
+
...newSelection,
|
|
193
|
+
postType: postTypeToSearch
|
|
194
|
+
}]);
|
|
195
|
+
}
|
|
196
|
+
return onChange([]);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// For multi-select, include currently selected post type in selection results.
|
|
200
|
+
onChange(newSelections.map(selection => ({
|
|
201
|
+
...selection,
|
|
202
|
+
postType: postTypeToSearch
|
|
203
|
+
})));
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get tokens for FormTokenField.
|
|
208
|
+
*/
|
|
209
|
+
const getTokens = () => {
|
|
210
|
+
return getLabelsForValues(selectedItems);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Render a single suggestion object that can be clicked to select it immediately.
|
|
215
|
+
*
|
|
216
|
+
* @param {Object} suggestion Suggestion object with value and label keys.
|
|
217
|
+
*/
|
|
218
|
+
const renderSuggestion = suggestion => {
|
|
219
|
+
if (multiSelect) {
|
|
220
|
+
const isSelected = !!selectedItems.find(_selection => parseInt(_selection.value) === parseInt(suggestion.value) && _selection.label === suggestion.label);
|
|
221
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.CheckboxControl, {
|
|
222
|
+
checked: isSelected,
|
|
223
|
+
onChange: () => {
|
|
224
|
+
// For multi-select, we need to toggle the selection
|
|
225
|
+
const currentTokens = getTokens();
|
|
226
|
+
const suggestionLabel = suggestion.label;
|
|
227
|
+
if (isSelected) {
|
|
228
|
+
// Remove the suggestion
|
|
229
|
+
const newTokens = currentTokens.filter(token => token !== suggestionLabel);
|
|
230
|
+
handleTokenChange(newTokens);
|
|
231
|
+
} else {
|
|
232
|
+
// Add the suggestion
|
|
233
|
+
handleTokenChange([...currentTokens, suggestionLabel]);
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
label: suggestion.label
|
|
237
|
+
}, suggestion.value);
|
|
238
|
+
}
|
|
239
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
|
|
240
|
+
isLink: true,
|
|
241
|
+
onClick: () => handleTokenChange([...getTokens(), suggestion.label]),
|
|
242
|
+
children: suggestion.label
|
|
243
|
+
}, suggestion.value);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Render a list of suggestions that can be clicked to select instead of searching by title.
|
|
248
|
+
*/
|
|
249
|
+
const renderSuggestions = () => {
|
|
250
|
+
if (0 === suggestions.length) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const className = multiSelect ? 'newspack-autocomplete-with-latest-posts__search-suggestions-multiselect' : 'newspack-autocomplete-with-latest-posts__search-suggestions';
|
|
254
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
|
|
255
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
256
|
+
className: "newspack-autocomplete-with-latest-posts__search-suggestions-container",
|
|
257
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
|
|
258
|
+
className: "newspack-autocomplete-with-suggestions__label",
|
|
259
|
+
children: (0, _i18n.__)('Latest Posts', 'newspack-plugin')
|
|
260
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
261
|
+
className: className,
|
|
262
|
+
children: [suggestions.map(renderSuggestion), suggestions.length < (maxItemsToSuggest || maxSuggestions) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
|
|
263
|
+
disabled: isLoadingMore,
|
|
264
|
+
isSecondary: true,
|
|
265
|
+
onClick: () => setIsLoadingMore(true),
|
|
266
|
+
children: isLoadingMore ? (0, _i18n.__)('Loading…', 'newspack-plugin') : (0, _i18n.__)('Load more', 'newspack-plugin')
|
|
267
|
+
})]
|
|
268
|
+
})]
|
|
269
|
+
})
|
|
270
|
+
});
|
|
271
|
+
};
|
|
272
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
273
|
+
className: classNames.join(' '),
|
|
274
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
275
|
+
className: "newspack-autocomplete-with-latest-posts__input-container",
|
|
276
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.FormTokenField, {
|
|
277
|
+
value: getTokens(),
|
|
278
|
+
suggestions: searchSuggestions.map(suggestion => suggestion.label),
|
|
279
|
+
onChange: handleTokenChange,
|
|
280
|
+
onInputChange: debouncedUpdateSuggestions,
|
|
281
|
+
label: label,
|
|
282
|
+
help: !hideHelp && help,
|
|
283
|
+
__next40pxDefaultSize: true,
|
|
284
|
+
__nextHasNoMarginBottom: true
|
|
285
|
+
}), (isLoading || isSearching) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Spinner, {})]
|
|
286
|
+
}), renderSuggestions()]
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
var _default = exports.default = AutocompleteWithLatestPosts;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Styles for AutocompleteWithLatestPosts component.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
@use "~@wordpress/base-styles/colors" as wp-colors;
|
|
6
|
+
|
|
7
|
+
.newspack-autocomplete-with-latest-posts {
|
|
8
|
+
display: grid;
|
|
9
|
+
gap: 16px;
|
|
10
|
+
width: 100%;
|
|
11
|
+
|
|
12
|
+
.components-select-control__input {
|
|
13
|
+
max-width: none;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.components-input-control__backdrop {
|
|
17
|
+
border-color: wp-colors.$gray-700 !important;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.newspack-autocomplete-with-latest-posts__label,
|
|
21
|
+
.components-form-token-field__label,
|
|
22
|
+
.components-input-control__label,
|
|
23
|
+
.components-checkbox-control__label {
|
|
24
|
+
color: wp-colors.$gray-900 !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.newspack-autocomplete-tokenfield__help {
|
|
28
|
+
font-size: inherit;
|
|
29
|
+
margin-bottom: 8px !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.components-form-token-field {
|
|
33
|
+
&__input-container {
|
|
34
|
+
border-color: wp-colors.$gray-700;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&__suggestion {
|
|
38
|
+
padding: 8px;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Hide the built-in "Separate with commas or the Enter key." help text when hideFormTokenHelp is true
|
|
43
|
+
&.hide-form-token-help .components-form-token-field__help {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
p.newspack-autocomplete-with-latest-posts__label {
|
|
48
|
+
font-size: inherit;
|
|
49
|
+
margin: 0 0 8px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&__selected-items {
|
|
53
|
+
border-bottom: 1px solid wp-colors.$gray-700;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-wrap: wrap;
|
|
56
|
+
gap: 8px;
|
|
57
|
+
margin-bottom: 16px;
|
|
58
|
+
padding-bottom: 12px;
|
|
59
|
+
|
|
60
|
+
p.newspack-autocomplete-with-latest-posts__label {
|
|
61
|
+
flex: 0 0 100%;
|
|
62
|
+
margin: 0;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&__selected-item-button.components-button.is-tertiary {
|
|
67
|
+
background-color: wp-colors.$gray-300;
|
|
68
|
+
border-radius: 2px;
|
|
69
|
+
color: wp-colors.$gray-900;
|
|
70
|
+
font-size: 13px;
|
|
71
|
+
height: auto;
|
|
72
|
+
margin: 0;
|
|
73
|
+
padding-left: 8px;
|
|
74
|
+
padding-right: 8px;
|
|
75
|
+
|
|
76
|
+
&::after {
|
|
77
|
+
content: "✕";
|
|
78
|
+
margin-left: 8px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&:hover {
|
|
82
|
+
box-shadow: none;
|
|
83
|
+
color: black;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
&__search-suggestions,
|
|
88
|
+
&__search-suggestions-multiselect {
|
|
89
|
+
max-height: 220px;
|
|
90
|
+
overflow: visible auto;
|
|
91
|
+
margin: 0 -4px;
|
|
92
|
+
padding: 4px 4px 0;
|
|
93
|
+
|
|
94
|
+
.components-button {
|
|
95
|
+
display: block;
|
|
96
|
+
line-height: normal;
|
|
97
|
+
margin: auto;
|
|
98
|
+
|
|
99
|
+
&.is-link {
|
|
100
|
+
color: wp-colors.$gray-900;
|
|
101
|
+
padding: 8px;
|
|
102
|
+
text-decoration: none;
|
|
103
|
+
width: 100%;
|
|
104
|
+
|
|
105
|
+
&:active:not(:disabled),
|
|
106
|
+
&:hover:not(:disabled) {
|
|
107
|
+
background: wp-colors.$gray-900;
|
|
108
|
+
background: var(--wp-admin-theme-color);
|
|
109
|
+
color: white;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
&.is-secondary {
|
|
113
|
+
margin-bottom: 8px;
|
|
114
|
+
margin-top: 8px;
|
|
115
|
+
width: 100%;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&__search-suggestions {
|
|
121
|
+
border-radius: 2px;
|
|
122
|
+
border: 1px solid wp-colors.$gray-700;
|
|
123
|
+
padding: 0;
|
|
124
|
+
|
|
125
|
+
.components-button {
|
|
126
|
+
line-height: normal;
|
|
127
|
+
|
|
128
|
+
&:focus:not(:disabled) {
|
|
129
|
+
border-radius: 0;
|
|
130
|
+
box-shadow: inset 0 0 0 1.5px wp-colors.$gray-900;
|
|
131
|
+
box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
&.is-secondary {
|
|
135
|
+
width: calc(100% - 16px);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Temp: CSS work-around.
|
|
141
|
+
.newspack-autocomplete-with-suggestions__label {
|
|
142
|
+
font-size: 11px;
|
|
143
|
+
font-weight: 500;
|
|
144
|
+
line-height: 1.4;
|
|
145
|
+
text-transform: uppercase;
|
|
146
|
+
display: block;
|
|
147
|
+
margin: 0 0 8px;
|
|
148
|
+
padding: 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
require("./style.scss");
|
|
9
|
+
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* External dependencies
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Badge component
|
|
21
|
+
*/
|
|
22
|
+
const Badge = ({
|
|
23
|
+
text,
|
|
24
|
+
level = 'default'
|
|
25
|
+
}) => {
|
|
26
|
+
const classes = (0, _classnames.default)('newspack-badge', `is-${level}`);
|
|
27
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
28
|
+
className: classes,
|
|
29
|
+
children: text
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
var _default = exports.default = Badge;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Badge
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
@use "~@wordpress/base-styles/colors" as wp-colors;
|
|
6
|
+
@use "../../../colors/colors.module" as colors;
|
|
7
|
+
|
|
8
|
+
$badge-colors: (
|
|
9
|
+
"info": colors.$primary-600,
|
|
10
|
+
"warning": wp-colors.$alert-yellow,
|
|
11
|
+
"error": wp-colors.$alert-red,
|
|
12
|
+
"success": wp-colors.$alert-green,
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
.newspack-badge {
|
|
16
|
+
background-color: color-mix(in srgb, wp-colors.$white 90%, var(--base-color));
|
|
17
|
+
border-radius: 2px;
|
|
18
|
+
color: color-mix(in srgb, wp-colors.$black 50%, var(--base-color));
|
|
19
|
+
display: block;
|
|
20
|
+
flex: 0 0 auto;
|
|
21
|
+
font-size: 12px;
|
|
22
|
+
line-height: 1.5;
|
|
23
|
+
padding: 1px 4px;
|
|
24
|
+
|
|
25
|
+
&:where(.is-default) {
|
|
26
|
+
background-color: wp-colors.$gray-100;
|
|
27
|
+
color: wp-colors.$gray-800;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Generate color variants
|
|
31
|
+
@each $type, $color in $badge-colors {
|
|
32
|
+
&.is-#{$type} {
|
|
33
|
+
--base-color: #{$color};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|