mirror of
https://github.com/wah-su/wahs.wah.su.git
synced 2025-04-05 15:54:37 +00:00
feat: add image favorite client-side
This commit is contained in:
parent
9e754e4a88
commit
a0c54cdc0e
15 changed files with 541 additions and 127 deletions
|
@ -22,6 +22,7 @@ import type { ReactNode } from "react";
|
|||
import ImagesPage from "./templates/images";
|
||||
import VideosPage from "./templates/videos";
|
||||
import ImagePage from "./templates/viewImage";
|
||||
import FavoritesPage from "./templates/favorites";
|
||||
|
||||
const S3 = new S3Client({
|
||||
region: "auto",
|
||||
|
@ -202,6 +203,7 @@ generateHTMLFile(
|
|||
videos.length
|
||||
} Videos | ${images.length + videos.length} Total`,
|
||||
[
|
||||
environment == "dev" ? "/static/Placeholders.js" : "/static/Placeholders.min.js",
|
||||
environment == "dev" ? "/static/utils.js" : "/static/utils.min.js",
|
||||
environment == "dev"
|
||||
? "/static/populateIndex.js"
|
||||
|
@ -216,6 +218,7 @@ generateHTMLFile(
|
|||
"/images/",
|
||||
`Image page of Wah-Collection | ${images.length} Images`,
|
||||
[
|
||||
environment == "dev" ? "/static/Placeholders.js" : "/static/Placeholders.min.js",
|
||||
environment == "dev" ? "/static/utils.js" : "/static/utils.min.js",
|
||||
environment == "dev"
|
||||
? "/static/populateImages.js"
|
||||
|
@ -241,6 +244,7 @@ generateHTMLFile(
|
|||
"/videos/",
|
||||
`Video page of Wah-Collection | ${videos.length} Videos`,
|
||||
[
|
||||
environment == "dev" ? "/static/Placeholders.js" : "/static/Placeholders.min.js",
|
||||
environment == "dev" ? "/static/utils.js" : "/static/utils.min.js",
|
||||
environment == "dev"
|
||||
? "/static/populateVideos.js"
|
||||
|
@ -249,3 +253,19 @@ generateHTMLFile(
|
|||
<VideosPage />,
|
||||
"out/videos/index.html"
|
||||
);
|
||||
|
||||
generateHTMLFile(
|
||||
"Wah-Collection/Favorites",
|
||||
"/favorites/",
|
||||
`Your favorites of Wah-Collection`,
|
||||
[
|
||||
environment == "dev" ? "/static/Placeholders.js" : "/static/Placeholders.min.js",
|
||||
environment == "dev" ? "/static/utils.js" : "/static/utils.min.js",
|
||||
environment == "dev"
|
||||
? "/static/populateFavorites.js"
|
||||
: "/static/populateFavorites.min.js",
|
||||
],
|
||||
<FavoritesPage />,
|
||||
"out/favorites/index.html"
|
||||
);
|
||||
|
||||
|
|
|
@ -132,3 +132,27 @@
|
|||
-webkit-mask-size: 100% 100%;
|
||||
mask-size: 100% 100%;
|
||||
}
|
||||
|
||||
.material-symbols--favorite-outline-rounded {
|
||||
display: inline-block;
|
||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 20.325q-.35 0-.712-.125t-.638-.4l-1.725-1.575q-2.65-2.425-4.788-4.812T2 8.15Q2 5.8 3.575 4.225T7.5 2.65q1.325 0 2.5.562t2 1.538q.825-.975 2-1.537t2.5-.563q2.35 0 3.925 1.575T22 8.15q0 2.875-2.125 5.275T15.05 18.25l-1.7 1.55q-.275.275-.637.4t-.713.125M11.05 6.75q-.725-1.025-1.55-1.563t-2-.537q-1.5 0-2.5 1t-1 2.5q0 1.3.925 2.763t2.213 2.837t2.65 2.575T12 18.3q.85-.775 2.213-1.975t2.65-2.575t2.212-2.837T20 8.15q0-1.5-1-2.5t-2.5-1q-1.175 0-2 .538T12.95 6.75q-.175.25-.425.375T12 7.25t-.525-.125t-.425-.375m.95 4.725'/%3E%3C/svg%3E");
|
||||
background-color: currentColor;
|
||||
-webkit-mask-image: var(--svg);
|
||||
mask-image: var(--svg);
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100% 100%;
|
||||
mask-size: 100% 100%;
|
||||
}
|
||||
|
||||
.material-symbols--favorite-rounded {
|
||||
display: inline-block;
|
||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 20.325q-.35 0-.712-.125t-.638-.4l-1.725-1.575q-2.65-2.425-4.788-4.812T2 8.15Q2 5.8 3.575 4.225T7.5 2.65q1.325 0 2.5.562t2 1.538q.825-.975 2-1.537t2.5-.563q2.35 0 3.925 1.575T22 8.15q0 2.875-2.125 5.275T15.05 18.25l-1.7 1.55q-.275.275-.637.4t-.713.125'/%3E%3C/svg%3E");
|
||||
background-color: currentColor;
|
||||
-webkit-mask-image: var(--svg);
|
||||
mask-image: var(--svg);
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100% 100%;
|
||||
mask-size: 100% 100%;
|
||||
}
|
62
src/static/Placeholders.js
Normal file
62
src/static/Placeholders.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
function Placeholder() {
|
||||
const placeholderRoot = document.createElement("div");
|
||||
placeholderRoot.dataset.type = "placeholder__image__container";
|
||||
placeholderRoot.className =
|
||||
"relative aspect-square min-w-48 min-h-48 sm:min-w-auto rounded-sm overflow-hidden";
|
||||
|
||||
const placeholderImage = document.createElement("a");
|
||||
placeholderImage.dataset.type = "placeholder__image";
|
||||
|
||||
const placeholderImageLoader = document.createElement("div");
|
||||
placeholderImageLoader.dataset.type = "placeholder__image__loader";
|
||||
placeholderImageLoader.className =
|
||||
"w-full h-full absolute inset-0 bg-gray-400 opacity-30 animate-pulse z-[3]";
|
||||
|
||||
const favoriteButton = document.createElement("button");
|
||||
favoriteButton.dataset.type = "image__fav";
|
||||
favoriteButton.className =
|
||||
"hidden absolute right-2 top-2 w-8 h-8 cursor-pointer";
|
||||
favoriteButton.innerHTML =
|
||||
'<div class="text-[#faebeb] hover:text-orange-500 transition-colors material-symbols--favorite-outline-rounded w-full h-full"></div>';
|
||||
const unfavoriteButton = document.createElement("button");
|
||||
unfavoriteButton.dataset.type = "image__unfav";
|
||||
unfavoriteButton.className =
|
||||
"hidden absolute right-2 top-2 w-8 h-8 cursor-pointer";
|
||||
unfavoriteButton.innerHTML =
|
||||
'<div class="text-[#faebeb] hover:text-orange-500 transition-colors material-symbols--favorite-rounded w-full h-full"></div>';
|
||||
|
||||
placeholderImage.appendChild(placeholderImageLoader);
|
||||
placeholderRoot.appendChild(placeholderImage);
|
||||
placeholderRoot.appendChild(favoriteButton);
|
||||
placeholderRoot.appendChild(unfavoriteButton);
|
||||
|
||||
return placeholderRoot;
|
||||
}
|
||||
|
||||
function PlaceholderVid() {
|
||||
const placeholder = document.createElement("video");
|
||||
placeholder.dataset.type = "placeholder__video";
|
||||
placeholder.controls = true;
|
||||
placeholder.className =
|
||||
"relative aspect-square w-full h-full rounded-sm [&:not(:fullscreen)]:object-cover";
|
||||
|
||||
const placeholder_loader = document.createElement("div");
|
||||
placeholder_loader.dataset.type = "placeholder__video__loader";
|
||||
placeholder_loader.className =
|
||||
"w-full h-full absolute inset-0 bg-gray-400 opacity-30 animate-pulse z-[3]";
|
||||
const placeholder_source = document.createElement("source");
|
||||
placeholder.appendChild(placeholder_loader);
|
||||
placeholder.appendChild(placeholder_source);
|
||||
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
function AllLink(href, title) {
|
||||
const link = document.createElement("a");
|
||||
link.href = href;
|
||||
link.className = "text-[#f9ebeb] hover:bg-orange-600 rounded-sm overflow-hidden transition-colors aspect-square bg-yellow-950 min-w-48 sm:min-w-auto flex items-center justify-center flex-col";
|
||||
link.innerHTML = `
|
||||
<span class="material-symbols--arrow-forward-rounded w-16 h-16"></span>
|
||||
<p class="text-xl">${title}</p>`
|
||||
return link
|
||||
}
|
|
@ -40,12 +40,12 @@ function renderImage(endpoint, bucket, prefix, isrc, placeholder) {
|
|||
const Img = document.createElement("img");
|
||||
blurImg.src = `https://wsrv.nl/?url=${encodeURI(src)}&w=16&h=16`;
|
||||
blurImg.className = "object-cover w-full h-full absolute inset-0";
|
||||
blurImg.alt = `Loading: ${isrc}`
|
||||
blurImg.alt = `Loading: ${isrc}`;
|
||||
Img.dataset.hi = "fit";
|
||||
Img.src = `https://wsrv.nl/?url=${encodeURI(src)}`;
|
||||
Img.className = "invisible w-full h-full object-contain";
|
||||
Img.loading = "lazy";
|
||||
Img.alt = isrc
|
||||
Img.alt = isrc;
|
||||
|
||||
placeholder.appendChild(blurImg);
|
||||
placeholder.appendChild(Img);
|
||||
|
@ -150,6 +150,33 @@ function _tmp_loadNav(url, iid) {
|
|||
const download = document.querySelectorAll("#act_download");
|
||||
const newtab = document.querySelectorAll("#act_newtab");
|
||||
|
||||
const favoriteButton = document.querySelectorAll('[data-type="image__fav"]');
|
||||
const unfavoriteButton = document.querySelectorAll(
|
||||
'[data-type="image__unfav"]'
|
||||
);
|
||||
|
||||
const isFav = getFavorites().find((el) => el.iid == iid) || false;
|
||||
favoriteButton.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
addFavorites(iid, "image");
|
||||
favoriteButton.forEach((item) => item.classList.add("hidden"));
|
||||
unfavoriteButton.forEach((item) => item.classList.remove("hidden"));
|
||||
});
|
||||
});
|
||||
unfavoriteButton.forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
removeFavorites(iid);
|
||||
favoriteButton.forEach((item) => item.classList.remove("hidden"));
|
||||
unfavoriteButton.forEach((item) => item.classList.add("hidden"));
|
||||
});
|
||||
});
|
||||
console.log(isFav)
|
||||
if (!isFav) {
|
||||
favoriteButton.forEach((item) => item.classList.remove("hidden"));
|
||||
} else {
|
||||
unfavoriteButton.forEach((item) => item.classList.remove("hidden"));
|
||||
}
|
||||
|
||||
function handleClickPrev() {
|
||||
window.location.href = `/image/?id=${Number(iid) - 1}`;
|
||||
}
|
||||
|
@ -211,6 +238,38 @@ function _tmp_loadNav(url, iid) {
|
|||
});
|
||||
}
|
||||
|
||||
function getFavorites() {
|
||||
const favs = localStorage.getItem("favorites");
|
||||
if (!favs) {
|
||||
setFavorites([]);
|
||||
return [];
|
||||
}
|
||||
return JSON.parse(favs);
|
||||
}
|
||||
|
||||
function setFavorites(favs) {
|
||||
localStorage.setItem("favorites", JSON.stringify(favs));
|
||||
}
|
||||
|
||||
function addFavorites(iid, type) {
|
||||
const favs = getFavorites();
|
||||
const newFav = {
|
||||
iid,
|
||||
type,
|
||||
};
|
||||
const newFavs = [...favs, newFav];
|
||||
setFavorites(newFavs);
|
||||
}
|
||||
|
||||
function removeFavorites(iid) {
|
||||
const idx = getFavorites().findIndex((el) => el.iid == iid);
|
||||
const favs = getFavorites();
|
||||
if (idx > -1) {
|
||||
favs.splice(idx, 1);
|
||||
setFavorites(favs);
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const u = new URL(window.location.href);
|
||||
const iid = u.searchParams.get("id");
|
||||
|
|
40
src/static/populateFavorites.js
Normal file
40
src/static/populateFavorites.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
async function __tmp_loadFavs() {
|
||||
const container = document.getElementById("favorites_favorites");
|
||||
|
||||
let config = await get("/data/config.json");
|
||||
let images = await get("/data/images.json");
|
||||
let videos = await get("/data/videos.json");
|
||||
let favs = getFavorites();
|
||||
|
||||
const start = getOffset();
|
||||
const end = getOffset() + getImagesPerPage();
|
||||
|
||||
if (favs.slice(start, end) <= 0) {
|
||||
container.innerHTML =
|
||||
'<p class="text-2xl mx-auto col-span-full">No Favorites Found!</p>';
|
||||
}
|
||||
|
||||
favs.slice(start, end).forEach((item, idx) => {
|
||||
let pl;
|
||||
if (item.type == "image") {
|
||||
pl = container.appendChild(Placeholder());
|
||||
setTimeout(() => {
|
||||
renderImage(
|
||||
config.endpoint,
|
||||
config.bucket,
|
||||
config.prefix,
|
||||
images[item.iid],
|
||||
item.iid,
|
||||
pl
|
||||
);
|
||||
}, 250);
|
||||
} else {
|
||||
console.log("video not supported");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
enableNav();
|
||||
__tmp_loadFavs();
|
||||
};
|
|
@ -1,19 +1,3 @@
|
|||
function Placeholder() {
|
||||
const placeholder = document.createElement("a");
|
||||
placeholder.dataset.type = "placeholder__image";
|
||||
placeholder.className =
|
||||
"relative aspect-square min-w-48 sm:min-w-auto rounded-sm overflow-hidden";
|
||||
|
||||
const placeholder_loader = document.createElement("div");
|
||||
placeholder_loader.dataset.type = "placeholder__image__loader";
|
||||
placeholder_loader.className =
|
||||
"w-full h-full absolute inset-0 bg-gray-400 opacity-30 animate-pulse z-[3]";
|
||||
|
||||
placeholder.appendChild(placeholder_loader);
|
||||
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
async function __tmp_loadImages() {
|
||||
const container = document.getElementById("images_images");
|
||||
|
||||
|
@ -35,7 +19,7 @@ async function __tmp_loadImages() {
|
|||
|
||||
images.slice(start, end).forEach((image, idx) => {
|
||||
container.appendChild(Placeholder());
|
||||
let Images = document.querySelectorAll('[data-type="placeholder__image"]');
|
||||
let Images = document.querySelectorAll('[data-type="placeholder__image__container"]');
|
||||
const iid = Number(start) + Number(idx);
|
||||
setTimeout(() => {
|
||||
renderImage(
|
||||
|
|
|
@ -16,7 +16,11 @@ async function populateIndex() {
|
|||
let images = await get("/data/images.json");
|
||||
let videos = await get("/data/videos.json");
|
||||
|
||||
const Images = document.querySelectorAll('[data-type="placeholder__image"]');
|
||||
let favs = getFavorites();
|
||||
|
||||
const Images = document.querySelectorAll(
|
||||
'[data-type="placeholder__image__container"]'
|
||||
);
|
||||
const VisibleImages = [];
|
||||
Images.forEach((placeholder) => {
|
||||
if (placeholder.checkVisibility()) {
|
||||
|
@ -49,8 +53,38 @@ async function populateIndex() {
|
|||
config.bucket,
|
||||
config.prefix,
|
||||
video.src,
|
||||
VisibleVideos[idx])
|
||||
VisibleVideos[idx]
|
||||
);
|
||||
});
|
||||
|
||||
const FavoritesContainer = document.getElementById("index_favorites");
|
||||
if (favs.length > 0) {
|
||||
FavoritesContainer.innerHTML = "";
|
||||
favs.forEach((item, idx) => {
|
||||
if (idx >= 11) return;
|
||||
let pl;
|
||||
if (item.type == "image") {
|
||||
pl = FavoritesContainer.appendChild(Placeholder());
|
||||
setTimeout(() => {
|
||||
renderImage(
|
||||
config.endpoint,
|
||||
config.bucket,
|
||||
config.prefix,
|
||||
images[item.iid],
|
||||
item.iid,
|
||||
pl
|
||||
);
|
||||
}, 250);
|
||||
} else {
|
||||
console.log("video not supported");
|
||||
}
|
||||
});
|
||||
if (favs.length >= 11) {
|
||||
FavoritesContainer.appendChild(
|
||||
AllLink("/favorites/", "View All Favorites")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
window.onload = () => {
|
||||
populateIndex();
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
function PlaceholderVid() {
|
||||
const placeholder = document.createElement("video");
|
||||
placeholder.dataset.type = "placeholder__video";
|
||||
placeholder.controls = true;
|
||||
placeholder.className =
|
||||
"relative aspect-square w-full h-full rounded-sm [&:not(:fullscreen)]:object-cover";
|
||||
|
||||
const placeholder_loader = document.createElement("div");
|
||||
placeholder_loader.dataset.type = "placeholder__video__loader";
|
||||
placeholder_loader.className =
|
||||
"w-full h-full absolute inset-0 bg-gray-400 opacity-30 animate-pulse z-[3]";
|
||||
const placeholder_source = document.createElement("source");
|
||||
placeholder.appendChild(placeholder_loader);
|
||||
placeholder.appendChild(placeholder_source);
|
||||
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
async function __tmp_loadVideos() {
|
||||
const container = document.getElementById("videos_videos");
|
||||
|
||||
|
|
|
@ -521,6 +521,12 @@
|
|||
}
|
||||
}
|
||||
@layer utilities {
|
||||
.pointer-events-auto {
|
||||
pointer-events: auto;
|
||||
}
|
||||
.pointer-events-none {
|
||||
pointer-events: none;
|
||||
}
|
||||
.collapse {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
@ -545,21 +551,51 @@
|
|||
.top-0 {
|
||||
top: calc(var(--spacing) * 0);
|
||||
}
|
||||
.top-2 {
|
||||
top: calc(var(--spacing) * 2);
|
||||
}
|
||||
.right-0 {
|
||||
right: calc(var(--spacing) * 0);
|
||||
}
|
||||
.right-2 {
|
||||
right: calc(var(--spacing) * 2);
|
||||
}
|
||||
.right-8 {
|
||||
right: calc(var(--spacing) * 8);
|
||||
}
|
||||
.bottom-2 {
|
||||
bottom: calc(var(--spacing) * 2);
|
||||
}
|
||||
.bottom-8 {
|
||||
bottom: calc(var(--spacing) * 8);
|
||||
}
|
||||
.left-0 {
|
||||
left: calc(var(--spacing) * 0);
|
||||
}
|
||||
.z-4 {
|
||||
z-index: 4;
|
||||
}
|
||||
.z-5 {
|
||||
z-index: 5;
|
||||
}
|
||||
.z-10 {
|
||||
z-index: 10;
|
||||
}
|
||||
.z-\[3\] {
|
||||
z-index: 3;
|
||||
}
|
||||
.z-\[4\] {
|
||||
z-index: 4;
|
||||
}
|
||||
.z-\[15\] {
|
||||
z-index: 15;
|
||||
}
|
||||
.\[grid-column\:span_2\] {
|
||||
grid-column: span 2;
|
||||
}
|
||||
.col-span-full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
.\[grid-row\:span_2\] {
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
@ -638,6 +674,12 @@
|
|||
.h-8 {
|
||||
height: calc(var(--spacing) * 8);
|
||||
}
|
||||
.h-12 {
|
||||
height: calc(var(--spacing) * 12);
|
||||
}
|
||||
.h-14 {
|
||||
height: calc(var(--spacing) * 14);
|
||||
}
|
||||
.h-16 {
|
||||
height: calc(var(--spacing) * 16);
|
||||
}
|
||||
|
@ -659,6 +701,12 @@
|
|||
.w-8 {
|
||||
width: calc(var(--spacing) * 8);
|
||||
}
|
||||
.w-12 {
|
||||
width: calc(var(--spacing) * 12);
|
||||
}
|
||||
.w-14 {
|
||||
width: calc(var(--spacing) * 14);
|
||||
}
|
||||
.w-16 {
|
||||
width: calc(var(--spacing) * 16);
|
||||
}
|
||||
|
@ -752,6 +800,9 @@
|
|||
.bg-gray-400\/50 {
|
||||
background-color: color-mix(in oklab, var(--color-gray-400) 50%, transparent);
|
||||
}
|
||||
.bg-gray-600 {
|
||||
background-color: var(--color-gray-600);
|
||||
}
|
||||
.bg-orange-800 {
|
||||
background-color: var(--color-orange-800);
|
||||
}
|
||||
|
@ -779,6 +830,10 @@
|
|||
.py-4 {
|
||||
padding-block: calc(var(--spacing) * 4);
|
||||
}
|
||||
.text-2xl {
|
||||
font-size: var(--text-2xl);
|
||||
line-height: var(--tw-leading, var(--text-2xl--line-height));
|
||||
}
|
||||
.text-4xl {
|
||||
font-size: var(--text-4xl);
|
||||
line-height: var(--tw-leading, var(--text-4xl--line-height));
|
||||
|
@ -801,15 +856,30 @@
|
|||
.text-\[\#f9ebeb\] {
|
||||
color: #f9ebeb;
|
||||
}
|
||||
.text-\[\#faebeb\] {
|
||||
color: #faebeb;
|
||||
}
|
||||
.text-gray-100 {
|
||||
color: var(--color-gray-100);
|
||||
}
|
||||
.text-gray-200 {
|
||||
color: var(--color-gray-200);
|
||||
}
|
||||
.text-gray-200\/75 {
|
||||
color: color-mix(in oklab, var(--color-gray-200) 75%, transparent);
|
||||
}
|
||||
.text-gray-300 {
|
||||
color: var(--color-gray-300);
|
||||
}
|
||||
.text-gray-500 {
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
.text-orange-400 {
|
||||
color: var(--color-orange-400);
|
||||
}
|
||||
.text-slate-900 {
|
||||
color: var(--color-slate-900);
|
||||
}
|
||||
.text-white {
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
@ -1099,6 +1169,28 @@
|
|||
-webkit-mask-size: 100% 100%;
|
||||
mask-size: 100% 100%;
|
||||
}
|
||||
.material-symbols--favorite-outline-rounded {
|
||||
display: inline-block;
|
||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 20.325q-.35 0-.712-.125t-.638-.4l-1.725-1.575q-2.65-2.425-4.788-4.812T2 8.15Q2 5.8 3.575 4.225T7.5 2.65q1.325 0 2.5.562t2 1.538q.825-.975 2-1.537t2.5-.563q2.35 0 3.925 1.575T22 8.15q0 2.875-2.125 5.275T15.05 18.25l-1.7 1.55q-.275.275-.637.4t-.713.125M11.05 6.75q-.725-1.025-1.55-1.563t-2-.537q-1.5 0-2.5 1t-1 2.5q0 1.3.925 2.763t2.213 2.837t2.65 2.575T12 18.3q.85-.775 2.213-1.975t2.65-2.575t2.212-2.837T20 8.15q0-1.5-1-2.5t-2.5-1q-1.175 0-2 .538T12.95 6.75q-.175.25-.425.375T12 7.25t-.525-.125t-.425-.375m.95 4.725'/%3E%3C/svg%3E");
|
||||
background-color: currentColor;
|
||||
-webkit-mask-image: var(--svg);
|
||||
mask-image: var(--svg);
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100% 100%;
|
||||
mask-size: 100% 100%;
|
||||
}
|
||||
.material-symbols--favorite-rounded {
|
||||
display: inline-block;
|
||||
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 20.325q-.35 0-.712-.125t-.638-.4l-1.725-1.575q-2.65-2.425-4.788-4.812T2 8.15Q2 5.8 3.575 4.225T7.5 2.65q1.325 0 2.5.562t2 1.538q.825-.975 2-1.537t2.5-.563q2.35 0 3.925 1.575T22 8.15q0 2.875-2.125 5.275T15.05 18.25l-1.7 1.55q-.275.275-.637.4t-.713.125'/%3E%3C/svg%3E");
|
||||
background-color: currentColor;
|
||||
-webkit-mask-image: var(--svg);
|
||||
mask-image: var(--svg);
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-size: 100% 100%;
|
||||
mask-size: 100% 100%;
|
||||
}
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
|
|
|
@ -13,17 +13,27 @@ function getAspectVid(video) {
|
|||
return Number(video.videoWidth / video.videoHeight);
|
||||
}
|
||||
|
||||
function renderImage(endpoint, bucket, prefix, isrc, iid, placeholder) {
|
||||
function renderImage(endpoint, bucket, prefix, isrc, iid, placeholderRoot) {
|
||||
const src = `${endpoint}/${bucket}/${prefix}/${isrc}`;
|
||||
const loader = placeholder.querySelector(
|
||||
const placeholderImage = placeholderRoot.querySelector(
|
||||
'[data-type="placeholder__image"]'
|
||||
);
|
||||
const placeholderImageLoader = placeholderImage.querySelector(
|
||||
'[data-type="placeholder__image__loader"]'
|
||||
);
|
||||
const favoriteButton = placeholderRoot.querySelector(
|
||||
'[data-type="image__fav"]'
|
||||
);
|
||||
const unfavoriteButton = placeholderRoot.querySelector(
|
||||
'[data-type="image__unfav"]'
|
||||
);
|
||||
|
||||
const blurImg = document.createElement("img");
|
||||
const Img = document.createElement("img");
|
||||
blurImg.src = `https://wsrv.nl/?url=${encodeURI(src)}&w=16&h=16`;
|
||||
blurImg.className = "object-cover w-full h-full absolute inset-0";
|
||||
blurImg.loading = "lazy";
|
||||
blurImg.alt = `Loading: ${isrc}`
|
||||
blurImg.alt = `Loading: ${isrc}`;
|
||||
Img.src = `https://wsrv.nl/?url=${encodeURI(src)}&w=256&h=256`;
|
||||
Img.srcset = `https://wsrv.nl/?url=${encodeURI(
|
||||
src
|
||||
|
@ -31,19 +41,41 @@ function renderImage(endpoint, bucket, prefix, isrc, iid, placeholder) {
|
|||
Img.sizes = `(max-width: 600px) 256px, 512px`;
|
||||
Img.className = "invisible object-cover w-full h-full absolute inset-0";
|
||||
Img.loading = "lazy";
|
||||
Img.alt = isrc
|
||||
Img.alt = isrc;
|
||||
|
||||
const isFav = getFavorites().find((el) => el.iid == iid) || false;
|
||||
favoriteButton.addEventListener("click", () => {
|
||||
addFavorites(iid, "image");
|
||||
favoriteButton.classList.add("hidden");
|
||||
unfavoriteButton.classList.remove("hidden");
|
||||
});
|
||||
unfavoriteButton.addEventListener("click", () => {
|
||||
removeFavorites(iid);
|
||||
favoriteButton.classList.remove("hidden");
|
||||
unfavoriteButton.classList.add("hidden");
|
||||
});
|
||||
if (!isFav) {
|
||||
favoriteButton.classList.remove("hidden");
|
||||
} else {
|
||||
unfavoriteButton.classList.remove("hidden");
|
||||
}
|
||||
|
||||
const view = getView();
|
||||
const container = document.getElementById("images_images");
|
||||
const container = document.getElementById("images_images") || document.getElementById("favorites_favorites");
|
||||
|
||||
placeholder.appendChild(blurImg);
|
||||
placeholder.appendChild(Img);
|
||||
placeholderImage.appendChild(blurImg);
|
||||
placeholderImage.appendChild(Img);
|
||||
|
||||
if (
|
||||
view == "masonry" &&
|
||||
["/images", "/images/", "/images/index.html"].includes(
|
||||
window.location.pathname
|
||||
)
|
||||
[
|
||||
"/images",
|
||||
"/images/",
|
||||
"/images/index.html",
|
||||
"/favorites",
|
||||
"/favorites/",
|
||||
"/favorites/index.html",
|
||||
].includes(window.location.pathname)
|
||||
) {
|
||||
container.classList.remove(
|
||||
"xl:grid-cols-[repeat(auto-fill,minmax(20%,1fr))]"
|
||||
|
@ -55,21 +87,21 @@ function renderImage(endpoint, bucket, prefix, isrc, iid, placeholder) {
|
|||
const aspect = getAspect(blurImg);
|
||||
|
||||
if (aspect < 0.95) {
|
||||
placeholder.classList.remove("aspect-square");
|
||||
placeholder.classList.add("aspect-[1/2]");
|
||||
placeholder.classList.add("w-full");
|
||||
placeholder.classList.add("h-full");
|
||||
placeholderRoot.classList.remove("aspect-square");
|
||||
placeholderRoot.classList.add("aspect-[1/2]");
|
||||
placeholderRoot.classList.add("w-full");
|
||||
placeholderRoot.classList.add("h-full");
|
||||
Img.classList.add("object-cover");
|
||||
blurImg.classList.add("object-cover");
|
||||
placeholder.classList.add("[grid-row:span_2]");
|
||||
placeholderRoot.classList.add("[grid-row:span_2]");
|
||||
} else if (aspect > 1.05) {
|
||||
placeholder.classList.remove("aspect-square");
|
||||
placeholder.classList.add("aspect-[2/1]");
|
||||
placeholder.classList.add("w-full");
|
||||
placeholder.classList.add("h-full");
|
||||
placeholderRoot.classList.remove("aspect-square");
|
||||
placeholderRoot.classList.add("aspect-[2/1]");
|
||||
placeholderRoot.classList.add("w-full");
|
||||
placeholderRoot.classList.add("h-full");
|
||||
Img.classList.add("object-cover");
|
||||
blurImg.classList.add("object-cover");
|
||||
placeholder.classList.add("[grid-column:span_2]");
|
||||
placeholderRoot.classList.add("[grid-column:span_2]");
|
||||
}
|
||||
|
||||
blurImg.removeEventListener("load", this);
|
||||
|
@ -78,9 +110,9 @@ function renderImage(endpoint, bucket, prefix, isrc, iid, placeholder) {
|
|||
|
||||
Img.addEventListener("load", () => {
|
||||
Img.classList.remove("invisible");
|
||||
if (view == "grid") blurImg.remove();
|
||||
loader.remove();
|
||||
placeholder.href = `/image/?id=${iid}`;
|
||||
blurImg.remove();
|
||||
placeholderImageLoader.remove();
|
||||
placeholderImage.href = `/image/?id=${iid}`;
|
||||
Img.removeEventListener("load", this);
|
||||
});
|
||||
}
|
||||
|
@ -134,6 +166,38 @@ function renderVideo(endpoint, bucket, prefix, vsrc, placeholder) {
|
|||
}
|
||||
}
|
||||
|
||||
function getFavorites() {
|
||||
const favs = localStorage.getItem("favorites");
|
||||
if (!favs) {
|
||||
setFavorites([]);
|
||||
return [];
|
||||
}
|
||||
return JSON.parse(favs);
|
||||
}
|
||||
|
||||
function setFavorites(favs) {
|
||||
localStorage.setItem("favorites", JSON.stringify(favs));
|
||||
}
|
||||
|
||||
function addFavorites(iid, type) {
|
||||
const favs = getFavorites();
|
||||
const newFav = {
|
||||
iid,
|
||||
type,
|
||||
};
|
||||
const newFavs = [...favs, newFav];
|
||||
setFavorites(newFavs);
|
||||
}
|
||||
|
||||
function removeFavorites(iid) {
|
||||
const idx = getFavorites().findIndex((el) => el.iid == iid);
|
||||
const favs = getFavorites();
|
||||
if (idx > -1) {
|
||||
favs.splice(idx, 1);
|
||||
setFavorites(favs);
|
||||
}
|
||||
}
|
||||
|
||||
function setView(view) {
|
||||
localStorage.setItem("view", view);
|
||||
}
|
||||
|
@ -244,9 +308,14 @@ function getOffset() {
|
|||
function enableNav() {
|
||||
function handleClickPrev() {
|
||||
if (
|
||||
["/images", "/images/", "/images/index.html"].includes(
|
||||
window.location.pathname
|
||||
)
|
||||
[
|
||||
"/images",
|
||||
"/images/",
|
||||
"/images/index.html",
|
||||
"/favorites",
|
||||
"/favorites/",
|
||||
"/favorites/index.html",
|
||||
].includes(window.location.pathname)
|
||||
) {
|
||||
setOffset(getOffset() - getImagesPerPage());
|
||||
} else if (
|
||||
|
@ -260,9 +329,14 @@ function enableNav() {
|
|||
|
||||
function handleClickNext() {
|
||||
if (
|
||||
["/images", "/images/", "/images/index.html"].includes(
|
||||
window.location.pathname
|
||||
)
|
||||
[
|
||||
"/images",
|
||||
"/images/",
|
||||
"/images/index.html",
|
||||
"/favorites",
|
||||
"/favorites/",
|
||||
"/favorites/index.html",
|
||||
].includes(window.location.pathname)
|
||||
) {
|
||||
setOffset(getOffset() + getImagesPerPage());
|
||||
} else if (
|
||||
|
@ -274,20 +348,18 @@ function enableNav() {
|
|||
}
|
||||
}
|
||||
|
||||
function handleClickIpp(ipp) {
|
||||
setVideosPerPage(ipp);
|
||||
const url = new URL(window.location.toString());
|
||||
url.searchParams.set("VideosPP", ipp);
|
||||
window.location.href = url.href;
|
||||
}
|
||||
|
||||
function handleClickIpp(ipp) {
|
||||
const url = new URL(window.location.toString());
|
||||
|
||||
if (
|
||||
["/images", "/images/", "/images/index.html"].includes(
|
||||
window.location.pathname
|
||||
)
|
||||
[
|
||||
"/images",
|
||||
"/images/",
|
||||
"/images/index.html",
|
||||
"/favorites",
|
||||
"/favorites/",
|
||||
"/favorites/index.html",
|
||||
].includes(window.location.pathname)
|
||||
) {
|
||||
setImagesPerPage(ipp);
|
||||
url.searchParams.set("ImagesPP", ipp);
|
||||
|
|
|
@ -44,6 +44,18 @@ export default function ImagePageNav() {
|
|||
>
|
||||
<div className="material-symbols--open-in-new w-8 h-8"></div>
|
||||
</button>
|
||||
<button
|
||||
data-type="image__fav"
|
||||
className="hidden w-8 h-8 cursor-pointer"
|
||||
>
|
||||
<div className="text-[#faebeb] hover:text-orange-500 transition-colors material-symbols--favorite-outline-rounded w-full h-full"></div>
|
||||
</button>
|
||||
<button
|
||||
data-type="image__unfav"
|
||||
className="hidden w-8 h-8 cursor-pointer"
|
||||
>
|
||||
<div className="text-[#faebeb] hover:text-orange-500 transition-colors material-symbols--favorite-rounded w-full h-full"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ export default function PageNav(props: { path: string }) {
|
|||
className="flex justify-center items-center cursor-pointer hover:text-orange-500 transition-colors"
|
||||
id="nav_prev"
|
||||
>
|
||||
<div className="material-symbols--navigate-before w-16 h-16"></div>
|
||||
<div className="material-symbols--navigate-before w-14 h-14"></div>
|
||||
</button>
|
||||
<div className="flex gap-4">
|
||||
{ipp.map((item, idx) => {
|
||||
|
@ -51,7 +51,7 @@ export default function PageNav(props: { path: string }) {
|
|||
className="flex justify-center items-center cursor-pointer hover:text-orange-500 transition-colors"
|
||||
id="nav_next"
|
||||
>
|
||||
<div className="material-symbols--navigate-next w-16 h-16"></div>
|
||||
<div className="material-symbols--navigate-next w-14 h-14"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
export default function Placeholder(props: {
|
||||
isMobileHidden?: boolean;
|
||||
}) {
|
||||
export default function Placeholder(props: { isMobileHidden?: boolean }) {
|
||||
return (
|
||||
<a
|
||||
data-type="placeholder__image"
|
||||
<div
|
||||
data-type="placeholder__image__container"
|
||||
className={`relative aspect-square min-w-48 min-h-48 sm:min-w-auto rounded-sm overflow-hidden ${
|
||||
props.isMobileHidden ? "hidden xl:block" : ""
|
||||
}`}
|
||||
>
|
||||
<a data-type="placeholder__image">
|
||||
<div
|
||||
data-type="placeholder__image__loader"
|
||||
className="w-full h-full absolute inset-0 bg-gray-400 opacity-30 animate-pulse z-[3]"
|
||||
></div>
|
||||
</a>
|
||||
<button
|
||||
data-type="image__fav"
|
||||
className="hidden absolute right-2 top-2 w-8 h-8 cursor-pointer"
|
||||
>
|
||||
<div className="text-[#faebeb] hover:text-orange-500 transition-colors material-symbols--favorite-outline-rounded w-full h-full"></div>
|
||||
</button>
|
||||
<button
|
||||
data-type="image__unfav"
|
||||
className="hidden absolute right-2 top-2 w-8 h-8 cursor-pointer"
|
||||
>
|
||||
<div className="text-[#faebeb] hover:text-orange-500 transition-colors material-symbols--favorite-rounded w-full h-full"></div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
14
src/templates/favorites.tsx
Normal file
14
src/templates/favorites.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import PageNav from "./Components/PageNavigation";
|
||||
|
||||
export default function FavoritesPage() {
|
||||
return (
|
||||
<>
|
||||
<PageNav path="/images"/>
|
||||
<div
|
||||
id="favorites_favorites"
|
||||
className="my-2 overflow-hidden grid grid-cols-[repeat(auto-fill,minmax(250px,1fr))] xl:grid-cols-[repeat(auto-fill,minmax(20%,1fr))] sm:items-center sm:justify-center gap-2"
|
||||
></div>
|
||||
<PageNav path="/images"/>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -23,7 +23,7 @@ export default function IndexPage() {
|
|||
<p className="text-4xl text-white dark:text-white bg-orange-800 dark:bg-orange-800/50 rounded-sm p-4">Videos</p>
|
||||
<div
|
||||
id="index_videos"
|
||||
className="mt-2 flex overflow-x-auto sm:overflow-x-hidden sm:grid sm:grid-cols-[repeat(auto-fill,minmax(25%,1fr))] xl:grid-cols-[repeat(auto-fill,minmax(20%,1fr))] sm:items-center sm:justify-center gap-2"
|
||||
className="mt-2 mb-4 flex overflow-x-auto sm:overflow-x-hidden sm:grid sm:grid-cols-[repeat(auto-fill,minmax(25%,1fr))] xl:grid-cols-[repeat(auto-fill,minmax(20%,1fr))] sm:items-center sm:justify-center gap-2"
|
||||
>
|
||||
{[...Array(3).keys()].map((idx) => {
|
||||
return (
|
||||
|
@ -35,6 +35,13 @@ export default function IndexPage() {
|
|||
})}
|
||||
<AllLink location="/videos/" text="View All Videos" />
|
||||
</div>
|
||||
<p className="text-4xl text-white dark:text-white bg-orange-800 dark:bg-orange-800/50 rounded-sm p-4">Favorites</p>
|
||||
<div
|
||||
id="index_favorites"
|
||||
className="mt-2 flex overflow-x-auto sm:overflow-x-hidden sm:grid sm:grid-cols-[repeat(auto-fill,minmax(25%,1fr))] xl:grid-cols-[repeat(auto-fill,minmax(20%,1fr))] sm:items-center sm:justify-center gap-2"
|
||||
>
|
||||
<p className="text-2xl mx-auto col-span-full">You have not added any Favorites</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue