<commit_before>/*
 * Copyright (c) 1997-2013, SalesLogix, NA., LLC. All rights reserved.
 */

/**
 * @class Mobile.SalesLogix.SpeedSearchWidget
 *
 * @mixins Sage.Platform.Mobile._Templated
 *
 */
define('Mobile/SalesLogix/SpeedSearchWidget', [
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/_base/event',
    'dojo/dom-class',
    'dijit/_Widget',
    'Sage/Platform/Mobile/_Templated'
], function(
    declare,
    lang,
    event,
    domClass,
    _Widget,
    _Templated
) {
    return declare('Mobile.SalesLogix.SpeedSearchWidget', [_Widget, _Templated], {
        /**
         * @property {Object} attributeMap
         */
        attributeMap: {
            queryValue: {node: 'queryNode', type: 'attribute', attribute: 'value'}
        },

        /**
         * @property {Simplate} widgetTemplate
         */
        widgetTemplate: new Simplate([
            '
',
            '
',
                '
<input type="text" placeholder="{%= $.searchText %}" name="query" class="query" autocorrect="off" autocapitalize="off" data-dojo-attach-point="queryNode" data-dojo-attach-event="onfocus:_onFocus,onblur:_onBlur,onkeypress:_onKeyPress" />
',
            '
',
            '
'
        ]),
        /**
         * @property {DOMNode} queryNode HTML input node. The dojo attach point.
         */
        queryNode: null,

        /**
         * @property {String} searchText The placeholder text for the input.
         */
        searchText: 'SpeedSearch',

        _setQueryValueAttr: function(value) {
            this._onFocus();
            this.queryNode.value = value;
        },
        /**
         * Clears the current search query.
         */
        clear: function() {
            domClass.remove(this.domNode, 'search-active');
            this.set('queryValue', '');
        },
        /**
         * Fires onSearchExpression using the current search query.
         */
        search: function() {
            var queryTerm = this.getQuery();
            if (!lang.trim(queryTerm)) {
                return;
            }

            this.onSearchExpression(queryTerm, this);
        },
        /**
         * Returns the current search query.
         * @returns {String}
         */
        getQuery: function() {
            return this.queryNode.value;
        },
        configure: function(options) {
            lang.mixin(this, options);
        },
        _onClearClick: function(evt) {
            event.stop(evt);
            this.clear();
            this.queryNode.focus();
            this.queryNode.click();
        },
        _onBlur: function() {
            domClass.toggle(this.domNode, 'search-active', !!this.queryNode.value);
        },
        _onFocus: function() {
            domClass.add(this.domNode, 'search-active');

            // Work around a chrome issue were mouseup after a focus will de-select the text
            setTimeout(function() {
                this.queryNode.select();
            }.bind(this), 50);
        },
        _onKeyPress: function(evt) {
            if (evt.keyCode == 13 || evt.keyCode == 10) {
                event.stop(evt);
                this.queryNode.blur();
                this.search();
            }
        },
        /**
         * The event that fires when the search widget provides an explicit search query
         * @param {String} expression
         * @param {Object} widget
         */
        onSearchExpression: function(expression, widget) {
        }
    });
});

<commit_msg>switching from select to setSelectionRange to work around IOS issues. #MBL-10764<commit_after>/*
 * Copyright (c) 1997-2013, SalesLogix, NA., LLC. All rights reserved.
 */

/**
 * @class Mobile.SalesLogix.SpeedSearchWidget
 *
 * @mixins Sage.Platform.Mobile._Templated
 *
 */
define('Mobile/SalesLogix/SpeedSearchWidget', [
    'dojo/_base/declare',
    'dojo/_base/lang',
    'dojo/_base/event',
    'dojo/dom-class',
    'dijit/_Widget',
    'Sage/Platform/Mobile/_Templated'
], function(
    declare,
    lang,
    event,
    domClass,
    _Widget,
    _Templated
) {
    return declare('Mobile.SalesLogix.SpeedSearchWidget', [_Widget, _Templated], {
        /**
         * @property {Object} attributeMap
         */
        attributeMap: {
            queryValue: {node: 'queryNode', type: 'attribute', attribute: 'value'}
        },

        /**
         * @property {Simplate} widgetTemplate
         */
        widgetTemplate: new Simplate([
            '
',
            '
',
                '
<input type="text" placeholder="{%= $.searchText %}" name="query" class="query" autocorrect="off" autocapitalize="off" data-dojo-attach-point="queryNode" data-dojo-attach-event="onfocus:_onFocus,onblur:_onBlur,onkeypress:_onKeyPress" />
',
            '
',
            '
'
        ]),
        /**
         * @property {DOMNode} queryNode HTML input node. The dojo attach point.
         */
        queryNode: null,

        /**
         * @property {String} searchText The placeholder text for the input.
         */
        searchText: 'SpeedSearch',

        _setQueryValueAttr: function(value) {
            this._onFocus();
            this.queryNode.value = value;
        },
        /**
         * Clears the current search query.
         */
        clear: function() {
            domClass.remove(this.domNode, 'search-active');
            this.set('queryValue', '');
        },
        /**
         * Fires onSearchExpression using the current search query.
         */
        search: function() {
            var queryTerm = this.getQuery();
            if (!lang.trim(queryTerm)) {
                return;
            }

            this.onSearchExpression(queryTerm, this);
        },
        /**
         * Returns the current search query.
         * @returns {String}
         */
        getQuery: function() {
            return this.queryNode.value;
        },
        configure: function(options) {
            lang.mixin(this, options);
        },
        _onClearClick: function(evt) {
            event.stop(evt);
            this.clear();
            this.queryNode.focus();
            this.queryNode.click();
        },
        _onBlur: function() {
            domClass.toggle(this.domNode, 'search-active', !!this.queryNode.value);
        },
        _onFocus: function() {
            domClass.add(this.domNode, 'search-active');

            // Work around a chrome issue where mouseup after a focus will de-select the text
            setTimeout(function() {
                this.queryNode.setSelectionRange(0, 9999);
            }.bind(this), 50);
        },
        _onKeyPress: function(evt) {
            if (evt.keyCode == 13 || evt.keyCode == 10) {
                event.stop(evt);
                this.queryNode.blur();
                this.search();
            }
        },
        /**
         * The event that fires when the search widget provides an explicit search query
         * @param {String} expression
         * @param {Object} widget
         */
        onSearchExpression: function(expression, widget) {
        }
    });
});

