@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.
Files changed (91) hide show
  1. package/dist/conditional-logic.js +83 -0
  2. package/{src → dist}/control-context.js +3 -2
  3. package/{src → dist}/controls/MediaCapabilityGate.js +12 -8
  4. package/dist/controls/MediaPicker.js +149 -0
  5. package/dist/controls/MediaTriggerBadges.js +35 -0
  6. package/{src → dist}/controls/PopoverOrModal.js +49 -43
  7. package/dist/controls/SortableItem.js +126 -0
  8. package/dist/controls/button-group.js +46 -0
  9. package/dist/controls/checkbox-group.js +65 -0
  10. package/dist/controls/checkbox.js +15 -0
  11. package/dist/controls/code.js +24 -0
  12. package/dist/controls/color.js +249 -0
  13. package/dist/controls/date.js +55 -0
  14. package/dist/controls/datetime.js +61 -0
  15. package/dist/controls/email.js +17 -0
  16. package/dist/controls/file.js +163 -0
  17. package/dist/controls/gallery.js +371 -0
  18. package/dist/controls/google-map.js +143 -0
  19. package/dist/controls/heading-level.js +93 -0
  20. package/dist/controls/icon.js +292 -0
  21. package/dist/controls/image.js +360 -0
  22. package/dist/controls/index.js +88 -0
  23. package/dist/controls/message.js +86 -0
  24. package/dist/controls/number.js +19 -0
  25. package/dist/controls/oembed.js +42 -0
  26. package/{src → dist}/controls/page-link.js +1 -2
  27. package/dist/controls/post-object.js +913 -0
  28. package/dist/controls/radio.js +19 -0
  29. package/dist/controls/range.js +108 -0
  30. package/{src → dist}/controls/relationship.js +12 -7
  31. package/dist/controls/repeater.js +277 -0
  32. package/dist/controls/richtext.js +494 -0
  33. package/dist/controls/select.js +144 -0
  34. package/dist/controls/size.js +59 -0
  35. package/dist/controls/spacing.js +172 -0
  36. package/dist/controls/taxonomy.js +569 -0
  37. package/dist/controls/text.js +16 -0
  38. package/dist/controls/textarea.js +17 -0
  39. package/dist/controls/toggle-group.js +28 -0
  40. package/dist/controls/toggle.js +15 -0
  41. package/dist/controls/url.js +235 -0
  42. package/dist/controls/user.js +383 -0
  43. package/{src → dist}/controls/wysiwyg.js +1 -1
  44. package/{src → dist}/hooks/useTokens.js +25 -21
  45. package/{src → dist}/index.js +2 -8
  46. package/dist/inspector.js +163 -0
  47. package/{src → dist}/provider.js +18 -17
  48. package/dist/utils/map-utils.js +54 -0
  49. package/dist/utils/token-helper.js +396 -0
  50. package/{src → dist}/validation-context.js +4 -4
  51. package/package.json +20 -13
  52. package/src/conditional-logic.js +0 -77
  53. package/src/controls/MediaPicker.js +0 -139
  54. package/src/controls/MediaTriggerBadges.js +0 -31
  55. package/src/controls/SortableItem.js +0 -110
  56. package/src/controls/button-group.js +0 -49
  57. package/src/controls/checkbox-group.js +0 -55
  58. package/src/controls/checkbox.js +0 -13
  59. package/src/controls/code.js +0 -21
  60. package/src/controls/color.js +0 -235
  61. package/src/controls/date.js +0 -37
  62. package/src/controls/datetime.js +0 -54
  63. package/src/controls/email.js +0 -15
  64. package/src/controls/file.js +0 -134
  65. package/src/controls/gallery.js +0 -338
  66. package/src/controls/google-map.js +0 -117
  67. package/src/controls/heading-level.js +0 -99
  68. package/src/controls/icon.js +0 -301
  69. package/src/controls/image.js +0 -334
  70. package/src/controls/index.js +0 -95
  71. package/src/controls/message.js +0 -56
  72. package/src/controls/number.js +0 -17
  73. package/src/controls/oembed.js +0 -32
  74. package/src/controls/post-object.js +0 -788
  75. package/src/controls/radio.js +0 -18
  76. package/src/controls/range.js +0 -110
  77. package/src/controls/repeater.js +0 -290
  78. package/src/controls/richtext.js +0 -505
  79. package/src/controls/select.js +0 -141
  80. package/src/controls/size.js +0 -49
  81. package/src/controls/spacing.js +0 -141
  82. package/src/controls/taxonomy.js +0 -488
  83. package/src/controls/text.js +0 -14
  84. package/src/controls/textarea.js +0 -15
  85. package/src/controls/toggle-group.js +0 -34
  86. package/src/controls/toggle.js +0 -13
  87. package/src/controls/url.js +0 -164
  88. package/src/controls/user.js +0 -343
  89. package/src/inspector.js +0 -174
  90. package/src/utils/map-utils.js +0 -51
  91. package/src/utils/token-helper.js +0 -243
