block.json
4 months ago
bundle.php
1 week ago
controller.php
1 month ago
index.asset.php
1 week ago
index.js
1 week ago
radio.php
1 month ago
select.php
1 month ago
style-index-rtl.css
1 month ago
style-index.css
1 month ago
view.php
4 months ago
bundle.php
105 lines
| 1 | <?php |
| 2 | $page_block = new \SureCart\Models\Blocks\ProductPageBlock(); |
| 3 | |
| 4 | ob_start(); |
| 5 | |
| 6 | // Order components by their `position` (the order set in the bundle). |
| 7 | $bundle_items = $product->bundle_items->data ?? array(); |
| 8 | usort( $bundle_items, fn( $a, $b ) => ( $a->position ?? 0 ) <=> ( $b->position ?? 0 ) ); |
| 9 | |
| 10 | foreach ( $bundle_items as $bundle_item ) : |
| 11 | // Resolve the component (live associations on the buy page, else the |
| 12 | // component's own synced cache) so variant stock is always current. |
| 13 | $component = $product->resolveBundleComponent( $bundle_item ); |
| 14 | if ( empty( $component->id ) ) { |
| 15 | continue; |
| 16 | } |
| 17 | |
| 18 | $variant_options = $component->variant_options->data ?? array(); |
| 19 | |
| 20 | // Skip components with no selectable variants — nothing to interact with. |
| 21 | if ( empty( $variant_options ) ) { |
| 22 | continue; |
| 23 | } |
| 24 | |
| 25 | $variants = $component->variants->data ?? array(); |
| 26 | |
| 27 | $variants_payload = array_map( |
| 28 | fn( $variant ) => array( |
| 29 | 'id' => $variant->id, |
| 30 | 'option_1' => $variant->option_1 ?? null, |
| 31 | 'option_2' => $variant->option_2 ?? null, |
| 32 | 'option_3' => $variant->option_3 ?? null, |
| 33 | 'available_stock' => $variant->available_stock ?? 0, |
| 34 | 'has_unlimited_stock' => $variant->has_unlimited_stock, |
| 35 | ), |
| 36 | $variants |
| 37 | ); |
| 38 | |
| 39 | $initial_variant = $page_block->findInitialBundleComponentVariant( $component ); |
| 40 | |
| 41 | $initial_option_values = $initial_variant |
| 42 | ? (object) array_filter( |
| 43 | array( |
| 44 | 'option_1' => $initial_variant->option_1 ?? null, |
| 45 | 'option_2' => $initial_variant->option_2 ?? null, |
| 46 | 'option_3' => $initial_variant->option_3 ?? null, |
| 47 | ), |
| 48 | fn( $value ) => null !== $value && '' !== $value |
| 49 | ) |
| 50 | : (object) array(); |
| 51 | |
| 52 | $component_context = wp_interactivity_data_wp_context( |
| 53 | array( |
| 54 | 'componentProductId' => $component->id ?? null, |
| 55 | 'componentProductSlug' => ! empty( $component->slug ) ? $component->slug : ( $component->id ?? null ), |
| 56 | 'componentVariants' => $variants_payload, |
| 57 | 'componentHasUnlimitedStock' => ! empty( $component->has_unlimited_stock ), |
| 58 | 'componentOptionValues' => $initial_option_values, |
| 59 | ) |
| 60 | ); |
| 61 | |
| 62 | $component_name = $component->name ?? ''; |
| 63 | $quantity = max( 1, (int) ( $bundle_item->quantity ?? 1 ) ); |
| 64 | ?> |
| 65 | <div class="sc-bundle-item" <?php echo wp_kses_data( $component_context ); ?>> |
| 66 | <?php |
| 67 | foreach ( $variant_options as $key => $option ) : |
| 68 | // Label folds the component name (and quantity, once per component) |
| 69 | // into the option label — the single visual difference for bundles. |
| 70 | $pill_group_label = trim( $component_name . ' – ' . $option->name ); |
| 71 | if ( 0 === $key && $quantity > 1 ) { |
| 72 | $pill_group_label .= ' × ' . $quantity; |
| 73 | } |
| 74 | ?> |
| 75 | <div |
| 76 | class="sc-bundle-item__option" |
| 77 | <?php echo wp_kses_data( wp_interactivity_data_wp_context( array( 'optionNumber' => (int) $key + 1 ) ) ); ?> |
| 78 | data-wp-interactive='{ "namespace": "surecart/product-page" }' |
| 79 | > |
| 80 | <?php |
| 81 | if ( 'dropdown' === $option->display_type ) : |
| 82 | include 'select.php'; |
| 83 | else : |
| 84 | include 'radio.php'; |
| 85 | endif; |
| 86 | ?> |
| 87 | </div> |
| 88 | <?php |
| 89 | endforeach; |
| 90 | ?> |
| 91 | </div> |
| 92 | <?php |
| 93 | endforeach; |
| 94 | |
| 95 | $bundle_pickers = trim( ob_get_clean() ); |
| 96 | |
| 97 | // No variable components — render nothing. |
| 98 | if ( '' === $bundle_pickers ) { |
| 99 | return; |
| 100 | } |
| 101 | ?> |
| 102 | <div <?php echo wp_kses_data( get_block_wrapper_attributes( array( 'class' => 'sc-bundle-picker' ) ) ); ?>> |
| 103 | <?php echo $bundle_pickers; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> |
| 104 | </div> |
| 105 |