Customizing the right click menu in Document Manager, ReactJS

We are using the Webix Document Manager in our application and want to customize the right click menu, but haven’t been able to figure out how to do so.

Here is how we load the document manager into our react component:

class FileManager extends Component {
  constructor(props) {
    super(props);

    this.scriptSrcElements = [];
    this.timeouts = [];
  }

  componentDidMount() {
    const scriptSrcs = [
      {
        src: `/external-lib/webix/webix/webix.js`
      },
      {
        src: `/external-lib/webix/docmanager.js`,
        onload: this.webixScriptLoaded,
      },
    ];

    scriptSrcs.forEach((scriptSrc) => {
      const script = document.createElement('script');
      script.src = scriptSrc.src;
      script.async = false;
      script.onload = scriptSrc.onload ? scriptSrc.onload : () => {};
      script.onerror = (error) => {
        console.log(error);
      };
      document.body.appendChild(script);
      this.scriptSrcElements.push(script);
    });
  }

  componentWillUnmount() {
    this.scriptSrcElements.forEach((script) => {
      document.body.removeChild(script);
    });
    if (this.ui) {
      this.ui.destructor();
      this.ui = null;
      for (const to of this.timeouts) {
        window.clearTimeout(to);
      }
    }
  }

  webixScriptLoaded = () => {
    const self = this;

      webix.ready(function () {
        self.ui = webix.ui({
          view: 'docmanager',
          url: `${baseURL}/files/`,
          editor: false,
          container: 'webix-container',
          id: 'webix-filemanager',
          css: 'webix-filemanager',
          minHeight: 600,
          override: new Map([[docManager.services.Backend, Backend]]),
          on: {
            onInit: (app) => {
              const state = app.getState();

              // Overrides
              const uploader = app.getService('upload').getUploader();
              if (uploader) {
                uploader.attachEvent('onAfterFileAdd', self.onAfterFileAdd);
              }
              app.getService('operations').download = self.onDownload;
              app.getService('operations').open = self.onOpen;
            },
          },
          onContext: {},
        });

        // We use this event to handle overrides where we modify the ajax call
        webix.attachEvent('onBeforeAjax', self.onBeforeAjax);

        // This responds to window resize and scales the webix layout appropriately
        webix.event(window, 'resize', function () {
          self.ui.resize();
        });
      });
  };

  render() {
    return (
      <div>
        <Helmet>
          <link
            rel="stylesheet"
            href={`/external-lib/webix/webix/webix.css`}
          />
          <link
            rel="stylesheet"
            href={`/external-lib/webix/docmanager.css`}
          />
        </Helmet>
        <div id="webix-container" className="webix-container" />
      </div>
    );
  }
}

I have tried all the ways to attach in the Context Menu documentation

The closest I was able to get was to add this right after the docmanager webix.ui:

webix.ui({
          view: 'contextmenu',
          id: 'customContextMenu',
          data: ['Custom'],
          master: 'webix-container',
        });

This adds a custom right click menu in the sidebar and top bar, but not when clicking files/folders. Changing the master to ‘webix-filemanager’ does not work.

Any help is appreciated!

We have a demo of using complex widgets with React. It’s a bit different in the way DM files are imported webix-react-complex-widgets/FilesView.js at main · HelgaListopad/webix-react-complex-widgets · GitHub (here there’s example with FM, but they are very similar)

To customize the context menu in any way you need to get to the object that’s exported from docmanager.js.
In our demo, that would be:

const dm = require("@xbs/docmanager");

Next your strategy would depend on what you want to customize in that menu. All parts of DM are in separate classes.
The body of the menu - its options, the way they are shown for this or that type of file, is in menubody.js, so to redefine it extend its class:

class menu extends dm.views["menu/menubody"] {
   // ...  redefine methods here
  // config() {} - redefine menu config
  // FilterOptions(files) {} - redefine the way these options are shown
}

If you want to change something that concerns the way the whole menu is shown (where it’s shown, for which files it’s shown, etc), redefine contextmenu.js:

class context extends dm.views["menu/contextmenu"] {
  /*  methods here  */
}

If you want to remove the default menu and replace with your own, then go to list.js and cards.js and make identical changes to these classes:

class list extends dm.views.list  {
  init(){
    super.init();

    // here you can remove the default menu, which is stored in the class as this.Menu
    // init your own and attach it to the grid or dataview, which are stored as this.WTable 
  }
}

Also the same menu is attached to the file tree, class folders.js. So you will need to make similar changes there.

Also you need to add all redefined classes to “override” in DM config, e.g:

this.ui = webix.ui({ 
     view: "docmanager",
     url: "https://docs.webix.com/docmanager-backend/",
     container: ReactDOM.findDOMNode(this.uiContainer.current),
     override: new Map([
        [dm.views.list, list]
    ])
});
1 Like

Thank you! I will test this today and either close or come back with more questions.

1 Like

@kuro Thank you so much! I was finally able to get this working successfully.

One thing I have been looking through the docs but haven’t been able to find - is there a way to update the menubody only on folders? I am trying to have the right click menu show “Download folder” when a user clicks on a folder item, but do not want it to show when a user clicks on a file directly.

I’m glad it helped!

This is how: Code Snippet
Redefine docManager.views["menus/menubody"] and docManager.services.Operations
In the first class you will have to add the option itself and make it visible only for folders. (The inner logic of option filtering has become pretty complex by now, so this is the way I managed to do this)
And in Operations you will need to write the logic for this option.

1 Like