Initial commit
commit
bef2601391
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"presets": [ "env", "stage-1", "react" ],
|
||||
"retainLines": true
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
module.exports = {
|
||||
"parser": "babel-eslint",
|
||||
"env": {
|
||||
"es6": true,
|
||||
"browser": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"experimentalObjectRestSpread": true,
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"linebreak-style": [
|
||||
"error",
|
||||
"unix"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"no-control-regex": [
|
||||
"off"
|
||||
],
|
||||
"no-empty": [
|
||||
"off"
|
||||
]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,137 @@
|
|||
// "Generic dropdown component"
|
||||
// Renders something and then when that "something" is focused renders a popup layer next to it
|
||||
// For example, a text input with a popup selection list
|
||||
// ...Or maybe a button with a popup menu
|
||||
// (c) Vitaliy Filippov 2019+
|
||||
// Version 2019-08-27
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export class Picker extends React.Component
|
||||
{
|
||||
static propTypes = {
|
||||
direction: PropTypes.string,
|
||||
clearOnClick: PropTypes.bool,
|
||||
minWidth: PropTypes.number,
|
||||
style: PropTypes.object,
|
||||
renderInput: PropTypes.func.isRequired,
|
||||
renderPicker: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
state = {
|
||||
focused: false,
|
||||
height: 0,
|
||||
width: 0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
}
|
||||
|
||||
focus = () =>
|
||||
{
|
||||
this.setState({ focused: true, height: 0 });
|
||||
this.calculateDirection();
|
||||
if (this.props.clearOnClick)
|
||||
{
|
||||
document.body.addEventListener('click', this.blurExt);
|
||||
}
|
||||
}
|
||||
|
||||
blur = () =>
|
||||
{
|
||||
this.setState({ focused: false });
|
||||
if (this.props.clearOnClick)
|
||||
{
|
||||
document.body.removeEventListener('click', this.blurExt);
|
||||
}
|
||||
}
|
||||
|
||||
blurExt = (ev) =>
|
||||
{
|
||||
let n = this.input ? ReactDOM.findDOMNode(this.input) : null;
|
||||
let e = ev.target||ev.srcElement;
|
||||
while (e)
|
||||
{
|
||||
// calendar-box is calendar.js's class
|
||||
if (e == this.picker || e == n || /\bcalendar-box\b/.exec(e.className||''))
|
||||
{
|
||||
return;
|
||||
}
|
||||
e = e.parentNode;
|
||||
}
|
||||
this.blur();
|
||||
}
|
||||
|
||||
setInput = (e) =>
|
||||
{
|
||||
this.input = e;
|
||||
}
|
||||
|
||||
setPicker = (e) =>
|
||||
{
|
||||
this.picker = e;
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
return (<div style={this.props.style}>
|
||||
{this.props.renderInput({
|
||||
onFocus: this.focus,
|
||||
onBlur: this.blur,
|
||||
focused: this.state.focused,
|
||||
ref: this.setInput,
|
||||
})}
|
||||
{this.state.focused
|
||||
? <div style={{
|
||||
position: 'fixed',
|
||||
background: 'white',
|
||||
top: this.state.top,
|
||||
width: this.state.width||'auto',
|
||||
left: this.state.left,
|
||||
zIndex: 100,
|
||||
}} ref={this.setPicker}>
|
||||
{this.props.renderPicker()}
|
||||
</div>
|
||||
: null}
|
||||
</div>);
|
||||
}
|
||||
|
||||
componentDidUpdate()
|
||||
{
|
||||
if (this.state.focused && !this.state.height)
|
||||
{
|
||||
this.calculateDirection();
|
||||
}
|
||||
}
|
||||
|
||||
calculateDirection()
|
||||
{
|
||||
if (!this.input || !this.picker)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const picker_height = ReactDOM.findDOMNode(this.picker).getBoundingClientRect().height;
|
||||
const client = ReactDOM.findDOMNode(this.input).getBoundingClientRect();
|
||||
const screen_height = window.innerHeight || document.documentElement.offsetHeight;
|
||||
let direction = this.props.direction;
|
||||
if (!direction || direction === 'auto')
|
||||
{
|
||||
const down = client.top + picker_height < screen_height;
|
||||
direction = down ? 'down' : 'up';
|
||||
}
|
||||
let top = client.top
|
||||
+ (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop)
|
||||
- (document.documentElement.clientTop || document.body.clientTop || 0);
|
||||
top = direction == 'down' ? (top + client.height) + 'px' : (top - picker_height) + 'px';
|
||||
const left = (client.left
|
||||
+ (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft)
|
||||
- (document.documentElement.clientLeft || document.body.clientLeft || 0)) + 'px';
|
||||
const width = (this.props.minWidth && client.width < this.props.minWidth ? this.props.minWidth : client.width)+'px';
|
||||
if (this.state.top !== top || this.state.left !== left ||
|
||||
this.state.width !== width || this.state.height !== picker_height)
|
||||
{
|
||||
this.setState({ top, left, width, height: picker_height });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
// Simple Dropdown/Autocomplete with single/multiple selection and easy customisation via CSS modules
|
||||
// Version 2019-08-27
|
||||
// (c) Vitaliy Filippov 2019+
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import autocomplete_css from './autocomplete.css';
|
||||
import input_css from './input.css';
|
||||
import { Picker } from './Picker.js';
|
||||
|
||||
export default class Selectbox extends React.PureComponent
|
||||
{
|
||||
state = {
|
||||
active: null,
|
||||
query: null,
|
||||
inputWidth: 20,
|
||||
}
|
||||
|
||||
setQuery = (ev) =>
|
||||
{
|
||||
this.setState({ query: ev.target.value });
|
||||
}
|
||||
|
||||
onKeyDown = (ev) =>
|
||||
{
|
||||
if ((ev.which == 40 || ev.which == 38) && this.filtered_items.length)
|
||||
{
|
||||
// up / down
|
||||
this.setState({
|
||||
active: this.state.active == null ? 0 : (
|
||||
(this.state.active + (event.which === 40 ? 1 : this.filtered_items.length-1)) % this.filtered_items.length
|
||||
),
|
||||
});
|
||||
if (!this.picker.state.focused)
|
||||
{
|
||||
this.picker.focus();
|
||||
}
|
||||
}
|
||||
else if ((ev.which == 10 || ev.which == 13) && this.state.active != null &&
|
||||
this.state.active < this.filtered_items.length)
|
||||
{
|
||||
// enter
|
||||
this.onMouseDown();
|
||||
}
|
||||
}
|
||||
|
||||
onMouseDown = () =>
|
||||
{
|
||||
this.setState({ query: null });
|
||||
this.picker.blur();
|
||||
const sel = this.filtered_items[this.state.active][this.props.valueKey||'id'];
|
||||
let value = sel;
|
||||
if (this.props.multiple)
|
||||
{
|
||||
const already = (this.props.value||[]).indexOf(sel);
|
||||
if (already < 0)
|
||||
{
|
||||
// add
|
||||
value = [ ...(this.props.value||[]), sel ];
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove
|
||||
value = [ ...this.props.value ];
|
||||
value.splice(already, 1);
|
||||
}
|
||||
}
|
||||
this.props.onChange(value);
|
||||
}
|
||||
|
||||
onMouseOver = (ev) =>
|
||||
{
|
||||
let e = ev.target;
|
||||
while (e && e != ev.currentTarget && !e.id)
|
||||
{
|
||||
e = e.parentNode;
|
||||
}
|
||||
if (e && e.id)
|
||||
{
|
||||
this.setState({ active: e.id });
|
||||
}
|
||||
}
|
||||
|
||||
focusInput = () =>
|
||||
{
|
||||
this.input.focus();
|
||||
}
|
||||
|
||||
renderInput = (p) =>
|
||||
{
|
||||
const value = this.state.query == null ? (this.props.multiple ? '' : this.item_hash[this.props.value]||'') : this.state.query;
|
||||
const input = <input
|
||||
readOnly={this.props.readOnly}
|
||||
ref={this.setInput}
|
||||
className={autocomplete_css.inputInputElement}
|
||||
style={this.props.multiple ? { height: '29px', padding: 0, display: 'inline-block', width: this.state.inputWidth+'px' } : undefined}
|
||||
onFocus={p.onFocus}
|
||||
value={value}
|
||||
onChange={this.setQuery}
|
||||
onKeyDown={this.onKeyDown}
|
||||
/>;
|
||||
return (<div ref={p.ref} className={autocomplete_css.input}
|
||||
style={this.props.multiple ? { fontSize: '13px', display: 'flex', overflow: 'hidden', cursor: 'text', height: 'auto' } : undefined}
|
||||
onClick={this.props.multiple ? this.focusInput : undefined}>
|
||||
{this.props.multiple
|
||||
? <div style={{overflow: 'hidden', flex: 1, margin: '0 2em 0 .5em'}}>
|
||||
<span className={autocomplete_css.inputInputElement} ref={this.setSizer}
|
||||
style={{display: 'inline-block', width: 'auto', position: 'absolute', padding: '0 2px', top: '-100px', whiteSpace: 'pre', visibility: 'hidden'}}>
|
||||
{value}
|
||||
</span>
|
||||
{(this.props.value||[]).map(id => <span style={{cursor: 'pointer', display: 'inline-block', borderRadius: '2px', background: '#e0f0ff', border: '1px solid #c0e0ff', padding: '3px', margin: '2px 5px 2px 0'}}>{this.item_hash[id]}</span>)}
|
||||
{input}
|
||||
</div>
|
||||
: input}
|
||||
</div>);
|
||||
}
|
||||
|
||||
renderSuggestions = () =>
|
||||
{
|
||||
return (<div className={autocomplete_css.suggestions} onMouseDown={this.onMouseDown} onMouseOver={this.onMouseOver}>
|
||||
{this.filtered_items.map((e, i) => (<div key={i} id={i}
|
||||
className={autocomplete_css.suggestion+(this.state.active == i ? ' '+autocomplete_css.active : '')}>
|
||||
{e[this.props.labelKey||'name']}
|
||||
</div>))}
|
||||
</div>);
|
||||
}
|
||||
|
||||
setSizer = (e) =>
|
||||
{
|
||||
this.sizer = e;
|
||||
}
|
||||
|
||||
setPicker = (e) =>
|
||||
{
|
||||
this.picker = e;
|
||||
}
|
||||
|
||||
setInput = (e) =>
|
||||
{
|
||||
this.input = e;
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
if (!this.prevProps || this.props.source != this.prevProps.source)
|
||||
{
|
||||
if (!this.props.source)
|
||||
{
|
||||
this.items = [];
|
||||
this.item_hash = {};
|
||||
}
|
||||
else if (this.props.source instanceof Array)
|
||||
{
|
||||
this.items = this.props.source;
|
||||
this.item_hash = this.items.reduce((a, c) => { a[c[this.props.valueKey||'id']] = c[this.props.labelKey||'name']; return a; }, {});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.items = Object.keys(this.props.source).map(id => ({ id, name: this.props.source[id] }));
|
||||
this.item_hash = this.props.source;
|
||||
}
|
||||
}
|
||||
if (!this.prevProps || this.props.suggestionMatch != this.prevProps.suggestionMatch ||
|
||||
this.state.query != this.prevState.query)
|
||||
{
|
||||
if (!this.props.suggestionMatch || this.props.suggestionMatch == 'disabled' || !this.state.query)
|
||||
{
|
||||
this.filtered_items = this.items;
|
||||
}
|
||||
else
|
||||
{
|
||||
const n = this.props.labelKey||'name';
|
||||
const q = this.state.query.toLowerCase();
|
||||
this.filtered_items = this.items.filter(e => e[n].toLowerCase().indexOf(q) >= 0);
|
||||
}
|
||||
}
|
||||
this.prevProps = this.props;
|
||||
this.prevState = this.state;
|
||||
return (<Picker
|
||||
ref={this.setPicker}
|
||||
minWidth={this.props.minWidth}
|
||||
clearOnClick={true}
|
||||
renderInput={this.renderInput}
|
||||
renderPicker={this.renderSuggestions}
|
||||
style={this.props.style}
|
||||
/>);
|
||||
}
|
||||
|
||||
componentDidUpdate()
|
||||
{
|
||||
if (this.sizer)
|
||||
{
|
||||
this.setState({ inputWidth: this.sizer.offsetWidth });
|
||||
if (this.prevHeight && this.picker.input.offsetHeight != this.prevHeight)
|
||||
{
|
||||
this.picker.calculateDirection();
|
||||
}
|
||||
this.prevHeight = this.picker.input.offsetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount()
|
||||
{
|
||||
this.componentDidUpdate();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
.autocomplete
|
||||
{
|
||||
padding: 0;
|
||||
position: relative;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.input
|
||||
{
|
||||
background: white;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 0;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
height: 28px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input:after
|
||||
{
|
||||
border: 0;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
right: 1em;
|
||||
text-align: right;
|
||||
font-size: 1.5em;
|
||||
margin-top: -0.375em;
|
||||
font-family: FontAwesome;
|
||||
content: "\f107";
|
||||
color: #c0c0c0;
|
||||
height: 0.75em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.suggestions
|
||||
{
|
||||
margin-top: 0;
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12), 0 1px 4px rgba(0, 0, 0, 0.24);
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
transition-duration: 0.35s;
|
||||
transition-property: max-height;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.suggestion
|
||||
{
|
||||
font-family: helvetica, arial, verdana, sans-serif;
|
||||
font-size: 13px;
|
||||
padding: 7px;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.suggestion.active
|
||||
{
|
||||
background: #d6e8f6;
|
||||
}
|
||||
|
||||
.clear
|
||||
{
|
||||
top: 4px;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.values
|
||||
{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.inputInputElement
|
||||
{
|
||||
font-size: inherit;
|
||||
line-height: 200%;
|
||||
border: 0;
|
||||
padding: 0 2em 0 .5em;
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
display: block;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inputInputElement::-ms-clear
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inputBar
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.inputInputElement:focus + .inputBar
|
||||
{
|
||||
content: " ";
|
||||
display: block;
|
||||
border: 1px solid #4196d4;
|
||||
border-radius: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.inputBar:after, .inputBar:before
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.withclear input
|
||||
{
|
||||
text-indent: 0;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.clear
|
||||
{
|
||||
color: #aaa;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #aaa;
|
||||
left: auto;
|
||||
top: 4px;
|
||||
right: 28px;
|
||||
line-height: 18px;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
|
||||
<title>Selectbox</title>
|
||||
<style>
|
||||
html { margin: 0; height: 100%; font-family: Roboto, sans-serif; font-size: 15px; }
|
||||
body { margin: 0; height: 100%; line-height: 1.2em; background: white; color: black; }
|
||||
input, select, textarea, button { font-size: 100%; font-family: Roboto, sans-serif; }
|
||||
table { border-collapse: collapse; font-size: 100%; }
|
||||
form table td, form table th { vertical-align: top; }
|
||||
table.left th { text-align: left; padding-right: .5em; }
|
||||
td, th { padding: 0; }
|
||||
img { border: 0; }
|
||||
ul, p, form { margin: 0; }
|
||||
input { margin: 1px; }
|
||||
* { box-sizing: border-box; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
</div>
|
||||
<script type="text/javascript" src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,49 @@
|
|||
.input
|
||||
{
|
||||
background: white;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 0;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
color: rgb(33, 33, 33);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.inputElement
|
||||
{
|
||||
font-size: inherit;
|
||||
border: 0;
|
||||
padding: 0 2em 0 .5em;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
outline: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inputElement:focus
|
||||
{
|
||||
box-shadow: inset 0 0 0 1px #4196d4;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input.inputElement
|
||||
{
|
||||
line-height: 200%;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.inputElement::-ms-clear
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bar
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bar:after, .bar:before
|
||||
{
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import Selectbox from './Selectbox.js';
|
||||
|
||||
const OPTIONS = {
|
||||
day: 'День',
|
||||
week: 'Неделя',
|
||||
month: 'Месяц',
|
||||
kak: 'Полный период детализации',
|
||||
};
|
||||
|
||||
class Test extends React.PureComponent
|
||||
{
|
||||
state = {
|
||||
value: [ 'day' ],
|
||||
}
|
||||
|
||||
onChange = (v) =>
|
||||
{
|
||||
this.setState({ value: v });
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
return <div style={{padding: '20px', width: '300px', background: '#e0e8ff'}}>
|
||||
<Selectbox
|
||||
source={OPTIONS}
|
||||
multiple={true}
|
||||
value={this.state.value}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Test />, document.getElementById('app')
|
||||
);
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "selectbox",
|
||||
"author": {
|
||||
"name": "Vitaliy Filippov",
|
||||
"email": "vitalif@yourcmc.ru",
|
||||
"url": "http://yourcmc.ru/wiki/"
|
||||
},
|
||||
"description": "Simple React Dropdown/Autocomplete",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-loader": "^7.1.5",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-1": "^6.24.1",
|
||||
"css-loader": "^1.0.0",
|
||||
"eslint": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.10.0",
|
||||
"postcss": "^6.0.23",
|
||||
"postcss-cssnext": "^3.1.0",
|
||||
"postcss-each": "^0.10.0",
|
||||
"postcss-import": "^11.1.0",
|
||||
"postcss-loader": "^2.1.5",
|
||||
"postcss-mixins": "^6.2.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"style-loader": "^0.21.0",
|
||||
"webpack": "^4.20.2",
|
||||
"webpack-bundle-analyzer": "^2.13.1",
|
||||
"webpack-cli": "^3.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "webpack --mode=production --optimize-minimize; nodejs copy-css.js",
|
||||
"stats": "NODE_ENV=production webpack --mode=production --optimize-minimize --profile --json > stats.json; webpack-bundle-analyzer stats.json -h 0.0.0.0",
|
||||
"watch-dev": "NODE_ENV=development webpack --mode=development -w",
|
||||
"watch": "webpack --mode=production -w --optimize-minimize"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
'postcss-import': {
|
||||
root: __dirname,
|
||||
},
|
||||
'postcss-mixins': {},
|
||||
'postcss-each': {},
|
||||
'postcss-cssnext': {
|
||||
features: {
|
||||
customProperties: {
|
||||
variables: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
|
@ -0,0 +1,55 @@
|
|||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
main: [ "babel-polyfill", './main.js' ]
|
||||
},
|
||||
context: __dirname,
|
||||
output: {
|
||||
path: __dirname+'/dist',
|
||||
filename: '[name].js'
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /.jsx?$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node_modules(?!\/react-toolbox\/components|\/dynamic-virtual-scroll)/
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
{
|
||||
loader: "style-loader",
|
||||
options: {
|
||||
singleton: true
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
modules: true, // default is false
|
||||
sourceMap: true,
|
||||
importLoaders: 1,
|
||||
localIdentName: "[name]--[local]--[hash:base64:8]"
|
||||
}
|
||||
},
|
||||
"postcss-loader"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
"process.env": {
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV || "production")
|
||||
}
|
||||
})
|
||||
],
|
||||
performance: {
|
||||
maxEntrypointSize: 5000000,
|
||||
maxAssetSize: 5000000
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue