跳到主要内容

Vue Gantt Customization

This guide covers the main customization layers in the Vue wrapper and when to use each one. Use it after you can render the wrapper and need application-specific UI or behavior.

Use this page with:

Choose The Right Customization Layer

Start with the lowest-cost option that solves the task:

  • Use config and templates for visual output and built-in behavior changes
  • Use events and @ready for interaction rules and startup orchestration
  • Use modals for delete-confirmation control
  • Use customLightbox and inlineEditors when edit UI must be application-specific
  • Use templateWrapper when you need a shared wrapper around VNodes returned by templates

Keep one data ownership model while customizing UI. If the chart edits data, make sure your Vue state strategy matches it.

Template-Driven Visual Customization

Use templates for task text, CSS classes, scale labels, and grid output.

<script setup lang="ts">
const templates = {
task_text: (_start, _end, task) => `#${task.id}: ${task.text}`,
task_class: (_start, _end, task) => (task.priority === "high" ? "task--high" : "")
};
</script>

<template>
<VueGantt :tasks="tasks" :links="links" :templates="templates" />
</template>

Use this when your changes map cleanly to native Gantt template APIs.

Replace The Task Form (customLightbox)

Use customLightbox when the built-in lightbox is not enough and you need a Vue component for task editing.

<script setup lang="ts">
import CustomLightbox from "./CustomLightbox.vue";

const data = {
batchSave: changes => {
console.log(changes);
}
};
</script>

<template>
<VueGantt :tasks="tasks" :links="links" :customLightbox="CustomLightbox" :data="data" />
</template>

Your custom component receives:

  • data
  • onSave(updatedTask)
  • onCancel()
  • onDelete()
  • ganttInstance
CustomLightbox.vue
<script setup lang="ts">
import { ref, watch } from "vue";

interface CustomLightboxProps {
data?: any;
onSave?: (updatedTask: any) => void;
onCancel?: () => void;
onDelete?: () => void;
}

const props = defineProps<CustomLightboxProps>();

const text = ref("");

watch(
() => props.data,
nextValue => {
text.value = String(nextValue?.text ?? "");
},
{ immediate: true }
);

const save = () => {
props.onSave?.({ ...props.data, text: text.value });
};
</script>

<template>
<div className="lightbox-backdrop" data-cy="custom-lightbox">
<div className="lightbox-panel" role="dialog" aria-modal="true">
<h3>Edit Task</h3>
<label className="field">
<span>Task Text</span>
<input data-cy="custom-lightbox-text" type="text" v-model="text" />
</label>
<div className="actions">
<button data-cy="custom-lightbox-save" type="button" @click="save">Save</button>
<button data-cy="custom-lightbox-cancel" type="button" @click="props.onCancel?.()">Cancel</button>
<button data-cy="custom-lightbox-delete" type="button" @click="props.onDelete?.()">Delete</button>
</div>
</div>
</div>
</template>

<style scoped>
.lightbox-backdrop {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(16, 27, 38, 0.45);
z-index: 1000;
}

.lightbox-panel {
width: min(420px, calc(100vw - 24px));
background: #fff;
border: 1px solid #cfd8e2;
border-radius: 8px;
padding: 14px;
}

h3 {
margin: 0 0 12px;
}

.field {
display: block;
}

.field span {
display: inline-block;
font-size: 12px;
margin-bottom: 4px;
color: #455a6f;
}

.field input {
width: 100%;
border: 1px solid #cfd8e2;
padding: 8px;
font-size: 14px;
}

.actions {
margin-top: 12px;
display: flex;
gap: 8px;
justify-content: flex-end;
}

.actions button {
border: 1px solid #cfd8e2;
background: #fff;
padding: 6px 10px;
cursor: pointer;
}

.actions button:hover {
border-color: #91abd0;
}
</style>

Custom Grid Inline Editors

Use inlineEditors when users need custom grid cell editors.

<script setup lang="ts">
import TextEditor from "./editors/TextEditor.vue";

const config = {
columns: [
{
name: "text",
tree: true,
width: 220,
editor: { type: "TextEditor", map_to: "text" }
}
]
};

const inlineEditors = {
TextEditor
};
</script>

<template>
<VueGantt
:tasks="tasks"
:links="links"
:config="config"
:inlineEditors="inlineEditors"
:data="data" />
</template>
TextEditor.vue
<script setup lang="ts">
import { nextTick, onMounted, ref, watch } from "vue";

interface TextEditorProps {
initialValue: any;
save: () => void;
cancel: () => void;
}

const props = defineProps<TextEditorProps>();

const inputRef = ref<any>(null);
const value = ref("");

const normalize = (nextValue: any) => String(nextValue ?? "");

watch(
() => props.initialValue,
nextValue => {
value.value = normalize(nextValue);
},
{ immediate: true }
);

const getInputElement = () => {
const candidate = inputRef.value?.$el ?? inputRef.value;
if (!candidate || typeof candidate.querySelector !== "function") {
return null;
}
return candidate.querySelector("input") as HTMLInputElement | null;
};

const focus = () => {
inputRef.value?.focus?.();
getInputElement()?.focus();
};

const setValue = (nextValue: any) => {
value.value = normalize(nextValue);
};

const getValue = () => value.value;
const isChanged = (rawValue: any) => normalize(rawValue) !== value.value;
const isValid = () => value.value.trim().length > 0;
const save = () => props.save();

onMounted(async () => {
await nextTick();
focus();
});

defineExpose({
focus,
setValue,
getValue,
isChanged,
isValid,
save
});
</script>

<template>
<v-sheet className="editor-shell" border elevation="2" rounded="lg">
<v-text-field
ref="inputRef"
v-model="value"
className="editor-field editor-field-text"
density="compact"
hide-details
type="text"
variant="outlined"
/>
</v-sheet>
</template>

<style scoped>
.editor-shell {
display: flex;
align-items: center;
gap: 8px;
background: #fff;
}

.editor-field {
flex: 0 0 auto;
}

.editor-field-text {
width: 100%;
min-width: 100%;
}

</style>

Use this when grid editing is central to the workflow and built-in editors are not enough.

Custom Delete Confirmation Modals

Use modals to replace built-in task/link deletion confirmations.

const modals = {
onBeforeTaskDelete: ({ task, callback }) => {
if (window.confirm(`Delete task ${task.text}?`)) callback();
},
onBeforeLinkDelete: ({ link, callback }) => {
if (window.confirm(`Delete link ${link.id}?`)) callback();
}
};

Use this when your app needs a branded confirmation flow or extra business checks before deletion.

Orchestrate Behavior With events + @ready

Use events for interaction logic and @ready for one-time setup that needs instance.

<script setup lang="ts">
import { defineGanttEvents, type GanttStatic } from "@dhtmlx/trial-vue-gantt";

const events = defineGanttEvents({
onBeforeLightbox: taskId => {
console.log("Open custom editor route", taskId);
return false;
}
});

const onReady = (instance: GanttStatic) => {
instance.showDate(new Date());
};
</script>

<template>
<VueGantt :events="events" @ready="onReady" />
</template>

Continue With

Need help?
Got a question about the documentation? Reach out to our technical support team for help and guidance. For custom component solutions, visit the Services page.