PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
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 0.7.0 All 126 releases
extendify / src / Draft / components / GenerateImageButtons.jsx

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

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