@plaudit/gutenberg-api-extensions 2.9.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/blocks/common-native-property-implementations.d.ts +118 -0
- package/build/blocks/common-native-property-implementations.js +59 -16
- package/build/blocks/common-native-property-implementations.js.map +1 -1
- package/build/blocks/index.d.ts +5 -0
- package/build/blocks/layered-styles.d.ts +31 -0
- package/build/blocks/simple-block.d.ts +22 -0
- package/build/blocks/simple-native-property.d.ts +30 -0
- package/build/blocks/simple-native-property.js +99 -94
- package/build/blocks/simple-native-property.js.map +1 -1
- package/build/controls/AsynchronousFormTokenField.d.ts +20 -0
- package/build/controls/ExtendedPostPicker.d.ts +13 -0
- package/build/controls/InspectorPanel.d.ts +6 -0
- package/build/controls/LazySuggestionsComboboxControl.d.ts +7 -0
- package/build/controls/PickOne.d.ts +19 -0
- package/build/controls/SimpleToggle.d.ts +3 -0
- package/build/controls/SortableItemsControl.d.ts +11 -0
- package/build/controls/SortableItemsControl.js +142 -31
- package/build/controls/SortableItemsControl.js.map +1 -1
- package/build/controls/shared.d.ts +8 -0
- package/build/controls/types.d.ts +12 -0
- package/build/lib/plaudit-icons/column-1.d.ts +2 -0
- package/build/lib/plaudit-icons/column-2.d.ts +2 -0
- package/build/lib/plaudit-icons/column-3.d.ts +2 -0
- package/build/lib/plaudit-icons/placement-center.d.ts +2 -0
- package/build/lib/plaudit-icons/placement-end.d.ts +2 -0
- package/build/lib/plaudit-icons/placement-start.d.ts +2 -0
- package/build/lib/plaudit-icons/placement-stretch.d.ts +2 -0
- package/build/lib/plaudit-icons/plaudit-icon.d.ts +2 -0
- package/build/lib/plaudit-icons/reusable-block-marker.d.ts +2 -0
- package/{src/lib/plaudit-icons.ts → build/lib/plaudit-icons.d.ts} +0 -4
- package/package.json +5 -5
- package/src/blocks/common-native-property-implementations.tsx +0 -361
- package/src/blocks/index.ts +0 -12
- package/src/blocks/layered-styles.tsx +0 -108
- package/src/blocks/simple-block.tsx +0 -72
- package/src/blocks/simple-native-property.tsx +0 -204
- package/src/controls/AsynchronousFormTokenField.tsx +0 -158
- package/src/controls/ExtendedPostPicker.tsx +0 -50
- package/src/controls/InspectorPanel.tsx +0 -16
- package/src/controls/LazySuggestionsComboboxControl.tsx +0 -84
- package/src/controls/PickOne.tsx +0 -80
- package/src/controls/SimpleToggle.tsx +0 -9
- package/src/controls/SortableItemsControl.tsx +0 -70
- package/src/controls/shared.ts +0 -7
- package/src/controls/types.ts +0 -11
- package/src/lib/plaudit-icons/column-1.tsx +0 -8
- package/src/lib/plaudit-icons/column-2.tsx +0 -8
- package/src/lib/plaudit-icons/column-3.tsx +0 -8
- package/src/lib/plaudit-icons/placement-center.tsx +0 -5
- package/src/lib/plaudit-icons/placement-end.tsx +0 -5
- package/src/lib/plaudit-icons/placement-start.tsx +0 -5
- package/src/lib/plaudit-icons/placement-stretch.tsx +0 -5
- package/src/lib/plaudit-icons/plaudit-icon.tsx +0 -6
- package/src/lib/plaudit-icons/reusable-block-marker.tsx +0 -5
- /package/{src/controls/index.ts → build/controls/index.d.ts} +0 -0
- /package/{src/index.ts → build/index.d.ts} +0 -0
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import {MediaUpload, MediaUploadCheck, __experimentalLinkControl as LinkControl, type LinkControlProps} from "@wordpress/block-editor";
|
|
2
|
-
import {
|
|
3
|
-
Button,
|
|
4
|
-
Card,
|
|
5
|
-
CardBody,
|
|
6
|
-
CardFooter,
|
|
7
|
-
CardHeader,
|
|
8
|
-
FocalPointPicker,
|
|
9
|
-
RadioControl,
|
|
10
|
-
RangeControl,
|
|
11
|
-
SelectControl,
|
|
12
|
-
ResponsiveWrapper,
|
|
13
|
-
TextareaControl,
|
|
14
|
-
TextControl,
|
|
15
|
-
ToggleControl,
|
|
16
|
-
__experimentalHeading as Heading,
|
|
17
|
-
__experimentalToggleGroupControl as ToggleGroupControl,
|
|
18
|
-
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
|
|
19
|
-
__experimentalToggleGroupControlOptionIcon as ToggleGroupControlOptionIcon
|
|
20
|
-
} from "@wordpress/components";
|
|
21
|
-
import type {ComboboxControlOption} from "@wordpress/components/build-types/combobox-control/types";
|
|
22
|
-
import type {RadioControlProps} from "@wordpress/components/build-types/radio-control/types";
|
|
23
|
-
import type {RangeControlProps} from "@wordpress/components/build-types/range-control/types";
|
|
24
|
-
import type {TextControlProps} from "@wordpress/components/build-types/text-control/types";
|
|
25
|
-
import type {TextareaControlProps} from "@wordpress/components/build-types/textarea-control/types";
|
|
26
|
-
import type {ToggleControlProps} from "@wordpress/components/build-types/toggle-control/types";
|
|
27
|
-
import type {ToggleGroupControlProps} from "@wordpress/components/build-types/toggle-group-control/types";
|
|
28
|
-
import {useSelect} from "@wordpress/data";
|
|
29
|
-
import {__} from "@wordpress/i18n";
|
|
30
|
-
|
|
31
|
-
import {ExtendedPostPicker, type ExtendedPostPickerConstructorProps, LazySuggestionsComboboxControl, type LazySuggestionsComboboxControlProps} from "../controls";
|
|
32
|
-
import {requestPostsFromAPI} from "../controls/shared";
|
|
33
|
-
import type {PickableOptions} from "../controls/types";
|
|
34
|
-
import type {SimpleNativeProperty} from "./simple-native-property";
|
|
35
|
-
|
|
36
|
-
import React, {type ReactElement} from "react";
|
|
37
|
-
|
|
38
|
-
export type ActualBlockEditProps = { attributes: Record<string, any>, setAttributes: (attributes: Record<string, any>) => void, name: string };
|
|
39
|
-
type CommonPropertyConfig<T, V> = {
|
|
40
|
-
name: string,
|
|
41
|
-
label: string,
|
|
42
|
-
default?: T,
|
|
43
|
-
enum?: T[],
|
|
44
|
-
component?: Partial<V>,
|
|
45
|
-
help?: string,
|
|
46
|
-
alwaysStore?: boolean,
|
|
47
|
-
condition?(blockEditProps: ActualBlockEditProps): boolean
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function rangeProperty(config: CommonPropertyConfig<number, RangeControlProps> & {
|
|
51
|
-
min: number,
|
|
52
|
-
max: number,
|
|
53
|
-
step?: number,
|
|
54
|
-
enum?: number[]
|
|
55
|
-
}): SimpleNativeProperty {
|
|
56
|
-
return {
|
|
57
|
-
name: config.name,
|
|
58
|
-
type: 'number',
|
|
59
|
-
enum: config.enum,
|
|
60
|
-
default: config.default,
|
|
61
|
-
condition: config.condition,
|
|
62
|
-
alwaysStore: config.alwaysStore,
|
|
63
|
-
renderer(value, onChange) {
|
|
64
|
-
return <RangeControl value={value} onChange={onChange} min={config.min} max={config.max} label={config.label}
|
|
65
|
-
step={config.step} help={config.help} {...config.component} />;
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export function radioProperty(config: CommonPropertyConfig<string, RadioControlProps> & {
|
|
71
|
-
options: PickableOptions<string>
|
|
72
|
-
}): SimpleNativeProperty {
|
|
73
|
-
return {
|
|
74
|
-
name: config.name,
|
|
75
|
-
type: 'string',
|
|
76
|
-
enum: config.enum,
|
|
77
|
-
default: config.default,
|
|
78
|
-
condition: config.condition,
|
|
79
|
-
alwaysStore: config.alwaysStore,
|
|
80
|
-
renderer(value, onChange) {
|
|
81
|
-
return <RadioControl selected={value} onChange={onChange} options={asOptions(config.options)} label={config.label}
|
|
82
|
-
help={config.help} {...config.component} />;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function selectProperty(config: CommonPropertyConfig<string, never> & {
|
|
88
|
-
options: PickableOptions<string>
|
|
89
|
-
}): SimpleNativeProperty {
|
|
90
|
-
return {
|
|
91
|
-
name: config.name,
|
|
92
|
-
type: 'string',
|
|
93
|
-
enum: config.enum,
|
|
94
|
-
default: config.default,
|
|
95
|
-
condition: config.condition,
|
|
96
|
-
alwaysStore: config.alwaysStore,
|
|
97
|
-
renderer(value, onChange) {
|
|
98
|
-
return <SelectControl value={value ?? ''} onChange={onChange} options={asOptions(config.options, '')} label={config.label} help={config.help} />;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function linkProperty(config: CommonPropertyConfig<Record<string, unknown>, LinkControlProps>): SimpleNativeProperty {
|
|
104
|
-
return {
|
|
105
|
-
name: config.name,
|
|
106
|
-
type: 'object',
|
|
107
|
-
default: config.default,
|
|
108
|
-
condition: config.condition,
|
|
109
|
-
alwaysStore: config.alwaysStore,
|
|
110
|
-
renderer(value, onChange) {
|
|
111
|
-
return wrapInCard(() => <LinkControl value={value} onChange={onChange} {...config.component} />, config.label, config.help);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function comboboxControlOptionFromAPIResponse(response: Array<{id: string, title: string}>): ComboboxControlOption[] {
|
|
117
|
-
return response.map(({id, title}) => ({value: id, label: `${title} (#${id})`}));
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
type SharedPostPropertyType = {
|
|
121
|
-
postTypes?: string[];
|
|
122
|
-
};
|
|
123
|
-
type PostPropertyType =
|
|
124
|
-
(CommonPropertyConfig<number, LazySuggestionsComboboxControlProps>&SharedPostPropertyType&{multiple?: false})|
|
|
125
|
-
(CommonPropertyConfig<number[], ExtendedPostPickerConstructorProps>&SharedPostPropertyType&{multiple: true});
|
|
126
|
-
export function postProperty(config: PostPropertyType): SimpleNativeProperty {
|
|
127
|
-
if (config.multiple) {
|
|
128
|
-
return {
|
|
129
|
-
name: config.name,
|
|
130
|
-
type: 'array',
|
|
131
|
-
default: config.default,
|
|
132
|
-
condition: config.condition,
|
|
133
|
-
alwaysStore: config.alwaysStore,
|
|
134
|
-
renderer(value: number[]|undefined, onChange) {
|
|
135
|
-
return <ExtendedPostPicker
|
|
136
|
-
label={config.label}
|
|
137
|
-
help={config.help}
|
|
138
|
-
value={value?.map(v => v.toString())}
|
|
139
|
-
onChange={(v) => onChange(v.map(v => parseInt(v)))}
|
|
140
|
-
postTypes={config.postTypes}
|
|
141
|
-
{...config.component}
|
|
142
|
-
/>;
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
} else {
|
|
146
|
-
const makeRequest = (v: string|undefined) => requestPostsFromAPI({search: v, postTypes: config.postTypes?.join(',')});
|
|
147
|
-
return {
|
|
148
|
-
name: config.name,
|
|
149
|
-
type: 'number',
|
|
150
|
-
default: config.default,
|
|
151
|
-
condition: config.condition,
|
|
152
|
-
alwaysStore: config.alwaysStore,
|
|
153
|
-
renderer(value: number|undefined, onChange) {
|
|
154
|
-
return <LazySuggestionsComboboxControl
|
|
155
|
-
value={value?.toString()}
|
|
156
|
-
onChange={v => onChange(v ? parseInt(v) : undefined)}
|
|
157
|
-
getOption={v => makeRequest(v).then(comboboxControlOptionFromAPIResponse).then(res => res[0])}
|
|
158
|
-
getSuggestions={v => makeRequest(v).then(comboboxControlOptionFromAPIResponse)}
|
|
159
|
-
{...config.component}
|
|
160
|
-
/>
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export function toggleProperty(config: CommonPropertyConfig<boolean, ToggleControlProps>): SimpleNativeProperty {
|
|
167
|
-
return {
|
|
168
|
-
name: config.name,
|
|
169
|
-
type: 'boolean',
|
|
170
|
-
default: config.default,
|
|
171
|
-
condition: config.condition,
|
|
172
|
-
alwaysStore: config.alwaysStore,
|
|
173
|
-
renderer(value, onChange) {
|
|
174
|
-
return <ToggleControl checked={value} onChange={onChange} label={config.label} help={config.help} {...config.component} />;
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
type ToggleGroupPropertyConfigBase<T extends string|number> = CommonPropertyConfig<T, ToggleGroupControlProps>&{options: PickableOptions<T, {icon: ReactElement}>};
|
|
180
|
-
export type ToggleGroupPropertyConfig = ToggleGroupPropertyConfigBase<string>|ToggleGroupPropertyConfigBase<number>;
|
|
181
|
-
|
|
182
|
-
export function toggleGroupProperty(config: ToggleGroupPropertyConfig): SimpleNativeProperty {
|
|
183
|
-
const children = config.options.map(([value, label]) => typeof label === 'string'
|
|
184
|
-
? <ToggleGroupControlOption key={value} value={value} label={label}/>
|
|
185
|
-
: <ToggleGroupControlOptionIcon key={value} value={value} label={label.text} icon={label.icon}/>);
|
|
186
|
-
|
|
187
|
-
if (typeof config.default === 'string') {
|
|
188
|
-
return {
|
|
189
|
-
name: config.name,
|
|
190
|
-
type: 'string',
|
|
191
|
-
default: config.default,
|
|
192
|
-
condition: config.condition,
|
|
193
|
-
alwaysStore: config.alwaysStore,
|
|
194
|
-
renderer(value, onChange) {
|
|
195
|
-
return <ToggleGroupControl
|
|
196
|
-
value={value} onChange={v => onChange(v?.toString())}
|
|
197
|
-
label={config.label} help={config.help} isBlock {...config.component}
|
|
198
|
-
children={children} />;
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
} else {
|
|
202
|
-
return {
|
|
203
|
-
name: config.name,
|
|
204
|
-
type: 'number',
|
|
205
|
-
default: config.default,
|
|
206
|
-
condition: config.condition,
|
|
207
|
-
alwaysStore: config.alwaysStore,
|
|
208
|
-
renderer(value, onChange) {
|
|
209
|
-
return <ToggleGroupControl
|
|
210
|
-
value={value} label={config.label} help={config.help} isBlock {...config.component}
|
|
211
|
-
onChange={v => onChange(v === undefined || typeof v === 'number' ? v : (v.includes('.') ? parseFloat(v) : parseInt(v)))}
|
|
212
|
-
children={children} />;
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
export function textProperty(config: CommonPropertyConfig<string, TextControlProps>&{placeholder?: string|undefined}): SimpleNativeProperty {
|
|
219
|
-
return {
|
|
220
|
-
name: config.name,
|
|
221
|
-
type: 'string',
|
|
222
|
-
default: config.default,
|
|
223
|
-
condition: config.condition,
|
|
224
|
-
alwaysStore: config.alwaysStore,
|
|
225
|
-
renderer(value, onChange) {
|
|
226
|
-
return <TextControl value={value ?? ''} onChange={onChange} label={config.label} help={config.help} placeholder={config.placeholder} {...config.component} />;
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
export function textareaProperty(config: CommonPropertyConfig<string, TextareaControlProps>&{placeholder?: string|undefined, newline?: undefined|"\n"|"br"|"p"}): SimpleNativeProperty {
|
|
232
|
-
return {
|
|
233
|
-
name: config.name,
|
|
234
|
-
type: 'string',
|
|
235
|
-
default: config.default,
|
|
236
|
-
condition: config.condition,
|
|
237
|
-
alwaysStore: config.alwaysStore,
|
|
238
|
-
renderer(value, onChange) {
|
|
239
|
-
let v = value ?? '';
|
|
240
|
-
if (config.newline === "br") {
|
|
241
|
-
v = v.replaceAll("<br/>", "\n");
|
|
242
|
-
} else if (config.newline === "p") {
|
|
243
|
-
v = v.replaceAll(/<p>(.*?)<\/p>/gi, "$1\n");
|
|
244
|
-
}
|
|
245
|
-
return <TextareaControl value={v} onChange={v => {
|
|
246
|
-
if (config.newline === "br") {
|
|
247
|
-
onChange(v.replaceAll(/\r?\n/g, "<br/>"));
|
|
248
|
-
} else if (config.newline === "p") {
|
|
249
|
-
onChange(v.split(/\r?\n/g).map(v => "<p>" + v + "</p>").join(""));
|
|
250
|
-
} else {
|
|
251
|
-
onChange(v);
|
|
252
|
-
}
|
|
253
|
-
}} label={config.label} help={config.help} placeholder={config.placeholder} {...config.component} />;
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export type ImagePropertyData = {media?: {id?: number, url?: string}, pos?: {x?: number, y?: number}};
|
|
259
|
-
export function imageProperty(config: Omit<CommonPropertyConfig<ImagePropertyData, {}>, 'default'> & Partial<Pick<CommonPropertyConfig<ImagePropertyData, {}>, 'default'>> & {
|
|
260
|
-
enableFocalPointPicker?: boolean
|
|
261
|
-
}): SimpleNativeProperty {
|
|
262
|
-
return {
|
|
263
|
-
name: config.name,
|
|
264
|
-
type: "object",
|
|
265
|
-
condition: config.condition,
|
|
266
|
-
alwaysStore: config.alwaysStore,
|
|
267
|
-
default: {media: {id: config.default?.media?.id ?? 0, url: config.default?.media?.url ?? ''}, pos: {x: config.default?.pos?.x ?? 50, y: config.default?.pos?.y ?? 50}},
|
|
268
|
-
renderer(value: ImagePropertyData, onChange) {
|
|
269
|
-
const {media} = useSelect(select => {
|
|
270
|
-
return { media: value.media?.id ? (select('core') as any).getMedia(value.media.id) : undefined };
|
|
271
|
-
}, [value]);
|
|
272
|
-
|
|
273
|
-
const noImageUploadedVersion = <MediaUploadCheck>
|
|
274
|
-
<MediaUpload
|
|
275
|
-
onSelect={media => onChange({...value, media: {id: media.id, url: media['url']}})}
|
|
276
|
-
value={value.media?.id ?? 0}
|
|
277
|
-
allowedTypes={['image']}
|
|
278
|
-
render={({open}) => (
|
|
279
|
-
<Button
|
|
280
|
-
className={!value.media?.id ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview'}
|
|
281
|
-
onClick={open}
|
|
282
|
-
>
|
|
283
|
-
{!value.media?.id && __('Choose an image', 'plaudit')}
|
|
284
|
-
{media &&
|
|
285
|
-
<ResponsiveWrapper
|
|
286
|
-
naturalWidth={media.media_details.width}
|
|
287
|
-
naturalHeight={media.media_details.height}
|
|
288
|
-
>
|
|
289
|
-
<img src={media.source_url} alt="The currently-selected image." />
|
|
290
|
-
</ResponsiveWrapper>
|
|
291
|
-
}
|
|
292
|
-
</Button>
|
|
293
|
-
)}
|
|
294
|
-
/>
|
|
295
|
-
</MediaUploadCheck>;
|
|
296
|
-
let fppOrMedia;
|
|
297
|
-
if (config.enableFocalPointPicker !== false) {
|
|
298
|
-
fppOrMedia = <FocalPointPicker
|
|
299
|
-
onChange={pos => onChange({...value, pos: {x: pos.x * 100, y: pos.y * 100}})}
|
|
300
|
-
url={value.media?.url ?? ''}
|
|
301
|
-
value={{
|
|
302
|
-
x: (value.pos?.x ?? 50) / 100,
|
|
303
|
-
y: (value.pos?.y ?? 50) / 100
|
|
304
|
-
}}
|
|
305
|
-
/>;
|
|
306
|
-
} else {
|
|
307
|
-
fppOrMedia = <ResponsiveWrapper
|
|
308
|
-
naturalWidth={media?.media_details.width}
|
|
309
|
-
naturalHeight={media?.media_details.height}
|
|
310
|
-
>
|
|
311
|
-
<img src={value.media?.url ?? ''} alt="The currently-selected image." />
|
|
312
|
-
</ResponsiveWrapper>
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const imageUploadedVersion = <>
|
|
316
|
-
{value.media?.url && fppOrMedia}
|
|
317
|
-
<MediaUploadCheck>
|
|
318
|
-
<MediaUpload
|
|
319
|
-
title={__('Replace image', 'plaudit')}
|
|
320
|
-
value={value.media?.id ?? 0}
|
|
321
|
-
onSelect={media => onChange({...value, media: {id: media.id, url: media['url']}})}
|
|
322
|
-
allowedTypes={['image']}
|
|
323
|
-
render={({open}) => (
|
|
324
|
-
<Button onClick={open} variant="secondary">{__('Replace image', 'plaudit')}</Button>
|
|
325
|
-
)}
|
|
326
|
-
/>
|
|
327
|
-
</MediaUploadCheck>
|
|
328
|
-
<MediaUploadCheck>
|
|
329
|
-
<Button onClick={() => onChange({...value, media: {id: 0, url: ''}})} isDestructive>{__('Remove image', 'plaudit')}</Button>
|
|
330
|
-
</MediaUploadCheck>
|
|
331
|
-
</>;
|
|
332
|
-
return wrapInCard(() =>
|
|
333
|
-
<div className="editor-post-featured-image">
|
|
334
|
-
{value.media?.id ? imageUploadedVersion : noImageUploadedVersion}
|
|
335
|
-
</div>,
|
|
336
|
-
config.label, config.help);
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function wrapInCard(wrapped: () => ReactElement, label: string, help?: string): ReactElement {
|
|
342
|
-
return <Card>
|
|
343
|
-
<CardHeader>
|
|
344
|
-
<Heading>{label}</Heading>
|
|
345
|
-
</CardHeader>
|
|
346
|
-
<CardBody>
|
|
347
|
-
{wrapped()}
|
|
348
|
-
</CardBody>
|
|
349
|
-
{help && <CardFooter>
|
|
350
|
-
{help}
|
|
351
|
-
</CardFooter>}
|
|
352
|
-
</Card>;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function asOptions<T extends string|number>(options: PickableOptions<T>, noSelectionValue?: T): Array<{ value: T, label: string }> {
|
|
356
|
-
const res = options.map(opt => ({value: opt[0], label: typeof opt[1] === 'string' ? opt[1] : opt[1].text}));
|
|
357
|
-
if (noSelectionValue !== undefined && !res.some(opt => !opt.value)) {
|
|
358
|
-
res.splice(0, 0, {value: noSelectionValue, label: "-- No Selection --"});
|
|
359
|
-
}
|
|
360
|
-
return res;
|
|
361
|
-
}
|
package/src/blocks/index.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {installLayeredBlockStylesSupport} from "./layered-styles";
|
|
2
|
-
import {installSimpleNativePropertiesSupport} from "./simple-native-property";
|
|
3
|
-
|
|
4
|
-
export * from "./simple-block";
|
|
5
|
-
export * from "./layered-styles";
|
|
6
|
-
export * from "./simple-native-property";
|
|
7
|
-
export * from './common-native-property-implementations';
|
|
8
|
-
|
|
9
|
-
export function installGutenbergBlockExtensions() {
|
|
10
|
-
installLayeredBlockStylesSupport();
|
|
11
|
-
installSimpleNativePropertiesSupport();
|
|
12
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import {createHigherOrderComponent} from "@wordpress/compose";
|
|
2
|
-
import {addFilter} from "@wordpress/hooks";
|
|
3
|
-
|
|
4
|
-
import React, {type ReactElement} from "react";
|
|
5
|
-
|
|
6
|
-
import {InspectorPanel, PickOneFromColors, PickOneFromRadios, PickOneFromSelect, PickOneFromToggleGroup} from "../controls";
|
|
7
|
-
import type {SimpleBlockControlProps, PickableOptions, SpecificValueBlockProps} from "../controls/types";
|
|
8
|
-
|
|
9
|
-
type BaseLayeredBlockStyleLayer = {
|
|
10
|
-
name: string;
|
|
11
|
-
title: string;
|
|
12
|
-
default?: string|undefined;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
type LayeredBlockStyleColorLayer = BaseLayeredBlockStyleLayer&{
|
|
16
|
-
picker: 'color';
|
|
17
|
-
options: PickableOptions<string, {color?: string}>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
type LayeredBlockStyleToggleLayer = BaseLayeredBlockStyleLayer&{
|
|
21
|
-
picker?: 'toggle-control';
|
|
22
|
-
options: PickableOptions<string, {icon: ReactElement}>;
|
|
23
|
-
}
|
|
24
|
-
type LayeredBlockStyleSelectOrRadioLayer = BaseLayeredBlockStyleLayer&{
|
|
25
|
-
picker: 'select'|'radio';
|
|
26
|
-
options: PickableOptions<string>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type LayeredBlockStyleLayer = LayeredBlockStyleColorLayer|LayeredBlockStyleToggleLayer|LayeredBlockStyleSelectOrRadioLayer;
|
|
30
|
-
|
|
31
|
-
export interface LayeredBlockStylesConfig {
|
|
32
|
-
block: `${string}/${string}`;
|
|
33
|
-
layers: LayeredBlockStyleLayer[];
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function registerLayeredBlockStyles(config: LayeredBlockStylesConfig) {
|
|
37
|
-
const layeredBlockStyles: Map<string, LayeredBlockStylesConfig> = (window as any).plauditLayeredBlockStyles ?? ((window as any).plauditLayeredBlockStyles = new Map());
|
|
38
|
-
layeredBlockStyles.set(config.block, config);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function installLayeredBlockStylesSupport() {
|
|
42
|
-
if ((window as any).plauditLayeredBlockStylesInstalled) {
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
(window as any).plauditLayeredBlockStylesInstalled = true;
|
|
46
|
-
const layeredBlockStyles: Map<string, LayeredBlockStylesConfig> = (window as any).plauditLayeredBlockStyles ?? ((window as any).plauditLayeredBlockStyles = new Map());
|
|
47
|
-
|
|
48
|
-
function layeredStyleValueGetter(layer: LayeredBlockStyleLayer, block: SpecificValueBlockProps<'className', string|undefined>) {
|
|
49
|
-
const currentLayerClassPrefix = `style-${layer.name}-`;
|
|
50
|
-
return block.attributes['className']?.split(/\s+/).filter(cn => cn.startsWith(currentLayerClassPrefix))[0]?.substring(currentLayerClassPrefix.length) ?? layer.default ?? '';
|
|
51
|
-
}
|
|
52
|
-
function layeredStyleValueSetter(
|
|
53
|
-
attrs: Record<string, string>, layers: LayeredBlockStylesConfig['layers'], block: SpecificValueBlockProps<'className', string|undefined>
|
|
54
|
-
) {
|
|
55
|
-
const [key, value] = Object.entries(attrs)[0];
|
|
56
|
-
if (!key.startsWith("layeredStyles__")) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const layerName = key.substring(15);
|
|
61
|
-
const currentLayerClassPrefix = `style-${layerName}-`;
|
|
62
|
-
const classNames = block.attributes['className'] ? block.attributes['className'].split(/\s+/).filter(cn => !cn.startsWith(currentLayerClassPrefix)) : [];
|
|
63
|
-
|
|
64
|
-
const layerClassPrefixes = layers.map(layer => `style-${layer.name}-`);
|
|
65
|
-
const layerClasses = classNames.filter(cn => layerClassPrefixes.some(lcp => cn.startsWith(lcp)));
|
|
66
|
-
const nonLayerClasses = classNames.filter(cn => !layerClasses.includes(cn));
|
|
67
|
-
|
|
68
|
-
layerClasses.push(`${currentLayerClassPrefix}${value}`);
|
|
69
|
-
layerClasses.sort();
|
|
70
|
-
|
|
71
|
-
block.setAttributes({className: [...nonLayerClasses, ...layerClasses].join(' ')});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function convertLayersToRadios(
|
|
75
|
-
layer: LayeredBlockStyleLayer, layers: LayeredBlockStylesConfig['layers'], props: SimpleBlockControlProps<'className', string|undefined>
|
|
76
|
-
): React.JSX.Element {
|
|
77
|
-
const value = layeredStyleValueGetter(layer, props);
|
|
78
|
-
const sharedProps: SimpleBlockControlProps<string, string> = {
|
|
79
|
-
label: layer.title,
|
|
80
|
-
attribute: `layeredStyles__${layer.name}`,
|
|
81
|
-
attributes: {[`layeredStyles__${layer.name}`]: value},
|
|
82
|
-
setAttributes: attrs => layeredStyleValueSetter(attrs, layers, props)
|
|
83
|
-
}
|
|
84
|
-
switch (layer.picker) {
|
|
85
|
-
case undefined:
|
|
86
|
-
case 'toggle-control':
|
|
87
|
-
return <PickOneFromToggleGroup {...layer} {...sharedProps} />;
|
|
88
|
-
case 'color':
|
|
89
|
-
return <PickOneFromColors {...layer} {...sharedProps} />;
|
|
90
|
-
case 'radio':
|
|
91
|
-
return <PickOneFromRadios {...layer} {...sharedProps} />;
|
|
92
|
-
case 'select':
|
|
93
|
-
return <PickOneFromSelect {...layer} {...sharedProps} />;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
addFilter('editor.BlockEdit', 'plaudit/gutenberg-api-extensions/layered-styles', createHigherOrderComponent(BlockEdit => (props: any) => {
|
|
98
|
-
if (!layeredBlockStyles.has(props['name'])) {
|
|
99
|
-
return <BlockEdit {...props} />;
|
|
100
|
-
}
|
|
101
|
-
return <>
|
|
102
|
-
<BlockEdit {...props} />
|
|
103
|
-
<InspectorPanel title="Layered Styles" initialOpen={true} group="styles">
|
|
104
|
-
{...layeredBlockStyles.get(props['name'])!.layers.map(layer => convertLayersToRadios(layer, layeredBlockStyles.get(props['name'])!.layers, props))}
|
|
105
|
-
</InspectorPanel>
|
|
106
|
-
</>;
|
|
107
|
-
}, 'plauditGutenbergApiExtensionsLayeredStyles'));
|
|
108
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import {type Block, type BlockConfiguration, type BlockSaveProps, registerBlockType} from "@wordpress/blocks";
|
|
2
|
-
import {InnerBlocks, useBlockProps, useInnerBlocksProps} from "@wordpress/block-editor";
|
|
3
|
-
import type {UseBlockProps} from "@wordpress/block-editor/components/use-block-props";
|
|
4
|
-
|
|
5
|
-
import {plauditIcon} from "../lib/plaudit-icons";
|
|
6
|
-
|
|
7
|
-
import React, {type ComponentType, type ReactNode} from "react";
|
|
8
|
-
|
|
9
|
-
export type SimpleBlockTypeConfig<TAttributes extends Record<string, any>> = {
|
|
10
|
-
icon?: BlockConfiguration<TAttributes>['icon']|undefined,
|
|
11
|
-
defaults?: Required<TAttributes>,
|
|
12
|
-
controls?: NonNullable<Block<TAttributes>['edit']>,
|
|
13
|
-
renderer: (attributes: BlockSaveProps<TAttributes>, wrapperProps: UseBlockProps|UseBlockProps['save'], InnerBlocks: () => React.ReactElement, mode: "edit"|"save") => ReactNode,
|
|
14
|
-
innerBlocksProps?: InnerBlocks.Props
|
|
15
|
-
settings?: Partial<BlockConfiguration<TAttributes>>,
|
|
16
|
-
};
|
|
17
|
-
function applyDefaults<TAttributes extends Record<string, any>, K extends BlockSaveProps<TAttributes>>(config: Omit<SimpleBlockTypeConfig<TAttributes>, 'renderer'>, props: K) {
|
|
18
|
-
type attrs = { [P in keyof K['attributes']]: K['attributes'][P] };
|
|
19
|
-
const mutableProps = {...props, attributes: {...props.attributes}} as Omit<typeof props, 'attributes'>&{attributes: attrs};
|
|
20
|
-
for (const [k, v] of Object.entries(config.defaults ?? {}) as Array<[keyof typeof mutableProps.attributes, any]>) {
|
|
21
|
-
if (mutableProps.attributes[k] === undefined) {
|
|
22
|
-
mutableProps.attributes[k] = v;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return mutableProps;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function registerSimpleBlockType<TAttributes extends Record<string, any> = {}>(
|
|
29
|
-
metadata: BlockConfiguration<TAttributes>, config: SimpleBlockTypeConfig<TAttributes>
|
|
30
|
-
): Block<TAttributes>|undefined {
|
|
31
|
-
return registerBlockType<TAttributes>(metadata, {
|
|
32
|
-
...config.settings,
|
|
33
|
-
icon: config.icon ?? plauditIcon,
|
|
34
|
-
edit(props) {
|
|
35
|
-
const populatedProps = applyDefaults(config, props);
|
|
36
|
-
return <>
|
|
37
|
-
{config.controls && <config.controls {...populatedProps} />}
|
|
38
|
-
{config.renderer(populatedProps, useBlockProps, () => <InnerBlocks {...config.innerBlocksProps} />, "edit")}
|
|
39
|
-
</>;
|
|
40
|
-
},
|
|
41
|
-
save(props) {
|
|
42
|
-
return config.renderer(applyDefaults(config, props), useBlockProps.save, () => <InnerBlocks.Content />, "save");
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
type SimpleContainerTypeConfig<TAttributes extends Record<string, any>> = Omit<SimpleBlockTypeConfig<TAttributes>, 'renderer'>&{
|
|
48
|
-
tag?: ComponentType,
|
|
49
|
-
attributesFromProps?: (arg: ReturnType<typeof applyDefaults<TAttributes, BlockSaveProps<TAttributes>>>, mode: "edit"|"save") => Parameters<UseBlockProps|UseBlockProps['save']>[0]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function registerSimpleContainerBlock<TAttributes extends Record<string, any> = {}>(
|
|
53
|
-
metadata: BlockConfiguration<TAttributes>, config: SimpleContainerTypeConfig<TAttributes>
|
|
54
|
-
): Block<TAttributes>|undefined {
|
|
55
|
-
const Tag = config.tag ?? ((props: any) => <div {...props} />);
|
|
56
|
-
return registerBlockType<TAttributes>(metadata, {
|
|
57
|
-
...config.settings,
|
|
58
|
-
icon: config.icon ?? plauditIcon,
|
|
59
|
-
edit(props) {
|
|
60
|
-
const populatedProps = applyDefaults(config, props);
|
|
61
|
-
return <>
|
|
62
|
-
{config.controls && <config.controls {...populatedProps} />}
|
|
63
|
-
<Tag {...useInnerBlocksProps(useBlockProps(
|
|
64
|
-
(config.attributesFromProps ?? (() => ({} as Parameters<UseBlockProps>[0])))(populatedProps, "edit")), config.innerBlocksProps)} />
|
|
65
|
-
</>;
|
|
66
|
-
},
|
|
67
|
-
save(props) {
|
|
68
|
-
return <Tag {...useInnerBlocksProps.save(useBlockProps.save(
|
|
69
|
-
(config.attributesFromProps ?? (() => ({} as Parameters<UseBlockProps['save']>[0])))(applyDefaults(config, props), "edit")))} />;
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
}
|