@ganttloom/gantt-vue 0.5.1 → 0.6.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/README.md +359 -272
- package/dist/index.cjs +14 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -1
- package/dist/index.d.ts +45 -1
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,272 +1,359 @@
|
|
|
1
|
-
# @ganttloom/gantt-vue
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@ganttloom/gantt-vue)
|
|
4
|
-
[](../../LICENSE)
|
|
5
|
-
|
|
6
|
-
Vue 3 component wrapping [`@ganttloom/gantt-core`](../gantt-core). A thin
|
|
7
|
-
wrapper: it owns no scheduling logic of its own, just Vue lifecycle,
|
|
8
|
-
props-as-events, and an exposed handle for the escape hatches (undo/redo,
|
|
9
|
-
zoom, export) that don't fit the declarative-props model.
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
|
|
13
|
-
## Install
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install @ganttloom/gantt-vue
|
|
17
|
-
# vue >=3.3 is a peer dependency
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Quick start
|
|
21
|
-
|
|
22
|
-
```vue
|
|
23
|
-
<script setup lang="ts">
|
|
24
|
-
import { GanttChart } from "@ganttloom/gantt-vue";
|
|
25
|
-
|
|
26
|
-
const tasks = [
|
|
27
|
-
{ id: "1", name: "Design", start: new Date("2026-01-05"), end: new Date("2026-01-09"), progress: 60 },
|
|
28
|
-
{ id: "2", name: "Build", start: new Date("2026-01-09"), end: new Date("2026-01-16"), progress: 10 },
|
|
29
|
-
];
|
|
30
|
-
const dependencies = [{ fromId: "1", toId: "2", type: "FS" as const }];
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
<template>
|
|
34
|
-
<GanttChart
|
|
35
|
-
:tasks="tasks"
|
|
36
|
-
:dependencies="dependencies"
|
|
37
|
-
view-mode="day"
|
|
38
|
-
show-critical-path
|
|
39
|
-
enable-history
|
|
40
|
-
@date-change="(task, start, end) => console.log(task.name, start, end)"
|
|
41
|
-
/>
|
|
42
|
-
</template>
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
`<GanttChart />`
|
|
46
|
-
automatically
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
import {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<button @click="chartRef?.
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
`
|
|
77
|
-
`
|
|
78
|
-
`
|
|
79
|
-
`
|
|
80
|
-
`
|
|
81
|
-
`
|
|
82
|
-
`
|
|
83
|
-
`
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
@
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
>
|
|
159
|
-
>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
:
|
|
172
|
-
|
|
173
|
-
@column-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
`
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
import {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
tooltip
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
:
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
`
|
|
271
|
-
|
|
272
|
-
|
|
1
|
+
# @ganttloom/gantt-vue
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ganttloom/gantt-vue)
|
|
4
|
+
[](../../LICENSE)
|
|
5
|
+
|
|
6
|
+
Vue 3 component wrapping [`@ganttloom/gantt-core`](../gantt-core). A thin
|
|
7
|
+
wrapper: it owns no scheduling logic of its own, just Vue lifecycle,
|
|
8
|
+
props-as-events, and an exposed handle for the escape hatches (undo/redo,
|
|
9
|
+
zoom, export) that don't fit the declarative-props model.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @ganttloom/gantt-vue
|
|
17
|
+
# vue >=3.3 is a peer dependency
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
```vue
|
|
23
|
+
<script setup lang="ts">
|
|
24
|
+
import { GanttChart } from "@ganttloom/gantt-vue";
|
|
25
|
+
|
|
26
|
+
const tasks = [
|
|
27
|
+
{ id: "1", name: "Design", start: new Date("2026-01-05"), end: new Date("2026-01-09"), progress: 60 },
|
|
28
|
+
{ id: "2", name: "Build", start: new Date("2026-01-09"), end: new Date("2026-01-16"), progress: 10 },
|
|
29
|
+
];
|
|
30
|
+
const dependencies = [{ fromId: "1", toId: "2", type: "FS" as const }];
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<GanttChart
|
|
35
|
+
:tasks="tasks"
|
|
36
|
+
:dependencies="dependencies"
|
|
37
|
+
view-mode="day"
|
|
38
|
+
show-critical-path
|
|
39
|
+
enable-history
|
|
40
|
+
@date-change="(task, start, end) => console.log(task.name, start, end)"
|
|
41
|
+
/>
|
|
42
|
+
</template>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`<GanttChart />` injects `@ganttloom/gantt-core`'s default stylesheet
|
|
46
|
+
automatically (a `<style>` tag added to `<head>` on first mount; no CSS
|
|
47
|
+
import, so the package also loads under SSR/Node/Jest). Props mirror `@ganttloom/gantt-core`'s `GanttOptions`
|
|
48
|
+
one-to-one (in kebab-case, per Vue convention) — see that package's README
|
|
49
|
+
for what each one does. Pass new array/object references to trigger a sync
|
|
50
|
+
(`setTasks`/`setDependencies`/`setOptions` underneath); mutating in place
|
|
51
|
+
won't re-render.
|
|
52
|
+
|
|
53
|
+
`class`/`style` fall through onto the chart's root element automatically —
|
|
54
|
+
there's no separate prop for them.
|
|
55
|
+
|
|
56
|
+
## Imperative handle
|
|
57
|
+
|
|
58
|
+
Props/events cover the declarative surface (tasks, dependencies, view
|
|
59
|
+
options, event listeners). For everything else, use a template ref:
|
|
60
|
+
|
|
61
|
+
```vue
|
|
62
|
+
<script setup lang="ts">
|
|
63
|
+
import { ref } from "vue";
|
|
64
|
+
import { GanttChart, type GanttChartHandle } from "@ganttloom/gantt-vue";
|
|
65
|
+
|
|
66
|
+
const chartRef = ref<GanttChartHandle | null>(null);
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<template>
|
|
70
|
+
<button @click="chartRef?.undo()">Undo</button>
|
|
71
|
+
<button @click="chartRef?.zoomIn()">Zoom in</button>
|
|
72
|
+
<GanttChart ref="chartRef" :tasks="tasks" enable-history />
|
|
73
|
+
</template>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The exposed `GanttChartHandle` has: `undo`/`redo`/`canUndo`/`canRedo`,
|
|
77
|
+
`expandAll`/`collapseAll`/`toggleGroup`, `removeDependency`,
|
|
78
|
+
`updateDependency`, `updateTask`, `setViewMode`,
|
|
79
|
+
`zoomIn`/`zoomOut`/`getZoomLevel`, `fitToViewport`, `scrollToToday`,
|
|
80
|
+
`scrollToRangeStart`/`scrollToRangeEnd`, `getPageCount`/`setPage`,
|
|
81
|
+
`getResourceHistogram`, `getLeveledTasks`,
|
|
82
|
+
`selectTask`/`selectAllVisible`/`clearSelection`/`getSelectedTaskIds`/`bulkShiftDates`/`bulkDelete`,
|
|
83
|
+
`setSort`/`getSort`, `toSVGString`/`toPNGDataURL`/`toPNGDataURLs`,
|
|
84
|
+
`getRenderModel`, and `getInstance()` for direct access to the underlying
|
|
85
|
+
`@ganttloom/gantt-core` `GanttChart` when you need something not on this
|
|
86
|
+
list.
|
|
87
|
+
|
|
88
|
+
## Multi-select and bulk actions
|
|
89
|
+
|
|
90
|
+
```vue
|
|
91
|
+
<GanttChart
|
|
92
|
+
:tasks="tasks"
|
|
93
|
+
selectable
|
|
94
|
+
@selection-change="(taskIds) => (selectedCount = taskIds.length)"
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Click selects one bar, ctrl/cmd-click toggles it into the selection,
|
|
99
|
+
shift-click selects a range. Then drive it from the handle:
|
|
100
|
+
`chartRef.value?.bulkShiftDates(dayMs)`, `chartRef.value?.bulkDelete()`.
|
|
101
|
+
|
|
102
|
+
## Row reorder and dependency editing
|
|
103
|
+
|
|
104
|
+
```vue
|
|
105
|
+
<GanttChart
|
|
106
|
+
:tasks="tasks"
|
|
107
|
+
@task-reorder="(draggedId, targetId, position) => { /* position: before | after | inside */ }"
|
|
108
|
+
@dependency-dblclick="(dep) => {
|
|
109
|
+
// show your own 'edit link' UI, then:
|
|
110
|
+
chartRef?.updateDependency(dep.fromId, dep.toId, { type: 'SS' });
|
|
111
|
+
}"
|
|
112
|
+
/>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Both off by default (same construction-time-only wiring caveat as
|
|
116
|
+
`task-create` below — attach the listener from the first render).
|
|
117
|
+
Double-clicking a task bar itself fires the separate
|
|
118
|
+
`@task-dblclick="(task) => ..."`, same caveat.
|
|
119
|
+
|
|
120
|
+
## Filling the viewport at any zoom level
|
|
121
|
+
|
|
122
|
+
A short project at month/year zoom, or just a handful of tasks, naturally
|
|
123
|
+
renders narrow. `auto-fit-to-viewport` stretches (never shrinks) the
|
|
124
|
+
timeline to fill the container instead of leaving empty space:
|
|
125
|
+
|
|
126
|
+
```vue
|
|
127
|
+
<GanttChart :tasks="tasks" auto-fit-to-viewport />
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Off by default — upgrading doesn't silently change an existing chart's
|
|
131
|
+
pixel layout for anyone who hasn't opted in.
|
|
132
|
+
|
|
133
|
+
Always on, separately: the visible range is padded to a minimum tick count
|
|
134
|
+
per zoom level, so a short project at year/quarter zoom never collapses
|
|
135
|
+
into one or two lonely columns, and the date header stays pinned to the
|
|
136
|
+
top while scrolling a tall task list vertically (still scrolling
|
|
137
|
+
horizontally in sync with the bars).
|
|
138
|
+
|
|
139
|
+
## Header position
|
|
140
|
+
|
|
141
|
+
`header-position` places the grid and timeline headers at `"top"` (the
|
|
142
|
+
default), `"bottom"`, or `"none"` to hide them entirely:
|
|
143
|
+
|
|
144
|
+
```vue
|
|
145
|
+
<GanttChart :tasks="tasks" header-position="bottom" />
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Drag-to-create
|
|
149
|
+
|
|
150
|
+
Off by default; attach a `@task-create` listener to turn it on (the core
|
|
151
|
+
only wires the drag listener when a creation handler is present, so existing
|
|
152
|
+
consumers see no behavior change):
|
|
153
|
+
|
|
154
|
+
```vue
|
|
155
|
+
<GanttChart :tasks="tasks" @task-create="(task) => console.log('created', task)" />
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
> The core only checks for a listener **at mount** (same caveat as the other
|
|
159
|
+
> gated events above) — attach it from the first render if you want
|
|
160
|
+
> drag-to-create enabled; toggling it on after mount won't retroactively wire it.
|
|
161
|
+
|
|
162
|
+
## Timeline markers, column resize/reorder, and search
|
|
163
|
+
|
|
164
|
+
```vue
|
|
165
|
+
<script setup lang="ts">
|
|
166
|
+
import { filterTasks } from "@ganttloom/gantt-vue";
|
|
167
|
+
</script>
|
|
168
|
+
|
|
169
|
+
<template>
|
|
170
|
+
<GanttChart
|
|
171
|
+
:tasks="filterTasks(tasks, (t) => t.name.includes(query)).tasks"
|
|
172
|
+
:markers="[{ date: releaseDate, label: 'Release' }]"
|
|
173
|
+
@column-resize="(id, width) => (columnWidths[id] = width)"
|
|
174
|
+
@column-reorder="(order) => (columnOrder = order)"
|
|
175
|
+
/>
|
|
176
|
+
</template>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
See `@ganttloom/gantt-core`'s README for the full behavior of each — the
|
|
180
|
+
props/events here are 1:1 mirrors of its `GanttOptions` fields, and
|
|
181
|
+
`filterTasks` is a pure re-export.
|
|
182
|
+
|
|
183
|
+
## Column sorting
|
|
184
|
+
|
|
185
|
+
Mark a column `sortable`; clicking its header cycles asc → desc → none:
|
|
186
|
+
|
|
187
|
+
```vue
|
|
188
|
+
<GanttChart
|
|
189
|
+
:tasks="tasks"
|
|
190
|
+
:columns="[{ id: 'name', title: 'Task', sortable: true }]"
|
|
191
|
+
@sort-change="(sort) => console.log(sort)"
|
|
192
|
+
/>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`chartRef.value?.setSort(columnId, direction)` / `.getSort()` are also
|
|
196
|
+
available on the handle.
|
|
197
|
+
|
|
198
|
+
## Resource leveling and multi-page PNG export
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
const { tasks: leveled, shifted } = chartRef.value!.getLeveledTasks();
|
|
202
|
+
if (shifted.length) tasks.value = leveled; // if you're holding tasks in your own state
|
|
203
|
+
|
|
204
|
+
const pages = await chartRef.value!.toPNGDataURLs({ pageWidthPx: 1600, pageHeightPx: 1200 });
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Resource histogram
|
|
208
|
+
|
|
209
|
+
```vue
|
|
210
|
+
<script setup lang="ts">
|
|
211
|
+
import { computed } from "vue";
|
|
212
|
+
import { renderResourceHistogramSVG } from "@ganttloom/gantt-vue";
|
|
213
|
+
|
|
214
|
+
const histogramSvg = computed(() => renderResourceHistogramSVG(chartRef.value?.getResourceHistogram() ?? []));
|
|
215
|
+
</script>
|
|
216
|
+
|
|
217
|
+
<template>
|
|
218
|
+
<div v-html="histogramSvg" />
|
|
219
|
+
</template>
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Hover tooltips
|
|
223
|
+
|
|
224
|
+
On by default (name/dates/progress/assignees/notes), styled via plain CSS
|
|
225
|
+
(`.gantt-tooltip`, see `gantt-core`'s README) — not the browser's native
|
|
226
|
+
tooltip. `renderTooltip` fully replaces the content:
|
|
227
|
+
|
|
228
|
+
```vue
|
|
229
|
+
<GanttChart
|
|
230
|
+
:tasks="tasks"
|
|
231
|
+
:render-tooltip="(task) => (task.isMilestone ? null : `${task.name} — ${task.progress ?? 0}%`)"
|
|
232
|
+
/>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Set `:show-tooltip="false"` to disable it entirely.
|
|
236
|
+
|
|
237
|
+
## i18n / overridable labels
|
|
238
|
+
|
|
239
|
+
A handful of built-in strings (aria-labels, the default name for a
|
|
240
|
+
newly-created task, the tooltip's date/progress formatting) can be overridden
|
|
241
|
+
via the `labels` prop — see `gantt-core`'s README for the full field list:
|
|
242
|
+
|
|
243
|
+
```vue
|
|
244
|
+
<GanttChart
|
|
245
|
+
:tasks="tasks"
|
|
246
|
+
:labels="{
|
|
247
|
+
expand: 'Développer',
|
|
248
|
+
collapse: 'Réduire',
|
|
249
|
+
newTaskName: 'Nouvelle tâche',
|
|
250
|
+
formatDate: (d) => d.toLocaleDateString('fr-FR'),
|
|
251
|
+
}"
|
|
252
|
+
/>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`labels.formatTickLabel` overrides the timeline header's own day/week/month
|
|
256
|
+
tick labels (distinct from `formatDate` above, which only covers the tooltip
|
|
257
|
+
and bar aria-label) - it receives each tick's `Date` and the current
|
|
258
|
+
`view-mode`:
|
|
259
|
+
|
|
260
|
+
```vue
|
|
261
|
+
<GanttChart
|
|
262
|
+
:tasks="tasks"
|
|
263
|
+
view-mode="week"
|
|
264
|
+
:labels="{ formatTickLabel: (date, viewMode) => date.toLocaleDateString('fr-FR') }"
|
|
265
|
+
/>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Custom sort comparator
|
|
269
|
+
|
|
270
|
+
Set `sortFn` on a sortable `GanttColumn` to fully control its comparison
|
|
271
|
+
instead of the default accessor-based one - see `gantt-core`'s README for
|
|
272
|
+
details:
|
|
273
|
+
|
|
274
|
+
```vue
|
|
275
|
+
<GanttChart
|
|
276
|
+
:tasks="tasks"
|
|
277
|
+
:columns="[
|
|
278
|
+
{
|
|
279
|
+
id: 'priority',
|
|
280
|
+
title: 'Priority',
|
|
281
|
+
accessor: (t) => t.data?.priority,
|
|
282
|
+
sortable: true,
|
|
283
|
+
sortFn: (a, b) => (a.data?.priority === 'high' ? -1 : 1),
|
|
284
|
+
},
|
|
285
|
+
]"
|
|
286
|
+
/>
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Custom task-bar rendering
|
|
290
|
+
|
|
291
|
+
`render-bar` lets you draw custom SVG content into a task bar (icons,
|
|
292
|
+
patterns, extra shapes) without forking the renderer - see `gantt-core`'s
|
|
293
|
+
README for the append-only contract that keeps drag/resize hit-testing intact:
|
|
294
|
+
|
|
295
|
+
```vue
|
|
296
|
+
<GanttChart
|
|
297
|
+
:tasks="tasks"
|
|
298
|
+
:render-bar="
|
|
299
|
+
({ task, bar, group }) => {
|
|
300
|
+
if (!task.data?.flagged) return;
|
|
301
|
+
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
302
|
+
icon.setAttribute('x', String(bar.width + 6));
|
|
303
|
+
icon.setAttribute('y', '-4');
|
|
304
|
+
icon.textContent = '⚑';
|
|
305
|
+
group.appendChild(icon);
|
|
306
|
+
}
|
|
307
|
+
"
|
|
308
|
+
/>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Arbitrary attributes on the root element
|
|
312
|
+
|
|
313
|
+
`<GanttChart>` has a single root `<div>`, so any attribute you pass that
|
|
314
|
+
isn't a declared prop - `id`, `data-*`, `aria-*`, `class`, `style`, etc. -
|
|
315
|
+
falls through onto it automatically (Vue's default [attribute
|
|
316
|
+
inheritance](https://vuejs.org/guide/components/attrs.html)), no extra prop
|
|
317
|
+
needed:
|
|
318
|
+
|
|
319
|
+
```vue
|
|
320
|
+
<GanttChart :tasks="tasks" data-testid="project-gantt" aria-label="Project schedule" />
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## `useGanttChart` (lower-level composable)
|
|
324
|
+
|
|
325
|
+
If you want to own the container element/DOM tree yourself instead of using
|
|
326
|
+
`<GanttChart />`:
|
|
327
|
+
|
|
328
|
+
```vue
|
|
329
|
+
<script setup lang="ts">
|
|
330
|
+
import { ref } from "vue";
|
|
331
|
+
import { useGanttChart, type GanttTask, type GanttDependency } from "@ganttloom/gantt-vue";
|
|
332
|
+
|
|
333
|
+
const props = defineProps<{ tasks: GanttTask[]; dependencies: GanttDependency[] }>();
|
|
334
|
+
const containerRef = ref<HTMLDivElement | null>(null);
|
|
335
|
+
const { chart, canUndo, canRedo } = useGanttChart(
|
|
336
|
+
containerRef,
|
|
337
|
+
() => props.tasks,
|
|
338
|
+
() => props.dependencies,
|
|
339
|
+
() => ({ enableHistory: true })
|
|
340
|
+
);
|
|
341
|
+
</script>
|
|
342
|
+
|
|
343
|
+
<template>
|
|
344
|
+
<div ref="containerRef" />
|
|
345
|
+
</template>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
`useGanttChart` takes getter functions (not refs) for `tasks`/`dependencies`/`options`
|
|
349
|
+
so it can track whatever reactive source each one reads — a prop, a `ref`, a `computed`.
|
|
350
|
+
|
|
351
|
+
## Re-exported types & helpers
|
|
352
|
+
|
|
353
|
+
For convenience, this package re-exports every `@ganttloom/gantt-core` type
|
|
354
|
+
(`GanttTask`, `GanttDependency`, `GanttColumn`, `GanttTheme`, `ViewMode`,
|
|
355
|
+
`WorkingCalendar`, `ConstraintType`, `ResourceLoadBucket`, etc.) and the
|
|
356
|
+
standalone helper functions (`computeWBSCodes`, `applyConstraint`,
|
|
357
|
+
`isWorkingDay`, `nextWorkingDay`, `computeResourceHistogram`,
|
|
358
|
+
`renderResourceHistogramSVG`, ...) so you never need
|
|
359
|
+
`@ganttloom/gantt-core` as a direct dependency just to reference them.
|