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