<commit_before>
import React from "react";
import Component from "ui-lib/component";
import LoadIndicator from "./LoadIndicator";
import ListComponentItem from "./ListItem";
import ListComponent from "./ListComponent";
import { Button } from "react-toolbox/lib/button";
import { filterFields as qbFilterFields } from "ui-lib/query_builder";

const END_MARK_HEAD = "__HEAD__";
const END_MARK_TAIL = "__TAIL__";
const isEndMark = (value) => value === END_MARK_HEAD || value === END_MARK_TAIL;

const TURN_PAGE_KIND = {
    NEXT: "NEXT",
    PREV: "PREV",
    FIRST: "FIRST",
    LAST: "LAST"
};

const isFromRelative = (relative) => relative === END_MARK_HEAD || relative.startsWith(">");
const isToRelative = (relative) => relative === END_MARK_TAIL || relative.startsWith("<");
const encodeRelative = (value, isFrom = null) =>
    `${typeof isFrom !== "boolean" ? "" : (isFrom ? ">" : "<")}${value}`;

class PagedListComponent extends Component {
    constructor(props) {
        super(props);

        this.addStateVariable("nextPageHasMoreData", false);

        this.addTypePagedListStateVariable(
            "list",
            (props) => props.type,
            (props) => ({
                pageSize: props.pageSize,
                sortOn: props.sortOn,
                sortOnType: props.sortOnType,
                relativeValue: !isEndMark(props.relative.value)
                    ? props.relative.value.substr(1) : null,
                sortDesc: isFromRelative(props.relative.value),
                hasMoreDataCb: (nextPageHasMoreData) => this.state.nextPageHasMoreData.set(nextPageHasMoreData)
            }),
            (props) => {
                let filterQuery = {};
                if (props.filterFields.length > 0 && props.filter && props.filter.length > 0) {
                    filterQuery = qbFilterFields(props.filterFields, props.filter, "si");
                }

                return Object.assign({}, props.query, filterQuery);
            },
            true
        );
    }

    turnPage(turnKind) {
        const isFrom = turnKind === TURN_PAGE_KIND.NEXT || turnKind === TURN_PAGE_KIND.FIRST;
        let itemIdx;
        let value = encodeRelative(isFrom ? END_MARK_HEAD : END_MARK_TAIL);
        if (turnKind === TURN_PAGE_KIND.NEXT || turnKind === TURN_PAGE_KIND.PREV) {
            const wasLastTurnPrev = isToRelative(this.props.relative.value);
            if (isFrom) {
                itemIdx = wasLastTurnPrev ? 0 : this.state.list.length - 1;
            } else {
                itemIdx = wasLastTurnPrev ? this.state.list.length - 1 : 0;
            }
            const relativeToItem = this.state.list[itemIdx];
            value = encodeRelative(relativeToItem[this.props.sortOn], isFrom);
        }
        this.props.relative.set(value);
    }

    componentDidUpdate() {
        // If we find that we have navigated to head or tail, change
        // route so that we follow head or tail
        const nextHasNoData = this.state.nextPageHasMoreData.value === false;
        if (nextHasNoData && !isEndMark(this.props.relative.value)) {
            if (isFromRelative(this.props.relative.value)) {
                // We are at end, follow tail
                this.props.relative.set(encodeRelative(END_MARK_TAIL));
            } else {
                // We are at start, follow head
                this.props.relative.set(encodeRelative(END_MARK_HEAD));
            }
        }
    }