<|endoftext|>


<commit_before>/**
 * @author Luc Calaresu <dev@calaresu.com>
 *
 * Mail
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

define([
	'service/attachmentservice',
	'OC',
	'radio',
], function(AttachmentService, OC, Radio) {

	describe('AttachmentService', function() {

		var fakeModel;
		var fakeFile;

		beforeEach(function() {
			jasmine.Ajax.install();
			fakeModel = jasmine.createSpyObj('fakeModel', ['onProgress', 'set', 'get', 'unset']);
			fakeFile = {filename: 'file.zip', size: '12345' };
		});

		afterEach(function() {
			jasmine.Ajax.uninstall();
		});

		/* when everything went as expected on attachment upload */
		it('uploads a new attachment on the server', function() {
			spyOn(OC, 'generateUrl').and.returnValue('index.php/apps/mail/attachments');

			jasmine.Ajax.stubRequest('index.php/apps/mail/attachments').andReturn({
				status: 200,
				contentType: 'application/json',
				responseText: '{"id": "33", "fileName": "file.zip"}'
			});

			var promise = AttachmentService.uploadLocalAttachment(fakeFile, fakeModel);
			expect(OC.generateUrl).toHaveBeenCalledWith('/apps/mail/attachments');

			promise
				.then(function(attachment, fileId) {
					expect(jasmine.Ajax.requests.count()).toBe(1);
					expect(jasmine.Ajax.requests.mostRecent().url).toBe('index.php/apps/mail/attachments');
				})
				.catch(function(attachment) {
					fail('Attachment upload is not supposed to fail');
				});
		});

		/* when an error occurred on server side on attachment upload */
		it('handles errors during attachment uploads', function() {
			spyOn(OC, 'generateUrl').and.returnValue('index.php/apps/mail/attachments');

			jasmine.Ajax.stubRequest('index.php/apps/mail/attachments').andReturn({
				status: 500,
				contentType: 'text/plain'
			});

			var promise = AttachmentService.uploadLocalAttachment(fakeFile, fakeModel);
			expect(OC.generateUrl).toHaveBeenCalledWith('/apps/mail/attachments');

			promise
				.then(function(attachment, fileId) {
					fail('Attachment upload is supposed to fail');
				})
				.catch(function(attachment) {
					expect(jasmine.Ajax.requests.count()).toBe(1);
					expect(jasmine.Ajax.requests.mostRecent().url).toBe('index.php/apps/mail/attachments');
				});
		});

		/* when upload finished with success */
		it('handles upload finished with success', function() {
			// progress=1, for upload succeeded
			fakeModel.progress = 1;
			AttachmentService.uploadLocalAttachmentFinished(fakeModel, 33);

			// the model ID and upload status are updated on succes
			expect(fakeModel.set).toHaveBeenCalledWith('id', 33);
			expect(fakeModel.set).toHaveBeenCalledWith('uploadStatus', 3);

			// make sure the xhr request has been removed from the model
			expect(fakeModel.unset).toHaveBeenCalledWith('uploadRequest');

		});

		/* when a problem occured on upload */
		it('handles upload finished with error', function() {
			// progress=1, but no id returned from the server
			fakeModel.progress = 1;
			AttachmentService.uploadLocalAttachmentFinished(fakeModel);
			// the upload status are updated on succes
			expect(fakeModel.set).toHaveBeenCalledWith('uploadStatus', 2);
			// make sure the xhr request has been removed from the model
			expect(fakeModel.unset).toHaveBeenCalledWith('uploadRequest');

			// == id returned from server but file not uploaded ==
			// not sure that is possible though
			fakeModel.progress = 0.5;
			AttachmentService.uploadLocalAttachmentFinished(fakeModel, 33);
			// the upload status are updated on succes
			expect(fakeModel.set).toHaveBeenCalledWith('uploadStatus', 2);
			// make sure the xhr request has been removed from the model
			expect(fakeModel.unset).toHaveBeenCalledWith('uploadRequest');
		});
	});
});
<commit_msg>Fix async tests<commit_after>/**
 * @author Luc Calaresu <dev@calaresu.com>
 *
 * Mail
 *
 * This code is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License, version 3,
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 */

