Практические руководства

Общие правила кастомизации File Manager читайте здесь.

Как добавить нижнюю панель

Чтобы добавить нижнюю панель, создайте класс и опишите в нём интерфейс панели и её поведение:

// новые компоненты наследуются от JetView
class BottomBar extends fileManager.views.JetView {
  config() {
    const info = {
      view: "button", value: "Info", width: 100,
      click: () => this.ShowInfo()
    };
    return { view: "toolbar", cols: [info, {}] };
  }
  ShowInfo() {
    webix.message("Custom button clicked");
  }
}

Затем вставьте этот компонент в основной лейаут (fileManager.views.top) в качестве последнего ряда:

class CustomTopView extends fileManager.views.top {
  config() {
    const ui = super.config();
    ui.rows.push(BottomBar);
    return ui;
  }
}

Не забудьте переопределить класс по умолчанию с помощью свойства override:

{
  view: "filemanager",
  url: "https://docs.webix.com/filemanager-backend/",
  override: new Map([[fileManager.views.topbar, CustomTopBar]]),
}

Related sample:  File Manager: Bottom Bar

Одиночный режим отображения

Чтобы показывать определённую вкладку ("cards", "grid", "double") без возможности переключения между ними, скройте компонент Tabbar у класса fileManager.views.topbar:

class CustomTopBar extends fileManager.views.topbar {
  init() {
    // логика по умолчанию
    super.init();
    /* удаление компонента может привести к ошибкам
    скрыть компонент безопаснее, чем удалить его */
    this.$$("modes").hide();
  }
}

После этого замените класс по умолчанию новым классом с помощью свойства override:

{
    view:"filemanager",
    mode: "cards", // показываем любую вкладку (кроме "grid")
    override: new Map([[fileManager.views.topbar, CustomTopBar]]),
}

Related sample:  File Manager: Single Mode

Как добавить дополнительный режим отображения

Чтобы добавить ещё один режим для работы с директориями, вам необходимо:

1. Описать интерфейс.
2. Добавить опцию в кнопку Segmented на тулбаре.
3. Показать вкладку при клике по опции.

Создаём новую вкладку

1. Опишите метод config(), так чтобы он возвращал компонент Treemap.
2. Опишите метод init() для загрузки файлов в Treemap:

// новые компоненты наследуются от JetView
class SpaceChartView extends fileManager.views.JetView {
  config() {
    return {
      view: "treemap",
      localId: "space",
      template: a => a.value,
      value: a => a.size,
    };
  }
}
init(){
   // парсим файлы из текущей директории в Treemap
   const state = this.getParam("state");
   this.on(state.$changes, "path", v => {
       this.app
           .getService("local")
           .files(v)
           .then(fs => this.$$("space").sync(fs));
   });
}

3. Чтобы добавить новый компонент в File Manager, используйте свойство views:

{
    view:"filemanager",
  mode: "space",
  views: { "custom.space": SpaceChartView }
}

Показываем новую вкладку

Прежде всего добавьте ещё одну опцию в кнопку segmented на тулбаре:

class CustomTopBar extends fileManager.views.topbar {
    config(value, old) {
      const ui = super.config();
      const modes = ui.cols[ui.cols.length - 1];
 
      modes.width += 42;
      modes.options.push({
        value: "<span class='space_option'>S</span>",
        id: "space" // id опции
      });
      return ui;
    }
 }

Затем обработайте клик по опции в компоненте fileManager.views.top, переопределив метод ShowMode:

class CustomTop extends fileManager.views.top {
    ShowMode(value, old) {
        const state = this.getParam("state");
 
        if (value === "space") { // обработка лика по опции с ID "space"
            this.Tree.show();
            this.show("custom.space", {
                target: "center",
                params: { state },
            });
        } else {
            super.ShowMode(value, old);
        }
    }
 }

Не забудьте переопределить классы по умолчанию с помощью свойства override:

{
  view:"filemanager"
  override: new Map([
    [fileManager.views.top, CustomTop],
    [fileManager.views.topbar, CustomTopBar],
  ])
}

