PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.19
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.19
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.3.19, at app/Hooks/Handlers/BlockEditors/BlockEditor.php

250 lines 7.1 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 $this->enqueueStyles();
83 }
84 });
85
86 $blockArgs = [
87 'api_version' => 3,
88 'version' => 3,
89 'category' => 'fluent-cart',
90 'editor_script' => $this->getScriptName(),
91 'editor_css' => $this->getStyleName(),
92 'render_callback' => [$this, 'render_block'],
93 'provides_context' => $this->provideContext(),
94 'uses_context' => $this->useContext(),
95 'supports' => $this->supports()
96 ];
97
98 $ancestor = $this->ancestor();
99 if (!empty($ancestor)) {
100 $blockArgs['ancestor'] = $ancestor;
101 }
102
103 $attributes = $this->blockAttributes();
104 if (!empty($attributes)) {
105 $blockArgs['attributes'] = $attributes;
106 }
107
108 if ($this->skipInnerBlocks()) {
109 $blockArgs['skip_inner_blocks'] = true;
110 }
111
112 register_block_type($this->slugPrefix . '/' . static::getEditorName(), $blockArgs);
113
114 }
115
116 public function blockAttributes(): array
117 {
118 return [];
119 }
120
121 /**
122 * Register just the block type (render callback, attributes, supports)
123 * without editor scripts/styles. Used for email-only blocks that handle
124 * asset enqueuing through the email editor handler instead of globally.
125 */
126 public function registerBlockType(): void
127 {
128 $blockArgs = [
129 'api_version' => 3,
130 'category' => 'fluent-cart',
131 'render_callback' => [$this, 'render_block'],
132 'supports' => $this->supports(),
133 ];
134
135 $attributes = $this->blockAttributes();
136 if (!empty($attributes)) {
137 $blockArgs['attributes'] = $attributes;
138 }
139
140 register_block_type($this->slugPrefix . '/' . static::getEditorName(), $blockArgs);
141 }
142
143 public function supports(): array
144 {
145 return [
146 'renaming' => false,
147 'innerBlocks' => true,
148 'align' => true,
149 ];
150 }
151
152 public function ancestor(): array
153 {
154 return [];
155 }
156
157 public function provideContext()
158 {
159 return null;
160 }
161
162 public function useContext()
163 {
164 return null;
165 }
166
167 /**
168 * Whether to skip automatic inner block rendering in WP_Block::render().
169 *
170 * Override to return true in blocks that manually render their inner blocks
171 * in the render callback. This prevents WordPress from auto-rendering inner
172 * blocks before the callback runs (which causes double rendering and can
173 * trigger WP 6.x's empty-block script dequeue mechanism).
174 */
175 protected function skipInnerBlocks(): bool
176 {
177 return false;
178 }
179
180 public function render_block($attributes, $content, $block)
181 {
182 $prefix = '';
183 if (!self::$blockSupportStylesEnqueued && !is_admin()) {
184 $prefix = self::getBlockSupportFallbackStyles();
185 self::$blockSupportStylesEnqueued = true;
186 }
187
188 $attributes = Arr::wrap($attributes);
189 return $prefix . $this->render($attributes, $block, $content);
190 }
191
192 /**
193 * Ensure WordPress global styles (preset colors, typography, spacing) are available
194 * on the frontend. Some themes (e.g., Bricks) strip the global-styles stylesheet,
195 * which breaks block support classes like .has-vivid-red-color.
196 *
197 * This outputs a minimal inline fallback only if global styles aren't already loaded.
198 */
199 private static function getBlockSupportFallbackStyles(): string
200 {
201 // Skip if global styles are already loaded by the theme
202 if (wp_style_is('global-styles', 'done') || wp_style_is('global-styles', 'enqueued')) {
203 return '';
204 }
205
206 // Use WordPress API to get preset variables and class rules
207 if (function_exists('wp_get_global_stylesheet')) {
208 $css = wp_get_global_stylesheet(['variables', 'presets']);
209 if (!empty($css)) {
210 return '<style id="fluent-cart-block-supports-css">' . wp_strip_all_tags($css) . '</style>';
211 }
212 }
213
214 return '';
215 }
216
217
218 public function getStyles(): array
219 {
220 return [];
221 }
222
223
224 public static function make(): BlockEditor
225 {
226 return new static();
227 }
228
229 protected function isBlockEditor(): bool
230 {
231 if (!function_exists('get_current_screen')) {
232 require_once ABSPATH . '/wp-admin/includes/screen.php';
233 }
234 $current_screen = \get_current_screen();
235
236 // Check for regular block editor (posts/pages)
237 if ($current_screen instanceof \WP_Screen && $current_screen->is_block_editor()) {
238 return true;
239 }
240
241 // Check for site editor (theme templates)
242 $path = wp_unslash(App::request()->get('path'));
243 if (is_admin() && isset($path) && strpos($path, '/wp_template') !== false) {
244 return true;
245 }
246
247 return false;
248 }
249 }
250