@operato/gantt 2.0.0-beta.14 → 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 +19 -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 +134 -440
- 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 +13 -1
- package/dist/src/ox-gantt.js +144 -27
- 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.js +91 -85
- 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 +141 -487
- package/src/ox-gantt-resize-splitter.ts +18 -25
- package/src/ox-gantt.ts +151 -29
- package/src/ox-task-tooltip.ts +120 -0
- package/src/types.ts +11 -3
- package/stories/ox-gantt.stories.ts +93 -85
- 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
package/src/ox-gantt-chart.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import './ox-task-tooltip'
|
|
2
|
+
|
|
1
3
|
import { LitElement, html, css, PropertyValues, PropertyValueMap } from 'lit'
|
|
2
|
-
import { customElement, property, query
|
|
3
|
-
import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles'
|
|
4
|
+
import { customElement, property, query } from 'lit/decorators.js'
|
|
4
5
|
import * as d3 from 'd3'
|
|
6
|
+
import { locales } from './locales'
|
|
5
7
|
|
|
6
|
-
import {
|
|
7
|
-
import { levelDown, levelUp } from './utils'
|
|
8
|
-
|
|
8
|
+
import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles'
|
|
9
9
|
import { ScrollbarStyles } from '@operato/styles'
|
|
10
|
-
|
|
10
|
+
import { GanttChartController } from './gantt-chart-controller'
|
|
11
11
|
import { Task } from './types.js'
|
|
12
|
+
import { levelDown, levelUp } from './utils'
|
|
13
|
+
import { OxTaskTooltip } from './ox-task-tooltip.js'
|
|
12
14
|
|
|
13
15
|
type TimeScale = 'year-quarter' | 'year-month' | 'quarter-week' | 'week-day'
|
|
14
16
|
|
|
@@ -20,23 +22,17 @@ export class OxGanttChart extends LitElement {
|
|
|
20
22
|
css`
|
|
21
23
|
:host {
|
|
22
24
|
display: flex;
|
|
23
|
-
|
|
24
25
|
overflow: hidden;
|
|
26
|
+
position: relative;
|
|
25
27
|
}
|
|
26
|
-
|
|
27
28
|
div[chart-container] {
|
|
28
29
|
display: block;
|
|
29
|
-
|
|
30
30
|
overflow-x: auto;
|
|
31
|
-
overflow-y:
|
|
32
|
-
|
|
33
|
-
position: relative;
|
|
31
|
+
overflow-y: hidden;
|
|
34
32
|
}
|
|
35
|
-
|
|
36
33
|
svg {
|
|
37
34
|
min-width: 100%;
|
|
38
35
|
}
|
|
39
|
-
|
|
40
36
|
svg[header] {
|
|
41
37
|
display: block;
|
|
42
38
|
min-width: 100%;
|
|
@@ -44,475 +40,152 @@ export class OxGanttChart extends LitElement {
|
|
|
44
40
|
border-bottom: 1px solid var(--ox-gantt-border-color, var(--md-sys-color-on-surface));
|
|
45
41
|
height: 40px;
|
|
46
42
|
background-color: var(--ox-gantt-header-background-color, var(--md-sys-color-secondary-container));
|
|
47
|
-
|
|
48
43
|
position: sticky;
|
|
49
44
|
top: 0;
|
|
50
45
|
z-index: 1;
|
|
51
46
|
}
|
|
52
|
-
|
|
53
47
|
.top-axis text,
|
|
54
48
|
.bottom-axis text {
|
|
55
49
|
font-size: 12px;
|
|
56
50
|
}
|
|
57
|
-
|
|
58
51
|
.grid line {
|
|
59
52
|
stroke: #e0e0e0;
|
|
60
53
|
}
|
|
61
|
-
|
|
62
54
|
.grid path {
|
|
63
55
|
stroke-width: 0;
|
|
64
56
|
}
|
|
65
|
-
|
|
66
57
|
.no-select {
|
|
67
58
|
user-select: none;
|
|
68
59
|
}
|
|
60
|
+
.selected {
|
|
61
|
+
stroke-width: 2px;
|
|
62
|
+
stroke: var(--ox-gantt-selected-color, blue);
|
|
63
|
+
}
|
|
69
64
|
`
|
|
70
65
|
]
|
|
71
66
|
|
|
72
|
-
@property({ type: String, attribute: 'from-date' })
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@property({ type: String, attribute: 'to-date' })
|
|
76
|
-
|
|
77
|
-
|
|
67
|
+
@property({ type: String, attribute: 'from-date' }) fromDate: string = new Date(new Date().getFullYear(), 0, 1)
|
|
68
|
+
.toISOString()
|
|
69
|
+
.split('T')[0]
|
|
70
|
+
@property({ type: String, attribute: 'to-date' }) toDate: string = new Date(new Date().getFullYear(), 11, 31)
|
|
71
|
+
.toISOString()
|
|
72
|
+
.split('T')[0]
|
|
78
73
|
@property({ type: String, attribute: 'axis-scale' }) axisScale: TimeScale = 'week-day'
|
|
79
74
|
@property({ type: Boolean, attribute: 'extend-grid-lines' }) extendGridLines: boolean = false
|
|
80
75
|
@property({ type: Array }) tasks: Task[] = []
|
|
81
|
-
|
|
82
|
-
@
|
|
83
|
-
@state() level: LEVEL = this.axisScale
|
|
84
|
-
|
|
85
|
-
private fromTime: Date = new Date(this.fromDate!)
|
|
86
|
-
private toTime: Date = new Date(this.toDate)
|
|
87
|
-
private totalDays: number = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1))
|
|
88
|
-
private _timeScale?: d3.ScaleTime<number, number, never>
|
|
76
|
+
@property({ type: String, attribute: 'selected-task-id' }) selectedTaskId?: string
|
|
77
|
+
@property({ type: String }) locale: string = 'en-US'
|
|
89
78
|
|
|
90
79
|
@query('svg[header]') svgHeader!: SVGElement
|
|
91
80
|
@query('svg[body]') svgBody!: SVGElement
|
|
92
81
|
@query('[chart-container]') container!: HTMLDivElement
|
|
82
|
+
@query('ox-task-tooltip') tooltip!: OxTaskTooltip // 툴팁 요소 쿼리 추가
|
|
83
|
+
|
|
84
|
+
private ganttChartController: GanttChartController
|
|
85
|
+
|
|
86
|
+
constructor() {
|
|
87
|
+
super()
|
|
88
|
+
this.ganttChartController = new GanttChartController(this, this.fromDate, this.toDate, this.axisScale)
|
|
89
|
+
}
|
|
93
90
|
|
|
94
91
|
protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
|
|
95
92
|
this.initHeaderGestureHandlers()
|
|
96
93
|
}
|
|
97
94
|
|
|
98
95
|
updated(changedProperties: PropertyValues<this>) {
|
|
99
|
-
var redrawRequired = false
|
|
100
|
-
|
|
101
96
|
if (changedProperties.has('fromDate') || changedProperties.has('toDate')) {
|
|
102
|
-
this.
|
|
103
|
-
this.toTime = new Date(this.toDate)
|
|
104
|
-
this.totalDays = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1))
|
|
105
|
-
|
|
106
|
-
this._timeScale = undefined
|
|
107
|
-
redrawRequired = true
|
|
97
|
+
this.ganttChartController.updateRange(this.fromDate, this.toDate)
|
|
108
98
|
}
|
|
109
99
|
|
|
110
|
-
if (changedProperties.has('
|
|
111
|
-
|
|
100
|
+
if (changedProperties.has('tasks')) {
|
|
101
|
+
this.ganttChartController.setTasks(this.tasks)
|
|
102
|
+
this.svgBody.style.height =
|
|
103
|
+
Math.max(this.tasks.length * 30, 2000 /* 태스크가 많이 없어도, 빈 공간이 없도록, 충분히 높게 */) + 'px'
|
|
112
104
|
}
|
|
113
105
|
|
|
114
|
-
|
|
115
|
-
|
|
106
|
+
// Update locale
|
|
107
|
+
if (changedProperties.has('locale')) {
|
|
108
|
+
this.updateLocale(this.locale)
|
|
116
109
|
}
|
|
117
110
|
|
|
118
|
-
if (changedProperties.has('
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
if (changedProperties.has('selectedTaskId')) {
|
|
112
|
+
this.ganttChartController.selectedTaskId = this.selectedTaskId
|
|
113
|
+
}
|
|
121
114
|
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
if (changedProperties.has('extendGridLines')) {
|
|
116
|
+
this.ganttChartController.extendGridLines = this.extendGridLines
|
|
124
117
|
}
|
|
125
118
|
|
|
126
|
-
|
|
119
|
+
this.ganttChartController.drawAxes(this.svgHeader)
|
|
120
|
+
this.ganttChartController.drawBody(this.svgBody, this.tooltip)
|
|
127
121
|
}
|
|
128
122
|
|
|
129
123
|
render() {
|
|
130
124
|
return html`
|
|
131
|
-
<div
|
|
125
|
+
<div
|
|
126
|
+
chart-container
|
|
127
|
+
@wheel=${(e: WheelEvent) => this.ganttChartController.handleWheelForBody(e, this.container)}
|
|
128
|
+
@touchstart=${(e: TouchEvent) => this.ganttChartController.handleTouchStartForBody(e, this.container)}
|
|
129
|
+
@touchmove=${(e: TouchEvent) => this.ganttChartController.handleTouchMoveForBody(e, this.container)}
|
|
130
|
+
>
|
|
132
131
|
<svg
|
|
133
132
|
header
|
|
134
|
-
@date-range-selected=${(e: CustomEvent) =>
|
|
135
|
-
console.log(e.detail)
|
|
136
|
-
}}
|
|
133
|
+
@date-range-selected=${(e: CustomEvent) => console.log(e.detail)}
|
|
137
134
|
@wheel=${this.handleWheelForHeader.bind(this)}
|
|
138
135
|
></svg>
|
|
139
136
|
<svg body></svg>
|
|
137
|
+
<ox-task-tooltip></ox-task-tooltip> // 툴팁 요소 추가
|
|
140
138
|
</div>
|
|
141
139
|
`
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
setVerticalOffset(offset: number) {
|
|
145
|
-
console.log(offset)
|
|
146
143
|
this.container.scrollTop = offset
|
|
147
144
|
}
|
|
148
145
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return 7 / 2
|
|
152
|
-
} else if (this.level == 'year-month') {
|
|
153
|
-
return 30 / 2
|
|
154
|
-
} else if (this.level == 'year-quarter') {
|
|
155
|
-
return 90 / 2
|
|
156
|
-
}
|
|
157
|
-
return 1
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
getChartWidth() {
|
|
161
|
-
return (this.totalDays * this.baseWidth) / this.getScale() // 총 차트 폭 계산
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
get timeScale() {
|
|
165
|
-
if (!this._timeScale) {
|
|
166
|
-
// 시간축 스케일 생성
|
|
167
|
-
this._timeScale = d3
|
|
168
|
-
.scaleTime()
|
|
169
|
-
.domain([d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1)])
|
|
170
|
-
.range([0, this.getChartWidth()])
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return this._timeScale
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
updateTimeScale() {
|
|
177
|
-
this.timeScale.range([0, this.getChartWidth()])
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
redraw() {
|
|
181
|
-
this.drawAxes()
|
|
182
|
-
this.drawBody()
|
|
146
|
+
getVerticalOffset(): number {
|
|
147
|
+
return this.container.scrollTop
|
|
183
148
|
}
|
|
184
149
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
body.selectAll('*').remove()
|
|
189
|
-
body.attr('width', this.getChartWidth())
|
|
190
|
-
|
|
191
|
-
if (this.extendGridLines) {
|
|
192
|
-
const addGridLines = (ticks: Date[]) => {
|
|
193
|
-
ticks.forEach(tick => {
|
|
194
|
-
const startX = this.timeScale(tick)
|
|
195
|
-
body
|
|
196
|
-
.append('line')
|
|
197
|
-
.attr('x1', startX)
|
|
198
|
-
.attr('x2', startX)
|
|
199
|
-
.attr('y1', 0)
|
|
200
|
-
.attr('y2', Number(this.svgBody.getBoundingClientRect().height) || 0)
|
|
201
|
-
.attr('stroke', 'var(--ox-gantt-color-gridlines, var(--md-sys-color-outline))')
|
|
202
|
-
.attr('stroke-width', 1)
|
|
203
|
-
})
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (this.level === 'week-day') {
|
|
207
|
-
const dayTicks = d3.timeDay.every(1)!.range(d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1))
|
|
208
|
-
addGridLines(dayTicks)
|
|
209
|
-
} else if (this.level === 'quarter-week') {
|
|
210
|
-
const weekTicks = d3.timeWeek
|
|
211
|
-
.every(1)!
|
|
212
|
-
.range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1))
|
|
213
|
-
addGridLines(weekTicks)
|
|
214
|
-
} else if (this.level === 'year-month') {
|
|
215
|
-
const monthTicks = d3.timeMonth
|
|
216
|
-
.every(1)!
|
|
217
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 1))
|
|
218
|
-
addGridLines(monthTicks)
|
|
219
|
-
} else if (this.level === 'year-quarter') {
|
|
220
|
-
const quarterTicks = d3.timeMonth
|
|
221
|
-
.every(3)!
|
|
222
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3))
|
|
223
|
-
addGridLines(quarterTicks)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
150
|
+
handleWheelForHeader(event: WheelEvent) {
|
|
151
|
+
event.preventDefault()
|
|
152
|
+
event.stopPropagation()
|
|
226
153
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.tasks.forEach((task, index) => {
|
|
231
|
-
const startX = this.timeScale(d3.timeDay.floor(d3.min([task.startTime, task.endTime])!))
|
|
232
|
-
const endX = this.timeScale(d3.timeDay.offset(d3.timeDay.ceil(d3.max([task.startTime, task.endTime])!), -1))
|
|
233
|
-
|
|
234
|
-
const taskWidth = endX - startX + this.baseWidth / this.getScale()
|
|
235
|
-
const taskY = index * 30 + 7.5
|
|
236
|
-
|
|
237
|
-
// Main task bar
|
|
238
|
-
const taskBar = body
|
|
239
|
-
.append('rect')
|
|
240
|
-
.attr('x', startX)
|
|
241
|
-
.attr('y', taskY)
|
|
242
|
-
.attr('width', taskWidth)
|
|
243
|
-
.attr('height', 15)
|
|
244
|
-
.attr('fill', this.getTaskColor(task.tags || []))
|
|
245
|
-
.attr('stroke', 'black')
|
|
246
|
-
.attr('stroke-width', 1)
|
|
247
|
-
|
|
248
|
-
// Add click event listener to task bar
|
|
249
|
-
taskBar.on('click', () => {
|
|
250
|
-
const customEvent = new CustomEvent('task-clicked', {
|
|
251
|
-
detail: { task },
|
|
252
|
-
bubbles: true,
|
|
253
|
-
composed: true
|
|
254
|
-
})
|
|
255
|
-
this.dispatchEvent(customEvent)
|
|
154
|
+
if (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey) {
|
|
155
|
+
this.container.scrollBy({
|
|
156
|
+
left: event.shiftKey ? event.deltaX : event.deltaY
|
|
256
157
|
})
|
|
158
|
+
} else {
|
|
159
|
+
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9
|
|
160
|
+
this.ganttChartController.baseWidth *= zoomFactor
|
|
257
161
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
.
|
|
263
|
-
.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
.
|
|
162
|
+
if (this.ganttChartController.baseWidth > 40) {
|
|
163
|
+
if (this.ganttChartController.level == 'week-day') {
|
|
164
|
+
this.ganttChartController.baseWidth = 40
|
|
165
|
+
} else {
|
|
166
|
+
this.ganttChartController.level = levelDown(this.ganttChartController.level)
|
|
167
|
+
this.ganttChartController.baseWidth = 30
|
|
168
|
+
}
|
|
169
|
+
} else if (this.ganttChartController.baseWidth < 30) {
|
|
170
|
+
if (this.ganttChartController.level == 'year-quarter') {
|
|
171
|
+
this.ganttChartController.baseWidth = 30
|
|
172
|
+
}
|
|
268
173
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
// Add linking lines
|
|
275
|
-
this.tasks.forEach(task => {
|
|
276
|
-
if (task.after && taskPositions[task.after]) {
|
|
277
|
-
const prevTask = taskPositions[task.after]
|
|
278
|
-
const currTask = taskPositions[task.id]
|
|
279
|
-
|
|
280
|
-
// Draw the linking line
|
|
281
|
-
body
|
|
282
|
-
.append('path')
|
|
283
|
-
.attr(
|
|
284
|
-
'd',
|
|
285
|
-
`
|
|
286
|
-
M${prevTask.x},${prevTask.y}
|
|
287
|
-
L${prevTask.x + 10},${prevTask.y}
|
|
288
|
-
${
|
|
289
|
-
prevTask.x + 10 < currTask.x - 10 - currTask.width
|
|
290
|
-
? `
|
|
291
|
-
L${(prevTask.x + currTask.x - currTask.width) / 2},${prevTask.y}
|
|
292
|
-
L${(prevTask.x + currTask.x - currTask.width) / 2},${currTask.y}
|
|
293
|
-
`
|
|
294
|
-
: `
|
|
295
|
-
L${prevTask.x + 10},${(prevTask.y + currTask.y) / 2}
|
|
296
|
-
L${currTask.x - 10 - currTask.width},${(prevTask.y + currTask.y) / 2}
|
|
297
|
-
`
|
|
298
|
-
}
|
|
299
|
-
L${currTask.x - 10 - currTask.width},${currTask.y}
|
|
300
|
-
L${currTask.x - currTask.width},${currTask.y}
|
|
301
|
-
`
|
|
302
|
-
)
|
|
303
|
-
.attr('stroke', 'black')
|
|
304
|
-
.attr('fill', 'none')
|
|
305
|
-
.attr('stroke-width', 1)
|
|
174
|
+
if (this.ganttChartController.baseWidth < 20) {
|
|
175
|
+
this.ganttChartController.level = levelUp(this.ganttChartController.level)
|
|
176
|
+
this.ganttChartController.baseWidth = 30
|
|
306
177
|
}
|
|
307
|
-
})
|
|
308
|
-
}
|
|
309
178
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if (tags.includes('critical')) {
|
|
313
|
-
return 'red'
|
|
314
|
-
} else if (tags.includes('milestone')) {
|
|
315
|
-
return 'blue'
|
|
316
|
-
} else if (tags.includes('done')) {
|
|
317
|
-
return 'green'
|
|
318
|
-
} else {
|
|
319
|
-
return 'grey'
|
|
179
|
+
this.ganttChartController.updateTimeScale()
|
|
180
|
+
this.requestUpdate()
|
|
320
181
|
}
|
|
321
182
|
}
|
|
322
183
|
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
// 차트 컨테이너의 폭 설정 및 스크롤 가능하도록 설정
|
|
330
|
-
header.attr('width', this.getChartWidth())
|
|
331
|
-
|
|
332
|
-
const addGridLines = (ticks: Date[], yStart: number, yEnd: number, extendable?: boolean) => {
|
|
333
|
-
ticks.forEach(tick => {
|
|
334
|
-
const startX = this.timeScale(tick)
|
|
335
|
-
header
|
|
336
|
-
.append('line')
|
|
337
|
-
.attr('x1', startX)
|
|
338
|
-
.attr('x2', startX)
|
|
339
|
-
.attr('y1', yStart)
|
|
340
|
-
.attr('y2', yEnd)
|
|
341
|
-
.attr('stroke', 'var(--ox-gantt-color-gridlines, var(--md-sys-color-outline))')
|
|
342
|
-
.attr('stroke-width', 1)
|
|
343
|
-
})
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const centerLabel = (
|
|
347
|
-
selection: d3.Selection<SVGGElement, unknown, null, any>,
|
|
348
|
-
tickInterval: d3.CountableTimeInterval
|
|
349
|
-
) => {
|
|
350
|
-
selection.selectAll('text').attr('transform', (d: any) => {
|
|
351
|
-
const tickIntervalWidth = this.timeScale(tickInterval.offset(d, 1)) - this.timeScale(d)
|
|
352
|
-
return `translate(${tickIntervalWidth / 2}, -2)`
|
|
353
|
-
})
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (this.level === 'week-day') {
|
|
357
|
-
// Level 1: week-day
|
|
358
|
-
// Weekly axis
|
|
359
|
-
const weekAxis = d3
|
|
360
|
-
.axisTop(this.timeScale)
|
|
361
|
-
.ticks(d3.timeWeek.every(1))
|
|
362
|
-
.tickFormat(d3.timeFormat('%B %d') as any)
|
|
363
|
-
.tickSize(0)
|
|
364
|
-
const weekTicks = d3.timeWeek
|
|
365
|
-
.every(1)!
|
|
366
|
-
.range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1))
|
|
367
|
-
addGridLines(weekTicks, 0, 20)
|
|
368
|
-
|
|
369
|
-
const weekAxisG = header
|
|
370
|
-
.append('g')
|
|
371
|
-
.classed('week-axis no-select md-typescale-label-small', true)
|
|
372
|
-
.attr('transform', `translate(0, 20)`)
|
|
373
|
-
.call(weekAxis)
|
|
374
|
-
|
|
375
|
-
centerLabel(weekAxisG, d3.timeWeek)
|
|
376
|
-
|
|
377
|
-
// Daily axis
|
|
378
|
-
const dayAxis = d3
|
|
379
|
-
.axisTop(this.timeScale)
|
|
380
|
-
.ticks(d3.timeDay.every(1))
|
|
381
|
-
.tickFormat(d3.timeFormat('%d') as any)
|
|
382
|
-
.tickSize(0)
|
|
383
|
-
|
|
384
|
-
const dayTicks = d3.timeDay.every(1)!.range(d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1))
|
|
385
|
-
addGridLines(dayTicks, 20, 40)
|
|
386
|
-
|
|
387
|
-
const dayAxisG = header
|
|
388
|
-
.append('g')
|
|
389
|
-
.classed('day-axis no-select md-typescale-label-small', true)
|
|
390
|
-
.attr('transform', `translate(0, 40)`)
|
|
391
|
-
.call(dayAxis)
|
|
392
|
-
|
|
393
|
-
centerLabel(dayAxisG, d3.timeDay)
|
|
394
|
-
} else if (this.level === 'quarter-week') {
|
|
395
|
-
// Level 2: quarter-week
|
|
396
|
-
// Quarterly axis
|
|
397
|
-
const quarterAxis = d3
|
|
398
|
-
.axisTop(this.timeScale)
|
|
399
|
-
.ticks(d3.timeMonth.every(3))
|
|
400
|
-
.tickFormat(d3.timeFormat('%Y %b') as any)
|
|
401
|
-
.tickSize(0)
|
|
402
|
-
|
|
403
|
-
const quarterTicks = d3.timeMonth
|
|
404
|
-
.every(3)!
|
|
405
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3))
|
|
406
|
-
addGridLines(quarterTicks, 0, 20)
|
|
407
|
-
|
|
408
|
-
const quarterAxisG = header
|
|
409
|
-
.append('g')
|
|
410
|
-
.classed('quarter-axis no-select md-typescale-label-small', true)
|
|
411
|
-
.attr('transform', `translate(0, 20)`)
|
|
412
|
-
.call(quarterAxis)
|
|
413
|
-
|
|
414
|
-
centerLabel(quarterAxisG, d3.timeMonth.every(3) as any)
|
|
415
|
-
|
|
416
|
-
// Weekly axis
|
|
417
|
-
const weekAxis = d3
|
|
418
|
-
.axisTop(this.timeScale)
|
|
419
|
-
.ticks(d3.timeWeek.every(1))
|
|
420
|
-
.tickFormat(d3.timeFormat('%b %d') as any)
|
|
421
|
-
.tickSize(0)
|
|
422
|
-
const weekTicks = d3.timeWeek
|
|
423
|
-
.every(1)!
|
|
424
|
-
.range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1))
|
|
425
|
-
addGridLines(weekTicks, 20, 40)
|
|
426
|
-
|
|
427
|
-
const weekAxisG = header
|
|
428
|
-
.append('g')
|
|
429
|
-
.classed('week-axis no-select md-typescale-label-small', true)
|
|
430
|
-
.attr('transform', `translate(0, 40)`)
|
|
431
|
-
.call(weekAxis)
|
|
432
|
-
|
|
433
|
-
centerLabel(weekAxisG, d3.timeWeek)
|
|
434
|
-
} else if (this.level === 'year-month') {
|
|
435
|
-
// Level 3: year-month
|
|
436
|
-
// Yearly axis
|
|
437
|
-
const yearAxis = d3
|
|
438
|
-
.axisTop(this.timeScale)
|
|
439
|
-
.ticks(d3.timeYear.every(1))
|
|
440
|
-
.tickFormat(d3.timeFormat('%Y') as any)
|
|
441
|
-
.tickSize(0)
|
|
442
|
-
|
|
443
|
-
const yearTicks = d3.timeYear
|
|
444
|
-
.every(1)!
|
|
445
|
-
.range(d3.timeYear.floor(this.fromTime), d3.timeYear.offset(this.toTime, 1))
|
|
446
|
-
addGridLines(yearTicks, 0, 20)
|
|
447
|
-
|
|
448
|
-
const yearAxisG = header
|
|
449
|
-
.append('g')
|
|
450
|
-
.classed('year-axis no-select md-typescale-label-small', true)
|
|
451
|
-
.attr('transform', `translate(0, 20)`)
|
|
452
|
-
.call(yearAxis)
|
|
453
|
-
|
|
454
|
-
centerLabel(yearAxisG, d3.timeYear.every(1)! as any)
|
|
455
|
-
|
|
456
|
-
// Monthly axis
|
|
457
|
-
const monthAxis = d3
|
|
458
|
-
.axisTop(this.timeScale)
|
|
459
|
-
.ticks(d3.timeMonth.every(1))
|
|
460
|
-
.tickFormat(d3.timeFormat('%b') as any)
|
|
461
|
-
.tickSize(0)
|
|
462
|
-
|
|
463
|
-
const monthTicks = d3.timeMonth
|
|
464
|
-
.every(1)!
|
|
465
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 1))
|
|
466
|
-
addGridLines(monthTicks, 20, 40)
|
|
467
|
-
|
|
468
|
-
const monthAxisG = header
|
|
469
|
-
.append('g')
|
|
470
|
-
.classed('month-axis no-select md-typescale-label-small', true)
|
|
471
|
-
.attr('transform', `translate(0, 40)`)
|
|
472
|
-
.call(monthAxis)
|
|
473
|
-
|
|
474
|
-
centerLabel(monthAxisG, d3.timeMonth.every(1) as any)
|
|
475
|
-
} else if (this.level === 'year-quarter') {
|
|
476
|
-
// Level 4: year-quarter
|
|
477
|
-
// Yearly axis
|
|
478
|
-
const yearAxis = d3
|
|
479
|
-
.axisTop(this.timeScale)
|
|
480
|
-
.ticks(d3.timeYear.every(1))
|
|
481
|
-
.tickFormat(d3.timeFormat('%Y') as any)
|
|
482
|
-
.tickSize(0)
|
|
483
|
-
|
|
484
|
-
const yearTicks = d3.timeYear
|
|
485
|
-
.every(1)!
|
|
486
|
-
.range(d3.timeYear.floor(this.fromTime), d3.timeYear.offset(this.toTime, 1))
|
|
487
|
-
addGridLines(yearTicks, 0, 20)
|
|
488
|
-
|
|
489
|
-
const yearAxisG = header
|
|
490
|
-
.append('g')
|
|
491
|
-
.classed('year-axis no-select md-typescale-label-small', true)
|
|
492
|
-
.attr('transform', `translate(0, 20)`)
|
|
493
|
-
.call(yearAxis)
|
|
494
|
-
|
|
495
|
-
centerLabel(yearAxisG, d3.timeYear.every(1)! as any)
|
|
496
|
-
|
|
497
|
-
// Quarterly axis
|
|
498
|
-
const quarterAxis = d3
|
|
499
|
-
.axisTop(this.timeScale)
|
|
500
|
-
.ticks(d3.timeMonth.every(3))
|
|
501
|
-
.tickFormat(d3.timeFormat('%Y %b') as any)
|
|
502
|
-
.tickSize(0)
|
|
503
|
-
|
|
504
|
-
const quarterTicks = d3.timeMonth
|
|
505
|
-
.every(3)!
|
|
506
|
-
.range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3))
|
|
507
|
-
addGridLines(quarterTicks, 20, 40)
|
|
508
|
-
|
|
509
|
-
const quarterAxisG = header
|
|
510
|
-
.append('g')
|
|
511
|
-
.classed('quarter-axis no-select md-typescale-label-small', true)
|
|
512
|
-
.attr('transform', `translate(0, 40)`)
|
|
513
|
-
.call(quarterAxis)
|
|
514
|
-
|
|
515
|
-
centerLabel(quarterAxisG, d3.timeMonth.every(3) as any)
|
|
184
|
+
updateLocale(locale: string) {
|
|
185
|
+
const localeDefinition = (locales as any)[locale || 'en-US']
|
|
186
|
+
if (localeDefinition) {
|
|
187
|
+
d3.timeFormatDefaultLocale(localeDefinition)
|
|
188
|
+
this.ganttChartController.updateTimeScale()
|
|
516
189
|
}
|
|
517
190
|
}
|
|
518
191
|
|
|
@@ -524,7 +197,7 @@ export class OxGanttChart extends LitElement {
|
|
|
524
197
|
let lastTouchDistance: number | null = null
|
|
525
198
|
|
|
526
199
|
const handleTouchStart = (event: TouchEvent) => {
|
|
527
|
-
event.preventDefault()
|
|
200
|
+
event.preventDefault()
|
|
528
201
|
|
|
529
202
|
if (event.touches.length === 2) {
|
|
530
203
|
lastTouchDistance = getTouchDistance(event.touches)
|
|
@@ -537,32 +210,32 @@ export class OxGanttChart extends LitElement {
|
|
|
537
210
|
}
|
|
538
211
|
|
|
539
212
|
const handleTouchMove = (event: TouchEvent) => {
|
|
540
|
-
event.preventDefault()
|
|
213
|
+
event.preventDefault()
|
|
541
214
|
|
|
542
215
|
if (event.touches.length === 2 && lastTouchDistance !== null) {
|
|
543
216
|
const currentDistance = getTouchDistance(event.touches)
|
|
544
217
|
const zoomFactor = currentDistance / lastTouchDistance
|
|
545
|
-
this.baseWidth *= zoomFactor
|
|
218
|
+
this.ganttChartController.baseWidth *= zoomFactor
|
|
546
219
|
|
|
547
|
-
if (this.baseWidth > 60) {
|
|
548
|
-
if (this.level == 'year-quarter') {
|
|
549
|
-
this.baseWidth = 60
|
|
220
|
+
if (this.ganttChartController.baseWidth > 60) {
|
|
221
|
+
if (this.ganttChartController.level == 'year-quarter') {
|
|
222
|
+
this.ganttChartController.baseWidth = 60
|
|
550
223
|
} else {
|
|
551
|
-
this.level = levelUp(this.level)
|
|
552
|
-
this.baseWidth = 30
|
|
224
|
+
this.ganttChartController.level = levelUp(this.ganttChartController.level)
|
|
225
|
+
this.ganttChartController.baseWidth = 30
|
|
553
226
|
}
|
|
554
|
-
} else if (this.baseWidth < 30) {
|
|
555
|
-
if (this.level == 'week-day') {
|
|
556
|
-
this.baseWidth = 30
|
|
227
|
+
} else if (this.ganttChartController.baseWidth < 30) {
|
|
228
|
+
if (this.ganttChartController.level == 'week-day') {
|
|
229
|
+
this.ganttChartController.baseWidth = 30
|
|
557
230
|
} else {
|
|
558
|
-
this.level = levelDown(this.level)
|
|
559
|
-
this.baseWidth = 60
|
|
231
|
+
this.ganttChartController.level = levelDown(this.ganttChartController.level)
|
|
232
|
+
this.ganttChartController.baseWidth = 60
|
|
560
233
|
}
|
|
561
234
|
}
|
|
562
235
|
|
|
563
236
|
lastTouchDistance = currentDistance
|
|
564
|
-
this.updateTimeScale()
|
|
565
|
-
this.
|
|
237
|
+
this.ganttChartController.updateTimeScale()
|
|
238
|
+
this.requestUpdate()
|
|
566
239
|
} else if (isDragging && startX !== null) {
|
|
567
240
|
const touch = event.touches[0]
|
|
568
241
|
const containerOffsetX = header.node()!.getBoundingClientRect().left
|
|
@@ -572,14 +245,13 @@ export class OxGanttChart extends LitElement {
|
|
|
572
245
|
}
|
|
573
246
|
|
|
574
247
|
const handleTouchEnd = (event: TouchEvent) => {
|
|
575
|
-
event.preventDefault()
|
|
248
|
+
event.preventDefault()
|
|
576
249
|
|
|
577
250
|
if (event.touches.length < 2) {
|
|
578
251
|
lastTouchDistance = null
|
|
579
252
|
}
|
|
580
253
|
|
|
581
254
|
if (!isDragging || startX === null) {
|
|
582
|
-
// 클릭 이벤트 처리
|
|
583
255
|
const touch = event.changedTouches[0]
|
|
584
256
|
const containerOffsetX = header.node()!.getBoundingClientRect().left
|
|
585
257
|
const clickX = touch.clientX - containerOffsetX
|
|
@@ -618,7 +290,6 @@ export class OxGanttChart extends LitElement {
|
|
|
618
290
|
|
|
619
291
|
const handleMouseUp = (event: MouseEvent) => {
|
|
620
292
|
if (!isDragging || startX === null) {
|
|
621
|
-
// 클릭 이벤트 처리
|
|
622
293
|
const containerOffsetX = header.node()!.getBoundingClientRect().left
|
|
623
294
|
const clickX = event.clientX - containerOffsetX
|
|
624
295
|
this.finalizeSelection(clickX, clickX)
|
|
@@ -645,21 +316,21 @@ export class OxGanttChart extends LitElement {
|
|
|
645
316
|
const header = d3.select(this.svgHeader)
|
|
646
317
|
const body = d3.select(this.svgBody)
|
|
647
318
|
|
|
648
|
-
let startDate = this.timeScale.invert(startX)
|
|
649
|
-
let endDate = this.timeScale.invert(endX)
|
|
319
|
+
let startDate = this.ganttChartController.timeScale.invert(startX)
|
|
320
|
+
let endDate = this.ganttChartController.timeScale.invert(endX)
|
|
650
321
|
|
|
651
322
|
let start, end
|
|
652
323
|
|
|
653
|
-
if (this.level === 'week-day') {
|
|
324
|
+
if (this.ganttChartController.level === 'week-day') {
|
|
654
325
|
start = d3.timeDay.floor(d3.min([startDate, endDate])!)
|
|
655
326
|
end = d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])!), -1)
|
|
656
|
-
} else if (this.level === 'quarter-week') {
|
|
327
|
+
} else if (this.ganttChartController.level === 'quarter-week') {
|
|
657
328
|
start = d3.timeWeek.floor(d3.min([startDate, endDate])!)
|
|
658
329
|
end = d3.timeDay.offset(d3.timeWeek.ceil(d3.max([startDate, endDate])!), -1)
|
|
659
|
-
} else if (this.level === 'year-month') {
|
|
330
|
+
} else if (this.ganttChartController.level === 'year-month') {
|
|
660
331
|
start = d3.timeMonth.floor(d3.min([startDate, endDate])!)
|
|
661
332
|
end = d3.timeDay.offset(d3.timeMonth.ceil(d3.max([startDate, endDate])!), -1)
|
|
662
|
-
} else if (this.level === 'year-quarter') {
|
|
333
|
+
} else if (this.ganttChartController.level === 'year-quarter') {
|
|
663
334
|
start = d3.timeMonth.every(3)!.floor(d3.min([startDate, endDate])!)
|
|
664
335
|
end = d3.timeDay.offset(d3.timeMonth.every(3)!.ceil(d3.max([startDate, endDate])!), -1)
|
|
665
336
|
}
|
|
@@ -670,9 +341,14 @@ export class OxGanttChart extends LitElement {
|
|
|
670
341
|
header
|
|
671
342
|
.append('rect')
|
|
672
343
|
.attr('class', 'selection-rect')
|
|
673
|
-
.attr('x', this.timeScale(start!))
|
|
344
|
+
.attr('x', this.ganttChartController.timeScale(start!))
|
|
674
345
|
.attr('y', 20)
|
|
675
|
-
.attr(
|
|
346
|
+
.attr(
|
|
347
|
+
'width',
|
|
348
|
+
this.ganttChartController.timeScale(end!) -
|
|
349
|
+
this.ganttChartController.timeScale(start!) +
|
|
350
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale()
|
|
351
|
+
)
|
|
676
352
|
.attr('height', 20)
|
|
677
353
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
678
354
|
.attr('opacity', '.2')
|
|
@@ -680,9 +356,14 @@ export class OxGanttChart extends LitElement {
|
|
|
680
356
|
body
|
|
681
357
|
.append('rect')
|
|
682
358
|
.attr('class', 'selection-rect')
|
|
683
|
-
.attr('x', this.timeScale(start!))
|
|
359
|
+
.attr('x', this.ganttChartController.timeScale(start!))
|
|
684
360
|
.attr('y', 0)
|
|
685
|
-
.attr(
|
|
361
|
+
.attr(
|
|
362
|
+
'width',
|
|
363
|
+
this.ganttChartController.timeScale(end!) -
|
|
364
|
+
this.ganttChartController.timeScale(start!) +
|
|
365
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale()
|
|
366
|
+
)
|
|
686
367
|
.attr('height', '100%')
|
|
687
368
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
688
369
|
.attr('opacity', '.2')
|
|
@@ -692,21 +373,21 @@ export class OxGanttChart extends LitElement {
|
|
|
692
373
|
const header = d3.select(this.svgHeader)
|
|
693
374
|
const body = d3.select(this.svgBody)
|
|
694
375
|
|
|
695
|
-
let startDate = this.timeScale.invert(startX)
|
|
696
|
-
let endDate = this.timeScale.invert(endX)
|
|
376
|
+
let startDate = this.ganttChartController.timeScale.invert(startX)
|
|
377
|
+
let endDate = this.ganttChartController.timeScale.invert(endX)
|
|
697
378
|
|
|
698
379
|
let start, end
|
|
699
380
|
|
|
700
|
-
if (this.level === 'week-day') {
|
|
381
|
+
if (this.ganttChartController.level === 'week-day') {
|
|
701
382
|
start = d3.timeDay.floor(d3.min([startDate, endDate])!)
|
|
702
383
|
end = d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])!), -1)
|
|
703
|
-
} else if (this.level === 'quarter-week') {
|
|
384
|
+
} else if (this.ganttChartController.level === 'quarter-week') {
|
|
704
385
|
start = d3.timeWeek.floor(d3.min([startDate, endDate])!)
|
|
705
386
|
end = d3.timeDay.offset(d3.timeWeek.ceil(d3.max([startDate, endDate])!), -1)
|
|
706
|
-
} else if (this.level === 'year-month') {
|
|
387
|
+
} else if (this.ganttChartController.level === 'year-month') {
|
|
707
388
|
start = d3.timeMonth.floor(d3.min([startDate, endDate])!)
|
|
708
389
|
end = d3.timeDay.offset(d3.timeMonth.ceil(d3.max([startDate, endDate])!), -1)
|
|
709
|
-
} else if (this.level === 'year-quarter') {
|
|
390
|
+
} else if (this.ganttChartController.level === 'year-quarter') {
|
|
710
391
|
start = d3.timeMonth.every(3)!.floor(d3.min([startDate, endDate])!)
|
|
711
392
|
end = d3.timeDay.offset(d3.timeMonth.every(3)!.ceil(d3.max([startDate, endDate])!), -1)
|
|
712
393
|
}
|
|
@@ -717,9 +398,14 @@ export class OxGanttChart extends LitElement {
|
|
|
717
398
|
header
|
|
718
399
|
.append('rect')
|
|
719
400
|
.attr('class', 'selection-rect')
|
|
720
|
-
.attr('x', this.timeScale(start!))
|
|
401
|
+
.attr('x', this.ganttChartController.timeScale(start!))
|
|
721
402
|
.attr('y', 20)
|
|
722
|
-
.attr(
|
|
403
|
+
.attr(
|
|
404
|
+
'width',
|
|
405
|
+
this.ganttChartController.timeScale(end!) -
|
|
406
|
+
this.ganttChartController.timeScale(start!) +
|
|
407
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale()
|
|
408
|
+
)
|
|
723
409
|
.attr('height', 20)
|
|
724
410
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
725
411
|
.attr('opacity', '.2')
|
|
@@ -727,9 +413,14 @@ export class OxGanttChart extends LitElement {
|
|
|
727
413
|
body
|
|
728
414
|
.append('rect')
|
|
729
415
|
.attr('class', 'selection-rect')
|
|
730
|
-
.attr('x', this.timeScale(start!))
|
|
416
|
+
.attr('x', this.ganttChartController.timeScale(start!))
|
|
731
417
|
.attr('y', 0)
|
|
732
|
-
.attr(
|
|
418
|
+
.attr(
|
|
419
|
+
'width',
|
|
420
|
+
this.ganttChartController.timeScale(end!) -
|
|
421
|
+
this.ganttChartController.timeScale(start!) +
|
|
422
|
+
this.ganttChartController.baseWidth / this.ganttChartController.getScale()
|
|
423
|
+
)
|
|
733
424
|
.attr('height', '100%')
|
|
734
425
|
.attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-secondary))')
|
|
735
426
|
.attr('opacity', '.2')
|
|
@@ -741,41 +432,4 @@ export class OxGanttChart extends LitElement {
|
|
|
741
432
|
|
|
742
433
|
header.node()?.dispatchEvent(customEvent)
|
|
743
434
|
}
|
|
744
|
-
|
|
745
|
-
handleWheelForHeader(event: WheelEvent) {
|
|
746
|
-
event.preventDefault()
|
|
747
|
-
event.stopPropagation()
|
|
748
|
-
|
|
749
|
-
if (event.altKey || event.ctrlKey || event.shiftKey || event.metaKey) {
|
|
750
|
-
// 좌우 스크롤
|
|
751
|
-
this.container.scrollBy({
|
|
752
|
-
left: event.shiftKey ? event.deltaX : event.deltaY,
|
|
753
|
-
behavior: 'smooth'
|
|
754
|
-
})
|
|
755
|
-
} else {
|
|
756
|
-
// Zoom in/out
|
|
757
|
-
const zoomFactor = event.deltaY < 0 ? 1.1 : 0.9
|
|
758
|
-
this.baseWidth *= zoomFactor
|
|
759
|
-
|
|
760
|
-
if (this.baseWidth > 40) {
|
|
761
|
-
if (this.level == 'week-day') {
|
|
762
|
-
this.baseWidth = 40
|
|
763
|
-
} else {
|
|
764
|
-
this.level = levelDown(this.level)
|
|
765
|
-
this.baseWidth = 30
|
|
766
|
-
}
|
|
767
|
-
} else if (this.baseWidth < 30) {
|
|
768
|
-
if (this.level == 'year-quarter') {
|
|
769
|
-
this.baseWidth = 30
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
if (this.baseWidth < 20) {
|
|
773
|
-
this.level = levelUp(this.level)
|
|
774
|
-
this.baseWidth = 30
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
this.updateTimeScale()
|
|
778
|
-
this.redraw()
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
435
|
}
|