增加全局组件,更改Tile样式

master
落雨楓 2 years ago
parent 1bab22e474
commit 6b8d9e88cf

@ -160,6 +160,46 @@
"desktop", "desktop",
"mobile" "mobile"
] ]
},
"ext.isekai.baseWidgets": {
"scripts": [
"baseWidgets/ext.isekai.baseWidgets.js"
],
"styles": [
"baseWidgets/ext.isekai.baseWidgets.less"
],
"dependencies": [
"oojs-ui-core",
"oojs-ui-toolbars"
],
"targets": [
"desktop",
"mobile"
],
"messages": [
"isekai-fab-hide-fab-button",
"isekai-fab-hide-fab-button-success"
]
},
"ext.isekai.offcanvasTOC": {
"scripts": [
"offcanvasTOC/ext.isekai.offcanvasTOC.js"
],
"styles": [
"offcanvasTOC/ext.isekai.offcanvasTOC.less"
],
"dependencies": [
"ext.isekai.baseWidgets",
"oojs-ui.styles.icons-layout"
],
"targets": [
"desktop",
"mobile"
],
"messages": [
"isekai-offcanvastoc-menubutton",
"isekai-offcanvastoc-description-item"
]
} }
}, },
"ResourceFileModulePaths": { "ResourceFileModulePaths": {
@ -180,6 +220,9 @@
"config": { "config": {
"IsekaiWidgetInformationTextSeparator": { "IsekaiWidgetInformationTextSeparator": {
"value": ": " "value": ": "
},
"IsekaiGlobalWidgets": {
"value": ["baseWidgets", "offcanvasTOC"]
} }
}, },
"manifest_version": 2 "manifest_version": 2

@ -28,5 +28,11 @@
"isekai-font-error-font-not-imported": "未导入字体: \"$1\"。", "isekai-font-error-font-not-imported": "未导入字体: \"$1\"。",
"isekai-information-title-base-information": "基本资料", "isekai-information-title-base-information": "基本资料",
"isekai-information-error-invalid-type": "提供的信息框类型错误" "isekai-information-error-invalid-type": "提供的信息框类型错误",
"isekai-fab-hide-fab-button": "隐藏按钮",
"isekai-fab-hide-fab-button-success": "浮动按钮已隐藏,刷新页面可重新显示。",
"isekai-offcanvastoc-menubutton": "目录",
"isekai-offcanvastoc-description-item": "简介"
} }

@ -2,6 +2,7 @@
namespace Isekai\Widgets; namespace Isekai\Widgets;
use MapCacheLRU; use MapCacheLRU;
use MediaWiki\MediaWikiServices;
use Parser; use Parser;
class Widgets { class Widgets {
@ -41,5 +42,20 @@ class Widgets {
"ext.isekai.information.infobox", "ext.isekai.information.infobox",
"ext.isekai.collapse" "ext.isekai.collapse"
]); ]);
$globalModuleMap = [
'baseWidgets' => 'ext.isekai.baseWidgets',
'offcanvasTOC' => 'ext.isekai.offcanvasTOC',
];
$config = MediaWikiServices::getInstance()->getMainConfig();
$modules = $config->get('IsekaiGlobalWidgets');
$outputModules = [];
foreach ($modules as $module) {
if (isset($globalModuleMap[$module])) {
$outputModules[] = $globalModuleMap[$module];
}
}
$outputPage->addModules($outputModules);
} }
} }

File diff suppressed because one or more lines are too long

@ -0,0 +1,217 @@
// =============================================================================
// Fab button
// =============================================================================
.isekai-fab-group {
display: flex;
flex-direction: column;
gap: 10px;
position: fixed;
z-index: 50;
right: 18px;
bottom: 8em;
bottom: 15vh;
@media screen and (min-width: 1340px) {
right: 44px;
}
}
.isekai-fab-hidden {
display: none;
}
.isekai-fab-btn {
display: flex;
width: 48px;
height: 48px;
padding: 0;
border: 1px #eee solid;
outline: none;
align-items: center;
justify-content: center;
text-align: center;
border-radius: 50%;
text-shadow: none;
background-color: rgba(255, 255, 255, 0.95);
color: #333;
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.12);
user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
&:hover {
background-color: rgba(255, 255, 255, 0.9);
color: #000;
}
&:active,
&:focus {
outline: none;
}
@media screen and (max-width: 850px) {
display: none;
}
@media print {
display: none;
}
}
// =============================================================================
// Context menu
// =============================================================================
.isekai-contextmenu-cover {
position: fixed;
z-index: 100;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: transparent;
cursor: default;
}
#iseai-contextmenu {
position: fixed;
z-index: 105;
}
// =============================================================================
// Bottom Navbar
// =============================================================================
@isekai-bottom-nav-button-size: 40px;
.isekai-bottom-nav-container {
position: fixed;
z-index: 100;
bottom: -2px; // Fix white line at the bottom
left: 0;
width: 100%;
height: (@isekai-bottom-nav-button-size + 2px);
padding-bottom: 2px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.12);
background-color: #fff;
box-sizing: border-box;
user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
// Safe area
padding-left: var(--safe-area-inset-left);
padding-right: var(--safe-area-inset-right);
padding-bottom: ~"calc(var(--safe-area-inset-bottom, 0px) + 2px)";
@media print {
display: none;
}
@media screen and (min-width: 851px) {
display: none;
}
}
.isekai-bottom-nav {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
@supports(backdrop-filter: blur(10px)) {
.isekai-bottom-nav-container {
background-color: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(20px) saturate(120%);
}
}
@supports(-webkit-backdrop-filter: blur(10px)) {
.isekai-bottom-nav {
background-color: rgba(255, 255, 255, 0.8);
-webkit-backdrop-filter: blur(20px) saturate(120%);
}
}
.isekai-bottom-nav-placeholder {
display: flex;
width: 100%;
height: @isekai-bottom-nav-button-size;
}
.isekai-bottom-nav-btn {
display: block;
flex-grow: 0;
flex-shrink: 0;
flex-basis: (@isekai-bottom-nav-button-size + 10px);
width: (@isekai-bottom-nav-button-size + 10px);
height: @isekai-bottom-nav-button-size;
line-height: @isekai-bottom-nav-button-size;
text-align: left;
font-size: 18px;
color: #333;
&:hover {
background-color: rgba(0, 0, 0, 0.05);
color: #333;
text-decoration: none;
}
&:active {
background-color: rgba(0, 0, 0, 0.1);
color: #333;
text-decoration: none;
}
}
.isekai-bottom-nav-btn-expand {
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;
width: 100%;
display: flex;
}
.isekai-bottom-nav-btn-icon {
display: flex;
flex-grow: 0;
flex-shrink: 0;
flex-basis: (@isekai-bottom-nav-button-size + 10px);
width: (@isekai-bottom-nav-button-size + 10px);
height: @isekai-bottom-nav-button-size;
line-height: @isekai-bottom-nav-button-size;
text-align: center;
align-items: center;
justify-content: center;
}
.isekai-bottom-nav-btn-label {
display: inline-block;
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;
line-height: @isekai-bottom-nav-button-size;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
padding-right: 10px;
}
.ve-activated {
.isekai-fab-group {
display: none;
}
.isekai-bottom-nav {
display: none;
}
}
.use-isekai-bottom-nav {
&.skin-minerva {
#mw-mf-page-center {
padding-bottom: 40px;
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -98,6 +98,102 @@
} }
} }
.isekai-list {
margin: 0 !important;
padding: 0 0 0.5rem 0 !important;
list-style: none;
background-color: transparent;
.isekai-list-item {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
gap: 0.25rem;
-webkit-box-sizing: border-box;
box-sizing: border-box;
min-height: 3rem;
padding: 0 1rem;
text-decoration: none;
cursor: pointer;
border-bottom: 1px solid rgba(0,0,0,.12);
&:hover {
background-color: rgba(0,0,0,.08);
}
&:last-of-type {
border-bottom: none;
}
&.active {
box-shadow: inset 4px 0 0 0 #3366cc;
}
}
a {
color: #000;
text-decoration: none;
&:hover {
color: #000;
text-decoration: none;
}
&:visited {
color: #000;
text-decoration: none;
&:hover {
color: #000;
text-decoration: none;
}
}
}
.isekai-list-item-title {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 1.5rem;
.tag {
opacity: 0.6;
font-size: 0.8rem;
padding: 2px 8px;
background-color: rgba(0, 0, 0, 0.1);
}
}
.isekai-list-item-content {
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
padding-top: 0.625rem;
padding-bottom: 0.625rem;
font-weight: 400;
font-size: 1rem;
line-height: 1.25rem;
}
.isekai-list-item-title~.isekai-list-item-text {
margin-top: 0.25rem;
}
.isekai-list-item-text {
font-size: 0.875rem;
opacity: 0.54;
-webkit-line-clamp: 1;
height: 1.25rem;
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
}
}
.isekai-well { .isekai-well {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06), 0 -1px 4px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(53,72,91,.07); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06), 0 -1px 4px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(53,72,91,.07);
border-radius: 5px; border-radius: 5px;
@ -107,6 +203,28 @@
} }
.skin-citizen-dark, .skin-timeless-dark { .skin-citizen-dark, .skin-timeless-dark {
.isekai-list {
a {
color: white;
text-decoration: none;
&:hover {
color: white;
text-decoration: none;
}
&:visited {
color: white;
text-decoration: none;
&:hover {
color: white;
text-decoration: none;
}
}
}
}
.isekai-well { .isekai-well {
background-color: #090909; background-color: #090909;
} }

@ -176,7 +176,12 @@ module.exports = {
}); });
} }
onLoaded(); onLoaded();
}).catch(console.error); }).catch((err) => {
console.error(err);
//
recentData.recentThread = [];
onLoaded();
});
} else { } else {
recentData.recentThread = []; recentData.recentThread = [];
} }

