<commit_before>/*

  ## Table

  A paginated table of events matching a query

  ### Parameters
  * query ::  A string representing then current query
  * size :: Number of events per page to show
  * pages :: Number of pages to show. size * pages = number of cached events.
             Bigger = more memory usage byh the browser
  * offset :: Position from which to start in the array of hits
  * sort :: An array with 2 elements. sort[0]: field, sort[1]: direction ('asc' or 'desc')
  * style :: hash of css properties
  * fields :: columns to show in table
  * overflow :: 'height' or 'min-height' controls wether the row will expand (min-height) to
                to fit the table, or if the table will scroll to fit the row (height)
  * sortable :: Allow sorting?
  * spyable :: Show the 'eye' icon that reveals the last ES query for this panel
  ### Group Events
  #### Sends
  * table_documents :: An array containing all of the documents in the table.
                       Only used by the fields panel so far.
  #### Receives
  * time :: An object containing the time range to use and the index(es) to query
  * query :: An Array of queries, even if its only one
  * sort :: An array with 2 elements. sort[0]: field, sort[1]: direction ('asc' or 'desc')
  * selected_fields :: An array of fields to show
*/

angular.module('kibana.table', [])
.controller('table', function($scope, eventBus, fields) {

  // Set and populate defaults
  var _d = {
    query   : "*",
    size    : 100, // Per page
    pages   : 5,   // Pages available
    offset  : 0,
    sort    : ['@timestamp','desc'],
    group   : "default",
    style   : {'font-size': '9pt'},
    overflow: 'height',
    fields  : [],
    highlight : [],
    sortable: true,
    header  : true,
    paging  : true,
    spyable: true
  }
  _.defaults($scope.panel,_d)

  $scope.init = function () {

    $scope.set_listeners($scope.panel.group)

    // Now that we're all setup, request the time from our group
    eventBus.broadcast($scope.$id,$scope.panel.group,"get_time")
  }

  $scope.set_listeners = function(group) {
    eventBus.register($scope,'time',function(event,time) {
      $scope.panel.offset = 0;
      set_time(time)
    });
    eventBus.register($scope,'query',function(event,query) {
      $scope.panel.offset = 0;
      $scope.panel.query = _.isArray(query) ? query[0] : query;
      $scope.get_data();
    });
    eventBus.register($scope,'sort', function(event,sort){
      $scope.panel.sort = _.clone(sort);
      $scope.get_data();
    });
    eventBus.register($scope,'selected_fields', function(event, fields) {
      $scope.panel.fields = _.clone(fields)
    });
    eventBus.register($scope,'table_documents', function(event, docs) {
        $scope.panel.query = docs.query;
        $scope.data = docs.docs;
    });
  }

  $scope.set_sort = function(field) {
    if($scope.panel.sort[0] === field)
      $scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc';
    else
      $scope.panel.sort[0] = field;
    $scope.get_data();
  }

  $scope.toggle_field = function(field) {
    if (_.indexOf($scope.panel.fields,field) > -1)
      $scope.panel.fields = _.without($scope.panel.fields,field)
    else
      $scope.panel.fields.push(field)
    broadcast_results();
  }

  $scope.toggle_highlight = function(field) {
    if (_.indexOf($scope.panel.highlight,field) > -1)
      $scope.panel.highlight = _.without($scope.panel.highlight,field)
    else
      $scope.panel.highlight.push(field)
  }

  $scope.toggle_details = function(row) {
    row.kibana = row.kibana || {};
    row.kibana.details = !row.kibana.details ? $scope.without_kibana(row) : false;
  }

  $scope.page = function(page) {
    $scope.panel.offset = page*$scope.panel.size
    $scope.get_data();
  }

  $scope.build_search = function(field,value,negate) {
    $scope.panel.query = add_to_query($scope.panel.query,field,value,negate)
    $scope.panel.offset = 0;
    $scope.get_data();
    eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]);
  }

  $scope.get_data = function(segment,query_id) {
    $scope.panel.error =  false;

    // Make sure we have everything for the request to complete
    if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
      return

    $scope.panel.loading = true;

    var _segment = _.isUndefined(segment) ? 0 : segment
    $scope.segment = _segment;

    var request = $scope.ejs.Request().indices($scope.index[_segment])
      .query(ejs.FilteredQuery(
        ejs.QueryStringQuery($scope.panel.query || '*'),
        ejs.RangeFilter($scope.time.field)
          .from($scope.time.from)
          .to($scope.time.to)
        )
      )
      .highlight(
        ejs.Highlight($scope.panel.highlight)
        .fragmentSize(2147483647) // Max size of a 32bit unsigned int
        .preTags('@start-highlight@')
        .postTags('@end-highlight@')
      )
      .size($scope.panel.size*$scope.panel.pages)
      .sort($scope.panel.sort[0],$scope.panel.sort[1]);

    $scope.populate_modal(request)

    var results = request.doSearch()

    // Populate scope when we have results
    results.then(function(results) {
      $scope.panel.loading = false;

      if(_segment === 0) {
        $scope.hits = 0;
        $scope.data = [];
        query_id = $scope.query_id = new Date().getTime()
      }

      // Check for error and abort if found
      if(!(_.isUndefined(results.error))) {
        $scope.panel.error = $scope.parse_error(results.error);
        return;
      }

      // Check that we're still on the same query, if not stop
      if($scope.query_id === query_id) {
        $scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) {
          return {
            _source   : flatten_json(hit['_source']),
            highlight : flatten_json(hit['highlight']||{})
          }
        }));

        $scope.hits += results.hits.total;

        // Sort the data
        $scope.data = _.sortBy($scope.data, function(v){
          return v._source[$scope.panel.sort[0]]
        });

        // Reverse if needed
        if($scope.panel.sort[1] == 'desc')
          $scope.data.reverse();

        // Keep only what we need for the set
        $scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages)

      } else {
        return;
      }

      // This breaks, use $scope.data for this
      $scope.all_fields = get_all_fields(_.pluck($scope.data,'_source'));
      broadcast_results();

      // If we're not sorting in reverse chrono order, query every index for
      // size*pages results
      // Otherwise, only get size*pages results then stop querying
      if(
          ($scope.data.length < $scope.panel.size*$scope.panel.pages ||
            !(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc')) &&
          _segment+1 < $scope.index.length
      ) {
        $scope.get_data(_segment+1,$scope.query_id)
      }

    });
  }

  $scope.populate_modal = function(request) {
    $scope.modal = {
      title: "Table Inspector",
      body : "
Last Elasticsearch Query

"+
          'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
          angular.toJson(JSON.parse(request.toString()),true)+
        "'
",
    }
  }

  $scope.without_kibana = function (row) {
    return {
      _source   : row._source,
      highlight : row.highlight
    }
  }

  // Broadcast a list of all fields. Note that receivers of field array
  // events should be able to receive from multiple sources, merge, dedupe
  // and sort on the fly if needed.
  function broadcast_results() {
    eventBus.broadcast($scope.$id,$scope.panel.group,"fields", {
      all   : $scope.all_fields,
      sort  : $scope.panel.sort,
      active: $scope.panel.fields
    });
    eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents",
      {
        query: $scope.panel.query,
        docs : _.pluck($scope.data,'_source'),
        index: $scope.index
      });
  }

  $scope.set_refresh = function (state) {
    $scope.refresh = state;
  }

  $scope.close_edit = function() {
    if($scope.refresh)
      $scope.get_data();
    $scope.refresh =  false;
  }

  function set_time(time) {
    $scope.time = time;
    $scope.index = _.isUndefined(time.index) ? $scope.index : time.index
    $scope.get_data();
  }

})
.filter('highlight', function() {
  return function(text) {
    if (text.toString().length) {
      return text.toString().
        replace(/&/g, '&').
        replace(/</g, '<').
        replace(/>/g, '>').
        replace(/@start-highlight@/g, '').
        replace(/@end-highlight@/g, '')
    }
    return '';
  }
});<commit_msg>Added null and undefined handling to highlight filter, closes #123<commit_after>/*

  ## Table

  A paginated table of events matching a query

  ### Parameters
  * query ::  A string representing then current query
  * size :: Number of events per page to show
  * pages :: Number of pages to show. size * pages = number of cached events.
             Bigger = more memory usage byh the browser
  * offset :: Position from which to start in the array of hits
  * sort :: An array with 2 elements. sort[0]: field, sort[1]: direction ('asc' or 'desc')
  * style :: hash of css properties
  * fields :: columns to show in table
  * overflow :: 'height' or 'min-height' controls wether the row will expand (min-height) to
                to fit the table, or if the table will scroll to fit the row (height)
  * sortable :: Allow sorting?
  * spyable :: Show the 'eye' icon that reveals the last ES query for this panel
  ### Group Events
  #### Sends
  * table_documents :: An array containing all of the documents in the table.
                       Only used by the fields panel so far.
  #### Receives
  * time :: An object containing the time range to use and the index(es) to query
  * query :: An Array of queries, even if its only one
  * sort :: An array with 2 elements. sort[0]: field, sort[1]: direction ('asc' or 'desc')
  * selected_fields :: An array of fields to show
*/

angular.module('kibana.table', [])
.controller('table', function($scope, eventBus, fields) {

  // Set and populate defaults
  var _d = {
    query   : "*",
    size    : 100, // Per page
    pages   : 5,   // Pages available
    offset  : 0,
    sort    : ['@timestamp','desc'],
    group   : "default",
    style   : {'font-size': '9pt'},
    overflow: 'height',
    fields  : [],
    highlight : [],
    sortable: true,
    header  : true,
    paging  : true,
    spyable: true
  }
  _.defaults($scope.panel,_d)

  $scope.init = function () {

    $scope.set_listeners($scope.panel.group)

    // Now that we're all setup, request the time from our group
    eventBus.broadcast($scope.$id,$scope.panel.group,"get_time")
  }

  $scope.set_listeners = function(group) {
    eventBus.register($scope,'time',function(event,time) {
      $scope.panel.offset = 0;
      set_time(time)
    });
    eventBus.register($scope,'query',function(event,query) {
      $scope.panel.offset = 0;
      $scope.panel.query = _.isArray(query) ? query[0] : query;
      $scope.get_data();
    });
    eventBus.register($scope,'sort', function(event,sort){
      $scope.panel.sort = _.clone(sort);
      $scope.get_data();
    });
    eventBus.register($scope,'selected_fields', function(event, fields) {
      $scope.panel.fields = _.clone(fields)
    });
    eventBus.register($scope,'table_documents', function(event, docs) {
        $scope.panel.query = docs.query;
        $scope.data = docs.docs;
    });
  }

  $scope.set_sort = function(field) {
    if($scope.panel.sort[0] === field)
      $scope.panel.sort[1] = $scope.panel.sort[1] == 'asc' ? 'desc' : 'asc';
    else
      $scope.panel.sort[0] = field;
    $scope.get_data();
  }

  $scope.toggle_field = function(field) {
    if (_.indexOf($scope.panel.fields,field) > -1)
      $scope.panel.fields = _.without($scope.panel.fields,field)
    else
      $scope.panel.fields.push(field)
    broadcast_results();
  }

  $scope.toggle_highlight = function(field) {
    if (_.indexOf($scope.panel.highlight,field) > -1)
      $scope.panel.highlight = _.without($scope.panel.highlight,field)
    else
      $scope.panel.highlight.push(field)
  }

  $scope.toggle_details = function(row) {
    row.kibana = row.kibana || {};
    row.kibana.details = !row.kibana.details ? $scope.without_kibana(row) : false;
  }

  $scope.page = function(page) {
    $scope.panel.offset = page*$scope.panel.size
    $scope.get_data();
  }

  $scope.build_search = function(field,value,negate) {
    $scope.panel.query = add_to_query($scope.panel.query,field,value,negate)
    $scope.panel.offset = 0;
    $scope.get_data();
    eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]);
  }

  $scope.get_data = function(segment,query_id) {
    $scope.panel.error =  false;

    // Make sure we have everything for the request to complete
    if(_.isUndefined($scope.index) || _.isUndefined($scope.time))
      return

    $scope.panel.loading = true;

    var _segment = _.isUndefined(segment) ? 0 : segment
    $scope.segment = _segment;

    var request = $scope.ejs.Request().indices($scope.index[_segment])
      .query(ejs.FilteredQuery(
        ejs.QueryStringQuery($scope.panel.query || '*'),
        ejs.RangeFilter($scope.time.field)
          .from($scope.time.from)
          .to($scope.time.to)
        )
      )
      .highlight(
        ejs.Highlight($scope.panel.highlight)
        .fragmentSize(2147483647) // Max size of a 32bit unsigned int
        .preTags('@start-highlight@')
        .postTags('@end-highlight@')
      )
      .size($scope.panel.size*$scope.panel.pages)
      .sort($scope.panel.sort[0],$scope.panel.sort[1]);

    $scope.populate_modal(request)

    var results = request.doSearch()

    // Populate scope when we have results
    results.then(function(results) {
      $scope.panel.loading = false;

      if(_segment === 0) {
        $scope.hits = 0;
        $scope.data = [];
        query_id = $scope.query_id = new Date().getTime()
      }

      // Check for error and abort if found
      if(!(_.isUndefined(results.error))) {
        $scope.panel.error = $scope.parse_error(results.error);
        return;
      }

      // Check that we're still on the same query, if not stop
      if($scope.query_id === query_id) {
        $scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) {
          return {
            _source   : flatten_json(hit['_source']),
            highlight : flatten_json(hit['highlight']||{})
          }
        }));

        $scope.hits += results.hits.total;

        // Sort the data
        $scope.data = _.sortBy($scope.data, function(v){
          return v._source[$scope.panel.sort[0]]
        });

        // Reverse if needed
        if($scope.panel.sort[1] == 'desc')
          $scope.data.reverse();

        // Keep only what we need for the set
        $scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages)

      } else {
        return;
      }

      // This breaks, use $scope.data for this
      $scope.all_fields = get_all_fields(_.pluck($scope.data,'_source'));
      broadcast_results();

      // If we're not sorting in reverse chrono order, query every index for
      // size*pages results
      // Otherwise, only get size*pages results then stop querying
      if(
          ($scope.data.length < $scope.panel.size*$scope.panel.pages ||
            !(($scope.panel.sort[0] === $scope.time.field) && $scope.panel.sort[1] === 'desc')) &&
          _segment+1 < $scope.index.length
      ) {
        $scope.get_data(_segment+1,$scope.query_id)
      }

    });
  }

  $scope.populate_modal = function(request) {
    $scope.modal = {
      title: "Table Inspector",
      body : "
Last Elasticsearch Query

"+
          'curl -XGET '+config.elasticsearch+'/'+$scope.index+"/_search?pretty -d'\n"+
          angular.toJson(JSON.parse(request.toString()),true)+
        "'
",
    }
  }

  $scope.without_kibana = function (row) {
    return {
      _source   : row._source,
      highlight : row.highlight
    }
  }

  // Broadcast a list of all fields. Note that receivers of field array
  // events should be able to receive from multiple sources, merge, dedupe
  // and sort on the fly if needed.
  function broadcast_results() {
    eventBus.broadcast($scope.$id,$scope.panel.group,"fields", {
      all   : $scope.all_fields,
      sort  : $scope.panel.sort,
      active: $scope.panel.fields
    });
    eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents",
      {
        query: $scope.panel.query,
        docs : _.pluck($scope.data,'_source'),
        index: $scope.index
      });
  }

  $scope.set_refresh = function (state) {
    $scope.refresh = state;
  }

  $scope.close_edit = function() {
    if($scope.refresh)
      $scope.get_data();
    $scope.refresh =  false;
  }

  function set_time(time) {
    $scope.time = time;
    $scope.index = _.isUndefined(time.index) ? $scope.index : time.index
    $scope.get_data();
  }

})
.filter('highlight', function() {
  return function(text) {
    if (text.toString().length > 0 && !_.isUndefined(text) && !_.isNull(text)) {
      return text.toString().
        replace(/&/g, '&').
        replace(/</g, '<').
        replace(/>/g, '>').
        replace(/@start-highlight@/g, '').
        replace(/@end-highlight@/g, '')
    }
    return '';
  }
});<|endoftext|>


