@plaudit/gutenberg-api-extensions 2.0.2 → 2.2.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.
@@ -0,0 +1,33 @@
1
+ import type { RangeControlProps } from "@wordpress/components/build-types/range-control/types";
2
+ import type { TextControlProps } from "@wordpress/components/build-types/text-control/types";
3
+ import type { TextareaControlProps } from "@wordpress/components/build-types/textarea-control/types";
4
+ import type { SimpleNativeProperty } from "./simple-native-property";
5
+ type CommonPropertyConfig<T, V> = {
6
+ name: string;
7
+ label: string;
8
+ default: T;
9
+ enum?: T[];
10
+ component?: V;
11
+ };
12
+ export declare function rangeProperty(config: CommonPropertyConfig<number, RangeControlProps> & {
13
+ min: number;
14
+ max: number;
15
+ step?: number;
16
+ enum?: number[];
17
+ }): SimpleNativeProperty;
18
+ export declare function textProperty(config: CommonPropertyConfig<string, TextControlProps>): SimpleNativeProperty;
19
+ export declare function textareaProperty(config: CommonPropertyConfig<string, TextareaControlProps>): SimpleNativeProperty;
20
+ export type ImagePropertyData = {
21
+ media?: {
22
+ id?: number;
23
+ url?: string;
24
+ };
25
+ pos?: {
26
+ x?: number;
27
+ y?: number;
28
+ };
29
+ };
30
+ export declare function imageProperty(config: Omit<CommonPropertyConfig<ImagePropertyData, {}>, 'default'> & Partial<Pick<CommonPropertyConfig<ImagePropertyData, {}>, 'default'>> & {
31
+ enableFocalPointPicker?: boolean;
32
+ }): SimpleNativeProperty;
33
+ export {};
@@ -0,0 +1,82 @@
1
+ import { MediaUpload, MediaUploadCheck } from "@wordpress/block-editor";
2
+ import { Button, Card, CardBody, CardHeader, FocalPointPicker, RangeControl, ResponsiveWrapper, TextareaControl, TextControl, __experimentalHeading as Heading } from "@wordpress/components";
3
+ import { useSelect } from "@wordpress/data";
4
+ import { __ } from "@wordpress/i18n";
5
+ import React from "react";
6
+ export function rangeProperty(config) {
7
+ return {
8
+ name: config.name,
9
+ type: 'number',
10
+ enum: config.enum,
11
+ default: config.default,
12
+ renderer(value, onChange) {
13
+ return React.createElement(RangeControl, Object.assign({ value: value, onChange: onChange, min: config.min, max: config.max, label: config.label, step: config.step }, config.component));
14
+ }
15
+ };
16
+ }
17
+ export function textProperty(config) {
18
+ return {
19
+ name: config.name,
20
+ type: 'string',
21
+ default: config.default,
22
+ renderer(value, onChange) {
23
+ return React.createElement(TextControl, Object.assign({ value: value, onChange: onChange }, config.component));
24
+ }
25
+ };
26
+ }
27
+ export function textareaProperty(config) {
28
+ return {
29
+ name: config.name,
30
+ type: 'string',
31
+ default: config.default,
32
+ renderer(value, onChange) {
33
+ return React.createElement(TextareaControl, Object.assign({ value: value, onChange: onChange }, config.component));
34
+ }
35
+ };
36
+ }
37
+ export function imageProperty(config) {
38
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
39
+ return {
40
+ name: config.name,
41
+ type: "object",
42
+ default: { media: { id: (_c = (_b = (_a = config.default) === null || _a === void 0 ? void 0 : _a.media) === null || _b === void 0 ? void 0 : _b.id) !== null && _c !== void 0 ? _c : 0, url: (_f = (_e = (_d = config.default) === null || _d === void 0 ? void 0 : _d.media) === null || _e === void 0 ? void 0 : _e.url) !== null && _f !== void 0 ? _f : '' }, pos: { x: (_j = (_h = (_g = config.default) === null || _g === void 0 ? void 0 : _g.pos) === null || _h === void 0 ? void 0 : _h.x) !== null && _j !== void 0 ? _j : 50, y: (_m = (_l = (_k = config.default) === null || _k === void 0 ? void 0 : _k.pos) === null || _l === void 0 ? void 0 : _l.y) !== null && _m !== void 0 ? _m : 50 } },
43
+ renderer(value, onChange) {
44
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
45
+ const { media } = useSelect(select => {
46
+ var _a;
47
+ return { media: ((_a = value.media) === null || _a === void 0 ? void 0 : _a.id) ? select('core').getMedia(value.media.id) : undefined };
48
+ }, [value]);
49
+ const noImageUploadedVersion = React.createElement(MediaUploadCheck, null,
50
+ React.createElement(MediaUpload, { onSelect: media => onChange(Object.assign(Object.assign({}, value), { media: { id: media.id, url: media['url'] } })), value: (_b = (_a = value.media) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : 0, allowedTypes: ['image'], render: ({ open }) => {
51
+ var _a, _b;
52
+ return (React.createElement(Button, { className: !((_a = value.media) === null || _a === void 0 ? void 0 : _a.id) ? 'editor-post-featured-image__toggle' : 'editor-post-featured-image__preview', onClick: open },
53
+ !((_b = value.media) === null || _b === void 0 ? void 0 : _b.id) && __('Choose an image', 'plaudit'),
54
+ media &&
55
+ React.createElement(ResponsiveWrapper, { naturalWidth: media.media_details.width, naturalHeight: media.media_details.height },
56
+ React.createElement("img", { src: media.source_url, alt: "The currently-selected image." }))));
57
+ } }));
58
+ let fppOrMedia;
59
+ if (config.enableFocalPointPicker !== false) {
60
+ fppOrMedia = React.createElement(FocalPointPicker, { onChange: pos => onChange(Object.assign(Object.assign({}, value), { pos: { x: pos.x * 100, y: pos.y * 100 } })), url: (_d = (_c = value.media) === null || _c === void 0 ? void 0 : _c.url) !== null && _d !== void 0 ? _d : '', value: {
61
+ x: ((_f = (_e = value.pos) === null || _e === void 0 ? void 0 : _e.x) !== null && _f !== void 0 ? _f : 50) / 100,
62
+ y: ((_h = (_g = value.pos) === null || _g === void 0 ? void 0 : _g.y) !== null && _h !== void 0 ? _h : 50) / 100
63
+ } });
64
+ }
65
+ else {
66
+ fppOrMedia = React.createElement(ResponsiveWrapper, { naturalWidth: media === null || media === void 0 ? void 0 : media.media_details.width, naturalHeight: media === null || media === void 0 ? void 0 : media.media_details.height },
67
+ React.createElement("img", { src: (_k = (_j = value.media) === null || _j === void 0 ? void 0 : _j.url) !== null && _k !== void 0 ? _k : '', alt: "The currently-selected image." }));
68
+ }
69
+ const imageUploadedVersion = React.createElement(React.Fragment, null,
70
+ ((_l = value.media) === null || _l === void 0 ? void 0 : _l.url) && fppOrMedia,
71
+ React.createElement(MediaUploadCheck, null,
72
+ React.createElement(MediaUpload, { title: __('Replace image', 'plaudit'), value: (_o = (_m = value.media) === null || _m === void 0 ? void 0 : _m.id) !== null && _o !== void 0 ? _o : 0, onSelect: media => onChange(Object.assign(Object.assign({}, value), { media: { id: media.id, url: media['url'] } })), allowedTypes: ['image'], render: ({ open }) => (React.createElement(Button, { onClick: open, variant: "secondary" }, __('Replace image', 'plaudit'))) })),
73
+ React.createElement(MediaUploadCheck, null,
74
+ React.createElement(Button, { onClick: () => onChange(Object.assign(Object.assign({}, value), { media: { id: 0, url: '' } })), isDestructive: true }, __('Remove image', 'plaudit'))));
75
+ return React.createElement(Card, null,
76
+ React.createElement(CardHeader, null,
77
+ React.createElement(Heading, null, config.label)),
78
+ React.createElement(CardBody, null,
79
+ React.createElement("div", { className: "editor-post-featured-image" }, ((_p = value.media) === null || _p === void 0 ? void 0 : _p.id) ? imageUploadedVersion : noImageUploadedVersion)));
80
+ }
81
+ };
82
+ }
@@ -1,2 +1,4 @@
1
1
  export * from "./simple-block";
2
2
  export * from "./layered-styles";
3
+ export * from "./simple-native-property";
4
+ export * from './common-native-property-implementations';
@@ -1,2 +1,4 @@
1
1
  export * from "./simple-block";
2
2
  export * from "./layered-styles";
3
+ export * from "./simple-native-property";
4
+ export * from './common-native-property-implementations';
@@ -60,6 +60,6 @@ export function installLayeredBlockStylesSupport() {
60
60
  }
61
61
  return React.createElement(React.Fragment, null,
62
62
  React.createElement(BlockEdit, Object.assign({}, props)),
63
- React.createElement(InspectorPanel, { title: "Layered Styles", initialOpen: true }, ...layeredBlockStyles.get(props['name']).layers.map(layer => convertLayersToRadios(layer, layeredBlockStyles.get(props['name']).layers, props))));
63
+ React.createElement(InspectorPanel, { title: "Layered Styles", initialOpen: true, group: "styles" }, ...layeredBlockStyles.get(props['name']).layers.map(layer => convertLayersToRadios(layer, layeredBlockStyles.get(props['name']).layers, props))));
64
64
  }, 'plauditGutenbergApiExtensionsLayeredStyles'));