define([
	'service/attachmentservice',
	'OC',
	'radio',
], function(AttachmentService, OC, Radio) {

	describe('AttachmentService', function() {

		var fakeModel;
		var fakeFile;

		beforeEach(function() {
			jasmine.Ajax.install();
			fakeModel = jasmine.createSpyObj('fakeModel', ['onProgress', 'set', 'get', 'unset']);
			fakeFile = {filename: 'file.zip', size: '12345' };
		});

		afterEach(function() {
			jasmine.Ajax.uninstall();
		});

		/* when everything went as expected on attachment upload */
		it('uploads a new attachment on the server', function(done) {
			spyOn(OC, 'generateUrl').and.returnValue('index.php/apps/mail/attachments');

			jasmine.Ajax.stubRequest('index.php/apps/mail/attachments').andReturn({
				status: 200,
				contentType: 'application/json',
				responseText: '{"id": "33", "fileName": "file.zip"}'
			});

			var promise = AttachmentService.uploadLocalAttachment(fakeFile, fakeModel);
			expect(OC.generateUrl).toHaveBeenCalledWith('/apps/mail/attachments');

			promise
				.then(function(attachment, fileId) {
					expect(jasmine.Ajax.requests.count()).toBe(1);
					expect(jasmine.Ajax.requests.mostRecent().url).toBe('index.php/apps/mail/attachments');
					done();
				})
				.catch(function(error) {
					console.error(error);
					done.fail('Attachment upload is not supposed to fail', error);
				});
		});

		/* when an error occurred on server side on attachment upload */
		it('handles errors during attachment uploads', function(done) {
			spyOn(OC, 'generateUrl').and.returnValue('index.php/apps/mail/attachments');

			jasmine.Ajax.stubRequest('index.php/apps/mail/attachments').andReturn({
				status: 500,
				contentType: 'text/plain'
			});

			var promise = AttachmentService.uploadLocalAttachment(fakeFile, fakeModel);
			expect(OC.generateUrl).toHaveBeenCalledWith('/apps/mail/attachments');

			promise
				.then(function(attachment, fileId) {
					done.fail('Attachment upload is supposed to fail');
				})
				.catch(function(attachment) {
					expect(jasmine.Ajax.requests.count()).toBe(1);
					expect(jasmine.Ajax.requests.mostRecent().url).toBe('index.php/apps/mail/attachments');
					done();
				});
		});

		/* when upload finished with success */
		it('handles upload finished with success', function() {
			// progress=1, for upload succeeded
			fakeModel.progress = 1;
			AttachmentService.uploadLocalAttachmentFinished(fakeModel, 33);

			// the model ID and upload status are updated on succes
			expect(fakeModel.set).toHaveBeenCalledWith('id', 33);
			expect(fakeModel.set).toHaveBeenCalledWith('uploadStatus', 3);

			// make sure the xhr request has been removed from the model
			expect(fakeModel.unset).toHaveBeenCalledWith('uploadRequest');

		});

		/* when a problem occured on upload */
		it('handles upload finished with error', function() {
			// progress=1, but no id returned from the server
			fakeModel.progress = 1;
			AttachmentService.uploadLocalAttachmentFinished(fakeModel);
			// the upload status are updated on succes
			expect(fakeModel.set).toHaveBeenCalledWith('uploadStatus', 2);
			// make sure the xhr request has been removed from the model
			expect(fakeModel.unset).toHaveBeenCalledWith('uploadRequest');

			// == id returned from server but file not uploaded ==
			// not sure that is possible though
			fakeModel.progress = 0.5;
			AttachmentService.uploadLocalAttachmentFinished(fakeModel, 33);
			// the upload status are updated on succes
			expect(fakeModel.set).toHaveBeenCalledWith('uploadStatus', 2);
			// make sure the xhr request has been removed from the model
			expect(fakeModel.unset).toHaveBeenCalledWith('uploadRequest');
		});
	});
});
<|endoftext|>


<commit_before>/* eslint default-case: 0 */
/* eslint guard-for-in: 0 */
import React, { Component, PropTypes } from 'react';
import classSet from 'classnames';
import Const from './Const';
import Util from './util';
import DateFilter from './filters/Date';
import TextFilter from './filters/Text';
import RegexFilter from './filters/Regex';
import SelectFilter from './filters/Select';
import NumberFilter from './filters/Number';

class TableHeaderColumn extends Component {

  constructor(props) {
    super(props);
    this.handleFilter = this.handleFilter.bind(this);
  }

  handleColumnClick = () => {
    if (this.props.isOnlyHead || !this.props.dataSort) return;
    const order = this.props.sort === Const.SORT_DESC ? Const.SORT_ASC : Const.SORT_DESC;
    this.props.onSort(order, this.props.dataField);
  }

  handleFilter(value, type) {
    this.props.filter.emitter.handleFilter(this.props.dataField, value, type);
  }

  getFilters() {
    const { headerText, children } = this.props;
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      return (
        <TextFilter ref='textFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.REGEX: {
      return (
        <RegexFilter ref='regexFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.SELECT: {
      return (
        <SelectFilter ref='selectFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.NUMBER: {
      return (
        <NumberFilter ref='numberFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.DATE: {
      return (
        <DateFilter ref='dateFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.CUSTOM: {
      const elm = this.props.filter.getElement(this.handleFilter,
          this.props.filter.customFilterParameters);

      return React.cloneElement(elm, { ref: 'customFilter' });
    }
    }
  }

  componentDidMount() {
    this.refs['header-col'].setAttribute('data-field', this.props.dataField);
  }

  render() {
    let defaultCaret;
    let sortCaret;
    const {
      headerText,
      dataAlign,
      dataField,
      headerAlign,
      headerTitle,
      hidden,
      sort,
      dataSort,
      sortIndicator,
      children,
      caretRender,
      className,
      isOnlyHead
    } = this.props;
    const thStyle = {
      textAlign: headerAlign || dataAlign,
      display: hidden ? 'none' : null
    };
    if (!isOnlyHead) {
      if (sortIndicator) {
        defaultCaret = (!dataSort) ? null : (

        );
      }
      sortCaret = sort ? Util.renderReactSortCaret(sort) : defaultCaret;
      if (caretRender) {
        sortCaret = caretRender(sort, dataField);
      }
    }

    const classes = classSet(
      typeof className === 'function' ? className() : className,
      !isOnlyHead && dataSort ? 'sort-column' : '');

    const title = {
      title: ((headerTitle && typeof children === 'string') ? children : headerText)
    };
    return (

        { children }{ sortCaret }

 e.stopPropagation() }>
          { this.props.filter ? this.getFilters() : null }

    );
  }

  cleanFiltered() {
    if (this.props.filter === undefined) {
      return;
    }

    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.CUSTOM: {
      this.refs.customFilter.cleanFiltered();
      break;
    }
    }
  }

  applyFilter(val) {
    if (this.props.filter === undefined) return;
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.applyFilter(val);
      break;
    }
    }
  }
}

