diff --git a/client/src/modules/pluginapi.js b/client/src/modules/pluginapi.js
index 953a0ce2..ac5bfae7 100644
--- a/client/src/modules/pluginapi.js
+++ b/client/src/modules/pluginapi.js
@@ -12,6 +12,7 @@ import { EmoteModule } from 'builtin';
import { SettingsSet, SettingsCategory, Setting, SettingsScheme } from 'structs';
import { BdMenu, Modals, DOM, DOMObserver, VueInjector, Toasts, Notifications, BdContextMenu, DiscordContextMenu } from 'ui';
import * as CommonComponents from 'commoncomponents';
+import { default as Components } from '../ui/components/generic';
import { Utils, Filters, ClientLogger as Logger, ClientIPC, AsyncEventEmitter } from 'common';
import Settings from './settings';
import ExtModuleManager from './extmodulemanager';
@@ -64,6 +65,7 @@ export default class PluginApi {
get EventsWrapper() { return EventsWrapper }
get CommonComponents() { return CommonComponents }
+ get Components() { return Components }
get Filters() { return Filters }
get Discord() { return DiscordApi }
get DiscordApi() { return DiscordApi }
diff --git a/client/src/ui/components/generic/Button.vue b/client/src/ui/components/generic/Button.vue
new file mode 100644
index 00000000..190d5507
--- /dev/null
+++ b/client/src/ui/components/generic/Button.vue
@@ -0,0 +1,21 @@
+/**
+ * BetterDiscord Generic 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.
+*/
+
+
+
+ {{text}}
+
+
+
+
diff --git a/client/src/ui/components/generic/ButtonGroup.vue b/client/src/ui/components/generic/ButtonGroup.vue
new file mode 100644
index 00000000..d57011cd
--- /dev/null
+++ b/client/src/ui/components/generic/ButtonGroup.vue
@@ -0,0 +1,23 @@
+/**
+ * BetterDiscord Generic Button 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.
+*/
+
+
+
+
+
+
+
+
diff --git a/client/src/ui/components/generic/index.js b/client/src/ui/components/generic/index.js
new file mode 100644
index 00000000..e89b4651
--- /dev/null
+++ b/client/src/ui/components/generic/index.js
@@ -0,0 +1,29 @@
+import VrWrapper from '../../vrwrapper';
+
+import ButtonGroupComponent from './ButtonGroup.vue';
+class ButtonGroupWrapper extends VrWrapper {
+ get component() { return ButtonGroupComponent }
+ constructor(props) {
+ super();
+ this.props = props;
+ }
+}
+
+import ButtonComponent from './Button.vue';
+class ButtonWrapper extends VrWrapper {
+ get component() { return ButtonComponent }
+ constructor(props) {
+ super();
+ this.props = props;
+ }
+}
+
+export default class {
+ static Button(props) {
+ return new ButtonWrapper(props);
+ }
+
+ static ButtonGroup(props) {
+ return new ButtonGroupWrapper(props);
+ }
+}
diff --git a/tests/ext/plugins/Custom Elements Example/index.js b/tests/ext/plugins/Custom Elements Example/index.js
index d441bfee..f54c8bda 100644
--- a/tests/ext/plugins/Custom Elements Example/index.js
+++ b/tests/ext/plugins/Custom Elements Example/index.js
@@ -19,6 +19,7 @@ module.exports = (Plugin, Api, Vendor) => {
Logger.log('Custom Elements Example Started');
this.injectStyle();
this.patchGuildTextChannel();
+ this.patchMessages();
return true;
}
@@ -31,6 +32,8 @@ module.exports = (Plugin, Api, Vendor) => {
// Force update elements to remove our changes
const GuildTextChannel = await ReactComponents.getComponent('GuildTextChannel');
GuildTextChannel.forceUpdateAll();
+ const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector });
+ MessageContent.forceUpdateAll();
return true;
}
@@ -47,7 +50,14 @@ module.exports = (Plugin, Api, Vendor) => {
&:hover {
opacity: 1;
}
- }`;
+ }
+ .exampleBtnGroup {
+ .bd-button {
+ font-size: 14px;
+ padding: 5px;
+ }
+ }
+ `;
await CssUtils.injectSass(css);
}
@@ -59,6 +69,14 @@ module.exports = (Plugin, Api, Vendor) => {
GuildTextChannel.forceUpdateAll();
}
+ async patchMessages() {
+ // Get Message component and patch it's render function
+ const MessageContent = await ReactComponents.getComponent('MessageContent', { selector: Reflection.resolve('container', 'containerCozy', 'containerCompact', 'edited').selector });
+ monkeyPatch(MessageContent.component.prototype).after('render', this.injectGenericComponents.bind(this));
+ // Force update to see our changes immediatly
+ MessageContent.forceUpdateAll();
+ }
+
/*
* Injecting a custom React element using React.createElement
* https://reactjs.org/docs/react-api.html#createelement
@@ -77,6 +95,30 @@ module.exports = (Plugin, Api, Vendor) => {
child.children.push(customVueComponent(Vuewrap, { onClick: e => this.handleClick(e, child.channel) }));
}
+ /**
+ * Inject generic components provided by BD
+ */
+ injectGenericComponents(that, args, returnValue) {
+ // If children is not an array make it into one
+ if (!returnValue.props.children instanceof Array) returnValue.props.children = [returnValue.props.children];
+ // Add a generic Button component provided by BD
+ returnValue.props.children.push(Api.Components.ButtonGroup({
+ classes: [ 'exampleBtnGroup' ], // Additional classes for button group
+ buttons: [
+ {
+ classes: ['exampleBtn'], // Additional classes for button
+ text: 'Hello World!', // Text for button
+ onClick: e => Logger.log('Hello World!') // Button click handler
+ },
+ {
+ classes: ['exampleBtn'],
+ text: 'Button',
+ onClick: e => Logger.log('Button!')
+ }
+ ]
+ }).render()); // Render will return the wrapped component that can then be displayed
+ }
+
/**
* Will log the channel object
*/