PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.4.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.4.0
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 / ShopApp / InnerBlocks / InnerBlocks.php

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

1,429 lines 60.0 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\ShopApp\InnerBlocks;
4
5 use FluentCart\Api\Contracts\CanEnqueue;
6 use FluentCart\Api\Resource\ShopResource;
7 use FluentCart\Api\StoreSettings;
8 use FluentCart\App\App;
9 use FluentCart\App\Helpers\Helper;
10 use FluentCart\App\Models\Product;
11 use FluentCart\App\Modules\Data\ProductDataSetup;
12 use FluentCart\App\Modules\Templating\TemplateLoader;
13 use FluentCart\App\Services\TemplateService;
14 use FluentCart\App\Services\Renderer\ShopAppRenderer;
15 use FluentCart\App\Services\Translations\TransStrings;
16 use FluentCart\Framework\Pagination\CursorPaginator;
17 use FluentCart\Framework\Support\Arr;
18 use FluentCart\App\Vite;
19
20 class InnerBlocks
21 {
22 use CanEnqueue;
23
24 public static $parentBlock = 'fluent-cart/products';
25
26 public array $blocks = [];
27
28 public static function textBlockSupport(): array
29 {
30 return [
31 'html' => false,
32 'align' => ['left', 'center', 'right'],
33 'typography' => [
34 'fontSize' => true,
35 'lineHeight' => true
36 ],
37 'spacing' => [
38 'margin' => true,
39 'padding' => true
40 ],
41 'color' => [
42 'text' => true,
43 ]
44 ];
45 }
46
47 public static function buttonBlockSupport(): array
48 {
49 return [
50 'html' => false,
51 'align' => ['left', 'center', 'right'],
52 'typography' => [
53 'fontSize' => true,
54 'lineHeight' => true,
55 'fontWeight' => true,
56 'textTransform' => true,
57 ],
58 'spacing' => [
59 'margin' => true,
60 'padding' => true,
61 ],
62 'color' => [
63 'text' => true,
64 'background' => true,
65 ],
66 'border' => [
67 'radius' => true,
68 'color' => true,
69 'width' => true,
70 ],
71 'shadow' => true,
72 ];
73 }
74
75
76 public static function register()
77 {
78 $self = new self();
79 $blocks = $self->getInnerBlocks();
80
81 foreach ($blocks as $block) {
82
83 register_block_type($block['slug'], [
84 'apiVersion' => 3,
85 'api_version' => 3,
86 'version' => 3,
87 'title' => $block['title'],
88 'category' => 'fluent-cart',
89 'parent' => array_merge($block['parent'] ?? [], [static::$parentBlock]),
90 'render_callback' => $block['callback'],
91 'supports' => Arr::get($block, 'supports', []),
92 'attributes' => Arr::get($block, 'attributes', []),
93 'uses_context' => Arr::get($block, 'uses_context', []),
94 ]);
95 }
96
97 add_action('enqueue_block_editor_assets', function () use ($self) {
98 $self->enqueueScripts();
99 });
100
101 //$self->enqueueStyles();
102 }
103
104 public function getInnerBlocks(): array
105 {
106 return [
107 [
108 'title' => __('Product Title', 'fluent-cart'),
109 'slug' => 'fluent-cart/shopapp-product-title',
110 'callback' => [$this, 'renderTitle'],
111 'component' => 'ProductTitleBlock',
112 'icon' => 'heading',
113 'supports' => static::textBlockSupport(),
114 'parent' => [
115 'fluent-cart/shopapp-product-loop',
116 'fluent-cart/shopapp-product-image',
117 'fluent-cart/product-info'
118 ],
119 ],
120
121 [
122 'title' => __('Product Excerpt', 'fluent-cart'),
123 'slug' => 'fluent-cart/shopapp-product-excerpt',
124 'callback' => [$this, 'renderExcerpt'],
125 'component' => 'ProductExcerptBlock',
126 'icon' => 'editor-code',
127 'supports' => static::textBlockSupport(),
128 'parent' => [
129 'fluent-cart/shopapp-product-loop',
130 'fluent-cart/shopapp-product-image'
131 ],
132 ],
133
134 [
135 'title' => __('Product Price', 'fluent-cart'),
136 'slug' => 'fluent-cart/shopapp-product-price',
137 'callback' => [$this, 'renderPrice'],
138 'parent' => [
139 'fluent-cart/shopapp-product-loop',
140 'fluent-cart/shopapp-product-image',
141 'fluent-cart/product-info',
142 'core/columns'
143 ],
144 'component' => 'ProductPriceBlock',
145 'icon' => 'editor-code',
146 'supports' => static::textBlockSupport(),
147 'uses_context' => [
148 'fluent-cart/price_format'
149 ]
150 ],
151
152 [
153 'title' => __('Product Image', 'fluent-cart'),
154 'slug' => 'fluent-cart/shopapp-product-image',
155 'parent' => [
156 'fluent-cart/shopapp-product-loop',
157 ],
158 'callback' => [$this, 'renderImage'],
159 'component' => 'ProductImageBlock',
160 'icon' => 'format-image'
161 ],
162
163 [
164 'title' => __('Product Button', 'fluent-cart'),
165 'slug' => 'fluent-cart/shopapp-product-buttons',
166 'callback' => [$this, 'renderButtons'],
167 'component' => 'ProductButtonBlock',
168 'icon' => 'admin-links',
169 'supports' => [
170 'html' => false,
171 'align' => ['left', 'center', 'right'],
172 'typography' => [
173 'fontSize' => true,
174 'lineHeight' => true
175 ],
176 'spacing' => [
177 'margin' => true,
178 'padding' => true
179 ],
180 'color' => [
181 'text' => true,
182 ],
183 '__experimentalBorder' => [
184 'radius' => true,
185 'color' => true,
186 'width' => true,
187 'style' => true,
188 ]
189 ],
190 'parent' => [
191 'fluent-cart/shopapp-product-loop',
192 'fluent-cart/shopapp-product-image'
193 ],
194 ],
195
196
197 //Product container -> filter, loop
198 [
199 'title' => __('Product Container', 'fluent-cart'),
200 'slug' => 'fluent-cart/shopapp-product-container',
201 'callback' => [$this, 'renderProductContainer'],
202 'component' => 'ProductContainerBlock',
203 'icon' => 'screenoptions',
204 'parent' => ['fluent-cart/products', 'core/column', 'core/group'],
205 'uses_context' => [
206 'fluent-cart/paginator',
207 'fluent-cart/per_page',
208 'fluent-cart/enable_filter',
209 'fluent-cart/product_box_grid_size',
210 'fluent-cart/view_mode',
211 'fluent-cart/filters',
212 'fluent-cart/default_filters',
213 'fluent-cart/order_type',
214 'fluent-cart/order_by',
215 'fluent-cart/live_filter',
216 'fluent-cart/price_format',
217 'fluent-cart/enable_wildcard_filter'
218 ],
219 'supports' => [
220 'align' => ['wide', 'full'],
221 'html' => false,
222 ],
223 'attributes' => array_merge(
224 \WP_Block_Type_Registry::get_instance()->get_registered('core/group')->attributes,
225 [
226 'customAttr' => ['type' => 'string'],
227 ]
228 )
229 ],
230
231 [
232 'title' => __('Product Filter', 'fluent-cart'),
233 'slug' => 'fluent-cart/shopapp-product-filter',
234 'callback' => [$this, 'renderFilter'],
235 'component' => 'ProductFilterBlock',
236 'icon' => 'editor-code',
237 'parent' => ['fluent-cart/shopapp-product-container'],
238 'uses_context' => [
239 'fluent-cart/paginator',
240 'fluent-cart/per_page',
241 'fluent-cart/enable_filter',
242 'fluent-cart/product_box_grid_size',
243 'fluent-cart/view_mode',
244 'fluent-cart/filters',
245 'fluent-cart/default_filters',
246 'fluent-cart/order_type',
247 'fluent-cart/order_by',
248 'fluent-cart/live_filter',
249 'fluent-cart/price_format',
250 'fluent-cart/enable_wildcard_filter'
251 ],
252 ],
253 [
254 'title' => __('Product View Switcher', 'fluent-cart'),
255 'slug' => 'fluent-cart/shopapp-product-view-switcher',
256 'callback' => [$this, 'renderFilterViewSwitcher'],
257 'component' => 'ProductFilterViewSwitcherBlock',
258 'icon' => 'editor-code',
259 'parent' => [
260 'fluent-cart/shopapp-product-action-container'
261 ],
262 'uses_context' => [
263 'fluent-cart/view_mode',
264 'fluent-cart/enable_filter',
265 'fluent-cart/default_filters',
266 ]
267 ],
268 [
269 'title' => __('Filter Sort By', 'fluent-cart'),
270 'slug' => 'fluent-cart/shopapp-product-filter-sort-by',
271 'callback' => [$this, 'renderFilterSortBy'],
272 'component' => 'ProductFilterSortByBlock',
273 'icon' => 'editor-code',
274 'parent' => [
275 'fluent-cart/shopapp-product-action-container'
276 ],
277 'uses_context' => [
278 'fluent-cart/view_mode',
279 'fluent-cart/enable_filter',
280 'fluent-cart/default_filters',
281 ]
282 ],
283 [
284 'title' => __('Product Action Container', 'fluent-cart'),
285 'slug' => 'fluent-cart/shopapp-product-action-container',
286 'callback' => [$this, 'renderProductActionContainer'],
287 'component' => 'ProductActionContainerBlock',
288 'icon' => 'editor-code',
289 'parent' => [
290 'fluent-cart/products'
291 ],
292 'uses_context' => [
293 'fluent-cart/enable_filter'
294 ]
295 ],
296 [
297 'title' => __('Product No Result', 'fluent-cart'),
298 'slug' => 'fluent-cart/shopapp-product-no-result',
299 'callback' => [$this, 'renderNoResultBlock'],
300 'component' => 'ProductNoResultBlock',
301 'icon' => 'editor-code',
302 'parent' => [
303 'fluent-cart/products',
304 'core/column',
305 'core/group',
306 'fluent-cart/shopapp-product-container'
307 ],
308 'uses_context' => [
309 'fluent-cart/paginator',
310 'fluent-cart/per_page',
311 'fluent-cart/enable_filter',
312 'fluent-cart/product_box_grid_size',
313 'fluent-cart/view_mode',
314 'fluent-cart/filters',
315 'fluent-cart/default_filters',
316 'fluent-cart/order_type',
317 'fluent-cart/order_by',
318 'fluent-cart/live_filter',
319 'fluent-cart/price_format',
320 'fluent-cart/enable_wildcard_filter'
321 ],
322 ],
323
324 [
325 'title' => __('Product Filter Search Box', 'fluent-cart'),
326 'slug' => 'fluent-cart/shopapp-product-filter-search-box',
327 'callback' => [$this, 'renderFilterSearchBox'],
328 'component' => 'ProductFilterSearchBlock',
329 'icon' => 'editor-code',
330 'parent' => ['fluent-cart/shopapp-product-filter'],
331 'uses_context' => [
332 'fluent-cart/paginator',
333 'fluent-cart/per_page',
334 'fluent-cart/enable_filter',
335 'fluent-cart/product_box_grid_size',
336 'fluent-cart/view_mode',
337 'fluent-cart/filters',
338 'fluent-cart/default_filters',
339 'fluent-cart/order_type',
340 'fluent-cart/order_by',
341 'fluent-cart/live_filter',
342 'fluent-cart/price_format',
343 'fluent-cart/enable_wildcard_filter'
344 ]
345 ],
346
347 [
348 'title' => __('Product Filter Filters', 'fluent-cart'),
349 'slug' => 'fluent-cart/shopapp-product-filter-filters',
350 'callback' => [$this, 'renderFilterFilters'],
351 'component' => 'ProductFilterFilters',
352 'icon' => 'editor-code',
353 'parent' => ['fluent-cart/shopapp-product-filter']
354 ],
355 [
356 'title' => __('Product Filter Button', 'fluent-cart'),
357 'slug' => 'fluent-cart/shopapp-product-filter-button',
358 'callback' => [$this, 'renderFilterButton'],
359 'component' => 'ProductFilterButtonBlock',
360 'icon' => 'editor-code',
361 'parent' => ['fluent-cart/shopapp-product-filter'],
362 'uses_context' => [
363 'fluent-cart/live_filter'
364 ]
365 ],
366 [
367 'title' => __('Product Filter Apply Button', 'fluent-cart'),
368 'slug' => 'fluent-cart/shopapp-product-filter-apply-button',
369 'callback' => [$this, 'renderFilterApplyButton'],
370 'component' => 'ProductFilterApplyButtonBlock',
371 'icon' => 'editor-code',
372 'parent' => ['fluent-cart/shopapp-product-filter-button'],
373 'supports' => static::textBlockSupport()
374 ],
375 [
376 'title' => __('Product Filter Reset Button', 'fluent-cart'),
377 'slug' => 'fluent-cart/shopapp-product-filter-reset-button',
378 'callback' => [$this, 'renderFilterResetButton'],
379 'component' => 'ProductFilterResetButtonBlock',
380 'icon' => 'editor-code',
381 'parent' => ['fluent-cart/shopapp-product-filter-button'],
382 'supports' => static::buttonBlockSupport()
383 ],
384
385 [
386 'title' => __('Product Loop', 'fluent-cart'),
387 'slug' => 'fluent-cart/shopapp-product-loop',
388 'callback' => [$this, 'renderProductLoop'],
389 'component' => 'ProductLoopBlock',
390 'icon' => 'screenoptions',
391 'parent' => ['fluent-cart/products', 'core/column', 'core/group'],
392 'supports' => [
393 'align' => ['wide', 'full'],
394 'html' => false,
395 ],
396 'attributes' => array_merge(
397 \WP_Block_Type_Registry::get_instance()->get_registered('core/group')->attributes,
398 [
399 'customAttr' => ['type' => 'string'],
400 ]
401 ),
402 'uses_context' => [
403 'fluent-cart/paginator',
404 'fluent-cart/per_page',
405 'fluent-cart/enable_filter',
406 'fluent-cart/product_box_grid_size',
407 'fluent-cart/view_mode',
408 'fluent-cart/filters',
409 'fluent-cart/default_filters',
410 'fluent-cart/order_type',
411 'fluent-cart/order_by',
412 'fluent-cart/live_filter',
413 'fluent-cart/price_format',
414 'fluent-cart/enable_wildcard_filter'
415 ],
416 ],
417
418 [
419 'title' => __('Product Paginator', 'fluent-cart'),
420 'slug' => 'fluent-cart/product-paginator',
421 'callback' => [$this, 'renderProductPaginator'],
422 'component' => 'InnerBlocks',
423 'icon' => 'editor-code',
424 'supports' => static::textBlockSupport(),
425 'uses_context' => [
426 'fluent-cart/paginator',
427 'fluent-cart/per_page'
428 ]
429 ],
430 [
431 'title' => __('Product Paginator Info', 'fluent-cart'),
432 'slug' => 'fluent-cart/product-paginator-info',
433 'callback' => [$this, 'renderProductPaginatorInfo'],
434 'parent' => [
435 'fluent-cart/product-paginator'
436 ],
437 'component' => 'ProductPaginatorInfoBlock',
438 'icon' => 'editor-code',
439 'supports' => static::textBlockSupport(),
440 'uses_context' => [
441 'fluent-cart/paginator',
442 'fluent-cart/per_page',
443 'fluent-cart/enable_filter',
444 'fluent-cart/product_box_grid_size',
445 'fluent-cart/view_mode',
446 'fluent-cart/filters',
447 'fluent-cart/default_filters',
448 'fluent-cart/order_type',
449 'fluent-cart/order_by',
450 'fluent-cart/live_filter',
451 'fluent-cart/price_format',
452 'fluent-cart/enable_wildcard_filter'
453 ],
454 ],
455 [
456 'title' => __('Product Paginator Number', 'fluent-cart'),
457 'slug' => 'fluent-cart/product-paginator-number',
458 'callback' => [$this, 'renderProductPaginatorNumber'],
459 'parent' => [
460 'fluent-cart/product-paginator'
461 ],
462 'component' => 'ProductPaginatorNumberBlock',
463 'icon' => 'editor-code',
464 'supports' => static::textBlockSupport(),
465 'uses_context' => [
466 'fluent-cart/paginator',
467 'fluent-cart/per_page',
468 'fluent-cart/enable_filter',
469 'fluent-cart/product_box_grid_size',
470 'fluent-cart/view_mode',
471 'fluent-cart/filters',
472 'fluent-cart/default_filters',
473 'fluent-cart/order_type',
474 'fluent-cart/order_by',
475 'fluent-cart/live_filter',
476 'fluent-cart/price_format',
477 'fluent-cart/enable_wildcard_filter'
478 ],
479 ],
480 [
481 'title' => __('Product Loader', 'fluent-cart'),
482 'slug' => 'fluent-cart/shopapp-product-loader',
483 'callback' => [$this, 'renderProductLoaderBlock'],
484 'component' => 'ProductLoaderBlock',
485 'icon' => 'editor-code',
486 'parent' => [
487 'fluent-cart/products',
488 'core/column',
489 'core/group',
490 'fluent-cart/shopapp-product-container'
491 ],
492 ],
493 [
494 'title' => __('Product Spinner', 'fluent-cart'),
495 'slug' => 'fluent-cart/shopapp-product-spinner',
496 'callback' => [$this, 'renderProductSpinnerBlock'],
497 'component' => 'ProductSpinnerBlock',
498 'icon' => 'editor-code',
499 'parent' => [
500 'fluent-cart/products',
501 'core/column',
502 'core/group',
503 'fluent-cart/shopapp-product-loader'
504 ],
505 ],
506 ];
507 }
508
509
510 public function getProductFromBlockContext($block)
511 {
512 return fluent_cart_get_current_product();
513 }
514
515 public function renderFilter($attributes, $content, $block)
516 {
517 $enableFilter = Arr::get($block->context, 'fluent-cart/enable_filter', false);
518 $filters = Arr::get($block->context, 'fluent-cart/filters', []);
519
520 if (!$enableFilter) {
521 return '';
522 }
523
524 $renderer = new \FluentCart\App\Services\Renderer\ProductFilterRender($filters);
525 $innerBlocksContent = '';
526 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
527 $blockContext = $block->context;
528 $innerBlocksContent .= '<div class="fct-shop-filter-wrapper" data-fluent-cart-shop-app-filter-wrapper>';
529 $innerBlocksContent .= '<div class="fct-shop-filter-wrapper-inner">';
530 $innerBlocksContent .= '<form class="fct-shop-filter-form" data-fluent-cart-product-filter-form>';
531 foreach ($block->inner_blocks as $inner_block) {
532 if (isset($inner_block->parsed_block)) {
533 $innerContext = array_merge($inner_block->context, $blockContext);
534 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
535 $instance->context['filter_renderer'] = $renderer;
536 $innerBlocksContent .= $instance->render();
537 }
538 }
539 $innerBlocksContent .= '</form>';
540 $innerBlocksContent .= '</div>';
541 $innerBlocksContent .= '</div>';
542
543 }
544 return $innerBlocksContent;
545 }
546
547 public function renderFilterSortBy($attributes, $content, $block)
548 {
549 $viewMode = Arr::get($block->context, 'fluent-cart/view_mode', 'grid');
550 $enableFilter = Arr::get($block->context, 'fluent-cart/enable_filter', false);
551 $orderBy = Arr::get($block->context, 'fluent-cart/order_by', 'id');
552 $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC');
553
554 $allProducts = $this->getInitialProducts($block);
555
556 if (!$enableFilter) {
557 return '';
558 }
559
560 ob_start();
561 (new ShopAppRenderer($allProducts, [
562 'view_mode' => $viewMode,
563 'custom_filters' => [
564 'enabled' => $enableFilter,
565 ],
566 'default_filters' => [
567 'order_by' => $orderBy,
568 'order_type' => $orderType
569 ],
570 'enabled' => $enableFilter,
571 ]))->renderSortByFilter();
572 return ob_get_clean();
573
574 }
575
576 public function renderProductActionContainer($attributes, $content, $block)
577 {
578 $innerBlocksContent = '';
579
580 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
581 $blockContext = $block->context;
582 $innerBlocksContent .= '<div class="fct-product-action-container fct-shop-view-switcher-wrap">';
583 foreach ($block->inner_blocks as $inner_block) {
584 if (isset($inner_block->parsed_block)) {
585 $innerContext = array_merge($inner_block->context, $blockContext);
586 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
587 $innerBlocksContent .= $instance->render();
588 }
589 }
590 $innerBlocksContent .= '</div>';
591 }
592 return $innerBlocksContent;
593 }
594
595 public function renderFilterViewSwitcher($attributes, $content, $block)
596 {
597
598 $viewMode = Arr::get($block->context, 'fluent-cart/view_mode', 'grid');
599 $enableFilter = Arr::get($block->context, 'fluent-cart/enable_filter', false);
600 $allProducts = $this->getInitialProducts($block);
601 ob_start();
602 (new ShopAppRenderer($allProducts, [
603 'view_mode' => $viewMode,
604 'custom_filters' => [
605 'enabled' => $enableFilter
606 ]
607 ]))->renderViewSwitcherButton();
608 return ob_get_clean();
609 }
610
611 public function renderFilterSearchBox($attributes, $content, $block): string
612 {
613 $filterRenderer = Arr::get($block->context, 'filter_renderer');
614 $wildcardFilter = Arr::get($block->context, 'fluent-cart/enable_wildcard_filter', false);
615
616 if (!$filterRenderer || !$wildcardFilter) {
617 return '';
618 }
619 ob_start();
620 $filterRenderer->renderSearch();
621 return ob_get_clean();
622 }
623
624 public function renderFilterFilters($attributes, $content, $block)
625 {
626 $filterRenderer = Arr::get($block->context, 'filter_renderer');
627 if (!$filterRenderer) {
628 return '';
629 }
630 ob_start();
631 $filterRenderer->renderOptions();
632 return ob_get_clean();
633 }
634
635 public function renderFilterButton($attributes, $content, $block)
636 {
637 $liveFilter = Arr::get($block->context, 'fluent-cart/live_filter', true);
638
639 if ($liveFilter) {
640 return '';
641 }
642
643 $innerBlocksContent = '';
644 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
645 $blockContext = $block->context;
646 $innerBlocksContent .= '<div class="fct-shop-filter-item"><div class="fct-shop-button-group">';
647 foreach ($block->inner_blocks as $inner_block) {
648 if (isset($inner_block->parsed_block)) {
649 $innerContext = array_merge($inner_block->context, $blockContext);
650 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
651 $innerBlocksContent .= $instance->render();
652 }
653 }
654 $innerBlocksContent .= '</div></div>';
655 }
656 return $innerBlocksContent;
657 }
658
659 public function renderFilterApplyButton($attributes, $content, $block)
660 {
661 $wrapper_attribute = get_block_wrapper_attributes([
662 'class' => 'fct-shop-apply-filter-button',
663 ]);
664
665 return sprintf('<button %s>%s</button>',
666 $wrapper_attribute,
667 esc_html__('Apply Filter', 'fluent-cart')
668 );
669 }
670
671 public function renderFilterResetButton($attributes, $content, $block)
672 {
673
674 $wrapper_attribute = get_block_wrapper_attributes([
675 'class' => 'fct-shop-reset-filter-button',
676 'data-fluent-cart-shop-app-reset-button' => '',
677 ]);
678
679 return sprintf(
680 '<button %s>
681 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none">
682 <path d="M14.2501 9.75V15.75M9.75012 9.75V15.75M20.2498 5.25L3.74976 5.25001M18.7501 5.25V19.5C18.7501 19.6989 18.6711 19.8897 18.5305 20.0303C18.3898 20.171 18.199 20.25 18.0001 20.25H6.00012C5.80121 20.25 5.61044 20.171 5.46979 20.0303C5.32914 19.8897 5.25012 19.6989 5.25012 19.5V5.25M15.7501 5.25V3.75C15.7501 3.35218 15.5921 2.97064 15.3108 2.68934C15.0295 2.40804 14.6479 2.25 14.2501 2.25H9.75012C9.3523 2.25 8.97077 2.40804 8.68946 2.68934C8.40816 2.97064 8.25012 3.35218 8.25012 3.75V5.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
683 </svg>
684 </button>',
685 $wrapper_attribute
686 );
687 }
688
689 public function renderNoResultBlock($attributes, $content, $block)
690 {
691 $innerBlocksContent = '';
692 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
693 $blockContext = $block->context;
694 $innerBlocksContent .= '<div class="fluent-cart-shop-no-result-found hide" data-fluent-cart-shop-no-result-found="">';
695 foreach ($block->inner_blocks as $inner_block) {
696 if (isset($inner_block->parsed_block)) {
697 $innerContext = array_merge($inner_block->context, $blockContext);
698 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
699 $innerBlocksContent .= $instance->render();
700 }
701 }
702 $innerBlocksContent .= '</div>';
703 }
704 return $innerBlocksContent;
705 }
706
707 public function renderProductLoaderBlock($attributes, $content, $block)
708 {
709 $innerBlocksContent = '';
710 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
711 $blockContext = $block->context;
712 $innerBlocksContent .= '<div class="fluent-cart-product-loader loader-hidden" data-fluent-cart-product-loader>';
713 foreach ($block->inner_blocks as $inner_block) {
714 if (isset($inner_block->parsed_block)) {
715 $innerContext = array_merge($inner_block->context, $blockContext);
716 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
717 $innerBlocksContent .= $instance->render();
718 }
719 }
720 $innerBlocksContent .= '</div>';
721 }
722 return $innerBlocksContent;
723 }
724
725 public function renderTitle($attributes, $content, $block): string
726 {
727
728 $product = $this->getProductFromBlockContext($block);
729
730 $isLink = Arr::get($attributes, 'isLink', true);
731 $target = Arr::get($attributes, 'linkTarget', '_self');
732
733
734 if (empty($product)) {
735 return 'not found';
736 }
737
738 $wrapper_attributes = get_block_wrapper_attributes(
739 [
740 'class' => 'fct-product-card-title wc-block-grid__product-title',
741 ]
742 );
743
744 $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product);
745 ob_start();
746 $render->renderTitle($wrapper_attributes, [
747 'isLink' => $isLink,
748 'target' => $target,
749 ]);
750 return ob_get_clean();
751 }
752
753 public function renderPrice($attributes, $content, $block): string
754 {
755 $product = $this->getProductFromBlockContext($block);
756
757 $defaultPriceFormat = 'starts_from';
758 if (is_singular('fluent-products') && TemplateService::getCurrentFcPageType() === 'single_product') {
759 $defaultPriceFormat = 'range';
760 }
761 $priceFormat = Arr::get($block->context, 'fluent-cart/price_format', $defaultPriceFormat);
762
763 if (empty($product)) {
764 return '';
765 }
766
767 $wrapper_attributes = get_block_wrapper_attributes(
768 [
769 'class' => 'fct-product-card-prices',
770 ]
771 );
772
773 $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product, [
774 'price_format' => $priceFormat,
775 ]);
776 ob_start();
777 $render->renderPrices($wrapper_attributes);
778 return ob_get_clean();
779 }
780
781 public function renderImage($attributes, $content, $block): string
782 {
783 $product = $this->getProductFromBlockContext($block);
784 if (empty($product)) {
785 return '';
786 }
787 $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product);
788 $renderedImage = '';
789
790
791 $innerBlocksContent = '';
792 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
793 $blockContext = $block->context;
794 $innerBlocksContent .= '<div class="fct-product-image-inner-blocks">';
795 foreach ($block->inner_blocks as $inner_block) {
796 if (isset($inner_block->parsed_block)) {
797 $innerContext = array_merge($inner_block->context, $blockContext);
798 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
799 $innerBlocksContent .= $instance->render();
800 }
801 }
802 $innerBlocksContent .= '</div>';
803 }
804 ob_start();
805 $render->renderProductImage();
806 $renderedImage = ob_get_clean();
807
808 if (!empty($innerBlocksContent)) {
809 return sprintf(
810 "<div class='fct-product-image-wrap' style='position: relative; overflow: clip;'>
811 %s
812 <div class='fct-product-image-inner-blocks'>
813 %s
814 </div>
815 </div>",
816 $renderedImage,
817 $innerBlocksContent
818 );
819 }
820
821 return $renderedImage;
822 }
823
824 public function renderButtons($attributes, $content, $block): string
825 {
826 $product = $this->getProductFromBlockContext($block);
827 if (empty($product)) {
828 return '';
829 }
830 $wrapper_attributes = get_block_wrapper_attributes();
831 $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product);
832 ob_start();
833 $render->showBuyButton($wrapper_attributes);
834 return ob_get_clean();
835 }
836
837 public function renderExcerpt($attributes, $content, $block): string
838 {
839 $product = $this->getProductFromBlockContext($block);
840 if (empty($product) || empty($product->post_excerpt)) {
841 return '';
842 }
843 $wrapper_attributes = get_block_wrapper_attributes(
844 [
845 'class' => 'fct-product-card-excerpt',
846 ]
847 );
848 $render = new \FluentCart\App\Services\Renderer\ProductCardRender($product);
849 ob_start();
850 $render->renderExcerpt($wrapper_attributes);
851 return ob_get_clean();
852 }
853
854 public function renderProductLoop($attributes, $content, $block): string
855 {
856
857 $lastChanged = Arr::get($attributes,'last_changed');
858 $clientId = Arr::get($attributes, 'wp_client_id', '');
859 if ($clientId) {
860 $blockId = 'fct_product_loop_client_' . $attributes['wp_client_id'];
861 $existingTransient = get_transient($blockId);
862 $needsReset = false;
863 if (empty($existingTransient) || !is_array($existingTransient)) {
864 $needsReset = true;
865 } else {
866 // Check if last_changed has changed
867 if (!empty($lastChanged) && $lastChanged !== $existingTransient['last_changed']) {
868 $needsReset = true;
869 } else {
870 // Check age of the transient
871 $setAt = strtotime($existingTransient['set_at'] ?? '');
872 if ($setAt && (time() - $setAt) > (4 * DAY_IN_SECONDS)) {
873 $needsReset = true;
874 }
875 }
876 }
877
878 if ($needsReset || true) {
879 $raw_block_markup = $block->parsed_block['blockName']
880 ? serialize_block($block->parsed_block)
881 : '';
882 $newTransient = [
883 'set_at' => gmdate('Y-m-d H:i:s'),
884 'last_changed' => $lastChanged,
885 'markup' => $raw_block_markup,
886 ];
887 // Store for 7 days
888 set_transient($blockId, $newTransient, 7 * DAY_IN_SECONDS);
889 }
890 }
891
892 $products = $this->getInitialProducts($block);
893 $products = $products['products'];
894
895 $parsed = '';
896 if ($products instanceof CursorPaginator) {
897 $parsed = wp_parse_args(wp_parse_url($products->nextPageUrl(), PHP_URL_QUERY));
898 }
899
900 ProductDataSetup::setProductsCache($products);
901
902 $innerBlocksContent = '';
903 $blockContext = $block->context;
904
905 foreach ($products as $key => $product) {
906 setup_postdata($product->ID);
907 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
908
909
910 if ($key === 0) {
911 //add attribute provider with value wp-block
912 $innerBlocksContent .= '<div class="fct-product-card" data-template-provider="wp-block" data-fct-product-card data-fluent-cart-cursor="' . esc_attr(Arr::get($parsed, 'cursor', '')) . '" data-fluent-client-id="' . esc_attr($clientId) . '">';
913 } else {
914 $innerBlocksContent .= '<div class="fct-product-card" data-fct-product-card>';
915 }
916
917 foreach ($block->inner_blocks as $inner_block) {
918 if (isset($inner_block->parsed_block)) {
919 $innerContext = array_merge($inner_block->context, $blockContext);
920 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
921 $innerBlocksContent .= $instance->render();
922 }
923 }
924 $innerBlocksContent .= '</div>';
925 }
926 }
927
928 wp_reset_postdata();
929
930 if ($products->count() === 0) {
931 add_action('wp_footer', function () {
932 ?>
933 <script>
934 document.addEventListener("DOMContentLoaded", function () {
935 const noResultFound = document.querySelector("[data-fluent-cart-shop-no-result-found]");
936 if (noResultFound) {
937 noResultFound.classList.remove("hide");
938 }
939 });
940 </script>
941 <?php
942 });
943 }
944 return $innerBlocksContent;
945 }
946
947 public function renderProductContainer($attributes, $content, $block): string
948 {
949 $isFilterEnabled = Arr::get($block->context, 'fluent-cart/enable_filter', false);
950 $ff = Arr::get($block->context, 'fluent-cart/filters', []);
951 $paginator = Arr::get($block->context, 'fluent-cart/paginator', 'scroll');
952 $productBoxSize = Arr::get($block->context, 'fluent-cart/product_box_grid_size', 4);
953 $perPage = Arr::get($block->context, 'fluent-cart/per_page', 10);
954 $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC');
955 $orderBy = Arr::get($block->context, 'fluent-cart/order_by', 'id');
956 $liveFilter = Arr::get($block->context, 'fluent-cart/live_filter', true);
957 $priceFormat = Arr::get($block->context, 'fluent-cart/price_format', 'starts_from');
958 $viewMode = Arr::get($block->context, 'fluent-cart/view_mode', 'grid');
959 $defaultFilters = Arr::get($block->context, 'fluent-cart/default_filters', []);
960 $innerBlocksContent = '';
961
962 if (is_main_query()) {
963 $taxonomy = TemplateLoader::$currentTaxonomy;
964 if ($taxonomy) {
965 $defaultFilters = [
966 $taxonomy->taxonomy => [$taxonomy->term_id],
967 'enabled' => true
968 ];
969 }
970 }
971
972 Vite::enqueueStyle(
973 'fluentcart-add-to-cart-btn-css',
974 'public/buttons/add-to-cart/style/style.scss'
975 );
976
977 Vite::enqueueStyle(
978 'fluentcart-direct-checkout-btn-css',
979 'public/buttons/direct-checkout/style/style.scss'
980 );
981
982 Vite::enqueueStyle(
983 'fluentcart-single-product-page-css',
984 'public/single-product/single-product.scss',
985 );
986
987 Vite::enqueueStyle(
988 'fluentcart-zoom-css',
989 'public/single-product/xzoom/xzoom.css',
990 );
991
992 Vite::enqueueStyle(
993 'fluentcart-similar-product-page-css',
994 'public/single-product/similar-product.scss',
995 );
996
997 Vite::enqueueStyle(
998 'fluentcart-product-card-page-css',
999 'public/product-card/style/product-card.scss',
1000 );
1001
1002 Vite::enqueueStyle(
1003 'fluent-cart-sold-out-badge',
1004 'admin/BlockEditor/SoldOutBadge/style/sold-out-badge-block-editor.scss'
1005 );
1006
1007 Vite::enqueueStyle(
1008 'fluent-cart-sale-badge',
1009 'admin/BlockEditor/SaleBadge/style/sale-badge-block-editor.scss'
1010 );
1011
1012 Vite::enqueueScript(
1013 'fluentcart-zoom-js',
1014 'public/single-product/xzoom/xzoom.js',
1015 []
1016 );
1017
1018 Vite::enqueueScript(
1019 'fluentcart-single-product-page-js',
1020 'public/single-product/SingleProduct.js',
1021 []
1022 )->with([
1023 'fluentcart_single_product_vars' => [
1024 'trans' => TransStrings::singleProductPageString(),
1025 'cart_button_text' => apply_filters('fluent_cart/product/add_to_cart_text', __('Add To Cart', 'fluent-cart'), []),
1026 // App::storeSettings()->get('cart_button_text', __('Add to Cart', 'fluent-cart')),
1027 'out_of_stock_button_text' => apply_filters('fluent_cart/product/out_of_stock_text', __('Not Available', 'fluent-cart'), []),
1028 'in_stock_status' => Helper::IN_STOCK,
1029 'out_of_stock_status' => Helper::OUT_OF_STOCK,
1030 'enable_image_zoom' => (new StoreSettings())->get('enable_image_zoom_in_single_product'),
1031 'enable_image_zoom_in_modal' => (new StoreSettings())->get('enable_image_zoom_in_modal')
1032 ]
1033 ]);
1034
1035 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
1036 $blockContext = $block->context;
1037 // start wrapper
1038 $innerBlocksContent .= sprintf(
1039 '<div class="fct-products-wrapper-inner %s%s" data-fluent-cart-product-wrapper-inner
1040 data-per-page="%s"
1041 data-order-type="%s"
1042 data-order-by="%s"
1043 data-live-filter="%s"
1044 data-price-format="%s"
1045 data-paginator="%s"
1046 data-default-filters="%s"
1047 >',
1048 ($viewMode === 'list' ? 'mode-list' : 'mode-grid'),
1049 (!$isFilterEnabled ? ' fct-full-container-width' : ''),
1050 esc_attr($perPage),
1051 esc_attr($orderType),
1052 esc_attr($orderBy),
1053 esc_attr($liveFilter),
1054 esc_attr($priceFormat),
1055 esc_attr($paginator),
1056 esc_attr(wp_json_encode($defaultFilters))
1057 );
1058
1059 foreach ($block->inner_blocks as $inner_block) {
1060 if ('fluent-cart/shopapp-product-filter' === $inner_block->name) {
1061 if (!$isFilterEnabled) {
1062 continue;
1063 }
1064 }
1065
1066 $isProductLoop = 'fluent-cart/shopapp-product-loop' === $inner_block->name;
1067
1068 if ($isProductLoop) {
1069 $styleAttr = $productBoxSize ? ' style="--grid-columns: ' . esc_attr($productBoxSize) . ';"' : '';
1070 $innerBlocksContent .= '<div class="fct-products-container grid-columns-' . esc_attr($productBoxSize) . '" data-fluent-cart-shop-app-product-list ' . $styleAttr . '>';
1071 }
1072
1073 if (isset($inner_block->parsed_block)) {
1074 $innerContext = array_merge($inner_block->context, $blockContext);
1075 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
1076 $innerBlocksContent .= $instance->render();
1077 }
1078
1079 if ($isProductLoop) {
1080 $innerBlocksContent .= '</div>';
1081 }
1082 }
1083
1084 // close wrapper
1085 $innerBlocksContent .= '</div>';
1086
1087 }
1088 return $innerBlocksContent;
1089 }
1090
1091 protected function getLocalizationKey(): string
1092 {
1093 return 'fluent_cart_products_inner_blocks';
1094 }
1095
1096 public function localizeData(): array
1097 {
1098 return [
1099 $this->getLocalizationKey() => [
1100 'blocks' => Arr::except($this->getInnerBlocks(), ['callback']),
1101 ],
1102 'fluentcart_single_product_vars' => [
1103 'trans' => TransStrings::singleProductPageString(),
1104 'cart_button_text' => apply_filters('fluent_cart/product/add_to_cart_text', __('Add To Cart', 'fluent-cart'), []),
1105 // App::storeSettings()->get('cart_button_text', __('Add to Cart', 'fluent-cart')),
1106 'out_of_stock_button_text' => apply_filters('fluent_cart/product/out_of_stock_text', __('Not Available', 'fluent-cart'), []),
1107 'in_stock_status' => Helper::IN_STOCK,
1108 'out_of_stock_status' => Helper::OUT_OF_STOCK,
1109 'enable_image_zoom' => (new StoreSettings())->get('enable_image_zoom_in_single_product'),
1110 'enable_image_zoom_in_modal' => (new StoreSettings())->get('enable_image_zoom_in_modal')
1111 ]
1112 ];
1113 }
1114
1115 public function getStyles(): array
1116 {
1117 return ['public/single-product/single-product.scss'];
1118 }
1119
1120 public function getScripts(): array
1121 {
1122 $scripts = [
1123 [
1124 'source' => 'admin/BlockEditor/ReactSupport.js',
1125 'dependencies' => ['wp-blocks', 'wp-components']
1126 ],
1127 [
1128 'source' => 'admin/BlockEditor/ShopApp/InnerBlocks/InnerBlocks.jsx',
1129 'dependencies' => ['wp-blocks', 'wp-components', 'wp-block-editor']
1130 ],
1131 // [
1132 // 'source'=> 'public/single-product/SingleProduct.js',
1133 // 'dependencies' => []
1134 // ]
1135 ];
1136
1137 if (App::isDevMode() || true) {
1138 $scripts[] = [
1139 'source' => 'admin/BlockEditor/ReactSupport.js',
1140 'dependencies' => ['wp-blocks', 'wp-components']
1141 ];
1142 }
1143 return $scripts;
1144 }
1145
1146 protected function generateEnqueueSlug(): string
1147 {
1148 return 'fluent_cart_shop_app_inner_blocks';
1149 }
1150
1151 public function renderProductPaginator($attributes, $content, $block)
1152 {
1153 $paginatorType = Arr::get($block->context, 'fluent-cart/paginator', 'scroll');
1154 // $paginatorType = 'numbers';
1155 $innerBlocksContent = '';
1156
1157 if ($paginatorType === 'scroll') {
1158 return $innerBlocksContent;
1159 }
1160
1161 if ($block instanceof \WP_Block && !empty($block->inner_blocks)) {
1162 $innerBlocksContent .= '<div class="fct-shop-paginator">';
1163 $blockContext = $block->context;
1164 foreach ($block->inner_blocks as $inner_block) {
1165 if (isset($inner_block->parsed_block)) {
1166 $innerContext = array_merge($inner_block->context, $blockContext);
1167 $instance = new \WP_Block($inner_block->parsed_block, $innerContext);
1168 $innerBlocksContent .= $instance->render();
1169 }
1170 }
1171 $innerBlocksContent .= '</div>';
1172 }
1173 return $innerBlocksContent;
1174 }
1175
1176 public function renderProductPaginatorInfo($attributes, $content, $block)
1177 {
1178 $allProducts = $this->getInitialProducts($block);
1179 $products = $allProducts['products'];
1180
1181
1182 if($products instanceof CursorPaginator){
1183 return '';
1184 }
1185 // $total = $allProducts['total'];
1186 $page = Arr::get($block->context, 'fluent-cart/per_page', 10);
1187 $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC');
1188 $orderBy = Arr::get($block->context, 'fluent-cart/order_by','ID');
1189
1190 $wrapper_attributes = get_block_wrapper_attributes(
1191 [
1192 'class' => 'fct-shop-paginator-results wc-block-grid__fluent-cart-shop-app-paginator-results',
1193 ]
1194 );
1195 ob_start();
1196 (new ShopAppRenderer($allProducts, [
1197 'default_filters' => [
1198 'per_page' => $page,
1199 'sort_type' => $orderType,
1200 'sort_by' => $orderBy,
1201 ]
1202 ]))->renderPaginatorResultWrapper($wrapper_attributes);
1203 return ob_get_clean();
1204 }
1205
1206 public function renderProductPaginatorNumber($attributes, $content, $block)
1207 {
1208 $allProducts = $this->getInitialProducts($block);
1209 $products = $allProducts['products'];
1210
1211 if($products instanceof CursorPaginator){
1212 return '';
1213 }
1214
1215 $currentPage = Arr::get(App::request()->all(), 'current_page', 1);
1216 $perPage = $products->perPage();
1217 $total = $allProducts['total'];
1218
1219 $page = Arr::get($block->context, 'fluent-cart/per_page', 10);
1220 $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC');
1221 $orderBy = Arr::get($block->context, 'fluent-cart/order_by','ID');
1222
1223 ob_start();
1224 (new ShopAppRenderer($allProducts, [
1225 'default_filters' => [
1226 'per_page' => $page,
1227 'sort_type' => $orderType,
1228 'sort_by' => $orderBy,
1229 ]
1230 ]))->renderPerPageSelector();
1231 return ob_get_clean();
1232
1233 $lastPage = $total / $perPage;
1234
1235 if ($total % $perPage > 0) {
1236 $lastPage++;
1237 }
1238
1239 $lastPage = intval($lastPage);
1240
1241 $paginator = [
1242 'currentPage' => $currentPage,
1243 'lastPage' => $lastPage
1244 ];
1245
1246
1247 $html = $this->renderPaginatorItems($paginator);
1248 return $html;
1249 }
1250
1251 private function renderPaginatorItems($paginator, $paginationJump = 3)
1252 {
1253 if (!$paginator || !isset($paginator['lastPage'], $paginator['currentPage'])) {
1254 return '';
1255 }
1256
1257 $totalPages = (int)$paginator['lastPage'];
1258 $currentPage = (int)$paginator['currentPage'];
1259 $jumpSize = (int)$paginationJump;
1260
1261 $svgLeft = '<svg width="16" height="16" viewBox="0 0 20 20" fill="none"><path d="M12.9168 15.8333L7.7906 10.7071C7.40008 10.3166 7.40008 9.68342 7.7906 9.29289L12.9168 4.16667" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
1262 $svgRight = '<svg width="16" height="16" viewBox="0 0 20 20" fill="none"><path d="M7.91667 4.16666L13.0429 9.29289C13.4334 9.68341 13.4334 10.3166 13.0429 10.7071L7.91667 15.8333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
1263
1264 $html = '';
1265
1266 // Helper to build button
1267 $createButton = function ($label, $page, $isActive = false, $isArrow = false) {
1268 $classes = [];
1269 if ($isActive) $classes[] = 'active';
1270 if ($isArrow) $classes[] = 'arrow';
1271
1272 $classAttr = 'pager-number ';
1273 if ($classes) {
1274 $classAttr .= implode(' ', $classes);
1275 }
1276 return '<li class="' . esc_attr($classAttr) . '" data-fluent-cart-shop-app-paginator-item data-page="' . $page . '">' . $label . '</li>';
1277 };
1278
1279 // ← Previous
1280 if ($currentPage > 1) {
1281 $html .= $createButton($svgLeft, $currentPage - 1, false, true);
1282 }
1283
1284 // First page
1285 $html .= $createButton('1', 1, $currentPage == '1');
1286
1287 // « Jump back
1288 if ($currentPage > $jumpSize + 1) {
1289 $jumpBack = max(2, $currentPage - $jumpSize);
1290 $html .= $createButton('«', $jumpBack, false, true);
1291 }
1292
1293 // Pages before current
1294 for ($i = $currentPage - 2; $i < $currentPage; $i++) {
1295 if ($i > 1) {
1296 $html .= $createButton($i, $i);
1297 }
1298 }
1299
1300 // Current page
1301 if ($currentPage !== 1 && $currentPage !== $totalPages) {
1302 $html .= $createButton($currentPage, $currentPage, true);
1303 }
1304
1305 // Pages after current
1306 for ($i = $currentPage + 1; $i <= $currentPage + 2; $i++) {
1307 if ($i < $totalPages) {
1308 $html .= $createButton($i, $i);
1309 }
1310 }
1311
1312 // » Jump forward
1313 if ($currentPage < $totalPages - $jumpSize) {
1314 $jumpForward = min($totalPages - 1, $currentPage + $jumpSize);
1315 $html .= $createButton('»', $jumpForward, false, true);
1316 }
1317
1318 // Last page
1319 if ($totalPages > 1) {
1320 $html .= $createButton($totalPages, $totalPages, $currentPage === $totalPages);
1321 }
1322
1323 // → Next
1324 if ($currentPage < $totalPages) {
1325 $html .= $createButton($svgRight, $currentPage + 1, false, true);
1326 }
1327
1328 return '<ul class="fct-shop-paginator-pager" data-fluent-cart-shop-app-paginator-items-wrapper>' . $html . '</ul>';
1329 }
1330
1331 public function getInitialProducts($block = null)
1332 {
1333
1334 $request = App::request()->all();
1335 $paginator = Arr::get($block->context, 'fluent-cart/paginator', 'scroll');
1336
1337 if (App::request()->has('current_page')) {
1338 $paginator = 'numbers';
1339 }
1340
1341 $perPage = Arr::get($block->context, 'fluent-cart/per_page', 10);
1342 $perPage = Arr::get($request, 'per_page', $perPage);
1343
1344 $defaultFilters = Arr::get($block->context, 'fluent-cart/default_filters', []);
1345 $defaultFilters = Arr::get($request, 'default_filters', $defaultFilters);
1346
1347 $orderType = Arr::get($block->context, 'fluent-cart/order_type', 'DESC');
1348 $orderBy = Arr::get($block->context, 'fluent-cart/order_by','ID');
1349 $liveFilter = Arr::get($block->context, 'fluent-cart/live_filter', true);
1350
1351 $priceFormat = Arr::get($block->context, 'fluent-cart/price_format', 'starts_from');
1352 $priceFormat = Arr::get($request, 'price_format', $priceFormat);
1353
1354 $wildCardFilter = Arr::get($block->context, 'fluent-cart/enable_wildcard_filter', false);
1355 $currentPage = Arr::get(App::request()->all(), 'current_page', 1);
1356
1357 $paginatorMethod = $paginator === 'numbers' ? 'simple' : 'cursor';
1358
1359
1360 $defaultFilterEnable = Arr::get($defaultFilters, 'enabled', false) ? true : false;
1361
1362 $allowOutOfStock = ($defaultFilterEnable === true &&
1363 Arr::get($defaultFilters, 'allow_out_of_stock', false) === true) ||
1364 Arr::get($request, 'allow_out_of_stock', false) == true;
1365
1366
1367 if (!$defaultFilterEnable) {
1368 $defaultFilters = [];
1369 }
1370
1371 $urlFilters = Arr::get(App::request()->all(), 'filters', []);
1372
1373 $status = ["post_status" => ["column" => "post_status", "operator" => "in", "value" => ["publish"]]];
1374
1375 $urlTerms = Helper::parseTermIdsForFilter($urlFilters);
1376 $defaultTerms = Helper::parseTermIdsForFilter($defaultFilters);
1377 $mergedTerms = Helper::mergeTermIdsForFilter($defaultTerms, $urlTerms);
1378
1379 $mergedTerms = apply_filters('fluent_cart/shop_app_product_query_taxonomy_filters', $mergedTerms, [
1380 'default_terms' => $defaultTerms,
1381 'url_terms' => $urlTerms,
1382 'url_filters' => $urlFilters,
1383 'default_filters' => $defaultFilters,
1384 'block' => $block,
1385 'is_main_query' => is_main_query(),
1386 ]);
1387
1388 if (is_main_query()) {
1389 $taxonomy = TemplateLoader::$currentTaxonomy;
1390 if ($taxonomy) {
1391 $mergedTerms = [
1392 $taxonomy->taxonomy => [$taxonomy->term_id],
1393 ];
1394 }
1395 }
1396
1397 $params = [
1398 "select" => '*',
1399 "with" => ['detail', 'variants', 'categories', 'licensesMeta'],
1400 "selected_status" => true,
1401 "status" => $status,
1402 "shop_app_default_filters" => $defaultFilters,
1403 "taxonomy_filters" => $mergedTerms,
1404 "paginate" => $perPage,
1405 "per_page" => $perPage,
1406 "page" => $currentPage,
1407 'filters' => $urlFilters,
1408 'paginate_using' => $paginatorMethod,
1409 'allow_out_of_stock' => $allowOutOfStock,
1410 'order_type' => $orderType,
1411 'order_by' => $orderBy,
1412 'live_filter' => $liveFilter
1413 ];
1414
1415 $products = ShopResource::get($params);
1416
1417 return [
1418 'products' => ($products['products']->setCollection(
1419 $products['products']->getCollection()->transform(function ($product) {
1420 $product->setAppends(['view_url', 'has_subscription']);
1421 return $product;
1422 })
1423 )),
1424 'total' => $products['total']
1425 ];
1426 }
1427
1428 }
1429