const filterTypeArray = [];
for (const key in Const.FILTER_TYPE) {
  filterTypeArray.push(Const.FILTER_TYPE[key]);
}

TableHeaderColumn.propTypes = {
  dataField: PropTypes.string,
  dataAlign: PropTypes.string,
  headerAlign: PropTypes.string,
  headerTitle: PropTypes.bool,
  headerText: PropTypes.string,
  dataSort: PropTypes.bool,
  onSort: PropTypes.func,
  dataFormat: PropTypes.func,
  csvFormat: PropTypes.func,
  csvHeader: PropTypes.string,
  isKey: PropTypes.bool,
  editable: PropTypes.any,
  hidden: PropTypes.bool,
  hiddenOnInsert: PropTypes.bool,
  searchable: PropTypes.bool,
  className: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func
  ]),
  width: PropTypes.string,
  sortFunc: PropTypes.func,
  sortFuncExtraData: PropTypes.any,
  columnClassName: PropTypes.any,
  editColumnClassName: PropTypes.any,
  columnTitle: PropTypes.bool,
  filterFormatted: PropTypes.bool,
  filterValue: PropTypes.func,
  sort: PropTypes.string,
  caretRender: PropTypes.func,
  formatExtraData: PropTypes.any,
  filter: PropTypes.shape({
    type: PropTypes.oneOf(filterTypeArray),
    delay: PropTypes.number,
    options: PropTypes.oneOfType([
      PropTypes.object, // for SelectFilter
      PropTypes.arrayOf(PropTypes.number) // for NumberFilter
    ]),
    numberComparators: PropTypes.arrayOf(PropTypes.string),
    emitter: PropTypes.object,
    placeholder: PropTypes.string,
    getElement: PropTypes.func,
    customFilterParameters: PropTypes.object
  }),
  sortIndicator: PropTypes.bool,
  export: PropTypes.bool,
  expandable: PropTypes.bool
};

TableHeaderColumn.defaultProps = {
  dataAlign: 'left',
  headerAlign: undefined,
  headerTitle: true,
  dataSort: false,
  dataFormat: undefined,
  csvFormat: undefined,
  csvHeader: undefined,
  isKey: false,
  editable: true,
  onSort: undefined,
  hidden: false,
  hiddenOnInsert: false,
  searchable: true,
  className: '',
  columnTitle: false,
  width: null,
  sortFunc: undefined,
  columnClassName: '',
  editColumnClassName: '',
  filterFormatted: false,
  filterValue: undefined,
  sort: undefined,
  formatExtraData: undefined,
  sortFuncExtraData: undefined,
  filter: undefined,
  sortIndicator: true,
  expandable: true
};

export default TableHeaderColumn;
<commit_msg>blocking the functionality for filter on span header column; BTW, it should be supported, but some bug need fix firstly<commit_after>/* eslint default-case: 0 */
/* eslint guard-for-in: 0 */
import React, { Component, PropTypes } from 'react';
import classSet from 'classnames';
import Const from './Const';
import Util from './util';
import DateFilter from './filters/Date';
import TextFilter from './filters/Text';
import RegexFilter from './filters/Regex';
import SelectFilter from './filters/Select';
import NumberFilter from './filters/Number';

class TableHeaderColumn extends Component {

  constructor(props) {
    super(props);
    this.handleFilter = this.handleFilter.bind(this);
  }

  handleColumnClick = () => {
    if (this.props.isOnlyHead || !this.props.dataSort) return;
    const order = this.props.sort === Const.SORT_DESC ? Const.SORT_ASC : Const.SORT_DESC;
    this.props.onSort(order, this.props.dataField);
  }

  handleFilter(value, type) {
    this.props.filter.emitter.handleFilter(this.props.dataField, value, type);
  }

