How-tos

Disabling drag-n-drop

You can disable default DnD behaviour by returning false in the handler of the onBeforeDrag event for the datatable and dataview components used in grid and cards views.

To listen to this event, you need to to define a new custom class and inherit it from the default class ( fileManager.views.list or fileManager.views.cards ).

Disabling dnd in Cards view

class MyCards extends fileManager.views.cards {
  init() {
    super.init();
 
    // disable dnd
    this.on(this.$$("cards"), "onBeforeDrag", () => false);
  }
}

After that, you need to tell the component that you are going to override the default class with a custom one:

{
  view: "filemanager",
  override: new Map([
    [fileManager.views.cards, MyCards]
  ])
};

Related sample:  Disabling DnD in grids/cards

Changing context menu options

You can add, remove or modify context menu options.

Removing options

You can remove an option from the context menu by calling its remove method with the ID of the unnecessary option as a parameter:

Removing 'upload' option

 class CustomAddNewMenu extends fileManager.views["menus/addnewmenu"] {
  init(view) {
    // default logic
    super.init();
 
    // remove an option from the created view 
    view.queryView("menu").remove("upload");
  }
}

Adding options

Also, you can add a custom option (e.g. "clone") to the array of default options:

Adding a custom option

class CustomMenuBody extends fileManager.views["menus/menubody"] {
  config() {
    const menu = super.config();
    const mode = 0x01 | 0x20;
 
    //add option to the JS config
    menu.data.push({
      id: "clone", value: "Clone",
      show: mode, icon: "wxi-plus",
    });
    return menu;
  }
}

For the new option you need to provide an action to perform when clicking on it:

class Operations extends fileManager.services.Operations {
  initEvents() {
    super.initEvents();
 
    this.app.attachEvent("app:action", (name, info) => {
      if (name === "clone") {
        this.addToClipboard("copy");
        this.paste();
      }
    });
  }
}

Related sample:  File Manager: Customizing menus

Finally don't forget to tell the component that you are going to override its default views and services:

{
  view: "filemanager",
  override: new Map([
    [fileManager.views["menus/menubody"], CustomMenuBody],
    [fileManager.views["menus/addnewmenu"], CustomAddNewMenu],
    [fileManager.services.Operations, Operations]
  ])
}

Sorting files initially

You can provide the default sorting order. To achieve it, you need to:

  • listen to the changes in path to sort each directory you are entering;
  • get the related collection of files for this path;
  • sort the collection whichever you like - the view will reflect these changes.

Initial descending order by title

init(){
  super.init();
  const data = this.app.getService("local");
  //listen to changes in path
  this.app.getState().$observe("path", (path)=>{
    // get files and sort them
    data.files(path).then((fs)=>{
      fs.sort(/*..custom sorting function*/);
      this.$$("table").markSorting("value", dir);
    });
  })
}

Related sample:  File Manager: Initial sorting order

Loading local data

To work with File Manager it isn't necessary to have a backend server i.e. you can work with local data provided in the supported format.

You can override default backend methods and return a promise with JSON data.

Loading folders

You can provide data for a folder tree (left side) by changing the folders method logic of the Backend service.

class MyBackend extends fileManager.services.Backend {
  folders() {
    const data = [
      {"value":"webinars","id":"/webinars","size":0,"date":1580820822,
      "type":"folder","data":[
        {"value":"lections","id":"/webinars/lections","size":0,
        "date":1581677679,"type":"folder"},
      ]}
    ];
 
    return webix.promise.resolve(data); 
  }
}

Loading files

To load files, override the files method. It takes the ID of a directory as a parameter. You can load various data depending on the directory you are in.

Loading local data

class MyBackend extends fileManager.services.Backend {
  files(id) { 
    let data = []; 
    if(path =="/") { // root directory
      data = [
          {"value":"videos","id":"/videos", "type":"folder"},
          // other data 
      ]
    };
    return webix.promise.resolve(data); 
  }
}

Returning meta data

You can return meta data of your file system (total, free and used space; in bytes) in the getInfo method:

Returning file system data

