Add close button for image details view

- Implement close button in image details container
- Update CSS for LTR and RTL layouts to position close button
- Add click event listener to close image details view
- Refactor TypeScript code to separate image-related functionality
- Update HTML template to include close button with icon
This commit is contained in:
Gnkalk 2025-02-18 01:06:03 +03:30
parent 6bf060df92
commit aa690ea712
8 changed files with 287 additions and 188 deletions

View file

@ -739,6 +739,17 @@ img, video {
cursor: not-allowed; cursor: not-allowed;
} }
.image-details-container .image-details .close {
color: var(--text-color);
background-color: var(--secondary-background-color);
cursor: pointer;
border-radius: 9999px;
padding: .25rem;
position: absolute;
top: 1rem;
right: 1rem;
}
#preferences { #preferences {
grid-template: "header links" min-content grid-template: "header links" min-content
"main main" "main main"

View file

@ -685,6 +685,17 @@ img, video {
cursor: not-allowed; cursor: not-allowed;
} }
.image-details-container .image-details .close {
color: var(--text-color);
background-color: var(--secondary-background-color);
cursor: pointer;
border-radius: 9999px;
padding: .25rem;
position: absolute;
top: 1rem;
left: 1rem;
}
#preferences { #preferences {
grid-template: "header links" min-content grid-template: "header links" min-content
"main main" "main main"

View file

