@wordpress-gcb/fields 0.2.1 → 0.2.3
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/conditional-logic.js +83 -0
- package/{src → dist}/control-context.js +3 -2
- package/{src → dist}/controls/MediaCapabilityGate.js +12 -8
- package/dist/controls/MediaPicker.js +149 -0
- package/dist/controls/MediaTriggerBadges.js +35 -0
- package/{src → dist}/controls/PopoverOrModal.js +49 -43
- package/dist/controls/SortableItem.js +126 -0
- package/dist/controls/button-group.js +46 -0
- package/dist/controls/checkbox-group.js +65 -0
- package/dist/controls/checkbox.js +15 -0
- package/dist/controls/code.js +24 -0
- package/dist/controls/color.js +249 -0
- package/dist/controls/date.js +55 -0
- package/dist/controls/datetime.js +61 -0
- package/dist/controls/email.js +17 -0
- package/dist/controls/file.js +163 -0
- package/dist/controls/gallery.js +371 -0
- package/dist/controls/google-map.js +143 -0
- package/dist/controls/heading-level.js +93 -0
- package/dist/controls/icon.js +292 -0
- package/dist/controls/image.js +360 -0
- package/dist/controls/index.js +88 -0
- package/dist/controls/message.js +86 -0
- package/dist/controls/number.js +19 -0
- package/dist/controls/oembed.js +42 -0
- package/{src → dist}/controls/page-link.js +1 -2
- package/dist/controls/post-object.js +913 -0
- package/dist/controls/radio.js +19 -0
- package/dist/controls/range.js +108 -0
- package/{src → dist}/controls/relationship.js +12 -7
- package/dist/controls/repeater.js +277 -0
- package/dist/controls/richtext.js +494 -0
- package/dist/controls/select.js +144 -0
- package/dist/controls/size.js +59 -0
- package/dist/controls/spacing.js +172 -0
- package/dist/controls/taxonomy.js +569 -0
- package/dist/controls/text.js +16 -0
- package/dist/controls/textarea.js +17 -0
- package/dist/controls/toggle-group.js +28 -0
- package/dist/controls/toggle.js +15 -0
- package/dist/controls/url.js +235 -0
- package/dist/controls/user.js +383 -0
- package/{src → dist}/controls/wysiwyg.js +1 -1
- package/{src → dist}/hooks/useTokens.js +25 -21
- package/{src → dist}/index.js +2 -8
- package/dist/inspector.js +163 -0
- package/{src → dist}/provider.js +18 -17
- package/dist/utils/map-utils.js +54 -0
- package/dist/utils/token-helper.js +396 -0
- package/{src → dist}/validation-context.js +4 -4
- package/package.json +20 -13
- package/src/conditional-logic.js +0 -77
- package/src/controls/MediaPicker.js +0 -139
- package/src/controls/MediaTriggerBadges.js +0 -31
- package/src/controls/SortableItem.js +0 -110
- package/src/controls/button-group.js +0 -49
- package/src/controls/checkbox-group.js +0 -55
- package/src/controls/checkbox.js +0 -13
- package/src/controls/code.js +0 -21
- package/src/controls/color.js +0 -235
- package/src/controls/date.js +0 -37
- package/src/controls/datetime.js +0 -54
- package/src/controls/email.js +0 -15
- package/src/controls/file.js +0 -134
- package/src/controls/gallery.js +0 -338
- package/src/controls/google-map.js +0 -117
- package/src/controls/heading-level.js +0 -99
- package/src/controls/icon.js +0 -301
- package/src/controls/image.js +0 -334
- package/src/controls/index.js +0 -95
- package/src/controls/message.js +0 -56
- package/src/controls/number.js +0 -17
- package/src/controls/oembed.js +0 -32
- package/src/controls/post-object.js +0 -788
- package/src/controls/radio.js +0 -18
- package/src/controls/range.js +0 -110
- package/src/controls/repeater.js +0 -290
- package/src/controls/richtext.js +0 -505
- package/src/controls/select.js +0 -141
- package/src/controls/size.js +0 -49
- package/src/controls/spacing.js +0 -141
- package/src/controls/taxonomy.js +0 -488
- package/src/controls/text.js +0 -14
- package/src/controls/textarea.js +0 -15
- package/src/controls/toggle-group.js +0 -34
- package/src/controls/toggle.js +0 -13
- package/src/controls/url.js +0 -164
- package/src/controls/user.js +0 -343
- package/src/inspector.js +0 -174
- package/src/utils/map-utils.js +0 -51
- package/src/utils/token-helper.js +0 -243
|
@@ -1,788 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PostObjectField — ported verbatim from the original GCB PostObjectControlComponent.
|
|
3
|
-
*
|
|
4
|
-
* Features preserved:
|
|
5
|
-
* - Single OR multiple selection (control.multiple)
|
|
6
|
-
* - Stored as ID(s) or full post object(s) (control.returnFormat)
|
|
7
|
-
* - Multiple post types (CSV string or array)
|
|
8
|
-
* - Multiple post statuses
|
|
9
|
-
* - Optional taxonomy filter dropdowns (control.enableTaxonomyFilter / filterTaxonomies)
|
|
10
|
-
* - Optional post-type filter dropdown (control.enablePostTypeFilter)
|
|
11
|
-
* - Drag-and-drop reordering of selected posts (multi-select only)
|
|
12
|
-
* - REST endpoint discovery via /wp/v2/types
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { __ } from '@wordpress/i18n';
|
|
16
|
-
import {
|
|
17
|
-
Button,
|
|
18
|
-
SelectControl,
|
|
19
|
-
TextControl,
|
|
20
|
-
__experimentalTruncate as Truncate,
|
|
21
|
-
} from '@wordpress/components';
|
|
22
|
-
import PopoverOrModal from './PopoverOrModal';
|
|
23
|
-
import { useState, useEffect, useMemo, useCallback } from '@wordpress/element';
|
|
24
|
-
import apiFetch from '@wordpress/api-fetch';
|
|
25
|
-
import {
|
|
26
|
-
DndContext,
|
|
27
|
-
closestCenter,
|
|
28
|
-
PointerSensor,
|
|
29
|
-
useSensor,
|
|
30
|
-
useSensors,
|
|
31
|
-
DragOverlay,
|
|
32
|
-
} from '@dnd-kit/core';
|
|
33
|
-
import {
|
|
34
|
-
SortableContext,
|
|
35
|
-
verticalListSortingStrategy,
|
|
36
|
-
useSortable,
|
|
37
|
-
arrayMove,
|
|
38
|
-
} from '@dnd-kit/sortable';
|
|
39
|
-
import { CSS } from '@dnd-kit/utilities';
|
|
40
|
-
|
|
41
|
-
const TOGGLE_BUTTON_STYLE = {
|
|
42
|
-
width: '100%',
|
|
43
|
-
height: 'auto',
|
|
44
|
-
padding: '12px',
|
|
45
|
-
justifyContent: 'flex-start',
|
|
46
|
-
border: '1px solid #ddd',
|
|
47
|
-
borderRadius: '2px',
|
|
48
|
-
backgroundColor: '#fff',
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Sortable selected-post row.
|
|
53
|
-
*/
|
|
54
|
-
function SortablePostItem({ post, onRemove }) {
|
|
55
|
-
const {
|
|
56
|
-
attributes,
|
|
57
|
-
listeners,
|
|
58
|
-
setNodeRef,
|
|
59
|
-
transform,
|
|
60
|
-
transition,
|
|
61
|
-
isDragging,
|
|
62
|
-
} = useSortable({ id: post.id });
|
|
63
|
-
|
|
64
|
-
const style = {
|
|
65
|
-
transform: CSS.Transform.toString(transform),
|
|
66
|
-
transition,
|
|
67
|
-
opacity: isDragging ? 0.5 : 1,
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<div
|
|
72
|
-
ref={setNodeRef}
|
|
73
|
-
style={style}
|
|
74
|
-
className="gcb-post-object-selected-item"
|
|
75
|
-
{...attributes}
|
|
76
|
-
{...listeners}
|
|
77
|
-
>
|
|
78
|
-
<div className="gcb-post-object-drag-handle" aria-hidden>
|
|
79
|
-
<svg viewBox="0 0 20 20" width="12">
|
|
80
|
-
<path d="M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z" />
|
|
81
|
-
</svg>
|
|
82
|
-
</div>
|
|
83
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" style={{ marginRight: 8, flexShrink: 0, opacity: 0.6 }}>
|
|
84
|
-
<path d="M15.5 7.5h-7V9h7V7.5Zm-7 3.5h7v1.5h-7V11Zm7 3.5h-7V16h7v-1.5Z" />
|
|
85
|
-
<path d="M17 4H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2ZM7 5.5h10a.5.5 0 0 1 .5.5v12a.5.5 0 0 1-.5.5H7a.5.5 0 0 1-.5-.5V6a.5.5 0 0 1 .5-.5Z" />
|
|
86
|
-
</svg>
|
|
87
|
-
<span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', userSelect: 'none' }}>
|
|
88
|
-
{post.title?.rendered || __('(no title)', 'gcblite')}
|
|
89
|
-
</span>
|
|
90
|
-
<button
|
|
91
|
-
type="button"
|
|
92
|
-
onClick={(e) => {
|
|
93
|
-
e.stopPropagation();
|
|
94
|
-
onRemove(post.id);
|
|
95
|
-
}}
|
|
96
|
-
onPointerDown={(e) => e.stopPropagation()}
|
|
97
|
-
className="gcb-sortable-remove"
|
|
98
|
-
aria-label={__('Remove', 'gcblite')}
|
|
99
|
-
>
|
|
100
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20">
|
|
101
|
-
<path d="M12 13.06l3.712 3.713 1.061-1.06L13.061 12l3.712-3.712-1.06-1.06L12 10.938 8.288 7.227l-1.061 1.06L10.939 12l-3.712 3.712 1.06 1.061L12 13.061z" />
|
|
102
|
-
</svg>
|
|
103
|
-
</button>
|
|
104
|
-
</div>
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* The multi-select panel — search dropdown plus a sortable list of selected posts.
|
|
110
|
-
*/
|
|
111
|
-
function PostObjectMultiSelect({
|
|
112
|
-
posts,
|
|
113
|
-
selectedIds,
|
|
114
|
-
selectedPosts,
|
|
115
|
-
loading,
|
|
116
|
-
search,
|
|
117
|
-
setSearch,
|
|
118
|
-
loadPosts,
|
|
119
|
-
handleSelect,
|
|
120
|
-
handleRemove,
|
|
121
|
-
handleReorder,
|
|
122
|
-
handleClear,
|
|
123
|
-
control,
|
|
124
|
-
postTypeFilter,
|
|
125
|
-
setPostTypeFilter,
|
|
126
|
-
availablePostTypes,
|
|
127
|
-
}) {
|
|
128
|
-
const [activeId, setActiveId] = useState(null);
|
|
129
|
-
const [taxonomyTermsBySlug, setTaxonomyTermsBySlug] = useState({});
|
|
130
|
-
const [loadingTaxonomies, setLoadingTaxonomies] = useState(false);
|
|
131
|
-
const [taxonomyFilters, setTaxonomyFilters] = useState({});
|
|
132
|
-
|
|
133
|
-
useEffect(() => {
|
|
134
|
-
if (!control.enableTaxonomyFilter || !Array.isArray(control.filterTaxonomies) || control.filterTaxonomies.length === 0) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
setLoadingTaxonomies(true);
|
|
138
|
-
const promises = control.filterTaxonomies.map((tax) =>
|
|
139
|
-
apiFetch({ path: `/wp/v2/${tax.slug}?per_page=100` })
|
|
140
|
-
.then((terms) => ({
|
|
141
|
-
slug: tax.slug,
|
|
142
|
-
label: tax.label,
|
|
143
|
-
terms: terms.map((t) => ({ value: t.id, label: t.name })),
|
|
144
|
-
}))
|
|
145
|
-
.catch(() => ({ slug: tax.slug, label: tax.label, terms: [] }))
|
|
146
|
-
);
|
|
147
|
-
Promise.all(promises)
|
|
148
|
-
.then((results) => {
|
|
149
|
-
const map = {};
|
|
150
|
-
const initialFilters = {};
|
|
151
|
-
results.forEach((r) => {
|
|
152
|
-
map[r.slug] = { label: r.label, terms: r.terms };
|
|
153
|
-
initialFilters[r.slug] = 'all';
|
|
154
|
-
});
|
|
155
|
-
setTaxonomyTermsBySlug(map);
|
|
156
|
-
setTaxonomyFilters(initialFilters);
|
|
157
|
-
})
|
|
158
|
-
.finally(() => setLoadingTaxonomies(false));
|
|
159
|
-
}, [control.enableTaxonomyFilter, control.filterTaxonomies]);
|
|
160
|
-
|
|
161
|
-
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }));
|
|
162
|
-
|
|
163
|
-
const handleDragStart = (event) => setActiveId(event.active.id);
|
|
164
|
-
const handleDragCancel = () => setActiveId(null);
|
|
165
|
-
|
|
166
|
-
const handleDragEnd = (event) => {
|
|
167
|
-
const { active, over } = event;
|
|
168
|
-
if (over && active.id !== over.id) {
|
|
169
|
-
const oldIndex = selectedIds.indexOf(active.id);
|
|
170
|
-
const newIndex = selectedIds.indexOf(over.id);
|
|
171
|
-
handleReorder(arrayMove(selectedIds, oldIndex, newIndex));
|
|
172
|
-
}
|
|
173
|
-
setActiveId(null);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const activePost = activeId ? selectedPosts.find((p) => p.id === activeId) : null;
|
|
177
|
-
const availablePosts = posts.filter((p) => !selectedIds.includes(p.id));
|
|
178
|
-
|
|
179
|
-
return (
|
|
180
|
-
<div className="gcb-post-object-stacked">
|
|
181
|
-
<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
|
182
|
-
<PopoverOrModal
|
|
183
|
-
modalTitle={control.label || __('Select posts', 'gcblite')}
|
|
184
|
-
dropdownProps={{ popoverProps: { placement: 'left-start' } }}
|
|
185
|
-
renderToggle={({ isOpen, onToggle }) => (
|
|
186
|
-
<Button
|
|
187
|
-
onClick={onToggle}
|
|
188
|
-
aria-expanded={isOpen}
|
|
189
|
-
className="gcb-modal-toggle-button"
|
|
190
|
-
style={{ ...TOGGLE_BUTTON_STYLE, flex: 1 }}
|
|
191
|
-
>
|
|
192
|
-
{selectedPosts.length > 0
|
|
193
|
-
? `${selectedPosts.length} ${selectedPosts.length === 1 ? __('post', 'gcblite') : __('posts', 'gcblite')} ${__('selected', 'gcblite')}`
|
|
194
|
-
: __('Select Posts', 'gcblite')}
|
|
195
|
-
</Button>
|
|
196
|
-
)}
|
|
197
|
-
renderContent={({ close: onClose, variant }) => (
|
|
198
|
-
<div style={variant === 'modal' ? { width: '100%' } : { minWidth: 320, maxWidth: 400 }}>
|
|
199
|
-
<div style={{ padding: '0 16px 8px 16px' }}>
|
|
200
|
-
<TextControl
|
|
201
|
-
value={search}
|
|
202
|
-
onChange={(val) => {
|
|
203
|
-
setSearch(val);
|
|
204
|
-
loadPosts(val, postTypeFilter, taxonomyFilters);
|
|
205
|
-
}}
|
|
206
|
-
placeholder={__('Search…', 'gcblite')}
|
|
207
|
-
__nextHasNoMarginBottom
|
|
208
|
-
/>
|
|
209
|
-
|
|
210
|
-
{control.enablePostTypeFilter && availablePostTypes.length > 1 && (
|
|
211
|
-
<div style={{ marginTop: 8 }}>
|
|
212
|
-
<SelectControl
|
|
213
|
-
label={__('Filter by Post Type', 'gcblite')}
|
|
214
|
-
value={postTypeFilter}
|
|
215
|
-
options={[
|
|
216
|
-
{ value: 'all', label: __('All Types', 'gcblite') },
|
|
217
|
-
...availablePostTypes,
|
|
218
|
-
]}
|
|
219
|
-
onChange={(val) => {
|
|
220
|
-
setPostTypeFilter(val);
|
|
221
|
-
loadPosts(search, val, taxonomyFilters);
|
|
222
|
-
}}
|
|
223
|
-
__nextHasNoMarginBottom
|
|
224
|
-
/>
|
|
225
|
-
</div>
|
|
226
|
-
)}
|
|
227
|
-
|
|
228
|
-
{control.enableTaxonomyFilter && Object.keys(taxonomyTermsBySlug).length > 0 &&
|
|
229
|
-
Object.entries(taxonomyTermsBySlug).map(([slug, data]) => (
|
|
230
|
-
<div key={slug} style={{ marginTop: 8 }}>
|
|
231
|
-
<SelectControl
|
|
232
|
-
label={__('Filter by ', 'gcblite') + data.label}
|
|
233
|
-
value={taxonomyFilters[slug] || 'all'}
|
|
234
|
-
options={[
|
|
235
|
-
{ value: 'all', label: __('All', 'gcblite') },
|
|
236
|
-
...data.terms,
|
|
237
|
-
]}
|
|
238
|
-
onChange={(val) => {
|
|
239
|
-
const newFilters = { ...taxonomyFilters, [slug]: val };
|
|
240
|
-
setTaxonomyFilters(newFilters);
|
|
241
|
-
loadPosts(search, postTypeFilter, newFilters);
|
|
242
|
-
}}
|
|
243
|
-
disabled={loadingTaxonomies}
|
|
244
|
-
__nextHasNoMarginBottom
|
|
245
|
-
/>
|
|
246
|
-
</div>
|
|
247
|
-
))}
|
|
248
|
-
</div>
|
|
249
|
-
|
|
250
|
-
<div className="block-editor-link-control__search-results-wrapper" style={{ maxHeight: 300, overflowY: 'auto' }}>
|
|
251
|
-
{loading && (
|
|
252
|
-
<p style={{ textAlign: 'center', color: '#757575', padding: 16 }}>
|
|
253
|
-
{__('Loading…', 'gcblite')}
|
|
254
|
-
</p>
|
|
255
|
-
)}
|
|
256
|
-
{!loading && availablePosts.length === 0 && (
|
|
257
|
-
<p style={{ textAlign: 'center', color: '#757575', padding: 16 }}>
|
|
258
|
-
{__('No posts found', 'gcblite')}
|
|
259
|
-
</p>
|
|
260
|
-
)}
|
|
261
|
-
{!loading && availablePosts.length > 0 && (
|
|
262
|
-
<div className="block-editor-link-control__search-results" role="listbox">
|
|
263
|
-
<div className="components-menu-group">
|
|
264
|
-
<div role="group">
|
|
265
|
-
{availablePosts.map((post) => (
|
|
266
|
-
<button
|
|
267
|
-
key={post.id}
|
|
268
|
-
type="button"
|
|
269
|
-
role="option"
|
|
270
|
-
className="components-button components-menu-item__button block-editor-link-control__search-item"
|
|
271
|
-
onClick={() => handleSelect(post.id)}
|
|
272
|
-
style={{
|
|
273
|
-
display: 'flex',
|
|
274
|
-
alignItems: 'center',
|
|
275
|
-
width: '100%',
|
|
276
|
-
padding: '8px 16px',
|
|
277
|
-
textAlign: 'left',
|
|
278
|
-
border: 'none',
|
|
279
|
-
background: 'transparent',
|
|
280
|
-
justifyContent: 'space-between',
|
|
281
|
-
}}
|
|
282
|
-
>
|
|
283
|
-
<span style={{ display: 'flex', alignItems: 'center', flex: 1, overflow: 'hidden' }}>
|
|
284
|
-
<svg
|
|
285
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
286
|
-
viewBox="0 0 24 24"
|
|
287
|
-
width="24"
|
|
288
|
-
height="24"
|
|
289
|
-
className="block-editor-link-control__search-item-icon"
|
|
290
|
-
style={{ marginRight: 12, flexShrink: 0 }}
|
|
291
|
-
>
|
|
292
|
-
<path d="M15.5 7.5h-7V9h7V7.5Zm-7 3.5h7v1.5h-7V11Zm7 3.5h-7V16h7v-1.5Z" />
|
|
293
|
-
<path d="M17 4H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2ZM7 5.5h10a.5.5 0 0 1 .5.5v12a.5.5 0 0 1-.5.5H7a.5.5 0 0 1-.5-.5V6a.5.5 0 0 1 .5-.5Z" />
|
|
294
|
-
</svg>
|
|
295
|
-
<span
|
|
296
|
-
className="components-menu-item__item"
|
|
297
|
-
style={{ fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
|
|
298
|
-
>
|
|
299
|
-
{post.title?.rendered || __('(no title)', 'gcblite')}
|
|
300
|
-
</span>
|
|
301
|
-
</span>
|
|
302
|
-
</button>
|
|
303
|
-
))}
|
|
304
|
-
</div>
|
|
305
|
-
</div>
|
|
306
|
-
</div>
|
|
307
|
-
)}
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
)}
|
|
311
|
-
/>
|
|
312
|
-
|
|
313
|
-
{selectedPosts.length > 0 && (
|
|
314
|
-
<Button
|
|
315
|
-
onClick={handleClear}
|
|
316
|
-
variant="secondary"
|
|
317
|
-
isSmall
|
|
318
|
-
className="components-range-control__reset"
|
|
319
|
-
>
|
|
320
|
-
{__('Reset', 'gcblite')}
|
|
321
|
-
</Button>
|
|
322
|
-
)}
|
|
323
|
-
</div>
|
|
324
|
-
|
|
325
|
-
{selectedPosts.length > 0 && (
|
|
326
|
-
<div className="gcb-post-object-selected-list" style={{ marginTop: 8 }}>
|
|
327
|
-
<DndContext
|
|
328
|
-
sensors={sensors}
|
|
329
|
-
collisionDetection={closestCenter}
|
|
330
|
-
onDragStart={handleDragStart}
|
|
331
|
-
onDragEnd={handleDragEnd}
|
|
332
|
-
onDragCancel={handleDragCancel}
|
|
333
|
-
>
|
|
334
|
-
<SortableContext items={selectedIds} strategy={verticalListSortingStrategy}>
|
|
335
|
-
{selectedPosts.map((post) => (
|
|
336
|
-
<SortablePostItem key={post.id} post={post} onRemove={handleRemove} />
|
|
337
|
-
))}
|
|
338
|
-
</SortableContext>
|
|
339
|
-
<DragOverlay>
|
|
340
|
-
{activePost ? (
|
|
341
|
-
<div
|
|
342
|
-
className="gcb-post-object-selected-item"
|
|
343
|
-
style={{ opacity: 0.8, boxShadow: '0 2px 8px rgba(0,0,0,0.15)' }}
|
|
344
|
-
>
|
|
345
|
-
<div className="gcb-post-object-drag-handle" aria-hidden>
|
|
346
|
-
<svg viewBox="0 0 20 20" width="12">
|
|
347
|
-
<path d="M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z" />
|
|
348
|
-
</svg>
|
|
349
|
-
</div>
|
|
350
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" style={{ marginRight: 8, flexShrink: 0, opacity: 0.6 }}>
|
|
351
|
-
<path d="M15.5 7.5h-7V9h7V7.5Zm-7 3.5h7v1.5h-7V11Zm7 3.5h-7V16h7v-1.5Z" />
|
|
352
|
-
<path d="M17 4H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2ZM7 5.5h10a.5.5 0 0 1 .5.5v12a.5.5 0 0 1-.5.5H7a.5.5 0 0 1-.5-.5V6a.5.5 0 0 1 .5-.5Z" />
|
|
353
|
-
</svg>
|
|
354
|
-
<span style={{ flex: 1 }}>
|
|
355
|
-
{activePost.title?.rendered || __('(no title)', 'gcblite')}
|
|
356
|
-
</span>
|
|
357
|
-
</div>
|
|
358
|
-
) : null}
|
|
359
|
-
</DragOverlay>
|
|
360
|
-
</DndContext>
|
|
361
|
-
</div>
|
|
362
|
-
)}
|
|
363
|
-
</div>
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Normalise whatever shape the post-object value used to take into the
|
|
368
|
-
// canonical { post_type, ids[] } shape. Handles:
|
|
369
|
-
// - canonical { post_type, ids[] } (preferred since v0.2)
|
|
370
|
-
// - bare scalar id (legacy single)
|
|
371
|
-
// - array of ids (legacy multi)
|
|
372
|
-
// - { id, ... } object (returnFormat=object, single)
|
|
373
|
-
// - array of those objects (returnFormat=object, multi)
|
|
374
|
-
function normalisePostObjectValue(value, schemaDefault) {
|
|
375
|
-
if (!value) return { post_type: schemaDefault, ids: [] };
|
|
376
|
-
if (typeof value === 'object' && !Array.isArray(value) && Array.isArray(value.ids)) {
|
|
377
|
-
return { post_type: value.post_type || schemaDefault, ids: value.ids };
|
|
378
|
-
}
|
|
379
|
-
if (typeof value === 'number' || typeof value === 'string') {
|
|
380
|
-
return { post_type: schemaDefault, ids: [Number(value)] };
|
|
381
|
-
}
|
|
382
|
-
if (typeof value === 'object' && !Array.isArray(value) && value.id != null) {
|
|
383
|
-
return { post_type: value.type || schemaDefault, ids: [Number(value.id)] };
|
|
384
|
-
}
|
|
385
|
-
if (Array.isArray(value)) {
|
|
386
|
-
const ids = value
|
|
387
|
-
.map((entry) => typeof entry === 'object' ? Number(entry.id) : Number(entry))
|
|
388
|
-
.filter(Boolean);
|
|
389
|
-
const pt = value.find((entry) => typeof entry === 'object' && entry?.type)?.type;
|
|
390
|
-
return { post_type: pt || schemaDefault, ids };
|
|
391
|
-
}
|
|
392
|
-
return { post_type: schemaDefault, ids: [] };
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
export default function PostObjectField({ control, value, onChange }) {
|
|
396
|
-
const [allPosts, setAllPosts] = useState([]);
|
|
397
|
-
const [searchResults, setSearchResults] = useState([]);
|
|
398
|
-
const [loading, setLoading] = useState(false);
|
|
399
|
-
const [search, setSearch] = useState('');
|
|
400
|
-
const [postTypeFilter, setPostTypeFilter] = useState('all');
|
|
401
|
-
const [postTypeEndpoints, setPostTypeEndpoints] = useState({});
|
|
402
|
-
const [allAvailableTypes, setAllAvailableTypes] = useState([]);
|
|
403
|
-
|
|
404
|
-
// Schema-locked vs editor-picks-at-edit-time. When control.postType
|
|
405
|
-
// is omitted entirely, the field is dynamic — user picks at edit
|
|
406
|
-
// time and we store { post_type, ids } alongside the selection.
|
|
407
|
-
const dynamic = !control.postType;
|
|
408
|
-
const schemaDefault = (() => {
|
|
409
|
-
if (!control.postType) return 'post';
|
|
410
|
-
if (typeof control.postType === 'string') {
|
|
411
|
-
return control.postType.split(',')[0].trim() || 'post';
|
|
412
|
-
}
|
|
413
|
-
if (Array.isArray(control.postType)) return control.postType[0] || 'post';
|
|
414
|
-
return 'post';
|
|
415
|
-
})();
|
|
416
|
-
|
|
417
|
-
const normalised = useMemo(
|
|
418
|
-
() => normalisePostObjectValue(value, schemaDefault),
|
|
419
|
-
[value, schemaDefault]
|
|
420
|
-
);
|
|
421
|
-
const currentPostType = normalised.post_type;
|
|
422
|
-
|
|
423
|
-
const availablePostTypes = useMemo(() => {
|
|
424
|
-
if (!control.postType) {
|
|
425
|
-
// Dynamic mode: offer everything the live registry knows about.
|
|
426
|
-
return allAvailableTypes.length > 0 ? allAvailableTypes : [{ value: 'post', label: 'Posts' }];
|
|
427
|
-
}
|
|
428
|
-
const types = typeof control.postType === 'string'
|
|
429
|
-
? control.postType.split(',').map((pt) => pt.trim()).filter(Boolean)
|
|
430
|
-
: Array.isArray(control.postType) ? control.postType : ['post'];
|
|
431
|
-
return types.map((type) => ({
|
|
432
|
-
value: type,
|
|
433
|
-
label: type.charAt(0).toUpperCase() + type.slice(1) + 's',
|
|
434
|
-
}));
|
|
435
|
-
}, [control.postType, allAvailableTypes]);
|
|
436
|
-
|
|
437
|
-
useEffect(() => {
|
|
438
|
-
(async () => {
|
|
439
|
-
try {
|
|
440
|
-
const types = await apiFetch({ path: '/wp/v2/types' });
|
|
441
|
-
const endpoints = {};
|
|
442
|
-
const list = [];
|
|
443
|
-
Object.entries(types).forEach(([key, info]) => {
|
|
444
|
-
endpoints[key] = info.rest_base || key;
|
|
445
|
-
// Skip non-author-facing types in the dynamic dropdown.
|
|
446
|
-
if (info.viewable && key !== 'attachment') {
|
|
447
|
-
list.push({ value: key, label: info.name || key });
|
|
448
|
-
}
|
|
449
|
-
});
|
|
450
|
-
setPostTypeEndpoints(endpoints);
|
|
451
|
-
setAllAvailableTypes(list);
|
|
452
|
-
} catch {
|
|
453
|
-
setPostTypeEndpoints({ post: 'posts', page: 'pages', media: 'media', attachment: 'media' });
|
|
454
|
-
setAllAvailableTypes([
|
|
455
|
-
{ value: 'post', label: 'Posts' },
|
|
456
|
-
{ value: 'page', label: 'Pages' },
|
|
457
|
-
]);
|
|
458
|
-
}
|
|
459
|
-
})();
|
|
460
|
-
}, []);
|
|
461
|
-
|
|
462
|
-
const isMultiple = !!control.multiple;
|
|
463
|
-
const selectedIds = isMultiple ? normalised.ids : null;
|
|
464
|
-
const singleSelectedId = !isMultiple ? (normalised.ids[0] || null) : null;
|
|
465
|
-
|
|
466
|
-
// Emit the canonical shape — always { post_type, ids[] } regardless
|
|
467
|
-
// of single/multi. Renderer reads both old and canonical so this is
|
|
468
|
-
// safe across migration.
|
|
469
|
-
const emitChange = useCallback((ids) => {
|
|
470
|
-
onChange({ post_type: currentPostType, ids });
|
|
471
|
-
}, [onChange, currentPostType]);
|
|
472
|
-
|
|
473
|
-
const handlePostTypeChange = (newType) => {
|
|
474
|
-
// Switching post type clears the selected IDs — IDs from one
|
|
475
|
-
// type don't translate to another.
|
|
476
|
-
onChange({ post_type: newType, ids: [] });
|
|
477
|
-
setAllPosts([]);
|
|
478
|
-
setSearchResults([]);
|
|
479
|
-
};
|
|
480
|
-
|
|
481
|
-
const mergePostsIntoCache = useCallback((newPosts) => {
|
|
482
|
-
setAllPosts((prev) => {
|
|
483
|
-
const merged = [...prev];
|
|
484
|
-
newPosts.forEach((np) => {
|
|
485
|
-
if (!merged.find((p) => p.id === np.id)) merged.push(np);
|
|
486
|
-
});
|
|
487
|
-
return merged;
|
|
488
|
-
});
|
|
489
|
-
}, []);
|
|
490
|
-
|
|
491
|
-
const selectedPosts = useMemo(() => {
|
|
492
|
-
if (isMultiple) {
|
|
493
|
-
return selectedIds.map((id) => allPosts.find((p) => p.id === id)).filter(Boolean);
|
|
494
|
-
}
|
|
495
|
-
return allPosts.find((p) => p.id === singleSelectedId) || null;
|
|
496
|
-
}, [allPosts, selectedIds, singleSelectedId, isMultiple]);
|
|
497
|
-
|
|
498
|
-
const loadPosts = useCallback(async (searchTerm = '', filterPostType = 'all', taxFilters = {}) => {
|
|
499
|
-
setLoading(true);
|
|
500
|
-
try {
|
|
501
|
-
let postTypes;
|
|
502
|
-
if (dynamic) {
|
|
503
|
-
// In dynamic mode the editor has chosen the type via the
|
|
504
|
-
// picker dropdown; search only within that one type.
|
|
505
|
-
postTypes = [currentPostType];
|
|
506
|
-
} else {
|
|
507
|
-
postTypes = control.postType;
|
|
508
|
-
if (!postTypes || postTypes === '') {
|
|
509
|
-
postTypes = ['post'];
|
|
510
|
-
} else if (typeof postTypes === 'string') {
|
|
511
|
-
postTypes = postTypes.split(',').map((pt) => pt.trim()).filter(Boolean);
|
|
512
|
-
if (postTypes.length === 0) postTypes = ['post'];
|
|
513
|
-
} else if (!Array.isArray(postTypes)) {
|
|
514
|
-
postTypes = ['post'];
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
if (filterPostType !== 'all') postTypes = [filterPostType];
|
|
518
|
-
|
|
519
|
-
let postStatuses = control.postStatus;
|
|
520
|
-
if (!postStatuses || postStatuses === '') {
|
|
521
|
-
postStatuses = ['publish'];
|
|
522
|
-
} else if (typeof postStatuses === 'string') {
|
|
523
|
-
postStatuses = postStatuses.split(',').map((s) => s.trim()).filter(Boolean);
|
|
524
|
-
if (postStatuses.length === 0) postStatuses = ['publish'];
|
|
525
|
-
} else if (!Array.isArray(postStatuses)) {
|
|
526
|
-
postStatuses = ['publish'];
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
if (Object.keys(postTypeEndpoints).length === 0) {
|
|
530
|
-
setLoading(false);
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
const collected = [];
|
|
535
|
-
for (const postType of postTypes) {
|
|
536
|
-
try {
|
|
537
|
-
const endpoint = postTypeEndpoints[postType] || postType;
|
|
538
|
-
let statusParam = 'publish';
|
|
539
|
-
if (postStatuses.length > 1 || (postStatuses.length === 1 && postStatuses[0] !== 'publish')) {
|
|
540
|
-
statusParam = 'any';
|
|
541
|
-
} else if (postStatuses.length === 1) {
|
|
542
|
-
statusParam = postStatuses[0];
|
|
543
|
-
}
|
|
544
|
-
const fields = postStatuses.length > 1 ? 'id,title,type,status' : 'id,title,type';
|
|
545
|
-
let query = `search=${searchTerm}&per_page=50&_fields=${fields}&status=${statusParam}`;
|
|
546
|
-
|
|
547
|
-
if (taxFilters && typeof taxFilters === 'object') {
|
|
548
|
-
Object.entries(taxFilters).forEach(([slug, termId]) => {
|
|
549
|
-
if (termId !== 'all') query += `&${slug}=${termId}`;
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
if (control.taxonomy && control.taxonomyTerms) {
|
|
554
|
-
const taxonomies = typeof control.taxonomy === 'string'
|
|
555
|
-
? control.taxonomy.split(',').map((t) => t.trim()).filter(Boolean)
|
|
556
|
-
: [control.taxonomy];
|
|
557
|
-
taxonomies.forEach((tax) => {
|
|
558
|
-
query += `&${tax}=${control.taxonomyTerms}`;
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const response = await apiFetch({ path: `/wp/v2/${endpoint}?${query}` });
|
|
563
|
-
if (Array.isArray(response)) {
|
|
564
|
-
if (postStatuses.length > 1 && statusParam === 'any') {
|
|
565
|
-
collected.push(...response.filter((post) => postStatuses.includes(post.status)));
|
|
566
|
-
} else {
|
|
567
|
-
collected.push(...response);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
} catch {
|
|
571
|
-
// ignore per-type errors; continue
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
const unique = collected.filter((post, i, self) => i === self.findIndex((p) => p.id === post.id));
|
|
576
|
-
setSearchResults(unique);
|
|
577
|
-
mergePostsIntoCache(unique);
|
|
578
|
-
} catch {
|
|
579
|
-
// ignore
|
|
580
|
-
}
|
|
581
|
-
setLoading(false);
|
|
582
|
-
}, [control.postType, control.postStatus, control.taxonomy, control.taxonomyTerms, mergePostsIntoCache, postTypeEndpoints, dynamic, currentPostType]);
|
|
583
|
-
|
|
584
|
-
useEffect(() => {
|
|
585
|
-
loadPosts();
|
|
586
|
-
}, [loadPosts]);
|
|
587
|
-
|
|
588
|
-
const handleSelect = (postId) => {
|
|
589
|
-
if (isMultiple) {
|
|
590
|
-
const newIds = selectedIds.includes(postId)
|
|
591
|
-
? selectedIds.filter((id) => id !== postId)
|
|
592
|
-
: [...selectedIds, postId];
|
|
593
|
-
emitChange(newIds);
|
|
594
|
-
} else {
|
|
595
|
-
emitChange([postId]);
|
|
596
|
-
}
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
const handleRemove = (postId) => {
|
|
600
|
-
if (!isMultiple) return;
|
|
601
|
-
emitChange(selectedIds.filter((id) => id !== postId));
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
const handleReorder = (newOrder) => {
|
|
605
|
-
emitChange(newOrder);
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
const handleClear = () => emitChange([]);
|
|
609
|
-
|
|
610
|
-
const getDisplayText = () => {
|
|
611
|
-
if (isMultiple) {
|
|
612
|
-
return selectedPosts.length > 0
|
|
613
|
-
? `${selectedPosts.length} ${__('selected', 'gcblite')}`
|
|
614
|
-
: __('Select Posts', 'gcblite');
|
|
615
|
-
}
|
|
616
|
-
return selectedPosts?.title?.rendered || __('Select Post', 'gcblite');
|
|
617
|
-
};
|
|
618
|
-
|
|
619
|
-
return (
|
|
620
|
-
<div className="components-base-control gcb-post-object-control">
|
|
621
|
-
<div className="components-base-control__field">
|
|
622
|
-
<label className="components-base-control__label">{control.label}</label>
|
|
623
|
-
</div>
|
|
624
|
-
{control.helpText && (
|
|
625
|
-
<p className="components-base-control__help">{control.helpText}</p>
|
|
626
|
-
)}
|
|
627
|
-
|
|
628
|
-
{dynamic && (
|
|
629
|
-
<div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 8 }}>
|
|
630
|
-
<label style={{ fontSize: 12, fontWeight: 600, color: '#1e1e1e', minWidth: 70 }}>
|
|
631
|
-
{__('Post type', 'gcblite')}
|
|
632
|
-
</label>
|
|
633
|
-
<select
|
|
634
|
-
value={currentPostType}
|
|
635
|
-
onChange={(e) => handlePostTypeChange(e.target.value)}
|
|
636
|
-
style={{
|
|
637
|
-
flex: 1,
|
|
638
|
-
padding: '6px 8px',
|
|
639
|
-
border: '1px solid #8c8f94',
|
|
640
|
-
borderRadius: 4,
|
|
641
|
-
fontSize: 13,
|
|
642
|
-
background: '#fff',
|
|
643
|
-
}}
|
|
644
|
-
>
|
|
645
|
-
{availablePostTypes.map((pt) => (
|
|
646
|
-
<option key={pt.value} value={pt.value}>
|
|
647
|
-
{pt.label} ({pt.value})
|
|
648
|
-
</option>
|
|
649
|
-
))}
|
|
650
|
-
</select>
|
|
651
|
-
</div>
|
|
652
|
-
)}
|
|
653
|
-
|
|
654
|
-
{isMultiple ? (
|
|
655
|
-
<PostObjectMultiSelect
|
|
656
|
-
posts={searchResults}
|
|
657
|
-
selectedIds={selectedIds}
|
|
658
|
-
selectedPosts={selectedPosts}
|
|
659
|
-
loading={loading}
|
|
660
|
-
search={search}
|
|
661
|
-
setSearch={setSearch}
|
|
662
|
-
loadPosts={loadPosts}
|
|
663
|
-
handleSelect={handleSelect}
|
|
664
|
-
handleRemove={handleRemove}
|
|
665
|
-
handleReorder={handleReorder}
|
|
666
|
-
handleClear={handleClear}
|
|
667
|
-
control={control}
|
|
668
|
-
postTypeFilter={postTypeFilter}
|
|
669
|
-
setPostTypeFilter={setPostTypeFilter}
|
|
670
|
-
availablePostTypes={availablePostTypes}
|
|
671
|
-
/>
|
|
672
|
-
) : (
|
|
673
|
-
<PopoverOrModal
|
|
674
|
-
modalTitle={control.label || __('Select a post', 'gcblite')}
|
|
675
|
-
dropdownProps={{ popoverProps: { placement: 'left-start' } }}
|
|
676
|
-
renderToggle={({ isOpen, onToggle }) => (
|
|
677
|
-
<Button
|
|
678
|
-
onClick={onToggle}
|
|
679
|
-
aria-expanded={isOpen}
|
|
680
|
-
className="gcb-modal-toggle-button"
|
|
681
|
-
style={TOGGLE_BUTTON_STYLE}
|
|
682
|
-
>
|
|
683
|
-
<Truncate numberOfLines={1}>{getDisplayText()}</Truncate>
|
|
684
|
-
</Button>
|
|
685
|
-
)}
|
|
686
|
-
renderContent={({ close: onClose, variant }) => (
|
|
687
|
-
<div style={variant === 'modal' ? { width: '100%' } : { minWidth: 320, maxWidth: 400 }}>
|
|
688
|
-
<div style={{ padding: '0 16px 8px 16px' }}>
|
|
689
|
-
<TextControl
|
|
690
|
-
value={search}
|
|
691
|
-
onChange={(val) => {
|
|
692
|
-
setSearch(val);
|
|
693
|
-
loadPosts(val);
|
|
694
|
-
}}
|
|
695
|
-
placeholder={__('Search or type title', 'gcblite')}
|
|
696
|
-
__nextHasNoMarginBottom
|
|
697
|
-
/>
|
|
698
|
-
</div>
|
|
699
|
-
|
|
700
|
-
<div className="block-editor-link-control__search-results-wrapper" style={{ maxHeight: 300, overflowY: 'auto' }}>
|
|
701
|
-
{loading && (
|
|
702
|
-
<p style={{ textAlign: 'center', color: '#757575', padding: 16 }}>
|
|
703
|
-
{__('Loading…', 'gcblite')}
|
|
704
|
-
</p>
|
|
705
|
-
)}
|
|
706
|
-
{!loading && searchResults.length === 0 && (
|
|
707
|
-
<p style={{ textAlign: 'center', color: '#757575', padding: 16 }}>
|
|
708
|
-
{__('No posts found', 'gcblite')}
|
|
709
|
-
</p>
|
|
710
|
-
)}
|
|
711
|
-
{!loading && searchResults.length > 0 && (
|
|
712
|
-
<div className="block-editor-link-control__search-results" role="listbox">
|
|
713
|
-
<div className="components-menu-group">
|
|
714
|
-
<div role="group">
|
|
715
|
-
{searchResults.map((post) => {
|
|
716
|
-
const isSelected = singleSelectedId === post.id;
|
|
717
|
-
return (
|
|
718
|
-
<button
|
|
719
|
-
key={post.id}
|
|
720
|
-
type="button"
|
|
721
|
-
role="option"
|
|
722
|
-
className="components-button components-menu-item__button block-editor-link-control__search-item"
|
|
723
|
-
onClick={() => handleSelect(post.id)}
|
|
724
|
-
style={{
|
|
725
|
-
display: 'flex',
|
|
726
|
-
alignItems: 'center',
|
|
727
|
-
width: '100%',
|
|
728
|
-
padding: '8px 16px',
|
|
729
|
-
textAlign: 'left',
|
|
730
|
-
border: 'none',
|
|
731
|
-
background: 'transparent',
|
|
732
|
-
justifyContent: 'space-between',
|
|
733
|
-
}}
|
|
734
|
-
>
|
|
735
|
-
<span style={{ display: 'flex', alignItems: 'center', flex: 1, overflow: 'hidden' }}>
|
|
736
|
-
<svg
|
|
737
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
738
|
-
viewBox="0 0 24 24"
|
|
739
|
-
width="24"
|
|
740
|
-
height="24"
|
|
741
|
-
className="block-editor-link-control__search-item-icon"
|
|
742
|
-
style={{ marginRight: 12, flexShrink: 0 }}
|
|
743
|
-
>
|
|
744
|
-
<path d="M15.5 7.5h-7V9h7V7.5Zm-7 3.5h7v1.5h-7V11Zm7 3.5h-7V16h7v-1.5Z" />
|
|
745
|
-
<path d="M17 4H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2ZM7 5.5h10a.5.5 0 0 1 .5.5v12a.5.5 0 0 1-.5.5H7a.5.5 0 0 1-.5-.5V6a.5.5 0 0 1 .5-.5Z" />
|
|
746
|
-
</svg>
|
|
747
|
-
<span
|
|
748
|
-
className="components-menu-item__item"
|
|
749
|
-
style={{ fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
|
|
750
|
-
>
|
|
751
|
-
{post.title?.rendered || __('(no title)', 'gcblite')}
|
|
752
|
-
</span>
|
|
753
|
-
</span>
|
|
754
|
-
{isSelected && (
|
|
755
|
-
<svg
|
|
756
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
757
|
-
viewBox="0 0 24 24"
|
|
758
|
-
width="24"
|
|
759
|
-
height="24"
|
|
760
|
-
className="components-menu-items__item-icon has-icon-right"
|
|
761
|
-
style={{ marginLeft: 8, flexShrink: 0, fill: '#2271b1' }}
|
|
762
|
-
>
|
|
763
|
-
<path d="M16.5 7.5 10 13.9l-2.5-2.4-1 1 3.5 3.6 7.5-7.6z" />
|
|
764
|
-
</svg>
|
|
765
|
-
)}
|
|
766
|
-
</button>
|
|
767
|
-
);
|
|
768
|
-
})}
|
|
769
|
-
</div>
|
|
770
|
-
</div>
|
|
771
|
-
</div>
|
|
772
|
-
)}
|
|
773
|
-
</div>
|
|
774
|
-
|
|
775
|
-
{selectedPosts && (
|
|
776
|
-
<div style={{ padding: '8px 16px 16px', borderTop: '1px solid #ddd' }}>
|
|
777
|
-
<Button onClick={handleClear} variant="tertiary" style={{ width: '100%' }}>
|
|
778
|
-
{__('Clear Selection', 'gcblite')}
|
|
779
|
-
</Button>
|
|
780
|
-
</div>
|
|
781
|
-
)}
|
|
782
|
-
</div>
|
|
783
|
-
)}
|
|
784
|
-
/>
|
|
785
|
-
)}
|
|
786
|
-
</div>
|
|
787
|
-
);
|
|
788
|
-
}
|