From 9ff4d9817432b022a8ab8744c1f9f57f8d158cf4 Mon Sep 17 00:00:00 2001
From: Giannis Kopanas <jkopanas@gmail.com>
Date: Sat, 7 Dec 2019 20:12:26 +0200
Subject: [PATCH] feat(menuService): add Tool and ToolGroup libraries

---
 editors/editoria/src/EditorConfig.js          | 22 ++++----
 editors/editoria/src/config/menu.js           | 10 ++--
 wax-prosemirror-components/index.js           |  2 +
 wax-prosemirror-core/src/Application.js       |  4 +-
 wax-prosemirror-core/src/Config/Config.js     | 23 +++++----
 .../src/Config/defaultConfig.js               |  3 +-
 wax-prosemirror-core/src/Wax.js               |  1 +
 .../src/config/Config/config.js               |  6 ---
 wax-prosemirror-core/src/ioc-react.js         |  7 ++-
 .../src/services/Config/config.js             |  6 ---
 .../src/services/LayoutService/Layout.js      | 15 ++++--
 .../components/componentPlugin.js             | 13 +++--
 .../src/services/MenuService/Menu.js          | 27 ----------
 .../src/services/MenuService/MenuService.js   | 10 ----
 wax-prosemirror-core/src/services/Service.js  |  4 +-
 wax-prosemirror-plugins/index.js              |  4 +-
 wax-prosemirror-plugins/package.json          |  4 +-
 .../src/MenuService/Menu.js                   | 39 +++++++++++++++
 .../src/MenuService/MenuCollection.js         | 13 +++++
 .../src/MenuService/MenuService.js            | 50 +++++++++++++++++++
 .../src/MenuService/MenuWrapper.js            | 20 ++++++++
 .../src/RedoUndoService/Redo.js               | 18 +++++++
 .../src/RedoUndoService/RedoUndo.js           | 19 +++++++
 .../src/RedoUndoService/RedoUndoService.js    | 15 ++++++
 .../src/RedoUndoService/Undo.js               | 18 +++++++
 wax-prosemirror-plugins/src/lib/GroupTool.js  | 29 +++++++++++
 wax-prosemirror-plugins/src/lib/Tools.js      | 48 ++++++++++++++++++
 27 files changed, 334 insertions(+), 96 deletions(-)
 delete mode 100644 wax-prosemirror-core/src/config/Config/config.js
 delete mode 100644 wax-prosemirror-core/src/services/Config/config.js
 delete mode 100644 wax-prosemirror-core/src/services/MenuService/Menu.js
 delete mode 100644 wax-prosemirror-core/src/services/MenuService/MenuService.js
 create mode 100644 wax-prosemirror-plugins/src/MenuService/Menu.js
 create mode 100644 wax-prosemirror-plugins/src/MenuService/MenuCollection.js
 create mode 100644 wax-prosemirror-plugins/src/MenuService/MenuService.js
 create mode 100644 wax-prosemirror-plugins/src/MenuService/MenuWrapper.js
 create mode 100644 wax-prosemirror-plugins/src/RedoUndoService/Redo.js
 create mode 100644 wax-prosemirror-plugins/src/RedoUndoService/RedoUndo.js
 create mode 100644 wax-prosemirror-plugins/src/RedoUndoService/RedoUndoService.js
 create mode 100644 wax-prosemirror-plugins/src/RedoUndoService/Undo.js
 create mode 100644 wax-prosemirror-plugins/src/lib/GroupTool.js
 create mode 100644 wax-prosemirror-plugins/src/lib/Tools.js

diff --git a/editors/editoria/src/EditorConfig.js b/editors/editoria/src/EditorConfig.js
index c3cffe3b8..c0a25e94b 100644
--- a/editors/editoria/src/EditorConfig.js
+++ b/editors/editoria/src/EditorConfig.js
@@ -86,18 +86,18 @@ const plugins = [
   columnResizing(),
   tableEditing(),
   TrackChangePlugin({ options: {} }),
-  invisibles([hardBreak()]),
+  invisibles([hardBreak()])
   // FindAndReplacePlugin,
-  MenuBarPlugin({
-    Component: MainMenuBar,
-    renderArea: "topBar",
-    menuItems: ["undo", "redo"]
-  }),
-  MenuBarPlugin({
-    Component: SideMenuBar,
-    renderArea: "leftSideBar"
-    //menuItems: ["plain"]
-  })
+  // MenuBarPlugin({
+  //   Component: MainMenuBar,
+  //   renderArea: "topBar",
+  //   menuItems: ["undo", "redo"]
+  // }),
+  // MenuBarPlugin({
+  //   Component: SideMenuBar,
+  //   renderArea: "leftSideBar"
+  //   //menuItems: ["plain"]
+  // })
 ];
 
 const services = [new LinkService()];
