НОВОСТИ [Из песочницы] Как встроить ColorPicker в JavaScript Гант для изменения цвета задач

Bonnie
Оффлайн
Регистрация
12.04.17
Сообщения
19.095
Реакции
107
Репутация
0
frzkdx2ofotkygeyebeieime0h8.jpeg



Привет, меня зовут Женя, и я просто еще один из обитателей JavaScript вселенной, который хочет поделиться с вами интересным опытом в frontend-разработке, а именно как кастомизировать диаграмму Ганта.


Не так давно я присоединился к команде энтузиастов, которая работает над проектом по производству оборудования для ремонта автомобилей. Как и для любого проектного менеджмента, нам нужен был инструмент для планирования задач с следующим функционалом:

  • оптимальное распределение ресурсов и нагрузки между сотрудниками
  • мониторинг выполнения задач
  • оценка эффективности и временных рамок проекта


Во многих современных приложениях для управления проектами для решения таких задач используются . И я взялся за реализацию ее функциональности в нашем приложении.


Хотя можно было бы использовать готовый софт, в нашем случае нужно было сильно кастомизировать Гант под нужды проекта. Альтернатива разработки диаграммы с нуля — это слишком накладное и времязатратное мероприятие. Посовещавшись с коллегами, мы решили, что лучше всего найти готовый компонент среди библиотек JavaScript и настроить его под наш проект.



Основная проблема диаграмм Ганта заключается в том, что с большим количеством данных они становятся громоздкими и трудными для восприятия и управления. При поиске подходящей библиотеки мы выделили три основных критерия:

  • простой для понимания интерфейс
  • богатые возможности кастомизации
  • ну и, само собой, хорошая производительность


Один из иностранных коллег уберег меня от долгих скитаний по просторам интернета в поисках необходимого виджета и подсказал JavaScript библиотеку . Ознакомившись с функциональными возможностями продукта, я решил, что стоит попробовать — библиотека удовлетворила наши требованиям по настройке диаграммы и оказалась простой в использовании.


Сама библиотека доступна в двух версиях — бесплатной и платной:

  • в бесплатной нет некоторых фич и есть GPL v2 лицензия;
  • а платная стоит от 700$ до 3000$ в зависимости от условий использования.


Для нашего проекта хватает фич бесплатной версии — ее мы и решили использовать.

Про выбор лицензии
Насколько я понял, загвоздка с GPL лицензиями и GPLv2 конкретно в том, что они требуют раскрывать исходный код ( ). При этом не очень понятно, какой именно код веб приложения надо открывать — все приложение/только front-end/только код, который взаимодействует с библиотекой. Но так как наш проект мы делаем для внутренних нужд и не планируем никому распространять, как я понимаю, нас требование раскрытия кода не коснется. Если мы когда-нибудь решим продавать наше приложение другим предприятиям, тогда придется купить платную лицензию.



В этой статье я хочу поделиться с вами несколькими приемами работы с DHTMLX JavaScript Гантом. Я не стану расписывать все детали по настройке Ганта, но расскажу о наиболее интересных фишках, которые пригодились мне в работе.


Для проекта мне нужно было предоставить возможность пользователям обозначать задачи на диаграмме Ганта разными цветами прямо из интерфейса.


kvd6rmvek5m2ocz8ydn0jpi_yw0.png



В DHTMLX Gantt контент можно редактировать двумя способами:

  • вызвав форму редактирования ( )
  • воспользовавшись встроенными редакторами в области таблицы ( ), когда пользователь может редактировать данные с помощью горячих клавиш


Второй вариант редактирования задач как раз оказался тем, что мне нужно. Используя DHTMLX Gantt, я без труда смог реализовать свой тип редактора для выбора цвета — color picker, позволяющий присваивать цвет задачам проекта. Далее я подробно расскажу о реализации этой функциональности.

Инициализация



Пример я покажу на простом HTML5 и JavaScript, чтобы не требовалась сборка и было максимально понятно и без лишних сложностей.


Для начала нужно инициализировать Гант на странице. Для этого нужно подключить JS и CSS файлы библиотеки из коробки, а затем создать контейнер, в котором инициализировать Гант (подробная инструкция ):


DOCTYPE html>












Прямо в HTML файле или же отдельно в js файле, после инициализации Ганта, нужно добавить данные, на основании которых Гант построит диаграмму. Для примера я добавлю тут 1 проект (Открытие производства оборудования) и 2 вложенные в него задачи (Определение рынка сбыта и Определение маркетинговой стратегии). Тут также задаются даты начала задач, их длительность, порядок выполнения и прогресс (степень выполнения задач):


