<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

import * as React from 'react';
import { StyleSheet, css, StyleDeclaration } from 'aphrodite';
import { merge, clone, cloneDeep } from 'lodash';
import Flyout from './Flyout';
import RaisedButton from './RaisedButton';
import { ql } from '..';

export interface GridViewStyle extends StyleDeclaration {
  container: React.CSSProperties;
  header: React.CSSProperties;
  headerItem: React.CSSProperties;
  sortableHeaderItem: React.CSSProperties;
  grid: React.CSSProperties;
  gridItem: React.CSSProperties;
  row: React.CSSProperties;
  rowMenu: React.CSSProperties;
  pagination: React.CSSProperties;
  pageButton: React.CSSProperties;
}

export const defaultGridViewStyle: GridViewStyle = {
  container: {
    flex: '1 1 auto',
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'stretch',
  },

  header: {
    display: 'flex',
    color: '#777',
    fontWeight: 'bold',
    minHeight: '2em',
    padding: '5px',
  },

  headerItem: {
    userSelect: 'none',
    cursor: 'default',
    flex: '1 1 auto',
    margin: '0 5px',
  },

  sortableHeaderItem: {
    cursor: 'pointer',
  },

  grid: {
    display: 'flex',
    flex: '1 1 auto',
    flexDirection: 'column',
    flexWrap: 'nowrap',
    overflowY: 'auto',
    paddingTop: '10px',
  },

  gridItem: {
    flex: '1 1 auto',
    margin: '0 5px',
    minWidth: '0',
    display: 'flex',
  },

  row: {
    display: 'flex',
    flex: '0 0 auto',
    padding: '5px',
    ':nth-child(even)': {
      backgroundColor: 'rgba(0, 0, 0, 0.1)',
    },
  },

  rowMenu: {
    minWidth: '10px',
    maxWidth: '10px',
    width: '10px',
    cursor: 'pointer',
    flex: '0 0 auto',
  },

  pagination: {
    display: 'flex',
    alignSelf: 'center',
    flex: '0 0 auto',
    alignContent: 'center',
    justifyContent: 'center',
  },

  pageButton: {
    flex: '0 0 auto',
    fontSize: '.8em',
  },
};

export interface SortFunc<T> {
  (a: T, b: T): number;
}

export interface ColumnDefinition {
  key: (item: any) => any;
  title: string;
  style?: React.CSSProperties;
  sortable?: boolean;
  viewPermission?: string;
  editPermission?: string;
  sortFunction?: SortFunc<any>;
  renderItem?: (item: any, renderData?: { [id: string]: any }) => JSX.Element;
}


export enum GridViewSort {
  None,
  Up,
  Down,
}

export interface SortInfo {
  index: number;
  sorted: GridViewSort;
}

export interface RowMenuFunc<T extends {}> {
  (item: T, closeMenu: () => void): JSX.Element;
}

export interface GridViewProps {
  items: any[];
  columnDefinitions: ColumnDefinition[];
  userPermissions?: ql.PermissionInfo[];
  itemsPerPage?: number;
  styles?: Partial<GridViewStyle>;
  rowMenu?: RowMenuFunc<any>;
  rowMenuStyle?: React.CSSProperties;
  renderData?: {
    [id: string]: any,
  };
}

export interface GridViewState {
  currentSort: SortInfo;
  items: any[];
  itemsPerPage: number;
  sortedItems: any[];
  page: number;
}

export class GridViewImpl<P extends GridViewProps, S extends GridViewState> extends React.Component<P, S> {
  constructor(props: P) {
    super(props);

    const items = cloneDeep(props.items);
    this.state = {
      items,
      currentSort: { index: -1, sorted: GridViewSort.None },
      itemsPerPage: props.itemsPerPage || 25,
      sortedItems: items,
      page: 0,
    } as S;
  }

  public render() {
    const ss = StyleSheet.create(defaultGridViewStyle);
    const custom = StyleSheet.create(this.props.styles || {});
    return (
      <div className={css(ss.container, custom.container)}>
        <div className={css(ss.header, custom.header)}>
          {this.renderHeaderItems(ss, custom)}
        </div>
        {this.renderGrid(ss, custom)}
        {this.renderPagination(ss, custom)}
      </div>
    );
  }

  public sortItems = (input: any[], column: ColumnDefinition, sorted: GridViewSort) => {
    if (sorted === GridViewSort.None) return input;
    if (column.sortFunction) {
      return input.sort((a, b) => sorted === GridViewSort.Down ?
        column.sortFunction(a, b) : (column.sortFunction(a, b) * -1));
    }
    return sorted === GridViewSort.Down ? input.sort() : input.sort().reverse();
  }

  public setSort = (index: number, sortBy: GridViewSort) => {
    const currentSort = { index, sorted: sortBy };
    this.setState({
      currentSort,
      sortedItems: this.sortItems(
        this.state.sortedItems,
        this.props.columnDefinitions[index],
        currentSort.sorted,
      ),
    } as S);
  }

  /*
   * PAGING interface
   */

  public getItemCount = (): number => {
    return (this.state as S).items.length;
  }

  public getItemsPerPage = (): number => {
    return (this.state as S).itemsPerPage;
  }