65
65
  }
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ export type InspectorPanelGroup = 'default' | 'advanced' | 'background' | 'border' | 'color' | 'dimensions' | 'effects' | 'filter' | 'list' | 'position' | 'settings' | 'styles' | 'typography';
3
+ type GenericSimpleNativeProperty<T, V extends 'string' | 'number' | 'boolean' | 'array' | 'object'> = {
4
+ name: string;
5
+ type: V;
6
+ enum?: T[];
7
+ default: T;
8
+ renderer(value: T, onChange: (v: T | undefined) => void): React.JSX.Element;
9
+ };
10
+ export type SimpleNativeProperty = GenericSimpleNativeProperty<string, 'string'> & {
11
+ enum?: string[];
12
+ } | GenericSimpleNativeProperty<number, 'number'> & {
13
+ enum?: number[];
14
+ } | GenericSimpleNativeProperty<boolean, 'boolean'> | GenericSimpleNativeProperty<any[], 'array'> | GenericSimpleNativeProperty<Record<string | number, unknown>, 'object'>;
15
+ export type SimpleNativePanel = {
16
+ title: string;
17
+ group?: InspectorPanelGroup;
18
+ initialOpen?: boolean;
19
+ properties: SimpleNativeProperty[];
20
+ };
21
+ export declare function registerSimpleNativeProperties(config: {
22
+ block: `${string}/${string}` | Array<`${string}/${string}`>;
23
+ panel: SimpleNativePanel | Array<SimpleNativePanel>;
24
+ }): void;
25
+ export declare function installSimpleNativePropertiesSupport(): void;
26
+ export {};
@@ -0,0 +1,156 @@
1
+ import { createHigherOrderComponent } from "@wordpress/compose";
2
+ import { addFilter } from "@wordpress/hooks";
3
+ import { InspectorPanel } from "../controls";
4
+ import React from "react";
5
+ export function registerSimpleNativeProperties(config) {
6
+ var _a;
7
+ const simpleNativePanels = (_a = window.plauditSimpleNativePanels) !== null && _a !== void 0 ? _a : (window.plauditSimpleNativePanels = {});
8
+ if (Array.isArray(config.block)) {
9
+ for (const b of config.block) {
10
+ addPanels(b, config.panel, simpleNativePanels);
11
+ }
12
+ }
13
+ else {
14
+ addPanels(config.block, config.panel, simpleNativePanels);
15
+ }
16
+ }
17
+ function addPanels(block, panel, simpleNativePanels) {
18
+ var _a;
19
+ const panels = ((_a = simpleNativePanels[block]) !== null && _a !== void 0 ? _a : (simpleNativePanels[block] = []));
20
+ if (Array.isArray(panel)) {
21
+ panels.push(...panel);
22
+ }
23
+ else {
24
+ panels.push(panel);
25
+ }
26
+ }
27
+ export function installSimpleNativePropertiesSupport() {
28
+ var _a;
29
+ if (window.plauditSimpleNativePropertiesSupportInstalled) {
30
+ return;
31
+ }
32
+ window.plauditSimpleNativePropertiesSupportInstalled = true;
33
+ const simpleNativePanels = (_a = window.plauditSimpleNativePanels) !== null && _a !== void 0 ? _a : (window.plauditSimpleNativePanels = {});
34
+ addFilter('blocks.registerBlockType', 'plaudit/gutenberg-api-extensions/attach-simple-native-properties', (atts) => {
35
+ const blockSimpleNativePanels = simpleNativePanels[atts.name];
36
+ if (blockSimpleNativePanels) {
37
+ const injectableProperties = {};
38
+ for (const blockSimpleNativePanel of blockSimpleNativePanels) {
39
+ for (const property of blockSimpleNativePanel.properties) {
40
+ const attrPath = property.name.split('.');
41
+ if (attrPath.length === 1) {
42
+ injectableProperties[property.name] = { type: property.type, default: property.default };
43
+ if ('enum' in property) {
44
+ injectableProperties[property.name].enum = property.enum;
45
+ }
46
+ }
47
+ else {
48
+ let actualProp = injectableProperties[attrPath[0]];
49
+ if (actualProp === undefined) {
50
+ injectableProperties[attrPath[0]] = actualProp = { type: "object" };
51
+ }
52
+ else if (actualProp.type !== "object") {
53
+ throw new Error(`Property type collision on ${property.name}. Attempted treat the property as an object when it was already a ${actualProp.type}`);
54
+ }
55
+ if (property.default !== undefined) {
56
+ if (actualProp.default === undefined) {
57
+ actualProp.default = {};
58
+ }
59
+ let target = actualProp.default;
60
+ for (let i = 1; i < attrPath.length - 1; i++) {
61
+ target = target[attrPath[i]] = {};
62
+ }
63
+ target[attrPath[attrPath.length - 1]] = property.default;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ for (const [name, config] of Object.entries(injectableProperties)) {
69
+ atts.attributes[name] = config;
70
+ }
71
+ }
72
+ return atts;
73
+ });
74
+ function setDottedAttribute(blockEditProps, attr, value) {
75
+ const attrPath = attr.split('.');
76
+ if (attrPath.length === 1) {
77
+ return blockEditProps.setAttributes({ [attr]: value });
78
+ }
79
+ const payload = Object.assign({}, blockEditProps.attributes[attrPath[0]]);
80
+ let currentLayer = payload;
81
+ for (let i = 1; i < attrPath.length - 1; i++) {
82
+ const attrPathNode = attrPath[i];
83
+ currentLayer = currentLayer[attrPathNode] = {};
84
+ }
85
+ currentLayer[attrPath[attrPath.length - 1]] = value;
86
+ blockEditProps.setAttributes({ [attrPath[0]]: payload });
87
+ }
88
+ addFilter('editor.BlockEdit', 'plaudit/gutenberg-api-extensions/simple-native-properties', createHigherOrderComponent(BlockEdit => (blockEditProps) => {
89
+ const blockSimpleNativePanels = simpleNativePanels[blockEditProps.name];
90
+ if (!blockSimpleNativePanels) {
91
+ return React.createElement(BlockEdit, Object.assign({}, blockEditProps));
92
+ }
93
+ return React.createElement(React.Fragment, null,
94
+ React.createElement(BlockEdit, Object.assign({}, blockEditProps)),
95
+ ...blockSimpleNativePanels.map(p => React.createElement(React.Fragment, null,
96
+ React.createElement(InspectorPanel, Object.assign({}, p), ...p.properties.map(prop => {
97
+ const propPath = prop.name.split('.');
98
+ let existingValue;
99
+ if (propPath.length === 1) {
100
+ existingValue = blockEditProps.attributes[prop.name];
101
+ if (existingValue === undefined) {
102
+ existingValue = prop.default;
103
+ blockEditProps.setAttributes({ [prop.name]: prop.default });
104
+ }
105
+ }
106
+ else {
107
+ let graphExistingValue = blockEditProps.attributes[propPath[0]];
108
+ if (graphExistingValue === undefined) {
109
+ blockEditProps.attributes[propPath[0]] = graphExistingValue = {};
110
+ }
111
+ for (let i = 1; i < propPath.length; i++) {
112
+ if (graphExistingValue[propPath[i]] === undefined) {
113
+ for (; i < propPath.length - 1; i++) {
114
+ graphExistingValue = graphExistingValue[propPath[i]] = {};
115
+ }
116
+ graphExistingValue[propPath[propPath.length - 1]] = existingValue = prop.default;
117
+ blockEditProps.setAttributes({ [propPath[0]]: blockEditProps.attributes[propPath[0]] });
118
+ break;
119
+ }
120
+ else {
121
+ graphExistingValue = graphExistingValue[propPath[i]];
122
+ }
123
+ }
124
+ existingValue = graphExistingValue !== null && graphExistingValue !== void 0 ? graphExistingValue : prop.default;
125
+ }
126
+ switch (prop.type) {
127
+ case "array":
128
+ // If the value is not an array or is a sparse array, then it will cause unrecoverable errors upon conversion to PHP
129
+ if (!Array.isArray(existingValue) || existingValue.length > existingValue.filter(() => true).length) {
130
+ throw new Error(`Invalid value passed to an array-type property: ${existingValue}`);
131
+ }
132
+ return prop.renderer(existingValue, value => setDottedAttribute(blockEditProps, prop.name, value));
133
+ case "object":
134
+ if (Array.isArray(existingValue) || typeof existingValue !== 'object') {
135
+ throw new Error(`Invalid value passed to an object-type property: ${existingValue}`);
136
+ }
137
+ return prop.renderer(existingValue, value => setDottedAttribute(blockEditProps, prop.name, value));
138
+ case "boolean":
139
+ if (typeof existingValue !== 'boolean' && existingValue !== undefined) {
140
+ existingValue = !!existingValue;
141
+ }
142
+ return prop.renderer(existingValue, value => setDottedAttribute(blockEditProps, prop.name, value));
143
+ case 'string':
144
+ if (typeof existingValue !== 'string' && existingValue !== undefined) {
145
+ existingValue = existingValue.toString();
146
+ }
147
+ return prop.renderer(existingValue, value => setDottedAttribute(blockEditProps, prop.name, value));
148
+ case "number":
149
+ if (typeof existingValue !== 'number' && existingValue !== undefined) {
150
+ existingValue = parseFloat(existingValue);
151
+ }
152
+ return prop.renderer(existingValue, value => setDottedAttribute(blockEditProps, prop.name, value));
153
+ }
154
+ })))));
155
+ }, 'plauditGutenbergApiExtensionsSimpleNativeProperties'));
156
+ }
@@ -1,3 +1,5 @@
1
1
  import type { PanelBodyProps } from "@wordpress/components/build-types/panel/types";
2
2
  import React from "react";
3
- export declare function InspectorPanel(props: PanelBodyProps): React.JSX.Element;
3
+ export declare function InspectorPanel(props: PanelBodyProps & {
4
+ group?: string;
5
+ }): React.JSX.Element;
@@ -2,6 +2,7 @@ import { InspectorControls } from "@wordpress/block-editor";
2
2
  import { PanelBody } from "@wordpress/components";
3
3
  import React from "react";
4
4
  export function InspectorPanel(props) {
5
- return React.createElement(InspectorControls, null,
5
+ const FullyTypedInspectorControls = InspectorControls;
6
+ return React.createElement(FullyTypedInspectorControls, { group: props.group },
6
7
  React.createElement(PanelBody, Object.assign({}, props, { children: props.children })));
7
8
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plaudit/gutenberg-api-extensions",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "scripts": {
5
5
  "prepublishOnly": "rm -rf build && mkdir build && tsc",
6
6
  "build": "tsc",
@@ -21,22 +21,22 @@
21
21
  }
22
22
  },
23
23
  "dependencies": {
24
- "@wordpress/block-editor": "^12.4.0",
25
- "@wordpress/blocks": "^12.13.0",
26
- "@wordpress/components": "^25.2.0",
27
- "@wordpress/compose": "^6.13.0",
28
- "@wordpress/core-data": "^6.13.0",
29
- "@wordpress/data": "^9.6.0",
30
- "@wordpress/element": "^5.13.0",
31
- "@wordpress/hooks": "^3.36.0",
32
- "@wordpress/i18n": "^4.36.0",
24
+ "@wordpress/block-editor": "^12.19.0",
25
+ "@wordpress/blocks": "^12.28.0",
26
+ "@wordpress/components": "^26.0.0",
27
+ "@wordpress/compose": "^6.28.0",
28
+ "@wordpress/core-data": "^6.28.0",
29
+ "@wordpress/data": "^9.21.0",
30
+ "@wordpress/element": "^5.28.0",
31
+ "@wordpress/hooks": "^3.51.0",
32
+ "@wordpress/i18n": "^4.51.0",
33
33
  "react": "^18.2.0",
34
34
  "react-dom": "^18.2.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/react": "^18.2.14",
38
- "@types/wordpress__block-editor": "^11.5.1",
39
- "@types/wordpress__blocks": "^12.5.0",
40
- "typescript": "^5.1.6"
37
+ "@types/react": "^18.2.55",
38
+ "@types/wordpress__block-editor": "^11.5.10",
39
+ "@types/wordpress__blocks": "^12.5.13",
40
+ "typescript": "^5.3.3"
41
41
  }
42
42
  }