import * as React from 'react'; import { Col, Grid, Row } from 'react-bootstrap'; import { DropdownButton, DropdownButtonProps, MenuItem, Pagination, PaginationProps, } from 'react-bootstrap'; import { Observable } from 'rxjs'; import { BaseView, BaseViewProps } from '../../React'; import { PagerViewModel, StandardLimits } from './PagerViewModel'; // clone of react-bootstrap PaginationProps, but without the subclassing export type BootstrapPaginationProps = Omit< PaginationProps, React.HTMLProps >; export interface PagerProps extends BootstrapPaginationProps { info?: boolean; limits?: number[]; dropdownProps?: DropdownButtonProps; order?: PagerComponentTypes[]; emptyInfo?: string; } export interface PagerViewProps extends BaseViewProps, PagerProps {} export type PagerComponentTypes = 'info' | 'controls' | 'limit' | undefined; export const StandardPagerComponentOrder: PagerComponentTypes[] = [ 'info', 'controls', 'limit', ]; export class PagerView extends BaseView { public static displayName = 'PagerView'; static defaultProps: Partial = { first: true, prev: true, next: true, last: true, boundaryLinks: true, maxButtons: 3, info: true, limits: StandardLimits, order: StandardPagerComponentOrder, emptyInfo: 'No Items to Display', }; private readonly renderFunctions: { [type: string]: (() => {} | null | undefined); } = { info: this.renderInfo.bind(this), controls: this.renderControls.bind(this), limit: this.renderLimit.bind(this), }; updateOn(viewModel: Readonly) { return [ viewModel.itemCount.changed, viewModel.limit.changed, viewModel.offset.changed, viewModel.pageCount.changed, viewModel.selectedPage.changed, ]; } render() { const { className, props, rest } = this.restProps(x => { const { info, limits, dropdownProps, order, emptyInfo } = x; return { info, limits, dropdownProps, order, emptyInfo }; }); const pagerProps = Object.rest(rest, x => { const { activePage, boundaryLinks, bsSize, bsStyle, buttonComponentClass, ellipsis, first, items, last, maxButtons, next, onSelect, prev, } = x; return { activePage, boundaryLinks, bsSize, bsStyle, buttonComponentClass, ellipsis, first, items, last, maxButtons, next, onSelect, prev, }; }); return this.wxr.renderConditional(this.shouldRenderPager(), () => (
{this.renderComponents()}
)); } protected isEmpty() { return (this.viewModel.itemCount.value || 0) === 0; } protected renderComponents() { if (this.shouldRenderEmpty()) { return this.renderEmpty(); } const types = this.props.order || []; const components = types.map(x => this.renderComponent(x)); if (components.length === 1) { return ( {components[0]} ); } if (components.length === 2) { return ( {components[0]} {components[1]} ); } return ( {components[0]} {components[1]} {components[2]} ); } private shouldRenderPager() { return ( (this.props.order || []).length > 0 && (this.shouldRenderEmpty() || this.shouldRenderInfo() || this.shouldRenderControls() || this.shouldRenderLimit()) ); } private shouldRenderEmpty() { return ( this.isEmpty() && String.isNullOrEmpty(this.props.emptyInfo) === false ); } private shouldRenderInfo() { return this.props.info === true && (this.viewModel.limit.value || 0) > 0; } private shouldRenderControls() { return (this.viewModel.pageCount.value || 1) > 1; } private shouldRenderLimit() { return ( (this.props.limits || []).length > 1 && (this.viewModel.itemCount.value || 0) > 0 ); } private renderComponent(type: PagerComponentTypes | undefined) { return type && this.renderFunctions[type](); } private renderEmpty() { return (
{this.props.emptyInfo}
); } private renderInfo() { return this.wxr.renderConditional( this.shouldRenderInfo(), () => { const start = this.viewModel.offset.value + 1; const end = Math.min( this.viewModel.itemCount.value, this.viewModel.offset.value + (this.viewModel.limit.value || 0), ); const total = this.viewModel.itemCount.value; return (
{`Showing Items ${start} through ${end} of ${total}`}
); }, () => '', ); } private renderControls() { const { props } = this.restProps(x => { const { activePage, bsSize, bsStyle, buttonComponentClass, ellipsis, first, items, last, maxButtons, next, prev, } = x; return { activePage, bsSize, bsStyle, buttonComponentClass, ellipsis, first, items, last, maxButtons, next, prev, }; }); return this.wxr.renderConditional( this.shouldRenderControls(), () => ( x.selectPage)} {...Object.assign(props, { items: this.viewModel.pageCount.value, activePage: this.viewModel.selectedPage.value, })} /> ), () => '', ); } private renderLimit() { return this.wxr.renderConditional( this.shouldRenderLimit(), () => ( x.limit)} > {this.props.limits!.map((x, i) => ( {x || 'All'} ))} ), () => '', ); } private renderLimitTitle() { return `Items per Page (${this.viewModel.limit.value || 'All'})`; } }