PluginProbe
AI Builder – Generate pages, blocks, images & translate with AI / 2.7.2
AI Builder – Generate pages, blocks, images & translate with AI v2.7.2
2.8.0 2.7.10 2.7.9 2.7.8 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 All 123 releases
ai-builder / assets / js / src / editor-blocks / fixed-bg-group / block.js

block.js in AI Builder – Generate pages, blocks, images & translate with AI 2.7.2, at assets/js/src/editor-blocks/fixed-bg-group/block.js

382 lines 10.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { registerBlockType } from "@wordpress/blocks";
2 import { __ } from "@wordpress/i18n";
3 import {
4 InspectorControls,
5 useBlockProps,
6 InnerBlocks,
7 MediaUpload,
8 MediaUploadCheck,
9 } from "@wordpress/block-editor";
10 import {
11 PanelBody,
12 ToggleControl,
13 TextControl,
14 Button,
15 Placeholder,
16 RangeControl,
17 } from "@wordpress/components";
18
19 // Template pour les blocs enfants
20 const TEMPLATE = [
21 ["core/heading", { level: 2, placeholder: "Titre de la section" }],
22 ["core/paragraph", { placeholder: "Contenu de la section..." }],
23 ];
24
25 // Composant ColorPicker sécurisé
26 const SafeColorPicker = ({ color, onChange, label }) => {
27 return (
28 <div style={{ marginBottom: "16px" }}>
29 <label
30 style={{
31 display: "block",
32 marginBottom: "8px",
33 fontWeight: "600",
34 }}
35 >
36 {label}
37 </label>
38 <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
39 <input
40 type="color"
41 value={color || "#3f1763"}
42 onChange={(e) => onChange && onChange(e.target.value)}
43 style={{
44 width: "50px",
45 height: "40px",
46 border: "none",
47 borderRadius: "4px",
48 cursor: "pointer",
49 }}
50 />
51 <TextControl
52 value={color || "#3f1763"}
53 onChange={(value) => onChange && onChange(value)}
54 placeholder="#3f1763"
55 style={{ flex: 1 }}
56 />
57 </div>
58 </div>
59 );
60 };
61
62 // Fonction pour convertir une couleur hex en rgba
63 const hexToRgba = (hex, opacity) => {
64 // Nettoyer la couleur hex (enlever # si présent)
65 const cleanHex = hex.replace("#", "");
66
67 // Vérifier si c'est un format hex valide (3 ou 6 caractères)
68 if (!/^[0-9A-F]{6}$/i.test(cleanHex) && !/^[0-9A-F]{3}$/i.test(cleanHex)) {
69 console.log("Invalid hex color:", hex, "using fallback");
70 return `rgba(63, 23, 99, ${opacity})`; // Fallback
71 }
72
73 // Convertir hex en RGB
74 let r, g, b;
75 if (cleanHex.length === 3) {
76 // Format court #RGB -> #RRGGBB
77 r = parseInt(cleanHex[0] + cleanHex[0], 16);
78 g = parseInt(cleanHex[1] + cleanHex[1], 16);
79 b = parseInt(cleanHex[2] + cleanHex[2], 16);
80 } else {
81 // Format long #RRGGBB
82 r = parseInt(cleanHex.substring(0, 2), 16);
83 g = parseInt(cleanHex.substring(2, 4), 16);
84 b = parseInt(cleanHex.substring(4, 6), 16);
85 }
86
87 const result = `rgba(${r}, ${g}, ${b}, ${opacity})`;
88 console.log("Color conversion:", hex, "->", result);
89 return result;
90 };
91
92 // Fonction pour appliquer les styles au bloc
93 const applyBlockStyles = (blockProps, attributes) => {
94 const { fixedBgImage, fixedBgOverlay } = attributes;
95
96 const styles = {
97 ...blockProps.style,
98 position: "relative",
99 minHeight: "400px",
100 backgroundSize: "cover",
101 backgroundPosition: "center",
102 backgroundRepeat: "no-repeat",
103 overflow: "hidden",
104 };
105
106 // Appliquer l'image de fond
107 if (fixedBgImage && fixedBgImage.url) {
108 styles.backgroundImage = `url(${fixedBgImage.url})`;
109 } else {
110 // Image par défaut (fixedBg est toujours activé)
111 styles.backgroundImage = `url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1920&h=1080&fit=crop')`;
112 }
113
114 // Appliquer background-attachment selon le contexte
115 if (
116 typeof document !== "undefined" &&
117 document.body &&
118 document.body.classList.contains("block-editor-page")
119 ) {
120 // Dans l'éditeur
121 styles.backgroundAttachment = "scroll";
122 } else {
123 // Sur le frontend
124 styles.backgroundAttachment = "fixed";
125 }
126
127 // Ajouter la variable CSS pour l'overlay
128 const overlayColor = hexToRgba(
129 attributes.fixedBgOverlay || "#3f1763",
130 attributes.fixedBgOverlayOpacity || 0.8
131 );
132 styles["--fixed-bg-overlay-color"] = overlayColor;
133
134 return {
135 ...blockProps,
136 style: styles,
137 className: `${
138 blockProps.className || ""
139 } aibui-fixed-bg-group fixed-bg-section`.trim(),
140 };
141 };
142
143 // Composant Edit
144 const Edit = ({ attributes, setAttributes }) => {
145 const { fixedBg, fixedBgImage, fixedBgOverlay, fixedBgOverlayOpacity } =
146 attributes;
147
148 const blockProps = useBlockProps(
149 applyBlockStyles(
150 {
151 className: "aibui-fixed-bg-group",
152 },
153 attributes
154 )
155 );
156
157 return (
158 <>
159 <InspectorControls>
160 <PanelBody title={__("Fixed Background Settings", "ai-builder")}>
161 {/* Fixed Background est toujours activé */}
162 <MediaUploadCheck>
163 <MediaUpload
164 onSelect={(media) => setAttributes({ fixedBgImage: media })}
165 allowedTypes={["image"]}
166 value={fixedBgImage?.id}
167 render={({ open }) => (
168 <div>
169 <Button
170 onClick={open}
171 variant="secondary"
172 style={{ marginBottom: "10px" }}
173 >
174 {fixedBgImage
175 ? __("Change Background Image", "ai-builder")
176 : __("Select Background Image", "ai-builder")}
177 </Button>
178 {fixedBgImage && (
179 <div>
180 <img
181 src={
182 fixedBgImage.sizes?.medium?.url || fixedBgImage.url
183 }
184 alt={fixedBgImage.alt}
185 style={{
186 width: "100%",
187 height: "auto",
188 borderRadius: "4px",
189 }}
190 />
191 <Button
192 onClick={() => setAttributes({ fixedBgImage: null })}
193 variant="link"
194 isDestructive
195 style={{ marginTop: "5px" }}
196 >
197 {__("Remove Image", "ai-builder")}
198 </Button>
199 </div>
200 )}
201 </div>
202 )}
203 />
204 </MediaUploadCheck>
205
206 <SafeColorPicker
207 color={fixedBgOverlay || "#3f1763"}
208 onChange={(color) => setAttributes({ fixedBgOverlay: color })}
209 label={__("Overlay Color", "ai-builder")}
210 />
211
212 <RangeControl
213 label={__("Overlay Opacity", "ai-builder")}
214 value={fixedBgOverlayOpacity || 0.8}
215 onChange={(value) =>
216 setAttributes({ fixedBgOverlayOpacity: value })
217 }
218 min={0}
219 max={1}
220 step={0.1}
221 help={__("Adjust the opacity of the overlay", "ai-builder")}
222 />
223 </PanelBody>
224 </InspectorControls>
225
226 <div {...blockProps}>
227 {/* Overlay avec ombres */}
228 {/* Overlay géré par CSS ::before - pas besoin d'élément JavaScript */}
229
230 {/* Message d'état dans l'éditeur */}
231 {document.body.classList.contains("block-editor-page") && (
232 <div
233 style={{
234 position: "absolute",
235 top: "20px",
236 left: "50%",
237 transform: "translateX(-50%)",
238 background: "rgba(0, 0, 0, 0.8)",
239 color: "white",
240 padding: "12px 20px",
241 borderRadius: "8px",
242 fontSize: "14px",
243 zIndex: 2,
244 pointerEvents: "none",
245 textAlign: "center",
246 maxWidth: "80%",
247 wordWrap: "break-word",
248 }}
249 >
250 {fixedBgImage ? (
251 <span>�
252 Fixed Background Group</span>
253 ) : (
254 <span>
255 ⚠️ Please select a background image in the settings panel
256 </span>
257 )}
258 </div>
259 )}
260
261 {/* Contenu */}
262 <div style={{ position: "relative", zIndex: 1 }}>
263 <InnerBlocks
264 template={[]} // Bloc vide par défaut
265 templateLock={false}
266 allowedBlocks={[
267 "core/heading",
268 "core/paragraph",
269 "core/button",
270 "core/spacer",
271 "core/group",
272 "core/cover",
273 ]}
274 />
275 </div>
276 </div>
277 </>
278 );
279 };
280
281 // Composant Save
282 const Save = ({ attributes }) => {
283 const { fixedBgImage, fixedBgOverlay, fixedBgOverlayOpacity } = attributes;
284
285 // Styles pour le frontend (save)
286 const frontendStyles = {
287 position: "relative",
288 minHeight: "400px",
289 backgroundSize: "cover",
290 backgroundPosition: "center",
291 backgroundRepeat: "no-repeat",
292 backgroundAttachment: "fixed",
293 overflow: "hidden",
294 };
295
296 // Appliquer l'image de fond
297 if (fixedBgImage && fixedBgImage.url) {
298 frontendStyles.backgroundImage = `url(${fixedBgImage.url})`;
299 } else {
300 // Image par défaut (fixedBg est toujours activé)
301 frontendStyles.backgroundImage = `url('https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=1920&h=1080&fit=crop')`;
302 }
303
304 // Ajouter la variable CSS pour l'overlay (fixedBg est toujours activé)
305 frontendStyles["--fixed-bg-overlay-color"] = hexToRgba(
306 fixedBgOverlay || "#3f1763",
307 fixedBgOverlayOpacity || 0.8
308 );
309
310 const blockProps = useBlockProps.save({
311 className: "aibui-fixed-bg-group fixed-bg-section",
312 style: frontendStyles,
313 });
314
315 return (
316 <div {...blockProps}>
317 {/* Overlay géré par CSS ::before - pas besoin d'élément JavaScript */}
318
319 {/* Contenu */}
320 <div style={{ position: "relative", zIndex: 1 }}>
321 <InnerBlocks.Content />
322 </div>
323 </div>
324 );
325 };
326
327 // Enregistrer le bloc
328 registerBlockType("ai-builder/fixed-bg-group", {
329 title: __("Fixed Background Group", "ai-builder"),
330 description: __(
331 "A group block with fixed background and shadow effects",
332 "ai-builder"
333 ),
334 icon: "cover-image",
335 category: "layout",
336 keywords: [
337 __("group", "ai-builder"),
338 __("background", "ai-builder"),
339 __("fixed", "ai-builder"),
340 ],
341
342 attributes: {
343 fixedBg: {
344 type: "boolean",
345 default: true,
346 },
347 fixedBgImage: {
348 type: "object",
349 default: null,
350 },
351 fixedBgOverlay: {
352 type: "string",
353 default: "#3f1763",
354 },
355 fixedBgOverlayOpacity: {
356 type: "number",
357 default: 0.8,
358 },
359 },
360
361 supports: {
362 align: ["wide", "full"],
363 spacing: {
364 padding: true,
365 margin: true,
366 },
367 color: {
368 background: true,
369 text: true,
370 },
371 },
372
373 edit: Edit,
374 save: Save,
375
376 // Bloc vide par défaut
377 innerBlocks: [],
378 template: [], // Pas de contenu par défaut
379 });
380
381 export default {};
382