gantt.parse({
data: [
{
id: 1, text: "Открытие производства оборудования", start_date: "01-05-2020", duration: 18, open: true
},
{
id: 2, text: "Определение рынка сбыта", start_date: "02-05-2020", duration: 4, parent: 1
},
{
id: 3, text: "Определение маркетинговой стратегии", start_date: "07-05-2020", duration: 5, parent: 1
}
],
links: [
{id: 1, source: 1, target: 2, type: "1"},
{id: 2, source: 2, target: 3, type: "0"}
]
});
});


d1dzm06ccsnalabjtjxbsjueoey.png



Это самая простая часть.

Добавление Inline Editors



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


Колонку можно сделать редактируемой, добавив к ней свойство editor:


gantt.config.columns = [
{name: "text", tree: true, width: '*', resize: true, editor: textEditor},
{name: "start_date", align: "center", resize: true, editor: dateEditor},
{name: "duration", align: "center", editor: durationEditor},
{name: "add", width: 44}
];


Объект редактора должен иметь свойство type, которое соответствует нужному типу редактора, и свойство map_to, которое определяет свойство объекта задачи, в которое редактор будет сохранять значения. Например, так настраивается редактор для полей с текстом, датами и длительностями задач:


const textEditor = {type: "text", map_to: "text"};
const dateEditor = {type: "date", map_to: "start_date", min: new Date(2020, 0, 1),
max: new Date(2021, 0, 1)};
const durationEditor = {type: "number", map_to: "duration", min:0, max: 100};


Остальные настройки относятся к определенным типам редакторов.

Создаем Свой Собственный Редактор



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


Встроенного редактора для выбора цвета в библиотеке нет, поэтому вариантов у нас немного: использовать выпадающий список ( ) или сделать новый редактор с селектором цвета (color picker). Я решил пойти по второму пути, то есть создать редактор-colorpicker.


Сначала я покажу на примере, как встроить простой HTML5 элемент формы input с типом color, чтобы выбирать цвет задачи.


Для создания собственного редактора нужно добавить новый объект редактора в конфигурацию Ганта. Я сделал это по шаблону из :


gantt.config.editor_types.custom_editor = {
show: (id, column, config, placeholder) => {
// called when input is displayed, put html markup of the editor into placeholder

// and initialize your editor if needed:
placeholder.innerHTML ``;
},

hide: () => {
// called when input is hidden
// destroy any complex editors or detach event listeners from here
},

set_value: (value, id, column, node) => {
// set input value
},

get_value: (id, column, node) => {
// return input value
},

is_changed: (value, id, column, node) => {
// called before save/close. Return true if new value differs from the original one
// returning true will trigger saving changes, returning false will skip saving
},

is_valid: (value, id, column, node) => {
// validate, changes will be discarded if the method returns false
return true/false;
},

save: (id, column, node) => {
// only for inputs with map_to:auto. complex save behavior goes here
},

focus: (node) => {
}
}


