From 21cbbd8a97ea292f6758801f70948d4bd5e8e4f3 Mon Sep 17 00:00:00 2001 From: Yufan Sheng <syhily@gmail.com> Date: Wed, 4 Dec 2024 01:16:19 +0800 Subject: [PATCH] feat: add lightbox for posts. --- .vscode/settings.json | 2 ++ package-lock.json | 29 ++++++++++++++++++++++----- package.json | 4 +++- plugins/images.ts | 16 +++++++++++++++ src/assets/scripts/yufan.me.js | 35 +++++++++++++++++++++++++++++++++ src/assets/styles/globals.css | 4 ++++ src/content/pages/about.mdx | 4 ++-- src/content/pages/guestbook.mdx | 4 ++-- src/content/pages/links.mdx | 2 +- src/layouts/BaseLayout.astro | 4 +++- 10 files changed, 92 insertions(+), 12 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3c8423d..d21d96a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -108,10 +108,12 @@ "owspace", "pandiyan", "penheulim", + "photoswipe", "pilgi", "plaiceholder", "playform", "psql", + "pswp", "pwsz", "qrcode", "quan", diff --git a/package-lock.json b/package-lock.json index 04c54c9..3815e6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@astrojs/node": "^9.0.0", "@astrojs/rss": "^4.0.9", "astro": "^5.0.1", - "drizzle-orm": "^0.36.4", + "drizzle-orm": "^0.37.0", "fuse.js": "^7.0.0", "lodash": "^4.17.21", "luxon": "^3.5.0", @@ -37,6 +37,8 @@ "aplayer": "^1.10.1", "astro-uploader": "^1.2.2", "bootstrap": "^5.3.3", + "photoswipe": "^5.4.4", + "photoswipe-dynamic-caption-plugin": "^1.2.7", "prettier": "^3.4.1", "prettier-plugin-astro": "^0.14.1", "prettier-plugin-astro-organize-imports": "^0.4.11", @@ -3092,13 +3094,13 @@ "license": "MIT" }, "node_modules/drizzle-orm": { - "version": "0.36.4", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.36.4.tgz", - "integrity": "sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.37.0.tgz", + "integrity": "sha512-AsCNACQ/T2CyZUkrBRUqFT2ibHJ9ZHz3+lzYJFFn3hnj7ylIeItMz5kacRG89uSE74nXYShqehr6u+6ks4JR1A==", "license": "Apache-2.0", "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", - "@cloudflare/workers-types": ">=3", + "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", @@ -6030,6 +6032,23 @@ "split2": "^4.1.0" } }, + "node_modules/photoswipe": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/photoswipe/-/photoswipe-5.4.4.tgz", + "integrity": "sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.12.0" + } + }, + "node_modules/photoswipe-dynamic-caption-plugin": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/photoswipe-dynamic-caption-plugin/-/photoswipe-dynamic-caption-plugin-1.2.7.tgz", + "integrity": "sha512-5XXdXLf2381nwe7KqQvcyStiUBi9TitYXppUQTrzPwYAi4lZsmWNnNKMclM7I4QGlX6fXo42v3bgb6rlK9pY1Q==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", diff --git a/package.json b/package.json index 23e065d..6cf867f 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "@astrojs/node": "^9.0.0", "@astrojs/rss": "^4.0.9", "astro": "^5.0.1", - "drizzle-orm": "^0.36.4", + "drizzle-orm": "^0.37.0", "fuse.js": "^7.0.0", "lodash": "^4.17.21", "luxon": "^3.5.0", @@ -70,6 +70,8 @@ "aplayer": "^1.10.1", "astro-uploader": "^1.2.2", "bootstrap": "^5.3.3", + "photoswipe": "^5.4.4", + "photoswipe-dynamic-caption-plugin": "^1.2.7", "prettier": "^3.4.1", "prettier-plugin-astro": "^0.14.1", "prettier-plugin-astro-organize-imports": "^0.4.11", diff --git a/plugins/images.ts b/plugins/images.ts index eb13743..305426c 100644 --- a/plugins/images.ts +++ b/plugins/images.ts @@ -13,6 +13,11 @@ type ImageNode = Parent & { attributes: (Literal & { name: string })[]; }; +type LinkNode = Node & { + url: string; + children?: ImageNode[]; +}; + export const astroImage = () => { return async (tree: Node) => { // Find all the image node. @@ -23,6 +28,17 @@ export const astroImage = () => { // Process image with blur metadata. await Promise.all(imageNodes); + + // Find all the image link nodes and replace the relative links. + for (const node of selectAll('link', tree)) { + const link = node as LinkNode; + if (link.children !== undefined && link.children.length !== 0) { + const images = link.children.filter((child) => child.type === 'mdxJsxFlowElement' && child.name === 'Image'); + if (images.length > 0) { + link.url = link.url.startsWith('/') ? urlJoin(options.assetsPrefix(), link.url) : link.url; + } + } + } return tree; }; }; diff --git a/src/assets/scripts/yufan.me.js b/src/assets/scripts/yufan.me.js index 135830f..2c233ce 100644 --- a/src/assets/scripts/yufan.me.js +++ b/src/assets/scripts/yufan.me.js @@ -1,7 +1,42 @@ import Aplayer from 'aplayer/dist/APlayer.min.js'; import { actions, isInputError } from 'astro:actions'; +import PhotoSwipe from 'photoswipe'; +import PhotoSwipeDynamicCaption from 'photoswipe-dynamic-caption-plugin'; +import PhotoSwipeLightbox from 'photoswipe/lightbox'; import stickySidebar from './sticky-sidebar.js'; +// Lightbox support for post images. +const imageLinks = Array.from(document.querySelectorAll('.post-content a')).filter((link) => { + const img = link.querySelector('img'); + return typeof img !== 'undefined' && img !== null; +}); + +if (imageLinks.length > 0) { + // Append the required data attributes. + for (const imageLink of imageLinks) { + const image = imageLink.querySelector('img'); + if (image.getAttribute('width') !== null) { + imageLink.dataset.pswpWidth = image.getAttribute('width'); + } + if (image.getAttribute('height') !== null) { + imageLink.dataset.pswpHeight = image.getAttribute('height'); + } + } + + const lightbox = new PhotoSwipeLightbox({ + gallery: imageLinks, + showHideAnimationType: 'zoom', + showAnimationDuration: 300, + hideAnimationDuration: 300, + pswpModule: () => PhotoSwipe, + }); + new PhotoSwipeDynamicCaption(lightbox, { + captionContent: (slide) => slide.data.alt, + }); + + lightbox.init(); +} + // Error Popup. const handleActionError = (error) => { const errorMsg = isInputError(error) diff --git a/src/assets/styles/globals.css b/src/assets/styles/globals.css index 9376f9b..3a3bb3d 100644 --- a/src/assets/styles/globals.css +++ b/src/assets/styles/globals.css @@ -2466,6 +2466,10 @@ a:hover .overlay { margin: 0 0 1rem 1rem; } +.post-content a img { + cursor: zoom-in; +} + @media (max-width: 767.98px) { .post-content h1, .post-content h2, diff --git a/src/content/pages/about.mdx b/src/content/pages/about.mdx index 03156d7..7060428 100644 --- a/src/content/pages/about.mdx +++ b/src/content/pages/about.mdx @@ -8,7 +8,7 @@ cover: /images/2024/11/2024112723215500.jpg published: true --- - +[](/images/2024/11/2024112723242700.jpg) <MusicPlayer netease={22705492} /> @@ -38,4 +38,4 @@ published: true 雨帆就是我,一直很年轻的孩子。 - +[](/images/2024/11/2024112723270800.jpg) diff --git a/src/content/pages/guestbook.mdx b/src/content/pages/guestbook.mdx index c54a5a0..9a719c6 100644 --- a/src/content/pages/guestbook.mdx +++ b/src/content/pages/guestbook.mdx @@ -8,7 +8,7 @@ cover: /images/2024/11/2024112723314900.jpg published: true --- - +[](/images/2024/11/2024112723303500.jpg) <MusicPlayer netease={2166180181} /> @@ -24,4 +24,4 @@ published: true 人生似飞花匆匆,飞花也有过绚丽。只是,当多年后的你再次看到这些留言时,是否能像飞花一样泰然。 - +[](/images/2024/11/2024112723400500.jpg) diff --git a/src/content/pages/links.mdx b/src/content/pages/links.mdx index cc33c1d..1a7c699 100644 --- a/src/content/pages/links.mdx +++ b/src/content/pages/links.mdx @@ -9,7 +9,7 @@ friend: true published: true --- - +[](/images/2024/11/2024112723183300.jpg) <MusicPlayer netease={28306936} /> diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index 751ed5e..5e7a16e 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -2,9 +2,11 @@ // tslint:disable:ordered-imports import 'bootstrap/dist/css/bootstrap.min.css'; import '@/assets/styles/iconfont/iconfont.css'; -import 'aplayer/dist/APlayer.min.css'; import '@/assets/styles/reset.css'; import '@/assets/styles/globals.css'; +import 'aplayer/dist/APlayer.min.css'; +import 'photoswipe/style.css'; +import 'photoswipe-dynamic-caption-plugin/photoswipe-dynamic-caption-plugin.css'; import '@/assets/styles/opposans/opposans.css'; import Footer from '@/components/footer/Footer.astro';