|string $atts Shortcode attributes. * @return string HTML output. */ public function render( $atts = array() ): string { $atts = shortcode_atts( array( 'peptide' => '', // Pre-select a peptide by slug. ), $atts, 'prc_calculator' ); $this->enqueue_assets(); $selected_slug = sanitize_title( $atts['peptide'] ); ob_start(); $this->render_calculator_html( $selected_slug ); return ob_get_clean(); } /** * Enqueue calculator JS and CSS, with inline preset data. * * Why inline presets: Avoids a REST round-trip on page load. * The presets are small (< 5KB) and cached by the provider. * * @return void Side effect: enqueues scripts/styles. */ private function enqueue_assets(): void { if ( $this->assets_enqueued ) { return; } wp_enqueue_style( 'prc-calculator', PRC_PLUGIN_URL . 'assets/css/calculator.css', array(), PRC_VERSION ); wp_enqueue_script( 'prc-calculator', PRC_PLUGIN_URL . 'assets/js/calculator.js', array(), PRC_VERSION, true ); // Pass presets and config to JS — all REST endpoints are public-read, // so no nonce is needed. The restUrl is provided for future use. $provider = new PRC_Preset_Provider(); wp_localize_script( 'prc-calculator', 'prcConfig', array( 'presets' => $provider->get_all_presets(), 'restUrl' => esc_url_raw( rest_url( 'prc/v1' ) ), 'syringeUnits' => 100, // Must match PRC_Math::SYRINGE_UNITS_PER_ML and calculator.js SYRINGE_UNITS_PER_ML. 'i18n' => array( 'selectPeptide' => __( 'Select a peptide…', 'peptide-reconstitution-calculator' ), 'customEntry' => __( 'Custom / Manual Entry', 'peptide-reconstitution-calculator' ), 'calculate' => __( 'Calculate', 'peptide-reconstitution-calculator' ), 'reset' => __( 'Reset', 'peptide-reconstitution-calculator' ), 'concentration' => __( 'Concentration', 'peptide-reconstitution-calculator' ), 'injectionVol' => __( 'Injection Volume', 'peptide-reconstitution-calculator' ), 'syringeUnits' => __( 'Syringe Units', 'peptide-reconstitution-calculator' ), 'dosesPerVial' => __( 'Doses Per Vial', 'peptide-reconstitution-calculator' ), 'perUnit' => __( 'per unit', 'peptide-reconstitution-calculator' ), 'disclaimer' => __( 'For informational purposes only. Not medical advice. Always consult a qualified healthcare professional.', 'peptide-reconstitution-calculator' ), ), ) ); $this->assets_enqueued = true; } /** * Output the calculator HTML shell. * * The JS calculator populates the interactive elements. * This provides the semantic structure and no-JS fallback message. * * @param string $selected_slug Pre-selected peptide slug (empty = none). * @return void Side effect: outputs HTML. */ private function render_calculator_html( string $selected_slug ): void { ?>