balihr:
I hate saying this, but... Told ya :P
You don't want to be enlarging the container because images are responsive and will always try to fill up as much space as they can, which then results in the bottom part of the image being "out of screen". This is really bad for User Experience and should NEVER be pushed for. If you want to allow people to see details on the photo, you need another library to allow zooming, for example ElevateZoom. If you just want to display larger images, you want to adapt them to the user's viewport. Otherwise, you could upload a 5000x5000px image and let the user go wild with the horizontal and vertical scrollbars...
The "solution" I gave you originally was the simplest, although not the optimal one. Anything more than that was simply going against the above.
I had to install the template on my dev site and play with it because the solution was not a super-simple one. Here's my final version, but please note that it includes modifications to template files as well - the Bootstrap template has some bugs and uses a combination of BS3 and BS4 markup so I opted for BS4.
It's a shame that this template isn't being updated to BS5 - both 3 and 4 are obsolete and quite hard to work with nowadays, especially when there's SO many helpful articles and code snippets available for BS5. All of my work if focused on BS5 so I might be missing something here, in which case I hope someone here will correct me and provide better code.
What this does?
It will always take the full viewport height to display the modal without vertical scroll, thumbs will always be placed at the bottom and the main image will take up all remaining space on top. In other words, it gets as big as possible on the current screen without any elements "dropping out" from the viewport.
Here you go, step by step:
includes/templates/bootstrap/templates/tpl_bootstrap_images.php : moved controls out of carousel-inner, removed .item class, removed container, row and col:
<?php
/**
* New Modal for popup_image_additional carousel
*
* BOOTSTRAP v3.7.6
*
* @package templateSystem
* @copyright Copyright 2003-2016 Zen Cart Development Team
* @copyright Portions Copyright 2003 osCommerce
* @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0
*/
if (!defined('IMAGE_ADDITIONAL_DISPLAY_LINK_EVEN_WHEN_NO_LARGE')) {
define('IMAGE_ADDITIONAL_DISPLAY_LINK_EVEN_WHEN_NO_LARGE', 'Yes');
}
?>
<!-- Modal -->
<!-- BOOTSTRAP -->
<div class="modal fade bootstrap-slide-modal-lg" tabindex="-1" role="dialog" aria-labelledby="bootStrapImagesModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="bootStrapImagesModalLabel"><?= $products_name ?></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="<?= TEXT_MODAL_CLOSE ?>"><span
aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<div id="productImagesCarousel" class="carousel slide">
<!-- main slider carousel items -->
<div class="carousel-inner text-center">
<?php
require DIR_WS_MODULES . zen_get_module_directory('main_product_image.php');
?>
<div class="active carousel-item"
data-slide-number="0"><?= zen_image($products_image_large) ?></div>
<?php
require DIR_WS_MODULES . zen_get_module_directory('bootstrap_slide_additional_images.php');
if ($flag_show_product_info_additional_images !== '0' && $num_images > 0) {
if (is_array($list_box_contents)) {
for ($row = 0, $rn = count($list_box_contents); $row < $rn; $row++) {
$params = '';
for ($col = 0, $cn = count($list_box_contents[$row]); $col < $cn; $col++) {
$r_params = '';
if (isset($list_box_contents[$row][$col]['params'])) {
$r_params .= ' ' . (string)$list_box_contents[$row][$col]['params'];
}
if (isset($list_box_contents[$row][$col]['text'])) {
echo '<div' . $r_params . '>' . $list_box_contents[$row][$col]['text'] . '</div>';
}
}
}
}
}
?>
</div>
<!-- main slider carousel nav controls -->
<div id="carousel-btn-toolbar" class="btn-toolbar justify-content-between p-3" role="toolbar">
<a class="carousel-control-prev left pt-3" data-slide="prev"
data-target="#productImagesCarousel">
<i class="fas fa-chevron-left" title="<?= BUTTON_PREVIOUS_ALT ?>"></i>
</a>
<a class="carousel-control-next right pt-3" data-slide="next"
data-target="#productImagesCarousel">
<i class="fas fa-chevron-right" title="<?= BUTTON_NEXT_ALT ?>"></i>
</a>
</div>
<ul class="carousel-indicators list-inline mx-auto justify-content-center py-3">
<li class="list-inline-item active">
<a id="carousel-selector-0" class="selected" data-slide-to="0"
data-target="#productImagesCarousel">
<?php
require DIR_WS_MODULES . zen_get_module_directory('main_product_image.php');
?>
<?= zen_image($products_image_large, $products_name, SMALL_IMAGE_WIDTH, SMALL_IMAGE_HEIGHT) ?>
</a>
</li>
<?php
require DIR_WS_MODULES . zen_get_module_directory('bootstrap_additional_images.php');
if ($flag_show_product_info_additional_images !== '0' && $num_images > 0) {
if (is_array($list_box_contents) > 0) {
for ($row = 0, $rn = count($list_box_contents); $row < $rn; $row++) {
$params = '';
for ($col = 0, $cn = count($list_box_contents[$row]); $col < $cn; $col++) {
$r_params = '';
if (isset($list_box_contents[$row][$col]['params'])) {
$r_params .= ' ' . (string)$list_box_contents[$row][$col]['params'];
}
if (isset($list_box_contents[$row][$col]['text'])) {
echo '<li' . $r_params . '>' . $list_box_contents[$row][$col]['text'] . '</li>';
}
}
}
}
}
?>
</ul>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?= TEXT_MODAL_CLOSE ?></button>
</div>
</div>
</div>
</div>
```
>
> **includes/modules/bootstrap/bootstrap_slide_additional_images.php** : removed .item class, moved $slideNumber++ to the end as it was causing incorrect data-slide sequence
> ```
<?php
/**
* additional_images module
*
* BOOTSTRAP v3.7.1
*
* Prepares list of additional product images to be displayed in template
*
* @package templateSystem
* @copyright Copyright 2003-2016 Zen Cart Development Team
* @copyright Portions Copyright 2003 osCommerce
* @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0
* @version $Id: Author: DrByte Wed Jan 6 12:47:43 2016 -0500 Modified in v1.5.5 $
*/
if (!defined('IS_ADMIN_FLAG')) {
die('Illegal Access');
}
$zco_notifier->notify('NOTIFY_MODULES_ADDITIONAL_PRODUCT_IMAGES_START');
$images_array = [];
// do not check for additional images when turned off
if ($products_image !== '' && $flag_show_product_info_additional_images !== '0') {
$products_image_info = pathinfo($products_image);
$products_image_extension = $products_image_info['extension']; //-Note, does not include the leading '.'!
$products_image_base = $products_image_info['filename'];
$products_image_directory = $products_image_info['dirname'];
// -----
// Additional images in subdirectories *always" require an intervening '_' to match.
// So do those in the /images root if we're running on zc210 or later and the
// additional images' "mode" setting indicates that we're running in 'strict' mode,
// in which case the intervening '_' is also needed.
//
zen_define_default('ADDITIONAL_IMAGES_MODE', 'legacy');
if (ADDITIONAL_IMAGES_MODE === 'legacy' && $products_image_directory === '.') {
$products_image_base .= '?';
$products_image_directory = '';
} else {
$products_image_base .= '_';
$products_image_directory .= '/';
}
$products_image_directory = DIR_WS_IMAGES . $products_image_directory;
// Check for additional matching images
foreach (glob($products_image_directory . $products_image_base . '*.' . $products_image_extension) as $file) {
$images_array[] = $file;
}
}
// Build output based on images found
$num_images = count($images_array);
$list_box_contents = [];
$title = '';
if ($num_images !== 0) {
$row = 0;
$col = 0;
$images_auto_added = (int)IMAGES_AUTO_ADDED;
if ($num_images < $images_auto_added || $images_auto_added === 0) {
$col_width = floor(100 / $num_images);
} else {
$col_width = floor(100 / $images_auto_added);
}
$slideNumber = 1;
$image_extension = '.' . $products_image_extension;
foreach ($images_array as $file) {
$products_image_large = str_replace(
[
DIR_WS_IMAGES,
$image_extension,
],
[
DIR_WS_IMAGES . 'large/',
IMAGE_SUFFIX_LARGE . $image_extension
],
$file
);
// -----
// This notifier lets any image-handler know the current image being processed, providing the following parameters:
//
// $p1 ... (r/o) ... The current product's name
// $p2 ... (r/w) ... The (possibly updated) filename (including path) of the current additional image.
//
$zco_notifier->notify('NOTIFY_MODULES_ADDITIONAL_IMAGES_GET_LARGE', $products_name, $products_image_large);
$flag_has_large = file_exists($products_image_large);
$products_image_large = ($flag_has_large === true) ? $products_image_large : $file;
$flag_display_large = (IMAGE_ADDITIONAL_DISPLAY_LINK_EVEN_WHEN_NO_LARGE === 'Yes' || $flag_has_large === true);
$base_image = $file;
$thumb_slashes = zen_image(addslashes($base_image), addslashes($products_name), SMALL_IMAGE_WIDTH, SMALL_IMAGE_HEIGHT);
$thumb_regular = zen_image($base_image, $products_name, SMALL_IMAGE_WIDTH, SMALL_IMAGE_HEIGHT);
$large_link = zen_href_link(FILENAME_POPUP_IMAGE_ADDITIONAL, 'pID=' . $_GET['products_id'] . '&pic=' . $slideNumber . '&products_image_large_additional=' . $products_image_large);
$slide = zen_image($products_image_large);
// List Box array generation:
$list_box_contents[$row][$col] = [
'params' => 'class="carousel-item" data-slide-number="' . $slideNumber . '"',
'text' => $slide
];
$col++;
if ($col >= $images_auto_added) {
$col = 0;
$row++;
}
$slideNumber++;
} // end for loop
} // endif
$zco_notifier->notify('NOTIFY_MODULES_ADDITIONAL_PRODUCT_IMAGES_END');
>
> **includes/templates/bootstrap/css/product_info.css** : new file with all the css required to display the modal properly. If you have an existing file, just append contents.
> ```
.modal-dialog {
height: 100vh;
margin: 0 auto;
}
.modal-content {
height: 100vh;
}
.modal-body {
flex: 1 1 auto;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 0 !important;
}
#productImagesCarousel {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
position: relative;
}
#productImagesCarousel .carousel-inner {
flex: 1 1 auto;
min-height: 0;
}
#productImagesCarousel .carousel-item {
height: 100%;
}
#productImagesCarousel .carousel-item img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
}
#productImagesCarousel .carousel-indicators {
position: static !important;
margin: 0 !important;
padding: 10px 10px 12px !important;
flex: 0 0 auto;
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
background: #fff;
}
#productImagesCarousel .carousel-indicators img {
height: 70px;
width: auto;
display: block;
}
#carousel-btn-toolbar {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
z-index: 10;
pointer-events: none;
}
#carousel-btn-toolbar a {
pointer-events: auto;
}
However, it's still not as good as it could/should be. There's quite some rework to do, but I believe the best layout would be to put the thumbnails to the side (on large and medium screens only, of course). I've played with it a bit and got this, but it's not a complete solution and needs a lot more attention to get it perfected...

balihris I was just doing what he asked for. But i dont disagree with you. So here the solution to
keep the product images and secondary image displayed in the viewport
includes/templates/usc_bs4/css/stylesheet.css
put at the very bottom
.modal-body .container {
max-width:600px
}
