@schukai/monster 3.112.2 → 3.112.4

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +1 -1
  3. package/source/components/form/style/context-help.pcss +1 -1
  4. package/source/components/form/stylesheet/context-help.mjs +1 -1
  5. package/source/components/layout/popper.mjs +1 -1
  6. package/source/components/layout/stylesheet/panel.mjs +13 -6
  7. package/source/components/navigation/stylesheet/table-of-content.mjs +13 -6
  8. package/source/components/navigation/table-of-content.mjs +4 -5
  9. package/source/components/time/day.mjs +407 -0
  10. package/source/components/time/month-calendar.mjs +258 -54
  11. package/source/components/time/style/day.pcss +159 -0
  12. package/source/components/time/style/month-calendar.pcss +24 -14
  13. package/source/components/time/stylesheet/day.mjs +31 -0
  14. package/source/components/time/stylesheet/month-calendar.mjs +1 -1
  15. package/source/components/time/timeline/appointment.mjs +147 -0
  16. package/source/components/time/timeline/segment.mjs +7 -15
  17. package/source/components/time/timeline/style/appointment.pcss +25 -0
  18. package/source/components/time/timeline/style/segment.pcss +10 -1
  19. package/source/components/time/timeline/stylesheet/appointment.mjs +38 -0
  20. package/source/components/time/timeline/stylesheet/segment.mjs +1 -1
  21. package/source/data/pipe.mjs +1 -1
  22. package/source/dom/locale.mjs +3 -2
  23. package/source/i18n/internal.mjs +4 -0
  24. package/source/monster.mjs +1 -1
  25. package/source/types/version.mjs +1 -1
  26. package/test/cases/monster.mjs +1 -1
  27. package/test/web/test.html +2 -2
  28. package/test/web/tests.js +5 -5
  29. package/source/components/time/timeline/collection.mjs +0 -218
  30. package/source/components/time/timeline/item.mjs +0 -192
