Constants
22 hours ago
Accordion.php
22 hours ago
Alert.php
22 hours ago
AttachmentCard.php
22 hours ago
Avatar.php
22 hours ago
Badge.php
22 hours ago
BaseComponent.php
22 hours ago
Button.php
22 hours ago
ConfirmationModal.php
22 hours ago
CourseFilter.php
22 hours ago
DateFilter.php
22 hours ago
DropdownFilter.php
22 hours ago
EmptyState.php
22 hours ago
FileUploader.php
22 hours ago
InputField.php
22 hours ago
Modal.php
22 hours ago
Nav.php
22 hours ago
Pagination.php
22 hours ago
Popover.php
22 hours ago
PreviewTrigger.php
22 hours ago
Progress.php
22 hours ago
SearchFilter.php
22 hours ago
Sorting.php
22 hours ago
StarRating.php
22 hours ago
StarRatingInput.php
22 hours ago
StatusSelect.php
22 hours ago
SvgIcon.php
22 hours ago
Table.php
22 hours ago
Tabs.php
22 hours ago
Tooltip.php
22 hours ago
WPEditor.php
22 hours ago
ConfirmationModal.php
479 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Tutor Component: Confirmation Modal |
| 4 | * |
| 5 | * Provides a reusable confirmation modal with Alpine.js integration. |
| 6 | * |
| 7 | * @package Tutor\Components |
| 8 | * @author Themeum |
| 9 | * @link https://themeum.com |
| 10 | * @since 4.0.0 |
| 11 | */ |
| 12 | |
| 13 | namespace Tutor\Components; |
| 14 | |
| 15 | use TUTOR\Icon; |
| 16 | |
| 17 | defined( 'ABSPATH' ) || exit; |
| 18 | |
| 19 | /** |
| 20 | * Confirmation Modal Component Class. |
| 21 | * |
| 22 | * ``` |
| 23 | * // Example usage: |
| 24 | * |
| 25 | * ConfirmationModal::make() |
| 26 | * ->id( 'delete-course-modal' ) |
| 27 | * ->title( 'Delete This Course?' ) |
| 28 | * ->message( 'Are you sure you want to delete this course permanently? Please confirm your choice.' ) |
| 29 | * ->confirm_handler( 'handleDeleteCourse(payload?.courseId)' ) |
| 30 | * ->mutation_state( 'deleteMutation' ) |
| 31 | * ->render(); |
| 32 | * |
| 33 | * // With custom icon |
| 34 | * ConfirmationModal::make() |
| 35 | * ->id( 'delete-announcement-modal' ) |
| 36 | * ->title( 'Delete This Announcement?' ) |
| 37 | * ->message( 'This action cannot be undone.' ) |
| 38 | * ->icon( Icon::DELETE_2, 80 ) |
| 39 | * ->confirm_handler( 'handleDeleteAnnouncement(payload?.announcementId)' ) |
| 40 | * ->render(); |
| 41 | * ``` |
| 42 | * |
| 43 | * @since 4.0.0 |
| 44 | */ |
| 45 | class ConfirmationModal extends BaseComponent { |
| 46 | const ICON_TYPE_HTML = 'html'; |
| 47 | |
| 48 | /** |
| 49 | * Modal unique ID. |
| 50 | * |
| 51 | * @since 4.0.0 |
| 52 | * |
| 53 | * @var string |
| 54 | */ |
| 55 | protected $id = ''; |
| 56 | |
| 57 | /** |
| 58 | * Modal title. |
| 59 | * |
| 60 | * @since 4.0.0 |
| 61 | * |
| 62 | * @var string |
| 63 | */ |
| 64 | protected $title; |
| 65 | |
| 66 | /** |
| 67 | * Confirmation message. |
| 68 | * |
| 69 | * @since 4.0.0 |
| 70 | * |
| 71 | * @var string |
| 72 | */ |
| 73 | protected $message; |
| 74 | |
| 75 | /** |
| 76 | * Allowed HTML tags and attributes. Keys are tag names and values are allowed attributes. |
| 77 | * |
| 78 | * @since 4.0.0 |
| 79 | * |
| 80 | * @var array |
| 81 | */ |
| 82 | protected $allowed_html_tags = array( |
| 83 | 'span' => array( |
| 84 | 'class' => true, |
| 85 | 'x-text' => true, |
| 86 | ), |
| 87 | 'b' => array(), |
| 88 | 'strong' => array(), |
| 89 | 'i' => array(), |
| 90 | 'em' => array(), |
| 91 | 'br' => array(), |
| 92 | ); |
| 93 | |
| 94 | /** |
| 95 | * Icon name from Icon class. |
| 96 | * |
| 97 | * @since 4.0.0 |
| 98 | * |
| 99 | * @var string |
| 100 | */ |
| 101 | protected $icon = ''; |
| 102 | |
| 103 | /** |
| 104 | * Icon width. |
| 105 | * |
| 106 | * @since 4.0.0 |
| 107 | * |
| 108 | * @var int |
| 109 | */ |
| 110 | protected $icon_width = 100; |
| 111 | |
| 112 | /** |
| 113 | * Icon height. |
| 114 | * |
| 115 | * @since 4.0.0 |
| 116 | * |
| 117 | * @var int |
| 118 | */ |
| 119 | protected $icon_height = 100; |
| 120 | |
| 121 | /** |
| 122 | * Icon type. |
| 123 | * |
| 124 | * @since 4.0.0 |
| 125 | * |
| 126 | * @var string|null |
| 127 | */ |
| 128 | protected $icon_type = null; |
| 129 | |
| 130 | /** |
| 131 | * Confirm handler function name (Alpine.js method). |
| 132 | * |
| 133 | * @since 4.0.0 |
| 134 | * |
| 135 | * @var string |
| 136 | */ |
| 137 | protected $confirm_handler = ''; |
| 138 | |
| 139 | /** |
| 140 | * Mutation state variable name (e.g., 'deleteMutation'). |
| 141 | * |
| 142 | * @since 4.0.0 |
| 143 | * |
| 144 | * @var string |
| 145 | */ |
| 146 | protected $mutation_state = 'confirmMutation'; |
| 147 | |
| 148 | /** |
| 149 | * Cancel button text. |
| 150 | * |
| 151 | * @since 4.0.0 |
| 152 | * |
| 153 | * @var string |
| 154 | */ |
| 155 | protected $cancel_text = ''; |
| 156 | |
| 157 | /** |
| 158 | * Confirm button text. |
| 159 | * |
| 160 | * @since 4.0.0 |
| 161 | * |
| 162 | * @var string |
| 163 | */ |
| 164 | protected $confirm_text = ''; |
| 165 | |
| 166 | /** |
| 167 | * Custom width for modal content. |
| 168 | * |
| 169 | * @since 4.0.0 |
| 170 | * |
| 171 | * @var string |
| 172 | */ |
| 173 | protected $width = '426px'; |
| 174 | |
| 175 | /** |
| 176 | * Custom confirm button HTML. |
| 177 | * |
| 178 | * @since 4.0.0 |
| 179 | * |
| 180 | * @var string |
| 181 | */ |
| 182 | protected $confirm_btn = ''; |
| 183 | |
| 184 | /** |
| 185 | * Custom cancel button HTML. |
| 186 | * |
| 187 | * @since 4.0.0 |
| 188 | * |
| 189 | * @var string |
| 190 | */ |
| 191 | protected $cancel_btn = ''; |
| 192 | |
| 193 | /** |
| 194 | * Set modal ID. |
| 195 | * |
| 196 | * @since 4.0.0 |
| 197 | * |
| 198 | * @param string $id Modal unique ID. |
| 199 | * |
| 200 | * @return $this |
| 201 | */ |
| 202 | public function id( $id ) { |
| 203 | $this->id = sanitize_key( $id ); |
| 204 | return $this; |
| 205 | } |
| 206 | |
| 207 | /** |
| 208 | * Set modal title. |
| 209 | * |
| 210 | * @since 4.0.0 |
| 211 | * |
| 212 | * @param string $title Modal title. |
| 213 | * |
| 214 | * @return $this |
| 215 | */ |
| 216 | public function title( string $title ) { |
| 217 | $this->title = $title; |
| 218 | return $this; |
| 219 | } |
| 220 | |
| 221 | /** |
| 222 | * Sets the confirmation message. |
| 223 | * |
| 224 | * @since 4.0.0 |
| 225 | * |
| 226 | * @param string $message The confirmation message text. |
| 227 | * @param array $allowed_html_tags Optional. Allowed HTML tags and attributes. Keys are tag names and values are allowed attributes. |
| 228 | * |
| 229 | * @return $this |
| 230 | */ |
| 231 | public function message( string $message, array $allowed_html_tags = array() ) { |
| 232 | $this->message = $message; |
| 233 | $this->allowed_html_tags = wp_parse_args( $allowed_html_tags, $this->allowed_html_tags ); |
| 234 | return $this; |
| 235 | } |
| 236 | |
| 237 | /** |
| 238 | * Set icon. |
| 239 | * |
| 240 | * @since 4.0.0 |
| 241 | * |
| 242 | * @param string $icon Icon name from Icon class. |
| 243 | * @param int $width Icon width. |
| 244 | * @param int $height Icon height. |
| 245 | * @param string $type Icon type. |
| 246 | * |
| 247 | * @return $this |
| 248 | */ |
| 249 | public function icon( string $icon, int $width = 100, int $height = 100, $type = null ) { |
| 250 | $this->icon = $icon; |
| 251 | $this->icon_width = $width; |
| 252 | $this->icon_height = $height; |
| 253 | $this->icon_type = $type; |
| 254 | return $this; |
| 255 | } |
| 256 | |
| 257 | /** |
| 258 | * Set confirm handler function name. |
| 259 | * |
| 260 | * @since 4.0.0 |
| 261 | * |
| 262 | * @param string $handler Alpine.js method name. |
| 263 | * |
| 264 | * @return $this |
| 265 | */ |
| 266 | public function confirm_handler( string $handler ) { |
| 267 | $this->confirm_handler = $handler; |
| 268 | return $this; |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * Set mutation state variable name. |
| 273 | * |
| 274 | * @since 4.0.0 |
| 275 | * |
| 276 | * @param string $state Mutation state variable name. |
| 277 | * |
| 278 | * @return $this |
| 279 | */ |
| 280 | public function mutation_state( string $state ) { |
| 281 | $this->mutation_state = $state; |
| 282 | return $this; |
| 283 | } |
| 284 | |
| 285 | /** |
| 286 | * Set cancel button text. |
| 287 | * |
| 288 | * @since 4.0.0 |
| 289 | * |
| 290 | * @param string $text Cancel button text. |
| 291 | * |
| 292 | * @return $this |
| 293 | */ |
| 294 | public function cancel_text( string $text ) { |
| 295 | $this->cancel_text = $text; |
| 296 | return $this; |
| 297 | } |
| 298 | |
| 299 | /** |
| 300 | * Set confirm button text. |
| 301 | * |
| 302 | * @since 4.0.0 |
| 303 | * |
| 304 | * @param string $text Confirm button text. |
| 305 | * |
| 306 | * @return $this |
| 307 | */ |
| 308 | public function confirm_text( string $text ) { |
| 309 | $this->confirm_text = $text; |
| 310 | return $this; |
| 311 | } |
| 312 | |
| 313 | /** |
| 314 | * Set custom width for modal content. |
| 315 | * |
| 316 | * @since 4.0.0 |
| 317 | * |
| 318 | * @param string $width Width value (e.g., '426px', '50%'). |
| 319 | * |
| 320 | * @return $this |
| 321 | */ |
| 322 | public function width( string $width ) { |
| 323 | $this->width = $width; |
| 324 | return $this; |
| 325 | } |
| 326 | |
| 327 | /** |
| 328 | * Set custom confirm button HTML. |
| 329 | * |
| 330 | * @since 4.0.0 |
| 331 | * |
| 332 | * @param string $html Button HTML. |
| 333 | * |
| 334 | * @return $this |
| 335 | */ |
| 336 | public function confirm_button( string $html ) { |
| 337 | $this->confirm_btn = $html; |
| 338 | return $this; |
| 339 | } |
| 340 | |
| 341 | /** |
| 342 | * Set custom cancel button HTML. |
| 343 | * |
| 344 | * @since 4.0.0 |
| 345 | * |
| 346 | * @param string $html Button HTML. |
| 347 | * |
| 348 | * @return $this |
| 349 | */ |
| 350 | public function cancel_button( string $html ) { |
| 351 | $this->cancel_btn = $html; |
| 352 | return $this; |
| 353 | } |
| 354 | |
| 355 | /** |
| 356 | * Get the modal HTML. |
| 357 | * |
| 358 | * @since 4.0.0 |
| 359 | * |
| 360 | * @return string HTML output. |
| 361 | */ |
| 362 | public function get(): string { |
| 363 | if ( empty( $this->id ) ) { |
| 364 | return ''; |
| 365 | } |
| 366 | |
| 367 | if ( empty( $this->icon ) ) { |
| 368 | $this->icon = tutor_utils()->get_themed_svg( 'images/illustrations/delete.svg' ); |
| 369 | $this->icon_type = self::ICON_TYPE_HTML; |
| 370 | } |
| 371 | |
| 372 | // Set default button texts if not provided. |
| 373 | if ( empty( $this->cancel_text ) ) { |
| 374 | $this->cancel_text = __( 'Cancel', 'tutor' ); |
| 375 | } |
| 376 | if ( empty( $this->confirm_text ) ) { |
| 377 | $this->confirm_text = __( 'Yes', 'tutor' ); |
| 378 | } |
| 379 | |
| 380 | // Build Alpine.js x-data. |
| 381 | $alpine_data = array( 'id' => $this->id ); |
| 382 | $alpine_json = wp_json_encode( $alpine_data ); |
| 383 | |
| 384 | // Build style attribute for custom width. |
| 385 | $style_attr = $this->width |
| 386 | ? sprintf( ' style="max-width: %s;"', esc_attr( $this->width ) ) |
| 387 | : ''; |
| 388 | |
| 389 | // Build icon HTML. |
| 390 | $icon_html = ''; |
| 391 | if ( ! empty( $this->icon ) ) { |
| 392 | if ( self::ICON_TYPE_HTML === $this->icon_type ) { |
| 393 | $icon_html = $this->icon; |
| 394 | } elseif ( filter_var( $this->icon, FILTER_VALIDATE_URL ) !== false ) { |
| 395 | $icon_html = sprintf( '<img src="%s" style="width:%spx;height:%spx;"/>', $this->icon, $this->icon_width, $this->icon_height ); |
| 396 | } else { |
| 397 | ob_start(); |
| 398 | SvgIcon::make()->name( $this->icon )->width( $this->icon_width )->height( $this->icon_height )->render(); |
| 399 | $icon_html = ob_get_clean(); |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | // Prepare message HTML with allowed HTML tags. |
| 404 | $message_html = wp_kses( $this->message ?? __( 'Are you sure you want to perform this action? Please confirm your choice.', 'tutor' ), $this->allowed_html_tags ); |
| 405 | |
| 406 | // Prepare confirm button HTML. |
| 407 | $confirm_html = $this->confirm_btn; |
| 408 | if ( empty( $confirm_html ) ) { |
| 409 | $confirm_html = sprintf( |
| 410 | '<button |
| 411 | class="tutor-btn tutor-btn-destructive tutor-btn-small" |
| 412 | :class="%s?.isPending ? \'tutor-btn-loading\' : \'\'" |
| 413 | @click="%s" |
| 414 | :disabled="%s?.isPending" |
| 415 | > |
| 416 | %s |
| 417 | </button>', |
| 418 | esc_js( $this->mutation_state ), |
| 419 | $this->confirm_handler, |
| 420 | esc_js( $this->mutation_state ), |
| 421 | esc_html( $this->confirm_text ) |
| 422 | ); |
| 423 | } |
| 424 | |
| 425 | // Prepare cancel button HTML. |
| 426 | $cancel_html = $this->cancel_btn; |
| 427 | if ( empty( $cancel_html ) ) { |
| 428 | $cancel_html = sprintf( |
| 429 | '<button |
| 430 | class="tutor-btn tutor-btn-secondary tutor-btn-small" |
| 431 | @click="TutorCore.modal.closeModal(\'%s\')" |
| 432 | > |
| 433 | %s |
| 434 | </button>', |
| 435 | esc_js( $this->id ), |
| 436 | esc_html( $this->cancel_text ) |
| 437 | ); |
| 438 | } |
| 439 | |
| 440 | $this->component_string = sprintf( |
| 441 | '<div x-data="tutorModal(%s)" x-cloak style="display: none;"> |
| 442 | <template x-teleport="body"> |
| 443 | <div x-bind="getModalBindings()"> |
| 444 | <div x-bind="getBackdropBindings()"></div> |
| 445 | <div x-bind="getModalContentBindings()"%s> |
| 446 | <button x-data="tutorIcon({ name: \'cross\', width: 16, height: 16})" x-bind="getCloseButtonBindings()" aria-label="%s"></button> |
| 447 | |
| 448 | <div class="tutor-p-7 tutor-pt-10 tutor-flex tutor-flex-column tutor-items-center"> |
| 449 | %s |
| 450 | <h5 class="tutor-h5 tutor-font-medium tutor-mt-7"> |
| 451 | %s |
| 452 | </h5> |
| 453 | <p class="tutor-p3 tutor-text-secondary tutor-mt-2 tutor-text-center"> |
| 454 | %s |
| 455 | </p> |
| 456 | </div> |
| 457 | |
| 458 | <div class="tutor-grid tutor-grid-cols-2 tutor-gap-4 tutor-px-7 tutor-pb-6"> |
| 459 | %s |
| 460 | %s |
| 461 | </div> |
| 462 | </div> |
| 463 | </div> |
| 464 | </template> |
| 465 | </div>', |
| 466 | esc_attr( $alpine_json ), |
| 467 | $style_attr, |
| 468 | esc_attr__( 'Close', 'tutor' ), |
| 469 | $icon_html, |
| 470 | esc_html( $this->title ?? __( 'Are you sure?', 'tutor' ) ), |
| 471 | $message_html, |
| 472 | $cancel_html, |
| 473 | $confirm_html, |
| 474 | ); |
| 475 | |
| 476 | return $this->component_string; |
| 477 | } |
| 478 | } |
| 479 |