moved to newest version of webpack and babel

This commit is contained in:
andrelandgraf 2019-06-12 23:55:00 +02:00
parent 223fe7400b
commit 9b27332323
9 changed files with 2851 additions and 3094 deletions

View File

@ -1,7 +1,4 @@
{ {
"presets": ["env"], "presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": [ "plugins": ["@babel/plugin-proposal-class-properties"]
"transform-object-rest-spread",
"transform-react-jsx"
]
} }

14
dist/main.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,40 +0,0 @@
.datalist-input {
/*the container must be positioned relative:*/
position: relative;
display: inline-block;
width: 100%;
height: 100%;
}
.autocomplete-input {
border: 1px solid transparent;
background-color: #f1f1f1;
width: 100%;
height: 100%;
}
.datalist-items {
position: absolute;
border: 1px solid #d4d4d4;
border-bottom: none;
border-top: none;
z-index: 99;
/*position the autocomplete items to be the same width as the container:*/
top: 100%;
left: 0;
right: 0;
}
.datalist-items div {
padding: 10px;
cursor: pointer;
background-color: #fff;
border-bottom: 1px solid #d4d4d4;
}
.datalist-items div:hover {
/*when hovering an item:*/
background-color: #e9e9e9;
}
.datalist-active-item {
/*when navigating through the items using the arrow keys:*/
background-color: DodgerBlue !important;
color: #ffffff;
}

View File

