@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
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RadioControl } from '@wordpress/components';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
export default function RadioField({
|
|
4
|
+
control,
|
|
5
|
+
value,
|
|
6
|
+
onChange
|
|
7
|
+
}) {
|
|
8
|
+
const options = (control.options || []).map(o => ({
|
|
9
|
+
label: o.label,
|
|
10
|
+
value: String(o.value)
|
|
11
|
+
}));
|
|
12
|
+
return /*#__PURE__*/_jsx(RadioControl, {
|
|
13
|
+
label: control.label,
|
|
14
|
+
help: control.helpText,
|
|
15
|
+
selected: value != null ? String(value) : '',
|
|
16
|
+
options: options,
|
|
17
|
+
onChange: onChange
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RangeField — ported verbatim from the original.
|
|
3
|
+
* Supports raw numeric ranges, legacy `map` configs, and `tokenGroup` binding.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { __ } from '@wordpress/i18n';
|
|
7
|
+
import { RangeControl, Spinner } from '@wordpress/components';
|
|
8
|
+
import { parseMap, getTokenFromKey, getKeyFromToken, getMapKeys, mapToRangeMarks } from '../utils/map-utils';
|
|
9
|
+
import { useTokens, getTokensByGroup, generateMapFromTokens } from '../hooks/useTokens';
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
function RangeFieldImpl({
|
|
12
|
+
label,
|
|
13
|
+
value,
|
|
14
|
+
onChange,
|
|
15
|
+
min = 0,
|
|
16
|
+
max = 100,
|
|
17
|
+
step = 1,
|
|
18
|
+
help,
|
|
19
|
+
allowReset = true,
|
|
20
|
+
resetFallbackValue,
|
|
21
|
+
className = '',
|
|
22
|
+
map,
|
|
23
|
+
tokenGroup,
|
|
24
|
+
defaultOptionKey
|
|
25
|
+
}) {
|
|
26
|
+
const {
|
|
27
|
+
tokens,
|
|
28
|
+
loading
|
|
29
|
+
} = useTokens();
|
|
30
|
+
let normalizedMap = null;
|
|
31
|
+
if (tokenGroup && tokens) {
|
|
32
|
+
const groupTokens = getTokensByGroup(tokens, tokenGroup);
|
|
33
|
+
if (groupTokens) {
|
|
34
|
+
const generatedMap = generateMapFromTokens(groupTokens);
|
|
35
|
+
normalizedMap = generatedMap ? parseMap(generatedMap) : null;
|
|
36
|
+
}
|
|
37
|
+
} else if (map) {
|
|
38
|
+
normalizedMap = parseMap(map);
|
|
39
|
+
}
|
|
40
|
+
if (tokenGroup && loading) {
|
|
41
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
42
|
+
style: {
|
|
43
|
+
padding: '12px 0'
|
|
44
|
+
},
|
|
45
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
46
|
+
style: {
|
|
47
|
+
fontWeight: 500,
|
|
48
|
+
marginBottom: '8px'
|
|
49
|
+
},
|
|
50
|
+
children: label
|
|
51
|
+
}), /*#__PURE__*/_jsx(Spinner, {})]
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const allowedKeys = normalizedMap ? getMapKeys(normalizedMap) : null;
|
|
55
|
+
const marks = normalizedMap ? mapToRangeMarks(normalizedMap) : null;
|
|
56
|
+
if ((value === undefined || value === null || value === '') && defaultOptionKey && normalizedMap) {
|
|
57
|
+
const defaultToken = getTokenFromKey(normalizedMap, defaultOptionKey);
|
|
58
|
+
if (defaultToken && onChange) onChange(defaultToken);
|
|
59
|
+
}
|
|
60
|
+
let displayValue;
|
|
61
|
+
if (normalizedMap && value) {
|
|
62
|
+
displayValue = Number(getKeyFromToken(normalizedMap, value)) || value;
|
|
63
|
+
} else if (typeof value === 'string' && !isNaN(value)) {
|
|
64
|
+
displayValue = Number(value);
|
|
65
|
+
} else {
|
|
66
|
+
displayValue = value;
|
|
67
|
+
}
|
|
68
|
+
const handleChange = newSliderValue => {
|
|
69
|
+
let finalValue = normalizedMap ? getTokenFromKey(normalizedMap, String(newSliderValue)) : newSliderValue;
|
|
70
|
+
if (onChange) onChange(finalValue);
|
|
71
|
+
};
|
|
72
|
+
const effectiveStep = normalizedMap && allowedKeys && allowedKeys.length > 1 ? 1 : step;
|
|
73
|
+
const effectiveMin = normalizedMap && allowedKeys?.length ? Math.min(...allowedKeys) : min;
|
|
74
|
+
const effectiveMax = normalizedMap && allowedKeys?.length ? Math.max(...allowedKeys) : max;
|
|
75
|
+
return /*#__PURE__*/_jsx(RangeControl, {
|
|
76
|
+
label: label,
|
|
77
|
+
value: displayValue,
|
|
78
|
+
onChange: handleChange,
|
|
79
|
+
min: effectiveMin,
|
|
80
|
+
max: effectiveMax,
|
|
81
|
+
step: effectiveStep,
|
|
82
|
+
marks: marks,
|
|
83
|
+
help: help,
|
|
84
|
+
allowReset: allowReset,
|
|
85
|
+
resetFallbackValue: resetFallbackValue,
|
|
86
|
+
className: className,
|
|
87
|
+
__nextHasNoMarginBottom: true,
|
|
88
|
+
__next40pxDefaultSize: true
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
export default function RangeField({
|
|
92
|
+
control,
|
|
93
|
+
value,
|
|
94
|
+
onChange
|
|
95
|
+
}) {
|
|
96
|
+
return /*#__PURE__*/_jsx(RangeFieldImpl, {
|
|
97
|
+
label: control.label,
|
|
98
|
+
value: value,
|
|
99
|
+
onChange: onChange,
|
|
100
|
+
min: control.min ?? 0,
|
|
101
|
+
max: control.max ?? 100,
|
|
102
|
+
step: control.step ?? 1,
|
|
103
|
+
help: control.helpText,
|
|
104
|
+
map: control.map,
|
|
105
|
+
tokenGroup: control.tokenGroup,
|
|
106
|
+
defaultOptionKey: control.defaultOptionKey ?? control.default
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -4,11 +4,16 @@
|
|
|
4
4
|
* multi-select mode, which handles the same use case.
|
|
5
5
|
*/
|
|
6
6
|
import PostObjectField from './post-object';
|
|
7
|
-
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
8
|
export default function RelationshipField(props) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
9
|
+
const merged = {
|
|
10
|
+
...props,
|
|
11
|
+
control: {
|
|
12
|
+
...props.control,
|
|
13
|
+
multiple: true
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return /*#__PURE__*/_jsx(PostObjectField, {
|
|
17
|
+
...merged
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repeater Inspector control — multi-row form-of-forms.
|
|
3
|
+
*
|
|
4
|
+
* Distinct from the `gcb/repeater` block (which is an InnerBlocks-based
|
|
5
|
+
* canvas surface). This control lives in the Inspector / post-fields /
|
|
6
|
+
* options page, and stores an ARRAY OF OBJECTS — each object a "row"
|
|
7
|
+
* shaped by the `fields` sub-config.
|
|
8
|
+
*
|
|
9
|
+
* {
|
|
10
|
+
* attributeKey: 'social_links',
|
|
11
|
+
* type: 'repeater',
|
|
12
|
+
* label: 'Social links',
|
|
13
|
+
* fields: [
|
|
14
|
+
* { attributeKey: 'label', type: 'text', label: 'Label' },
|
|
15
|
+
* { attributeKey: 'url', type: 'url', label: 'URL' },
|
|
16
|
+
* ],
|
|
17
|
+
* min: 0,
|
|
18
|
+
* max: null, // null = unlimited
|
|
19
|
+
* addButtonLabel: 'Add link',
|
|
20
|
+
* collapsedTitle: 'label', // which sub-field to show in row header
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* Stored:
|
|
24
|
+
* [
|
|
25
|
+
* { _id: 'r1', label: 'GitHub', url: 'https://...' },
|
|
26
|
+
* { _id: 'r2', label: 'X', url: 'https://...' },
|
|
27
|
+
* ]
|
|
28
|
+
*
|
|
29
|
+
* Each row carries a stable `_id` so React keys + dnd-kit don't lose
|
|
30
|
+
* track during reorders. _id is generated on add and persisted with
|
|
31
|
+
* the row.
|
|
32
|
+
*
|
|
33
|
+
* Sub-fields can be any registered control type — the row body uses
|
|
34
|
+
* the same `controlComponents` registry as the top-level Inspector,
|
|
35
|
+
* so adding a control type automatically makes it usable inside a
|
|
36
|
+
* repeater.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { __ } from '@wordpress/i18n';
|
|
40
|
+
import { useEffect, useState } from '@wordpress/element';
|
|
41
|
+
import { Button } from '@wordpress/components';
|
|
42
|
+
import { DndContext, closestCenter, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
|
43
|
+
import { SortableContext, verticalListSortingStrategy, useSortable, arrayMove } from '@dnd-kit/sortable';
|
|
44
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
45
|
+
import { controlComponents } from './index';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* cheap-enough unique id. Crypto.randomUUID would be better but is
|
|
49
|
+
* unsupported in some Playground / older Safari builds we still want to
|
|
50
|
+
* cover. Collision risk on a single repeater is negligible.
|
|
51
|
+
*/
|
|
52
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
53
|
+
function newRowId() {
|
|
54
|
+
return 'r' + Math.random().toString(36).slice(2, 10);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Read `control.collapsedTitle` (a sub-field attributeKey) and pull the
|
|
59
|
+
* matching value off the row. Falls back to the index-based "Item N"
|
|
60
|
+
* label so empty rows are still identifiable.
|
|
61
|
+
*/
|
|
62
|
+
function rowTitle(row, control, index) {
|
|
63
|
+
const key = control.collapsedTitle;
|
|
64
|
+
if (key && typeof row?.[key] === 'string' && row[key].trim() !== '') {
|
|
65
|
+
return row[key];
|
|
66
|
+
}
|
|
67
|
+
return `${__('Item', 'gcblite')} ${index + 1}`;
|
|
68
|
+
}
|
|
69
|
+
function SortableRow({
|
|
70
|
+
row,
|
|
71
|
+
index,
|
|
72
|
+
control,
|
|
73
|
+
isOpen,
|
|
74
|
+
onToggle,
|
|
75
|
+
onUpdate,
|
|
76
|
+
onRemove,
|
|
77
|
+
attributes: parentAttrs
|
|
78
|
+
}) {
|
|
79
|
+
const {
|
|
80
|
+
attributes,
|
|
81
|
+
listeners,
|
|
82
|
+
setNodeRef,
|
|
83
|
+
transform,
|
|
84
|
+
transition,
|
|
85
|
+
isDragging
|
|
86
|
+
} = useSortable({
|
|
87
|
+
id: row._id
|
|
88
|
+
});
|
|
89
|
+
const style = {
|
|
90
|
+
transform: CSS.Transform.toString(transform),
|
|
91
|
+
transition,
|
|
92
|
+
opacity: isDragging ? 0.5 : 1
|
|
93
|
+
};
|
|
94
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
95
|
+
ref: setNodeRef,
|
|
96
|
+
style: style,
|
|
97
|
+
className: "gcb-repeater-row",
|
|
98
|
+
children: [/*#__PURE__*/_jsxs("div", {
|
|
99
|
+
className: "gcb-repeater-row__header",
|
|
100
|
+
children: [/*#__PURE__*/_jsx("button", {
|
|
101
|
+
type: "button",
|
|
102
|
+
...attributes,
|
|
103
|
+
...listeners,
|
|
104
|
+
className: "gcb-repeater-row__handle",
|
|
105
|
+
"aria-label": __('Drag to reorder', 'gcblite'),
|
|
106
|
+
onClick: e => e.preventDefault(),
|
|
107
|
+
children: /*#__PURE__*/_jsx("svg", {
|
|
108
|
+
viewBox: "0 0 20 20",
|
|
109
|
+
width: "12",
|
|
110
|
+
height: "12",
|
|
111
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
112
|
+
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"
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
}), /*#__PURE__*/_jsxs("button", {
|
|
116
|
+
type: "button",
|
|
117
|
+
className: "gcb-repeater-row__title",
|
|
118
|
+
onClick: onToggle,
|
|
119
|
+
"aria-expanded": isOpen,
|
|
120
|
+
children: [/*#__PURE__*/_jsx("span", {
|
|
121
|
+
className: "gcb-repeater-row__caret",
|
|
122
|
+
"aria-hidden": true,
|
|
123
|
+
children: isOpen ? '▾' : '▸'
|
|
124
|
+
}), /*#__PURE__*/_jsx("span", {
|
|
125
|
+
className: "gcb-repeater-row__title-text",
|
|
126
|
+
children: rowTitle(row, control, index)
|
|
127
|
+
})]
|
|
128
|
+
}), /*#__PURE__*/_jsx("button", {
|
|
129
|
+
type: "button",
|
|
130
|
+
className: "gcb-repeater-row__remove",
|
|
131
|
+
onClick: onRemove,
|
|
132
|
+
"aria-label": __('Remove row', 'gcblite'),
|
|
133
|
+
children: /*#__PURE__*/_jsx("svg", {
|
|
134
|
+
viewBox: "0 0 24 24",
|
|
135
|
+
width: "16",
|
|
136
|
+
height: "16",
|
|
137
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
138
|
+
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"
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
})]
|
|
142
|
+
}), isOpen && /*#__PURE__*/_jsx("div", {
|
|
143
|
+
className: "gcb-repeater-row__body",
|
|
144
|
+
children: (control.fields || []).map(sub => {
|
|
145
|
+
const SubComponent = controlComponents[sub.type];
|
|
146
|
+
if (!SubComponent) {
|
|
147
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
148
|
+
style: {
|
|
149
|
+
padding: 8,
|
|
150
|
+
background: '#fff3cd',
|
|
151
|
+
border: '1px solid #ffeeba',
|
|
152
|
+
marginBottom: 8
|
|
153
|
+
},
|
|
154
|
+
children: [/*#__PURE__*/_jsx("strong", {
|
|
155
|
+
children: sub.label
|
|
156
|
+
}), ":", ' ', __('unknown control type', 'gcblite'), ' ', /*#__PURE__*/_jsx("code", {
|
|
157
|
+
children: sub.type
|
|
158
|
+
})]
|
|
159
|
+
}, sub.attributeKey);
|
|
160
|
+
}
|
|
161
|
+
return /*#__PURE__*/_jsx(SubComponent, {
|
|
162
|
+
control: sub,
|
|
163
|
+
value: row?.[sub.attributeKey],
|
|
164
|
+
onChange: next => onUpdate({
|
|
165
|
+
...row,
|
|
166
|
+
[sub.attributeKey]: next
|
|
167
|
+
}),
|
|
168
|
+
attributes: parentAttrs
|
|
169
|
+
}, sub.attributeKey);
|
|
170
|
+
})
|
|
171
|
+
})]
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
export default function RepeaterField({
|
|
175
|
+
control,
|
|
176
|
+
value,
|
|
177
|
+
onChange,
|
|
178
|
+
attributes
|
|
179
|
+
}) {
|
|
180
|
+
const rows = Array.isArray(value) ? value : [];
|
|
181
|
+
const [openId, setOpenId] = useState(null);
|
|
182
|
+
const min = typeof control.min === 'number' ? control.min : 0;
|
|
183
|
+
const max = typeof control.max === 'number' && control.max > 0 ? control.max : null;
|
|
184
|
+
const canAdd = max === null || rows.length < max;
|
|
185
|
+
const canRemove = i => rows.length > min;
|
|
186
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
187
|
+
activationConstraint: {
|
|
188
|
+
distance: 6
|
|
189
|
+
}
|
|
190
|
+
}));
|
|
191
|
+
const handleDragEnd = event => {
|
|
192
|
+
const {
|
|
193
|
+
active,
|
|
194
|
+
over
|
|
195
|
+
} = event;
|
|
196
|
+
if (!over || active.id === over.id) return;
|
|
197
|
+
const oldIndex = rows.findIndex(r => r._id === active.id);
|
|
198
|
+
const newIndex = rows.findIndex(r => r._id === over.id);
|
|
199
|
+
if (oldIndex < 0 || newIndex < 0) return;
|
|
200
|
+
onChange(arrayMove(rows, oldIndex, newIndex));
|
|
201
|
+
};
|
|
202
|
+
const addRow = () => {
|
|
203
|
+
const fresh = {
|
|
204
|
+
_id: newRowId()
|
|
205
|
+
};
|
|
206
|
+
(control.fields || []).forEach(sub => {
|
|
207
|
+
if (Object.prototype.hasOwnProperty.call(sub, 'default')) {
|
|
208
|
+
fresh[sub.attributeKey] = sub.default;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
const next = [...rows, fresh];
|
|
212
|
+
onChange(next);
|
|
213
|
+
setOpenId(fresh._id);
|
|
214
|
+
};
|
|
215
|
+
const removeRow = rowId => {
|
|
216
|
+
onChange(rows.filter(r => r._id !== rowId));
|
|
217
|
+
if (openId === rowId) setOpenId(null);
|
|
218
|
+
};
|
|
219
|
+
const updateRow = (rowId, nextRow) => {
|
|
220
|
+
onChange(rows.map(r => r._id === rowId ? nextRow : r));
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Lazy backfill: rows authored before this control existed (or
|
|
224
|
+
// imported from an export) may lack a stable _id. Patch in an
|
|
225
|
+
// effect (not during render) so reorders work without warning.
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (rows.some(r => !r || !r._id)) {
|
|
228
|
+
onChange(rows.map(r => r && r._id ? r : {
|
|
229
|
+
...r,
|
|
230
|
+
_id: newRowId()
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
}, [rows, onChange]);
|
|
234
|
+
const normalizedRows = rows.map(r => r && r._id ? r : {
|
|
235
|
+
...r,
|
|
236
|
+
_id: 'pending-' + Math.random()
|
|
237
|
+
});
|
|
238
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
239
|
+
className: "components-base-control gcb-repeater-control",
|
|
240
|
+
children: [/*#__PURE__*/_jsxs("div", {
|
|
241
|
+
className: "components-base-control__field",
|
|
242
|
+
children: [control.label && /*#__PURE__*/_jsx("label", {
|
|
243
|
+
className: "components-base-control__label",
|
|
244
|
+
children: control.label
|
|
245
|
+
}), /*#__PURE__*/_jsx(DndContext, {
|
|
246
|
+
sensors: sensors,
|
|
247
|
+
collisionDetection: closestCenter,
|
|
248
|
+
onDragEnd: handleDragEnd,
|
|
249
|
+
children: /*#__PURE__*/_jsx(SortableContext, {
|
|
250
|
+
items: normalizedRows.map(r => r._id),
|
|
251
|
+
strategy: verticalListSortingStrategy,
|
|
252
|
+
children: /*#__PURE__*/_jsx("div", {
|
|
253
|
+
className: "gcb-repeater-rows",
|
|
254
|
+
children: normalizedRows.map((row, index) => /*#__PURE__*/_jsx(SortableRow, {
|
|
255
|
+
row: row,
|
|
256
|
+
index: index,
|
|
257
|
+
control: control,
|
|
258
|
+
isOpen: openId === row._id,
|
|
259
|
+
onToggle: () => setOpenId(openId === row._id ? null : row._id),
|
|
260
|
+
onUpdate: next => updateRow(row._id, next),
|
|
261
|
+
onRemove: () => canRemove(index) ? removeRow(row._id) : null,
|
|
262
|
+
attributes: attributes
|
|
263
|
+
}, row._id))
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
}), canAdd && /*#__PURE__*/_jsx(Button, {
|
|
267
|
+
variant: "secondary",
|
|
268
|
+
onClick: addRow,
|
|
269
|
+
className: "gcb-repeater__add",
|
|
270
|
+
children: control.addButtonLabel || __('Add item', 'gcblite')
|
|
271
|
+
})]
|
|
272
|
+
}), control.helpText && /*#__PURE__*/_jsx("p", {
|
|
273
|
+
className: "components-base-control__help",
|
|
274
|
+
children: control.helpText
|
|
275
|
+
})]
|
|
276
|
+
});
|
|
277
|
+
}
|