@ -585,14 +585,27 @@ function hmrAccept(bundle /*: ParcelRequire */ , id /*: string */ ) {
} }
},{}],"3uyIQ":[function(require,module,exports,__globalThis) { },{}],"3uyIQ":[function(require,module,exports,__globalThis) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "$", ()=>$);
parcelHelpers.export(exports, "$$", ()=>$$);
var _images = require("./images");
function $(selector) { function $(selector) {
return document.querySelector(selector); return document.querySelector(selector);
} }
function $$(selector) { function $$(selector) {
return document.querySelectorAll(selector); return document.querySelectorAll(selector);
} }
(0, _images.checkImagePage)();
document.addEventListener("DOMContentLoaded", function() {});
},{"./images":"jxVSe","@parcel/transformer-js/src/esmodule-helpers.js":"j7FRh"}],"jxVSe":[function(require,module,exports,__globalThis) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "checkImagePage", ()=>checkImagePage);
var _app = require("./app");
function handleImageDetails(image, { imageSrc, imageTitle, imageContent, imageSource, imageFilesize, imageDownload, formatSpan, filesizeSpan, engineSpan }) { function handleImageDetails(image, { imageSrc, imageTitle, imageContent, imageSource, imageFilesize, imageDownload, formatSpan, filesizeSpan, engineSpan }) {
const resultsContainer = $("#results"); const resultsContainer = (0, _app.$)("#results");
resultsContainer.classList.add("image-open"); resultsContainer.classList.add("image-open");
imageSrc.src = image.getAttribute("data-gitee-src"); imageSrc.src = image.getAttribute("data-gitee-src");
imageTitle.innerText = image.getAttribute("data-gitee-title"); imageTitle.innerText = image.getAttribute("data-gitee-title");
@ -608,17 +621,17 @@ function handleImageDetails(image, { imageSrc, imageTitle, imageContent, imageSo
engineSpan.innerText = image.getAttribute("data-gitee-engine"); engineSpan.innerText = image.getAttribute("data-gitee-engine");
} }
function setupImages(resultsContainer) { function setupImages(resultsContainer) {
const imageSrc = $("#image-src"); const imageSrc = (0, _app.$)("#image-src");
const imageTitle = $("#image-title"); const imageTitle = (0, _app.$)("#image-title");
const imageContent = $("#image-content"); const imageContent = (0, _app.$)("#image-content");
const imageFormat = $("#image-format"); const imageFormat = (0, _app.$)("#image-format");
const imageFilesize = $("#image-filesize"); const imageFilesize = (0, _app.$)("#image-filesize");
const imageSource = $("#image-source"); const imageSource = (0, _app.$)("#image-source");
const imageDownload = $("#image-download"); const imageDownload = (0, _app.$)("#image-download");
const imageCopy = $("#image-copy"); const imageCopy = (0, _app.$)("#image-copy");
const formatSpan = imageFormat.getElementsByClassName("value")[0]; const formatSpan = imageFormat.getElementsByClassName("value")[0];
const filesizeSpan = imageFilesize.getElementsByClassName("value")[0]; const filesizeSpan = imageFilesize.getElementsByClassName("value")[0];
const engineSpan = $("#image-engine"); const engineSpan = (0, _app.$)("#image-engine");
const ImagesObserver = new MutationObserver((mutationsList)=>{ const ImagesObserver = new MutationObserver((mutationsList)=>{
for (const mutation of mutationsList)if (mutation.type === "childList") mutation.addedNodes.forEach((node)=>{ for (const mutation of mutationsList)if (mutation.type === "childList") mutation.addedNodes.forEach((node)=>{
if (node.nodeType === Node.ELEMENT_NODE) return; if (node.nodeType === Node.ELEMENT_NODE) return;
@ -643,7 +656,7 @@ function setupImages(resultsContainer) {
childList: true, childList: true,
subtree: true subtree: true
}); });
$$(".image").forEach((node)=>{ (0, _app.$$)(".image").forEach((node)=>{
if (node.nodeType !== Node.ELEMENT_NODE) return; if (node.nodeType !== Node.ELEMENT_NODE) return;
const image = node; const image = node;
image.addEventListener("click", (e)=>{ image.addEventListener("click", (e)=>{
@ -678,8 +691,8 @@ function setupImages(resultsContainer) {
}); });
canvas.remove(); canvas.remove();
}); });
const bodyContainer = $("#results"); const bodyContainer = (0, _app.$)("#results");
const imageDetailsContainer = $("#image-details"); const imageDetailsContainer = (0, _app.$)("#image-details");
bodyContainer.addEventListener("scroll", function() { bodyContainer.addEventListener("scroll", function() {
if (bodyContainer.scrollTop < 50) { if (bodyContainer.scrollTop < 50) {
imageDetailsContainer.style.top = "0px"; imageDetailsContainer.style.top = "0px";
@ -689,13 +702,45 @@ function setupImages(resultsContainer) {
imageDetailsContainer.style.height = "95vh"; imageDetailsContainer.style.height = "95vh";
imageDetailsContainer.style.top = `calc(${bodyContainer.scrollTop}px - 15vh)`; imageDetailsContainer.style.top = `calc(${bodyContainer.scrollTop}px - 15vh)`;
}); });
imageDetailsContainer.getElementsByClassName("close")[0].addEventListener("click", function() {
bodyContainer.classList.remove("image-open");
});
} }
function afterLoad() { function checkImagePage() {
const resultsContainer = $(".results-container"); const resultsContainer = (0, _app.$)(".results-container");
if (!resultsContainer || resultsContainer.nodeType !== Node.ELEMENT_NODE) return; if (!resultsContainer || resultsContainer.nodeType !== Node.ELEMENT_NODE) return;
if (resultsContainer.classList.contains("image-page")) setupImages(resultsContainer); if (resultsContainer.classList.contains("image-page")) setupImages(resultsContainer);
} }
document.addEventListener("DOMContentLoaded", afterLoad);
},{"@parcel/transformer-js/src/esmodule-helpers.js":"j7FRh","./app":"3uyIQ"}],"j7FRh":[function(require,module,exports,__globalThis) {
exports.interopDefault = function(a) {
return a && a.__esModule ? a : {
default: a
};
};
exports.defineInteropFlag = function(a) {
Object.defineProperty(a, '__esModule', {
value: true
});
};
exports.exportAll = function(source, dest) {
Object.keys(source).forEach(function(key) {
if (key === 'default' || key === '__esModule' || Object.prototype.hasOwnProperty.call(dest, key)) return;
Object.defineProperty(dest, key, {
enumerable: true,
get: function() {
return source[key];
}
});
});
return dest;
};
exports.export = function(dest, destName, get) {
Object.defineProperty(dest, destName, {
enumerable: true,
get: get
});
};
},{}]},["1Y09f","3uyIQ"], "3uyIQ", "parcelRequire94c2") },{}]},["1Y09f","3uyIQ"], "3uyIQ", "parcelRequire94c2")

View file

@ -2,11 +2,17 @@
@use '../definitions.scss' as *; @use '../definitions.scss' as *;
.images_group { .images_group {
// this thing make some lags
columns: 15rem; columns: 15rem;
// allternative way
// display: flex;
// flex-wrap: wrap;
gap: 1rem; gap: 1rem;
.image { .image {
cursor: pointer; cursor: pointer;
// height: 15rem;
.image-box { .image-box {
overflow: hidden; overflow: hidden;
@ -158,5 +164,16 @@
} }
} }
} }
.close {
position: absolute;
top: 1rem;
@include mixin.right(1rem);
color: var(--text-color);
@include mixin.rounded(full);
background-color: var(--secondary-background-color);
padding: 0.25rem;
cursor: pointer;
}
} }
} }