@@ -1,235 +0,0 @@
1
- /**
2
- * ColorField — ported verbatim from the original GCB.
3
- * Tabbed color/gradient picker that uses theme.json palettes via useSetting.
4
- *
5
- * Control config can opt out of gradients with `showGradients: false`.
6
- * For dual-attribute mode (separate color + gradient attrs), set
7
- * `gradientAttributeKey` on the control config.
8
- */
9
-
10
- import { __ } from '@wordpress/i18n';
11
- import {
12
- BaseControl,
13
- Button,
14
- ColorPalette,
15
- GradientPicker,
16
- TabPanel,
17
- __experimentalHStack as HStack,
18
- } from '@wordpress/components';
19
- import { useSetting } from '@wordpress/block-editor';
20
- import PopoverOrModal from './PopoverOrModal';
21
-
22
- /**
23
- * The popover contents — palette + (optional) gradient tab.
24
- */
25
- function ColorPanel({
26
- colorValue,
27
- gradientValue,
28
- onColorChange,
29
- onGradientChange,
30
- colors,
31
- gradients,
32
- enableAlpha,
33
- disableCustomColors,
34
- disableCustomGradients,
35
- showGradients,
36
- }) {
37
- const palette = (
38
- <>
39
- <ColorPalette
40
- colors={colors}
41
- value={colorValue}
42
- onChange={onColorChange}
43
- clearable
44
- disableCustomColors={disableCustomColors}
45
- enableAlpha={enableAlpha}
46
- />
47
- {colorValue && (
48
- <Button variant="secondary" onClick={() => onColorChange('')} style={{ marginTop: 10 }}>
49
- {__('Reset Color', 'gcblite')}
50
- </Button>
51
- )}
52
- </>
53
- );
54
-
55
- if (!showGradients) {
56
- return <div style={{ minWidth: 260, padding: 12 }}>{palette}</div>;
57
- }
58
-
59
- return (
60
- <div style={{ minWidth: 260, padding: 12 }}>
61
- <TabPanel
62
- className="gcb-color-field-tabs"
63
- activeClass="is-active"
64
- tabs={[
65
- { name: 'color', title: __('Color', 'gcblite'), className: 'tab-color' },
66
- { name: 'gradient', title: __('Gradient', 'gcblite'), className: 'tab-gradient' },
67
- ]}
68
- >
69
- {(tab) => (
70
- <>
71
- {tab.name === 'color' && palette}
72
- {tab.name === 'gradient' && (
73
- <>
74
- <GradientPicker
75
- value={gradientValue || undefined}
76
- onChange={(next) => onGradientChange(next || '')}
77
- gradients={gradients || []}
78
- clearable
79
- disableCustomGradients={disableCustomGradients}
80
- __experimentalIsRenderedInSidebar
81
- />
82
- {gradientValue && (
83
- <Button
84
- variant="secondary"
85
- onClick={() => onGradientChange('')}
86
- style={{ marginTop: 10 }}
87
- >
88
- {__('Reset Gradient', 'gcblite')}
89
- </Button>
90
- )}
91
- </>
92
- )}
93
- </>
94
- )}
95
- </TabPanel>
96
- </div>
97
- );
98
- }
99
-
100
- /**
101
- * The trigger swatch — large white tile with a colour swatch on the left,
102
- * the resolved value (or label) on the right. Same "popout" pattern as the
103
- * image / post-object controls.
104
- */
105
- function ColorTrigger({ label, colorValue, gradientValue, onToggle, isOpen }) {
106
- const hasValue = !!(colorValue || gradientValue);
107
- const swatchBackground = gradientValue || colorValue || 'transparent';
108
- const isCheckered = !hasValue;
109
-
110
- return (
111
- <Button
112
- onClick={onToggle}
113
- aria-expanded={isOpen}
114
- aria-label={label}
115
- className="gcb-modal-toggle-button gcb-color-trigger"
116
- >
117
- <HStack spacing={3}>
118
- <span
119
- aria-hidden
120
- className={isCheckered ? 'gcb-color-trigger__swatch is-empty' : 'gcb-color-trigger__swatch'}
121
- style={{
122
- width: 24,
123
- height: 24,
124
- borderRadius: '100%',
125
- background: swatchBackground,
126
- flexShrink: 0,
127
- border: '1px solid #ddd',
128
- display: 'inline-block',
129
- }}
130
- />
131
- <span style={{ flex: 1, textAlign: 'left', fontSize: 13, color: '#1e1e1e', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
132
- {hasValue ? (gradientValue ? __('Gradient', 'gcblite') : colorValue) : __('Select a color', 'gcblite')}
133
- </span>
134
- </HStack>
135
- </Button>
136
- );
137
- }
138
-
139
- function ColorFieldImpl({
140
- label = __('Color', 'gcblite'),
141
- colorValue,
142
- gradientValue,
143
- onColorChange,
144
- onGradientChange,
145
- colors,
146
- gradients,
147
- enableAlpha = true,
148
- disableCustomColors = false,
149
- disableCustomGradients = false,
150
- showGradients = true,
151
- className = '',
152
- help,
153
- }) {
154
- const themeColors = useSetting('color.palette');
155
- const themeGradients = useSetting('color.gradients');
156
-
157
- // Always end up with arrays — `useSetting('color.gradients')` returns
158
- // undefined on themes that don't declare any gradients, and feeding that
159
- // to <GradientPicker> crashes on `.orientation`.
160
- const finalColors = (colors && colors.length ? colors : (themeColors || []));
161
- const finalGradients = (gradients && gradients.length ? gradients : (themeGradients || []));
162
-
163
- return (
164
- <BaseControl
165
- label={label}
166
- help={help}
167
- className={`gcb-color-control components-base-control ${className}`.trim()}
168
- __nextHasNoMarginBottom
169
- >
170
- <PopoverOrModal
171
- modalTitle={label}
172
- dropdownProps={{ popoverProps: { placement: 'left-start' } }}
173
- renderToggle={({ isOpen, onToggle }) => (
174
- <ColorTrigger
175
- label={label}
176
- colorValue={colorValue}
177
- gradientValue={gradientValue}
178
- onToggle={onToggle}
179
- isOpen={isOpen}
180
- />
181
- )}
182
- renderContent={() => (
183
- <ColorPanel
184
- colorValue={colorValue}
185
- gradientValue={gradientValue}
186
- onColorChange={onColorChange}
187
- onGradientChange={onGradientChange}
188
- colors={finalColors}
189
- gradients={finalGradients}
190
- enableAlpha={enableAlpha}
191
- disableCustomColors={disableCustomColors}
192
- disableCustomGradients={disableCustomGradients}
193
- showGradients={showGradients}
194
- />
195
- )}
196
- />
197
- </BaseControl>
198
- );
199
- }
200
-
201
- /**
202
- * Adapter for the registry contract.
203
- *
204
- * The control stores a single string value — either a colour (e.g. `#ff0000`,
205
- * `var(--wp--preset--color--primary)`) or a gradient (e.g. `linear-gradient(...)`).
206
- * When the user picks one, the other is cleared. This matches the WP attribute
207
- * type `string` that the loader generates for `color` controls.
208
- *
209
- * For separate color + gradient attributes on the same block, declare two
210
- * controls (one with `showGradients: false`, one purely gradient — coming in v0.2).
211
- */
212
- export default function ColorField({ control, value, onChange }) {
213
- const stringValue = typeof value === 'string' ? value : '';
214
- const isGradient = stringValue.includes('gradient(');
215
- const colorValue = isGradient ? '' : stringValue;
216
- const gradientValue = isGradient ? stringValue : '';
217
-
218
- const handleColor = (next) => onChange(next || '');
219
- const handleGradient = (next) => onChange(next || '');
220
-
221
- return (
222
- <ColorFieldImpl
223
- label={control.label}
224
- help={control.helpText}
225
- colorValue={colorValue}
226
- gradientValue={gradientValue}
227
- onColorChange={handleColor}
228
- onGradientChange={handleGradient}
229
- showGradients={control.showGradients !== false}
230
- enableAlpha={control.enableAlpha !== false}
231
- disableCustomColors={control.disableCustomColors === true}
232
- disableCustomGradients={control.disableCustomGradients === true}
233
- />
234
- );
235
- }
@@ -1,37 +0,0 @@
1
- import { BaseControl, Button, DatePicker } from '@wordpress/components';
2
- import { __ } from '@wordpress/i18n';
3
- import PopoverOrModal from './PopoverOrModal';
4
-
5
- export default function DateField({ control, value, onChange }) {
6
- return (
7
- <BaseControl label={control.label} help={control.helpText} __nextHasNoMarginBottom>
8
- <PopoverOrModal
9
- modalTitle={control.label || __('Pick a date', 'gcblite')}
10
- dropdownProps={{ popoverProps: { placement: 'bottom-start' } }}
11
- renderToggle={({ onToggle }) => (
12
- <>
13
- <Button variant="secondary" onClick={onToggle}>
14
- {value ? new Date(value).toLocaleDateString() : __('Pick a date', 'gcblite')}
15
- </Button>
16
- {value && (
17
- <Button variant="tertiary" size="small" isDestructive onClick={() => onChange('')} style={{ marginLeft: 8 }}>
18
- {__('Clear', 'gcblite')}
19
- </Button>
20
- )}
21
- </>
22
- )}
23
- renderContent={({ close }) => (
24
- <div style={{ padding: 8 }}>
25
- <DatePicker
26
- currentDate={value || undefined}
27
- onChange={(next) => {
28
- onChange(next || '');
29
- close();
30
- }}
31
- />
32
- </div>
33
- )}
34
- />
35
- </BaseControl>
36
- );
37
- }
@@ -1,54 +0,0 @@
1
- /**
2
- * DateTime — like Date, but with a time picker too. Stores an ISO 8601
3
- * timestamp string (e.g. "2026-05-25T14:30:00").
4
- *
5
- * Uses WP's DateTimePicker rather than DatePicker so the panel shows hours
6
- * and minutes alongside the calendar. Renders inside PopoverOrModal so it
7
- * behaves correctly in both sidebar (popover) and metabox (modal) contexts.
8
- */
9
-
10
- import { BaseControl, Button, DateTimePicker } from '@wordpress/components';
11
- import { __ } from '@wordpress/i18n';
12
- import PopoverOrModal from './PopoverOrModal';
13
-
14
- export default function DatetimeField({ control, value, onChange }) {
15
- const displayLabel = value
16
- ? new Date(value).toLocaleString()
17
- : __('Pick a date and time', 'gcblite');
18
-
19
- return (
20
- <BaseControl label={control.label} help={control.helpText} __nextHasNoMarginBottom>
21
- <PopoverOrModal
22
- modalTitle={control.label || __('Pick a date and time', 'gcblite')}
23
- dropdownProps={{ popoverProps: { placement: 'bottom-start' } }}
24
- renderToggle={({ onToggle }) => (
25
- <>
26
- <Button variant="secondary" onClick={onToggle}>
27
- {displayLabel}
28
- </Button>
29
- {value && (
30
- <Button
31
- variant="tertiary"
32
- size="small"
33
- isDestructive
34
- onClick={() => onChange('')}
35
- style={{ marginLeft: 8 }}
36
- >
37
- {__('Clear', 'gcblite')}
38
- </Button>
39
- )}
40
- </>
41
- )}
42
- renderContent={() => (
43
- <div style={{ padding: 8 }}>
44
- <DateTimePicker
45
- currentDate={value || undefined}
46
- onChange={(next) => onChange(next || '')}
47
- is12Hour
48
- />
49
- </div>
50
- )}
51
- />
52
- </BaseControl>
53
- );
54
- }
@@ -1,15 +0,0 @@
1
- import { TextControl } from '@wordpress/components';
2
-
3
- export default function EmailField({ control, value, onChange }) {
4
- return (
5
- <TextControl
6
- label={control.label}
7
- help={control.helpText}
8
- placeholder={control.placeholder ?? 'name@example.com'}
9
- type="email"
10
- value={value ?? ''}
11
- onChange={onChange}
12
- __nextHasNoMarginBottom
13
- />
14
- );
15
- }
@@ -1,134 +0,0 @@
1
- /**
2
- * FileField — non-image attachment picker. Stores `{ id, url, filename, title }`.
3
- *
4
- * The trigger button mirrors the image control's "popout" tile (file icon +
5
- * filename + chevron) so the Inspector feels consistent.
6
- */
7
-
8
- import { __ } from '@wordpress/i18n';
9
- import {
10
- Button,
11
- __experimentalHStack as HStack,
12
- __experimentalTruncate as Truncate,
13
- } from '@wordpress/components';
14
- import MediaPicker from './MediaPicker';
15
- import PopoverOrModal from './PopoverOrModal';
16
- import MediaCapabilityGate from './MediaCapabilityGate';
17
- import MediaTriggerBadges from './MediaTriggerBadges';
18
-
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
-
29
- function FileIcon() {
30
- return (
31
- <svg
32
- xmlns="http://www.w3.org/2000/svg"
33
- viewBox="0 0 24 24"
34
- width="20"
35
- height="20"
36
- style={{ flexShrink: 0, fill: '#1e1e1e' }}
37
- aria-hidden
38
- >
39
- <path d="M14 9V3.5L19.5 9H14zM6 2h9l5 5v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2zm0 2v16h12V11h-5V4H6z" />
40
- </svg>
41
- );
42
- }
43
-
44
- export default function FileField({ control, value, onChange }) {
45
- const fileValue = (typeof value === 'object' && value !== null)
46
- ? value
47
- : { id: null, url: value || '', filename: '', title: '' };
48
- const hasFile = !!(fileValue.id || fileValue.url);
49
- const displayTitle = fileValue.title || fileValue.filename || __('(no title)', 'gcblite');
50
-
51
- return (
52
- <div className="components-base-control gcb-file-control">
53
- <div className="components-base-control__field">
54
- <label className="components-base-control__label">{control.label}</label>
55
- </div>
56
- {control.helpText && (
57
- <p className="components-base-control__help">{control.helpText}</p>
58
- )}
59
-
60
- <MediaCapabilityGate>
61
- <MediaPicker
62
- onSelect={(media) => onChange({
63
- id: media.id,
64
- url: media.url,
65
- filename: media.filename,
66
- title: media.title,
67
- })}
68
- allowedTypes={control.allowedTypes || ['application', 'text', 'image', 'video', 'audio']}
69
- value={fileValue.id}
70
- render={({ open }) => (
71
- <div>
72
- {!hasFile && (
73
- <Button
74
- onClick={open}
75
- variant="secondary"
76
- style={{ marginBottom: 8 }}
77
- >
78
- {__('Select File', 'gcblite')}
79
- </Button>
80
- )}
81
-
82
- {hasFile && (
83
- <PopoverOrModal
84
- modalTitle={control.label || __('File', 'gcblite')}
85
- dropdownProps={{ popoverProps: { placement: 'left-start' } }}
86
- renderToggle={({ isOpen, onToggle }) => (
87
- <MediaTriggerBadges onClear={() => onChange({ id: null, url: '', filename: '', title: '' })}>
88
- <Button
89
- onClick={onToggle}
90
- aria-expanded={isOpen}
91
- className="gcb-modal-toggle-button gcb-file-control-toggle"
92
- style={TOGGLE_BUTTON_STYLE}
93
- >
94
- <HStack spacing={3}>
95
- <FileIcon />
96
- <Truncate numberOfLines={1}>{displayTitle}</Truncate>
97
- </HStack>
98
- </Button>
99
- </MediaTriggerBadges>
100
- )}
101
- renderContent={({ close }) => (
102
- <div style={{ padding: 16, minWidth: 280 }}>
103
- <Button
104
- onClick={() => { close(); open(); }}
105
- variant="secondary"
106
- style={{ width: '100%', marginBottom: 12 }}
107
- >
108
- {__('Replace File', 'gcblite')}
109
- </Button>
110
- <Button
111
- onClick={() => { onChange({ id: null, url: '', filename: '', title: '' }); close(); }}
112
- variant="tertiary"
113
- isDestructive
114
- style={{ width: '100%' }}
115
- >
116
- {__('Remove File', 'gcblite')}
117
- </Button>
118
- </div>
119
- )}
120
- />
121
- )}
122
-
123
- {!hasFile && (
124
- <p style={{ fontSize: 13, color: '#757575', margin: 0 }}>
125
- {__('No file selected', 'gcblite')}
126
- </p>
127
- )}
128
- </div>
129
- )}
130
- />
131
- </MediaCapabilityGate>
132
- </div>
133
- );
134
- }