newspack-components 4.1.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/colors/colors.module.scss +1 -1
- package/dist/cjs/action-card/index.js +21 -12
- package/dist/cjs/action-card/style.scss +32 -14
- 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/popover/style.scss +1 -1
- 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 +32 -14
- 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/popover/style.scss +1 -1
- 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 +32 -14
- 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/popover/style.scss +1 -1
- 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
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
@use "sass:color";
|
|
7
7
|
|
|
8
8
|
@function to-rgb($color) {
|
|
9
|
-
@return color.
|
|
9
|
+
@return color.channel($color, "red", $space: rgb) + ", " + color.channel($color, "green", $space: rgb) + ", " + color.channel($color, "blue", $space: rgb);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// Primary: Hex
|
|
@@ -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;
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
.components-toggle-control {
|
|
56
56
|
margin-bottom: 16px;
|
|
57
57
|
|
|
58
|
-
@media screen and (
|
|
58
|
+
@media screen and (min-width: 744px) {
|
|
59
59
|
margin-bottom: 0;
|
|
60
60
|
margin-right: 16px;
|
|
61
61
|
}
|
|
@@ -77,10 +77,6 @@
|
|
|
77
77
|
padding: 0 4px;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
& + & {
|
|
81
|
-
margin-top: -16px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
80
|
&.mv0 {
|
|
85
81
|
border-left-color: wp-colors.$gray-300;
|
|
86
82
|
border-radius: 0;
|
|
@@ -122,7 +118,7 @@
|
|
|
122
118
|
.components-toggle-control {
|
|
123
119
|
margin-bottom: 16px;
|
|
124
120
|
|
|
125
|
-
@media screen and (
|
|
121
|
+
@media screen and (min-width: 744px) {
|
|
126
122
|
margin-bottom: 0;
|
|
127
123
|
margin-right: 16px;
|
|
128
124
|
}
|
|
@@ -132,7 +128,7 @@
|
|
|
132
128
|
.newspack-action-card__region-left {
|
|
133
129
|
margin-bottom: 16px;
|
|
134
130
|
|
|
135
|
-
@media screen and (
|
|
131
|
+
@media screen and (min-width: 744px) {
|
|
136
132
|
margin-bottom: 0;
|
|
137
133
|
margin-right: 16px;
|
|
138
134
|
}
|
|
@@ -252,7 +248,7 @@
|
|
|
252
248
|
padding: 24px;
|
|
253
249
|
display: block;
|
|
254
250
|
|
|
255
|
-
@media screen and (
|
|
251
|
+
@media screen and (min-width: 744px) {
|
|
256
252
|
display: flex;
|
|
257
253
|
}
|
|
258
254
|
|
|
@@ -261,9 +257,19 @@
|
|
|
261
257
|
margin-bottom: 24px;
|
|
262
258
|
margin-top: 0;
|
|
263
259
|
|
|
264
|
-
@media screen and (
|
|
260
|
+
@media screen and (min-width: 744px) {
|
|
265
261
|
margin-bottom: 0;
|
|
266
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;
|
|
267
273
|
}
|
|
268
274
|
|
|
269
275
|
.components-base-control__field {
|
|
@@ -308,7 +314,7 @@
|
|
|
308
314
|
.newspack-action-card__region-left {
|
|
309
315
|
margin-bottom: 24px;
|
|
310
316
|
|
|
311
|
-
@media screen and (
|
|
317
|
+
@media screen and (min-width: 744px) {
|
|
312
318
|
margin-bottom: 0;
|
|
313
319
|
margin-right: 24px;
|
|
314
320
|
}
|
|
@@ -335,7 +341,7 @@
|
|
|
335
341
|
margin-top: 24px;
|
|
336
342
|
text-align: right;
|
|
337
343
|
|
|
338
|
-
@media screen and (
|
|
344
|
+
@media screen and (min-width: 744px) {
|
|
339
345
|
align-items: flex-end;
|
|
340
346
|
flex-direction: column;
|
|
341
347
|
margin-left: 24px;
|
|
@@ -437,13 +443,21 @@
|
|
|
437
443
|
text-decoration: inherit;
|
|
438
444
|
}
|
|
439
445
|
|
|
440
|
-
.newspack-action-card__region-children {
|
|
441
|
-
border-top: 1px solid wp-colors.$gray-
|
|
446
|
+
> .newspack-action-card__region-children {
|
|
447
|
+
border-top: 1px solid wp-colors.$gray-300;
|
|
442
448
|
padding-top: 24px;
|
|
443
449
|
|
|
444
450
|
+ .newspack-action-card__region-children {
|
|
445
451
|
border-top: none;
|
|
446
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
|
+
}
|
|
447
461
|
}
|
|
448
462
|
|
|
449
463
|
&.is-medium .newspack-action-card__region-children {
|
|
@@ -473,6 +487,11 @@
|
|
|
473
487
|
margin-left: 8px;
|
|
474
488
|
}
|
|
475
489
|
}
|
|
490
|
+
|
|
491
|
+
hr:not(.newspack-divider) {
|
|
492
|
+
background: wp-colors.$gray-100;
|
|
493
|
+
margin: 0;
|
|
494
|
+
}
|
|
476
495
|
}
|
|
477
496
|
|
|
478
497
|
.newspack-card {
|
|
@@ -557,7 +576,6 @@
|
|
|
557
576
|
}
|
|
558
577
|
|
|
559
578
|
.newspack-action-card__region-top {
|
|
560
|
-
flex: 1 1 auto;
|
|
561
579
|
padding: 8px 16px 8px 0 !important;
|
|
562
580
|
flex: 1;
|
|
563
581
|
}
|
|
@@ -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;
|