[feat] tag-plugin: albums

This commit is contained in:
xaoxuu 2024-01-03 00:32:19 +08:00
parent 4c10b83e14
commit 8d02f6af66
15 changed files with 159 additions and 177 deletions

View File

@ -333,7 +333,7 @@ plugins:
# 可以处理评论区的图片(不支持 iframe 类评论系统)例如:
# 使用twikoo评论可以写: .tk-content img:not([class*="emo"])
# 使用waline评论可以写: #waline_container .vcontent img
selector: .swiper-slide img, .gallery img # 多个选择器用英文逗号隔开
selector: .fancybox img # 多个选择器用英文逗号隔开
# swiper
swiper:

View File

@ -16,6 +16,7 @@ hexo.extend.tag.register('banner', require('./lib/banner')(hexo), true)
// data
hexo.extend.tag.register('users', require('./lib/friends')(hexo))
hexo.extend.tag.register('friends', require('./lib/friends')(hexo))
hexo.extend.tag.register('albums', require('./lib/albums')(hexo))
hexo.extend.tag.register('sites', require('./lib/sites')(hexo))
hexo.extend.tag.register('ghcard', require('./lib/ghcard')(hexo))
hexo.extend.tag.register('toc', require('./lib/toc')(hexo))

View File

@ -0,0 +1,47 @@
/**
* albums.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
* 格式与官方标签插件一致使用空格分隔中括号内的是可选参数中括号不需要写出来
*
* {% albums [group] [repo:owner/repo] [api:http] [size:s/m/l/xl/mix] %}
*/
'use strict'
module.exports = ctx => function(args) {
var args = ctx.args.map(args, ['repo', 'api', 'size'], ['group'])
if (args.size == null) {
args.size = 's'
}
var api
if (args.api) {
api = args.api
} else if (args.repo) {
api = 'https://api.vlts.cc/output_data/v2/' + args.repo
}
var el = ''
el += `<div class="tag-plugin albums-wrap">`
if (api) {
el += `<div class="stellar-friends-api" api="${api}"><div class="tag-plugin gallery grid-box" layout="grid" ratio="square" ${ctx.args.joinTags(args, ['size']).join(' ')}></div></div>`
} else if (args.group) {
const links = ctx.theme.config.links || {}
el += `<div class="tag-plugin gallery grid-box" layout="grid" ratio="square" ${ctx.args.joinTags(args, ['size']).join(' ')}>`
for (let item of (links[args.group] || [])) {
if (item?.url) {
el += `<div class="grid-cell album-card">`
el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
el += `<img src="${item.icon || item.avatar || ctx.theme.config.default.cover}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
el += `<div class="image-meta">`
if (item.title) {
el += `<span class="image-caption">${item.title}</span>`
}
el += `</div>`
el += `</a>`
el += `</div>`
}
}
el += `</div>`
}
el += `</div>`
return el
}

View File

@ -9,10 +9,6 @@
module.exports = ctx => function(args) {
args = ctx.args.map(args, ['repo', 'api'], ['group'])
var links = ctx.theme.config.links
if (links == undefined) {
links = {}
}
var api
if (args.api) {
api = args.api
@ -22,29 +18,22 @@ module.exports = ctx => function(args) {
var el = '<div class="tag-plugin users-wrap">'
if (api) {
el += '<div class="stellar-friends-api"'
el += ' api="' + api + '"'
el += '>'
el += '<div class="group-body"></div>'
el += '</div>'
el += `<div class="stellar-friends-api" ${ctx.args.joinTags(args, ['size']).join(' ')} api="${api}"><div class="grid-box"></div></div>`
} else if (args.group) {
function cell(item) {
if (item.url && item.title) {
var cell = '<div class="user-card">'
cell += '<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="' + item.url + '">'
cell += '<img src="' + (item.icon || item.avatar || ctx.theme.config.default.avatar) + '" onerror="javascript:this.removeAttribute(&quotdata-src&quot)this.src=&quot' + ctx.theme.config.default.avatar + '&quot"/>'
cell += '<div class="name"><span>' + item.title + '</span></div>'
cell += '</a></div>'
return cell
} else {
return ''
const links = ctx.theme.config.links || {}
el += '<div class="grid-box">'
for (let item of (links[args.group] || [])) {
if (item?.url && item?.title) {
el += `<div class="grid-cell user-card">`
el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
el += `<img src="${item.icon || item.avatar || ctx.theme.config.default.avatar}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.avatar}&quot;;"/>`
el += `<div class="name">`
el += `<span>${item.title}</span>`
el += `</div>`
el += `</a>`
el += `</div>`
}
}
el += '<div class="group-body">'
const items = links[args.group] || []
items.forEach((item, i) => {
el += cell(item)
})
el += '</div>'
}

View File

@ -33,9 +33,6 @@ function img(src, alt) {
module.exports = ctx => function(args, content) {
args = ctx.args.map(args, ['layout', 'size', 'ratio'])
if (args.layout == null) {
args.layout = ctx.theme.config.tag_plugins.gallery.layout
}
if (args.size == null) {
args.size = ctx.theme.config.tag_plugins.gallery.size
}
@ -43,14 +40,18 @@ module.exports = ctx => function(args, content) {
args.ratio = ctx.theme.config.tag_plugins.gallery.ratio
}
var el = ''
el += `<div class="tag-plugin gallery" ${ctx.args.joinTags(args, ['layout', 'size', 'ratio']).join(' ')}>`
var layoutType = 'grid'
if (args.layout == 'flow') {
layoutType = 'flow'
}
el += `<div class="tag-plugin gallery fancybox ${layoutType}-box" ${ctx.args.joinTags(args, ['size', 'ratio']).join(' ')}>`
const img_mds = content.split('\n').filter(item => item.trim().length > 0)
for (let md of img_mds) {
const matches = md.match(/\!\[(.*?)\]\((.*?)\)/i)
if (matches?.length > 2) {
let alt = matches[1]
let src = matches[2]
el += `<div class="cell">${img(src, alt)}</div>`
el += `<div class="${layoutType}-cell">${img(src, alt)}</div>`
}
}
el += `</div>`

View File

@ -9,10 +9,6 @@
module.exports = ctx => function(args) {
args = ctx.args.map(args, ['repo', 'api'], ['group'])
var links = ctx.theme.config.links
if (links == undefined) {
links = {}
}
var api
if (args.api) {
api = args.api
@ -25,30 +21,25 @@ module.exports = ctx => function(args) {
el += '<div class="stellar-sites-api"'
el += ' api="' + api + '"'
el += '>'
el += '<div class="group-body"></div>'
el += '<div class="grid-box"></div>'
el += '</div>'
} else if (args.group) {
function cell(item) {
if (item.url && item.title) {
var cell = '<div class="site-card">'
cell += '<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="' + item.url + '">'
cell += '<img src="' + (item.cover || item.screenshot || ('https://api.vlts.cc/screenshot?url=' + item.url + '&width=1280&height=720')) + '" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;' + ctx.theme.config.default.cover + '&quot;;"/>'
cell += '<div class="info">'
cell += '<img src="' + (item.icon || item.avatar || ctx.theme.config.default.link) + '" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;' + ctx.theme.config.default.link + '&quot;;"/>'
cell += '<span class="title">' + item.title + '</span>'
cell += '<span class="desc">' + (item.description || item.url) + '</span>'
cell += '</div>'
cell += '</a></div>'
return cell
} else {
return ''
const links = ctx.theme.config.links || {}
el += '<div class="grid-box">'
for (let item of (links[args.group] || [])) {
if (item?.url && item?.title) {
el += `<div class="grid-cell site-card">`
el += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`
el += `<img src="${item.cover || item.screenshot || ('https://api.vlts.cc/screenshot?url=' + item.url + '&width=1280&height=720')}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${ctx.theme.config.default.cover}&quot;;"/>`
el += `<div class="info">`
el += `<img src="${item.icon || item.avatar || ctx.theme.config.default.link}" onerror="javascript:this.removeAttribute(&quot;data-src&quot;);this.src=&quot;${item.icon || item.avatar || ctx.theme.config.default.link}&quot;;"/>`
el += `<span class="title">${item.title}</span>`
el += `<span class="desc">${item.description || item.url}</span>`
el += `</div>`
el += `</a>`
el += `</div>`
}
}
el += '<div class="group-body">'
const items = links[args.group] || []
items.forEach((item, i) => {
el += cell(item)
})
el += '</div>'
}

View File

@ -22,7 +22,7 @@ module.exports = ctx => function(args, content) {
})
}
}
el += '<div class="tag-plugin swiper" id="swiper-api"'
el += '<div class="tag-plugin swiper fancybox" id="swiper-api"'
el += ' ' + ctx.args.joinTags(args, ['width', 'effect']).join(' ')
el += '>'
el += '<div class="swiper-wrapper">'

