HEX
Server: LiteSpeed
System: Linux s787.bom1.mysecurecloudhost.com 4.18.0-477.13.1.lve.el8.x86_64 #1 SMP Thu Jun 1 16:40:47 EDT 2023 x86_64
User: mobilech (5348)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/mobilech/alliancelaptoptraining.com.np/wp-content/plugins/tutor/components/Modal.php
<?php
/**
 * Tutor Component: Modal
 *
 * Provides a fluent builder for rendering modals with Alpine.js integration.
 *
 * @package Tutor\Components
 * @author Themeum
 * @link https://themeum.com
 * @since 4.0.0
 */

namespace Tutor\Components;

defined( 'ABSPATH' ) || exit;

/**
 * Modal Component Class.
 *
 * ```
 * // Example usage:
 *
 * Modal::make()
 *     ->id( 'confirm-modal' )
 *     ->title( 'Confirm Submission' )
 *     ->subtitle( 'Are you sure you want to submit?' )
 *     ->body( 'This action cannot be undone.' )
 *     ->footer_buttons( '<button class="tutor-btn">Cancel</button>' )
 *     ->footer_alignment( 'right' )
 *     ->render();
 *
 * // With template path
 * Modal::make()
 *     ->id( 'course-modal' )
 *     ->title( 'Course Details' )
 *     ->template( 'path/to/template.php' )
 *     ->render();
 *
 * // Headless modal (no close button)
 * Modal::make()
 *     ->id( 'headless-modal' )
 *     ->closeable( false )
 *     ->body( '<h3>Success!</h3>' )
 *     ->render();
 *
 * // Open by default (state is 'closed' by default)
 * Modal::make()
 *     ->id( 'welcome-modal' )
 *     ->title( 'Welcome' )
 *     ->body( 'Please confirm to continue.' )
 *     ->state( 'open' )
 *     ->render();
 * ```
 *
 * @since 4.0.0
 */
class Modal extends BaseComponent {

	/**
	 * Modal unique ID.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $id = '';

	/**
	 * Modal title.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $title = '';

	/**
	 * Modal title icon.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $title_icon = '';

	/**
	 * Modal title esc func.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $title_esc_cb = 'esc_html';

	/**
	 * Modal subtitle.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $subtitle = '';

	/**
	 * Modal subtitle esc func.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $subtitle_esc_cb = 'esc_html';

	/**
	 * Modal body content or template path.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $body = '';

	/**
	 * Modal body content esc callback.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $body_esc_cb = 'wp_kses_post';

	/**
	 * Whether body is a template path.
	 *
	 * @since 4.0.0
	 *
	 * @var bool
	 */
	protected $is_template = false;

	/**
	 * Template data.
	 *
	 * @since 4.0.0
	 *
	 * @var array
	 */
	protected $template_data = array();

	/**
	 * Footer buttons HTML.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $footer_buttons = '';

	/**
	 * Footer alignment (left|center|right).
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $footer_alignment = 'right';

	/**
	 * Whether modal is closeable.
	 *
	 * @since 4.0.0
	 *
	 * @var bool
	 */
	protected $closeable = true;

	/**
	 * Initial modal state: 'open' or 'closed'.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $state = 'closed';

	/**
	 * Custom width for modal content.
	 *
	 * @since 4.0.0
	 *
	 * @var string
	 */
	protected $width = '';

	/**
	 * Set modal ID.
	 *
	 * @since 4.0.0
	 *
	 * @param string $id Modal unique ID.
	 *
	 * @return $this
	 */
	public function id( $id ) {
		$this->id = sanitize_key( $id );
		return $this;
	}

	/**
	 * Set modal title.
	 *
	 * @since 4.0.0
	 *
	 * @param string $title Modal title.
	 * @param string $esc_cb Callable escaping function.
	 *
	 * @return $this
	 */
	public function title( string $title, $esc_cb = 'esc_html' ) {
		$this->title        = $title;
		$this->title_esc_cb = $esc_cb;
		return $this;
	}

	/**
	 * Set modal title icon.
	 *
	 * @since 4.0.0
	 *
	 * @param string $title_icon the modal title icon name.
	 *
	 * @return $this
	 */
	public function title_icon( string $title_icon ) {
		$this->title_icon = $title_icon;
		return $this;
	}

	/**
	 * Set modal subtitle.
	 *
	 * @since 4.0.0
	 *
	 * @param string $subtitle Modal subtitle.
	 * @param string $esc_cb Callable escaping function.
	 *
	 * @return $this
	 */
	public function subtitle( string $subtitle, $esc_cb = 'esc_html' ) {
		$this->subtitle        = $subtitle;
		$this->subtitle_esc_cb = $esc_cb;
		return $this;
	}

	/**
	 * Set modal body content.
	 *
	 * @since 4.0.0
	 *
	 * @param string $body Body content HTML.
	 * @param string $esc_cb Body content HTML.
	 *
	 * @return $this
	 */
	public function body( $body, $esc_cb = 'wp_kses_post' ) {
		$this->body        = $body;
		$this->body_esc_cb = $esc_cb;
		$this->is_template = false;
		return $this;
	}

	/**
	 * Set modal body from template path.
	 *
	 * @since 4.0.0
	 *
	 * @param string $path Template file path.
	 * @param array  $data Template data.
	 *
	 * @return $this
	 */
	public function template( $path, $data = array() ) {
		$this->body          = $path;
		$this->is_template   = true;
		$this->template_data = $data;
		return $this;
	}

	/**
	 * Set footer buttons HTML.
	 *
	 * @since 4.0.0
	 *
	 * @param string $buttons Footer buttons HTML.
	 *
	 * @return $this
	 */
	public function footer_buttons( $buttons ) {
		$this->footer_buttons = $buttons;
		return $this;
	}

