2023-04-17 15:44:55 +02:00
|
|
|
<div class="timeline-container">
|
|
|
|
<div class="period" style="left: 0%; width: 20%">
|
|
|
|
<div class="period-handle left"></div>
|
|
|
|
<div class="period-handle right"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
|
|
|
|
|
|
|
|
|
|
const timelineContainer = document.querySelector(".timeline-container");
|
|
|
|
const periodTimeLine = document.querySelector(".period");
|
2023-04-25 22:59:06 +02:00
|
|
|
const t_start = {{ t_start }}
|
|
|
|
const t_end = {{ t_end }}
|
2023-04-17 15:44:55 +02:00
|
|
|
|
|
|
|
function createTicks() {
|
2023-04-25 22:59:06 +02:00
|
|
|
let i = t_start
|
|
|
|
|
|
|
|
while (i <= t_end) {
|
2023-04-17 15:44:55 +02:00
|
|
|
const hourTick = document.createElement("div");
|
|
|
|
hourTick.classList.add("tick", "hour");
|
2023-04-25 22:59:06 +02:00
|
|
|
hourTick.style.left = `${((i - t_start) / (t_end - t_start)) * 100}%`;
|
2023-04-17 15:44:55 +02:00
|
|
|
timelineContainer.appendChild(hourTick);
|
|
|
|
|
|
|
|
const tickLabel = document.createElement("div");
|
|
|
|
tickLabel.classList.add("tick-label");
|
2023-04-25 22:59:06 +02:00
|
|
|
tickLabel.style.left = `${((i - t_start) / (t_end - t_start)) * 100}%`;
|
|
|
|
tickLabel.textContent = numberToTime(i);
|
2023-04-17 15:44:55 +02:00
|
|
|
timelineContainer.appendChild(tickLabel);
|
|
|
|
|
2023-04-25 22:59:06 +02:00
|
|
|
if (i < t_end) {
|
|
|
|
let j = Math.floor(i + 1)
|
|
|
|
|
|
|
|
while (i < j) {
|
|
|
|
i += 0.25;
|
|
|
|
|
|
|
|
if (i <= t_end) {
|
|
|
|
const quarterTick = document.createElement("div");
|
|
|
|
quarterTick.classList.add("tick", "quarter");
|
|
|
|
quarterTick.style.left = `${computePercentage(i, t_start)}%`;
|
|
|
|
timelineContainer.appendChild(quarterTick);
|
|
|
|
}
|
|
|
|
|
2023-04-17 15:44:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-25 22:59:06 +02:00
|
|
|
function numberToTime(num) {
|
|
|
|
const integer = Math.floor(num)
|
|
|
|
const decimal = (num % 1) * 60
|
|
|
|
|
|
|
|
let dec = `:${decimal}`
|
|
|
|
if (decimal < 10) {
|
|
|
|
dec = `:0${decimal}`
|
|
|
|
}
|
|
|
|
|
|
|
|
let int = `${integer}`
|
|
|
|
if (integer < 10) {
|
|
|
|
int = `0${integer}`
|
|
|
|
}
|
|
|
|
|
|
|
|
return int + dec
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-04-17 15:44:55 +02:00
|
|
|
function snapToQuarter(value) {
|
|
|
|
return Math.round(value * 4) / 4;
|
|
|
|
}
|
|
|
|
|
2023-04-19 20:58:15 +02:00
|
|
|
function setupTimeLine(callback) {
|
|
|
|
const func_call = callback ? callback : () => { }
|
|
|
|
timelineContainer.addEventListener("mousedown", (event) => {
|
|
|
|
const startX = event.clientX;
|
|
|
|
|
|
|
|
if (event.target === periodTimeLine) {
|
|
|
|
const startLeft = parseFloat(periodTimeLine.style.left);
|
|
|
|
|
|
|
|
const onMouseMove = (moveEvent) => {
|
|
|
|
const deltaX = moveEvent.clientX - startX;
|
|
|
|
const containerWidth = timelineContainer.clientWidth;
|
2023-04-17 15:44:55 +02:00
|
|
|
const newLeft = startLeft + (deltaX / containerWidth) * 100;
|
2023-04-19 20:58:15 +02:00
|
|
|
|
|
|
|
adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width));
|
|
|
|
};
|
|
|
|
|
|
|
|
document.addEventListener("mousemove", onMouseMove);
|
|
|
|
document.addEventListener(
|
|
|
|
"mouseup",
|
|
|
|
() => {
|
|
|
|
generateAllEtudRow();
|
|
|
|
snapHandlesToQuarters()
|
|
|
|
document.removeEventListener("mousemove", onMouseMove);
|
|
|
|
func_call()
|
|
|
|
},
|
|
|
|
{ once: true }
|
|
|
|
);
|
|
|
|
} else if (event.target.classList.contains("period-handle")) {
|
|
|
|
const startWidth = parseFloat(periodTimeLine.style.width);
|
|
|
|
const startLeft = parseFloat(periodTimeLine.style.left);
|
|
|
|
const isLeftHandle = event.target.classList.contains("left");
|
|
|
|
|
|
|
|
const onMouseMove = (moveEvent) => {
|
|
|
|
const deltaX = moveEvent.clientX - startX;
|
|
|
|
const containerWidth = timelineContainer.clientWidth;
|
|
|
|
const newWidth =
|
|
|
|
startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100;
|
|
|
|
|
|
|
|
if (isLeftHandle) {
|
|
|
|
const newLeft = startLeft + (deltaX / containerWidth) * 100;
|
|
|
|
adjustPeriodPosition(newLeft, newWidth);
|
|
|
|
} else {
|
|
|
|
adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
document.addEventListener("mousemove", onMouseMove);
|
|
|
|
document.addEventListener(
|
|
|
|
"mouseup",
|
|
|
|
() => {
|
|
|
|
snapHandlesToQuarters()
|
|
|
|
generateAllEtudRow();
|
|
|
|
|
|
|
|
document.removeEventListener("mousemove", onMouseMove);
|
|
|
|
|
|
|
|
func_call()
|
|
|
|
|
|
|
|
},
|
|
|
|
{ once: true }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2023-04-17 15:44:55 +02:00
|
|
|
|
|
|
|
function adjustPeriodPosition(newLeft, newWidth) {
|
|
|
|
|
|
|
|
const snappedLeft = snapToQuarter(newLeft);
|
|
|
|
const snappedWidth = snapToQuarter(newWidth);
|
|
|
|
const minLeft = 0;
|
|
|
|
const maxLeft = 100 - snappedWidth;
|
|
|
|
|
|
|
|
const clampedLeft = Math.min(Math.max(snappedLeft, minLeft), maxLeft);
|
|
|
|
|
|
|
|
periodTimeLine.style.left = `${clampedLeft}%`;
|
|
|
|
periodTimeLine.style.width = `${snappedWidth}%`;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPeriodValues() {
|
|
|
|
const leftPercentage = parseFloat(periodTimeLine.style.left);
|
|
|
|
const widthPercentage = parseFloat(periodTimeLine.style.width);
|
|
|
|
|
2023-04-25 22:59:06 +02:00
|
|
|
const startHour = (leftPercentage / 100) * (t_end - t_start) + t_start;
|
|
|
|
const endHour = ((leftPercentage + widthPercentage) / 100) * (t_end - t_start) + t_start;
|
2023-04-17 15:44:55 +02:00
|
|
|
|
|
|
|
const startValue = Math.round(startHour * 4) / 4;
|
|
|
|
const endValue = Math.round(endHour * 4) / 4;
|
|
|
|
|
2023-04-25 22:59:06 +02:00
|
|
|
const computedValues = [Math.max(startValue, t_start), Math.min(t_end, endValue)]
|
|
|
|
|
|
|
|
if (computedValues[0] > t_end || computedValues[1] < t_start) {
|
|
|
|
return [8, 10]
|
|
|
|
}
|
|
|
|
|
|
|
|
if (computedValues[1] - computedValues[0] <= 0.25 && computedValues[1] < t_end - 0.25) {
|
|
|
|
computedValues[1] += 0.25;
|
|
|
|
}
|
|
|
|
|
|
|
|
return computedValues
|
2023-04-17 15:44:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function setPeriodValues(deb, fin) {
|
2023-04-25 22:59:06 +02:00
|
|
|
let leftPercentage = (deb - t_start) / (t_end - t_start) * 100
|
|
|
|
let widthPercentage = (fin - deb) / (t_end - t_start) * 100
|
2023-04-17 15:44:55 +02:00
|
|
|
periodTimeLine.style.left = `${leftPercentage}%`
|
|
|
|
periodTimeLine.style.width = `${widthPercentage}%`
|
|
|
|
|
|
|
|
snapHandlesToQuarters()
|
|
|
|
generateAllEtudRow();
|
|
|
|
}
|
|
|
|
|
|
|
|
function snapHandlesToQuarters() {
|
|
|
|
const periodValues = getPeriodValues();
|
2023-04-25 22:59:06 +02:00
|
|
|
let lef = Math.min(computePercentage(periodValues[0], t_start), computePercentage(t_end, 0.25))
|
2023-04-17 15:44:55 +02:00
|
|
|
if (lef < 0) {
|
|
|
|
lef = 0;
|
|
|
|
}
|
|
|
|
const left = `${lef}%`
|
2023-04-25 22:59:06 +02:00
|
|
|
|
|
|
|
let wid = Math.max(computePercentage(periodValues[1], periodValues[0]), computePercentage(0.25, 0))
|
2023-04-17 15:44:55 +02:00
|
|
|
if (wid > 100) {
|
|
|
|
wid = 100;
|
|
|
|
}
|
|
|
|
const width = `${wid}%`
|
|
|
|
periodTimeLine.style.left = left;
|
|
|
|
periodTimeLine.style.width = width;
|
|
|
|
}
|
|
|
|
|
2023-04-25 22:59:06 +02:00
|
|
|
function computePercentage(a, b) {
|
|
|
|
return ((a - b) / (t_end - t_start)) * 100
|
|
|
|
}
|
|
|
|
|
2023-04-17 15:44:55 +02:00
|
|
|
createTicks();
|
2023-04-25 22:59:06 +02:00
|
|
|
setPeriodValues(8, 9)
|
2023-04-17 15:44:55 +02:00
|
|
|
|
|
|
|
</script>
|
|
|
|
<style>
|
|
|
|
.timeline-container {
|
|
|
|
width: 75%;
|
|
|
|
margin-left: 5%;
|
|
|
|
background-color: white;
|
|
|
|
border-radius: 15px;
|
|
|
|
position: relative;
|
|
|
|
height: 40px;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
.tick {
|
|
|
|
position: absolute;
|
|
|
|
bottom: 0;
|
|
|
|
width: 1px;
|
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
.tick.hour {
|
|
|
|
height: 100%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tick.quarter {
|
|
|
|
height: 50%;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tick-label {
|
|
|
|
position: absolute;
|
|
|
|
bottom: 0;
|
|
|
|
font-size: 12px;
|
|
|
|
text-align: center;
|
|
|
|
transform: translateY(100%) translateX(-50%);
|
|
|
|
user-select: none;
|
|
|
|
-webkit-user-select: none;
|
|
|
|
-moz-user-select: none;
|
|
|
|
-ms-user-select: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.period {
|
|
|
|
position: absolute;
|
|
|
|
height: 100%;
|
|
|
|
background-color: rgba(0, 183, 255, 0.5);
|
|
|
|
border-radius: 15px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.period-handle {
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
bottom: 0;
|
|
|
|
width: 8px;
|
|
|
|
border-radius: 0 4px 4px 0;
|
|
|
|
cursor: col-resize;
|
|
|
|
}
|
|
|
|
|
|
|
|
.period-handle.right {
|
|
|
|
right: 0;
|
|
|
|
border-radius: 4px 0 0 4px;
|
|
|
|
}
|
|
|
|
</style>
|