@operato/gantt 2.0.0-beta.13 → 2.0.0-beta.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/src/gantt-chart-controller.d.ts +32 -0
- package/dist/src/gantt-chart-controller.js +393 -0
- package/dist/src/gantt-chart-controller.js.map +1 -0
- package/dist/src/locales.d.ts +52 -0
- package/dist/src/locales.js +79 -0
- package/dist/src/locales.js.map +1 -0
- package/dist/src/ox-gantt-chart.d.ts +10 -17
- package/dist/src/ox-gantt-chart.js +135 -435
- package/dist/src/ox-gantt-chart.js.map +1 -1
- package/dist/src/ox-gantt-resize-splitter.d.ts +2 -3
- package/dist/src/ox-gantt-resize-splitter.js +15 -22
- package/dist/src/ox-gantt-resize-splitter.js.map +1 -1
- package/dist/src/ox-gantt.d.ts +14 -1
- package/dist/src/ox-gantt.js +149 -28
- package/dist/src/ox-gantt.js.map +1 -1
- package/dist/src/ox-task-tooltip.d.ts +12 -0
- package/dist/src/ox-task-tooltip.js +132 -0
- package/dist/src/ox-task-tooltip.js.map +1 -0
- package/dist/src/types.d.ts +10 -3
- package/dist/src/types.js.map +1 -1
- package/dist/stories/ox-gantt.stories.d.ts +12 -0
- package/dist/stories/ox-gantt.stories.js +100 -88
- package/dist/stories/ox-gantt.stories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/gantt-chart-controller.ts +470 -0
- package/src/locales.ts +78 -0
- package/src/ox-gantt-chart.ts +143 -481
- package/src/ox-gantt-resize-splitter.ts +18 -25
- package/src/ox-gantt.ts +153 -30
- package/src/ox-task-tooltip.ts +120 -0
- package/src/types.ts +11 -3
- package/stories/ox-gantt.stories.ts +105 -88
- package/translations/en.json +11 -1
- package/translations/ja.json +11 -1
- package/translations/ko.json +11 -1
- package/translations/ms.json +11 -1
- package/translations/zh.json +11 -1
- package/dist/src/locale.d.ts +0 -1
- package/dist/src/locale.js +0 -49
- package/dist/src/locale.js.map +0 -1
- package/src/locale.ts +0 -51
|
@@ -1,47 +1,31 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
+
import './ox-task-tooltip';
|
|
2
3
|
import { LitElement, html, css } from 'lit';
|
|
3
|
-
import { customElement, property, query
|
|
4
|
-
import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles';
|
|
4
|
+
import { customElement, property, query } from 'lit/decorators.js';
|
|
5
5
|
import * as d3 from 'd3';
|
|
6
|
-
import {
|
|
6
|
+
import { locales } from './locales';
|
|
7
|
+
import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles';
|
|
7
8
|
import { ScrollbarStyles } from '@operato/styles';
|
|
9
|
+
import { GanttChartController } from './gantt-chart-controller';
|
|
10
|
+
import { levelDown, levelUp } from './utils';
|
|
8
11
|
let OxGanttChart = class OxGanttChart extends LitElement {
|
|
9
|
-
constructor() {
|
|
10
|
-
super(...arguments);
|
|
11
|
-
this.fromDate = new Date(new Date().getFullYear(), 0, 1).toISOString().split('T')[0]; /* beginning of this year */
|
|
12
|
-
this.toDate = new Date(new Date().getFullYear(), 11, 31).toISOString().split('T')[0]; /* end of this year */
|
|
13
|
-
this.axisScale = 'week-day';
|
|
14
|
-
this.extendGridLines = false;
|
|
15
|
-
this.tasks = [];
|
|
16
|
-
this.baseWidth = 30;
|
|
17
|
-
this.level = this.axisScale;
|
|
18
|
-
this.fromTime = new Date(this.fromDate);
|
|
19
|
-
this.toTime = new Date(this.toDate);
|
|
20
|
-
this.totalDays = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1));
|
|
21
|
-
}
|
|
22
12
|
static { this.styles = [
|
|
23
13
|
MDTypeScaleStyles,
|
|
24
14
|
ScrollbarStyles,
|
|
25
15
|
css `
|
|
26
16
|
:host {
|
|
27
17
|
display: flex;
|
|
28
|
-
|
|
29
18
|
overflow: hidden;
|
|
19
|
+
position: relative;
|
|
30
20
|
}
|
|
31
|
-
|
|
32
21
|
div[chart-container] {
|
|
33
22
|
display: block;
|
|
34
|
-
|
|
35
23
|
overflow-x: auto;
|
|
36
|
-
overflow-y:
|
|
37
|
-
|
|
38
|
-
position: relative;
|
|
24
|
+
overflow-y: hidden;
|
|
39
25
|
}
|
|
40
|
-
|
|
41
26
|
svg {
|
|
42
27
|
min-width: 100%;
|
|
43
28
|
}
|
|
44
|
-
|
|
45
29
|
svg[header] {
|
|
46
30
|
display: block;
|
|
47
31
|
min-width: 100%;
|
|
@@ -49,387 +33,130 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
49
33
|
border-bottom: 1px solid var(--ox-gantt-border-color, var(--md-sys-color-on-surface));
|
|
50
34
|
height: 40px;
|
|
51
35
|
background-color: var(--ox-gantt-header-background-color, var(--md-sys-color-secondary-container));
|
|
52
|
-
|
|
53
36
|
position: sticky;
|
|
54
37
|
top: 0;
|
|
55
38
|
z-index: 1;
|
|
56
39
|
}
|
|
57
|
-
|
|
58
40
|
.top-axis text,
|
|
59
41
|
.bottom-axis text {
|
|
60
42
|
font-size: 12px;
|
|
61
43
|
}
|
|
62
|
-
|
|
63
44
|
.grid line {
|
|
64
45
|
stroke: #e0e0e0;
|
|
65
46
|
}
|
|
66
|
-
|
|
67
47
|
.grid path {
|
|
68
48
|
stroke-width: 0;
|
|
69
49
|
}
|
|
70
|
-
|
|
71
50
|
.no-select {
|
|
72
51
|
user-select: none;
|
|
73
52
|
}
|
|
53
|
+
.selected {
|
|
54
|
+
stroke-width: 2px;
|
|
55
|
+
stroke: var(--ox-gantt-selected-color, blue);
|
|
56
|
+
}
|
|
74
57
|
`
|
|
75
58
|
]; }
|
|
59
|
+
constructor() {
|
|
60
|
+
super();
|
|
61
|
+
this.fromDate = new Date(new Date().getFullYear(), 0, 1)
|
|
62
|
+
.toISOString()
|
|
63
|
+
.split('T')[0];
|
|
64
|
+
this.toDate = new Date(new Date().getFullYear(), 11, 31)
|
|
65
|
+
.toISOString()
|
|
66
|
+
.split('T')[0];
|
|
67
|
+
this.axisScale = 'week-day';
|
|
68
|
+
this.extendGridLines = false;
|
|
69
|
+
this.tasks = [];
|
|
70
|
+
this.locale = 'en-US';
|
|
71
|
+
this.ganttChartController = new GanttChartController(this, this.fromDate, this.toDate, this.axisScale);
|
|
72
|
+
}
|
|
76
73
|
firstUpdated(_changedProperties) {
|
|
77
74
|
this.initHeaderGestureHandlers();
|
|
78
75
|
}
|
|
79
76
|
updated(changedProperties) {
|
|
80
|
-
var redrawRequired = false;
|
|
81
77
|
if (changedProperties.has('fromDate') || changedProperties.has('toDate')) {
|
|
82
|
-
this.
|
|
83
|
-
this.toTime = new Date(this.toDate);
|
|
84
|
-
this.totalDays = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1));
|
|
85
|
-
redrawRequired = true;
|
|
86
|
-
}
|
|
87
|
-
if (changedProperties.has('axisScale')) {
|
|
88
|
-
redrawRequired = true;
|
|
78
|
+
this.ganttChartController.updateRange(this.fromDate, this.toDate);
|
|
89
79
|
}
|
|
90
80
|
if (changedProperties.has('tasks')) {
|
|
91
|
-
this.
|
|
92
|
-
|
|
81
|
+
this.ganttChartController.setTasks(this.tasks);
|
|
82
|
+
this.svgBody.style.height =
|
|
83
|
+
Math.max(this.tasks.length * 30, 2000 /* 태스크가 많이 없어도, 빈 공간이 없도록, 충분히 높게 */) + 'px';
|
|
84
|
+
}
|
|
85
|
+
// Update locale
|
|
86
|
+
if (changedProperties.has('locale')) {
|
|
87
|
+
this.updateLocale(this.locale);
|
|
88
|
+
}
|
|
89
|
+
if (changedProperties.has('selectedTaskId')) {
|
|
90
|
+
this.ganttChartController.selectedTaskId = this.selectedTaskId;
|
|
93
91
|
}
|
|
94
|
-
|
|
92
|
+
if (changedProperties.has('extendGridLines')) {
|
|
93
|
+
this.ganttChartController.extendGridLines = this.extendGridLines;
|
|
94
|
+
}
|
|
95
|
+
this.ganttChartController.drawAxes(this.svgHeader);
|
|
96
|
+
this.ganttChartController.drawBody(this.svgBody, this.tooltip);
|
|
95
97
|
}
|
|
96
98
|
render() {
|
|
97
99
|
return html `
|
|
98
|
-
<div
|
|
100
|
+
<div
|
|
101
|
+
chart-container
|
|
102
|
+
@wheel=${(e) => this.ganttChartController.handleWheelForBody(e, this.container)}
|
|
103
|
+
@touchstart=${(e) => this.ganttChartController.handleTouchStartForBody(e, this.container)}
|
|
104
|
+
@touchmove=${(e) => this.ganttChartController.handleTouchMoveForBody(e, this.container)}
|
|
105
|
+
>
|
|
99
106
|
<svg
|
|
100
107
|
header
|
|
101
|
-
@date-range-selected=${(e) =>
|
|
102
|
-
console.log(e.detail);
|
|
103
|
-
}}
|
|
108
|
+
@date-range-selected=${(e) => console.log(e.detail)}
|
|
104
109
|
@wheel=${this.handleWheelForHeader.bind(this)}
|
|
105
110
|
></svg>
|
|
106
111
|
<svg body></svg>
|
|
112
|
+
<ox-task-tooltip></ox-task-tooltip> // 툴팁 요소 추가
|
|
107
113
|
</div>
|
|
108
114
|
`;
|
|
109
115
|
}
|
|
110
116
|
setVerticalOffset(offset) {
|
|
111
|
-
console.log(offset);
|
|
112
117
|
this.container.scrollTop = offset;
|
|
113
118
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return 7 / 2;
|
|
117
|
-
}
|
|
118
|
-
else if (this.level == 'year-month') {
|
|
119
|
-
return 30 / 2;
|
|
120
|
-
}
|
|
121
|
-
else if (this.level == 'year-quarter') {
|
|
122
|
-
return 90 / 2;
|
|
123
|
-
}
|
|
124
|
-
return 1;
|
|
125
|
-
}
|
|
126
|
-
getChartWidth() {
|
|
127
|
-
return (this.totalDays * this.baseWidth) / this.getScale(); // 총 차트 폭 계산
|
|
119
|
+
getVerticalOffset() {
|
|
120
|
+
return this.container.scrollTop;
|
|
128
121
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
.
|
|
135
|
-
|
|
122
|
+
handleWheelForHeader(event) {
|
|
123
|
+
event.preventDefault();
|
|
124
|
+
event.stopPropagation();
|
|
125
|
+
if (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey) {
|
|
126
|
+
this.container.scrollBy({
|
|
127
|
+
left: event.shiftKey ? event.deltaX : event.deltaY
|
|
128
|
+
});
|
|
136
129
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
body.selectAll('*').remove();
|
|
149
|
-
body.attr('width', this.getChartWidth());
|
|
150
|
-
if (this.extendGridLines) {
|
|
151
|
-
const addGridLines = (ticks) => {
|
|
152
|
-
ticks.forEach(tick => {
|
|
153
|
-
const startX = this.timeScale(tick);
|
|
154
|
-
body
|
|
155
|
-
.append('line')
|
|
156
|
-
.attr('x1', startX)
|
|
157
|
-
.attr('x2', startX)
|
|
158
|
-
.attr('y1', 0)
|
|
159
|
-
.attr('y2', Number(this.svgBody.getBoundingClientRect().height) || 0)
|
|
160
|
-
.attr('stroke', 'var(--ox-gantt-color-gridlines, var(--md-sys-color-outline))')
|
|
161
|
-
.attr('stroke-width', 1);
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
-
if (this.level === 'week-day') {
|
|
165
|
-
const dayTicks = d3.timeDay.every(1).range(d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1));
|
|
166
|
-
addGridLines(dayTicks);
|
|
167
|
-
}
|
|
168
|
-
else if (this.level === 'quarter-week') {
|
|
169
|
-
const weekTicks = d3.timeWeek
|
|
170
|
-
.every(1)
|
|
171
|
-
.range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1));
|
|
172
|
-
addGridLines(weekTicks);
|
|
173
|
-
}
|
|
174
|
-
else if (this.level === 'year-month') {
|
|
175
|
-
const monthTicks = d3.timeMonth
|
|
176
|
-
.every(1)
|
|
177
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 1));
|
|
178
|
-
addGridLines(monthTicks);
|
|
179
|
-
}
|
|
180
|
-
else if (this.level === 'year-quarter') {
|
|
181
|
-
const quarterTicks = d3.timeMonth
|
|
182
|
-
.every(3)
|
|
183
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3));
|
|
184
|
-
addGridLines(quarterTicks);
|
|
130
|
+
else {
|
|
131
|
+
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9;
|
|
132
|
+
this.ganttChartController.baseWidth *= zoomFactor;
|
|
133
|
+
if (this.ganttChartController.baseWidth > 40) {
|
|
134
|
+
if (this.ganttChartController.level == 'week-day') {
|
|
135
|
+
this.ganttChartController.baseWidth = 40;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
this.ganttChartController.level = levelDown(this.ganttChartController.level);
|
|
139
|
+
this.ganttChartController.baseWidth = 30;
|
|
140
|
+
}
|
|
185
141
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const startX = this.timeScale(d3.timeDay.floor(d3.min([task.startTime, task.endTime])));
|
|
191
|
-
const endX = this.timeScale(d3.timeDay.offset(d3.timeDay.ceil(d3.max([task.startTime, task.endTime])), -1));
|
|
192
|
-
const taskWidth = endX - startX + this.baseWidth / this.getScale();
|
|
193
|
-
const taskY = index * 30 + 7.5;
|
|
194
|
-
// Main task bar
|
|
195
|
-
const taskBar = body
|
|
196
|
-
.append('rect')
|
|
197
|
-
.attr('x', startX)
|
|
198
|
-
.attr('y', taskY)
|
|
199
|
-
.attr('width', taskWidth)
|
|
200
|
-
.attr('height', 15)
|
|
201
|
-
.attr('fill', this.getTaskColor(task.tags || []))
|
|
202
|
-
.attr('stroke', 'black')
|
|
203
|
-
.attr('stroke-width', 1);
|
|
204
|
-
// Add click event listener to task bar
|
|
205
|
-
taskBar.on('click', () => {
|
|
206
|
-
const customEvent = new CustomEvent('task-clicked', {
|
|
207
|
-
detail: { task },
|
|
208
|
-
bubbles: true,
|
|
209
|
-
composed: true
|
|
210
|
-
});
|
|
211
|
-
this.dispatchEvent(customEvent);
|
|
212
|
-
});
|
|
213
|
-
// Progress bar
|
|
214
|
-
if (task.progress !== undefined) {
|
|
215
|
-
const progressWidth = taskWidth * (task.progress / 100);
|
|
216
|
-
body
|
|
217
|
-
.append('rect')
|
|
218
|
-
.attr('x', startX)
|
|
219
|
-
.attr('y', taskY + 7.5 - 3.75) // Adjust to center within the task bar
|
|
220
|
-
.attr('width', progressWidth)
|
|
221
|
-
.attr('height', 7.5) // Set height to 50% of the task bar height
|
|
222
|
-
.attr('fill', 'lightgreen');
|
|
142
|
+
else if (this.ganttChartController.baseWidth < 30) {
|
|
143
|
+
if (this.ganttChartController.level == 'year-quarter') {
|
|
144
|
+
this.ganttChartController.baseWidth = 30;
|
|
145
|
+
}
|
|
223
146
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// Add linking lines
|
|
228
|
-
this.tasks.forEach(task => {
|
|
229
|
-
if (task.after && taskPositions[task.after]) {
|
|
230
|
-
const prevTask = taskPositions[task.after];
|
|
231
|
-
const currTask = taskPositions[task.id];
|
|
232
|
-
// Draw the linking line
|
|
233
|
-
body
|
|
234
|
-
.append('path')
|
|
235
|
-
.attr('d', `
|
|
236
|
-
M${prevTask.x},${prevTask.y}
|
|
237
|
-
L${prevTask.x + 10},${prevTask.y}
|
|
238
|
-
${prevTask.x + 10 < currTask.x - 10 - currTask.width
|
|
239
|
-
? `
|
|
240
|
-
L${(prevTask.x + currTask.x - currTask.width) / 2},${prevTask.y}
|
|
241
|
-
L${(prevTask.x + currTask.x - currTask.width) / 2},${currTask.y}
|
|
242
|
-
`
|
|
243
|
-
: `
|
|
244
|
-
L${prevTask.x + 10},${(prevTask.y + currTask.y) / 2}
|
|
245
|
-
L${currTask.x - 10 - currTask.width},${(prevTask.y + currTask.y) / 2}
|
|
246
|
-
`}
|
|
247
|
-
L${currTask.x - 10 - currTask.width},${currTask.y}
|
|
248
|
-
L${currTask.x - currTask.width},${currTask.y}
|
|
249
|
-
`)
|
|
250
|
-
.attr('stroke', 'black')
|
|
251
|
-
.attr('fill', 'none')
|
|
252
|
-
.attr('stroke-width', 1);
|
|
147
|
+
if (this.ganttChartController.baseWidth < 20) {
|
|
148
|
+
this.ganttChartController.level = levelUp(this.ganttChartController.level);
|
|
149
|
+
this.ganttChartController.baseWidth = 30;
|
|
253
150
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// Helper function to determine task color based on tags
|
|
257
|
-
getTaskColor(tags) {
|
|
258
|
-
if (tags.includes('critical')) {
|
|
259
|
-
return 'red';
|
|
260
|
-
}
|
|
261
|
-
else if (tags.includes('milestone')) {
|
|
262
|
-
return 'blue';
|
|
263
|
-
}
|
|
264
|
-
else if (tags.includes('done')) {
|
|
265
|
-
return 'green';
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
return 'grey';
|
|
151
|
+
this.ganttChartController.updateTimeScale();
|
|
152
|
+
this.requestUpdate();
|
|
269
153
|
}
|
|
270
154
|
}
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
header.attr('width', this.getChartWidth());
|
|
277
|
-
const addGridLines = (ticks, yStart, yEnd, extendable) => {
|
|
278
|
-
ticks.forEach(tick => {
|
|
279
|
-
const startX = this.timeScale(tick);
|
|
280
|
-
header
|
|
281
|
-
.append('line')
|
|
282
|
-
.attr('x1', startX)
|
|
283
|
-
.attr('x2', startX)
|
|
284
|
-
.attr('y1', yStart)
|
|
285
|
-
.attr('y2', yEnd)
|
|
286
|
-
.attr('stroke', 'var(--ox-gantt-color-gridlines, var(--md-sys-color-outline))')
|
|
287
|
-
.attr('stroke-width', 1);
|
|
288
|
-
});
|
|
289
|
-
};
|
|
290
|
-
const centerLabel = (selection, tickInterval) => {
|
|
291
|
-
selection.selectAll('text').attr('transform', (d) => {
|
|
292
|
-
const tickIntervalWidth = this.timeScale(tickInterval.offset(d, 1)) - this.timeScale(d);
|
|
293
|
-
return `translate(${tickIntervalWidth / 2}, -2)`;
|
|
294
|
-
});
|
|
295
|
-
};
|
|
296
|
-
if (this.level === 'week-day') {
|
|
297
|
-
// Level 1: week-day
|
|
298
|
-
// Weekly axis
|
|
299
|
-
const weekAxis = d3
|
|
300
|
-
.axisTop(this.timeScale)
|
|
301
|
-
.ticks(d3.timeWeek.every(1))
|
|
302
|
-
.tickFormat(d3.timeFormat('%B %d'))
|
|
303
|
-
.tickSize(0);
|
|
304
|
-
const weekTicks = d3.timeWeek
|
|
305
|
-
.every(1)
|
|
306
|
-
.range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1));
|
|
307
|
-
addGridLines(weekTicks, 0, 20);
|
|
308
|
-
const weekAxisG = header
|
|
309
|
-
.append('g')
|
|
310
|
-
.classed('week-axis no-select md-typescale-label-small', true)
|
|
311
|
-
.attr('transform', `translate(0, 20)`)
|
|
312
|
-
.call(weekAxis);
|
|
313
|
-
centerLabel(weekAxisG, d3.timeWeek);
|
|
314
|
-
// Daily axis
|
|
315
|
-
const dayAxis = d3
|
|
316
|
-
.axisTop(this.timeScale)
|
|
317
|
-
.ticks(d3.timeDay.every(1))
|
|
318
|
-
.tickFormat(d3.timeFormat('%d'))
|
|
319
|
-
.tickSize(0);
|
|
320
|
-
const dayTicks = d3.timeDay.every(1).range(d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1));
|
|
321
|
-
addGridLines(dayTicks, 20, 40);
|
|
322
|
-
const dayAxisG = header
|
|
323
|
-
.append('g')
|
|
324
|
-
.classed('day-axis no-select md-typescale-label-small', true)
|
|
325
|
-
.attr('transform', `translate(0, 40)`)
|
|
326
|
-
.call(dayAxis);
|
|
327
|
-
centerLabel(dayAxisG, d3.timeDay);
|
|
328
|
-
}
|
|
329
|
-
else if (this.level === 'quarter-week') {
|
|
330
|
-
// Level 2: quarter-week
|
|
331
|
-
// Quarterly axis
|
|
332
|
-
const quarterAxis = d3
|
|
333
|
-
.axisTop(this.timeScale)
|
|
334
|
-
.ticks(d3.timeMonth.every(3))
|
|
335
|
-
.tickFormat(d3.timeFormat('%Y %b'))
|
|
336
|
-
.tickSize(0);
|
|
337
|
-
const quarterTicks = d3.timeMonth
|
|
338
|
-
.every(3)
|
|
339
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3));
|
|
340
|
-
addGridLines(quarterTicks, 0, 20);
|
|
341
|
-
const quarterAxisG = header
|
|
342
|
-
.append('g')
|
|
343
|
-
.classed('quarter-axis no-select md-typescale-label-small', true)
|
|
344
|
-
.attr('transform', `translate(0, 20)`)
|
|
345
|
-
.call(quarterAxis);
|
|
346
|
-
centerLabel(quarterAxisG, d3.timeMonth.every(3));
|
|
347
|
-
// Weekly axis
|
|
348
|
-
const weekAxis = d3
|
|
349
|
-
.axisTop(this.timeScale)
|
|
350
|
-
.ticks(d3.timeWeek.every(1))
|
|
351
|
-
.tickFormat(d3.timeFormat('%b %d'))
|
|
352
|
-
.tickSize(0);
|
|
353
|
-
const weekTicks = d3.timeWeek
|
|
354
|
-
.every(1)
|
|
355
|
-
.range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1));
|
|
356
|
-
addGridLines(weekTicks, 20, 40);
|
|
357
|
-
const weekAxisG = header
|
|
358
|
-
.append('g')
|
|
359
|
-
.classed('week-axis no-select md-typescale-label-small', true)
|
|
360
|
-
.attr('transform', `translate(0, 40)`)
|
|
361
|
-
.call(weekAxis);
|
|
362
|
-
centerLabel(weekAxisG, d3.timeWeek);
|
|
363
|
-
}
|
|
364
|
-
else if (this.level === 'year-month') {
|
|
365
|
-
// Level 3: year-month
|
|
366
|
-
// Yearly axis
|
|
367
|
-
const yearAxis = d3
|
|
368
|
-
.axisTop(this.timeScale)
|
|
369
|
-
.ticks(d3.timeYear.every(1))
|
|
370
|
-
.tickFormat(d3.timeFormat('%Y'))
|
|
371
|
-
.tickSize(0);
|
|
372
|
-
const yearTicks = d3.timeYear
|
|
373
|
-
.every(1)
|
|
374
|
-
.range(d3.timeYear.floor(this.fromTime), d3.timeYear.offset(this.toTime, 1));
|
|
375
|
-
addGridLines(yearTicks, 0, 20);
|
|
376
|
-
const yearAxisG = header
|
|
377
|
-
.append('g')
|
|
378
|
-
.classed('year-axis no-select md-typescale-label-small', true)
|
|
379
|
-
.attr('transform', `translate(0, 20)`)
|
|
380
|
-
.call(yearAxis);
|
|
381
|
-
centerLabel(yearAxisG, d3.timeYear.every(1));
|
|
382
|
-
// Monthly axis
|
|
383
|
-
const monthAxis = d3
|
|
384
|
-
.axisTop(this.timeScale)
|
|
385
|
-
.ticks(d3.timeMonth.every(1))
|
|
386
|
-
.tickFormat(d3.timeFormat('%b'))
|
|
387
|
-
.tickSize(0);
|
|
388
|
-
const monthTicks = d3.timeMonth
|
|
389
|
-
.every(1)
|
|
390
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 1));
|
|
391
|
-
addGridLines(monthTicks, 20, 40);
|
|
392
|
-
const monthAxisG = header
|
|
393
|
-
.append('g')
|
|
394
|
-
.classed('month-axis no-select md-typescale-label-small', true)
|
|
395
|
-
.attr('transform', `translate(0, 40)`)
|
|
396
|
-
.call(monthAxis);
|
|
397
|
-
centerLabel(monthAxisG, d3.timeMonth.every(1));
|
|
398
|
-
}
|
|
399
|
-
else if (this.level === 'year-quarter') {
|
|
400
|
-
// Level 4: year-quarter
|
|
401
|
-
// Yearly axis
|
|
402
|
-
const yearAxis = d3
|
|
403
|
-
.axisTop(this.timeScale)
|
|
404
|
-
.ticks(d3.timeYear.every(1))
|
|
405
|
-
.tickFormat(d3.timeFormat('%Y'))
|
|
406
|
-
.tickSize(0);
|
|
407
|
-
const yearTicks = d3.timeYear
|
|
408
|
-
.every(1)
|
|
409
|
-
.range(d3.timeYear.floor(this.fromTime), d3.timeYear.offset(this.toTime, 1));
|
|
410
|
-
addGridLines(yearTicks, 0, 20);
|
|
411
|
-
const yearAxisG = header
|
|
412
|
-
.append('g')
|
|
413
|
-
.classed('year-axis no-select md-typescale-label-small', true)
|
|
414
|
-
.attr('transform', `translate(0, 20)`)
|
|
415
|
-
.call(yearAxis);
|
|
416
|
-
centerLabel(yearAxisG, d3.timeYear.every(1));
|
|
417
|
-
// Quarterly axis
|
|
418
|
-
const quarterAxis = d3
|
|
419
|
-
.axisTop(this.timeScale)
|
|
420
|
-
.ticks(d3.timeMonth.every(3))
|
|
421
|
-
.tickFormat(d3.timeFormat('%Y %b'))
|
|
422
|
-
.tickSize(0);
|
|
423
|
-
const quarterTicks = d3.timeMonth
|
|
424
|
-
.every(3)
|
|
425
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3));
|
|
426
|
-
addGridLines(quarterTicks, 20, 40);
|
|
427
|
-
const quarterAxisG = header
|
|
428
|
-
.append('g')
|
|
429
|
-
.classed('quarter-axis no-select md-typescale-label-small', true)
|
|
430
|
-
.attr('transform', `translate(0, 40)`)
|
|
431
|
-
.call(quarterAxis);
|
|
432
|
-
centerLabel(quarterAxisG, d3.timeMonth.every(3));
|
|
155
|
+
updateLocale(locale) {
|
|
156
|
+
const localeDefinition = locales[locale || 'en-US'];
|
|
157
|
+
if (localeDefinition) {
|
|
158
|
+
d3.timeFormatDefaultLocale(localeDefinition);
|
|
159
|
+
this.ganttChartController.updateTimeScale();
|
|
433
160
|
}
|
|
434
161
|
}
|
|
435
162
|
initHeaderGestureHandlers() {
|
|
@@ -438,7 +165,7 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
438
165
|
let startX = null;
|
|
439
166
|
let lastTouchDistance = null;
|
|
440
167
|
const handleTouchStart = (event) => {
|
|
441
|
-
event.preventDefault();
|
|
168
|
+
event.preventDefault();
|
|
442
169
|
if (event.touches.length === 2) {
|
|
443
170
|
lastTouchDistance = getTouchDistance(event.touches);
|
|
444
171
|
}
|
|
@@ -450,32 +177,32 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
450
177
|
}
|
|
451
178
|
};
|
|
452
179
|
const handleTouchMove = (event) => {
|
|
453
|
-
event.preventDefault();
|
|
180
|
+
event.preventDefault();
|
|
454
181
|
if (event.touches.length === 2 && lastTouchDistance !== null) {
|
|
455
182
|
const currentDistance = getTouchDistance(event.touches);
|
|
456
183
|
const zoomFactor = currentDistance / lastTouchDistance;
|
|
457
|
-
this.baseWidth *= zoomFactor;
|
|
458
|
-
if (this.baseWidth > 60) {
|
|
459
|
-
if (this.level == 'year-quarter') {
|
|
460
|
-
this.baseWidth = 60;
|
|
184
|
+
this.ganttChartController.baseWidth *= zoomFactor;
|
|
185
|
+
if (this.ganttChartController.baseWidth > 60) {
|
|
186
|
+
if (this.ganttChartController.level == 'year-quarter') {
|
|
187
|
+
this.ganttChartController.baseWidth = 60;
|
|
461
188
|
}
|
|
462
189
|
else {
|
|
463
|
-
this.level = levelUp(this.level);
|
|
464
|
-
this.baseWidth = 30;
|
|
190
|
+
this.ganttChartController.level = levelUp(this.ganttChartController.level);
|
|
191
|
+
this.ganttChartController.baseWidth = 30;
|
|
465
192
|
}
|
|
466
193
|
}
|
|
467
|
-
else if (this.baseWidth < 30) {
|
|
468
|
-
if (this.level == 'week-day') {
|
|
469
|
-
this.baseWidth = 30;
|
|
194
|
+
else if (this.ganttChartController.baseWidth < 30) {
|
|
195
|
+
if (this.ganttChartController.level == 'week-day') {
|
|
196
|
+
this.ganttChartController.baseWidth = 30;
|
|
470
197
|
}
|
|
471
198
|
else {
|
|
472
|
-
this.level = levelDown(this.level);
|
|
473
|
-
this.baseWidth = 60;
|
|
199
|
+
this.ganttChartController.level = levelDown(this.ganttChartController.level);
|
|
200
|
+
this.ganttChartController.baseWidth = 60;
|
|
474
201
|
}
|
|
475
202
|
}
|
|
476
203
|
lastTouchDistance = currentDistance;
|
|
477
|
-
this.updateTimeScale();
|
|
478
|
-
this.
|
|
204
|
+
this.ganttChartController.updateTimeScale();
|
|
205
|
+
this.requestUpdate();
|
|
479
206
|
}
|
|
480
207
|
else if (isDragging && startX !== null) {
|
|
481
208
|
const touch = event.touches[0];
|
|
@@ -485,12 +212,11 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
485
212
|
}
|
|
486
213
|
};
|
|
487
214
|
const handleTouchEnd = (event) => {
|
|
488
|
-
event.preventDefault();
|
|
215
|
+
event.preventDefault();
|
|
489
216
|
if (event.touches.length < 2) {
|
|
490
217
|
lastTouchDistance = null;
|
|
491
218
|
}
|
|
492
219
|
if (!isDragging || startX === null) {
|
|
493
|
-
// 클릭 이벤트 처리
|
|
494
220
|
const touch = event.changedTouches[0];
|
|
495
221
|
const containerOffsetX = header.node().getBoundingClientRect().left;
|
|
496
222
|
const clickX = touch.clientX - containerOffsetX;
|
|
@@ -523,7 +249,6 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
523
249
|
};
|
|
524
250
|
const handleMouseUp = (event) => {
|
|
525
251
|
if (!isDragging || startX === null) {
|
|
526
|
-
// 클릭 이벤트 처리
|
|
527
252
|
const containerOffsetX = header.node().getBoundingClientRect().left;
|
|
528
253
|
const clickX = event.clientX - containerOffsetX;
|
|
529
254
|
this.finalizeSelection(clickX, clickX);
|
|
@@ -545,22 +270,22 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
545
270
|
highlightSelection(startX, endX) {
|
|
546
271
|
const header = d3.select(this.svgHeader);
|
|
547
272
|
const body = d3.select(this.svgBody);
|
|
548
|
-
let startDate = this.timeScale.invert(startX);
|
|
549
|
-
let endDate = this.timeScale.invert(endX);
|
|
273
|
+
let startDate = this.ganttChartController.timeScale.invert(startX);
|
|
274
|
+
let endDate = this.ganttChartController.timeScale.invert(endX);
|
|
550
275
|
let start, end;
|
|
551
|
-
if (this.level === 'week-day') {
|
|
276
|
+
if (this.ganttChartController.level === 'week-day') {
|
|
552
277
|
start = d3.timeDay.floor(d3.min([startDate, endDate]));
|
|
553
278
|
end = d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])), -1);
|
|
554
279
|
}
|
|
555
|
-
else if (this.level === 'quarter-week') {
|
|
280
|
+
else if (this.ganttChartController.level === 'quarter-week') {
|
|
556
281
|
start = d3.timeWeek.floor(d3.min([startDate, endDate]));
|
|
557
282
|
end = d3.timeDay.offset(d3.timeWeek.ceil(d3.max([startDate, endDate])), -1);
|
|
558
283
|
}
|
|
559
|
-
else if (this.level === 'year-month') {
|
|
284
|
+
else if (this.ganttChartController.level === 'year-month') {
|
|
560
285
|
start = d3.timeMonth.floor(d3.min([startDate, endDate]));
|
|
561
286
|
end = d3.timeDay.offset(d3.timeMonth.ceil(d3.max([startDate, endDate])), -1);
|
|
562
287
|
}
|
|
563
|
-
else if (this.level === 'year-quarter') {
|
|
288
|
+
else if (this.ganttChartController.level === 'year-quarter') {
|
|
564
289
|
start = d3.timeMonth.every(3).floor(d3.min([startDate, endDate]));
|
|
565
290
|
end = d3.timeDay.offset(d3.timeMonth.every(3).ceil(d3.max([startDate, endDate])), -1);
|
|
566
291
|
}
|
|
@@ -569,18 +294,22 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
569
294
|
header
|
|
570
295
|
.append('rect')
|
|
571
296
|
.attr('class', 'selection-rect')
|
|
572
|
-
.attr('x', this.timeScale(start))
|
|
297
|
+
.attr('x', this.ganttChartController.timeScale(start))
|
|
573
298
|
.attr('y', 20)
|
|
574
|
-
.attr('width', this.timeScale(end) -
|
|
299
|
+
.attr('width', this.ganttChartController.timeScale(end) -
|
|
300
|
+
this.ganttChartController.timeScale(start) +
|
|
301
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale())
|
|
575
302
|
.attr('height', 20)
|
|
576
303
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
577
304
|
.attr('opacity', '.2');
|
|
578
305
|
body
|
|
579
306
|
.append('rect')
|
|
580
307
|
.attr('class', 'selection-rect')
|
|
581
|
-
.attr('x', this.timeScale(start))
|
|
308
|
+
.attr('x', this.ganttChartController.timeScale(start))
|
|
582
309
|
.attr('y', 0)
|
|
583
|
-
.attr('width', this.timeScale(end) -
|
|
310
|
+
.attr('width', this.ganttChartController.timeScale(end) -
|
|
311
|
+
this.ganttChartController.timeScale(start) +
|
|
312
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale())
|
|
584
313
|
.attr('height', '100%')
|
|
585
314
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
586
315
|
.attr('opacity', '.2');
|
|
@@ -588,22 +317,22 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
588
317
|
finalizeSelection(startX, endX) {
|
|
589
318
|
const header = d3.select(this.svgHeader);
|
|
590
319
|
const body = d3.select(this.svgBody);
|
|
591
|
-
let startDate = this.timeScale.invert(startX);
|
|
592
|
-
let endDate = this.timeScale.invert(endX);
|
|
320
|
+
let startDate = this.ganttChartController.timeScale.invert(startX);
|
|
321
|
+
let endDate = this.ganttChartController.timeScale.invert(endX);
|
|
593
322
|
let start, end;
|
|
594
|
-
if (this.level === 'week-day') {
|
|
323
|
+
if (this.ganttChartController.level === 'week-day') {
|
|
595
324
|
start = d3.timeDay.floor(d3.min([startDate, endDate]));
|
|
596
325
|
end = d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])), -1);
|
|
597
326
|
}
|
|
598
|
-
else if (this.level === 'quarter-week') {
|
|
327
|
+
else if (this.ganttChartController.level === 'quarter-week') {
|
|
599
328
|
start = d3.timeWeek.floor(d3.min([startDate, endDate]));
|
|
600
329
|
end = d3.timeDay.offset(d3.timeWeek.ceil(d3.max([startDate, endDate])), -1);
|
|
601
330
|
}
|
|
602
|
-
else if (this.level === 'year-month') {
|
|
331
|
+
else if (this.ganttChartController.level === 'year-month') {
|
|
603
332
|
start = d3.timeMonth.floor(d3.min([startDate, endDate]));
|
|
604
333
|
end = d3.timeDay.offset(d3.timeMonth.ceil(d3.max([startDate, endDate])), -1);
|
|
605
334
|
}
|
|
606
|
-
else if (this.level === 'year-quarter') {
|
|
335
|
+
else if (this.ganttChartController.level === 'year-quarter') {
|
|
607
336
|
start = d3.timeMonth.every(3).floor(d3.min([startDate, endDate]));
|
|
608
337
|
end = d3.timeDay.offset(d3.timeMonth.every(3).ceil(d3.max([startDate, endDate])), -1);
|
|
609
338
|
}
|
|
@@ -612,18 +341,22 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
612
341
|
header
|
|
613
342
|
.append('rect')
|
|
614
343
|
.attr('class', 'selection-rect')
|
|
615
|
-
.attr('x', this.timeScale(start))
|
|
344
|
+
.attr('x', this.ganttChartController.timeScale(start))
|
|
616
345
|
.attr('y', 20)
|
|
617
|
-
.attr('width', this.timeScale(end) -
|
|
346
|
+
.attr('width', this.ganttChartController.timeScale(end) -
|
|
347
|
+
this.ganttChartController.timeScale(start) +
|
|
348
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale())
|
|
618
349
|
.attr('height', 20)
|
|
619
350
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
620
351
|
.attr('opacity', '.2');
|
|
621
352
|
body
|
|
622
353
|
.append('rect')
|
|
623
354
|
.attr('class', 'selection-rect')
|
|
624
|
-
.attr('x', this.timeScale(start))
|
|
355
|
+
.attr('x', this.ganttChartController.timeScale(start))
|
|
625
356
|
.attr('y', 0)
|
|
626
|
-
.attr('width', this.timeScale(end) -
|
|
357
|
+
.attr('width', this.ganttChartController.timeScale(end) -
|
|
358
|
+
this.ganttChartController.timeScale(start) +
|
|
359
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale())
|
|
627
360
|
.attr('height', '100%')
|
|
628
361
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
629
362
|
.attr('opacity', '.2');
|
|
@@ -633,42 +366,6 @@ let OxGanttChart = class OxGanttChart extends LitElement {
|
|
|
633
366
|
});
|
|
634
367
|
header.node()?.dispatchEvent(customEvent);
|
|
635
368
|
}
|
|
636
|
-
handleWheelForHeader(event) {
|
|
637
|
-
event.preventDefault();
|
|
638
|
-
event.stopPropagation();
|
|
639
|
-
if (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey) {
|
|
640
|
-
// 좌우 스크롤
|
|
641
|
-
this.container.scrollBy({
|
|
642
|
-
left: event.shiftKey ? event.deltaX : event.deltaY,
|
|
643
|
-
behavior: 'smooth'
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
else {
|
|
647
|
-
// Zoom in/out
|
|
648
|
-
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9;
|
|
649
|
-
this.baseWidth *= zoomFactor;
|
|
650
|
-
if (this.baseWidth > 40) {
|
|
651
|
-
if (this.level == 'week-day') {
|
|
652
|
-
this.baseWidth = 40;
|
|
653
|
-
}
|
|
654
|
-
else {
|
|
655
|
-
this.level = levelDown(this.level);
|
|
656
|
-
this.baseWidth = 30;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
else if (this.baseWidth < 30) {
|
|
660
|
-
if (this.level == 'year-quarter') {
|
|
661
|
-
this.baseWidth = 30;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
if (this.baseWidth < 20) {
|
|
665
|
-
this.level = levelUp(this.level);
|
|
666
|
-
this.baseWidth = 30;
|
|
667
|
-
}
|
|
668
|
-
this.updateTimeScale();
|
|
669
|
-
this.redraw();
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
369
|
};
|
|
673
370
|
__decorate([
|
|
674
371
|
property({ type: String, attribute: 'from-date' })
|
|
@@ -686,11 +383,11 @@ __decorate([
|
|
|
686
383
|
property({ type: Array })
|
|
687
384
|
], OxGanttChart.prototype, "tasks", void 0);
|
|
688
385
|
__decorate([
|
|
689
|
-
|
|
690
|
-
], OxGanttChart.prototype, "
|
|
386
|
+
property({ type: String, attribute: 'selected-task-id' })
|
|
387
|
+
], OxGanttChart.prototype, "selectedTaskId", void 0);
|
|
691
388
|
__decorate([
|
|
692
|
-
|
|
693
|
-
], OxGanttChart.prototype, "
|
|
389
|
+
property({ type: String })
|
|
390
|
+
], OxGanttChart.prototype, "locale", void 0);
|
|
694
391
|
__decorate([
|
|
695
392
|
query('svg[header]')
|
|
696
393
|
], OxGanttChart.prototype, "svgHeader", void 0);
|
|
@@ -700,6 +397,9 @@ __decorate([
|
|
|
700
397
|
__decorate([
|
|
701
398
|
query('[chart-container]')
|
|
702
399
|
], OxGanttChart.prototype, "container", void 0);
|
|
400
|
+
__decorate([
|
|
401
|
+
query('ox-task-tooltip')
|
|
402
|
+
], OxGanttChart.prototype, "tooltip", void 0);
|
|
703
403
|
OxGanttChart = __decorate([
|
|
704
404
|
customElement('ox-gantt-chart')
|
|
705
405
|
], OxGanttChart);
|