Improve toolbox module loading and goal tree previews
All checks were successful
Deploy Sokko G / deploy (push) Successful in 15s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 15s
This commit is contained in:
parent
abfa23fb3e
commit
50029b45a2
9 changed files with 259 additions and 177 deletions
|
|
@ -25,16 +25,18 @@ import {
|
|||
parseLocalDateKey,
|
||||
timeValueToMinutes
|
||||
} from "./calendarUtils.js";
|
||||
import {
|
||||
HOUR_HALF_SLOT_COUNT,
|
||||
WEEK_COMPACT_EVENT_HEIGHT,
|
||||
WEEK_SLOT_COUNT,
|
||||
WEEKDAY_LABELS,
|
||||
getMonthOccurrenceSegments,
|
||||
getWeekGridLayout,
|
||||
getWeekMinutePosition,
|
||||
layoutConcurrentOccurrences
|
||||
} from "./calendarLayout.js";
|
||||
import { ALERT_MODES } from "./timerOptions.js";
|
||||
|
||||
const DAY_HOUR_COUNT = 24;
|
||||
const HOUR_HALF_SLOT_COUNT = 2;
|
||||
const WEEK_SLOT_COUNT = DAY_HOUR_COUNT * HOUR_HALF_SLOT_COUNT;
|
||||
const WEEK_HOUR_ROW_HEIGHT = 27;
|
||||
const WEEK_HIDDEN_ROW_HEIGHT = 20;
|
||||
const WEEK_COMPACT_EVENT_HEIGHT = WEEK_HOUR_ROW_HEIGHT / HOUR_HALF_SLOT_COUNT;
|
||||
const WEEKDAY_LABELS = ["lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche"];
|
||||
|
||||
const CALENDAR_COLOR_VALUES = {
|
||||
violet: "#8b5cf6",
|
||||
gold: "#f6c453",
|
||||
|
|
@ -163,149 +165,6 @@ function normalizeDraftAlertOffsets(value) {
|
|||
return offsets.sort((a, b) => b - a);
|
||||
}
|
||||
|
||||
function occurrencesOverlap(first, second) {
|
||||
const firstEnd = first.endMs > first.startMs ? first.endMs : first.startMs + WEEK_SLOT_MINUTES * 60000;
|
||||
const secondEnd = second.endMs > second.startMs ? second.endMs : second.startMs + WEEK_SLOT_MINUTES * 60000;
|
||||
return first.startMs < secondEnd && second.startMs < firstEnd;
|
||||
}
|
||||
|
||||
function layoutConcurrentOccurrences(dayOccurrences) {
|
||||
const laneEndMs = [0, 0];
|
||||
return dayOccurrences
|
||||
.map((occurrence, index, all) => {
|
||||
const lane = laneEndMs[0] <= occurrence.startMs ? 0 : 1;
|
||||
laneEndMs[lane] = occurrence.endMs > occurrence.startMs ? occurrence.endMs : occurrence.startMs + WEEK_SLOT_MINUTES * 60000;
|
||||
const overlaps = all.some((item, itemIndex) => itemIndex !== index && occurrencesOverlap(occurrence, item));
|
||||
return { ...occurrence, lane, laneCount: overlaps ? 2 : 1 };
|
||||
});
|
||||
}
|
||||
|
||||
function eventMinutesRange(occurrence) {
|
||||
const startMinutes = Math.max(0, Math.min(24 * 60 - 1, Number(occurrence.event.startMinutes) || 0));
|
||||
const endMinutes = occurrence.event.endMinutes && occurrence.event.endMinutes > startMinutes
|
||||
? occurrence.event.endMinutes
|
||||
: startMinutes + WEEK_SLOT_MINUTES;
|
||||
return {
|
||||
startMinutes,
|
||||
endMinutes: Math.max(startMinutes + WEEK_SLOT_MINUTES, Math.min(24 * 60, endMinutes))
|
||||
};
|
||||
}
|
||||
|
||||
function getWeekGridLayout(occurrences, hideEmptyHours) {
|
||||
const occupiedHours = Array.from({ length: DAY_HOUR_COUNT }, () => false);
|
||||
occurrences.forEach((occurrence) => {
|
||||
const { startMinutes, endMinutes } = eventMinutesRange(occurrence);
|
||||
for (let hourIndex = 0; hourIndex < DAY_HOUR_COUNT; hourIndex += 1) {
|
||||
const hourStart = hourIndex * 60;
|
||||
const hourEnd = hourStart + 60;
|
||||
if (startMinutes < hourEnd && endMinutes > hourStart) occupiedHours[hourIndex] = true;
|
||||
}
|
||||
});
|
||||
|
||||
const rows = [];
|
||||
let hourIndex = 0;
|
||||
while (hourIndex < DAY_HOUR_COUNT) {
|
||||
if (!hideEmptyHours || occupiedHours[hourIndex]) {
|
||||
rows.push({ type: "hour", hourIndex });
|
||||
hourIndex += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
const runStart = hourIndex;
|
||||
while (hourIndex < DAY_HOUR_COUNT && !occupiedHours[hourIndex]) hourIndex += 1;
|
||||
const runEnd = hourIndex - 1;
|
||||
if (runEnd - runStart + 1 >= 3) {
|
||||
rows.push({ type: "hour", hourIndex: runStart });
|
||||
rows.push({ type: "hidden", startHour: runStart + 1, endHour: runEnd - 1 });
|
||||
rows.push({ type: "hour", hourIndex: runEnd });
|
||||
} else {
|
||||
for (let visibleHour = runStart; visibleHour <= runEnd; visibleHour += 1) rows.push({ type: "hour", hourIndex: visibleHour });
|
||||
}
|
||||
}
|
||||
|
||||
const hourTopPx = {};
|
||||
const hiddenRanges = [];
|
||||
let topPx = 0;
|
||||
rows.forEach((row) => {
|
||||
row.topPx = topPx;
|
||||
if (row.type === "hidden") {
|
||||
hiddenRanges.push(row);
|
||||
topPx += WEEK_HIDDEN_ROW_HEIGHT;
|
||||
return;
|
||||
}
|
||||
hourTopPx[row.hourIndex] = topPx;
|
||||
topPx += WEEK_HOUR_ROW_HEIGHT;
|
||||
});
|
||||
|
||||
return {
|
||||
rows,
|
||||
hourTopPx,
|
||||
hiddenRanges,
|
||||
totalHeightPx: topPx,
|
||||
gridTemplateRows: rows.map((row) => `${row.type === "hidden" ? WEEK_HIDDEN_ROW_HEIGHT : WEEK_HOUR_ROW_HEIGHT}px`).join(" ")
|
||||
};
|
||||
}
|
||||
|
||||
function getWeekMinutePosition(layout, minutes) {
|
||||
const boundedMinutes = Math.max(0, Math.min(24 * 60, minutes));
|
||||
const hourIndex = Math.min(DAY_HOUR_COUNT - 1, Math.floor(boundedMinutes / 60));
|
||||
const visibleHourTop = layout.hourTopPx[hourIndex];
|
||||
if (visibleHourTop !== undefined) {
|
||||
return visibleHourTop + ((boundedMinutes - hourIndex * 60) / 60) * WEEK_HOUR_ROW_HEIGHT;
|
||||
}
|
||||
|
||||
const hiddenRange = layout.hiddenRanges.find((range) => hourIndex >= range.startHour && hourIndex <= range.endHour);
|
||||
if (hiddenRange) {
|
||||
const rangeStartMinutes = hiddenRange.startHour * 60;
|
||||
const rangeMinutes = (hiddenRange.endHour - hiddenRange.startHour + 1) * 60;
|
||||
return hiddenRange.topPx + ((boundedMinutes - rangeStartMinutes) / rangeMinutes) * WEEK_HIDDEN_ROW_HEIGHT;
|
||||
}
|
||||
|
||||
return layout.totalHeightPx;
|
||||
}
|
||||
|
||||
function getMonthOccurrenceSegments(days, occurrences) {
|
||||
const laneUsage = days.map(() => [false, false, false]);
|
||||
const segments = occurrences.flatMap((occurrence) => {
|
||||
const coveredIndexes = days
|
||||
.map((day, index) => {
|
||||
const dayStart = day.date.getTime();
|
||||
const dayEnd = addDays(day.date, 1).getTime();
|
||||
const occurrenceEnd = Math.max(occurrence.endMs, occurrence.startMs + WEEK_SLOT_MINUTES * 60000);
|
||||
return day.inMonth && occurrence.startMs < dayEnd && occurrenceEnd > dayStart ? index : -1;
|
||||
})
|
||||
.filter((index) => index >= 0);
|
||||
if (!coveredIndexes.length) return [];
|
||||
|
||||
const lane = [0, 1, 2].find((candidate) => coveredIndexes.every((index) => !laneUsage[index][candidate])) ?? 2;
|
||||
coveredIndexes.forEach((index) => {
|
||||
laneUsage[index][lane] = true;
|
||||
});
|
||||
|
||||
const segments = [];
|
||||
let segmentStart = coveredIndexes[0];
|
||||
let previous = coveredIndexes[0];
|
||||
coveredIndexes.slice(1).forEach((index) => {
|
||||
const sameWeekRow = Math.floor(index / WEEKDAY_LABELS.length) === Math.floor(previous / WEEKDAY_LABELS.length);
|
||||
if (index === previous + 1 && sameWeekRow) {
|
||||
previous = index;
|
||||
return;
|
||||
}
|
||||
segments.push({ occurrence, lane, startIndex: segmentStart, endIndex: previous });
|
||||
segmentStart = index;
|
||||
previous = index;
|
||||
});
|
||||
segments.push({ occurrence, lane, startIndex: segmentStart, endIndex: previous });
|
||||
return segments;
|
||||
});
|
||||
return segments.map((segment) => {
|
||||
const laneCount = Math.max(1, ...segments
|
||||
.filter((item) => item.startIndex <= segment.endIndex && item.endIndex >= segment.startIndex)
|
||||
.map((item) => item.lane + 1));
|
||||
return { ...segment, laneCount };
|
||||
});
|
||||
}
|
||||
|
||||
export function CalendarModule({ toolboxId, moduleId, context, editing = false, setEditing, textExchangeOpen }) {
|
||||
const data = context.normalizeCalendarData(context.getModuleData(toolboxId, moduleId, { initialized: false, viewMode: "week", events: [] }));
|
||||
const textContent = context.moduleText?.calendar || {};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue