From ac7fb3291db1f2406252746232e15270322427c4 Mon Sep 17 00:00:00 2001 From: Merve7 Date: Wed, 12 Jul 2017 12:10:59 +0300 Subject: [PATCH] Fixed #42 --- src/components/dropdown/Dropdown.js | 176 ++++++++++--- src/showcase/dropdown/DropdownDemo.js | 365 +++++++++++++++----------- 2 files changed, 357 insertions(+), 184 deletions(-) diff --git a/src/components/dropdown/Dropdown.js b/src/components/dropdown/Dropdown.js index bfd72f69df..c4d0207025 100644 --- a/src/components/dropdown/Dropdown.js +++ b/src/components/dropdown/Dropdown.js @@ -14,7 +14,12 @@ export class Dropdown extends Component { style: null, className: null, autoWidth: true, - scrollHeight: '200px' + scrollHeight: '200px', + filter:false, + filterBy:null, + filterPlaceholder:null, + editable:false, + placeholder:null }; static propTypes = { @@ -25,13 +30,24 @@ export class Dropdown extends Component { style: PropTypes.object, className: PropTypes.string, autoWidth: PropTypes.bool, - scrollHeight: PropTypes.string + scrollHeight: PropTypes.string, + filter:PropTypes.bool, + filterBy: PropTypes.string, + filterPlaceholder: PropTypes.string, + editable:PropTypes.bool, + placeholder: PropTypes.string }; constructor(props) { super(props); this.state = {focus: false}; this.onClick = this.onClick.bind(this); + this.onFilterInputClick=this.onFilterInputClick.bind(this) + this.onInputBlur=this.onInputBlur.bind(this); + this.onInputFocus=this.onInputFocus.bind(this); + this.onKeydown=this.onKeydown.bind(this); + this.onEditableInputClick=this.onEditableInputClick.bind(this); + this.onEditableInputFocus=this.onEditableInputFocus.bind(this); } componentDidMount() { @@ -49,28 +65,28 @@ export class Dropdown extends Component { if(this.documentClickListener) { document.removeEventListener('click', this.documentClickListener); } - } + } onDocumentClick() { - if(!this.selfClick&&!this.itemClick) { + if(!this.selfClick&&!this.itemClick && !this.filterClick) { this.hide(); } - + this.filterClick=false; this.selfClick = false; this.optionClick = false; } onOptionClick(event, option, index) { this.optionClick = true; - this.highlightOption = option; - this.selectItem(event, option, index); + this.highlightOption=option; + this.selectItem(event, option, index); this.hide(); this.input.focus(); event.preventDefault(); } selectItem(event, option, index) { - if(!DomHandler.hasClass(event.target,'ui-state-highlight')) { + if(this.findSelectedOption()!==option){ this.props.onChange({ originalEvent: event, value: option.value, @@ -79,7 +95,7 @@ export class Dropdown extends Component { } } - onClick() { + onClick(event) { if(this.props.disabled) { return; } @@ -87,11 +103,22 @@ export class Dropdown extends Component { this.selfClick = true; if(!this.optionClick) { - this.input.focus(); - if(this.panel.offsetParent) + if(this.props.filter){ + this.filterInput.focus(); + } + + if(this.panel.offsetParent){ + this.input.focus(); this.hide(); - else + } + else { this.show(); + if (this.props.filter) { + setTimeout(() => { + this.filterInput.focus(); + }, 200); + } + } } } @@ -105,7 +132,7 @@ export class Dropdown extends Component { } } } - + return selectedOption; } @@ -115,7 +142,9 @@ export class Dropdown extends Component { DomHandler.relativePosition(this.panel, this.container); DomHandler.fadeIn(this.panel, 250); this.panel.style.display = 'block'; + this.bindDocumentClickListener(); } + this.input.focus(); } hide() { @@ -125,7 +154,7 @@ export class Dropdown extends Component { onInputFocus(event) { this.setState({focus: true}); } - + onInputBlur(event) { this.setState({focus: false}); } @@ -144,7 +173,7 @@ export class Dropdown extends Component { } onKeydown(event) { - let highlightItemIndex = this.highlightOption ? this.findOptionIndex(this.highlightOption) : 0; + let highlightItemIndex =this.highlightOption? this.findOptionIndex(this.highlightOption):0; switch(event.which) { //down @@ -196,6 +225,11 @@ export class Dropdown extends Component { event.preventDefault(); break; + //tab + case 9: + this.hide(); + break; + //space case 32: if(this.panel.style.display==='block') { @@ -209,13 +243,70 @@ export class Dropdown extends Component { } event.preventDefault(); break; - default: break; - } this.setState({highlightOption: this.highlightOption}); } + onFilter(event) { + var inputValue = event.target.value.toLowerCase(); + if(inputValue && inputValue.length) { + this.filterValue = inputValue; + this.activateFilter(); + } + else { + this.filterValue = null; + this.setState({filteredOption: this.props.options}) + } + } + + activateFilter() { + var searchFields = this.props.filterBy.split(','); + if(this.props.options && this.props.options.length) { + this.setState({filteredOption: ObjectUtils.filter(this.props.options, searchFields, this.filterValue)}) + } + } + + onFilterInputClick(event){ + this.filterClick=true; + event.stopPropagation(); + } + bindDocumentClickListener() { + if(!this.documentClickListener) { + this.documentClickListener = document.addEventListener('click', () => { + if(!this.selfClick&&!this.itemClick) { + this.panel.style.display = 'none'; + this.unbindDocumentClickListener(); + } + + this.selfClick = false; + this.itemClick = false; + }); + } + } + unbindDocumentClickListener() { + if(this.documentClickListener) { + this.documentClickListener(); + this.documentClickListener = null; + } + } + onEditableInputFocus(event) { + this.setState({focus:true}); + this.hide(); + } + + onEditableInputClick(event) { + this.optionClick = false; + event.stopPropagation(); + } + + onEditable(event){ + this.editValue=event.target.value; + this.props.onChange({ + originalEvent: event, + value: this.editValue + }); + } render() { var styleClass = classNames('ui-dropdown ui-widget ui-state-default ui-corner-all', this.props.className, { @@ -226,41 +317,64 @@ export class Dropdown extends Component { var selectedOption = this.findSelectedOption(); var label = selectedOption ? selectedOption.label : (this.props.options ? this.props.options[0].label : null); var listItems, optionElements; + var filterInput,filter; + var editable; if(this.props.options) { - listItems = this.props.options.map((option, index) => { - var listItemContent = this.props.itemTemplate ? this.props.itemTemplate(option) : option.label; - var selected = (this.props.value != null && this.props.value === option.value) || (this.props.value == null && index === 0) || this.state.highlightOption === option; - var listItemStyleClass = classNames('ui-dropdown-item ui-corner-all', {'ui-state-highlight': selected}); - var listItem =
  • this.onOptionClick(event, option)}> - {listItemContent} -
  • ; - - return listItem; - }); + + var listItemContent; + var optionMap=this.state.filteredOption?this.state.filteredOption:this.props.options; + + listItems = optionMap.map((option, index) => { + listItemContent =this.props.itemTemplate ? this.props.itemTemplate(option) : option.label; + var selected = (this.props.value !== null && this.props.value === option.value) || (this.props.value === null && index === 0); + var listItemStyleClass = classNames('ui-dropdown-item ui-corner-all', {'ui-state-highlight': selected || this.state.highlightOption === option}); + var listItem =
  • this.onOptionClick(event, option)} + > + {listItemContent} +
  • ; + + return listItem; + }) optionElements = this.props.options.map((option, index) => { return ; }); } + if(this.props.filter){ + filterInput= {this.filterInput = el;}} placeholder={this.props.filterPlaceholder} + onKeyDown={event=>event.which===13?event.preventDefault():null} /> + filter=
    this.onFilter(event)} + onClick={this.onFilterInputClick} >{filterInput}
    + } + if(this.props.editable){ + editable= {this.editableInput = el;}} onChange={(event) => this.onEditable(event)} + onClick={this.onEditableInputClick} placeholder={this.props.placeholder} + onFocus={this.onEditableInputFocus} onBlur={this.onInputBlur} /> + } + return (
    {this.container = el;}} style={this.props.style}>
    - {this.input = el;}} type="text" onFocus={this.onInputFocus.bind(this)} onKeyDown={this.onKeydown.bind(this)} onBlur={this.onInputBlur.bind(this)}/> + {this.input = el;}} type="text" onFocus={this.onInputFocus} + onKeyDown={this.onKeydown} onBlur={this.onInputBlur}/>
    - + {editable} + {!this.props.editable && }
    {this.panel = el;}}> + {filter}
      - {listItems} + {listItems}
    diff --git a/src/showcase/dropdown/DropdownDemo.js b/src/showcase/dropdown/DropdownDemo.js index c20f916f1b..4275adde5d 100644 --- a/src/showcase/dropdown/DropdownDemo.js +++ b/src/showcase/dropdown/DropdownDemo.js @@ -5,12 +5,13 @@ import {TabView,TabPanel} from '../../components/tabview/TabView'; import {CodeHighlight} from '../../components/codehighlight/CodeHighlight'; export class DropdownDemo extends Component { - + constructor() { super(); this.state = {}; this.onCityChange = this.onCityChange.bind(this); this.onCarChange = this.onCarChange.bind(this); + this.onCarChange2 = this.onCarChange2.bind(this); } onCityChange(e) { @@ -21,6 +22,10 @@ export class DropdownDemo extends Component { this.setState({car: e.value}); } + onCarChange2(e) { + this.setState({car2: e.value}); + } + carTemplate(option) { if(!option.value) { return option.label; @@ -48,7 +53,6 @@ export class DropdownDemo extends Component { ]; var cars = [ - {label: 'Select Car', value: null}, {label: 'Audi', value: 'Audi'}, {label: 'BMW', value: 'BMW'}, {label: 'Fiat', value: 'Fiat'}, @@ -74,9 +78,15 @@ export class DropdownDemo extends Component {
    {this.state.city ? 'Selected City: ' + this.state.city : 'No city selected'}
    -

    Advanced

    - +

    Editable

    +
    {this.state.car ? 'Selected Car: ' + this.state.car : 'No car selected'}
    + +

    Advanced

    + +
    {this.state.car2 ? 'Selected Car: ' + this.state.car2 : 'No car selected'}
    @@ -93,48 +103,38 @@ class DropdownDoc extends Component {

    Import

    - -{` + + {` import {Dropdown} from 'primereact/components/dropdown/Dropdown'; `} - +

    Getting Started

    Dropdown requires a collection of options with label-value pairs and an onChange event to provide the selected value.

    - -{` + + {` `} - + - -{` + + {` constructor() { super(); - this.state = {}; - this.onCarChange = this.onCarChange.bind(this); -} - -onCarChange(e) { - this.setState({car: e.value}); + this.state = {cities: []}; + this.onCityChange = this.onCityChange.bind(this); } -carTemplate(option) { - if(!option.value) { - return option.label; - } - else { - var logoPath = 'showcase/resources/demo/images/car/' + option.label + '.gif'; +onCityChange(e) { + var selectedCities = [...this.state.cities]; + if(e.checked) + selectedCities.push(e.value); + else + selectedCities.splice(selectedCities.indexOf(e.value), 1); - return ( -
    - {option.label} - {option.label} -
    - ); - } + this.setState({cities: selectedCities}); } render() { @@ -151,33 +151,54 @@ render() { } `} -
    +
    + +

    Filtering

    +

    Options can be filtered using an input field in the overlay by enabling the filter property. By default filtering is done against + label of the SelectItem and filterBy property is available to choose one or more properties of the SelectItem API.

    + + {` + + +`} +

    Custom Content

    Label of an option is used as the display text of an item by default, for custom content support define an itemTemplate fucntion that gets the SelectItem as a property and returns the content.

    - -{` + + {` `} - + + + + {` - -{` constructor() { super(); - this.state = {cities: []}; - this.onCityChange = this.onCityChange.bind(this); + this.state = {}; + this.onCarChange = this.onCarChange.bind(this); } -onCityChange(e) { - var selectedCities = [...this.state.cities]; - if(e.checked) - selectedCities.push(e.value); - else - selectedCities.splice(selectedCities.indexOf(e.value), 1); +onCarChange(e) { + this.setState({car: e.value}); +} - this.setState({cities: selectedCities}); +carTemplate(option) { + if(!option.value) { + return option.label; + } + else { + var logoPath = 'showcase/resources/demo/images/car/' + option.label + '.gif'; + + return ( +
    + {option.label} + {option.label} +
    + ); + } } render() { @@ -198,62 +219,92 @@ render() { } `} -
    +

    Attributes

    - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameTypeDefaultDescription
    NameTypeDefaultDescription
    valueanynullValue of the component.
    optionsarraynullAn array of selectitems to display as the available options.
    scrollHeightstring200pxHeight of the viewport in pixels, a scrollbar is defined if height of list exceeds this value.
    stylestringnullInline style of the element.
    classNamestringnullStyle class of the element.
    autoWidthbooleantrueCalculates the width based on options width, set to false for custom width.
    itemTemplatefunctionnullFunction that gets the option and returns the content for it.
    valueanynullValue of the component.
    optionsarraynullAn array of selectitems to display as the available options.
    scrollHeightstring200pxHeight of the viewport in pixels, a scrollbar is defined if height of list exceeds this value.
    stylestringnullInline style of the element.
    classNamestringnullStyle class of the element.
    autoWidthbooleantrueCalculates the width based on options width, set to false for custom width.
    itemTemplatefunctionnullFunction that gets the option and returns the content for it.
    filterbooleanfalseWhen specified, displays an input field to filter the items on keyup.
    filterBystringnullWhen filtering is enabled, filterBy decides which field or fields (comma separated) to search against.
    filterPlaceholderstringnullPlaceholder text to show when filter input is empty.
    editablebooleanfalseWhen present, custom value instead of predefined options can be entered using the editable input field.
    placeholderstringnullDefault text to display when no option is selected.
    @@ -269,13 +320,13 @@ render() { - - onChange - event.originalEvent: Original event
    - event.value: Value of the checkbox
    - event.checked: Checked state as a boolean. - Callback to invoke on value change - + + onChange + event.originalEvent: Original event
    + event.value: Value of the checkbox
    + event.checked: Checked state as a boolean. + Callback to invoke on value change + @@ -285,52 +336,52 @@ render() {
    - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameElement
    NameElement
    ui-dropdownContainer element.
    ui-dropdown-labelElement to display label of selected option.
    ui-dropdown-triggerIcon element.
    ui-dropdown-panelIcon element.
    ui-dropdown-items-wrapperWrapper element of items list.
    ui-dropdown-itemsList element of items.
    ui-dropdown-itemAn item in the list.
    ui-dropdown-filter-containerContainer of filter input.
    ui-dropdown-filterFilter element.
    ui-dropdown-openContainer element when overlay is visible.
    ui-dropdownContainer element.
    ui-dropdown-labelElement to display label of selected option.
    ui-dropdown-triggerIcon element.
    ui-dropdown-panelIcon element.
    ui-dropdown-items-wrapperWrapper element of items list.
    ui-dropdown-itemsList element of items.
    ui-dropdown-itemAn item in the list.
    ui-dropdown-filter-containerContainer of filter input.
    ui-dropdown-filterFilter element.
    ui-dropdown-openContainer element when overlay is visible.
    @@ -340,15 +391,16 @@ render() {
    - -{` + + {` export class DropdownDemo extends Component { - + constructor() { super(); this.state = {}; this.onCityChange = this.onCityChange.bind(this); this.onCarChange = this.onCarChange.bind(this); + this.onCarChange2 = this.onCarChange2.bind(this); } onCityChange(e) { @@ -359,6 +411,10 @@ export class DropdownDemo extends Component { this.setState({car: e.value}); } + onCarChange2(e) { + this.setState({car2: e.value}); + } + carTemplate(option) { if(!option.value) { return option.label; @@ -386,7 +442,6 @@ export class DropdownDemo extends Component { ]; var cars = [ - {label: 'Select Car', value: null}, {label: 'Audi', value: 'Audi'}, {label: 'BMW', value: 'BMW'}, {label: 'Fiat', value: 'Fiat'}, @@ -412,9 +467,13 @@ export class DropdownDemo extends Component {
    {this.state.city ? 'Selected City: ' + this.state.city : 'No city selected'}
    -

    Advanced

    - +

    Editable

    +
    {this.state.car ? 'Selected Car: ' + this.state.car : 'No car selected'}
    + +

    Advanced

    + +
    {this.state.car2 ? 'Selected Car: ' + this.state.car2 : 'No car selected'}
    @@ -424,7 +483,7 @@ export class DropdownDemo extends Component { } `} -
    +