From 73647e479575bbb9b80daa76fc0bdcfa622bdae6 Mon Sep 17 00:00:00 2001 From: Josako Date: Fri, 30 May 2025 05:05:13 +0200 Subject: [PATCH] We have a reasonable layout for the table-editor in the specialist. To be further refined. --- .../TRAICIE_SELECTION_SPECIALIST/1.0.0.yaml | 6 +- .../templates/eveai_ordered_list_editor.html | 396 ++++++++++++++++-- eveai_app/templates/macros.html | 6 +- eveai_app/templates/scripts.html | 19 +- nginx/frontend_src/js/main.js | 7 +- nginx/frontend_src/js/tabulator-setup.js | 8 + nginx/static/dist/main.js | 4 +- 7 files changed, 395 insertions(+), 51 deletions(-) create mode 100644 nginx/frontend_src/js/tabulator-setup.js diff --git a/config/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1.0.0.yaml b/config/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1.0.0.yaml index 25b0079..6c4d323 100644 --- a/config/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1.0.0.yaml +++ b/config/specialists/traicie/TRAICIE_SELECTION_SPECIALIST/1.0.0.yaml @@ -41,17 +41,17 @@ configuration: required: false competency_details: title: - name: "title" + name: "Title" description: "Competency Title" type: "str" required: true description: - name: "description" + name: "Description" description: "Description (in context of the role) of the competency" type: "text" required: true is_knockout: - name: "Is Knockout" + name: "KO" description: "Defines if the competency is a knock-out criterium" type: "boolean" required: true diff --git a/eveai_app/templates/eveai_ordered_list_editor.html b/eveai_app/templates/eveai_ordered_list_editor.html index d79f937..f8cb989 100644 --- a/eveai_app/templates/eveai_ordered_list_editor.html +++ b/eveai_app/templates/eveai_ordered_list_editor.html @@ -33,21 +33,47 @@ window.EveAI.OrderedListEditors = { // Create column definitions from list type const columns = this._createColumnsFromListType(listTypeConfig); + // Debug log for data and columns + console.log('Data for Tabulator:', data); + console.log('Columns for Tabulator:', columns); + + // Debug log for column titles + console.log('Column titles:', columns.map(col => col.title || '')); + // Initialize Tabulator try { console.log('Creating Tabulator for', containerId); const table = new Tabulator(container, { data: data || [], columns: columns, - layout: "fitColumns", + layout: "fitColumns", // Changed to fitColumns to ensure columns display horizontally movableRows: true, - height: "400px", + {#rowHeader: {headerSort:false, resizable: false, minWidth:30, width:30, rowHandle:true, formatter:"handle"},#} + maxHeight: "50%", // Auto height to display all rows + placeholder: "No Data Available", + autoResize: false, + resizableColumnFit: true, + responsiveLayout: false, + tooltips: true, // Enable tooltips + tooltipsHeader: true, + selectable: false, // Disable row selection to prevent jumping + selectableRangeMode: "click", // Only select on click, not on drag + selectableRollingSelection: false, // Disable rolling selection + scrollToRowIfVisible: false, // Don't scroll to row even if it's already visible + scrollToRowPosition: "nearest", ...options }); console.log('Tabulator created for', containerId); container.classList.add('tabulator-initialized'); + // Debug: Log table structure + console.log('Table structure:', { + tableElement: container, + tableData: table.getData(), + tableColumns: table.getColumnDefinitions() + }); + // Add row button const addRowBtn = document.createElement('button'); addRowBtn.className = 'btn btn-sm btn-primary mt-2'; @@ -56,21 +82,65 @@ window.EveAI.OrderedListEditors = { const newRow = {}; // Create empty row with default values Object.entries(listTypeConfig).forEach(([key, field]) => { - newRow[key] = field.default || ''; + if (field.type === 'boolean') { + newRow[key] = field.default === true; + } else { + newRow[key] = field.default !== undefined ? field.default : ''; + } }); table.addRow(newRow); this._updateTextarea(containerId, table); }); container.parentNode.insertBefore(addRowBtn, container.nextSibling); + // Add explode button for fullscreen mode + const explodeBtn = document.createElement('button'); + explodeBtn.className = 'btn btn-sm btn-secondary mt-2 ms-2'; + explodeBtn.innerHTML = 'fullscreen Expand'; + explodeBtn.addEventListener('click', () => { + container.classList.toggle('fullscreen-mode'); + + // Update button text based on current state + if (container.classList.contains('fullscreen-mode')) { + explodeBtn.innerHTML = 'fullscreen_exit Collapse'; + } else { + explodeBtn.innerHTML = 'fullscreen Expand'; + } + + // Redraw table to adjust to new size + table.redraw(true); + }); + container.parentNode.insertBefore(explodeBtn, addRowBtn.nextSibling); + // Store instance this.instances[containerId] = { table: table, textarea: document.getElementById(containerId.replace('-editor', '')) }; - // Update textarea on data change + // Prevent scrolling when clicking on cells + container.addEventListener('click', function(e) { + // Prevent the default behavior which might cause scrolling + if (e.target.closest('.tabulator-cell')) { + e.preventDefault(); + } + }, { passive: false }); + + // Update textarea on various events that change data table.on("dataChanged", () => { + console.log("dataChanged event triggered"); + this._updateTextarea(containerId, table); + }); + + // Listen for row movement + table.on("rowMoved", () => { + console.log("rowMoved event triggered"); + this._updateTextarea(containerId, table); + }); + + // Listen for cell edits + table.on("cellEdited", () => { + console.log("cellEdited event triggered"); this._updateTextarea(containerId, table); }); @@ -87,65 +157,147 @@ window.EveAI.OrderedListEditors = { _updateTextarea: function(containerId, table) { const instance = this.instances[containerId]; if (instance && instance.textarea) { - instance.textarea.value = JSON.stringify(table.getData()); + const data = table.getData(); + console.log('Updating textarea with data:', data); + instance.textarea.value = JSON.stringify(data); + console.log('Textarea value updated:', instance.textarea.value); + + // Trigger change event on textarea to ensure form validation recognizes the change + const event = new Event('change', { bubbles: true }); + instance.textarea.dispatchEvent(event); + + // Also trigger input event for any listeners that might be using that + const inputEvent = new Event('input', { bubbles: true }); + instance.textarea.dispatchEvent(inputEvent); + } else { + console.error('Cannot update textarea: instance or textarea not found for', containerId); } }, _getListTypeConfig: function(listType) { - // This would need to be implemented to fetch the list type configuration - // Could be from a global variable set in the template or via an API call - return window.listTypeConfigs && window.listTypeConfigs[listType]; + // Try to get the list type configuration from window.listTypeConfigs + if (window.listTypeConfigs && window.listTypeConfigs[listType]) { + return window.listTypeConfigs[listType]; + } + + // If not found, log a warning and return a default configuration + console.warn(`List type configuration for ${listType} not found in window.listTypeConfigs. Using a default configuration.`); + return { + title: { + name: "Title", + description: "Title", + type: "str", + required: true + }, + description: { + name: "Description", + description: "Description", + type: "text", + required: true + } + }; + }, + + // Custom formatter for text columns to truncate text in normal mode + _truncateFormatter: function(cell, formatterParams, onRendered) { + const value = cell.getValue(); + const maxLength = formatterParams.maxLength || 100; + + if (value && value.length > maxLength) { + // Create a truncated version with "..." and show more indicator + const truncated = value.substring(0, maxLength) + "..."; + + // Return HTML with truncated text and a "show more" button + return `
+
${truncated}
+
+ more_horiz +
+
`; + } + + return value; }, _createColumnsFromListType: function(listTypeConfig) { const columns = []; - // Add drag handle column for row reordering - columns.push({ - formatter: "handle", - headerSort: false, - frozen: true, - width: 30, - minWidth: 30 - }); - // Add columns for each field in the list type Object.entries(listTypeConfig).forEach(([key, field]) => { const column = { title: field.name || key, field: key, - tooltip: field.description + headerTooltip: field.description, + headerSort: false, + visible: true, + resizable: "header", }; + console.log("Column ", field.name, " type: ", field.type) + // Set width based on field type + if (field.type === 'boolean') { + column.minWidth = 50; + column.maxWidth = 80; // Limit maximum width + column.widthGrow = 0; // Don't allow boolean columns to grow + } else if (field.type === 'text') { + column.width = 400; // Much larger width for text columns (especially description) + column.minWidth = 300; // Ensure text columns have adequate minimum width + column.widthGrow = 2; // Allow text columns to grow significantly more + } else { + column.width = 150; // Default width for other columns + column.minWidth = 100; + column.widthGrow = 1; // Allow some growth + } + + // Ensure consistent width calculation + column.widthShrink = 0; // Don't allow shrinking below minWidth // Set editor based on field type if (field.type === 'boolean') { column.formatter = 'tickCross'; column.editor = 'tickCross'; column.hozAlign = 'center'; + column.headerHozAlign = 'center'; + column.formatterParams = { + allowEmpty: true, + allowTruthy: true, + tickElement: "check_circle", + crossElement: "cancel" + }; } else if (field.type === 'enum' && field.allowed_values) { column.editor = 'select'; column.editorParams = { values: field.allowed_values }; + column.hozAlign = 'left'; + column.headerHozAlign = 'left'; + } else if (field.type === 'text') { + column.editor = 'textarea'; + column.formatter = this._truncateFormatter; // Use custom formatter to truncate text + column.variableHeight = true; + // Configure formatter parameters + column.formatterParams = { + maxLength: 50, + autoResize: true + }; + // Prevent scrolling when editing text cells + column.editorParams = { + elementAttributes: { + preventScroll: true + } + }; + column.hozAlign = 'left'; + column.headerHozAlign = 'left'; } else { column.editor = 'input'; + column.hozAlign = 'left'; + column.headerHozAlign = 'left'; } columns.push(column); }); - // Add delete button column - columns.push({ - formatter: function(cell, formatterParams, onRendered) { - return ""; - }, - width: 40, - hozAlign: "center", - headerSort: false, - cellClick: function(e, cell) { - cell.getRow().delete(); - } - }); + // We don't add a delete button column as per requirements + // to prevent users from deleting rows return columns; }, @@ -176,8 +328,17 @@ document.addEventListener('DOMContentLoaded', function() { // Initialize ordered list editors document.querySelectorAll('.ordered-list-field').forEach(function(textarea) { const containerId = textarea.id + '-editor'; - const container = document.getElementById(containerId); - if (!container) return; + console.log('Initializing ordered list editor for', containerId); + + // Create container if it doesn't exist + let container = document.getElementById(containerId); + if (!container) { + container = document.createElement('div'); + container.id = containerId; + container.className = 'ordered-list-editor'; + textarea.parentNode.insertBefore(container, textarea.nextSibling); + textarea.classList.add('d-none'); // Hide the textarea + } try { const data = textarea.value ? JSON.parse(textarea.value) : []; @@ -185,7 +346,7 @@ document.addEventListener('DOMContentLoaded', function() { // Check if we have the list type configuration if (listType && !window.listTypeConfigs[listType]) { - console.warn(`List type configuration for ${listType} not found. The editor may not work correctly.`); + console.warn(`List type configuration for ${listType} not found. Using default configuration.`); } window.EveAI.OrderedListEditors.initialize(containerId, data, listType); @@ -203,19 +364,57 @@ document.addEventListener('DOMContentLoaded', function() { /* Tabulator styling */ .ordered-list-editor { margin-bottom: 1rem; + min-height: 200px; /* Minimum height, will expand as needed */ } /* Make sure the Tabulator container has a proper height */ .ordered-list-editor .tabulator { - height: 400px; + height: auto; /* Auto height to display all rows */ + min-height: 200px; /* Minimum height */ width: 100%; border: 1px solid #dee2e6; border-radius: 0.25rem; + margin-bottom: 0.5rem; +} + +/* Ensure the table holder has a scrollbar */ +.ordered-list-editor .tabulator-tableholder { + /* overflow-y: auto !important; - Removed to allow Tabulator to handle overflow */ + /* max-height: calc(100% - 42px) !important; - Removed to allow Tabulator to handle height */ + /* Consider using non-!important values if specific scrolling behavior is needed */ + overflow-y: auto; + max-height: calc(100% - 42px); +} + +/* Style for the table element */ +.ordered-list-editor .tabulator-table { + display: table !important; /* Force display as table */ + width: 100% !important; + table-layout: fixed !important; /* Use fixed table layout for consistent column widths */ } /* Style for the handle column */ .ordered-list-editor .tabulator-row-handle { cursor: move; + background-color: #f8f9fa; + border-right: 1px solid #dee2e6; +} + +/* Style for the handle bars to make them more visible */ +.ordered-list-editor .tabulator-row-handle-box { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; +} + +.ordered-list-editor .tabulator-row-handle-bar { + background: #666; + display: inline-block; + width: 10px; + height: 2px; + margin: 1px 0; } /* Style for the delete button */ @@ -228,4 +427,133 @@ document.addEventListener('DOMContentLoaded', function() { .ordered-list-editor .tabulator-cell[data-type="boolean"] { text-align: center; } + +/* Style for the table header */ +.ordered-list-editor .tabulator-header { + background-color: #f8f9fa; + border-bottom: 2px solid #dee2e6; +} + +/* Style for the headers container */ +.ordered-list-editor .tabulator-headers { + display: table-row !important; /* Force display as table row */ +} + +/* Style for the header cells */ +.ordered-list-editor .tabulator-col { + background-color: #f8f9fa; + padding: 8px; + font-weight: bold; + text-align: center; + display: table-cell !important; /* Force display as table cell */ + box-sizing: border-box !important; /* Include padding in width calculation */ + position: relative !important; /* Ensure proper positioning */ +} + +/* Override any inline styles that might hide column headers */ +.ordered-list-editor .tabulator-col[style*="display: none"] { + display: table-cell !important; /* Force display as table cell */ +} + +/* Ensure header cells have the same width as their corresponding data cells */ +.ordered-list-editor .tabulator-col, +.ordered-list-editor .tabulator-cell { + {#width: auto !important; /* Let the table-layout: fixed handle the width */#} +} + +/* Style for the header cell content */ +.ordered-list-editor .tabulator-col-title { + white-space: normal; /* Allow header text to wrap */ + word-break: break-word; /* Break words to prevent horizontal overflow */ + font-weight: bold; + color: #333; +} + +/* Style for the table rows */ +.ordered-list-editor .tabulator-row { + border-bottom: 1px solid #dee2e6; + display: table-row !important; /* Force display as table row */ +} + +/* Style for the table cells */ +.ordered-list-editor .tabulator-cell { + padding: 8px; + white-space: normal; /* Allow text to wrap */ + overflow: visible; /* Show overflowing content */ + height: auto !important; /* Allow cell to grow as needed */ + word-break: break-word; /* Break words to prevent horizontal overflow */ + display: table-cell !important; /* Force display as table cell */ + scroll-margin-top: 100px; /* Prevent unwanted scrolling when focusing */ + scroll-behavior: auto; /* Disable smooth scrolling which might cause jumping */ + font-size: 0.85rem; /* Smaller font size */ +} + +/* Style for truncated cells */ +.ordered-list-editor .truncated-cell { + position: relative; + padding-right: 20px; +} + +.ordered-list-editor .truncated-content { + white-space: normal; + word-break: break-word; +} + +.ordered-list-editor .show-more { + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + color: #007bff; + cursor: pointer; +} + +/* Style for the visible cells */ +.ordered-list-editor .tabulator-cell-visible { + display: table-cell !important; /* Force display as table cell */ +} + +/* Override any inline styles that might hide cells */ +.ordered-list-editor .tabulator-cell[style*="display: none"] { + display: table-cell !important; /* Force display as table cell */ +} + +/* Style for the textarea editor */ +.ordered-list-editor .tabulator-cell textarea { + min-height: 60px; + resize: vertical; + width: 100%; /* Ensure textarea fills the cell */ +} + +/* Style for the placeholder */ +.ordered-list-editor .tabulator-placeholder { + padding: 20px; + text-align: center; + color: #6c757d; +} + +/* Style for the Add Row button */ +.ordered-list-editor + .btn-primary { + margin-top: 0.5rem; +} + +/* Fullscreen mode styles */ +.ordered-list-editor.fullscreen-mode { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 9999; + background: white; + padding: 20px; + margin: 0; + overflow: auto; + box-sizing: border-box; +} + +.ordered-list-editor.fullscreen-mode .tabulator { + height: calc(100vh - 100px) !important; + width: 100% !important; +} diff --git a/eveai_app/templates/macros.html b/eveai_app/templates/macros.html index 43e0c08..d513f75 100644 --- a/eveai_app/templates/macros.html +++ b/eveai_app/templates/macros.html @@ -69,8 +69,9 @@
{{ field(class="form-control d-none " + class, disabled=disabled, readonly=readonly) }} {% elif field.type == 'OrderedListField' or 'ordered-list-field' in field_class %} - {# Behoud ordered-list-field class en voeg form-control toe #} - {{ field(class="form-control " + field_class|trim, disabled=disabled, readonly=readonly) }} + {# Create container for ordered list editor and hide the textarea #} +
+ {{ field(class="form-control d-none " + field_class|trim, disabled=disabled, readonly=readonly) }} {% elif field.type == 'SelectField' %} {{ field(class="form-control form-select " + class, disabled=disabled, readonly=readonly) }} {% else %} @@ -449,4 +450,3 @@ {% endmacro %} - diff --git a/eveai_app/templates/scripts.html b/eveai_app/templates/scripts.html index ca5c2ea..876bfce 100644 --- a/eveai_app/templates/scripts.html +++ b/eveai_app/templates/scripts.html @@ -13,11 +13,15 @@