  getFilters() {
    const { headerText, children } = this.props;
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      return (
        <TextFilter ref='textFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.REGEX: {
      return (
        <RegexFilter ref='regexFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.SELECT: {
      return (
        <SelectFilter ref='selectFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.NUMBER: {
      return (
        <NumberFilter ref='numberFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.DATE: {
      return (
        <DateFilter ref='dateFilter' { ...this.props.filter }
          columnName={ headerText || children } filterHandler={ this.handleFilter } />
      );
    }
    case Const.FILTER_TYPE.CUSTOM: {
      const elm = this.props.filter.getElement(this.handleFilter,
          this.props.filter.customFilterParameters);

      return React.cloneElement(elm, { ref: 'customFilter' });
    }
    }
  }

  componentDidMount() {
    this.refs['header-col'].setAttribute('data-field', this.props.dataField);
  }

  render() {
    let defaultCaret;
    let sortCaret;
    const {
      headerText,
      dataAlign,
      dataField,
      headerAlign,
      headerTitle,
      hidden,
      sort,
      dataSort,
      sortIndicator,
      children,
      caretRender,
      className,
      isOnlyHead
    } = this.props;
    const thStyle = {
      textAlign: headerAlign || dataAlign,
      display: hidden ? 'none' : null
    };
    if (!isOnlyHead) {
      if (sortIndicator) {
        defaultCaret = (!dataSort) ? null : (

        );
      }
      sortCaret = sort ? Util.renderReactSortCaret(sort) : defaultCaret;
      if (caretRender) {
        sortCaret = caretRender(sort, dataField);
      }
    }

    const classes = classSet(
      typeof className === 'function' ? className() : className,
      !isOnlyHead && dataSort ? 'sort-column' : '');

    const title = {
      title: ((headerTitle && typeof children === 'string') ? children : headerText)
    };
    return (

        { children }{ sortCaret }

 e.stopPropagation() }>
          { this.props.filter && !isOnlyHead ? this.getFilters() : null }

    );
  }

  cleanFiltered() {
    if (this.props.filter === undefined) {
      return;
    }

    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.cleanFiltered();
      break;
    }
    case Const.FILTER_TYPE.CUSTOM: {
      this.refs.customFilter.cleanFiltered();
      break;
    }
    }
  }

  applyFilter(val) {
    if (this.props.filter === undefined) return;
    switch (this.props.filter.type) {
    case Const.FILTER_TYPE.TEXT: {
      this.refs.textFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.REGEX: {
      this.refs.regexFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.SELECT: {
      this.refs.selectFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.NUMBER: {
      this.refs.numberFilter.applyFilter(val);
      break;
    }
    case Const.FILTER_TYPE.DATE: {
      this.refs.dateFilter.applyFilter(val);
      break;
    }
    }
  }
}

const filterTypeArray = [];
for (const key in Const.FILTER_TYPE) {
  filterTypeArray.push(Const.FILTER_TYPE[key]);
}

TableHeaderColumn.propTypes = {
  dataField: PropTypes.string,
  dataAlign: PropTypes.string,
  headerAlign: PropTypes.string,
  headerTitle: PropTypes.bool,
  headerText: PropTypes.string,
  dataSort: PropTypes.bool,
  onSort: PropTypes.func,
  dataFormat: PropTypes.func,
  csvFormat: PropTypes.func,
  csvHeader: PropTypes.string,
  isKey: PropTypes.bool,
  editable: PropTypes.any,
  hidden: PropTypes.bool,
  hiddenOnInsert: PropTypes.bool,
  searchable: PropTypes.bool,
  className: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func
  ]),
  width: PropTypes.string,
  sortFunc: PropTypes.func,
  sortFuncExtraData: PropTypes.any,
  columnClassName: PropTypes.any,
  editColumnClassName: PropTypes.any,
  columnTitle: PropTypes.bool,
  filterFormatted: PropTypes.bool,
  filterValue: PropTypes.func,
  sort: PropTypes.string,
  caretRender: PropTypes.func,
  formatExtraData: PropTypes.any,
  filter: PropTypes.shape({
    type: PropTypes.oneOf(filterTypeArray),
    delay: PropTypes.number,
    options: PropTypes.oneOfType([
      PropTypes.object, // for SelectFilter
      PropTypes.arrayOf(PropTypes.number) // for NumberFilter
    ]),
    numberComparators: PropTypes.arrayOf(PropTypes.string),
    emitter: PropTypes.object,
    placeholder: PropTypes.string,
    getElement: PropTypes.func,
    customFilterParameters: PropTypes.object
  }),
  sortIndicator: PropTypes.bool,
  export: PropTypes.bool,
  expandable: PropTypes.bool
};

TableHeaderColumn.defaultProps = {
  dataAlign: 'left',
  headerAlign: undefined,
  headerTitle: true,
  dataSort: false,
  dataFormat: undefined,
  csvFormat: undefined,
  csvHeader: undefined,
  isKey: false,
  editable: true,
  onSort: undefined,
  hidden: false,
  hiddenOnInsert: false,
  searchable: true,
  className: '',
  columnTitle: false,
  width: null,
  sortFunc: undefined,
  columnClassName: '',
  editColumnClassName: '',
  filterFormatted: false,
  filterValue: undefined,
  sort: undefined,
  formatExtraData: undefined,
  sortFuncExtraData: undefined,
  filter: undefined,
  sortIndicator: true,
  expandable: true
};

export default TableHeaderColumn;
<|endoftext|>


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

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

import EditableField from '../EditableField';
import IconButton from '../IconButton';

import Util from '../_vendors/Util';
import Dom from '../_vendors/Dom';
import CharSize from '../_vendors/CharSize';
import Event from '../_vendors/Event';
import ComponentUtil from '../_vendors/ComponentUtil';

class TagField extends Component {

    static DEFAULT_SEPARATORS = [','];

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.inputSymbol = Symbol('input');
        this.wrapper = createRef();
        this.wrapperEl = null;
        this.inputWrapper = createRef();
        this.input = createRef();
        this.test = createRef();
        this.testEl = null;

