PluginProbe
Extendify / 3.0.4
Extendify v3.0.4
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / src / Draft / components / GenerateImageButtons.jsx

GenerateImageButtons.jsx in Extendify 3.0.4, at src/Draft/components/GenerateImageButtons.jsx

187 lines 5.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { magic } from '@draft/svg';
2 import { render } from '@shared/lib/dom';
3 import { BlockControls } from '@wordpress/block-editor';
4 import {
5 Button,
6 MenuItem,
7 ToolbarButton,
8 ToolbarGroup,
9 } from '@wordpress/components';
10 import { useDispatch } from '@wordpress/data';
11 import { store as editPostStore } from '@wordpress/edit-post';
12 import { useEffect } from '@wordpress/element';
13 import { __ } from '@wordpress/i18n';
14
15 const supportedBlocks = [
16 'core/image',
17 'core/media-text',
18 'core/gallery',
19 'core/cover',
20 ];
21
22 export const GenerateImageButtons = (CurrentComponents, props) => {
23 const { openGeneralSidebar } = useDispatch(editPostStore);
24 const { clientId: blockId, name } = props;
25
26 useEffect(() => {
27 if (!supportedBlocks.includes(name)) return;
28
29 const frameSelector = 'iframe[name="editor-canvas"]';
30 const frame = document.querySelector(frameSelector)?.contentDocument;
31
32 const block = frame
33 ? frame.querySelector(`[data-block="${blockId}"]`)
34 : document.querySelector(`[data-block="${blockId}"]`);
35 if (!block) return;
36
37 const parentSelector =
38 '.block-editor-media-placeholder .components-form-file-upload';
39 const placeHolder = Object.assign(document.createElement('div'), {
40 className: 'components-form-file-upload',
41 });
42 block.querySelector(parentSelector)?.after(placeHolder);
43
44 let root;
45 const component = (
46 <>
47 <Button
48 variant="primary"
49 __next40pxDefaultSize
50 onClick={async () => {
51 openGeneralSidebar('extendify-draft/draft');
52 await new Promise((r) => requestAnimationFrame(r));
53 const btn = document.getElementById(
54 'extendify-draft-image-gen-button',
55 );
56 btn?.focus();
57 btn?.classList.add('animate-pulse-flash');
58 }}
59 >
60 {__('Get Personalized Image', 'extendify-local')}
61 </Button>
62 {/* layout placeholder */}
63 <span aria-hidden="true" />
64 </>
65 );
66 const id = requestAnimationFrame(() => {
67 root = render(component, placeHolder);
68 });
69 return () => {
70 cancelAnimationFrame(id);
71 root?.unmount();
72 placeHolder?.remove();
73 };
74 }, [blockId, openGeneralSidebar, name]);
75
76 return (
77 <>
78 <CurrentComponents {...props} />
79 <BlockControls>
80 <ToolbarButtons {...props} />
81 </BlockControls>
82 </>
83 );
84 };
85
86 const GetPersonalizedImage = () => {
87 const { openGeneralSidebar } = useDispatch(editPostStore);
88 return (
89 <MenuItem
90 icon={magic}
91 onClick={async () => {
92 openGeneralSidebar('extendify-draft/draft');
93 await new Promise((r) => requestAnimationFrame(r));
94 const btn = document.getElementById('extendify-draft-image-gen-button');
95 btn?.focus();
96 btn?.classList.add('animate-pulse-flash');
97 }}
98 >
99 {__('Get Personalized Image', 'extendify-local')}
100 </MenuItem>
101 );
102 };
103 const GetPersonalizedImageToolbar = () => {
104 const { openGeneralSidebar } = useDispatch(editPostStore);
105 return (
106 <ToolbarGroup className="extendify-draft">
107 <ToolbarButton
108 className="py-1.5 pl-2 pr-3 text-white before:bg-editor-main before:content-[''] hover:before:bg-editor-main-darker"
109 icon={magic}
110 onClick={async () => {
111 openGeneralSidebar('extendify-draft/draft');
112 await new Promise((r) => requestAnimationFrame(r));
113 const btn = document.getElementById(
114 'extendify-draft-image-gen-button',
115 );
116 btn?.focus();
117 btn?.classList.add('animate-pulse-flash');
118 }}
119 >
120 {__('Ask AI', 'extendify-local')}
121 </ToolbarButton>
122 </ToolbarGroup>
123 );
124 };
125
126 const ToolbarButtons = ({ name, attributes }) => {
127 useEffect(() => {
128 if (!supportedBlocks.includes(name)) return;
129
130 let placeholder, root, rafInsert, rafOuter, observer;
131 // use async iife to allow frame delays
132 (async () => {
133 await new Promise((r) => {
134 rafOuter = requestAnimationFrame(r);
135 });
136 // Find a button on the toolbar that says replace or add
137 const replaceBtn = Array.from(
138 document.querySelectorAll('[data-toolbar-item="true"]'),
139 )?.find(
140 (btn) =>
141 btn.textContent === __('Replace') || btn.textContent === __('Add'),
142 );
143 if (!replaceBtn) return;
144
145 observer = new MutationObserver((mutations) => {
146 // Button is open
147 if (mutations[0].target.getAttribute('aria-expanded') === 'true') {
148 // Find the popover section we want to attach to
149 const pClass = '.block-editor-media-replace-flow__media-upload-menu';
150 const popover = document.querySelector(pClass);
151 if (!popover) return;
152
153 // Attach the placeholder to the popover then render
154 placeholder = document.createElement('div');
155 popover.prepend(placeholder);
156 rafInsert = requestAnimationFrame(() => {
157 root = render(<GetPersonalizedImage />, placeholder);
158 });
159 return;
160 }
161 // Replace button is closed
162 cancelAnimationFrame(rafInsert);
163 root?.unmount();
164 placeholder?.remove();
165 });
166
167 // Watch for aria-expanded attribute only
168 observer.observe(replaceBtn, {
169 attributes: true,
170 childList: false,
171 subtree: false,
172 });
173 })();
174
175 return () => {
176 [rafInsert, rafOuter].forEach(cancelAnimationFrame);
177 root?.unmount();
178 placeholder?.remove();
179 observer?.disconnect();
180 };
181 }, [name, attributes]);
182
183 if (!supportedBlocks.includes(name)) return null;
184
185 return <GetPersonalizedImageToolbar />;
186 };
187