GerkinDev/vuejs-datatable

View on GitHub
src/components/vue-datatable-pager/vue-datatable-pager.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { Component, Prop, Vue } from 'vue-property-decorator';

import { EPagerType, namespaceEvent } from '../../utils';
import { VueDatatable } from '../vue-datatable/vue-datatable';
import { TableType } from './../../classes';

import { VueDatatablePagerButton } from './vue-datatable-pager-button/vue-datatable-pager-button';
import template from './vue-datatable-pager.html';

/**
 * The component that is used to manage & change pages on a [[VueDatatable]].
 */
@Component( {
    ...template,
    components: {
        PagerButton: VueDatatablePagerButton,
    },
} )
export class VueDatatablePager<TSub extends VueDatatablePager<TSub>> extends Vue {
    /**
     * The id of the associated [[VueDatatable]].
     *
     * @vue-prop
     */
    @Prop( { type: String, default: 'default' } ) public readonly table!: string;

    /**
     * The kind of the pager
     *
     * @vue-prop
     */
    @Prop( { type: String, default: EPagerType.Long } ) public readonly type!: EPagerType;

    /**
     * The number of pages visible on each side (only for [[EPageType.Abbreviated]])
     *
     * @vue-prop
     */
    @Prop( { type: Number, default: 2 } ) public readonly sidesCount!: number;

    public get sidesIndexes() {
        return [...Array( this.sidesCount ).keys()].map( v => v + 1 );
    }
    private ptableInstance: VueDatatable<any, any> | null = null;
    public get tableInstance(): VueDatatable<any, any> {
        if ( !this.ptableInstance ) {
            throw new ReferenceError( 'Invalid operation: the pager must be attached to a table.' );
        }
        return this.ptableInstance;
    }

    /** Returns `true` if the pager has an associated [[VueDatatable]] with some rows. */
    public get show(): boolean {
        return this.totalRows > 0;
    }
    /** The total number of rows in the associated [[VueDatatable]]. */
    private get totalRows(): number {
        try {
            return this.tableInstance.totalRows;
        } catch {
            return 0;
        }
    }
    /** The total number of pages in the associated [[VueDatatable]]. */
    public totalPages = 0;
    /** The current page index in the associated [[VueDatatable]]. */
    public page = 1;

    /** HTML class on the wrapping `ul` around the pager buttons. */
    public get paginationClass(): string {
        return this.tableType.setting( 'pager.classes.pager' );
    }
    /** HTML content of the previous page's button. */
    public get previousIcon(): string {
        return this.tableType.setting( 'pager.icons.previous' );
    }
    /** HTML content of the next page's button. */
    public get nextIcon(): string {
        return this.tableType.setting( 'pager.icons.next' );
    }

    protected readonly tableType!: TableType<any>;
    public get identifier() {
        return this.tableType.id + '-pager';
    }

    /**
     * Try to link the pager with the table, or bind the `vuejs-datatable::ready` event to watch for new tables addition.
     */
    public created() {
        // Try to link with table
        if ( !this.linkWithTable( this.table ) ) {
            // If it fail, bind next tables initialization
            const tableReadyHandler = ( tableName: string ) => {
                // If it is the correct table and linking is OK...
                if ( tableName === this.table && this.linkWithTable( tableName ) ) {
                    // Unbind table initializations
                    this.$root.$off( namespaceEvent( 'ready' ), tableReadyHandler );
                }
            };
            this.$root.$on( namespaceEvent( 'ready' ), tableReadyHandler );
        }
    }

    /**
     * Link the pager with the table, assign to the table some properties, and trigger an event on the table.
     *
     * @vue-event VueDatatable.vuejs-datatable::pager-bound
     * @vue-event VueDatatable.vuejs-datatable::page-count-changed
     * @vue-event VueDatatable.vuejs-datatable::page-changed
     * @param tableName - The name of the table to bind the pager with.
     * @returns `true` if the link is succesfull, or `false` if it could not find a table to associate with.
     */
    private linkWithTable( tableName: string ) {
        if ( this.$datatables && this.$datatables[tableName] ) {
            const targetTable = this.$datatables[tableName];
            this.ptableInstance = targetTable;
            targetTable.pagers.push( this );
            // Notify that the pager was bound through the datatable
            targetTable.$emit( namespaceEvent( 'pager-bound' ), this );
            // Bind pagination events
            targetTable.$on( namespaceEvent( 'page-count-changed' ), this.onPageCountChanged );
            targetTable.$on( namespaceEvent( 'page-changed' ), this.onPageChanged );
            this.$on( namespaceEvent( 'set-page' ), this.onSetPage );
            return true;
        } else {
            return false;
        }
    }

    /**
     * Callback of the `vuejs-datatable::page-count-changed` event, setting the total pages count.
     *
     * @param totalPages - The new total pages count emitted by the datatable.
     */
    private onPageCountChanged( totalPages: number ) {
        this.totalPages = totalPages;
    }

    /**
     * Callback of the `vuejs-datatable::page-changed` event, setting the page index.
     *
     * @param page - The page index emitted by the datatable.
     */
    private onPageChanged( page: number ) {
        this.page = page;
    }

    /**
     * Propagate new page from the pager to the datatable.
     *
     * @param page - The page index emitted by sub buttons.
     */
    private onSetPage( page: number ) {
        if ( this.ptableInstance ) {
            this.ptableInstance.page = page;
        }
    }
}