        this.state = {
            wrapperWidth: 'auto',
            data: props.data,
            inputValue: props.inputValue,
            inputIndex: props.data.length,
            itemEditing: false,
            editingItemIndex: -1
        };

    }

    getSeparators = (separators = this.props.separators) => {

        if (separators && separators.length > 0) {
            return separators;
        }

        return DEFAULT_SEPARATORS;

    };

    generateSeparatorReg = (separators = this.props.separators) => {
        const seps = this.getSeparators(separators);
        return new RegExp(`\\s*${seps ? seps.map(sep => `\\${sep}`).join('|') : ''}\\s*`, 'g');
    };

    removeItem = index => {

        const {data} = this.state;

        if (!data || data.length < 1 || !(index in data)) {
            return;
        }

        data.splice(index, 1);

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    calInputIndex = e => {

        if (!this.wrapperEl) {
            return -1;
        }

        const offset = Dom.getOffset(this.wrapperEl),
            {left: minX} = offset,
            wrapperWidth = this.wrapperEl.getBoundingClientRect().width,
            maxX = minX + wrapperWidth;

        let x = e.clientX,
            y = e.clientY,
            inputIndex = -1;

        while (x >= minX) {

            const item = document.elementFromPoint(x, y),
                el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

            if (el) {
                inputIndex = +el.dataset.index + 1;
                break;
            }

            x--;

        }

        if (inputIndex < 0) {
            while (x <= maxX) {

                const item = document.elementFromPoint(x, y),
                    el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

                if (el) {
                    inputIndex = +el.dataset.index;
                    break;
                }

                x++;

            }
        }

        return inputIndex < 0 ? this.state.data.length : inputIndex;

    };

    handleMouseDown = e => {

        if (this.props.disabled || Dom.findParentByClassName(e.target, 'tag-field-item-wrapper')
            || Dom.hasClass(e.target, 'tag-field-input')) {
            return;
        }

        const callback = (function (e) {

            return () => {

                if (this.state.itemEditing
                    || (this.wrapperEl
                        && Dom.findParentByClassName(e.target, 'tag-field') != this.wrapperEl)) {
                    this.setState({
                        inputIndex: this.state.data.length
                    });
                    return;
                }

                let x = e.clientX,
                    y = e.clientY;

                if (!x || !y || !this.wrapperEl) {
                    return;
                }

                const offset = Dom.getOffset(this.wrapperEl);

                if (!offset) {
                    return;
                }

                const inputIndex = this.calInputIndex(e);

                this.setState({
                    inputIndex
                }, () => {
                    setTimeout(() => {
                        this.input && this.input.current && this.input.current.focus();
                        // wrapperEl.scrollLeft = 0;
                        // wrapperEl.scrollTop = 0;
                    }, 0);
                });

            };

        }.bind(this))(e);

        if (this.state.inputValue) {
            this.insertInputValue(callback);
        } else {
            callback();
        }

    };

    handleInputChange = e => {

        if (this.props.disabled) {
            return;
        }

        const value = e.target.value,
            inputValue = value ? value.replace(/\r?\n/gm, '') : value;

        this.setState({
            inputValue
        }, () => {

            const {onInputChange} = this.props,
                width = CharSize.calculateStringWidth(inputValue, this.testEl),
                inputWrapperEl = this.inputWrapper && this.inputWrapper.current,
                inputEl = this.input && this.input.current;

            inputWrapperEl && (inputWrapperEl.style.width = `${width + 9}px`);
            inputEl && (inputEl.style.height = Math.max(inputEl.scrollHeight, 24) + 'px');
            onInputChange && onInputChange(inputValue);

        });

    };

    handleInputKeyDown = e => {

        if (this.props.disabled) {
            return;
        }

        if (e.keyCode === 13) {
            this.insertInputValue();
        }

    };

    insertInputValue = callback => {

        if (this.props.disabled) {
            return;
        }

        const {data, inputValue, inputIndex} = this.state;

        if (!inputValue) {
            return;
        }

        const splitedValue = trim(inputValue).split(this.generateSeparatorReg()).filter(item => !!item);

        data.splice(inputIndex, 0, ...splitedValue);

        this.setState({
            data,
            inputValue: '',
            inputIndex: inputIndex + splitedValue.length
        }, () => {

            this.inputWrapper && this.inputWrapper.current && (this.inputWrapper.current.style.width = '9px');

            const {onChange} = this.props;
            onChange && onChange(data);

            callback && callback();

        });

    };

    handleItemChange = (value, index) => {

        if (this.props.disabled) {
            return;
        }

        const {data} = this.state;

        if (value) {
            const splitedValue = value.split(this.generateSeparatorReg()).filter(item => item);
            data.splice(index, 1, ...splitedValue);
        } else {
            data.splice(index, 1);
        }

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    handleItemEditStart = editingItemIndex => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: true,
            editingItemIndex
        });

    };

    handleItemEditEnd = () => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: false
        });

    };

    componentDidMount() {

        this.wrapperEl = this.wrapper && this.wrapper.current;
        this.testEl = this.test && this.test.current;

        Event.addEvent(document, 'mousedown', this.handleMouseDown);

        this.setState({
            wrapperWidth: this.wrapperEl ? this.wrapperEl.clientWidth : 'auto'
        });

    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousedown', this.handleMouseDown);
    }

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            data: ComponentUtil.getDerivedState(props, state, 'data'),
            inputValue: ComponentUtil.getDerivedState(props, state, 'inputValue')
        };
    }

    render() {

        const {
                className, style, valueField, displayField, disabled,
                placeholder, isTagAutoWidth, tagRemoveIconCls
            } = this.props,
            {wrapperWidth, data, inputValue, inputIndex, itemEditing, editingItemIndex} = this.state,
            indexData = Util.genIndexArray(data.length);

        indexData.splice(inputIndex, 0, this.inputSymbol);

        return (

                {
                    indexData && indexData.map(index => index === this.inputSymbol ?
                        !disabled ?

                                <textarea ref={this.input}
                                          className="tag-field-input"
                                          style={{width: wrapperWidth}}
                                          autoFocus={true}
                                          value={inputValue}
                                          placeholder={data.length < 1 && placeholder ? placeholder : ''}
                                          onChange={this.handleInputChange}
                                          onKeyDown={this.handleInputKeyDown}/>

                            :
                            null
                        :

                            <EditableField className="tag-field-item-field"
                                           value={Util.getTextByDisplayField(data[index], displayField, valueField)}
                                           disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                           autoWidth={isTagAutoWidth}
                                           onChange={value => this.handleItemChange(value, index)}
                                           onEditStart={() => this.handleItemEditStart(index)}
                                           onEditEnd={this.handleItemEditEnd}>
                                <IconButton className="tag-field-item-field-delete-button"
                                            iconCls={tagRemoveIconCls}
                                            disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                            onClick={() => this.removeItem(index)}/>
                            </EditableField>

                    )
                }

        );

    }
}

TagField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    data: PropTypes.array,
    inputValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    valueField: PropTypes.string,
    displayField: PropTypes.string,
    separators: PropTypes.array,

    disabled: PropTypes.bool,
    placeholder: PropTypes.string,
    isTagAutoWidth: PropTypes.bool,
    tagRemoveIconCls: PropTypes.string,

    onChange: PropTypes.func,
    onInputChange: PropTypes.func

};

