PluginProbe
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management / trunk
SureDonation – Donation Forms, Fundraising Campaigns & Donor Management vtrunk
1.5.1 1.5.0 1.4.0 1.3.0 trunk 0.0.1 1.0.0 1.1.0 1.1.1 1.1.2 1.2.0
suredonation / inc / fields / dropdown-markup.php

dropdown-markup.php in SureDonation – Donation Forms, Fundraising Campaigns & Donor Management trunk, at inc/fields/dropdown-markup.php

207 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * SureDonation Dropdown Markup Class.
4 *
5 * @package SureDonation
6 * @since 1.1.1
7 */
8
9 namespace SureDonation\Inc\Fields;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly.
13 }
14
15 use SureDonation\Inc\Helper;
16
17 /**
18 * Dropdown Markup Class.
19 *
20 * Renders a native <select> enhanced on the front end by tom-select. The native
21 * markup is the no-JS fallback; a class-based placeholder (built from tom-select's
22 * own .ts-wrapper/.ts-control classes) is shown while the library mounts so there
23 * is no flash of the unstyled native control. See assets/build/blocks/dropdown.
24 *
25 * @since 1.1.1
26 */
27 class Dropdown_Markup extends Base {
28 /**
29 * Whether multiple options can be selected.
30 *
31 * @var bool
32 * @since 1.1.1
33 */
34 protected $multi_select = false;
35
36 /**
37 * Whether the dropdown is searchable.
38 *
39 * @var bool
40 * @since 1.1.1
41 */
42 protected $searchable = true;
43
44 /**
45 * Minimum number of selections (multi-select only; 0 = no minimum).
46 *
47 * @var int
48 * @since 1.1.1
49 */
50 protected $min_selection = 0;
51
52 /**
53 * Maximum number of selections (multi-select only; 0 = no maximum).
54 *
55 * @var int
56 * @since 1.1.1
57 */
58 protected $max_selection = 0;
59
60 /**
61 * Indices of options preselected by default.
62 *
63 * @var array<int>
64 * @since 1.1.1
65 */
66 protected $preselected = [];
67
68 /**
69 * Initialize the properties based on block attributes.
70 *
71 * @param array<string, mixed> $attributes Block attributes.
72 * @since 1.1.1
73 */
74 public function __construct( $attributes ) {
75 $this->slug = 'dropdown';
76 $this->multi_select = ! empty( $attributes['multiSelect'] );
77 $this->searchable = ! isset( $attributes['searchable'] ) || ! empty( $attributes['searchable'] );
78 $this->min_selection = isset( $attributes['minSelection'] ) ? absint( Helper::get_string_value( $attributes['minSelection'] ) ) : 0;
79 $this->max_selection = isset( $attributes['maxSelection'] ) ? absint( Helper::get_string_value( $attributes['maxSelection'] ) ) : 0;
80 $this->preselected = isset( $attributes['preselectedOptions'] ) && is_array( $attributes['preselectedOptions'] )
81 ? array_map( 'absint', $attributes['preselectedOptions'] )
82 : [];
83
84 $this->set_properties( $attributes );
85 $this->set_unique_slug();
86 $this->set_markup_properties();
87 }
88
89 /**
90 * Render dropdown markup.
91 *
92 * @since 1.1.1
93 * @return string
94 */
95 public function markup() {
96 $classes = $this->get_field_classes( [ 'sd-dropdown-wrap-block' ] );
97 $aria_desc = $this->get_aria_describedby();
98 $placeholder = '' !== $this->placeholder ? $this->placeholder : __( 'Select an option', 'suredonation' );
99 $data_slug = $this->block_slug ? $this->block_slug : $this->unique_slug;
100
101 // Resolve the preselected option labels (single-select keeps only the first).
102 $selected_labels = [];
103 foreach ( $this->preselected as $index ) {
104 if ( isset( $this->options[ $index ]['label'] ) ) {
105 $selected_labels[] = Helper::get_string_value( $this->options[ $index ]['label'] );
106 }
107 }
108 if ( ! $this->multi_select && count( $selected_labels ) > 1 ) {
109 $selected_labels = [ $selected_labels[0] ];
110 }
111
112 ob_start();
113 ?>
114 <div data-block-id="<?php echo esc_attr( $this->block_id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
115 <?php echo wp_kses_post( $this->label_markup ); ?>
116 <?php echo wp_kses_post( $this->help_markup ); ?>
117 <div class="sd-block-wrap">
118 <?php echo $this->placeholder_markup( $placeholder, $selected_labels ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Internal markup, values escaped within. ?>
119 <select
120 class="sd-dropdown-common sd-dropdown-input<?php echo esc_attr( $this->multi_select ? '' : ' sd-input-common' ); ?>"
121 <?php if ( ! $this->multi_select ) { ?>
122 name="<?php echo esc_attr( $this->field_name ); ?>"
123 <?php } ?>
124 id="<?php echo esc_attr( $this->unique_slug ); ?>"
125 data-slug="<?php echo esc_attr( $data_slug ); ?>"
126 <?php if ( ! empty( $aria_desc ) ) { ?>
127 aria-describedby="<?php echo esc_attr( $aria_desc ); ?>"
128 <?php } ?>
129 data-required="<?php echo esc_attr( $this->data_require_attr ); ?>"
130 aria-required="<?php echo esc_attr( $this->data_require_attr ); ?>"
131 data-multiple="<?php echo esc_attr( $this->multi_select ? 'true' : 'false' ); ?>"
132 data-searchable="<?php echo esc_attr( $this->searchable ? 'true' : 'false' ); ?>"
133 data-min-selection="<?php echo esc_attr( (string) $this->min_selection ); ?>"
134 data-max-selection="<?php echo esc_attr( (string) $this->max_selection ); ?>"
135 data-placeholder="<?php echo esc_attr( $placeholder ); ?>"
136 autocomplete="off"
137 tabindex="0"
138 <?php echo esc_attr( $this->multi_select ? 'multiple' : '' ); ?>
139 >
140 <?php if ( ! $this->multi_select ) { ?>
141 <option value="" class="sd-dropdown-placeholder-option" <?php echo empty( $selected_labels ) ? 'selected' : ''; ?> disabled><?php echo esc_html( $placeholder ); ?></option>
142 <?php } ?>
143 <?php foreach ( $this->options as $option ) { ?>
144 <?php
145 $label = isset( $option['label'] ) ? Helper::get_string_value( $option['label'] ) : '';
146 if ( '' === $label ) {
147 continue;
148 }
149 ?>
150 <option value="<?php echo esc_attr( $label ); ?>" <?php echo in_array( $label, $selected_labels, true ) ? 'selected' : ''; ?>><?php echo esc_html( $label ); ?></option>
151 <?php } ?>
152 </select>
153 <?php if ( $this->multi_select ) { ?>
154 <input
155 type="hidden"
156 class="sd-input-common sd-dropdown-hidden"
157 name="<?php echo esc_attr( $this->field_name ); ?>"
158 data-slug="<?php echo esc_attr( $data_slug ); ?>"
159 data-required="<?php echo esc_attr( $this->data_require_attr ); ?>"
160 data-multiple="true"
161 data-min-selection="<?php echo esc_attr( (string) $this->min_selection ); ?>"
162 data-max-selection="<?php echo esc_attr( (string) $this->max_selection ); ?>"
163 value="<?php echo esc_attr( implode( '|', $selected_labels ) ); ?>"
164 />
165 <?php } ?>
166 </div>
167 <div class="sd-error-wrap"><?php echo wp_kses_post( $this->error_msg_markup ); ?></div>
168 </div>
169 <?php
170 return (string) ob_get_clean();
171 }
172
173 /**
174 * Build the class-based placeholder shown until tom-select mounts.
175 *
176 * Reuses tom-select's own .ts-wrapper/.ts-control classes so it is painted by
177 * the same stylesheet (and any custom overrides) as the real control — an exact
178 * match with no duplicated styling. JS removes it once the control is mounted;
179 * CSS hides it entirely when JavaScript is unavailable (the native <select>
180 * remains the fallback). See sass/blocks/default/components/dropdown.scss.
181 *
182 * @param string $placeholder Placeholder text.
183 * @param array<string> $selected_labels Preselected option labels.
184 * @return string
185 * @since 1.1.1
186 */
187 protected function placeholder_markup( $placeholder, $selected_labels ) {
188 $wrapper_classes = 'sd-dropdown-placeholder ts-wrapper ' . ( $this->multi_select ? 'multi plugin-remove_button' : 'single' );
189
190 ob_start();
191 ?>
192 <div class="<?php echo esc_attr( $wrapper_classes ); ?>" aria-hidden="true">
193 <div class="ts-control"><div class="ts-dropdown-icon"><svg aria-hidden="true" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4 6L8 10L12 6" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round" /></svg></div>
194 <?php if ( ! empty( $selected_labels ) ) { ?>
195 <?php foreach ( $selected_labels as $label ) { ?>
196 <div class="item"><?php echo esc_html( $label ); ?></div>
197 <?php } ?>
198 <?php } else { ?>
199 <div class="item sd-dropdown-placeholder-text"><?php echo esc_html( $placeholder ); ?></div>
200 <?php } ?>
201 </div>
202 </div>
203 <?php
204 return (string) ob_get_clean();
205 }
206 }
207