<commit_before>/**
 * @file ListItem component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import Checkbox from '../Checkbox';
import Radio from '../Radio';
import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import TouchRipple from '../TouchRipple';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import Position from '../_statics/Position';
import SelectMode from '../_statics/SelectMode';

export default class ListItem extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;
    static propTypes = {

        index: PropTypes.number,

        className: PropTypes.string,
        style: PropTypes.object,
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
        selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

        data: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
        value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
        text: PropTypes.any,
        desc: PropTypes.string,

        disabled: PropTypes.bool,
        isLoading: PropTypes.bool,
        disableTouchRipple: PropTypes.bool,
        rippleDisplayCenter: PropTypes.bool,
        checked: PropTypes.bool,
        readOnly: PropTypes.bool,

        iconCls: PropTypes.string,
        rightIconCls: PropTypes.string,

        tip: PropTypes.string,
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

        radioUncheckedIconCls: PropTypes.string,
        radioCheckedIconCls: PropTypes.string,
        checkboxUncheckedIconCls: PropTypes.string,
        checkboxCheckedIconCls: PropTypes.string,
        checkboxIndeterminateIconCls: PropTypes.string,

        itemRenderer: PropTypes.func,
        renderer: PropTypes.func,

        onTouchTap: PropTypes.func,
        onSelect: PropTypes.func,
        onDeselect: PropTypes.func,
        onMouseEnter: PropTypes.func,
        onMouseLeave: PropTypes.func

    };
    static defaultProps = {

        index: 0,

        className: null,
        style: null,
        theme: Theme.DEFAULT,

        selectTheme: Theme.DEFAULT,
        selectMode: SelectMode.SINGLE_SELECT,

        data: null,
        value: null,
        text: null,
        desc: null,

        disabled: false,
        isLoading: false,
        disableTouchRipple: false,
        rippleDisplayCenter: false,
        checked: false,
        readOnly: false,

        iconCls: null,
        rightIconCls: null,

        tip: null,
        tipPosition: Position.BOTTOM,

        radioUncheckedIconCls: null,
        radioCheckedIconCls: null,
        checkboxUncheckedIconCls: 'fa fa-square-o',
        checkboxCheckedIconCls: 'fa fa-check-square',
        checkboxIndeterminateIconCls: 'fa fa-minus-square'

    };

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            checked: props.checked
        };

        this.checkboxChangeHandler = ::this.checkboxChangeHandler;
        this.radioChangeHandler = ::this.radioChangeHandler;
        this.touchTapHandler = ::this.touchTapHandler;

    }

    checkboxChangeHandler(checked) {
        this.setState({
            checked
        }, () => {

            const {onSelect, onDeselect} = this.props;

            if (checked) {
                onSelect && onSelect();
            } else {
                onDeselect && onDeselect();
            }

        });
    }

    radioChangeHandler() {

        const {checked} = this.state;

        if (!checked) {
            this.setState({
                checked: true
            }, () => {
                const {onSelect} = this.props;
                onSelect && onSelect();
            });
        }

    }

    touchTapHandler(e) {

        e.preventDefault();

        const {disabled, isLoading, readOnly} = this.props;

        if (disabled || isLoading || readOnly) {
            return;
        }

        const {onTouchTap} = this.props;
        onTouchTap && onTouchTap(e);

        switch (this.props.selectMode) {
            case SelectMode.MULTI_SELECT:
                this.checkboxChangeHandler(!this.state.checked);
                return;
            case SelectMode.SINGLE_SELECT:
                this.radioChangeHandler();
                return;
        }

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.checked !== this.state.checked) {
            this.setState({
                checked: nextProps.checked
            });
        }
    }

    render() {

        const {

                index, className, style, theme, data, text, desc, iconCls, rightIconCls, tip, tipPosition,
                disabled, isLoading, disableTouchRipple, rippleDisplayCenter, renderer, itemRenderer, readOnly,

                selectTheme, selectMode, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                onMouseEnter, onMouseLeave

            } = this.props,
            {checked} = this.state,

            listItemClassName = (theme ? ` theme-${theme}` : '') + (checked ? ' activated' : '')
                + (className ? ' ' + className : ''),
            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left';

        return (
            <TipProvider className='block'
                         text={tip}
                         tipPosition={tipPosition}>

                    {
                        selectMode === SelectMode.SINGLE_SELECT && (radioUncheckedIconCls || radioCheckedIconCls) ?
                            <Radio className="list-item-select"
                                   theme={selectTheme}
                                   checked={checked}
                                   disabled={disabled || isLoading}
                                   uncheckedIconCls={radioUncheckedIconCls}
                                   checkedIconCls={radioCheckedIconCls}
                                   disableTouchRipple={true}/>
                            :
                            null
                    }

                    {
                        selectMode === SelectMode.MULTI_SELECT ?
                            <Checkbox className="list-item-select"
                                      theme={selectTheme}
                                      checked={checked}
                                      disabled={disabled || isLoading}
                                      uncheckedIconCls={checkboxUncheckedIconCls}
                                      checkedIconCls={checkboxCheckedIconCls}
                                      indeterminateIconCls={checkboxIndeterminateIconCls}
                                      disableTouchRipple={true}/>
                            :
                            null
                    }

                    {
                        isLoading && loadingIconPosition === 'left' ?

                                <CircularLoading className="button-loading-icon"
                                                 size="small"/>

                            :
                            (
                                iconCls ?
                                    <i className={`button-icon button-icon-left ${iconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        itemRenderer && typeof itemRenderer === 'function' ?
                            itemRenderer(data, index)
                            :
                            (
                                renderer && typeof renderer === 'function' ?
                                    renderer(data, index)
                                    :
                                    (
                                        desc ?

                                                    {text}

                                                    {desc}

                                            :
                                            text
                                    )
                            )
                    }

                    {
                        isLoading && loadingIconPosition === 'right' ?
                            <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                             size="small"/>
                            :
                            (
                                rightIconCls ?
                                    <i className={`button-icon button-icon-right ${rightIconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        disableTouchRipple || readOnly ?
                            null
                            :
                            <TouchRipple ref="touchRipple"
                                         className={disabled || isLoading ? 'hidden' : ''}
                                         displayCenter={rippleDisplayCenter}/>
                    }

            </TipProvider>
        );

    }
};<commit_msg>update ListItem<commit_after>/**
 * @file ListItem component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import Checkbox from '../Checkbox';
import Radio from '../Radio';
import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import TouchRipple from '../TouchRipple';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import Position from '../_statics/Position';
import SelectMode from '../_statics/SelectMode';

class ListItem extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            checked: props.checked
        };

        this.checkboxChangeHandler = ::this.checkboxChangeHandler;
        this.radioChangeHandler = ::this.radioChangeHandler;
        this.touchTapHandler = ::this.touchTapHandler;

    }

    checkboxChangeHandler(checked) {
        this.setState({
            checked
        }, () => {

            const {onSelect, onDeselect} = this.props;

            if (checked) {
                onSelect && onSelect();
            } else {
                onDeselect && onDeselect();
            }

        });
    }

    radioChangeHandler() {

        const {checked} = this.state;

        if (!checked) {
            this.setState({
                checked: true
            }, () => {
                const {onSelect} = this.props;
                onSelect && onSelect();
            });
        }

    }

    touchTapHandler(e) {

        e.preventDefault();

        const {disabled, isLoading, readOnly} = this.props;

        if (disabled || isLoading || readOnly) {
            return;
        }

        const {onTouchTap} = this.props;
        onTouchTap && onTouchTap(e);

        switch (this.props.selectMode) {
            case SelectMode.MULTI_SELECT:
                this.checkboxChangeHandler(!this.state.checked);
                return;
            case SelectMode.SINGLE_SELECT:
                this.radioChangeHandler();
                return;
        }

    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.checked !== this.state.checked) {
            this.setState({
                checked: nextProps.checked
            });
        }
    }

    render() {

        const {

                index, className, style, theme, data, text, desc, iconCls, rightIconCls, tip, tipPosition,
                disabled, isLoading, disableTouchRipple, rippleDisplayCenter, renderer, itemRenderer, readOnly,

                selectTheme, selectMode, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                onMouseEnter, onMouseLeave

            } = this.props,
            {checked} = this.state,

            listItemClassName = (theme ? ` theme-${theme}` : '') + (checked ? ' activated' : '')
                + (className ? ' ' + className : ''),
            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left';

        return (
            <TipProvider className='block'
                         text={tip}
                         tipPosition={tipPosition}>

                    {
                        selectMode === SelectMode.SINGLE_SELECT && (radioUncheckedIconCls || radioCheckedIconCls) ?
                            <Radio className="list-item-select"
                                   theme={selectTheme}
                                   checked={checked}
                                   disabled={disabled || isLoading}
                                   uncheckedIconCls={radioUncheckedIconCls}
                                   checkedIconCls={radioCheckedIconCls}
                                   disableTouchRipple={true}/>
                            :
                            null
                    }

                    {
                        selectMode === SelectMode.MULTI_SELECT ?
                            <Checkbox className="list-item-select"
                                      theme={selectTheme}
                                      checked={checked}
                                      disabled={disabled || isLoading}
                                      uncheckedIconCls={checkboxUncheckedIconCls}
                                      checkedIconCls={checkboxCheckedIconCls}
                                      indeterminateIconCls={checkboxIndeterminateIconCls}
                                      disableTouchRipple={true}/>
                            :
                            null
                    }

                    {
                        isLoading && loadingIconPosition === 'left' ?

                                <CircularLoading className="button-loading-icon"
                                                 size="small"/>

                            :
                            (
                                iconCls ?
                                    <i className={`button-icon button-icon-left ${iconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        itemRenderer && typeof itemRenderer === 'function' ?
                            itemRenderer(data, index)
                            :
                            (
                                renderer && typeof renderer === 'function' ?
                                    renderer(data, index)
                                    :
                                    (
                                        desc ?

                                                    {text}

                                                    {desc}

                                            :
                                            text
                                    )
                            )
                    }

                    {
                        isLoading && loadingIconPosition === 'right' ?
                            <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                             size="small"/>
                            :
                            (
                                rightIconCls ?
                                    <i className={`button-icon button-icon-right ${rightIconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        disableTouchRipple || readOnly ?
                            null
                            :
                            <TouchRipple ref="touchRipple"
                                         className={disabled || isLoading ? 'hidden' : ''}
                                         displayCenter={rippleDisplayCenter}/>
                    }

            </TipProvider>
        );

    }
};

ListItem.propTypes = {

    index: PropTypes.number,

    className: PropTypes.string,
    style: PropTypes.object,
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    data: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]),
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    text: PropTypes.any,
    desc: PropTypes.string,

    disabled: PropTypes.bool,
    isLoading: PropTypes.bool,
    disableTouchRipple: PropTypes.bool,
    rippleDisplayCenter: PropTypes.bool,
    checked: PropTypes.bool,
    readOnly: PropTypes.bool,

    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,

    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    itemRenderer: PropTypes.func,
    renderer: PropTypes.func,

    onTouchTap: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func

};

ListItem.defaultProps = {

    index: 0,

    className: null,
    style: null,
    theme: Theme.DEFAULT,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,

    data: null,
    value: null,
    text: null,
    desc: null,

    disabled: false,
    isLoading: false,
    disableTouchRipple: false,
    rippleDisplayCenter: false,
    checked: false,
    readOnly: false,

    iconCls: null,
    rightIconCls: null,

    tip: null,
    tipPosition: Position.BOTTOM,

    radioUncheckedIconCls: null,
    radioCheckedIconCls: null,
    checkboxUncheckedIconCls: 'fa fa-square-o',
    checkboxCheckedIconCls: 'fa fa-check-square',
    checkboxIndeterminateIconCls: 'fa fa-minus-square'

};

export default ListItem;<|endoftext|>


<commit_before>/**
 * @file TableRow component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

class TableRow extends Component {

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    stringContentRenderer = (data, template) => {

        if (!data) {
            return template;
        }

        if (/\$\{.+\}/.test(template)) { // handle replacement in template
            let result = template;
            for (let key in data) {
                result = result.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), data[key]);
            }
            return result;
        } else { // show prop value directly
            return data[template];
        }

    };

    calSpan = (col, colIndex) => {
        return col.span && typeof col.span === 'function' ?
            col.span(this.props.rowIndex, colIndex)
            :
            null;
    };

    calColumns = () => {

        const {columns} = this.props,
            result = [];
        let spanFlag = 0;

        for (let i = 0, len = columns.length; i < len; i++) {

            if (spanFlag > 1) {
                spanFlag--;
                continue;
            }

            const col = columns[i],
                span = this.calSpan(columns[i], i);

            if (span && span > 1) {
                spanFlag = span;
            }

            result.push({
                col,
                span
            });

        }

        return result;

    };

    contentRenderer = (renderer, colIndex) => {

        const {rowIndex, data} = this.props;

        switch (typeof renderer) {
            case 'string':
                return this.stringContentRenderer(data, renderer);
            case 'function':
                return renderer(data, rowIndex, colIndex);
            default:
                return renderer;
        }

    };

    rowClickHandler = e => {
        const {data, rowIndex, disabled, onRowClick} = this.props;
        !disabled && onRowClick && onRowClick(data, rowIndex, e);
    };

    cellClickHandler = (e, colIndex) => {
        const {data, rowIndex, disabled, onCellClick} = this.props;
        !disabled && onCellClick && onCellClick(data, rowIndex, colIndex, e);

    };

    toggleExpandHandler = (colIndex, col, e) => {
        const {data, isExpanded, onExpand, onCollapse} = this.props;

        e.stopPropagation();

        if (!col.collapseAble || !data[col.childrenNumKey]) {
            return;
        }

        if (isExpanded) {
            onCollapse && onCollapse(colIndex);
        } else {
            onExpand && onExpand(colIndex);
        }
    };

    render() {

        const {data, className, isChecked, disabled, isExpanded, expandedIconCls} = this.props,

            columns = this.calColumns(),

            trClassName = classNames('table-row', {
                [className]: className,
                activated: isChecked,
                [data.rowClassName]: data.rowClassName
            });

        return (

                {
                    columns && columns.map(({col, span}, colIndex) =>
                         this.cellClickHandler(e, colIndex, col)}>
                            {col.collapseAble && data[col.childrenNumKey] > 0 &&
                             this.toggleExpandHandler(colIndex, col, e)}/>}
                            {this.contentRenderer(col.renderer, colIndex)}

                    )
                }

        );

    }
}

TableRow.propTypes = {

    rowIndex: PropTypes.number,
    columns: PropTypes.array,
    data: PropTypes.object,
    isChecked: PropTypes.bool,
    disabled: PropTypes.bool,
    expandedIconCls: PropTypes.string,
    className: PropTypes.string,

    onRowClick: PropTypes.func,
    onCellClick: PropTypes.func

};

TableRow.defaultProps = {
    className: '',
    rowIndex: 0,
    expandedIconCls: 'fas fa-angle-right',
    columns: [],
    data: {},
    isChecked: false,
    disabled: false
};

export default TableRow;
<commit_msg>remove root<commit_after>/**
 * @file TableRow component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

class TableRow extends Component {

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    stringContentRenderer = (data, template) => {

        if (!data) {
            return template;
        }

        if (/\$\{.+\}/.test(template)) { // handle replacement in template
            let result = template;
            for (let key in data) {
                result = result.replace(new RegExp('\\$\\{' + key + '\\}', 'g'), data[key]);
            }
            return result;
        } else { // show prop value directly
            return data[template];
        }

    };

    calSpan = (col, colIndex) => {
        return col.span && typeof col.span === 'function' ?
            col.span(this.props.rowIndex, colIndex)
            :
            null;
    };

    calColumns = () => {

        const {columns} = this.props,
            result = [];
        let spanFlag = 0;

        for (let i = 0, len = columns.length; i < len; i++) {

            if (spanFlag > 1) {
                spanFlag--;
                continue;
            }

            const col = columns[i],
                span = this.calSpan(columns[i], i);

            if (span && span > 1) {
                spanFlag = span;
            }

            result.push({
                col,
                span
            });

        }

        return result;

    };

    contentRenderer = (renderer, colIndex) => {

        const {rowIndex, data} = this.props;

        switch (typeof renderer) {
            case 'string':
                return this.stringContentRenderer(data, renderer);
            case 'function':
                return renderer(data, rowIndex, colIndex);
            default:
                return renderer;
        }

    };

    rowClickHandler = e => {
        const {data, rowIndex, disabled, onRowClick} = this.props;
        !disabled && onRowClick && onRowClick(data, rowIndex, e);
    };

    cellClickHandler = (e, colIndex) => {
        const {data, rowIndex, disabled, onCellClick} = this.props;
        !disabled && onCellClick && onCellClick(data, rowIndex, colIndex, e);

    };

    toggleExpandHandler = (colIndex, col, e) => {
        const {data, isExpanded, onExpand, onCollapse} = this.props;

        e.stopPropagation();

        if (!col.collapseAble || !data[col.childrenNumKey]) {
            return;
        }

        if (isExpanded) {
            onCollapse && onCollapse(colIndex);
        } else {
            onExpand && onExpand(colIndex);
        }
    };

    render() {

        const {data, className, isChecked, disabled, isExpanded, expandedIconCls} = this.props,

            columns = this.calColumns(),

            trClassName = classNames('table-row', {
                [className]: className,
                activated: isChecked,
                [data.rowClassName]: data.rowClassName
            });

        return (

                {
                    columns && columns.map(({col, span}, colIndex) =>
                         this.cellClickHandler(e, colIndex, col)}>
                            {col.collapseAble && data[col.childrenNumKey] > 0 &&
                             this.toggleExpandHandler(colIndex, col, e)}/>}
                            {this.contentRenderer(col.renderer, colIndex)}

                    )
                }

        );

    }
}

TableRow.propTypes = {

    rowIndex: PropTypes.number,
    columns: PropTypes.array,
    data: PropTypes.object,
    isChecked: PropTypes.bool,
    disabled: PropTypes.bool,
    expandedIconCls: PropTypes.string,
    className: PropTypes.string,

    onRowClick: PropTypes.func,
    onCellClick: PropTypes.func

};

TableRow.defaultProps = {
    className: '',
    rowIndex: 0,
    expandedIconCls: 'fas fa-angle-right',
    columns: [],
    data: {},
    isChecked: false,
    disabled: false
};

export default TableRow;
<|endoftext|>


<commit_before>import React from 'react';
import ModalContainer from 'react-mf-modal/container';
import AnimChildren from './animation-helper';
import Backdrop from './animated-backdrop';

export default class AnimatedContainer extends ModalContainer {
  render() {
    var modal = null;
    var backdrop = null;
    if (this.state.current) {
      modal = this.state.current.Element;
      backdrop = <Backdrop onClick={this.dismissModal} />;
    }
    return

      {this.props.children}
      <AnimChildren>
        { backdrop }
        { modal }
      </AnimChildren>

  }
}<commit_msg>Handle page clicks while fading out on anim example<commit_after>import React from 'react';
import ModalContainer from 'react-mf-modal/container';
import AnimChildren from './animation-helper';
import Backdrop from './animated-backdrop';

export default class AnimatedContainer extends ModalContainer {
  render() {
    var modal = null;
    var backdrop = null;
    var modalContainerStyle = {};
    if (this.state.current) {
      modal = this.state.current.Element;
      backdrop = <Backdrop onClick={this.dismissModal} />;
    } else {
      modalContainerStyle.pointerEvents = 'none';
    }
    return

      {this.props.children}

        <AnimChildren>
          { backdrop }
          { modal }
        </AnimChildren>

  }
}<|endoftext|>


<commit_before>/**
 * @file TreeNode component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import Theme from '../Theme';
import IconButton from '../IconButton';
import Radio from '../Radio';
import Checkbox from '../Checkbox';

import Util from '../_vendors/Util';
import Calculation from '../_vendors/Calculation';
import Position from '../_statics/Position';
import SelectMode from '../_statics/SelectMode';

export default class TreeNode extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            collapsed: false
        };

        this.toggleTreeNode = ::this.toggleTreeNode;
        this.touchTapHandler = ::this.touchTapHandler;

    }

    toggleTreeNode(e) {

        e.stopPropagation();

        this.setState({
            collapsed: !this.state.collapsed
        });

    }

    checkboxChangeHandler(e) {

        const {data, path, value, onSelect, onDeselect} = this.props;

        if (!Calculation.isItemChecked(data, value, this.props)) {
            onSelect && onSelect(data, path, e);
        } else {
            onDeselect && onDeselect(data, path, e);
        }

    }

    radioChangeHandler(e) {
        const {data, path, onSelect} = this.props;
        onSelect && onSelect(data, path, e);
    }

    touchTapHandler(e) {

        e.preventDefault();

        const {data, path, disabled, isLoading, readOnly} = this.props;

        if (disabled || isLoading || readOnly || data.disabled || data.isLoading || data.readOnly) {
            return;
        }

        const {onTouchTap} = this.props;
        onTouchTap && onTouchTap(data, path, e);

        const {selectMode} = this.props;

        switch (selectMode) {
            case SelectMode.MULTI_SELECT:
                this.checkboxChangeHandler(e);
                return;
            case SelectMode.SINGLE_SELECT:
                this.radioChangeHandler(e);
                return;
        }

    }

    render() {

        const {

                index, depth, path, theme, selectTheme, selectMode, data, value,
                disabled, isLoading, readOnly, allowCollapse,

                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                renderer, onMouseEnter, onMouseLeave

            } = this.props,
            {collapsed} = this.state,

            checked = Calculation.isItemChecked(data, value, this.props),

            isNodeLoading = data.isLoading || isLoading,
            isNodeDisabled = data.disabled || disabled || isNodeLoading,

            nodeClassName = (theme ? ` theme-${theme}` : '') + (data.className ? ' ' + data.className : ''),
            nodeStyle = {
                ...data.style,
                paddingLeft: (depth + 1) * 20
            },

            loadingIconPosition = (data.rightIconCls && !data.iconCls) ? 'right' : 'left';

        return (

                <TipProvider className='block'
                             text={data.tip}
                             tipPosition={data.tipPosition}>

                            {
                                allowCollapse && data.children && data.children.length > 0 ?
                                    <IconButton className="tree-node-collapse-icon"
                                                iconCls={collapsed ?
                                                    data.collapsedIconCls || collapsedIconCls
                                                    :
                                                    data.expandedIconCls || expandedIconCls}
                                                onTouchTap={this.toggleTreeNode}/>
                                    :
                                    null
                            }

                            {
                                selectMode === SelectMode.SINGLE_SELECT && (radioUncheckedIconCls || radioCheckedIconCls) ?
                                    <Radio className="tree-node-select"
                                           theme={selectTheme}
                                           checked={checked}
                                           disabled={isNodeDisabled}
                                           uncheckedIconCls={data.radioUncheckedIconCls || radioUncheckedIconCls}
                                           checkedIconCls={data.radioCheckedIconCls || radioCheckedIconCls}
                                           disableTouchRipple={true}/>
                                    :
                                    null
                            }

                            {
                                selectMode === SelectMode.MULTI_SELECT ?
                                    <Checkbox className="tree-node-select"
                                              theme={selectTheme}
                                              checked={checked}
                                              disabled={isNodeDisabled}
                                              uncheckedIconCls={data.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                              checkedIconCls={data.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                              indeterminateIconCls={data.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                              disableTouchRipple={true}/>
                                    :
                                    null
                            }

                            {
                                isNodeLoading && loadingIconPosition === 'left' ?

                                        <CircularLoading className="button-loading-icon"
                                                         size="small"/>

                                    :
                                    (
                                        data.iconCls ?
                                            <i className={`button-icon button-icon-left ${data.iconCls}`}
                                               aria-hidden="true">
                                            :
                                            null
                                    )
                            }

                            {
                                data.itemRenderer && typeof data.itemRenderer === 'function' ?
                                    data.itemRenderer(data, index)
                                    :
                                    (
                                        renderer && typeof renderer === 'function' ?
                                            renderer(data, index)
                                            :
                                            (
                                                data.desc ?

                                                        {data.text}

                                                        {data.desc}

                                                    :
                                                    data.text
                                            )
                                    )
                            }

                            {
                                isNodeLoading && loadingIconPosition === 'right' ?
                                    <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                                     size="small"/>
                                    :
                                    (
                                        data.rightIconCls ?
                                            <i className={`button-icon button-icon-right ${data.rightIconCls}`}
                                               aria-hidden="true">
                                            :
                                            null
                                    )
                            }

                </TipProvider>

                {
                    data.children && data.children.length > 0 ?

                            {
                                data.children.map((item, index) => {
                                    return (
                                        <TreeNode {...this.props}
                                                  key={index}
                                                  data={item}
                                                  index={index}
                                                  depth={depth + 1}
                                                  path={
                                                      path ?
                                                          [...path, {index, node: item}]
                                                          :
                                                          [{index, node: item}]
                                                  }/>
                                    );
                                })
                            }

                        :
                        null
                }

        );

    }
};

TreeNode.propTypes = {

    index: PropTypes.number,
    depth: PropTypes.number,
    path: PropTypes.array,

    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    data: PropTypes.object,
    value: PropTypes.any,

    idField: PropTypes.string,
    valueField: PropTypes.string,
    displayField: PropTypes.string,
    descriptionField: PropTypes.string,

    disabled: PropTypes.bool,
    isLoading: PropTypes.bool,
    readOnly: PropTypes.bool,
    allowCollapse: PropTypes.bool,

    renderer: PropTypes.func,

    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    onTouchTap: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func

};

TreeNode.defaultProps = {

    index: 0,
    depth: 0,
    path: null,

    theme: Theme.DEFAULT,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,

    data: null,
    value: null,

    idField: 'id',
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    disabled: false,
    isLoading: false,
    readOnly: false,
    allowCollapse: true,

    iconCls: null,
    rightIconCls: null,

    tip: null,
    tipPosition: Position.BOTTOM,

    collapsedIconCls: 'fa fa-caret-right',
    expandedIconCls: 'fa fa-caret-down',
    radioUncheckedIconCls: null,
    radioCheckedIconCls: null,
    checkboxUncheckedIconCls: 'fa fa-square-o',
    checkboxCheckedIconCls: 'fa fa-check-square',
    checkboxIndeterminateIconCls: 'fa fa-minus-square'

};<commit_msg>update TreeNode<commit_after>/**
 * @file TreeNode component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import Theme from '../Theme';
import IconButton from '../IconButton';
import Radio from '../Radio';
import Checkbox from '../Checkbox';

import Util from '../_vendors/Util';
import Calculation from '../_vendors/Calculation';
import Position from '../_statics/Position';
import SelectMode from '../_statics/SelectMode';

export default class TreeNode extends Component {

    static SelectMode = SelectMode;
    static Theme = Theme;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            collapsed: false
        };

        this.toggleTreeNode = ::this.toggleTreeNode;
        this.touchTapHandler = ::this.touchTapHandler;

    }

    toggleTreeNode(e) {

        e.stopPropagation();

        const {onNodeToggleStart} = this.props;
        onNodeToggleStart && onNodeToggleStart();

        this.setState({
            collapsed: !this.state.collapsed
        }, () => {
            const {onNodeToggleEnd} = this.props;
            onNodeToggleEnd && onNodeToggleEnd();
        });

    }

    checkboxChangeHandler(e) {

        const {data, path, value, onSelect, onDeselect} = this.props;

        if (!Calculation.isItemChecked(data, value, this.props)) {
            onSelect && onSelect(data, path, e);
        } else {
            onDeselect && onDeselect(data, path, e);
        }

    }

    radioChangeHandler(e) {
        const {data, path, onSelect} = this.props;
        onSelect && onSelect(data, path, e);
    }

    touchTapHandler(e) {

        e.preventDefault();

        const {data, path, disabled, isLoading, readOnly} = this.props;

        if (disabled || isLoading || readOnly || data.disabled || data.isLoading || data.readOnly) {
            return;
        }

        const {onTouchTap} = this.props;
        onTouchTap && onTouchTap(data, path, e);

        const {selectMode} = this.props;

        switch (selectMode) {
            case SelectMode.MULTI_SELECT:
                this.checkboxChangeHandler(e);
                return;
            case SelectMode.SINGLE_SELECT:
                this.radioChangeHandler(e);
                return;
        }

    }

    render() {

        const {

                index, depth, path, theme, selectTheme, selectMode, data, value,
                disabled, isLoading, readOnly, allowCollapse,

                collapsedIconCls, expandedIconCls, radioUncheckedIconCls, radioCheckedIconCls,
                checkboxUncheckedIconCls, checkboxCheckedIconCls, checkboxIndeterminateIconCls,

                renderer, onMouseEnter, onMouseLeave

            } = this.props,
            {collapsed} = this.state,

            checked = Calculation.isItemChecked(data, value, this.props),

            isNodeLoading = data.isLoading || isLoading,
            isNodeDisabled = data.disabled || disabled || isNodeLoading,

            nodeClassName = (theme ? ` theme-${theme}` : '') + (data.className ? ' ' + data.className : ''),
            nodeStyle = {
                ...data.style,
                paddingLeft: (depth + 1) * 20
            },

            loadingIconPosition = (data.rightIconCls && !data.iconCls) ? 'right' : 'left';

        return (

                <TipProvider className='block'
                             text={data.tip}
                             tipPosition={data.tipPosition}>

                            {
                                allowCollapse && data.children && data.children.length > 0 ?
                                    <IconButton className="tree-node-collapse-icon"
                                                iconCls={collapsed ?
                                                    data.collapsedIconCls || collapsedIconCls
                                                    :
                                                    data.expandedIconCls || expandedIconCls}
                                                onTouchTap={this.toggleTreeNode}/>
                                    :
                                    null
                            }

                            {
                                selectMode === SelectMode.SINGLE_SELECT && (radioUncheckedIconCls || radioCheckedIconCls) ?
                                    <Radio className="tree-node-select"
                                           theme={selectTheme}
                                           checked={checked}
                                           disabled={isNodeDisabled}
                                           uncheckedIconCls={data.radioUncheckedIconCls || radioUncheckedIconCls}
                                           checkedIconCls={data.radioCheckedIconCls || radioCheckedIconCls}
                                           disableTouchRipple={true}/>
                                    :
                                    null
                            }

                            {
                                selectMode === SelectMode.MULTI_SELECT ?
                                    <Checkbox className="tree-node-select"
                                              theme={selectTheme}
                                              checked={checked}
                                              disabled={isNodeDisabled}
                                              uncheckedIconCls={data.checkboxUncheckedIconCls || checkboxUncheckedIconCls}
                                              checkedIconCls={data.checkboxCheckedIconCls || checkboxCheckedIconCls}
                                              indeterminateIconCls={data.checkboxIndeterminateIconCls || checkboxIndeterminateIconCls}
                                              disableTouchRipple={true}/>
                                    :
                                    null
                            }

                            {
                                isNodeLoading && loadingIconPosition === 'left' ?

                                        <CircularLoading className="button-loading-icon"
                                                         size="small"/>

                                    :
                                    (
                                        data.iconCls ?
                                            <i className={`button-icon button-icon-left ${data.iconCls}`}
                                               aria-hidden="true">
                                            :
                                            null
                                    )
                            }

                            {
                                data.itemRenderer && typeof data.itemRenderer === 'function' ?
                                    data.itemRenderer(data, index)
                                    :
                                    (
                                        renderer && typeof renderer === 'function' ?
                                            renderer(data, index)
                                            :
                                            (
                                                data.desc ?

                                                        {data.text}

                                                        {data.desc}

                                                    :
                                                    data.text
                                            )
                                    )
                            }

                            {
                                isNodeLoading && loadingIconPosition === 'right' ?
                                    <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                                     size="small"/>
                                    :
                                    (
                                        data.rightIconCls ?
                                            <i className={`button-icon button-icon-right ${data.rightIconCls}`}
                                               aria-hidden="true">
                                            :
                                            null
                                    )
                            }

                </TipProvider>

                {
                    data.children && data.children.length > 0 ?

                            {
                                data.children.map((item, index) => {
                                    return (
                                        <TreeNode {...this.props}
                                                  key={index}
                                                  data={item}
                                                  index={index}
                                                  depth={depth + 1}
                                                  path={
                                                      path ?
                                                          [...path, {index, node: item}]
                                                          :
                                                          [{index, node: item}]
                                                  }/>
                                    );
                                })
                            }

                        :
                        null
                }

        );

    }
};

TreeNode.propTypes = {

    index: PropTypes.number,
    depth: PropTypes.number,
    path: PropTypes.array,

    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    selectTheme: PropTypes.oneOf(Util.enumerateValue(Theme)),
    selectMode: PropTypes.oneOf(Util.enumerateValue(SelectMode)),

    data: PropTypes.object,
    value: PropTypes.any,

    idField: PropTypes.string,
    valueField: PropTypes.string,
    displayField: PropTypes.string,
    descriptionField: PropTypes.string,

    disabled: PropTypes.bool,
    isLoading: PropTypes.bool,
    readOnly: PropTypes.bool,
    allowCollapse: PropTypes.bool,
    isNodeToggling: PropTypes.bool,

    renderer: PropTypes.func,

    collapsedIconCls: PropTypes.string,
    expandedIconCls: PropTypes.string,
    radioUncheckedIconCls: PropTypes.string,
    radioCheckedIconCls: PropTypes.string,
    checkboxUncheckedIconCls: PropTypes.string,
    checkboxCheckedIconCls: PropTypes.string,
    checkboxIndeterminateIconCls: PropTypes.string,

    onTouchTap: PropTypes.func,
    onSelect: PropTypes.func,
    onDeselect: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func,
    onNodeToggleStart: PropTypes.func,
    onNodeToggleEnd: PropTypes.func

};

TreeNode.defaultProps = {

    index: 0,
    depth: 0,
    path: null,

    theme: Theme.DEFAULT,

    selectTheme: Theme.DEFAULT,
    selectMode: SelectMode.SINGLE_SELECT,

    data: null,
    value: null,

    idField: 'id',
    valueField: 'value',
    displayField: 'text',
    descriptionField: 'desc',

    disabled: false,
    isLoading: false,
    readOnly: false,
    allowCollapse: true,
    isNodeToggling: false,

    iconCls: null,
    rightIconCls: null,

    tip: null,
    tipPosition: Position.BOTTOM,

    collapsedIconCls: 'fa fa-caret-right',
    expandedIconCls: 'fa fa-caret-down',
    radioUncheckedIconCls: null,
    radioCheckedIconCls: null,
    checkboxUncheckedIconCls: 'fa fa-square-o',
    checkboxCheckedIconCls: 'fa fa-check-square',
    checkboxIndeterminateIconCls: 'fa fa-minus-square'

};<|endoftext|>


<commit_before>var rescue = require('rescue')
var abend = require('abend')
var cadence = require('cadence')
var Chunk = require('prolific.chunk')

function Queue (stream, stderr) {
    this._buffers = []
    this._chunks = []
    this._chunkNumber = 1
    this._previousChecksum = 'aaaaaaaa'
    this._stream = stream
    this._terminated = false
    this._closed = false
    this._writing = false
    this._stderr = stderr
    this._chunks.push(new Chunk(0, new Buffer(''), 1))
}

Queue.prototype.write = function (buffer) {
    this._buffers.push(buffer)
    if (!this._writing) {
        this._writing = true
        this.flush(abend)
    }
}

Queue.prototype._chunkEntries = function () {
    var buffers = this._buffers
    if (buffers.length == 0) {
        return
    }
    this._buffers = []

    var buffer = Buffer.concat(buffers)
    this._chunks.push(new Chunk(this._chunkNumber++, buffer, buffer.length))
}

Queue.prototype._checkTerminated = function () {
    if (this._closed) {
        throw new Error('bigeasy.prolific.queue#closed')
    }
}

Queue.prototype.flush = cadence(function (async) {
    async([function () {
        var loop = async(function () {
            this._checkTerminated()
            if (this._chunks.length == 0) {
                this._chunkEntries()
                if (this._chunks.length == 0) {
                    this._writing = false
                    return [ loop.break ]
                }
            }
            var chunk = this._chunks[0]
            async(function () {
                this._checkTerminated()
                this._stream.write(chunk.header(this._previousChecksum), async())
            }, function () {
                this._checkTerminated()
    // TODO Wait for a response, let's get for reals here.
                this._stream.write(chunk.buffer, async())
            }, function () {
                this._checkTerminated()
                this._previousChecksum = chunk.checksum
                this._chunks.shift()
            })
        })()
    }, rescue(/^bigeasy.prolific.queue#closed$/, function () {
        this.exit(async())
    })])
})

// Necessary for uncaught exception when the default shutdown hooks in Node.js
// are disabled by a `SIGTERM` handler. My test to assert that we'd shutdown
// normally passed because the only hook was the async pipe between the parent
// and child. That is closed by `exit`. Anything else is going to keep the
// socket open, so you need to exit explicitly. If you exit immediately after
// write, then you will not flush STDERR.

// Put this note somewhere where you'll not delete it.
Queue.prototype.close = function () {
    if (!this._closed) {
        this._closed = true
        this._stream.end()
    }
}

Queue.prototype.exit = function (callback) {
    this._writing = false

    this.close()

    this._chunkEntries()

    if (!this._terminated) {
        this._terminated = true
        this._chunks.unshift(new Chunk(0, new Buffer(''), this._chunks[0].number))
        this._previousChecksum = 'aaaaaaaa'
    }

    var buffers = []
    while (this._chunks.length) {
        var chunk = this._chunks.shift()
        buffers.push(chunk.header(this._previousChecksum), chunk.buffer)
        this._previousChecksum = chunk.checksum
    }

    this._stderr.write(Buffer.concat(buffers), callback)
}

module.exports = Queue
<commit_msg>Fix exit test.<commit_after>var rescue = require('rescue')
var abend = require('abend')
var cadence = require('cadence')
var Chunk = require('prolific.chunk')

function Queue (stream, stderr) {
    this._buffers = []
    this._chunks = []
    this._chunkNumber = 1
    this._previousChecksum = 'aaaaaaaa'
    this._stream = stream
    this._terminated = false
    this._closed = false
    this._writing = false
    this._stderr = stderr
    this._chunks.push(new Chunk(0, new Buffer(''), 1))
}

Queue.prototype.write = function (buffer) {
    this._buffers.push(buffer)
    if (!this._writing) {
        this._writing = true
        this.flush(abend)
    }
}

Queue.prototype._chunkEntries = function () {
    var buffers = this._buffers
    if (buffers.length == 0) {
        return
    }
    this._buffers = []

    var buffer = Buffer.concat(buffers)
    this._chunks.push(new Chunk(this._chunkNumber++, buffer, buffer.length))
}

Queue.prototype._checkTerminated = function () {
    if (this._closed) {
        throw new Error('bigeasy.prolific.queue#closed')
    }
}

Queue.prototype.flush = cadence(function (async) {
    async([function () {
        var loop = async(function () {
            this._checkTerminated()
            if (this._chunks.length == 0) {
                this._chunkEntries()
                if (this._chunks.length == 0) {
                    this._writing = false
                    return [ loop.break ]
                }
            }
            var chunk = this._chunks[0]
            async(function () {
                this._checkTerminated()
                this._stream.write(chunk.header(this._previousChecksum), async())
            }, function () {
                this._checkTerminated()
    // TODO Wait for a response, let's get for reals here.
                this._stream.write(chunk.buffer, async())
            }, function () {
                this._checkTerminated()
                this._previousChecksum = chunk.checksum
                this._chunks.shift()
            })
        })()
    }, rescue(/^bigeasy.prolific.queue#closed$/, function () {
        this.exit(async())
    })])
})

// Necessary for uncaught exception when the default shutdown hooks in Node.js
// are disabled by a `SIGTERM` handler. My test to assert that we'd shutdown
// normally passed because the only hook was the async pipe between the parent
// and child. That is closed by `exit`. Anything else is going to keep the
// socket open, so you need to exit explicitly. If you exit immediately after
// write, then you will not flush STDERR.

// Put this note somewhere where you'll not delete it.
Queue.prototype.close = function () {
    if (!this._closed) {
        this._closed = true
        this._stream.end()
    }
}

Queue.prototype.exit = function (callback) {
    this._writing = false

    this.close()

    this._chunkEntries()

    if (!this._terminated) {
        var number = this._chunks.length ? this._chunks[0].number : this._chunkNumber
        this._chunks.unshift(new Chunk(0, new Buffer(''), number))
        this._previousChecksum = 'aaaaaaaa'
        this._terminated = true
    }

    var buffers = []
    while (this._chunks.length) {
        var chunk = this._chunks.shift()
        buffers.push(chunk.header(this._previousChecksum), chunk.buffer)
        this._previousChecksum = chunk.checksum
    }

    this._stderr.write(Buffer.concat(buffers), callback)
}

module.exports = Queue
<|endoftext|>


<commit_before>const React = require('react');
const moment = require('moment');

const { TimeBasedLineChart } = require('mx-react-components');

const Markdown = require('components/Markdown');

const TimeBasedLineChartDocs = React.createClass({
  render () {
    const lineChartData = [];

    for (let i = 6; i > 0; i--) {
      lineChartData.push({
        x: moment().subtract(i, 'months').startOf('month').unix(),
        y: Math.floor(Math.random() * 1000)
      });
    }

    for (let i = 0; i < 6; i++) {
      lineChartData.push({
        x: moment().add(i, 'months').startOf('month').unix(),
        y: Math.floor(Math.random() * 1000)
      });
    }

    return (

          TimeBasedLineChart
          A D3 time-based line chart chart that supports tooltips and a data breakpoint.

Demo

        <TimeBasedLineChart
          breakPointDate={moment().startOf('month').unix()}
          breakPointLabel={'This Month'}
          data={lineChartData}
          height={400}
          rangeType={'month'}
          shadeBelowZero={true}
          showZeroLine={true}
          style={{ boxSizing: 'content-box' }}
          width={700}
        />

Usage

breakPointDate Number

Default: today (as a Unix timestamp)

A Unix timestamp that sets the breakpoint for the chart. The breakpoint is displayed as a vertical line on the chart. If 'dashedFutureLine' is set to 'true', then the line after the breakpoint will be dashed instead of solid.

breakPointLabel String

Default: 'Today'

The text to display in the breakpoint label. The label is displayed at the top of the chart above the breakpoint line.

children Node(s)

If defined, these nodes will be used for the tooltip. Typically this is used in conjunction with 'hoverCallBack' to create custom tooltips.

data Arrray

An array of data objects that are used to set the x axis, y axis, and line values. Example:

        <Markdown lang='js'>
        {`
          [{
            x: 1446063248, //unix timestamp, required
            y: 10 //number or string
          }]
        `}
        </Markdown>

height Number

Default: 400

Sets the overall height of the chart.

limitLineCircles Bool

Default: false

If set to true, limits the circles on the line to the beginning, middle, and end of the line.

lineColor String

Default: #359BCF

A CSS color value that sets the color of the data line.

margin Object

A object that contains top, right, bottom, and left margin values. These can be used to adjust the spacing around the chart. Default:

        <Markdown lang='js'>
        {`
          {
            top: 20,
            right: 50,
            bottom: 20,
            left: 50
          }
        `}
        </Markdown>

rangeType String ('day', 'month')

Default: 'day'

This should match the type of data you're passing in. It is used to normalize the data points to month or day intervals.

shadeBelowZero Boolean

Default: false

If set to 'true', then the area below zero will be shaded. This is set to 'true' in the example above.

shadeFutureOnGraph Boolean

Default: true

Set this to 'false' if don't want any part of the graph that is in the future to be shaded.

showBreakPoint Boolean

Default: true

Set this to 'false' if you don't want to display the vertical line and label that indicates the breakpoint.

showZeroLine Boolean

Default: false

If set to 'true', then a tick, label, and line will be displayed for the '0' yAxis value.

width Number

Default: 550

Sets the overall height of the chart.

yAxisFormatter Function

This function will be used to determine the format of the y axis tick labels. If should return some type of value.

zeroState Node

A JSX node rendered if the data supplied has no length.

Example

        <Markdown lang='js'>
  {`
    const lineChartData = [];

    for (let i = 6; i > 0; i--) {
      lineChartData.push({
        timeStamp: moment().subtract(i, 'months').startOf('month').unix(),
        value: Math.floor(Math.random() * 1000)
      });
    }

    for (let i = 0; i < 6; i++) {
      lineChartData.push({
        timeStamp: moment().add(i, 'months').startOf('month').unix(),
        value: Math.floor(Math.random() * 1000)
      });
    }

    <TimeBasedLineChart
      breakPointDate={moment().startOf('month').unix()}
      breakPointLabel={'This Month'}
      data={lineChartData}
      height={400}
      rangeType={'month'}
      shadeBelowZero={true}
      showZeroLine={true}
      style={{ boxSizing: 'content-box' }}
      width={700}
    />
  `}
        </Markdown>

    );
  }
});

module.exports = TimeBasedLineChartDocs;
<commit_msg>decided not to change docs in this PR<commit_after>const React = require('react');
const moment = require('moment');

const { TimeBasedLineChart } = require('mx-react-components');

const Markdown = require('components/Markdown');

const TimeBasedLineChartDocs = React.createClass({
  render () {
    const lineChartData = [];

    for (let i = 6; i > 0; i--) {
      lineChartData.push({
        x: moment().subtract(i, 'months').startOf('month').unix(),
        y: Math.floor(Math.random() * 1000)
      });
    }

    for (let i = 0; i < 6; i++) {
      lineChartData.push({
        x: moment().add(i, 'months').startOf('month').unix(),
        y: Math.floor(Math.random() * 1000)
      });
    }

    return (

          TimeBasedLineChart
          A D3 time-based line chart chart that supports tooltips and a data breakpoint.

Demo

        <TimeBasedLineChart
          breakPointDate={moment().startOf('month').unix()}
          breakPointLabel={'This Month'}
          data={lineChartData}
          height={400}
          margin={{ top: 30, right: 0, bottom: 30, left: 75 }}
          rangeType={'month'}
          shadeBelowZero={true}
          showZeroLine={true}
          style={{ boxSizing: 'content-box' }}
          width={700}
        />

Usage

breakPointDate Number

Default: today (as a Unix timestamp)

A Unix timestamp that sets the breakpoint for the chart. The breakpoint is displayed as a vertical line on the chart. If 'dashedFutureLine' is set to 'true', then the line after the breakpoint will be dashed instead of solid.

breakPointLabel String

Default: 'Today'

The text to display in the breakpoint label. The label is displayed at the top of the chart above the breakpoint line.

children Node(s)

If defined, these nodes will be used for the tooltip. Typically this is used in conjunction with 'hoverCallBack' to create custom tooltips.

data Arrray

An array of data objects that are used to set the x axis, y axis, and line values. Example:

        <Markdown lang='js'>
        {`
          [{
            x: 1446063248, //unix timestamp, required
            y: 10 //number or string
          }]
        `}
        </Markdown>

height Number

Default: 400

Sets the overall height of the chart.

limitLineCircles Bool

Default: false

If set to true, limits the circles on the line to the beginning, middle, and end of the line.

lineColor String

Default: #359BCF

A CSS color value that sets the color of the data line.

margin Object

A object that contains top, right, bottom, and left margin values. These can be used to adjust the spacing around the chart. Default:

        <Markdown lang='js'>
        {`
          {
            top: 20,
            right: 50,
            bottom: 20,
            left: 50
          }
        `}
        </Markdown>

rangeType String ('day', 'month')

Default: 'day'

This should match the type of data you're passing in. It is used to normalize the data points to month or day intervals.

shadeBelowZero Boolean

Default: false

If set to 'true', then the area below zero will be shaded. This is set to 'true' in the example above.

shadeFutureOnGraph Boolean

Default: true

Set this to 'false' if don't want any part of the graph that is in the future to be shaded.

showBreakPoint Boolean

Default: true

Set this to 'false' if you don't want to display the vertical line and label that indicates the breakpoint.

showZeroLine Boolean

Default: false

If set to 'true', then a tick, label, and line will be displayed for the '0' yAxis value.

width Number

Default: 550

Sets the overall height of the chart.

yAxisFormatter Function

This function will be used to determine the format of the y axis tick labels. If should return some type of value.

zeroState Node

A JSX node rendered if the data supplied has no length.

Example

        <Markdown lang='js'>
  {`
    const lineChartData = [];

    for (let i = 6; i > 0; i--) {
      lineChartData.push({
        timeStamp: moment().subtract(i, 'months').startOf('month').unix(),
        value: Math.floor(Math.random() * 1000)
      });
    }

    for (let i = 0; i < 6; i++) {
      lineChartData.push({
        timeStamp: moment().add(i, 'months').startOf('month').unix(),
        value: Math.floor(Math.random() * 1000)
      });
    }

    <TimeBasedLineChart
      breakPointDate={moment().startOf('month').unix()}
      breakPointLabel={'This Month'}
      data={lineChartData}
      height={400}
      margin={{ top: 30, right: 0, bottom: 30, left: 75 }}
      rangeType={'month'}
      shadeBelowZero={true}
      showZeroLine={true}
      style={{ boxSizing: 'content-box' }}
      width={700}
    />
  `}
        </Markdown>

    );
  }
});

module.exports = TimeBasedLineChartDocs;
<|endoftext|>