TagField.defaultProps = {

    data: [],
    inputValue: '',

    valueField: 'value',
    displayField: 'text',
    separators: TagField.DEFAULT_SEPARATORS,

    disabled: false,

    placeholder: '',
    isTagAutoWidth: true,
    tagRemoveIconCls: 'fas fa-times'

};

export default TagField;
<commit_msg>TagField bug fixed<commit_after>/**
 * @file TagField component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

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

import EditableField from '../EditableField';
import IconButton from '../IconButton';

import Util from '../_vendors/Util';
import Dom from '../_vendors/Dom';
import CharSize from '../_vendors/CharSize';
import Event from '../_vendors/Event';
import ComponentUtil from '../_vendors/ComponentUtil';

class TagField extends Component {

    static DEFAULT_SEPARATORS = [','];

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.inputSymbol = Symbol('input');
        this.wrapper = createRef();
        this.wrapperEl = null;
        this.inputWrapper = createRef();
        this.input = createRef();
        this.test = createRef();
        this.testEl = null;

        this.state = {
            wrapperWidth: 'auto',
            data: props.data,
            inputValue: props.inputValue,
            inputIndex: props.data.length,
            itemEditing: false,
            editingItemIndex: -1
        };

    }

    getSeparators = (separators = this.props.separators) => {

        if (separators && separators.length > 0) {
            return separators;
        }

        return DEFAULT_SEPARATORS;

    };

    generateSeparatorReg = (separators = this.props.separators) => {
        const seps = this.getSeparators(separators);
        return new RegExp(`\\s*${seps ? seps.map(sep => `\\${sep}`).join('|') : ''}\\s*`, 'g');
    };

    removeItem = index => {

        const {data} = this.state;

        if (!data || data.length < 1 || !(index in data)) {
            return;
        }

        data.splice(index, 1);

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    calInputIndex = e => {

        if (!this.wrapperEl) {
            return -1;
        }

        const offset = Dom.getOffset(this.wrapperEl),
            {left: minX} = offset,
            wrapperWidth = this.wrapperEl.getBoundingClientRect().width,
            maxX = minX + wrapperWidth;

        let x = e.clientX,
            y = e.clientY,
            inputIndex = -1;

        while (x >= minX) {

            const item = document.elementFromPoint(x, y),
                el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

            if (el) {
                inputIndex = +el.dataset.index + 1;
                break;
            }

            x--;

        }

        if (inputIndex < 0) {
            while (x <= maxX) {

                const item = document.elementFromPoint(x, y),
                    el = Dom.findParentByClassName(item, 'tag-field-item-wrapper');

                if (el) {
                    inputIndex = +el.dataset.index;
                    break;
                }

                x++;

            }
        }

        return inputIndex < 0 ? this.state.data.length : inputIndex;

    };

    handleMouseDown = e => {

        if (this.props.disabled || Dom.findParentByClassName(e.target, 'tag-field-item-wrapper')
            || Dom.hasClass(e.target, 'tag-field-input')) {
            return;
        }

        const callback = (function (e) {

            return () => {

                if (this.state.itemEditing
                    || (this.wrapperEl
                        && Dom.findParentByClassName(e.target, 'tag-field') != this.wrapperEl)) {
                    this.setState({
                        inputIndex: this.state.data.length
                    });
                    return;
                }

                let x = e.clientX,
                    y = e.clientY;

                if (!x || !y || !this.wrapperEl) {
                    return;
                }

                const offset = Dom.getOffset(this.wrapperEl);

                if (!offset) {
                    return;
                }

                const inputIndex = this.calInputIndex(e);

                this.setState({
                    inputIndex
                }, () => {
                    setTimeout(() => {
                        this.input && this.input.current && this.input.current.focus();
                        // wrapperEl.scrollLeft = 0;
                        // wrapperEl.scrollTop = 0;
                    }, 0);
                });

            };

        }.bind(this))(e);

        if (this.state.inputValue) {
            this.insertInputValue(callback);
        } else {
            callback();
        }

    };

    handleInputChange = e => {

        if (this.props.disabled) {
            return;
        }

        const value = e.target.value,
            inputValue = value ?
                value.replace(/\r?\n$/gm, '') // remove last \r\n
                     .replace(/\r?\n/gm, this.getSeparators()[0])
                :
                value;

        this.setState({
            inputValue
        }, () => {

            const {onInputChange} = this.props,
                width = CharSize.calculateStringWidth(inputValue, this.testEl),
                inputWrapperEl = this.inputWrapper && this.inputWrapper.current,
                inputEl = this.input && this.input.current;

            inputWrapperEl && (inputWrapperEl.style.width = `${width + 9}px`);
            inputEl && (inputEl.style.height = Math.max(inputEl.scrollHeight, 24) + 'px');
            onInputChange && onInputChange(inputValue);

        });

    };

    handleInputKeyDown = e => {

        if (this.props.disabled) {
            return;
        }

        if (e.keyCode === 13) {
            this.insertInputValue();
        }

    };

    insertInputValue = callback => {

        if (this.props.disabled) {
            return;
        }

        const {data, inputValue, inputIndex} = this.state;

        if (!inputValue) {
            return;
        }

        const splitedValue = trim(inputValue).split(this.generateSeparatorReg()).filter(item => !!item);

        data.splice(inputIndex, 0, ...splitedValue);

        this.setState({
            data,
            inputValue: '',
            inputIndex: inputIndex + splitedValue.length
        }, () => {

            this.inputWrapper && this.inputWrapper.current && (this.inputWrapper.current.style.width = '9px');

            const {onChange} = this.props;
            onChange && onChange(data);

            callback && callback();

        });

    };

    handleItemChange = (value, index) => {

        if (this.props.disabled) {
            return;
        }

        const {data} = this.state;

        if (value) {
            const splitedValue = value.split(this.generateSeparatorReg()).filter(item => item);
            data.splice(index, 1, ...splitedValue);
        } else {
            data.splice(index, 1);
        }

        this.setState({
            data
        }, () => {
            const {onChange} = this.props;
            onChange && onChange(data);
        });

    };

    handleItemEditStart = editingItemIndex => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: true,
            editingItemIndex
        });

    };

    handleItemEditEnd = () => {

        if (this.props.disabled) {
            return;
        }

        this.setState({
            itemEditing: false
        });

    };

    componentDidMount() {

        this.wrapperEl = this.wrapper && this.wrapper.current;
        this.testEl = this.test && this.test.current;

        Event.addEvent(document, 'mousedown', this.handleMouseDown);

        this.setState({
            wrapperWidth: this.wrapperEl ? this.wrapperEl.clientWidth : 'auto'
        });

    }

    componentWillUnmount() {
        Event.removeEvent(document, 'mousedown', this.handleMouseDown);
    }

    static getDerivedStateFromProps(props, state) {
        return {
            prevProps: props,
            data: ComponentUtil.getDerivedState(props, state, 'data'),
            inputValue: ComponentUtil.getDerivedState(props, state, 'inputValue')
        };
    }

    render() {

        const {
                className, style, valueField, displayField, disabled,
                placeholder, isTagAutoWidth, tagRemoveIconCls
            } = this.props,
            {wrapperWidth, data, inputValue, inputIndex, itemEditing, editingItemIndex} = this.state,
            indexData = Util.genIndexArray(data.length);

        indexData.splice(inputIndex, 0, this.inputSymbol);

        return (

                {
                    indexData && indexData.map(index => index === this.inputSymbol ?
                        !disabled ?

                                <textarea ref={this.input}
                                          className="tag-field-input"
                                          style={{width: wrapperWidth}}
                                          autoFocus={true}
                                          value={inputValue}
                                          placeholder={data.length < 1 && placeholder ? placeholder : ''}
                                          onChange={this.handleInputChange}
                                          onKeyDown={this.handleInputKeyDown}/>

                            :
                            null
                        :

                            <EditableField className="tag-field-item-field"
                                           value={Util.getTextByDisplayField(data[index], displayField, valueField)}
                                           disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                           autoWidth={isTagAutoWidth}
                                           onChange={value => this.handleItemChange(value, index)}
                                           onEditStart={() => this.handleItemEditStart(index)}
                                           onEditEnd={this.handleItemEditEnd}>
                                <IconButton className="tag-field-item-field-delete-button"
                                            iconCls={tagRemoveIconCls}
                                            disabled={disabled || (itemEditing && index !== editingItemIndex)}
                                            onClick={() => this.removeItem(index)}/>
                            </EditableField>

                    )
                }

        );

    }
}

TagField.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    data: PropTypes.array,
    inputValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    valueField: PropTypes.string,
    displayField: PropTypes.string,
    separators: PropTypes.array,

    disabled: PropTypes.bool,
    placeholder: PropTypes.string,
    isTagAutoWidth: PropTypes.bool,
    tagRemoveIconCls: PropTypes.string,

    onChange: PropTypes.func,
    onInputChange: PropTypes.func

};

TagField.defaultProps = {

    data: [],
    inputValue: '',

    valueField: 'value',
    displayField: 'text',
    separators: TagField.DEFAULT_SEPARATORS,

    disabled: false,

    placeholder: '',
    isTagAutoWidth: true,
    tagRemoveIconCls: 'fas fa-times'

};

export default TagField;
<|endoftext|>


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

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

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

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

export default class DraggableGridItem 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,
        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,

        anchorIconCls: PropTypes.string,

        col: PropTypes.number,

        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,
        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',

        anchorIconCls: 'fa fa-bars',

        col: 3

    };

    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, renderer, itemRenderer, readOnly, col, anchorIconCls,

                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 (
            <Draggable key={index}
                       draggableId={index}>
                {
                    dragProvided => (
                        <div className="draggable-grid-item-drag-provided-wrapper"
                             style={col ? {width: `${100 / col}%`} : null}>

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

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

                                            {
                                                selectMode === SelectMode.MULTI_SELECT ?
                                                    <Checkbox className="draggable-grid-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
                                                    )
                                            }

                                            <i className={`${anchorIconCls} draggable-grid-item-anchor`}
                                               aria-hidden="true">

                                    </TipProvider>

                            {dragProvided.placeholder}

                    )
                }
            </Draggable>
        );

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

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

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

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

class DraggableGridItem 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, renderer, itemRenderer, readOnly, col, anchorIconCls,

                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 (
            <Draggable key={index}
                       draggableId={index}>
                {
                    dragProvided => (
                        <div className="draggable-grid-item-drag-provided-wrapper"
                             style={col ? {width: `${100 / col}%`} : null}>

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

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

                                            {
                                                selectMode === SelectMode.MULTI_SELECT ?
                                                    <Checkbox className="draggable-grid-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
                                                    )
                                            }

                                            <i className={`${anchorIconCls} draggable-grid-item-anchor`}
                                               aria-hidden="true">

                                    </TipProvider>

                            {dragProvided.placeholder}

                    )
                }
            </Draggable>
        );

    }
};

DraggableGridItem.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,
    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,

    anchorIconCls: PropTypes.string,

    col: PropTypes.number,

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

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

};

DraggableGridItem.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,
    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',

    anchorIconCls: 'fa fa-bars',

    col: 3

};

export default DraggableGridItem;<|endoftext|>