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 / Popover.php
tutor / components Last commit date
Constants 19 hours ago Accordion.php 19 hours ago Alert.php 19 hours ago AttachmentCard.php 19 hours ago Avatar.php 19 hours ago Badge.php 19 hours ago BaseComponent.php 19 hours ago Button.php 19 hours ago ConfirmationModal.php 19 hours ago CourseFilter.php 19 hours ago DateFilter.php 19 hours ago DropdownFilter.php 19 hours ago EmptyState.php 19 hours ago FileUploader.php 19 hours ago InputField.php 19 hours ago Modal.php 19 hours ago Nav.php 19 hours ago Pagination.php 19 hours ago Popover.php 19 hours ago PreviewTrigger.php 19 hours ago Progress.php 19 hours ago SearchFilter.php 19 hours ago Sorting.php 19 hours ago StarRating.php 19 hours ago StarRatingInput.php 19 hours ago StatusSelect.php 19 hours ago SvgIcon.php 19 hours ago Table.php 19 hours ago Tabs.php 19 hours ago Tooltip.php 19 hours ago WPEditor.php 19 hours ago
Popover.php
582 lines
1 <?php
2 /**
3 * Popover Component Class.
4 *
5 * @package Tutor\Components
6 * @author Themeum
7 * @link https://themeum.com
8 * @since 4.0.0
9 */
10
11 namespace Tutor\Components;
12
13 use Tutor\Components\Constants\Positions;
14 use TUTOR\Icon;
15
16 defined( 'ABSPATH' ) || exit;
17
18 /**
19 * Class Popover
20 *
21 * Responsible for rendering popover component on button
22 * at different placement positions with footer component and menu items.
23 *
24 * Example Usage: Basic Popover
25 * ```
26 * Popover::make()
27 * ->title( 'Basic' )
28 * ->body( '<p>This is a popover component</p>' )
29 * ->closeable( true )
30 * ->trigger(
31 * Button::make()
32 * ->label( 'Show Popover' )
33 * ->attr( 'x-ref', 'trigger' )
34 * ->attr( '@click', 'toggle()' )
35 * ->size( 'medium' )
36 * ->variant( 'primary' )
37 * ->get()
38 * )
39 * ->render();
40 * ```
41 *
42 * @since 4.0.0
43 */
44 class Popover extends BaseComponent {
45
46 /**
47 * Transform origin map for popover transitions.
48 *
49 * @since 4.0.0
50 */
51 public const TRANSFORM_ORIGIN_MAP = array(
52 Positions::TOP => 'center.bottom',
53 Positions::TOP_START => 'left.bottom',
54 Positions::TOP_END => 'right.bottom',
55 Positions::BOTTOM => 'center.top',
56 Positions::BOTTOM_START => 'left.top',
57 Positions::BOTTOM_END => 'right.top',
58 Positions::LEFT => 'right.center',
59 Positions::LEFT_TOP => 'right.top',
60 Positions::LEFT_BOTTOM => 'right.bottom',
61 Positions::RIGHT => 'left.center',
62 Positions::RIGHT_TOP => 'left.top',
63 Positions::RIGHT_BOTTOM => 'left.bottom',
64 );
65
66 /**
67 * The popover title.
68 *
69 * @var string
70 */
71 protected $popover_title;
72
73 /**
74 * The popover body.
75 *
76 * @var string
77 */
78 protected $popover_body;
79
80 /**
81 * Callback function for popover body escaping.
82 *
83 * @var string
84 */
85 protected $popover_body_esc = 'wp_kses_post';
86
87 /**
88 * Allowed HTML tags and attributes. Keys are tag names and values are allowed attributes.
89 *
90 * @since 4.0.0
91 *
92 * @var array
93 */
94 protected $allowed_html_tags = array();
95
96 /**
97 * The popover placement location (left | right | top | bottom ).
98 *
99 * Default 'bottom-start'.
100 *
101 * @var string
102 */
103 protected $popover_placement = Positions::BOTTOM_START;
104
105 /**
106 * Popover button element.
107 *
108 * @var string
109 */
110 protected $popover_button;
111
112 /**
113 * Popover menu items list.
114 *
115 * @var array
116 */
117 protected $popover_menu_items;
118
119 /**
120 * Whether to show or hide popover close button.
121 *
122 * Default true.
123 *
124 * @var bool
125 */
126 protected $show_close_button = false;
127
128 /**
129 * Popover footer buttons.
130 *
131 * @var array
132 */
133 protected $popover_footer_buttons;
134
135 /**
136 * Popover footer button alignment class ( left | center ).
137 *
138 * Default 'right'.
139 *
140 * @var array
141 */
142 protected $popover_footer_alignment;
143
144 /**
145 * Popover menu item icon svg.
146 *
147 * @var string
148 */
149 protected $popover_menu_item_icon;
150
151 /**
152 * Popover menu item icon alignment.
153 *
154 * @var string
155 */
156 protected $popover_menu_item_icon_alignment;
157
158 /**
159 * Popover menu item custom class.
160 *
161 * @var string
162 */
163 protected $popover_menu_item_class;
164
165 /**
166 * Attributes of Popover menu items.
167 *
168 * @var array
169 */
170 protected $attributes;
171
172 /**
173 * Prevent close from outside
174 *
175 * @var bool
176 */
177 protected $popover_close_outside = true;
178
179 /**
180 * Menu min width.
181 *
182 * @var string
183 */
184 protected $menu_min_width;
185
186 /**
187 * Set Popover title
188 *
189 * @since 4.0.0
190 *
191 * @param string $popover_title the popover title.
192 *
193 * @return self
194 */
195 public function title( string $popover_title ): self {
196 $this->popover_title = $popover_title;
197 return $this;
198 }
199
200 /**
201 * Set Popover body html.
202 *
203 * @since 4.0.0
204 *
205 * @param string $popover_body the popover body html.
206 * @param array $allowed_html_tags html tags to allow.
207 *
208 * @return self
209 */
210 public function body( string $popover_body, array $allowed_html_tags = array() ): self {
211 $this->popover_body = $popover_body;
212 $this->allowed_html_tags = $allowed_html_tags;
213 return $this;
214 }
215
216 /**
217 * Set popover placement position.
218 *
219 * @since 4.0.0
220 *
221 * @param string $popover_placement the placement position.
222 *
223 * @return self
224 */
225 public function placement( string $popover_placement = 'bottom-start' ): self {
226 $placement_positions = array(
227 Positions::TOP,
228 Positions::TOP_START,
229 Positions::TOP_END,
230 Positions::LEFT,
231 Positions::LEFT_TOP,
232 Positions::LEFT_BOTTOM,
233 Positions::RIGHT,
234 Positions::RIGHT_TOP,
235 Positions::RIGHT_BOTTOM,
236 Positions::BOTTOM,
237 Positions::BOTTOM_START,
238 Positions::BOTTOM_END,
239 );
240 if ( ! in_array( $popover_placement, $placement_positions, true ) ) {
241 $this->popover_placement = Positions::BOTTOM_START;
242 return $this;
243 }
244
245 $this->popover_placement = $popover_placement;
246 return $this;
247 }
248
249 /**
250 * Popover button component.
251 *
252 * @since 4.0.0
253 *
254 * @param string $popover_button the popover button element html.
255 *
256 * @return self
257 */
258 public function trigger( string $popover_button ): self {
259 $this->popover_button = $popover_button;
260 return $this;
261 }
262
263 /**
264 * Whether to hide close button on popover.
265 *
266 * @since 4.0.0
267 *
268 * @param boolean $show_close_button determines whether to hide close button.
269 *
270 * @return self
271 */
272 public function closeable( bool $show_close_button = false ): self {
273 $this->show_close_button = $show_close_button;
274 return $this;
275 }
276
277 /**
278 * Set the popover footer alignment.
279 *
280 * @since 4.0.0
281 *
282 * @param string $popover_footer_alignment the popover footer alignment class.
283 *
284 * @return self
285 */
286 public function footer_alignment( string $popover_footer_alignment ): self {
287 $this->popover_footer_alignment = $popover_footer_alignment;
288 return $this;
289 }
290
291 /**
292 * Set popover footer buttons.
293 *
294 * @since 4.0.0
295 *
296 * @param array $popover_footer_buttons the footer button list.
297 *
298 * @return self
299 */
300 public function footer( array $popover_footer_buttons = array() ): self {
301 $this->popover_footer_buttons = $popover_footer_buttons;
302 return $this;
303 }
304
305 /**
306 * Menu items for creating popover menu.
307 *
308 * @since 4.0.0
309 *
310 * @param array $args {
311 * Array of arguments to be provided for the menu items.
312 *
313 * @type string $tag the tag of the menu item.
314 * @type string $class the class for the menu items.
315 * @type string $icon the icon svg.
316 * @type string $icon_alignment the alignment of menu item icon (left | right).
317 * @type string $content the popover menu item content.
318 * @type array $attr the popover menu item attributes.
319 * }
320 *
321 * @return self
322 */
323 public function menu_item( array $args ): self {
324 $menu_item_tag = isset( $args['tag'] ) ? $this->esc( $args['tag'], $this->popover_body_esc ) : '';
325 $this->popover_menu_item_class = $args['class'] ?? '';
326 $this->popover_menu_item_icon = $args['icon'] ?? '';
327 $this->popover_menu_item_icon_alignment = isset( $args['icon_alignment'] ) && in_array( $args['icon_alignment'], array( Positions::LEFT, Positions::RIGHT ), true ) ? $args['icon_alignment'] : Positions::LEFT;
328
329 $this->popover_menu_items[] = array(
330 'tag' => $menu_item_tag,
331 'content' => $args['content'] ?? '',
332 'class' => $this->popover_menu_item_class,
333 'icon' => $this->popover_menu_item_icon,
334 'icon_alignment' => $this->popover_menu_item_icon_alignment,
335 'attr' => $args['attr'] ?? array(),
336 );
337
338 return $this;
339 }
340
341 /**
342 * Set menu min width.
343 *
344 * @since 4.0.0
345 *
346 * @param string $width the min width value.
347 *
348 * @return self
349 */
350 public function menu_min_width( string $width ): self {
351 $this->menu_min_width = $width;
352 return $this;
353 }
354
355 /**
356 * Render popover header element.
357 *
358 * @since 4.0.0
359 *
360 * @return string
361 */
362 protected function render_header(): string {
363 if ( empty( $this->popover_title ) ) {
364 return '';
365 }
366
367 $title = sprintf( '<h3 class="tutor-popover-title">%s</h3>', esc_attr( $this->popover_title ) );
368
369 $close_button = sprintf(
370 '<button @click="hide()" class="tutor-popover-close" aria-label="%s">
371 %s
372 </button>',
373 esc_attr__( 'Close', 'tutor' ),
374 SvgIcon::make()->name( Icon::CROSS )->size( 14 )->get()
375 );
376
377 if ( $this->show_close_button ) {
378 return sprintf(
379 '<div class="tutor-popover-header">
380 %s
381 %s
382 </div>
383 ',
384 $title,
385 $close_button
386 );
387 }
388
389 return sprintf(
390 '<div class="tutor-popover-header">
391 %s
392 </div>
393 ',
394 $title,
395 );
396 }
397
398 /**
399 * Render popover body component.
400 *
401 * @since 4.0.0
402 *
403 * @return string
404 */
405 protected function render_body(): string {
406 if ( empty( $this->popover_body ) ) {
407 return '';
408 }
409
410 $body = wp_kses( $this->popover_body, $this->get_allowed_html_tags( $this->allowed_html_tags ) );
411
412 return sprintf(
413 '<div class="tutor-popover-body">
414 %s
415 </div>',
416 $body
417 );
418 }
419
420 /**
421 * Render footer component for popover.
422 *
423 * @since 4.0.0
424 *
425 * @return string
426 */
427 protected function render_footer(): string {
428 $footer_buttons = '';
429 $footer_alignment = in_array( $this->popover_footer_alignment, array( Positions::LEFT, Positions::CENTER ), true ) ? $this->popover_footer_alignment : '';
430
431 if ( ! tutor_utils()->count( $this->popover_footer_buttons ) ) {
432 return $footer_buttons;
433 }
434
435 $alignment = Positions::LEFT === $footer_alignment ? 'tutor-justify-start' : 'tutor-justify-center';
436
437 foreach ( $this->popover_footer_buttons as $button ) {
438 $footer_buttons .= $button;
439 }
440
441 if ( ! empty( $footer_alignment ) ) {
442 return sprintf(
443 '<div class="tutor-popover-footer %s">
444 %s
445 </div>',
446 $alignment,
447 $footer_buttons
448 );
449 }
450
451 return sprintf(
452 '<div class="tutor-popover-footer">
453 %s
454 </div>',
455 $footer_buttons
456 );
457 }
458
459 /**
460 * Handle closing popover on outside click.
461 *
462 * @since 4.0.0
463 *
464 * @param boolean $is_closeable whether allow to close from outside click.
465 *
466 * @return self
467 */
468 public function dismissible( bool $is_closeable ): self {
469 $this->popover_close_outside = $is_closeable;
470 return $this;
471 }
472
473 /**
474 * Render Menu for popover.
475 *
476 * @since 4.0.0
477 *
478 * @return string
479 */
480 protected function render_menu(): string {
481 $menu_items = '';
482 if ( ! tutor_utils()->count( $this->popover_menu_items ) ) {
483 return $menu_items;
484 }
485
486 foreach ( $this->popover_menu_items as $item ) {
487 $tag = $item['tag'] ?? 'div';
488 $content = isset( $item['content'] ) ? $this->esc( $item['content'], $this->popover_body_esc ) : '';
489 $class = isset( $item['class'] ) ? esc_attr( $item['class'] ) : '';
490 $icon = $item['icon'] ?? '';
491 $icon_alignment = $item['icon_alignment'] ?? Positions::LEFT;
492
493 $this->attributes = tutor_utils()->count( $item['attr'] ) ? $item['attr'] : array();
494
495 $menu_item_attr = $this->get_attributes_string();
496
497 if ( empty( $icon ) ) {
498 $menu_items .= sprintf(
499 '<%1$s class="tutor-popover-menu-item %2$s" %4$s>%3$s</%1$s>',
500 $tag,
501 $class,
502 $content,
503 $menu_item_attr
504 );
505 } elseif ( Positions::RIGHT === $icon_alignment ) {
506 $menu_items .= sprintf(
507 '<%1$s class="tutor-popover-menu-item %2$s" %5$s>%3$s%4$s</%1$s>',
508 $tag,
509 $class,
510 $content,
511 $icon,
512 $menu_item_attr
513 );
514 } else {
515 $menu_items .= sprintf(
516 '<%1$s class="tutor-popover-menu-item %2$s" %5$s>%4$s%3$s</%1$s>',
517 $tag,
518 $class,
519 $content,
520 $icon,
521 $menu_item_attr
522 );
523 }
524 }
525
526 $style = $this->menu_min_width ? " style=\"min-width: {$this->menu_min_width}\"" : '';
527 return sprintf( '<div class="tutor-popover-menu"%s>%s</div>', $style, $menu_items );
528 }
529
530 /**
531 * Get the final Popover HTML Component.
532 *
533 * @since 4.0.0
534 *
535 * @return string HTML content.
536 */
537 public function get(): string {
538
539 $header = $this->render_header();
540 $body = $this->render_body();
541 $placement_position = $this->popover_placement;
542 $button = $this->popover_button ?? '';
543 $footer = $this->render_footer();
544 $menu = $this->render_menu();
545
546 $placement_class = 'tutor-popover-' . explode( '-', $placement_position )[0];
547 $class = 'tutor-popover ' . $placement_class;
548
549 $closeable_attr = $this->popover_close_outside ? '@click.outside="handleClickOutside()"' : '';
550
551 $origin = self::TRANSFORM_ORIGIN_MAP[ $placement_position ] ?? 'center.top';
552
553 return sprintf(
554 '<div x-data="tutorPopover({ placement: \'%s\' })">
555 %s
556 <div
557 x-ref="content"
558 x-show="open"
559 x-cloak
560 x-transition.%s
561 class="%s"
562 %s
563 >
564 %s
565 %s
566 %s
567 %s
568 </div>
569 </div>',
570 $placement_position,
571 $button,
572 $origin,
573 $class,
574 $closeable_attr,
575 $header,
576 $body,
577 $footer,
578 $menu
579 );
580 }
581 }
582