@ -1,235 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import './input.scss';
class DataListInput extends React.Component {
constructor( props ) {
super( props );
this.state = {
/* last valid item that was selected from the drop down menu */
lastValidItem: undefined,
/* current input text */
currentInput: '',
/* current set of matching items */
matchingItems: [],
/* visibility property of the drop down menu */
visible: false,
/* index of the currently focused item in the drop down menu */
focusIndex: 0,
};
}
/**
* gets called when someone starts to write in the input field
* @param value
*/
onHandleInput = ( event ) => {
const currentInput = event.target.value;
const { items, match } = this.props;
const matchingItems = items.filter( ( item ) => {
if ( typeof ( match ) === typeof ( Function ) ) { return match( currentInput, item ); }
return this.match( currentInput, item );
} );
this.setState( {
currentInput,
matchingItems,
focusIndex: 0,
visible: true,
} );
};
/**
* default function for matching the current input value (needle)
* and the values of the items array
* @param currentInput
* @param item
* @returns {boolean}
*/
match = ( currentInput, item ) => item
.label.substr( 0, currentInput.length ).toUpperCase() === currentInput.toUpperCase();
/**
* function for getting the index of the currentValue inside a value of the values array
* @param currentInput
* @param item
* @returns {number}
*/
indexOfMatch = ( currentInput, item ) => item
.label.toUpperCase().indexOf( currentInput.toUpperCase() );
/**
* handle key events
* @param event
*/
onHandleKeydown = ( event ) => {
const { visible, focusIndex, matchingItems } = this.state;
// only do something if drop-down div is visible
if ( !visible ) return;
let currentFocusIndex = focusIndex;
if ( event.keyCode === 40 || event.keyCode === 9 ) {
// If the arrow DOWN key or tab is pressed increase the currentFocus variable:
currentFocusIndex += 1;
if ( currentFocusIndex >= matchingItems.length ) currentFocusIndex = 0;
this.setState( {
focusIndex: currentFocusIndex,
} );
// prevent tab to jump to the next input field if drop down is still open
event.preventDefault();
} else if ( event.keyCode === 38 ) {
// If the arrow UP key is pressed, decrease the currentFocus variable:
currentFocusIndex -= 1;
if ( currentFocusIndex <= -1 ) currentFocusIndex = matchingItems.length - 1;
this.setState( {
focusIndex: currentFocusIndex,
} );
} else if ( event.keyCode === 13 ) {
// Enter pressed, similar to onClickItem
if ( focusIndex > -1 ) {
// Simulate a click on the "active" item:
const selectedItem = matchingItems[ currentFocusIndex ];
this.onSelect( selectedItem );
}
}
};
/**
* onClickItem gets called when onClick happens on one of the list elements
* @param event
*/
onClickItem = ( event ) => {
const { matchingItems } = this.state;
// update the input value and close the dropdown again
const elements = event.currentTarget.children;
let selectedKey;
for ( let i = 0; i < elements.length; i += 1 ) {
if ( elements[ i ].tagName === 'INPUT' ) {
selectedKey = Number( elements[ i ].value );
break;
}
}
const selectedItem = matchingItems.find( item => item.key === selectedKey );
this.onSelect( selectedItem );
};
/**
* onSelect is called onClickItem and onEnter upon an option of the drop down menu
* does nothing if the key has not changed since the last onSelect event
* @param selectedItem
*/
onSelect = ( selectedItem ) => {
console.log( selectedItem );
const { lastValidItem } = this.state;
if ( lastValidItem && selectedItem.key === lastValidItem.key ) {
// do not trigger the callback function
// but still change state to fit new selection
this.setState( {
currentInput: selectedItem.label,
visible: false,
focusIndex: -1,
} );
return;
}
// change state to fit new selection
this.setState( {
currentInput: selectedItem.label,
lastValidItem: selectedItem,
visible: false,
focusIndex: -1,
} );
// callback function onSelect
const { onSelect } = this.props;
onSelect( selectedItem );
};
renderItemLabel = ( currentInput, item ) => (
<React.Fragment>
{item.label.substr( 0, this.indexOfMatch( currentInput, item ) )}
<strong>
{item.label.substr( this.indexOfMatch( currentInput, item ), currentInput.length )}
</strong>
{item.label.substr( this.indexOfMatch( currentInput, item ) + currentInput.length )}
</React.Fragment>
)
renderItems = ( currentInput, items, focusIndex, activeItemClassName, itemClassName ) => (
<div className="datalist-items">
{items.map( ( item, i ) => {
const isActive = focusIndex === i;
const itemActiveClasses = isActive ? `datalist-active-item ${ activeItemClassName }` : '';
const itemClasses = `${ itemClassName } ${ itemActiveClasses };`;
return (
<div
onClick={this.onClickItem}
className={itemClasses}
key={item.key}
tabIndex={0}
role="button"
onKeyUp={event => event.preventDefault()}
>
{ this.renderItemLabel( currentInput, item )}
<input type="hidden" value={item.key} />
</div>
);
} )}
</div>
);
renderInputField = ( placeholder, currentInput, inputClassName ) => (
<input
onChange={this.onHandleInput}
onKeyDown={this.onHandleKeydown}
type="text"
className={`autocomplete-input ${ inputClassName }`}
placeholder={placeholder}
value={currentInput}
/>
)
render() {
const {
currentInput, matchingItems, focusIndex, visible,
} = this.state;
const {
placeholder, inputClassName, activeItemClassName, itemClassName, requiredInputLength,
} = this.props;
const reachedRequiredLength = currentInput.length >= requiredInputLength;
return (
<div className="datalist-input">
{ this.renderInputField( placeholder, currentInput, inputClassName ) }
{ reachedRequiredLength && visible
&& this.renderItems( currentInput, matchingItems, focusIndex,
activeItemClassName, itemClassName )
}
</div>
);
}
}
DataListInput.propTypes = {
items: PropTypes.arrayOf(
PropTypes.shape( {
label: PropTypes.string.isRequired,
key: PropTypes.number.isRequired,
} ),
).isRequired,
placeholder: PropTypes.string,
onSelect: PropTypes.func.isRequired,
match: PropTypes.func,
inputClassName: PropTypes.string,
itemClassName: PropTypes.string,
activeItemClassName: PropTypes.string,
requiredInputLength: PropTypes.number,
};
DataListInput.defaultProps = {
placeholder: '',
match: undefined,
inputClassName: '',
itemClassName: '',
activeItemClassName: '',
requiredInputLength: 1,
};
export default DataListInput;

View File

@ -1 +0,0 @@
Feel free to use those files instead of the npm package if you want to tinker with the component / css and just use it inside your project.

5458
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{ {
"name": "react-datalist-input", "name": "react-datalist-input",
"version": "1.0.12", "version": "1.1.0",
"description": "This package provides a react component as follows: an input field with a drop down menu to pick a possible option based on the current input.", "description": "This package provides a react component as follows: an input field with a drop down menu to pick a possible option based on the current input.",
"main": "build/index.js", "main": "dist/main.js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/andrelandgraf/react-datalist-input.git" "url": "https://github.com/andrelandgraf/react-datalist-input.git"
@ -28,17 +28,18 @@
"input" "input"
], ],
"devDependencies": { "devDependencies": {
"babel-cli": "^6.26.0", "@babel/core": "^7.4.5",
"babel-core": "^6.26.0", "@babel/plugin-proposal-class-properties": "^7.4.4",
"babel-loader": "^7.1.1", "@babel/preset-env": "^7.4.5",
"babel-plugin-transform-object-rest-spread": "^6.23.0", "@babel/preset-react": "^7.0.0",
"babel-plugin-transform-react-jsx": "^6.24.1", "babel-loader": "^8.0.6",
"babel-plugin-transform-react-remove-prop-types": "^0.4.8", "css-loader": "^3.0.0",
"babel-preset-react": "^6.24.1", "html-loader": "^0.5.5",
"babel-preset-env": "^1.5.1", "react": "^16.8.6",
"style-loader": "^0.21.0", "react-dom": "^16.8.6",
"css-loader": "^1.0.0", "style-loader": "^0.23.1",
"webpack": "^3.5.5" "webpack": "^4.33.0",
"webpack-cli": "^3.3.4"
}, },
"dependencies": { "dependencies": {
"prop-types": "^15.6.1" "prop-types": "^15.6.1"
@ -48,7 +49,6 @@
}, },
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack --watch", "build": "webpack --mode production"
"build": "webpack"
} }
} }

View File

@ -4,15 +4,14 @@ import PropTypes from 'prop-types';
import './index.css'; import './index.css';
class DataListInput extends React.Component { class DataListInput extends React.Component {
constructor( props ) {
constructor(props) { super( props );
super(props);
this.state = { this.state = {
/* last valid item that was selected from the drop down menu */ /* last valid item that was selected from the drop down menu */
lastValidItem: undefined, lastValidItem: undefined,
/* current input text */ /* current input text */
currentInput: "", currentInput: '',
/* current set of matching items */ /* current set of matching items */
matchingItems: [], matchingItems: [],
/* visibility property of the drop down menu */ /* visibility property of the drop down menu */
@ -20,23 +19,13 @@ class DataListInput extends React.Component {
/* index of the currently focused item in the drop down menu */ /* index of the currently focused item in the drop down menu */
focusIndex: 0, focusIndex: 0,
}; };
this.onHandleInput = this.onHandleInput.bind(this);
this.match = this.match.bind(this);
this.indexOfMatch = this.indexOfMatch.bind(this);
this.onHandleKeydown = this.onHandleKeydown.bind(this);
this.onClickItem = this.onClickItem.bind(this);
this.onSelect = this.onSelect.bind(this);
this.renderItems = this.renderItems.bind(this);
this.renderInputField = this.renderInputField.bind(this);
this.renderItemLabel = this.renderItemLabel.bind(this);
} }
/** /**
* gets called when someone starts to write in the input field * gets called when someone starts to write in the input field
* @param value * @param value
*/ */
onHandleInput( event ){ onHandleInput = ( event ) => {
const currentInput = event.target.value; const currentInput = event.target.value;
const { items, match } = this.props; const { items, match } = this.props;
const matchingItems = items.filter( ( item ) => { const matchingItems = items.filter( ( item ) => {
@ -49,7 +38,7 @@ class DataListInput extends React.Component {
focusIndex: 0, focusIndex: 0,
visible: true, visible: true,
} ); } );
} };
/** /**
* default function for matching the current input value (needle) * default function for matching the current input value (needle)
@ -58,9 +47,8 @@ class DataListInput extends React.Component {
* @param item * @param item
* @returns {boolean} * @returns {boolean}
*/ */
match( currentInput, item ) { match = ( currentInput, item ) => item
return item.label.substr( 0, currentInput.length ).toUpperCase() === currentInput.toUpperCase(); .label.substr( 0, currentInput.length ).toUpperCase() === currentInput.toUpperCase();
}
/** /**
* function for getting the index of the currentValue inside a value of the values array * function for getting the index of the currentValue inside a value of the values array
@ -68,15 +56,14 @@ class DataListInput extends React.Component {
* @param item * @param item
* @returns {number} * @returns {number}
*/ */
indexOfMatch( currentInput, item ) { indexOfMatch = ( currentInput, item ) => item
return item.label.toUpperCase().indexOf( currentInput.toUpperCase() ); .label.toUpperCase().indexOf( currentInput.toUpperCase() );
}
/** /**
* handle key events * handle key events
* @param event * @param event
*/ */
onHandleKeydown( event ) { onHandleKeydown = ( event ) => {
const { visible, focusIndex, matchingItems } = this.state; const { visible, focusIndex, matchingItems } = this.state;
// only do something if drop-down div is visible // only do something if drop-down div is visible
if ( !visible ) return; if ( !visible ) return;
@ -105,13 +92,13 @@ class DataListInput extends React.Component {
this.onSelect( selectedItem ); this.onSelect( selectedItem );
} }
} }
} };
/** /**
* onClickItem gets called when onClick happens on one of the list elements * onClickItem gets called when onClick happens on one of the list elements
* @param event * @param event
*/ */
onClickItem( event ) { onClickItem = ( event ) => {
const { matchingItems } = this.state; const { matchingItems } = this.state;
// update the input value and close the dropdown again // update the input value and close the dropdown again
const elements = event.currentTarget.children; const elements = event.currentTarget.children;
@ -124,14 +111,15 @@ class DataListInput extends React.Component {
} }
const selectedItem = matchingItems.find( item => item.key === selectedKey ); const selectedItem = matchingItems.find( item => item.key === selectedKey );
this.onSelect( selectedItem ); this.onSelect( selectedItem );
} };
/** /**
* onSelect is called onClickItem and onEnter upon an option of the drop down menu * onSelect is called onClickItem and onEnter upon an option of the drop down menu
* does nothing if the key has not changed since the last onSelect event * does nothing if the key has not changed since the last onSelect event
* @param selectedItem * @param selectedItem
*/ */
onSelect( selectedItem ) { onSelect = ( selectedItem ) => {
console.log( selectedItem );
const { lastValidItem } = this.state; const { lastValidItem } = this.state;
if ( lastValidItem && selectedItem.key === lastValidItem.key ) { if ( lastValidItem && selectedItem.key === lastValidItem.key ) {
// do not trigger the callback function // do not trigger the callback function
@ -153,22 +141,19 @@ class DataListInput extends React.Component {
// callback function onSelect // callback function onSelect
const { onSelect } = this.props; const { onSelect } = this.props;
onSelect( selectedItem ); onSelect( selectedItem );
} };
renderItemLabel( currentInput, item ) { renderItemLabel = ( currentInput, item ) => (
return ( <React.Fragment>
<React.Fragment> {item.label.substr( 0, this.indexOfMatch( currentInput, item ) )}
{item.label.substr( 0, this.indexOfMatch( currentInput, item ) )} <strong>
<strong> {item.label.substr( this.indexOfMatch( currentInput, item ), currentInput.length )}
{item.label.substr( this.indexOfMatch( currentInput, item ), currentInput.length )} </strong>
</strong> {item.label.substr( this.indexOfMatch( currentInput, item ) + currentInput.length )}
{item.label.substr( this.indexOfMatch( currentInput, item ) + currentInput.length )} </React.Fragment>
</React.Fragment> )
);
}
renderItems( currentInput, items, focusIndex, activeItemClassName, itemClassName ) { renderItems = ( currentInput, items, focusIndex, activeItemClassName, itemClassName ) => (
return (
<div className="datalist-items"> <div className="datalist-items">
{items.map( ( item, i ) => { {items.map( ( item, i ) => {
const isActive = focusIndex === i; const isActive = focusIndex === i;
@ -188,21 +173,19 @@ class DataListInput extends React.Component {
</div> </div>
); );
} )} } )}
</div> ); </div>
} );
renderInputField( placeholder, currentInput, inputClassName ) { renderInputField = ( placeholder, currentInput, inputClassName ) => (
return ( <input
<input onChange={this.onHandleInput}
onChange={this.onHandleInput} onKeyDown={this.onHandleKeydown}
onKeyDown={this.onHandleKeydown} type="text"
type="text" className={`autocomplete-input ${ inputClassName }`}
className={`autocomplete-input ${ inputClassName }`} placeholder={placeholder}
placeholder={placeholder} value={currentInput}
value={currentInput} />
/> )
);
}
render() { render() {
const { const {
@ -249,4 +232,4 @@ DataListInput.defaultProps = {
requiredInputLength: 1, requiredInputLength: 1,
}; };
export default DataListInput; export default DataListInput;

View File

@ -1,40 +1,35 @@
var path = require('path');
module.exports = { module.exports = {
entry: './src/index.js', module: {
output: { rules: [
path: path.resolve(__dirname, 'build'), {
filename: 'index.js', test: /\.(js|jsx)$/,
libraryTarget: 'commonjs2' exclude: /node_modules/,
use: {
loader: "babel-loader"
}
},
{
test: /^(?!.*?\.module).*\.css$/,
use: ['style-loader', 'css-loader']
}, },
module: { {
rules: [ test: /\.module\.css$/,
{ use: ['style-loader', {
test: /\.js$/, loader: 'css-loader',
include: path.resolve(__dirname, 'src'), options: {
exclude: /(node_modules|bower_components|build)/, modules: true
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
},
{
test: /^(?!.*?\.module).*\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.module\.css$/,
use: ['style-loader', {
loader: 'css-loader',
options: {
modules: true
}
}]
} }
] }]
}, },
externals: {
'react': 'commonjs react' {
} test: /\.html$/,
use: [
{
loader: "html-loader"
}
]
}
]
}
}; };