@@ -3314,4 +3314,261 @@ Page.Base = class Base extends Page {
33143314 }
33153315 }
33163316
3317+ //
3318+ // Sortble grid stuff
3319+ //
3320+
3321+ getSortedTableRows ( id ) {
3322+ // get sorted (and filtered!) table rows
3323+ var opts = this . tables [ id ] ;
3324+ var sort_by = opts . sort_by ;
3325+ var sort_dir = opts . sort_dir ;
3326+ var sort_type = 'number' ;
3327+ if ( opts . rows . length && ( typeof ( opts . rows [ 0 ] [ sort_by ] ) == 'string' ) ) sort_type = 'string' ;
3328+
3329+ // apply filter
3330+ var rows = [ ] ;
3331+ if ( typeof ( opts . filter ) == 'function' ) {
3332+ // custom filter function
3333+ rows = opts . rows . filter ( opts . filter ) ;
3334+ }
3335+ else {
3336+ // default string regex match
3337+ var filter_re = new RegExp ( escape_regexp ( opts . filter ) || '.*' , 'i' ) ;
3338+ rows = opts . rows . filter ( function ( row ) {
3339+ var blob = hash_values_to_array ( row ) . join ( ' ' ) ;
3340+ return ! ! blob . match ( filter_re ) ;
3341+ } ) ;
3342+ }
3343+
3344+ // apply custom sort
3345+ rows . sort ( function ( a , b ) {
3346+ if ( sort_type == 'number' ) {
3347+ return ( ( a [ sort_by ] - b [ sort_by ] ) * sort_dir ) ;
3348+ }
3349+ else {
3350+ return ( a [ sort_by ] . toString ( ) . localeCompare ( b [ sort_by ] ) * sort_dir ) ;
3351+ }
3352+ } ) ;
3353+
3354+ return rows ;
3355+ }
3356+
3357+ updateTableRows ( id , rows ) {
3358+ // replace sorted table rows, redraw
3359+ var opts = this . tables [ id ] ;
3360+ if ( rows ) opts . rows = rows ;
3361+
3362+ var disp_rows = this . getSortedTableRows ( opts . id ) ;
3363+
3364+ // redraw pagination thing
3365+ this . div . find ( '#st_hinfo_' + opts . id ) . html (
3366+ this . getTableHeaderInfo ( id , disp_rows )
3367+ ) ;
3368+
3369+ // redraw rows
3370+ this . div . find ( '#st_' + opts . id ) . html (
3371+ this . getTableColumnHTML ( opts . id ) +
3372+ this . getTableContentHTML ( opts . id , disp_rows )
3373+ ) ;
3374+ }
3375+
3376+ applyTableFilter ( elem ) {
3377+ // key typed in table filter box, redraw
3378+ var id = $ ( elem ) . data ( 'id' ) ;
3379+ var opts = this . tables [ id ] ;
3380+ opts . filter = $ ( elem ) . val ( ) ;
3381+
3382+ var disp_rows = this . getSortedTableRows ( opts . id ) ;
3383+
3384+ // redraw pagination thing
3385+ this . div . find ( '#st_hinfo_' + opts . id ) . html (
3386+ this . getTableHeaderInfo ( id , disp_rows )
3387+ ) ;
3388+
3389+ // redraw rows
3390+ this . div . find ( '#st_' + opts . id ) . html (
3391+ this . getTableColumnHTML ( opts . id ) +
3392+ this . getTableContentHTML ( opts . id , disp_rows )
3393+ ) ;
3394+ }
3395+
3396+ getTableHeaderInfo ( id , disp_rows ) {
3397+ // construct HTML for sortable table header info widget
3398+ var opts = this . tables [ id ] ;
3399+ var rows = opts . rows ;
3400+ var html = '' ;
3401+
3402+ if ( disp_rows . length < rows . length ) {
3403+ html += commify ( disp_rows . length ) + ' of ' + commify ( rows . length ) + ' ' + pluralize ( opts . item_name , rows . length ) + '' ;
3404+ }
3405+ else {
3406+ html += commify ( rows . length ) + ' ' + pluralize ( opts . item_name , rows . length ) + '' ;
3407+ }
3408+
3409+ var bold_idx = opts . column_ids . indexOf ( opts . sort_by ) ;
3410+ html += ', sorted by ' + opts . column_labels [ bold_idx ] + '' ;
3411+ html += ' <i class="mdi mdi-menu-' + ( ( opts . sort_dir == 1 ) ? 'up' : 'down' ) + '"></i>' ;
3412+ // html += ((opts.sort_dir == 1) ? ' ascending' : ' descending');
3413+
3414+ return html ;
3415+ }
3416+
3417+ getTableColumnHTML ( id ) {
3418+ // construct HTML for sortable table column headers (THs)
3419+ var opts = this . tables [ id ] ;
3420+ var html = '' ;
3421+ html += '<ul class="grid_row_header">' ;
3422+
3423+ opts . column_ids . forEach ( function ( col_id , idx ) {
3424+ var col_label = opts . column_labels [ idx ] ;
3425+ if ( ! col_id ) {
3426+ html += '<div>' + col_label + '</div>' ;
3427+ return ;
3428+ }
3429+ var classes = [ 'st_col_header' ] ;
3430+ var icon = '' ;
3431+ if ( col_id == opts . sort_by ) {
3432+ classes . push ( 'active' ) ;
3433+ icon = ' <i class="mdi mdi-menu-' + ( ( opts . sort_dir == 1 ) ? 'up' : 'down' ) + '"></i>' ;
3434+ }
3435+ html += '<div class="' + classes . join ( ' ' ) + '" data-id="' + opts . id + '" data-col="' + col_id + '" onClick="$P().toggleTableSort(this)">' + col_label + icon + '</div>' ;
3436+ } ) ;
3437+
3438+ html += '</ul>' ;
3439+ return html ;
3440+ }
3441+
3442+ getTableContentHTML ( id , disp_rows ) {
3443+ // construct HTML for sortable table content (rows)
3444+ var opts = this . tables [ id ] ;
3445+ var html = '' ;
3446+ var bold_idx = opts . column_ids . indexOf ( opts . sort_by ) ;
3447+
3448+ var len = disp_rows . length ;
3449+ var max_rows = config . max_table_rows || 0 ;
3450+ var chopped = 0 ;
3451+ if ( max_rows && ( disp_rows . length > max_rows ) ) {
3452+ chopped = disp_rows . length - max_rows ;
3453+ len = max_rows ;
3454+ }
3455+
3456+ for ( var idx = 0 ; idx < len ; idx ++ ) {
3457+ var row = disp_rows [ idx ] ;
3458+ var tds = opts . callback ( row , idx ) ;
3459+ html += '<ul class="grid_row ' + ( tds . className || '' ) + '">' ;
3460+ for ( var idy = 0 , ley = tds . length ; idy < ley ; idy ++ ) {
3461+ html += '<div' + ( ( bold_idx == idy ) ? ' style="font-weight:bold"' : '' ) + '>' + tds [ idy ] + '</div>' ;
3462+ }
3463+ html += '</ul>' ;
3464+ } // foreach row
3465+
3466+ if ( ! disp_rows . length ) {
3467+ html += '<ul class="grid_row_empty"><div style="grid-column: 1 / -1;">' ;
3468+ html += 'No ' + pluralize ( opts . item_name ) + ' found.' ;
3469+ html += '</div></ul>' ;
3470+ }
3471+ else if ( chopped ) {
3472+ html += '<ul class="grid_row_more"><div style="grid-column: 1 / -1;">' ;
3473+ html += `(${ commify ( chopped ) } more ${ pluralize ( opts . item_name , chopped ) } not shown)` ;
3474+ html += '</div></ul>' ;
3475+ }
3476+
3477+ return html ;
3478+ }
3479+
3480+ toggleTableSort ( elem ) {
3481+ var id = $ ( elem ) . data ( 'id' ) ;
3482+ var col_id = $ ( elem ) . data ( 'col' ) ;
3483+ var opts = this . tables [ id ] ;
3484+
3485+ // swap sort dir or change sort column
3486+ if ( col_id == opts . sort_by ) {
3487+ // swap dir
3488+ opts . sort_dir *= - 1 ;
3489+ }
3490+ else {
3491+ // same sort dir but change column
3492+ opts . sort_by = col_id ;
3493+ }
3494+
3495+ var disp_rows = this . getSortedTableRows ( opts . id ) ;
3496+
3497+ // redraw pagination thing
3498+ this . div . find ( '#st_hinfo_' + opts . id ) . html (
3499+ this . getTableHeaderInfo ( id , disp_rows )
3500+ ) ;
3501+
3502+ // redraw grid
3503+ this . div . find ( '#st_' + opts . id ) . html (
3504+ this . getTableColumnHTML ( id ) +
3505+ this . getTableContentHTML ( opts . id , disp_rows )
3506+ ) ;
3507+ }
3508+
3509+ getSortableTable ( rows , opts , callback ) {
3510+ // get HTML for sortable and filterable table
3511+ // opts: { id, item_name, sort_by, sort_dir, filter, column_ids, column_labels }
3512+ var self = this ;
3513+ var html = '' ;
3514+ if ( ! this . tables ) this . tables = { } ;
3515+
3516+ // retrieve previous settings if applicable
3517+ if ( this . tables [ opts . id ] ) {
3518+ if ( this . tables [ opts . id ] . filter ) opts . filter = this . tables [ opts . id ] . filter ;
3519+ if ( this . tables [ opts . id ] . sort_by ) opts . sort_by = this . tables [ opts . id ] . sort_by ;
3520+ if ( this . tables [ opts . id ] . sort_dir ) opts . sort_dir = this . tables [ opts . id ] . sort_dir ;
3521+ }
3522+
3523+ // save in page for resort / filtering
3524+ opts . rows = rows ;
3525+ opts . callback = callback ;
3526+ this . tables [ opts . id ] = opts ;
3527+
3528+ var disp_rows = this . getSortedTableRows ( opts . id ) ;
3529+
3530+ if ( ! opts . hide_pagination ) {
3531+ // pagination
3532+ html += '<div class="data_grid_pagination">' ;
3533+
3534+ html += '<div style="text-align:left" id="st_hinfo_' + opts . id + '">' ;
3535+ html += this . getTableHeaderInfo ( opts . id , disp_rows ) ;
3536+ html += '</div>' ;
3537+
3538+ html += '<div style="text-align:center">' ;
3539+ html += ' ' ;
3540+ html += '</div>' ;
3541+
3542+ html += '<div style="text-align:right">' ;
3543+ html += 'Page 1 of 1' ;
3544+ html += '</div>' ;
3545+
3546+ html += '</div>' ;
3547+
3548+ html += '<div style="margin-top:5px;">' ;
3549+ }
3550+ else {
3551+ // no pagination
3552+ html += '<div>' ;
3553+ }
3554+
3555+ var tattrs = opts . attribs || { } ;
3556+ if ( opts . class ) tattrs . class = opts . class ;
3557+ if ( ! tattrs . class ) {
3558+ tattrs . class = 'data_grid' ;
3559+ if ( opts . item_name . match ( / ^ \w + $ / ) ) tattrs . class += ' ' + opts . item_name + '_grid' ;
3560+ }
3561+ if ( ! tattrs . style ) tattrs . style = '' ;
3562+ tattrs . style += 'grid-template-columns: repeat(' + opts . column_ids . length + ', auto);' ;
3563+ html += '<div id="st_' + opts . id + '" ' + compose_attribs ( tattrs ) + '>' ;
3564+
3565+ html += this . getTableColumnHTML ( opts . id ) ;
3566+ html += this . getTableContentHTML ( opts . id , disp_rows ) ;
3567+
3568+ html += '</div>' ; // scroll wrapper
3569+ html += '</div>' ; // grid
3570+
3571+ return html ;
3572+ }
3573+
33173574} ;
0 commit comments