basic refactor

This commit is contained in:
Víctor Losada Hernández
2026-10-29 14:55:44 +01:00
parent af97716d28
commit 302373e19c
+91 -97
View File
@@ -1,117 +1,113 @@
import React from 'react'; import React, { useState, useEffect, useRef } from 'react';
import createReactClass from 'create-react-class';
import _ from 'lodash'; import _ from 'lodash';
import './combobox.less'; import './combobox.less';
const Combobox = createReactClass({ const Combobox = ({
displayName : 'Combobox', id = '',
getDefaultProps : function() { className = '',
return { trigger = 'hover',
id : '', default: defaultValue = '',
className : '', placeholder = '',
trigger : 'hover', tooltip = '',
default : '', autoSuggest = {
placeholder : '',
tooltip : '',
autoSuggest : {
clearAutoSuggestOnClick : true, clearAutoSuggestOnClick : true,
suggestMethod : 'includes', suggestMethod : 'includes',
filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter filterOn : [] // should allow as array to filter on multiple attributes, or even custom filter
}, },
valuePatterns : /.+/ valuePatterns = /.+/,
}; ...props
}, })=>{
getInitialState : function() {
this.dropdownRef = React.createRef(); const dropdownRef = useRef(null);
return { const [showDropdown, setShowDropdown] = useState(false);
showDropdown : false, const [value, setValue] = useState('');
value : '', const [options, setOptions] = useState([...props.options]);
options : [...this.props.options], const [inputFocused, setInputFocused] = useState(false);
inputFocused : false
};
}, useEffect(()=>{
componentDidMount : function() { if(trigger == 'click') {
if(this.props.trigger == 'click') document.addEventListener('click', handleClickOutside);
document.addEventListener('click', this.handleClickOutside);
this.setState({
value : this.props.default
});
},
componentWillUnmount : function() {
if(this.props.trigger == 'click')
document.removeEventListener('click', this.handleClickOutside);
},
handleClickOutside : function(e){
// Close dropdown when clicked outside
if(this.dropdownRef.current && !this.dropdownRef.current.contains(e.target)) {
this.handleDropdown(false);
} }
}, setValue(defaultValue);
handleDropdown : function(show){
this.setState({ return ()=>{
value : show ? '' : this.props.default, if(trigger == 'click') {
showDropdown : show, document.removeEventListener('click', handleClickOutside);
inputFocused : this.props.autoSuggest.clearAutoSuggestOnClick ? show : false }
}); };
}, }, []);
handleInput : function(e){
e.persist(); const handleClickOutside = (e)=>{
this.setState({ // Close dropdown when clicked outside
value : e.target.value, if(dropdownRef.current && !dropdownRef.current.contains(e.target)) {
inputFocused : false handleDropdown(false);
}, ()=>{ }
this.props.onEntry(e); };
});
}, const handleDropdown = (show)=>{
handleSelect : function(value, data=value){ setValue(show ? '' : defaultValue);
this.setState({ setShowDropdown(show);
value : value setInputFocused(autoSuggest.clearAutoSuggestOnClick ? show : false);
}, ()=>{this.props.onSelect(data);}); };
;
}, const handleInput = (e)=>{
renderTextInput : function(){ setValue(e.target.value);
setInputFocused(false);
props.onEntry(e);
};
const handleSelect = (value, data=value)=>{
setValue(value);
props.onSelect(data);
};
const renderTextInput = ()=>{
return ( return (
<div className='dropdown-input item' <div className='dropdown-input item'
onMouseEnter={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(true);} : undefined} onMouseEnter={trigger == 'hover' ? ()=>{handleDropdown(true);} : undefined}
onClick= {this.props.trigger == 'click' ? ()=>{this.handleDropdown(true);} : undefined} onClick= {trigger == 'click' ? ()=>{handleDropdown(true);} : undefined}
{...(this.props.tooltip ? { 'data-tooltip-right': this.props.tooltip } : {})}> {...(tooltip ? { 'data-tooltip-right': tooltip } : {})}>
<input <input
id={this.props.id} id={id}
type='text' type='text'
onChange={(e)=>this.handleInput(e)} onChange={(e)=>handleInput(e)}
value={this.state.value || ''} value={value || ''}
title='' title=''
pattern={this.props.valuePatterns} pattern={valuePatterns}
placeholder={this.props.placeholder} placeholder={placeholder}
onBlur={(e)=>{ onBlur={(e)=>{
if(!e.target.checkValidity()){ if(!e.target.checkValidity()){
this.setState({ setValue(defaultValue);
value : this.props.default
});
} }
}} }}
onKeyDown={(e)=>{ onKeyDown={(e)=>{
if(e.key === 'Enter') { if(e.key === 'Enter') {
e.preventDefault(); e.preventDefault();
this.props.onEntry(e); props.onEntry(e);
} }
}} }}
/> />
<i className='fas fa-caret-down'/> <i className='fas fa-caret-down'/>
</div> </div>
); );
}, };
renderDropdown : function(dropdownChildren){
if(!this.state.showDropdown) return null; const renderDropdown = ()=>{
if(this.props.autoSuggest && !this.state.inputFocused){ if(!showDropdown) return null;
const suggestMethod = this.props.autoSuggest.suggestMethod; const dropdownChildren = options.map((child, i)=>{
const filterOn = _.isString(this.props.autoSuggest.filterOn) ? [this.props.autoSuggest.filterOn] : this.props.autoSuggest.filterOn; const clone = React.cloneElement(child, { onClick: ()=>handleSelect(child.props.value, child.props.data) });
return clone;
});
if(autoSuggest && !inputFocused){
const suggestMethod = autoSuggest.suggestMethod;
const filterOn = _.isString(autoSuggest.filterOn) ? [autoSuggest.filterOn] : autoSuggest.filterOn;
const filteredArrays = filterOn.map((attr)=>{ const filteredArrays = filterOn.map((attr)=>{
const children = dropdownChildren.filter((item)=>{ const children = dropdownChildren.filter((item)=>{
if(suggestMethod === 'includes') if(suggestMethod === 'includes')
return item.props[attr]?.toLowerCase().includes(this.state.value.toLowerCase()); return item.props[attr]?.toLowerCase().includes(value.toLowerCase());
if(suggestMethod === 'startsWith') if(suggestMethod === 'startsWith')
return item.props[attr]?.toLowerCase().startsWith(this.state.value.toLowerCase()); return item.props[attr]?.toLowerCase().startsWith(value.toLowerCase());
}); });
return children; return children;
}); });
@@ -123,21 +119,19 @@ const Combobox = createReactClass({
{dropdownChildren} {dropdownChildren}
</div> </div>
); );
}, };
render : function () {
const dropdownChildren = this.state.options.map((child, i)=>{
const clone = React.cloneElement(child, { onClick: ()=>this.handleSelect(child.props.value, child.props.data) });
return clone;
});
return ( return (
<div className={`dropdown-container ${this.props.className}`} <div className={`dropdown-container ${className}`}
ref={this.dropdownRef} ref={dropdownRef}
onMouseLeave={this.props.trigger == 'hover' ? ()=>{this.handleDropdown(false);} : undefined}> onMouseLeave={trigger == 'hover' ? ()=>{handleDropdown(false);} : undefined}>
{this.renderTextInput()} {renderTextInput()}
{this.renderDropdown(dropdownChildren)} {renderDropdown()}
</div> </div>
); );
}
});
};
export default Combobox; export default Combobox;