@@ -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() {
831838document .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