@ -32,120 +32,4 @@
@media (max-width: 850px) { @media (max-width: 850px) {
height: @feed-list-height-mobile; height: @feed-list-height-mobile;
} }
}
.isekai-list {
margin: 0 !important;
padding: 0 0 0.5rem 0 !important;
list-style: none;
background-color: transparent;
.isekai-list-item {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
gap: 0.25rem;
-webkit-box-sizing: border-box;
box-sizing: border-box;
min-height: 3rem;
padding: 0 1rem;
text-decoration: none;
cursor: pointer;
border-bottom: 1px solid rgba(0,0,0,.12);
&:hover {
background-color: rgba(0,0,0,.08);
}
&:last-of-type {
border-bottom: none;
}
}
a {
color: #000;
text-decoration: none;
&:hover {
color: #000;
text-decoration: none;
}
&:visited {
color: #000;
text-decoration: none;
&:hover {
color: #000;
text-decoration: none;
}
}
}
.isekai-list-item-title {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 1.5rem;
.tag {
opacity: 0.6;
font-size: 0.8rem;
padding: 2px 8px;
background-color: rgba(0, 0, 0, 0.1);
}
}
.isekai-list-item-content {
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
padding-top: 0.625rem;
padding-bottom: 0.625rem;
font-weight: 400;
font-size: 1rem;
line-height: 1.25rem;
}
.isekai-list-item-title~.isekai-list-item-text {
margin-top: 0.25rem;
}
.isekai-list-item-text {
font-size: 0.875rem;
opacity: 0.54;
-webkit-line-clamp: 1;
height: 1.25rem;
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
}
}
.skin-citizen-dark, .skin-timeless-dark {
.isekai-list {
a {
color: white;
text-decoration: none;
&:hover {
color: white;
text-decoration: none;
}
&:visited {
color: white;
text-decoration: none;
&:hover {
color: white;
text-decoration: none;
}
}
}
}
} }

