admin管理员组文章数量:1124811
I'm building a custom Masonry (non-jQuery version) gallery block and I managed to make it work in the block editor. I'm using WP 6.3. This is roughly the code:
JS:
const containerRef = useRef(null);
const {
attributes: { items, columns, gap },
setAttributes,
} = props;
useEffect(() => {
var msnry;
if (containerRef.current) {
imagesLoaded(containerRef.current, function () {
msnry = new Masonry(containerRef.current, {
itemSelector: '.gallery-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: parseInt(gap),
});
});
}
return () => {
msnry?.destroy();
}
}, [items, columns, gap]);
....
const displayItems = (items) => {
return (
items.map((item, index) => {
return (
<div className="gallery-item" key={index}>
<figure>
<img className={`wp-image-${item.id}`} src={item.url} alt={item.alt} key={item.id} />
</figure>
</div>
)
})
)
};
{items.length > 0 ?
<div {...blockProps}>
<div className="gallery-items" style={{ '--gap': gap }} ref={containerRef}>
<div className="grid-sizer"></div>
{displayItems(items)}
</div>
</div>
}
SCSS:
.wp-block-my-masonry-gallery {
.gallery-items {
--gap: 10px;
display: flex;
flex-wrap: wrap;
gap: var(--gap);
}
@for $i from 2 through 8 {
&.columns-#{$i} {
.gallery-item,
.grid-sizer {
width: calc(percentage(math.div(1, $i)) - var(--gap) + (var(--gap) / $i));
}
}
}
}
block.json
{
"apiVersion": 3,
"name": "my/masonry-gallery",
"title": "My Masonry Gallery",
"attributes": {
"items": {
"type": "array",
"default": []
},
"columns": {
"type": "number",
"minimum": 1,
"maximum": 8,
"default": 3
},
"gap": {
"type": "string",
"default": "10px"
}
},
"editorScript": [
"file:./index.js",
"imagesloaded",
"masonry"
],
"viewScript": [
"file:./view.js",
"imagesloaded",
"masonry"
],
"style": [
"file:./style-index.css",
"file:./view.css"
],
"editorStyle": "file:./index.css"
}
As I said before, the block works in the block editor, but when I try it in the new FSE template editor, the Masonry lib is not initialized correctly as the height of the Masonry container is set to zero and therefore the gallery images are overlapping the next blocks in the screen.
Block in the block editor:
Block in the FSE template editor:
Someone pointed me to the following post that mentions that the template editor is loaded in an iframe to isolate it from the rest of the admin screen and might cause issues with non-React libraries and suggest some changes to make them work in the iframed editor:
/
I refactored the code following the examples in the post but I’m getting the same results as before:
const containerRef = useRefEffect((element) => {
var msnry;
imagesLoaded(element, function () {
msnry = new Masonry(element, {
itemSelector: '.gallery-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: parseInt(gap),
});
});
return () => {
msnry?.destroy();
}
}, [items, columns, gap]);
Following @stokesman suggestions, I refactored the code to the following with no success:
const containerRef = useRefEffect((element) => {
var msnry;
const { ownerDocument } = element;
const { defaultView } = ownerDocument;
if ( ! defaultView.imagesLoaded || ! defaultView.Masonry ) {
return;
}
defaultView.imagesLoaded(element, function () {
msnry = new defaultView.Masonry(element, {
itemSelector: '.gallery-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: parseInt(gap),
});
});
return () => {
msnry?.destroy();
}
}, [items, columns, gap]);
The if ( ! defaultView.imagesLoaded || ! defaultView.Masonry )
check never evaluates to false
and the Masonry part is never executed.
Any ideas?
UPDATE (02/20/2024):
In order to inject the masonry and imagesloaded scripts into the iframe so they could be used from within the iframe, you must add them to the script
prop (not the editorScript
prop) in the block.json
file. Otherwise, they are loaded OUTSIDE the iframe for some reason.
...
"script": [
"imagesloaded",
"masonry"
],
"editorScript": "file:./index.js",
"viewScript": "file:./view.js",
...
I'm building a custom Masonry (non-jQuery version) gallery block and I managed to make it work in the block editor. I'm using WP 6.3. This is roughly the code:
JS:
const containerRef = useRef(null);
const {
attributes: { items, columns, gap },
setAttributes,
} = props;
useEffect(() => {
var msnry;
if (containerRef.current) {
imagesLoaded(containerRef.current, function () {
msnry = new Masonry(containerRef.current, {
itemSelector: '.gallery-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: parseInt(gap),
});
});
}
return () => {
msnry?.destroy();
}
}, [items, columns, gap]);
....
const displayItems = (items) => {
return (
items.map((item, index) => {
return (
<div className="gallery-item" key={index}>
<figure>
<img className={`wp-image-${item.id}`} src={item.url} alt={item.alt} key={item.id} />
</figure>
</div>
)
})
)
};
{items.length > 0 ?
<div {...blockProps}>
<div className="gallery-items" style={{ '--gap': gap }} ref={containerRef}>
<div className="grid-sizer"></div>
{displayItems(items)}
</div>
</div>
}
SCSS:
.wp-block-my-masonry-gallery {
.gallery-items {
--gap: 10px;
display: flex;
flex-wrap: wrap;
gap: var(--gap);
}
@for $i from 2 through 8 {
&.columns-#{$i} {
.gallery-item,
.grid-sizer {
width: calc(percentage(math.div(1, $i)) - var(--gap) + (var(--gap) / $i));
}
}
}
}
block.json
{
"apiVersion": 3,
"name": "my/masonry-gallery",
"title": "My Masonry Gallery",
"attributes": {
"items": {
"type": "array",
"default": []
},
"columns": {
"type": "number",
"minimum": 1,
"maximum": 8,
"default": 3
},
"gap": {
"type": "string",
"default": "10px"
}
},
"editorScript": [
"file:./index.js",
"imagesloaded",
"masonry"
],
"viewScript": [
"file:./view.js",
"imagesloaded",
"masonry"
],
"style": [
"file:./style-index.css",
"file:./view.css"
],
"editorStyle": "file:./index.css"
}
As I said before, the block works in the block editor, but when I try it in the new FSE template editor, the Masonry lib is not initialized correctly as the height of the Masonry container is set to zero and therefore the gallery images are overlapping the next blocks in the screen.
Block in the block editor:
Block in the FSE template editor:
Someone pointed me to the following post that mentions that the template editor is loaded in an iframe to isolate it from the rest of the admin screen and might cause issues with non-React libraries and suggest some changes to make them work in the iframed editor:
https://make.wordpress.org/core/2021/06/29/blocks-in-an-iframed-template-editor/
I refactored the code following the examples in the post but I’m getting the same results as before:
const containerRef = useRefEffect((element) => {
var msnry;
imagesLoaded(element, function () {
msnry = new Masonry(element, {
itemSelector: '.gallery-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: parseInt(gap),
});
});
return () => {
msnry?.destroy();
}
}, [items, columns, gap]);
Following @stokesman suggestions, I refactored the code to the following with no success:
const containerRef = useRefEffect((element) => {
var msnry;
const { ownerDocument } = element;
const { defaultView } = ownerDocument;
if ( ! defaultView.imagesLoaded || ! defaultView.Masonry ) {
return;
}
defaultView.imagesLoaded(element, function () {
msnry = new defaultView.Masonry(element, {
itemSelector: '.gallery-item',
columnWidth: '.grid-sizer',
percentPosition: true,
gutter: parseInt(gap),
});
});
return () => {
msnry?.destroy();
}
}, [items, columns, gap]);
The if ( ! defaultView.imagesLoaded || ! defaultView.Masonry )
check never evaluates to false
and the Masonry part is never executed.
Any ideas?
UPDATE (02/20/2024):
In order to inject the masonry and imagesloaded scripts into the iframe so they could be used from within the iframe, you must add them to the script
prop (not the editorScript
prop) in the block.json
file. Otherwise, they are loaded OUTSIDE the iframe for some reason.
...
"script": [
"imagesloaded",
"masonry"
],
"editorScript": "file:./index.js",
"viewScript": "file:./view.js",
...
Share
Improve this question
edited Feb 20, 2024 at 11:12
leemon
asked Aug 12, 2023 at 8:56
leemonleemon
2,0124 gold badges22 silver badges51 bronze badges
16
|
Show 11 more comments
1 Answer
Reset to default 1Your code isn't at fault. The Masonry library doesn't support this cross frame scenario. I can think of three ways you might get past this:
- Inject the masonry script into the iframe so it could be used from within the iframe
- Patch the masonry script and ship that version with your plugin
- Find an alternative library that doesn't have the issue
The third option might be fruitless. I'm not sure there are many options out there and they might have the same sort of issues.
The second option I've tried. I patched a couple places in the library and it seems to work okay now. I'm not sure it's 100% covered i.e. you might encounter an issue with some configuration I haven't tried. Here’s a gist with the patched version if you want to try it. In WordPress, you'll have to deregister the masonry script they bundled and register that one of course.
The first option might be a good one but you'd have to be sure to only try injecting the script into the iframe when there actually is one and you'd have to make sure your block waits to be sure the script has been made available in the iframe. There may be other potential gotchas as well.
On a side note, I'm considering submitting PRs to the libraries (the parts I patched are in separate dependencies (fizzy-ui-utils and outlayer) but not sure if/when they'd make it into a release.
本文标签: javascriptMasonry gallery block is working in the block editor but not the template editor
版权声明:本文标题:javascript - Masonry gallery block is working in the block editor but not the template editor 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736645141a1946085.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
new Masonry(…
it’d be something like:new element.ownerDocument.defaultView.Masonry(…
but you'd want some optional chaining or null checks in there as demonstrated in the article. – stokesman Commented Aug 19, 2023 at 19:38element.ownerDocument.defaultView.imagesLoaded
andelement.ownerDocument.defaultView.Masonry
like you suggested I get anUncaught TypeError: s.imagesLoaded is not a function
error in the console. – leemon Commented Aug 23, 2023 at 9:01imagesLoaded
call I'd put thisif ( ! defaultView.imagesLoaded || ! defaultView.Masonry ) return;
. That assumes you've assignedconst { defaultView } = element.ownerDocument;
on a line before that. As stated in the last code example in that article: "Scripts are loaded asynchronously, so check that the script is loaded. After the dependencies have loaded, the block will re-render." – stokesman Commented Aug 23, 2023 at 14:33undefined
and never change totrue
. – leemon Commented Aug 23, 2023 at 15:32apiVersion < 3
(or custom fields or other legacy metaboxes are displayed). I take it the refactored version no longer works even in the post editor? I've been doing a little experimenting and it seems like "We’ve loaded all front-end scripts in the iframe to fix these cases" is no longer true. Anyway, this might not even be the cause of the problem. So when you said it was working in the post editor that was withapiVersion: 3
? – stokesman Commented Aug 23, 2023 at 19:29