@wordpress-gcb/fields 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +51 -35
  2. package/dist/conditional-logic.js +83 -0
  3. package/{src → dist}/control-context.js +3 -2
  4. package/{src → dist}/controls/MediaCapabilityGate.js +12 -8
  5. package/dist/controls/MediaPicker.js +149 -0
  6. package/dist/controls/MediaTriggerBadges.js +35 -0
  7. package/{src → dist}/controls/PopoverOrModal.js +49 -43
  8. package/dist/controls/SortableItem.js +126 -0
  9. package/dist/controls/button-group.js +46 -0
  10. package/dist/controls/checkbox-group.js +65 -0
  11. package/dist/controls/checkbox.js +15 -0
  12. package/dist/controls/code.js +24 -0
  13. package/dist/controls/color.js +241 -0
  14. package/dist/controls/date.js +55 -0
  15. package/dist/controls/datetime.js +61 -0
  16. package/dist/controls/email.js +17 -0
  17. package/dist/controls/file.js +163 -0
  18. package/dist/controls/gallery.js +371 -0
  19. package/dist/controls/google-map.js +143 -0
  20. package/dist/controls/heading-level.js +93 -0
  21. package/dist/controls/icon.js +292 -0
  22. package/dist/controls/image.js +360 -0
  23. package/dist/controls/index.js +88 -0
  24. package/dist/controls/message.js +86 -0
  25. package/dist/controls/number.js +19 -0
  26. package/dist/controls/oembed.js +42 -0
  27. package/{src → dist}/controls/page-link.js +1 -2
  28. package/dist/controls/post-object.js +913 -0
  29. package/dist/controls/radio.js +19 -0
  30. package/dist/controls/range.js +108 -0
  31. package/{src → dist}/controls/relationship.js +12 -7
  32. package/dist/controls/repeater.js +277 -0
  33. package/dist/controls/richtext.js +494 -0
  34. package/dist/controls/select.js +144 -0
  35. package/dist/controls/size.js +59 -0
  36. package/dist/controls/spacing.js +141 -0
  37. package/dist/controls/taxonomy.js +569 -0
  38. package/dist/controls/text.js +16 -0
  39. package/dist/controls/textarea.js +17 -0
  40. package/dist/controls/toggle-group.js +28 -0
  41. package/dist/controls/toggle.js +15 -0
  42. package/dist/controls/url.js +235 -0
  43. package/dist/controls/user.js +383 -0
  44. package/{src → dist}/controls/wysiwyg.js +1 -1
  45. package/{src → dist}/hooks/useTokens.js +25 -21
  46. package/{src → dist}/index.js +2 -8
  47. package/dist/inspector.js +163 -0
  48. package/{src → dist}/provider.js +18 -17
  49. package/dist/utils/map-utils.js +54 -0
  50. package/dist/utils/token-helper.js +396 -0
  51. package/{src → dist}/validation-context.js +4 -4
  52. package/package.json +35 -13
  53. package/src/conditional-logic.js +0 -77
  54. package/src/controls/MediaPicker.js +0 -139
  55. package/src/controls/MediaTriggerBadges.js +0 -31
  56. package/src/controls/SortableItem.js +0 -110
  57. package/src/controls/button-group.js +0 -49
  58. package/src/controls/checkbox-group.js +0 -55
  59. package/src/controls/checkbox.js +0 -13
  60. package/src/controls/code.js +0 -21
  61. package/src/controls/color.js +0 -235
  62. package/src/controls/date.js +0 -37
  63. package/src/controls/datetime.js +0 -54
  64. package/src/controls/email.js +0 -15
  65. package/src/controls/file.js +0 -134
  66. package/src/controls/gallery.js +0 -338
  67. package/src/controls/google-map.js +0 -117
  68. package/src/controls/heading-level.js +0 -99
  69. package/src/controls/icon.js +0 -301
  70. package/src/controls/image.js +0 -334
  71. package/src/controls/index.js +0 -95
  72. package/src/controls/message.js +0 -56
  73. package/src/controls/number.js +0 -17
  74. package/src/controls/oembed.js +0 -32
  75. package/src/controls/post-object.js +0 -788
  76. package/src/controls/radio.js +0 -18
  77. package/src/controls/range.js +0 -110
  78. package/src/controls/repeater.js +0 -290
  79. package/src/controls/richtext.js +0 -505
  80. package/src/controls/select.js +0 -141
  81. package/src/controls/size.js +0 -49
  82. package/src/controls/spacing.js +0 -141
  83. package/src/controls/taxonomy.js +0 -488
  84. package/src/controls/text.js +0 -14
  85. package/src/controls/textarea.js +0 -15
  86. package/src/controls/toggle-group.js +0 -34
  87. package/src/controls/toggle.js +0 -13
  88. package/src/controls/url.js +0 -164
  89. package/src/controls/user.js +0 -343
  90. package/src/inspector.js +0 -174
  91. package/src/utils/map-utils.js +0 -51
  92. package/src/utils/token-helper.js +0 -243
