@@ -73,6 +73,8 @@ impl PyTableProvider {
73
73
}
74
74
}
75
75
const MAX_TABLE_BYTES_TO_DISPLAY : usize = 2 * 1024 * 1024 ; // 2 MB
76
+ const MIN_TABLE_ROWS_TO_DISPLAY : usize = 20 ;
77
+ const MAX_LENGTH_CELL_WITHOUT_MINIMIZE : usize = 25 ;
76
78
77
79
/// A PyDataFrame is a representation of a logical plan and an API to compose statements.
78
80
/// Use it to build a plan and `.collect()` to execute the plan and collect the result.
@@ -130,7 +132,37 @@ impl PyDataFrame {
130
132
return Ok ( "No data to display" . to_string ( ) ) ;
131
133
} ;
132
134
135
+ let table_uuid = uuid:: Uuid :: new_v4 ( ) . to_string ( ) ;
136
+
133
137
let mut html_str = "
138
+ <style>
139
+ .expandable-container {
140
+ display: inline-block;
141
+ max-width: 200px;
142
+ }
143
+ .expandable {
144
+ white-space: nowrap;
145
+ overflow: hidden;
146
+ text-overflow: ellipsis;
147
+ display: block;
148
+ }
149
+ .full-text {
150
+ display: none;
151
+ white-space: normal;
152
+ }
153
+ .expand-btn {
154
+ cursor: pointer;
155
+ color: blue;
156
+ text-decoration: underline;
157
+ border: none;
158
+ background: none;
159
+ font-size: inherit;
160
+ display: block;
161
+ margin-top: 5px;
162
+ }
163
+ </style>
164
+
165
+
134
166
<div style=\" width: 100%; max-width: 1000px; max-height: 300px; overflow: auto; border: 1px solid #ccc;\" >
135
167
<table style=\" border-collapse: collapse; min-width: 100%\" >
136
168
<thead>\n " . to_string ( ) ;
@@ -154,24 +186,64 @@ impl PyDataFrame {
154
186
let batch_size = batch. get_array_memory_size ( ) ;
155
187
let num_rows_to_display = match batch_size > MAX_TABLE_BYTES_TO_DISPLAY {
156
188
true => {
157
- has_more = true ;
189
+ let num_batch_rows = batch . num_rows ( ) ;
158
190
let ratio = MAX_TABLE_BYTES_TO_DISPLAY as f32 / batch_size as f32 ;
159
- ( batch. num_rows ( ) as f32 * ratio) . round ( ) as usize
191
+ let mut reduced_row_num = ( num_batch_rows as f32 * ratio) . round ( ) as usize ;
192
+ if reduced_row_num < MIN_TABLE_ROWS_TO_DISPLAY {
193
+ reduced_row_num = MIN_TABLE_ROWS_TO_DISPLAY . min ( num_batch_rows) ;
194
+ }
195
+
196
+ has_more = has_more || reduced_row_num < num_batch_rows;
197
+ reduced_row_num
160
198
}
161
199
false => batch. num_rows ( ) ,
162
200
} ;
163
201
164
202
for row in 0 ..num_rows_to_display {
165
203
let mut cells = Vec :: new ( ) ;
166
- for formatter in & formatters {
167
- cells. push ( format ! ( "<td style='border: 1px solid black; padding: 8px; text-align: left; white-space: nowrap;'>{}</td>" , formatter. value( row) ) ) ;
204
+ for ( col, formatter) in formatters. iter ( ) . enumerate ( ) {
205
+ let cell_data = formatter. value ( row) . to_string ( ) ;
206
+ // From testing, primitive data types do not typically get larger than 21 characters
207
+ if cell_data. len ( ) > MAX_LENGTH_CELL_WITHOUT_MINIMIZE {
208
+ let short_cell_data = & cell_data[ 0 ..MAX_LENGTH_CELL_WITHOUT_MINIMIZE ] ;
209
+ cells. push ( format ! ( "
210
+ <td style='border: 1px solid black; padding: 8px; text-align: left; white-space: nowrap;'>
211
+ <div class=\" expandable-container\" >
212
+ <span class=\" expandable\" id=\" {table_uuid}-min-text-{row}-{col}\" >{short_cell_data}</span>
213
+ <span class=\" full-text\" id=\" {table_uuid}-full-text-{row}-{col}\" >{cell_data}</span>
214
+ <button class=\" expand-btn\" onclick=\" toggleDataFrameCellText('{table_uuid}',{row},{col})\" >...</button>
215
+ </div>
216
+ </td>" ) ) ;
217
+ } else {
218
+ cells. push ( format ! ( "<td style='border: 1px solid black; padding: 8px; text-align: left; white-space: nowrap;'>{}</td>" , formatter. value( row) ) ) ;
219
+ }
168
220
}
169
221
let row_str = cells. join ( "" ) ;
170
222
html_str. push_str ( & format ! ( "<tr>{}</tr>\n " , row_str) ) ;
171
223
}
172
224
173
225
html_str. push_str ( "</tbody></table></div>\n " ) ;
174
226
227
+ html_str. push_str ( "
228
+ <script>
229
+ function toggleDataFrameCellText(table_uuid, row, col) {
230
+ var shortText = document.getElementById(table_uuid + \" -min-text-\" + row + \" -\" + col);
231
+ var fullText = document.getElementById(table_uuid + \" -full-text-\" + row + \" -\" + col);
232
+ var button = event.target;
233
+
234
+ if (fullText.style.display === \" none\" ) {
235
+ shortText.style.display = \" none\" ;
236
+ fullText.style.display = \" inline\" ;
237
+ button.textContent = \" (less)\" ;
238
+ } else {
239
+ shortText.style.display = \" inline\" ;
240
+ fullText.style.display = \" none\" ;
241
+ button.textContent = \" ...\" ;
242
+ }
243
+ }
244
+ </script>
245
+ " ) ;
246
+
175
247
if has_more {
176
248
html_str. push_str ( "Data truncated due to size." ) ;
177
249
}
0 commit comments