@ -0,0 +1,329 @@
var headingList = [];
var menuList = [];
var bottomNavBtn;
var bottomNavBtnLabel;
var scrollBehaviorAvaliable = (function() {
// 检测Chrome
var v = navigator.userAgent.match(/Chrome\/(?<version>\S+)/);
if (v && v.groups.version) {
var chromeVersion = parseInt(v.groups.version);
return chromeVersion >= 61;
}
// 检测Firefox
v = navigator.userAgent.match(/Firefox\/(?<version>\S+)/);
if (v && v.groups.version) {
var firefoxVersion = parseInt(v.groups.version);
return firefoxVersion >= 36;
}
// 检测Safari
v = navigator.userAgent.match(/Version\/(?<version>\S+)/);
if (v && v.groups.version) { // Safari
var safariVersion = parseFloat(v.groups.version);
return safariVersion >= 14;
}
return false;
})();
function getScrollOffset() {
if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) {
return 60;
} else if (mw.config.get('skin') === 'minerva') {
return 60;
} else {
return 10;
}
}
function getAnchorOffset() {
if (mw.config.get('skin') === 'timeless' && window.innerWidth > 850) {
return 70;
} else if (mw.config.get('skin') === 'minerva') {
return 70;
} else {
return 20;
}
}
function scrollToAnchor(link) {
var el = document.getElementById(link.replace(/^#/, ''));
if (el) {
var target = $(el);
function doScroll() {
var position = target.offset().top - getScrollOffset();
if (scrollBehaviorAvaliable) {
window.scrollTo({
top: position,
behavior: 'smooth'
});
} else {
$('html, body').animate({
scrollTop: position,
}, 500);
}
}
if (mw.config.get('skin') === 'minerva') { // 手机端主题,需要检测折叠状态
var collapseBlock = target.parents('.collapsible-block');
if (collapseBlock.length > 0 && !collapseBlock.hasClass('open-block')) {
var h1Elem = collapseBlock.prev('.collapsible-heading');
if (h1Elem.length > 0) {
// 展开目录
h1Elem.click();
var tid = setInterval(function() {
// 检测是否已经展开
if (collapseBlock.hasClass('open-block')) {
doScroll();
clearInterval(tid);
}
}, 100);
return false;
}
}
doScroll();
} else {
doScroll();
}
return false;
} else {
return true;
}
}
function getScrollbarWidth() {
if (window.innerWidth && document.body.clientWidth) {
return window.innerWidth - document.body.clientWidth;
} else {
return 0;
}
}
var lastActiveId = null;
function updateActive() {
var scrollTop = $(window).scrollTop() + getAnchorOffset();
if (headingList.length > 0) {
var activedId;
for (var i = 0; i < headingList.length; i ++) {
var headItem = headingList[i];
var headPos = headItem.offset().top;
if (i === 0 && scrollTop < headPos) { // 比第一个head位置靠上则是简介
activedId = 'bodyContent';
break;
} else if (scrollTop < headPos) { // 如果当前滚动条高度低于目前head则是上一个
activedId = headingList[i - 1].attr('id');
break;
}
}
if (!activedId) {
activedId = headingList[headingList.length - 1].attr('id');
}
if (activedId !== lastActiveId) {
$('#isekai-offcanvas-toc ul .list-item').removeClass('active');
$('#isekai-offcanvas-toc ul .list-item[data-id="' + activedId + '"]').addClass('active');
var menuItem = menuList.find((item) => {
return item.id === activedId;
});
if (menuItem) {
if (menuItem.number) {
bottomNavBtnLabel.text(menuItem.number + '. ' + menuItem.text);
} else {
bottomNavBtnLabel.text(menuItem.text);
}
}
lastActiveId = activedId;
}
}
}
function openOffcanvas() {
$('#iseai-offcanvas-btn').show();
menuHidden = false;
let scrollbarWidth = getScrollbarWidth();
$('#isekai-offcanvas-cover').removeClass('hidden');
window.requestAnimationFrame(function() {
$('body').addClass(['toc-offcanvas-show', 'toc-offcanvas-open'])
.css('margin-right', scrollbarWidth);
$('#isekai-fab-container').css('margin-right', scrollbarWidth);
if (mw.config.get('skin') === 'timeless') {
$('#mw-header-container').css('padding-right', scrollbarWidth);
}
// 滚动到当前项目
let activedItem = $('#isekai-offcanvas-toc ul .list-item.active');
if (activedItem.length > 0) {
let targetY = Math.max(activedItem.eq(0).prop('offsetTop') - 50, 0);
$('#isekai-offcanvas-toc').scrollTop(targetY);
}
});
}
function closeOffcanvas() {
if ($('#iseai-offcanvas-contextmenu').length > 0) {
$('#iseai-offcanvas-contextmenu').remove();
$('#isekai-offcanvas-cover').addClass('hidden');
} else {
$('body').removeClass('toc-offcanvas-open');
setTimeout(function() {
$('body').removeClass('toc-offcanvas-show').css('margin-right', 0);
$('#isekai-offcanvas-cover').addClass('hidden');
$('#isekai-fab-container').css('margin-right', 0);
if (mw.config.get('skin') === 'timeless') {
$('#mw-header-container').css('padding-right', 0);
}
}, 260);
}
}
function throttle(fn, delay) {
var timer = null;
return function() {
if (!timer) {
timer = setTimeout(function() {
fn();
timer = null;
}, delay);
}
};
}
$(function() {
if (mw.config.get('wgIsArticle')) {
// 创建目录dom
$('body').append(`
<div id="isekai-offcanvas-toc" class="toc-offcanvas">
<ul></ul>
</div>
<div id="isekai-offcanvas-cover" class="toc-offcanvas-cover hidden"></div>
`);
// 创建按钮
var menuIcon = new OO.ui.IconWidget({ icon: 'menu' });
isekai.fab.addButton({
id: 'offcanvas-toc',
label: mw.msg('isekai-offcanvastoc-menubutton'),
icon: menuIcon.$element[0],
priority: 0,
onClick: function() {
openOffcanvas();
}
});
var bottomMenuIcon = new OO.ui.IconWidget({ icon: 'menu' });
bottomNavBtn = isekai.bottomNav.addButton({
id: 'offcanvas-toc',
label: mw.msg('isekai-offcanvastoc-menubutton'),
icon: bottomMenuIcon.$element[0],
priority: 0,
expand: true,
onClick: function() {
openOffcanvas();
}
});
bottomNavBtnLabel = $(bottomNavBtn).find('.isekai-bottom-nav-btn-label');
bottomNavBtnLabel.text('');
// 生成目录
var parserOutput = $('.mw-parser-output');
var headings = parserOutput.find('h1,h2,h3,h4,h5,h6,heading-6');
var headNum = new Array(6).fill(0);
menuList = [{
number: false,
text: mw.msg('isekai-offcanvastoc-description-item'),
id: 'bodyContent'
}];
headings.each(function() {
var headline = $(this).find('.mw-headline');
if (headline.length > 0) {
headingList.push(headline);
var text = headline.text();
var headId = headline.prop('id');
var indentNum = parseInt(this.tagName.replace(/^H/, ''));
this.classList.forEach((className) => {
if (className.indexOf('heading-') === 0) {
indentNum = parseInt(className.replace(/^heading-/, ''));
}
})
// 计算折叠
var menuNumStringBuilder = [];
headNum[indentNum - 1] ++;
for (var i = 0; i < indentNum; i ++) {
menuNumStringBuilder.push(headNum[i]);
}
for (var i = indentNum; i < headNum.length; i ++) {
headNum[i] = 0;
}
var menuNum = menuNumStringBuilder.join('.');
menuList.push({
number: menuNum,
text: text,
id: headId
});
}
});
// 生成dom
var tocContainer = $('#isekai-offcanvas-toc ul');
menuList.forEach(function(menuInfo) {
var listItem = document.createElement('a');
listItem.href = '#' + menuInfo.id;
listItem.dataset.id = menuInfo.id;
listItem.classList.add('list-item');
var titleItem = document.createElement('span');
titleItem.classList.add('title');
titleItem.innerText = menuInfo.text;
if (menuInfo.number) {
var numberItem = document.createElement('span');
numberItem.classList.add('number');
numberItem.innerText = menuInfo.number;
listItem.appendChild(numberItem);
}
listItem.appendChild(titleItem);
tocContainer[0].appendChild(listItem);
});
// 事件
$('#isekai-offcanvas-cover').on('click', function() {
closeOffcanvas();
});
$('#isekai-offcanvas-toc ul .list-item').on('click', function(e) {
// 点击链接
e.preventDefault();
var target = $(this).data('id');
if (target && target != '') {
target = '#' + target;
$('#isekai-offcanvas-toc ul .list-item').removeClass('active');
$(this).addClass('active');
scrollToAnchor(target);
if (window.innerWidth < 550) { // 手机端,关闭抽屉
closeOffcanvas();
}
}
return false;
});
$('#content').on('dblclick', function(e) {
if (window._openOffcanvasTocViaDblclick) {
openOffcanvas();
}
});
window.addEventListener('scroll', throttle(updateActive, 500), { passive: true });
updateActive();
}
});

@ -0,0 +1,113 @@
.toc-offcanvas {
position: fixed;
visibility: hidden;
opacity: 0;
top: 0;
right: 0;
z-index: 102;
margin: 0;
height: 100vh;
min-width: 275px;
max-width: 80%;
box-shadow: 1px 0 8px 0 rgba(0, 0, 0, 0.35);
transform: translate3d(100%, 0, 0);
transition: opacity 250ms ease-in-out, transform 250ms ease-in-out;
will-change: transform;
overflow-y: auto;
background-color: #eaecf0;
scrollbar-width: thin;
padding-top: env(safe-area-inset-top, 0);
padding-bottom: env(safe-area-inset-bottom, 0);
padding-right: env(safe-area-inset-right, 0);
> ul {
list-style: none;
margin: 0;
padding: 0;
a.list-item {
text-decoration: none;
&:hover,
&:active,
&:focus,
&:visited {
color: #54595d;
outline: none;
}
}
.list-item {
display: block;
color: #54595d;
background-color: #fff;
border-top: 1px solid #eaecf0;
max-width: 100%;
padding: 12px 10px 12px 15px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.title {
margin-inline-start: 10px;
}
&:active {
background-color: #ccc;
}
&:first-of-type {
border-top: none;
margin-top: 8px;
}
&:last-of-type {
margin-bottom: 8px;
}
&.active {
box-shadow: inset 4px 0 0 0 #3366cc;
}
}
}
}
.toc-offcanvas-cover {
position: fixed;
top: 0;
left: 0;
right: 0;
opacity: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
z-index: 101;
transition: opacity 250ms linear;
will-change: opacity;
&.hidden {
display: none;
}
}
body.toc-offcanvas-show {
overflow-y: hidden;
.toc-offcanvas {
visibility: visible;
}
#mw-overscroll-bottom-cover {
display: none;
}
}
body.toc-offcanvas-open {
.toc-offcanvas-cover {
opacity: 0.5;
}
.toc-offcanvas {
transform: translate3d(0, 0, 0);
opacity: 1;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

@ -0,0 +1,110 @@
export class BottomNavWidget {
constructor() {
this.domCreated = false;
this.btnList = [];
}
initDom() {
if (!this.domCreated) {
let bottomNavContainer = document.querySelector('#isekai-bottom-nav');
if (bottomNavContainer) {
this.bottomNavContainer = bottomNavContainer;
this.bottomNavElem = bottomNavContainer.querySelector('.isekai-bottom-nav');
} else {
bottomNavContainer = document.createElement('nav');
bottomNavContainer.id = 'isekai-bottom-nav';
bottomNavContainer.className = 'isekai-bottom-nav-container';
this.bottomNavContainer = bottomNavContainer;
let bottomNavElem = document.createElement('div');
bottomNavElem.className = 'isekai-bottom-nav';
this.bottomNavElem = bottomNavElem;
let bottomNavPlaceholder = document.createElement('div');
bottomNavPlaceholder.id = 'isekai-bottom-nav-placeholder';
bottomNavPlaceholder.className = 'isekai-bottom-nav-placeholder';
bottomNavContainer.appendChild(bottomNavElem);
document.body.appendChild(bottomNavContainer);
document.body.appendChild(bottomNavPlaceholder);
document.body.classList.add('use-isekai-bottom-nav');
}
}
}
addButton(btnInfo) {
if (!this.domCreated) {
this.initDom();
}
let btnElem = document.createElement('a');
btnElem.role = 'button';
btnElem.href = 'javascript:void(0);';
btnElem.classList.add('isekai-bottom-nav-btn');
btnElem.dataset.id = btnInfo.id;
if (btnInfo.className) {
btnElem.classList.add(...btnInfo.className.split(' '));
}
if (btnInfo.icon) {
let iconElem = document.createElement('span');
iconElem.className = 'isekai-bottom-nav-btn-icon';
if (typeof btnInfo.icon === 'string') {
iconElem.innerHTML = btnInfo.icon;
} else if (typeof btnInfo.icon === 'object') {
iconElem.appendChild(btnInfo.icon);
}
btnElem.appendChild(iconElem);
}
if (btnInfo.label) {
btnElem.title = btnInfo.label;
btnElem.setAttribute('aria-label', btnInfo.label);
}
if (btnInfo.expand) {
btnElem.classList.add('isekai-bottom-nav-btn-expand');
let labelElem = document.createElement('span');
labelElem.className = 'isekai-bottom-nav-btn-label';
labelElem.innerText = btnInfo.label ?? '';
btnElem.appendChild(labelElem);
}
if (btnInfo.onClick) {
btnElem.addEventListener('click', btnInfo.onClick);
}
let newBtnInfo = {...btnInfo, element: btnElem, proiorty: btnInfo.priority ?? 0};
let insertAfter = null;
let insertAfterIndex = 0;
this.btnList.forEach((one, index) => {
if (newBtnInfo.priority > one.priority) {
insertAfter = one.element;
insertAfterIndex = index;
}
});
if (insertAfter) {
this.bottomNavElem.insertAfter(btnElem, insertAfter);
this.btnList = [...this.btnList.slice(0, insertAfterIndex + 1), newBtnInfo, ...this.btnList.slice(insertAfterIndex + 1)];
} else {
this.bottomNavElem.prepend(btnElem);
this.btnList.unshift(newBtnInfo);
}
return btnElem;
}
removeButton(btnInfo) {
let btnId = '';
if (typeof btnInfo === 'string') {
btnId = btnInfo;
} else if (typeof btnInfo === 'object') {
btnId = btnInfo.id;
}
let btnElem = this.bottomNavElem.querySelector(`.isekai-bottom-nav-btn[data-id="${btnId}"]`);
if (btnElem) {
btnElem.remove();
}
}
}

@ -0,0 +1,149 @@
const isMobileSafari = (() => {
let v = navigator.userAgent.match(/Version\/(?<version>\S+) Mobile\/\S+/);
if (v && v.groups.version) { // Safari
return true;
}
return false;
})();
export class ContextMenuWidget {
constructor() {
this.menuItems = [];
this.menuItemsUpdated = true;
this.initDom();
}
initDom() {
let menuContainer = document.createElement('div');
menuContainer.id = 'iseai-contextmenu';
menuContainer.className = 'oo-ui-toolGroup-tools oo-ui-popupToolGroup-tools oo-ui-listToolGroup-tools oo-ui-toolGroup-enabled-tools oo-ui-popupToolGroup-active-tools';
menuContainer.style.minWidth = 'unset';
menuContainer.style.display = 'none';
this.menuContainer = menuContainer;
let menuCover = document.createElement('div');
menuCover.className = 'isekai-contextmenu-cover';
menuCover.style.display = 'none';
menuCover.addEventListener('click', () => {
this.hide();
});
this.menuCover = menuCover;
document.body.appendChild(menuContainer);
document.body.appendChild(menuCover);
}
setMenuItem(menuItems) {
this.menuItems = menuItems;
this.menuItemsUpdated = true;
}
updateMenuItemDom() {
if (this.menuItemsUpdated) {
this.menuContainer.innerHTML = '';
this.menuItems.sort((a, b) => {
return (b.priority ?? 0) - (a.priority ?? 0);
}).forEach((menuItem) => {
let menuElem = document.createElement('span');
menuElem.className = 'oo-ui-widget oo-ui-widget-enabled oo-ui-tool';
let menuLink = document.createElement('a');
menuLink.className = 'oo-ui-tool-link';
menuLink.tabIndex = 0;
menuLink.role = 'button';
menuLink.addEventListener('click', (e) => {
e.preventDefault();
menuItem.onClick?.();
this.hide();
});
let menuLabel = document.createElement('span');
menuLabel.className = 'oo-ui-tool-title';
menuLabel.innerText = menuItem.label;
menuLink.appendChild(menuLabel);
menuElem.appendChild(menuLink);
this.menuContainer.appendChild(menuElem);
});
this.menuItemsUpdated = false;
}
}
show(...args) {
let x = 0;
let y = 0;
if (args[0] instanceof MouseEvent) {
x = args[0].clientX;
y = args[0].clientY;
} else if (args[0] instanceof Element) {
let rect = args[0].getBoundingClientRect();
x = rect.left;
y = rect.top;
} else if (args.length === 2 && typeof args[0] === 'number' && typeof args[1] === 'number') {
x = args[0];
y = args[1];
}
if (this.menuItemsUpdated) {
this.updateMenuItemDom();
}
this.menuContainer.style.display = 'block';
this.menuCover.style.display = 'block';
let menuWidth = this.menuContainer.clientWidth;
let menuHeight = this.menuContainer.clientHeight;
if (x + menuWidth > window.innerWidth) {
x -= menuWidth;
}
if (y + menuHeight > window.innerHeight) {
y -= menuHeight;
}
this.menuContainer.style.left = x + 'px';
this.menuContainer.style.top = y + 'px';
}
hide() {
this.menuContainer.style.display = 'none';
this.menuCover.style.display = 'none';
}
bindToDom(dom) {
dom.addEventListener('contextmenu', (e) => {
e.preventDefault();
this.show(e);
});
if (isMobileSafari) {
// Safari下对长按的特殊处理
let longPressTimer;
dom.addEventListener('touchstart', (e) => {
document.body.classList.add('isekai-contextmenu-ios-longpress');
longPressTimer = setTimeout(() => {
e.preventDefault();
this.show({
x: e.pageX - window.scrollX,
y: e.pageY - window.scrollY
});
}, 200);
});
dom.addEventListener('touchend', function (e) {
document.body.classList.remove('isekai-contextmenu-ios-longpress');
if (longPressTimer) {
clearInterval(longPressTimer);
}
});
}
}
}

@ -0,0 +1,14 @@
import { registerModule } from '../moduleRegister';
import { ContextMenuWidget } from './contextMenu';
import { FabWidget } from './fab';
import { BottomNavWidget } from './bottomNav';
registerModule('ui.ContextMenuWidget', ContextMenuWidget);
registerModule('ui.FabWidget', FabWidget);
registerModule('ui.BottomNavWidget', BottomNavWidget);
const fabInstance = new FabWidget();
registerModule('fab', fabInstance);
const bottomNavInstance = new BottomNavWidget();
registerModule('bottomNav', bottomNavInstance);

@ -0,0 +1,123 @@
export class FabWidget {
constructor() {
this.domCreated = false;
this.btnList = [];
this.hidden = false;
}
initDom() {
if (!this.domCreated) {
let fabContainer = document.querySelector('#isekai-fab-container');
if (fabContainer) {
this.fabContainer = fabContainer;
} else {
fabContainer = document.createElement('div');
fabContainer.id = 'isekai-fab-container';
fabContainer.className = 'isekai-fab-group';
this.fabContainer = fabContainer;
document.body.appendChild(fabContainer);
// 右键隐藏
let menuItems = [
{
label: mw.msg('isekai-fab-hide-fab-button'),
onClick: () => {
this.hide();
mw.notify(mw.msg('isekai-fab-hide-fab-button-success'));
},
}
];
let contextMenu = new isekai.ui.ContextMenuWidget();
contextMenu.setMenuItem(menuItems);
contextMenu.bindToDom(fabContainer);
this.contextMenu = contextMenu;
}
this.domCreated = true;
}
}
addButton(btnInfo) {
if (!this.domCreated) {
this.initDom();
}
let btnElem = document.createElement('a');
btnElem.role = 'button';
btnElem.href = 'javascript:void(0);';
btnElem.className = 'isekai-fab-btn';
btnElem.dataset.id = btnInfo.id;
if (btnInfo.icon) {
let iconElem = document.createElement('span');
iconElem.className = 'isekai-fab-btn-icon';
if (typeof btnInfo.icon === 'string') {
iconElem.innerHTML = btnInfo.icon;
} else if (typeof btnInfo.icon === 'object') {
iconElem.appendChild(btnInfo.icon);
}
btnElem.appendChild(iconElem);
}
if (btnInfo.label) {
btnElem.title = btnInfo.label;
btnElem.setAttribute('aria-label', btnInfo.label);
}
if (btnInfo.onClick) {
btnElem.addEventListener('click', btnInfo.onClick);
}
let newBtnInfo = {...btnInfo, element: btnElem, proiorty: btnInfo.priority ?? 0};
let insertBefore = null;
let insertBeforeIndex = 0;
this.btnList.forEach((one, index) => {
if (one.priority < newBtnInfo.priority) {
insertBefore = one.element;
insertBeforeIndex = index;
}
});
if (insertBefore) {
this.fabContainer.insertBefore(btnElem, insertBefore);
this.btnList = [...this.btnList.slice(0, insertBeforeIndex), newBtnInfo, ...this.btnList.slice(insertBeforeIndex)];
} else {
this.fabContainer.appendChild(btnElem);
this.btnList.push(newBtnInfo);
}
return btnElem;
}
removeButton(btnInfo) {
let btnId = '';
if (typeof btnInfo === 'string') {
btnId = btnInfo;
} else if (typeof btnInfo === 'object') {
btnId = btnInfo.id;
}
let fabBtn = this.fabContainer.querySelector(`.isekai-fab-btn[data-id="${btnId}"]`);
if (fabBtn) {
fabBtn.remove();
}
}
hide() {
this.fabContainer.classList.add('isekai-fab-hidden');
this.hidden = true;
document.body.addEventListener('contextmenu', this._onContextMenu);
}
show() {
this.fabContainer.classList.remove('isekai-fab-hidden');
this.hidden = false;
}
_onContextMenu = (e) => {
e.preventDefault();
document.body.removeEventListener('contextmenu', this._onContextMenu);
this.show();
}
}

@ -32,7 +32,7 @@
@blue: #00AFF0; @blue: #00AFF0;
@teal: #00aba9; @teal: #00aba9;
@cyan: #1ba1e2; @cyan: #1ba1e2;
@cobalt: #0050ef; @cobalt: #0f6cbd;
@indigo: #6a00ff; @indigo: #6a00ff;
@violet: #aa00ff; @violet: #aa00ff;
@pink: #dc4fad; @pink: #dc4fad;

@ -7,7 +7,7 @@
.tile-large, .tile-large,
.tile-app { .tile-app {
display: block; display: block;
background-color: @cyan; background: linear-gradient(95deg, #007ec6, #0080cd, #008dd3);
color: @white; color: @white;
width: extract(@tileMedium, 1); width: extract(@tileMedium, 1);
height: extract(@tileMedium, 2); height: extract(@tileMedium, 2);
@ -17,6 +17,7 @@
overflow: hidden; overflow: hidden;
user-select: none; user-select: none;
max-width: none!important; max-width: none!important;
border-radius: 6px;
} }
.tile { .tile {
@ -47,6 +48,10 @@
.tile-wide, .tile-wide,
.tile-large, .tile-large,
.tile-app { .tile-app {
transition: transform 250ms ease-in-out, box-shadow 250ms ease-in-out;
box-shadow: none;
transform: none;
.icon { .icon {
max-width: 33%; max-width: 33%;
height: 33%; height: 33%;
@ -95,7 +100,9 @@
} }
&:hover { &:hover {
outline: @tileOutlineColor solid 4px; // outline: @tileOutlineColor solid 4px;
transform: translate3d(0, -4px, 0);
box-shadow: 0 4px 2px 0 rgba(0, 0, 0, 0.25);
} }
} }

@ -10,6 +10,7 @@ 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'
}, },
output: { output: {
filename: '[name]/ext.isekai.[name].js', filename: '[name]/ext.isekai.[name].js',

Loading…
Cancel
Save