Merge pull request #394 from safwansamsudeen/master

Tooling and Bug Fixes
This commit is contained in:
Safwan Samsudeen 2024-09-11 20:19:52 +05:30 committed by GitHub
commit 22ff6e202e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 17538 additions and 9054 deletions

View File

@ -1,3 +0,0 @@
{
"presets": ["env"]
}

1
.gitignore vendored
View File

@ -25,6 +25,7 @@ build/Release
# Dependency directory # Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules node_modules
.yarn
.DS_Store .DS_Store

1
.yarnrc.yml Normal file
View File

@ -0,0 +1 @@
nodeLinker: node-modules

View File

@ -51,18 +51,18 @@ var gantt = new Gantt("#gantt", tasks);
You can also pass various options to the Gantt constructor: You can also pass various options to the Gantt constructor:
```js ```js
var gantt = new Gantt("#gantt", tasks, { var gantt = new Gantt('#gantt', tasks, {
header_height: 50, header_height: 50,
column_width: 30, column_width: 30,
step: 24, step: 24,
view_modes: ["Quarter Day", "Half Day", "Day", "Week", "Month"], view_modes: ['Quarter Day', 'Half Day', 'Day', 'Week', 'Month'],
bar_height: 20, bar_height: 20,
bar_corner_radius: 3, bar_corner_radius: 3,
arrow_curve: 5, arrow_curve: 5,
padding: 18, padding: 18,
view_mode: "Day", view_mode: 'Day',
date_format: "YYYY-MM-DD", date_format: 'YYYY-MM-DD',
language: "en", // or 'es', 'it', 'ru', 'ptBr', 'fr', 'tr', 'zh', 'de', 'hu' language: 'en', // or 'es', 'it', 'ru', 'ptBr', 'fr', 'tr', 'zh', 'de', 'hu'
custom_popup_html: null, custom_popup_html: null,
}); });
``` ```

295
dist/frappe-gantt.css vendored
View File

@ -1,295 +0,0 @@
.dark > .gantt-container .gantt .grid-header {
fill: #252525;
stroke: #616161;
}
.dark > .gantt-container .gantt .grid-row {
fill: #252525;
}
.dark > .gantt-container .gantt .grid-row:nth-child(even) {
fill: #3e3e3e;
}
.dark > .gantt-container .gantt .row-line {
stroke: #3e3e3e;
}
.dark > .gantt-container .gantt .tick {
stroke: #616161;
}
.dark > .gantt-container .gantt .today-highlight {
opacity: 0.2;
}
.dark > .gantt-container .gantt .arrow {
stroke: #eee;
}
.dark > .gantt-container .gantt .bar {
fill: #616161;
stroke: none;
}
.dark > .gantt-container .gantt .bar-progress {
fill: #8a8aff;
}
.dark > .gantt-container .gantt .bar-invalid {
fill: transparent;
stroke: #c6ccd2;
}
.dark > .gantt-container .gantt .bar-invalid ~ .bar-label {
fill: #ececec;
}
.dark > .gantt-container .gantt .bar-label.big {
fill: #ececec;
}
.dark > .gantt-container .gantt .bar-wrapper:hover .bar {
fill: #6e6e6e;
}
.dark > .gantt-container .gantt .bar-wrapper:hover .bar-progress {
fill: #a4a4ff;
}
.dark > .gantt-container .gantt .bar-wrapper.active .bar {
fill: #6e6e6e;
}
.dark > .gantt-container .gantt .bar-wrapper.active .bar-progress {
fill: #a4a4ff;
}
.dark > .gantt-container .gantt .upper-text {
fill: #a2a2a2;
}
.dark > .gantt-container .gantt .lower-text {
fill: #f7f7f7;
}
.dark > .gantt-container .popup-wrapper {
background-color: #333;
}
.dark > .gantt-container .popup-wrapper .title {
border-color: #a4a4ff;
}
.dark > .gantt-container .popup-wrapper .pointer {
border-top-color: #333;
}
.gantt-container {
line-height: 14.5px;
}
.gantt-container .grid-header {
background-color: #ffffff;
position: sticky;
top: 0;
left: 0;
z-index: 10;
}
.gantt-container .lower-text,
.gantt-container .upper-text {
text-anchor: middle;
color: #111;
}
.gantt-container .upper-header {
height: 40px;
}
.gantt-container .lower-header {
height: 30px;
}
.gantt-container .lower-text {
font-size: 14px;
position: absolute;
width: fit-content;
}
.gantt-container .upper-text {
position: absolute;
width: fit-content;
font-weight: 500;
font-size: 16px;
}
.gantt-container .current-upper {
position: fixed;
}
.gantt-container .side-header {
position: fixed;
padding: 0 10px;
margin-right: 10px;
background: white;
line-height: 20px;
font-weight: 400;
}
.gantt-container .today-button,
.gantt-container .viewmode-select {
background: #F4F5F6;
text-align: -webkit-center;
text-align: center;
height: 25px;
border-radius: 8px;
border: none;
color: #111;
padding: 4px 10px;
border-radius: 8px;
height: 25px;
}
.gantt-container .viewmode-select {
outline: none !important;
padding: 4px 8px;
margin-right: 4px;
-webkit-appearance: none;
-moz-appearance: none;
text-indent: 1px;
text-overflow: "";
}
.gantt-container .date-highlight {
background-color: #EBEEF0;
border-radius: 12px;
position: absolute;
display: none;
}
.gantt-container .current-highlight {
position: absolute;
background: #2c94ec;
width: 1px;
}
.gantt-container .current-date-highlight {
background: #2c94ec;
color: #fff;
padding: 4px 8px;
border-radius: 200px;
}
.gantt {
user-select: none;
-webkit-user-select: none;
position: absolute;
}
.gantt .grid-background {
fill: none;
}
.gantt .grid-row {
fill: #ffffff;
}
.gantt .row-line {
stroke: #ebeff2;
}
.gantt .tick {
stroke: #EBEEF0;
stroke-width: 0.4;
}
.gantt .tick.thick {
stroke: #e0e0e0;
stroke-width: 0.7;
}
.gantt .holiday-highlight {
fill: #F9FAFA;
}
.gantt .arrow {
fill: none;
stroke: #9FA9B1;
stroke-width: 1;
}
.gantt .bar-wrapper .bar {
fill: #fff;
stroke: #fff;
stroke-width: 0;
transition: stroke-width 0.3s ease;
}
.gantt .bar-progress {
fill: #EBEEF0;
}
.gantt .bar-expected-progress {
fill: #c4c4e9;
}
.gantt .bar-invalid {
fill: transparent;
stroke: #fff;
stroke-width: 1;
stroke-dasharray: 5;
}
.gantt .bar-invalid ~ .bar-label {
fill: #fff;
}
.gantt .bar-label {
fill: #111;
dominant-baseline: central;
font-family: Helvetica;
font-size: 13px;
font-weight: 400;
}
.gantt .bar-label.big {
fill: #111;
text-anchor: start;
}
.gantt .bar-wrapper.important .bar {
fill: #94c4f4;
}
.gantt .bar-wrapper.important .bar-progress {
fill: #2c94ec;
}
.gantt .bar-wrapper.important .bar-label {
fill: #fff;
}
.gantt .bar-wrapper.important .handle {
fill: #94c4f4;
}
.gantt .bar-wrapper.important .handle.progress {
fill: #fff;
}
.gantt .handle {
fill: #dcdce4;
cursor: ew-resize;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
.gantt .handle.progress {
fill: #666;
}
.gantt .bar-wrapper {
cursor: pointer;
outline: none;
}
.gantt .bar-wrapper.active .handle {
visibility: visible;
opacity: 1;
}
.gantt .bar-wrapper .bar {
-webkit-filter: drop-shadow(3px 3px 2px rgba(0, 0, 0, 0.7));
filter: drop-shadow(0 0 2px rgba(17, 43, 66, 0.16));
border-radius: 3px;
}
.gantt .bar-wrapper:hover .bar {
transition: transform 0.3s ease;
}
.gantt .bar-wrapper:hover .date-highlight {
display: block;
}
.gantt .hide {
display: none;
}
.gantt-container {
position: relative;
overflow: auto;
font-size: 12px;
height: 500px;
}
.gantt-container .popup-wrapper {
position: absolute;
top: 0;
left: 0;
background: #171B1F;
padding: 10px;
border-radius: 5px;
width: max-content;
}
.gantt-container .popup-wrapper.hidden {
opacity: 0 !important;
}
.gantt-container .popup-wrapper .title {
margin-bottom: 5px;
text-align: -webkit-center;
text-align: center;
color: #fff;
}
.gantt-container .popup-wrapper .subtitle {
color: #98A1A9;
}
.gantt-container .popup-wrapper .pointer {
position: absolute;
height: 5px;
margin: 0 0 0 -5px;
border: 5px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.8);
}