View file

@ -1,178 +1,13 @@
function $(selector: string) { import { checkImagePage } from "./images";
export function $(selector: string) {
return document.querySelector(selector); return document.querySelector(selector);
} }
function $$(selector: string) { export function $$(selector: string) {
return document.querySelectorAll(selector); return document.querySelectorAll(selector);
} }
interface ImageDetails { checkImagePage();
imageSrc: HTMLImageElement;
imageTitle: HTMLSpanElement;
imageContent: HTMLSpanElement;
imageSource: HTMLLinkElement;
imageFilesize: HTMLSpanElement;
imageDownload: HTMLLinkElement;
formatSpan: HTMLSpanElement;
filesizeSpan: HTMLSpanElement;
engineSpan: HTMLSpanElement;
}
function handleImageDetails( document.addEventListener("DOMContentLoaded", function () {});
image: HTMLElement,
{
imageSrc,
imageTitle,
imageContent,
imageSource,
imageFilesize,
imageDownload,
formatSpan,
filesizeSpan,
engineSpan,
}: ImageDetails
) {
const resultsContainer = $("#results") as HTMLDivElement;
resultsContainer.classList.add("image-open");
imageSrc.src = image.getAttribute("data-gitee-src") as string;
imageTitle.innerText = image.getAttribute("data-gitee-title") as string;
imageContent.innerText = image.getAttribute("data-gitee-content") as string;
if (image.hasAttribute("data-gitee-format")) {
formatSpan.innerText = image.getAttribute(
"data-gitee-format"
) as string;
} else {
formatSpan.innerText = "Unknown";
}
if (image.hasAttribute("data-gitee-filesize")) {
if (imageFilesize.hasAttribute("hidden"))
imageFilesize.setAttribute("hidden", "");
filesizeSpan.innerText = image.getAttribute(
"data-gitee-filesize"
) as string;
} else {
imageFilesize.setAttribute("hidden", "");
}
imageSource.href = image.getAttribute("data-gitee-source") as string;
imageDownload.href = image.getAttribute("data-gitee-src") as string;
engineSpan.innerText = image.getAttribute("data-gitee-engine") as string;
}
function setupImages(resultsContainer: HTMLElement) {
const imageSrc = $("#image-src") as HTMLImageElement;
const imageTitle = $("#image-title") as HTMLHeadingElement;
const imageContent = $("#image-content") as HTMLParagraphElement;
const imageFormat = $("#image-format") as HTMLDivElement;
const imageFilesize = $("#image-filesize") as HTMLDivElement;
const imageSource = $("#image-source") as HTMLLinkElement;
const imageDownload = $("#image-download") as HTMLLinkElement;
const imageCopy = $("#image-copy") as HTMLButtonElement;
const formatSpan = imageFormat.getElementsByClassName(
"value"
)[0] as HTMLSpanElement;
const filesizeSpan = imageFilesize.getElementsByClassName(
"value"
)[0] as HTMLSpanElement;
const engineSpan = $("#image-engine") as HTMLSpanElement;
const ImagesObserver = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) return;
const element = node as HTMLElement;
if (element.classList.contains("image")) {
element.addEventListener("click", (e) => {
e.preventDefault();
handleImageDetails(element, {
imageSrc,
imageTitle,
imageContent,
imageSource,
imageFilesize,
imageDownload,
filesizeSpan,
formatSpan,
engineSpan,
});
});
}
});
}
}
});
ImagesObserver.observe(resultsContainer, {
childList: true,
subtree: true,
});
$$(".image").forEach((node) => {
if (node.nodeType !== Node.ELEMENT_NODE) return;
const image = node as HTMLElement;
void image.addEventListener("click", (e) => {
e.preventDefault();
handleImageDetails(image, {
imageSrc,
imageTitle,
imageContent,
imageSource,
imageFilesize,
imageDownload,
filesizeSpan,
formatSpan,
engineSpan,
});
});
});
// Copy not working at localhost and only works in HTTPS pages
if (location.protocol === "http:") {
imageCopy.setAttribute("disabled", "true");
}
imageCopy.addEventListener("click", () => {
const canvas = document.createElement("canvas");
canvas.width = imageSrc.width;
canvas.height = imageSrc.height;
(canvas.getContext("2d") as CanvasRenderingContext2D).drawImage(
imageSrc,
0,
0,
imageSrc.width,
imageSrc.height
);
canvas.toBlob((blob) => {
if (!blob) return;
navigator.clipboard.write([new ClipboardItem({ "image/*": blob })]);
});
void canvas.remove();
});
const bodyContainer = $("#results") as HTMLDivElement;
const imageDetailsContainer = $("#image-details") as HTMLDivElement;
bodyContainer.addEventListener("scroll", function () {
if (bodyContainer.scrollTop < 50) {
imageDetailsContainer.style.top = "0px";
imageDetailsContainer.style.height = "80vh";
return;
}
imageDetailsContainer.style.height = "95vh";
imageDetailsContainer.style.top = `calc(${bodyContainer.scrollTop}px - 15vh)`;
});
}
function afterLoad() {
const resultsContainer = $(".results-container");
if (!resultsContainer || resultsContainer.nodeType !== Node.ELEMENT_NODE)
return;
if (resultsContainer.classList.contains("image-page"))
setupImages(resultsContainer as HTMLDivElement);
}
document.addEventListener("DOMContentLoaded", afterLoad);

View file

@ -0,0 +1,176 @@
import { $, $$ } from "./app";
interface ImageDetails {
imageSrc: HTMLImageElement;
imageTitle: HTMLSpanElement;
imageContent: HTMLSpanElement;
imageSource: HTMLLinkElement;
imageFilesize: HTMLSpanElement;
imageDownload: HTMLLinkElement;
formatSpan: HTMLSpanElement;
filesizeSpan: HTMLSpanElement;
engineSpan: HTMLSpanElement;
}
function handleImageDetails(
image: HTMLElement,
{
imageSrc,
imageTitle,
imageContent,
imageSource,
imageFilesize,
imageDownload,
formatSpan,
filesizeSpan,
engineSpan,
}: ImageDetails
) {
const resultsContainer = $("#results") as HTMLDivElement;
resultsContainer.classList.add("image-open");
imageSrc.src = image.getAttribute("data-gitee-src") as string;
imageTitle.innerText = image.getAttribute("data-gitee-title") as string;
imageContent.innerText = image.getAttribute("data-gitee-content") as string;
if (image.hasAttribute("data-gitee-format")) {
formatSpan.innerText = image.getAttribute(
"data-gitee-format"
) as string;
} else {
formatSpan.innerText = "Unknown";
}
if (image.hasAttribute("data-gitee-filesize")) {
if (imageFilesize.hasAttribute("hidden"))
imageFilesize.setAttribute("hidden", "");
filesizeSpan.innerText = image.getAttribute(
"data-gitee-filesize"
) as string;
} else {
imageFilesize.setAttribute("hidden", "");
}
imageSource.href = image.getAttribute("data-gitee-source") as string;
imageDownload.href = image.getAttribute("data-gitee-src") as string;
engineSpan.innerText = image.getAttribute("data-gitee-engine") as string;
}
function setupImages(resultsContainer: HTMLElement) {
const imageSrc = $("#image-src") as HTMLImageElement;
const imageTitle = $("#image-title") as HTMLHeadingElement;
const imageContent = $("#image-content") as HTMLParagraphElement;
const imageFormat = $("#image-format") as HTMLDivElement;
const imageFilesize = $("#image-filesize") as HTMLDivElement;
const imageSource = $("#image-source") as HTMLLinkElement;
const imageDownload = $("#image-download") as HTMLLinkElement;
const imageCopy = $("#image-copy") as HTMLButtonElement;
const formatSpan = imageFormat.getElementsByClassName(
"value"
)[0] as HTMLSpanElement;
const filesizeSpan = imageFilesize.getElementsByClassName(
"value"
)[0] as HTMLSpanElement;
const engineSpan = $("#image-engine") as HTMLSpanElement;
const ImagesObserver = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) return;
const element = node as HTMLElement;
if (element.classList.contains("image")) {
element.addEventListener("click", (e) => {
e.preventDefault();
handleImageDetails(element, {
imageSrc,
imageTitle,
imageContent,
imageSource,
imageFilesize,
imageDownload,
filesizeSpan,
formatSpan,
engineSpan,
});
});
}
});
}
}
});
ImagesObserver.observe(resultsContainer, {
childList: true,
subtree: true,
});
$$(".image").forEach((node) => {
if (node.nodeType !== Node.ELEMENT_NODE) return;
const image = node as HTMLElement;
void image.addEventListener("click", (e) => {
e.preventDefault();
handleImageDetails(image, {
imageSrc,
imageTitle,
imageContent,
imageSource,
imageFilesize,
imageDownload,
filesizeSpan,
formatSpan,
engineSpan,
});
});
});
// Copy not working at localhost and only works in HTTPS pages
if (location.protocol === "http:") {
imageCopy.setAttribute("disabled", "true");
}
imageCopy.addEventListener("click", () => {
const canvas = document.createElement("canvas");
canvas.width = imageSrc.width;
canvas.height = imageSrc.height;
(canvas.getContext("2d") as CanvasRenderingContext2D).drawImage(
imageSrc,
0,
0,
imageSrc.width,
imageSrc.height
);
canvas.toBlob((blob) => {
if (!blob) return;
navigator.clipboard.write([new ClipboardItem({ "image/*": blob })]);
});
void canvas.remove();
});
const bodyContainer = $("#results") as HTMLDivElement;
const imageDetailsContainer = $("#image-details") as HTMLDivElement;
bodyContainer.addEventListener("scroll", function () {
if (bodyContainer.scrollTop < 50) {
imageDetailsContainer.style.top = "0px";
imageDetailsContainer.style.height = "80vh";
return;
}
imageDetailsContainer.style.height = "95vh";
imageDetailsContainer.style.top = `calc(${bodyContainer.scrollTop}px - 15vh)`;
});
imageDetailsContainer
.getElementsByClassName("close")[0]
.addEventListener("click", function () {
bodyContainer.classList.remove("image-open");
});
}
export function checkImagePage() {
const resultsContainer = $(".results-container");
if (!resultsContainer || resultsContainer.nodeType !== Node.ELEMENT_NODE)
return;
if (resultsContainer.classList.contains("image-page"))
setupImages(resultsContainer as HTMLDivElement);
}

View file

@ -105,6 +105,9 @@
</div> </div>
{% if endpoint == 'index' or endpoint == 'results' %} {% include {% if endpoint == 'index' or endpoint == 'results' %} {% include
'smart/madules/sidebar.html' %} {% endif %} 'smart/madules/sidebar.html' %} {% endif %}
<script src="{{ url_for('static', filename='js/app.js') }}"></script> <script
src="{{ url_for('static', filename='js/app.js') }}"
async
></script>
</body> </body>
</html> </html>

View file

@ -73,6 +73,7 @@ block body %} {% include 'smart/madules/search_header.html' %}
<button class="btn copy" id="image-copy">Copy</button> <button class="btn copy" id="image-copy">Copy</button>
</div> </div>
<div class="detail-info"> <div class="detail-info">
<div class="close">{{ icon_small('close') }}</div>
<h3 id="image-title">...</h3> <h3 id="image-title">...</h3>
<p class="description" id="image-content">...</p> <p class="description" id="image-content">...</p>
<div class="meta"> <div class="meta">