PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.3
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.3
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / app / Hooks / Handlers / BlockEditors / BlockEditor.php

BlockEditor.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.5.3, at app/Hooks/Handlers/BlockEditors/BlockEditor.php

253 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Hooks\Handlers\BlockEditors;
4
5 use FluentCart\Api\Contracts\CanEnqueue;
6 use FluentCart\App\App;
7 use FluentCart\App\Vite;
8 use FluentCart\Framework\Support\Arr;
9 use FluentCart\Framework\Support\Str;
10
11 abstract class BlockEditor
12 {
13 use CanEnqueue;
14
15 const PARENT_BLOCK_DATA_NAME = 'fluent_cart_parent_block_data';
16
17 protected static string $editorName;
18 private static bool $isReactSupportAdded = false;
19 private static bool $blockSupportStylesEnqueued = false;
20
21 public function __construct()
22 {
23 $this->slugPrefix = App::config()->get('app.slug');
24 }
25
26 private function addReactSupport()
27 {
28 if (!static::$isReactSupportAdded) {
29 if (Vite::underDevelopment()) {
30 Vite::enqueueScript(
31 'react-support',
32 'admin/BlockEditor/ReactSupport.js',
33 ['wp-blocks', 'wp-components']
34 );
35 }
36 static::$isReactSupportAdded = true;
37 }
38 }
39
40 abstract public function render(array $shortCodeAttribute, $block = null);
41
42 protected function generateEnqueueSlug(): string
43 {
44 return Str::of(
45 $this->slugPrefix . '_' . static::getEditorName()
46 )->snake('')->replace('-', '_')->toString();
47 }
48
49 public static function getEditorName(): string
50 {
51 return static::$editorName;
52 }
53
54 public static function register()
55 {
56 add_action('init', [static::make(), 'init']);
57 }
58
59 private function enqueueAsset()
60 {
61 $this->addReactSupport();
62 $this->enqueueScripts();
63 $this->enqueueStyles();
64 $this->enqueueGlobalStyles();
65 }
66
67 private function enqueueGlobalStyles()
68 {
69 Vite::enqueueStyle(
70 'fluent-cart-global-block-editor',
71 'admin/BlockEditor/Components/style/fct-global-block-editor.scss',
72 );
73 }
74
75 public function init(): void
76 {
77 add_action('enqueue_block_editor_assets', function () {
78 $this->enqueueAsset(); // Enqueue block editor-specific JS/CSS here
79 });
80 add_action('enqueue_block_assets', function () {
81 if($this->isBlockEditor()) {
82 // Reset flag so styles also load inside the WP 6.3+ editor iframe
83 // (enqueue_block_editor_assets already set it on the outer admin page)
84 $this->isStylesLoaded = false;
85 $this->enqueueStyles();
86 }
87 });
88
89 $blockArgs = [
90 'api_version' => 3,
91 'version' => 3,
92 'category' => 'fluent-cart',
93 'editor_script' => $this->getScriptName(),
94 'editor_css' => $this->getStyleName(),
95 'render_callback' => [$this, 'render_block'],
96 'provides_context' => $this->provideContext(),
97 'uses_context' => $this->useContext(),
98 'supports' => $this->supports()
99 ];
100
101 $ancestor = $this->ancestor();
102 if (!empty($ancestor)) {
103 $blockArgs['ancestor'] = $ancestor;
104 }
105
106 $attributes = $this->blockAttributes();
107 if (!empty($attributes)) {
108 $blockArgs['attributes'] = $attributes;
109 }
110
111 if ($this->skipInnerBlocks()) {
112 $blockArgs['skip_inner_blocks'] = true;
113 }
114
115 register_block_type($this->slugPrefix . '/' . static::getEditorName(), $blockArgs);
116
117 }
118
119 public function blockAttributes(): array
120 {
121 return [];
122 }
123
124 /**
125 * Register just the block type (render callback, attributes, supports)
126 * without editor scripts/styles. Used for email-only blocks that handle
127 * asset enqueuing through the email editor handler instead of globally.
128 */
129 public function registerBlockType(): void
130 {
131 $blockArgs = [
132 'api_version' => 3,
133 'category' => 'fluent-cart',
134 'render_callback' => [$this, 'render_block'],
135 'supports' => $this->supports(),
136 ];
137
138 $attributes = $this->blockAttributes();
139 if (!empty($attributes)) {
140 $blockArgs['attributes'] = $attributes;
141 }
142
143 register_block_type($this->slugPrefix . '/' . static::getEditorName(), $blockArgs);
144 }
145
146 public function supports(): array
147 {
148 return [
149 'renaming' => false,
150 'innerBlocks' => true,
151 'align' => true,
152 ];
153 }
154
155 public function ancestor(): array
156 {
157 return [];
158 }
159
160 public function provideContext()
161 {
162 return null;
163 }
164
165 public function useContext()
166 {
167 return null;
168 }
169
170 /**
171 * Whether to skip automatic inner block rendering in WP_Block::render().
172 *
173 * Override to return true in blocks that manually render their inner blocks
174 * in the render callback. This prevents WordPress from auto-rendering inner
175 * blocks before the callback runs (which causes double rendering and can
176 * trigger WP 6.x's empty-block script dequeue mechanism).
177 */
178 protected function skipInnerBlocks(): bool
179 {
180 return false;
181 }
182
183 public function render_block($attributes, $content, $block)
184 {
185 $prefix = '';
186 if (!self::$blockSupportStylesEnqueued && !is_admin()) {
187 $prefix = self::getBlockSupportFallbackStyles();
188 self::$blockSupportStylesEnqueued = true;
189 }
190
191 $attributes = Arr::wrap($attributes);
192 return $prefix . $this->render($attributes, $block, $content);
193 }
194
195 /**
196 * Ensure WordPress global styles (preset colors, typography, spacing) are available
197 * on the frontend. Some themes (e.g., Bricks) strip the global-styles stylesheet,
198 * which breaks block support classes like .has-vivid-red-color.
199 *
200 * This outputs a minimal inline fallback only if global styles aren't already loaded.
201 */
202 private static function getBlockSupportFallbackStyles(): string
203 {
204 // Skip if global styles are already loaded by the theme
205 if (wp_style_is('global-styles', 'done') || wp_style_is('global-styles', 'enqueued')) {
206 return '';
207 }
208
209 // Use WordPress API to get preset variables and class rules
210 if (function_exists('wp_get_global_stylesheet')) {
211 $css = wp_get_global_stylesheet(['variables', 'presets']);
212 if (!empty($css)) {
213 return '<style id="fluent-cart-block-supports-css">' . wp_strip_all_tags($css) . '</style>';
214 }
215 }
216
217 return '';
218 }
219
220
221 public function getStyles(): array
222 {
223 return [];
224 }
225
226
227 public static function make(): BlockEditor
228 {
229 return new static();
230 }
231
232 protected function isBlockEditor(): bool
233 {
234 if (!function_exists('get_current_screen')) {
235 require_once ABSPATH . '/wp-admin/includes/screen.php';
236 }
237 $current_screen = \get_current_screen();
238
239 // Check for regular block editor (posts/pages)
240 if ($current_screen instanceof \WP_Screen && $current_screen->is_block_editor()) {
241 return true;
242 }
243
244 // Check for site editor (theme templates)
245 $path = wp_unslash(App::request()->get('path'));
246 if (is_admin() && isset($path) && strpos($path, '/wp_template') !== false) {
247 return true;
248 }
249
250 return false;
251 }
252 }
253