Related sample:  File Manager: Custom Mode

Как отключить драг-н-дроп

По умолчанию файлы и папки в File Manager можно перетаскивать. Чтобы отключить это поведение, необходимо вернуть false внутри обработчика событий onBeforeDrag.

Чтобы подписаться на это событие, необходимо создать новый класс, унаследовав его от класса по умолчанию ( fileManager.views.list or fileManager.views.cards ).

Отключение ДнД в режиме "Cards"

class MyCards extends fileManager.views.cards {
  init() {
    super.init();
 
    // отключение ДнД
    this.on(this.$$("cards"), "onBeforeDrag", () => false);
  }
}

После этого необходимо заменить класс по умолчанию новым классом:

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

Related sample:  Disabling DnD in grids/cards

Как изменить опции выпадающего меню

Вы можете добавлять, удалять или изменять опции контекстного меню.

Удаление опций

Чтобы убрать опцию из контекстного меню, достаточно вызвать метод remove у этого меню и передать в него ID опции в качестве параметра:

Удаление опции 'upload'

class CustomAddNewMenu extends fileManager.views["menus/addnewmenu"] {
  init(view) {
    // логика по умолчанию
    super.init();
 
    // убирает опцию из контекстного меню
    view.queryView("menu").remove("upload");
  }
}

Добавление опций

Вы можете добавить свою опцию (например "clone") в массив опций по умолчанию.

Маска для фильтрации должна быть добавлена к опции как значение свойства show. Вы можете указать одну маску или объединить несколько с помощью "|".

Следующие маски поддерживаются File Manager и Document Manager:

  • FOLDER - папки (любые папки в дереве и правой части)
  • MANY - deprecated
  • TEXT - для файлов типа код
  • VIEW - для файлов, которые можно открыть в браузере (некоторые аудиофайлы можно скачать)
  • COMPACT - компактный режим
  • ДЕРЕВО - deprecated
  • SEARCH - меню, открываемое при поиске (результаты поиска)
  • EMPTY - deprecated
  • FILE - для файлов
  • SINGLE - меню открывается для 1 элемента (файла или папки)
  • RIGHT - меню открывается справа (элемент или пустое место)
  • ITEMS - файлы и папки в обеих частях

Добавление опции

class CustomMenuBody extends fileManager.views["menus/menubody"] {
  config() {
    const menu = super.config();
    const RIGHT = 0x80; 
        const ITEMS = 0x100;
 
    // добавление опции в конфигурацию JS
    menu.data.push({
      id: "clone", value: "Clone",
      show: RIGHT | ITEMS, icon: "wxi-plus",
    });
    return menu;
  }
}

Затем нужно описать действие при клике на новую опцию:

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

После этого переопределите сервисы и классы по умолчанию с помощью свойства override:

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

Первоначальная сортировка файлов

Вы можете настроить порядок сортировки по умолчанию. Для этого необходимо:

1. Переопределить метод RenderData у компонентов list и cards для сортировки директорий при их открытии.
2. Получить коллекцию файлов этой директории.
3. Отсортировать коллекцию как вам угодно - view отразит эти изменения.

Первоначальная сортировка по названиям

class MyGrid extends fileManager.views.list {
  RenderData(data) {
    super.RenderData(data);
 
    // ждём данных от сервера/из кээша и сортируем их после
    data.waitData.then(() => {
      data.sort(/*..функция для сортировки*/);
      this.$$("table").markSorting("value", dir);
    });
  }
}
 
// и переопределяем RenderData у fileManager.views.cards таким же образом

Related sample:  File Manager: Initial sorting order

Как загрузить локальные данные

Для работы с File Manager наличие backend сервера необязательно. Вы можете работать с локальными данными в поддерживаемом формате.

Вы можете переопределить backend методы по умолчанию с помощью свойства override и вернуть промис с JSON данными.

Загрузка папок

Чтобы передать данные для дерева папок (в левой части экрана), необходимо переписать логику метода folders() у сервиса Backend.

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); 
  }
}

Загрузка файлов

Чтобы загрузить файлы, необходимо переопределить метод files. Метод принимает ID директории в качестве параметра. Вы можете подгружать различные данные в зависимости от директории, в которой находитесь.

Загрузка локальных данных

class MyBackend extends fileManager.services.Backend {
  files(id) { 
    let data = []; 
      if (path == "/") { // корневая директория
      data = [
        {"value":"videos","id":"/videos", "type":"folder"},
          // остальные данные
      ];
    }
    return webix.promise.resolve(data); 
  }
}

Получение метаданных

Вернуть метаданные вашей файловой системы (свободное и использованное место на диске, общая вместимость; в байтах) с помощью метода getInfo:

Получение метаданных

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

Как загружать папки динамически

Предположим, вы не хотите, чтобы директории подгружались все и сразу:

Прежде всего необходимо переопределить метод folders у сервисов Backend и LocalData для того чтобы возвращать только первый уровень директорий. Обратите внимание, что мы иммитируем получение данных для примера. В реальных проектах вам не придётся переопределять сервис Backend для того, чтобы реализовать динамическую загрузку папок.

Переопределение Backend

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

И для сервиса LocalData:

Переопределение LocalData

class MyLocal extends fileManager.services.LocalData{
  folders(force, path) {
    const hierarchy = this.hierarchy;
    // без параметра *path* метод вернёт только первый уровень директорий
    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);
  }
}

После этого можно создать обработчик событий, который будет динамически подгружать данные остальных директорий (при открытии директории):

Обработчик для динамической загрузки

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

Как настроить редактор кода

Настройка редактора (файлы типа "code") следует общим правилам.

В этой секции мы поговорим о том, как заменить редактор по умолчанию CodeMirror на Ace редактор и достичь следующего результата:

Related sample:  File Manager: Customizing code editor

Замена редактора по умолчанию

Для того, чтобы изменить редактор по умолчанию, создайте новый класс, унаследовав его от fileManager.views.editor. Затем опишите конфигурацию нового редактора в методе config() и замените им редактор по умолчанию:

Переопределение конфига для редактора

class Editor extends fileManager.views.editor {
  config() {
    const ui = super.config();
 
    let editor = {  // конфиг для нового редактора
      localId: "editor", // не меняйте локальный ID
      view: "ace-editor",
      theme: "dracula",
    };
    ui.rows[1] = editor; // заменяет редактор по умолчанию новым
 
    return ui;
  }
}

Добавление файлов

Чтобы добавить файлы, используя API другого редактора (в нашем случае - Ace), переопределите метод AddDoc. Создайте новую сессию с помощью new ace.EditSession и сохраните её в буфер редактора. После чего, вам необходимо указать тип файла для редактора:

AddDoc(file, text) {
  this._oldValue[file.id] = text;
 
  // используем api ace 
  this.Buffers[file.id] = new ace.EditSession(text);
  this.Buffers[file.id].setMode(this.GetFileType(file));
}

Открытие файлов

Чтобы показывать содержимое файлов в редакторе, используя API Ace, вам необходимо переопределить метод OpenDoc:

OpenDoc(name) {
  this.$$("editor")
    .getEditor(true)
    .then(editor => {
      // используем api ace 
      editor.setSession(this.Buffers[name]);
      // фокус области редактирования
      editor.focus();
    });
  }

Не забудьте переопределить редактор по умолчанию с помощью свойства override:

Переопределяем редактор по умолчанию

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

К этому моменту у вас должен получится следующий результат:

Добавление контролов

Добавить новые контролы в редактор можно следующими способами:

  • переопределить верхнюю панель инструментов:

Переопределение верхней панели

class Editor extends fileManager.views.editor {
  config() {
    const ui = super.config();
 
    // получает массив элементов панели и добавляет новый
    const toolbarEls = ui.rows[0].cols;
    toolbarEls.push({ /* новый элемент */ });
    return ui;
  }
}
  • создать нижнюю панель управления и добавить контролы туда:

Добавление нижней панели инструментов

