PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.0
Tutor LMS – eLearning and online course solution v4.0.0
4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / components / Modal.php
tutor / components Last commit date
Constants 23 hours ago Accordion.php 23 hours ago Alert.php 23 hours ago AttachmentCard.php 23 hours ago Avatar.php 23 hours ago Badge.php 23 hours ago BaseComponent.php 23 hours ago Button.php 23 hours ago ConfirmationModal.php 23 hours ago CourseFilter.php 23 hours ago DateFilter.php 23 hours ago DropdownFilter.php 23 hours ago EmptyState.php 23 hours ago FileUploader.php 23 hours ago InputField.php 23 hours ago Modal.php 23 hours ago Nav.php 23 hours ago Pagination.php 23 hours ago Popover.php 23 hours ago PreviewTrigger.php 23 hours ago Progress.php 23 hours ago SearchFilter.php 23 hours ago Sorting.php 23 hours ago StarRating.php 23 hours ago StarRatingInput.php 23 hours ago StatusSelect.php 23 hours ago SvgIcon.php 23 hours ago Table.php 23 hours ago Tabs.php 23 hours ago Tooltip.php 23 hours ago WPEditor.php 23 hours ago
Modal.php
527 lines
1 <?php
2 /**
3 * Tutor Component: Modal
4 *
5 * Provides a fluent builder for rendering modals 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 defined( 'ABSPATH' ) || exit;
16
17 /**
18 * Modal Component Class.
19 *
20 * ```
21 * // Example usage:
22 *
23 * Modal::make()
24 * ->id( 'confirm-modal' )
25 * ->title( 'Confirm Submission' )
26 * ->subtitle( 'Are you sure you want to submit?' )
27 * ->body( 'This action cannot be undone.' )
28 * ->footer_buttons( '<button class="tutor-btn">Cancel</button>' )
29 * ->footer_alignment( 'right' )
30 * ->render();
31 *
32 * // With template path
33 * Modal::make()
34 * ->id( 'course-modal' )
35 * ->title( 'Course Details' )
36 * ->template( 'path/to/template.php' )
37 * ->render();
38 *
39 * // Headless modal (no close button)
40 * Modal::make()
41 * ->id( 'headless-modal' )
42 * ->closeable( false )
43 * ->body( '<h3>Success!</h3>' )
44 * ->render();
45 *
46 * // Open by default (state is 'closed' by default)
47 * Modal::make()
48 * ->id( 'welcome-modal' )
49 * ->title( 'Welcome' )
50 * ->body( 'Please confirm to continue.' )
51 * ->state( 'open' )
52 * ->render();
53 * ```
54 *
55 * @since 4.0.0
56 */
57 class Modal extends BaseComponent {
58
59 /**
60 * Modal unique ID.
61 *
62 * @since 4.0.0
63 *
64 * @var string
65 */
66 protected $id = '';
67
68 /**
69 * Modal title.
70 *
71 * @since 4.0.0
72 *
73 * @var string
74 */
75 protected $title = '';
76
77 /**
78 * Modal title icon.
79 *
80 * @since 4.0.0
81 *
82 * @var string
83 */
84 protected $title_icon = '';
85
86 /**
87 * Modal title esc func.
88 *
89 * @since 4.0.0
90 *
91 * @var string
92 */
93 protected $title_esc_cb = 'esc_html';
94
95 /**
96 * Modal subtitle.
97 *
98 * @since 4.0.0
99 *
100 * @var string
101 */
102 protected $subtitle = '';
103
104 /**
105 * Modal subtitle esc func.
106 *
107 * @since 4.0.0
108 *
109 * @var string
110 */
111 protected $subtitle_esc_cb = 'esc_html';
112
113 /**
114 * Modal body content or template path.
115 *
116 * @since 4.0.0
117 *
118 * @var string
119 */
120 protected $body = '';
121
122 /**
123 * Modal body content esc callback.
124 *
125 * @since 4.0.0
126 *
127 * @var string
128 */
129 protected $body_esc_cb = 'wp_kses_post';
130
131 /**
132 * Whether body is a template path.
133 *
134 * @since 4.0.0
135 *
136 * @var bool
137 */
138 protected $is_template = false;
139
140 /**
141 * Template data.
142 *
143 * @since 4.0.0
144 *
145 * @var array
146 */
147 protected $template_data = array();
148
149 /**
150 * Footer buttons HTML.
151 *
152 * @since 4.0.0
153 *
154 * @var string
155 */
156 protected $footer_buttons = '';
157
158 /**
159 * Footer alignment (left|center|right).
160 *
161 * @since 4.0.0
162 *
163 * @var string
164 */
165 protected $footer_alignment = 'right';
166
167 /**
168 * Whether modal is closeable.
169 *
170 * @since 4.0.0
171 *
172 * @var bool
173 */
174 protected $closeable = true;
175
176 /**
177 * Initial modal state: 'open' or 'closed'.
178 *
179 * @since 4.0.0
180 *
181 * @var string
182 */
183 protected $state = 'closed';
184
185 /**
186 * Custom width for modal content.
187 *
188 * @since 4.0.0
189 *
190 * @var string
191 */
192 protected $width = '';
193
194 /**
195 * Set modal ID.
196 *
197 * @since 4.0.0
198 *
199 * @param string $id Modal unique ID.
200 *
201 * @return $this
202 */
203 public function id( $id ) {
204 $this->id = sanitize_key( $id );
205 return $this;
206 }
207
208 /**
209 * Set modal title.
210 *
211 * @since 4.0.0
212 *
213 * @param string $title Modal title.
214 * @param string $esc_cb Callable escaping function.
215 *
216 * @return $this
217 */
218 public function title( string $title, $esc_cb = 'esc_html' ) {
219 $this->title = $title;
220 $this->title_esc_cb = $esc_cb;
221 return $this;
222 }
223
224 /**
225 * Set modal title icon.
226 *
227 * @since 4.0.0
228 *
229 * @param string $title_icon the modal title icon name.
230 *
231 * @return $this
232 */
233 public function title_icon( string $title_icon ) {
234 $this->title_icon = $title_icon;
235 return $this;
236 }
237
238 /**
239 * Set modal subtitle.
240 *
241 * @since 4.0.0
242 *
243 * @param string $subtitle Modal subtitle.
244 * @param string $esc_cb Callable escaping function.
245 *
246 * @return $this
247 */
248 public function subtitle( string $subtitle, $esc_cb = 'esc_html' ) {
249 $this->subtitle = $subtitle;
250 $this->subtitle_esc_cb = $esc_cb;
251 return $this;
252 }
253
254 /**
255 * Set modal body content.
256 *
257 * @since 4.0.0
258 *
259 * @param string $body Body content HTML.
260 * @param string $esc_cb Body content HTML.
261 *
262 * @return $this
263 */
264 public function body( $body, $esc_cb = 'wp_kses_post' ) {
265 $this->body = $body;
266 $this->body_esc_cb = $esc_cb;
267 $this->is_template = false;
268 return $this;
269 }
270
271 /**
272 * Set modal body from template path.
273 *
274 * @since 4.0.0
275 *
276 * @param string $path Template file path.
277 * @param array $data Template data.
278 *
279 * @return $this
280 */
281 public function template( $path, $data = array() ) {
282 $this->body = $path;
283 $this->is_template = true;
284 $this->template_data = $data;
285 return $this;
286 }
287
288 /**
289 * Set footer buttons HTML.
290 *
291 * @since 4.0.0
292 *
293 * @param string $buttons Footer buttons HTML.
294 *
295 * @return $this
296 */
297 public function footer_buttons( $buttons ) {
298 $this->footer_buttons = $buttons;
299 return $this;
300 }
301
302 /**
303 * Set footer button alignment.
304 *
305 * @since 4.0.0
306 *
307 * @param string $alignment Alignment (left|center|right).
308 *
309 * @return $this
310 */
311 public function footer_alignment( $alignment ) {
312 $allowed = array( 'left', 'center', 'right' );
313 if ( in_array( $alignment, $allowed, true ) ) {
314 $this->footer_alignment = $alignment;
315 }
316 return $this;
317 }
318
319 /**
320 * Set whether modal is closeable.
321 *
322 * @since 4.0.0
323 *
324 * @param bool $closeable Whether modal can be closed.
325 *
326 * @return $this
327 */
328 public function closeable( $closeable = true ) {
329 $this->closeable = (bool) $closeable;
330 return $this;
331 }
332
333 /**
334 * Set initial modal state (open or closed).
335 *
336 * @since 4.0.0
337 *
338 * @param string $state Either 'open' or 'closed'. Default is 'closed'.
339 *
340 * @return $this
341 */
342 public function state( string $state ) {
343 $this->state = 'open' === $state ? 'open' : 'closed';
344 return $this;
345 }
346
347 /**
348 * Set custom width for modal content.
349 *
350 * @since 4.0.0
351 *
352 * @param string $width Width value (e.g., '354px', '50%').
353 *
354 * @return $this
355 */
356 public function width( $width ) {
357 $this->width = $width;
358 return $this;
359 }
360
361 /**
362 * Render modal header.
363 *
364 * @since 4.0.0
365 *
366 * @return string Header HTML.
367 */
368 protected function render_header() {
369 if ( empty( $this->title ) && empty( $this->subtitle ) ) {
370 return '';
371 }
372
373 $icon = '';
374
375 $title_html = $this->title
376 ? sprintf( '<div class="tutor-modal-title">%s</div>', $this->esc( $this->title, $this->title_esc_cb ) )
377 : '';
378
379 $subtitle_html = $this->subtitle
380 ? sprintf( '<div class="tutor-modal-subtitle">%s</div>', $this->esc( $this->subtitle, $this->subtitle_esc_cb ) )
381 : '';
382
383 if ( ! empty( $this->title_icon ) ) {
384 ob_start();
385 SvgIcon::make()->name( $this->title_icon )->size( 24 )->render();
386 $icon = ob_get_clean();
387
388 return sprintf(
389 '<div class="tutor-modal-header">
390 <div class="tutor-flex tutor-items-center tutor-gap-4">
391 %3$s%1$s
392 </div>
393 %2$s
394 </div>',
395 $title_html,
396 $subtitle_html,
397 $icon,
398 );
399
400 }
401
402 return sprintf(
403 '<div class="tutor-modal-header">%s%s</div>',
404 $title_html,
405 $subtitle_html
406 );
407 }
408
409 /**
410 * Render modal body.
411 *
412 * @since 4.0.0
413 *
414 * @return string Body HTML.
415 */
416 protected function render_body() {
417 if ( empty( $this->body ) ) {
418 return '';
419 }
420
421 $content = '';
422
423 if ( $this->is_template ) {
424 if ( file_exists( $this->body ) ) {
425 ob_start();
426 $data = $this->template_data;
427 include $this->body;
428 $content = ob_get_clean();
429 return $content ? $content : '';
430 }
431 } else {
432 $content = $this->esc( $this->body, $this->body_esc_cb );
433 }
434
435 return sprintf( '<div class="tutor-modal-body">%s</div>', $content );
436 }
437
438 /**
439 * Render modal footer.
440 *
441 * @since 4.0.0
442 *
443 * @return string Footer HTML.
444 */
445 protected function render_footer() {
446 if ( empty( $this->footer_buttons ) ) {
447 return '';
448 }
449
450 $alignment_class = '';
451 if ( 'left' === $this->footer_alignment ) {
452 $alignment_class = ' tutor-justify-start';
453 } elseif ( 'center' === $this->footer_alignment ) {
454 $alignment_class = ' tutor-justify-center';
455 }
456
457 return sprintf(
458 '<div class="tutor-modal-footer%s">%s</div>',
459 esc_attr( $alignment_class ),
460 $this->footer_buttons
461 );
462 }
463
464 /**
465 * Get the modal HTML.
466 *
467 * @since 4.0.0
468 *
469 * @return string HTML output.
470 */
471 public function get(): string {
472 if ( empty( $this->id ) ) {
473 return '';
474 }
475
476 // Build Alpine.js x-data.
477 $alpine_data = array(
478 'id' => $this->id,
479 'initialOpen' => 'open' === $this->state,
480 );
481 if ( ! $this->closeable ) {
482 $alpine_data['isCloseable'] = false;
483 }
484
485 $alpine_json = wp_json_encode( $alpine_data );
486
487 // Build close button.
488 $close_button = $this->closeable
489 ? '<button x-data="tutorIcon({ name: \'cross\', width: 16, height: 16})" x-bind="getCloseButtonBindings()" aria-label="' . esc_attr__( 'Close', 'tutor' ) . '"></button>'
490 : '';
491
492 // Build style attribute for custom width.
493 $style_attr = $this->width
494 ? sprintf( ' style="max-width: %s;"', esc_attr( $this->width ) )
495 : '';
496
497 // Build modal content.
498 $header = $this->render_header();
499 $body = $this->render_body();
500 $footer = $this->render_footer();
501
502 $this->component_string = sprintf(
503 '<div x-data="tutorModal(%s)" x-cloak style="display: none;">
504 <template x-teleport="body">
505 <div x-bind="getModalBindings()">
506 <div x-bind="getBackdropBindings()"></div>
507 <div x-bind="getModalContentBindings()"%s>
508 %s
509 %s
510 %s
511 %s
512 </div>
513 </div>
514 </template>
515 </div>',
516 esc_attr( $alpine_json ),
517 $style_attr,
518 $close_button,
519 $header,
520 $body,
521 $footer
522 );
523
524 return $this->component_string;
525 }
526 }
527