Controller
```
use app\widgets\forms\ModalInquiryForm;

class ProductController extends BaseController
{
    public function actionView(int $id): string
    {
        $model = $this->findModel($id);
        
        // Modal Inquiry Form needs product ID and product slug
        $formModal = new ModalInquiryForm($model->id, $model->slug);
        
        
        return $this->render('view', [
            'model' => $model,
            'formModal' => $formModal,
        ]);
    }
}
```
the Modal Inquiry Form
```
namespace app\widgets\forms;

// PHP built-in exception:
use \Throwable;

use Yii;
use yii\base\Model;
use yii\base\Exception as YiiBaseException;
use yii\db\Exception as YiiDbException;
use yii\base\InvalidArgumentException as YiiBaseInvalidArgumentException;

use app\models\Message;
use app\models\Product;
use app\helpers\ExceptionLogger;
use app\helpers\ParamHelper;

class ModalInquiryForm extends Model
{
    /**
    * Note: Public typed properties must be initialized with default 
    * values (e.g. empty string) to avoid "must not be accessed before 
    * initialization" PHP errors.
    */
    public int $productId = 0;
    public string $productSlug = '';
    public string $name = '';
    public string $email = '';
    public string $message = '';
    public string $verifyCode = '';
    
    
    // Private property to hold the validated product.
    private ?Product $_product = null;
    
    // @var Product $product getters
    private string $_title;
    private ?string $_artist;
    private ?string $_medium;
    private ?string $_dimensions;
    private ?int $_year;
    private ?int $_price;
    private ?int $_quantity;
    
    // @var ParamHelper to class property.
    private string $_emailWebmaster;
    private string $_emailSender;
    private string $_emailSenderName;
    
    public function __construct(int $productId, string $productSlug, array $config = [])
    {
        // Retrieve the product object based on product ID.
        $this->_product = Product::find()->where(['id' => $productId])->andWhere(['slug' => $productSlug])->one();
        
        if (!$this->_product)
        {
            throw new YiiBaseInvalidArgumentException('Unable to find product for inquiry modal form.');
        }
        
        // Valid product found, initialize properties based on the product
        $this->productId = $this->_product->id;
        $this->productSlug = $this->_product->slug;
        $this->_title = $this->_product->title;
        $this->_artist = $this->_product->artist ?? '';
        $this->_medium = $this->_product->medium ?? '';
        $this->_dimensions = $this->_product->dimensions ?? '';
        $this->_year = $this->_product->year ?? null;
        $this->_price = $this->_product->price ?? null;
        $this->_quantity = $this->_product->quantity ?? null;
        
        // @var ParamHelper to class property.
        $this->_emailWebmaster = ParamHelper::get('email.webmaster');
        $this->_emailSender = ParamHelper::get('email.sender');
        $this->_emailSenderName = ParamHelper::get('email.senderName');
        
        parent::__construct($config);
    }
    
    public function rules(): array
    {
        return [
            [['name', 'email', 'message'], 'required'],
            
            // Name.
            [['name'], 'trim'],
            [['name'], 'string', 'min' => 1, 'max' => 255],
            // Accept letters with some special characters.
            ['name', 'match', 'pattern' => '/^[\p{L} .\'-]+$/u', 
                'message' => \Yii::t(
                    'app',
                    'Names can only contain letters, spaces, and certain characters.'
                )
            ],
            
            // Email has to be a valid email address.
            ['email', 'email'],
            
            // Message.
            ['message', 'string'],
            
            // verifyCode needs to be entered correctly
            ['verifyCode', 'captcha'],
        ];
    }
    
    public function attributeLabels(): array
    {
        return [
            'name' => \Yii::t('app', 'Full Name'),
            'email' => \Yii::t('app', 'Email'),
            'message' => \Yii::t('app', 'Message'),
            'verifyCode' => \Yii::t('app', 'Verification Code'),
        ];
    }
    
    public function process(): bool
    {
        $productDetails = "Product Information:\n";
        $productDetails .= "Title: " . $this->_product->title . "\n";
        $productDetails .= "Artist: " . $this->_product->artist . "\n";
        $productDetails .= "Medium: " . $this->_product->medium . "\n";
        $productDetails .= "Dimensions: " . $this->_product->dimensions . "\n";
        $productDetails .= "Year: " . $this->_product->year . "\n";
        $productDetails .= "Price: " . $this->_product->price . "\n";
        $productDetails .= "Quantity: " . $this->_product->quantity . "\n\n";
    
        $transaction = \Yii::$app->db->beginTransaction();
        try
        {
            $message = new Message();
            $message->type = Message::TYPE_INQUIRY;
            $message->subject = $this->_product->title . "by: " . $this->_product->artist;
            $message->name = $this->name;
            $message->email = $this->email;
            $message->body = $productDetails . "User Message:\n" . $this->message;
            
            // Set Status unread
            $message->addStatus(Message::STATUS_UNREAD);
            
            // Use 'false' to skip validation as model is already validated.
            if (!$message->save(false))
            {
                throw new YiiDbException("Failed to save message data to the database.");
            }
            
            // If all saves are successful, commit the transaction.
            $transaction->commit();
            
            // Pass the message object to the sendEmail function.
            return $this->sendEmail($message);
        }
        catch (Throwable $e)
        {
            // An error occurred; roll back the transaction.
            $transaction->rollBack();
            
            // Use the ExceptionLogger to handle and log the exception.
            ExceptionLogger::log($e, 'modal_inquiry_form_error_');
        }
        
        return false;
    }
    
    private function sendEmail(Message $message): bool
    {
        try
        {
            $isSent = \Yii::$app->mailer->compose()
                ->setTo($this->_emailWebmaster)
                ->setFrom([$this->_emailSender => $this->_emailSenderName])
                ->setReplyTo([$this->email => $this->name])
                ->setSubject($message->subject)
                ->setTextBody($message->body)
                ->send();
            
            if (!$isSent)
            {
                throw new YiiBaseException("The email could not be sent to {$this->_emailWebmaster}.");
            }
            
            return true;
        }
        catch (Throwable $e)
        {
            // Use the ExceptionLogger to handle and log the exception.
            ExceptionLogger::log($e, 'modal_inquiry_form_email_error_');
        }
        
        return false;
    }
    
    public function getTitle(): string
    {
        return $this->_title;
    }
    
    public function getArtist(): ?string
    {
        return $this->_artist;
    }
    
    public function getMedium(): ?string
    {
        return $this->_medium;
    }
    
    public function getDimensions(): ?string
    {
        return $this->_dimensions;
    }
    
    public function getYear(): ?int
    {
        return $this->_year;
    }
    
    public function getPrice(): ?int
    {
        return $this->_price;
    }
    
    public function getQuantity(): ?int
    {
        return $this->_quantity;
    }
}
```
The view file
```
use yii\bootstrap5\ActiveForm;
use yii\bootstrap5\Html;
use yii\captcha\Captcha;

use app\helpers\ValueHelper;
?>


<style>
span.artwork-detail
{
	font-size: 14px;/* Smaller font size */
	color: #555;/* Dark gray for subtle appearance */
	margin-bottom: 5px;/* Space between lines */
}
</style>


<div id="modal-jqry-form">

<div class="row mb-3">
<div class="col-sm-6">
<span class="artwork-detail"><?= \Yii::t('app', 'Title: {title}', ['title' => Html::encode($model->getTitle())]); ?></span><br />

<?php if (ValueHelper::isSetNotNull($model->getArtist())) : ?>
<span class="artwork-detail"><?= \Yii::t('app', 'Artist: {artist}', ['artist' => Html::encode($model->getArtist())]); ?></span><br />
<?php endif; ?>

<?php if (ValueHelper::isSetNotNull($model->getDimensions())) : ?>
<span class="artwork-detail"><?= \Yii::t('app', 'Dimensions: {dimensions}', ['dimensions' => Html::encode($model->getDimensions())]); ?></span><br />
<?php endif; ?>
</div><!-- .col-sm-6 -->

<div class="col-sm-6">
<?php if (ValueHelper::isSetNotNull($model->getMedium())) : ?>
<span class="artwork-detail"><?= \Yii::t('app', 'Medium: {medium}', ['medium' => Html::encode($model->getMedium())]); ?></span><br />
<?php endif; ?>

<?php if (ValueHelper::isSetNotNull($model->getYear())) : ?>
<span class="artwork-detail"><?= \Yii::t('app', 'Year: {year}', ['year' => Html::encode($model->getYear())]); ?></span><br />
<?php endif; ?>
</div><!-- .col-sm-6 -->

<?php /*<div class="col-sm-6 mt-4">
<span class="artwork-detail"><?= \Yii::t('app', 'Price: {price}', ['price' => Html::encode($model->getPrice())]); ?></span><br />
<span class="artwork-detail"><?= \Yii::t('app', 'Quantity: {quantity}', ['quantity' => Html::encode($model->getQuantity())]); ?></span><br />
</div><!-- .col-sm-6 -->*/ ?>
</div><!-- .row -->

<div class="row justify-content-center">
<div class ="col">
<?php
$form = ActiveForm::begin([
	'id' => $id . '-form',// id: inquiry-modal-form
	'action' => ['modal-widget/inquiry'],
	'enableAjaxValidation' => true,
	'method' => 'post',
]);
?>
<div class="row">


<div class="col-md-6 mb-2">
<?= $form->field($model, 'name')->textInput([
	'class' => 'form-control square',
	'placeholder' => \Yii::t('app','enter full name'),
])->label(\Yii::t('app', 'your name')); ?>
</div><!-- .col-md-6 .mb-2 -->

<div class="col-md-6 mb-2">
<?= $form->field($model, 'email')->textInput([
	'class' => 'form-control square',
	'placeholder' => \Yii::t('app','enter email'),
])->label(\Yii::t('app', 'your email')); ?>
</div><!-- .col-md-6 .mb-2 -->


</div><!-- .row -->
<div class="row">


<div class="col-md-12 mb-2">
<?= $form->field($model, 'message')->textarea([
	'class' => 'form-control square',
	'rows' => 6,
	'placeholder' =>  \Yii::t('app', 'Type your message here...'),
]); ?>
</div><!-- .col-md-12 .mb-2 -->

<div class="col-md-12 mb-2">
<?php
$verifyHint = \Yii::t('app', 'Click the image to reload a new verification code.');
echo $form->field($model, 'verifyCode')->label(\Yii::t('app', 'Enter Security Code'))->widget(Captcha::class, [
    'imageOptions' => [
        'title' =>  \Yii::t('app', 'Click to refresh'),
        'class' => 'mb-1',
    ],
    'options' => [
        'class' => 'form-control square',
    ],
    'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
])->hint($verifyHint);
?>
</div><!-- .col-md-12 .mb-2 -->

<?= $form->field($model, 'productId')->hiddenInput()->label(false); ?>
<?= $form->field($model, 'productSlug')->hiddenInput()->label(false); ?>

<div class="text-left">
<?= Html::submitButton(\Yii::t('app', 'Submit'), [
	'id' => $id . '-submit',// id: inquiry-modal-submit
	'class' => 'btn btn-primary square',
	'name' => 'inquiry-button',
]); ?>
</div><!-- .text-left -->


</div><!-- .row -->
<?php ActiveForm::end(); ?>
</div><!-- .active-form .col -->
</div><!-- .row .justify-content-center -->


</div><!-- #modal-jqry-form -->
```

