PluginProbe ʕ •ᴥ•ʔ
FrontBlocks for Gutenberg/GeneratePress / 1.5.2
FrontBlocks for Gutenberg/GeneratePress v1.5.2
1.5.2 1.5.1 1.4.0 1.5.0 trunk 0.2.0 0.2.1 0.2.2 0.2.3 0.2.4 0.2.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 ci-artifacts
frontblocks / assets / shape-animations / frontblocks-shape-animation-option.js
frontblocks / assets / shape-animations Last commit date
frontblocks-shape-animation-option.js 2 months ago frontblocks-shape-animation-option.jsx 2 months ago frontblocks-shape-animations.css 2 months ago frontblocks-shape-animations.js 9 months ago
frontblocks-shape-animation-option.js
383 lines
1 "use strict";
2
3 function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
4 function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
5 function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6 function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
7 function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8 function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
9 (function () {
10 'use strict';
11
12 var __ = wp.i18n.__;
13 var addFilter = wp.hooks.addFilter;
14 var _wp$element = wp.element,
15 Fragment = _wp$element.Fragment,
16 useState = _wp$element.useState;
17 var InspectorControls = wp.blockEditor.InspectorControls;
18 var _wp$components = wp.components,
19 PanelBody = _wp$components.PanelBody,
20 ToggleControl = _wp$components.ToggleControl,
21 TextareaControl = _wp$components.TextareaControl,
22 Button = _wp$components.Button,
23 Notice = _wp$components.Notice;
24
25 /**
26 * Add custom SVG animation controls to GenerateBlocks Shape block.
27 */
28 function addShapeAnimationControls(BlockEdit) {
29 return function (props) {
30 // Only add controls to shape and icon blocks.
31 if (props.name !== 'generateblocks/shape' && props.name !== 'core/icon') {
32 return wp.element.createElement(BlockEdit, props);
33 }
34 var attributes = props.attributes,
35 setAttributes = props.setAttributes;
36 var _attributes$frblCusto = attributes.frblCustomSvgAnimationEnabled,
37 frblCustomSvgAnimationEnabled = _attributes$frblCusto === void 0 ? false : _attributes$frblCusto,
38 _attributes$frblCusto2 = attributes.frblCustomSvgAnimationJson,
39 frblCustomSvgAnimationJson = _attributes$frblCusto2 === void 0 ? '' : _attributes$frblCusto2;
40 var _useState = useState(''),
41 _useState2 = _slicedToArray(_useState, 2),
42 jsonError = _useState2[0],
43 setJsonError = _useState2[1];
44 var _useState3 = useState(null),
45 _useState4 = _slicedToArray(_useState3, 2),
46 jsonPreview = _useState4[0],
47 setJsonPreview = _useState4[1];
48
49 // Detect if JSON is Lottie format.
50 var isLottieJson = function isLottieJson(parsed) {
51 return parsed.v && parsed.fr && parsed.layers;
52 };
53
54 // Validate JSON.
55 var validateJson = function validateJson(jsonString) {
56 try {
57 var parsed = JSON.parse(jsonString);
58
59 // Check if it's Lottie format.
60 if (isLottieJson(parsed)) {
61 setJsonError('');
62 setJsonPreview({
63 type: 'lottie',
64 name: parsed.nm || 'Lottie Animation',
65 version: parsed.v,
66 framerate: parsed.fr,
67 duration: parsed.op ? (parsed.op / parsed.fr).toFixed(2) + 's' : 'N/A'
68 });
69 return true;
70 }
71
72 // Check if it's custom SVG + CSS format.
73 if (!parsed.svg) {
74 setJsonError(__('Missing "svg" property in JSON (or not valid Lottie format)', 'frontblocks'));
75 return false;
76 }
77 if (!parsed.animation || !parsed.animation.keyframes || !parsed.animation.name) {
78 setJsonError(__('Missing required animation properties (keyframes, name)', 'frontblocks'));
79 return false;
80 }
81 setJsonError('');
82 setJsonPreview({
83 type: 'css',
84 name: parsed.animation.name,
85 trigger: parsed.animation.trigger || 'load',
86 duration: parsed.animation.duration || '1s'
87 });
88 return true;
89 } catch (e) {
90 setJsonError(__('Invalid JSON format: ', 'frontblocks') + e.message);
91 return false;
92 }
93 };
94
95 // Handle JSON change.
96 var handleJsonChange = function handleJsonChange(value) {
97 setAttributes({
98 frblCustomSvgAnimationJson: value
99 });
100 if (value.trim()) {
101 validateJson(value);
102 } else {
103 setJsonError('');
104 setJsonPreview(null);
105 }
106 };
107
108 // Example JSON template.
109 var exampleJson = JSON.stringify({
110 "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"48\" height=\"48\" fill=\"currentColor\"><path d=\"M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5z\"/></svg>",
111 "animation": {
112 "name": "myCustomAnimation",
113 "keyframes": "@keyframes myCustomAnimation { 0% { transform: rotate(0deg) scale(1); } 50% { transform: rotate(180deg) scale(1.2); } 100% { transform: rotate(360deg) scale(1); } }",
114 "duration": "2s",
115 "delay": "0s",
116 "infinite": true,
117 "trigger": "load"
118 }
119 }, null, 2);
120 return wp.element.createElement(Fragment, {}, wp.element.createElement(BlockEdit, props), wp.element.createElement(InspectorControls, {}, wp.element.createElement(PanelBody, {
121 title: __('FrontBlocks Custom SVG Animation', 'frontblocks'),
122 initialOpen: false
123 }, wp.element.createElement(ToggleControl, {
124 label: __('Enable Custom SVG Animation', 'frontblocks'),
125 checked: frblCustomSvgAnimationEnabled,
126 onChange: function onChange(value) {
127 setAttributes({
128 frblCustomSvgAnimationEnabled: value
129 });
130 },
131 help: __('Replace SVG and add custom CSS animations via JSON', 'frontblocks')
132 }), frblCustomSvgAnimationEnabled && wp.element.createElement(Fragment, {}, wp.element.createElement('p', {
133 style: {
134 fontSize: '12px',
135 marginBottom: '8px',
136 color: '#757575'
137 }
138 }, __('Paste your JSON configuration below:', 'frontblocks')), wp.element.createElement(TextareaControl, {
139 label: __('JSON Configuration', 'frontblocks'),
140 value: frblCustomSvgAnimationJson,
141 onChange: handleJsonChange,
142 rows: 10,
143 help: __('JSON with svg and animation properties', 'frontblocks')
144 }), jsonError && wp.element.createElement(Notice, {
145 status: 'error',
146 isDismissible: false
147 }, jsonError), jsonPreview && wp.element.createElement(Notice, {
148 status: 'success',
149 isDismissible: false
150 }, __('✓ Valid JSON configuration', 'frontblocks')), wp.element.createElement('details', {
151 style: {
152 marginTop: '16px',
153 fontSize: '12px'
154 }
155 }, wp.element.createElement('summary', {
156 style: {
157 cursor: 'pointer',
158 fontWeight: '600',
159 marginBottom: '8px'
160 }
161 }, __('📋 Show example JSON', 'frontblocks')), wp.element.createElement('pre', {
162 style: {
163 background: '#f6f7f7',
164 padding: '12px',
165 borderRadius: '4px',
166 overflow: 'auto',
167 fontSize: '11px',
168 maxHeight: '300px'
169 }
170 }, exampleJson), wp.element.createElement(Button, {
171 isSecondary: true,
172 isSmall: true,
173 onClick: function onClick() {
174 setAttributes({
175 frblCustomSvgAnimationJson: exampleJson
176 });
177 validateJson(exampleJson);
178 },
179 style: {
180 marginTop: '8px'
181 }
182 }, __('Use this example', 'frontblocks'))), jsonPreview && wp.element.createElement('div', {
183 style: {
184 marginTop: '16px',
185 padding: '12px',
186 background: jsonPreview.type === 'lottie' ? '#e8f5e9' : '#f6f7f7',
187 borderRadius: '4px',
188 fontSize: '12px'
189 }
190 }, wp.element.createElement('strong', {}, jsonPreview.type === 'lottie' ? '🎬 Lottie Animation' : '🎨 CSS Animation'), wp.element.createElement('br'), wp.element.createElement('span', {}, __('Name:', 'frontblocks') + ' ' + (jsonPreview.name || 'N/A')), wp.element.createElement('br'), jsonPreview.type === 'lottie' ? wp.element.createElement('span', {}, __('Version:', 'frontblocks') + ' ' + jsonPreview.version + ', ' + __('FPS:', 'frontblocks') + ' ' + jsonPreview.framerate) : wp.element.createElement('span', {}, __('Trigger:', 'frontblocks') + ' ' + jsonPreview.trigger), wp.element.createElement('br'), wp.element.createElement('span', {}, __('Duration:', 'frontblocks') + ' ' + jsonPreview.duration))))));
191 };
192 }
193 addFilter('editor.BlockEdit', 'frontblocks/shape-custom-svg-animation-controls', addShapeAnimationControls);
194
195 /**
196 * Add preview and handle size/color inheritance in editor.
197 */
198 var createHigherOrderComponent = wp.compose.createHigherOrderComponent;
199 var _wp$element2 = wp.element,
200 useEffect = _wp$element2.useEffect,
201 useRef = _wp$element2.useRef;
202 var withShapeAnimationPreview = createHigherOrderComponent(function (BlockListBlock) {
203 return function (props) {
204 if (props.name !== 'generateblocks/shape' && props.name !== 'core/icon') {
205 return wp.element.createElement(BlockListBlock, props);
206 }
207 var attributes = props.attributes;
208 var isIconBlock = props.name === 'core/icon';
209 var _attributes$frblCusto3 = attributes.frblCustomSvgAnimationEnabled,
210 frblCustomSvgAnimationEnabled = _attributes$frblCusto3 === void 0 ? false : _attributes$frblCusto3,
211 _attributes$frblCusto4 = attributes.frblCustomSvgAnimationJson,
212 frblCustomSvgAnimationJson = _attributes$frblCusto4 === void 0 ? '' : _attributes$frblCusto4,
213 _attributes$styles = attributes.styles,
214 styles = _attributes$styles === void 0 ? {} : _attributes$styles;
215 var lottieContainerRef = useRef(null);
216 var lottieInstanceRef = useRef(null);
217 useEffect(function () {
218 if (!frblCustomSvgAnimationEnabled || !frblCustomSvgAnimationJson) {
219 return;
220 }
221 try {
222 var parsed = JSON.parse(frblCustomSvgAnimationJson);
223
224 // Check if it's Lottie.
225 var isLottie = parsed.v && parsed.fr && parsed.layers;
226 if (isLottie && typeof lottie !== 'undefined') {
227 // Wait for DOM to be ready.
228 setTimeout(function () {
229 var blockElement = document.querySelector('[data-block="' + props.clientId + '"]');
230 if (!blockElement) return;
231
232 // Find or create Lottie container in the block.
233 var lottieContainer = blockElement.querySelector('.frbl-lottie-preview');
234 if (!lottieContainer) {
235 // Find the inner element: .gb-shape for generateblocks/shape, .wp-block-icon for core/icon.
236 var containerSelector = isIconBlock ? '.wp-block-icon' : '.gb-shape';
237 var shapeElement = blockElement.querySelector(containerSelector);
238 if (shapeElement) {
239 // HIDE original SVG completely.
240 var originalSvg = shapeElement.querySelector('svg');
241 if (originalSvg) {
242 originalSvg.style.display = 'none';
243 }
244
245 // Create Lottie preview container.
246 lottieContainer = document.createElement('div');
247 lottieContainer.className = 'frbl-lottie-preview';
248 lottieContainer.style.cssText = 'position: relative; width: 100%; height: 100%; pointer-events: none;';
249
250 // Replace content with Lottie container.
251 shapeElement.appendChild(lottieContainer);
252 }
253 }
254 if (lottieContainer) {
255 // Clean up previous instance.
256 if (lottieInstanceRef.current) {
257 lottieInstanceRef.current.destroy();
258 }
259 lottieContainer.innerHTML = '';
260
261 // Apply size from Shape block styles.
262 var svgStyles = styles.svg || {};
263 if (svgStyles.width) {
264 lottieContainer.style.width = svgStyles.width;
265 }
266 if (svgStyles.height) {
267 lottieContainer.style.height = svgStyles.height;
268 }
269
270 // Set color BEFORE creating animation.
271 if (svgStyles.fill) {
272 lottieContainer.style.setProperty('--lottie-color', svgStyles.fill);
273 }
274
275 // Create Lottie animation.
276 var loop = parsed.animation && typeof parsed.animation.loop !== 'undefined' ? parsed.animation.loop : true;
277 var autoplay = parsed.animation && typeof parsed.animation.autoplay !== 'undefined' ? parsed.animation.autoplay : true;
278 var speed = parsed.animation && parsed.animation.speed ? parsed.animation.speed : 1;
279 var animation = lottie.loadAnimation({
280 container: lottieContainer,
281 renderer: 'svg',
282 loop: loop,
283 autoplay: autoplay,
284 animationData: parsed
285 });
286 animation.setSpeed(speed);
287 lottieInstanceRef.current = animation;
288
289 // Apply color IMMEDIATELY after creating animation.
290 applyColorToLottie(lottieContainer, svgStyles.fill);
291
292 // Apply color immediately after SVG is added to DOM.
293 animation.addEventListener('DOMLoaded', function () {
294 applyColorToLottie(lottieContainer, svgStyles.fill);
295 });
296
297 // Apply color on EVERY SINGLE FRAME (most aggressive approach).
298 animation.addEventListener('enterFrame', function () {
299 applyColorToLottie(lottieContainer, svgStyles.fill);
300 });
301
302 // Apply color multiple times immediately for instant coverage.
303 setTimeout(function () {
304 applyColorToLottie(lottieContainer, svgStyles.fill);
305 }, 0);
306 setTimeout(function () {
307 applyColorToLottie(lottieContainer, svgStyles.fill);
308 }, 10);
309 setTimeout(function () {
310 applyColorToLottie(lottieContainer, svgStyles.fill);
311 }, 20);
312 setTimeout(function () {
313 applyColorToLottie(lottieContainer, svgStyles.fill);
314 }, 50);
315 setTimeout(function () {
316 applyColorToLottie(lottieContainer, svgStyles.fill);
317 }, 100);
318 }
319 }, 300);
320 }
321 } catch (error) {
322 console.warn('FrontBlocks: Error rendering Lottie preview', error);
323 }
324
325 // Cleanup on unmount.
326 return function () {
327 if (lottieInstanceRef.current) {
328 lottieInstanceRef.current.destroy();
329 lottieInstanceRef.current = null;
330 }
331 };
332 }, [frblCustomSvgAnimationEnabled, frblCustomSvgAnimationJson, props.clientId, styles, isIconBlock]);
333 return wp.element.createElement(BlockListBlock, props);
334 };
335 }, 'withShapeAnimationPreview');
336
337 /**
338 * Apply color to Lottie animation SVG elements - AGGRESSIVELY.
339 */
340 function applyColorToLottie(container, color) {
341 if (!container) return;
342
343 // If no color provided, return early.
344 if (!color) return;
345
346 // Set CSS variable.
347 container.style.setProperty('--lottie-color', color);
348
349 // Find ALL SVG elements - be very aggressive.
350 var elements = container.querySelectorAll('svg *, [fill], [stroke]');
351 elements.forEach(function (element) {
352 // Get current fill and stroke.
353 var currentFill = element.getAttribute('fill');
354 var currentStroke = element.getAttribute('stroke');
355 var computedFill = window.getComputedStyle(element).fill;
356
357 // Apply to anything that has color.
358 if (currentFill && currentFill !== 'none' && currentFill !== 'transparent') {
359 element.setAttribute('fill', color);
360 element.style.setProperty('fill', color, 'important');
361 }
362
363 // Also check computed style.
364 if (computedFill && computedFill !== 'none' && computedFill !== 'transparent' && computedFill !== 'rgba(0, 0, 0, 0)') {
365 element.style.setProperty('fill', color, 'important');
366 }
367
368 // Also handle stroke if it exists.
369 if (currentStroke && currentStroke !== 'none' && currentStroke !== 'transparent') {
370 element.setAttribute('stroke', color);
371 element.style.setProperty('stroke', color, 'important');
372 }
373 });
374
375 // Also try to set fill on the SVG itself.
376 var svg = container.querySelector('svg');
377 if (svg) {
378 svg.style.setProperty('fill', color, 'important');
379 }
380 }
381 addFilter('editor.BlockListBlock', 'frontblocks/shape-animation-preview', withShapeAnimationPreview);
382 })();
383