2790
dist/frappe-gantt.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.dark>.gantt-container .gantt .grid-header{fill:#252525;stroke:#616161}.dark>.gantt-container .gantt .grid-row{fill:#252525}.dark>.gantt-container .gantt .grid-row:nth-child(even){fill:#3e3e3e}.dark>.gantt-container .gantt .row-line{stroke:#3e3e3e}.dark>.gantt-container .gantt .tick{stroke:#616161}.dark>.gantt-container .gantt .today-highlight{opacity:.2}.dark>.gantt-container .gantt .arrow{stroke:#eee}.dark>.gantt-container .gantt .bar{fill:#616161;stroke:none}.dark>.gantt-container .gantt .bar-progress{fill:#8a8aff}.dark>.gantt-container .gantt .bar-invalid{fill:rgba(0,0,0,0);stroke:#c6ccd2}.dark>.gantt-container .gantt .bar-invalid~.bar-label{fill:#ececec}.dark>.gantt-container .gantt .bar-label.big{fill:#ececec}.dark>.gantt-container .gantt .bar-wrapper:hover .bar{fill:#6e6e6e}.dark>.gantt-container .gantt .bar-wrapper:hover .bar-progress{fill:#a4a4ff}.dark>.gantt-container .gantt .bar-wrapper.active .bar{fill:#6e6e6e}.dark>.gantt-container .gantt .bar-wrapper.active .bar-progress{fill:#a4a4ff}.dark>.gantt-container .gantt .upper-text{fill:#a2a2a2}.dark>.gantt-container .gantt .lower-text{fill:#f7f7f7}.dark>.gantt-container .popup-wrapper{background-color:#333}.dark>.gantt-container .popup-wrapper .title{border-color:#a4a4ff}.dark>.gantt-container .popup-wrapper .pointer{border-top-color:#333}.gantt-container{line-height:14.5px}.gantt-container .grid-header{background-color:#fff;position:sticky;top:0;left:0;z-index:10}.gantt-container .lower-text,.gantt-container .upper-text{text-anchor:middle;color:#111}.gantt-container .upper-header{height:40px}.gantt-container .lower-header{height:30px}.gantt-container .lower-text{font-size:14px;position:absolute;width:fit-content}.gantt-container .upper-text{position:absolute;width:fit-content;font-weight:500;font-size:16px}.gantt-container .current-upper{position:fixed}.gantt-container .side-header{position:fixed;padding:0 10px;margin-right:10px;background:#fff;line-height:20px;font-weight:400}.gantt-container .today-button,.gantt-container .viewmode-select{background:#f4f5f6;text-align:-webkit-center;text-align:center;height:25px;border-radius:8px;border:none;color:#111;padding:4px 10px;border-radius:8px;height:25px}.gantt-container .viewmode-select{outline:none !important;padding:4px 8px;margin-right:4px;-webkit-appearance:none;-moz-appearance:none;text-indent:1px;text-overflow:""}.gantt-container .date-highlight{background-color:#ebeef0;border-radius:12px;position:absolute;display:none}.gantt-container .current-highlight{position:absolute;background:#2c94ec;width:1px}.gantt-container .current-date-highlight{background:#2c94ec;color:#fff;padding:4px 8px;border-radius:200px}.gantt{user-select:none;-webkit-user-select:none;position:absolute}.gantt .grid-background{fill:none}.gantt .grid-row{fill:#fff}.gantt .row-line{stroke:#ebeff2}.gantt .tick{stroke:#ebeef0;stroke-width:.4}.gantt .tick.thick{stroke:#e0e0e0;stroke-width:.7}.gantt .holiday-highlight{fill:#f9fafa}.gantt .arrow{fill:none;stroke:#9fa9b1;stroke-width:1}.gantt .bar-wrapper .bar{fill:#fff;stroke:#fff;stroke-width:0;transition:stroke-width .3s ease}.gantt .bar-progress{fill:#ebeef0}.gantt .bar-expected-progress{fill:#c4c4e9}.gantt .bar-invalid{fill:rgba(0,0,0,0);stroke:#fff;stroke-width:1;stroke-dasharray:5}.gantt .bar-invalid~.bar-label{fill:#fff}.gantt .bar-label{fill:#111;dominant-baseline:central;font-family:Helvetica;font-size:13px;font-weight:400}.gantt .bar-label.big{fill:#111;text-anchor:start}.gantt .bar-wrapper.important .bar{fill:#94c4f4}.gantt .bar-wrapper.important .bar-progress{fill:#2c94ec}.gantt .bar-wrapper.important .bar-label{fill:#fff}.gantt .bar-wrapper.important .handle{fill:#94c4f4}.gantt .bar-wrapper.important .handle.progress{fill:#fff}.gantt .handle{fill:#dcdce4;cursor:ew-resize;opacity:0;visibility:hidden;transition:opacity .3s ease}.gantt .handle.progress{fill:#666}.gantt .bar-wrapper{cursor:pointer;outline:none}.gantt .bar-wrapper.active .handle{visibility:visible;opacity:1}.gantt .bar-wrapper .bar{-webkit-filter:drop-shadow(3px 3px 2px rgba(0, 0, 0, 0.7));filter:drop-shadow(0 0 2px rgba(17, 43, 66, 0.16));border-radius:3px}.gantt .bar-wrapper:hover .bar{transition:transform .3s ease}.gantt .bar-wrapper:hover .date-highlight{display:block}.gantt .hide{display:none}.gantt-container{position:relative;overflow:auto;font-size:12px;height:500px}.gantt-container .popup-wrapper{position:absolute;top:0;left:0;background:#171b1f;padding:10px;border-radius:5px;width:max-content}.gantt-container .popup-wrapper.hidden{opacity:0 !important}.gantt-container .popup-wrapper .title{margin-bottom:5px;text-align:-webkit-center;text-align:center;color:#fff}.gantt-container .popup-wrapper .subtitle{color:#98a1a9}.gantt-container .popup-wrapper .pointer{position:absolute;height:5px;margin:0 0 0 -5px;border:5px solid rgba(0,0,0,0);border-bottom-color:rgba(0,0,0,.8)}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

23
dist/frappe-gantt.umd.cjs vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/style.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -23,53 +23,57 @@
background-color: #252525; background-color: #252525;
} }
</style> </style>
<link rel="stylesheet" href="dist/frappe-gantt.css" /> <link rel="stylesheet" href="dist/style.css" />
<script src="dist/frappe-gantt.js"></script> <script src="dist/frappe-gantt.umd.cjs"></script>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h2 class="heading">Interactive Gantt Chart entirely made in SVG!</h2> <h2 class="heading">
Interactive Gantt Chart entirely made in SVG!
</h2>
<div class="gantt-target"></div> <div class="gantt-target"></div>
</div> </div>
<script> <script type="module">
// import Gantt from "./dist/frappe-gantt.js";
let tasks = [ let tasks = [
{ {
start: '2024-04-01', start: '2024-04-01',
end: '2024-04-01', end: '2024-04-01',
name: 'Redesign website', name: 'Redesign website',
id: "Task 0", id: 'Task 0',
progress: 30 progress: 30,
}, },
{ {
start: '2024-03-26', start: '2024-03-26',
// Utilizes duration // Utilizes duration
duration: '6d', duration: '6d',
name: 'Write new content', name: 'Write new content',
id: "Task 1", id: 'Task 1',
progress: 5, progress: 5,
important: true important: true,
}, },
{ {
start: '2024-04-04', start: '2024-04-04',
end: '2024-04-08', end: '2024-04-08',
name: 'Apply new styles', name: 'Apply new styles',
id: "Task 2", id: 'Task 2',
progress: 80, progress: 80,
dependencies: 'Task 1' dependencies: 'Task 1',
}, },
{ {
start: '2024-04-08', start: '2024-04-08',
end: '2024-04-09', end: '2024-04-09',
name: 'Review', name: 'Review',
id: "Task 3", id: 'Task 3',
progress: 5, progress: 5,
dependencies: 'Task 2' dependencies: 'Task 2',
}, },
{ {
start: '2024-04-08', start: '2024-04-08',
end: '2024-04-10', end: '2024-04-10',
name: 'Deploy', name: 'Deploy',
id: "Task 4", id: 'Task 4',
progress: 0, progress: 0,
// dependencies: 'Task 2' // dependencies: 'Task 2'
}, },
@ -77,10 +81,10 @@
start: '2024-04-21', start: '2024-04-21',
end: '2024-04-29', end: '2024-04-29',
name: 'Go Live!', name: 'Go Live!',
id: "Task 5", id: 'Task 5',
progress: 0, progress: 0,
dependencies: 'Task 2', dependencies: 'Task 2',
custom_class: 'bar-milestone' custom_class: 'bar-milestone',
}, },
// { // {
// start: '2014-01-05', // start: '2014-01-05',
@ -92,11 +96,17 @@
]; ];
// Uncomment to test fixed header // Uncomment to test fixed header
tasks = [...tasks, ...Array.from({length: tasks.length * 3}, (_, i) => ({...tasks[i % 3], id: i}))] // tasks = [
// ...tasks,
// ...Array.from({ length: tasks.length * 3 }, (_, i) => ({
// ...tasks[i % 3],
// id: i,
// })),
// ];
let gantt_chart = new Gantt(".gantt-target", tasks, { let gantt_chart = new Gantt('.gantt-target', tasks, {
on_click: (task) => { on_click: (task) => {
console.log("Click", task); console.log('Click', task);
}, },
// on_double_click: (task) => { // on_double_click: (task) => {
// console.log("Double Click", task); // console.log("Double Click", task);
@ -113,18 +123,18 @@
// on_hover: (task, x, y) => { // on_hover: (task, x, y) => {
// console.log("Hover", x, y); // console.log("Hover", x, y);
// }, // },
view_mode: "Day", view_mode: 'Day',
view_mode_padding: { DAY: "3d" }, view_mode_padding: { DAY: '3d' },
popup: false, // popup: false,
// scroll_to: 'today', // scroll_to: 'today',
// view_mode_select: true, // view_mode_select: true,
// dates_readonly: true,
// today_button: false, // today_button: false,
// readonly: true, // readonly: true,
// lines: 'vertical', // lines: 'vertical',
// lower_text: (date) => date.getDay(), // lower_text: (date) => date.getDay(),
// upper_text: (date, view_mode, def) => def, // upper_text: (date, view_mode, def) => def,
}); });
console.log(gantt_chart);
</script> </script>
</body> </body>
</html> </html>

View File

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016 Frappe Technologies Pvt. Ltd. Copyright (c) 2024 Frappe Technologies Pvt. Ltd.
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: 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:

6265
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,16 @@
{ {
"name": "frappe-gantt", "name": "frappe-gantt",
"version": "0.6.1", "version": "0.8",
"description": "A simple, modern, interactive gantt library for the web", "description": "A simple, modern, interactive gantt library for the web",
"main": "src/index.js", "main": "src/index.js",
"type": "module",
"scripts": { "scripts": {
"start": "yarn run dev", "start": "yarn run dev",
"build": "rollup -c", "dev": "vite",
"dev": "rollup -c -w", "build-dev": "vite build --watch",
"build": "vite build",
"test": "jest", "test": "jest",
"lint": "eslint src/**/*.js",
"test:watch": "jest --watch", "test:watch": "jest --watch",
"prettier": "prettier --write \"{src/*,tests/*,rollup.config}.js\"", "prettier": "prettier --write \"{src/*,tests/*,rollup.config}.js\"",
"prettier-check": "prettier --check \"{src/*,tests/*,rollup.config}.js\"" "prettier-check": "prettier --check \"{src/*,tests/*,rollup.config}.js\""
@ -21,6 +24,12 @@
"dist", "dist",
"README.md" "README.md"
], ],
"exports": {
".": {
"import": "./dist/frappe-gantt.es.js",
"require": "./dist/frappe-gantt.umd.js"
}
},
"keywords": [ "keywords": [
"gantt", "gantt",
"svg", "svg",
@ -36,17 +45,16 @@
}, },
"homepage": "https://github.com/frappe/gantt", "homepage": "https://github.com/frappe/gantt",
"devDependencies": { "devDependencies": {
"babel-preset-env": "^1.6.1",
"eslint": "^4.17.0", "eslint": "^4.17.0",
"eslint-config-prettier": "^2.9.0", "eslint-config-prettier": "^2.9.0",
"eslint-plugin-prettier": "^2.6.0", "eslint-plugin-prettier": "^2.6.0",
"jest": "^22.2.1", "jest": "^22.2.1",
"postcss-nesting": "^12.1.2",
"prettier": "3.2.5", "prettier": "3.2.5",
"rollup": "^2.70.2", "vite": "^5.2.10"
"rollup-plugin-sass": "^1.2.12",
"rollup-plugin-terser": "^7.0.2"
}, },
"eslintIgnore": [ "eslintIgnore": [
"dist" "dist"
] ],
"packageManager": "yarn@3.2.3"
} }