    render() {
        this.log("render", this.props, this.state);

        if (this.state.errorAsync.value) {
            return (
                <div>{this.state.errorAsync.value}</div>
            );
        }

        if (this.state.loadingAsync.value) {
            return (
                <LoadIndicator
                    theme={this.props.theme}
                />
            );
        }

        const relative = this.props.relative.value;
        const isRelativeFrom = isFromRelative(relative);
        const noMoreData = this.state.nextPageHasMoreData.value === false;

        const nextPageHasNoData = isRelativeFrom && noMoreData;
        const prevPageHasNoData = !isRelativeFrom && noMoreData;
        if ((nextPageHasNoData || prevPageHasNoData) && !isEndMark(relative)) {
            // Skip rendering since componentDidUpdate will trigger a re-render anyway
            return;
        }

        const isFirstPage = isRelativeFrom && relative === END_MARK_HEAD;
        const isLastPage = !isRelativeFrom && relative === END_MARK_TAIL;

        let list = this.state.list;
        if (list && !isRelativeFrom) {
            list = list.slice(0).reverse();
        }

        return (
            <div>
                <this.props.ListComponent
                    theme={this.props.theme}
                    listContext={this.props.listContext}
                    children={list && list.map((item) => (
                        <this.props.ListItemComponent
                            key={item._id}
                            theme={this.props.theme}
                            onClick={this.props.onSelect}
                            item={item}
                            itemContext={this.props.listItemContext}
                        />
                    ))}
                />
                <div>
                    <Button
                        icon="first_page"
                        disabled={isFirstPage}
                        onClick={() => this.turnPage(TURN_PAGE_KIND.FIRST)}
                    />
                    <Button
                        icon="navigate_before"
                        disabled={list.length === 0 ||
                            isFirstPage ||
                            prevPageHasNoData
                        }
                        onClick={() => this.turnPage(TURN_PAGE_KIND.PREV)}
                    />
                    <Button
                        icon="navigate_next"
                        disabled={list.length === 0 ||
                            isLastPage ||
                            nextPageHasNoData
                        }
                        onClick={() => this.turnPage(TURN_PAGE_KIND.NEXT)}
                    />
                    <Button
                        icon="last_page"
                        disabled={isLastPage}
                        onClick={() => this.turnPage(TURN_PAGE_KIND.LAST)}
                    />
                </div>
            </div>
        );
    }
}

PagedListComponent.defaultProps = {
    ListComponent: ListComponent,
    ListItemComponent: ListComponentItem,
    query: {},
    pageSize: 5,
    sortOn: "created",
    sortOnType: "Date",
    filterFields: []
};

PagedListComponent.propTypes = {
    theme: React.PropTypes.object,
    type: React.PropTypes.string.isRequired,
    filter: React.PropTypes.string,
    filterFields: React.PropTypes.array,
    query: React.PropTypes.object,
    onSelect: React.PropTypes.func,
    ListComponent: React.PropTypes.func.isRequired,
    listContext: React.PropTypes.any,
    ListItemComponent: React.PropTypes.func.isRequired,
    listItemContext: React.PropTypes.any,
    pageSize: React.PropTypes.number,
    sortOn: React.PropTypes.string,
    sortOnType: React.PropTypes.string,
    relative: React.PropTypes.object.isRequired,
    // Required by state_var when linkToLocation is used
    route: React.PropTypes.object.isRequired
};

PagedListComponent.contextTypes = {
    // Required by state_var when linkToLocation is used
    router: React.PropTypes.object.isRequired
};

export default PagedListComponent;<commit_msg>UI: Convert TAPagedList to LightComponent<commit_after>
import React from "react";
import LightComponent from "ui-lib/light_component";
import LoadIndicator from "./LoadIndicator";
import ListComponentItem from "./ListItem";
import ListComponent from "./ListComponent";
import ListPager from "./ListPager";
import PagedTypeList from "ui-observables/paged_type_list";
import { States as ObservableDataStates } from "ui-lib/observable_data";

class PagedListComponent extends LightComponent {
    constructor(props) {
        super(props);

        this.typeList = new PagedTypeList({
            type: props.type,
            query: props.query,
            sortOn: props.sortOn,
            sortOnType: props.sortOnType,
            sortDesc: props.sortDesc,
            limit: props.limit,
            filter: props.filter,
            filterFields: props.filterFields
        });

        this.state = {
            list: this.typeList.value.getValue(),
            state: this.typeList.state.getValue(),
            error: this.typeList.error.getValue()
        };
    }

