| 1 |
<?php |
| 2 |
/** |
| 3 |
* SureDonation Donation Amount Markup Class. |
| 4 |
* |
| 5 |
* @package SureDonation |
| 6 |
* @since 0.0.1 |
| 7 |
*/ |
| 8 |
|
| 9 |
namespace SureDonation\Inc\Fields; |
| 10 |
|
| 11 |
use SureDonation\Inc\Helper; |
| 12 |
use SureDonation\Inc\Payments\Payment_Helper; |
| 13 |
|
| 14 |
if ( ! defined( 'ABSPATH' ) ) { |
| 15 |
exit; // Exit if accessed directly. |
| 16 |
} |
| 17 |
|
| 18 |
/** |
| 19 |
* Donation Amount Markup Class. |
| 20 |
* |
| 21 |
* @since 0.0.1 |
| 22 |
*/ |
| 23 |
class Donation_Amount_Markup extends Base { |
| 24 |
/** |
| 25 |
* Choice type (radio or checkbox). |
| 26 |
* |
| 27 |
* @var string |
| 28 |
* @since 0.0.1 |
| 29 |
*/ |
| 30 |
protected $choice_type; |
| 31 |
|
| 32 |
/** |
| 33 |
* Layout (horizontal or vertical). |
| 34 |
* |
| 35 |
* @var string |
| 36 |
* @since 0.0.1 |
| 37 |
*/ |
| 38 |
protected $layout; |
| 39 |
|
| 40 |
/** |
| 41 |
* Choice width (width of each option in horizontal layout). |
| 42 |
* |
| 43 |
* @var string |
| 44 |
* @since 0.0.1 |
| 45 |
*/ |
| 46 |
protected $choice_width; |
| 47 |
|
| 48 |
/** |
| 49 |
* Whether the optional custom amount input is shown after the presets. |
| 50 |
* |
| 51 |
* @var bool |
| 52 |
* @since 1.0.0 |
| 53 |
*/ |
| 54 |
protected $allow_custom_amount; |
| 55 |
|
| 56 |
/** |
| 57 |
* Minimum allowed value for the custom amount input. 0 means no min. |
| 58 |
* |
| 59 |
* @var float |
| 60 |
* @since 1.0.0 |
| 61 |
*/ |
| 62 |
protected $custom_amount_min; |
| 63 |
|
| 64 |
/** |
| 65 |
* Maximum allowed value for the custom amount input. 0 means no max. |
| 66 |
* |
| 67 |
* @var float |
| 68 |
* @since 1.0.0 |
| 69 |
*/ |
| 70 |
protected $custom_amount_max; |
| 71 |
|
| 72 |
/** |
| 73 |
* Initialize the properties based on block attributes. |
| 74 |
* |
| 75 |
* @param array<string, mixed> $attributes Block attributes. |
| 76 |
* @since 0.0.1 |
| 77 |
*/ |
| 78 |
public function __construct( $attributes ) { |
| 79 |
$this->slug = 'donation-amount'; |
| 80 |
$this->choice_type = isset( $attributes['choiceType'] ) ? Helper::get_string_value( $attributes['choiceType'] ) : 'radio'; |
| 81 |
$this->layout = isset( $attributes['layout'] ) ? Helper::get_string_value( $attributes['layout'] ) : 'horizontal'; |
| 82 |
$this->choice_width = isset( $attributes['choiceWidth'] ) ? Helper::get_string_value( $attributes['choiceWidth'] ) : '50'; |
| 83 |
$this->allow_custom_amount = ! isset( $attributes['allowCustomAmount'] ) || (bool) $attributes['allowCustomAmount']; |
| 84 |
$this->custom_amount_min = isset( $attributes['customAmountMin'] ) && is_numeric( $attributes['customAmountMin'] ) |
| 85 |
? (float) $attributes['customAmountMin'] |
| 86 |
: 0.0; |
| 87 |
$this->custom_amount_max = isset( $attributes['customAmountMax'] ) && is_numeric( $attributes['customAmountMax'] ) |
| 88 |
? (float) $attributes['customAmountMax'] |
| 89 |
: 0.0; |
| 90 |
|
| 91 |
$this->set_properties( $attributes ); |
| 92 |
$this->set_unique_slug(); |
| 93 |
$this->set_markup_properties(); |
| 94 |
} |
| 95 |
|
| 96 |
/** |
| 97 |
* Render donation-amount markup |
| 98 |
* |
| 99 |
* @since 0.0.1 |
| 100 |
* @return string |
| 101 |
*/ |
| 102 |
public function markup() { |
| 103 |
$classes = $this->get_field_classes( [ 'sd-' . $this->choice_type . '-mode' ] ); |
| 104 |
$vertical_class = 'vertical' === $this->layout ? ' sd-vertical-layout' : ''; |
| 105 |
$choice_width_class = ' sd-choice-width-' . str_replace( '.', '-', $this->choice_width ); |
| 106 |
$wrap_class = 'sd-block-wrap sd-donation-amount-wrap' . $choice_width_class . $vertical_class; |
| 107 |
$svg_type = 'radio' === $this->choice_type ? 'circle' : 'square'; |
| 108 |
$checked_svg = $this->get_svg_icon( $svg_type . '-checked', 'sd-donation-amount-icon' ); |
| 109 |
$unchecked_svg = $this->get_svg_icon( $svg_type . '-unchecked', 'sd-donation-amount-icon-unchecked' ); |
| 110 |
$input_name = 'checkbox' === $this->choice_type ? $this->field_name . '[]' : $this->field_name; |
| 111 |
|
| 112 |
// Get the default value for the hidden input (preselected option). |
| 113 |
$hidden_value = ''; |
| 114 |
if ( ! empty( $this->default ) && ! empty( $this->options ) && is_array( $this->options ) ) { |
| 115 |
foreach ( $this->options as $option ) { |
| 116 |
$option_value = $option['value'] ?? ( $option['label'] ?? '' ); |
| 117 |
if ( $this->default === $option_value ) { |
| 118 |
$hidden_value = $option_value; |
| 119 |
break; |
| 120 |
} |
| 121 |
} |
| 122 |
} |
| 123 |
|
| 124 |
ob_start(); |
| 125 |
?> |
| 126 |
<div data-block-id="<?php echo esc_attr( $this->block_id ); ?>" class="<?php echo esc_attr( $classes ); ?>"> |
| 127 |
<fieldset> |
| 128 |
<!-- Hidden input to store the selected value for use by payment block and form submission --> |
| 129 |
<input |
| 130 |
type="hidden" |
| 131 |
class="sd-input-donation-amount-hidden sd-input-common" |
| 132 |
name="<?php echo esc_attr( $this->field_name ); ?>-value" |
| 133 |
data-slug="<?php echo esc_attr( $this->block_slug ? $this->block_slug : $this->unique_slug ); ?>" |
| 134 |
data-required="<?php echo esc_attr( $this->data_require_attr ); ?>" |
| 135 |
value="<?php echo esc_attr( $hidden_value ); ?>" |
| 136 |
/> |
| 137 |
<legend class="sd-block-legend"> |
| 138 |
<?php echo wp_kses_post( $this->label_markup ); ?> |
| 139 |
</legend> |
| 140 |
<?php echo wp_kses_post( $this->help_markup ); ?> |
| 141 |
<?php |
| 142 |
$show_custom_amount = $this->allow_custom_amount && 'radio' === $this->choice_type; |
| 143 |
?> |
| 144 |
<?php if ( ! empty( $this->options ) && is_array( $this->options ) ) { ?> |
| 145 |
<div class="<?php echo esc_attr( $wrap_class ); ?>" role="group" aria-labelledby="sd-label-<?php echo esc_attr( $this->block_id ); ?>"> |
| 146 |
<?php foreach ( $this->options as $index => $option ) { ?> |
| 147 |
<?php |
| 148 |
$option_label = $option['label'] ?? ''; |
| 149 |
$option_value = $option['value'] ?? $option_label; |
| 150 |
$option_id = $this->unique_slug . '-' . $index; |
| 151 |
$is_checked = $this->default === $option_value; |
| 152 |
$checked_attr = $is_checked ? 'checked' : ''; |
| 153 |
?> |
| 154 |
<div class="sd-donation-amount-single"> |
| 155 |
<input |
| 156 |
type="<?php echo esc_attr( $this->choice_type ); ?>" |
| 157 |
id="<?php echo esc_attr( $option_id ); ?>" |
| 158 |
name="<?php echo esc_attr( $input_name ); ?>" |
| 159 |
value="<?php echo esc_attr( $option_value ); ?>" |
| 160 |
class="sd-input-<?php echo esc_attr( $this->choice_type ); ?>" |
| 161 |
data-required="<?php echo esc_attr( $this->data_require_attr ); ?>" |
| 162 |
<?php echo esc_attr( $checked_attr ); ?> |
| 163 |
/> |
| 164 |
<div class="sd-block-content-wrap"> |
| 165 |
<div class="sd-option-container"> |
| 166 |
<label for="<?php echo esc_attr( $option_id ); ?>"><?php echo esc_html( $this->format_option_label( $option_label ) ); ?></label> |
| 167 |
</div> |
| 168 |
<div class="sd-icon-container"> |
| 169 |
<?php |
| 170 |
$allowed_svg = [ |
| 171 |
'svg' => [ |
| 172 |
'class' => true, |
| 173 |
'width' => true, |
| 174 |
'height' => true, |
| 175 |
'viewbox' => true, |
| 176 |
'fill' => true, |
| 177 |
'xmlns' => true, |
| 178 |
'aria-hidden' => true, |
| 179 |
], |
| 180 |
'circle' => [ |
| 181 |
'cx' => true, |
| 182 |
'cy' => true, |
| 183 |
'r' => true, |
| 184 |
'stroke' => true, |
| 185 |
'stroke-width' => true, |
| 186 |
'fill' => true, |
| 187 |
], |
| 188 |
'rect' => [ |
| 189 |
'x' => true, |
| 190 |
'y' => true, |
| 191 |
'width' => true, |
| 192 |
'height' => true, |
| 193 |
'rx' => true, |
| 194 |
'stroke' => true, |
| 195 |
'stroke-width' => true, |
| 196 |
], |
| 197 |
'path' => [ |
| 198 |
'd' => true, |
| 199 |
'stroke' => true, |
| 200 |
'stroke-width' => true, |
| 201 |
'stroke-linecap' => true, |
| 202 |
'stroke-linejoin' => true, |
| 203 |
], |
| 204 |
]; |
| 205 |
echo wp_kses( $checked_svg, $allowed_svg ); |
| 206 |
echo wp_kses( $unchecked_svg, $allowed_svg ); |
| 207 |
?> |
| 208 |
</div> |
| 209 |
</div> |
| 210 |
</div> |
| 211 |
<?php } ?> |
| 212 |
<?php if ( $show_custom_amount ) { ?> |
| 213 |
<div class="sd-donation-amount-single sd-donation-amount-custom"> |
| 214 |
<input |
| 215 |
type="number" |
| 216 |
id="sd-custom-amount-<?php echo esc_attr( $this->block_id ); ?>" |
| 217 |
class="sd-input-common sd-donation-amount-custom-input" |
| 218 |
data-slug="<?php echo esc_attr( $this->block_slug ? $this->block_slug : $this->unique_slug ); ?>" |
| 219 |
step="0.01" |
| 220 |
<?php if ( $this->custom_amount_min > 0 ) { ?> |
| 221 |
min="<?php echo esc_attr( (string) $this->custom_amount_min ); ?>" |
| 222 |
<?php } ?> |
| 223 |
<?php if ( $this->custom_amount_max > 0 ) { ?> |
| 224 |
max="<?php echo esc_attr( (string) $this->custom_amount_max ); ?>" |
| 225 |
<?php } ?> |
| 226 |
placeholder="<?php esc_attr_e( 'Enter custom amount', 'suredonation' ); ?>" |
| 227 |
aria-label="<?php esc_attr_e( 'Enter custom amount', 'suredonation' ); ?>" |
| 228 |
/> |
| 229 |
</div> |
| 230 |
<?php } ?> |
| 231 |
</div> |
| 232 |
<?php } ?> |
| 233 |
<div class="sd-error-wrap"><?php echo wp_kses_post( $this->error_msg_markup ); ?></div> |
| 234 |
</fieldset> |
| 235 |
</div> |
| 236 |
<?php |
| 237 |
return (string) ob_get_clean(); |
| 238 |
} |
| 239 |
|
| 240 |
/** |
| 241 |
* Get SVG icon for radio/checkbox |
| 242 |
* |
| 243 |
* @param string $type Icon type (circle-checked, circle-unchecked, square-checked, square-unchecked). |
| 244 |
* @param string $classes CSS class. |
| 245 |
* @return string SVG markup. |
| 246 |
* @since 0.0.1 |
| 247 |
*/ |
| 248 |
private function get_svg_icon( $type, $classes = '' ) { |
| 249 |
$class_attr = $classes ? ' class="' . esc_attr( $classes ) . '"' : ''; |
| 250 |
|
| 251 |
switch ( $type ) { |
| 252 |
case 'circle-checked': |
| 253 |
return '<svg' . $class_attr . ' width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
| 254 |
<path d="M15.1663 7.38674V8.00007C15.1655 9.43769 14.7 10.8365 13.8392 11.988C12.9785 13.1394 11.7685 13.9817 10.3899 14.3893C9.0113 14.797 7.53785 14.748 6.18932 14.2498C4.8408 13.7516 3.68944 12.8308 2.90698 11.6248C2.12452 10.4188 1.75287 8.99211 1.84746 7.55761C1.94205 6.12312 2.49781 4.75762 3.43186 3.66479C4.36591 2.57195 5.6282 1.81033 7.03047 1.4935C8.43274 1.17668 9.89985 1.32163 11.213 1.90674" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> |
| 255 |
<path d="M15.1667 2.6665L8.5 9.33984L6.5 7.33984" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> |
| 256 |
</svg>'; |
| 257 |
case 'circle-unchecked': |
| 258 |
return '<svg' . $class_attr . ' width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
| 259 |
<path d="M7.99967 14.6668C11.6816 14.6668 14.6663 11.6821 14.6663 8.00016C14.6663 4.31826 11.6816 1.3335 7.99967 1.3335C4.31778 1.3335 1.33301 4.31826 1.33301 8.00016C1.33301 11.6821 4.31778 14.6668 7.99967 14.6668Z" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> |
| 260 |
</svg>'; |
| 261 |
case 'square-checked': |
| 262 |
return '<svg' . $class_attr . ' width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
| 263 |
<path d="M6.5 7.33366L8.5 9.33366L15.1667 2.66699" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> |
| 264 |
<path d="M14.5 8V12.6667C14.5 13.0203 14.3595 13.3594 14.1095 13.6095C13.8594 13.8595 13.5203 14 13.1667 14H3.83333C3.47971 14 3.14057 13.8595 2.89052 13.6095C2.64048 13.3594 2.5 13.0203 2.5 12.6667V3.33333C2.5 2.97971 2.64048 2.64057 2.89052 2.39052C3.14057 2.14048 3.47971 2 3.83333 2H11.1667" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> |
| 265 |
</svg>'; |
| 266 |
case 'square-unchecked': |
| 267 |
return '<svg' . $class_attr . ' width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> |
| 268 |
<path d="M12.6667 2H3.33333C2.59695 2 2 2.59695 2 3.33333V12.6667C2 13.403 2.59695 14 3.33333 14H12.6667C13.403 14 14 13.403 14 12.6667V3.33333C14 2.59695 13.403 2 12.6667 2Z" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/> |
| 269 |
</svg>'; |
| 270 |
default: |
| 271 |
return ''; |
| 272 |
} |
| 273 |
} |
| 274 |
|
| 275 |
/** |
| 276 |
* Format an option label — if the label is purely numeric, prepend the |
| 277 |
* configured currency symbol. Custom (non-numeric) labels render as-is. |
| 278 |
* |
| 279 |
* @param string $label Raw option label. |
| 280 |
* @return string Display label. |
| 281 |
* @since 1.0.0 |
| 282 |
*/ |
| 283 |
private function format_option_label( $label ) { |
| 284 |
$trimmed = trim( $label ); |
| 285 |
|
| 286 |
if ( '' === $trimmed || ! is_numeric( $trimmed ) ) { |
| 287 |
return $label; |
| 288 |
} |
| 289 |
|
| 290 |
$currency = Payment_Helper::get_global_setting( 'currency', 'USD' ); |
| 291 |
$symbol = Payment_Helper::get_currency_symbol( is_string( $currency ) ? $currency : 'USD' ); |
| 292 |
|
| 293 |
return $symbol . $trimmed; |
| 294 |
} |
| 295 |
} |
| 296 |
|