Style) that adds a box-shadow * to every VEFmultiBooking button, and to VEFmultiBooking Pro's buttons * through the "vefmb_button_shadow_selectors" filter. * * Kept in its own class, with neutral input/output (plain values in, CSS * out), so it can be reused by other plugins. * * @package VEFmultiBooking * @since 0.20.3 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Builds the button shadow value and the CSS rule that applies it. */ class VEFMB_Button_Shadow { /** * Default offsets/blur/spread (px), used when a field is left empty. * A soft shadow slightly below the button. */ const DEFAULTS = array( 'x' => 0, 'y' => 2, 'blur' => 6, 'spread' => 0, ); /** * Allowed range for each value (px). */ const LIMITS = array( 'x' => array( -50, 50 ), 'y' => array( -50, 50 ), 'blur' => array( 0, 100 ), 'spread' => array( -50, 50 ), ); /** * Free buttons that receive the shadow. Filled buttons and outlined * buttons only — icon-only controls (volume, lyrics, quantity...) * are left out, a shadow around a transparent box looks broken. */ const SELECTORS = array( '.vefmb-widget__button', ); /** * Sanitizes one numeric field for saving: empty stays empty (default * applied at output time), anything else is an integer within range. * * @param string $key Field key: x, y, blur or spread. * @param mixed $raw Submitted value (already unslashed). * @return int|string Integer within range, or '' when left empty. */ public static function sanitize_number( $key, $raw ) { $raw = trim( (string) $raw ); if ( '' === $raw || ! isset( self::LIMITS[ $key ] ) ) { return ''; } list( $min, $max ) = self::LIMITS[ $key ]; return max( $min, min( $max, (int) $raw ) ); } /** * Builds the box-shadow value from raw settings. * * @param array $settings Keys: color, x, y, blur, spread. * @return string CSS value (e.g. "0px 2px 6px 0px #00000040"), or '' * when no color is set (no shadow at all). */ public static function build_value( array $settings ) { $color = isset( $settings['color'] ) ? preg_replace( '/[^#a-zA-Z0-9(),.%\s-]/', '', (string) $settings['color'] ) : ''; $color = trim( $color ); if ( '' === $color ) { return ''; } $parts = array(); foreach ( array_keys( self::DEFAULTS ) as $key ) { $value = isset( $settings[ $key ] ) ? self::sanitize_number( $key, $settings[ $key ] ) : ''; $parts[] = ( '' === $value ? self::DEFAULTS[ $key ] : $value ) . 'px'; } return implode( ' ', $parts ) . ' ' . $color; } /** * Returns the full list of selectors, Free ones plus those added by * other plugins through the filter. * * @return string[] Cleaned selectors. */ public static function get_selectors() { /** * Filters the buttons that receive the global button shadow * (VEFmultiBooking Pro adds its own). * * @since 0.20.3 * * @param string[] $selectors CSS selectors, one button type each. */ $selectors = apply_filters( 'vefmb_button_shadow_selectors', self::SELECTORS ); $clean = array(); foreach ( (array) $selectors as $selector ) { // A selector never needs braces, semicolons, at-rules or // markup: stripping them keeps a filtered value from breaking // out of the rule. $selector = trim( preg_replace( '/[{};@<\\\\]/', '', wp_strip_all_tags( (string) $selector ) ) ); if ( '' !== $selector ) { $clean[] = $selector; } } return array_values( array_unique( $clean ) ); } /** * Builds the CSS applying the shadow, or '' when no shadow is set. * * The rule is only printed when a shadow is set: a declaration with an * empty variable would remove a shadow the theme gives its buttons. * * @param array $settings Keys: color, x, y, blur, spread. * @return string CSS rules. */ public static function build_css( array $settings ) { $value = self::build_value( $settings ); if ( '' === $value ) { return ''; } $selectors = self::get_selectors(); if ( ! $selectors ) { return ''; } $css = ':root{--vefmb-button-shadow:' . $value . '}'; // One rule per selector: in a grouped list, a single invalid // selector (e.g. one added through the filter) would make the // browser drop the whole rule for every button. foreach ( $selectors as $selector ) { $css .= $selector . '{box-shadow:var(--vefmb-button-shadow)}'; } return $css; } /** * Reads the saved settings. * * @return array Keys: color, x, y, blur, spread. */ public static function get_settings() { return array( 'color' => VEFMB_Settings::get( 'style_button_shadow_color', '' ), 'x' => VEFMB_Settings::get( 'style_button_shadow_x', '' ), 'y' => VEFMB_Settings::get( 'style_button_shadow_y', '' ), 'blur' => VEFMB_Settings::get( 'style_button_shadow_blur', '' ), 'spread' => VEFMB_Settings::get( 'style_button_shadow_spread', '' ), ); } }