@@ -2,7 +2,7 @@ use crossterm::event::{KeyCode, KeyEvent};
22use ratatui:: layout:: { Constraint , Layout , Rect } ;
33use ratatui:: style:: Modifier ;
44use ratatui:: text:: { Line , Span } ;
5- use ratatui:: widgets:: { Block , Borders , Clear , List , ListItem , ListState , Paragraph } ;
5+ use ratatui:: widgets:: { Block , Borders , Cell , Clear , Paragraph , Row , Table , TableState } ;
66use ratatui:: Frame ;
77
88use crate :: action:: Action ;
@@ -14,7 +14,7 @@ pub struct SearchDialog {
1414 pub visible : bool ,
1515 pub filter : String ,
1616 pub results : Vec < LdapEntry > ,
17- list_state : ListState ,
17+ table_state : TableState ,
1818 theme : Theme ,
1919}
2020
@@ -24,15 +24,15 @@ impl SearchDialog {
2424 visible : false ,
2525 filter : String :: new ( ) ,
2626 results : Vec :: new ( ) ,
27- list_state : ListState :: default ( ) ,
27+ table_state : TableState :: default ( ) ,
2828 theme,
2929 }
3030 }
3131
3232 pub fn show_results ( & mut self , filter : String , results : Vec < LdapEntry > ) {
3333 self . filter = filter;
3434 self . results = results;
35- self . list_state . select ( if self . results . is_empty ( ) {
35+ self . table_state . select ( if self . results . is_empty ( ) {
3636 None
3737 } else {
3838 Some ( 0 )
@@ -51,21 +51,21 @@ impl SearchDialog {
5151
5252 match key. code {
5353 KeyCode :: Up | KeyCode :: Char ( 'k' ) => {
54- let i = self . list_state . selected ( ) . unwrap_or ( 0 ) ;
54+ let i = self . table_state . selected ( ) . unwrap_or ( 0 ) ;
5555 if i > 0 {
56- self . list_state . select ( Some ( i - 1 ) ) ;
56+ self . table_state . select ( Some ( i - 1 ) ) ;
5757 }
5858 Action :: None
5959 }
6060 KeyCode :: Down | KeyCode :: Char ( 'j' ) => {
61- let i = self . list_state . selected ( ) . unwrap_or ( 0 ) ;
61+ let i = self . table_state . selected ( ) . unwrap_or ( 0 ) ;
6262 if i + 1 < self . results . len ( ) {
63- self . list_state . select ( Some ( i + 1 ) ) ;
63+ self . table_state . select ( Some ( i + 1 ) ) ;
6464 }
6565 Action :: None
6666 }
6767 KeyCode :: Enter => {
68- if let Some ( idx) = self . list_state . selected ( ) {
68+ if let Some ( idx) = self . table_state . selected ( ) {
6969 if let Some ( entry) = self . results . get ( idx) {
7070 let dn = entry. dn . clone ( ) ;
7171 self . visible = false ;
@@ -87,7 +87,7 @@ impl SearchDialog {
8787 return ;
8888 }
8989
90- let popup_width = ( full. width as u32 * 80 / 100 ) . min ( 100 ) as u16 ;
90+ let popup_width = ( full. width as u32 * 90 / 100 ) as u16 ;
9191 let popup_height = ( full. height as u32 * 70 / 100 ) . min ( 40 ) as u16 ;
9292
9393 let x = full. x + ( full. width . saturating_sub ( popup_width) ) / 2 ;
@@ -112,7 +112,7 @@ impl SearchDialog {
112112 return ;
113113 }
114114
115- // Layout: hint (1 line) | results list
115+ // Layout: hint (1 line) | results table
116116 let layout = Layout :: vertical ( [ Constraint :: Length ( 1 ) , Constraint :: Min ( 1 ) ] ) . split ( inner) ;
117117
118118 let hint = Line :: from ( vec ! [
@@ -125,22 +125,46 @@ impl SearchDialog {
125125 ] ) ;
126126 frame. render_widget ( Paragraph :: new ( hint) , layout[ 0 ] ) ;
127127
128- let items: Vec < ListItem > = self
128+ let header = Row :: new ( vec ! [
129+ Cell :: from( Span :: styled( "DN" , self . theme. header) ) ,
130+ Cell :: from( Span :: styled( "sAMAccountName" , self . theme. header) ) ,
131+ Cell :: from( Span :: styled( "Display Name" , self . theme. header) ) ,
132+ Cell :: from( Span :: styled( "Mail" , self . theme. header) ) ,
133+ ] ) ;
134+
135+ let rows: Vec < Row > = self
129136 . results
130137 . iter ( )
131138 . map ( |entry| {
132- let oc = entry. object_classes ( ) . join ( ", " ) ;
133- let line = Line :: from ( vec ! [
134- Span :: styled( & entry. dn, self . theme. normal) ,
135- Span :: styled( format!( " [{}]" , oc) , self . theme. dimmed) ,
136- ] ) ;
137- ListItem :: new ( line)
139+ Row :: new ( vec ! [
140+ Cell :: from( Span :: styled( & entry. dn, self . theme. normal) ) ,
141+ Cell :: from( Span :: styled(
142+ entry. first_value( "sAMAccountName" ) . unwrap_or( "" ) ,
143+ self . theme. normal,
144+ ) ) ,
145+ Cell :: from( Span :: styled(
146+ entry. first_value( "displayName" ) . unwrap_or( "" ) ,
147+ self . theme. normal,
148+ ) ) ,
149+ Cell :: from( Span :: styled(
150+ entry. first_value( "mail" ) . unwrap_or( "" ) ,
151+ self . theme. normal,
152+ ) ) ,
153+ ] )
138154 } )
139155 . collect ( ) ;
140156
141- let list =
142- List :: new ( items) . highlight_style ( self . theme . selected . add_modifier ( Modifier :: BOLD ) ) ;
157+ let widths = [
158+ Constraint :: Percentage ( 40 ) ,
159+ Constraint :: Percentage ( 15 ) ,
160+ Constraint :: Percentage ( 20 ) ,
161+ Constraint :: Percentage ( 25 ) ,
162+ ] ;
163+
164+ let table = Table :: new ( rows, widths)
165+ . header ( header. style ( self . theme . header ) )
166+ . highlight_style ( self . theme . selected . add_modifier ( Modifier :: BOLD ) ) ;
143167
144- frame. render_stateful_widget ( list , layout[ 1 ] , & mut self . list_state . clone ( ) ) ;
168+ frame. render_stateful_widget ( table , layout[ 1 ] , & mut self . table_state . clone ( ) ) ;
145169 }
146170}
0 commit comments