@affino/datagrid-gantt 0.1.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/LICENSE +21 -0
- package/dist/calendar.d.ts +16 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +109 -0
- package/dist/contracts.d.ts +222 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +784 -0
- package/dist/timeline.d.ts +16 -0
- package/dist/timeline.d.ts.map +1 -0
- package/dist/timeline.js +287 -0
- package/package.json +48 -0
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,784 @@
|
|
|
1
|
+
import { addDataGridWorkingDays, resolveDataGridWorkingCalendar, snapDataGridDateToWorkingDay, startOfUtcDay, startOfUtcWeek, } from "./calendar.js";
|
|
2
|
+
import { DAY_MS, clampDataGridTimelineScrollLeft, resolveDataGridTimelineDateToPixel, resolveDataGridTimelineRange, resolveDataGridTimelineScrollLeftForDate, } from "./timeline.js";
|
|
3
|
+
const DEFAULT_GANTT_OPTIONS = {
|
|
4
|
+
startKey: "start",
|
|
5
|
+
endKey: "end",
|
|
6
|
+
baselineStartKey: null,
|
|
7
|
+
baselineEndKey: null,
|
|
8
|
+
progressKey: "progress",
|
|
9
|
+
dependencyKey: "dependencies",
|
|
10
|
+
labelKey: "task",
|
|
11
|
+
idKey: "id",
|
|
12
|
+
criticalKey: null,
|
|
13
|
+
computedCriticalPath: false,
|
|
14
|
+
paneWidth: 520,
|
|
15
|
+
pixelsPerDay: 24,
|
|
16
|
+
zoomLevel: "day",
|
|
17
|
+
timelineStart: null,
|
|
18
|
+
timelineEnd: null,
|
|
19
|
+
rangePaddingDays: 0,
|
|
20
|
+
workingCalendar: resolveDataGridWorkingCalendar(null),
|
|
21
|
+
rowBarHeight: 18,
|
|
22
|
+
minBarWidth: 6,
|
|
23
|
+
resizeHandleWidth: 8,
|
|
24
|
+
};
|
|
25
|
+
const PIXELS_PER_DAY_BY_ZOOM = {
|
|
26
|
+
day: 24,
|
|
27
|
+
week: 8,
|
|
28
|
+
month: 3,
|
|
29
|
+
};
|
|
30
|
+
function normalizeNumericOption(value, fallback, min) {
|
|
31
|
+
if (!Number.isFinite(value)) {
|
|
32
|
+
return fallback;
|
|
33
|
+
}
|
|
34
|
+
return Math.max(min, Number(value));
|
|
35
|
+
}
|
|
36
|
+
function readRowCell(row, key) {
|
|
37
|
+
if (!row || !key) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
const record = row.row;
|
|
41
|
+
return record[key];
|
|
42
|
+
}
|
|
43
|
+
function resolveTaskId(row, options) {
|
|
44
|
+
const explicit = readRowCell(row, options.idKey);
|
|
45
|
+
if (explicit != null && String(explicit).trim().length > 0) {
|
|
46
|
+
return String(explicit).trim();
|
|
47
|
+
}
|
|
48
|
+
if (row.rowId != null) {
|
|
49
|
+
return String(row.rowId);
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
}
|
|
53
|
+
function resolveLabel(row, options) {
|
|
54
|
+
var _a;
|
|
55
|
+
const explicit = readRowCell(row, options.labelKey);
|
|
56
|
+
if (explicit != null && String(explicit).trim().length > 0) {
|
|
57
|
+
return String(explicit);
|
|
58
|
+
}
|
|
59
|
+
if (row.kind === "group") {
|
|
60
|
+
const groupValue = (_a = row.groupMeta) === null || _a === void 0 ? void 0 : _a.groupValue;
|
|
61
|
+
if (groupValue != null && String(groupValue).trim().length > 0) {
|
|
62
|
+
return String(groupValue);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return resolveTaskId(row, options);
|
|
66
|
+
}
|
|
67
|
+
function createResolvedGanttRowSnapshotReader(options) {
|
|
68
|
+
const cache = new WeakMap();
|
|
69
|
+
return row => {
|
|
70
|
+
const cached = cache.get(row);
|
|
71
|
+
if (cached) {
|
|
72
|
+
return cached;
|
|
73
|
+
}
|
|
74
|
+
const snapshot = {
|
|
75
|
+
startMs: resolveDataGridGanttDateMs(readRowCell(row, options.startKey)),
|
|
76
|
+
endMs: resolveDataGridGanttDateMs(readRowCell(row, options.endKey)),
|
|
77
|
+
baselineStartMs: resolveDataGridGanttDateMs(readRowCell(row, options.baselineStartKey)),
|
|
78
|
+
baselineEndMs: resolveDataGridGanttDateMs(readRowCell(row, options.baselineEndKey)),
|
|
79
|
+
progress: resolveDataGridGanttProgress(readRowCell(row, options.progressKey)),
|
|
80
|
+
dependencies: resolveDataGridGanttDependencies(readRowCell(row, options.dependencyKey)),
|
|
81
|
+
dependencyRefs: resolveDataGridGanttDependencyRefs(readRowCell(row, options.dependencyKey)),
|
|
82
|
+
critical: Boolean(options.criticalKey
|
|
83
|
+
&& readRowCell(row, options.criticalKey)),
|
|
84
|
+
taskId: resolveTaskId(row, options),
|
|
85
|
+
label: resolveLabel(row, options),
|
|
86
|
+
};
|
|
87
|
+
cache.set(row, snapshot);
|
|
88
|
+
return snapshot;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function resolveDataGridGanttDateMs(value) {
|
|
92
|
+
if (value instanceof Date) {
|
|
93
|
+
const ms = value.getTime();
|
|
94
|
+
return Number.isFinite(ms) ? ms : null;
|
|
95
|
+
}
|
|
96
|
+
if (typeof value === "number") {
|
|
97
|
+
return Number.isFinite(value) ? value : null;
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
100
|
+
const ms = Date.parse(value);
|
|
101
|
+
return Number.isFinite(ms) ? ms : null;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
export function resolveDataGridGanttProgress(value) {
|
|
106
|
+
const numeric = typeof value === "number"
|
|
107
|
+
? value
|
|
108
|
+
: (typeof value === "string" && value.trim().length > 0 ? Number(value) : NaN);
|
|
109
|
+
if (!Number.isFinite(numeric)) {
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
const normalized = numeric > 1 ? numeric / 100 : numeric;
|
|
113
|
+
return Math.min(1, Math.max(0, normalized));
|
|
114
|
+
}
|
|
115
|
+
export function resolveDataGridGanttSnapDays(zoomLevel) {
|
|
116
|
+
return zoomLevel === "week" || zoomLevel === "month" ? 7 : 1;
|
|
117
|
+
}
|
|
118
|
+
export function snapDataGridGanttDateMs(dateMs, zoomLevel, workingCalendar) {
|
|
119
|
+
if (!Number.isFinite(dateMs)) {
|
|
120
|
+
return dateMs;
|
|
121
|
+
}
|
|
122
|
+
if (resolveDataGridGanttSnapDays(zoomLevel) === 7) {
|
|
123
|
+
return startOfUtcWeek(dateMs);
|
|
124
|
+
}
|
|
125
|
+
return workingCalendar
|
|
126
|
+
? snapDataGridDateToWorkingDay(dateMs, workingCalendar)
|
|
127
|
+
: startOfUtcDay(dateMs);
|
|
128
|
+
}
|
|
129
|
+
export function snapDataGridGanttDayDelta(dayDelta, zoomLevel) {
|
|
130
|
+
if (!Number.isFinite(dayDelta) || dayDelta === 0) {
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
const snapDays = resolveDataGridGanttSnapDays(zoomLevel);
|
|
134
|
+
if (snapDays === 1) {
|
|
135
|
+
return Math.round(dayDelta);
|
|
136
|
+
}
|
|
137
|
+
return Math.round(dayDelta / snapDays) * snapDays;
|
|
138
|
+
}
|
|
139
|
+
export function resolveDataGridGanttDependencies(value) {
|
|
140
|
+
if (Array.isArray(value)) {
|
|
141
|
+
return value
|
|
142
|
+
.map(entry => String(entry !== null && entry !== void 0 ? entry : "").trim())
|
|
143
|
+
.filter(entry => entry.length > 0);
|
|
144
|
+
}
|
|
145
|
+
if (typeof value === "string") {
|
|
146
|
+
return value
|
|
147
|
+
.split(",")
|
|
148
|
+
.map(entry => entry.trim())
|
|
149
|
+
.filter(entry => entry.length > 0);
|
|
150
|
+
}
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
function resolveDataGridGanttDependencyType(value) {
|
|
154
|
+
const normalized = value.trim().toUpperCase();
|
|
155
|
+
return normalized === "FS" || normalized === "SS" || normalized === "FF" || normalized === "SF"
|
|
156
|
+
? normalized
|
|
157
|
+
: null;
|
|
158
|
+
}
|
|
159
|
+
function parseDataGridGanttDependencyRef(token) {
|
|
160
|
+
var _a, _b, _c, _d, _e, _f;
|
|
161
|
+
const trimmed = token.trim();
|
|
162
|
+
if (trimmed.length === 0) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
const delimitedMatch = trimmed.match(/^(.+?)(?::|->|\s+)(FS|SS|FF|SF)$/i);
|
|
166
|
+
if (delimitedMatch) {
|
|
167
|
+
const taskId = (_b = (_a = delimitedMatch[1]) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : "";
|
|
168
|
+
const type = resolveDataGridGanttDependencyType((_c = delimitedMatch[2]) !== null && _c !== void 0 ? _c : "");
|
|
169
|
+
if (taskId.length > 0 && type) {
|
|
170
|
+
return {
|
|
171
|
+
taskId,
|
|
172
|
+
type,
|
|
173
|
+
raw: trimmed,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const compactNumericMatch = trimmed.match(/^(\d+)(FS|SS|FF|SF)$/i);
|
|
178
|
+
if (compactNumericMatch) {
|
|
179
|
+
const taskId = (_e = (_d = compactNumericMatch[1]) === null || _d === void 0 ? void 0 : _d.trim()) !== null && _e !== void 0 ? _e : "";
|
|
180
|
+
const type = resolveDataGridGanttDependencyType((_f = compactNumericMatch[2]) !== null && _f !== void 0 ? _f : "");
|
|
181
|
+
if (taskId.length > 0 && type) {
|
|
182
|
+
return {
|
|
183
|
+
taskId,
|
|
184
|
+
type,
|
|
185
|
+
raw: trimmed,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
taskId: trimmed,
|
|
191
|
+
type: "FS",
|
|
192
|
+
raw: trimmed,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export function resolveDataGridGanttDependencyRefs(value) {
|
|
196
|
+
return resolveDataGridGanttDependencies(value)
|
|
197
|
+
.map(parseDataGridGanttDependencyRef)
|
|
198
|
+
.filter((dependencyRef) => dependencyRef !== null);
|
|
199
|
+
}
|
|
200
|
+
export function normalizeDataGridGanttOptions(input) {
|
|
201
|
+
var _a, _b;
|
|
202
|
+
if (input == null || input === false) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
if (input === true) {
|
|
206
|
+
return { ...DEFAULT_GANTT_OPTIONS };
|
|
207
|
+
}
|
|
208
|
+
const zoomLevel = input.zoomLevel === "week" || input.zoomLevel === "month"
|
|
209
|
+
? input.zoomLevel
|
|
210
|
+
: "day";
|
|
211
|
+
return {
|
|
212
|
+
startKey: typeof input.startKey === "string" && input.startKey.length > 0
|
|
213
|
+
? input.startKey
|
|
214
|
+
: DEFAULT_GANTT_OPTIONS.startKey,
|
|
215
|
+
endKey: typeof input.endKey === "string" && input.endKey.length > 0
|
|
216
|
+
? input.endKey
|
|
217
|
+
: DEFAULT_GANTT_OPTIONS.endKey,
|
|
218
|
+
baselineStartKey: typeof input.baselineStartKey === "string" && input.baselineStartKey.length > 0
|
|
219
|
+
? input.baselineStartKey
|
|
220
|
+
: DEFAULT_GANTT_OPTIONS.baselineStartKey,
|
|
221
|
+
baselineEndKey: typeof input.baselineEndKey === "string" && input.baselineEndKey.length > 0
|
|
222
|
+
? input.baselineEndKey
|
|
223
|
+
: DEFAULT_GANTT_OPTIONS.baselineEndKey,
|
|
224
|
+
progressKey: typeof input.progressKey === "string" && input.progressKey.length > 0
|
|
225
|
+
? input.progressKey
|
|
226
|
+
: DEFAULT_GANTT_OPTIONS.progressKey,
|
|
227
|
+
dependencyKey: typeof input.dependencyKey === "string" && input.dependencyKey.length > 0
|
|
228
|
+
? input.dependencyKey
|
|
229
|
+
: DEFAULT_GANTT_OPTIONS.dependencyKey,
|
|
230
|
+
labelKey: typeof input.labelKey === "string" && input.labelKey.length > 0
|
|
231
|
+
? input.labelKey
|
|
232
|
+
: DEFAULT_GANTT_OPTIONS.labelKey,
|
|
233
|
+
idKey: typeof input.idKey === "string" && input.idKey.length > 0
|
|
234
|
+
? input.idKey
|
|
235
|
+
: DEFAULT_GANTT_OPTIONS.idKey,
|
|
236
|
+
criticalKey: typeof input.criticalKey === "string" && input.criticalKey.length > 0
|
|
237
|
+
? input.criticalKey
|
|
238
|
+
: DEFAULT_GANTT_OPTIONS.criticalKey,
|
|
239
|
+
computedCriticalPath: input.computedCriticalPath === true,
|
|
240
|
+
paneWidth: normalizeNumericOption(input.paneWidth, DEFAULT_GANTT_OPTIONS.paneWidth, 280),
|
|
241
|
+
pixelsPerDay: normalizeNumericOption(input.pixelsPerDay, PIXELS_PER_DAY_BY_ZOOM[zoomLevel], 1),
|
|
242
|
+
zoomLevel,
|
|
243
|
+
timelineStart: (_a = input.timelineStart) !== null && _a !== void 0 ? _a : null,
|
|
244
|
+
timelineEnd: (_b = input.timelineEnd) !== null && _b !== void 0 ? _b : null,
|
|
245
|
+
rangePaddingDays: normalizeNumericOption(input.rangePaddingDays, DEFAULT_GANTT_OPTIONS.rangePaddingDays, 0),
|
|
246
|
+
workingCalendar: resolveDataGridWorkingCalendar(input.workingCalendar),
|
|
247
|
+
rowBarHeight: normalizeNumericOption(input.rowBarHeight, DEFAULT_GANTT_OPTIONS.rowBarHeight, 6),
|
|
248
|
+
minBarWidth: normalizeNumericOption(input.minBarWidth, DEFAULT_GANTT_OPTIONS.minBarWidth, 1),
|
|
249
|
+
resizeHandleWidth: normalizeNumericOption(input.resizeHandleWidth, DEFAULT_GANTT_OPTIONS.resizeHandleWidth, 2),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
export function resolveDataGridGanttTimelineState(rows, options) {
|
|
253
|
+
return resolveDataGridGanttAnalysis(rows, options).timeline;
|
|
254
|
+
}
|
|
255
|
+
function resolveDataGridGanttCriticalTaskIdsFromTaskNodes(taskNodes, taskIdByRowId) {
|
|
256
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
257
|
+
const successorIdsByTaskId = new Map();
|
|
258
|
+
const predecessorIdsByTaskId = new Map();
|
|
259
|
+
const indegreeByTaskId = new Map();
|
|
260
|
+
for (const taskId of taskNodes.keys()) {
|
|
261
|
+
successorIdsByTaskId.set(taskId, []);
|
|
262
|
+
predecessorIdsByTaskId.set(taskId, []);
|
|
263
|
+
indegreeByTaskId.set(taskId, 0);
|
|
264
|
+
}
|
|
265
|
+
for (const node of taskNodes.values()) {
|
|
266
|
+
const predecessors = predecessorIdsByTaskId.get(node.taskId);
|
|
267
|
+
const uniquePredecessors = new Set();
|
|
268
|
+
for (const dependency of node.dependencyRefs) {
|
|
269
|
+
if (dependency.type !== "FS") {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
const dependencyId = dependency.taskId;
|
|
273
|
+
const predecessorTaskId = taskNodes.has(dependencyId)
|
|
274
|
+
? dependencyId
|
|
275
|
+
: ((_a = taskIdByRowId.get(dependencyId)) !== null && _a !== void 0 ? _a : null);
|
|
276
|
+
if (!predecessorTaskId || predecessorTaskId === node.taskId || uniquePredecessors.has(predecessorTaskId)) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
uniquePredecessors.add(predecessorTaskId);
|
|
280
|
+
predecessors === null || predecessors === void 0 ? void 0 : predecessors.push(predecessorTaskId);
|
|
281
|
+
(_b = successorIdsByTaskId.get(predecessorTaskId)) === null || _b === void 0 ? void 0 : _b.push(node.taskId);
|
|
282
|
+
indegreeByTaskId.set(node.taskId, ((_c = indegreeByTaskId.get(node.taskId)) !== null && _c !== void 0 ? _c : 0) + 1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const queue = Array.from(taskNodes.keys()).filter(taskId => { var _a; return ((_a = indegreeByTaskId.get(taskId)) !== null && _a !== void 0 ? _a : 0) === 0; });
|
|
286
|
+
const topologicalOrder = [];
|
|
287
|
+
for (let index = 0; index < queue.length; index += 1) {
|
|
288
|
+
const taskId = queue[index];
|
|
289
|
+
if (!taskId) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
topologicalOrder.push(taskId);
|
|
293
|
+
for (const successorTaskId of (_d = successorIdsByTaskId.get(taskId)) !== null && _d !== void 0 ? _d : []) {
|
|
294
|
+
const nextIndegree = ((_e = indegreeByTaskId.get(successorTaskId)) !== null && _e !== void 0 ? _e : 0) - 1;
|
|
295
|
+
indegreeByTaskId.set(successorTaskId, nextIndegree);
|
|
296
|
+
if (nextIndegree === 0) {
|
|
297
|
+
queue.push(successorTaskId);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (topologicalOrder.length !== taskNodes.size) {
|
|
302
|
+
return new Set();
|
|
303
|
+
}
|
|
304
|
+
const earliestStartByTaskId = new Map();
|
|
305
|
+
const earliestFinishByTaskId = new Map();
|
|
306
|
+
let projectEndMs = Number.NEGATIVE_INFINITY;
|
|
307
|
+
for (const taskId of topologicalOrder) {
|
|
308
|
+
const node = taskNodes.get(taskId);
|
|
309
|
+
if (!node) {
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const durationMs = Math.max(0, node.endMs - node.startMs);
|
|
313
|
+
const predecessorFinishMs = ((_f = predecessorIdsByTaskId.get(taskId)) !== null && _f !== void 0 ? _f : []).reduce((maxFinish, predecessorTaskId) => {
|
|
314
|
+
var _a;
|
|
315
|
+
return Math.max(maxFinish, (_a = earliestFinishByTaskId.get(predecessorTaskId)) !== null && _a !== void 0 ? _a : Number.NEGATIVE_INFINITY);
|
|
316
|
+
}, Number.NEGATIVE_INFINITY);
|
|
317
|
+
const earliestStartMs = Math.max(node.startMs, Number.isFinite(predecessorFinishMs) ? predecessorFinishMs : node.startMs);
|
|
318
|
+
const earliestFinishMs = earliestStartMs + durationMs;
|
|
319
|
+
earliestStartByTaskId.set(taskId, earliestStartMs);
|
|
320
|
+
earliestFinishByTaskId.set(taskId, earliestFinishMs);
|
|
321
|
+
projectEndMs = Math.max(projectEndMs, earliestFinishMs, node.endMs);
|
|
322
|
+
}
|
|
323
|
+
if (!Number.isFinite(projectEndMs)) {
|
|
324
|
+
return new Set();
|
|
325
|
+
}
|
|
326
|
+
const latestStartByTaskId = new Map();
|
|
327
|
+
for (let index = topologicalOrder.length - 1; index >= 0; index -= 1) {
|
|
328
|
+
const taskId = topologicalOrder[index];
|
|
329
|
+
const node = taskNodes.get(taskId);
|
|
330
|
+
if (!node) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
const durationMs = Math.max(0, node.endMs - node.startMs);
|
|
334
|
+
const successorStartMs = ((_g = successorIdsByTaskId.get(taskId)) !== null && _g !== void 0 ? _g : []).reduce((minStart, successorTaskId) => {
|
|
335
|
+
var _a;
|
|
336
|
+
return Math.min(minStart, (_a = latestStartByTaskId.get(successorTaskId)) !== null && _a !== void 0 ? _a : Number.POSITIVE_INFINITY);
|
|
337
|
+
}, Number.POSITIVE_INFINITY);
|
|
338
|
+
const latestFinishMs = Number.isFinite(successorStartMs) ? successorStartMs : projectEndMs;
|
|
339
|
+
latestStartByTaskId.set(taskId, latestFinishMs - durationMs);
|
|
340
|
+
}
|
|
341
|
+
const criticalTaskIds = new Set();
|
|
342
|
+
const slackEpsilonMs = 60 * 1000;
|
|
343
|
+
for (const taskId of topologicalOrder) {
|
|
344
|
+
const earliestStartMs = earliestStartByTaskId.get(taskId);
|
|
345
|
+
const latestStartMs = latestStartByTaskId.get(taskId);
|
|
346
|
+
if (earliestStartMs == null || latestStartMs == null) {
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (Math.abs(latestStartMs - earliestStartMs) <= slackEpsilonMs) {
|
|
350
|
+
criticalTaskIds.add(taskId);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return criticalTaskIds;
|
|
354
|
+
}
|
|
355
|
+
function resolveDataGridGanttAnalysisInternal(rows, options, computeCriticalPath) {
|
|
356
|
+
const readResolvedRow = createResolvedGanttRowSnapshotReader(options);
|
|
357
|
+
const explicitStartMs = resolveDataGridGanttDateMs(options.timelineStart);
|
|
358
|
+
const explicitEndMs = resolveDataGridGanttDateMs(options.timelineEnd);
|
|
359
|
+
let minMs = explicitStartMs;
|
|
360
|
+
let maxMs = explicitEndMs;
|
|
361
|
+
const taskNodes = new Map();
|
|
362
|
+
const taskIdByRowId = new Map();
|
|
363
|
+
if (explicitStartMs == null || explicitEndMs == null || computeCriticalPath) {
|
|
364
|
+
const count = rows.getCount();
|
|
365
|
+
for (let index = 0; index < count; index += 1) {
|
|
366
|
+
const row = rows.get(index);
|
|
367
|
+
if (!row) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const resolvedRow = readResolvedRow(row);
|
|
371
|
+
const startMs = resolvedRow.startMs;
|
|
372
|
+
const endMs = resolvedRow.endMs;
|
|
373
|
+
if (startMs == null && endMs == null) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const boundedStart = startMs !== null && startMs !== void 0 ? startMs : endMs;
|
|
377
|
+
const boundedEnd = endMs !== null && endMs !== void 0 ? endMs : startMs;
|
|
378
|
+
if (boundedStart == null || boundedEnd == null) {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (explicitStartMs == null) {
|
|
382
|
+
minMs = minMs == null ? boundedStart : Math.min(minMs, boundedStart);
|
|
383
|
+
}
|
|
384
|
+
if (explicitEndMs == null) {
|
|
385
|
+
maxMs = maxMs == null ? boundedEnd : Math.max(maxMs, boundedEnd);
|
|
386
|
+
}
|
|
387
|
+
if (computeCriticalPath) {
|
|
388
|
+
if (row.kind === "group" || boundedEnd < boundedStart) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
const taskId = resolvedRow.taskId || (row.rowId == null ? String(index) : String(row.rowId));
|
|
392
|
+
const rowId = row.rowId == null ? String(index) : String(row.rowId);
|
|
393
|
+
taskNodes.set(taskId, {
|
|
394
|
+
taskId,
|
|
395
|
+
rowId,
|
|
396
|
+
startMs: boundedStart,
|
|
397
|
+
endMs: boundedEnd,
|
|
398
|
+
dependencyRefs: resolvedRow.dependencyRefs,
|
|
399
|
+
});
|
|
400
|
+
taskIdByRowId.set(rowId, taskId);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const range = resolveDataGridTimelineRange({
|
|
405
|
+
minTaskDateMs: minMs,
|
|
406
|
+
maxTaskDateMs: maxMs,
|
|
407
|
+
pixelsPerDay: options.pixelsPerDay,
|
|
408
|
+
rangePaddingDays: options.rangePaddingDays,
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
timeline: {
|
|
412
|
+
startMs: range.startMs,
|
|
413
|
+
endMs: range.endMs,
|
|
414
|
+
pixelsPerDay: options.pixelsPerDay,
|
|
415
|
+
totalWidth: range.totalWidth,
|
|
416
|
+
zoomLevel: options.zoomLevel,
|
|
417
|
+
},
|
|
418
|
+
criticalTaskIds: computeCriticalPath
|
|
419
|
+
? resolveDataGridGanttCriticalTaskIdsFromTaskNodes(taskNodes, taskIdByRowId)
|
|
420
|
+
: new Set(),
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
export function resolveDataGridGanttAnalysis(rows, options) {
|
|
424
|
+
return resolveDataGridGanttAnalysisInternal(rows, options, options.computedCriticalPath === true);
|
|
425
|
+
}
|
|
426
|
+
export function resolveDataGridGanttRangeFrame(range, timeline, minBarWidth, milestoneWidth) {
|
|
427
|
+
if (range.endMs <= range.startMs) {
|
|
428
|
+
const width = Math.max(minBarWidth, milestoneWidth !== null && milestoneWidth !== void 0 ? milestoneWidth : minBarWidth);
|
|
429
|
+
return {
|
|
430
|
+
x: resolveDataGridTimelineDateToPixel(range.startMs, timeline) - (width / 2),
|
|
431
|
+
width,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
return {
|
|
435
|
+
x: resolveDataGridTimelineDateToPixel(range.startMs, timeline),
|
|
436
|
+
width: Math.max(minBarWidth, resolveDataGridTimelineDateToPixel(range.endMs, timeline)
|
|
437
|
+
- resolveDataGridTimelineDateToPixel(range.startMs, timeline)),
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
export function resolveDataGridGanttDateOffset(dateMs, timeline) {
|
|
441
|
+
return resolveDataGridTimelineDateToPixel(dateMs, timeline);
|
|
442
|
+
}
|
|
443
|
+
export function clampDataGridGanttScrollLeft(scrollLeft, totalWidth, viewportWidth) {
|
|
444
|
+
return clampDataGridTimelineScrollLeft(scrollLeft, totalWidth, viewportWidth);
|
|
445
|
+
}
|
|
446
|
+
export function resolveDataGridGanttScrollLeftForDate(input) {
|
|
447
|
+
return resolveDataGridTimelineScrollLeftForDate({
|
|
448
|
+
dateMs: input.dateMs,
|
|
449
|
+
timeline: input.timeline,
|
|
450
|
+
viewportWidth: input.viewportWidth,
|
|
451
|
+
align: input.align,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
export function buildDataGridGanttVisibleBars(input) {
|
|
455
|
+
var _a, _b, _c, _d, _e, _f;
|
|
456
|
+
const readResolvedRow = createResolvedGanttRowSnapshotReader(input.options);
|
|
457
|
+
const bars = [];
|
|
458
|
+
const rowContexts = [];
|
|
459
|
+
let currentY = input.topSpacerHeight;
|
|
460
|
+
const rowBarHeight = input.options.rowBarHeight;
|
|
461
|
+
input.rows.forEach((row, rowOffset) => {
|
|
462
|
+
var _a, _b, _c;
|
|
463
|
+
const rowIndex = input.viewportRowStart + rowOffset;
|
|
464
|
+
const metric = (_a = input.rowMetrics) === null || _a === void 0 ? void 0 : _a[rowOffset];
|
|
465
|
+
const rowHeight = Math.max(1, (_b = metric === null || metric === void 0 ? void 0 : metric.height) !== null && _b !== void 0 ? _b : (input.resolveRowHeight(rowIndex) || input.baseRowHeight));
|
|
466
|
+
const rowTop = (_c = metric === null || metric === void 0 ? void 0 : metric.top) !== null && _c !== void 0 ? _c : currentY;
|
|
467
|
+
rowContexts.push({
|
|
468
|
+
row,
|
|
469
|
+
rowIndex,
|
|
470
|
+
rowId: row.rowId == null ? String(rowIndex) : String(row.rowId),
|
|
471
|
+
rowUpdateId: row.rowId == null ? rowIndex : row.rowId,
|
|
472
|
+
rowHeight,
|
|
473
|
+
y: (rowTop - input.scrollTop) + Math.max(0, (rowHeight - rowBarHeight) / 2),
|
|
474
|
+
resolvedRow: readResolvedRow(row),
|
|
475
|
+
});
|
|
476
|
+
currentY = rowTop + rowHeight;
|
|
477
|
+
});
|
|
478
|
+
const summaryRangesByRowId = new Map();
|
|
479
|
+
const activeGroups = [];
|
|
480
|
+
function absorbRangeIntoActiveGroups(startMs, endMs) {
|
|
481
|
+
for (const group of activeGroups) {
|
|
482
|
+
group.startMs = group.startMs == null ? startMs : Math.min(group.startMs, startMs);
|
|
483
|
+
group.endMs = group.endMs == null ? endMs : Math.max(group.endMs, endMs);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
function closeGroupsAtLevel(level) {
|
|
487
|
+
while (activeGroups.length > 0) {
|
|
488
|
+
const current = activeGroups[activeGroups.length - 1];
|
|
489
|
+
if (!current || current.level < level) {
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
activeGroups.pop();
|
|
493
|
+
if (current.startMs != null && current.endMs != null && current.endMs >= current.startMs) {
|
|
494
|
+
summaryRangesByRowId.set(current.rowId, {
|
|
495
|
+
startMs: current.startMs,
|
|
496
|
+
endMs: current.endMs,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
for (const context of rowContexts) {
|
|
502
|
+
if (context.row.kind === "group") {
|
|
503
|
+
const level = Math.max(0, Math.trunc((_b = (_a = context.row.groupMeta) === null || _a === void 0 ? void 0 : _a.level) !== null && _b !== void 0 ? _b : 0));
|
|
504
|
+
closeGroupsAtLevel(level);
|
|
505
|
+
activeGroups.push({
|
|
506
|
+
rowId: context.rowId,
|
|
507
|
+
level,
|
|
508
|
+
startMs: null,
|
|
509
|
+
endMs: null,
|
|
510
|
+
});
|
|
511
|
+
const ownStartMs = context.resolvedRow.startMs;
|
|
512
|
+
const ownEndMs = context.resolvedRow.endMs;
|
|
513
|
+
if (ownStartMs != null && ownEndMs != null && ownEndMs >= ownStartMs) {
|
|
514
|
+
absorbRangeIntoActiveGroups(ownStartMs, ownEndMs);
|
|
515
|
+
}
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const startMs = context.resolvedRow.startMs;
|
|
519
|
+
const endMs = context.resolvedRow.endMs;
|
|
520
|
+
if (startMs != null && endMs != null && endMs >= startMs) {
|
|
521
|
+
absorbRangeIntoActiveGroups(startMs, endMs);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
closeGroupsAtLevel(0);
|
|
525
|
+
for (const context of rowContexts) {
|
|
526
|
+
const ownStartMs = context.resolvedRow.startMs;
|
|
527
|
+
const ownEndMs = context.resolvedRow.endMs;
|
|
528
|
+
const ownBaselineStartMs = context.resolvedRow.baselineStartMs;
|
|
529
|
+
const ownBaselineEndMs = context.resolvedRow.baselineEndMs;
|
|
530
|
+
const summaryRange = context.row.kind === "group"
|
|
531
|
+
? summaryRangesByRowId.get(context.rowId)
|
|
532
|
+
: null;
|
|
533
|
+
const startMs = (_c = summaryRange === null || summaryRange === void 0 ? void 0 : summaryRange.startMs) !== null && _c !== void 0 ? _c : ownStartMs;
|
|
534
|
+
const endMs = (_d = summaryRange === null || summaryRange === void 0 ? void 0 : summaryRange.endMs) !== null && _d !== void 0 ? _d : ownEndMs;
|
|
535
|
+
if (startMs == null || endMs == null || endMs < startMs) {
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
const summary = context.row.kind === "group" && summaryRange != null;
|
|
539
|
+
const computedCritical = (_f = (_e = input.criticalTaskIds) === null || _e === void 0 ? void 0 : _e.has(context.resolvedRow.taskId || context.rowId)) !== null && _f !== void 0 ? _f : false;
|
|
540
|
+
const height = rowBarHeight;
|
|
541
|
+
const y = context.y;
|
|
542
|
+
if (y + height < 0 || y > input.viewportHeight) {
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const milestone = !summary && endMs === startMs;
|
|
546
|
+
const frame = resolveDataGridGanttRangeFrame({ startMs, endMs }, input.timeline, input.options.minBarWidth, milestone ? height : undefined);
|
|
547
|
+
bars.push({
|
|
548
|
+
row: context.row,
|
|
549
|
+
rowId: context.rowId,
|
|
550
|
+
rowUpdateId: context.rowUpdateId,
|
|
551
|
+
taskId: context.resolvedRow.taskId || context.rowId,
|
|
552
|
+
rowIndex: context.rowIndex,
|
|
553
|
+
label: context.resolvedRow.label,
|
|
554
|
+
dependencies: summary ? [] : context.resolvedRow.dependencies,
|
|
555
|
+
dependencyRefs: summary ? [] : context.resolvedRow.dependencyRefs,
|
|
556
|
+
critical: summary ? false : (context.resolvedRow.critical || computedCritical),
|
|
557
|
+
criticalSource: summary
|
|
558
|
+
? null
|
|
559
|
+
: (context.resolvedRow.critical ? "manual" : (computedCritical ? "computed" : null)),
|
|
560
|
+
milestone,
|
|
561
|
+
summary,
|
|
562
|
+
progress: summary ? 0 : context.resolvedRow.progress,
|
|
563
|
+
startMs,
|
|
564
|
+
endMs,
|
|
565
|
+
baselineStartMs: summary || ownBaselineStartMs == null || ownBaselineEndMs == null || ownBaselineEndMs < ownBaselineStartMs
|
|
566
|
+
? null
|
|
567
|
+
: ownBaselineStartMs,
|
|
568
|
+
baselineEndMs: summary || ownBaselineStartMs == null || ownBaselineEndMs == null || ownBaselineEndMs < ownBaselineStartMs
|
|
569
|
+
? null
|
|
570
|
+
: ownBaselineEndMs,
|
|
571
|
+
x: frame.x,
|
|
572
|
+
width: frame.width,
|
|
573
|
+
y,
|
|
574
|
+
height,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
return bars;
|
|
578
|
+
}
|
|
579
|
+
export function resolveDataGridGanttCriticalTaskIds(rows, options) {
|
|
580
|
+
return resolveDataGridGanttAnalysisInternal(rows, options, true).criticalTaskIds;
|
|
581
|
+
}
|
|
582
|
+
export function buildDataGridGanttDependencyPaths(input) {
|
|
583
|
+
var _a, _b;
|
|
584
|
+
const barsByTaskId = new Map();
|
|
585
|
+
const barsByRowId = new Map();
|
|
586
|
+
for (const bar of input.bars) {
|
|
587
|
+
const taskId = bar.taskId.trim();
|
|
588
|
+
const rowId = bar.rowId.trim();
|
|
589
|
+
if (taskId.length > 0) {
|
|
590
|
+
barsByTaskId.set(taskId, bar);
|
|
591
|
+
}
|
|
592
|
+
if (rowId.length > 0) {
|
|
593
|
+
barsByRowId.set(rowId, bar);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const paths = [];
|
|
597
|
+
const resolveFrame = (_a = input.resolveFrame) !== null && _a !== void 0 ? _a : (bar => bar);
|
|
598
|
+
const minBendPx = Math.max(0, (_b = input.minBendPx) !== null && _b !== void 0 ? _b : 12);
|
|
599
|
+
const portStubPx = Math.max(4, Math.min(8, Math.round(minBendPx / 2)));
|
|
600
|
+
const compactGapThresholdPx = (portStubPx * 2) + minBendPx;
|
|
601
|
+
function resolveAnchorX(frame, side) {
|
|
602
|
+
return side === "start" ? frame.x : frame.x + frame.width;
|
|
603
|
+
}
|
|
604
|
+
function resolveLeadX(anchorX, side) {
|
|
605
|
+
return side === "start" ? anchorX - portStubPx : anchorX + portStubPx;
|
|
606
|
+
}
|
|
607
|
+
for (const targetBar of input.bars) {
|
|
608
|
+
const targetFrame = resolveFrame(targetBar);
|
|
609
|
+
targetBar.dependencyRefs.forEach((dependency, dependencyIndex) => {
|
|
610
|
+
var _a;
|
|
611
|
+
const dependencyTaskId = dependency.taskId;
|
|
612
|
+
const sourceBar = (_a = barsByTaskId.get(dependencyTaskId)) !== null && _a !== void 0 ? _a : barsByRowId.get(dependencyTaskId);
|
|
613
|
+
if (!sourceBar) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
const sourceFrame = resolveFrame(sourceBar);
|
|
617
|
+
const sourceSide = dependency.type === "SS" || dependency.type === "SF" ? "start" : "end";
|
|
618
|
+
const targetSide = dependency.type === "FS" || dependency.type === "SS" ? "start" : "end";
|
|
619
|
+
const sourceX = resolveAnchorX(sourceFrame, sourceSide);
|
|
620
|
+
const targetX = resolveAnchorX(targetFrame, targetSide);
|
|
621
|
+
const sourceY = sourceFrame.y + (sourceFrame.height / 2);
|
|
622
|
+
const targetY = targetFrame.y + (targetFrame.height / 2);
|
|
623
|
+
const laneOffsetPx = minBendPx + ((dependencyIndex % 4) * 8);
|
|
624
|
+
const sourceLeadX = resolveLeadX(sourceX, sourceSide);
|
|
625
|
+
const targetApproachX = resolveLeadX(targetX, targetSide);
|
|
626
|
+
const points = (() => {
|
|
627
|
+
if (sourceSide === targetSide) {
|
|
628
|
+
const laneX = sourceSide === "start"
|
|
629
|
+
? Math.min(sourceLeadX, targetApproachX) - laneOffsetPx
|
|
630
|
+
: Math.max(sourceLeadX, targetApproachX) + laneOffsetPx;
|
|
631
|
+
return [
|
|
632
|
+
{ x: sourceX, y: sourceY },
|
|
633
|
+
{ x: sourceLeadX, y: sourceY },
|
|
634
|
+
{ x: laneX, y: sourceY },
|
|
635
|
+
{ x: laneX, y: targetY },
|
|
636
|
+
{ x: targetApproachX, y: targetY },
|
|
637
|
+
{ x: targetX, y: targetY },
|
|
638
|
+
];
|
|
639
|
+
}
|
|
640
|
+
if (sourceSide === "end" && targetSide === "start" && targetX >= sourceX) {
|
|
641
|
+
const gapPx = targetX - sourceX;
|
|
642
|
+
const laneX = gapPx <= compactGapThresholdPx
|
|
643
|
+
? sourceLeadX
|
|
644
|
+
: Math.max(sourceLeadX, sourceX + Math.max(laneOffsetPx, (targetApproachX - sourceX) / 2));
|
|
645
|
+
return [
|
|
646
|
+
{ x: sourceX, y: sourceY },
|
|
647
|
+
{ x: sourceLeadX, y: sourceY },
|
|
648
|
+
{ x: laneX, y: sourceY },
|
|
649
|
+
{ x: laneX, y: targetY },
|
|
650
|
+
{ x: targetApproachX, y: targetY },
|
|
651
|
+
{ x: targetX, y: targetY },
|
|
652
|
+
];
|
|
653
|
+
}
|
|
654
|
+
const detourX = sourceSide === "end"
|
|
655
|
+
? Math.max(sourceFrame.x + sourceFrame.width, targetFrame.x + targetFrame.width) + laneOffsetPx
|
|
656
|
+
: Math.min(sourceFrame.x, targetFrame.x) - laneOffsetPx;
|
|
657
|
+
const laneY = sourceY + ((targetY - sourceY) / 2);
|
|
658
|
+
return [
|
|
659
|
+
{ x: sourceX, y: sourceY },
|
|
660
|
+
{ x: sourceLeadX, y: sourceY },
|
|
661
|
+
{ x: detourX, y: sourceY },
|
|
662
|
+
{ x: detourX, y: laneY },
|
|
663
|
+
{ x: targetApproachX, y: laneY },
|
|
664
|
+
{ x: targetApproachX, y: targetY },
|
|
665
|
+
{ x: targetX, y: targetY },
|
|
666
|
+
];
|
|
667
|
+
})();
|
|
668
|
+
paths.push({
|
|
669
|
+
dependencyTaskId,
|
|
670
|
+
dependencyType: dependency.type,
|
|
671
|
+
sourceBar,
|
|
672
|
+
targetBar,
|
|
673
|
+
points,
|
|
674
|
+
});
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
return paths;
|
|
678
|
+
}
|
|
679
|
+
export function hitTestDataGridGanttBar(bars, point, resizeHandleWidth) {
|
|
680
|
+
for (let index = bars.length - 1; index >= 0; index -= 1) {
|
|
681
|
+
const bar = bars[index];
|
|
682
|
+
if (!bar) {
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
if (point.x < bar.x
|
|
686
|
+
|| point.x > bar.x + bar.width
|
|
687
|
+
|| point.y < bar.y
|
|
688
|
+
|| point.y > bar.y + bar.height) {
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if (bar.summary) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
if (bar.milestone) {
|
|
695
|
+
return { bar, mode: "move" };
|
|
696
|
+
}
|
|
697
|
+
const distanceToStart = point.x - bar.x;
|
|
698
|
+
const distanceToEnd = (bar.x + bar.width) - point.x;
|
|
699
|
+
if (distanceToStart <= resizeHandleWidth) {
|
|
700
|
+
return { bar, mode: "resize-start" };
|
|
701
|
+
}
|
|
702
|
+
if (distanceToEnd <= resizeHandleWidth) {
|
|
703
|
+
return { bar, mode: "resize-end" };
|
|
704
|
+
}
|
|
705
|
+
return { bar, mode: "move" };
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
export function applyDataGridGanttDragDelta(range, mode, dayDelta, zoomLevel = "day", workingCalendar) {
|
|
710
|
+
const snappedDayDelta = snapDataGridGanttDayDelta(dayDelta, zoomLevel);
|
|
711
|
+
if (!Number.isFinite(snappedDayDelta) || snappedDayDelta === 0) {
|
|
712
|
+
return range;
|
|
713
|
+
}
|
|
714
|
+
if (workingCalendar && resolveDataGridGanttSnapDays(zoomLevel) === 1) {
|
|
715
|
+
if (mode === "move") {
|
|
716
|
+
return {
|
|
717
|
+
startMs: addDataGridWorkingDays(range.startMs, snappedDayDelta, workingCalendar),
|
|
718
|
+
endMs: addDataGridWorkingDays(range.endMs, snappedDayDelta, workingCalendar),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
if (mode === "resize-start") {
|
|
722
|
+
const startMs = addDataGridWorkingDays(range.startMs, snappedDayDelta, workingCalendar);
|
|
723
|
+
return {
|
|
724
|
+
startMs: Math.min(startMs, addDataGridWorkingDays(range.endMs, -1, workingCalendar)),
|
|
725
|
+
endMs: range.endMs,
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
const endMs = addDataGridWorkingDays(range.endMs, snappedDayDelta, workingCalendar);
|
|
729
|
+
return {
|
|
730
|
+
startMs: range.startMs,
|
|
731
|
+
endMs: Math.max(addDataGridWorkingDays(range.startMs, 1, workingCalendar), endMs),
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
const deltaMs = snappedDayDelta * DAY_MS;
|
|
735
|
+
if (mode === "move") {
|
|
736
|
+
const durationMs = Math.max(0, range.endMs - range.startMs);
|
|
737
|
+
const startMs = snapDataGridGanttDateMs(range.startMs + deltaMs, zoomLevel, workingCalendar);
|
|
738
|
+
return {
|
|
739
|
+
startMs,
|
|
740
|
+
endMs: startMs + durationMs,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
if (mode === "resize-start") {
|
|
744
|
+
const startMs = snapDataGridGanttDateMs(range.startMs + deltaMs, zoomLevel, workingCalendar);
|
|
745
|
+
return {
|
|
746
|
+
startMs: Math.min(startMs, range.endMs - DAY_MS),
|
|
747
|
+
endMs: range.endMs,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
const endMs = snapDataGridGanttDateMs(range.endMs + deltaMs, zoomLevel, workingCalendar);
|
|
751
|
+
return {
|
|
752
|
+
startMs: range.startMs,
|
|
753
|
+
endMs: Math.max(range.startMs + DAY_MS, endMs),
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
export function buildDataGridGanttRowEditPatch(rowId, range, options) {
|
|
757
|
+
return {
|
|
758
|
+
rowId,
|
|
759
|
+
data: {
|
|
760
|
+
[options.startKey]: new Date(range.startMs),
|
|
761
|
+
[options.endKey]: new Date(range.endMs),
|
|
762
|
+
},
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
export function formatDataGridGanttDayLabel(dayMs, zoomLevel) {
|
|
766
|
+
const date = new Date(dayMs);
|
|
767
|
+
if (zoomLevel === "month") {
|
|
768
|
+
return date.toLocaleDateString(undefined, {
|
|
769
|
+
month: "short",
|
|
770
|
+
year: "numeric",
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
if (zoomLevel === "week") {
|
|
774
|
+
return date.toLocaleDateString(undefined, {
|
|
775
|
+
month: "short",
|
|
776
|
+
day: "numeric",
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
return date.toLocaleDateString(undefined, {
|
|
780
|
+
weekday: "short",
|
|
781
|
+
month: "short",
|
|
782
|
+
day: "numeric",
|
|
783
|
+
});
|
|
784
|
+
}
|