增加全局组件,更改Tile样式
parent
1bab22e474
commit
6b8d9e88cf
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
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue