@secretstache/wordpress-gutenberg 0.4.14 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/SpacingControl.js +0 -1
- package/src/hooks/useAllowedBlocks.js +1 -1
- package/src/utils/helpers.js +93 -2
- package/src/utils/index.js +1 -1
- package/src/utils/rootBlock/{setRootBlockAppender.js → appender.js} +26 -18
- package/src/utils/rootBlock/index.js +5 -3
- package/src/utils/rootBlock/rootBlockVisibilityFilter.js +35 -0
- package/src/utils/rootBlock/setRootBlockFilter.js +29 -0
- package/src/utils/rootBlock/setRootBlockForPostTypes.js +73 -0
- package/src/utils/rootBlock/unsetRootBlockFilter.js +26 -0
- package/src/utils/rootContainer/README.md +72 -0
- package/src/utils/rootContainer/index.js +42 -0
- package/src/utils/rootBlock/README.md +0 -156
- package/src/utils/rootBlock/hideRootBlockForOtherBlocks.js +0 -33
- package/src/utils/rootBlock/setRootBlock.js +0 -115
- package/src/utils/waitForContainer/README.md +0 -40
- package/src/utils/waitForContainer/index.js +0 -26
package/package.json
CHANGED
@@ -27,7 +27,6 @@ const Control = ({ label, max, min, value, onChange, disabled, tooltip, ...other
|
|
27
27
|
max={max}
|
28
28
|
marks={generateMarks(min, max)}
|
29
29
|
resetFallbackValue={-1}
|
30
|
-
help="Use -1 for default settings."
|
31
30
|
renderTooltipContent={(value) => {
|
32
31
|
if (value === -1) return 'Default';
|
33
32
|
|
package/src/utils/helpers.js
CHANGED
@@ -1,7 +1,18 @@
|
|
1
|
+
import { filters } from '@wordpress/hooks';
|
1
2
|
import apiFetch from '@wordpress/api-fetch';
|
2
3
|
import slugify from 'slugify';
|
3
4
|
import classNames from 'classnames';
|
5
|
+
import { select, subscribe } from '@wordpress/data';
|
6
|
+
import { getBlockType, unregisterBlockType } from '@wordpress/blocks';
|
4
7
|
|
8
|
+
/**
|
9
|
+
* Loads select options by fetching posts from WordPress REST API.
|
10
|
+
* @async
|
11
|
+
* @param {string} inputValue - Search term to filter posts
|
12
|
+
* @param {string} postType - WordPress post type to query
|
13
|
+
* @param {Function|null} [mapper=null] - Optional function to transform API response items
|
14
|
+
* @returns {Promise<Array<{value: number, label: string}>>} Array of select options
|
15
|
+
*/
|
5
16
|
export const loadSelectOptions = async (inputValue, postType, mapper = null) => {
|
6
17
|
const response = await apiFetch({
|
7
18
|
path: `/wp/v2/${postType}?search=${encodeURIComponent(inputValue)}`,
|
@@ -23,12 +34,22 @@ export const loadSelectOptions = async (inputValue, postType, mapper = null) =>
|
|
23
34
|
}
|
24
35
|
};
|
25
36
|
|
37
|
+
/**
|
38
|
+
* Converts a string to a URL-friendly slug.
|
39
|
+
* @param {string} name - String to convert to slug
|
40
|
+
* @returns {string} URL-friendly slug in lowercase with hyphens
|
41
|
+
*/
|
26
42
|
export const getSlug = (name) => slugify(name, {
|
27
43
|
replacement: '-',
|
28
44
|
lower: true,
|
29
45
|
strict: true,
|
30
46
|
});
|
31
47
|
|
48
|
+
/**
|
49
|
+
* Cleans SVG string by removing XML declaration and extra whitespace.
|
50
|
+
* @param {string} svgString - Raw SVG string
|
51
|
+
* @returns {string} Cleaned SVG string
|
52
|
+
*/
|
32
53
|
export const cleanSvgString = (svgString) => {
|
33
54
|
if (svgString.startsWith('<?xml')) {
|
34
55
|
const endOfXml = svgString.indexOf('?>');
|
@@ -43,9 +64,20 @@ export const cleanSvgString = (svgString) => {
|
|
43
64
|
return svgString;
|
44
65
|
};
|
45
66
|
|
46
|
-
|
47
|
-
|
67
|
+
/**
|
68
|
+
* Fetches and processes image data, handling both SVG and regular images.
|
69
|
+
* @async
|
70
|
+
* @param {Object} mediaData - WordPress media object
|
71
|
+
* @param {string} mediaData.mime - Media MIME type
|
72
|
+
* @param {string} mediaData.mime_type - Alternative MIME type property
|
73
|
+
* @param {string} mediaData.url - Media URL
|
74
|
+
* @param {number} mediaData.width - Image width
|
75
|
+
* @param {number} mediaData.height - Image height
|
76
|
+
* @returns {Promise<{isSvg: boolean, imageUrl: string|null, svgCode: string|null, width: number, height: number}>}
|
77
|
+
*/
|
48
78
|
export const getImage = async (mediaData) => {
|
79
|
+
const SVG_MIME_TYPE = 'image/svg+xml';
|
80
|
+
|
49
81
|
const isSvg = mediaData?.mime === SVG_MIME_TYPE || mediaData?.mime_type === SVG_MIME_TYPE;
|
50
82
|
const imagePayload = {
|
51
83
|
isSvg,
|
@@ -64,6 +96,11 @@ export const getImage = async (mediaData) => {
|
|
64
96
|
return imagePayload;
|
65
97
|
};
|
66
98
|
|
99
|
+
/**
|
100
|
+
* Formats a phone number string into XXX-XXX-XXXX format.
|
101
|
+
* @param {string} phone - Phone number starting with '+1' followed by 10 digits
|
102
|
+
* @returns {string} Formatted phone number or empty string if invalid
|
103
|
+
*/
|
67
104
|
export const getPhoneNumber = (phone) => {
|
68
105
|
if (!phone) return '';
|
69
106
|
|
@@ -79,6 +116,16 @@ export const getPhoneNumber = (phone) => {
|
|
79
116
|
return formatted;
|
80
117
|
};
|
81
118
|
|
119
|
+
/**
|
120
|
+
* Generates a formatted address string from location components.
|
121
|
+
* @param {Object} location - Location object
|
122
|
+
* @param {string} [location.street_number] - Street number
|
123
|
+
* @param {string} [location.street_name] - Street name
|
124
|
+
* @param {string} [location.city] - City
|
125
|
+
* @param {string} [location.state_short] - State abbreviation
|
126
|
+
* @param {string} [location.post_code] - Postal code
|
127
|
+
* @returns {string} Formatted address with HTML line breaks
|
128
|
+
*/
|
82
129
|
export const getLocationAddress = (location) => {
|
83
130
|
const {
|
84
131
|
street_number = '',
|
@@ -110,6 +157,11 @@ export const getLocationAddress = (location) => {
|
|
110
157
|
return addressParts.join('');
|
111
158
|
};
|
112
159
|
|
160
|
+
/**
|
161
|
+
* Decodes HTML entities to their corresponding characters.
|
162
|
+
* @param {string} text - Text containing HTML entities
|
163
|
+
* @returns {string} Decoded text
|
164
|
+
*/
|
113
165
|
export const decodeHtmlEntities = (text) => {
|
114
166
|
const tempElement = document.createElement('div');
|
115
167
|
tempElement.innerHTML = text;
|
@@ -144,3 +196,42 @@ export const getSpacingClasses = (
|
|
144
196
|
[`${mobilePrefix}pb-${spacing?.mobile?.padding?.bottom}`]: spacing?.mobile?.padding?.bottom !== -1,
|
145
197
|
});
|
146
198
|
};
|
199
|
+
|
200
|
+
/**
|
201
|
+
* Retrieves WordPress filters by namespace.
|
202
|
+
* @param {string} namespace - Filter namespace to search for
|
203
|
+
* @returns {Array<{filterName: string, namespace: string}>} Array of matching filters
|
204
|
+
*/
|
205
|
+
const getFiltersByNamespace = (namespace) => {
|
206
|
+
const list = [];
|
207
|
+
|
208
|
+
Object.entries(filters).forEach(([filterName, filterData]) => {
|
209
|
+
const handlers = filterData.handlers || [];
|
210
|
+
|
211
|
+
handlers.forEach((handler) => {
|
212
|
+
if (handler.namespace.startsWith(namespace)) {
|
213
|
+
list.push({ filterName, namespace: handler.namespace });
|
214
|
+
}
|
215
|
+
});
|
216
|
+
});
|
217
|
+
|
218
|
+
return list;
|
219
|
+
};
|
220
|
+
|
221
|
+
/**
|
222
|
+
* Unregisters a block type for a specific post type when editor loads.
|
223
|
+
* @param {string} blockName - Name of the block to unregister
|
224
|
+
* @param {string} postType - Post type to check against
|
225
|
+
*/
|
226
|
+
const unsetBlockForPostType = (blockName, postType) => {
|
227
|
+
const unsubscribe = subscribe(
|
228
|
+
() => {
|
229
|
+
const currentPostType = select('core/editor').getCurrentPostType();
|
230
|
+
if (currentPostType === postType && getBlockType(blockName)) {
|
231
|
+
unregisterBlockType(blockName);
|
232
|
+
unsubscribe();
|
233
|
+
}
|
234
|
+
},
|
235
|
+
'core/editor'
|
236
|
+
);
|
237
|
+
}
|
package/src/utils/index.js
CHANGED
@@ -1,25 +1,17 @@
|
|
1
1
|
import { createBlock } from '@wordpress/blocks';
|
2
2
|
import { dispatch } from '@wordpress/data';
|
3
3
|
|
4
|
-
import {
|
4
|
+
import { getRootContainer } from '../rootContainer/index.js';
|
5
5
|
|
6
|
-
const
|
7
|
-
const ROOT_BLOCK_APPENDER_SELECTOR = '.is-root-container .root-block-appender';
|
6
|
+
const ROOT_BLOCK_APPENDER_SELECTOR = '.root-block-appender';
|
8
7
|
|
9
8
|
/**
|
10
9
|
* Initializes the custom button for the root appender.
|
10
|
+
* @param {Element} rootContainer - The root container of the editor.
|
11
11
|
* @param {string} blockName - The name of the block to be created when the appender is clicked.
|
12
12
|
* @param {string} tooltipText - The tooltip text displayed on the appender.
|
13
13
|
*/
|
14
|
-
const initialize = (blockName, tooltipText) => {
|
15
|
-
const rootContainer = document.querySelector(ROOT_CONTAINER_SELECTOR);
|
16
|
-
|
17
|
-
if (!rootContainer) {
|
18
|
-
console.error('Root container not found');
|
19
|
-
|
20
|
-
return;
|
21
|
-
}
|
22
|
-
|
14
|
+
const initialize = (rootContainer, blockName, tooltipText) => {
|
23
15
|
const button = document.createElement('button');
|
24
16
|
|
25
17
|
button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" focusable="false"><path d="M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z"></path></svg>';
|
@@ -32,6 +24,8 @@ const initialize = (blockName, tooltipText) => {
|
|
32
24
|
});
|
33
25
|
|
34
26
|
rootContainer.prepend(button);
|
27
|
+
|
28
|
+
return !!rootContainer.querySelector(ROOT_BLOCK_APPENDER_SELECTOR);
|
35
29
|
};
|
36
30
|
|
37
31
|
/**
|
@@ -42,13 +36,27 @@ const initialize = (blockName, tooltipText) => {
|
|
42
36
|
* @param {string} [tooltipText='Add Row'] - The tooltip text displayed on the appender.
|
43
37
|
*/
|
44
38
|
export const setRootBlockAppender = (blockName, tooltipText = 'Add Row') => {
|
45
|
-
|
39
|
+
const rootContainer = getRootContainer();
|
40
|
+
|
41
|
+
if (rootContainer) {
|
42
|
+
initialize(rootContainer, blockName, tooltipText);
|
43
|
+
} else {
|
44
|
+
console.error('Root container is not found.')
|
45
|
+
}
|
46
46
|
};
|
47
47
|
|
48
48
|
export const unsetRootBlockAppender = () => {
|
49
|
-
const
|
50
|
-
|
51
|
-
if (
|
52
|
-
appender.
|
49
|
+
const rootContainer = getRootContainer();
|
50
|
+
|
51
|
+
if (rootContainer) {
|
52
|
+
const appender = rootContainer.querySelector(ROOT_BLOCK_APPENDER_SELECTOR);
|
53
|
+
|
54
|
+
if (appender) {
|
55
|
+
appender.remove();
|
56
|
+
} else {
|
57
|
+
console.error('Root block appender is not found.');
|
58
|
+
}
|
59
|
+
} else {
|
60
|
+
console.error('Root container is not found.')
|
53
61
|
}
|
54
|
-
}
|
62
|
+
};
|
@@ -1,4 +1,6 @@
|
|
1
|
-
export * from './
|
2
|
-
export * from './
|
1
|
+
export * from './setRootBlockForPostTypes.js'
|
2
|
+
export * from './setRootBlockFilter.js'
|
3
|
+
export * from './unsetRootBlockFilter.js';
|
4
|
+
export * from './rootBlockVisibilityFilter.js';
|
5
|
+
export * from './appender.js';
|
3
6
|
export * from './hideRootBlockForInlineInserter.js';
|
4
|
-
export * from './hideRootBlockForOtherBlocks.js';
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import { addFilter, removeFilter } from '@wordpress/hooks';
|
2
|
+
import { getBlockTypes } from '@wordpress/blocks';
|
3
|
+
|
4
|
+
export const rootBlockVisibilityFilter = {
|
5
|
+
add({ rootBlockName }) {
|
6
|
+
addFilter(
|
7
|
+
'blocks.registerBlockType',
|
8
|
+
'ssm/root-block-visibility',
|
9
|
+
(blockSettings, blockName) => {
|
10
|
+
const isRootBlock = blockName === rootBlockName;
|
11
|
+
const hasOwnAllowedBlocks = !!blockSettings?.allowedBlocks;
|
12
|
+
const hasParent = !!blockSettings?.parent;
|
13
|
+
|
14
|
+
if (isRootBlock || hasParent || hasOwnAllowedBlocks) {
|
15
|
+
return blockSettings;
|
16
|
+
}
|
17
|
+
|
18
|
+
// get all blockTypes
|
19
|
+
blockSettings.allowedBlocks = getBlockTypes()
|
20
|
+
?.filter((allowedBlock) => {
|
21
|
+
const isRootBlock = allowedBlock.name === rootBlockName;
|
22
|
+
const hasParent = !!allowedBlock?.parent;
|
23
|
+
|
24
|
+
return !isRootBlock && !hasParent;
|
25
|
+
})
|
26
|
+
?.map(allowedBlock => allowedBlock.name);
|
27
|
+
|
28
|
+
return blockSettings;
|
29
|
+
},
|
30
|
+
);
|
31
|
+
},
|
32
|
+
remove() {
|
33
|
+
removeFilter('blocks.registerBlockType', 'ssm/root-block-visibility');
|
34
|
+
},
|
35
|
+
};
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { addFilter, removeFilter } from '@wordpress/hooks';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Adds a filter to set the specified block as the root block by modifying block settings during registration.
|
5
|
+
* Blocks other than the root block will have their 'ancestor' property set to the root block name,
|
6
|
+
* making them only insertable within the root block.
|
7
|
+
*/
|
8
|
+
export const setRootBlockFilter = {
|
9
|
+
add(rootBlockName) {
|
10
|
+
addFilter(
|
11
|
+
'blocks.registerBlockType',
|
12
|
+
'ssm/set-root-block',
|
13
|
+
(settings, name) => {
|
14
|
+
const isRootBlock = name === rootBlockName;
|
15
|
+
const isBaseBlock = name === 'core/block';
|
16
|
+
const hasAncestor = !!settings?.ancestor;
|
17
|
+
|
18
|
+
if (!isRootBlock && !isBaseBlock && !hasAncestor) {
|
19
|
+
settings.ancestor = [rootBlockName];
|
20
|
+
}
|
21
|
+
|
22
|
+
return settings;
|
23
|
+
},
|
24
|
+
);
|
25
|
+
},
|
26
|
+
remove() {
|
27
|
+
removeFilter('blocks.registerBlockType', 'ssm/set-root-block');
|
28
|
+
},
|
29
|
+
};
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import { dispatch, select, subscribe } from '@wordpress/data';
|
2
|
+
import { setRootBlockFilter } from './setRootBlockFilter.js';
|
3
|
+
import { unsetRootBlockFilter } from './unsetRootBlockFilter.js';
|
4
|
+
import { rootBlockVisibilityFilter } from './rootBlockVisibilityFilter.js';
|
5
|
+
import { waitForRootContainer } from '../rootContainer/index.js';
|
6
|
+
import { setRootBlockAppender, unsetRootBlockAppender } from './appender.js';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Configures a root block for specific post types
|
10
|
+
*
|
11
|
+
* @param {string} rootBlockName - The name of the root block to set.
|
12
|
+
* @param {Array<string>} [postTypes=['page', 'post']] - The post types for which the root block should be enabled.
|
13
|
+
* @param {Function} [callback] - Optional callback to execute when the root block state changes.
|
14
|
+
* @param {Array<Object>} [filters=[rootBlockVisibilityFilter]] - Filters to apply or remove when enabling/disabling the root block.
|
15
|
+
* @param {boolean} [initAppender=true] - Whether to initialize the root block appender.
|
16
|
+
* @param {string} [appenderTooltipText='Add Row'] - Tooltip text for the root block appender.
|
17
|
+
*/
|
18
|
+
export const setRootBlockForPostTypes = (
|
19
|
+
rootBlockName,
|
20
|
+
postTypes = ['page', 'post'],
|
21
|
+
callback,
|
22
|
+
filters = [ rootBlockVisibilityFilter ],
|
23
|
+
initAppender = true,
|
24
|
+
appenderTooltipText = 'Add Row',
|
25
|
+
) => {
|
26
|
+
let isRootBlockEnabled = false;
|
27
|
+
|
28
|
+
waitForRootContainer().then(() => {
|
29
|
+
console.log('Root Container found.');
|
30
|
+
|
31
|
+
subscribe(() => {
|
32
|
+
const currentPostType = select('core/editor').getCurrentPostType();
|
33
|
+
|
34
|
+
if (postTypes.includes(currentPostType) && !isRootBlockEnabled) {
|
35
|
+
isRootBlockEnabled = true;
|
36
|
+
|
37
|
+
setRootBlockFilter.add(rootBlockName);
|
38
|
+
unsetRootBlockFilter.remove();
|
39
|
+
|
40
|
+
if (filters?.length > 0) {
|
41
|
+
filters.forEach((filter) => filter.add({ rootBlockName, isRootBlockEnabled, currentPostType }));
|
42
|
+
dispatch('core/blocks').reapplyBlockTypeFilters();
|
43
|
+
}
|
44
|
+
|
45
|
+
if (callback) {
|
46
|
+
callback({ isRootBlockEnabled, currentPostType });
|
47
|
+
}
|
48
|
+
|
49
|
+
if (initAppender) {
|
50
|
+
setRootBlockAppender(rootBlockName, appenderTooltipText);
|
51
|
+
}
|
52
|
+
} else if (!postTypes.includes(currentPostType) && isRootBlockEnabled) {
|
53
|
+
isRootBlockEnabled = false;
|
54
|
+
|
55
|
+
setRootBlockFilter.remove()
|
56
|
+
unsetRootBlockFilter.add(rootBlockName);
|
57
|
+
|
58
|
+
if (filters?.length > 0) {
|
59
|
+
filters.forEach((filter) => filter.remove({ rootBlockName, isRootBlockEnabled, currentPostType }));
|
60
|
+
dispatch('core/blocks').reapplyBlockTypeFilters();
|
61
|
+
}
|
62
|
+
|
63
|
+
if (callback) {
|
64
|
+
callback({ isRootBlockEnabled, currentPostType });
|
65
|
+
}
|
66
|
+
|
67
|
+
if (initAppender) {
|
68
|
+
unsetRootBlockAppender();
|
69
|
+
}
|
70
|
+
}
|
71
|
+
}, 'core/block-editor');
|
72
|
+
})
|
73
|
+
};
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { addFilter, removeFilter } from '@wordpress/hooks';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Adds a filter to unset the root block restrictions by removing the 'ancestor' property from block settings
|
5
|
+
* if it includes the specified root block name.
|
6
|
+
*/
|
7
|
+
export const unsetRootBlockFilter = {
|
8
|
+
add(rootBlockName) {
|
9
|
+
addFilter(
|
10
|
+
'blocks.registerBlockType',
|
11
|
+
'ssm/unset-root-block',
|
12
|
+
(settings) => {
|
13
|
+
const hasRootAncestor = settings.ancestor && settings.ancestor.includes(rootBlockName);
|
14
|
+
|
15
|
+
if (hasRootAncestor) {
|
16
|
+
settings.ancestor = null;
|
17
|
+
}
|
18
|
+
|
19
|
+
return settings;
|
20
|
+
},
|
21
|
+
);
|
22
|
+
},
|
23
|
+
remove() {
|
24
|
+
removeFilter('blocks.registerBlockType', 'ssm/unset-root-block');
|
25
|
+
},
|
26
|
+
};
|
@@ -0,0 +1,72 @@
|
|
1
|
+
## Overview
|
2
|
+
|
3
|
+
The `waitForRootContainer` function is a utility that periodically checks for the presence of the Gutenberg editor's root container, identified by the class `.is-root-container`. Once the container is found, it resolves a promise, allowing for additional initialization logic.
|
4
|
+
|
5
|
+
## Function Signature
|
6
|
+
|
7
|
+
```javascript
|
8
|
+
/**
|
9
|
+
* Periodically checks for the presence of the Gutenberg editor's root container and resolves when found.
|
10
|
+
*
|
11
|
+
* @param {number} [maxAttempts=10] - The maximum number of attempts to check for the root container.
|
12
|
+
* @param {number} [interval=500] - The interval time (in milliseconds) between attempts.
|
13
|
+
* @returns {Promise<Element>} - Resolves with the root container element if found, or rejects if not found after max attempts.
|
14
|
+
*/
|
15
|
+
export const waitForRootContainer = (maxAttempts = 10, interval = 500);
|
16
|
+
```
|
17
|
+
|
18
|
+
### Parameters
|
19
|
+
- **maxAttempts** (`number`, optional): The maximum number of attempts to check for the root container. Default is `10`.
|
20
|
+
- **interval** (`number`, optional): The interval time (in milliseconds) between each attempt. Default is `500`.
|
21
|
+
|
22
|
+
### Returns
|
23
|
+
- **Promise<Element>**: Resolves with the root container element when found. Rejects with an error if the container is not found after the maximum attempts.
|
24
|
+
|
25
|
+
---
|
26
|
+
|
27
|
+
## Usage Example
|
28
|
+
To use the `waitForRootContainer` function, import it into your script and handle the promise:
|
29
|
+
|
30
|
+
```javascript
|
31
|
+
import { waitForRootContainer } from '@secretstache/wordpress-gutenberg';
|
32
|
+
|
33
|
+
waitForRootContainer(10, 500)
|
34
|
+
.then((rootContainer) => {
|
35
|
+
console.log('Gutenberg root container found:', rootContainer);
|
36
|
+
// Your initialization logic here
|
37
|
+
})
|
38
|
+
.catch((error) => {
|
39
|
+
console.error('Failed to find Gutenberg root container:', error);
|
40
|
+
});
|
41
|
+
```
|
42
|
+
|
43
|
+
### Example Output
|
44
|
+
In this example:
|
45
|
+
- The function checks for the Gutenberg root container (identified by `.is-root-container`) up to 10 times, waiting 500ms between each check.
|
46
|
+
- If the container is found, it resolves with the container element.
|
47
|
+
- If the container is not found after 10 attempts, it rejects with an error.
|
48
|
+
|
49
|
+
---
|
50
|
+
|
51
|
+
## getRootContainer
|
52
|
+
|
53
|
+
The `getRootContainer` function retrieves the Gutenberg editor's root container element from the DOM.
|
54
|
+
|
55
|
+
### Usage Example
|
56
|
+
|
57
|
+
```javascript
|
58
|
+
import { getRootContainer } from '@secretstache/wordpress-gutenberg';
|
59
|
+
|
60
|
+
const rootContainer = getRootContainer();
|
61
|
+
if (rootContainer) {
|
62
|
+
console.log('Gutenberg root container found:', rootContainer);
|
63
|
+
} else {
|
64
|
+
console.log('Gutenberg root container not found');
|
65
|
+
}
|
66
|
+
```
|
67
|
+
|
68
|
+
---
|
69
|
+
|
70
|
+
### Notes
|
71
|
+
- The `getRootContainer` function searches both the main DOM and an iframe (if applicable) for the Gutenberg root container with the class `.is-root-container`.
|
72
|
+
- `waitForRootContainer` is built on top of `getRootContainer` and provides retry logic for situations where the root container is not immediately available.
|
@@ -0,0 +1,42 @@
|
|
1
|
+
const ROOT_CONTAINER_SELECTOR = '.is-root-container';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Retrieves the Gutenberg editor's root container element from the DOM or an iframe.
|
5
|
+
*
|
6
|
+
* @returns {Element|null} - Returns the root container element if found, or null if not found.
|
7
|
+
*/
|
8
|
+
export const getRootContainer = () => {
|
9
|
+
const rootContainer = document.querySelector(ROOT_CONTAINER_SELECTOR);
|
10
|
+
|
11
|
+
if (rootContainer) {
|
12
|
+
return rootContainer;
|
13
|
+
}
|
14
|
+
|
15
|
+
const iframe = document.querySelector('.block-editor iframe');
|
16
|
+
const iframeDocument = iframe?.contentDocument || iframe?.contentWindow?.document;
|
17
|
+
|
18
|
+
return iframeDocument?.querySelector(ROOT_CONTAINER_SELECTOR) || null;
|
19
|
+
};
|
20
|
+
|
21
|
+
export const waitForRootContainer = (maxAttempts = 10, interval = 500) => {
|
22
|
+
return new Promise((resolve, reject) => {
|
23
|
+
let attempts = 0;
|
24
|
+
|
25
|
+
const checkRootContainer = () => {
|
26
|
+
const rootContainer = getRootContainer();
|
27
|
+
|
28
|
+
if (rootContainer) {
|
29
|
+
return resolve(rootContainer);
|
30
|
+
} else {
|
31
|
+
if (attempts <= maxAttempts) {
|
32
|
+
attempts++;
|
33
|
+
setTimeout(checkRootContainer, interval);
|
34
|
+
} else {
|
35
|
+
reject(new Error('Root container not found after max attempts.'));
|
36
|
+
}
|
37
|
+
}
|
38
|
+
};
|
39
|
+
|
40
|
+
checkRootContainer();
|
41
|
+
});
|
42
|
+
};
|