The ModalJQry widget
```
namespace app\widgets;

use Yii;
use yii\base\Widget;
use yii\base\InvalidArgumentException as YiiBaseInvalidArgumentException;
use yii\bootstrap5\Modal as BootstrapModal;
use yii\helpers\Html;
use yii\web\View;

use app\components\traits\CacheWidgetTrait;
use app\helpers\LogHelper;
use app\helpers\ParamHelper;
use app\helpers\TimeHelper;

class ModalJQry extends Widget
{
	// @var array Options for modal.
	public array $options = [];
	
	// @var string Modal title
	public string $title = 'Modal Title';
	
	// @var string Modal size
	public string $size = 'modal-lg';
	
	// This property will hold the instance of the form model passed
	public object $formModel;
	
	// Optional property to enforce a specific form model class
	public ?string $expectedFormModelClass = null;
	
	// Default modal view
	public string $view = '_defaultModal';
	
	// View params
	public array $viewParams = [];
	
	
	// Note: Every protected variable below CacheWidgetTrait is used by the trait
	use CacheWidgetTrait;
	
	protected bool $_debugMode = false;
	protected bool $_compressJsCss = false;
	protected bool $_cacheEnable = false;
	protected int $_cacheJsCssTime = 3600;// +1 hour
	protected string $_cacheKeyPrefix = 'widget_modalJQry_Data_';
	
	const DEFAULT_DURATION_SECONDS = 3600;// +1 hour
	
	/**
	* Initializes the widget.
	* 
	* Note: 
	* The check for required properties like $formModel must be done here in `init()`
	* instead of the constructor because Yii applies the widget configuration (i.e., 
	* assigning public properties passed in via the config array) *after* the constructor 
	* is called but *before* `init()` is invoked.
	*/
	public function init()
	{
		/**
		* Always call the parent init method first to ensure any
		* initialization logic defined in the base class is executed first.
		*/
		parent::init();
		
		// ... initialization after configuration is applied...
		
		if (!isset($this->formModel))
		{
			throw new YiiBaseInvalidArgumentException('The "model" property must be set.');
		}
		
		if ($this->expectedFormModelClass !== null && 
			!($this->formModel instanceof $this->expectedFormModelClass))
		{
			throw new InvalidArgumentException(
				'The "model" property must be an instance of ' . $this->expectedFormModelClass
			);
		}
		
		// Get cacheDuration from Param and set with TimeHelper
		$cacheDuration = ParamHelper::get('widgets.cacheDuration');
		$this->_cacheJsCssTime = TimeHelper::convertDurationToSeconds($cacheDuration);
		
		// Cache & Compression
		$this->_cacheEnable = ParamHelper::get('widgets.cacheEnable');
		$this->_compressJsCss = ParamHelper::get('widgets.compressJsCss');
	}
		
	/**
	* Runs the widget and renders the modal with the form.
	* @return void
	*/
	public function run(): void
	{
		// @var ID string - Generate a unique ID for the input field if not provided.
		$id = $this->options['id'] ?? $this->getId();
		
		// Render the Bootstrap modal.
		BootstrapModal::begin([
			'id' => $id,
			'title' => $this->title,
			'options' => ['class' => ['modal fade']],
			'size' => $this->size,
		]);
		
		/**
		* Render the Bootstrap Modal with the Inquiry Form.
		*
		* The model is accessible via $this->formModel, which eliminates the 
		* need to pass it repeatedly through different methods.
		*/
		$params = array_merge(['model' => $this->formModel, 'id' => $id], $this->viewParams);
		echo $this->render($this->view, $params);
		
		// End the modal definition.
		BootstrapModal::end();
		
		// Register JavaScript for form submission handling.
		$this->registerJs($id);
	}
	
	public static function button(string $label, string $modalId, array $options = []): string
	{
		$options = array_merge([
			'type' => 'button',
			'class' => 'btn btn-primary',
			'data-bs-toggle' => 'modal',
			'data-bs-target' => "#{$modalId}",
		], $options);
		
		$attrString = '';
		
		foreach ($options as $key => $value)
		{
			$attrString .= " {$key}=\"" . htmlspecialchars($value, ENT_QUOTES) . "\"";
		}
		
		return "<button{$attrString}>{$label}</button>";
	}
	
	/**
	* Registers the JavaScript necessary for form submission via AJAX.
	* @param string $id The unique identifier for the modal.
	*/
	protected function registerJs(string $id): void
	{
		$cacheKey = $this->_cacheKeyPrefix . $id . '_js_block_';
		$jsBlock = $this->getCachedJsBlock($cacheKey, function () use ($id)
		{
			return <<<JS
				(function(\$)
				{
					/* DOM ready shorthand */
					\$(function()
					{
						/* Move modal div to body to fix z-index and stacking issues */
						 \$('#{$id}').appendTo('body');
					
						/* Define the context as the inquiry form container. */
						var \$container = \$('#modal-jqry-form');
						
						if (!\$container.length)
						{
							return;
						}
						
						var \$form = \$container.find('#{$id}-form');
						var \$submitButton = \$container.find('#{$id}-submit');
						var \$messageContainer = \$container.find('#message-container');
						var modal = document.getElementById('{$id}'); // modal element
						
						if (!\$form.length || !\$submitButton.length || !\$messageContainer.length)
						{
							return;
						}
						
						\$form.on('submit', function(event)
						{
							event.preventDefault();
							
							\$submitButton.prop('disabled', true).text('Sending...');
							
							/* Serialize form data */
							var formData = new FormData(this);
							
							fetch(this.action,
							{
								method: 'POST',
								body: formData,
								headers: {
									/* To identify AJAX request on server */
									'X-Requested-With': 'XMLHttpRequest'
								}
							})
							.then(function(response)
							{
								return response.json();
							})
							.then(function(data)
							{
								/* Clear previous messages and validation errors */
								\$messageContainer.empty();
								\$form.find('.invalid-feedback').remove();
								\$form.find('.is-invalid').removeClass('is-invalid');
								
								if (data.success)
								{
									\$messageContainer.html('<div class="alert alert-success">' + data.message + '</div>');
									
									setTimeout(function()
									{
										\$messageContainer.empty();
										\$form[0].reset();
										
										/* Close Bootstrap 5 modal if available */
										if (modal && window.bootstrap && bootstrap.Modal)
										{
											var bsModal = bootstrap.Modal.getInstance(modal);
											
											if (bsModal)
											{
												bsModal.hide();
											}
										}
									},
									2000);
								}
								else if (data.errors)
								{
									/* Show validation errors */
									\$.each(data.errors, function(field, messages)
									{
										var \$inputField = \$form.find('[name="' + field + '"]');
										
										if (\$inputField.length)
										{
											\$inputField.addClass('is-invalid');
											
											var \$feedbackDiv = \$('<div>')
												.addClass('invalid-feedback')
												.text(messages.join(', '));
											
											\$inputField.after(\$feedbackDiv);
										}
									});
								}
							})
							.catch(function()
							{
								\$messageContainer.html('<div class="alert alert-danger">An error occurred while sending the inquiry. Please try again later.</div>');
							})
							.finally(function()
							{
								\$submitButton.prop('disabled', false).text('Submit');
							});
						});
					});
				})(jQuery);
			JS;
		});
		
		// Register the JavaScript.
		$this->getView()->registerJs($jsBlock, View::POS_END);
	}
}
```

Getting some weird flickering and depending on where I postition:

```
<?php
use app\widgets\ModalJQry;
use app\widgets\forms\ModalInquiryForm;
?>


<?= ModalJQry::button('Open Inquiry Form', 'inquiry-modal') ?>


// Keep towards the bottom of the page
<?= ModalJQry::widget([
	//'id' => 'inquiry-modal',
	'formModel' => $formModal,
	// Enforce exact model class
	'expectedFormModelClass' => ModalInquiryForm::class,
	'view' => '_modalInquiry',// support view is default
	'options' => ['id' => 'inquiry-modal'],
]); ?>
```

in the view file using ModalJQry widget... the z-index works or doesnt.. but the flicking on pageloads is a major issue.