4
postcss.config.cjs Normal file
View File

@ -0,0 +1,4 @@
/* eslint-disable */
module.exports = {
plugins: [require('postcss-nesting')],
};

View File

@ -1,37 +0,0 @@
import sass from "rollup-plugin-sass";
import { terser } from "rollup-plugin-terser";
const dev = {
input: "src/index.js",
output: {
name: "Gantt",
file: "dist/frappe-gantt.js",
sourcemap: true,
format: "iife",
},
plugins: [
sass({
output: true,
}),
],
};
const prod = {
input: "src/index.js",
output: {
name: "Gantt",
file: "dist/frappe-gantt.min.js",
sourcemap: true,
format: "iife",
},
plugins: [
sass({
output: true,
options: {
outputStyle: "compressed",
},
}),
terser(),
],
};
export default [dev, prod];

View File

@ -1,4 +1,4 @@
import { createSVG } from "./svg_utils"; import { createSVG } from './svg_utils';
export default class Arrow { export default class Arrow {
constructor(gantt, from_task, to_task) { constructor(gantt, from_task, to_task) {
@ -29,7 +29,8 @@ export default class Arrow {
this.from_task.task._index + this.from_task.task._index +
this.gantt.options.padding; this.gantt.options.padding;
const end_x = this.to_task.$bar.getX() - this.gantt.options.padding / 2 - 7; const end_x =
this.to_task.$bar.getX() - this.gantt.options.padding / 2 - 7;
const end_y = const end_y =
this.gantt.options.header_height + this.gantt.options.header_height +
this.gantt.options.bar_height / 2 + this.gantt.options.bar_height / 2 +
@ -61,7 +62,9 @@ export default class Arrow {
) { ) {
const down_1 = this.gantt.options.padding / 2 - curve; const down_1 = this.gantt.options.padding / 2 - curve;
const down_2 = const down_2 =
this.to_task.$bar.getY() + this.to_task.$bar.getHeight() / 2 - curve_y; this.to_task.$bar.getY() +
this.to_task.$bar.getHeight() / 2 -
curve_y;
const left = this.to_task.$bar.getX() - this.gantt.options.padding; const left = this.to_task.$bar.getX() - this.gantt.options.padding;
this.path = ` this.path = `
@ -80,15 +83,15 @@ export default class Arrow {
} }
draw() { draw() {
this.element = createSVG("path", { this.element = createSVG('path', {
d: this.path, d: this.path,
"data-from": this.from_task.task.id, 'data-from': this.from_task.task.id,
"data-to": this.to_task.task.id, 'data-to': this.to_task.task.id,
}); });
} }
update() { update() {
this.calculate_path(); this.calculate_path();
this.element.setAttribute("d", this.path); this.element.setAttribute('d', this.path);
} }
} }

View File

@ -1,5 +1,5 @@
import date_utils from "./date_utils"; import date_utils from './date_utils';
import { $, createSVG, animateSVG } from "./svg_utils"; import { $, createSVG, animateSVG } from './svg_utils';
export default class Bar { export default class Bar {
constructor(gantt, task) { constructor(gantt, task) {
@ -33,32 +33,35 @@ export default class Bar {
this.gantt.options.column_width * this.gantt.options.column_width *
this.duration * this.duration *
(this.task.progress / 100) || 0; (this.task.progress / 100) || 0;
this.group = createSVG("g", { this.group = createSVG('g', {
class: "bar-wrapper" + (this.task.custom_class ? " " + this.task.custom_class : "") + (this.task.important ? ' important' : ''), class:
"data-id": this.task.id, 'bar-wrapper' +
(this.task.custom_class ? ' ' + this.task.custom_class : '') +
(this.task.important ? ' important' : ''),
'data-id': this.task.id,
}); });
this.bar_group = createSVG("g", { this.bar_group = createSVG('g', {
class: "bar-group", class: 'bar-group',
append_to: this.group, append_to: this.group,
}); });
this.handle_group = createSVG("g", { this.handle_group = createSVG('g', {
class: "handle-group", class: 'handle-group',
append_to: this.group, append_to: this.group,
}); });
} }
prepare_helpers() { prepare_helpers() {
SVGElement.prototype.getX = function () { SVGElement.prototype.getX = function () {
return +this.getAttribute("x"); return +this.getAttribute('x');
}; };
SVGElement.prototype.getY = function () { SVGElement.prototype.getY = function () {
return +this.getAttribute("y"); return +this.getAttribute('y');
}; };
SVGElement.prototype.getWidth = function () { SVGElement.prototype.getWidth = function () {
return +this.getAttribute("width"); return +this.getAttribute('width');
}; };
SVGElement.prototype.getHeight = function () { SVGElement.prototype.getHeight = function () {
return +this.getAttribute("height"); return +this.getAttribute('height');
}; };
SVGElement.prototype.getEndX = function () { SVGElement.prototype.getEndX = function () {
return this.getX() + this.getWidth(); return this.getX() + this.getWidth();
@ -89,40 +92,45 @@ export default class Bar {
} }
draw_bar() { draw_bar() {
this.$bar = createSVG("rect", { this.$bar = createSVG('rect', {
x: this.x, x: this.x,
y: this.y, y: this.y,
width: this.width, width: this.width,
height: this.height, height: this.height,
rx: this.corner_radius, rx: this.corner_radius,
ry: this.corner_radius, ry: this.corner_radius,
class: "bar", class:
'bar' +
(/^((?!chrome|android).)*safari/i.test(navigator.userAgent) &&
!this.task.important
? ' safari'
: ''),
append_to: this.bar_group, append_to: this.bar_group,
}); });
animateSVG(this.$bar, "width", 0, this.width); animateSVG(this.$bar, 'width', 0, this.width);
if (this.invalid) { if (this.invalid) {
this.$bar.classList.add("bar-invalid"); this.$bar.classList.add('bar-invalid');
} }
} }
draw_expected_progress_bar() { draw_expected_progress_bar() {
if (this.invalid) return; if (this.invalid) return;
this.$expected_bar_progress = createSVG("rect", { this.$expected_bar_progress = createSVG('rect', {
x: this.x, x: this.x,
y: this.y, y: this.y,
width: this.expected_progress_width, width: this.expected_progress_width,
height: this.height, height: this.height,
rx: this.corner_radius, rx: this.corner_radius,
ry: this.corner_radius, ry: this.corner_radius,
class: "bar-expected-progress", class: 'bar-expected-progress',
append_to: this.bar_group, append_to: this.bar_group,
}); });
animateSVG( animateSVG(
this.$expected_bar_progress, this.$expected_bar_progress,
"width", 'width',
0, 0,
this.expected_progress_width, this.expected_progress_width,
); );
@ -130,33 +138,33 @@ export default class Bar {
draw_progress_bar() { draw_progress_bar() {
if (this.invalid) return; if (this.invalid) return;
this.$bar_progress = createSVG("rect", { this.$bar_progress = createSVG('rect', {
x: this.x, x: this.x,
y: this.y, y: this.y,
width: this.progress_width, width: this.progress_width,
height: this.height, height: this.height,
rx: this.corner_radius, rx: this.corner_radius,
ry: this.corner_radius, ry: this.corner_radius,
class: "bar-progress", class: 'bar-progress',
append_to: this.bar_group, append_to: this.bar_group,
}); });
const x = (date_utils.diff(this.task._start, this.gantt.gantt_start, 'hour') / const x =
(date_utils.diff(this.task._start, this.gantt.gantt_start, 'hour') /
this.gantt.options.step) * this.gantt.options.step) *
this.gantt.options.column_width; this.gantt.options.column_width;
let $date_highlight = document.createElement("div"); let $date_highlight = document.createElement('div');
$date_highlight.id = `${this.task.id}-highlight` $date_highlight.id = `${this.task.id}-highlight`;
$date_highlight.classList.add('date-highlight') $date_highlight.classList.add('date-highlight');
$date_highlight.style.height = this.height * 0.8 + 'px' $date_highlight.style.height = this.height * 0.8 + 'px';
$date_highlight.style.width = this.width + 'px' $date_highlight.style.width = this.width + 'px';
$date_highlight.style.top = this.gantt.options.header_height - 25 + 'px' $date_highlight.style.top =
$date_highlight.style.left = x + 'px' this.gantt.options.header_height - 25 + 'px';
this.$date_highlight = $date_highlight $date_highlight.style.left = x + 'px';
this.gantt.$lower_header.prepend($date_highlight) this.$date_highlight = $date_highlight;
this.gantt.$lower_header.prepend($date_highlight);
animateSVG(this.$bar_progress, 'width', 0, this.progress_width);
animateSVG(this.$bar_progress, "width", 0, this.progress_width);
} }
draw_label() { draw_label() {
@ -166,22 +174,23 @@ export default class Bar {
x_coord = this.x + this.image_size + 5; x_coord = this.x + this.image_size + 5;
} }
createSVG("text", { createSVG('text', {
x: x_coord, x: x_coord,
y: this.y + this.height / 2, y: this.y + this.height / 2,
innerHTML: this.task.name, innerHTML: this.task.name,
class: "bar-label", class: 'bar-label',
append_to: this.bar_group, append_to: this.bar_group,
}); });
// labels get BBox in the next tick // labels get BBox in the next tick
requestAnimationFrame(() => this.update_label_position()); requestAnimationFrame(() => this.update_label_position());
} }
draw_thumbnail() { draw_thumbnail() {
let x_offset = 10, y_offset = 2; let x_offset = 10,
y_offset = 2;
let defs, clipPath; let defs, clipPath;
defs = createSVG('defs', { defs = createSVG('defs', {
append_to: this.bar_group append_to: this.bar_group,
}); });
createSVG('rect', { createSVG('rect', {
@ -192,17 +201,17 @@ export default class Bar {
height: this.image_size, height: this.image_size,
rx: '15', rx: '15',
class: 'img_mask', class: 'img_mask',
append_to: defs append_to: defs,
}); });
clipPath = createSVG('clipPath', { clipPath = createSVG('clipPath', {
id: 'clip_' + this.task.id, id: 'clip_' + this.task.id,
append_to: defs append_to: defs,
}); });
createSVG('use', { createSVG('use', {
href: '#rect_' + this.task.id, href: '#rect_' + this.task.id,
append_to: clipPath append_to: clipPath,
}); });
createSVG('image', { createSVG('image', {
@ -213,7 +222,7 @@ export default class Bar {
class: 'bar-img', class: 'bar-img',
href: this.task.thumbnail, href: this.task.thumbnail,
clipPath: 'clip_' + this.task.id, clipPath: 'clip_' + this.task.id,
append_to: this.bar_group append_to: this.bar_group,
}); });
} }
@ -222,57 +231,39 @@ export default class Bar {
const bar = this.$bar; const bar = this.$bar;
const handle_width = 8; const handle_width = 8;
if (!this.gantt.options.dates_readonly) {
createSVG("rect", { createSVG('rect', {
x: bar.getX() + bar.getWidth() + handle_width - 4, x: bar.getX() + bar.getWidth() + handle_width - 4,
y: bar.getY() + 1, y: bar.getY() + 1,
width: handle_width, width: handle_width,
height: this.height - 2, height: this.height - 2,
rx: this.corner_radius, rx: this.corner_radius,
ry: this.corner_radius, ry: this.corner_radius,
class: "handle right", class: 'handle right',
append_to: this.handle_group, append_to: this.handle_group,
}); });
createSVG("rect", { createSVG('rect', {
x: bar.getX() - handle_width - 4, x: bar.getX() - handle_width - 4,
y: bar.getY() + 1, y: bar.getY() + 1,
width: handle_width, width: handle_width,
height: this.height - 2, height: this.height - 2,
rx: this.corner_radius, rx: this.corner_radius,
ry: this.corner_radius, ry: this.corner_radius,
class: "handle left", class: 'handle left',
append_to: this.handle_group,
});
this.$handle_progress = createSVG("polygon", {
points: this.get_progress_polygon_points().join(","),
class: "handle progress",
append_to: this.handle_group, append_to: this.handle_group,
}); });
} }
if (!this.gantt.options.progress_readonly) {
get_progress_polygon_points() {
const bar_progress = this.$bar_progress; const bar_progress = this.$bar_progress;
let icon_width = 10; this.$handle_progress = createSVG('circle', {
let icon_height = 15; cx: bar_progress.getEndX(),
cy: bar_progress.getY() + bar_progress.getHeight() / 2,
return [ r: 5,
bar_progress.getEndX() - icon_width / 2, class: 'handle progress',
bar_progress.getY() + bar_progress.getHeight() / 2, append_to: this.handle_group,
});
bar_progress.getEndX(), }
bar_progress.getY() + bar_progress.getHeight() / 2 - icon_height / 2,
bar_progress.getEndX() + icon_width / 2,
bar_progress.getY() + bar_progress.getHeight() / 2,
bar_progress.getEndX(),
bar_progress.getY() + bar_progress.getHeight() / 2 + icon_height / 2,
bar_progress.getEndX() - icon_width / 2,
bar_progress.getY() + bar_progress.getHeight() / 2,
];
} }
bind() { bind() {
@ -282,34 +273,49 @@ export default class Bar {
setup_click_event() { setup_click_event() {
let task_id = this.task.id; let task_id = this.task.id;
$.on(this.group, "mouseover", (e) => { $.on(this.group, 'mouseover', (e) => {
this.gantt.trigger_event("hover", [this.task, e.screenX, e.screenY, e]) this.gantt.trigger_event('hover', [
}) this.task,
e.screenX,
let timeout; e.screenY,
$.on(this.group, "mouseenter", (e) => timeout = setTimeout(() => { e,
this.show_popup(e.offsetX) ]);
document.querySelector(`#${task_id}-highlight`).style.display = 'block'
}, 200))
$.on(this.group, "mouseleave", () => {
clearTimeout(timeout)
this.gantt.popup?.hide?.()
document.querySelector(`#${task_id}-highlight`).style.display = 'none'
})
$.on(this.group, this.gantt.options.popup_trigger, () => {
this.gantt.trigger_event("click", [this.task]);
}); });
$.on(this.group, "dblclick", (e) => { let timeout;
$.on(
this.group,
'mouseenter',
(e) =>
(timeout = setTimeout(() => {
this.show_popup(e.offsetX || e.layerX);
document.getElementById(
`${task_id}-highlight`,
).style.display = 'block';
}, 200)),
);
$.on(this.group, 'mouseleave', () => {
clearTimeout(timeout);
this.gantt.popup?.hide?.();
document.getElementById(`${task_id}-highlight`).style.display =
'none';
});
$.on(this.group, 'click', () => {
this.gantt.trigger_event('click', [this.task]);
});
$.on(this.group, 'dblclick', (e) => {
if (this.action_completed) { if (this.action_completed) {
// just finished a move action, wait for a few seconds // just finished a move action, wait for a few seconds
return; return;
} }
this.group.classList.remove('active');
if (this.gantt.popup)
this.gantt.popup.parent.classList.remove('hidden');
this.gantt.trigger_event("double_click", [this.task]); this.gantt.trigger_event('double_click', [this.task]);
}); });
} }
@ -318,12 +324,12 @@ export default class Bar {
const start_date = date_utils.format( const start_date = date_utils.format(
this.task._start, this.task._start,
"MMM D", 'MMM D',
this.gantt.options.language, this.gantt.options.language,
); );
const end_date = date_utils.format( const end_date = date_utils.format(
date_utils.add(this.task._end, -1, "second"), date_utils.add(this.task._end, -1, 'second'),
"MMM D", 'MMM D',
this.gantt.options.language, this.gantt.options.language,
); );
const subtitle = `${start_date} - ${end_date}<br/>Progress: ${this.task.progress}`; const subtitle = `${start_date} - ${end_date}<br/>Progress: ${this.task.progress}`;
@ -352,12 +358,12 @@ export default class Bar {
width = null; width = null;
return; return;
} }
this.update_attr(bar, "x", x); this.update_attr(bar, 'x', x);
this.$date_highlight.style.left = x + 'px' this.$date_highlight.style.left = x + 'px';
} }
if (width) { if (width) {
this.update_attr(bar, "width", width); this.update_attr(bar, 'width', width);
this.$date_highlight.style.width = width + 'px' this.$date_highlight.style.width = width + 'px';
} }
this.update_label_position(); this.update_label_position();
this.update_handle_position(); this.update_handle_position();
@ -378,8 +384,8 @@ export default class Bar {
let barWidthLimit = this.$bar.getX() + this.$bar.getWidth(); let barWidthLimit = this.$bar.getX() + this.$bar.getWidth();
let newLabelX = label.getX() + x; let newLabelX = label.getX() + x;
let newImgX = img && img.getX() + x || 0; let newImgX = (img && img.getX() + x) || 0;
let imgWidth = img && img.getBBox().width + 7 || 7; let imgWidth = (img && img.getBBox().width + 7) || 7;
let labelEndX = newLabelX + label.getBBox().width + 7; let labelEndX = newLabelX + label.getBBox().width + 7;
let viewportCentral = sx + container.clientWidth / 2; let viewportCentral = sx + container.clientWidth / 2;
@ -391,13 +397,16 @@ export default class Bar {
img.setAttribute('x', newImgX); img.setAttribute('x', newImgX);
img_mask.setAttribute('x', newImgX); img_mask.setAttribute('x', newImgX);
} }
} else if ((newLabelX - imgWidth) > this.$bar.getX() && x < 0 && labelEndX > viewportCentral) { } else if (
newLabelX - imgWidth > this.$bar.getX() &&
x < 0 &&
labelEndX > viewportCentral
) {
label.setAttribute('x', newLabelX); label.setAttribute('x', newLabelX);
if (img) { if (img) {
img.setAttribute('x', newImgX); img.setAttribute('x', newImgX);
img_mask.setAttribute('x', newImgX); img_mask.setAttribute('x', newImgX);
} }
} }
} }
@ -417,17 +426,17 @@ export default class Bar {
if (!changed) return; if (!changed) return;
this.gantt.trigger_event("date_change", [ this.gantt.trigger_event('date_change', [
this.task, this.task,
new_start_date, new_start_date,
date_utils.add(new_end_date, -1, "second"), date_utils.add(new_end_date, -1, 'second'),
]); ]);
} }
progress_changed() { progress_changed() {
const new_progress = this.compute_progress(); const new_progress = this.compute_progress();
this.task.progress = new_progress; this.task.progress = new_progress;
this.gantt.trigger_event("progress_change", [this.task, new_progress]); this.gantt.trigger_event('progress_change', [this.task, new_progress]);
} }
set_action_completed() { set_action_completed() {
@ -438,16 +447,27 @@ export default class Bar {
compute_start_end_date() { compute_start_end_date() {
const bar = this.$bar; const bar = this.$bar;
const x_in_units = bar.getX() / this.gantt.options.column_width; const x_in_units = bar.getX() / this.gantt.options.column_width;
const new_start_date = date_utils.add( let new_start_date = date_utils.add(
this.gantt.gantt_start, this.gantt.gantt_start,
x_in_units * this.gantt.options.step, x_in_units * this.gantt.options.step,
"hour", 'hour',
); );
const start_offset =
this.gantt.gantt_start.getTimezoneOffset() -
new_start_date.getTimezoneOffset();
if (start_offset) {
new_start_date = date_utils.add(
new_start_date,
start_offset,
'minute',
);
}
const width_in_units = bar.getWidth() / this.gantt.options.column_width; const width_in_units = bar.getWidth() / this.gantt.options.column_width;
const new_end_date = date_utils.add( const new_end_date = date_utils.add(
new_start_date, new_start_date,
width_in_units * this.gantt.options.step, width_in_units * this.gantt.options.step,
"hour", 'hour',
); );
return { new_start_date, new_end_date }; return { new_start_date, new_end_date };
@ -461,7 +481,7 @@ export default class Bar {
compute_expected_progress() { compute_expected_progress() {
this.expected_progress = this.expected_progress =
date_utils.diff(date_utils.today(), this.task._start, "hour") / date_utils.diff(date_utils.today(), this.task._start, 'hour') /
this.gantt.options.step; this.gantt.options.step;
this.expected_progress = this.expected_progress =
((this.expected_progress < this.duration ((this.expected_progress < this.duration
@ -476,11 +496,11 @@ export default class Bar {
const task_start = this.task._start; const task_start = this.task._start;
const gantt_start = this.gantt.gantt_start; const gantt_start = this.gantt.gantt_start;
const diff = date_utils.diff(task_start, gantt_start, "hour"); const diff = date_utils.diff(task_start, gantt_start, 'hour');
let x = (diff / step) * column_width; let x = (diff / step) * column_width;
if (this.gantt.view_is("Month")) { if (this.gantt.view_is('Month')) {
const diff = date_utils.diff(task_start, gantt_start, "day"); const diff = date_utils.diff(task_start, gantt_start, 'day');
x = (diff * column_width) / 30; x = (diff * column_width) / 30;
} }
this.x = x; this.x = x;
@ -495,7 +515,7 @@ export default class Bar {
compute_duration() { compute_duration() {
this.duration = this.duration =
date_utils.diff(this.task._end, this.task._start, "hour") / date_utils.diff(this.task._end, this.task._start, 'hour') /
this.gantt.options.step; this.gantt.options.step;
} }
@ -504,7 +524,7 @@ export default class Bar {
rem, rem,
position; position;
if (this.gantt.view_is("Week")) { if (this.gantt.view_is('Week')) {
rem = dx % (this.gantt.options.column_width / 7); rem = dx % (this.gantt.options.column_width / 7);
position = position =
odx - odx -
@ -512,7 +532,7 @@ export default class Bar {
(rem < this.gantt.options.column_width / 14 (rem < this.gantt.options.column_width / 14
? 0 ? 0
: this.gantt.options.column_width / 7); : this.gantt.options.column_width / 7);
} else if (this.gantt.view_is("Month")) { } else if (this.gantt.view_is('Month')) {
rem = dx % (this.gantt.options.column_width / 30); rem = dx % (this.gantt.options.column_width / 30);
position = position =
odx - odx -
@ -542,10 +562,10 @@ export default class Bar {
update_expected_progressbar_position() { update_expected_progressbar_position() {
if (this.invalid) return; if (this.invalid) return;
this.$expected_bar_progress.setAttribute("x", this.$bar.getX()); this.$expected_bar_progress.setAttribute('x', this.$bar.getX());
this.compute_expected_progress(); this.compute_expected_progress();
this.$expected_bar_progress.setAttribute( this.$expected_bar_progress.setAttribute(
"width", 'width',
this.gantt.options.column_width * this.gantt.options.column_width *
this.duration * this.duration *
(this.expected_progress / 100) || 0, (this.expected_progress / 100) || 0,
@ -554,9 +574,9 @@ export default class Bar {
update_progressbar_position() { update_progressbar_position() {
if (this.invalid || this.gantt.options.readonly) return; if (this.invalid || this.gantt.options.readonly) return;
this.$bar_progress.setAttribute("x", this.$bar.getX()); this.$bar_progress.setAttribute('x', this.$bar.getX());
this.$bar_progress.setAttribute( this.$bar_progress.setAttribute(
"width", 'width',
this.$bar.getWidth() * (this.task.progress / 100), this.$bar.getWidth() * (this.task.progress / 100),
); );
} }
@ -564,31 +584,42 @@ export default class Bar {
update_label_position() { update_label_position() {
const img_mask = this.bar_group.querySelector('.img_mask') || ''; const img_mask = this.bar_group.querySelector('.img_mask') || '';
const bar = this.$bar, const bar = this.$bar,
label = this.group.querySelector(".bar-label"), label = this.group.querySelector('.bar-label'),
img = this.group.querySelector('.bar-img'); img = this.group.querySelector('.bar-img');
let padding = 5; let padding = 5;
let x_offset_label_img = this.image_size + 10; let x_offset_label_img = this.image_size + 10;
const labelWidth = label.getBBox().width const labelWidth = label.getBBox().width;
const barWidth = bar.getWidth() const barWidth = bar.getWidth();
if (labelWidth > barWidth) { if (labelWidth > barWidth) {
label.classList.add("big"); label.classList.add('big');
if (img) { if (img) {
img.setAttribute('x', bar.getX() + bar.getWidth() + padding); img.setAttribute('x', bar.getX() + bar.getWidth() + padding);
img_mask.setAttribute('x', bar.getX() + bar.getWidth() + padding); img_mask.setAttribute(
label.setAttribute('x', bar.getX() + bar.getWidth() + x_offset_label_img); 'x',
bar.getX() + bar.getWidth() + padding,
);
label.setAttribute(
'x',
bar.getX() + bar.getWidth() + x_offset_label_img,
);
} else { } else {
label.setAttribute('x', bar.getX() + bar.getWidth() + padding); label.setAttribute('x', bar.getX() + bar.getWidth() + padding);
} }
} else { } else {
label.classList.remove("big"); label.classList.remove('big');
if (img) { if (img) {
img.setAttribute('x', bar.getX() + padding); img.setAttribute('x', bar.getX() + padding);
img_mask.setAttribute('x', bar.getX() + padding); img_mask.setAttribute('x', bar.getX() + padding);
label.setAttribute('x', bar.getX() + barWidth / 2 + x_offset_label_img); label.setAttribute(
'x',
bar.getX() + barWidth / 2 + x_offset_label_img,
);
} else { } else {
label.setAttribute('x', bar.getX() + barWidth / 2 - labelWidth / 2); label.setAttribute(
'x',
bar.getX() + barWidth / 2 - labelWidth / 2,
);
} }
} }
} }
@ -597,13 +628,13 @@ export default class Bar {
if (this.invalid || this.gantt.options.readonly) return; if (this.invalid || this.gantt.options.readonly) return;
const bar = this.$bar; const bar = this.$bar;
this.handle_group this.handle_group
.querySelector(".handle.left") .querySelector('.handle.left')
.setAttribute("x", bar.getX() - 12); .setAttribute('x', bar.getX() - 12);
this.handle_group this.handle_group
.querySelector(".handle.right") .querySelector('.handle.right')
.setAttribute("x", bar.getEndX() + 4); .setAttribute('x', bar.getEndX() + 4);
const handle = this.group.querySelector(".handle.progress"); const handle = this.group.querySelector('.handle.progress');
handle && handle.setAttribute("points", this.get_progress_polygon_points()); handle && handle.setAttribute('cx', this.$bar_progress.getEndX());
} }
update_arrow_position() { update_arrow_position() {
@ -618,6 +649,6 @@ function isFunction(functionToCheck) {
let getType = {}; let getType = {};
return ( return (
functionToCheck && functionToCheck &&
getType.toString.call(functionToCheck) === "[object Function]" getType.toString.call(functionToCheck) === '[object Function]'
); );
} }

99
src/dark.css Normal file
View File

@ -0,0 +1,99 @@
:root {
--bar-color-dark: #616161;
--bar-stroke-dark: #c6ccd2;
--border-color-dark: #616161;
--light-bg-dark: #3e3e3e;
--light-border-color-dark: #3e3e3e;
--text-muted-dark: #eee;
--text-light-dark: #ececec;
--text-color-dark: #f7f7f7;
--blue-dark: #8a8aff;
}
.dark>.gantt-container .gantt {
& .grid-row {
fill: #252525;
}
/* & .grid-row:nth-child(even) {
fill: var(--light-bg-dark);
} */
& .row-line {
stroke: var(--light-border-color-dark);
}
& .tick {
stroke: var(--border-color-dark);
}
& .holiday-highlight {
fill: var(--light-bg-dark);
}
& .arrow {
stroke: var(--text-muted-dark);
}
& .bar {
fill: var(--bar-color-dark);
stroke: none;
}
& .bar-progress {
fill: var(--blue-dark);
}
& .bar-invalid {
fill: transparent;
stroke: var(--bar-stroke-dark);
&~.bar-label {
fill: var(--text-light-dark);
}
}
& .bar-label.big {
fill: var(--text-light-dark);
}
& .bar-wrapper {
&:hover {
.bar {
fill: lighten(var(--bar-color-dark, 5));
}
& .bar-progress {
fill: lighten(var(--blue-dark, 5));
}
}
&.active {
.bar {
fill: lighten(var(--bar-color-dark, 5));
}
& .bar-progress {
fill: lighten(var(--blue-dark, 5));
}
}
}
}
.dark>.gantt-container {
& .grid-header {
background-color: #252525;
}
& .popup-wrapper {
background-color: #333;
& .title {
border-color: lighten(var(--blue-dark, 5));
}
& .pointer {
border-top-color: #333;
}
}
}

View File

@ -1,97 +0,0 @@
$bar-color-dark: #616161;
$bar-stroke-dark: #c6ccd2;
$border-color-dark: #616161;
$light-bg-dark: #3e3e3e;
$light-border-color-dark: #3e3e3e;
$text-muted-dark: #eee;
$text-light-dark: #ececec;
$text-color-dark: #f7f7f7;
$blue-dark: #8a8aff;
.dark > .gantt-container .gantt {
.grid-header {
fill: #252525;
stroke: $border-color-dark;
}
.grid-row {
fill: #252525;
}
.grid-row:nth-child(even) {
fill: $light-bg-dark;
}
.row-line {
stroke: $light-border-color-dark;
}
.tick {
stroke: $border-color-dark;
}
.today-highlight {
opacity: 0.2;
}
.arrow {
stroke: $text-muted-dark;
}
.bar {
fill: $bar-color-dark;
stroke: none;
}
.bar-progress {
fill: $blue-dark;
}
.bar-invalid {
fill: transparent;
stroke: $bar-stroke-dark;
& ~ .bar-label {
fill: $text-light-dark;
}
}
.bar-label.big {
fill: $text-light-dark;
}
.bar-wrapper {
&:hover {
.bar {
fill: lighten($bar-color-dark, 5);
}
.bar-progress {
fill: lighten($blue-dark, 5);
}
}
&.active {
.bar {
fill: lighten($bar-color-dark, 5);
}
.bar-progress {
fill: lighten($blue-dark, 5);
}
}
}
.upper-text {
fill: #a2a2a2;
}
.lower-text {
fill: $text-color-dark;
}
}
.dark > .gantt-container {
.popup-wrapper {
background-color: #333;
.title {
border-color: lighten($blue-dark, 5);
}
.pointer {
border-top-color: #333;
}
}
}

View File

@ -1,56 +1,56 @@
const YEAR = "year"; const YEAR = 'year';
const MONTH = "month"; const MONTH = 'month';
const DAY = "day"; const DAY = 'day';
const HOUR = "hour"; const HOUR = 'hour';
const MINUTE = "minute"; const MINUTE = 'minute';
const SECOND = "second"; const SECOND = 'second';
const MILLISECOND = "millisecond"; const MILLISECOND = 'millisecond';
const SHORTENED = { const SHORTENED = {
January: "Jan", January: 'Jan',
February: "Feb", February: 'Feb',
March: "Mar", March: 'Mar',
April: "Apr", April: 'Apr',
May: "May", May: 'May',
June: "Jun", June: 'Jun',
July: "Jul", July: 'Jul',
August: "Aug", August: 'Aug',
September: "Sep", September: 'Sep',
October: "Oct", October: 'Oct',
November: "Nov", November: 'Nov',
December: "Dec" December: 'Dec',
}; };
export default { export default {
parse_duration(duration) { parse_duration(duration) {
const regex = /([0-9])+(y|m|d|h|min|s|ms)/gm; const regex = /([0-9]+)(y|m|d|h|min|s|ms)/gm;
const matches = regex.exec(duration); const matches = regex.exec(duration);
console.log(matches);
if (matches !== null) { if (matches !== null) {
if (matches[2] === "y") { if (matches[2] === 'y') {
return { duration: parseInt(matches[1]), scale: `year` }; return { duration: parseInt(matches[1]), scale: `year` };
} else if (matches[2] === "m") { } else if (matches[2] === 'm') {
return { duration: parseInt(matches[1]), scale: `month` }; return { duration: parseInt(matches[1]), scale: `month` };
} else if (matches[2] === "d") { } else if (matches[2] === 'd') {
return { duration: parseInt(matches[1]), scale: `day` }; return { duration: parseInt(matches[1]), scale: `day` };
} else if (matches[2] === "h") { } else if (matches[2] === 'h') {
return { duration: parseInt(matches[1]), scale: `hour` }; return { duration: parseInt(matches[1]), scale: `hour` };
} else if (matches[2] === "min") { } else if (matches[2] === 'min') {
return { duration: parseInt(matches[1]), scale: `minute` }; return { duration: parseInt(matches[1]), scale: `minute` };
} else if (matches[2] === "s") { } else if (matches[2] === 's') {
return { duration: parseInt(matches[1]), scale: `second` }; return { duration: parseInt(matches[1]), scale: `second` };
} else if (matches[2] === "ms") { } else if (matches[2] === 'ms') {
return { duration: parseInt(matches[1]), scale: `millisecond` }; return { duration: parseInt(matches[1]), scale: `millisecond` };
} }
} }
}, },
parse(date, date_separator = "-", time_separator = /[.:]/) { parse(date, date_separator = '-', time_separator = /[.:]/) {
if (date instanceof Date) { if (date instanceof Date) {
return date; return date;
} }
if (typeof date === "string") { if (typeof date === 'string') {
let date_parts, time_parts; let date_parts, time_parts;
const parts = date.split(" "); const parts = date.split(' ');
date_parts = parts[0] date_parts = parts[0]
.split(date_separator) .split(date_separator)
.map((val) => parseInt(val, 10)); .map((val) => parseInt(val, 10));
@ -63,7 +63,7 @@ export default {
if (time_parts && time_parts.length) { if (time_parts && time_parts.length) {
if (time_parts.length === 4) { if (time_parts.length === 4) {
time_parts[3] = "0." + time_parts[3]; time_parts[3] = '0.' + time_parts[3];
time_parts[3] = parseFloat(time_parts[3]) * 1000; time_parts[3] = parseFloat(time_parts[3]) * 1000;
} }
vals = vals.concat(time_parts); vals = vals.concat(time_parts);
@ -74,7 +74,7 @@ export default {
to_string(date, with_time = false) { to_string(date, with_time = false) {
if (!(date instanceof Date)) { if (!(date instanceof Date)) {
throw new TypeError("Invalid argument type"); throw new TypeError('Invalid argument type');
} }
const vals = this.get_date_values(date).map((val, i) => { const vals = this.get_date_values(date).map((val, i) => {
if (i === 1) { if (i === 1) {
@ -83,20 +83,20 @@ export default {
} }
if (i === 6) { if (i === 6) {
return padStart(val + "", 3, "0"); return padStart(val + '', 3, '0');
} }
return padStart(val + "", 2, "0"); return padStart(val + '', 2, '0');
}); });
const date_string = `${vals[0]}-${vals[1]}-${vals[2]}`; const date_string = `${vals[0]}-${vals[1]}-${vals[2]}`;
const time_string = `${vals[3]}:${vals[4]}:${vals[5]}.${vals[6]}`; const time_string = `${vals[3]}:${vals[4]}:${vals[5]}.${vals[6]}`;
return date_string + (with_time ? " " + time_string : ""); return date_string + (with_time ? ' ' + time_string : '');
}, },
format(date, format_string = "YYYY-MM-DD HH:mm:ss.SSS", lang = "en") { format(date, format_string = 'YYYY-MM-DD HH:mm:ss.SSS', lang = 'en') {
const dateTimeFormat = new Intl.DateTimeFormat(lang, { const dateTimeFormat = new Intl.DateTimeFormat(lang, {
month: "long", month: 'long',
}); });
const month_name = dateTimeFormat.format(date); const month_name = dateTimeFormat.format(date);
const month_name_capitalized = const month_name_capitalized =
@ -146,8 +146,8 @@ export default {
months = days / 30; months = days / 30;
years = months / 12; years = months / 12;
if (!scale.endsWith("s")) { if (!scale.endsWith('s')) {
scale += "s"; scale += 's';
} }
return Math.floor( return Math.floor(
@ -251,9 +251,9 @@ export default {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
function padStart(str, targetLength, padString) { function padStart(str, targetLength, padString) {
str = str + ""; str = str + '';
targetLength = targetLength >> 0; targetLength = targetLength >> 0;
padString = String(typeof padString !== "undefined" ? padString : " "); padString = String(typeof padString !== 'undefined' ? padString : ' ');
if (str.length > targetLength) { if (str.length > targetLength) {
return String(str); return String(str);
} else { } else {

306
src/gantt.css Normal file
View File

@ -0,0 +1,306 @@
@import './dark.css';
:root {
--bar-color: #fff;
--bar-color-important: #94c4f4;
--bar-stroke: #fff;
--dark-stroke-color: #e0e0e0;
--stroke-color: #ebeef0;
--light-bg: #f5f5f5;
--light-border-color: #ebeff2;
--light-yellow: #f6e796;
--holiday-color: #f9fafa;
--text-muted: #666;
--text-grey: #98a1a9;
--text-light: #fff;
--text-dark: #111;
--progress: #ebeef0;
--handle-color: #dcdce4;
--handle-color-important: #94c4f4;
--light-blue: #c4c4e9;
--middle-blue: #62b2f9;
--dark-blue: #2c94ec;
}
.gantt-container {
line-height: 14.5px;
position: relative;
overflow: auto;
font-size: 12px;
height: 500px;
& .popup-wrapper {
position: absolute;
top: 0;
left: 0;
background: #171b1f;
padding: 10px;
border-radius: 5px;
width: max-content;
&.hidden {
opacity: 0 !important;
}
& .title {
margin-bottom: 5px;
text-align: -webkit-center;
text-align: center;
color: var(--text-light);
}
& .subtitle {
color: var(--text-grey);
}
& .pointer {
position: absolute;
height: 5px;
margin: 0 0 0 -5px;
border: 5px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.8);
}
}
& .grid-header {
background-color: #ffffff;
position: sticky;
top: 0;
left: 0;
z-index: 10;
}
& .lower-text,
& .upper-text {
text-anchor: middle;
color: var(--text-dark);
}
& .upper-header {
height: 40px;
}
& .lower-header {
height: 30px;
}
& .lower-text {
font-size: 14px;
position: absolute;
width: fit-content;
}
& .upper-text {
position: absolute;
width: fit-content;
font-weight: 500;
font-size: 16px;
}
& .current-upper {
position: fixed;
}
& .side-header {
position: fixed;
padding: 0 10px;
margin-right: 10px;
background: white;
line-height: 20px;
font-weight: 400;
}
& .today-button,
& .viewmode-select {
background: #f4f5f6;
text-align: -webkit-center;
text-align: center;
height: 25px;
border-radius: 8px;
border: none;
color: var(--text-dark);
padding: 4px 10px;
border-radius: 8px;
height: 25px;
}
& .viewmode-select {
outline: none !important;
padding: 4px 8px;
margin-right: 4px;
/* -webkit-appearance: none; */
/* -moz-appearance: none; */
text-indent: 1px;
text-overflow: '';
}
& .date-highlight {
background-color: var(--progress);
border-radius: 12px;
position: absolute;
display: none;
}
& .current-highlight {
position: absolute;
background: var(--dark-blue);
width: 1px;
}
& .current-date-highlight {
background: var(--dark-blue);
color: var(--text-light);
padding: 4px 8px;
border-radius: 200px;
}
}
.gantt {
user-select: none;
-webkit-user-select: none;
position: absolute;
& .grid-background {
fill: none;
}
& .grid-row {
fill: #ffffff;
}
& .row-line {
stroke: var(--light-border-color);
}
& .tick {
stroke: var(--stroke-color);
stroke-width: 0.4;
&.thick {
stroke: var(--dark-stroke-color);
stroke-width: 0.7;
}
}
& .holiday-highlight {
fill: var(--holiday-color);
}
& .arrow {
fill: none;
stroke: #9fa9b1;
stroke-width: 1;
}
& .bar-wrapper .bar {
fill: var(--bar-color);
stroke: var(--bar-stroke);
stroke-width: 0;
transition: stroke-width 0.3s ease;
}
& .bar-progress {
fill: var(--progress);
}
& .bar-expected-progress {
fill: var(--light-blue);
}
& .bar-invalid {
fill: transparent;
stroke: var(--bar-stroke);
stroke-width: 1;
stroke-dasharray: 5;
&~.bar-label {
fill: var(--text-light);
}
}
& .bar-label {
fill: var(--text-dark);
dominant-baseline: central;
font-family: Helvetica;
font-size: 13px;
font-weight: 400;
&.big {
fill: var(--text-dark);
text-anchor: start;
}
}
& .bar-wrapper.important {
& .bar {
fill: var(--bar-color-important);
}
& .bar-progress {
fill: var(--dark-blue);
}
& .bar-label {
fill: var(--text-light);
}
& .handle {
fill: var(--handle-color-important);
}
& .handle.progress {
fill: var(--text-light);
}
}
& .handle {
fill: var(--handle-color);
cursor: ew-resize;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
& .handle.progress {
fill: var(--text-muted);
}
& .bar-wrapper {
cursor: pointer;
&.active {
& .handle {
visibility: visible;
opacity: 1;
}
}
& .bar {
-webkit-filter: drop-shadow(3px 3px 2px rgba(0, 0, 0, 0.7));
filter: drop-shadow(0 0 2px rgba(17, 43, 66, 0.16));
border-radius: 3px;
}
& .bar.safari {
outline: 1px solid black;
}
&:hover {
.bar {
transition: transform 0.3s ease;
}
.date-highlight {
display: block;
}
}
}
& .hide {
display: none;
}
}

View File

@ -1,316 +0,0 @@
@import "./dark.scss";
$bar-color: #fff !default;
$bar-color-important: #94c4f4 !default;
$bar-stroke: #fff !default;
$dark-stroke-color: #e0e0e0 !default;
$stroke-color: #EBEEF0 !default;
$light-bg: #f5f5f5 !default;
$light-border-color: #ebeff2 !default;
$light-yellow: #f6e796 !default;
$holiday-color: #F9FAFA !default;
$text-muted: #666 !default;
$text-grey: #98A1A9;
$text-light: #fff !default;
$text-dark: #111 !default;
$progress: #EBEEF0 !default;
$handle-color: #dcdce4 !default;
$handle-color-important: #94c4f4 !default;
$light-blue: #c4c4e9 !default;
$middle-blue: #62B2F9 !default;
$dark-blue: #2c94ec !default;
.gantt-container {
line-height: 14.5px;
.grid-header {
background-color: #ffffff;
position: sticky;
top: 0;
left: 0;
z-index: 10;
}
.lower-text,
.upper-text {
text-anchor: middle;
color: $text-dark;
}
.upper-header {
height: 40px;
}
.lower-header {
height: 30px;
}
.lower-text {
font-size: 14px;
position: absolute;
width: fit-content;
}
.upper-text {
position: absolute;
width: fit-content;
font-weight: 500;
font-size: 16px;
}
.current-upper {
position: fixed;
}
.side-header {
position: fixed;
padding: 0 10px;
margin-right: 10px;
background: white;
line-height: 20px;
font-weight: 400;
}
.today-button,
.viewmode-select {
background: #F4F5F6;
text-align: -webkit-center;
text-align: center;
height: 25px;
border-radius: 8px;
border: none;
color: $text-dark;
padding: 4px 10px;
border-radius: 8px;
height: 25px;
}
.viewmode-select {
outline: none !important;
padding: 4px 8px;
margin-right: 4px;
// Hide select icon
-webkit-appearance: none;
-moz-appearance: none;
text-indent: 1px;
text-overflow: '';
}
.date-highlight {
background-color: $progress;
border-radius: 12px;
position: absolute;
display: none;
}
.current-highlight {
position: absolute;
background: $dark-blue;
width: 1px;
}
.current-date-highlight {
background: $dark-blue;
color: $text-light;
padding: 4px 8px;
border-radius: 200px;
}
}
.gantt {
user-select: none;
-webkit-user-select: none;
position: absolute;
.grid-background {
fill: none;
}
.grid-row {
fill: #ffffff;
}
// .grid-row:nth-child(even) {
// fill: $light-bg;
// }
.row-line {
stroke: $light-border-color;
}
.tick {
stroke: $stroke-color;
stroke-width: 0.4;
&.thick {
stroke: $dark-stroke-color;
stroke-width: 0.7;
}
}
.holiday-highlight {
fill: $holiday-color;
}
.arrow {
fill: none;
stroke: #9FA9B1;
stroke-width: 1;
}
.bar-wrapper .bar {
fill: $bar-color;
stroke: $bar-stroke;
stroke-width: 0;
transition: stroke-width 0.3s ease;
}
.bar-progress {
fill: $progress;
}
.bar-expected-progress {
fill: $light-blue;
}
.bar-invalid {
fill: transparent;
stroke: $bar-stroke;
stroke-width: 1;
stroke-dasharray: 5;
&~.bar-label {
fill: $text-light;
}
}
.bar-label {
fill: $text-dark;
dominant-baseline: central;
// text-anchor: middle;
font-family: Helvetica;
font-size: 13px;
font-weight: 400;
&.big {
fill: $text-dark;
text-anchor: start;
}
}
.bar-wrapper.important {
.bar {
fill: $bar-color-important;
}
.bar-progress {
fill: $dark-blue;
}
.bar-label {
fill: $text-light;
}
.handle {
fill: $handle-color-important;
}
.handle.progress {
fill: $text-light;
}
}
.handle {
fill: $handle-color;
cursor: ew-resize;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
.handle.progress {
fill: $text-muted;
}
.bar-wrapper {
cursor: pointer;
outline: none;
&.active {
& .handle {
visibility: visible;
opacity: 1;
}
}
.bar {
-webkit-filter: drop-shadow(3px 3px 2px rgba(0, 0, 0, .7));
filter: drop-shadow(0 0 2px rgba(17, 43, 66, .16));
border-radius: 3px;
}
&:hover {
.bar {
transition: transform 0.3s ease;
}
.date-highlight {
display: block;
}
}
}
.hide {
display: none;
}
}
.gantt-container {
position: relative;
overflow: auto;
font-size: 12px;
height: 500px;
.popup-wrapper {
position: absolute;
top: 0;
left: 0;
background: #171B1F;
padding: 10px;
border-radius: 5px;
width: max-content;
&.hidden {
opacity: 0 !important;
}
.title {
margin-bottom: 5px;
text-align: -webkit-center;
text-align: center;
color: $text-light;
}
.subtitle {
color: $text-grey;
}
.pointer {
position: absolute;
height: 5px;
margin: 0 0 0 -5px;
border: 5px solid transparent;
border-bottom-color: rgba(0, 0, 0, 0.8);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -14,14 +14,14 @@ export default class Popup {
this.hide(); this.hide();
this.title = this.parent.querySelector(".title"); this.title = this.parent.querySelector('.title');
this.subtitle = this.parent.querySelector(".subtitle"); this.subtitle = this.parent.querySelector('.subtitle');
this.pointer = this.parent.querySelector(".pointer"); this.pointer = this.parent.querySelector('.pointer');
} }
show(options) { show(options) {
if (!options.target_element) { if (!options.target_element) {
throw new Error("target_element is required to show popup"); throw new Error('target_element is required to show popup');
} }
const target_element = options.target_element; const target_element = options.target_element;
@ -29,7 +29,7 @@ export default class Popup {
let html = this.custom_html(options.task); let html = this.custom_html(options.task);
html += '<div class="pointer"></div>'; html += '<div class="pointer"></div>';
this.parent.innerHTML = html; this.parent.innerHTML = html;
this.pointer = this.parent.querySelector(".pointer"); this.pointer = this.parent.querySelector('.pointer');
} else { } else {
// set data // set data
this.title.innerHTML = options.title; this.title.innerHTML = options.title;
@ -44,11 +44,12 @@ export default class Popup {
position_meta = options.target_element.getBBox(); position_meta = options.target_element.getBBox();
} }
this.parent.style.left = options.x - this.parent.clientWidth / 2 + "px"; this.parent.style.left = options.x - this.parent.clientWidth / 2 + 'px';
this.parent.style.top = position_meta.y + position_meta.height + 10 + "px"; this.parent.style.top =
position_meta.y + position_meta.height + 10 + 'px';
this.pointer.style.left = this.parent.clientWidth / 2 + "px"; this.pointer.style.left = this.parent.clientWidth / 2 + 'px';
this.pointer.style.top = "-15px"; this.pointer.style.top = '-15px';
// show // show
this.parent.style.opacity = 1; this.parent.style.opacity = 1;

View File

@ -1,16 +1,16 @@
export function $(expr, con) { export function $(expr, con) {
return typeof expr === "string" return typeof expr === 'string'
? (con || document).querySelector(expr) ? (con || document).querySelector(expr)
: expr || null; : expr || null;
} }
export function createSVG(tag, attrs) { export function createSVG(tag, attrs) {
const elem = document.createElementNS("http://www.w3.org/2000/svg", tag); const elem = document.createElementNS('http://www.w3.org/2000/svg', tag);
for (let attr in attrs) { for (let attr in attrs) {
if (attr === "append_to") { if (attr === 'append_to') {
const parent = attrs.append_to; const parent = attrs.append_to;
parent.appendChild(elem); parent.appendChild(elem);
} else if (attr === "innerHTML") { } else if (attr === 'innerHTML') {
elem.innerHTML = attrs.innerHTML; elem.innerHTML = attrs.innerHTML;
} else if (attr === 'clipPath') { } else if (attr === 'clipPath') {
elem.setAttribute('clip-path', 'url(#' + attrs[attr] + ')'); elem.setAttribute('clip-path', 'url(#' + attrs[attr] + ')');
@ -27,9 +27,9 @@ export function animateSVG(svgElement, attr, from, to) {
if (animatedSvgElement === svgElement) { if (animatedSvgElement === svgElement) {
// triggered 2nd time programmatically // triggered 2nd time programmatically
// trigger artificial click event // trigger artificial click event
const event = document.createEvent("HTMLEvents"); const event = document.createEvent('HTMLEvents');
event.initEvent("click", true, true); event.initEvent('click', true, true);
event.eventName = "click"; event.eventName = 'click';
animatedSvgElement.dispatchEvent(event); animatedSvgElement.dispatchEvent(event);
} }
} }
@ -39,31 +39,31 @@ function getAnimationElement(
attr, attr,
from, from,
to, to,
dur = "0.4s", dur = '0.4s',
begin = "0.1s", begin = '0.1s',
) { ) {
const animEl = svgElement.querySelector("animate"); const animEl = svgElement.querySelector('animate');
if (animEl) { if (animEl) {
$.attr(animEl, { $.attr(animEl, {
attributeName: attr, attributeName: attr,
from, from,
to, to,
dur, dur,
begin: "click + " + begin, // artificial click begin: 'click + ' + begin, // artificial click
}); });
return svgElement; return svgElement;
} }
const animateElement = createSVG("animate", { const animateElement = createSVG('animate', {
attributeName: attr, attributeName: attr,
from, from,
to, to,
dur, dur,
begin, begin,
calcMode: "spline", calcMode: 'spline',
values: from + ";" + to, values: from + ';' + to,
keyTimes: "0; 1", keyTimes: '0; 1',
keySplines: cubic_bezier("ease-out"), keySplines: cubic_bezier('ease-out'),
}); });
svgElement.appendChild(animateElement); svgElement.appendChild(animateElement);
@ -72,11 +72,11 @@ function getAnimationElement(
function cubic_bezier(name) { function cubic_bezier(name) {
return { return {
ease: ".25 .1 .25 1", ease: '.25 .1 .25 1',
linear: "0 0 1 1", linear: '0 0 1 1',
"ease-in": ".42 0 1 1", 'ease-in': '.42 0 1 1',
"ease-out": "0 0 .58 1", 'ease-out': '0 0 .58 1',
"ease-in-out": ".42 0 .58 1", 'ease-in-out': '.42 0 .58 1',
}[name]; }[name];
} }
@ -120,11 +120,11 @@ $.closest = (selector, element) => {
}; };
$.attr = (element, attr, value) => { $.attr = (element, attr, value) => {
if (!value && typeof attr === "string") { if (!value && typeof attr === 'string') {
return element.getAttribute(attr); return element.getAttribute(attr);
} }
if (typeof attr === "object") { if (typeof attr === 'object') {
for (let key in attr) { for (let key in attr) {
$.attr(element, key, attr[key]); $.attr(element, key, attr[key]);
} }

View File

@ -1,15 +1,15 @@
import date_utils from "../src/date_utils"; import date_utils from '../src/date_utils';
test("Parse: parses string date", () => { test('Parse: parses string date', () => {
const date = date_utils.parse("2017-09-09"); const date = date_utils.parse('2017-09-09');
expect(date.getDate()).toBe(9); expect(date.getDate()).toBe(9);
expect(date.getMonth()).toBe(8); expect(date.getMonth()).toBe(8);
expect(date.getFullYear()).toBe(2017); expect(date.getFullYear()).toBe(2017);
}); });
test("Parse: parses string datetime", () => { test('Parse: parses string datetime', () => {
const date = date_utils.parse("2017-08-27 16:08:34"); const date = date_utils.parse('2017-08-27 16:08:34');
expect(date.getFullYear()).toBe(2017); expect(date.getFullYear()).toBe(2017);
expect(date.getMonth()).toBe(7); expect(date.getMonth()).toBe(7);
@ -19,8 +19,8 @@ test("Parse: parses string datetime", () => {
expect(date.getSeconds()).toBe(34); expect(date.getSeconds()).toBe(34);
}); });
test("Parse: parses string datetime", () => { test('Parse: parses string datetime', () => {
const date = date_utils.parse("2016-02-29 16:08:34.3"); const date = date_utils.parse('2016-02-29 16:08:34.3');
expect(date.getFullYear()).toBe(2016); expect(date.getFullYear()).toBe(2016);
expect(date.getMonth()).toBe(1); expect(date.getMonth()).toBe(1);
@ -31,8 +31,8 @@ test("Parse: parses string datetime", () => {
expect(date.getMilliseconds()).toBe(300); expect(date.getMilliseconds()).toBe(300);
}); });
test("Parse: parses string datetime", () => { test('Parse: parses string datetime', () => {
const date = date_utils.parse("2015-07-01 00:00:59.200"); const date = date_utils.parse('2015-07-01 00:00:59.200');
expect(date.getFullYear()).toBe(2015); expect(date.getFullYear()).toBe(2015);
expect(date.getMonth()).toBe(6); expect(date.getMonth()).toBe(6);
@ -43,82 +43,82 @@ test("Parse: parses string datetime", () => {
expect(date.getMilliseconds()).toBe(200); expect(date.getMilliseconds()).toBe(200);
}); });
test("Format: converts date object to string", () => { test('Format: converts date object to string', () => {
const date = new Date("2017-09-18"); const date = new Date('2017-09-18');
expect(date_utils.to_string(date)).toBe("2017-09-18"); expect(date_utils.to_string(date)).toBe('2017-09-18');
}); });
test("Format: converts date object to string", () => { test('Format: converts date object to string', () => {
const date = new Date("2016-02-29 16:08:34.3"); const date = new Date('2016-02-29 16:08:34.3');
expect(date_utils.to_string(date, true)).toBe("2016-02-29 16:08:34.300"); expect(date_utils.to_string(date, true)).toBe('2016-02-29 16:08:34.300');
}); });
test("Format: converts date object to string", () => { test('Format: converts date object to string', () => {
const date = new Date("2016-02-29 16:08:34.3"); const date = new Date('2016-02-29 16:08:34.3');
expect(date_utils.to_string(date, true)).toBe("2016-02-29 16:08:34.300"); expect(date_utils.to_string(date, true)).toBe('2016-02-29 16:08:34.300');
}); });
test("Parse: returns Date Object as is", () => { test('Parse: returns Date Object as is', () => {
const d = new Date(); const d = new Date();
const date = date_utils.parse(d); const date = date_utils.parse(d);
expect(d).toBe(date); expect(d).toBe(date);
}); });
test("Diff: returns diff between 2 date objects", () => { test('Diff: returns diff between 2 date objects', () => {
const a = date_utils.parse("2017-09-08"); const a = date_utils.parse('2017-09-08');
const b = date_utils.parse("2017-06-07"); const b = date_utils.parse('2017-06-07');
expect(date_utils.diff(a, b, "day")).toBe(93); expect(date_utils.diff(a, b, 'day')).toBe(93);
expect(date_utils.diff(a, b, "month")).toBe(3); expect(date_utils.diff(a, b, 'month')).toBe(3);
expect(date_utils.diff(a, b, "year")).toBe(0); expect(date_utils.diff(a, b, 'year')).toBe(0);
}); });
test("StartOf", () => { test('StartOf', () => {
const date = date_utils.parse("2017-08-12 15:07:34.012"); const date = date_utils.parse('2017-08-12 15:07:34.012');
const start_of_millisecond = date_utils.start_of(date, "millisecond"); const start_of_millisecond = date_utils.start_of(date, 'millisecond');
expect(date_utils.to_string(start_of_millisecond, true)).toBe( expect(date_utils.to_string(start_of_millisecond, true)).toBe(
"2017-08-12 15:07:34.012", '2017-08-12 15:07:34.012',
); );
const start_of_second = date_utils.start_of(date, "second"); const start_of_second = date_utils.start_of(date, 'second');
expect(date_utils.to_string(start_of_second, true)).toBe( expect(date_utils.to_string(start_of_second, true)).toBe(
"2017-08-12 15:07:34.000", '2017-08-12 15:07:34.000',
); );
const start_of_minute = date_utils.start_of(date, "minute"); const start_of_minute = date_utils.start_of(date, 'minute');
expect(date_utils.to_string(start_of_minute, true)).toBe( expect(date_utils.to_string(start_of_minute, true)).toBe(
"2017-08-12 15:07:00.000", '2017-08-12 15:07:00.000',
); );
const start_of_hour = date_utils.start_of(date, "hour"); const start_of_hour = date_utils.start_of(date, 'hour');
expect(date_utils.to_string(start_of_hour, true)).toBe( expect(date_utils.to_string(start_of_hour, true)).toBe(
"2017-08-12 15:00:00.000", '2017-08-12 15:00:00.000',
); );
const start_of_day = date_utils.start_of(date, "day"); const start_of_day = date_utils.start_of(date, 'day');
expect(date_utils.to_string(start_of_day, true)).toBe( expect(date_utils.to_string(start_of_day, true)).toBe(
"2017-08-12 00:00:00.000", '2017-08-12 00:00:00.000',
); );
const start_of_month = date_utils.start_of(date, "month"); const start_of_month = date_utils.start_of(date, 'month');
expect(date_utils.to_string(start_of_month, true)).toBe( expect(date_utils.to_string(start_of_month, true)).toBe(
"2017-08-01 00:00:00.000", '2017-08-01 00:00:00.000',
); );
const start_of_year = date_utils.start_of(date, "year"); const start_of_year = date_utils.start_of(date, 'year');
expect(date_utils.to_string(start_of_year, true)).toBe( expect(date_utils.to_string(start_of_year, true)).toBe(
"2017-01-01 00:00:00.000", '2017-01-01 00:00:00.000',
); );
}); });
test("format", () => { test('format', () => {
const date = date_utils.parse("2017-08-12 15:07:23"); const date = date_utils.parse('2017-08-12 15:07:23');
expect(date_utils.format(date, "YYYY-MM-DD")).toBe("2017-08-12"); expect(date_utils.format(date, 'YYYY-MM-DD')).toBe('2017-08-12');
}); });
test("format", () => { test('format', () => {
const date = date_utils.parse("2016-02-29 16:08:34.3"); const date = date_utils.parse('2016-02-29 16:08:34.3');
expect(date_utils.format(date)).toBe("2016-02-29 16:08:34.300"); expect(date_utils.format(date)).toBe('2016-02-29 16:08:34.300');
}); });

22
vite.config.js Normal file
View File

@ -0,0 +1,22 @@
import { resolve } from 'path';
import { defineConfig } from 'vite';
export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'src/index.js'),
name: 'Gantt',
fileName: 'frappe-gantt',
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue'
}
},
},
},
output: { interop: 'auto' },
server: { watch: { include: ['dist/*', 'src/*'] } }
});

10471
yarn.lock

File diff suppressed because it is too large Load Diff