Created
October 27, 2026 11:11
-
-
Save xlplugins/0bbc0c9a17353670df58b96d0abb60fe to your computer and use it in GitHub Desktop.
showing choose plan inside the plan selector
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * Plugin Name: Sublium Choose Plan Label (test) | |
| * Description: Shows "Choose Plan" in the Sublium plan-selector trigger box and, when the Subscription option is active but no plan has been picked, blocks add-to-cart and shows a "Please select a subscription plan." message instead of silently disabling the button. | |
| */ | |
| if ( ! defined( 'ABSPATH' ) ) { | |
| exit; | |
| } | |
| add_action( | |
| 'wp_footer', | |
| function () { | |
| // Scope to the single product page only — not cart, checkout, or the | |
| // FunnelKit slide cart, which also render Sublium's plan dropdown but | |
| // should keep showing the real selected plan there. | |
| if ( ! is_product() ) { | |
| return; | |
| } | |
| $label = __( 'Choose Plan', 'sublium-subscriptions-for-woocommerce' ); | |
| $message = __( 'Please select a subscription plan.', 'sublium-subscriptions-for-woocommerce' ); | |
| ?> | |
| <script> | |
| ( function ( $ ) { | |
| var subliumPlanUserSelected = false; | |
| // Scope everything to the main product-page widget only. The | |
| // FunnelKit slide cart can be opened while still on the product page | |
| // (same URL, no navigation) and renders its own instance of the same | |
| // .sublium-selected-option/.sublium-dropdown-options markup for the | |
| // cart line item — that instance must keep showing the real plan. | |
| var subliumWidgetSelector = '.sublium-front-widget-container'; | |
| var subliumMessageText = '<?php echo esc_js( $message ); ?>'; | |
| var subliumMessageClass = 'sublium-plan-required-message'; | |
| function subliumSubscriptionIsActive( $widget ) { | |
| // The "Subscription" radio has id="sublium-plan-type-{type}"; a | |
| // separate "One Time Purchase" radio (id="one-time") can also | |
| // exist on products where it's enabled. Only treat a plan as | |
| // required when a subscription-type option is the one currently | |
| // checked — one-time purchase needs no plan selection at all. | |
| var $checked = $widget.closest( 'form.cart' ).find( 'input[name="sublium-purchase-option"]:checked' ); | |
| return $checked.length > 0 && $checked.attr( 'id' ).indexOf( 'sublium-plan-type-' ) === 0; | |
| } | |
| // Create the inline validation message once and place it on its OWN | |
| // full-width line immediately AFTER the .sublium-front-widget-container | |
| // box closes — directly under the Subscribe / Choose Plan area, which | |
| // is where the choice is actually made. It is inserted as a SIBLING of | |
| // the widget (not inside it), so Sublium re-rendering the widget's | |
| // internals never wipes the message. Inline styles keep it visible | |
| // regardless of theme CSS, on mobile and desktop. | |
| function subliumGetMessage( $widget ) { | |
| // Deduped globally: the message lives outside the widget, so a | |
| // widget-scoped lookup would miss it and stack duplicates per click. | |
| var $msg = $( '.' + subliumMessageClass ); | |
| if ( ! $msg.length ) { | |
| $msg = $( '<div></div>' ) | |
| .addClass( subliumMessageClass ) | |
| .attr( 'role', 'alert' ) | |
| .text( subliumMessageText ) | |
| .css( { | |
| display: 'none', | |
| color: '#d63638', | |
| fontSize: '14px', | |
| fontWeight: '600', | |
| margin: '12px 0 0', | |
| width: '100%', | |
| flexBasis: '100%', | |
| clear: 'both' | |
| } ); | |
| $widget.after( $msg ); | |
| } | |
| return $msg; | |
| } | |
| function subliumHideMessage() { | |
| $( '.' + subliumMessageClass ).css( 'display', 'none' ); | |
| } | |
| // Does the current state require a plan the customer has not chosen yet? | |
| function subliumNeedsPlan( $widget ) { | |
| return $widget.length > 0 && subliumSubscriptionIsActive( $widget ) && ! subliumPlanUserSelected; | |
| } | |
| function subliumApplyChoosePlanText() { | |
| var $widget = $( subliumWidgetSelector ); | |
| if ( ! $widget.length ) { | |
| return; | |
| } | |
| if ( subliumPlanUserSelected ) { | |
| subliumHideMessage(); | |
| return; | |
| } | |
| // Show "Choose Plan" and hide the stale billing summary while no | |
| // plan is chosen, regardless of which radio (One Time / | |
| // Subscription) is currently active — a customer switching to | |
| // Subscription later should see the placeholder immediately. | |
| $widget.find( '.sublium-selected-option .sublium-selected-text' ).each( function () { | |
| if ( $( this ).text() !== '<?php echo esc_js( $label ); ?>' ) { | |
| $( this ).text( '<?php echo esc_js( $label ); ?>' ); | |
| } | |
| } ); | |
| $widget.find( '.sublium-front-description' ).css( 'display', 'none' ); | |
| } | |
| // Block the add and show the message. Shared by the click and submit | |
| // interceptors so both the button and a plain form submit are covered. | |
| function subliumBlockAndWarn( e, $widget ) { | |
| e.preventDefault(); | |
| e.stopImmediatePropagation(); | |
| subliumGetMessage( $widget ).css( 'display', 'block' ); | |
| // Bring the plan dropdown into view so what to do is obvious on | |
| // mobile too. | |
| if ( $widget[0] && $widget[0].scrollIntoView ) { | |
| $widget[0].scrollIntoView( { behavior: 'smooth', block: 'center' } ); | |
| } | |
| } | |
| // Native capture-phase listeners run BEFORE Sublium's / WooCommerce's | |
| // own handlers, so we can stop the add cleanly. We intercept both the | |
| // button click and the form submit, because the button is a submit | |
| // control and a keyboard Enter can submit the form without a click. | |
| document.addEventListener( | |
| 'click', | |
| function ( e ) { | |
| var button = e.target.closest( 'form.cart .single_add_to_cart_button' ); | |
| if ( ! button ) { | |
| return; | |
| } | |
| var $widget = $( subliumWidgetSelector ); | |
| if ( subliumNeedsPlan( $widget ) ) { | |
| subliumBlockAndWarn( e, $widget ); | |
| } | |
| }, | |
| true | |
| ); | |
| document.addEventListener( | |
| 'submit', | |
| function ( e ) { | |
| var form = e.target; | |
| if ( ! form.matches || ! form.matches( 'form.cart' ) ) { | |
| return; | |
| } | |
| var $widget = $( subliumWidgetSelector ); | |
| if ( subliumNeedsPlan( $widget ) ) { | |
| subliumBlockAndWarn( e, $widget ); | |
| } | |
| }, | |
| true | |
| ); | |
| // isTrusted is a native, un-fakeable browser flag: true only for real | |
| // user-initiated events, always false for script-dispatched ones. | |
| // Sublium's own click handling stops native propagation and | |
| // re-dispatches via jQuery's .trigger('click') afterward, which strips | |
| // isTrusted by the time a jQuery delegated listener sees it — so this | |
| // uses a native, capture-phase listener instead. | |
| document.addEventListener( | |
| 'click', | |
| function ( e ) { | |
| if ( ! e.isTrusted ) { | |
| return; | |
| } | |
| if ( ! e.target.closest( subliumWidgetSelector + ' .sublium-dropdown-options li' ) ) { | |
| return; | |
| } | |
| // The customer picked a plan: clear the warning and unlock the add. | |
| subliumPlanUserSelected = true; | |
| subliumHideMessage(); | |
| subliumApplyChoosePlanText(); | |
| }, | |
| true | |
| ); | |
| // Re-evaluate when switching between Subscription / One Time Purchase. | |
| // Switching AWAY from Subscription resets the "user picked a plan" | |
| // flag, so returning to Subscription later always requires a fresh | |
| // explicit choice instead of silently carrying over a prior pick. | |
| $( document ).on( 'change', 'input[name="sublium-purchase-option"]', function () { | |
| var $widget = $( subliumWidgetSelector ); | |
| if ( $widget.length && ! subliumSubscriptionIsActive( $widget ) ) { | |
| subliumPlanUserSelected = false; | |
| } | |
| subliumHideMessage(); | |
| subliumApplyChoosePlanText(); | |
| } ); | |
| // Race-proof against Sublium's own render timing: react to DOM | |
| // mutations directly instead of relying on the sublium_plans_rendered | |
| // event firing after this script attaches its listener. | |
| var subliumObserver = new MutationObserver( subliumApplyChoosePlanText ); | |
| function subliumStartObserving() { | |
| subliumObserver.observe( document.body, { childList: true, subtree: true, characterData: true } ); | |
| subliumApplyChoosePlanText(); | |
| } | |
| if ( document.readyState === 'loading' ) { | |
| document.addEventListener( 'DOMContentLoaded', subliumStartObserving ); | |
| } else { | |
| subliumStartObserving(); | |
| } | |
| } )( jQuery ); | |
| </script> | |
| <?php | |
| }, | |
| 20 | |
| ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment