Skip to content

Commit 5a5997d

Browse files
[imageGalleryNavigation] Initial commit. (#568)
1 parent c1d1bc1 commit 5a5997d

File tree

4 files changed

+237
-0
lines changed

4 files changed

+237
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Image Gallery Navigation
2+
3+
This plugin adds features for navigating between images within a Gallery from the Image details page. This is intended to make it easier to edit metadata on each Image in a Gallery one at a time without constantly having to go back and forth between the Gallery and Image page.
4+
5+
This plugin currently adds two things to the Image details page:
6+
- A line above the tabs in the left panel that indicates the current page and total number of pages in the current Gallery. The current image number can be changed to jump to a specific image within the Gallery.
7+
- Buttons along the left/right side of the main Image display panel that allow moving to the previous/next image in the Gallery.
8+
9+
In the case of Images that are in multiple Galleries, the Gallery being navigated is set by accessing an Images from the Gallery you want to navigate. Otherwise, if you navigate directly to an Image, the first Gallery the Image belongs to will be used as the basis for navigation.
10+
11+
Known issues/limitations:
12+
- Currently is hardcoded to always sort Images by title.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.imageGalleryNav-NavInput {
2+
-moz-appearance: textfield;
3+
width: 60px;
4+
}
5+
6+
.imageGalleryNav-NavInput::-webkit-outer-spin-button,
7+
.imageGalleryNav-NavInput::-webkit-inner-spin-button {
8+
-webkit-appearance: none;
9+
margin: 0;
10+
}
11+
12+
.imageGalleryNav-leftButton,
13+
.imageGalleryNav-rightButton {
14+
position: absolute;
15+
top: 0;
16+
bottom: 120px;
17+
width: 200px;
18+
background: none;
19+
border: none;
20+
user-select: none;
21+
opacity: 0;
22+
font-size: 40px;
23+
font-weight: bold;
24+
z-index: 999;
25+
}
26+
27+
.imageGalleryNav-leftButton:hover,
28+
.imageGalleryNav-rightButton:hover {
29+
opacity: 0.6;
30+
}
31+
32+
.imageGalleryNav-leftButton {
33+
left: 15px;
34+
}
35+
36+
.imageGalleryNav-rightButton {
37+
right: 15px;
38+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
(async () => {
2+
const localStorageGalleryKey = "imageGalleryNavigation-GalleryID";
3+
4+
// In order to handle scenarios where an image is in multiple galleries, capture ID of gallery the user is navigating from.
5+
// If user navigates directly to an image URL and image is in multiple galleries, we will just use the first gallery in list.
6+
// This may break if user jumps around in browser history, in which case we will fall back to basic scenario of assuming first gallery in list.
7+
async function setupGalleryImageLinks() {
8+
document.querySelectorAll("a[href*='/images/']").forEach(function (link) {
9+
link.addEventListener("click", () => {
10+
var galleryID = window.location.pathname.split("/")[2];
11+
localStorage.setItem(localStorageGalleryKey, galleryID);
12+
});
13+
});
14+
}
15+
16+
// On image page, get data about gallery (image's position within gallery, next/prev image IDs),
17+
// add arrow buttons to page, and register arrow keypress handlers,
18+
async function setupImageContainer() {
19+
var imageID = window.location.pathname.split("/")[2];
20+
var imageGalleries = await findImage(imageID);
21+
22+
if (imageGalleries != null && imageGalleries.length > 0) {
23+
// Get first entry in galleries list.
24+
var galleryID = imageGalleries[0];
25+
26+
// Check if there is a saved gallery ID and it is in gallery list. If true, use saved ID.
27+
var savedGalleryId = localStorage.getItem(localStorageGalleryKey);
28+
if (savedGalleryId != null && imageGalleries.includes(savedGalleryId)) {
29+
galleryID = savedGalleryId;
30+
} else {
31+
localStorage.setItem(localStorageGalleryKey, galleryID);
32+
}
33+
34+
// Get gallery image list.
35+
var galleryImages = await findGalleryImages(galleryID);
36+
var totalImageCount = galleryImages.length;
37+
var currentImageIndex = galleryImages.indexOf(imageID);
38+
var nextImageID =
39+
galleryImages[wrapIndex(currentImageIndex + 1, totalImageCount)];
40+
var prevImageID =
41+
galleryImages[wrapIndex(currentImageIndex - 1, totalImageCount)];
42+
43+
// Add UI elements.
44+
insertGalleryToolbar(currentImageIndex, totalImageCount, galleryImages);
45+
insertArrowButtons(nextImageID, prevImageID);
46+
insertArrowKeyHandlers(nextImageID, prevImageID);
47+
}
48+
}
49+
50+
function insertGalleryToolbar(
51+
currentImageIndex,
52+
totalImageCount,
53+
galleryImages
54+
) {
55+
var galleryToolbar = document.createElement("div");
56+
galleryToolbar.innerHTML = `<span class="imageGalleryNav-NavTitle">Gallery Image: </span><input type="number" class="text-input imageGalleryNav-NavInput" value="${
57+
currentImageIndex + 1
58+
}"> <span class="imageGalleryNav-NavTotal">/ ${totalImageCount}</span>`;
59+
60+
var toolbar = document.querySelector("div.image-toolbar");
61+
toolbar.parentNode.insertBefore(galleryToolbar, toolbar.nextSibling);
62+
63+
galleryToolbar.querySelector("input").addEventListener("change", (e) => {
64+
var imageIndex = e.target.value - 1;
65+
if (imageIndex < 0 || imageIndex > totalImageCount - 1) {
66+
e.target.value = currentImageIndex + 1;
67+
e.target.select();
68+
} else {
69+
var imageID = galleryImages[imageIndex];
70+
redirectToImage(imageID);
71+
}
72+
});
73+
74+
galleryToolbar.querySelector("input").addEventListener("focus", (e) => {
75+
e.target.select();
76+
});
77+
}
78+
79+
function insertArrowButtons(nextImageID, prevImageID) {
80+
var leftButton = document.createElement("button");
81+
leftButton.className = "imageGalleryNav-leftButton btn btn-primary";
82+
leftButton.innerText = "<";
83+
leftButton.addEventListener("click", () => {
84+
redirectToImage(prevImageID);
85+
});
86+
87+
var rightButton = document.createElement("button");
88+
rightButton.className = "imageGalleryNav-rightButton btn btn-primary";
89+
rightButton.innerText = ">";
90+
rightButton.addEventListener("click", () => {
91+
redirectToImage(nextImageID);
92+
});
93+
94+
document.querySelector("div.image-container").prepend(leftButton);
95+
document.querySelector("div.image-container").prepend(rightButton);
96+
}
97+
98+
function insertArrowKeyHandlers(nextImageID, prevImageID) {
99+
// TODO: Determine how to cleanup this listener properly, so that we can handle left/right arrow key.
100+
// // Add keypress handlers for arrow keys.
101+
// document.addEventListener('keydown', function onKeydownHandler(e) {
102+
// if (!isTextboxFocused()) {
103+
// if (e.key === "ArrowRight") {
104+
// redirectToImage(nextImageID);
105+
// } else if (e.key === "ArrowLeft") {
106+
// redirectToImage(prevImageID);
107+
// }
108+
// }
109+
// });
110+
}
111+
112+
// *** Utility Functions ***
113+
114+
function redirectToImage(imageID) {
115+
const baseImagesPath = "/images/";
116+
// window.location.href = `${baseImagesPath}${imageID}`;
117+
window.location.replace(`${baseImagesPath}${imageID}`);
118+
}
119+
120+
function isTextboxFocused() {
121+
if (
122+
document.activeElement.nodeName == "TEXTAREA" ||
123+
document.activeElement.nodeName == "INPUT" ||
124+
(document.activeElement.nodeName == "DIV" &&
125+
document.activeElement.isContentEditable)
126+
) {
127+
return true;
128+
} else {
129+
return false;
130+
}
131+
}
132+
133+
function wrapIndex(index, arrayLength) {
134+
return ((index % arrayLength) + arrayLength) % arrayLength;
135+
}
136+
137+
// *** GQL Calls ***
138+
139+
// Find Image by ID
140+
// Return Galleries list (id)
141+
async function findImage(imageID) {
142+
const variables = { id: imageID };
143+
const query = `query ($id: ID!) { findImage(id: $id) { galleries { id } } }`;
144+
return await csLib
145+
.callGQL({ query, variables })
146+
.then((data) => data.findImage.galleries.map((item) => item.id));
147+
}
148+
149+
// Find Images by Gallery ID
150+
// Return Images list (id)
151+
async function findGalleryImages(galleryID) {
152+
const imageFilter = {
153+
galleries: { value: galleryID, modifier: "INCLUDES_ALL" },
154+
};
155+
const findFilter = { per_page: -1, sort: "title" };
156+
const variables = { image_filter: imageFilter, filter: findFilter };
157+
const query = `query ($image_filter: ImageFilterType!, $filter: FindFilterType!) { findImages(image_filter: $image_filter, filter: $filter) { images { id } } }`;
158+
return await csLib
159+
.callGQL({ query, variables })
160+
.then((data) => data.findImages.images.map((item) => item.id));
161+
}
162+
163+
// Wait for galleries page to load.
164+
csLib.PathElementListener(
165+
"/galleries/",
166+
".image-card",
167+
setupGalleryImageLinks
168+
); // PathElementListener is from cs-ui-lib.js
169+
170+
// Wait for images page to load.
171+
csLib.PathElementListener(
172+
"/images/",
173+
".image-container",
174+
setupImageContainer
175+
); // PathElementListener is from cs-ui-lib.js
176+
})();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: imageGalleryNavigation
2+
# requires: CommunityScriptsUILibrary
3+
description: This plugin adds features for navigating between images within a Gallery from the Image details page.
4+
version: 0.1
5+
ui:
6+
requires:
7+
- CommunityScriptsUILibrary
8+
javascript:
9+
- imageGalleryNavigation.js
10+
css:
11+
- imageGalleryNavigation.css

0 commit comments

Comments
 (0)