File "tmp0027__base04.php"
Full Path: /home/analogde/www/Dev tableau/tmp0027__base04.php
File size: 27.02 KB
MIME-type: text/x-php
Charset: utf-8
<?php
function getWorkingDays($startDate, $endDate, $holidays = []) {
$start = strtotime($startDate);
$end = strtotime($endDate);
$workingDays = [];
setlocale(LC_TIME, 'fr_FR.UTF-8');
for ($currentDate = $start; $currentDate <= $end; $currentDate = strtotime("+1 day", $currentDate)) {
$dayOfWeek = date("N", $currentDate);
if ($dayOfWeek < 6 && !in_array(date("Y-m-d", $currentDate), $holidays)) {
$workingDays[] = [
'day' => date("d", $currentDate),
'day_of_week_initial' => strtoupper(strftime("%A", $currentDate)[0]),
'week_number' => date("W", $currentDate),
'full_date' => date("Y-m-d", $currentDate) // Ajout de la date complète
];
}
}
return $workingDays;
}
// Liste des jours fériés et fêtes religieuses à exclure (exemple pour la France, à compléter avec d'autres fêtes)
$holidays = [
"2025-01-01", // Jour de l'An
"2025-04-14", // Lundi de Pâques
"2025-05-01", // Fête du Travail
"2025-05-08", // Victoire 1945
"2025-07-14", // Bastille
"2025-08-15", // Assomption
"2025-11-01", // Toussaint
"2025-12-25", // Noël
"2026-01-01", // Jour de l'An
"2026-04-06", // Lundi de Pâques
// Ajouter d'autres jours fériés ici...
];
// Définir la période de début et de fin
$startDate = "2025-01-10";
$endDate = "2026-02-14";
// Appeler la fonction pour obtenir les jours ouvrés entre les deux dates
$workingDays = getWorkingDays($startDate, $endDate, $holidays);
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tableau Sticky - 3 Lignes Fixes</title>
<style>
.table-wrapper {
width: 100%;
max-height: 400px;
overflow: auto;
border: 1px solid #ccc;
position: relative;
}
table {
border-collapse: collapse;
width: max-content;
table-layout: fixed;
}
th, td {
padding: 10px;
text-align: center;
height: 40px;
border: 1px solid #ccc;
white-space: nowrap;
min-width: 120px;
position: relative;
background-color: white;
box-sizing: border-box;
}
th {
background-color: #f2f2f2;
position: sticky;
top: 0;
z-index: 100;
}
th:first-child, td:first-child {
position: sticky;
left: 0;
z-index: 101;
background-color: white;
}
th:nth-child(2), td:nth-child(2) {
position: sticky;
left: 120px;
z-index: 100;
background-color: white;
}
th:nth-child(3), td:nth-child(3) {
position: sticky;
left: 240px;
z-index: 99;
background-color: white;
}
th:first-child::after, td:first-child::after,
th:nth-child(2)::after, td:nth-child(2)::after,
th:nth-child(3)::after, td:nth-child(3)::after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 2px;
height: 100%;
background-color: #ccc;
z-index: 102;
}
tbody tr:nth-child(odd) {
background-color: #f9f9f9;
}
th:nth-child(n+4), td:nth-child(n+4) {
min-width: 40px;
max-width: 40px;
width: 40px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.second-header th {
background-color: #f2f2f2;
position: sticky;
top: 40px;
z-index: 99;
}
.third-header th {
background-color: #f2f2f2;
position: sticky;
top: 80px;
z-index: 98;
}
.second-header th:first-child, .third-header th:first-child {
left: 0;
z-index: 101;
background-color: white;
}
.second-header th:nth-child(2), .third-header th:nth-child(2) {
left: 120px;
z-index: 100;
background-color: white;
}
.second-header th:nth-child(3), .third-header th:nth-child(3) {
left: 240px;
z-index: 99;
background-color: white;
}
thead tr:first-child th:first-child,
thead tr:first-child th:nth-child(2),
thead tr:first-child th:nth-child(3) {
z-index: 105;
background-color: #ddd;
}
.second-header th:first-child,
.second-header th:nth-child(2),
.second-header th:nth-child(3) {
z-index: 104;
}
.third-header th:first-child,
.third-header th:nth-child(2),
.third-header th:nth-child(3) {
z-index: 103;
}
thead tr:first-child th,
.second-header th,
.third-header th {
box-shadow: inset 0 -2px #aaa;
background-clip: padding-box;
padding-bottom: 2px;
margin-bottom: -2px;
z-index: 100;
}
.second-header th {
top: calc(40px + 2px);
margin-top: 2px;
}
.third-header th {
top: calc(80px + 2px);
margin-top: 2px;
}
.even-week {
background-color: #ffcc99 !important;
}
.odd-week {
background-color: #e6b3ff !important;
}
.fourth-header th {
background-color: #f2f2f2;
position: sticky;
top: 120px;
z-index: 97;
}
.fourth-header th:first-child {
left: 0;
z-index: 101;
background-color: white;
}
.fourth-header th:nth-child(2) {
left: 120px;
z-index: 100;
background-color: white;
}
.fourth-header th:nth-child(3) {
left: 240px;
z-index: 99;
background-color: white;
}
.fourth-header th:first-child,
.fourth-header th:nth-child(2),
.fourth-header th:nth-child(3) {
z-index: 102;
}
.fourth-header th {
top: calc(120px + 2px);
margin-top: 2px;
}
.fourth-header th {
box-shadow: inset 0 -2px #aaa;
background-clip: padding-box;
padding-bottom: 2px;
margin-bottom: -2px;
z-index: 97;
}
.even-week {
background-color: #ffcc99 !important;
}
.odd-week {
background-color: #e6b3ff !important;
}
.month-even {
background-color: #99ccff !important;
}
.month-odd {
background-color: #ff9999 !important;
}
#addRowButton {
margin-bottom: 20px;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
#addRowButton:hover {
background-color: #45a049;
}
#addSpecialRowButton {
margin-bottom: 20px;
padding: 10px;
background-color: #2196F3;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
}
#addSpecialRowButton:hover {
background-color: #1e88e5;
}
.special-row {
background-color: #f0f0f0;
}
#contextMenu {
position: absolute;
display: none;
flex-direction: column;
background: white;
border: 1px solid #ccc;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 5px;
z-index: 1000;
min-width: 180px;
}
#contextMenu .dropdown-item {
display: block;
width: 100%;
padding: 8px 12px;
text-align: left;
color: #333;
cursor: pointer;
}
#contextMenu .dropdown-item:hover {
background: #f8f9fa;
}
#specialContextMenu {
position: absolute;
display: none;
flex-direction: column;
background: white;
border: 1px solid #ccc;
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
border-radius: 5px;
z-index: 1000;
min-width: 180px;
}
#specialContextMenu .dropdown-item {
display: block;
width: 100%;
padding: 8px 12px;
text-align: left;
color: #333;
cursor: pointer;
}
#specialContextMenu .dropdown-item:hover {
background: #f8f9fa;
}
.add-button {
background-color: orange;
color: white;
border: none;
padding: 5px 10px;
cursor: pointer;
margin-left: 5px;
}
.editable-input {
width: 65px;
box-sizing: border-box;
}
.collapsed {
display: none;
}
</style>
</head>
<body>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th></th>
<th>plouf</th>
<th>cretin</th>
<?php foreach ($workingDays as $dateData): ?>
<th>
<?php echo $dateData['day']; ?> <br>
</th>
<?php endforeach; ?>
</tr>
<tr class="second-header">
<th></th>
<th>Valeur 1</th>
<th>Valeur 2</th>
<?php
$currentWeek = null;
$weekClass = "";
foreach ($workingDays as $dateData):
if ($currentWeek !== $dateData['week_number']) {
$currentWeek = $dateData['week_number'];
$weekClass = ($currentWeek % 2 === 0) ? 'even-week' : 'odd-week';
}
?>
<th class="<?= $weekClass; ?>"><?php echo $dateData['day_of_week_initial']; ?></th>
<?php endforeach; ?>
</tr>
<tr class="third-header">
<th></th>
<th>Info 1</th>
<th>Info 2</th>
<?php
$currentWeek = null;
$colspan = 0;
foreach ($workingDays as $index => $dateData):
if ($currentWeek !== $dateData['week_number']) {
if ($currentWeek !== null) {
$weekClass = $currentWeek % 2 === 0 ? 'even-week' : 'odd-week';
echo '<th class="' . $weekClass . '" colspan="' . $colspan . '">' . $currentWeek . '</th>';
}
$currentWeek = $dateData['week_number'];
$colspan = 1;
} else {
$colspan++;
}
endforeach;
if ($currentWeek !== null) {
$weekClass = $currentWeek % 2 === 0 ? 'even-week' : 'odd-week';
echo '<th class="' . $weekClass . '" colspan="' . $colspan . '">' . $currentWeek . '</th>';
}
?>
</tr>
<tr class="fourth-header">
<th></th>
<th>Label 1</th>
<th>Label 2</th>
<?php
$currentMonthYear = null;
$colspan = 0;
$monthClass = "";
$colorIndex = 0;
$colors = ['month-even', 'month-odd'];
foreach ($workingDays as $index => $dateData):
$monthYear = ucfirst(strftime("%B %Y", strtotime($dateData['full_date'])));
if ($currentMonthYear !== $monthYear) {
if ($currentMonthYear !== null) {
echo '<th class="' . $monthClass . '" colspan="' . $colspan . '">' . $currentMonthYear . '</th>';
}
$currentMonthYear = $monthYear;
$colspan = 1;
$monthClass = $colors[$colorIndex % 2];
$colorIndex++;
} else {
$colspan++;
}
endforeach;
if ($currentMonthYear !== null) {
echo '<th class="' . $monthClass . '" colspan="' . $colspan . '">' . $currentMonthYear . '</th>';
}
?>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<button id="addRowButton">Ajouter une ligne</button>
<button id="addSpecialRowButton">Ajouter une ligne spéciale</button>
<script>
document.addEventListener("DOMContentLoaded", function () {
const tableBody = document.querySelector("tbody");
let specialRowCounter = 1;
const specialRowStates = {}; // Objet pour stocker l'état des lignes spéciales
const contextMenu = document.createElement("div");
contextMenu.id = "contextMenu";
contextMenu.classList.add("dropdown-menu");
contextMenu.style.display = "none";
document.body.appendChild(contextMenu);
const specialContextMenu = document.createElement("div");
specialContextMenu.id = "specialContextMenu";
specialContextMenu.classList.add("dropdown-menu");
specialContextMenu.style.display = "none";
document.body.appendChild(specialContextMenu);
function addRow(afterRow = null) {
const rowNumber = tableBody.rows.length + 1;
const newRow = document.createElement("tr");
newRow.setAttribute("draggable", "true");
const firstCells = ["Row " + rowNumber, "Data " + rowNumber , "cretin " + rowNumber];
firstCells.forEach((text, index) => {
const td = document.createElement("td");
if (index === 0) {
const input = document.createElement("input");
input.type = "text";
input.classList.add("editable-input");
input.addEventListener("click", function (event) {
event.stopPropagation();
input.focus();
});
input.addEventListener("blur", function () {
input.classList.remove("editing");
});
input.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
input.classList.remove("editing");
input.blur();
}
});
td.appendChild(input);
} else {
td.textContent = text;
}
if (index === 0) {
td.classList.add("row-header");
td.addEventListener("contextmenu", function (event) {
event.preventDefault();
showContextMenu(event, newRow);
});
}
newRow.appendChild(td);
});
const headerCells = document.querySelectorAll("thead tr:nth-child(2) th:not(.weekend):not(.holiday)");
const workingDaysCount = headerCells.length;
for (let i = 0; i < workingDaysCount; i++) {
const td = document.createElement("td");
td.textContent = "0";
td.style.backgroundColor = "white";
td.addEventListener("click", function () {
td.textContent = td.textContent === "0" ? "1" : "0";
td.style.backgroundColor = td.textContent === "1" ? "green" : "white";
});
newRow.appendChild(td);
}
if (afterRow) {
tableBody.insertBefore(newRow, afterRow.nextSibling);
} else {
tableBody.appendChild(newRow);
}
newRow.addEventListener("dragstart", dragStart);
newRow.addEventListener("dragover", dragOver);
newRow.addEventListener("drop", drop);
}
function addSpecialRow(afterRow = null) {
const newRow = document.createElement("tr");
newRow.classList.add("special-row");
newRow.setAttribute("draggable", "true");
const firstCell = document.createElement("td");
firstCell.classList.add("row-header");
const input = document.createElement("input");
input.type = "text";
input.classList.add("editable-input");
input.placeholder = `Ligne spéciale ${specialRowCounter}`;
input.addEventListener("click", function (event) {
event.stopPropagation();
input.focus();
});
input.addEventListener("blur", function () {
input.classList.remove("editing");
});
input.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
input.classList.remove("editing");
input.blur();
}
});
const addButton = document.createElement("button");
addButton.classList.add("add-button");
addButton.textContent = "+";
addButton.addEventListener("click", function () {
toggleCollapse(newRow);
});
firstCell.appendChild(input);
firstCell.appendChild(addButton);
firstCell.addEventListener("contextmenu", function (event) {
event.preventDefault();
showSpecialContextMenu(event, newRow);
});
newRow.appendChild(firstCell);
const otherCells = [`Spéciale ${specialRowCounter}-2`, `Spéciale ${specialRowCounter}-3`];
otherCells.forEach(text => {
const td = document.createElement("td");
td.textContent = text;
newRow.appendChild(td);
});
const td = document.createElement("td");
td.textContent = `Ligne spéciale ${specialRowCounter} - Détails`;
td.colSpan = document.querySelector("thead tr:nth-child(2)").cells.length - 3;
newRow.appendChild(td);
if (afterRow) {
tableBody.insertBefore(newRow, afterRow.nextSibling);
} else {
tableBody.appendChild(newRow);
}
newRow.addEventListener("dragstart", dragStart);
newRow.addEventListener("dragover", dragOver);
newRow.addEventListener("drop", drop);
specialRowCounter++;
}
function toggleCollapse(specialRow) {
const nextSpecialRow = getNextSpecialRow(specialRow);
const rowsToToggle = [];
let currentRow = specialRow.nextElementSibling;
while (currentRow && currentRow !== nextSpecialRow) {
if (!currentRow.classList.contains("special-row")) {
rowsToToggle.push(currentRow);
}
currentRow = currentRow.nextElementSibling;
}
rowsToToggle.forEach(row => {
row.classList.toggle("collapsed");
});
}
function getNextSpecialRow(specialRow) {
let currentRow = specialRow.nextElementSibling;
while (currentRow) {
if (currentRow.classList.contains("special-row")) {
return currentRow;
}
currentRow = currentRow.nextElementSibling;
}
return null;
}
function showContextMenu(event, row) {
contextMenu.innerHTML = `
<button class="dropdown-item" id="add-row">Ajouter une ligne</button>
<button class="dropdown-item text-danger" id="delete-row">Supprimer cette ligne</button>
`;
contextMenu.style.top = `${event.clientY}px`;
contextMenu.style.left = `${event.clientX}px`;
contextMenu.style.display = "flex";
contextMenu.style.flexDirection = "column";
document.getElementById("add-row").addEventListener("click", function () {
addRow(row);
hideContextMenu();
});
document.getElementById("delete-row").addEventListener("click", function () {
row.remove();
hideContextMenu();
});
}
function showSpecialContextMenu(event, row) {
specialContextMenu.innerHTML = `
<button class="dropdown-item" id="add-special-row">Ajouter une ligne spéciale</button>
<button class="dropdown-item text-danger" id="delete-special-row">Supprimer cette ligne</button>
<label class="dropdown-item">
<input type="checkbox" id="special-checkbox"> Option spéciale
</label>
`;
specialContextMenu.style.top = `${event.clientY}px`;
specialContextMenu.style.left = `${event.clientX}px`;
specialContextMenu.style.display = "flex";
specialContextMenu.style.flexDirection = "column";
const checkbox = document.getElementById("special-checkbox");
const rowIndex = row.rowIndex;
// Vérifie s'il y a des lignes standards immédiatement en dessous de la ligne spéciale
let hasStandardRowsBelow = false;
let currentRow = row.nextElementSibling;
const standardRows = [];
while (currentRow && !currentRow.classList.contains("special-row")) {
if (!currentRow.classList.contains("special-row")) {
hasStandardRowsBelow = true;
standardRows.push(currentRow);
}
currentRow = currentRow.nextElementSibling;
}
if (hasStandardRowsBelow) {
checkbox.disabled = false;
} else {
checkbox.disabled = true;
checkbox.checked = false;
}
// Conserver l'état de la case à cocher
if (specialRowStates[rowIndex]) {
checkbox.checked = specialRowStates[rowIndex].checked;
}
checkbox.addEventListener("change", function () {
specialRowStates[rowIndex] = {
checked: checkbox.checked,
rows: standardRows
};
standardRows.forEach(standardRow => {
const firstCell = standardRow.querySelector("td:first-child");
if (checkbox.checked) {
firstCell.style.backgroundColor = "yellow";
standardRow.classList.add("no-drag");
} else {
firstCell.style.backgroundColor = "";
standardRow.classList.remove("no-drag");
}
});
console.log(`Lignes standards concernées : ${standardRows.map(row => row.rowIndex).join(", ")}`);
});
document.getElementById("add-special-row").addEventListener("click", function () {
addSpecialRow(row);
hideSpecialContextMenu();
});
document.getElementById("delete-special-row").addEventListener("click", function () {
row.remove();
hideSpecialContextMenu();
});
}
document.addEventListener("click", function (event) {
if (!contextMenu.contains(event.target)) {
hideContextMenu();
}
if (!specialContextMenu.contains(event.target)) {
hideSpecialContextMenu();
}
const inputs = document.querySelectorAll(".editable-input");
inputs.forEach(input => {
if (!input.contains(event.target)) {
input.classList.remove("editing");
}
});
});
function hideContextMenu() {
contextMenu.style.display = "none";
}
function hideSpecialContextMenu() {
specialContextMenu.style.display = "none";
}
document.getElementById("addRowButton").addEventListener("click", addRow);
document.getElementById("addSpecialRowButton").addEventListener("click", addSpecialRow);
document.querySelectorAll("tbody tr td:first-child").forEach(td => {
td.addEventListener("contextmenu", function (event) {
event.preventDefault();
showContextMenu(event, td.parentElement);
});
});
function dragStart(event) {
if (event.target.classList.contains("no-drag")) {
event.preventDefault();
return;
}
event.dataTransfer.setData("text/plain", event.target.rowIndex);
event.target.classList.add("dragging");
}
function dragOver(event) {
event.preventDefault();
const draggingRow = document.querySelector(".dragging");
const targetRow = event.target.closest("tr");
if (targetRow && draggingRow !== targetRow && !targetRow.classList.contains("no-drag")) {
const rect = targetRow.getBoundingClientRect();
const offset = rect.top + (rect.height / 2);
if (event.clientY - offset > 0) {
tableBody.insertBefore(draggingRow, targetRow.nextSibling);
} else {
tableBody.insertBefore(draggingRow, targetRow);
}
}
}
function drop(event) {
event.preventDefault();
const draggingRow = document.querySelector(".dragging");
draggingRow.classList.remove("dragging");
}
});
</script>
</body>
</html>