添加Masonry组件

master
落雨楓 2 months ago
parent be5ad56fe4
commit 6e6184f2b8

@ -223,6 +223,19 @@
"isekai-offcanvastoc-menubutton", "isekai-offcanvastoc-menubutton",
"isekai-offcanvastoc-description-item" "isekai-offcanvastoc-description-item"
] ]
},
"ext.isekai.masonry": {
"scripts": [
"masonry/ext.isekai.masonry.js",
"masonry/ext.isekai.masonry.base.js"
],
"styles": [
"masonry/ext.isekai.masonry.less"
],
"targets": [
"desktop",
"mobile"
]
} }
}, },
"ResourceFileModulePaths": { "ResourceFileModulePaths": {
@ -236,7 +249,9 @@
"exfont": "text/mediawiki", "exfont": "text/mediawiki",
"details": "text/mediawiki", "details": "text/mediawiki",
"summary": "text/mediawiki", "summary": "text/mediawiki",
"veval": "text/mediawiki" "veval": "text/mediawiki",
"masonry": "text/mediawiki",
"masonryitem": "text/mediawiki"
} }
} }
}, },

@ -8,7 +8,7 @@ class ExtraFontWidget {
public static function create($text, $params, \Parser $parser, \PPFrame $frame) { public static function create($text, $params, \Parser $parser, \PPFrame $frame) {
$existsFonts = $parser->extIsekaiWidgetsCache->get('extraFonts', INF, []); $existsFonts = $parser->extIsekaiWidgetsCache->get('extraFonts', INF, []);
$content = $text = $parser->recursiveTagParse($text, $frame); $content = $text = $parser->recursiveTagParseFully($text, $frame);
if (empty($params['name'])) { if (empty($params['name'])) {
return '<span class="error">' . wfMessage('isekai-font-error-invalid-params')->parse() . '</span>' . $content; return '<span class="error">' . wfMessage('isekai-font-error-invalid-params')->parse() . '</span>' . $content;
} }

@ -0,0 +1,69 @@
<?php
namespace Isekai\Widgets;
use Html;
class MasonryItemWidget {
/**
* @param string $text
* @param array $params
* @param \Parser $parser
* @param \PPFrame $frame
* @return string|string[]
*/
public static function create($text, $params, \Parser $parser, \PPFrame $frame) {
// 获取父级瀑布流参数
$masonryParams = MasonryWidget::getMasonryParams();
$content = $text = $parser->recursiveTagParse($text, $frame);
if (!$masonryParams) {
return '<span class="error">' . wfMessage('isekai-masonry-item-must-in-masonry')->parse() . '</span>' . $content;
}
$maxCol = $masonryParams['cols'];
$className = ['isekai-masonry-item'];
if (isset($params['col'])) {
$value = $params['col'];
$col = $value === 'full' ? $maxCol : min($maxCol, intval($value));
$className[] = 'col-' . $col;
} else {
$className[] = 'col-1';
}
if (isset($params['xs-col'])) {
$value = $params['xs-col'];
$col = $value === 'full' ? $maxCol : min($maxCol, intval($value));
$className[] = 'col-xs-' . $col;
}
if (isset($params['sm-col'])) {
$value = $params['sm-col'];
$col = $value === 'full' ? $maxCol : min($maxCol, intval($value));
$className[] = 'col-sm-' . $col;
}
if (isset($params['md-col'])) {
$value = $params['md-col'];
$col = $value === 'full' ? $maxCol : min($maxCol, intval($value));
$className[] = 'col-md-' . $col;
}
if (isset($params['lg-col'])) {
$value = $params['lg-col'];
$col = $value === 'full' ? $maxCol : min($maxCol, intval($value));
$className[] = 'col-lg-' . $col;
}
if (isset($params['xl-col'])) {
$value = $params['xl-col'];
$col = $value === 'full' ? $maxCol : min($maxCol, intval($value));
$className[] = 'col-xl-' . $col;
}
return Html::openElement('div', [
'class' => implode(' ', $className),
]) . $content . Html::closeElement('div');
}
}

@ -0,0 +1,100 @@
<?php
namespace Isekai\Widgets;
use Html;
class MasonryWidget {
public static $paramsStack = [];
/**
* 瀑布流子项可以通过此方法获取父级瀑布流参数
*/
public static function getMasonryParams() {
if (empty(self::$paramsStack)) {
return null;
}
return self::$paramsStack[count(self::$paramsStack) - 1];
}
public static function getStyle($params) {
$gutter = max(0, $params['gutter']);
$maxCol = max(1, $params['cols']);
$colWidth = round(100 / $maxCol, 6);
$css = <<<CSS
.isekai-masonry.max-col-{$maxCol} { --masonry-col-width: {$colWidth}%; --masonry-gutter: {$gutter}px; }
CSS;
$breakpoints = [
'xs' => 576,
'sm' => 768,
'md' => 992,
'lg' => 1200,
'xl' => 1400,
];
foreach ($breakpoints as $breakpoint => $width) {
$paramKey = "{$breakpoint}-cols";
if (!isset($params[$paramKey])) {
continue;
}
$maxCol = max(1, $params[$paramKey]);
$colWidth = round(100 / $maxCol, 6);
$css .= <<<CSS
@media (min-width: {$width}px) { .isekai-masonry.max-col-{$breakpoint}-${maxCol} { --masonry-col-width: {$colWidth}%; } }
CSS;
}
return Html::rawElement('style', [], $css);
}
/**
* @param string $text
* @param array $params
* @param \Parser $parser
* @param \PPFrame $frame
* @return string|string[]
*/
public static function create($text, $params, \Parser $parser, \PPFrame $frame) {
$parser->getOutput()->addModules(['ext.isekai.masonry']);
$params['cols'] = intval($params['cols'] ?? 2);
$params['xs-cols'] = intval($params['xs-cols'] ?? 2);
$params['sm-cols'] = intval($params['sm-cols'] ?? 2);
$params['md-cols'] = intval($params['md-cols'] ?? 2);
$params['lg-cols'] = intval($params['lg-cols'] ?? 2);
$params['xl-cols'] = intval($params['xl-cols'] ?? 3);
$params['gutter'] = intval($params['gutter'] ?? 10);
$className = ['isekai-masonry', "max-col-{$params['cols']}"];
self::$paramsStack[] = $params;
$content = $parser->recursiveTagParseFully($text, $frame);
array_pop(self::$paramsStack);
return [
self::getStyle($params) .
Html::openElement('div', [
'class' => implode(' ', $className),
'data-cols' => $params['cols'],
'data-xs-cols' => $params['xs-cols'],
'data-sm-cols' => $params['sm-cols'],
'data-md-cols' => $params['md-cols'],
'data-lg-cols' => $params['lg-cols'],
'data-xl-cols' => $params['xl-cols'],
'data-gutter' => $params['gutter'],
]) .
Html::element('div', [
'class' => 'isekai-masonry-sizer',
]) .
Html::element('div', [
'class' => 'isekai-masonry-gutter-sizer',
]) .
$content .
Html::closeElement('div'),
"markerType" => 'nowiki'
];
}
}

@ -34,6 +34,9 @@ class Widgets {
$parser->setHook('veval', [VEvalWidget::class, 'create']); $parser->setHook('veval', [VEvalWidget::class, 'create']);
$parser->setHook('masonry', [MasonryWidget::class, 'create']);
$parser->setHook('masonryitem', [MasonryItemWidget::class, 'create']);
return true; return true;
} }

@ -0,0 +1,9 @@
$(function () {
console.log('IsekaiWidgets: Masonry extension loaded');
new isekai.lib.Masonry('.isekai-masonry', {
itemSelector: '.isekai-masonry-item',
columnWidth: '.isekai-masonry-sizer',
gutter: '.isekai-masonry-gutter-sizer',
percentPosition: true
});
});

File diff suppressed because one or more lines are too long

@ -0,0 +1,19 @@
/*!
* Masonry v4.2.2
* Cascading grid layout library
* https://masonry.desandro.com
* MIT License
* by David DeSandro
*/
/*!
* Outlayer v2.1.1
* the brains and guts of a layout library
* MIT license
*/
/*!
* getSize v2.0.3
* measure size of elements
* MIT license
*/

@ -0,0 +1,76 @@
.isekai-masonry {
display: block;
width: 100%;
--masonry-col-width: 50%;
.isekai-masonry-gutter-sizer {
width: 0;
width: ~"var(--masonry-gutter)";
}
.isekai-masonry-sizer {
width: 100%;
width: ~"calc(var(--masonry-col-width) - var(--masonry-gutter) - 1px)";
}
// Column width tools
.col-1 {
width: 100%; // Fallback
width: ~"calc(var(--masonry-col-width) - var(--masonry-gutter) - 1px)";
}
.col-2 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 2 - var(--masonry-gutter) - 1px))";
}
.col-3 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 3 - var(--masonry-gutter) - 1px))";
}
.col-4 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 4 - var(--masonry-gutter) - 1px))";
}
.col-5 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 5 - var(--masonry-gutter) - 1px))";
}
.col-6 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 6 - var(--masonry-gutter) - 1px))";
}
.col-7 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 7 - var(--masonry-gutter) - 1px))";
}
.col-8 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 8 - var(--masonry-gutter) - 1px))";
}
.col-9 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 9 - var(--masonry-gutter) - 1px))";
}
.col-10 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 10 - var(--masonry-gutter) - 1px))";
}
.col-11 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 11 - var(--masonry-gutter) - 1px))";
}
.col-12 {
width: 100%;
width: ~"min(100%, calc(var(--masonry-col-width) * 12 - var(--masonry-gutter) - 1px))";
}
}

@ -1186,7 +1186,6 @@
width: 100%; width: 100%;
float: left; float: left;
overflow: visible; overflow: visible;
justify-content: center;
} }
.tiles-group::before { .tiles-group::before {
content: attr(data-group-title); content: attr(data-group-title);

@ -0,0 +1,4 @@
import Masonry from "masonry-layout";
import { registerModule } from "../moduleRegister";
registerModule('lib.Masonry', Masonry);

@ -10,7 +10,8 @@ const config = {
'createPage': './src/createPage/ext.isekai.createPage.js', 'createPage': './src/createPage/ext.isekai.createPage.js',
'discover': './src/discover/ext.isekai.discover.js', 'discover': './src/discover/ext.isekai.discover.js',
'tile': './src/tile/ext.isekai.tile.js', 'tile': './src/tile/ext.isekai.tile.js',
'baseWidgets': './src/baseWidgets/ext.isekai.baseWidgets.js' 'baseWidgets': './src/baseWidgets/ext.isekai.baseWidgets.js',
'masonry': './src/masonry/ext.isekai.masonry.js',
}, },
output: { output: {
filename: '[name]/ext.isekai.[name].js', filename: '[name]/ext.isekai.[name].js',

Loading…
Cancel
Save