	/**
	 * Set footer button alignment.
	 *
	 * @since 4.0.0
	 *
	 * @param string $alignment Alignment (left|center|right).
	 *
	 * @return $this
	 */
	public function footer_alignment( $alignment ) {
		$allowed = array( 'left', 'center', 'right' );
		if ( in_array( $alignment, $allowed, true ) ) {
			$this->footer_alignment = $alignment;
		}
		return $this;
	}

	/**
	 * Set whether modal is closeable.
	 *
	 * @since 4.0.0
	 *
	 * @param bool $closeable Whether modal can be closed.
	 *
	 * @return $this
	 */
	public function closeable( $closeable = true ) {
		$this->closeable = (bool) $closeable;
		return $this;
	}

	/**
	 * Set initial modal state (open or closed).
	 *
	 * @since 4.0.0
	 *
	 * @param string $state Either 'open' or 'closed'. Default is 'closed'.
	 *
	 * @return $this
	 */
	public function state( string $state ) {
		$this->state = 'open' === $state ? 'open' : 'closed';
		return $this;
	}

	/**
	 * Set custom width for modal content.
	 *
	 * @since 4.0.0
	 *
	 * @param string $width Width value (e.g., '354px', '50%').
	 *
	 * @return $this
	 */
	public function width( $width ) {
		$this->width = $width;
		return $this;
	}

	/**
	 * Render modal header.
	 *
	 * @since 4.0.0
	 *
	 * @return string Header HTML.
	 */
	protected function render_header() {
		if ( empty( $this->title ) && empty( $this->subtitle ) ) {
			return '';
		}

		$icon = '';

		$title_html = $this->title
			? sprintf( '<div class="tutor-modal-title">%s</div>', $this->esc( $this->title, $this->title_esc_cb ) )
			: '';

		$subtitle_html = $this->subtitle
			? sprintf( '<div class="tutor-modal-subtitle">%s</div>', $this->esc( $this->subtitle, $this->subtitle_esc_cb ) )
			: '';

		if ( ! empty( $this->title_icon ) ) {
			ob_start();
			SvgIcon::make()->name( $this->title_icon )->size( 24 )->render();
			$icon = ob_get_clean();

			return sprintf(
				'<div class="tutor-modal-header">
						<div class="tutor-flex tutor-items-center tutor-gap-4">
							%3$s%1$s
						</div>
						%2$s
					</div>',
				$title_html,
				$subtitle_html,
				$icon,
			);

		}

		return sprintf(
			'<div class="tutor-modal-header">%s%s</div>',
			$title_html,
			$subtitle_html
		);
	}

	/**
	 * Render modal body.
	 *
	 * @since 4.0.0
	 *
	 * @return string Body HTML.
	 */
	protected function render_body() {
		if ( empty( $this->body ) ) {
			return '';
		}

		$content = '';

		if ( $this->is_template ) {
			if ( file_exists( $this->body ) ) {
				ob_start();
				$data = $this->template_data;
				include $this->body;
				$content = ob_get_clean();
				return $content ? $content : '';
			}
		} else {
			$content = $this->esc( $this->body, $this->body_esc_cb );
		}

		return sprintf( '<div class="tutor-modal-body">%s</div>', $content );
	}

	/**
	 * Render modal footer.
	 *
	 * @since 4.0.0
	 *
	 * @return string Footer HTML.
	 */
	protected function render_footer() {
		if ( empty( $this->footer_buttons ) ) {
			return '';
		}

		$alignment_class = '';
		if ( 'left' === $this->footer_alignment ) {
			$alignment_class = ' tutor-justify-start';
		} elseif ( 'center' === $this->footer_alignment ) {
			$alignment_class = ' tutor-justify-center';
		}

		return sprintf(
			'<div class="tutor-modal-footer%s">%s</div>',
			esc_attr( $alignment_class ),
			$this->footer_buttons
		);
	}

	/**
	 * Get the modal HTML.
	 *
	 * @since 4.0.0
	 *
	 * @return string HTML output.
	 */
	public function get(): string {
		if ( empty( $this->id ) ) {
			return '';
		}

		// Build Alpine.js x-data.
		$alpine_data = array(
			'id'          => $this->id,
			'initialOpen' => 'open' === $this->state,
		);
		if ( ! $this->closeable ) {
			$alpine_data['isCloseable'] = false;
		}

		$alpine_json = wp_json_encode( $alpine_data );

		// Build close button.
		$close_button = $this->closeable
			? '<button x-data="tutorIcon({ name: \'cross\', width: 16, height: 16})" x-bind="getCloseButtonBindings()" aria-label="' . esc_attr__( 'Close', 'tutor' ) . '"></button>'
			: '';

		// Build style attribute for custom width.
		$style_attr = $this->width
			? sprintf( ' style="max-width: %s;"', esc_attr( $this->width ) )
			: '';

		// Build modal content.
		$header = $this->render_header();
		$body   = $this->render_body();
		$footer = $this->render_footer();

		$this->component_string = sprintf(
			'<div x-data="tutorModal(%s)" x-cloak style="display: none;">
				<template x-teleport="body">
					<div x-bind="getModalBindings()">
						<div x-bind="getBackdropBindings()"></div>
						<div x-bind="getModalContentBindings()"%s>
							%s
							%s
							%s
							%s
						</div>
					</div>
				</template>
			</div>',
			esc_attr( $alpine_json ),
			$style_attr,
			$close_button,
			$header,
			$body,
			$footer
		);

		return $this->component_string;
	}
}