@plaudit/gutenberg-api-extensions 2.9.0 → 2.11.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.
Files changed (56) hide show
  1. package/build/blocks/common-native-property-implementations.d.ts +118 -0
  2. package/build/blocks/common-native-property-implementations.js +59 -16
  3. package/build/blocks/common-native-property-implementations.js.map +1 -1
  4. package/build/blocks/index.d.ts +5 -0
  5. package/build/blocks/layered-styles.d.ts +31 -0
  6. package/build/blocks/simple-block.d.ts +22 -0
  7. package/build/blocks/simple-native-property.d.ts +30 -0
  8. package/build/blocks/simple-native-property.js +122 -86
  9. package/build/blocks/simple-native-property.js.map +1 -1
  10. package/build/controls/AsynchronousFormTokenField.d.ts +20 -0
  11. package/build/controls/ExtendedPostPicker.d.ts +13 -0
  12. package/build/controls/InspectorPanel.d.ts +6 -0
  13. package/build/controls/LazySuggestionsComboboxControl.d.ts +7 -0
  14. package/build/controls/PickOne.d.ts +19 -0
  15. package/build/controls/SimpleToggle.d.ts +3 -0
  16. package/build/controls/SortableItemsControl.d.ts +11 -0
  17. package/build/controls/SortableItemsControl.js +142 -31
  18. package/build/controls/SortableItemsControl.js.map +1 -1
  19. package/build/controls/shared.d.ts +8 -0
  20. package/build/controls/types.d.ts +12 -0
  21. package/build/lib/plaudit-icons/column-1.d.ts +2 -0
  22. package/build/lib/plaudit-icons/column-2.d.ts +2 -0
  23. package/build/lib/plaudit-icons/column-3.d.ts +2 -0
  24. package/build/lib/plaudit-icons/placement-center.d.ts +2 -0
  25. package/build/lib/plaudit-icons/placement-end.d.ts +2 -0
  26. package/build/lib/plaudit-icons/placement-start.d.ts +2 -0
  27. package/build/lib/plaudit-icons/placement-stretch.d.ts +2 -0
  28. package/build/lib/plaudit-icons/plaudit-icon.d.ts +2 -0
  29. package/build/lib/plaudit-icons/reusable-block-marker.d.ts +2 -0
  30. package/{src/lib/plaudit-icons.ts → build/lib/plaudit-icons.d.ts} +0 -4
  31. package/package.json +5 -5
  32. package/src/blocks/common-native-property-implementations.tsx +0 -361
  33. package/src/blocks/index.ts +0 -12
  34. package/src/blocks/layered-styles.tsx +0 -108
  35. package/src/blocks/simple-block.tsx +0 -72
  36. package/src/blocks/simple-native-property.tsx +0 -204
  37. package/src/controls/AsynchronousFormTokenField.tsx +0 -158
  38. package/src/controls/ExtendedPostPicker.tsx +0 -50
  39. package/src/controls/InspectorPanel.tsx +0 -16
  40. package/src/controls/LazySuggestionsComboboxControl.tsx +0 -84
  41. package/src/controls/PickOne.tsx +0 -80
  42. package/src/controls/SimpleToggle.tsx +0 -9
  43. package/src/controls/SortableItemsControl.tsx +0 -70
  44. package/src/controls/shared.ts +0 -7
  45. package/src/controls/types.ts +0 -11
  46. package/src/lib/plaudit-icons/column-1.tsx +0 -8
  47. package/src/lib/plaudit-icons/column-2.tsx +0 -8
  48. package/src/lib/plaudit-icons/column-3.tsx +0 -8
  49. package/src/lib/plaudit-icons/placement-center.tsx +0 -5
  50. package/src/lib/plaudit-icons/placement-end.tsx +0 -5
  51. package/src/lib/plaudit-icons/placement-start.tsx +0 -5
  52. package/src/lib/plaudit-icons/placement-stretch.tsx +0 -5
  53. package/src/lib/plaudit-icons/plaudit-icon.tsx +0 -6
  54. package/src/lib/plaudit-icons/reusable-block-marker.tsx +0 -5
  55. /package/{src/controls/index.ts → build/controls/index.d.ts} +0 -0
  56. /package/{src/index.ts → build/index.d.ts} +0 -0
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const placementCenter: React.JSX.Element;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const placementEnd: React.JSX.Element;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const placementStart: React.JSX.Element;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const placementStretch: React.JSX.Element;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const plauditIcon: React.JSX.Element;
@@ -0,0 +1,2 @@
1
+ import React from "react";
2
+ export declare const reusableBlockMarker: React.JSX.Element;
@@ -1,7 +1,3 @@
1
- // =============================================
2
- // Allows sharing of assets in block configurations
3
- // =============================================
4
-
5
1
  export * from './plaudit-icons/column-1';
6
2
  export * from './plaudit-icons/column-2';
7
3
  export * from './plaudit-icons/column-3';
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "@plaudit/gutenberg-api-extensions",
3
- "version": "2.9.0",
3
+ "version": "2.11.0",
4
4
  "scripts": {
5
5
  "prepublishOnly": "rm -rf build && mkdir build && tsc",
6
6
  "build": "tsc",
7
7
  "watch": "tsc -w"
8
8
  },
9
- "files": ["./build", "./src"],
9
+ "files": ["./build"],
10
10
  "sideEffects": false,
11
11
  "exports": {
12
12
  "./lib/plaudit-icons": {
13
- "types": "./src/lib/plaudit-icons.ts",
14
13
  "default": "./build/lib/plaudit-icons.js"
15
14
  },
16
15
  "./blocks": {
17
- "types": "./src/blocks/index.ts",
18
16
  "default": "./build/blocks/index.js"
19
17
  },
20
18
  "./controls": {
21
- "types": "./src/controls/index.ts",
22
19
  "default": "./build/controls/index.js"
20
+ },
21
+ ".": {
22
+ "default": "./build/index.js"
23
23
  }
24
24
  },
25
25
  "dependencies": {
@@ -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
- }
@@ -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
- }