feat: Integrate JS Gantt Chart library and update styles
- Added JS Gantt Chart library files including CSS and JS. - Updated existing Gantt chart styles for better visual representation. - Translated month and day names to Polish in the Gantt chart settings. - Implemented Gantt chart initialization in the main view with sample data. - Added a toggle switch for displaying tasks in the calendar. - Enhanced task edit form with a new checkbox for calendar visibility. - Improved the layout of the logged-in user template to include Gantt chart styles and scripts.
This commit is contained in:
2
libraries/JS-Gantt-Chart-master/.gitattributes
vendored
Normal file
2
libraries/JS-Gantt-Chart-master/.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
21
libraries/JS-Gantt-Chart-master/LICENSE
Normal file
21
libraries/JS-Gantt-Chart-master/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 mmacdonald
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
29
libraries/JS-Gantt-Chart-master/README.md
Normal file
29
libraries/JS-Gantt-Chart-master/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# JS-Gantt-Chart
|
||||
A Gantt Chart built using pure JS with CSS Grid and Flexbox
|
||||
|
||||
### At the moment the chart works best with items that are all on the same day.
|
||||
|
||||
The chart's constructor expects two parameters:
|
||||
|
||||
1. The id for the element that will hold the chart. A string
|
||||
2. An object holding a number of parameters for the chart:
|
||||
```
|
||||
noDataFoundMessage: If the chart refreshes and does not receive any data, this message is displayed. A string or HTML
|
||||
startTimeAlias: The name of the property within the objects of the array that holds the event's start time. A string
|
||||
endTimeAlias: The name of the property within the objects of the array that holds the event's end time. A string
|
||||
idAlias: The name of the property within the objects of the array that holds the id of the record.
|
||||
Objects with the same id will be placed in the same row. An int
|
||||
rowAlias: The name of the property within the objects of the array that holds the header that should be given to this object's row. A string
|
||||
linkAlias: The name of the property within the objects of the array that holds the link that should be followed when the object's entry is clicked.
|
||||
Passing in null will keep the chart from rendering links. A string
|
||||
tooltipAlias: The name of the property within the objects of the array that holds the html content that should be displayed when the user hovers over the chart entry. A string
|
||||
groupBy: This parameter functions like a SQL group by. Providing it to the chart will result in rows being grouped underneath collapsible headers.
|
||||
The values should be in order of decreasing importance.
|
||||
Each of the values in the string should be a property within the objects being provided to the chart.
|
||||
ex. 'state, county, city'
|
||||
groupByAlias: A comma delimited list of the properties in the given data that hold the names of each group. Should have the same number of entries as the groupBy parameter and will only be used if that parameter has also been provided. This is an optional parameter. If left null, the group names will be derived from the groupBy parameter instead.
|
||||
refreshFunction: The function that the chart should call when its refreshData() function is invoked. This function should return an array of objects.
|
||||
```
|
||||
|
||||
|
||||
The [sample-chart](sample-chart) has a barebones example of how the chart could be created.
|
||||
106
libraries/JS-Gantt-Chart-master/gantt.css
Normal file
106
libraries/JS-Gantt-Chart-master/gantt.css
Normal file
@@ -0,0 +1,106 @@
|
||||
.gfb-gantt-wrapper {
|
||||
--entry-color: #033b6a;
|
||||
--border-color: rgba(0, 0, 0, 0.1);
|
||||
--background-color: #f9f9f9;
|
||||
--hovered-background: #f0f0f0;
|
||||
}
|
||||
|
||||
.gfb-gantt-wrapper {
|
||||
position: relative;
|
||||
min-width: 550px;
|
||||
}
|
||||
|
||||
.gfb-gantt-headers {
|
||||
display: grid;
|
||||
background-color: var(--background-color);
|
||||
border: 1px solid var(--border-color);
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.gfb-gantt-headers>div:not(:last-child) {
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.gfb-gantt-header {
|
||||
display: flex;
|
||||
padding: 5px 0px 5px 5px;
|
||||
}
|
||||
|
||||
.gfb-gantt-sidebar-header {
|
||||
text-align: center;
|
||||
padding: 10px 5px 10px 5px;
|
||||
background-color: var(--background-color);
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.gfb-gantt-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.gfb-gantt-row {
|
||||
display: grid;
|
||||
min-height: 25px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-top: 0;
|
||||
border-right: 1px solid transparent;
|
||||
|
||||
}
|
||||
|
||||
.gfb-gantt-sub-row-wrapper {
|
||||
max-height: 15px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.gfb-gantt-row-entry {
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
background-color: var(--entry-color);
|
||||
}
|
||||
|
||||
.gfb-gantt-row-entry:hover {
|
||||
box-shadow: var(--entry-color) 0px 0px 2px .1px;
|
||||
}
|
||||
|
||||
.gfb-gantt-lines-container {
|
||||
display: grid;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border: 1px solid var(--border-color);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.gfb-gantt-lines-container>div:not(:last-child):not(.gfb-gantt-sidebar-template) {
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.gfb-gantt-row-entry-tooltip {
|
||||
position: absolute;
|
||||
display: none;
|
||||
max-width: 400px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 0 5px #aaa;
|
||||
padding: 10px;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.gfb-gantt-grouping-header {
|
||||
background-color: var(--background-color);
|
||||
padding: 5px 0;
|
||||
border: 1px solid var(--border-color);
|
||||
border-top: 0 transparent;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.gfb-gantt-grouping-header:hover {
|
||||
background-color: var(--hovered-background);
|
||||
}
|
||||
|
||||
.gfb-gantt {
|
||||
overflow: auto;
|
||||
}
|
||||
399
libraries/JS-Gantt-Chart-master/gantt.js
Normal file
399
libraries/JS-Gantt-Chart-master/gantt.js
Normal file
@@ -0,0 +1,399 @@
|
||||
class Gantt {
|
||||
constructor(staticID, params) {
|
||||
this.staticID = staticID;
|
||||
this.sidebarHeader = params.sidebarHeader || 'Unused parameter right now';
|
||||
this.noDataFoundMessage = params.noDataFoundMessage || 'No data found.';
|
||||
this.startTimeAlias = params.startTimeAlias || 'startTime';
|
||||
this.endTimeAlias = params.endTimeAlias || 'endTime';
|
||||
this.idAlias = params.idAlias || 'id';
|
||||
this.rowAlias = params.rowAlias || 'rowTitle';
|
||||
this.linkAlias = params.linkAlias;
|
||||
this.tooltipAlias = params.tooltipAlias || 'tooltip';
|
||||
this.groupBy = params.groupBy ? params.groupBy.split(',').map(group => group.trim()) : [];
|
||||
this.groupByAlias = this.groupBy ? (params.groupByAlias ? params.groupByAlias.split(',').map(group => group.trim()) : this.groupBy) : [];
|
||||
this.data = {};
|
||||
this.rawData = [];
|
||||
this.divisionCount = 0;
|
||||
this.maxTime;
|
||||
this.minTime;
|
||||
this.refreshFunction = params.refreshFunction;
|
||||
|
||||
this.wrapper = document.createElement('div');
|
||||
this.wrapper.classList.add('gfb-gantt-wrapper');
|
||||
document.getElementById(this.staticID).appendChild(this.wrapper);
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
refreshData() {
|
||||
this.rawData = this.refreshFunction();
|
||||
this.empty();
|
||||
if(this.rawData.length < 1){
|
||||
this.noDataFound();
|
||||
return;
|
||||
}
|
||||
this.processData();
|
||||
this.render();
|
||||
}
|
||||
|
||||
processData() {
|
||||
//Have to reset the divisionCount here. Otherwise it keeps incrementing on the divisionCount of the previous data set.
|
||||
this.divisionCount = 0;
|
||||
|
||||
//Sorts the data by the start time ascending before anything else is done
|
||||
//This ensures that the data is rendered in the proper location when we generate the chart.
|
||||
let sortedData = this.rawData.sort((a, b) => new Date(a[this.startTimeAlias]) - new Date(b[this.startTimeAlias])),
|
||||
groupedData = {};
|
||||
for (let dataRow of sortedData) {
|
||||
this.groupArray(groupedData, dataRow);
|
||||
//console.log(groupedData);
|
||||
}
|
||||
|
||||
this.data = groupedData;
|
||||
|
||||
//Get the max and min datetimes in the dataset
|
||||
//Used to determine the first and last times that are shown in the chart
|
||||
let maxTime = this.rawData.reduce((max, curr) => !!curr[this.endTimeAlias] ? max > new Date(curr[this.endTimeAlias]) ? max : new Date(curr[this.endTimeAlias]) : max, new Date(0));
|
||||
let minTime = this.rawData.reduce((min, curr) => !!curr[this.startTimeAlias] ? min > new Date(curr[this.startTimeAlias]) ? new Date(curr[this.startTimeAlias]) : min : min, new Date(maxTime.getTime()));
|
||||
this.maxTime = roundHourUp(maxTime);
|
||||
this.minTime = roundHourDown(minTime);
|
||||
|
||||
if(this.minTime === this.maxTime)
|
||||
return;
|
||||
|
||||
//Determines the number of columns(times) that will be shown in the chart.
|
||||
//Mostly used for iteration later in the render process
|
||||
for (let i = new Date(this.minTime.getTime()); i <= this.maxTime; i.setTime(i.getTime() + (60 * 60 * 1000))) {
|
||||
this.divisionCount++;
|
||||
}
|
||||
|
||||
}
|
||||
//Takes advantage of the fact that javascript is kind of pass-by-reference for object data types
|
||||
//So every change to result here is actually a change to our groupedData variable
|
||||
groupArray(result, entry, iter = 0) {
|
||||
let nextResult;
|
||||
if (iter === this.groupBy.length + 1) {
|
||||
result.push(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let groupingProperty = result[entry[this.groupBy[iter]]];
|
||||
let chartEntry = result[entry[this.idAlias]];
|
||||
|
||||
//If we can't find a property within result for the groupBy parameter at this level we create a new one.
|
||||
if (!result[entry[this.groupBy[iter]]] && iter < this.groupBy.length) {
|
||||
result[entry[this.groupBy[iter]]] = {groupName: entry[this.groupByAlias[iter]]};
|
||||
nextResult = result[entry[this.groupBy[iter]]];
|
||||
}
|
||||
else if (!result[entry[this.idAlias]] && iter === this.groupBy.length) {
|
||||
result[entry[this.idAlias]] = [];
|
||||
nextResult = result[entry[this.idAlias]];
|
||||
}
|
||||
else if (result[entry[this.groupBy[iter]]]) {
|
||||
nextResult = result[entry[this.groupBy[iter]]];
|
||||
}
|
||||
else if (result[entry[this.idAlias]]) {
|
||||
nextResult = result[entry[this.idAlias]];
|
||||
}
|
||||
iter++;
|
||||
this.groupArray(nextResult, entry, iter);
|
||||
}
|
||||
|
||||
//Creates the header for the Gantt chart
|
||||
buildHeader() {
|
||||
let headerDivs = `<div class="gfb-gantt-header-spacer"></div>`;
|
||||
if (this.divisionCount > 1) {
|
||||
for (let i = 0; i < this.divisionCount; i++) {
|
||||
let date = new Date(this.minTime.getTime() + ((60 * 60 * 1000) * i)),
|
||||
hour = date.getHours().toString().padStart(2, '0'),
|
||||
minutes = date.getMinutes().toString().padStart(2, '0');
|
||||
headerDivs += `<div class="gfb-gantt-header">${hour}:${minutes}</div>`;
|
||||
}
|
||||
}
|
||||
return `<div class="gfb-gantt-headers" style="grid-template-columns: 100px repeat(${this.divisionCount}, 1fr)">${headerDivs}</div>`;
|
||||
}
|
||||
|
||||
|
||||
buildLines() {
|
||||
let lines = '<div class="gfb-gantt-sidebar-template"></div>';
|
||||
for (let i = 0; i < this.divisionCount; i++) {
|
||||
lines += `<div class="gfb-gantt-line"></div>`;
|
||||
}
|
||||
return `<div class="gfb-gantt-lines-container" style="grid-template-columns: 100px repeat(${this.divisionCount}, 1fr)">${lines}</div>`;
|
||||
}
|
||||
|
||||
buildRow(rowArr, dataIndex) {
|
||||
let totalTime = this.maxTime - this.minTime,
|
||||
compositeRows = `<div style="grid-column: 2/${this.divisionCount + 1};grid-row:1;display:flex;align-items:center"><div class="gfb-gantt-sub-row-wrapper">`;
|
||||
for (let i = 0; i < rowArr.length; i++) {
|
||||
//Check to see if the current entry has a start and end time. If not we break
|
||||
if (!rowArr[i][this.startTimeAlias] || !rowArr[i][this.startTimeAlias])
|
||||
break;
|
||||
let currElStart = new Date(rowArr[i][this.startTimeAlias]),
|
||||
currElEnd = new Date(rowArr[i][this.endTimeAlias]),
|
||||
currElRunPercent = ((currElEnd - currElStart) / totalTime) * 100;
|
||||
if (i === 0 || (rowArr[i - 1] && new Date(rowArr[i - 1][this.endTimeAlias]) !== currElStart)) {
|
||||
let baseTime = (i === 0 ? this.minTime : new Date(rowArr[i - 1][this.endTimeAlias])),
|
||||
difference = ((currElStart - baseTime) / totalTime) * 100;
|
||||
compositeRows += `<div style="width:${difference}%;"></div>`;
|
||||
}
|
||||
//If we don't have a linkAlias we assume the entries are not meant to link anywhere, so we just render them as divs instead.
|
||||
if (this.linkAlias)
|
||||
compositeRows += `<a class="gfb-gantt-row-entry" style="width:${currElRunPercent}%;" href="${rowArr[i][this.linkAlias]}" data-index="${dataIndex.join('-')}-${i}"></a>`;
|
||||
else
|
||||
compositeRows += `<div class="gfb-gantt-row-entry" style="width:${currElRunPercent}%;" data-index="${dataIndex.join('-')}-${i}"></div>`;
|
||||
}
|
||||
return compositeRows + '</div></div>';
|
||||
}
|
||||
|
||||
buildContent() {
|
||||
let body = ['<div class="gfb-gantt-row-container">'],
|
||||
header = this.buildHeader(),
|
||||
this1 = this;
|
||||
|
||||
buildContent(this.data, body);
|
||||
body.push('</div>');
|
||||
|
||||
return `<div class="gfb-gantt-content">${header}${body.join('')}</div>`;
|
||||
|
||||
function buildContent(data, result, depth = 0, dataIndex = []) {
|
||||
for (let prop in data) {
|
||||
if (data[prop]) {
|
||||
if(prop !== 'groupName')
|
||||
dataIndex.push(prop);
|
||||
|
||||
if (typeof data[prop] === "object" && !Array.isArray(data[prop])) {
|
||||
result.push(`<div class="gfb-gantt-grouping-header" style="padding-left: ${5 + (depth * 20)}px">${data[prop].groupName}</div><div>`);
|
||||
depth += 1;
|
||||
buildContent(data[prop], result, depth, dataIndex);
|
||||
depth -= 1;
|
||||
}
|
||||
else if (Array.isArray(data[prop])) {
|
||||
result.push(`<div class="gfb-gantt-row" style="grid-template-columns: 100px repeat(${this1.divisionCount}, 1fr)"><div class="gfb-gantt-sidebar-header">${data[prop][0][this1.rowAlias]}</div>${this1.buildRow(data[prop], dataIndex)}</div>`);
|
||||
}
|
||||
dataIndex.pop();
|
||||
}
|
||||
}
|
||||
result.push('</div>');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
buildChart() {
|
||||
let content = this.buildContent();
|
||||
let lines = this.buildLines();
|
||||
return `${lines}${content}`;
|
||||
}
|
||||
|
||||
bindHover() {
|
||||
let bindElements = document.querySelectorAll(`#${this.staticID} .gfb-gantt-row-entry`);
|
||||
|
||||
bindElements.forEach(bindElement => {
|
||||
let toolTipElement,
|
||||
timeout;
|
||||
bindElement.addEventListener('mouseover', e => {
|
||||
let target = e.target,
|
||||
indexArray = target.getAttribute('data-index').split('-'),
|
||||
position = getElOffset(target),
|
||||
targetHeight = getBoundingRect(target).height,
|
||||
this1 = this;
|
||||
indexArray.push(this.tooltipAlias);
|
||||
position.top += (targetHeight + 5);
|
||||
target.classList.add('hovering');
|
||||
//Set a delay on the tooltip appearing so that it doesn't immediately appear if the user happens to swipe their mouse across the screen.
|
||||
//At the end of the delay we check to see if the mouse is still hovering, at which point we will show the tooltip
|
||||
timeout = setTimeout(() => {
|
||||
if (!target.classList.contains('hovering'))
|
||||
return;
|
||||
toolTipElement = document.createElement(`div`);
|
||||
toolTipElement.classList.add('gfb-gantt-row-entry-tooltip');
|
||||
toolTipElement.innerHTML = getToolTipData();
|
||||
toolTipElement.style.top = `${position.top}px`;
|
||||
toolTipElement.style.left = `${position.left}px`;
|
||||
document.body.appendChild(toolTipElement);
|
||||
fadeIn(toolTipElement, 300);
|
||||
}, 300);
|
||||
|
||||
function getToolTipData() {
|
||||
let dataArray = this1.data;
|
||||
for (let i = 0; i < indexArray.length; i++) {
|
||||
if (i === indexArray.length - 1)
|
||||
return dataArray[indexArray[i]]
|
||||
else
|
||||
dataArray = dataArray[indexArray[i]];
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
bindElement.addEventListener('mouseout', e => {
|
||||
let target = e.target;
|
||||
|
||||
clearTimeout(timeout);
|
||||
target.classList.remove('hovering');
|
||||
if (toolTipElement)
|
||||
fadeOut(toolTipElement, 300, () => toolTipElement.remove());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bindCollapse(){
|
||||
let bindElements = document.querySelectorAll(`#${this.staticID} .gfb-gantt-grouping-header`);
|
||||
|
||||
bindElements.forEach(bindElement => {
|
||||
bindElement.addEventListener('click', e => {
|
||||
let group = e.target.nextSibling,
|
||||
duration = 300;
|
||||
if(!group.classList.contains('gfb-gantt-group--collapsed')){
|
||||
slideUp(group, duration);
|
||||
group.classList.add('gfb-gantt-group--collapsed');
|
||||
}
|
||||
else{
|
||||
slideDown(group, duration);
|
||||
group.classList.remove('gfb-gantt-group--collapsed');
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
this.wrapper.innerHTML = this.buildChart();
|
||||
|
||||
this.bindCollapse();
|
||||
|
||||
if (this.tooltipAlias)
|
||||
this.bindHover();
|
||||
}
|
||||
|
||||
noDataFound() {
|
||||
this.wrapper.innerHTML = this.noDataFoundMessage;
|
||||
}
|
||||
|
||||
empty() {
|
||||
this.wrapper.innerHTML = '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function roundHourUp(date) {
|
||||
let m = 60 * 60 * 1000
|
||||
return new Date(Math.ceil(date.getTime() / m) * m);
|
||||
}
|
||||
function roundHourDown(date) {
|
||||
let m = 60 * 60 * 1000;
|
||||
return new Date(Math.floor(date.getTime() / m) * m);
|
||||
}
|
||||
|
||||
function fadeIn(element, ms, callback = null) {
|
||||
let duration = ms,
|
||||
interval = 50,
|
||||
gap = interval / duration,
|
||||
opacity = 0;
|
||||
|
||||
element.style.display = 'block';
|
||||
element.style.opacity = opacity;
|
||||
|
||||
let fading = window.setInterval(fade, interval);
|
||||
|
||||
function fade() {
|
||||
opacity = opacity + gap;
|
||||
element.style.opacity = opacity;
|
||||
|
||||
if (opacity <= 0)
|
||||
element.style.display = 'none';
|
||||
if (opacity >= 1) {
|
||||
window.clearInterval(fading);
|
||||
if (callback)
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
function fadeOut(element, ms, callback = null) {
|
||||
let duration = ms,
|
||||
interval = 50,
|
||||
gap = interval / duration,
|
||||
opacity = 1;
|
||||
|
||||
let fading = window.setInterval(fade, interval);
|
||||
|
||||
function fade() {
|
||||
opacity = opacity - gap;
|
||||
element.style.opacity = opacity;
|
||||
|
||||
if (opacity <= 0)
|
||||
element.style.display = 'none';
|
||||
if (opacity <= 0) {
|
||||
window.clearInterval(fading);
|
||||
if (callback)
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getElOffset(element) {
|
||||
let boundingRect = getBoundingRect(element);
|
||||
return { top: boundingRect.top + window.scrollY, left: boundingRect.left + window.scrollX };
|
||||
}
|
||||
function getBoundingRect(element) {
|
||||
return element.getBoundingClientRect();
|
||||
}
|
||||
|
||||
function slideUp(element, ms){
|
||||
|
||||
setProperty(element, 'height', `${element.offsetHeight}px`);
|
||||
setProperty(element, 'transition-property', 'height, margin, padding');
|
||||
setProperty(element, 'transition-duration', `${ms}ms`);
|
||||
setProperty(element, 'box-sizing', 'border-box');
|
||||
|
||||
zeroMultiProperty(element, ['margin-bottom', 'margin-top', 'padding-top', 'padding-bottom']);
|
||||
setProperty(element, 'overflow', 'hidden');
|
||||
|
||||
//Need a timeout otherwise the slide up animation is not performed
|
||||
setTimeout(() => setProperty(element, 'height'), 0);
|
||||
|
||||
setTimeout(() => {
|
||||
setProperty(element, 'display', 'none');
|
||||
removeMultiProperty(element, ['height', 'padding-top', 'padding-bottom', 'margin-top', 'margin-bottom', 'overflow', 'transition-duration', 'transition-property'])
|
||||
}, ms);
|
||||
}
|
||||
function slideDown(element, ms){
|
||||
|
||||
element.style.display = 'block';
|
||||
|
||||
let height = element.offsetHeight;
|
||||
|
||||
setProperty(element, 'height');
|
||||
setProperty(element, 'transition-property', 'height, margin, padding');
|
||||
setProperty(element, 'transition-duration', `${ms}ms`);
|
||||
setProperty(element, 'box-sizing', 'border-box');
|
||||
setProperty(element, 'overflow', 'hidden');
|
||||
|
||||
//Need a timeout otherwise the slide up animation is not performed
|
||||
setTimeout(() => setProperty(element, 'height', `${height}px`), 0);
|
||||
|
||||
setTimeout(() => {
|
||||
removeMultiProperty(element, ['height', 'overflow', 'transition-duration', 'transition-property']);
|
||||
}, ms);
|
||||
}
|
||||
|
||||
//Sets the inline property of the given element to the value provided.
|
||||
//If not provided, the value is set to 0
|
||||
function setProperty(element, property, value = 0) {
|
||||
element.style.setProperty(property, value);
|
||||
}
|
||||
|
||||
//Iteratively calls setProperty
|
||||
function zeroMultiProperty(element, properties) {
|
||||
for (property of properties) {
|
||||
setProperty(element, property);
|
||||
}
|
||||
}
|
||||
|
||||
//Iteratively calls removeProperty
|
||||
function removeMultiProperty(element, properties){
|
||||
for(property of properties)
|
||||
removeProperty(element, property);
|
||||
}
|
||||
//Removes the given property from the given element's inline styling
|
||||
function removeProperty(element, property){
|
||||
element.style.removeProperty(property);
|
||||
};
|
||||
1
libraries/JS-Gantt-Chart-master/gantt.min.css
vendored
Normal file
1
libraries/JS-Gantt-Chart-master/gantt.min.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.gfb-gantt-wrapper{--entry-color:#033b6a;--border-color:rgba(0, 0, 0, 0.1);--background-color:#f9f9f9;--hovered-background:#f0f0f0}.gfb-gantt-wrapper{position:relative;min-width:550px}.gfb-gantt-headers{display:grid;background-color:var(--background-color);border:1px solid var(--border-color);min-height:40px}.gfb-gantt-headers>div:not(:last-child){border-right:1px solid var(--border-color)}.gfb-gantt-header{display:flex;padding:10px 0 10px 5px}.gfb-gantt-sidebar-header{text-align:center;padding:10px 5px 10px 5px;background-color:var(--background-color);border-right:1px solid var(--border-color)}.gfb-gantt-content{position:relative}.gfb-gantt-row{display:grid;min-height:25px;border:1px solid var(--border-color);border-top:0;border-right:1px solid transparent}.gfb-gantt-sub-row-wrapper{max-height:15px;display:flex;width:100%;height:100%}.gfb-gantt-row-entry{border-radius:10px;cursor:pointer;background-color:var(--entry-color)}.gfb-gantt-row-entry:hover{box-shadow:var(--entry-color) 0 0 2px .1px}.gfb-gantt-lines-container{display:grid;position:absolute;top:0;left:0;height:100%;width:100%;border:1px solid var(--border-color);z-index:0}.gfb-gantt-lines-container>div:not(:last-child):not(.gfb-gantt-sidebar-template){border-right:1px solid var(--border-color)}.gfb-gantt-row-entry-tooltip{position:absolute;display:none;max-width:400px;background-color:#fff;box-shadow:0 0 5px #aaa;padding:10px;z-index:20}.gfb-gantt-grouping-header{background-color:var(--background-color);padding:5px 0;border:1px solid var(--border-color);border-top:0 transparent;cursor:pointer;user-select:none}.gfb-gantt-grouping-header:hover{background-color:var(--hovered-background)}.gfb-gantt{overflow:auto}
|
||||
1
libraries/JS-Gantt-Chart-master/gantt.min.js
vendored
Normal file
1
libraries/JS-Gantt-Chart-master/gantt.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
libraries/JS-Gantt-Chart-master/sample-chart/index.html
Normal file
11
libraries/JS-Gantt-Chart-master/sample-chart/index.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../gantt.css" type="text/css"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart"></div>
|
||||
<script src="../gantt.js"></script>
|
||||
<script src="initialize-gantt.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
let data = [
|
||||
{
|
||||
recordID: 1,
|
||||
row: "Row for ID #1",
|
||||
tooltip: "Tooltips here! Get your tooltips!",
|
||||
start: "Wed Jun 03 2020 14:21:55",
|
||||
end: "Wed Jun 03 2020 20:21:55",
|
||||
group: "This is a group with a new name",
|
||||
groupId: 5
|
||||
},
|
||||
{
|
||||
recordID: 2,
|
||||
row: "Row for ID #2",
|
||||
tooltip: "Tooltip for row 2",
|
||||
start: "Jun 03 2020 11:00:00",
|
||||
end: "Jun 03 2020 15:23:43",
|
||||
group: "empty",
|
||||
groupId: 1
|
||||
},
|
||||
{
|
||||
recordID: 1,
|
||||
row: "Row for ID #1",
|
||||
tooltip: "Tooltip unique to this item",
|
||||
start: "Wed Jun 03 2020 06:00:00",
|
||||
end: "Wed Jun 03 2020 10:00:00",
|
||||
group: "test",
|
||||
groupId: 2,
|
||||
subGroupId: 1,
|
||||
subGroup: "Subgroup #1"
|
||||
},
|
||||
{
|
||||
recordID: 5,
|
||||
row: "Now we have grouping",
|
||||
tooltip: "Tooltip after grouping",
|
||||
start: "Wed Jun 03 2020 06:00:00",
|
||||
end: "Wed Jun 03 2020 10:00:00",
|
||||
group: "test",
|
||||
groupId: 2,
|
||||
subGroupId: 1,
|
||||
subGroup: "Subgroup #1"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
//This could be an API call to grab data
|
||||
function refreshFunction() {
|
||||
return data;
|
||||
}
|
||||
|
||||
//Parameters that the chart expects
|
||||
let params = {
|
||||
sidebarHeader: "Unused right now",
|
||||
noDataFoundMessage: "No data found",
|
||||
startTimeAlias: "start",
|
||||
endTimeAlias: "end",
|
||||
idAlias: "recordID",
|
||||
rowAlias: "row",
|
||||
linkAlias: null,
|
||||
tooltipAlias: "tooltip",
|
||||
groupBy: "groupId,subGroupId",
|
||||
groupByAlias: "group,subGroup",
|
||||
refreshFunction: refreshFunction
|
||||
}
|
||||
|
||||
//Create the chart.
|
||||
//On first render the chart will call its refreshData function on its own.
|
||||
let ganttChart = new Gantt("chart", params);
|
||||
|
||||
//To refresh the chart's data
|
||||
ganttChart.refreshData();
|
||||
Reference in New Issue
Block a user