@operato/gantt 8.0.0-beta.0 → 8.0.0-beta.1

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/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
- }
@@ -1,418 +0,0 @@
1
- import './ox-task-tooltip.js'
2
- import './ox-resource-chart.js'
3
-
4
- import { LitElement, html, css, PropertyValues, PropertyValueMap } from 'lit'
5
- import { customElement, property, query, state } from 'lit/decorators.js'
6
- import * as d3 from 'd3'
7
- import { locales } from './locales'
8
-
9
- import { styles as MDTypeScaleStyles } from '@material/web/typography/md-typescale-styles'
10
- import { ScrollbarStyles } from '@operato/styles'
11
- import { GanttChartController } from './gantt-chart-controller'
12
- import { Task, TimeScale } from './types.js'
13
- import { levelDown, levelUp } from './utils'
14
- import { OxTaskTooltip } from './ox-task-tooltip.js'
15
-
16
- import { gesture } from '@operato/utils/gesture-mixin.js'
17
-
18
- @customElement('ox-gantt-chart')
19
- export class OxGanttChart extends gesture(LitElement) {
20
- static styles = [
21
- MDTypeScaleStyles,
22
- ScrollbarStyles,
23
- css`
24
- :host {
25
- display: flex;
26
- overflow: hidden;
27
- position: relative;
28
- user-select: none;
29
- }
30
-
31
- div[chart-container] {
32
- display: block;
33
- overflow-x: auto;
34
- overflow-y: hidden;
35
- overscroll-behavior: none;
36
- touch-action: none;
37
- }
38
-
39
- svg {
40
- min-width: 100%;
41
- }
42
-
43
- svg[header] {
44
- display: block;
45
- min-width: 100%;
46
- box-sizing: border-box;
47
- height: 40px;
48
- color: var(--ox-gantt-header-color, var(--md-sys-color-on-secondary-container));
49
- background-color: var(--ox-gantt-header-background-color, var(--md-sys-color-secondary-container));
50
- border-bottom: 1px solid var(--ox-gantt-header-border-color, var(--md-sys-color-outline));
51
- position: sticky;
52
- top: 0;
53
- z-index: 1;
54
- }
55
-
56
- .scroll-button {
57
- position: absolute;
58
- top: 46px; /* 헤더 바로 아래 위치 */
59
- width: 14px;
60
- height: 14px;
61
- background-color: var(--ox-gantt-header-color, var(--md-sys-color-on-secondary-container));
62
- opacity: 0.5;
63
- clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
64
- cursor: pointer;
65
- transform: rotate(90deg);
66
- }
67
-
68
- .scroll-button.left {
69
- left: 6px;
70
- transform: rotate(-90deg);
71
- }
72
-
73
- .scroll-button.right {
74
- transform: rotate(90deg);
75
- right: 6px;
76
- }
77
- `
78
- ]
79
-
80
- @property({ type: String, attribute: 'from-date' }) fromDate: string = new Date(new Date().getFullYear(), 0, 1)
81
- .toISOString()
82
- .split('T')[0]
83
- @property({ type: String, attribute: 'to-date' }) toDate: string = new Date(new Date().getFullYear(), 11, 31)
84
- .toISOString()
85
- .split('T')[0]
86
- @property({ type: String, attribute: 'axis-scale' }) axisScale: TimeScale = 'week-day'
87
- @property({ type: Array }) tasks: Task[] = []
88
- @property({ type: String, attribute: 'selected-task-id' }) selectedTaskId?: string
89
- @property({ type: String }) locale: string = 'en-US'
90
- @property({ type: Object }) colorProvider!: (task: Task) => string
91
-
92
- @state() dateRangeSelected?: { start: Date; end: Date }
93
-
94
- @query('svg[header]') svgHeader!: SVGElement
95
- @query('svg[body]') svgBody!: SVGElement
96
- @query('[chart-container]') container!: HTMLDivElement
97
- @query('ox-task-tooltip') tooltip!: OxTaskTooltip
98
-
99
- private ganttChartController!: GanttChartController
100
-
101
- protected firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): void {
102
- this.initHeaderGestureHandlers()
103
-
104
- // this.addEventListener('date-range-cleared', e => this.clearSelection())
105
- }
106
-
107
- connectedCallback(): void {
108
- super.connectedCallback()
109
- this.ganttChartController = new GanttChartController(this, this.fromDate, this.toDate, this.axisScale)
110
-
111
- this.addEventListener('pinch', this.ganttChartController.handlePinchForBody as EventListener)
112
- this.addEventListener('drag', this.ganttChartController.handleDragForBody as EventListener)
113
- this.addEventListener('doubletap', this.ganttChartController.handleDoubleTapForBody as EventListener)
114
- }
115
-
116
- disconnectedCallback(): void {
117
- super.disconnectedCallback()
118
-
119
- this.removeEventListener('pinch', this.ganttChartController.handlePinchForBody as EventListener)
120
- this.removeEventListener('drag', this.ganttChartController.handleDragForBody as EventListener)
121
- this.removeEventListener('doubletap', this.ganttChartController.handleDoubleTapForBody as EventListener)
122
-
123
- this.ganttChartController.dispose()
124
- }
125
-
126
- updated(changedProperties: PropertyValues<this>) {
127
- if (changedProperties.has('fromDate') || changedProperties.has('toDate')) {
128
- this.ganttChartController.updateRange(this.fromDate, this.toDate)
129
- }
130
-
131
- if (changedProperties.has('tasks')) {
132
- const flattenTasks = this.flattenTasks(this.tasks)
133
- this.ganttChartController.setTasks(flattenTasks)
134
- this.svgBody.style.height = Math.max(flattenTasks.length * 30, 2000) + 'px'
135
- }
136
-
137
- if (changedProperties.has('locale')) {
138
- this.updateLocale(this.locale)
139
- }
140
-
141
- if (changedProperties.has('selectedTaskId')) {
142
- this.ganttChartController.selectedTaskId = this.selectedTaskId
143
- }
144
-
145
- if (changedProperties.has('axisScale')) {
146
- this.ganttChartController.level = this.axisScale
147
- this.ganttChartController.updateTimeScale()
148
- }
149
-
150
- if (changedProperties.has('colorProvider')) {
151
- this.ganttChartController.setColorProvider(this.colorProvider)
152
- }
153
-
154
- this.ganttChartController.drawAxes(this.svgHeader)
155
- this.ganttChartController.drawBody(this.svgBody, this.tooltip)
156
- this.drawSelectedRange()
157
- }
158
-
159
- render() {
160
- return html`
161
- <div chart-container @wheel=${(e: WheelEvent) => this.ganttChartController.handleWheelForBody(e, this.container)}>
162
- <svg header @wheel=${this.handleWheelForHeader.bind(this)}></svg>
163
- <svg body></svg>
164
- <div class="scroll-button left" @click=${this.scrollToStart} @touchstart=${this.scrollToStart}></div>
165
- <div class="scroll-button right" @click=${this.scrollToEnd} @touchstart=${this.scrollToEnd}></div>
166
- <ox-task-tooltip></ox-task-tooltip>
167
- </div>
168
- `
169
- }
170
-
171
- scrollToStart() {
172
- this.container.scrollTo({ left: 0, behavior: 'auto' })
173
-
174
- this.syncScrollPosition()
175
- }
176
-
177
- scrollToEnd() {
178
- this.container.scrollTo({ left: this.container.scrollWidth, behavior: 'auto' })
179
- this.syncScrollPosition()
180
- }
181
-
182
- syncScrollPosition() {
183
- const scrollLeft = this.container.scrollLeft
184
- const customEvent = new CustomEvent('scroll', {
185
- detail: { scrollLeft },
186
- bubbles: true,
187
- composed: true
188
- })
189
-
190
- this.dispatchEvent(customEvent)
191
- }
192
-
193
- setVerticalOffset(offset: number) {
194
- this.container.scrollTop = offset
195
- }
196
-
197
- getVerticalOffset(): number {
198
- return this.container.scrollTop
199
- }
200
-
201
- setHorizontalOffset(offset: number) {
202
- this.container.scrollLeft = offset
203
- }
204
-
205
- getHorizontalOffset(): number {
206
- return this.container.scrollLeft
207
- }
208
-
209
- flattenTasks(tasks: Task[], level: number = 0): Task[] {
210
- return tasks.flatMap(task =>
211
- task.collapsed ? [{ ...task, level }] : [{ ...task, level }, ...this.flattenTasks(task.children || [], level + 1)]
212
- )
213
- }
214
-
215
- // 나머지 메서드는 기존 코드와 동일하게 유지
216
- handleWheelForHeader(event: WheelEvent) {
217
- event.preventDefault()
218
- event.stopPropagation()
219
-
220
- const deltaModeFactor =
221
- event.deltaMode === WheelEvent.DOM_DELTA_LINE
222
- ? 16
223
- : event.deltaMode === WheelEvent.DOM_DELTA_PAGE
224
- ? this.container.clientHeight
225
- : 1
226
-
227
- // deltaY 값이 0인 경우, deltaX 값을 사용하여 확대/축소를 처리
228
- const delta = event.deltaY !== 0 ? event.deltaY : event.deltaX
229
- const adjustedDelta = delta * deltaModeFactor
230
-
231
- // 확대/축소 처리
232
- const zoomFactor = adjustedDelta < 0 ? 1.1 : 0.9
233
- this.ganttChartController.baseWidth *= zoomFactor
234
-
235
- if (this.ganttChartController.baseWidth > 40) {
236
- if (this.ganttChartController.level === 'week-day') {
237
- this.ganttChartController.baseWidth = 40
238
- } else {
239
- this.ganttChartController.level = levelDown(this.ganttChartController.level)
240
- this.ganttChartController.baseWidth = 30
241
- }
242
- } else if (this.ganttChartController.baseWidth < 30) {
243
- if (this.ganttChartController.level === 'year-quarter') {
244
- this.ganttChartController.baseWidth = 30
245
- }
246
- }
247
-
248
- if (this.ganttChartController.baseWidth < 20) {
249
- this.ganttChartController.level = levelUp(this.ganttChartController.level)
250
- this.ganttChartController.baseWidth = 30
251
- }
252
-
253
- this.ganttChartController.updateTimeScale()
254
- this.requestUpdate()
255
-
256
- // Dispatch custom event to sync with ox-resource-chart
257
- const customEvent = new CustomEvent('timescale-changed', {
258
- detail: {
259
- baseWidth: this.ganttChartController.baseWidth,
260
- level: this.ganttChartController.level
261
- },
262
- bubbles: true,
263
- composed: true
264
- })
265
- this.dispatchEvent(customEvent)
266
- }
267
-
268
- updateLocale(locale: string) {
269
- const localeDefinition = (locales as any)[locale || 'en-US']
270
- if (localeDefinition) {
271
- d3.timeFormatDefaultLocale(localeDefinition)
272
- this.ganttChartController.updateTimeScale()
273
- }
274
- }
275
-
276
- initHeaderGestureHandlers() {
277
- const header = d3.select(this.svgHeader)
278
-
279
- let startX: number | null = null
280
-
281
- const handlePointerDown = (event: PointerEvent) => {
282
- event.preventDefault()
283
- event.stopPropagation()
284
-
285
- const containerOffsetX = header.node()!.getBoundingClientRect().left
286
- startX = event.clientX - containerOffsetX
287
-
288
- window.addEventListener('pointermove', handlePointerMove)
289
- window.addEventListener('pointerup', handlePointerUp)
290
- }
291
-
292
- const handlePointerMove = (event: PointerEvent) => {
293
- event.preventDefault()
294
- event.stopPropagation()
295
-
296
- const containerOffsetX = header.node()!.getBoundingClientRect().left
297
- const currentX = event.clientX - containerOffsetX
298
-
299
- this.highlightSelection(startX!, currentX)
300
- }
301
-
302
- const handlePointerUp = (event: PointerEvent) => {
303
- event.preventDefault()
304
- event.stopPropagation()
305
-
306
- window.removeEventListener('pointermove', handlePointerMove)
307
- window.removeEventListener('pointerup', handlePointerUp)
308
-
309
- const containerOffsetX = header.node()!.getBoundingClientRect().left
310
- const endX = event.clientX - containerOffsetX
311
- this.finalizeSelection(startX!, endX)
312
-
313
- startX = null
314
- }
315
-
316
- header.on('pointerdown', handlePointerDown)
317
- }
318
-
319
- highlightSelection(startX: number, endX: number) {
320
- let startDate = this.ganttChartController.timeScale.invert(startX)
321
- let endDate = this.ganttChartController.timeScale.invert(endX)
322
-
323
- let start, end
324
-
325
- if (this.ganttChartController.level === 'week-day') {
326
- start = d3.timeDay.floor(d3.min([startDate, endDate])!)
327
- end = d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])!), -1)
328
- } else if (this.ganttChartController.level === 'quarter-week') {
329
- start = d3.timeWeek.floor(d3.min([startDate, endDate])!)
330
- end = d3.timeDay.offset(d3.timeWeek.ceil(d3.max([startDate, endDate])!), -1)
331
- } else if (this.ganttChartController.level === 'year-month') {
332
- start = d3.timeMonth.floor(d3.min([startDate, endDate])!)
333
- end = d3.timeDay.offset(d3.timeMonth.ceil(d3.max([startDate, endDate])!), -1)
334
- } else if (this.ganttChartController.level === 'year-quarter') {
335
- start = d3.timeMonth.every(3)!.floor(d3.min([startDate, endDate])!)
336
- end = d3.timeDay.offset(d3.timeMonth.every(3)!.ceil(d3.max([startDate, endDate])!), -1)
337
- }
338
-
339
- this.dateRangeSelected = { start: start!, end: end! }
340
- }
341
-
342
- finalizeSelection(startX: number, endX: number) {
343
- let startDate = this.ganttChartController.timeScale.invert(startX)
344
- let endDate = this.ganttChartController.timeScale.invert(endX)
345
-
346
- let start, end
347
-
348
- if (this.ganttChartController.level === 'week-day') {
349
- start = d3.timeDay.floor(d3.min([startDate, endDate])!)
350
- end = d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])!), -1)
351
- } else if (this.ganttChartController.level === 'quarter-week') {
352
- start = d3.timeWeek.floor(d3.min([startDate, endDate])!)
353
- end = d3.timeDay.offset(d3.timeWeek.ceil(d3.max([startDate, endDate])!), -1)
354
- } else if (this.ganttChartController.level === 'year-month') {
355
- start = d3.timeMonth.floor(d3.min([startDate, endDate])!)
356
- end = d3.timeDay.offset(d3.timeMonth.ceil(d3.max([startDate, endDate])!), -1)
357
- } else if (this.ganttChartController.level === 'year-quarter') {
358
- start = d3.timeMonth.every(3)!.floor(d3.min([startDate, endDate])!)
359
- end = d3.timeDay.offset(d3.timeMonth.every(3)!.ceil(d3.max([startDate, endDate])!), -1)
360
- }
361
-
362
- this.dateRangeSelected = { start: start!, end: end! }
363
-
364
- const formatDate = d3.timeFormat('%Y-%m-%d')
365
-
366
- this.dispatchEvent(
367
- new CustomEvent('date-range-selected', {
368
- detail: { start: formatDate(start!), end: formatDate(end!) },
369
- bubbles: true,
370
- composed: true
371
- })
372
- )
373
- }
374
-
375
- drawSelectedRange() {
376
- if (!this.dateRangeSelected) {
377
- return
378
- }
379
-
380
- let { start, end } = this.dateRangeSelected
381
-
382
- const header = d3.select(this.svgHeader)
383
- const body = d3.select(this.svgBody)
384
-
385
- header.selectAll('.selection-rect').remove()
386
- body.selectAll('.selection-rect').remove()
387
-
388
- header
389
- .append('rect')
390
- .attr('class', 'selection-rect')
391
- .attr('x', this.ganttChartController.timeScale(start!))
392
- .attr('y', 20)
393
- .attr(
394
- 'width',
395
- this.ganttChartController.timeScale(end!) -
396
- this.ganttChartController.timeScale(start!) +
397
- this.ganttChartController.baseWidth / this.ganttChartController.getScale()
398
- )
399
- .attr('height', 20)
400
- .attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-on-primary-container))')
401
- .attr('opacity', '.2')
402
-
403
- body
404
- .append('rect')
405
- .attr('class', 'selection-rect')
406
- .attr('x', this.ganttChartController.timeScale(start!))
407
- .attr('y', 0)
408
- .attr(
409
- 'width',
410
- this.ganttChartController.timeScale(end!) -
411
- this.ganttChartController.timeScale(start!) +
412
- this.ganttChartController.baseWidth / this.ganttChartController.getScale()
413
- )
414
- .attr('height', '100%')
415
- .attr('fill', 'var(--ox-gantt-selection-color, var(--md-sys-color-on-primary-container))')
416
- .attr('opacity', '.2')
417
- }
418
- }
@@ -1,159 +0,0 @@
1
- import { LitElement, css, html } from 'lit'
2
- import { customElement } from 'lit/decorators.js'
3
-
4
- @customElement('ox-gantt-resize-splitter')
5
- export class OxGanttResizeSplitter extends LitElement {
6
- static styles = css`
7
- :host {
8
- position: relative;
9
- opacity: 0.7;
10
- background-color: var(--splitter-background-color);
11
- width: var(--width);
12
- height: var(--height);
13
- z-index: 2;
14
- }
15
-
16
- :host(:hover) {
17
- background-color: var(--splitter-hover-background-color);
18
- }
19
-
20
- /* 터치 영역을 넓히기 위해 실제 터치 영역을 더 크게 설정 */
21
- div {
22
- width: var(--width);
23
- height: var(--height);
24
- touch-action: none; /* 터치 이벤트 처리에 방해되지 않도록 설정 */
25
- }
26
-
27
- div.touch-target {
28
- width: var(--width);
29
- height: var(--height);
30
- transform: scale(1, 5);
31
- transform-origin: center;
32
- }
33
-
34
- :host([vertical]) div.touch-target {
35
- transform: scale(5, 1); /* 세로 스플리터는 세로 방향으로 터치 영역을 확장 */
36
- }
37
- `
38
-
39
- private dragstart?: { x: number; y: number }
40
-
41
- connectedCallback() {
42
- super.connectedCallback()
43
-
44
- if (this.hasAttribute('vertical')) {
45
- this.style.setProperty('--width', '3px')
46
- this.style.setProperty('--height', '100%')
47
- this.style.cursor = 'col-resize'
48
- } else {
49
- this.style.setProperty('--height', '3px')
50
- this.style.setProperty('--width', '100%')
51
- this.style.cursor = 'row-resize'
52
- }
53
- }
54
-
55
- render() {
56
- return html`
57
- <div
58
- class="touch-target"
59
- @mousedown=${(e: MouseEvent) => this.onMouseDown(e)}
60
- @touchstart=${this.onTouchStart}
61
- ></div>
62
- <div></div>
63
- <!-- 실제 보이는 스플리터 -->
64
- `
65
- }
66
-
67
- private onMouseDown(e: MouseEvent) {
68
- this.startDrag(e.clientX, e.clientY)
69
- window.addEventListener('mousemove', this.onMouseMove)
70
- window.addEventListener('mouseup', this.onMouseUp)
71
- e.stopPropagation()
72
- }
73
-
74
- private onTouchStart(e: TouchEvent) {
75
- // 터치가 touch-target에서 시작된 경우에만 작동
76
- if ((e.target as HTMLElement).classList.contains('touch-target')) {
77
- const touch = e.touches[0]
78
- this.startDrag(touch.clientX, touch.clientY)
79
- window.addEventListener('touchmove', this.onTouchMove)
80
- window.addEventListener('touchend', this.onTouchEnd)
81
- }
82
- e.stopPropagation()
83
- }
84
-
85
- private startDrag(clientX: number, clientY: number) {
86
- this.dragstart = { x: clientX, y: clientY }
87
-
88
- this.dispatchEvent(
89
- new CustomEvent('splitter-dragstart', {
90
- bubbles: true,
91
- composed: true
92
- })
93
- )
94
- }
95
-
96
- private onMouseMove = (e: MouseEvent) => {
97
- this.moveDrag(e.clientX, e.clientY)
98
- }
99
-
100
- private onTouchMove = (e: TouchEvent) => {
101
- const touch = e.touches[0]
102
- this.moveDrag(touch.clientX, touch.clientY)
103
- }
104
-
105
- private moveDrag(clientX: number, clientY: number) {
106
- if (!this.dragstart) return
107
-
108
- const deltaX = clientX - this.dragstart.x
109
- const deltaY = clientY - this.dragstart.y
110
-
111
- this.dispatchEvent(
112
- new CustomEvent('splitter-drag', {
113
- bubbles: true,
114
- composed: true,
115
- detail: { x: deltaX, y: deltaY }
116
- })
117
- )
118
-
119
- if (this.hasAttribute('vertical')) {
120
- document.body.style.cursor = 'col-resize'
121
- } else {
122
- document.body.style.cursor = 'row-resize'
123
- }
124
-
125
- document.body.style.userSelect = 'none'
126
- }
127
-
128
- private onMouseUp = (e: MouseEvent) => {
129
- this.endDrag(e.clientX, e.clientY)
130
- window.removeEventListener('mousemove', this.onMouseMove)
131
- window.removeEventListener('mouseup', this.onMouseUp)
132
- }
133
-
134
- private onTouchEnd = (e: TouchEvent) => {
135
- const touch = e.changedTouches[0]
136
- this.endDrag(touch.clientX, touch.clientY)
137
- window.removeEventListener('touchmove', this.onTouchMove)
138
- window.removeEventListener('touchend', this.onTouchEnd)
139
- }
140
-
141
- private endDrag(clientX: number, clientY: number) {
142
- if (!this.dragstart) return
143
-
144
- const deltaX = clientX - this.dragstart.x
145
- const deltaY = clientY - this.dragstart.y
146
-
147
- this.dispatchEvent(
148
- new CustomEvent('splitter-dragend', {
149
- bubbles: true,
150
- composed: true,
151
- detail: { x: deltaX, y: deltaY }
152
- })
153
- )
154
-
155
- this.dragstart = undefined
156
- document.body.style.cursor = ''
157
- document.body.style.userSelect = ''
158
- }
159
- }