@operato/gantt 8.0.0-alpha.6 → 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.
Files changed (49) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/dist/src/gantt-chart-controller.d.ts +5 -6
  3. package/dist/src/gantt-chart-controller.js +92 -37
  4. package/dist/src/gantt-chart-controller.js.map +1 -1
  5. package/dist/src/ox-gantt-chart.d.ts +378 -3
  6. package/dist/src/ox-gantt-chart.js +86 -180
  7. package/dist/src/ox-gantt-chart.js.map +1 -1
  8. package/dist/src/ox-gantt-resize-splitter.d.ts +6 -2
  9. package/dist/src/ox-gantt-resize-splitter.js +70 -39
  10. package/dist/src/ox-gantt-resize-splitter.js.map +1 -1
  11. package/dist/src/ox-gantt.js +11 -11
  12. package/dist/src/ox-gantt.js.map +1 -1
  13. package/dist/src/ox-resource-chart.js +2 -0
  14. package/dist/src/ox-resource-chart.js.map +1 -1
  15. package/dist/src/ox-task-tooltip.d.ts +3 -1
  16. package/dist/src/ox-task-tooltip.js +36 -11
  17. package/dist/src/ox-task-tooltip.js.map +1 -1
  18. package/dist/src/types.d.ts +1 -0
  19. package/dist/src/types.js.map +1 -1
  20. package/dist/stories/ox-gantt.stories.js +37 -1
  21. package/dist/stories/ox-gantt.stories.js.map +1 -1
  22. package/dist/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +13 -13
  24. package/themes/calendar-theme.css +3 -1
  25. package/.editorconfig +0 -29
  26. package/.storybook/main.js +0 -3
  27. package/.storybook/preview.js +0 -52
  28. package/.storybook/server.mjs +0 -8
  29. package/assets/images/icon-editor-gradient-direction.png +0 -0
  30. package/assets/images/icon-properties-label.png +0 -0
  31. package/assets/images/icon-properties-line-type.png +0 -0
  32. package/assets/images/icon-properties-table.png +0 -0
  33. package/src/draw-time-scale-header.ts +0 -142
  34. package/src/gantt-chart-controller.ts +0 -336
  35. package/src/index.ts +0 -0
  36. package/src/locales.ts +0 -78
  37. package/src/ox-gantt-chart.ts +0 -518
  38. package/src/ox-gantt-resize-splitter.ts +0 -131
  39. package/src/ox-gantt.ts +0 -532
  40. package/src/ox-resource-chart.ts +0 -145
  41. package/src/ox-task-tooltip.ts +0 -120
  42. package/src/resource-chart-controller.ts +0 -284
  43. package/src/types.ts +0 -32
  44. package/src/utils.ts +0 -13
  45. package/stories/ox-gantt-chart.stories.ts +0 -125
  46. package/stories/ox-gantt.stories.ts +0 -417
  47. package/tsconfig.json +0 -25
  48. package/web-dev-server.config.mjs +0 -27
  49. package/web-test-runner.config.mjs +0 -41
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/gantt",
3
3
  "description": "Webcomponents for gantt chart following open-wc recommendations",
4
4
  "author": "heartyoh@hatiolab.com",
5
- "version": "8.0.0-alpha.6",
5
+ "version": "8.0.0-beta.1",
6
6
  "main": "dist/src/index.js",
7
7
  "module": "dist/src/index.js",
8
8
  "license": "MIT",
@@ -42,9 +42,9 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@operato/i18n": "^8.0.0-alpha.0",
46
- "@operato/styles": "^8.0.0-alpha.4",
47
- "@operato/utils": "^8.0.0-alpha.0",
45
+ "@operato/i18n": "^8.0.0-beta.1",
46
+ "@operato/styles": "^8.0.0-beta.1",
47
+ "@operato/utils": "^8.0.0-beta.1",
48
48
  "d3": "^7.8.5",
49
49
  "d3-time-format": "^4.1.0",
50
50
  "i18next": "^23.11.5",
@@ -52,18 +52,18 @@
52
52
  "lodash-es": "^4.17.21"
53
53
  },