View File

@ -37,12 +37,12 @@
background: none
line-height: 1.5
font-size: $fs-15
&:hover
color: white
background: rgba(black, 0.5)
&.active
color: white
background: rgba(white, 0.25)
&:hover
color: white
background: rgba(black, 0.5) !important
.back
background: none
padding: 0

View File

@ -1,42 +1,9 @@
.users-wrap
overflow: hidden
.group-header
margin: 0 0 1rem
p
margin: 0
font-size: $fs-14
&:first-child
font-size: 1.25rem
font-weight: 500
.group-body
width: 100%
display: flex
flex-wrap: wrap
align-items: stretch
&+.group-header
margin-top: 2rem
.stellar-friends-api
display: block
.users-wrap .grid-box
display: grid
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr))
.users-wrap .user-card
flex-shrink: 1
display: flex
align-items: stretch
width: 12.5%
@media screen and (max-width: 980px)
width: 14.28%
@media screen and (max-width: 900px)
width: 16.66%
@media screen and (max-width: 820px)
width: 20%
@media screen and (max-width: $device-mobile-max)
width: 16.66%
@media screen and (max-width: $device-mobile)
width: 25%
.card-link
margin: 0
width: 100%
color: var(--text-p1)
font-size: 10px
font-weight: 500
@ -50,6 +17,9 @@
overflow: hidden
position: relative
padding: 1rem 0.5rem
.name
max-width: 100%
txt-ellipsis()
img
object-fit: cover
display: block

