Merge branch 'master' into master
This commit is contained in:
commit
3e28b63e14
16
.github/workflows/publish.yml
vendored
16
.github/workflows/publish.yml
vendored
@ -7,13 +7,17 @@ jobs:
|
|||||||
publish:
|
publish:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-node@v1
|
- uses: pnpm/action-setup@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
version: 9
|
||||||
- run: yarn install
|
- uses: actions/setup-node@v4
|
||||||
- run: yarn prettier-check
|
with:
|
||||||
- run: yarn test
|
node-version: '18'
|
||||||
|
cache: 'pnpm'
|
||||||
|
- run: pnpm install
|
||||||
|
- run: pnpm prettier-check
|
||||||
|
- run: pnpm build
|
||||||
- uses: JS-DevTools/npm-publish@v1
|
- uses: JS-DevTools/npm-publish@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.NPM_TOKEN }}
|
token: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
nodeLinker: node-modules
|
|
||||||
@ -30,6 +30,11 @@ Include it in your HTML:
|
|||||||
<link rel="stylesheet" href="frappe-gantt.css">
|
<link rel="stylesheet" href="frappe-gantt.css">
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or from the CDN:
|
||||||
|
```
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/frappe-gantt/dist/frappe-gantt.umd.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/frappe-gantt/dist/frappe-gantt.css">
|
||||||
|
```
|
||||||
And start hacking:
|
And start hacking:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -63,7 +68,7 @@ var gantt = new Gantt('#gantt', tasks, {
|
|||||||
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,
|
popup: null,
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
1260
dist/frappe-gantt.js
vendored
1260
dist/frappe-gantt.js
vendored
File diff suppressed because it is too large
Load Diff
23
dist/frappe-gantt.umd.cjs
vendored
23
dist/frappe-gantt.umd.cjs
vendored
File diff suppressed because one or more lines are too long
1
dist/style.css
vendored
1
dist/style.css
vendored
File diff suppressed because one or more lines are too long
50
index.html
50
index.html
@ -23,8 +23,8 @@
|
|||||||
background-color: #252525;
|
background-color: #252525;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<link rel="stylesheet" href="dist/style.css" />
|
<link rel="stylesheet" href="dist/frappe-gantt.css" />
|
||||||
<script src="dist/frappe-gantt.umd.cjs"></script>
|
<script src="dist/frappe-gantt.umd.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -34,8 +34,6 @@
|
|||||||
<div class="gantt-target"></div>
|
<div class="gantt-target"></div>
|
||||||
</div>
|
</div>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
// import Gantt from "./dist/frappe-gantt.js";
|
|
||||||
|
|
||||||
let tasks = [
|
let tasks = [
|
||||||
{
|
{
|
||||||
start: '2024-04-01',
|
start: '2024-04-01',
|
||||||
@ -96,36 +94,32 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Uncomment to test fixed header
|
// Uncomment to test fixed header
|
||||||
// tasks = [
|
tasks = [
|
||||||
// ...tasks,
|
...tasks,
|
||||||
// ...Array.from({ length: tasks.length * 3 }, (_, i) => ({
|
...Array.from({ length: tasks.length * 3 }, (_, i) => ({
|
||||||
// ...tasks[i % 3],
|
...tasks[i % 3],
|
||||||
// id: i,
|
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_hover (task, x, y) {
|
||||||
// console.log("Double Click", task);
|
|
||||||
// },
|
|
||||||
// on_date_change: (task, start, end) => {
|
|
||||||
// console.log("Date change", task, start, end);
|
|
||||||
// },
|
|
||||||
// on_progress_change: (task, progress) => {
|
|
||||||
// console.log("Progress Change", task, progress);
|
|
||||||
// },
|
|
||||||
// on_view_change: (mode) => {
|
|
||||||
// console.log("View Change", mode);
|
|
||||||
// },
|
|
||||||
// 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,
|
custom_view_modes: [
|
||||||
|
{
|
||||||
|
name: 'Custom Day',
|
||||||
|
padding: '1m',
|
||||||
|
step: 3,
|
||||||
|
unit: 'day',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// popup_on: 'click',
|
||||||
|
// move_dependencies: false,
|
||||||
// scroll_to: 'today',
|
// scroll_to: 'today',
|
||||||
// view_mode_select: true,
|
// view_mode_select: true,
|
||||||
// dates_readonly: true,
|
// dates_readonly: true,
|
||||||
|
|||||||
6265
package-lock.json
generated
6265
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
Executable file → Normal file
16
package.json
Executable file → Normal file
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "frappe-gantt",
|
"name": "frappe-gantt",
|
||||||
"version": "0.8",
|
"version": "0.9.0",
|
||||||
"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",
|
"type": "module",
|
||||||
@ -9,15 +9,13 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build-dev": "vite build --watch",
|
"build-dev": "vite build --watch",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"test": "jest",
|
|
||||||
"lint": "eslint src/**/*.js",
|
"lint": "eslint src/**/*.js",
|
||||||
"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\""
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/frappe/gantt.git"
|
"url": "git+https://github.com/frappe/gantt.git"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
@ -26,8 +24,9 @@
|
|||||||
],
|
],
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
|
"require": "./dist/frappe-gantt.umd.js",
|
||||||
"import": "./dist/frappe-gantt.es.js",
|
"import": "./dist/frappe-gantt.es.js",
|
||||||
"require": "./dist/frappe-gantt.umd.js"
|
"style": "./dist/frappe-gantt.css"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -45,10 +44,9 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/frappe/gantt",
|
"homepage": "https://github.com/frappe/gantt",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^4.17.0",
|
"eslint": "^9.15.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",
|
|
||||||
"postcss-nesting": "^12.1.2",
|
"postcss-nesting": "^12.1.2",
|
||||||
"prettier": "3.2.5",
|
"prettier": "3.2.5",
|
||||||
"vite": "^5.2.10"
|
"vite": "^5.2.10"
|
||||||
@ -56,5 +54,7 @@
|
|||||||
"eslintIgnore": [
|
"eslintIgnore": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"packageManager": "yarn@3.2.3"
|
"sideEffects": [
|
||||||
|
"*.css"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
1282
pnpm-lock.yaml
generated
Normal file
1282
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
34
src/bar.js
34
src/bar.js
@ -282,6 +282,21 @@ export default class Bar {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.gantt.options.popup_on === 'click') {
|
||||||
|
let opened = false;
|
||||||
|
$.on(this.group, 'click', (e) => {
|
||||||
|
console.log(opened);
|
||||||
|
if (!opened) {
|
||||||
|
this.show_popup(e.offsetX || e.layerX);
|
||||||
|
document.getElementById(
|
||||||
|
`highlight-${task_id}`,
|
||||||
|
).style.display = 'block';
|
||||||
|
} else {
|
||||||
|
this.gantt.hide_popup();
|
||||||
|
}
|
||||||
|
opened = !opened;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
let timeout;
|
let timeout;
|
||||||
$.on(
|
$.on(
|
||||||
this.group,
|
this.group,
|
||||||
@ -290,7 +305,7 @@ export default class Bar {
|
|||||||
(timeout = setTimeout(() => {
|
(timeout = setTimeout(() => {
|
||||||
this.show_popup(e.offsetX || e.layerX);
|
this.show_popup(e.offsetX || e.layerX);
|
||||||
document.getElementById(
|
document.getElementById(
|
||||||
`highlight-${task_id}`,
|
`${task_id}-highlight`,
|
||||||
).style.display = 'block';
|
).style.display = 'block';
|
||||||
}, 200)),
|
}, 200)),
|
||||||
);
|
);
|
||||||
@ -298,9 +313,10 @@ export default class Bar {
|
|||||||
$.on(this.group, 'mouseleave', () => {
|
$.on(this.group, 'mouseleave', () => {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
this.gantt.popup?.hide?.();
|
this.gantt.popup?.hide?.();
|
||||||
document.getElementById(`highlight-${task_id}`).style.display =
|
document.getElementById(`${task_id}-highlight`).style.display =
|
||||||
'none';
|
'none';
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$.on(this.group, 'click', () => {
|
$.on(this.group, 'click', () => {
|
||||||
this.gantt.trigger_event('click', [this.task]);
|
this.gantt.trigger_event('click', [this.task]);
|
||||||
@ -333,7 +349,6 @@ export default class Bar {
|
|||||||
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}`;
|
||||||
|
|
||||||
this.gantt.show_popup({
|
this.gantt.show_popup({
|
||||||
x,
|
x,
|
||||||
target_element: this.$bar,
|
target_element: this.$bar,
|
||||||
@ -499,8 +514,19 @@ export default class Bar {
|
|||||||
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;
|
||||||
|
|
||||||
|
/* Since the column width is based on 30,
|
||||||
|
we count the month-difference, multiply it by 30 for a "pseudo-month"
|
||||||
|
and then add the days in the month, making sure the number does not exceed 29
|
||||||
|
so it is within the column */
|
||||||
if (this.gantt.view_is('Month')) {
|
if (this.gantt.view_is('Month')) {
|
||||||
const diff = date_utils.diff(task_start, gantt_start, 'day');
|
const diffDaysBasedOn30DayMonths =
|
||||||
|
date_utils.diff(task_start, gantt_start, 'month') * 30;
|
||||||
|
const dayInMonth = Math.min(
|
||||||
|
29,
|
||||||
|
date_utils.format(task_start, 'DD'),
|
||||||
|
);
|
||||||
|
const diff = diffDaysBasedOn30DayMonths + dayInMonth;
|
||||||
|
|
||||||
x = (diff * column_width) / 30;
|
x = (diff * column_width) / 30;
|
||||||
}
|
}
|
||||||
this.x = x;
|
this.x = x;
|
||||||
|
|||||||
@ -131,7 +131,21 @@ export default {
|
|||||||
minutes = seconds / 60;
|
minutes = seconds / 60;
|
||||||
hours = minutes / 60;
|
hours = minutes / 60;
|
||||||
days = hours / 24;
|
days = hours / 24;
|
||||||
months = days / 30;
|
// Calculate months across years
|
||||||
|
const yearDiff = date_a.getFullYear() - date_b.getFullYear();
|
||||||
|
const monthDiff = date_a.getMonth() - date_b.getMonth();
|
||||||
|
|
||||||
|
/* If monthDiff is negative, date_b is in an earlier month than
|
||||||
|
date_a and thus subtracted from the year difference in months */
|
||||||
|
months = yearDiff * 12 + monthDiff;
|
||||||
|
|
||||||
|
/* If date_a's (e.g. march 1st) day of the month is smaller than date_b (e.g. february 28th),
|
||||||
|
adjust the month difference */
|
||||||
|
if (date_a.getDate() < date_b.getDate()) {
|
||||||
|
months--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate years based on actual months
|
||||||
years = months / 12;
|
years = months / 12;
|
||||||
|
|
||||||
if (!scale.endsWith('s')) {
|
if (!scale.endsWith('s')) {
|
||||||
|
|||||||
@ -22,13 +22,13 @@
|
|||||||
--dark-blue: #2c94ec;
|
--dark-blue: #2c94ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.gantt-container {
|
.gantt-container {
|
||||||
line-height: 14.5px;
|
line-height: 14.5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
height: 500px;
|
height: 500px;
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
& .popup-wrapper {
|
& .popup-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -158,7 +158,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.gantt {
|
.gantt {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
@ -217,7 +216,7 @@
|
|||||||
stroke-width: 1;
|
stroke-width: 1;
|
||||||
stroke-dasharray: 5;
|
stroke-dasharray: 5;
|
||||||
|
|
||||||
&~.bar-label {
|
& ~ .bar-label {
|
||||||
fill: var(--text-light);
|
fill: var(--text-light);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,7 +271,6 @@
|
|||||||
& .bar-wrapper {
|
& .bar-wrapper {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
& .handle {
|
& .handle {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
|||||||
212
src/index.js
212
src/index.js
@ -36,8 +36,10 @@ const DEFAULT_OPTIONS = {
|
|||||||
padding: 18,
|
padding: 18,
|
||||||
view_mode: 'Day',
|
view_mode: 'Day',
|
||||||
date_format: 'YYYY-MM-DD',
|
date_format: 'YYYY-MM-DD',
|
||||||
|
move_dependencies: true,
|
||||||
show_expected_progress: false,
|
show_expected_progress: false,
|
||||||
popup: null,
|
popup: null,
|
||||||
|
popup_on: 'hover',
|
||||||
language: 'en',
|
language: 'en',
|
||||||
readonly: false,
|
readonly: false,
|
||||||
progress_readonly: false,
|
progress_readonly: false,
|
||||||
@ -109,11 +111,20 @@ export default class Gantt {
|
|||||||
|
|
||||||
setup_options(options) {
|
setup_options(options) {
|
||||||
this.options = { ...DEFAULT_OPTIONS, ...options };
|
this.options = { ...DEFAULT_OPTIONS, ...options };
|
||||||
if (!options.view_mode_padding) options.view_mode_padding = {};
|
const custom_mode = this.options.custom_view_modes
|
||||||
for (let [key, value] of Object.entries(options.view_mode_padding)) {
|
? this.options.custom_view_modes.find(
|
||||||
|
(m) => m.name === this.options.view_mode,
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
if (custom_mode) this.options = { ...this.options, custom_mode };
|
||||||
|
if (!this.options.view_mode_padding)
|
||||||
|
this.options.view_mode_padding = {};
|
||||||
|
for (let [key, value] of Object.entries(
|
||||||
|
this.options.view_mode_padding,
|
||||||
|
)) {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
// Configure for single value given
|
// Configure for single value given
|
||||||
options.view_mode_padding[key] = [value, value];
|
this.options.view_mode_padding[key] = [value, value];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +215,6 @@ export default class Gantt {
|
|||||||
|
|
||||||
return task;
|
return task;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setup_dependencies();
|
this.setup_dependencies();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +243,23 @@ export default class Gantt {
|
|||||||
|
|
||||||
update_view_scale(view_mode) {
|
update_view_scale(view_mode) {
|
||||||
this.options.view_mode = view_mode;
|
this.options.view_mode = view_mode;
|
||||||
|
const custom_mode = this.options.custom_mode;
|
||||||
|
if (custom_mode) {
|
||||||
|
//configure step and column width for custom view case
|
||||||
|
if (custom_mode.unit === 'hour') {
|
||||||
|
this.options.step = custom_mode.step;
|
||||||
|
this.options.column_width = 38;
|
||||||
|
} else if (custom_mode.unit === 'day') {
|
||||||
|
this.options.step = custom_mode.step * 24;
|
||||||
|
this.options.column_width = 38;
|
||||||
|
} else if (custom_mode.unit === 'month') {
|
||||||
|
this.options.step = custom_mode.step * 24 * 30;
|
||||||
|
this.options.column_width = 120;
|
||||||
|
} else {
|
||||||
|
this.options.step = 24;
|
||||||
|
this.options.column_width = 38;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (view_mode === VIEW_MODE.HOUR) {
|
if (view_mode === VIEW_MODE.HOUR) {
|
||||||
this.options.step = 24 / 24;
|
this.options.step = 24 / 24;
|
||||||
this.options.column_width = 38;
|
this.options.column_width = 38;
|
||||||
@ -280,16 +307,27 @@ export default class Gantt {
|
|||||||
if (!this.gantt_end) gantt_end = new Date();
|
if (!this.gantt_end) gantt_end = new Date();
|
||||||
else gantt_end = date_utils.start_of(this.gantt_end, 'day');
|
else gantt_end = date_utils.start_of(this.gantt_end, 'day');
|
||||||
|
|
||||||
// add date padding on both sides
|
const custom_mode = this.options.custom_mode;
|
||||||
|
let [padding_start, padding_end] = [
|
||||||
|
{ duration: 1, scale: 'day' },
|
||||||
|
{ duration: 1, scale: 'day' },
|
||||||
|
];
|
||||||
|
if (custom_mode) {
|
||||||
|
[padding_start, padding_end] = [
|
||||||
|
custom_mode.padding,
|
||||||
|
custom_mode.padding,
|
||||||
|
].map(date_utils.parse_duration);
|
||||||
|
} else {
|
||||||
let viewKey;
|
let viewKey;
|
||||||
for (let [key, value] of Object.entries(VIEW_MODE)) {
|
for (let [key, value] of Object.entries(VIEW_MODE)) {
|
||||||
if (value === this.options.view_mode) {
|
if (value === this.options.view_mode) {
|
||||||
viewKey = key;
|
viewKey = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const [padding_start, padding_end] = this.options.view_mode_padding[
|
[padding_start, padding_end] = this.options.view_mode_padding[
|
||||||
viewKey
|
viewKey
|
||||||
].map(date_utils.parse_duration);
|
].map(date_utils.parse_duration);
|
||||||
|
}
|
||||||
gantt_start = date_utils.add(
|
gantt_start = date_utils.add(
|
||||||
gantt_start,
|
gantt_start,
|
||||||
-padding_start.duration,
|
-padding_start.duration,
|
||||||
@ -322,6 +360,16 @@ export default class Gantt {
|
|||||||
let cur_date = null;
|
let cur_date = null;
|
||||||
|
|
||||||
while (cur_date === null || cur_date < this.gantt_end) {
|
while (cur_date === null || cur_date < this.gantt_end) {
|
||||||
|
if (this.options.custom_mode) {
|
||||||
|
const step = this.options.custom_mode.step || 1;
|
||||||
|
const unit = this.options.custom_mode.unit || 'day';
|
||||||
|
|
||||||
|
if (!cur_date) {
|
||||||
|
cur_date = date_utils.clone(this.gantt_start);
|
||||||
|
} else {
|
||||||
|
cur_date = date_utils.add(cur_date, step, unit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (!cur_date) {
|
if (!cur_date) {
|
||||||
cur_date = date_utils.clone(this.gantt_start);
|
cur_date = date_utils.clone(this.gantt_start);
|
||||||
} else {
|
} else {
|
||||||
@ -337,6 +385,7 @@ export default class Gantt {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.dates.push(cur_date);
|
this.dates.push(cur_date);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,7 +440,6 @@ export default class Gantt {
|
|||||||
this.options.padding +
|
this.options.padding +
|
||||||
(this.options.bar_height + this.options.padding) *
|
(this.options.bar_height + this.options.padding) *
|
||||||
this.tasks.length;
|
this.tasks.length;
|
||||||
|
|
||||||
createSVG('rect', {
|
createSVG('rect', {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
@ -414,7 +462,6 @@ export default class Gantt {
|
|||||||
const row_height = this.options.bar_height + this.options.padding;
|
const row_height = this.options.bar_height + this.options.padding;
|
||||||
|
|
||||||
let row_y = this.options.header_height + this.options.padding / 2;
|
let row_y = this.options.header_height + this.options.padding / 2;
|
||||||
|
|
||||||
for (let _ of this.tasks) {
|
for (let _ of this.tasks) {
|
||||||
createSVG('rect', {
|
createSVG('rect', {
|
||||||
x: 0,
|
x: 0,
|
||||||
@ -516,6 +563,7 @@ export default class Gantt {
|
|||||||
const { left, y } = this.$header.getBoundingClientRect();
|
const { left, y } = this.$header.getBoundingClientRect();
|
||||||
|
|
||||||
// Check if the button is scrolled out of the container vertically
|
// Check if the button is scrolled out of the container vertically
|
||||||
|
|
||||||
if (
|
if (
|
||||||
buttonRect.top < containerRect.top ||
|
buttonRect.top < containerRect.top ||
|
||||||
buttonRect.bottom > containerRect.bottom
|
buttonRect.bottom > containerRect.bottom
|
||||||
@ -539,8 +587,10 @@ export default class Gantt {
|
|||||||
'px';
|
'px';
|
||||||
|
|
||||||
// Update the left value on page resize
|
// Update the left value on page resize
|
||||||
|
if (this.$today_button) {
|
||||||
this.$today_button.style.left = `${containerRect.left + 20}px`;
|
this.$today_button.style.left = `${containerRect.left + 20}px`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
make_grid_ticks() {
|
make_grid_ticks() {
|
||||||
if (!['both', 'vertical', 'horizontal'].includes(this.options.lines))
|
if (!['both', 'vertical', 'horizontal'].includes(this.options.lines))
|
||||||
@ -592,7 +642,6 @@ export default class Gantt {
|
|||||||
if (this.view_is(VIEW_MODE.MONTH) && date.getMonth() % 3 === 0) {
|
if (this.view_is(VIEW_MODE.MONTH) && date.getMonth() % 3 === 0) {
|
||||||
tick_class += ' thick';
|
tick_class += ' thick';
|
||||||
}
|
}
|
||||||
|
|
||||||
createSVG('path', {
|
createSVG('path', {
|
||||||
d: `M ${tick_x} ${tick_y} v ${tick_height}`,
|
d: `M ${tick_x} ${tick_y} v ${tick_height}`,
|
||||||
class: tick_class,
|
class: tick_class,
|
||||||
@ -682,10 +731,13 @@ export default class Gantt {
|
|||||||
x += this.options.column_width;
|
x += this.options.column_width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { x };
|
||||||
}
|
}
|
||||||
|
|
||||||
make_grid_highlights() {
|
make_grid_highlights() {
|
||||||
if (this.options.highlight_weekend) this.highlightWeekends();
|
if (this.options.highlight_weekend) this.highlightWeekends();
|
||||||
|
|
||||||
// highlight today's | week's | month's | year's
|
// highlight today's | week's | month's | year's
|
||||||
if (
|
if (
|
||||||
this.view_is(VIEW_MODE.DAY) ||
|
this.view_is(VIEW_MODE.DAY) ||
|
||||||
@ -742,10 +794,10 @@ export default class Gantt {
|
|||||||
classes: 'lower-text',
|
classes: 'lower-text',
|
||||||
append_to: this.$lower_header,
|
append_to: this.$lower_header,
|
||||||
});
|
});
|
||||||
|
|
||||||
$lower_text.innerText = date.lower_text;
|
$lower_text.innerText = date.lower_text;
|
||||||
$lower_text.style.left =
|
$lower_text.style.left =
|
||||||
+$lower_text.style.left.slice(0, -2) +
|
+$lower_text.style.left.slice(0, -2) + 'px';
|
||||||
'px';
|
|
||||||
|
|
||||||
if (date.upper_text) {
|
if (date.upper_text) {
|
||||||
this.upper_texts_x[date.upper_text] = date.upper_x;
|
this.upper_texts_x[date.upper_text] = date.upper_x;
|
||||||
@ -778,8 +830,61 @@ export default class Gantt {
|
|||||||
let last_date = last_date_info
|
let last_date = last_date_info
|
||||||
? last_date_info.date
|
? last_date_info.date
|
||||||
: date_utils.add(date, 1, 'day');
|
: date_utils.add(date, 1, 'day');
|
||||||
const date_text = {
|
let date_text = {};
|
||||||
Hour_lower: date_utils.format(date, 'HH', this.options.language),
|
const custom_mode = this.options.custom_mode;
|
||||||
|
if (custom_mode) {
|
||||||
|
let lower_text, upper_text;
|
||||||
|
const unit = custom_mode ? custom_mode.unit.toLowerCase() : 'day';
|
||||||
|
if (unit === 'hour') {
|
||||||
|
lower_text = date_utils.format(
|
||||||
|
date,
|
||||||
|
'HH',
|
||||||
|
this.options.language,
|
||||||
|
);
|
||||||
|
upper_text =
|
||||||
|
date.getDate() !== last_date.getDate()
|
||||||
|
? date_utils.format(
|
||||||
|
date,
|
||||||
|
'D MMMM',
|
||||||
|
this.options.language,
|
||||||
|
)
|
||||||
|
: '';
|
||||||
|
} else if (unit === 'day') {
|
||||||
|
lower_text =
|
||||||
|
date.getDate() !== last_date.getDate()
|
||||||
|
? date_utils.format(date, 'D', this.options.language)
|
||||||
|
: '';
|
||||||
|
upper_text =
|
||||||
|
date.getMonth() !== last_date.getMonth() || !last_date_info
|
||||||
|
? date_utils.format(date, 'MMMM', this.options.language)
|
||||||
|
: '';
|
||||||
|
} else if (unit === 'month') {
|
||||||
|
lower_text = date_utils.format(
|
||||||
|
date,
|
||||||
|
'MMMM',
|
||||||
|
this.options.language,
|
||||||
|
);
|
||||||
|
upper_text =
|
||||||
|
date.getFullYear() !== last_date.getFullYear()
|
||||||
|
? date_utils.format(date, 'YYYY', this.options.language)
|
||||||
|
: '';
|
||||||
|
} else {
|
||||||
|
lower_text = date_utils.format(
|
||||||
|
date,
|
||||||
|
'YYYY',
|
||||||
|
this.options.language,
|
||||||
|
);
|
||||||
|
upper_text = ''; // Default to no upper text for very large units
|
||||||
|
}
|
||||||
|
date_text[`${custom_mode.name}_upper`] = upper_text;
|
||||||
|
date_text[`${custom_mode.name}_lower`] = lower_text;
|
||||||
|
} else {
|
||||||
|
date_text = {
|
||||||
|
Hour_lower: date_utils.format(
|
||||||
|
date,
|
||||||
|
'HH',
|
||||||
|
this.options.language,
|
||||||
|
),
|
||||||
'Quarter Day_lower': date_utils.format(
|
'Quarter Day_lower': date_utils.format(
|
||||||
date,
|
date,
|
||||||
'HH',
|
'HH',
|
||||||
@ -796,17 +901,37 @@ export default class Gantt {
|
|||||||
: '',
|
: '',
|
||||||
Week_lower:
|
Week_lower:
|
||||||
date.getMonth() !== last_date.getMonth()
|
date.getMonth() !== last_date.getMonth()
|
||||||
? date_utils.format(date, 'D MMM', this.options.language)
|
? date_utils.format(
|
||||||
|
date,
|
||||||
|
'D MMM',
|
||||||
|
this.options.language,
|
||||||
|
)
|
||||||
: date_utils.format(date, 'D', this.options.language),
|
: date_utils.format(date, 'D', this.options.language),
|
||||||
Month_lower: date_utils.format(date, 'MMMM', this.options.language),
|
Month_lower: date_utils.format(
|
||||||
Year_lower: date_utils.format(date, 'YYYY', this.options.language),
|
date,
|
||||||
|
'MMMM',
|
||||||
|
this.options.language,
|
||||||
|
),
|
||||||
|
Year_lower: date_utils.format(
|
||||||
|
date,
|
||||||
|
'YYYY',
|
||||||
|
this.options.language,
|
||||||
|
),
|
||||||
Hour_upper:
|
Hour_upper:
|
||||||
date.getDate() !== last_date.getDate()
|
date.getDate() !== last_date.getDate()
|
||||||
? date_utils.format(date, 'D MMMM', this.options.language)
|
? date_utils.format(
|
||||||
|
date,
|
||||||
|
'D MMMM',
|
||||||
|
this.options.language,
|
||||||
|
)
|
||||||
: '',
|
: '',
|
||||||
'Quarter Day_upper':
|
'Quarter Day_upper':
|
||||||
date.getDate() !== last_date.getDate()
|
date.getDate() !== last_date.getDate()
|
||||||
? date_utils.format(date, 'D MMM', this.options.language)
|
? date_utils.format(
|
||||||
|
date,
|
||||||
|
'D MMM',
|
||||||
|
this.options.language,
|
||||||
|
)
|
||||||
: '',
|
: '',
|
||||||
'Half Day_upper':
|
'Half Day_upper':
|
||||||
date.getDate() !== last_date.getDate()
|
date.getDate() !== last_date.getDate()
|
||||||
@ -816,7 +941,11 @@ export default class Gantt {
|
|||||||
'D MMM',
|
'D MMM',
|
||||||
this.options.language,
|
this.options.language,
|
||||||
)
|
)
|
||||||
: date_utils.format(date, 'D', this.options.language)
|
: date_utils.format(
|
||||||
|
date,
|
||||||
|
'D',
|
||||||
|
this.options.language,
|
||||||
|
)
|
||||||
: '',
|
: '',
|
||||||
Day_upper:
|
Day_upper:
|
||||||
date.getMonth() !== last_date.getMonth() || !last_date_info
|
date.getMonth() !== last_date.getMonth() || !last_date_info
|
||||||
@ -835,10 +964,19 @@ export default class Gantt {
|
|||||||
? date_utils.format(date, 'YYYY', this.options.language)
|
? date_utils.format(date, 'YYYY', this.options.language)
|
||||||
: '',
|
: '',
|
||||||
};
|
};
|
||||||
let column_width = this.view_is(VIEW_MODE.MONTH)
|
}
|
||||||
? (date_utils.get_days_in_month(date) * this.options.column_width) /
|
|
||||||
|
let column_width =
|
||||||
|
custom_mode &&
|
||||||
|
custom_mode.lower_text &&
|
||||||
|
custom_mode.lower_text.column_width
|
||||||
|
? custom_mode.lower_text.column_width * (custom_mode.step ?? 1)
|
||||||
|
: this.view_is(VIEW_MODE.MONTH)
|
||||||
|
? (date_utils.get_days_in_month(date) *
|
||||||
|
this.options.column_width) /
|
||||||
30
|
30
|
||||||
: this.options.column_width;
|
: this.options.column_width;
|
||||||
|
|
||||||
const base_pos = {
|
const base_pos = {
|
||||||
x: last_date_info
|
x: last_date_info
|
||||||
? last_date_info.base_pos_x + last_date_info.column_width
|
? last_date_info.base_pos_x + last_date_info.column_width
|
||||||
@ -862,6 +1000,12 @@ export default class Gantt {
|
|||||||
Year_lower: column_width / 2,
|
Year_lower: column_width / 2,
|
||||||
Year_upper: (column_width * 30) / 2,
|
Year_upper: (column_width * 30) / 2,
|
||||||
};
|
};
|
||||||
|
if (custom_mode) {
|
||||||
|
x_pos[`${custom_mode.name}_upper`] = column_width / 2;
|
||||||
|
x_pos[`${custom_mode.name}_lower`] =
|
||||||
|
column_width /
|
||||||
|
(custom_mode.unit.toLowerCase() === 'day' ? 1 : 2);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
date,
|
date,
|
||||||
formatted_date: date_utils.format(date).replaceAll(' ', '_'),
|
formatted_date: date_utils.format(date).replaceAll(' ', '_'),
|
||||||
@ -1011,10 +1155,15 @@ export default class Gantt {
|
|||||||
y_on_start = e.offsetY || e.layerY;
|
y_on_start = e.offsetY || e.layerY;
|
||||||
|
|
||||||
parent_bar_id = bar_wrapper.getAttribute('data-id');
|
parent_bar_id = bar_wrapper.getAttribute('data-id');
|
||||||
const ids = [
|
let ids;
|
||||||
|
if (this.options.move_dependencies) {
|
||||||
|
ids = [
|
||||||
parent_bar_id,
|
parent_bar_id,
|
||||||
...this.get_all_dependent_tasks(parent_bar_id),
|
...this.get_all_dependent_tasks(parent_bar_id),
|
||||||
];
|
];
|
||||||
|
} else {
|
||||||
|
ids = [parent_bar_id];
|
||||||
|
}
|
||||||
bars = ids.map((id) => this.get_bar(id));
|
bars = ids.map((id) => this.get_bar(id));
|
||||||
|
|
||||||
this.bar_being_dragged = parent_bar_id;
|
this.bar_being_dragged = parent_bar_id;
|
||||||
@ -1027,6 +1176,7 @@ export default class Gantt {
|
|||||||
$bar.finaldx = 0;
|
$bar.finaldx = 0;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$.on(this.$container, 'scroll', (e) => {
|
$.on(this.$container, 'scroll', (e) => {
|
||||||
let elements = document.querySelectorAll('.bar-wrapper');
|
let elements = document.querySelectorAll('.bar-wrapper');
|
||||||
let localBars = [];
|
let localBars = [];
|
||||||
@ -1160,6 +1310,7 @@ export default class Gantt {
|
|||||||
is_resizing = true;
|
is_resizing = true;
|
||||||
x_on_start = e.offsetX || e.layerX;
|
x_on_start = e.offsetX || e.layerX;
|
||||||
y_on_start = e.offsetY || e.layerY;
|
y_on_start = e.offsetY || e.layerY;
|
||||||
|
console.log(e, handle);
|
||||||
|
|
||||||
const $bar_wrapper = $.closest('.bar-wrapper', handle);
|
const $bar_wrapper = $.closest('.bar-wrapper', handle);
|
||||||
const id = $bar_wrapper.getAttribute('data-id');
|
const id = $bar_wrapper.getAttribute('data-id');
|
||||||
@ -1170,14 +1321,19 @@ export default class Gantt {
|
|||||||
|
|
||||||
$bar_progress.finaldx = 0;
|
$bar_progress.finaldx = 0;
|
||||||
$bar_progress.owidth = $bar_progress.getWidth();
|
$bar_progress.owidth = $bar_progress.getWidth();
|
||||||
$bar_progress.min_dx = -$bar_progress.getWidth();
|
$bar_progress.min_dx = -$bar_progress.owidth;
|
||||||
$bar_progress.max_dx = $bar.getWidth() - $bar_progress.getWidth();
|
$bar_progress.max_dx = $bar.getWidth() - $bar_progress.getWidth();
|
||||||
});
|
});
|
||||||
|
|
||||||
$.on(this.$svg, 'mousemove', (e) => {
|
$.on(this.$svg, 'mousemove', (e) => {
|
||||||
if (!is_resizing) return;
|
if (!is_resizing) return;
|
||||||
let dx = (e.offsetX || e.layerX) - x_on_start;
|
let dx = (e.offsetX || e.layerX) - x_on_start;
|
||||||
|
console.log(
|
||||||
|
dx,
|
||||||
|
$bar_progress.getWidth(),
|
||||||
|
$bar_progress.min_dx,
|
||||||
|
$bar_progress.max_dx,
|
||||||
|
);
|
||||||
if (dx > $bar_progress.max_dx) {
|
if (dx > $bar_progress.max_dx) {
|
||||||
dx = $bar_progress.max_dx;
|
dx = $bar_progress.max_dx;
|
||||||
}
|
}
|
||||||
@ -1185,9 +1341,9 @@ export default class Gantt {
|
|||||||
dx = $bar_progress.min_dx;
|
dx = $bar_progress.min_dx;
|
||||||
}
|
}
|
||||||
|
|
||||||
const $handle = bar.$handle_progress;
|
$bar_progress.setAttribute('width', $bar_progress.owidth + dx);
|
||||||
$.attr($bar_progress, 'width', $bar_progress.owidth + dx);
|
$.attr(bar.$handle_progress, 'cx', $bar_progress.getEndX());
|
||||||
$.attr($handle, 'cx', $bar_progress.getEndX());
|
|
||||||
$bar_progress.finaldx = dx;
|
$bar_progress.finaldx = dx;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1298,7 +1454,7 @@ export default class Gantt {
|
|||||||
|
|
||||||
trigger_event(event, args) {
|
trigger_event(event, args) {
|
||||||
if (this.options['on_' + event]) {
|
if (this.options['on_' + event]) {
|
||||||
this.options['on_' + event].apply(null, args);
|
this.options['on_' + event].apply(this, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,6 +48,7 @@ export default class Popup {
|
|||||||
this.parent.style.top =
|
this.parent.style.top =
|
||||||
position_meta.y + position_meta.height + 10 + 'px';
|
position_meta.y + position_meta.height + 10 + 'px';
|
||||||
|
|
||||||
|
this.parent.classList.remove('hidden');
|
||||||
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';
|
||||||
|
|
||||||
|
|||||||
@ -9,11 +9,10 @@ export default defineConfig({
|
|||||||
fileName: 'frappe-gantt',
|
fileName: 'frappe-gantt',
|
||||||
},
|
},
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
external: ['vue'],
|
|
||||||
output: {
|
output: {
|
||||||
globals: {
|
format: 'cjs',
|
||||||
vue: 'Vue'
|
assetFileNames: 'frappe-gantt[extname]',
|
||||||
}
|
entryFileNames: 'frappe-gantt.[format].js'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user