@@ -1,218 +0,0 @@
1
- // Constants for possible appointment types
2
- import { ID } from "../../../types/id.mjs";
3
- import { isArray } from "../../../types/is.mjs";
4
-
5
- /**
6
- * Helper function: Check if two dates are on the same day (ignoring time)
7
- * @private
8
- * @param {string|Date} date1 - First date
9
- * @param {string|Date} date2 - Second date
10
- * @returns {boolean} True if the dates are on the same day, false otherwise
11
- */
12
- function isSameDay(date1, date2) {
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
- );
20
- }
21
-
22
- class AppointmentCollection {
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
- }
142
- }
143
-
144
- // Example usage
145
- try {
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));
216
- } catch (error) {
217
- console.error("Error:", error.message);
218
- }
@@ -1,192 +0,0 @@
1
- /**
2
- * Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
3
- * Node module: @schukai/monster
4
- *
5
- * This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
6
- * The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
7
- *
8
- * For those who do not wish to adhere to the AGPLv3, a commercial license is available.
9
- * Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
10
- * For more information about purchasing a commercial license, please contact schukai GmbH.
11
- */
12
-
13
- import { ID } from "../../../types/id.mjs";
14
- import { isArray } from "../../../types/is.mjs";
15
- import { BaseWithOptions } from "../../../types/basewithoptions.mjs";
16
-
17
- const AppointmentType = {
18
- TODO: "todo",
19
- TASK: "task",
20
- MILESTONE: "milestone",
21
- EVENT: "event",
22
- REMINDER: "reminder",
23
- MEETING: "meeting",
24
- CALL: "call",
25
- APPOINTMENT: "appointment",
26
- DEADLINE: "deadline",
27
- BIRTHDAY: "birthday",
28
- ANNIVERSARY: "anniversary",
29
- HOLIDAY: "holiday",
30
- VACATION: "vacation",
31
- SICKDAY: "sickday",
32
- OOO: "ooo",
33
- CUSTOM: "custom",
34
- };
35
-
36
- class Item extends BaseWithOptions {
37
- /**
38
- * Creates a new Item.
39
- *
40
- * @param {Object} params - Parameters for creating the item.
41
- * @param {string} [params.id] - Optional ID. If not provided, one will be generated.
42
- * @param {string} params.title - Title of the item.
43
- * @param {string} params.type - Type of the item (must be one of AppointmentType values).
44
- * @param {string|Date} params.startDate - Start date/time.
45
- * @param {string|Date} params.endDate - End date/time.
46
- * @param {string} [params.description=""] - Optional description.
47
- * @param {string|string[]} [params.userIds=[]] - One or more user IDs associated with this item.
48
- * @throws {Error} If type is invalid or dates are not valid or startDate is after endDate.
49
- */
50
- constructor({
51
- id,
52
- title,
53
- type,
54
- startDate,
55
- endDate,
56
- description = "",
57
- userIds = [],
58
- }) {
59
- // Validate item type
60
- if (!Object.values(AppointmentType).includes(type)) {
61
- throw new Error(`Invalid appointment type: ${type}`);
62
- }
63
-
64
- // Convert startDate and endDate to Date objects
65
- this.startDate = new Date(startDate);
66
- this.endDate = new Date(endDate);
67
- if (isNaN(this.startDate.getTime()) || isNaN(this.endDate.getTime())) {
68
- throw new Error("Invalid start or end date");
69
- }
70
- // Ensure startDate is not after endDate
71
- if (this.startDate > this.endDate) {
72
- throw new Error("Start date cannot be after end date");
73
- }
74
-
75
- // Initialize fields
76
- this.id = id || new ID().toString();
77
- this.title = title || "new appointment";
78
- this.type = type || AppointmentType.CUSTOM;
79
- this.description = description || "";
80
-
81
- // Ensure userIds is stored as an array
82
- this.userIds = isArray(userIds) ? userIds : [userIds];
83
- }
84
-
85
- /**
86
- * Calculates the duration of the item in days.
87
- *
88
- * @returns {number} Duration in days.
89
- */
90
- getDurationInDays() {
91
- const msPerDay = 1000 * 60 * 60 * 24;
92
- return Math.ceil((this.endDate - this.startDate) / msPerDay);
93
- }
94
-
95
- /**
96
- * Calculates the duration of the item in hours.
97
- *
98
- * @returns {number} Duration in hours.
99
- */
100
- getDurationInHours() {
101
- const msPerHour = 1000 * 60 * 60;
102
- return Math.ceil((this.endDate - this.startDate) / msPerHour);
103
- }
104
-
105
- /**
106
- * Calculates the duration of the item in minutes.
107
- *
108
- * @returns {number} Duration in minutes.
109
- */
110
- getDurationInMinutes() {
111
- const msPerMinute = 1000 * 60;
112
- return Math.ceil((this.endDate - this.startDate) / msPerMinute);
113
- }
114
-
115
- /**
116
- * Calculates the duration of the item in seconds.
117
- *
118
- * @returns {number} Duration in seconds.
119
- */
120
- getDurationInSeconds() {
121
- const msPerSecond = 1000;
122
- return Math.ceil((this.endDate - this.startDate) / msPerSecond);
123
- }
124
-
125
- /**
126
- * Checks if the item is active on the specified date.
127
- *
128
- * @param {string|Date} date - The date to check.
129
- * @returns {boolean} True if the item is active on the given date.
130
- * @throws {Error} If the provided date is invalid.
131
- */
132
- isActiveOn(date) {
133
- const d = new Date(date);
134
- if (isNaN(d.getTime())) {
135
- throw new Error("Invalid date");
136
- }
137
- return this.startDate <= d && this.endDate >= d;
138
- }
139
-
140
- /**
141
- * Returns a JSON-compatible object representing the item.
142
- *
143
- * @returns {Object} JSON representation of the item.
144
- */
145
- toJSON() {
146
- return {
147
- id: this.id,
148
- title: this.title,
149
- type: this.type,
150
- description: this.description,
151
- userIds: this.userIds,
152
- startDate: this.startDate.toISOString(),
153
- endDate: this.endDate.toISOString(),
154
- };
155
- }
156
-
157
- /**
158
- * Creates a new Item instance from a JSON object.
159
- *
160
- * @param {Object} json - The JSON object.
161
- * @param {string} json.id - The ID of the item.
162
- * @param {string} json.title - The title of the item.
163
- * @param {string} json.type - The type of the item.
164
- * @param {string} json.startDate - The start date in ISO format.
165
- * @param {string} json.endDate - The end date in ISO format.
166
- * @param {string} [json.description=""] - The description of the item.
167
- * @param {string|string[]} [json.userIds=[]] - One or more user IDs.
168
- * @returns {Item} A new Item instance.
169
- */
170
- static fromJson(json) {
171
- return new Item({
172
- id: json.id,
173
- title: json.title,
174
- type: json.type,
175
- startDate: json.startDate,
176
- endDate: json.endDate,
177
- description: json.description,
178
- userIds: json.userIds,
179
- });
180
- }
181
-
182
- /**
183
- * Returns a readable string representation of the item.
184
- *
185
- * @returns {string} String representation of the item.
186
- */
187
- toString() {
188
- return `[${this.type}] ${this.title} (${this.startDate.toLocaleString()} - ${this.endDate.toLocaleString()})`;
189
- }
190
- }
191
-
192
- export { Item, AppointmentType };