@operato/gantt 7.1.27 → 7.1.32

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.
@@ -1,336 +0,0 @@
1
- import { LitElement, ReactiveController, ReactiveControllerHost } from 'lit'
2
- import * as d3 from 'd3'
3
- import { TimeScale, Task, TaskTag } from './types'
4
- import { OxTaskTooltip } from './ox-task-tooltip'
5
- import { drawTimeScaleHeader, getScale, getChartWidth } from './draw-time-scale-header'
6
-
7
- export class GanttChartController implements ReactiveController {
8
- private host: ReactiveControllerHost & LitElement
9
- private fromTime: Date
10
- private toTime: Date
11
- private totalDays: number
12
- private _timeScale?: d3.ScaleTime<number, number, never>
13
-
14
- private touchStartX?: number
15
- private touchStartY?: number
16
- private scrollStartX?: number
17
- private scrollStartY?: number
18
-
19
- private colorProvider?: (task: Task) => string
20
-
21
- public baseWidth: number = 30
22
- public level: TimeScale
23
- public selectedTaskId?: string
24
-
25
- public tasks: Task[] = []
26
- public extendGridLines: boolean = false
27
-
28
- constructor(host: ReactiveControllerHost & LitElement, fromDate: string, toDate: string, axisScale: TimeScale) {
29
- this.host = host
30
- this.fromTime = new Date(fromDate)
31
- this.toTime = new Date(toDate)
32
- this.level = axisScale
33
- this.totalDays = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1))
34
- this.host.addController(this)
35
- }
36
-
37
- hostConnected() {
38
- this.updateTimeScale()
39
- }
40
-
41
- setColorProvider(colorProvider: (task: Task) => string) {
42
- this.colorProvider = colorProvider
43
- }
44
-
45
- get timeScale() {
46
- if (!this._timeScale) {
47
- this._timeScale = d3
48
- .scaleTime()
49
- .domain([d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1)])
50
- .range([0, this.getChartWidth()])
51
- }
52
- return this._timeScale
53
- }
54
-
55
- updateRange(fromDate: string, toDate: string) {
56
- this.fromTime = new Date(fromDate)
57
- this.toTime = new Date(toDate)
58
- this.totalDays = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1))
59
- this._timeScale = undefined
60
- this.host.requestUpdate()
61
- }
62
-
63
- setTasks(tasks: Task[]) {
64
- this.tasks = tasks
65
- this.host.requestUpdate()
66
- }
67
-
68
- updateTimeScale() {
69
- this.timeScale.range([0, this.getChartWidth()])
70
- }
71
-
72
- getScale() {
73
- return getScale(this.level)
74
- }
75
-
76
- getChartWidth() {
77
- return getChartWidth(this.fromTime, this.toTime, this.baseWidth, this.level)
78
- }
79
-
80
- drawAxes(svgHeader: SVGElement) {
81
- drawTimeScaleHeader(svgHeader, this.timeScale, this.fromTime, this.toTime, this.baseWidth, this.level)
82
- }
83
-
84
- drawBody(svgBody: SVGElement, tooltip: OxTaskTooltip) {
85
- const body = d3.select(svgBody)
86
-
87
- body.selectAll('*').remove()
88
- body.attr('width', this.getChartWidth())
89
-
90
- // Define the arrow marker
91
- const defs = body.append('defs')
92
- defs
93
- .append('marker')
94
- .attr('id', 'arrow')
95
- .attr('viewBox', '0 0 10 10')
96
- .attr('refX', 10) // Adjusted refX to move the arrow tip closer to the line's end
97
- .attr('refY', 5)
98
- .attr('markerWidth', 6)
99
- .attr('markerHeight', 6)
100
- .attr('orient', 'auto-start-reverse')
101
- .append('path')
102
- .attr('d', 'M 0 0 L 10 5 L 0 10 z')
103
- .attr('fill', 'var(--md-sys-color-on-primary-container, black)')
104
-
105
- if (this.extendGridLines) {
106
- const addGridLines = (ticks: Date[]) => {
107
- ticks.forEach(tick => {
108
- const startX = this.timeScale(tick)
109
- body
110
- .append('line')
111
- .attr('x1', startX)
112
- .attr('x2', startX)
113
- .attr('y1', 0)
114
- .attr('y2', Number(svgBody.getBoundingClientRect().height) || 0)
115
- .attr('stroke', 'var(--ox-gantt-color-gridlines, var(--md-sys-color-outline))')
116
- .attr('stroke-width', 1)
117
- })
118
- }
119
-
120
- if (this.level === 'week-day') {
121
- const dayTicks = d3.timeDay.every(1)!.range(d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1))
122
- addGridLines(dayTicks)
123
- } else if (this.level === 'quarter-week') {
124
- const weekTicks = d3.timeWeek
125
- .every(1)!
126
- .range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1))
127
- addGridLines(weekTicks)
128
- } else if (this.level === 'year-month') {
129
- const monthTicks = d3.timeMonth
130
- .every(1)!
131
- .range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 1))
132
- addGridLines(monthTicks)
133
- } else if (this.level === 'year-quarter') {
134
- const quarterTicks = d3.timeMonth
135
- .every(3)!
136
- .range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3))
137
- addGridLines(quarterTicks)
138
- }
139
- }
140
-
141
- const taskPositions: { [key: string]: { x: number; y: number; width: number } } = {}
142
-
143
- this.tasks.forEach((task, index) => {
144
- const startDate = new Date(task.startDate)
145
- const endDate = new Date(task.endDate)
146
-
147
- const startX = this.timeScale(d3.timeDay.floor(d3.min([startDate, endDate])!))
148
- const endX = this.timeScale(d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])!), -1))
149
-
150
- const taskWidth = endX - startX + this.baseWidth / this.getScale()
151
- const taskY = index * 30 + 7.5
152
-
153
- const showTooltip = (event: MouseEvent) => {
154
- tooltip.title = task.title
155
- tooltip.startDate = startDate.toISOString().split('T')[0]
156
- tooltip.endDate = endDate.toISOString().split('T')[0]
157
- tooltip.progress = task.progress ?? 0
158
- tooltip.type = task.type
159
-
160
- tooltip.show(event.clientX, event.clientY)
161
- }
162
-
163
- const hideTooltip = () => {
164
- tooltip.hide()
165
- }
166
-
167
- if (this.selectedTaskId === task.id) {
168
- body
169
- .append('rect')
170
- .attr('x', 0)
171
- .attr('y', taskY - 7.5)
172
- .attr('width', this.getChartWidth())
173
- .attr('height', 30)
174
- .attr('fill', 'var(--ox-gantt-selected-row-background-color, var(--md-sys-color-surface-variant))')
175
- }
176
-
177
- const taskBar = body
178
- .append('rect')
179
- .attr('x', startX)
180
- .attr('y', taskY)
181
- .attr('width', taskWidth)
182
- .attr('height', 15)
183
- .attr('fill', this.colorProvider ? this.colorProvider(task) : 'gray')
184
- .attr('stroke', 'black')
185
- .attr('stroke-width', 1)
186
-
187
- taskBar
188
- .on('click', () => {
189
- this.selectedTaskId = task.id
190
- const customEvent = new CustomEvent('task-clicked', {
191
- detail: { taskId: task.id },
192
- bubbles: true,
193
- composed: true
194
- })
195
- this.host.dispatchEvent(customEvent)
196
- this.host.requestUpdate()
197
- })
198
- .on('mouseover', showTooltip)
199
- .on('mouseout', hideTooltip)
200
-
201
- if (task.progress !== undefined) {
202
- const progressWidth = taskWidth * (task.progress / 100)
203
- const progressBar = body
204
- .append('rect')
205
- .attr('x', startX)
206
- .attr('y', taskY + 7.5 - 3.75)
207
- .attr('width', progressWidth)
208
- .attr('height', 7.5)
209
- .attr('fill', 'lightgreen')
210
-
211
- progressBar
212
- .on('click', () => {
213
- this.selectedTaskId = task.id
214
- const customEvent = new CustomEvent('task-clicked', {
215
- detail: { taskId: task.id },
216
- bubbles: true,
217
- composed: true
218
- })
219
- this.host.dispatchEvent(customEvent)
220
- this.host.requestUpdate()
221
- })
222
- .on('mouseover', showTooltip)
223
- .on('mouseout', hideTooltip)
224
- }
225
-
226
- taskPositions[task.id] = { x: endX + this.baseWidth / this.getScale(), y: taskY + 7.5, width: taskWidth }
227
- })
228
-
229
- this.tasks.forEach(task => {
230
- if (task.dependsOn && taskPositions[task.dependsOn]) {
231
- const prevTask = taskPositions[task.dependsOn]
232
- const currTask = taskPositions[task.id]
233
-
234
- body
235
- .append('path')
236
- .attr(
237
- 'd',
238
- `
239
- M${prevTask.x},${prevTask.y}
240
- L${prevTask.x + 10},${prevTask.y}
241
- ${
242
- prevTask.x + 10 < currTask.x - 10 - currTask.width
243
- ? `
244
- L${(prevTask.x + currTask.x - currTask.width) / 2},${prevTask.y}
245
- L${(prevTask.x + currTask.x - currTask.width) / 2},${currTask.y}
246
- `
247
- : `
248
- L${prevTask.x + 10},${(prevTask.y + currTask.y) / 2}
249
- L${currTask.x - 10 - currTask.width},${(prevTask.y + currTask.y) / 2}
250
- `
251
- }
252
- L${currTask.x - 10 - currTask.width},${currTask.y}
253
- L${currTask.x - currTask.width},${currTask.y}
254
- `
255
- )
256
- .attr('stroke', 'var(--md-sys-color-on-primary-container, black)')
257
- .attr('fill', 'none')
258
- .attr('stroke-width', 1)
259
- .attr('marker-end', 'url(#arrow)') // Add arrow marker
260
- }
261
- })
262
- }
263
-
264
- getTaskColor(tags: TaskTag[]): string {
265
- if (tags.includes('critical')) {
266
- return 'red'
267
- } else if (tags.includes('milestone')) {
268
- return 'blue'
269
- } else if (tags.includes('done')) {
270
- return 'green'
271
- } else {
272
- return 'grey'
273
- }
274
- }
275
-
276
- handleWheelForBody(event: WheelEvent, container: HTMLDivElement) {
277
- event.preventDefault()
278
- event.stopPropagation()
279
-
280
- const deltaModeFactor =
281
- event.deltaMode === WheelEvent.DOM_DELTA_LINE
282
- ? 16
283
- : event.deltaMode === WheelEvent.DOM_DELTA_PAGE
284
- ? container.clientHeight
285
- : 1
286
- const deltaX = event.deltaX * deltaModeFactor
287
- const deltaY = event.deltaY * deltaModeFactor
288
-
289
- // 가로 스크롤 및 세로 스크롤 모두 처리
290
- container.scrollLeft += deltaX
291
- container.scrollTop += deltaY
292
-
293
- const customEvent = new CustomEvent('scroll', {
294
- detail: {
295
- scrollLeft: container.scrollLeft,
296
- scrollTop: container.scrollTop
297
- },
298
- bubbles: true,
299
- composed: true
300
- })
301
-
302
- this.host.dispatchEvent(customEvent)
303
- }
304
-
305
- handleTouchStartForBody(event: TouchEvent, container: HTMLDivElement) {
306
- event.stopPropagation()
307
-
308
- this.touchStartX = event.touches[0].clientX
309
- this.touchStartY = event.touches[0].clientY
310
-
311
- this.scrollStartX = container.scrollLeft
312
- this.scrollStartY = container.scrollTop
313
- }
314
-
315
- handleTouchMoveForBody(event: TouchEvent, container: HTMLDivElement) {
316
- const touchCurrentX = event.touches[0].clientX
317
- const touchCurrentY = event.touches[0].clientY
318
-
319
- const deltaX = this.touchStartX! - touchCurrentX
320
- const deltaY = this.touchStartY! - touchCurrentY
321
-
322
- container.scrollLeft = this.scrollStartX! + deltaX
323
- container.scrollTop = this.scrollStartY! + deltaY
324
-
325
- const customEvent = new CustomEvent('scroll', {
326
- detail: {
327
- scrollLeft: container.scrollLeft,
328
- scrollTop: container.scrollTop
329
- },
330
- bubbles: true,
331
- composed: true
332
- })
333
-
334
- this.host.dispatchEvent(customEvent)
335
- }
336
- }
package/src/index.ts DELETED
File without changes
package/src/locales.ts DELETED
@@ -1,78 +0,0 @@
1
- export const locales = {
2
- 'en-US': {
3
- dateTime: '%x, %X',
4
- date: '%-m/%-d/%Y',
5
- time: '%-I:%M:%S %p',
6
- periods: ['AM', 'PM'],
7
- days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
8
- shortDays: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
9
- months: [
10
- 'January',
11
- 'February',
12
- 'March',
13
- 'April',
14
- 'May',
15
- 'June',
16
- 'July',
17
- 'August',
18
- 'September',
19
- 'October',
20
- 'November',
21
- 'December'
22
- ],
23
- shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
24
- },
25
- 'ko-KR': {
26
- dateTime: '%x, %X',
27
- date: '%Y-%m-%d',
28
- time: '%H:%M:%S',
29
- periods: ['오전', '오후'],
30
- days: ['일요일', '월요일', '화요일', '수요일', '목요일', '금요일', '토요일'],
31
- shortDays: ['일', '월', '화', '수', '목', '금', '토'],
32
- months: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'],
33
- shortMonths: ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월']
34
- },
35
- 'ja-JP': {
36
- dateTime: '%x, %X',
37
- date: '%Y/%m/%d',
38
- time: '%H:%M:%S',
39
- periods: ['午前', '午後'],
40
- days: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'],
41
- shortDays: ['日', '月', '火', '水', '木', '金', '土'],
42
- months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
43
- shortMonths: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
44
- },
45
- 'zh-CN': {
46
- dateTime: '%x, %X',
47
- date: '%Y-%m-%d',
48
- time: '%H:%M:%S',
49
- periods: ['上午', '下午'],
50
- days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
51
- shortDays: ['日', '一', '二', '三', '四', '五', '六'],
52
- months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
53
- shortMonths: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
54
- },
55
- 'ms-MY': {
56
- dateTime: '%x, %X',
57
- date: '%d/%m/%Y',
58
- time: '%H:%M:%S',
59
- periods: ['AM', 'PM'],
60
- days: ['Ahad', 'Isnin', 'Selasa', 'Rabu', 'Khamis', 'Jumaat', 'Sabtu'],
61
- shortDays: ['Ahd', 'Isn', 'Sel', 'Rab', 'Kha', 'Jum', 'Sab'],
62
- months: [
63
- 'Januari',
64
- 'Februari',
65
- 'Mac',
66
- 'April',
67
- 'Mei',
68
- 'Jun',
69
- 'Julai',
70
- 'Ogos',
71
- 'September',
72
- 'Oktober',
73
- 'November',
74
- 'Disember'
75
- ],
76
- shortMonths: ['Jan', 'Feb', 'Mac', 'Apr', 'Mei', 'Jun', 'Jul', 'Ogo', 'Sep', 'Okt', 'Nov', 'Dis']
77
- }
78
- }