class MyBackend extends fileManager.services.Backend {
  getInfo() {
    return webix.promise.resolve({
      stats: { 
        free: 52 * 1000 * 1000,
        total: 250 * 1000 * 1000,
        used: 198.4 * 1000 * 1000,
      }
    });
  }
}

Related sample:  File Manager: Load Local Data

Loading folders dynamically

Let's suppose you don't want to have all your directories loaded at once:

Firstly, you need to override default folders methods in Backend and LocalData services to return only the first level of directories:

Overriding Backend

class MyBackend extends fileManager.services.Backend {
  folders() {
    let data = [];
      data = totalFolders.serialize().map(f => {
        if (f.data) delete f.data;
        return f;
      });
    } 
    return webix.promise.resolve(data);
  }
}

And for the LocalData:

Overriding LocalData

class MyLocal extends fileManager.services.LocalData{
  folders(force, path) {
    const hierarchy = this.hierarchy;
    // the method without *path* parameter will return only the first level 
    if ((force || !this.folders_ready) && !path) {
      return this.app.getService("backend")
        .folders()
        .then(data => {
        hierarchy.parse({ parent: "../files", data });
        return hierarchy;
      });
    }
 
    return Promise.resolve(hierarchy);
  }
}

After that you can create an event handler that will load the contents of directories dynamically (after opening a directory):

Handler on dynamic loading

class MyTree extends fileManager.views.folders{
  init(){
    super.init();
 
    this.on(this.Tree, "onAfterSelect", id => {
      if (id !== "../files")
        this.app.getService("local").folders(false, id);
    });
  }
}

Related sample:  File Manager: Dynamically loaded folders

Customizing code editor

Customizing editor (files with type: "code") follows the common rules.

In this how-to we'll try to achieve the following result using the Ace editor instead of CodeMirror, which is used by default:

Related sample:  File Manager: Customizing code editor

Replacing default editor

You need to define a new custom class and inherit it from the default fileManager.views.editor one. Inside the config() method we set the configuration for the new editor and replace the default one:

Overriding editor config

class Editor extends fileManager.views.editor {
  config() {
    const ui = super.config();
    const fileType = this.GetFileType(this.Name);
 
    let editor = {  // config for the editor
      localId: "editor", //keep this localId
      view: "ace-editor",
      mode: fileType,
      theme: "dracula",
    };
    ui.rows[1] = editor; // replace the default editor with a new one
 
    return ui;
  }
}

Don't forget to override the default Editor view with the new one:

Overriding default editor

 var app = new fileManager.App({
    override: new Map([[fileManager.views.editor, Editor]])
  });

By this moment you already have something like this:

To be able to track changes made by users, you need to listen to the change event of the editor and call the textChanged() method with the editor as an argument:

Overriding init logic

class Editor extends fileManager.views.editor {
 //... 
 init() {
    super.init();
    // Ace has a different way to track changes
    const editor = this.$$("editor");
    editor.getEditor(true).then(editorObj => {
      this.Editor = editorObj;
      editorObj.session.on("change", delta => {
        this.TextChanged(editor); 
      });                        
    });
  }
}

Now our editor can track changes, and 'saved/unsaved' states will be reflected by the icons in the 'Save' button.

Adding controls

You can add controls to the editor. This can be done by:

  • Either overriding the default top bar:

Overriding default top bar

class Editor extends fileManager.views.editor {
  config() {
    const ui = super.config();
 
    // get the array of toolbar controls and add new ones here
    const toolbarEls = ui.rows[0].cols;
    toolbarEls.push({ /* some control here */ });
    return ui;
  }
}
  • Or creating a bottom toolbar and adding controls there:

Adding bottom toolbar

class Editor extends fileManager.views.editor {
  config() {
      const ui = super.config();
 
      const bottomBar = {
          view: "toolbar",
          cols: [
             // ... toolbar controls will be here
          ],
      };
 
      ui.rows.push(bottomBar);
      return ui;
  }
}

Related sample:  File Manager: Customizing code editor

This is how a bottom toolbar with controls may look like:

Additionally, you can apply our dark theme to the toolbars and buttons or add you own styles.

Back to top