@@ -0,0 +1,569 @@
1
+ /**
2
+ * TaxonomyField — ported verbatim from the original GCB.
3
+ *
4
+ * Single OR multiple term selection (control.multiple, default true).
5
+ * Stored as ID(s) or full term object(s) (control.returnFormat).
6
+ * Optional "create new term" UI (control.allowCreateTerms).
7
+ * Drag-and-drop reordering of selected terms (multi-select only).
8
+ */
9
+
10
+ import { __ } from '@wordpress/i18n';
11
+ import { Button, TextControl } from '@wordpress/components';
12
+ import PopoverOrModal from './PopoverOrModal';
13
+ import { useState, useEffect, useMemo, useCallback } from '@wordpress/element';
14
+ import apiFetch from '@wordpress/api-fetch';
15
+ import { DndContext, closestCenter, PointerSensor, useSensor, useSensors, DragOverlay } from '@dnd-kit/core';
16
+ import { SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from '@dnd-kit/sortable';
17
+ import { CSS } from '@dnd-kit/utilities';
18
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
+ const TOGGLE_BUTTON_STYLE = {
20
+ width: '100%',
21
+ height: 'auto',
22
+ padding: '12px',
23
+ justifyContent: 'flex-start',
24
+ border: '1px solid #ddd',
25
+ borderRadius: '2px',
26
+ backgroundColor: '#fff'
27
+ };
28
+ function TermIcon() {
29
+ return /*#__PURE__*/_jsxs("svg", {
30
+ xmlns: "http://www.w3.org/2000/svg",
31
+ viewBox: "0 0 24 24",
32
+ width: "20",
33
+ height: "20",
34
+ style: {
35
+ marginRight: 8,
36
+ flexShrink: 0,
37
+ opacity: 0.6
38
+ },
39
+ children: [/*#__PURE__*/_jsx("path", {
40
+ d: "M8 12c0 1.1.9 2 2 2s2-.9 2-2-.9-2-2-2-2 .9-2 2zm8-2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
41
+ }), /*#__PURE__*/_jsx("path", {
42
+ d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"
43
+ })]
44
+ });
45
+ }
46
+ function SortableTermItem({
47
+ term,
48
+ onRemove
49
+ }) {
50
+ const {
51
+ attributes: dndAttributes,
52
+ listeners,
53
+ setNodeRef,
54
+ transform,
55
+ transition,
56
+ isDragging
57
+ } = useSortable({
58
+ id: term.id
59
+ });
60
+ const style = {
61
+ transform: CSS.Transform.toString(transform),
62
+ transition,
63
+ opacity: isDragging ? 0.5 : 1
64
+ };
65
+ return /*#__PURE__*/_jsxs("div", {
66
+ ref: setNodeRef,
67
+ style: style,
68
+ className: "gcb-post-object-selected-item",
69
+ ...dndAttributes,
70
+ ...listeners,
71
+ children: [/*#__PURE__*/_jsx("div", {
72
+ className: "gcb-post-object-drag-handle",
73
+ "aria-hidden": true,
74
+ children: /*#__PURE__*/_jsx("svg", {
75
+ viewBox: "0 0 20 20",
76
+ width: "12",
77
+ children: /*#__PURE__*/_jsx("path", {
78
+ 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"
79
+ })
80
+ })
81
+ }), /*#__PURE__*/_jsx(TermIcon, {}), /*#__PURE__*/_jsx("span", {
82
+ style: {
83
+ flex: 1,
84
+ overflow: 'hidden',
85
+ textOverflow: 'ellipsis',
86
+ whiteSpace: 'nowrap',
87
+ userSelect: 'none'
88
+ },
89
+ children: term.name || __('(no name)', 'gcblite')
90
+ }), /*#__PURE__*/_jsx("button", {
91
+ type: "button",
92
+ onClick: e => {
93
+ e.stopPropagation();
94
+ onRemove(term.id);
95
+ },
96
+ onPointerDown: e => e.stopPropagation(),
97
+ className: "gcb-sortable-remove",
98
+ "aria-label": __('Remove', 'gcblite'),
99
+ children: /*#__PURE__*/_jsx("svg", {
100
+ xmlns: "http://www.w3.org/2000/svg",
101
+ viewBox: "0 0 24 24",
102
+ width: "20",
103
+ height: "20",
104
+ children: /*#__PURE__*/_jsx("path", {
105
+ 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"
106
+ })
107
+ })
108
+ })]
109
+ });
110
+ }
111
+
112
+ // Normalise whatever the value used to look like into the canonical
113
+ // { taxonomy, ids[] } shape. Handles:
114
+ // - bare ID (legacy single) → { taxonomy: schemaDefault, ids: [3] }
115
+ // - array of IDs (legacy multi) → { taxonomy: schemaDefault, ids: [3, 5] }
116
+ // - { id, name, taxonomy } (returnFormat=object, single)
117
+ // - array of those objects (returnFormat=object, multi)
118
+ // - new canonical { taxonomy, ids[] } → passes through
119
+ function normaliseTaxonomyValue(value, schemaDefault) {
120
+ if (!value) return {
121
+ taxonomy: schemaDefault,
122
+ ids: []
123
+ };
124
+ // Already canonical.
125
+ if (typeof value === 'object' && !Array.isArray(value) && Array.isArray(value.ids)) {
126
+ return {
127
+ taxonomy: value.taxonomy || schemaDefault,
128
+ ids: value.ids
129
+ };
130
+ }
131
+ // Bare scalar (legacy single).
132
+ if (typeof value === 'number' || typeof value === 'string') {
133
+ return {
134
+ taxonomy: schemaDefault,
135
+ ids: [Number(value)]
136
+ };
137
+ }
138
+ // Single object (returnFormat=object).
139
+ if (typeof value === 'object' && !Array.isArray(value) && value.id != null) {
140
+ return {
141
+ taxonomy: value.taxonomy || schemaDefault,
142
+ ids: [Number(value.id)]
143
+ };
144
+ }
145
+ // Array shapes.
146
+ if (Array.isArray(value)) {
147
+ const ids = value.map(entry => typeof entry === 'object' ? Number(entry.id) : Number(entry)).filter(Boolean);
148
+ const tx = value.find(entry => typeof entry === 'object' && entry?.taxonomy)?.taxonomy;
149
+ return {
150
+ taxonomy: tx || schemaDefault,
151
+ ids
152
+ };
153
+ }
154
+ return {
155
+ taxonomy: schemaDefault,
156
+ ids: []
157
+ };
158
+ }
159
+ const REST_BASE_OVERRIDES = {
160
+ category: 'categories',
161
+ post_tag: 'tags'
162
+ };
163
+ function resolveRestBase(taxonomy, override) {
164
+ if (override) return override;
165
+ return REST_BASE_OVERRIDES[taxonomy] || taxonomy;
166
+ }
167
+ export default function TaxonomyField({
168
+ control,
169
+ value,
170
+ onChange
171
+ }) {
172
+ const [allTerms, setAllTerms] = useState([]);
173
+ const [searchResults, setSearchResults] = useState([]);
174
+ const [loading, setLoading] = useState(false);
175
+ const [search, setSearch] = useState('');
176
+ const [creatingTerm, setCreatingTerm] = useState(false);
177
+ const [newTermName, setNewTermName] = useState('');
178
+ const [activeId, setActiveId] = useState(null);
179
+ const [availableTaxonomies, setAvailableTaxonomies] = useState([]);
180
+ const isMultiple = control.multiple !== false;
181
+ // Schema-locked vs author-picks-at-edit-time.
182
+ // When the schema omits `taxonomy`, the editor user picks via a
183
+ // dropdown. The picked value is stored alongside the IDs so the
184
+ // renderer can still resolve them at read-time.
185
+ const dynamic = !control.taxonomy;
186
+ const schemaDefault = control.taxonomy || 'category';
187
+
188
+ // Canonical { taxonomy, ids[] } shape.
189
+ const normalised = useMemo(() => normaliseTaxonomyValue(value, schemaDefault), [value, schemaDefault]);
190
+ const taxonomy = normalised.taxonomy || schemaDefault;
191
+ const selectedIds = normalised.ids;
192
+ const restBase = resolveRestBase(taxonomy, control.restBase);
193
+ const selectedTerms = useMemo(() => selectedIds.map(id => allTerms.find(t => t.id === id)).filter(Boolean), [selectedIds, allTerms]);
194
+
195
+ // Fetch the list of registered taxonomies once, only when the schema
196
+ // didn't lock to one. Used to populate the taxonomy dropdown.
197
+ useEffect(() => {
198
+ if (!dynamic) return;
199
+ let cancelled = false;
200
+ apiFetch({
201
+ path: '/wp/v2/taxonomies?context=view'
202
+ }).then(res => {
203
+ if (cancelled) return;
204
+ // REST returns an object keyed by taxonomy slug.
205
+ const list = Object.entries(res || {}).map(([slug, info]) => ({
206
+ slug,
207
+ name: info?.name || slug,
208
+ restBase: info?.rest_base || slug
209
+ }));
210
+ setAvailableTaxonomies(list);
211
+ }).catch(() => {});
212
+ return () => {
213
+ cancelled = true;
214
+ };
215
+ }, [dynamic]);
216
+
217
+ // Emit the canonical shape — always { taxonomy, ids[] }, regardless
218
+ // of single/multi, so the renderer never has to guess.
219
+ const emitChange = useCallback(ids => {
220
+ onChange({
221
+ taxonomy,
222
+ ids
223
+ });
224
+ }, [onChange, taxonomy]);
225
+ const handleTaxonomyChange = newTax => {
226
+ // Switching taxonomy clears the selected terms — IDs from one
227
+ // taxonomy don't translate to another.
228
+ onChange({
229
+ taxonomy: newTax,
230
+ ids: []
231
+ });
232
+ setAllTerms([]);
233
+ setSearchResults([]);
234
+ };
235
+ const mergeTermsIntoCache = useCallback(newTerms => {
236
+ setAllTerms(prev => {
237
+ const merged = [...prev];
238
+ newTerms.forEach(nt => {
239
+ if (!merged.find(t => t.id === nt.id)) merged.push(nt);
240
+ });
241
+ return merged;
242
+ });
243
+ }, []);
244
+ const loadTerms = useCallback(async (term = '') => {
245
+ setLoading(true);
246
+ try {
247
+ const response = await apiFetch({
248
+ path: `/wp/v2/${restBase}?search=${encodeURIComponent(term)}&per_page=100&_fields=id,name`
249
+ });
250
+ setSearchResults(response);
251
+ mergeTermsIntoCache(response);
252
+ } catch {
253
+ // ignore
254
+ }
255
+ setLoading(false);
256
+ }, [restBase, mergeTermsIntoCache]);
257
+ useEffect(() => {
258
+ loadTerms();
259
+ }, [loadTerms]);
260
+ const handleSelect = termId => {
261
+ const newIds = isMultiple ? selectedIds.includes(termId) ? selectedIds.filter(id => id !== termId) : [...selectedIds, termId] : [termId];
262
+ emitChange(newIds);
263
+ };
264
+ const handleRemove = termId => {
265
+ emitChange(selectedIds.filter(id => id !== termId));
266
+ };
267
+ const handleReorder = newOrder => {
268
+ emitChange(newOrder);
269
+ };
270
+ const handleClear = () => emitChange([]);
271
+ const handleCreateTerm = async () => {
272
+ if (!newTermName.trim() || !control.allowCreateTerms) return;
273
+ setCreatingTerm(true);
274
+ try {
275
+ const newTerm = await apiFetch({
276
+ path: `/wp/v2/${restBase}`,
277
+ method: 'POST',
278
+ data: {
279
+ name: newTermName.trim()
280
+ }
281
+ });
282
+ mergeTermsIntoCache([newTerm]);
283
+ setSearchResults(prev => [...prev, newTerm]);
284
+ handleSelect(newTerm.id);
285
+ setNewTermName('');
286
+ } catch {
287
+ // ignore
288
+ }
289
+ setCreatingTerm(false);
290
+ };
291
+ const sensors = useSensors(useSensor(PointerSensor, {
292
+ activationConstraint: {
293
+ distance: 8
294
+ }
295
+ }));
296
+ const handleDragStart = e => setActiveId(e.active.id);
297
+ const handleDragCancel = () => setActiveId(null);
298
+ const handleDragEnd = e => {
299
+ const {
300
+ active,
301
+ over
302
+ } = e;
303
+ if (over && active.id !== over.id) {
304
+ const oldIndex = selectedIds.indexOf(active.id);
305
+ const newIndex = selectedIds.indexOf(over.id);
306
+ handleReorder(arrayMove(selectedIds, oldIndex, newIndex));
307
+ }
308
+ setActiveId(null);
309
+ };
310
+ const activeTerm = activeId ? selectedTerms.find(t => t.id === activeId) : null;
311
+ const availableTerms = searchResults.filter(t => !selectedIds.includes(t.id));
312
+ return /*#__PURE__*/_jsxs("div", {
313
+ className: "components-base-control gcb-taxonomy-control",
314
+ children: [/*#__PURE__*/_jsx("div", {
315
+ className: "components-base-control__field",
316
+ children: /*#__PURE__*/_jsx("label", {
317
+ className: "components-base-control__label",
318
+ children: control.label
319
+ })
320
+ }), control.helpText && /*#__PURE__*/_jsx("p", {
321
+ className: "components-base-control__help",
322
+ children: control.helpText
323
+ }), /*#__PURE__*/_jsxs("div", {
324
+ className: "gcb-post-object-stacked",
325
+ children: [dynamic && /*#__PURE__*/_jsxs("div", {
326
+ style: {
327
+ display: 'flex',
328
+ gap: 8,
329
+ alignItems: 'center',
330
+ marginBottom: 8
331
+ },
332
+ children: [/*#__PURE__*/_jsx("label", {
333
+ style: {
334
+ fontSize: 12,
335
+ fontWeight: 600,
336
+ color: '#1e1e1e',
337
+ minWidth: 70
338
+ },
339
+ children: __('Taxonomy', 'gcblite')
340
+ }), /*#__PURE__*/_jsxs("select", {
341
+ value: taxonomy,
342
+ onChange: e => handleTaxonomyChange(e.target.value),
343
+ style: {
344
+ flex: 1,
345
+ padding: '6px 8px',
346
+ border: '1px solid #8c8f94',
347
+ borderRadius: 4,
348
+ fontSize: 13,
349
+ background: '#fff'
350
+ },
351
+ children: [availableTaxonomies.length === 0 && /*#__PURE__*/_jsx("option", {
352
+ value: taxonomy,
353
+ children: taxonomy
354
+ }), availableTaxonomies.map(tx => /*#__PURE__*/_jsxs("option", {
355
+ value: tx.slug,
356
+ children: [tx.name, " (", tx.slug, ")"]
357
+ }, tx.slug))]
358
+ })]
359
+ }), /*#__PURE__*/_jsxs("div", {
360
+ style: {
361
+ display: 'flex',
362
+ gap: 8,
363
+ alignItems: 'center'
364
+ },
365
+ children: [/*#__PURE__*/_jsx(PopoverOrModal, {
366
+ modalTitle: control.label || __('Select terms', 'gcblite'),
367
+ dropdownProps: {
368
+ popoverProps: {
369
+ placement: 'left-start'
370
+ }
371
+ },
372
+ renderToggle: ({
373
+ isOpen,
374
+ onToggle
375
+ }) => /*#__PURE__*/_jsx(Button, {
376
+ onClick: onToggle,
377
+ "aria-expanded": isOpen,
378
+ className: "gcb-modal-toggle-button",
379
+ style: {
380
+ ...TOGGLE_BUTTON_STYLE,
381
+ flex: 1
382
+ },
383
+ children: selectedTerms.length > 0 ? `${selectedTerms.length} ${selectedTerms.length === 1 ? __('term', 'gcblite') : __('terms', 'gcblite')} ${__('selected', 'gcblite')}` : __('Select Terms', 'gcblite')
384
+ }),
385
+ renderContent: ({
386
+ close: onClose,
387
+ variant
388
+ }) => /*#__PURE__*/_jsxs("div", {
389
+ style: variant === 'modal' ? {
390
+ width: '100%'
391
+ } : {
392
+ minWidth: 320,
393
+ maxWidth: 400
394
+ },
395
+ children: [/*#__PURE__*/_jsxs("div", {
396
+ style: {
397
+ padding: '0 16px 8px 16px'
398
+ },
399
+ children: [/*#__PURE__*/_jsx(TextControl, {
400
+ value: search,
401
+ onChange: val => {
402
+ setSearch(val);
403
+ loadTerms(val);
404
+ },
405
+ placeholder: __('Search…', 'gcblite'),
406
+ __nextHasNoMarginBottom: true
407
+ }), control.allowCreateTerms && /*#__PURE__*/_jsxs("div", {
408
+ style: {
409
+ marginTop: 12,
410
+ padding: 12,
411
+ background: '#f0f6fc',
412
+ borderRadius: 4
413
+ },
414
+ children: [/*#__PURE__*/_jsx(TextControl, {
415
+ label: __('Create New Term', 'gcblite'),
416
+ value: newTermName,
417
+ onChange: setNewTermName,
418
+ placeholder: __('Enter term name…', 'gcblite'),
419
+ disabled: creatingTerm,
420
+ __nextHasNoMarginBottom: true
421
+ }), /*#__PURE__*/_jsx(Button, {
422
+ variant: "primary",
423
+ size: "small",
424
+ onClick: handleCreateTerm,
425
+ disabled: !newTermName.trim() || creatingTerm,
426
+ style: {
427
+ marginTop: 8
428
+ },
429
+ children: creatingTerm ? __('Creating…', 'gcblite') : __('Create', 'gcblite')
430
+ })]
431
+ })]
432
+ }), /*#__PURE__*/_jsxs("div", {
433
+ className: "block-editor-link-control__search-results-wrapper",
434
+ style: {
435
+ maxHeight: 300,
436
+ overflowY: 'auto'
437
+ },
438
+ children: [loading && /*#__PURE__*/_jsx("p", {
439
+ style: {
440
+ textAlign: 'center',
441
+ color: '#757575',
442
+ padding: 16
443
+ },
444
+ children: __('Loading…', 'gcblite')
445
+ }), !loading && availableTerms.length === 0 && /*#__PURE__*/_jsx("p", {
446
+ style: {
447
+ textAlign: 'center',
448
+ color: '#757575',
449
+ padding: 16
450
+ },
451
+ children: __('No terms found', 'gcblite')
452
+ }), !loading && availableTerms.length > 0 && /*#__PURE__*/_jsx("div", {
453
+ className: "block-editor-link-control__search-results",
454
+ role: "listbox",
455
+ children: /*#__PURE__*/_jsx("div", {
456
+ className: "components-menu-group",
457
+ children: /*#__PURE__*/_jsx("div", {
458
+ role: "group",
459
+ children: availableTerms.map(term => /*#__PURE__*/_jsx("button", {
460
+ type: "button",
461
+ role: "option",
462
+ className: "components-button components-menu-item__button block-editor-link-control__search-item",
463
+ onClick: () => handleSelect(term.id),
464
+ style: {
465
+ display: 'flex',
466
+ alignItems: 'center',
467
+ width: '100%',
468
+ padding: '8px 16px',
469
+ textAlign: 'left',
470
+ border: 'none',
471
+ background: 'transparent',
472
+ justifyContent: 'space-between'
473
+ },
474
+ children: /*#__PURE__*/_jsxs("span", {
475
+ style: {
476
+ display: 'flex',
477
+ alignItems: 'center',
478
+ flex: 1,
479
+ overflow: 'hidden'
480
+ },
481
+ children: [/*#__PURE__*/_jsx(TermIcon, {}), /*#__PURE__*/_jsx("span", {
482
+ className: "components-menu-item__item",
483
+ style: {
484
+ fontWeight: 500,
485
+ overflow: 'hidden',
486
+ textOverflow: 'ellipsis',
487
+ whiteSpace: 'nowrap'
488
+ },
489
+ children: term.name
490
+ })]
491
+ })
492
+ }, term.id))
493
+ })
494
+ })
495
+ })]
496
+ })]
497
+ })
498
+ }), selectedTerms.length > 0 && /*#__PURE__*/_jsx(Button, {
499
+ onClick: handleClear,
500
+ variant: "secondary",
501
+ isSmall: true,
502
+ className: "components-range-control__reset",
503
+ children: __('Reset', 'gcblite')
504
+ })]
505
+ }), selectedTerms.length > 0 && isMultiple && /*#__PURE__*/_jsx("div", {
506
+ className: "gcb-post-object-selected-list",
507
+ style: {
508
+ marginTop: 8
509
+ },
510
+ children: /*#__PURE__*/_jsxs(DndContext, {
511
+ sensors: sensors,
512
+ collisionDetection: closestCenter,
513
+ onDragStart: handleDragStart,
514
+ onDragEnd: handleDragEnd,
515
+ onDragCancel: handleDragCancel,
516
+ children: [/*#__PURE__*/_jsx(SortableContext, {
517
+ items: selectedIds,
518
+ strategy: verticalListSortingStrategy,
519
+ children: selectedTerms.map(term => /*#__PURE__*/_jsx(SortableTermItem, {
520
+ term: term,
521
+ onRemove: handleRemove
522
+ }, term.id))
523
+ }), /*#__PURE__*/_jsx(DragOverlay, {
524
+ children: activeTerm ? /*#__PURE__*/_jsxs("div", {
525
+ className: "gcb-post-object-selected-item",
526
+ style: {
527
+ opacity: 0.8,
528
+ boxShadow: '0 2px 8px rgba(0,0,0,0.15)'
529
+ },
530
+ children: [/*#__PURE__*/_jsx("div", {
531
+ className: "gcb-post-object-drag-handle",
532
+ "aria-hidden": true,
533
+ children: /*#__PURE__*/_jsx("svg", {
534
+ viewBox: "0 0 20 20",
535
+ width: "12",
536
+ children: /*#__PURE__*/_jsx("path", {
537
+ 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"
538
+ })
539
+ })
540
+ }), /*#__PURE__*/_jsx(TermIcon, {}), /*#__PURE__*/_jsx("span", {
541
+ style: {
542
+ flex: 1
543
+ },
544
+ children: activeTerm.name
545
+ })]
546
+ }) : null
547
+ })]
548
+ })
549
+ }), selectedTerms.length > 0 && !isMultiple && /*#__PURE__*/_jsx("div", {
550
+ className: "gcb-post-object-selected-list",
551
+ style: {
552
+ marginTop: 8
553
+ },
554
+ children: /*#__PURE__*/_jsxs("div", {
555
+ className: "gcb-post-object-selected-item",
556
+ children: [/*#__PURE__*/_jsx(TermIcon, {}), /*#__PURE__*/_jsx("span", {
557
+ style: {
558
+ flex: 1,
559
+ overflow: 'hidden',
560
+ textOverflow: 'ellipsis',
561
+ whiteSpace: 'nowrap'
562
+ },
563
+ children: selectedTerms[0].name
564
+ })]
565
+ })
566
+ })]
567
+ })]
568
+ });
569
+ }
@@ -0,0 +1,16 @@
1
+ import { TextControl } from '@wordpress/components';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ export default function TextField({
4
+ control,
5
+ value,
6
+ onChange
7
+ }) {
8
+ return /*#__PURE__*/_jsx(TextControl, {
9
+ label: control.label,
10
+ help: control.helpText,
11
+ placeholder: control.placeholder,
12
+ value: value ?? '',
13
+ onChange: onChange,
14
+ __nextHasNoMarginBottom: true
15
+ });
16
+ }
@@ -0,0 +1,17 @@
1
+ import { TextareaControl } from '@wordpress/components';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ export default function TextareaField({
4
+ control,
5
+ value,
6
+ onChange
7
+ }) {
8
+ return /*#__PURE__*/_jsx(TextareaControl, {
9
+ label: control.label,
10
+ help: control.helpText,
11
+ placeholder: control.placeholder,
12
+ value: value ?? '',
13
+ onChange: onChange,
14
+ rows: 4,
15
+ __nextHasNoMarginBottom: true
16
+ });
17
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * ToggleGroup — radio-style segmented control. Stores a single value.
3
+ */
4
+ import { __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption } from '@wordpress/components';
5
+ import { jsx as _jsx } from "react/jsx-runtime";
6
+ export default function ToggleGroupField({
7
+ control,
8
+ value,
9
+ onChange
10
+ }) {
11
+ // ToggleGroupControl needs undefined (not '') when nothing is selected,
12
+ // otherwise it shows every option as checked.
13
+ const controlValue = value || undefined;
14
+ return /*#__PURE__*/_jsx(ToggleGroupControl, {
15
+ label: control.label,
16
+ value: controlValue,
17
+ onChange: onChange,
18
+ help: control.helpText,
19
+ isBlock: control.isBlock !== false,
20
+ className: "gcb-toggle-group-control",
21
+ __nextHasNoMarginBottom: true,
22
+ __next40pxDefaultSize: true,
23
+ children: (control.options || []).map((option, idx) => /*#__PURE__*/_jsx(ToggleGroupControlOption, {
24
+ value: option.value,
25
+ label: option.label
26
+ }, option.value || idx))
27
+ });
28
+ }
@@ -0,0 +1,15 @@
1
+ import { ToggleControl } from '@wordpress/components';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ export default function ToggleField({
4
+ control,
5
+ value,
6
+ onChange
7
+ }) {
8
+ return /*#__PURE__*/_jsx(ToggleControl, {
9
+ label: control.label,
10
+ help: control.helpText,
11
+ checked: !!value,
12
+ onChange: onChange,
13
+ __nextHasNoMarginBottom: true
14
+ });
15
+ }