54
54
  "devDependencies": {
55
- "@custom-elements-manifest/analyzer": "^0.9.2",
55
+ "@custom-elements-manifest/analyzer": "^0.10.0",
56
56
  "@hatiolab/prettier-config": "^1.0.0",
57
57
  "@open-wc/eslint-config": "^12.0.3",
58
- "@open-wc/testing": "^3.1.6",
58
+ "@open-wc/testing": "^4.0.0",
59
59
  "@types/lodash-es": "^4.17.5",
60
- "@typescript-eslint/eslint-plugin": "^7.0.1",
61
- "@typescript-eslint/parser": "^7.0.1",
62
- "@web/dev-server": "^0.3.0",
60
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
61
+ "@typescript-eslint/parser": "^8.0.0",
62
+ "@web/dev-server": "^0.4.0",
63
63
  "@web/dev-server-storybook": "^2.0.1",
64
- "@web/test-runner": "^0.18.0",
65
- "concurrently": "^8.0.1",
66
- "eslint": "^8.39.0",
64
+ "@web/test-runner": "^0.19.0",
65
+ "concurrently": "^9.0.0",
66
+ "eslint": "^9.0.0",
67
67
  "eslint-config-prettier": "^9.1.0",
68
68
  "husky": "^9.0.11",
69
69
  "lint-staged": "^15.2.2",
@@ -85,5 +85,5 @@
85
85
  "prettier --write"
86
86
  ]
87
87
  },
88
- "gitHead": "f0c1979adcf64df1f543f383ae7385a56df1e57a"
88
+ "gitHead": "d5b28a2e9deb632c0dc80132f6a7196dd6fe4220"
89
89
  }
