commit
5b63667bc2
|
@ -8,7 +8,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { DOM, BdUI, BdMenu, Modals, Reflection, Toasts, Notifications } from 'ui';
|
import { DOM, BdUI, BdMenu, Modals, Reflection, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui';
|
||||||
import BdCss from './styles/index.scss';
|
import BdCss from './styles/index.scss';
|
||||||
import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, WebpackModules, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache } from 'modules';
|
import { Events, CssEditor, Globals, Settings, Database, Updater, ModuleManager, PluginManager, ThemeManager, ExtModuleManager, Vendor, WebpackModules, Patcher, MonkeyPatch, ReactComponents, ReactHelpers, ReactAutoPatcher, DiscordApi, BdWebApi, Connectivity, Cache } from 'modules';
|
||||||
import { ClientLogger as Logger, ClientIPC, Utils } from 'common';
|
import { ClientLogger as Logger, ClientIPC, Utils } from 'common';
|
||||||
|
@ -28,7 +28,7 @@ class BetterDiscord {
|
||||||
Logger.log('main', 'BetterDiscord starting');
|
Logger.log('main', 'BetterDiscord starting');
|
||||||
|
|
||||||
this._bd = {
|
this._bd = {
|
||||||
DOM, BdUI, BdMenu, Modals, Reflection, Toasts, Notifications,
|
DOM, BdUI, BdMenu, Modals, Reflection, Toasts, Notifications, BdContextMenu, DiscordContextMenu,
|
||||||
|
|
||||||
Events, CssEditor, Globals, Settings, Database, Updater,
|
Events, CssEditor, Globals, Settings, Database, Updater,
|
||||||
ModuleManager, PluginManager, ThemeManager, ExtModuleManager,
|
ModuleManager, PluginManager, ThemeManager, ExtModuleManager,
|
||||||
|
@ -108,6 +108,13 @@ class BetterDiscord {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
showDummyNotif();
|
showDummyNotif();
|
||||||
|
|
||||||
|
DiscordContextMenu.add([
|
||||||
|
{
|
||||||
|
text: 'Hello',
|
||||||
|
onClick: () => { Toasts.info('Hello!'); }
|
||||||
|
}
|
||||||
|
]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Logger.err('main', ['FAILED TO LOAD!', err]);
|
Logger.err('main', ['FAILED TO LOAD!', err]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
.bd-cm,
|
||||||
|
.da-contextMenu { // sass-lint:disable-line class-name-format
|
||||||
|
background: #18191c;
|
||||||
|
box-shadow: 0 0 1px rgba(0, 0, 0, .82), 0 1px 4px rgba(0, 0, 0, .1);
|
||||||
|
border-radius: 5px;
|
||||||
|
position: fixed;
|
||||||
|
width: 170px;
|
||||||
|
z-index: 1005;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
&.bd-cmRenderLeft,
|
||||||
|
&.da-invertChildX { // sass-lint:disable-line class-name-format
|
||||||
|
.bd-cm {
|
||||||
|
margin-left: -170px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-cm {
|
||||||
|
left: 170px;
|
||||||
|
max-height: 270px;
|
||||||
|
overflow-y: auto;
|
||||||
|
contain: layout;
|
||||||
|
flex: 1;
|
||||||
|
min-height: 1px;
|
||||||
|
margin-left: 170px;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
height: 8px;
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-clip: padding-box;
|
||||||
|
background-color: rgba(32, 34, 37, .6);
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background-clip: padding-box;
|
||||||
|
border-radius: 7px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-cmGroup {
|
||||||
|
&:not(:first-child) {
|
||||||
|
&:not(:empty) {
|
||||||
|
border-top: 1px solid hsla(0, 0%, 96.1%, .08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-cmSub {
|
||||||
|
.bd-materialDesignIcon {
|
||||||
|
position: relative;
|
||||||
|
bottom: 2px;
|
||||||
|
fill: hsla(0, 0%, 100%, .6);
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
height: 20px;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
svg {
|
||||||
|
fill: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-cmItem {
|
||||||
|
cursor: default;
|
||||||
|
color: hsla(0, 0%, 100%, .6);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 16px;
|
||||||
|
margin: 2px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 6px 9px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&.bd-cmSub {
|
||||||
|
padding: 6px 0 6px 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-cmHint {
|
||||||
|
opacity: .8;
|
||||||
|
color: hsla(0, 0%, 100%, .6);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
max-width: 140px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #040405;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-cmToggle {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 5px 9px;
|
||||||
|
|
||||||
|
.bd-cmLabel {
|
||||||
|
overflow: hidden;
|
||||||
|
padding-right: 4px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-cmCheckbox {
|
||||||
|
margin-left: 3px;
|
||||||
|
pointer-events: none;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.bd-cmCheckboxInner {
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 18px;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: top;
|
||||||
|
width: 18px;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
&:checked {
|
||||||
|
+ span {
|
||||||
|
background-color: #7289da;
|
||||||
|
border-color: #7289da;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
border-color: #fff;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 2px 2px 0;
|
||||||
|
content: '';
|
||||||
|
display: table;
|
||||||
|
height: 10px;
|
||||||
|
left: 4px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
border: 2px solid hsla(0, 0%, 100%, .2);
|
||||||
|
border-radius: 2px;
|
||||||
|
bottom: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
transition: .24s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,3 +13,4 @@
|
||||||
@import './toasts';
|
@import './toasts';
|
||||||
@import './badges';
|
@import './badges';
|
||||||
@import './notifications';
|
@import './notifications';
|
||||||
|
@import './contextmenu';
|
||||||
|
|
|
@ -29,3 +29,7 @@
|
||||||
.bd-inline {
|
.bd-inline {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bd-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
|
|
||||||
.bd-notificationContainer {
|
.bd-notificationContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #202225;
|
background: #18191c;
|
||||||
width: 280px;
|
width: 280px;
|
||||||
height: 130px;
|
height: 130px;
|
||||||
top: 30px;
|
top: 30px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
box-shadow: 0 0 20px #202225;
|
box-shadow: 0 0 20px #18191c;
|
||||||
|
|
||||||
.bd-notificationHeader {
|
.bd-notificationHeader {
|
||||||
height: 10px;
|
height: 10px;
|
||||||
|
@ -70,6 +70,10 @@
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
&:not(:empty) {
|
||||||
|
border-top: 1px solid hsla(0, 0%, 96.1%, .08);
|
||||||
|
}
|
||||||
|
|
||||||
.bd-notificationBtn {
|
.bd-notificationBtn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
|
@ -79,11 +83,10 @@
|
||||||
color: #aeaeae;
|
color: #aeaeae;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background: rgba(0, 0, 0, .2);
|
background: transparent;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, .3);
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { Events, DiscordApi, Settings } from 'modules';
|
||||||
import { remote } from 'electron';
|
import { remote } from 'electron';
|
||||||
import DOM from './dom';
|
import DOM from './dom';
|
||||||
import Vue from './vue';
|
import Vue from './vue';
|
||||||
import { BdSettingsWrapper, BdModals, BdToasts, BdNotifications } from './components';
|
import { BdSettingsWrapper, BdModals, BdToasts, BdNotifications, BdContextMenu } from './components';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ export default class {
|
||||||
DOM.createElement('div', null, 'bd-modals').appendTo(DOM.bdModals);
|
DOM.createElement('div', null, 'bd-modals').appendTo(DOM.bdModals);
|
||||||
DOM.createElement('div', null, 'bd-toasts').appendTo(DOM.bdToasts);
|
DOM.createElement('div', null, 'bd-toasts').appendTo(DOM.bdToasts);
|
||||||
DOM.createElement('div', null, 'bd-notifications').appendTo(DOM.bdNotifications);
|
DOM.createElement('div', null, 'bd-notifications').appendTo(DOM.bdNotifications);
|
||||||
|
DOM.createElement('div', null, 'bd-contextmenu').appendTo(DOM.bdContextMenu);
|
||||||
DOM.createElement('bd-tooltips').appendTo(DOM.bdBody);
|
DOM.createElement('bd-tooltips').appendTo(DOM.bdBody);
|
||||||
|
|
||||||
this.toasts = new (Vue.extend(BdToasts))({
|
this.toasts = new (Vue.extend(BdToasts))({
|
||||||
|
@ -71,6 +72,10 @@ export default class {
|
||||||
el: '#bd-notifications'
|
el: '#bd-notifications'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.contextmenu = new (Vue.extend(BdContextMenu))({
|
||||||
|
el: '#bd-contextmenu'
|
||||||
|
});
|
||||||
|
|
||||||
return this.vueInstance;
|
return this.vueInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Context Menu Component
|
||||||
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
|
* All rights reserved.
|
||||||
|
* https://betterdiscord.net
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="root" class="bd-cm" :class="{'bd-cmRenderLeft': renderLeft}" v-if="activeMenu && activeMenu.menu" :style="calculatePosition()">
|
||||||
|
<CMGroup v-for="(group, index) in activeMenu.menu.groups" :items="group.items" :key="index" :closeMenu="hide" :left="left" :top="top"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Imports
|
||||||
|
import { BdContextMenu } from 'ui';
|
||||||
|
import CMGroup from './contextmenu/Group.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeMenu: BdContextMenu.activeMenu,
|
||||||
|
visibleSub: -1,
|
||||||
|
left: -1,
|
||||||
|
top: -1,
|
||||||
|
renderLeft: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: { CMGroup },
|
||||||
|
methods: {
|
||||||
|
calculatePosition() {
|
||||||
|
if (!this.activeMenu.menu.groups.length) return {};
|
||||||
|
this.mouseX = this.activeMenu.menu.x;
|
||||||
|
this.mouseY = this.activeMenu.menu.y;
|
||||||
|
const height = this.activeMenu.menu.groups.reduce((total, group) => total + group.items.length, 0) * 28;
|
||||||
|
this.top = window.innerHeight - this.mouseY - height < 0 ? this.mouseY - height : this.mouseY;
|
||||||
|
this.left = window.innerWidth - this.mouseX - 170 < 0 ? this.mouseX - 170 : this.mouseX;
|
||||||
|
this.renderLeft = (this.left + 170 * 2) > window.innerWidth;
|
||||||
|
window.addEventListener('mouseup', this.clickHide);
|
||||||
|
return { top: `${this.top}px`, left: `${this.left}px` };
|
||||||
|
},
|
||||||
|
hide() {
|
||||||
|
window.removeEventListener('mouseup', this.clickHide);
|
||||||
|
this.activeMenu.menu = null;
|
||||||
|
},
|
||||||
|
clickHide(e) {
|
||||||
|
if (!this.$refs.root) return;
|
||||||
|
if (this.$refs.root.contains(e.target)) return;
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Context Menu Button Component
|
||||||
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
|
* All rights reserved.
|
||||||
|
* https://betterdiscord.net
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="bd-cmItem" :style="{color: item.color || ''}" @click="onClick">
|
||||||
|
<span>{{item.text}}</span>
|
||||||
|
<div class="bd-cmHint" v-if="item.hint">{{item.hint}}</div>
|
||||||
|
<img :src="item.icon" v-else-if="item.icon"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['item', 'onClick']
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Context Menu Group Component
|
||||||
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
|
* All rights reserved.
|
||||||
|
* https://betterdiscord.net
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="bd-cmGroup" ref="test">
|
||||||
|
<template v-for="(item, index) in items">
|
||||||
|
<CMButton v-if="!item.type || item.type === 'button'" :item="item" :onClick="() => { item.onClick(); closeMenu(); }" />
|
||||||
|
<CMToggle v-else-if="item.type === 'toggle'" :item="item" :onClick="() => { item.checked = item.onChange(!item.checked) }" />
|
||||||
|
<div v-else-if="item.type === 'sub'" class="bd-cmItem bd-cmSub" @mouseenter="e => subMenuMouseEnter(e, index, item)" @mouseleave="e => subMenuMouseLeave(e, index, item)">
|
||||||
|
{{item.text}}
|
||||||
|
<MiChevronDown />
|
||||||
|
<div ref="test2" class="bd-cm" v-if="index === visibleSub" :style="subStyle">
|
||||||
|
<template v-for="(item, index) in item.items">
|
||||||
|
<CMButton v-if="!item.type || item.type === 'button'" :item="item" :onClick="() => { item.onClick(); closeMenu(); }" />
|
||||||
|
<CMToggle v-else-if="item.type === 'toggle'" :item="item" :onClick="() => { item.checked = item.onChange(!item.checked) }" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Imports
|
||||||
|
import CMButton from './Button.vue';
|
||||||
|
import CMToggle from './Toggle.vue';
|
||||||
|
import { MiChevronDown } from '../common';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visibleSub: -1,
|
||||||
|
subStyle: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: ['items', 'closeMenu', 'left', 'top'],
|
||||||
|
components: { CMButton, CMToggle, MiChevronDown },
|
||||||
|
methods: {
|
||||||
|
subMenuMouseEnter(e, index, sub) {
|
||||||
|
const subHeight = sub.items.length > 9 ? 270 : sub.items.length * e.target.offsetHeight;
|
||||||
|
const top = this.top + subHeight + e.target.offsetTop > window.innerHeight ?
|
||||||
|
this.top - subHeight + e.target.offsetTop + e.target.offsetHeight :
|
||||||
|
this.top + e.target.offsetTop;
|
||||||
|
this.subStyle = { top: `${top}px`, left: `${this.left}px` };
|
||||||
|
this.visibleSub = index;
|
||||||
|
},
|
||||||
|
subMenuMouseLeave(e, index, sub) {
|
||||||
|
this.visibleSub = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* BetterDiscord Context Menu Toggle Component
|
||||||
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
|
* All rights reserved.
|
||||||
|
* https://betterdiscord.net
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="bd-cmItem bd-cmToggle" @click="onClick">
|
||||||
|
<div class="bd-cmLabel">{{item.text}}</div>
|
||||||
|
<div class="bd-cmCheckbox">
|
||||||
|
<div class="bd-cmCheckboxInner">
|
||||||
|
<input type="checkbox" :checked="item.checked || item.enabled"/>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: ['item', 'onClick']
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -3,3 +3,4 @@ export { default as BdSettings } from './BdSettings.vue';
|
||||||
export { default as BdModals } from './BdModals.vue';
|
export { default as BdModals } from './BdModals.vue';
|
||||||
export { default as BdToasts } from './BdToasts.vue';
|
export { default as BdToasts } from './BdToasts.vue';
|
||||||
export { default as BdNotifications } from './BdNotifications.vue';
|
export { default as BdNotifications } from './BdNotifications.vue';
|
||||||
|
export { default as BdContextMenu } from './BdContextMenu.vue';
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* BetterDiscord Context Menus
|
||||||
|
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
|
||||||
|
* All rights reserved.
|
||||||
|
* https://betterdiscord.net
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { ReactComponents, WebpackModules, MonkeyPatch } from 'modules';
|
||||||
|
import { VueInjector, Toasts } from 'ui';
|
||||||
|
import CMGroup from './components/contextmenu/Group.vue';
|
||||||
|
|
||||||
|
export class BdContextMenu {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a context menu
|
||||||
|
* @param {MouseEvent|Object} e MouseEvent or Object { x: 0, y: 0 }
|
||||||
|
* @param {Object[]} grops Groups of items to show in context menu
|
||||||
|
*/
|
||||||
|
static show(e, groups) {
|
||||||
|
const x = e.x || e.clientX;
|
||||||
|
const y = e.y || e.clientY;
|
||||||
|
this.activeMenu.menu = { x, y, groups };
|
||||||
|
}
|
||||||
|
|
||||||
|
static get activeMenu() {
|
||||||
|
return this._activeMenu || (this._activeMenu = { menu: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DiscordContextMenu {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add items to Discord context menu
|
||||||
|
* @param {any} items items to add
|
||||||
|
* @param {Function} [filter] filter function for target filtering
|
||||||
|
*/
|
||||||
|
static add(items, filter) {
|
||||||
|
if (!this.patched) this.patch();
|
||||||
|
this.menus.push({ items, filter });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get menus() {
|
||||||
|
return this._menus || (this._menus = []);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async patch() {
|
||||||
|
if (this.patched) return;
|
||||||
|
this.patched = true;
|
||||||
|
const self = this;
|
||||||
|
MonkeyPatch('BD:DiscordCMOCM', WebpackModules.getModuleByProps(['openContextMenu'])).instead('openContextMenu', (_, [e, fn], originalFn) => {
|
||||||
|
const overrideFn = function (...args) {
|
||||||
|
const res = fn(...args);
|
||||||
|
if (!res.hasOwnProperty('type')) return res;
|
||||||
|
if (!res.type.prototype || !res.type.prototype.render || res.type.prototype.render.__patched) return res;
|
||||||
|
MonkeyPatch('BD:DiscordCMRender', res.type.prototype).after('render', (c, a, r) => self.renderCm(c, a, r, res));
|
||||||
|
res.type.prototype.render.__patched = true;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return originalFn(e, overrideFn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static renderCm(component, args, retVal, res) {
|
||||||
|
if (!retVal.props || !res.props) return;
|
||||||
|
const { target } = res.props;
|
||||||
|
const { top, left } = retVal.props.style;
|
||||||
|
if (!target || !top || !left) return;
|
||||||
|
if (!retVal.props.children) return;
|
||||||
|
if (!(retVal.props.children instanceof Array)) retVal.props.children = [retVal.props.children];
|
||||||
|
for (const menu of this.menus.filter(menu => { if (!menu.filter) return true; return menu.filter(target)})) {
|
||||||
|
retVal.props.children.push(VueInjector.createReactElement(CMGroup, {
|
||||||
|
top,
|
||||||
|
left,
|
||||||
|
closeMenu: () => WebpackModules.getModuleByProps(['closeContextMenu']).closeContextMenu(),
|
||||||
|
items: menu.items
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -186,6 +186,7 @@ export default class DOM {
|
||||||
static get bdModals() { return this.getElement('bd-modals') || this.createElement('bd-modals').appendTo(this.bdBody) }
|
static get bdModals() { return this.getElement('bd-modals') || this.createElement('bd-modals').appendTo(this.bdBody) }
|
||||||
static get bdToasts() { return this.getElement('bd-toasts') || this.createElement('bd-toasts').appendTo(this.bdBody) }
|
static get bdToasts() { return this.getElement('bd-toasts') || this.createElement('bd-toasts').appendTo(this.bdBody) }
|
||||||
static get bdNotifications() { return this.getElement('bd-notifications') || this.createElement('bd-notifications').appendTo(this.bdBody) }
|
static get bdNotifications() { return this.getElement('bd-notifications') || this.createElement('bd-notifications').appendTo(this.bdBody) }
|
||||||
|
static get bdContextMenu() { return this.getElement('bd-contextmenu') || this.createElement('bd-contextmenu').appendTo(this.bdBody) }
|
||||||
|
|
||||||
static getElement(e) {
|
static getElement(e) {
|
||||||
if (e instanceof BdNode) return e.element;
|
if (e instanceof BdNode) return e.element;
|
||||||
|
|
|
@ -4,6 +4,7 @@ export { default as BdMenu, BdMenuItems } from './bdmenu';
|
||||||
export { default as Modals } from './modals';
|
export { default as Modals } from './modals';
|
||||||
export { default as Toasts } from './toasts';
|
export { default as Toasts } from './toasts';
|
||||||
export { default as Notifications } from './notifications';
|
export { default as Notifications } from './notifications';
|
||||||
|
export * from './contextmenus';
|
||||||
|
|
||||||
export { default as VueInjector } from './vueinjector';
|
export { default as VueInjector } from './vueinjector';
|
||||||
export { default as Reflection } from './reflection';
|
export { default as Reflection } from './reflection';
|
||||||
|
|
Loading…
Reference in New Issue