export type SourceData = Record < string , any > ; export type OneOrMore < T > = T | T [ ] ; export type DataHandler < T extends SourceData > = ( data : T [ ] ) => T [ ] ; export interface Processor < T > { param source load : ( source : T [ ] ) => Processor < T > ; example search : ( searcher : string | ( ( val : T [ ] ) => T [ ] ) , fields ? : ( keyof T ) [ ] | undefined ) => Processor < T > ; example filter : ( option : { [ key in keyof T ] ? : T [ key ] | ( ( val : T [ key ] ) => boolean ) | ( T [ key ] | ( ( val : T [ key ] ) => boolean ) ) [ ] ; } ) => Processor < T > ; example sort : ( option : keyof T | ( keyof T ) [ ] | ( ( a : T , b : T ) => number ) , order ? : "acs" | "desc" | "ascing" | "descing" | ( "acs" | "desc" | "ascing" | "descing" ) [ ] | undefined ) => Processor < T > ; param size param current page : ( size ? : number , current ? : number ) => Processor < T > ; example onUpdate : ( callback : OnUpdateCallback < T > ) => Processor < T > ; /**
     * Calculate immediately.
     */ exec : ( ) => Result < T > ; } export interface Result < T > { /** Paged data */ current : T [ ] ; /** Current page */ page : number ; /** Total number of page */ pageCount : number ; /** Total number of data handled by filter, search, sort  */ total : number ; } export type OnUpdateCallback < T > = ( result : Result < T > ) => void ; function toArray ( item ) { return ! ( item instanceof Array ) ? [ item ] : item ; } function toString ( item ) { return item . toString ? item . toString ( ) : "" ; } function isEqual ( target , expect ) { if ( typeof expect === "function" ) { return expect ( target ) ; } else { return expect === target ; } } function isExists ( target ) { return typeof target !== "undefined" ; } param source param wait export function createProcessor < T extends SourceData > ( source ? ) { const state = { filter : { cache : [ ] as T [ ] , handler : ( data ) => data , update ( ) { state . filter . cache = state . filter . handler ( data ) ; } , } , search : { cache : [ ] as T [ ] , handler : ( data ) => data , update ( ) { state . search . cache = state . search . handler ( state . filter . cache ) ; } , } , sort : { cache : [ ] as T [ ] , handler : ( data ) => data , update ( ) { state . sort . cache = state . sort . handler ( state . search . cache ) ; } , } , page : { cache : { current : [ ] , page : 1 , pageCount : 1 , total : 0 , } as Result < T > , handler : ( data ) => ( { current : data , page : 1 , pageCount : 1 , total : data . length , } ) , update ( ) { state . page . cache = state . page . handler ( state . sort . cache ) ; } , } , } ; let data = source || [ ] ; let onUpdateHandler = null ; function calc ( cacheKeys ) { cacheKeys . forEach ( ( key ) => { state [ key ] . update ( ) ; } ) ; onUpdateHandler && onUpdateHandler ( state . page . cache ) ; } const processor = { load ( source ) { data = source . concat ( ) ; calc ( [ "filter" , "search" , "sort" , "page" ] ) ; return processor ; } , filter ( option ) { state . filter . handler = ( d ) => { return d . filter ( ( item ) => Object . keys ( option ) . every ( ( field ) => { const target = item [ field ] ; const expects = toArray ( option [ field ] ) ; return expects . some ( ( expect ) => { return ! isExists ( expect ) || isEqual ( target , expect ) ; } ) ; } ) ) ; } ; calc ( [ "filter" , "search" , "sort" , "page" ] ) ; return processor ; } , search ( searcher , fields ) { state . search . handler = ( d ) => { if ( ! searcher ) { return d . concat ( ) ; } if ( typeof searcher === "function" ) { return searcher ( d . concat ( ) ) ; } return d . filter ( ( item ) => { const searchColumns = fields || Object . keys ( item ) ; const regexp = new RegExp ( searcher , "i" ) ; return searchColumns . some ( ( column ) => toString ( item [ column ] ) . search ( regexp ) > - 1 ) ; } ) ; } ; calc ( [ "search" , "sort" , "page" ] ) ; return processor ; } , sort ( option , order ) { state . sort . handler = ( d ) => { d = d . concat ( ) ; if ( typeof option === "function" ) { return d . sort ( option ) ; } else { const options = toArray ( option ) ; const orders = toArray ( order ) ; const sorter = ( a , b ) => options . map ( ( o , i ) => { const dir = [ "acs" , "ascing" , undefined ] . includes ( orders [ i ] ) ? 1 : - 1 ; return a [ o ] > b [ o ] ? dir : a [ o ] < b [ o ] ? - dir : 0 ; } ) . reduce ( ( p , n ) => ( p ? p : n ) , 0 ) ; d . sort ( sorter ) ; return d ; } } ; calc ( [ "sort" , "page" ] ) ; return processor ; } , page ( size , current = 1 ) { state . page . handler = ( d ) => { const total = d . length ; if ( ! size || size <= 0 ) { return { current : d . concat ( ) , page : 1 , pageCount : 1 , total , } ; } if ( current < 1 ) { current = 1 ; } const pageCount = Math . ceil ( total / size ) ; current = Math . min ( current , pageCount ) ; return { current : d . slice ( ( current - 1 ) * size , current * size ) , page : current , total , pageCount , } ; } ; calc ( [ "page" ] ) ; return processor ; } , onUpdate ( func ) { onUpdateHandler = func ; return processor ; } , exec ( ) { calc ( [ "filter" , "search" , "sort" , "page" ] ) ; return state . page . cache ; } , } ; return processor ; }