@@ -9,7 +9,8 @@ body {
9
9
  /* monthly layout */
10
10
  --calendar-monthly-ol-margin: var(--margin-default) 0;
11
11
  --calendar-monthly-ol-top-border: 2px solid var(--md-sys-color-secondary);
12
- --calendar-current-monty-background-color: var(--md-sys-color-surface-variant);
12
+ --calendar-current-month-background-color: var(--md-sys-color-surface-variant);
13
+ --calendar-current-month-color: var(--md-sys-color-on-surface);
13
14
  --calendar-monthly-label-align: left;
14
15
  --calendar-monthly-label-padding: var(--padding-narrow) 0;
15
16
  --calendar-monthly-label-color: var(--md-sys-color-secondary);
@@ -35,6 +36,7 @@ body {
35
36
  --calendar-weekly-ol-margin: var(--margin-default) 0;
36
37
  --calendar-weekly-ol-top-border: 2px solid var(--md-sys-color-secondary);
37
38
  --calendar-current-week-background-color: var(--md-sys-color-surface-variant);
39
+ --calendar-current-week-color: var(--md-sys-color-on-surface);
38
40
  --calendar-weekly-label-align: center;
39
41
  --calendar-weekly-label-padding: var(--padding-narrow) 0;
40
42
  --calendar-weekly-label-color: var(--md-sys-color-secondary);
package/.editorconfig DELETED
@@ -1,29 +0,0 @@
1
- # EditorConfig helps developers define and maintain consistent
2
- # coding styles between different editors and IDEs
3
- # editorconfig.org
4
-
5
- root = true
6
-
7
-
8
- [*]
9
-
10
- # Change these settings to your own preference
11
- indent_style = space
12
- indent_size = 2
13
-
14
- # We recommend you to keep these unchanged
15
- end_of_line = lf
16
- charset = utf-8
17
- trim_trailing_whitespace = true
18
- insert_final_newline = true
19
-
20
- [*.md]
21
- trim_trailing_whitespace = false
22
-
23
- [*.json]
24
- indent_size = 2
25
-
26
- [*.{html,js,md}]
27
- block_comment_start = /**
28
- block_comment = *
29
- block_comment_end = */
@@ -1,3 +0,0 @@
1
- module.exports = {
2
- stories: ['../dist/stories/**/*.stories.{js,md,mdx}']
3
- }
@@ -1,52 +0,0 @@
1
- import { i18next } from '@operato/i18n'
2
-
3
- export const globalTypes = {
4
- locale: {
5
- name: 'Locale',
6
- description: 'Internationalization locale',
7
- toolbar: {
8
- icon: 'globe',
9
- items: [
10
- { value: 'en', right: '🇺🇸', title: 'English' },
11
- { value: 'ko', right: '🇰🇷', title: '한국어' },
12
- { value: 'zh', right: '🇨🇳', title: '中文' },
13
- { value: 'ja', right: '🇯🇵', title: '日本語' },
14
- { value: 'ms', right: '🇲🇾', title: 'Bahasa Melayu' }
15
- ],
16
- showName: true
17
- }
18
- },
19
- theme: {
20
- name: 'Theme',
21
- description: 'Global theme for components',
22
- toolbar: {
23
- icon: 'paintbrush',
24
- items: [
25
- { value: 'light', title: 'Light' },
26
- { value: 'dark', title: 'Dark' }
27
- ],
28
- showName: true
29
- }
30
- }
31
- }
32
-
33
- export const decorators = [
34
- (Story, context) => {
35
- const { locale, theme } = context.globals
36
-
37
- if (locale) {
38
- i18next.changeLanguage(locale)
39
- }
40
-
41
- // Set the theme class for the document
42
- if (theme === 'dark') {
43
- document.documentElement.classList.add('dark')
44
- document.documentElement.classList.remove('light')
45
- } else {
46
- document.documentElement.classList.add('light')
47
- document.documentElement.classList.remove('dark')
48
- }
49
-
50
- return Story()
51
- }
52
- ]
@@ -1,8 +0,0 @@
1
- import { storybookPlugin } from '@web/dev-server-storybook'
2
- import baseConfig from '../web-dev-server.config.mjs'
3
-
4
- export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
5
- ...baseConfig,
6
- open: '/',
7
- plugins: [storybookPlugin({ type: 'web-components' }), ...baseConfig.plugins]
8
- })
@@ -1,142 +0,0 @@
1
- import * as d3 from 'd3'
2
- import { TimeScale } from './types'
3
-
4
- export function drawTimeScaleHeader(
5
- svgHeader: SVGElement,
6
- timeScale: d3.ScaleTime<number, number, never>,
7
- fromTime: Date,
8
- toTime: Date,
9
- baseWidth: number,
10
- level: TimeScale,
11
- isTwoLevelHeader: boolean = true
12
- ) {
13
- const header = d3.select(svgHeader)
14
-
15
- header.selectAll('*').remove()
16
- header.attr('width', getChartWidth(fromTime, toTime, baseWidth, level))
17
-
18
- if (!isTwoLevelHeader) {
19
- header.attr('height', '30px')
20
- } else {
21
- header.attr('height', '40px')
22
- }
23
-
24
- const addGridLines = (ticks: Date[], yStart: number, yEnd: number) => {
25
- ticks.forEach(tick => {
26
- const startX = timeScale(tick)
27
- header
28
- .append('line')
29
- .attr('x1', startX)
30
- .attr('x2', startX)
31
- .attr('y1', yStart)
32
- .attr('y2', yEnd)
33
- .attr('stroke', 'var(--ox-gantt-color-gridlines, var(--md-sys-color-outline))')
34
- .attr('stroke-width', 1)
35
- })
36
- }
37
-
38
- const centerLabel = (
39
- selection: d3.Selection<SVGGElement, unknown, null, undefined>,
40
- tickInterval: d3.CountableTimeInterval
41
- ) => {
42
- selection.selectAll('text').attr('transform', (d: any) => {
43
- const tickIntervalWidth = timeScale(tickInterval.offset(d, 1)) - timeScale(d)
44
- return `translate(${tickIntervalWidth / 2}, ${isTwoLevelHeader ? -2 : -8})`
45
- })
46
- }
47
-
48
- const drawLevel = (
49
- mainTicks: Date[],
50
- mainFormat: (d: Date) => string,
51
- mainInterval: d3.CountableTimeInterval,
52
- detailTicks: Date[],
53
- detailFormat: (d: Date) => string,
54
- detailInterval: d3.CountableTimeInterval
55
- ) => {
56
- if (isTwoLevelHeader) {
57
- const mainAxis = d3
58
- .axisTop(timeScale)
59
- .ticks(mainInterval)
60
- .tickFormat(mainFormat as any)
61
- .tickSize(0)
62
- addGridLines(mainTicks, 0, 20)
63
-
64
- const mainAxisG = header
65
- .append('g')
66
- .classed('main-axis no-select md-typescale-label-small', true)
67
- .attr('transform', 'translate(0, 20)')
68
- .call(mainAxis)
69
-
70
- centerLabel(mainAxisG, mainInterval)
71
- }
72
-
73
- const detailAxis = d3
74
- .axisTop(timeScale)
75
- .ticks(detailInterval)
76
- .tickFormat(detailFormat as any)
77
- .tickSize(0)
78
- addGridLines(detailTicks, isTwoLevelHeader ? 20 : 0, isTwoLevelHeader ? 40 : 30)
79
-
80
- const detailAxisG = header
81
- .append('g')
82
- .classed('detail-axis no-select md-typescale-label-small', true)
83
- .attr('transform', `translate(0, ${isTwoLevelHeader ? 40 : 30})`)
84
- .call(detailAxis)
85
-
86
- centerLabel(detailAxisG, detailInterval)
87
- }
88
-
89
- if (level === 'week-day') {
90
- drawLevel(
91
- d3.timeWeek.every(1)!.range(d3.timeWeek.floor(fromTime), d3.timeWeek.offset(toTime, 1)),
92
- d3.timeFormat('%B %d'),
93
- d3.timeWeek,
94
- d3.timeDay.every(1)!.range(d3.timeDay.floor(fromTime), d3.timeDay.offset(toTime, 1)),
95
- d3.timeFormat('%d'),
96
- d3.timeDay
97
- )
98
- } else if (level === 'quarter-week') {
99
- drawLevel(
100
- d3.timeMonth.every(3)!.range(d3.timeMonth.floor(fromTime), d3.timeMonth.offset(toTime, 3)),
101
- d3.timeFormat('%Y %b'),
102
- d3.timeMonth.every(3)! as any,
103
- d3.timeWeek.every(1)!.range(d3.timeWeek.floor(fromTime), d3.timeWeek.offset(toTime, 1)),
104
- d3.timeFormat('%b %d'),
105
- d3.timeWeek
106
- )
107
- } else if (level === 'year-month') {
108
- drawLevel(
109
- d3.timeYear.every(1)!.range(d3.timeYear.floor(fromTime), d3.timeYear.offset(toTime, 1)),
110
- d3.timeFormat('%Y'),
111
- d3.timeYear,
112
- d3.timeMonth.every(1)!.range(d3.timeMonth.floor(fromTime), d3.timeMonth.offset(toTime, 1)),
113
- d3.timeFormat('%b'),
114
- d3.timeMonth
115
- )
116
- } else if (level === 'year-quarter') {
117
- drawLevel(
118
- d3.timeYear.every(1)!.range(d3.timeYear.floor(fromTime), d3.timeYear.offset(toTime, 1)),
119
- d3.timeFormat('%Y'),
120
- d3.timeYear,
121
- d3.timeMonth.every(3)!.range(d3.timeMonth.floor(fromTime), d3.timeMonth.offset(toTime, 3)),
122
- d3.timeFormat('%Y %b'),
123
- d3.timeMonth.every(3)! as any
124
- )
125
- }
126
- }
127
-
128
- export function getChartWidth(fromTime: Date, toTime: Date, baseWidth: number, level: TimeScale): number {
129
- const totalDays = d3.timeDay.count(fromTime, d3.timeDay.offset(toTime, 1))
130
- return (totalDays * baseWidth) / getScale(level)
131
- }
132
-
133
- export function getScale(level: TimeScale): number {
134
- if (level === 'quarter-week') {
135
- return 7 / 2
136
- } else if (level === 'year-month') {
137
- return 30 / 2
138
- } else if (level === 'year-quarter') {
139
- return 90 / 2
140
- }
141
- return 1
142
- }
@@ -1,336 +0,0 @@
1
- import { LitElement, ReactiveController, ReactiveControllerHost } from 'lit'
2
- import * as d3 from 'd3'
3
- import { TimeScale, Task, TaskTag } from './types'
4
- import { OxTaskTooltip } from './ox-task-tooltip'
5
- import { drawTimeScaleHeader, getScale, getChartWidth } from './draw-time-scale-header'
6
-
7
- export class GanttChartController implements ReactiveController {
8
- private host: ReactiveControllerHost & LitElement
9
- private fromTime: Date
10
- private toTime: Date
11
- private totalDays: number
12
- private _timeScale?: d3.ScaleTime<number, number, never>
13
-
14
- private touchStartX?: number
15
- private touchStartY?: number
16
- private scrollStartX?: number
17
- private scrollStartY?: number
18
-
19
- private colorProvider?: (task: Task) => string
20
-
21
- public baseWidth: number = 30
22
- public level: TimeScale
23
- public selectedTaskId?: string
24
-
25
- public tasks: Task[] = []
26
- public extendGridLines: boolean = false
27
-
28
- constructor(host: ReactiveControllerHost & LitElement, fromDate: string, toDate: string, axisScale: TimeScale) {
29
- this.host = host
30
- this.fromTime = new Date(fromDate)
31
- this.toTime = new Date(toDate)
32
- this.level = axisScale
33
- this.totalDays = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1))
34
- this.host.addController(this)
35
- }
36
-
37
- hostConnected() {
38
- this.updateTimeScale()
39
- }
40
-
41
- setColorProvider(colorProvider: (task: Task) => string) {
42
- this.colorProvider = colorProvider
43
- }
44
-
45
- get timeScale() {
46
- if (!this._timeScale) {
47
- this._timeScale = d3
48
- .scaleTime()
49
- .domain([d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1)])
50
- .range([0, this.getChartWidth()])
51
- }
52
- return this._timeScale
53
- }
54
-
55
- updateRange(fromDate: string, toDate: string) {
56
- this.fromTime = new Date(fromDate)
57
- this.toTime = new Date(toDate)
58
- this.totalDays = d3.timeDay.count(this.fromTime, d3.timeDay.offset(this.toTime, 1))
59
- this._timeScale = undefined
60
- this.host.requestUpdate()
61
- }
62
-
63
- setTasks(tasks: Task[]) {
64
- this.tasks = tasks
65
- this.host.requestUpdate()
66
- }
67
-
68
- updateTimeScale() {
69
- this.timeScale.range([0, this.getChartWidth()])
70
- }
71
-
72
- getScale() {
73
- return getScale(this.level)
74
- }
75
-
76
- getChartWidth() {
77
- return getChartWidth(this.fromTime, this.toTime, this.baseWidth, this.level)
78
- }
79
-
80
- drawAxes(svgHeader: SVGElement) {
81
- drawTimeScaleHeader(svgHeader, this.timeScale, this.fromTime, this.toTime, this.baseWidth, this.level)
82
- }
83
-
84
- drawBody(svgBody: SVGElement, tooltip: OxTaskTooltip) {
85
- const body = d3.select(svgBody)
86
-
87
- body.selectAll('*').remove()
88
- body.attr('width', this.getChartWidth())
89
-
90
- // Define the arrow marker
91
- const defs = body.append('defs')
92
- defs
93
- .append('marker')
94
- .attr('id', 'arrow')
95
- .attr('viewBox', '0 0 10 10')
96
- .attr('refX', 10) // Adjusted refX to move the arrow tip closer to the line's end
97
- .attr('refY', 5)
98
- .attr('markerWidth', 6)
99
- .attr('markerHeight', 6)
100
- .attr('orient', 'auto-start-reverse')
101
- .append('path')
102
- .attr('d', 'M 0 0 L 10 5 L 0 10 z')
103
- .attr('fill', 'var(--md-sys-color-on-primary-container, black)')
104
-
105
- if (this.extendGridLines) {
106
- const addGridLines = (ticks: Date[]) => {
107
- ticks.forEach(tick => {
108
- const startX = this.timeScale(tick)
109
- body
110
- .append('line')
111
- .attr('x1', startX)
112
- .attr('x2', startX)
113
- .attr('y1', 0)
114
- .attr('y2', Number(svgBody.getBoundingClientRect().height) || 0)
115
- .attr('stroke', 'var(--ox-gantt-color-gridlines, var(--md-sys-color-outline))')
116
- .attr('stroke-width', 1)
117
- })
118
- }
119
-
120
- if (this.level === 'week-day') {
121
- const dayTicks = d3.timeDay.every(1)!.range(d3.timeDay.floor(this.fromTime), d3.timeDay.offset(this.toTime, 1))
122
- addGridLines(dayTicks)
123
- } else if (this.level === 'quarter-week') {
124
- const weekTicks = d3.timeWeek
125
- .every(1)!
126
- .range(d3.timeWeek.floor(this.fromTime), d3.timeWeek.offset(this.toTime, 1))
127
- addGridLines(weekTicks)
128
- } else if (this.level === 'year-month') {
129
- const monthTicks = d3.timeMonth
130
- .every(1)!
131
- .range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 1))
132
- addGridLines(monthTicks)
133
- } else if (this.level === 'year-quarter') {
134
- const quarterTicks = d3.timeMonth
135
- .every(3)!
136
- .range(d3.timeMonth.floor(this.fromTime), d3.timeMonth.offset(this.toTime, 3))
137
- addGridLines(quarterTicks)
138
- }
139
- }
140
-
141
- const taskPositions: { [key: string]: { x: number; y: number; width: number } } = {}
142
-
143
- this.tasks.forEach((task, index) => {
144
- const startDate = new Date(task.startDate)
145
- const endDate = new Date(task.endDate)
146
-
147
- const startX = this.timeScale(d3.timeDay.floor(d3.min([startDate, endDate])!))
148
- const endX = this.timeScale(d3.timeDay.offset(d3.timeDay.ceil(d3.max([startDate, endDate])!), -1))
149
-
150
- const taskWidth = endX - startX + this.baseWidth / this.getScale()
151
- const taskY = index * 30 + 7.5
152
-
153
- const showTooltip = (event: MouseEvent) => {
154
- tooltip.title = task.title
155
- tooltip.startDate = startDate.toISOString().split('T')[0]
156
- tooltip.endDate = endDate.toISOString().split('T')[0]
157
- tooltip.progress = task.progress ?? 0
158
- tooltip.type = task.type
159
-
160
- tooltip.show(event.clientX, event.clientY)
161
- }
162
-
163
- const hideTooltip = () => {
164
- tooltip.hide()
165
- }
166
-
167
- if (this.selectedTaskId === task.id) {
168
- body
169
- .append('rect')
170
- .attr('x', 0)
171
- .attr('y', taskY - 7.5)
172
- .attr('width', this.getChartWidth())
173
- .attr('height', 30)
174
- .attr('fill', 'var(--ox-gantt-selected-row-background-color, var(--md-sys-color-surface-variant))')
175
- }
176
-
177
- const taskBar = body
178
- .append('rect')
179
- .attr('x', startX)
180
- .attr('y', taskY)
181
- .attr('width', taskWidth)
182
- .attr('height', 15)
183
- .attr('fill', this.colorProvider ? this.colorProvider(task) : 'gray')
184
- .attr('stroke', 'black')
185
- .attr('stroke-width', 1)
186
-
187
- taskBar
188
- .on('click', () => {
189
- this.selectedTaskId = task.id
190
- const customEvent = new CustomEvent('task-clicked', {
191
- detail: { taskId: task.id },
192
- bubbles: true,
193
- composed: true
194
- })
195
- this.host.dispatchEvent(customEvent)
196
- this.host.requestUpdate()
197
- })
198
- .on('mouseover', showTooltip)
199
- .on('mouseout', hideTooltip)
200
-
201
- if (task.progress !== undefined) {
202
- const progressWidth = taskWidth * (task.progress / 100)
203
- const progressBar = body
204
- .append('rect')
205
- .attr('x', startX)
206
- .attr('y', taskY + 7.5 - 3.75)
207
- .attr('width', progressWidth)
208
- .attr('height', 7.5)
209
- .attr('fill', 'lightgreen')
210
-
211
- progressBar
212
- .on('click', () => {
213
- this.selectedTaskId = task.id
214
- const customEvent = new CustomEvent('task-clicked', {
215
- detail: { taskId: task.id },
216
- bubbles: true,
217
- composed: true
218
- })
219
- this.host.dispatchEvent(customEvent)
220
- this.host.requestUpdate()
221
- })
222
- .on('mouseover', showTooltip)
223
- .on('mouseout', hideTooltip)
224
- }
225
-
226
- taskPositions[task.id] = { x: endX + this.baseWidth / this.getScale(), y: taskY + 7.5, width: taskWidth }
227
- })
228
-
229
- this.tasks.forEach(task => {
230
- if (task.dependsOn && taskPositions[task.dependsOn]) {
231
- const prevTask = taskPositions[task.dependsOn]
232
- const currTask = taskPositions[task.id]
233
-
234
- body
235
- .append('path')
236
- .attr(
237
- 'd',
238
- `
239
- M${prevTask.x},${prevTask.y}
240
- L${prevTask.x + 10},${prevTask.y}
241
- ${
242
- prevTask.x + 10 < currTask.x - 10 - currTask.width
243
- ? `
244
- L${(prevTask.x + currTask.x - currTask.width) / 2},${prevTask.y}
245
- L${(prevTask.x + currTask.x - currTask.width) / 2},${currTask.y}
246
- `
247
- : `
248
- L${prevTask.x + 10},${(prevTask.y + currTask.y) / 2}
249
- L${currTask.x - 10 - currTask.width},${(prevTask.y + currTask.y) / 2}
250
- `
251
- }
252
- L${currTask.x - 10 - currTask.width},${currTask.y}
253
- L${currTask.x - currTask.width},${currTask.y}
254
- `
255
- )
256
- .attr('stroke', 'var(--md-sys-color-on-primary-container, black)')
257
- .attr('fill', 'none')
258
- .attr('stroke-width', 1)
259
- .attr('marker-end', 'url(#arrow)') // Add arrow marker
260
- }
261
- })
262
- }
263
-
264
- getTaskColor(tags: TaskTag[]): string {
265
- if (tags.includes('critical')) {
266
- return 'red'
267
- } else if (tags.includes('milestone')) {
268
- return 'blue'
269
- } else if (tags.includes('done')) {
270
- return 'green'
271
- } else {
272
- return 'grey'
273
- }
274
- }
275
-
276
- handleWheelForBody(event: WheelEvent, container: HTMLDivElement) {
277
- event.preventDefault()
278
- event.stopPropagation()
279
-
280
- const deltaModeFactor =
281
- event.deltaMode === WheelEvent.DOM_DELTA_LINE
282
- ? 16
283
- : event.deltaMode === WheelEvent.DOM_DELTA_PAGE
284
- ? container.clientHeight
285
- : 1
286
- const deltaX = event.deltaX * deltaModeFactor
287
- const deltaY = event.deltaY * deltaModeFactor
288
-
289
- // 가로 스크롤 및 세로 스크롤 모두 처리
290
- container.scrollLeft += deltaX
291
- container.scrollTop += deltaY
292
-
293
- const customEvent = new CustomEvent('scroll', {
294
- detail: {
295
- scrollLeft: container.scrollLeft,
296
- scrollTop: container.scrollTop
297
- },
298
- bubbles: true,
299
- composed: true
300
- })
301
-
302
- this.host.dispatchEvent(customEvent)
303
- }
304
-
305
- handleTouchStartForBody(event: TouchEvent, container: HTMLDivElement) {
306
- event.stopPropagation()
307
-
308
- this.touchStartX = event.touches[0].clientX
309
- this.touchStartY = event.touches[0].clientY
310
-
311
- this.scrollStartX = container.scrollLeft
312
- this.scrollStartY = container.scrollTop
313
- }
314
-
315
- handleTouchMoveForBody(event: TouchEvent, container: HTMLDivElement) {
316
- const touchCurrentX = event.touches[0].clientX
317
- const touchCurrentY = event.touches[0].clientY
318
-
319
- const deltaX = this.touchStartX! - touchCurrentX
320
- const deltaY = this.touchStartY! - touchCurrentY
321
-
322
- container.scrollLeft = this.scrollStartX! + deltaX
323
- container.scrollTop = this.scrollStartY! + deltaY
324
-
325
- const customEvent = new CustomEvent('scroll', {
326
- detail: {
327
- scrollLeft: container.scrollLeft,
328
- scrollTop: container.scrollTop
329
- },
330
- bubbles: true,
331
- composed: true
332
- })
333
-
334
- this.host.dispatchEvent(customEvent)
335
- }
336
- }
package/src/index.ts DELETED
File without changes