diff --git a/src/bonsai/bonsai/bim/data/webui/static/css/components/costTable.css b/src/bonsai/bonsai/bim/data/webui/static/css/components/costTable.css deleted file mode 100644 index b407f8e5ec..0000000000 --- a/src/bonsai/bonsai/bim/data/webui/static/css/components/costTable.css +++ /dev/null @@ -1,172 +0,0 @@ -:root { - --fontSize: 14px; -} -body { - margin: 0; - padding: 0; -} - -.table-container { - margin-top: 1em; - overflow-x: auto; - position: relative; -} - -#cost-items { - height: calc(100vh - var(--margin-small)); -} - - -.table-scrollable-body { - max-height: 70vh; - overflow-y: auto; - display: block; -} - -table { - width: 100%; - border-collapse: collapse; - user-select: none; - table-layout: fixed; - font-size: var(--fontSize); -} - -thead { - position: sticky; - top: 0; - z-index: 1; - background-color: #727272; - font-size: larger; -} - -tbody { - width: 100%; -} - -td, -th { - padding: 0; - margin: 0; - border-left: 1px solid #ddd; - border-right: 1px solid #ddd; - text-align: center; - border-bottom: 0; - border-top: 0; -} - -th { - border-bottom: 1px solid #ddd; -} - -td { - align-items: center; -} - -td, -th { - box-sizing: border-box; /* Include padding and border in the element's total width and height */ -} - -/* first td of a row, must be flex */ -td:first-child { - display: flex; - align-items: center; -} - -.nested { - display: none; -} - -.expand-collapse { - cursor: pointer; -} - -.caret-cell { - border: none; - background: none; - padding: 0; -} - -input, -select { - background-color: var(--blender-button-background, var(--bg-color)); - color: var(--blender-button-text, var(--text-color)); - border-color: var(--blender-button-border, var(--border-color)); - transition: filter 0.2s ease; - border-radius: 6px; - padding: 6px 8px; - font-size: 14px; - line-height: 20px; - transition: border-color 0.2s cubic-bezier(0.3, 0, 0.5, 1); - height: inherit; - margin-left: 0.5em; - margin-right: 0.5em; - font-size: var(--fontSize); - min-width: 50px; /* Minimum width to prevent it from becoming too small */ -} - -input:focus { - border-color: #28a745; - outline: none; - box-shadow: 0 0 0 3px #28a745; -} - -#cost-items tr:hover, -.highlighted { - background-color: #ffb936; - color: black; -} - -.highlight { - background-color: #ffb936; - color: black; -} - -.cost-values-table { - width: 100%; - border-collapse: collapse; -} - -.cost-values-table th, -.cost-values-table td { - border: 1px solid #ddd; - padding: 8px; -} - -.cost-values-table th { - background-color: #444444; - text-align: center; -} - -/* Responsive Styles */ -@media (max-width: 768px) { - table { - font-size: 12px; - } - - td, - th { - padding: 4px; - } - - input { - padding: 2px; - font-size: 12px; - } -} - -@media (max-width: 480px) { - table { - font-size: 10px; - } - - td, - th { - padding: 2px; - } - - input { - padding: 1px; - font-size: 10px; - } -} \ No newline at end of file diff --git a/src/bonsai/bonsai/bim/data/webui/static/css/cost.css b/src/bonsai/bonsai/bim/data/webui/static/css/cost.css index 27f2b42238..557c42822c 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/css/cost.css +++ b/src/bonsai/bonsai/bim/data/webui/static/css/cost.css @@ -1,18 +1,30 @@ @import url("./shared.css"); @import url("./components/card.css"); @import url("./components/contextMenu.css"); -@import url("./components/costTable.css"); @import url("./components/headerBar.css"); +:root { + --fontSize: 14px; + --background-color: #363636; + --button-background: #28a745; + --button-hover-background: #218838; + --scrollbar-track-color: #f1f1f1; + --scrollbar-thumb-color: #888; + --scrollbar-thumb-hover-color: #555; +} +body { + margin: 0; + padding: 0; + box-sizing: border-box; +} .form-header { display: flex; justify-content: flex-start; align-items: center; margin-bottom: 10px; - background-color: #363636; + background-color: var(--background-color); padding: 10px; - /* trext size */ - font-size: 1.5em; + font-size: var(--fontSize); } span { @@ -20,14 +32,12 @@ span { } .floating-form { - height: 40vh; width: 50vw; + height: 50vh; max-height: 80vh; - height: auto; position: absolute; - background-color: #363636; + background-color: var(--background-color); border: 1px solid black; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); z-index: 9999; cursor: move; padding: 20px; @@ -36,8 +46,12 @@ span { overflow-x: hidden; } +.floating-form table tr { + margin-bottom: 10px; +} + .action-button { - background-color: #28a745; + background-color: var(--button-background); border: none; color: #fff; padding: 0.5em 1em; @@ -52,7 +66,7 @@ span { } .action-button:hover { - background-color: #218838; + background-color: var(--button-hover-background); } .close-button { @@ -66,7 +80,7 @@ span { .settings-menu { display: none; position: absolute; - background-color: #363636; + background-color: var(--background-color); border: 1px solid black; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); z-index: 9999; @@ -74,3 +88,210 @@ span { padding: 20px; user-select: none; } + +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--scrollbar-track-color); +} + +::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb-color); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--scrollbar-thumb-hover-color); +} + +/* Firefox */ +* { + scrollbar-width: thin; + scrollbar-color: var(--scrollbar-thumb-color) var(--scrollbar-track-color); +} + +/* Edge, IE */ +* { + -ms-overflow-style: -ms-autohiding-scrollbar; +} + +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.table-container { + margin-top: 1em; + overflow-x: auto; + overflow-y: auto; + position: relative; +} + +.floating-form .table-container { + height: 50%; + max-height: 50%; +} + +#cost-items { + max-height: calc(100vh - var(--margin-small)); +} + +/* if id starts with cost-values-form */ +[id^="cost-values-form"] table { + /* fixed */ + max-height: 50%; + table-layout: fixed; +} + +table { + width: 100%; + height: 100%; + border-collapse: collapse; + user-select: none; + font-size: var(--fontSize); + -webkit-font-smoothing: antialiased; /* Improve font rendering */ + -moz-osx-font-smoothing: grayscale; /* Improve font rendering */ +} + +thead { + position: sticky; + top: 0; + z-index: 1; + background-color: #727272; + font-size: large; +} + +tbody { + width: 100%; + height: 100%; + overflow: auto; +} + +td, +th { + padding: 0; + margin: 0; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd; + text-align: center; + border-bottom: 0; + border-top: 0; +} + +th { + border-bottom: 1px solid #ddd; +} + +td { + align-items: center; +} + +/* first td of a row, must be flex */ +td:first-child { + display: flex; + align-items: center; +} + +.nested { + display: none; +} + +.expand-collapse { + cursor: pointer; +} + +.caret-cell { + border: none; + background: none; + padding: 0; +} + +input, +select { + background-color: var(--blender-button-background, var(--bg-color)); + color: var(--blender-button-text, var(--text-color)); + border-color: var(--blender-button-border, var(--border-color)); + transition: filter 0.2s ease; + border-radius: 6px; + padding: 6px 8px; + font-size: 14px; + line-height: 20px; + transition: border-color 0.2s cubic-bezier(0.3, 0, 0.5, 1); + margin-left: 0.5em; + margin-right: 0.5em; + font-size: var(--fontSize); + min-width: 50px; /* Minimum width to prevent it from becoming too small */ +} + +input:focus { + border-color: #28a745; + outline: none; + box-shadow: 0 0 0 3px #28a745; + color: #ddd; +} + +#cost-items tr:hover, +.highlighted { + background-color: #28a74657; +} + +.highlighted { + background-color: #28a74657; +} + +.cost-values-table { + width: 100%; + border-collapse: collapse; +} + +.cost-values-table th, +.cost-values-table td { + border: 1px solid #ddd; + padding: 8px; +} + +.cost-values-table th { + background-color: #444444; + text-align: center; +} + +/* Responsive Styles */ +@media (max-width: 768px) { + table { + font-size: 12px; + } + + td, + th { + padding: 2px; + font-size: 12px; + } + + input { + padding: 2px; + font-size: 12px; + } +} + +@media (max-width: 480px) { + table { + font-size: 10px; + } + + td, + th { + padding: 2px; + } + + input { + padding: 1px; + font-size: 10px; + } +} + +form table { + max-height: 50%; +} diff --git a/src/bonsai/bonsai/bim/data/webui/static/js/cost.js b/src/bonsai/bonsai/bim/data/webui/static/js/cost.js index 335a095a60..3866fadf39 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/js/cost.js +++ b/src/bonsai/bonsai/bim/data/webui/static/js/cost.js @@ -17,16 +17,15 @@ $(document).ready(function () { icon: "fa-regular fa-eye-slash", callback: (button) => { CostUI.toggleSchedulesContainer(button); - } + }, }); CostUI.addRibbonButton({ text: "Settings", icon: "fas fa-cog", callback: (button) => { CostUI.toggleSettingsMenu(button); - } + }, }); - }); function connectSocket() { @@ -86,7 +85,6 @@ function handleSelectedProducts(data) { } function handlePredefinedTypes(data) { - CostUI.enableAddingCostSchedule( data.data["predefined_types"], addCostSchedule diff --git a/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js b/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js index 435258e5bf..4fdfb5c575 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js +++ b/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js @@ -29,7 +29,6 @@ export class CostUI { const tr = document.createElement("tr"); thead.appendChild(tr); - for (let i = 0; i < columnHeaders.length; i++) { const th = document.createElement("th"); th.textContent = columnHeaders[i]; @@ -73,7 +72,7 @@ export class CostUI { document.head.appendChild(style); } -static createContextMenu(callbacks) { + static createContextMenu(callbacks) { const contextMenu = document.createElement("div"); contextMenu.id = "context-menu"; contextMenu.classList.add("context-menu"); @@ -187,43 +186,55 @@ static createContextMenu(callbacks) { function getColumnNames(tableId) { const table = document.getElementById(tableId); const headerRow = table.querySelector("thead tr"); - return Array.from(headerRow.children).map(headerCell => headerCell.textContent.trim()); + return Array.from(headerRow.children).map((headerCell) => + headerCell.textContent.trim() + ); } - + // Add double-click event listener - document.getElementById("cost-items").addEventListener("dblclick", function (event) { - const targetRow = event.target.closest("tr"); - const targetCell = event.target.closest("td"); - if (targetRow && targetCell) { - const columnIndex = Array.from(targetRow.children).indexOf(targetCell); - const costItemId = parseInt(targetRow.getAttribute("id")); - const cellContent = targetCell.textContent; - - // Get column names from the table header - const columnNames = getColumnNames("cost-items"); - const cellName = columnNames[columnIndex]; - - console.log("Column Name: ", cellName); - console.log("Content: ", cellContent); - - // Implement different behaviors based on the column index - switch (columnIndex) { - case 0: - // Behavior for the first column - callbacks.firstColumnDoubleClick ? callbacks.firstColumnDoubleClick(costItemId) : null; - break; - case 1: - // Behavior for the second column - callbacks.secondColumnDoubleClick ? callbacks.secondColumnDoubleClick(costItemId) : null; - break; - // Add more cases as needed for other columns - default: - // Default behavior - callbacks.defaultDoubleClick ? callbacks.defaultDoubleClick(costItemId) : null; - break; + document + .getElementById("cost-items") + .addEventListener("dblclick", function (event) { + const targetRow = event.target.closest("tr"); + const targetCell = event.target.closest("td"); + if (targetRow && targetCell) { + const columnIndex = Array.from(targetRow.children).indexOf( + targetCell + ); + const costItemId = parseInt(targetRow.getAttribute("id")); + const cellContent = targetCell.textContent; + + // Get column names from the table header + const columnNames = getColumnNames("cost-items"); + const cellName = columnNames[columnIndex]; + + console.log("Column Name: ", cellName); + console.log("Content: ", cellContent); + + // Implement different behaviors based on the column index + switch (columnIndex) { + case 0: + // Behavior for the first column + callbacks.firstColumnDoubleClick + ? callbacks.firstColumnDoubleClick(costItemId) + : null; + break; + case 1: + // Behavior for the second column + callbacks.secondColumnDoubleClick + ? callbacks.secondColumnDoubleClick(costItemId) + : null; + break; + // Add more cases as needed for other columns + default: + // Default behavior + callbacks.defaultDoubleClick + ? callbacks.defaultDoubleClick(costItemId) + : null; + break; + } } - } - }); + }); } static deleteCostItemRow(targetRow, expandedState) { @@ -719,7 +730,11 @@ static createContextMenu(callbacks) { const cardBody = document.createElement("div"); cardBody.classList.add("card-body"); - const cardTitle = CostUI.Text(title, "fa-solid fa-money-bill-wave", "large"); + const cardTitle = CostUI.Text( + title, + "fa-solid fa-money-bill-wave", + "large" + ); cardTitle.classList.add("card-title"); const cardButton = document.createElement("button"); @@ -805,12 +820,15 @@ static createContextMenu(callbacks) { div.classList.add("table-container"); const table = document.createElement("table"); div.appendChild(table); - table.classList.add(className); + if (className !== "") { + table.classList.add(className); + } table.id = id; const headerRow = document.createElement("tr"); headers.forEach((headerText) => { const th = document.createElement("th"); + th.style.position = "relative"; th.textContent = headerText; headerRow.appendChild(th); }); @@ -1212,7 +1230,7 @@ static createContextMenu(callbacks) { { textContent: "", style: { border: "none" } }, { textContent: "", style: { border: "none" } }, { textContent: "", style: { border: "none" } }, - { textContent: "Subtotal", colSpan: 4, style: { border: "none" } }, + { textContent: "Subtotal", colSpan: 1, style: { border: "none" } }, { textContent: quantitySums.count }, ]; @@ -1250,23 +1268,19 @@ static createContextMenu(callbacks) { }) { if (products.length === 0) { const noProductsMessage = document.createElement("p"); - noProductsMessage.textContent = "Your Blender Selection is empty! Select objects first."; + noProductsMessage.textContent = + "Your Blender Selection is empty! Select objects first."; form.appendChild(noProductsMessage); return; } const { tableContainer, table } = CostUI.addTable({ headers: ["Step Id", "Class", "Name", "Type", "Count"], - className: "product-table", + className: "", id: "cost-values-table-" + costItemId, }); form.appendChild(tableContainer); - - const scrollableBody = document.createElement("div"); - scrollableBody.classList.add("table-scrollable-body"); - const tbody = document.createElement("tbody"); - scrollableBody.appendChild(tbody); - table.appendChild(scrollableBody); + table.appendChild(tbody); const quantityAssigned = CostUI.getAssinedQuantity(products, costItemId); const areProductsSameClass = products.every( @@ -1395,16 +1409,13 @@ static createContextMenu(callbacks) { }) { const { tableContainer, table } = CostUI.addTable({ headers: ["Step Id", "Class", "Name", "Type", "Count"], - className: "product-table", + className: "", id: "cost-values-table-" + costItemId, }); form.appendChild(tableContainer); - const scrollableBody = document.createElement("div"); - scrollableBody.classList.add("table-scrollable-body"); const tbody = document.createElement("tbody"); - scrollableBody.appendChild(tbody); - table.appendChild(scrollableBody); + table.appendChild(tbody); let quantityAssigned = CostUI.getAssinedQuantity(products, costItemId); const shouldAddQto = quantityAssigned ? true : false; @@ -1487,22 +1498,22 @@ static createContextMenu(callbacks) { icon: "fa-solid fa-gear", shouldHide: true, }); - + const picker = CostUI.createColorPicker(); const tableFontSize = CostUI.createFontSizePicker(); settingsMenu.appendChild(picker); settingsMenu.appendChild(tableFontSize); - + settingsMenu.style.display = "none"; const settingsButton = document.getElementById("settings-button"); settingsButton.addEventListener("click", function () { settingsMenu.style.display = "block"; }); - + // Apply saved settings on page load CostUI.applySavedSettings(); } - + static createFontSizePicker() { const div = document.createElement("div"); const fontSizeText = document.createElement("p"); @@ -1520,7 +1531,7 @@ static createContextMenu(callbacks) { }); return div; } - + static createColorPicker() { const div = document.createElement("div"); const colorText = document.createElement("p"); @@ -1538,26 +1549,26 @@ static createContextMenu(callbacks) { }); return div; } - + static saveFontSize(fontSize) { localStorage.setItem("tableFontSize", fontSize); } - + static saveColor(color) { localStorage.setItem("tableColor", color); } - + static applyFontSizeToAllTables(fontSize) { - document.documentElement.style.setProperty('--fontSize', fontSize); + document.documentElement.style.setProperty("--fontSize", fontSize); } - + static applyColorToAllTables(color) { const tables = document.querySelectorAll("table"); tables.forEach((table) => { table.style.backgroundColor = color; }); } - + static applySavedSettings() { console.log("Applying saved settings"); const savedFontSize = localStorage.getItem("tableFontSize"); @@ -1568,7 +1579,7 @@ static createContextMenu(callbacks) { fontSizePicker.value = parseInt(savedFontSize, 10); } } - + const savedColor = localStorage.getItem("tableColor"); if (savedColor) { CostUI.applyColorToAllTables(savedColor); @@ -1585,20 +1596,20 @@ static createContextMenu(callbacks) { static addRibbonButton({ text, icon, callback }) { const ribbonBar = CostUI.getRibbonBar(); - + const button = document.createElement("button"); button.classList.add("action-button", ...icon.split(" ")); button.dataset.tooltip = text; // Use dataset to set the tooltip attribute - + button.addEventListener("click", () => callback(button)); - + ribbonBar.appendChild(button); } - + static toggleSchedulesContainer(button) { const schedulesContainer = CostUI.getSchedulesContainer(); const isHidden = schedulesContainer.style.display === "none"; - + if (isHidden) { CostUI.displaySchedulesContainer(); button.classList.remove("fa-eye"); @@ -1611,16 +1622,16 @@ static createContextMenu(callbacks) { button.dataset.tooltip = "Display Schedules"; } } - + static getSchedulesContainer() { return document.getElementById("cost-schedules"); } - + static displaySchedulesContainer() { const schedulesContainer = CostUI.getSchedulesContainer(); schedulesContainer.style.display = "flex"; } - + static hideSchedulesContainer() { const schedulesContainer = CostUI.getSchedulesContainer(); schedulesContainer.style.display = "none"; @@ -1633,7 +1644,7 @@ static createContextMenu(callbacks) { static toggleSettingsMenu(button) { const settingsMenu = CostUI.getSettingsMenu(); const isHidden = settingsMenu.style.display === "none"; - + if (isHidden) { settingsMenu.style.display = "block"; button.classList.remove("fa-gear");