View File

@ -1,5 +1,5 @@
.tag-plugin.gallery
.cell
.grid-cell,.flow-cell
display: block
overflow hidden
z-index 0
@ -30,66 +30,80 @@
display: block
font-size: $fs-13
color: transparent
pointer-events: none
line-height: 1.2
margin: 0.5rem
trans1(color)
text-align: left
&:empty
display: none
.tag-plugin.gallery[layout='grid'] .cell
trans1(transform, 0.5s)
.tag-plugin.gallery .grid-cell
img
trans1(transform, 0.5s)
&:hover
img
transform: scale(1.1)
//
.tag-plugin.gallery[layout='grid']
.tag-plugin.gallery.grid-box
display: grid
&[size='xs']
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr))
grid-gap: 2px
.grid-cell,img
border-radius: 2px
.image-caption
font-size: $fs-12
&[size='s']
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr))
grid-gap: 2px
.cell,img
.grid-cell,img
border-radius: 2px
&[size='m']
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))
grid-gap: 4px
.cell,img
.grid-cell,img
border-radius: 4px
&[size='l']
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))
grid-gap: 8px
.cell,img
.grid-cell,img
border-radius: 8px
&[size='xl'] // 2
grid-template-columns: repeat(2, 1fr)
grid-gap: 16px
.cell,img
.grid-cell,img
border-radius: 16px
&[size='mix'] // 1+2
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))
grid-gap: 4px
.cell,img
.grid-cell,img
border-radius: 4px
.cell
.grid-cell
&:nth-child(3n+1)
grid-column: auto / span 2
grid-row: auto / span 2
&[ratio='square']
.cell
.grid-cell
aspect-ratio: 1
.cell
.grid-cell
background: var(--block)
img
width: 100%
height: 100%
//
.tag-plugin.gallery[layout='flow']
.tag-plugin.gallery.flow-box
column-count: 3
column-gap: 8px
.cell
.flow-cell
border-radius: 8px
padding-bottom: 8px
img
width: 100%
height: 100%
.image-meta
border-radius: 8px
margin-bottom: 8px

View File

@ -1,46 +1,19 @@
.sites-wrap
.group-header
margin: 1rem 0
p
margin: 0
font-size: $fs-14
&:first-child
font-size: 1.25rem
font-weight: 500
.group-body
width: 100%
&+.group-header
margin-top: 2rem
.stellar-sites-api
display: block
.sites-wrap .group-body
.sites-wrap .grid-box
display: grid
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr))
grid-gap: 1rem 1rem
grid-template-columns: repeat(auto-fill, "calc((100% - 3 * %s) / 4)" % 1rem)
@media screen and (max-width: $device-laptop)
grid-template-columns: repeat(auto-fill, "calc((100% - 2 * %s) / 3)" % 1rem)
@media screen and (max-width: 900px)
grid-template-columns: repeat(auto-fill, "calc((100% - 1 * %s) / 2)" % 1rem)
@media screen and (max-width: $device-mobile-max)
grid-template-columns: repeat(auto-fill, "calc((100% - 2 * %s) / 3)" % 1rem)
@media screen and (max-width: $device-mobile)
grid-template-columns: repeat(auto-fill, "calc((100% - 1 * %s) / 2)" % 1rem)
.grid-cell
aspect-ratio: 1.5
.sites-wrap .group-body .site-card .card-link
.sites-wrap .site-card .card-link
width: 100%
display: flex
flex-direction: column
>img
width: 100%
height 100px
@media screen and (max-width: $device-laptop)
height: 120px
@media screen and (max-width: 900px)
height: 150px
@media screen and (max-width: $device-tablet)
height: 120px
object-fit: cover
aspect-ratio: 1.5
width: 100%
height: 100%
box-shadow: 0 1px 2px 0px rgba(0, 0, 0, 0.2)
.info
margin-top: 0.5rem
@ -71,7 +44,7 @@
display: -webkit-box
-webkit-box-orient: vertical
overflow: hidden
-webkit-line-clamp: 2
-webkit-line-clamp: 1
// transform
.sites-wrap .site-card .card-link

View File

@ -1,9 +1,6 @@
img[fancybox='true']
cursor: zoom-in
.swiper-slide
cursor: zoom-in
.tag-plugin.gallery
.fancybox
img
cursor: zoom-in

View File

@ -14,9 +14,9 @@ if hexo-config('plugins.lazyload.transition') == 'blur'
&.loaded,&.error
filter none
-webkit-filter none
.group-body .site-card .card-link>img
.site-card .card-link>img
trans-site filter
.group-body .user-card .card-link>img
.user-card .card-link>img
trans-user filter
else
img.lazy
@ -25,7 +25,7 @@ else
opacity: 0
&.loaded,&.error
opacity: 1
.group-body .site-card .card-link>img
.site-card .card-link>img
trans-site opacity
.group-body .user-card .card-link>img
.user-card .card-link>img
trans-user opacity

View File

@ -47,17 +47,17 @@ const friendsjs = {
$(el).append('<div class="loading-wrap"><svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg></div>');
friendsjs.requestAPI(cfg.api, function(data) {
$(el).find('.loading-wrap').remove();
const arr = data.content || data;
arr.forEach((item, i) => {
var user = '<div class="user-card">';
user += '<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer"';
user += ' href="' + (item.html_url || item.url) + '">';
user += '<img src="' + (item.avatar_url || item.avatar || item.icon || cfg.avatar) + '" onerror="javascript:this.src=\'' + cfg.avatar + '\';">';
user += '<div class="name"><span>' + (item.title || item.login) + '</span></div>';
user += '</a>';
user += '</div>';
$(el).find('.group-body').append(user);
});
for (let item of (data.content || data)) {
var cell = `<div class="grid-cell user-card">`;
cell += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.html_url || item.url}">`;;
cell += `<img src="${item.avatar_url || item.avatar || item.icon || cfg.avatar}" onerror="javascript:this.removeAttribute(\'data-src\');this.src=\'${cfg.avatar}\';"/>`;
cell += `<div class="name image-meta">`;
cell += `<span class="image-caption">${item.title || item.login}</span>`;
cell += `</div>`;
cell += `</a>`;
cell += `</div>`;
$(el).find('.grid-box').append(cell);
}
}, function() {
$(el).find('.loading-wrap svg').remove();
$(el).find('.loading-wrap').append('<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" d="M12 3L21 20H3L12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.5s" values="60;0"/></path><path stroke-dasharray="6" stroke-dashoffset="6" d="M12 10V14"><animate fill="freeze" attributeName="stroke-dashoffset" begin="0.6s" dur="0.2s" values="6;0"/></path></g><circle cx="12" cy="17" r="1" fill="currentColor" fill-opacity="0"><animate fill="freeze" attributeName="fill-opacity" begin="0.8s" dur="0.4s" values="0;1"/></circle></svg>');

View File

@ -47,20 +47,19 @@ const sitesjs = {
$(el).append('<div class="loading-wrap"><svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg></div>');
sitesjs.requestAPI(cfg.api, function(data) {
$(el).find('.loading-wrap').remove();
const arr = data.content;
arr.forEach((item, i) => {
var cell = '<div class="site-card">';
cell += '<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="' + item.url + '">';
cell += '<img src="' + (item.cover || item.screenshot || ('https://api.vlts.cc/screenshot?url=' + item.url + '&width=1280&height=720')) + '" onerror="javascript:this.src=\'' + cfg.screenshot + '\';"/>';
cell += '<div class="info">';
cell += '<img src="' + (item.icon || item.avatar || cfg.avatar) + '" onerror="javascript:this.src=\'' + cfg.avatar + '\';"/>';
cell += '<span class="title">' + item.title + '</span>';
cell += '<span class="desc">' + (item.description || item.url) + '</span>';
cell += '</div>';
cell += '</a>';
cell += '</div>';
$(el).find('.group-body').append(cell);
});
for (let item of data.content) {
var cell = `<div class="grid-cell site-card">`;
cell += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.url}">`;
cell += `<img src="${item.cover || item.screenshot}" onerror="javascript:this.removeAttribute(\'data-src\');this.src=\'${cfg.screenshot}\';"/>`;
cell += `<div class="info">`;
cell += `<img src="${item.icon || item.avatar || cfg.avatar}" onerror="javascript:this.removeAttribute(\'data-src\');this.src=\'${cfg.avatar}\';"/>`;
cell += `<span class="title">${item.title}</span>`;
cell += `<span class="desc">${item.description || item.url}</span>`;
cell += `</div>`;
cell += `</a>`;
cell += `</div>`;
$(el).find('.grid-box').append(cell);
}
}, function() {
$(el).find('.loading-wrap svg').remove();
$(el).find('.loading-wrap').append('<svg xmlns="http://www.w3.org/2000/svg" width="2em" height="2em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" d="M12 3L21 20H3L12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.5s" values="60;0"/></path><path stroke-dasharray="6" stroke-dashoffset="6" d="M12 10V14"><animate fill="freeze" attributeName="stroke-dashoffset" begin="0.6s" dur="0.2s" values="6;0"/></path></g><circle cx="12" cy="17" r="1" fill="currentColor" fill-opacity="0"><animate fill="freeze" attributeName="fill-opacity" begin="0.8s" dur="0.4s" values="0;1"/></circle></svg>');