@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/src/gantt-chart-controller.d.ts +32 -0
  3. package/dist/src/gantt-chart-controller.js +393 -0
  4. package/dist/src/gantt-chart-controller.js.map +1 -0
  5. package/dist/src/locales.d.ts +52 -0
  6. package/dist/src/locales.js +79 -0
  7. package/dist/src/locales.js.map +1 -0
  8. package/dist/src/ox-gantt-chart.d.ts +10 -17
  9. package/dist/src/ox-gantt-chart.js +135 -435
  10. package/dist/src/ox-gantt-chart.js.map +1 -1
  11. package/dist/src/ox-gantt-resize-splitter.d.ts +2 -3
  12. package/dist/src/ox-gantt-resize-splitter.js +15 -22
  13. package/dist/src/ox-gantt-resize-splitter.js.map +1 -1
  14. package/dist/src/ox-gantt.d.ts +14 -1
  15. package/dist/src/ox-gantt.js +149 -28
  16. package/dist/src/ox-gantt.js.map +1 -1
  17. package/dist/src/ox-task-tooltip.d.ts +12 -0
  18. package/dist/src/ox-task-tooltip.js +132 -0
  19. package/dist/src/ox-task-tooltip.js.map +1 -0
  20. package/dist/src/types.d.ts +10 -3
  21. package/dist/src/types.js.map +1 -1
  22. package/dist/stories/ox-gantt.stories.d.ts +12 -0
  23. package/dist/stories/ox-gantt.stories.js +100 -88
  24. package/dist/stories/ox-gantt.stories.js.map +1 -1
  25. package/dist/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +3 -2
  27. package/src/gantt-chart-controller.ts +470 -0
  28. package/src/locales.ts +78 -0
  29. package/src/ox-gantt-chart.ts +143 -481
  30. package/src/ox-gantt-resize-splitter.ts +18 -25
  31. package/src/ox-gantt.ts +153 -30
  32. package/src/ox-task-tooltip.ts +120 -0
  33. package/src/types.ts +11 -3
  34. package/stories/ox-gantt.stories.ts +105 -88
  35. package/translations/en.json +11 -1
  36. package/translations/ja.json +11 -1
  37. package/translations/ko.json +11 -1
  38. package/translations/ms.json +11 -1
  39. package/translations/zh.json +11 -1
  40. package/dist/src/locale.d.ts +0 -1
  41. package/dist/src/locale.js +0 -49
  42. package/dist/src/locale.js.map +0 -1
  43. 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, state } from 'lit/decorators.js';
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 { levelDown, levelUp } from './utils';
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: clip;
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.fromTime = new Date(this.fromDate);
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.svgBody.style.height = this.tasks.length * 30 + 'px';
92
- redrawRequired = true;
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
- redrawRequired && this.redraw();
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 chart-container>
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
- getScale() {
115
- if (this.level == 'quarter-week') {
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
- get timeScale() {
130
- if (!this._timeScale) {
131
- // 시간축 스케일 생성
132
- this._timeScale = d3
133
- .scaleTime()
134
- .domain([d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1)])
135
- .range([0, this.getChartWidth()]);
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
- return this._timeScale;
138
- }
139
- updateTimeScale() {
140
- this.timeScale.range([0, this.getChartWidth()]);
141
- }
142
- redraw() {
143
- this.drawAxes();
144
- this.drawBody();
145
- }
146
- drawBody() {
147
- const body = d3.select(this.svgBody);
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
- const taskPositions = {};
188
- // Add task bars
189
- this.tasks.forEach((task, index) => {
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
- // Save task position for linking lines
225
- taskPositions[task.id] = { x: endX + this.baseWidth / this.getScale(), y: taskY + 7.5, width: taskWidth };
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
- drawAxes() {
272
- const header = d3.select(this.svgHeader);
273
- header.selectAll('*').remove();
274
- header.attr('width', this.getChartWidth());
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.redraw();
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) - this.timeScale(start) + this.baseWidth / this.getScale())
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) - this.timeScale(start) + this.baseWidth / this.getScale())
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) - this.timeScale(start) + this.baseWidth / this.getScale())
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) - this.timeScale(start) + this.baseWidth / this.getScale())
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
- state()
690
- ], OxGanttChart.prototype, "baseWidth", void 0);
386
+ property({ type: String, attribute: 'selected-task-id' })
387
+ ], OxGanttChart.prototype, "selectedTaskId", void 0);
691
388
  __decorate([
692
- state()
693
- ], OxGanttChart.prototype, "level", void 0);
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);