nuxt-ui-elements-pro 0.1.10 → 0.1.11
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/dist/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +240 -1
- package/dist/runtime/components/EventCalendar.vue +2 -2
- package/dist/runtime/components/FeedbackWidget.d.vue.ts +118 -0
- package/dist/runtime/components/FeedbackWidget.vue +141 -0
- package/dist/runtime/components/FeedbackWidget.vue.d.ts +118 -0
- package/dist/runtime/components/GanttChart.d.vue.ts +138 -0
- package/dist/runtime/components/GanttChart.vue +206 -0
- package/dist/runtime/components/GanttChart.vue.d.ts +138 -0
- package/dist/runtime/components/event-calendar/EventCalendarTimeGrid.vue +23 -3
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.d.vue.ts +22 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue +27 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue.d.ts +22 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.d.vue.ts +42 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue +288 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue.d.ts +42 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.d.vue.ts +16 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue +70 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue.d.ts +16 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.d.vue.ts +41 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.vue +56 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.vue.d.ts +41 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.d.vue.ts +34 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue +193 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue.d.ts +34 -0
- package/dist/runtime/composables/useEventCalendar.d.ts +2 -0
- package/dist/runtime/composables/useEventCalendar.js +8 -6
- package/dist/runtime/composables/useEventCalendarContext.d.ts +2 -7
- package/dist/runtime/composables/useEventCalendarContext.js +4 -11
- package/dist/runtime/composables/useFeedbackWidget.d.ts +46 -0
- package/dist/runtime/composables/useFeedbackWidget.js +137 -0
- package/dist/runtime/composables/useFeedbackWidgetContext.d.ts +3 -0
- package/dist/runtime/composables/useFeedbackWidgetContext.js +4 -0
- package/dist/runtime/composables/useGanttChart.d.ts +52 -0
- package/dist/runtime/composables/useGanttChart.js +224 -0
- package/dist/runtime/composables/useGanttChartContext.d.ts +3 -0
- package/dist/runtime/composables/useGanttChartContext.js +4 -0
- package/dist/runtime/composables/useGanttChartDragDrop.d.ts +28 -0
- package/dist/runtime/composables/useGanttChartDragDrop.js +68 -0
- package/dist/runtime/composables/useGanttChartKeyboard.d.ts +14 -0
- package/dist/runtime/composables/useGanttChartKeyboard.js +92 -0
- package/dist/runtime/composables/useGanttChartResize.d.ts +26 -0
- package/dist/runtime/composables/useGanttChartResize.js +89 -0
- package/dist/runtime/composables/useOrgChartContext.d.ts +2 -7
- package/dist/runtime/composables/useOrgChartContext.js +4 -11
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/server/api/_feedback.post.d.ts +3 -0
- package/dist/runtime/server/api/_feedback.post.js +115 -0
- package/dist/runtime/server/nodemailer.d.ts +29 -0
- package/dist/runtime/server/utils/feedback-captcha.d.ts +1 -0
- package/dist/runtime/server/utils/feedback-captcha.js +27 -0
- package/dist/runtime/server/utils/feedback-rate-limit.d.ts +4 -0
- package/dist/runtime/server/utils/feedback-rate-limit.js +26 -0
- package/dist/runtime/types/event-calendar.d.ts +10 -4
- package/dist/runtime/types/feedback-widget.d.ts +110 -0
- package/dist/runtime/types/feedback-widget.js +0 -0
- package/dist/runtime/types/gantt-chart.d.ts +223 -0
- package/dist/runtime/types/gantt-chart.js +0 -0
- package/dist/runtime/types/index.d.ts +4 -0
- package/dist/runtime/types/index.js +4 -0
- package/dist/runtime/utils/createComponentContext.d.ts +15 -0
- package/dist/runtime/utils/createComponentContext.js +17 -0
- package/dist/runtime/utils/date.d.ts +10 -0
- package/dist/runtime/utils/date.js +9 -0
- package/dist/runtime/utils/event-calendar.d.ts +1 -9
- package/dist/runtime/utils/event-calendar.js +2 -9
- package/dist/runtime/utils/gantt-chart.d.ts +85 -0
- package/dist/runtime/utils/gantt-chart.js +549 -0
- package/dist/runtime/utils/recurrence.js +2 -1
- package/package.json +17 -8
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { today, getLocalTimeZone } from "@internationalized/date";
|
|
2
|
+
import { asCalendarDate } from "#std/date";
|
|
3
|
+
import { computed, ref, watch } from "vue";
|
|
4
|
+
import {
|
|
5
|
+
computeGanttLayout,
|
|
6
|
+
buildTaskTree,
|
|
7
|
+
flattenVisibleTasks,
|
|
8
|
+
computeTimelineBounds,
|
|
9
|
+
getAllGroupIds,
|
|
10
|
+
dateToPixel as dateToPixelUtil,
|
|
11
|
+
pixelToDate as pixelToDateUtil,
|
|
12
|
+
DEFAULT_ZOOM_PRESETS
|
|
13
|
+
} from "../utils/gantt-chart.js";
|
|
14
|
+
export function useGanttChart(options) {
|
|
15
|
+
const displayDate = computed(() => {
|
|
16
|
+
const mv = options.modelValue();
|
|
17
|
+
if (mv) return asCalendarDate(mv);
|
|
18
|
+
return today(getLocalTimeZone());
|
|
19
|
+
});
|
|
20
|
+
const zoomLevel = computed(() => options.zoomLevel());
|
|
21
|
+
const zoomLevels = computed(() => options.zoomLevels());
|
|
22
|
+
const rowHeight = computed(() => options.rowHeight());
|
|
23
|
+
const normalizedTasks = computed(() => {
|
|
24
|
+
return options.tasks().map((task) => ({
|
|
25
|
+
id: task.id,
|
|
26
|
+
title: task.title,
|
|
27
|
+
start: asCalendarDate(task.start),
|
|
28
|
+
end: asCalendarDate(task.end),
|
|
29
|
+
parentId: task.parentId ?? null,
|
|
30
|
+
color: task.color ?? "primary",
|
|
31
|
+
progress: Math.max(0, Math.min(100, task.progress ?? 0)),
|
|
32
|
+
milestone: task.milestone ?? false,
|
|
33
|
+
draggable: task.draggable ?? true,
|
|
34
|
+
resizable: task.resizable ?? true,
|
|
35
|
+
metadata: task.metadata ?? {},
|
|
36
|
+
original: task
|
|
37
|
+
}));
|
|
38
|
+
});
|
|
39
|
+
const normalizedDependencies = computed(() => {
|
|
40
|
+
return options.dependencies().map((dep) => ({
|
|
41
|
+
...dep,
|
|
42
|
+
type: dep.type ?? "finish-to-start"
|
|
43
|
+
}));
|
|
44
|
+
});
|
|
45
|
+
const taskTree = computed(() => {
|
|
46
|
+
return buildTaskTree(normalizedTasks.value);
|
|
47
|
+
});
|
|
48
|
+
const expandedIds = ref(/* @__PURE__ */ new Set());
|
|
49
|
+
let initialized = false;
|
|
50
|
+
watch(taskTree, (tree) => {
|
|
51
|
+
if (!initialized && tree.length > 0) {
|
|
52
|
+
const propsExpanded = options.expanded();
|
|
53
|
+
if (propsExpanded) {
|
|
54
|
+
expandedIds.value = new Set(propsExpanded);
|
|
55
|
+
} else {
|
|
56
|
+
expandedIds.value = getAllGroupIds(tree);
|
|
57
|
+
}
|
|
58
|
+
initialized = true;
|
|
59
|
+
}
|
|
60
|
+
}, { immediate: true });
|
|
61
|
+
watch(() => options.expanded(), (ids) => {
|
|
62
|
+
if (ids) {
|
|
63
|
+
expandedIds.value = new Set(ids);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
function toggleExpand(id) {
|
|
67
|
+
const newSet = new Set(expandedIds.value);
|
|
68
|
+
if (newSet.has(id)) {
|
|
69
|
+
newSet.delete(id);
|
|
70
|
+
} else {
|
|
71
|
+
newSet.add(id);
|
|
72
|
+
}
|
|
73
|
+
expandedIds.value = newSet;
|
|
74
|
+
options.onUpdateExpanded(Array.from(newSet));
|
|
75
|
+
}
|
|
76
|
+
function expandAll() {
|
|
77
|
+
expandedIds.value = getAllGroupIds(taskTree.value);
|
|
78
|
+
options.onUpdateExpanded(Array.from(expandedIds.value));
|
|
79
|
+
}
|
|
80
|
+
function collapseAll() {
|
|
81
|
+
expandedIds.value = /* @__PURE__ */ new Set();
|
|
82
|
+
options.onUpdateExpanded([]);
|
|
83
|
+
}
|
|
84
|
+
function isExpanded(id) {
|
|
85
|
+
return expandedIds.value.has(id);
|
|
86
|
+
}
|
|
87
|
+
const flatVisible = computed(() => {
|
|
88
|
+
return flattenVisibleTasks(taskTree.value, expandedIds.value);
|
|
89
|
+
});
|
|
90
|
+
const timelineBounds = computed(() => {
|
|
91
|
+
return computeTimelineBounds(normalizedTasks.value, zoomLevel.value);
|
|
92
|
+
});
|
|
93
|
+
const timelineStart = computed(() => timelineBounds.value.start);
|
|
94
|
+
const timelineEnd = computed(() => timelineBounds.value.end);
|
|
95
|
+
const unitWidth = computed(() => {
|
|
96
|
+
return DEFAULT_ZOOM_PRESETS[zoomLevel.value].unitWidth;
|
|
97
|
+
});
|
|
98
|
+
const layout = computed(() => {
|
|
99
|
+
return computeGanttLayout(
|
|
100
|
+
normalizedTasks.value,
|
|
101
|
+
normalizedDependencies.value,
|
|
102
|
+
expandedIds.value,
|
|
103
|
+
zoomLevel.value,
|
|
104
|
+
unitWidth.value,
|
|
105
|
+
rowHeight.value,
|
|
106
|
+
options.locale()
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
const headerTitle = computed(() => {
|
|
110
|
+
const start = timelineStart.value;
|
|
111
|
+
const end = timelineEnd.value;
|
|
112
|
+
const locale = options.locale();
|
|
113
|
+
const startDate = new Date(start.year, start.month - 1, start.day);
|
|
114
|
+
const endDate = new Date(end.year, end.month - 1, end.day);
|
|
115
|
+
if (start.year === end.year && start.month === end.month) {
|
|
116
|
+
return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(startDate);
|
|
117
|
+
}
|
|
118
|
+
if (start.year === end.year) {
|
|
119
|
+
const startStr2 = new Intl.DateTimeFormat(locale, { month: "short" }).format(startDate);
|
|
120
|
+
const endStr2 = new Intl.DateTimeFormat(locale, { month: "short", year: "numeric" }).format(endDate);
|
|
121
|
+
return `${startStr2} \u2013 ${endStr2}`;
|
|
122
|
+
}
|
|
123
|
+
const startStr = new Intl.DateTimeFormat(locale, { month: "short", year: "numeric" }).format(startDate);
|
|
124
|
+
const endStr = new Intl.DateTimeFormat(locale, { month: "short", year: "numeric" }).format(endDate);
|
|
125
|
+
return `${startStr} \u2013 ${endStr}`;
|
|
126
|
+
});
|
|
127
|
+
function goToPrev() {
|
|
128
|
+
const amount = getNavigationAmount(zoomLevel.value);
|
|
129
|
+
const newDate = displayDate.value.subtract(amount);
|
|
130
|
+
options.onUpdateModelValue(newDate);
|
|
131
|
+
}
|
|
132
|
+
function goToNext() {
|
|
133
|
+
const amount = getNavigationAmount(zoomLevel.value);
|
|
134
|
+
const newDate = displayDate.value.add(amount);
|
|
135
|
+
options.onUpdateModelValue(newDate);
|
|
136
|
+
}
|
|
137
|
+
function goToToday() {
|
|
138
|
+
options.onUpdateModelValue(today(getLocalTimeZone()));
|
|
139
|
+
}
|
|
140
|
+
function setZoomLevel(level) {
|
|
141
|
+
options.onUpdateZoomLevel(level);
|
|
142
|
+
}
|
|
143
|
+
const bodyEl = ref(null);
|
|
144
|
+
function scrollToDate(date) {
|
|
145
|
+
if (!bodyEl.value) return;
|
|
146
|
+
const px = dateToPixel(date);
|
|
147
|
+
bodyEl.value.scrollLeft = Math.max(0, px - bodyEl.value.clientWidth / 2);
|
|
148
|
+
}
|
|
149
|
+
function scrollToTask(taskId) {
|
|
150
|
+
if (!bodyEl.value) return;
|
|
151
|
+
const bars = layout.value.taskBars;
|
|
152
|
+
const bar = bars.find((b) => b.task.id === taskId);
|
|
153
|
+
if (!bar) return;
|
|
154
|
+
const rh = rowHeight.value;
|
|
155
|
+
const top = bar.rowIndex * rh;
|
|
156
|
+
const el = bodyEl.value;
|
|
157
|
+
if (top < el.scrollTop || top + rh > el.scrollTop + el.clientHeight) {
|
|
158
|
+
el.scrollTop = top - el.clientHeight / 2 + rh / 2;
|
|
159
|
+
}
|
|
160
|
+
if (bar.leftPx < el.scrollLeft || bar.leftPx + bar.widthPx > el.scrollLeft + el.clientWidth) {
|
|
161
|
+
el.scrollLeft = bar.leftPx - 40;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function dateToPixel(date) {
|
|
165
|
+
return dateToPixelUtil(date, timelineStart.value, zoomLevel.value, unitWidth.value);
|
|
166
|
+
}
|
|
167
|
+
function pixelToDate(px) {
|
|
168
|
+
return pixelToDateUtil(px, timelineStart.value, zoomLevel.value, unitWidth.value);
|
|
169
|
+
}
|
|
170
|
+
function getTaskStyle(task) {
|
|
171
|
+
const cssVar = task.color === "neutral" ? "var(--ui-bg-inverted)" : `var(--ui-${task.color})`;
|
|
172
|
+
return {
|
|
173
|
+
"--task-color": cssVar
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
displayDate,
|
|
178
|
+
zoomLevel,
|
|
179
|
+
zoomLevels,
|
|
180
|
+
rowHeight,
|
|
181
|
+
normalizedTasks,
|
|
182
|
+
normalizedDependencies,
|
|
183
|
+
taskTree,
|
|
184
|
+
expandedIds,
|
|
185
|
+
flatVisible,
|
|
186
|
+
timelineStart,
|
|
187
|
+
timelineEnd,
|
|
188
|
+
layout,
|
|
189
|
+
headerTitle,
|
|
190
|
+
unitWidth,
|
|
191
|
+
// Scroll container
|
|
192
|
+
bodyEl,
|
|
193
|
+
// Navigation
|
|
194
|
+
goToPrev,
|
|
195
|
+
goToNext,
|
|
196
|
+
goToToday,
|
|
197
|
+
setZoomLevel,
|
|
198
|
+
scrollToDate,
|
|
199
|
+
scrollToTask,
|
|
200
|
+
// Expand/collapse
|
|
201
|
+
toggleExpand,
|
|
202
|
+
expandAll,
|
|
203
|
+
collapseAll,
|
|
204
|
+
isExpanded,
|
|
205
|
+
// Helpers
|
|
206
|
+
dateToPixel,
|
|
207
|
+
pixelToDate,
|
|
208
|
+
getTaskStyle
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function getNavigationAmount(zoomLevel) {
|
|
212
|
+
switch (zoomLevel) {
|
|
213
|
+
case "day":
|
|
214
|
+
return { days: 1 };
|
|
215
|
+
case "week":
|
|
216
|
+
return { weeks: 1 };
|
|
217
|
+
case "month":
|
|
218
|
+
return { months: 1 };
|
|
219
|
+
case "quarter":
|
|
220
|
+
return { months: 3 };
|
|
221
|
+
case "year":
|
|
222
|
+
return { months: 12 };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CalendarDate } from "@internationalized/date";
|
|
2
|
+
import type { ComputedRef } from "vue";
|
|
3
|
+
import type { GanttTaskBar, GanttTaskDropPayload, GanttZoomLevel } from "../types/gantt-chart.js";
|
|
4
|
+
export interface UseGanttChartDragDropOptions {
|
|
5
|
+
editable: () => boolean;
|
|
6
|
+
layout: ComputedRef<{
|
|
7
|
+
taskBars: GanttTaskBar[];
|
|
8
|
+
}>;
|
|
9
|
+
zoomLevel: ComputedRef<GanttZoomLevel>;
|
|
10
|
+
unitWidth: ComputedRef<number>;
|
|
11
|
+
dateToPixel: (date: CalendarDate) => number;
|
|
12
|
+
pixelToDate: (px: number) => CalendarDate;
|
|
13
|
+
onTaskDrop: (payload: GanttTaskDropPayload) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function useGanttChartDragDrop(options: UseGanttChartDragDropOptions): {
|
|
16
|
+
draggedTaskId: import("vue").Ref<string | number | null, string | number | null>;
|
|
17
|
+
dragPreview: import("vue").Ref<{
|
|
18
|
+
leftPx: number;
|
|
19
|
+
widthPx: number;
|
|
20
|
+
} | null, {
|
|
21
|
+
leftPx: number;
|
|
22
|
+
widthPx: number;
|
|
23
|
+
} | {
|
|
24
|
+
leftPx: number;
|
|
25
|
+
widthPx: number;
|
|
26
|
+
} | null>;
|
|
27
|
+
onDragStart: (bar: GanttTaskBar, e: PointerEvent) => void;
|
|
28
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { onUnmounted, ref } from "vue";
|
|
2
|
+
import { snapToGrid } from "../utils/gantt-chart.js";
|
|
3
|
+
export function useGanttChartDragDrop(options) {
|
|
4
|
+
const { editable, dateToPixel, pixelToDate, onTaskDrop } = options;
|
|
5
|
+
const draggedTaskId = ref(null);
|
|
6
|
+
const dragPreview = ref(null);
|
|
7
|
+
let activeBar = null;
|
|
8
|
+
let pointerStartX = 0;
|
|
9
|
+
let originalLeftPx = 0;
|
|
10
|
+
function onPointerMove(e) {
|
|
11
|
+
if (!activeBar) return;
|
|
12
|
+
const deltaX = e.clientX - pointerStartX;
|
|
13
|
+
const rawLeft = originalLeftPx + deltaX;
|
|
14
|
+
const snappedLeft = snapToGrid(rawLeft, options.zoomLevel.value, options.unitWidth.value);
|
|
15
|
+
dragPreview.value = {
|
|
16
|
+
leftPx: snappedLeft,
|
|
17
|
+
widthPx: activeBar.widthPx
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function onPointerUp(_e) {
|
|
21
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
22
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
23
|
+
if (!activeBar || !dragPreview.value) {
|
|
24
|
+
cleanup();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (dragPreview.value.leftPx === originalLeftPx) {
|
|
28
|
+
cleanup();
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const newStart = pixelToDate(dragPreview.value.leftPx);
|
|
32
|
+
const newEnd = pixelToDate(dragPreview.value.leftPx + dragPreview.value.widthPx).subtract({ days: 1 });
|
|
33
|
+
onTaskDrop({
|
|
34
|
+
task: activeBar.task.original,
|
|
35
|
+
oldStart: activeBar.task.start,
|
|
36
|
+
oldEnd: activeBar.task.end,
|
|
37
|
+
newStart,
|
|
38
|
+
newEnd
|
|
39
|
+
});
|
|
40
|
+
cleanup();
|
|
41
|
+
}
|
|
42
|
+
function onDragStart(bar, e) {
|
|
43
|
+
if (!editable() || !bar.task.draggable || bar.isMilestone) return;
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
e.stopPropagation();
|
|
46
|
+
draggedTaskId.value = bar.task.id;
|
|
47
|
+
activeBar = bar;
|
|
48
|
+
pointerStartX = e.clientX;
|
|
49
|
+
originalLeftPx = bar.leftPx;
|
|
50
|
+
dragPreview.value = { leftPx: bar.leftPx, widthPx: bar.widthPx };
|
|
51
|
+
document.addEventListener("pointermove", onPointerMove);
|
|
52
|
+
document.addEventListener("pointerup", onPointerUp);
|
|
53
|
+
}
|
|
54
|
+
function cleanup() {
|
|
55
|
+
draggedTaskId.value = null;
|
|
56
|
+
dragPreview.value = null;
|
|
57
|
+
activeBar = null;
|
|
58
|
+
}
|
|
59
|
+
onUnmounted(() => {
|
|
60
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
61
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
draggedTaskId,
|
|
65
|
+
dragPreview,
|
|
66
|
+
onDragStart
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ComputedRef } from "vue";
|
|
2
|
+
import type { GanttTaskBar } from "../types/gantt-chart.js";
|
|
3
|
+
export interface UseGanttChartKeyboardOptions {
|
|
4
|
+
flatVisibleTasks: ComputedRef<GanttTaskBar[]>;
|
|
5
|
+
expandedIds: {
|
|
6
|
+
value: Set<string | number>;
|
|
7
|
+
};
|
|
8
|
+
toggleExpand: (id: string | number) => void;
|
|
9
|
+
scrollToTask: (id: string | number) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function useGanttChartKeyboard(options: UseGanttChartKeyboardOptions): {
|
|
12
|
+
focusedTaskId: import("vue").Ref<string | number | null, string | number | null>;
|
|
13
|
+
onKeydown: (e: KeyboardEvent) => void;
|
|
14
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
export function useGanttChartKeyboard(options) {
|
|
3
|
+
const { flatVisibleTasks, toggleExpand, scrollToTask } = options;
|
|
4
|
+
const focusedTaskId = ref(null);
|
|
5
|
+
function getCurrentIndex() {
|
|
6
|
+
if (focusedTaskId.value === null) return -1;
|
|
7
|
+
return flatVisibleTasks.value.findIndex((bar) => bar.task.id === focusedTaskId.value);
|
|
8
|
+
}
|
|
9
|
+
function focusTask(id) {
|
|
10
|
+
focusedTaskId.value = id;
|
|
11
|
+
scrollToTask(id);
|
|
12
|
+
}
|
|
13
|
+
function onKeydown(e) {
|
|
14
|
+
const bars = flatVisibleTasks.value;
|
|
15
|
+
if (bars.length === 0) return;
|
|
16
|
+
const currentIdx = getCurrentIndex();
|
|
17
|
+
switch (e.key) {
|
|
18
|
+
case "ArrowDown": {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
const nextIdx = currentIdx < bars.length - 1 ? currentIdx + 1 : 0;
|
|
21
|
+
focusTask(bars[nextIdx].task.id);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
case "ArrowUp": {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
const prevIdx = currentIdx > 0 ? currentIdx - 1 : bars.length - 1;
|
|
27
|
+
focusTask(bars[prevIdx].task.id);
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case "ArrowRight": {
|
|
31
|
+
e.preventDefault();
|
|
32
|
+
if (currentIdx >= 0) {
|
|
33
|
+
const bar = bars[currentIdx];
|
|
34
|
+
if (bar.isGroup && !bar.isExpanded) {
|
|
35
|
+
toggleExpand(bar.task.id);
|
|
36
|
+
} else if (bar.isGroup && bar.isExpanded && currentIdx < bars.length - 1) {
|
|
37
|
+
focusTask(bars[currentIdx + 1].task.id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case "ArrowLeft": {
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
if (currentIdx >= 0) {
|
|
45
|
+
const bar = bars[currentIdx];
|
|
46
|
+
if (bar.isGroup && bar.isExpanded) {
|
|
47
|
+
toggleExpand(bar.task.id);
|
|
48
|
+
} else if (bar.depth > 0) {
|
|
49
|
+
const parentBar = bars.find(
|
|
50
|
+
(b) => b.task.id === bar.task.parentId && b.isGroup
|
|
51
|
+
);
|
|
52
|
+
if (parentBar) {
|
|
53
|
+
focusTask(parentBar.task.id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
case "Enter":
|
|
60
|
+
case " ": {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
if (currentIdx >= 0) {
|
|
63
|
+
const bar = bars[currentIdx];
|
|
64
|
+
if (bar.isGroup) {
|
|
65
|
+
toggleExpand(bar.task.id);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "Home": {
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
if (bars.length > 0) {
|
|
73
|
+
focusTask(bars[0].task.id);
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "End": {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
if (bars.length > 0) {
|
|
80
|
+
focusTask(bars[bars.length - 1].task.id);
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
focusedTaskId,
|
|
90
|
+
onKeydown
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CalendarDate } from "@internationalized/date";
|
|
2
|
+
import type { ComputedRef } from "vue";
|
|
3
|
+
import type { GanttTaskBar, GanttTaskResizePayload, GanttZoomLevel } from "../types/gantt-chart.js";
|
|
4
|
+
export interface UseGanttChartResizeOptions {
|
|
5
|
+
editable: () => boolean;
|
|
6
|
+
zoomLevel: ComputedRef<GanttZoomLevel>;
|
|
7
|
+
unitWidth: ComputedRef<number>;
|
|
8
|
+
dateToPixel: (date: CalendarDate) => number;
|
|
9
|
+
pixelToDate: (px: number) => CalendarDate;
|
|
10
|
+
onTaskResize: (payload: GanttTaskResizePayload) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function useGanttChartResize(options: UseGanttChartResizeOptions): {
|
|
13
|
+
resizingTaskId: import("vue").Ref<string | number | null, string | number | null>;
|
|
14
|
+
resizeEdge: import("vue").Ref<"start" | "end" | null, "start" | "end" | null>;
|
|
15
|
+
resizePreview: import("vue").Ref<{
|
|
16
|
+
leftPx: number;
|
|
17
|
+
widthPx: number;
|
|
18
|
+
} | null, {
|
|
19
|
+
leftPx: number;
|
|
20
|
+
widthPx: number;
|
|
21
|
+
} | {
|
|
22
|
+
leftPx: number;
|
|
23
|
+
widthPx: number;
|
|
24
|
+
} | null>;
|
|
25
|
+
onResizePointerDown: (bar: GanttTaskBar, edge: "start" | "end", e: PointerEvent) => void;
|
|
26
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { onUnmounted, ref } from "vue";
|
|
2
|
+
import { snapToGrid } from "../utils/gantt-chart.js";
|
|
3
|
+
export function useGanttChartResize(options) {
|
|
4
|
+
const { editable, dateToPixel, pixelToDate, onTaskResize } = options;
|
|
5
|
+
const resizingTaskId = ref(null);
|
|
6
|
+
const resizeEdge = ref(null);
|
|
7
|
+
const resizePreview = ref(null);
|
|
8
|
+
let activeBar = null;
|
|
9
|
+
let pointerStartX = 0;
|
|
10
|
+
let originalLeftPx = 0;
|
|
11
|
+
let originalWidthPx = 0;
|
|
12
|
+
function onPointerMove(e) {
|
|
13
|
+
if (!activeBar) return;
|
|
14
|
+
const deltaX = e.clientX - pointerStartX;
|
|
15
|
+
const minWidth = options.unitWidth.value / 2;
|
|
16
|
+
if (resizeEdge.value === "end") {
|
|
17
|
+
const rawWidth = originalWidthPx + deltaX;
|
|
18
|
+
const snappedRight = snapToGrid(originalLeftPx + rawWidth, options.zoomLevel.value, options.unitWidth.value);
|
|
19
|
+
const newWidth = Math.max(minWidth, snappedRight - originalLeftPx);
|
|
20
|
+
resizePreview.value = {
|
|
21
|
+
leftPx: originalLeftPx,
|
|
22
|
+
widthPx: newWidth
|
|
23
|
+
};
|
|
24
|
+
} else {
|
|
25
|
+
const rawLeft = originalLeftPx + deltaX;
|
|
26
|
+
const snappedLeft = snapToGrid(rawLeft, options.zoomLevel.value, options.unitWidth.value);
|
|
27
|
+
const rightEdge = originalLeftPx + originalWidthPx;
|
|
28
|
+
const newWidth = Math.max(minWidth, rightEdge - snappedLeft);
|
|
29
|
+
resizePreview.value = {
|
|
30
|
+
leftPx: rightEdge - newWidth,
|
|
31
|
+
widthPx: newWidth
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function onPointerUp(_e) {
|
|
36
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
37
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
38
|
+
if (!activeBar || !resizePreview.value) {
|
|
39
|
+
cleanup();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (resizePreview.value.leftPx === originalLeftPx && resizePreview.value.widthPx === originalWidthPx) {
|
|
43
|
+
cleanup();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const edge = resizeEdge.value;
|
|
47
|
+
const newStart = pixelToDate(resizePreview.value.leftPx);
|
|
48
|
+
const newEnd = pixelToDate(resizePreview.value.leftPx + resizePreview.value.widthPx).subtract({ days: 1 });
|
|
49
|
+
onTaskResize({
|
|
50
|
+
task: activeBar.task.original,
|
|
51
|
+
oldStart: activeBar.task.start,
|
|
52
|
+
oldEnd: activeBar.task.end,
|
|
53
|
+
newStart,
|
|
54
|
+
newEnd,
|
|
55
|
+
edge
|
|
56
|
+
});
|
|
57
|
+
cleanup();
|
|
58
|
+
}
|
|
59
|
+
function onResizePointerDown(bar, edge, e) {
|
|
60
|
+
if (!editable() || !bar.task.resizable || bar.isMilestone) return;
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
e.stopPropagation();
|
|
63
|
+
resizingTaskId.value = bar.task.id;
|
|
64
|
+
resizeEdge.value = edge;
|
|
65
|
+
activeBar = bar;
|
|
66
|
+
pointerStartX = e.clientX;
|
|
67
|
+
originalLeftPx = bar.leftPx;
|
|
68
|
+
originalWidthPx = bar.widthPx;
|
|
69
|
+
resizePreview.value = { leftPx: bar.leftPx, widthPx: bar.widthPx };
|
|
70
|
+
document.addEventListener("pointermove", onPointerMove);
|
|
71
|
+
document.addEventListener("pointerup", onPointerUp);
|
|
72
|
+
}
|
|
73
|
+
function cleanup() {
|
|
74
|
+
resizingTaskId.value = null;
|
|
75
|
+
resizeEdge.value = null;
|
|
76
|
+
resizePreview.value = null;
|
|
77
|
+
activeBar = null;
|
|
78
|
+
}
|
|
79
|
+
onUnmounted(() => {
|
|
80
|
+
document.removeEventListener("pointermove", onPointerMove);
|
|
81
|
+
document.removeEventListener("pointerup", onPointerUp);
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
resizingTaskId,
|
|
85
|
+
resizeEdge,
|
|
86
|
+
resizePreview,
|
|
87
|
+
onResizePointerDown
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import type { InjectionKey } from "vue";
|
|
2
1
|
import type { OrgChartContext } from "../types/org-chart.js";
|
|
3
|
-
export declare const orgChartContextKey: InjectionKey<OrgChartContext>;
|
|
4
|
-
|
|
5
|
-
* Inject the OrgChart context in a sub-component.
|
|
6
|
-
* Throws if called outside an <UEOrgChart> root.
|
|
7
|
-
*/
|
|
8
|
-
export declare function useOrgChartContext(): OrgChartContext;
|
|
2
|
+
export declare const orgChartContextKey: import("vue").InjectionKey<OrgChartContext>;
|
|
3
|
+
export declare const useOrgChartContext: () => OrgChartContext;
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
if (!ctx) {
|
|
6
|
-
throw new Error(
|
|
7
|
-
"[OrgChart] useOrgChartContext() was called outside of an <UEOrgChart> root. Sub-components must be used as children of <UEOrgChart>."
|
|
8
|
-
);
|
|
9
|
-
}
|
|
10
|
-
return ctx;
|
|
11
|
-
}
|
|
1
|
+
import { createComponentContext } from "../utils/createComponentContext.js";
|
|
2
|
+
const { key, useContext } = createComponentContext("OrgChart");
|
|
3
|
+
export const orgChartContextKey = key;
|
|
4
|
+
export const useOrgChartContext = useContext;
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export * from "./types/index.js";
|
|
|
2
2
|
export { expandRecurringEvents, expandRecurringEventsAsync, getVisibleRange } from "./utils/recurrence.js";
|
|
3
3
|
export type { VisibleRange } from "./utils/recurrence.js";
|
|
4
4
|
export { buildTree, computeLayout, computeFullLayout, computeConnectionPaths, flattenVisibleTree, buildNodeMap, getAllNodeIds, getAncestors, getDescendants } from "./utils/org-chart.js";
|
|
5
|
+
export { computeGanttLayout, buildTaskTree, computeGroupSpans, flattenVisibleTasks, dateToPixel, pixelToDate, DEFAULT_ZOOM_PRESETS, DEFAULT_ROW_HEIGHT } from "./utils/gantt-chart.js";
|
package/dist/runtime/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export * from "./types/index.js";
|
|
2
2
|
export { expandRecurringEvents, expandRecurringEventsAsync, getVisibleRange } from "./utils/recurrence.js";
|
|
3
3
|
export { buildTree, computeLayout, computeFullLayout, computeConnectionPaths, flattenVisibleTree, buildNodeMap, getAllNodeIds, getAncestors, getDescendants } from "./utils/org-chart.js";
|
|
4
|
+
export { computeGanttLayout, buildTaskTree, computeGroupSpans, flattenVisibleTasks, dateToPixel, pixelToDate, DEFAULT_ZOOM_PRESETS, DEFAULT_ROW_HEIGHT } from "./utils/gantt-chart.js";
|