Skip to content

Commit 8a9e968

Browse files
authored
Merge pull request #5862 from Laravel-Backpack/fix-dropdown-position
Fix dropdown position
2 parents 3799356 + 8af3c61 commit 8a9e968

File tree

2 files changed

+127
-5
lines changed

2 files changed

+127
-5
lines changed

src/resources/assets/css/common.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,22 @@ div[id$="_wrapper"] .dt-processing {
107107
z-index: 1000 !important;
108108
inset: unset !important;
109109
}
110+
111+
/* Force all table containers to be visible */
112+
.table-responsive,
113+
.dataTables_wrapper,
114+
.card,
115+
.card-body,
116+
.container,
117+
.container-fluid {
118+
overflow: visible !important;
119+
}
120+
121+
.dataTables_wrapper .dropdown-menu {
122+
position: fixed !important;
123+
z-index: 999999 !important;
124+
}
125+
110126
.navbar-filters {
111127
min-height: 25px;
112128
border-radius: 0;

src/resources/views/crud/components/datatable/datatable_logic.blade.php

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,13 @@ function setupTableUI(tableId, config) {
580580
}
581581
});
582582
window.dispatchEvent(event);
583+
584+
// Initialize dropdown positioning fix if table has dropdown buttons
585+
if ($(`#${tableId}`).data('has-line-buttons-as-dropdown')) {
586+
setTimeout(() => {
587+
initDatatableDropdowns(tableId);
588+
}, 100);
589+
}
583590
}
584591
585592
// Function to set up table event handlers
@@ -831,7 +838,6 @@ function resizeCrudTableColumnWidths() {
831838
document.addEventListener('backpack:filter:changed', function (event) {
832839
const tableId = event.detail.componentId || '';
833840
if (!tableId) {
834-
console.log('No componentId provided in event detail. Exiting.');
835841
return;
836842
}
837843
@@ -912,14 +918,114 @@ function formatActionColumnAsDropdown(tableId) {
912918
// Only create dropdown if there are items to drop
913919
if (dropdownItems.length > 0) {
914920
// Wrap the cell with the component needed for the dropdown
915-
actionCell.wrapInner('<div class="nav-item dropdown"></div>');
916-
actionCell.wrapInner('<div class="dropdown-menu dropdown-menu-left"></div>');
921+
actionCell.wrapInner('<div class="dropdown-menu"></div>');
922+
actionCell.wrapInner('<div class="dropdown"></div>');
917923
918-
actionCell.prepend('<a class="btn btn-sm px-2 py-1 btn-outline-primary dropdown-toggle actions-buttons-column" href="#" data-toggle="dropdown" data-bs-toggle="dropdown" data-bs-auto-close="outside" aria-expanded="false">Actions</a>');
924+
actionCell.prepend('<button class="btn btn-sm px-2 py-1 btn-outline-primary dropdown-toggle actions-buttons-column" type="button" data-bs-toggle="dropdown" data-bs-boundary="viewport" data-bs-placement="bottom-start" aria-expanded="false">Actions</button>');
919925
920926
const remainingButtons = actionButtons.slice(0, buttonsToShowBeforeDropdown);
921927
actionCell.prepend(remainingButtons);
922928
}
923929
});
924930
}
925-
</script>
931+
932+
933+
function initDatatableDropdowns(tableId) {
934+
// Wait for table to be ready
935+
setTimeout(function() {
936+
const table = document.getElementById(tableId);
937+
if (!table) {
938+
return;
939+
}
940+
941+
$(document).ready(function() {
942+
// Use event delegation for dynamically created elements
943+
$('#' + tableId).off('click.lineActions').on('click.lineActions', '.actions-buttons-column.dropdown-toggle', function(e) {
944+
e.preventDefault();
945+
e.stopPropagation();
946+
947+
const $this = $(this);
948+
const $dropdown = $this.next('.dropdown');
949+
const $menu = $dropdown.find('.dropdown-menu');
950+
951+
// If no menu found, let's create one or find it differently
952+
if ($menu.length === 0) {
953+
// Try different selectors
954+
const $ul = $dropdown.find('ul');
955+
const $allMenus = $('.dropdown-menu');
956+
957+
// Try to find the menu as a sibling or next element
958+
const $nextSibling = $this.next();
959+
if ($ul.length > 0) {
960+
$ul.addClass('dropdown-menu show').show();
961+
962+
// Position the UL
963+
const buttonRect = this.getBoundingClientRect();
964+
$ul.css({
965+
'position': 'fixed',
966+
'top': (buttonRect.bottom + 5) + 'px',
967+
'left': buttonRect.left + 'px',
968+
'z-index': '999999',
969+
'display': 'block',
970+
'background': 'white',
971+
'border': '1px solid #dee2e6',
972+
'border-radius': '0.375rem',
973+
'box-shadow': '0 0.5rem 1rem rgba(0, 0, 0, 0.15)',
974+
'min-width': '160px',
975+
'padding': '0.5rem 0'
976+
});
977+
978+
return;
979+
}
980+
}
981+
982+
$('#' + tableId + ' .actions-buttons-column').next('.dropdown').find('.dropdown-menu').removeClass('show').hide();
983+
984+
// Show this dropdown
985+
$menu.addClass('show').show();
986+
987+
// Force positioning
988+
const buttonRect = this.getBoundingClientRect();
989+
const menuHeight = $menu.outerHeight() || 150;
990+
const windowHeight = $(window).height();
991+
992+
let top = buttonRect.bottom + window.scrollY + 5;
993+
if (top + menuHeight > windowHeight + window.scrollY) {
994+
top = buttonRect.top + window.scrollY - menuHeight - 5;
995+
}
996+
997+
// Apply positioning with maximum override
998+
const cssProps = {
999+
'position': 'fixed',
1000+
'top': (buttonRect.bottom + 5) + 'px',
1001+
'left': buttonRect.left + 'px',
1002+
'z-index': '999999',
1003+
'display': 'block',
1004+
'background': 'white',
1005+
'border': '1px solid #dee2e6',
1006+
'border-radius': '0.375rem',
1007+
'box-shadow': '0 0.5rem 1rem rgba(0, 0, 0, 0.15)',
1008+
'min-width': '160px',
1009+
'padding': '0.5rem 0'
1010+
};
1011+
1012+
$menu.css(cssProps);
1013+
1014+
// Auto-position if going off screen
1015+
if (buttonRect.bottom + menuHeight > windowHeight) {
1016+
$menu.css('top', (buttonRect.top - menuHeight - 5) + 'px');
1017+
}
1018+
});
1019+
1020+
// Close on outside click, but only for line action dropdowns in this table
1021+
$(document).off('click.lineActions' + tableId).on('click.lineActions' + tableId, function(e) {
1022+
// Only close line action dropdowns, not export button dropdowns
1023+
if (!$(e.target).closest('#' + tableId + ' .actions-buttons-column').length &&
1024+
!$(e.target).closest('#' + tableId + ' .actions-buttons-column').next('.dropdown').length) {
1025+
$('#' + tableId + ' .actions-buttons-column').next('.dropdown').find('.dropdown-menu').removeClass('show').hide();
1026+
}
1027+
});
1028+
});
1029+
}, 500);
1030+
}
1031+
</script>

0 commit comments

Comments
 (0)