class Editor extends fileManager.views.editor {
  config() {
    const ui = super.config();
 
    const bottomBar = {
      view: "toolbar",
      cols: [
          // ... контролы будут здесь
      ],
    };
 
    ui.rows.push(bottomBar);
    return ui;
  }
}

Related sample:  File Manager: Customizing code editor

Так выглядит панель инструментов с новыми контролами:

В добавок ко всему, у вас есть возможность добавить свои стили для редактора или применить тёмную тему Webix к панелям управления и кнопкам.

Как загружать папки

По умолчанию когда пользователь пытается загрузить папку (используя драг-н-дроп) File Manager загружает только содержимое этой папки, но не саму папку.

Вы можете изменить это поведение и предоставить пользователям возможность загружать папки целиком. В этом руководстве мы попытаемся добиться следующего результата:

Добавляем новый Uploader

Загрузками в File Manager занимается сервис Upload. Он использует встроенный компонент uploader, который может работать или только с папками, или только с файлами. Такое поведение нельзя изменить динамически, поэтому необходимо добавить новый загрузчик, который сможет работать с папками.

Создайте новый класс, унаследовав его от класса по умолчанию (fileManager.services.Upload).

class Upload extends fileManager.services.Upload {
  // код нового загрузчика
}

В метод initUploader() добавьте новый загрузчик. В объекте конфигурации загрузчика укажите свойству directory значение true.

// внутри класса Upload 
initUploader(app) {
  super.initUploader(app);
 
  this.dirUploader = webix.ui({
    view: "uploader",
    // разрешаем загружать папки
    directory: true,     // ...
 
    // ... другие свойства
  });
}

Для отображения добавленных папок, необходимо обновлять локальные данные, поэтому добавьте новому загрузчику обработчик, который будет обновлять текущую директорию (ту, в которую пользователь добавил папку).

// внутри нового загрузчика
on: {
  onUploadComplete: function() {
    const local = app.getService("local");
    // обновляем локальные данные
    local.refresh(this.config.tempUrlData.id);
  },
}

Добавляем диалоговое окно для загрузки

Теперь давайте создадим метод folderDialog() и в нём опишем логику, которая будет определять путь для загрузки папки, а также открывать диалог для выбора папки.

// внутри класса Upload
folderDialog(id) {
  this.dirUploader.config.tempUrlData = { id };
  this.dirUploader.fileDialog();
}

Добавляем новую опцию в меню

Чтобы добавить опцию в меню создайте новый класс, унаследовав его от класса по умолчанию fileManager.views["menus/addnewmenu"].

Добавляем новую опцию

class CustomAddNewMenu extends fileManager.views["menus/addnewmenu"] {
  config() {
    // исходный UI
    const ui = super.config();
    // добавляем опцию "Upload folder"
    const menu = ui.body;
    menu.data.push({
      id: "uploaddir",
      value: "Upload folder",
      icon: "webix_fmanager_icon mdi mdi-folder-upload-outline",
    });
    return ui;
  }
}

Опция добавится вот сюда:

После того, как вы добавили новую опцию, необходимо описать действия при выборе этой опции. Внутри метода initEvents добавьте обработчик, который будет реагировать на событие, если параметр name равен "uploaddir".

// внутри класса Upload 
initEvents(app, state) {
  super.initEvents(app, state);
 
  app.attachEvent("app:action", (name, info) => {
    if (name == "uploaddir") {
      info = info || (state.path || "/");
      app.getService("upload").folderDialog(info);
    }
  });
}

Логика для загрузки папок через меню добавлена, и теперь для корректной поддержки загрузки папок через Д-н-Д необходимо переопределить метод getUploader():

// внутри класса Upload
getUploader() {
  return this.dirUploader;
}

Обо всём остальном позаботятся Webix и File Manager.

И, конечно, не забудьте переопределить исходный компонент и сервис с помощью свойства override:

new fileManager.App({
  override: new Map([
    [fileManager.views["menus/addnewmenu"], CustomAddNewMenu],
    [fileManager.services.Upload, Upload]
  ])
});

Related sample:  File Manager: Uploading folders

Наверх