Fix Gallery View: medium screen devices get 3 max columns and small screen devices 2 max columns.
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Changes 4/14/2025
|
||||||
|
|
||||||
|
- Fix Gallery View: medium screen devices get 3 max columns and small screen devices 2 max columns.
|
||||||
|
|
||||||
## Changes 4/13/2025 v1.1.3
|
## Changes 4/13/2025 v1.1.3
|
||||||
|
|
||||||
- Decreased header height some more and clickable logo.
|
- Decreased header height some more and clickable logo.
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ function toggleAdvancedSearch() {
|
|||||||
|
|
||||||
window.imageCache = window.imageCache || {};
|
window.imageCache = window.imageCache || {};
|
||||||
function cacheImage(imgElem, key) {
|
function cacheImage(imgElem, key) {
|
||||||
// Save the current src for future renders.
|
// Save the current src for future renders.
|
||||||
window.imageCache[key] = imgElem.src;
|
window.imageCache[key] = imgElem.src;
|
||||||
}
|
}
|
||||||
window.cacheImage = cacheImage;
|
window.cacheImage = cacheImage;
|
||||||
|
|
||||||
@@ -108,25 +108,25 @@ function searchFiles(searchTerm) {
|
|||||||
|
|
||||||
// Define search keys.
|
// Define search keys.
|
||||||
let keys = [
|
let keys = [
|
||||||
{ name: 'name', weight: 0.1 },
|
{ name: 'name', weight: 0.1 },
|
||||||
{ name: 'uploader', weight: 0.1 },
|
{ name: 'uploader', weight: 0.1 },
|
||||||
{ name: 'tags.name', weight: 0.1 }
|
{ name: 'tags.name', weight: 0.1 }
|
||||||
];
|
];
|
||||||
if (window.advancedSearchEnabled) {
|
if (window.advancedSearchEnabled) {
|
||||||
keys.push({ name: 'content', weight: 0.7 });
|
keys.push({ name: 'content', weight: 0.7 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
keys: keys,
|
keys: keys,
|
||||||
threshold: 0.4,
|
threshold: 0.4,
|
||||||
minMatchCharLength: 2,
|
minMatchCharLength: 2,
|
||||||
ignoreLocation: true
|
ignoreLocation: true
|
||||||
};
|
};
|
||||||
|
|
||||||
const fuse = new Fuse(fileData, options);
|
const fuse = new Fuse(fileData, options);
|
||||||
let results = fuse.search(searchTerm);
|
let results = fuse.search(searchTerm);
|
||||||
return results.map(result => result.item);
|
return results.map(result => result.item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --- VIEW MODE TOGGLE BUTTON & Helpers ---
|
* --- VIEW MODE TOGGLE BUTTON & Helpers ---
|
||||||
@@ -134,43 +134,43 @@ function searchFiles(searchTerm) {
|
|||||||
export function createViewToggleButton() {
|
export function createViewToggleButton() {
|
||||||
let toggleBtn = document.getElementById("toggleViewBtn");
|
let toggleBtn = document.getElementById("toggleViewBtn");
|
||||||
if (!toggleBtn) {
|
if (!toggleBtn) {
|
||||||
toggleBtn = document.createElement("button");
|
toggleBtn = document.createElement("button");
|
||||||
toggleBtn.id = "toggleViewBtn";
|
toggleBtn.id = "toggleViewBtn";
|
||||||
toggleBtn.classList.add("btn", "btn-toggleview");
|
toggleBtn.classList.add("btn", "btn-toggleview");
|
||||||
|
|
||||||
// Set initial icon and tooltip based on current view mode.
|
// Set initial icon and tooltip based on current view mode.
|
||||||
if (window.viewMode === "gallery") {
|
if (window.viewMode === "gallery") {
|
||||||
toggleBtn.innerHTML = '<i class="material-icons">view_list</i>';
|
toggleBtn.innerHTML = '<i class="material-icons">view_list</i>';
|
||||||
toggleBtn.title = t("switch_to_table_view");
|
toggleBtn.title = t("switch_to_table_view");
|
||||||
} else {
|
} else {
|
||||||
toggleBtn.innerHTML = '<i class="material-icons">view_module</i>';
|
toggleBtn.innerHTML = '<i class="material-icons">view_module</i>';
|
||||||
toggleBtn.title = t("switch_to_gallery_view");
|
toggleBtn.title = t("switch_to_gallery_view");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the button before the last button in the header.
|
// Insert the button before the last button in the header.
|
||||||
const headerButtons = document.querySelector(".header-buttons");
|
const headerButtons = document.querySelector(".header-buttons");
|
||||||
if (headerButtons && headerButtons.lastElementChild) {
|
if (headerButtons && headerButtons.lastElementChild) {
|
||||||
headerButtons.insertBefore(toggleBtn, headerButtons.lastElementChild);
|
headerButtons.insertBefore(toggleBtn, headerButtons.lastElementChild);
|
||||||
} else if (headerButtons) {
|
} else if (headerButtons) {
|
||||||
headerButtons.appendChild(toggleBtn);
|
headerButtons.appendChild(toggleBtn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleBtn.onclick = () => {
|
toggleBtn.onclick = () => {
|
||||||
window.viewMode = window.viewMode === "gallery" ? "table" : "gallery";
|
window.viewMode = window.viewMode === "gallery" ? "table" : "gallery";
|
||||||
localStorage.setItem("viewMode", window.viewMode);
|
localStorage.setItem("viewMode", window.viewMode);
|
||||||
loadFileList(window.currentFolder);
|
loadFileList(window.currentFolder);
|
||||||
if (window.viewMode === "gallery") {
|
if (window.viewMode === "gallery") {
|
||||||
toggleBtn.innerHTML = '<i class="material-icons">view_list</i>';
|
toggleBtn.innerHTML = '<i class="material-icons">view_list</i>';
|
||||||
toggleBtn.title = t("switch_to_table_view");
|
toggleBtn.title = t("switch_to_table_view");
|
||||||
} else {
|
} else {
|
||||||
toggleBtn.innerHTML = '<i class="material-icons">view_module</i>';
|
toggleBtn.innerHTML = '<i class="material-icons">view_module</i>';
|
||||||
toggleBtn.title = t("switch_to_gallery_view");
|
toggleBtn.title = t("switch_to_gallery_view");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return toggleBtn;
|
return toggleBtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatFolderName(folder) {
|
export function formatFolderName(folder) {
|
||||||
if (folder === "root") return "(Root)";
|
if (folder === "root") return "(Root)";
|
||||||
@@ -392,32 +392,24 @@ export function renderFileTable(folder, container) {
|
|||||||
bindFileListContextMenu();
|
bindFileListContextMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Similarly, update renderGalleryView to accept an optional container.
|
|
||||||
*/
|
|
||||||
// A helper to compute the max image height based on the current column count.
|
// A helper to compute the max image height based on the current column count.
|
||||||
function getMaxImageHeight() {
|
function getMaxImageHeight() {
|
||||||
// Use the slider value (default to 3 if undefined).
|
|
||||||
const columns = parseInt(window.galleryColumns || 3, 10);
|
const columns = parseInt(window.galleryColumns || 3, 10);
|
||||||
// Simple scaling: fewer columns yield bigger images.
|
|
||||||
// For instance, if columns === 6, max-height is 150px,
|
|
||||||
// and if columns === 1, max-height could be 150 * 6 = 900px.
|
|
||||||
return 150 * (7 - columns); // adjust the multiplier as needed.
|
return 150 * (7 - columns); // adjust the multiplier as needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderGalleryView(folder, container) {
|
export function renderGalleryView(folder, container) {
|
||||||
const fileListContent = container || document.getElementById("fileList");
|
const fileListContent = container || document.getElementById("fileList");
|
||||||
const searchTerm = (window.currentSearchTerm || "").toLowerCase();
|
const searchTerm = (window.currentSearchTerm || "").toLowerCase();
|
||||||
const filteredFiles = searchFiles(searchTerm);
|
const filteredFiles = searchFiles(searchTerm);
|
||||||
const folderPath = folder === "root"
|
const folderPath = folder === "root"
|
||||||
? "uploads/"
|
? "uploads/"
|
||||||
: "uploads/" + folder.split("/").map(encodeURIComponent).join("/") + "/";
|
: "uploads/" + folder.split("/").map(encodeURIComponent).join("/") + "/";
|
||||||
|
|
||||||
// Use the current global column value (default to 3).
|
// Use the current global column value (default to 3).
|
||||||
const numColumns = window.galleryColumns || 3;
|
const numColumns = window.galleryColumns || 3;
|
||||||
|
|
||||||
// --- Insert slider controls ---
|
// --- Insert slider controls ---
|
||||||
// Build the slider HTML.
|
|
||||||
const sliderHTML = `
|
const sliderHTML = `
|
||||||
<div class="gallery-slider" style="margin: 10px; text-align: center;">
|
<div class="gallery-slider" style="margin: 10px; text-align: center;">
|
||||||
<label for="galleryColumnsSlider" style="margin-right: 5px;">${t('columns')}:</label>
|
<label for="galleryColumnsSlider" style="margin-right: 5px;">${t('columns')}:</label>
|
||||||
@@ -426,47 +418,44 @@ function getMaxImageHeight() {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Set up the grid container using the slider's value.
|
// Set up the grid container using the slider's current value.
|
||||||
const gridStyle = `display: grid; grid-template-columns: repeat(${numColumns}, 1fr); gap: 10px; padding: 10px;`;
|
const gridStyle = `display: grid; grid-template-columns: repeat(${numColumns}, 1fr); gap: 10px; padding: 10px;`;
|
||||||
|
|
||||||
// Build the gallery container HTML and include the slider above it.
|
// Build the gallery container HTML including the slider.
|
||||||
let galleryHTML = sliderHTML;
|
let galleryHTML = sliderHTML;
|
||||||
galleryHTML += `<div class="gallery-container" style="${gridStyle}">`;
|
galleryHTML += `<div class="gallery-container" style="${gridStyle}">`;
|
||||||
|
|
||||||
filteredFiles.forEach((file) => {
|
filteredFiles.forEach((file) => {
|
||||||
let thumbnail;
|
let thumbnail;
|
||||||
if (/\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)$/i.test(file.name)) {
|
if (/\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)$/i.test(file.name)) {
|
||||||
const cacheKey = folderPath + encodeURIComponent(file.name);
|
const cacheKey = folderPath + encodeURIComponent(file.name);
|
||||||
if (window.imageCache && window.imageCache[cacheKey]) {
|
if (window.imageCache && window.imageCache[cacheKey]) {
|
||||||
thumbnail = `<img src="${window.imageCache[cacheKey]}" class="gallery-thumbnail" alt="${escapeHTML(file.name)}" style="max-width: 100%; max-height: ${getMaxImageHeight()}px; display: block; margin: 0 auto;">`;
|
thumbnail = `<img src="${window.imageCache[cacheKey]}" class="gallery-thumbnail" alt="${escapeHTML(file.name)}" style="max-width: 100%; max-height: ${getMaxImageHeight()}px; display: block; margin: 0 auto;">`;
|
||||||
|
} else {
|
||||||
|
const imageUrl = folderPath + encodeURIComponent(file.name) + "?t=" + new Date().getTime();
|
||||||
|
thumbnail = `<img src="${imageUrl}" onload="cacheImage(this, '${cacheKey}')" class="gallery-thumbnail" alt="${escapeHTML(file.name)}" style="max-width: 100%; max-height: ${getMaxImageHeight()}px; display: block; margin: 0 auto;">`;
|
||||||
|
}
|
||||||
|
} else if (/\.(mp3|wav|m4a|ogg|flac|aac|wma|opus)$/i.test(file.name)) {
|
||||||
|
thumbnail = `<span class="material-icons gallery-icon">audiotrack</span>`;
|
||||||
} else {
|
} else {
|
||||||
const imageUrl = folderPath + encodeURIComponent(file.name) + "?t=" + new Date().getTime();
|
thumbnail = `<span class="material-icons gallery-icon">insert_drive_file</span>`;
|
||||||
thumbnail = `<img src="${imageUrl}" onload="cacheImage(this, '${cacheKey}')" class="gallery-thumbnail" alt="${escapeHTML(file.name)}" style="max-width: 100%; max-height: ${getMaxImageHeight()}px; display: block; margin: 0 auto;">`;
|
|
||||||
}
|
}
|
||||||
} else if (/\.(mp3|wav|m4a|ogg|flac|aac|wma|opus)$/i.test(file.name)) {
|
|
||||||
thumbnail = `<span class="material-icons gallery-icon">audiotrack</span>`;
|
|
||||||
} else {
|
|
||||||
thumbnail = `<span class="material-icons gallery-icon">insert_drive_file</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tagBadgesHTML = "";
|
let tagBadgesHTML = "";
|
||||||
if (file.tags && file.tags.length > 0) {
|
if (file.tags && file.tags.length > 0) {
|
||||||
tagBadgesHTML = `<div class="tag-badges" style="margin-top:4px;">`;
|
tagBadgesHTML = `<div class="tag-badges" style="margin-top:4px;">`;
|
||||||
file.tags.forEach(tag => {
|
file.tags.forEach(tag => {
|
||||||
tagBadgesHTML += `<span style="background-color: ${tag.color}; color: #fff; padding: 2px 4px; border-radius: 3px; margin-right: 2px; font-size: 0.8em;">${escapeHTML(tag.name)}</span>`;
|
tagBadgesHTML += `<span style="background-color: ${tag.color}; color: #fff; padding: 2px 4px; border-radius: 3px; margin-right: 2px; font-size: 0.8em;">${escapeHTML(tag.name)}</span>`;
|
||||||
});
|
});
|
||||||
tagBadgesHTML += `</div>`;
|
tagBadgesHTML += `</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
galleryHTML += `
|
galleryHTML += `
|
||||||
<div class="gallery-card" style="border: 1px solid #ccc; padding: 5px; text-align: center;">
|
<div class="gallery-card" style="border: 1px solid #ccc; padding: 5px; text-align: center;">
|
||||||
<div class="gallery-preview" style="cursor: pointer;" onclick="previewFile('${folderPath + encodeURIComponent(file.name)}?t=' + new Date().getTime(), '${file.name}')">
|
<div class="gallery-preview" style="cursor: pointer;" onclick="previewFile('${folderPath + encodeURIComponent(file.name)}?t=' + new Date().getTime(), '${file.name}')">
|
||||||
${thumbnail}
|
${thumbnail}
|
||||||
</div>
|
</div>
|
||||||
<div class="gallery-info" style="margin-top: 5px;">
|
<div class="gallery-info" style="margin-top: 5px;">
|
||||||
<span class="gallery-file-name" style="display: block; white-space: normal; overflow-wrap: break-word; word-wrap: break-word;">
|
<span class="gallery-file-name" style="display: block; white-space: normal; overflow-wrap: break-word; word-wrap: break-word;">${escapeHTML(file.name)}</span>
|
||||||
${escapeHTML(file.name)}
|
|
||||||
</span>
|
|
||||||
${tagBadgesHTML}
|
${tagBadgesHTML}
|
||||||
<div class="button-wrap" style="display: flex; justify-content: center; gap: 5px;">
|
<div class="button-wrap" style="display: flex; justify-content: center; gap: 5px;">
|
||||||
<button type="button" class="btn btn-sm btn-success download-btn"
|
<button type="button" class="btn btn-sm btn-success download-btn"
|
||||||
@@ -492,45 +481,77 @@ function getMaxImageHeight() {
|
|||||||
galleryHTML += "</div>"; // End gallery container.
|
galleryHTML += "</div>"; // End gallery container.
|
||||||
|
|
||||||
fileListContent.innerHTML = galleryHTML;
|
fileListContent.innerHTML = galleryHTML;
|
||||||
createViewToggleButton();
|
|
||||||
updateFileActionButtons();
|
// Re-apply slider constraints for the newly rendered slider.
|
||||||
|
updateSliderConstraints();
|
||||||
|
|
||||||
// Attach share button event listeners.
|
// Attach share button event listeners.
|
||||||
document.querySelectorAll(".share-btn").forEach(btn => {
|
document.querySelectorAll(".share-btn").forEach(btn => {
|
||||||
btn.addEventListener("click", e => {
|
btn.addEventListener("click", e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const fileName = btn.getAttribute("data-file");
|
const fileName = btn.getAttribute("data-file");
|
||||||
const file = fileData.find(f => f.name === fileName);
|
const file = fileData.find(f => f.name === fileName);
|
||||||
if (file) {
|
if (file) {
|
||||||
import('./filePreview.js').then(module => {
|
import('./filePreview.js').then(module => {
|
||||||
module.openShareModal(file, folder);
|
module.openShareModal(file, folder);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- Slider Event Listener ---
|
// --- Slider Event Listener ---
|
||||||
const slider = document.getElementById("galleryColumnsSlider");
|
const slider = document.getElementById("galleryColumnsSlider");
|
||||||
if (slider) {
|
if (slider) {
|
||||||
slider.addEventListener("input", function() {
|
slider.addEventListener("input", function () {
|
||||||
const value = this.value;
|
const value = this.value;
|
||||||
// Update the slider display.
|
document.getElementById("galleryColumnsValue").textContent = value;
|
||||||
document.getElementById("galleryColumnsValue").textContent = value;
|
window.galleryColumns = value;
|
||||||
// Update global value so new renders use the correct value.
|
const galleryContainer = document.querySelector(".gallery-container");
|
||||||
window.galleryColumns = value;
|
if (galleryContainer) {
|
||||||
// Update the grid columns.
|
galleryContainer.style.gridTemplateColumns = `repeat(${value}, 1fr)`;
|
||||||
const galleryContainer = document.querySelector(".gallery-container");
|
}
|
||||||
if (galleryContainer) {
|
const newMaxHeight = getMaxImageHeight();
|
||||||
galleryContainer.style.gridTemplateColumns = `repeat(${value}, 1fr)`;
|
document.querySelectorAll(".gallery-thumbnail").forEach(img => {
|
||||||
}
|
img.style.maxHeight = newMaxHeight + "px";
|
||||||
// Compute the new max image height.
|
});
|
||||||
const newMaxHeight = getMaxImageHeight();
|
|
||||||
document.querySelectorAll(".gallery-thumbnail").forEach(img => {
|
|
||||||
img.style.maxHeight = newMaxHeight + "px";
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Responsive slider constraints based on screen size.
|
||||||
|
function updateSliderConstraints() {
|
||||||
|
const slider = document.getElementById("galleryColumnsSlider");
|
||||||
|
if (!slider) return;
|
||||||
|
|
||||||
|
const width = window.innerWidth;
|
||||||
|
let min = 1, max = 6; // default for larger screens
|
||||||
|
|
||||||
|
if (width < 600) { // phone
|
||||||
|
max = 2;
|
||||||
|
} else if (width < 1024) { // medium screens
|
||||||
|
max = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust current slider value if it exceeds new max.
|
||||||
|
let currentVal = parseInt(slider.value, 10);
|
||||||
|
if (currentVal > max) {
|
||||||
|
currentVal = max;
|
||||||
|
slider.value = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
slider.min = min;
|
||||||
|
slider.max = max;
|
||||||
|
document.getElementById("galleryColumnsValue").textContent = currentVal;
|
||||||
|
|
||||||
|
// Update grid columns with new slider value.
|
||||||
|
const galleryContainer = document.querySelector(".gallery-container");
|
||||||
|
if (galleryContainer) {
|
||||||
|
galleryContainer.style.gridTemplateColumns = `repeat(${currentVal}, 1fr)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', updateSliderConstraints);
|
||||||
|
window.addEventListener('resize', updateSliderConstraints);
|
||||||
|
|
||||||
export function sortFiles(column, folder) {
|
export function sortFiles(column, folder) {
|
||||||
if (sortOrder.column === column) {
|
if (sortOrder.column === column) {
|
||||||
|
|||||||
Reference in New Issue
Block a user