diff --git a/editors/editoria/src/config/menu.js b/editors/editoria/src/config/menu.js
index 69523798b..c29b97c34 100644
--- a/editors/editoria/src/config/menu.js
+++ b/editors/editoria/src/config/menu.js
@@ -1,13 +1,9 @@
 export default {
-  menus: [
+  MenuService: [
     {
+      name: "top",
       templateArea: "topBar",
-      tools: ["redo", "undo"],
-      groups: [
-        "redo-undo",
-        "annotations",
-        { group: "Annotation", exclude: [], include: [] }
-      ]
+      groupTools: [{ name: "RedoUndo", exclude: [] }]
     }
   ]
 };
diff --git a/wax-prosemirror-components/index.js b/wax-prosemirror-components/index.js
index 0fb830248..0e8075554 100644
--- a/wax-prosemirror-components/index.js
+++ b/wax-prosemirror-components/index.js
@@ -2,3 +2,5 @@ export { default as MainMenuBar } from "./src/mainMenuBar/MainMenuBar";
 export { default as SideMenuBar } from "./src/sideMenuBar/SideMenuBar";
 export { default as InfoArea } from "./src/components/infoArea/InfoArea";
 export { default as Overlay } from "./src/components/Overlay";
+export { default as Button } from "./src/components/Button";
+export { default as icons } from "./src/icons/icons";
diff --git a/wax-prosemirror-core/src/Application.js b/wax-prosemirror-core/src/Application.js
index fc602e1b3..8c77d71bd 100644
--- a/wax-prosemirror-core/src/Application.js
+++ b/wax-prosemirror-core/src/Application.js
@@ -26,9 +26,8 @@ export default class Application {
 
   setConfig(config) {
     this.config = this.container.get("Config");
-
     Object.keys(config).forEach(conf => {
-      this.config.pushToArray(conf, config[conf]);
+      this.config = this.config.pushToArray(conf, config[conf]);
     });
   }
 
@@ -63,6 +62,7 @@ export default class Application {
     const app = container.get("Wax");
     app.setConfig(config);
     app.registerServices();
+
     return app;
   }
 }
diff --git a/wax-prosemirror-core/src/Config/Config.js b/wax-prosemirror-core/src/Config/Config.js
index a5b349e10..34f027839 100644
--- a/wax-prosemirror-core/src/Config/Config.js
+++ b/wax-prosemirror-core/src/Config/Config.js
@@ -3,27 +3,28 @@ import { injectable, inject } from "inversify";
 
 @injectable()
 export default class Config {
-  config = {};
+  _config = {};
   constructor(@inject("config") config) {
-    this.config = config;
+    this._config = config;
   }
 
   set(key, value) {
-    set(this.config, key, value);
-    return this.config;
+    set(this._config, key, value);
+    return this._config;
   }
 
   get(key) {
-    return get(this.config, key);
+    return get(this._config, key);
   }
 
   pushToArray(key, value) {
-    const oldValue = this.get(key);
-    let newValue = value;
-    if (oldValue && isArrayLikeObject(oldValue)) {
-      newValue = oldValue.push(value);
+    let oldValue = this.get(key);
+    if (oldValue) {
+      oldValue.push(value);
+    } else {
+      oldValue = value;
     }
-
-    return this.set(key, newValue);
+    this.set(key, oldValue);
+    return this;
   }
 }
diff --git a/wax-prosemirror-core/src/Config/defaultConfig.js b/wax-prosemirror-core/src/Config/defaultConfig.js
index d1138e2f1..3ecd95773 100644
--- a/wax-prosemirror-core/src/Config/defaultConfig.js
+++ b/wax-prosemirror-core/src/Config/defaultConfig.js
@@ -1,5 +1,6 @@
 import LayoutService from "../services/LayoutService/LayoutService";
+import { MenuService, RedoUndoService } from "wax-prosemirror-plugins";
 
 export default {
-  services: [new LayoutService()]
+  services: [new LayoutService(), new MenuService(), new RedoUndoService()]
 };
diff --git a/wax-prosemirror-core/src/Wax.js b/wax-prosemirror-core/src/Wax.js
index cdca230c3..e6cf49d29 100644
--- a/wax-prosemirror-core/src/Wax.js
+++ b/wax-prosemirror-core/src/Wax.js
@@ -45,6 +45,7 @@ class Wax extends Component {
   constructor(props) {
     super(props);
     const { config } = props;
+    console.log("Appp Started", config);
     this.application = Application.create(config);
   }
 
diff --git a/wax-prosemirror-core/src/config/Config/config.js b/wax-prosemirror-core/src/config/Config/config.js
deleted file mode 100644
index 3f6296f92..000000000
--- a/wax-prosemirror-core/src/config/Config/config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import LayoutService from "../services/LayoutService/LayoutService";
-import ConfigService from "./ConfigService";
-
-export default {
-  services: [new LayoutService(), new ConfigService()]
-};
diff --git a/wax-prosemirror-core/src/ioc-react.js b/wax-prosemirror-core/src/ioc-react.js
index 538a79c51..53e818b2e 100644
--- a/wax-prosemirror-core/src/ioc-react.js
+++ b/wax-prosemirror-core/src/ioc-react.js
@@ -10,12 +10,15 @@ export default props => (
 
 export function useInjection(identifier) {
   const {
-    app: { container }
+    app: { container },
+    view
   } = useContext(WaxContext);
 
   if (!container) {
     throw new Error();
   }
 
-  return container.isBound(identifier) ? container.get(identifier) : null;
+  return container.isBound(identifier)
+    ? { view, instance: container.get(identifier) }
+    : null;
 }
diff --git a/wax-prosemirror-core/src/services/Config/config.js b/wax-prosemirror-core/src/services/Config/config.js
deleted file mode 100644
index 7ffbb9afe..000000000
--- a/wax-prosemirror-core/src/services/Config/config.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import LayoutService from "../../services/LayoutService/LayoutService";
-import ConfigService from "./ConfigService";
-
-export default {
-  services: [new LayoutService(), new ConfigService()]
-};
diff --git a/wax-prosemirror-core/src/services/LayoutService/Layout.js b/wax-prosemirror-core/src/services/LayoutService/Layout.js
index 9fbbe4f05..734a53f7b 100644
--- a/wax-prosemirror-core/src/services/LayoutService/Layout.js
+++ b/wax-prosemirror-core/src/services/LayoutService/Layout.js
@@ -4,23 +4,32 @@ import LayoutFactory from "./components/LayoutFactory";
 
 @injectable()
 export default class Layout {
-  components = {};
+  components = [];
   layoutComponent = LayoutFactory(DefaultLayout);
   addComponent(renderArea, component) {
-    const size = this.components[renderArea].size();
+    if (!this.components[renderArea]) {
+      this.createArea(renderArea);
+    }
+    const size = this.components[renderArea].size;
     this.components[renderArea].set(size + 1, component);
     return this;
   }
 
   render(renderArea) {
     if (!this.components[renderArea]) return null;
-    return this.components[renderArea].values();
+    return this.getArray(this.components[renderArea]);
   }
 
   createArea(area) {
     this.components[area] = new Map();
   }
 
+  getArray(iterator) {
+    const components = [];
+    iterator.forEach(component => components.push(component));
+    return components;
+  }
+
   setLayout(component) {
     this.layoutComponent = LayoutFactory(component);
   }
diff --git a/wax-prosemirror-core/src/services/LayoutService/components/componentPlugin.js b/wax-prosemirror-core/src/services/LayoutService/components/componentPlugin.js
index 283c47c08..dd8eec1f3 100644
--- a/wax-prosemirror-core/src/services/LayoutService/components/componentPlugin.js
+++ b/wax-prosemirror-core/src/services/LayoutService/components/componentPlugin.js
@@ -2,11 +2,14 @@ import React from "react";
 import { useInjection } from "../../../ioc-react";
 
 const ComponentPlugin = renderArea => props => {
-  const Layout = useInjection("Layout");
-  const Components = Layout.render(renderArea);
+  const { view, instance } = useInjection("Layout");
 
-  return (Components || []).map(Component => (
-    <Component renderArea={renderArea} />
-  ));
+  const components = instance.render(renderArea);
+
+  return components
+    ? components.map(Component => {
+        return <Component view={view} />;
+      })
+    : null;
 };
 export default ComponentPlugin;
diff --git a/wax-prosemirror-core/src/services/MenuService/Menu.js b/wax-prosemirror-core/src/services/MenuService/Menu.js
deleted file mode 100644
index b4b09ca10..000000000
--- a/wax-prosemirror-core/src/services/MenuService/Menu.js
+++ /dev/null
@@ -1,27 +0,0 @@
-export default class Menu {
-  groups = new Map();
-  constructor(config = []) {
-
-    this.addGroup()
-  }
-
-  addGroup(name, value) {
-    this.groups.set(name, value);
-  }
-
-}
-
-
-{
-    groupNanme : "Annotation",
-    tools: [
-        {},{}
-    ]
-}
-,
-{
-    groupNanme : "Annotation",
-    tools: [
-        {},{}
-    ]
-}
\ No newline at end of file
diff --git a/wax-prosemirror-core/src/services/MenuService/MenuService.js b/wax-prosemirror-core/src/services/MenuService/MenuService.js
deleted file mode 100644
index ed98e9525..000000000
--- a/wax-prosemirror-core/src/services/MenuService/MenuService.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import Menu from "./Menu";
-export default class MenuService extends Services {
-  boot() {}
-  register() {
-    this.container
-      .bind("Menu")
-      .to(Menu)
-      .inSingletonScope();
-  }
-}
diff --git a/wax-prosemirror-core/src/services/Service.js b/wax-prosemirror-core/src/services/Service.js
index a7acc31eb..03bf881d7 100644
--- a/wax-prosemirror-core/src/services/Service.js
+++ b/wax-prosemirror-core/src/services/Service.js
@@ -9,8 +9,6 @@ export default class Service {
   }
 
   get config() {
-    return this.app.config;
+    return this.app.config.get(this.name);
   }
-
-  boot() {}
 }
diff --git a/wax-prosemirror-plugins/index.js b/wax-prosemirror-plugins/index.js
index 1456c63fc..a8d92ad8b 100644
--- a/wax-prosemirror-plugins/index.js
+++ b/wax-prosemirror-plugins/index.js
@@ -1,5 +1,7 @@
 export { default as TrackChangePlugin } from "./src/trackChanges/TrackChangePlugin";
 export { default as FindAndReplacePlugin } from "./src/FindAndReplacePlugin";
-export { default as MenuBarPlugin } from "./src/menuBar/MenuBarPlugin";
 export { default as OverlayPlugin } from "./src/overlay/OverlayPlugin";
 export { default as LinkService } from "./src/LinkService/LinkService";
+export { default as MenuService } from "./src/MenuService/MenuService";
+export { default as RedoUndoService } from "./src/RedoUndoService/RedoUndoService";
+export { default as Tool } from "./src/lib/Tools";
diff --git a/wax-prosemirror-plugins/package.json b/wax-prosemirror-plugins/package.json
index 7a97096f3..5cef4e552 100644
--- a/wax-prosemirror-plugins/package.json
+++ b/wax-prosemirror-plugins/package.json
@@ -14,6 +14,8 @@
     "inversify": "^5.0.1",
     "inversify-inject-decorators": "^3.1.0",
     "reflect-metadata": "^0.1.13",
-    "wax-prosemirror-layouts": "^0.0.3"
+    "wax-prosemirror-layouts": "^0.0.3",
+    "wax-prosemirror-core": "^0.0.3",
+    "wax-prosemirror-components": "^0.0.3"
   }
 }
diff --git a/wax-prosemirror-plugins/src/MenuService/Menu.js b/wax-prosemirror-plugins/src/MenuService/Menu.js
new file mode 100644
index 000000000..2848c41d2
--- /dev/null
+++ b/wax-prosemirror-plugins/src/MenuService/Menu.js
@@ -0,0 +1,39 @@
+import React from "react";
+import { injectable } from "inversify";
+import GroupTool from "../lib/GroupTool";
+
+import MenuWrapper from "./MenuWrapper";
+@injectable()
+export default class Menu {
+  groupTools = [];
+  config = {};
+  name = "";
+  constructor(config, createTools) {
+    this.name = config.name;
+    this.config = config;
+    console.log(this.config, "menu config");
+    this.groupTools = createTools(this.config.groupTools);
+
+    this.excludeIncludeTools();
+  }
+
+  excludeIncludeTools() {
+    this.groupTools.forEach(groupTool => {
+      if (groupTool instanceof GroupTool) {
+        groupTool.excludeIncludeTools();
+      }
+    });
+  }
+
+  render() {
+    return view => <MenuWrapper items={this.groupTools} view={view} />;
+  }
+}
+
+// {
+//   templateArea: "topBar",
+//   tools: [
+//     "redo-undo",
+//     { name: "Annotations", exclude: [], include: [] }
+//   ]
+// }
diff --git a/wax-prosemirror-plugins/src/MenuService/MenuCollection.js b/wax-prosemirror-plugins/src/MenuService/MenuCollection.js
new file mode 100644
index 000000000..ab72fc3b5
--- /dev/null
+++ b/wax-prosemirror-plugins/src/MenuService/MenuCollection.js
@@ -0,0 +1,13 @@
+import { injectable, multiInject } from "inversify";
+
+@injectable()
+export default class MenuCollection {
+  menus = [];
+  constructor(@multiInject("Menu") menus) {
+    this.menus = menus;
+  }
+
+  getMenu(name) {
+    return this.menus.find(menu => menu.name === name);
+  }
+}
diff --git a/wax-prosemirror-plugins/src/MenuService/MenuService.js b/wax-prosemirror-plugins/src/MenuService/MenuService.js
new file mode 100644
index 000000000..7afc254cd
--- /dev/null
+++ b/wax-prosemirror-plugins/src/MenuService/MenuService.js
@@ -0,0 +1,50 @@
+import { isPlainObject, isFunction } from "lodash";
+import Menu from "./Menu";
+import MenuCollection from "./MenuCollection";
+import Service from "wax-prosemirror-core/src/services/Service";
+
+export default class MenuService extends Service {
+  name = "MenuService";
+  boot() {
+    const { menus } = this.container.get("MenuCollection");
+    const layout = this.container.get("Layout");
+    menus.forEach(menu => {
+      layout.addComponent(menu.config.templateArea, menu.render());
+    });
+  }
+
+  register() {
+    /* create Menu Factory */
+    this.config.map(conf => {
+      this.container.bind("Menu").toFactory(context => {
+        return new Menu(conf, context.container.get("createTools"));
+      });
+    });
+
+    /*create MenuCollection of Menus */
+    this.container
+      .bind("MenuCollection")
+      .to(MenuCollection)
+      .inSingletonScope();
+
+    /* create factory of tools */
+    this.container.bind("createTools").toFactory(context => {
+      return configTools => {
+        const tools = [];
+        configTools.forEach(tool => {
+          let tl = {};
+          if (isPlainObject(tool)) {
+            tl = context.container.get(tool.name);
+            tl.setGroupConfig(tool);
+          } else if (isFunction(tool)) {
+            tl = context.container.get(tool());
+          } else {
+            tl = context.container.get(tool);
+          }
+          tools.push(tl);
+        });
+        return tools;
+      };
+    });
+  }
+}
diff --git a/wax-prosemirror-plugins/src/MenuService/MenuWrapper.js b/wax-prosemirror-plugins/src/MenuService/MenuWrapper.js
new file mode 100644
index 000000000..6c2bf21b9
--- /dev/null
+++ b/wax-prosemirror-plugins/src/MenuService/MenuWrapper.js
@@ -0,0 +1,20 @@
+import React from "react";
+import styled from "styled-components";
+import { useContext } from "react";
+
+import { map } from "lodash";
+
+const MainMenu = styled.div`
+  background: #fff;
+  padding: 2px 2px 2px 0;
+  position: relative;
+  background: transparent;
+`;
+
+const MainMenuBar = ({ items = [], view }) => (
+  <MainMenu key="MainMenu">
+    {map(items, item => item.renderTools(view))}
+  </MainMenu>
+);
+
+export default MainMenuBar;
diff --git a/wax-prosemirror-plugins/src/RedoUndoService/Redo.js b/wax-prosemirror-plugins/src/RedoUndoService/Redo.js
new file mode 100644
index 000000000..386932ba2
--- /dev/null
+++ b/wax-prosemirror-plugins/src/RedoUndoService/Redo.js
@@ -0,0 +1,18 @@
+import { redo } from "prosemirror-history";
+import Tools from "../lib/Tools";
+import { injectable } from "inversify";
+import { icons } from "wax-prosemirror-components";
+
+@injectable()
+export default class Redo extends Tools {
+  title = "Redo last undone change";
+  content = icons.redo;
+
+  get run() {
+    return redo;
+  }
+
+  get enable() {
+    return redo;
+  }
+}
diff --git a/wax-prosemirror-plugins/src/RedoUndoService/RedoUndo.js b/wax-prosemirror-plugins/src/RedoUndoService/RedoUndo.js
new file mode 100644
index 000000000..7c9242bc7
--- /dev/null
+++ b/wax-prosemirror-plugins/src/RedoUndoService/RedoUndo.js
@@ -0,0 +1,19 @@
+import { injectable, inject } from "inversify";
+import GroupTool from "../lib/GroupTool";
+
+@injectable()
+export default class RedoUndo extends GroupTool {
+  tools = [];
+  constructor(@inject("Redo") redo, @inject("Undo") undo) {
+    super();
+    this.tools = [redo, undo];
+  }
+
+  renderTools(view) {
+    const tools = [];
+    this.tools.forEach(tool => {
+      tools.push(tool.renderTool(view));
+    });
+    return tools;
+  }
+}
diff --git a/wax-prosemirror-plugins/src/RedoUndoService/RedoUndoService.js b/wax-prosemirror-plugins/src/RedoUndoService/RedoUndoService.js
new file mode 100644
index 000000000..477b33392
--- /dev/null
+++ b/wax-prosemirror-plugins/src/RedoUndoService/RedoUndoService.js
@@ -0,0 +1,15 @@
+import RedoUndo from "./RedoUndo";
+import Service from "wax-prosemirror-core/src/services/Service";
+import Redo from "./Redo";
+import Undo from "./Undo";
+
+export default class RedoService extends Service {
+  name = "RedoService";
+
+  register() {
+    this.container.bind("RedoUndo").to(RedoUndo);
+
+    this.container.bind("Redo").to(Redo);
+    this.container.bind("Undo").to(Undo);
+  }
+}
diff --git a/wax-prosemirror-plugins/src/RedoUndoService/Undo.js b/wax-prosemirror-plugins/src/RedoUndoService/Undo.js
new file mode 100644
index 000000000..9d07b6c25
--- /dev/null
+++ b/wax-prosemirror-plugins/src/RedoUndoService/Undo.js
@@ -0,0 +1,18 @@
+import { undo } from "prosemirror-history";
+import Tools from "../lib/Tools";
+import { injectable } from "inversify";
+import { icons } from "wax-prosemirror-components";
+
+@injectable()
+export default class Undo extends Tools {
+  title = "Undo last change";
+  content = icons.undo;
+
+  get run() {
+    return undo;
+  }
+
+  get enable() {
+    return undo;
+  }
+}
diff --git a/wax-prosemirror-plugins/src/lib/GroupTool.js b/wax-prosemirror-plugins/src/lib/GroupTool.js
new file mode 100644
index 000000000..19885aaa4
--- /dev/null
+++ b/wax-prosemirror-plugins/src/lib/GroupTool.js
@@ -0,0 +1,29 @@
+import { injectable } from "inversify";
+
+@injectable()
+export default class GroupTool {
+  _config = {};
+  constructor() {}
+  setGroupConfig(config) {
+    this._config = config;
+  }
+  excludeIncludeTools() {
+    const { exclude = [], include = [] } = this._config;
+
+    if (include.length > 0) {
+      this.tools.map(tool => {
+        if (include.includes(tool.constructor.name)) {
+          tool.enable();
+        } else {
+          tool.disable();
+        }
+      });
+    } else {
+      this.tools.map(tool => {
+        if (exclude.includes(tool.constructor.name)) {
+          tool.disable();
+        }
+      });
+    }
+  }
+}
diff --git a/wax-prosemirror-plugins/src/lib/Tools.js b/wax-prosemirror-plugins/src/lib/Tools.js
new file mode 100644
index 000000000..79eaadb92
--- /dev/null
+++ b/wax-prosemirror-plugins/src/lib/Tools.js
@@ -0,0 +1,48 @@
+import React from "react";
+import { v4 as uuid } from "uuid";
+import { injectable } from "inversify";
+import { Button } from "wax-prosemirror-components";
+
+@injectable()
+export default class Tools {
+  title = "title";
+  content = "content";
+
+  _isEnabled = true;
+
+  get run() {
+    return true;
+  }
+
+  get enable() {
+    return true;
+  }
+
+  select() {
+    return () => true;
+  }
+
+  toJSON() {
+    return {
+      title: this.title,
+      content: this.content,
+      run: this.run,
+      enable: () => this.enable,
+      select: this.select
+    };
+  }
+
+  renderTool(view) {
+    return this._isEnabled ? (
+      <Button key={uuid()} item={this.toJSON()} {...view} />
+    ) : null;
+  }
+
+  disable() {
+    this._isEnabled = false;
+  }
+
+  enable() {
+    this._isEnabled = true;
+  }
+}
-- 
GitLab