Общие правила кастомизации 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:
Добавление опции
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 загружает только содержимое этой папки, но не саму папку.
Вы можете изменить это поведение и предоставить пользователям возможность загружать папки целиком. В этом руководстве мы попытаемся добиться следующего результата:
Загрузками в 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
Наверх