Чтобы создать свой редактор, я внес следующие изменения в код шаблона:


  1. Первым делом я изменил название моего редактора и поменял функцию show, чтобы изменить тип вводимых данных на color:


    gantt.config.editor_types.color = {
    show: (id, column, config, placeholder) => {
    placeholder.innerHTML = ``;
    },


  2. Метод hide мне не понадобился, поскольку элементу выбора цвета не требуется никаких деструкторов или постобработки после того, как он становится скрытым, поэтому я оставил его пустым:


    hide: () => {},


  3. Далее — методы set_value и get_value:


    set_value: (value, id, column, node) => {
    const input = node.querySelector("input");
    input.value = value
    },
    get_value: (id, column, node) => {
    const input = node.querySelector("input");
    return input.value;
    },


    Первый метод вызывается при открытии редактора, чтобы установить значение из объекта task. Второй метод вызывается, когда пользователь сохраняет редактор, а возвращаемое значение применяется к объекту задачи.


  4. Следующая на очереди функция is_changed. Поскольку редакторы можно легко открывать и закрывать, я задал инициирование изменения данных только тогда, когда пользователь фактически меняет значение редактора:


    is_changed: (value, id, column, node) => {
    const input = node.querySelector("input");
    return input.value !== value;
    },


    Внутри этого метода сравнивается исходное значение, определенное для редактора, с текущим значением и возвращается логическое значение true, если показатели различаются. Значение true обновит задачу новым значением, а false просто закроет редактор.


  5. Принцип работы метода is_valid полностью соответствует своему названию и, возвращая false, сообщает Ганту, что введенное значение недопустимо и его необходимо сбросить:


    is_valid: (value, id, column, node) => {
    const input = node.querySelector("input");
    return !!input.value;
    },


  6. Метод save мне не понадобился, так как он необходим для сложных редакторов, которые выполняют несколько изменений сразу, а не изменяют одно свойство задачи.


  7. Использование метода focus помогло поместить фокус окна браузера в редактор:


    focus: node => {
    const input = node.querySelector("input");
    input.focus();
    },


    В итоге я получил свой собственный редактор для выбора цвета задачи в Ганте.

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



Далее мне нужно было добавить этот редактор в мой Гантт.
Для этого я добавил новый столбец к конфигу таблицы и привязал к нему конфиг редактора. Свойство type редактора цвета должно соответствовать значению для редактора, которое я использовал выше (в моем случае type: “color”).


const textEditor = {type: "text", map_to: "text"};
const dateEditor = {type: "date", map_to: "start_date", min: new Date(2020, 0, 1),
max: new Date(2021, 0, 1)};
const durationEditor = {type: "number", map_to: "duration", min:0, max: 100};
const colorEditor = {type: "color", map_to: "color"};
gantt.config.columns = [
{name: "text", tree: true, width: '*', resize: true, editor: textEditor},
{name: "start_date", align: "center", resize: true, editor: dateEditor},
{name: "duration", align: "center", editor: durationEditor},
{name: "color", align: "center", editor: colorEditor},
{name: "add", width: 44}
];


Я использовал свойство color, поскольку Гант автоматически применит цвета из этого свойства. Теперь можно посмотреть, как все работает, если добавить к задачам значение цвета — например, color:"#FF0000":


{
id: 2, text: "Определение рынка сбыта", start_date: "02-05-2020", duration: 4, parent: 1, color:"#FF0000"
},


gcy210mjifptxgi5ibydectwkuy.png



В качестве последнего штриха, я сделал так, чтобы цвета внутри столбца “color” отображались красиво. Для этого я использовал шаблон:


gantt.config.columns = [
{name: "text", tree: true, width: '*', resize: true, editor: textEditor},
{name: "start_date", align: "center", resize: true, editor: dateEditor},
{name: "duration", align: "center", editor: durationEditor},
{name: "color", align: "center", label:"Color", editor: colorEditor, template:
(task) => {
return ``
}
},
{name: "add", width: 44}
];


Я задал шаблон, который будет возвращать элемент контейнера div с указанным в стилях цветом фона. В файле css я добавил стили, чтобы красиво отображать цвет в контейнере:


.task-color-cell{
margin:10%;
width:20px;
height:20px;
border:1px solid #cecece;
display:inline-block;
border-radius:20px;
}


st__cnodfivfzcvqnf4-urdafho.png



Реализованный пример с кодом можно посмотреть по ссылке:

Использование готового Color Picker виджета в редакторе



Поскольку в DHTMLX Gantt изменять цвета можно только через селект, то я решил использовать более гибкий способ для обозначения задач цветом, а именно интегрировать в Гант плагин jquery под названием .


Первым шагом я добавил файлы библиотеки в Гант:


DOCTYPE html>
















После этого обновил контрол Ганта. Я задал переменную let editor, где будет храниться ссылка на наш редактор. Это необходимо, чтобы вызвать деструктор, когда элемент input будет скрыт.
Сначала я внес изменения в метод show. Когда он вызывается, необходимо инициализировать и отобразить виджет селектора цвета.


На этом месте возникла неожиданная сложность: если вызывать editor.spectrum("show") внутри метода show, редактор не появляется. Видимо, в момент вызова show placeholder-элемент еще не добавлен в документ и у него нет размеров и позиции. В итоге я просто добавил минимальный тайм-аут, чтобы запускать color picker уже после того, как метод завершился и placeholder висит над нужным местом в таблице.


document.addEventListener("DOMContentLoaded", function(event) {
let editor;
gantt.config.editor_types.color = {
show: (id, column, config, placeholder) => {
placeholder.innerHTML = ``;

editor = $(placeholder).find("input").spectrum({
change:() => {
gantt.ext.inlineEditors.save();
}
});
setTimeout(() => {
editor.spectrum("show");
})
}


Далее я определил метод “hide” — деструктор будет вызван, когда редактор будет закрыт:


hide: () => {
if(editor){
editor.spectrum("destroy");
editor = null;
}


Остальные методы не слишком отличаются от первоначальной реализации. Просто нужно изменить способ получения значений из контрола:


set_value: (value, id, column, node) => {
editor.spectrum("set", value);
},
get_value: (id, column, node) => {
return editor.spectrum("get").toHexString();
},
is_changed: function (value, id, column, node) {
const newValue = this.get_value(id, column, node);
return newValue !== value;
},
is_valid: function (value, id, column, node) {
const newValue = this.get_value(id, column, node);
return !!newValue;
},
focus:(node) => {
editor.spectrum("show");
}


l53_zntwakqepyr0eqf97owbj3q.png



После этого все должно работать как положено!


.


Надеюсь, что эта статья поможет вам в настройке цвета задач в диаграмме Ганта.
 
Сверху Снизу