@schukai/monster 3.112.0 → 3.112.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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/source/components/time/month-calendar.mjs +17 -12
- package/source/components/time/stylesheet/month-calendar.mjs +13 -6
- package/source/components/time/timeline/collection.mjs +200 -187
- package/source/components/time/timeline/item.mjs +159 -151
- package/source/components/tree-menu/tree-menu.mjs +36 -20
- package/source/monster.mjs +1 -1
- package/source/types/version.mjs +1 -1
- package/test/cases/monster.mjs +1 -1
- package/test/web/test.html +2 -2
- package/test/web/tests.js +7 -4
@@ -1,6 +1,6 @@
|
|
1
1
|
// Constants for possible appointment types
|
2
|
-
import {ID} from "../../../types/id.mjs";
|
3
|
-
import {isArray} from "../../../types/is.mjs";
|
2
|
+
import { ID } from "../../../types/id.mjs";
|
3
|
+
import { isArray } from "../../../types/is.mjs";
|
4
4
|
|
5
5
|
/**
|
6
6
|
* Helper function: Check if two dates are on the same day (ignoring time)
|
@@ -8,198 +8,211 @@ import {isArray} from "../../../types/is.mjs";
|
|
8
8
|
* @param {string|Date} date1 - First date
|
9
9
|
* @param {string|Date} date2 - Second date
|
10
10
|
* @returns {boolean} True if the dates are on the same day, false otherwise
|
11
|
-
|
11
|
+
*/
|
12
12
|
function isSameDay(date1, date2) {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
const d1 = new Date(date1);
|
14
|
+
const d2 = new Date(date2);
|
15
|
+
return (
|
16
|
+
d1.getFullYear() === d2.getFullYear() &&
|
17
|
+
d1.getMonth() === d2.getMonth() &&
|
18
|
+
d1.getDate() === d2.getDate()
|
19
|
+
);
|
18
20
|
}
|
19
21
|
|
20
22
|
class AppointmentCollection {
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
23
|
+
constructor() {
|
24
|
+
this.appointments = [];
|
25
|
+
}
|
26
|
+
|
27
|
+
// Add an appointment to the collection
|
28
|
+
addAppointment(appointment) {
|
29
|
+
if (!(appointment instanceof TimelineItem)) {
|
30
|
+
throw new Error("Only instances of Appointment can be added");
|
31
|
+
}
|
32
|
+
this.appointments.push(appointment);
|
33
|
+
}
|
34
|
+
|
35
|
+
// Get all appointments that overlap with the given date range
|
36
|
+
getAppointmentsInRange(rangeStart, rangeEnd) {
|
37
|
+
const start = new Date(rangeStart);
|
38
|
+
const end = new Date(rangeEnd);
|
39
|
+
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
|
40
|
+
throw new Error("Invalid date range");
|
41
|
+
}
|
42
|
+
return this.appointments.filter(
|
43
|
+
(app) => app.endDate >= start && app.startDate <= end,
|
44
|
+
);
|
45
|
+
}
|
46
|
+
|
47
|
+
// Get appointments that start on a specific day
|
48
|
+
getAppointmentsStartingOn(date) {
|
49
|
+
return this.appointments.filter((app) => isSameDay(app.startDate, date));
|
50
|
+
}
|
51
|
+
|
52
|
+
// Get appointments that end on a specific day
|
53
|
+
getAppointmentsEndingOn(date) {
|
54
|
+
return this.appointments.filter((app) => isSameDay(app.endDate, date));
|
55
|
+
}
|
56
|
+
|
57
|
+
// Get appointments active on a specific day (the day falls between startDate and endDate, inclusive)
|
58
|
+
getAppointmentsForDay(date) {
|
59
|
+
const d = new Date(date);
|
60
|
+
return this.appointments.filter(
|
61
|
+
(app) => app.startDate <= d && app.endDate >= d,
|
62
|
+
);
|
63
|
+
}
|
64
|
+
|
65
|
+
// Get appointments where the specified date is strictly between the start and end dates
|
66
|
+
getAppointmentsWithDateAsMiddleDay(date) {
|
67
|
+
const d = new Date(date);
|
68
|
+
return this.appointments.filter(
|
69
|
+
(app) =>
|
70
|
+
app.startDate < d &&
|
71
|
+
app.endDate > d &&
|
72
|
+
!isSameDay(app.startDate, d) &&
|
73
|
+
!isSameDay(app.endDate, d),
|
74
|
+
);
|
75
|
+
}
|
76
|
+
|
77
|
+
// Get appointments filtered by type (e.g., all milestones)
|
78
|
+
getAppointmentsByType(type) {
|
79
|
+
return this.appointments.filter((app) => app.type === type);
|
80
|
+
}
|
81
|
+
|
82
|
+
// Export the entire collection as JSON
|
83
|
+
toJSON() {
|
84
|
+
return this.appointments.map((app) => app.toJSON());
|
85
|
+
}
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Splits an appointment into slices of a specified duration (in days).
|
89
|
+
* If the appointment is provided as an ID, it will be looked up in the collection.
|
90
|
+
* Each slice is returned as an object with the original appointment's properties,
|
91
|
+
* plus sliceIndex and the new startDate/endDate for the slice.
|
92
|
+
*
|
93
|
+
* @param {TimelineItem|string} appointmentOrId - The appointment instance or its ID.
|
94
|
+
* @param {number} sliceDurationDays - Duration of each slice in days.
|
95
|
+
* @returns {Array} Array of slice objects.
|
96
|
+
*/
|
97
|
+
splitAppointment(appointmentOrId, sliceDurationDays) {
|
98
|
+
if (typeof sliceDurationDays !== "number" || sliceDurationDays <= 0) {
|
99
|
+
throw new Error("Slice duration must be a positive number");
|
100
|
+
}
|
101
|
+
|
102
|
+
let appointment;
|
103
|
+
if (appointmentOrId instanceof TimelineItem) {
|
104
|
+
appointment = appointmentOrId;
|
105
|
+
} else {
|
106
|
+
appointment = this.appointments.find((app) => app.id === appointmentOrId);
|
107
|
+
if (!appointment) {
|
108
|
+
throw new Error("Appointment not found");
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
const slices = [];
|
113
|
+
let currentStart = new Date(appointment.startDate);
|
114
|
+
let sliceIndex = 0;
|
115
|
+
|
116
|
+
while (currentStart < appointment.endDate) {
|
117
|
+
const currentEnd = new Date(currentStart);
|
118
|
+
currentEnd.setDate(currentEnd.getDate() + sliceDurationDays);
|
119
|
+
if (currentEnd > appointment.endDate) {
|
120
|
+
currentEnd.setTime(appointment.endDate.getTime());
|
121
|
+
}
|
122
|
+
|
123
|
+
const slice = {
|
124
|
+
parentId: appointment.id,
|
125
|
+
sliceIndex: sliceIndex,
|
126
|
+
title: appointment.title,
|
127
|
+
type: appointment.type,
|
128
|
+
description: appointment.description,
|
129
|
+
userIds: appointment.userIds,
|
130
|
+
startDate: currentStart.toISOString(),
|
131
|
+
endDate: currentEnd.toISOString(),
|
132
|
+
};
|
133
|
+
slices.push(slice);
|
134
|
+
|
135
|
+
// Prepare for next slice
|
136
|
+
currentStart = currentEnd;
|
137
|
+
sliceIndex++;
|
138
|
+
}
|
139
|
+
|
140
|
+
return slices;
|
141
|
+
}
|
136
142
|
}
|
137
143
|
|
138
144
|
// Example usage
|
139
145
|
try {
|
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
|
-
|
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
|
-
|
146
|
+
// Create some appointments with user associations
|
147
|
+
const appointment1 = new TimelineItem({
|
148
|
+
title: "Team Meeting",
|
149
|
+
type: AppointmentType.TASK,
|
150
|
+
startDate: "2025-03-05T09:00:00",
|
151
|
+
endDate: "2025-03-05T10:00:00",
|
152
|
+
description: "Weekly team meeting",
|
153
|
+
userIds: ["user1", "user2"],
|
154
|
+
});
|
155
|
+
|
156
|
+
const appointment2 = new TimelineItem({
|
157
|
+
title: "Project Kickoff",
|
158
|
+
type: AppointmentType.MILESTONE,
|
159
|
+
startDate: "2025-03-06T08:00:00",
|
160
|
+
endDate: "2025-03-06T12:00:00",
|
161
|
+
description: "Kickoff for the new project",
|
162
|
+
userIds: "user3", // single user id; will be converted to an array
|
163
|
+
});
|
164
|
+
|
165
|
+
// An appointment spanning over 30 days
|
166
|
+
const appointment3 = new TimelineItem({
|
167
|
+
title: "Long Term Project",
|
168
|
+
type: AppointmentType.TASK,
|
169
|
+
startDate: "2025-04-01T09:00:00",
|
170
|
+
endDate: "2025-05-15T17:00:00",
|
171
|
+
description: "Project spanning multiple weeks",
|
172
|
+
userIds: ["user4"],
|
173
|
+
});
|
174
|
+
|
175
|
+
// Create an appointment collection and add appointments
|
176
|
+
const collection = new AppointmentCollection();
|
177
|
+
collection.addAppointment(appointment1);
|
178
|
+
collection.addAppointment(appointment2);
|
179
|
+
collection.addAppointment(appointment3);
|
180
|
+
|
181
|
+
console.log("Appointments on 2025-03-05:");
|
182
|
+
collection
|
183
|
+
.getAppointmentsForDay("2025-03-05")
|
184
|
+
.forEach((app) => console.log(app.toString()));
|
185
|
+
|
186
|
+
console.log("\nAppointments in range 2025-03-05 to 2025-03-06:");
|
187
|
+
collection
|
188
|
+
.getAppointmentsInRange("2025-03-05T00:00:00", "2025-03-06T23:59:59")
|
189
|
+
.forEach((app) => console.log(app.toString()));
|
190
|
+
|
191
|
+
console.log("\nAll milestones:");
|
192
|
+
collection
|
193
|
+
.getAppointmentsByType(AppointmentType.MILESTONE)
|
194
|
+
.forEach((app) => console.log(app.toString()));
|
195
|
+
|
196
|
+
// Split appointment3 into weekly slices (7-day slices)
|
197
|
+
console.log("\nWeekly slices for Long Term Project:");
|
198
|
+
const weeklySlices = collection.splitAppointment(appointment3, 7);
|
199
|
+
weeklySlices.forEach((slice) => {
|
200
|
+
console.log(
|
201
|
+
`Slice ${slice.sliceIndex}: ${slice.startDate} to ${slice.endDate}`,
|
202
|
+
);
|
203
|
+
});
|
204
|
+
|
205
|
+
// Split appointment3 into 14-day slices
|
206
|
+
console.log("\n14-day slices for Long Term Project:");
|
207
|
+
const biweeklySlices = collection.splitAppointment(appointment3, 14);
|
208
|
+
biweeklySlices.forEach((slice) => {
|
209
|
+
console.log(
|
210
|
+
`Slice ${slice.sliceIndex}: ${slice.startDate} to ${slice.endDate}`,
|
211
|
+
);
|
212
|
+
});
|
213
|
+
|
214
|
+
console.log("\nJSON Export of the collection:");
|
215
|
+
console.log(JSON.stringify(collection.toJSON(), null, 2));
|
203
216
|
} catch (error) {
|
204
|
-
|
217
|
+
console.error("Error:", error.message);
|
205
218
|
}
|