@@ -53,6 +53,8 @@ use crate::{
5353} ;
5454
5555const MAX_TABLE_BYTES_TO_DISPLAY : usize = 2 * 1024 * 1024 ; // 2 MB
56+ const MIN_TABLE_ROWS_TO_DISPLAY : usize = 20 ;
57+ const MAX_LENGTH_CELL_WITHOUT_MINIMIZE : usize = 25 ;
5658
5759/// A PyDataFrame is a representation of a logical plan and an API to compose statements.
5860/// Use it to build a plan and `.collect()` to execute the plan and collect the result.
@@ -110,7 +112,37 @@ impl PyDataFrame {
110112 return Ok ( "No data to display" . to_string ( ) ) ;
111113 } ;
112114
115+ let table_uuid = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
116+
113117 let mut html_str = "
118+ <style>
119+ .expandable-container {
120+ display: inline-block;
121+ max-width: 200px;
122+ }
123+ .expandable {
124+ white-space: nowrap;
125+ overflow: hidden;
126+ text-overflow: ellipsis;
127+ display: block;
128+ }
129+ .full-text {
130+ display: none;
131+ white-space: normal;
132+ }
133+ .expand-btn {
134+ cursor: pointer;
135+ color: blue;
136+ text-decoration: underline;
137+ border: none;
138+ background: none;
139+ font-size: inherit;
140+ display: block;
141+ margin-top: 5px;
142+ }
143+ </style>
144+
145+
114146 <div style=\" width: 100%; max-width: 1000px; max-height: 300px; overflow: auto; border: 1px solid #ccc;\" >
115147 <table style=\" border-collapse: collapse; min-width: 100%\" >
116148 <thead>\n " . to_string ( ) ;
@@ -134,24 +166,64 @@ impl PyDataFrame {
134166 let batch_size = batch. get_array_memory_size ( ) ;
135167 let num_rows_to_display = match batch_size > MAX_TABLE_BYTES_TO_DISPLAY {
136168 true => {
137- has_more = true ;
169+ let num_batch_rows = batch . num_rows ( ) ;
138170 let ratio = MAX_TABLE_BYTES_TO_DISPLAY as f32 / batch_size as f32 ;
139- ( batch. num_rows ( ) as f32 * ratio) . round ( ) as usize
171+ let mut reduced_row_num = ( num_batch_rows as f32 * ratio) . round ( ) as usize ;
172+ if reduced_row_num < MIN_TABLE_ROWS_TO_DISPLAY {
173+ reduced_row_num = MIN_TABLE_ROWS_TO_DISPLAY . min ( num_batch_rows) ;
174+ }
175+
176+ has_more = has_more || reduced_row_num < num_batch_rows;
177+ reduced_row_num
140178 }
141179 false => batch. num_rows ( ) ,
142180 } ;
143181
144182 for row in 0 ..num_rows_to_display {
145183 let mut cells = Vec :: new ( ) ;
146- for formatter in & formatters {
147- cells. push ( format ! ( "<td style='border: 1px solid black; padding: 8px; text-align: left; white-space: nowrap;'>{}</td>" , formatter. value( row) ) ) ;
184+ for ( col, formatter) in formatters. iter ( ) . enumerate ( ) {
185+ let cell_data = formatter. value ( row) . to_string ( ) ;
186+ // From testing, primitive data types do not typically get larger than 21 characters
187+ if cell_data. len ( ) > MAX_LENGTH_CELL_WITHOUT_MINIMIZE {
188+ let short_cell_data = & cell_data[ 0 ..MAX_LENGTH_CELL_WITHOUT_MINIMIZE ] ;
189+ cells. push ( format ! ( "
190+ <td style='border: 1px solid black; padding: 8px; text-align: left; white-space: nowrap;'>
191+ <div class=\" expandable-container\" >
192+ <span class=\" expandable\" id=\" {table_uuid}-min-text-{row}-{col}\" >{short_cell_data}</span>
193+ <span class=\" full-text\" id=\" {table_uuid}-full-text-{row}-{col}\" >{cell_data}</span>
194+ <button class=\" expand-btn\" onclick=\" toggleDataFrameCellText('{table_uuid}',{row},{col})\" >...</button>
195+ </div>
196+ </td>" ) ) ;
197+ } else {
198+ cells. push ( format ! ( "<td style='border: 1px solid black; padding: 8px; text-align: left; white-space: nowrap;'>{}</td>" , formatter. value( row) ) ) ;
199+ }
148200 }
149201 let row_str = cells. join ( "" ) ;
150202 html_str. push_str ( & format ! ( "<tr>{}</tr>\n " , row_str) ) ;
151203 }
152204
153205 html_str. push_str ( "</tbody></table></div>\n " ) ;
154206
207+ html_str. push_str ( "
208+ <script>
209+ function toggleDataFrameCellText(table_uuid, row, col) {
210+ var shortText = document.getElementById(table_uuid + \" -min-text-\" + row + \" -\" + col);
211+ var fullText = document.getElementById(table_uuid + \" -full-text-\" + row + \" -\" + col);
212+ var button = event.target;
213+
214+ if (fullText.style.display === \" none\" ) {
215+ shortText.style.display = \" none\" ;
216+ fullText.style.display = \" inline\" ;
217+ button.textContent = \" (less)\" ;
218+ } else {
219+ shortText.style.display = \" inline\" ;
220+ fullText.style.display = \" none\" ;
221+ button.textContent = \" ...\" ;
222+ }
223+ }
224+ </script>
225+ " ) ;
226+
155227 if has_more {
156228 html_str. push_str ( "Data truncated due to size." ) ;
157229 }
0 commit comments