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");
|
|
|
|
|
|
|
|
function createTicks() {
|
|
|
|
for (let i = 8; i <= 18; i++) {
|
|
|
|
const hourTick = document.createElement("div");
|
|
|
|
hourTick.classList.add("tick", "hour");
|
|
|
|
hourTick.style.left = `${((i - 8) / 10) * 100}%`;
|
|
|
|
timelineContainer.appendChild(hourTick);
|
|
|
|
|
|
|
|
const tickLabel = document.createElement("div");
|
|
|
|
tickLabel.classList.add("tick-label");
|
|
|
|
tickLabel.style.left = `${((i - 8) / 10) * 100}%`;
|
|
|
|
tickLabel.textContent = i < 10 ? `0${i}:00` : `${i}:00`;
|
|
|
|
timelineContainer.appendChild(tickLabel);
|
|
|
|
|
|
|
|
if (i < 18) {
|
|
|
|
for (let j = 1; j < 4; j++) {
|
|
|
|
const quarterTick = document.createElement("div");
|
|
|
|
quarterTick.classList.add("tick", "quarter");
|
|
|
|
quarterTick.style.left = `${((i - 8 + j / 4) / 10) * 100}%`;
|
|
|
|
timelineContainer.appendChild(quarterTick);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
const startHour = (leftPercentage / 100) * 10 + 8;
|
|
|
|
const endHour = ((leftPercentage + widthPercentage) / 100) * 10 + 8;
|
|
|
|
|
|
|
|
const startValue = Math.round(startHour * 4) / 4;
|
|
|
|
const endValue = Math.round(endHour * 4) / 4;
|
|
|
|
|
|
|
|
return [startValue, endValue]
|
|
|
|
}
|
|
|
|
|
|
|
|
function setPeriodValues(deb, fin) {
|
|
|
|
let leftPercentage = (deb - 8) / 10 * 100
|
|
|
|
let widthPercentage = (fin - deb) / 10 * 100
|
|
|
|
periodTimeLine.style.left = `${leftPercentage}%`
|
|
|
|
periodTimeLine.style.width = `${widthPercentage}%`
|
|
|
|
|
|
|
|
snapHandlesToQuarters()
|
|
|
|
generateAllEtudRow();
|
|
|
|
}
|
|
|
|
|
|
|
|
function snapHandlesToQuarters() {
|
|
|
|
const periodValues = getPeriodValues();
|
|
|
|
let lef = Math.min((periodValues[0] - 8) * 10, 97.5)
|
|
|
|
if (lef < 0) {
|
|
|
|
lef = 0;
|
|
|
|
}
|
|
|
|
const left = `${lef}%`
|
|
|
|
let wid = Math.max((periodValues[1] - periodValues[0]) * 10, 2.5)
|
|
|
|
if (wid > 100) {
|
|
|
|
wid = 100;
|
|
|
|
}
|
|
|
|
const width = `${wid}%`
|
|
|
|
periodTimeLine.style.left = left;
|
|
|
|
periodTimeLine.style.width = width;
|
|
|
|
}
|
|
|
|
|
|
|
|
createTicks();
|
|
|
|
|
|
|
|
</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>
|