Skip to content

Commit 129f0d8

Browse files
committed
feat: develop weekendColWidth
1 parent 3c391be commit 129f0d8

File tree

13 files changed

+346
-108
lines changed

13 files changed

+346
-108
lines changed

packages/vtable-gantt/__tests__/gantt-baseline.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ describe('gantt baseline test', () => {
7474
expect(ganttInstance.parsedOptions.baselineStyle.barColor).toBe('gray');
7575
expect(ganttInstance.parsedOptions.baselineStyle.cornerRadius).toBe(5);
7676

77+
ganttInstance.release?.();
7778
container.remove();
7879
});
7980

@@ -121,6 +122,7 @@ describe('gantt baseline test', () => {
121122
expect(baselineInfo.baselineEndDate).not.toBeNull();
122123
expect(baselineInfo.baselineDays).toBeGreaterThan(0);
123124

125+
ganttInstance.release?.();
124126
container.remove();
125127
});
126128

@@ -166,6 +168,7 @@ describe('gantt baseline test', () => {
166168
expect(baselineInfo.baselineEndDate).toBeNull();
167169
expect(baselineInfo.baselineDays).toBe(0);
168170

171+
ganttInstance.release?.();
169172
container.remove();
170173
});
171174

@@ -212,6 +215,7 @@ describe('gantt baseline test', () => {
212215
const ganttInstance = new Gantt(container, option);
213216
expect(ganttInstance.parsedOptions.baselinePosition).toBe(position);
214217

218+
ganttInstance.release?.();
215219
container.remove();
216220
});
217221
});
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// @ts-nocheck
2+
3+
global.__VERSION__ = 'none';
4+
5+
import { createDiv } from './dom';
6+
import { Gantt } from '../src/index';
7+
8+
describe('gantt weekend column width', () => {
9+
test('weekendColWidth should override day cell width', () => {
10+
const container = createDiv();
11+
const option = {
12+
records: [
13+
{
14+
id: 1,
15+
title: '任务',
16+
startDate: '2024-07-01',
17+
endDate: '2024-07-02',
18+
progress: 50
19+
}
20+
],
21+
taskListTable: {
22+
columns: [{ field: 'title', title: '任务名称', width: 180 }],
23+
tableWidth: 300
24+
},
25+
timelineHeader: {
26+
colWidth: 50,
27+
weekendColWidth: 10,
28+
scales: [{ unit: 'day', step: 1 }]
29+
},
30+
minDate: '2024-07-01',
31+
maxDate: '2024-07-07'
32+
};
33+
const gantt = new Gantt(container, option);
34+
35+
const minScale = gantt.parsedOptions.reverseSortedTimelineScales[0];
36+
const dates = minScale.timelineDates;
37+
expect(dates.length).toBe(7);
38+
39+
let sum = 0;
40+
for (let i = 0; i < dates.length; i++) {
41+
const day = dates[i].startDate.getDay();
42+
const w = gantt.getDateColWidth(i);
43+
sum += w;
44+
if (day === 0 || day === 6) {
45+
expect(w).toBe(10);
46+
} else {
47+
expect(w).toBe(50);
48+
}
49+
}
50+
expect(gantt.getAllDateColsWidth()).toBe(sum);
51+
expect(sum).toBe(5 * 50 + 2 * 10);
52+
53+
gantt.release?.();
54+
container.remove();
55+
});
56+
57+
test('hideWeekend should collapse weekend columns to zero width', () => {
58+
const container = createDiv();
59+
const option = {
60+
records: [
61+
{
62+
id: 1,
63+
title: '任务',
64+
startDate: '2024-07-01',
65+
endDate: '2024-07-08',
66+
progress: 50
67+
}
68+
],
69+
taskListTable: {
70+
columns: [{ field: 'title', title: '任务名称', width: 180 }],
71+
tableWidth: 300
72+
},
73+
timelineHeader: {
74+
colWidth: 50,
75+
hideWeekend: true,
76+
scales: [{ unit: 'day', step: 1 }]
77+
},
78+
minDate: '2024-07-01',
79+
maxDate: '2024-07-08'
80+
};
81+
const gantt = new Gantt(container, option);
82+
83+
const minScale = gantt.parsedOptions.reverseSortedTimelineScales[0];
84+
const dates = minScale.timelineDates;
85+
expect(dates.length).toBe(8);
86+
87+
const mondayAfterWeekend = new Date('2024-07-08 00:00:00').getTime();
88+
const xAtMondayStart = gantt.getXByTime(mondayAfterWeekend);
89+
expect(xAtMondayStart).toBe(5 * 50);
90+
91+
const totalWidth = gantt.getAllDateColsWidth();
92+
expect(totalWidth).toBe(6 * 50);
93+
94+
gantt.release?.();
95+
container.remove();
96+
});
97+
});

packages/vtable-gantt/examples/gantt/gantt.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,8 @@ export function createTable() {
957957
}
958958
},
959959
timelineHeader: {
960+
// hideWeekend: true,
961+
weekendColWidth: 20,
960962
verticalLine: {
961963
lineWidth: 1,
962964
lineColor: '#e1e4e8'

packages/vtable-gantt/src/Gantt.ts

Lines changed: 173 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ import {
5454
} from './gantt-helper';
5555
import { EventTarget } from './event/EventTarget';
5656
import {
57-
computeCountToTimeScale,
5857
createDateAtLastHour,
5958
createDateAtLastMillisecond,
6059
createDateAtLastMinute,
@@ -220,6 +219,10 @@ export class Gantt extends EventTarget {
220219
// 时间缩放基准 - 每像素代表多少毫秒
221220
private millisecondsPerPixel: number;
222221
zoomScaleManager?: ZoomScaleManager;
222+
private _timelineColWidths: number[] = [];
223+
private _timelineColX: number[] = [];
224+
private _timelineColStartTimes: number[] = [];
225+
private _timelineColEndTimes: number[] = [];
223226

224227
/**
225228
* 重新计算时间相关的尺寸参数
@@ -749,6 +752,156 @@ export class Gantt extends EventTarget {
749752
);
750753
}
751754
}
755+
this._rebuildTimelineColXMap();
756+
}
757+
758+
private _rebuildTimelineColXMap() {
759+
const minScale = this.parsedOptions.reverseSortedTimelineScales?.[0];
760+
const timelineDates = minScale?.timelineDates ?? [];
761+
const baseWidth = this.parsedOptions.timelineColWidth ?? 0;
762+
763+
const hideWeekend = this.options?.timelineHeader?.hideWeekend === true;
764+
const weekendColWidth = this.options?.timelineHeader?.weekendColWidth;
765+
const enableWeekendWidth =
766+
minScale?.unit === 'day' && minScale?.step === 1 && (hideWeekend || weekendColWidth !== undefined);
767+
768+
this._timelineColWidths = new Array(timelineDates.length);
769+
this._timelineColX = new Array(timelineDates.length + 1);
770+
this._timelineColStartTimes = new Array(timelineDates.length);
771+
this._timelineColEndTimes = new Array(timelineDates.length);
772+
this._timelineColX[0] = 0;
773+
774+
let sumX = 0;
775+
for (let i = 0; i < timelineDates.length; i++) {
776+
const d = timelineDates[i];
777+
const startTime = d.startDate?.getTime?.() ?? 0;
778+
const endTime = d.endDate?.getTime?.() ?? startTime;
779+
this._timelineColStartTimes[i] = startTime;
780+
this._timelineColEndTimes[i] = endTime;
781+
782+
let w = baseWidth;
783+
if (enableWeekendWidth) {
784+
const day = d.startDate.getDay();
785+
const isWeekend = day === 0 || day === 6;
786+
if (isWeekend) {
787+
if (hideWeekend) {
788+
w = 0;
789+
} else if (typeof weekendColWidth === 'number') {
790+
w = weekendColWidth;
791+
} else if (typeof weekendColWidth === 'function') {
792+
w = weekendColWidth(baseWidth);
793+
}
794+
}
795+
}
796+
797+
w = Math.max(0, Number.isFinite(w) ? w : baseWidth);
798+
this._timelineColWidths[i] = w;
799+
sumX += w;
800+
this._timelineColX[i + 1] = sumX;
801+
}
802+
}
803+
804+
getXByTime(time: number) {
805+
const startTimes = this._timelineColStartTimes;
806+
const endTimes = this._timelineColEndTimes;
807+
const widths = this._timelineColWidths;
808+
const xPrefix = this._timelineColX;
809+
if (!startTimes?.length || !endTimes?.length || !widths?.length || !xPrefix?.length) {
810+
return 0;
811+
}
812+
if (time <= startTimes[0]) {
813+
return 0;
814+
}
815+
const lastIndex = endTimes.length - 1;
816+
if (time > endTimes[lastIndex]) {
817+
return xPrefix[lastIndex + 1] ?? 0;
818+
}
819+
820+
let low = 0;
821+
let high = lastIndex;
822+
while (low <= high) {
823+
const mid = (low + high) >> 1;
824+
const st = startTimes[mid];
825+
const et = endTimes[mid];
826+
if (time < st) {
827+
high = mid - 1;
828+
} else if (time > et) {
829+
low = mid + 1;
830+
} else {
831+
const duration = Math.max(1, et - st + 1);
832+
const offset = (time - st) / duration;
833+
return (xPrefix[mid] ?? 0) + (widths[mid] ?? 0) * offset;
834+
}
835+
}
836+
837+
const idx = Math.max(0, Math.min(lastIndex, high));
838+
const st = startTimes[idx];
839+
const et = endTimes[idx];
840+
const duration = Math.max(1, et - st + 1);
841+
const offset = Math.max(0, Math.min(1, (time - st) / duration));
842+
return (xPrefix[idx] ?? 0) + (widths[idx] ?? 0) * offset;
843+
}
844+
845+
getDateIndexByTime(time: number) {
846+
const startTimes = this._timelineColStartTimes;
847+
const endTimes = this._timelineColEndTimes;
848+
if (!startTimes?.length || !endTimes?.length) {
849+
return 0;
850+
}
851+
if (time <= startTimes[0]) {
852+
return 0;
853+
}
854+
const lastIndex = endTimes.length - 1;
855+
if (time > endTimes[lastIndex]) {
856+
return lastIndex;
857+
}
858+
let low = 0;
859+
let high = lastIndex;
860+
while (low <= high) {
861+
const mid = (low + high) >> 1;
862+
const st = startTimes[mid];
863+
const et = endTimes[mid];
864+
if (time < st) {
865+
high = mid - 1;
866+
} else if (time > et) {
867+
low = mid + 1;
868+
} else {
869+
return mid;
870+
}
871+
}
872+
return Math.max(0, Math.min(lastIndex, high));
873+
}
874+
875+
getDateIndexByX(x: number) {
876+
const totalX = x + this.stateManager.scroll.horizontalBarPos;
877+
const xPrefix = this._timelineColX;
878+
if (!xPrefix?.length) {
879+
return 0;
880+
}
881+
if (totalX <= 0) {
882+
return 0;
883+
}
884+
const lastIndex = xPrefix.length - 2;
885+
const totalWidth = xPrefix[lastIndex + 1] ?? 0;
886+
if (totalX >= totalWidth) {
887+
return Math.max(0, lastIndex);
888+
}
889+
890+
let low = 0;
891+
let high = lastIndex;
892+
while (low <= high) {
893+
const mid = (low + high) >> 1;
894+
const left = xPrefix[mid] ?? 0;
895+
const right = xPrefix[mid + 1] ?? left;
896+
if (totalX < left) {
897+
high = mid - 1;
898+
} else if (totalX >= right) {
899+
low = mid + 1;
900+
} else {
901+
return mid;
902+
}
903+
}
904+
return Math.max(0, Math.min(lastIndex, low));
752905
}
753906
getRowHeightByIndex(index: number) {
754907
if (this.taskListTableInstance) {
@@ -783,6 +936,10 @@ export class Gantt extends EventTarget {
783936
// return (this.parsedOptions.timeLineHeaderRowHeights as number) * this.timeLineHeaderLevel;
784937
}
785938
getAllDateColsWidth() {
939+
const xPrefix = this._timelineColX;
940+
if (xPrefix?.length) {
941+
return xPrefix[xPrefix.length - 1] ?? 0;
942+
}
786943
return (
787944
this.parsedOptions.timelineColWidth *
788945
(this.parsedOptions.reverseSortedTimelineScales[0].timelineDates?.length ?? 0)
@@ -1389,10 +1546,7 @@ export class Gantt extends EventTarget {
13891546
/** 滚动到scrollToMarkLineDate所指向的日期 */
13901547
_scrollToMarkLine() {
13911548
if (this.parsedOptions.scrollToMarkLineDate && this.parsedOptions.minDate) {
1392-
const minDate = this.parsedOptions.minDate;
1393-
const { unit, step } = this.parsedOptions.reverseSortedTimelineScales[0];
1394-
const count = computeCountToTimeScale(this.parsedOptions.scrollToMarkLineDate, minDate, unit, step);
1395-
const targetDayDistance = count * this.parsedOptions.timelineColWidth;
1549+
const targetDayDistance = this.getXByTime(this.parsedOptions.scrollToMarkLineDate.getTime());
13961550
const left = targetDayDistance - this.tableNoFrameWidth / 2;
13971551
this.stateManager.setScrollLeft(left);
13981552
}
@@ -1402,10 +1556,7 @@ export class Gantt extends EventTarget {
14021556
if (!date || !this.parsedOptions.minDate) {
14031557
return;
14041558
}
1405-
const minDate = this.parsedOptions.minDate;
1406-
const { unit, step } = this.parsedOptions.reverseSortedTimelineScales[0];
1407-
const count = computeCountToTimeScale(date, minDate, unit, step);
1408-
const targetDayDistance = count * this.parsedOptions.timelineColWidth;
1559+
const targetDayDistance = this.getXByTime(date.getTime());
14091560
const left = targetDayDistance - this.tableNoFrameWidth / 2;
14101561
this.stateManager.setScrollLeft(left);
14111562
}
@@ -1487,9 +1638,22 @@ export class Gantt extends EventTarget {
14871638
// }
14881639

14891640
getDateColWidth(dateIndex: number) {
1641+
const widths = this._timelineColWidths;
1642+
if (widths?.length && dateIndex >= 0 && dateIndex < widths.length) {
1643+
return widths[dateIndex] ?? 0;
1644+
}
14901645
return this.parsedOptions.timelineColWidth;
14911646
}
14921647
getDateColsWidth(startDateIndex: number, endDateIndex: number) {
1648+
const xPrefix = this._timelineColX;
1649+
if (xPrefix?.length) {
1650+
const start = Math.max(0, Math.min(startDateIndex, xPrefix.length - 1));
1651+
const end = Math.max(0, Math.min(endDateIndex + 1, xPrefix.length - 1));
1652+
if (end <= start) {
1653+
return 0;
1654+
}
1655+
return (xPrefix[end] ?? 0) - (xPrefix[start] ?? 0);
1656+
}
14931657
return (endDateIndex - startDateIndex + 1) * this.parsedOptions.timelineColWidth;
14941658
}
14951659

packages/vtable-gantt/src/gantt-helper.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,7 @@ function setWidthToDefaultTaskBarStyle(width: number) {
4949
const isNode = typeof window === 'undefined' || typeof window.window === 'undefined';
5050
export const DayTimes = 1000 * 60 * 60 * 24;
5151
export function getDateIndexByX(x: number, gantt: Gantt) {
52-
const totalX = x + gantt.stateManager.scroll.horizontalBarPos;
53-
const firstDateColWidth = gantt.getDateColWidth(0);
54-
const dateIndex = Math.floor((totalX - firstDateColWidth) / gantt.parsedOptions.timelineColWidth) + 1;
55-
return dateIndex;
52+
return gantt.getDateIndexByX(x);
5653
}
5754

5855
export function generateMarkLine(markLine?: boolean | IMarkLine | IMarkLine[]): IMarkLine[] {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export type { IGanttPlugin } from './interface';
2-
export { PluginManager } from './plugin-manager';
2+
export { PluginManager } from './plugin-manager';

0 commit comments

Comments
 (0)