    componentWillReceiveProps(nextProps) {
        this.log("componentWillReceiveProps", nextProps);
        this.typeList.setOpts({
            type: nextProps.type,
            query: nextProps.query,
            sortOn: nextProps.sortOn,
            sortOnType: nextProps.sortOnType,
            sortDesc: nextProps.sortDesc,
            limit: nextProps.limit,
            filter: nextProps.filter,
            filterFields: nextProps.filterFields
        });
    }

    componentDidMount() {
        this.log("componentDidMount");
        this.addDisposable(this.typeList.start());
        this.addDisposable(this.typeList.value.subscribe((list) => this.setState({ list })));
        this.addDisposable(this.typeList.state.subscribe((state) => this.setState({ state })));
        this.addDisposable(this.typeList.error.subscribe((error) => this.setState({ error })));
    }

    componentDidUpdate() {
        /* If relative paging is used and current page has no next or previous
         * page, then automatically navigate to last or first page.
         */
        const pagingInfo = this.typeList.pagingInfo.getValue().toJS();
        if (pagingInfo.isRelative) {
            if (!pagingInfo.hasNextPage) {
                this.log("setLastPage");
                this.typeList.setLastPage();
            } else if (!pagingInfo.hasPrevPage) {
                this.log("setFirstPage");
                this.typeList.setFirstPage();
            }
        }
    }

    render() {
        this.log("render", this.props, this.state);

        if (this.state.error) {
            return (
                <pre>{this.state.error.toJS()}</pre>
            );
        }

        if (this.state.state === ObservableDataStates.LOADING) {
            return (
                <LoadIndicator theme={this.props.theme}/>
            );
        }

        return (
            <div>
                <this.props.ListComponent
                    theme={this.props.theme}
                    listContext={this.props.listContext}
                    children={this.state.list && this.state.list.toJS().map((item) => (
                        <this.props.ListItemComponent
                            key={item._id}
                            theme={this.props.theme}
                            onClick={this.props.onSelect}
                            item={item}
                            itemContext={this.props.listItemContext}
                        />
                    ))}
                />
                {this.state.list && this.state.list.size > 0 &&
                    <ListPager
                        theme={this.props.theme}
                        pagedList={this.typeList}
                        pagingInfo={this.typeList.pagingInfo.getValue()}
                    />
                }
            </div>
        );
    }
}

PagedListComponent.defaultProps = {
    ListComponent: ListComponent,
    ListItemComponent: ListComponentItem,
    query: {},
    limit: 5,
    sortOn: "created",
    sortOnType: "Date",
    sortDesc: true,
    filter: "",
    filterFields: []
};

PagedListComponent.propTypes = {
    theme: React.PropTypes.object,
    type: React.PropTypes.string.isRequired,
    filter: React.PropTypes.string,
    filterFields: React.PropTypes.array,
    query: React.PropTypes.object,
    onSelect: React.PropTypes.func,
    ListComponent: React.PropTypes.func.isRequired,
    listContext: React.PropTypes.any,
    ListItemComponent: React.PropTypes.func.isRequired,
    listItemContext: React.PropTypes.any,
    limit: React.PropTypes.number,
    sortOn: React.PropTypes.string,
    sortOnType: React.PropTypes.string,
    sortDesc: React.PropTypes.bool
};

PagedListComponent.contextTypes = {
    // Required by state_var when linkToLocation is used
    router: React.PropTypes.object.isRequired
};

export default PagedListComponent;
{'fraction_non_alphanumeric': 0.10957591246705535, 'fraction_numerical': 0.0010382557303729733, 'mean_word_length': 1.780195381882771, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5007225', 'n_tokens_mistral': 3409, 'n_tokens_neox': 3312, 'n_words': 610}