  public getCurrentPage = (): number => {
    return (this.state as S).page;<commit_msg>Adding a default function for sorting. Most of the sort functions in columnDefinitions will be no longer needed, if the change is applied.<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

import * as React from 'react';
import { StyleSheet, css, StyleDeclaration } from 'aphrodite';
import { merge, clone, cloneDeep } from 'lodash';
import Flyout from './Flyout';
import RaisedButton from './RaisedButton';
import { ql } from '..';

export interface GridViewStyle extends StyleDeclaration {
  container: React.CSSProperties;
  header: React.CSSProperties;
  headerItem: React.CSSProperties;
  sortableHeaderItem: React.CSSProperties;
  grid: React.CSSProperties;
  gridItem: React.CSSProperties;
  row: React.CSSProperties;
  rowMenu: React.CSSProperties;
  pagination: React.CSSProperties;
  pageButton: React.CSSProperties;
}

export const defaultGridViewStyle: GridViewStyle = {
  container: {
    flex: '1 1 auto',
    display: 'flex',
    flexDirection: 'column',
    justifyContent: 'flex-start',
    alignItems: 'stretch',
  },

  header: {
    display: 'flex',
    color: '#777',
    fontWeight: 'bold',
    minHeight: '2em',
    padding: '5px',
  },

  headerItem: {
    userSelect: 'none',
    cursor: 'default',
    flex: '1 1 auto',
    margin: '0 5px',
  },

  sortableHeaderItem: {
    cursor: 'pointer',
  },

  grid: {
    display: 'flex',
    flex: '1 1 auto',
    flexDirection: 'column',
    flexWrap: 'nowrap',
    overflowY: 'auto',
    paddingTop: '10px',
  },

  gridItem: {
    flex: '1 1 auto',
    margin: '0 5px',
    minWidth: '0',
    display: 'flex',
  },

  row: {
    display: 'flex',
    flex: '0 0 auto',
    padding: '5px',
    ':nth-child(even)': {
      backgroundColor: 'rgba(0, 0, 0, 0.1)',
    },
  },

  rowMenu: {
    minWidth: '10px',
    maxWidth: '10px',
    width: '10px',
    cursor: 'pointer',
    flex: '0 0 auto',
  },

  pagination: {
    display: 'flex',
    alignSelf: 'center',
    flex: '0 0 auto',
    alignContent: 'center',
    justifyContent: 'center',
  },

  pageButton: {
    flex: '0 0 auto',
    fontSize: '.8em',
  },
};

export interface SortFunc<T> {
  (a: T, b: T): number;
}

export interface ColumnDefinition {
  key: (item: any) => any;
  title: string;
  style?: React.CSSProperties;
  sortable?: boolean;
  viewPermission?: string;
  editPermission?: string;
  sortFunction?: SortFunc<any>;
  renderItem?: (item: any, renderData?: { [id: string]: any }) => JSX.Element;
}


export enum GridViewSort {
  None,
  Up,
  Down,
}

export interface SortInfo {
  index: number;
  sorted: GridViewSort;
}

export interface RowMenuFunc<T extends {}> {
  (item: T, closeMenu: () => void): JSX.Element;
}

export interface GridViewProps {
  items: any[];
  columnDefinitions: ColumnDefinition[];
  userPermissions?: ql.PermissionInfo[];
  itemsPerPage?: number;
  styles?: Partial<GridViewStyle>;
  rowMenu?: RowMenuFunc<any>;
  rowMenuStyle?: React.CSSProperties;
  renderData?: {
    [id: string]: any,
  };
}

export interface GridViewState {
  currentSort: SortInfo;
  items: any[];
  itemsPerPage: number;
  sortedItems: any[];
  page: number;
}

export class GridViewImpl<P extends GridViewProps, S extends GridViewState> extends React.Component<P, S> {
  constructor(props: P) {
    super(props);

    const items = cloneDeep(props.items);
    this.state = {
      items,
      currentSort: { index: -1, sorted: GridViewSort.None },
      itemsPerPage: props.itemsPerPage || 25,
      sortedItems: items,
      page: 0,
    } as S;
  }

  public render() {
    const ss = StyleSheet.create(defaultGridViewStyle);
    const custom = StyleSheet.create(this.props.styles || {});
    return (
      <div className={css(ss.container, custom.container)}>
        <div className={css(ss.header, custom.header)}>
          {this.renderHeaderItems(ss, custom)}
        </div>
        {this.renderGrid(ss, custom)}
        {this.renderPagination(ss, custom)}
      </div>
    );
  }

  public sortItems = (input: any[], column: ColumnDefinition, sorted: GridViewSort) => {
    if (sorted === GridViewSort.None) return input;
    if (!column.sortFunction) {
      column.sortFunction = (a: any, b: any) => column.key(a) < column.key(b) ? 1 : -1;
    }
    return input.sort((a, b) => sorted === GridViewSort.Down ?
      column.sortFunction(a, b) : (column.sortFunction(a, b) * -1));
  }

  public setSort = (index: number, sortBy: GridViewSort) => {
    const currentSort = { index, sorted: sortBy };
    this.setState({
      currentSort,
      sortedItems: this.sortItems(
        this.state.sortedItems,
        this.props.columnDefinitions[index],
        currentSort.sorted,
      ),
    } as S);
  }

  /*
   * PAGING interface
   */

  public getItemCount = (): number => {
    return (this.state as S).items.length;
  }

  public getItemsPerPage = (): number => {
    return (this.state as S).itemsPerPage;
  }

  public getCurrentPage = (): number => {
    return (this.state as S).page;
{'fraction_non_alphanumeric': 0.1445354921512489, 'fraction_numerical': 0.010070095764636192, 'mean_word_length': 3.1533415334153343, 'pattern_counts': {'":': 0, '<': 26, '<?xml version=': 0, '>': 44, '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': '5813161', 'n_tokens_mistral': 3396, 'n_tokens_neox': 3312, 'n_words': 877}