@@ -410,8 +410,11 @@ $(document).on('click', '#editUser', function () {
410410 $ ( '#editUserModal select[name="ovpnConfig"]' ) . val ( u . ovpnConfig ) ;
411411
412412 request . get ( '/ovpn/group' ) . then ( ( data ) => {
413- $ ( '#editUserModal select[name="gid"]' ) . html ( data . map ( ( i ) => `<option value="${ i . id } ">${ i . name } </option>` ) ) ;
414- $ ( '#editUserModal select[name="gid"]' ) . val ( cgid ) ;
413+ $ ( '#editUserModal ul[name="groupList"]' ) . html ( renderDropdownItems ( buildTree ( data ) ) ) ;
414+
415+ const item = $ ( `#editUserModal [data-id="${ u . gid } "]` ) ;
416+ item . addClass ( 'selected' ) ;
417+ $ ( '#editUserModal button[name="groupBtn"]' ) . text ( item . data ( 'name' ) ) ;
415418 } ) ;
416419
417420 const elem = document . querySelector ( '#editUserModal input[name="expireDate"]' ) ;
@@ -436,7 +439,7 @@ $('#editUserModal form').submit(function () {
436439 const ipAddr = $ ( '#editUserModal input[name="ipAddr"]' ) . val ( ) ;
437440 const expireDate = $ ( '#editUserModal input[name="expireDate"]' ) . val ( ) ;
438441 const ovpnConfig = $ ( '#editUserModal select[name="ovpnConfig"]' ) . val ( ) || '' ;
439- const gid = $ ( '#editUserModal select [name="gid"]' ) . val ( ) ;
442+ const gid = $ ( '#editUserModal input [name="gid"]' ) . val ( ) ;
440443
441444 request . patch ( '/ovpn/user' , { id, name, username, ipAddr, expireDate, ovpnConfig, gid } ) . then ( ( data ) => {
442445 vtable . ajax . reload ( null , false ) ;
@@ -669,6 +672,31 @@ function renderTree(nodes, container) {
669672 } ) ;
670673}
671674
675+ function renderDropdownItems ( treeData ) {
676+ let html = '' ;
677+ treeData . forEach ( ( node ) => {
678+ const indent = ' ' . repeat ( node . depth ) ;
679+ const symbol = node . depth > 0 ? '└─ ' : '' ;
680+ const displayName = indent + symbol + node . name ;
681+
682+ html += `
683+ <li>
684+ <a class="dropdown-item group-select-item"
685+ href="#"
686+ data-id="${ node . id } "
687+ data-name="${ node . name } "
688+ onmouseenter="this.focus()"
689+ >
690+ ${ displayName }
691+ </a>
692+ </li>
693+ ` ;
694+ if ( node . children ) html += renderDropdownItems ( node . children ) ;
695+ } ) ;
696+
697+ return html ;
698+ }
699+
672700function refreshTree ( data ) {
673701 treeMenu . innerHTML = '' ;
674702 const tree = buildTree ( data ) ;
@@ -709,8 +737,26 @@ function handleContextMenu(e, node) {
709737 }
710738
711739 contextMenu . style . display = 'block' ;
712- contextMenu . style . left = `${ e . pageX } px` ;
713- contextMenu . style . top = `${ e . pageY } px` ;
740+
741+ const menuWidth = contextMenu . offsetWidth ;
742+ const menuHeight = contextMenu . offsetHeight ;
743+
744+ const pageWidth = window . innerWidth ;
745+ const pageHeight = window . innerHeight ;
746+
747+ let x = e . pageX ;
748+ let y = e . pageY ;
749+
750+ if ( x + menuWidth > pageWidth ) {
751+ x -= menuWidth ;
752+ }
753+
754+ if ( y + menuHeight > pageHeight ) {
755+ y -= menuHeight ;
756+ }
757+
758+ contextMenu . style . left = `${ x } px` ;
759+ contextMenu . style . top = `${ y } px` ;
714760}
715761
716762menuAdd . addEventListener ( 'click' , ( ) => {
@@ -769,6 +815,20 @@ document.addEventListener('click', (e) => {
769815 }
770816} ) ;
771817
818+ $ ( '#editUserModal .dropdown' ) . on ( 'shown.bs.dropdown' , function ( ) {
819+ $ ( '#editUserModal .group-select-item.selected' ) . focus ( ) ;
820+ } ) ;
821+
822+ $ ( document ) . on ( 'click' , '.group-select-item' , function ( e ) {
823+ e . preventDefault ( ) ;
824+
825+ $ ( '#editUserModal .group-select-item' ) . removeClass ( 'selected' ) ;
826+ $ ( this ) . addClass ( 'selected' ) ;
827+
828+ $ ( '#editUserModal button[name="groupBtn"]' ) . text ( $ ( this ) . data ( 'name' ) ) ;
829+ $ ( '#editUserModal input[name="gid"]' ) . val ( $ ( this ) . data ( 'id' ) ) ;
830+ } ) ;
831+
772832$ ( '#treeVpnConfigSumbit' ) . click ( function ( ) {
773833 const config = $ ( '#treeVpnConfigModal textarea[name="config"]' ) . val ( ) ;
774834 request . patch ( '/ovpn/group' , { id : currentNode . id , config : config ?. trim ( ) ?. replace ( / \n / g, '\\n' ) } ) . then ( ( ) => {
0 commit comments