Merge pull request #4 from Microsoft/excel-range
1. Removed gulp as build tool and switched to webpack.
This commit is contained in:
Коммит
8bf648c422
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"presets": [
|
||||
"latest",
|
||||
"react",
|
||||
"stage-1"
|
||||
],
|
||||
"env": {
|
||||
"development": {
|
||||
"presets": [
|
||||
"react-hmre"
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"plugins": [
|
||||
"transform-react-constant-elements",
|
||||
"transform-react-remove-prop-types"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -3,8 +3,6 @@
|
|||
<head>
|
||||
<meta http-equiv='Content-type' content='text/html; charset=utf-8'>
|
||||
<title>React Spreadsheet Example</title>
|
||||
<link rel="stylesheet" type="text/css" href="../styles/excel.css" />
|
||||
<link rel="stylesheet" type="text/css" href="example.css" />
|
||||
<link href='http://fonts.googleapis.com/css?family=Roboto:400,300' rel='stylesheet' type='text/css'>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -20,6 +18,5 @@
|
|||
<h3>Fancy Editable Spreadsheet</h3>
|
||||
<div id="exampleTwo"></div>
|
||||
</div>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -9,64 +9,100 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"jquery": "^2.1",
|
||||
"mousetrap": "^1.5.3"
|
||||
"mousetrap": "^1.5.3",
|
||||
"object-assign": "4.1.0",
|
||||
"react": "^15.4.1",
|
||||
"react-dom": "^15.4.1",
|
||||
"react-redux": ">=4.4.5",
|
||||
"react-router": ">=2.8.1",
|
||||
"react-router-redux": ">=4.0.6",
|
||||
"redux": ">=3.6.0",
|
||||
"redux-thunk": ">=2.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^0.14.3"
|
||||
"react": "^15.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "6.5.1",
|
||||
"babel-cli": "6.16.0",
|
||||
"babel-core": "6.17.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "6.2.5",
|
||||
"babel-plugin-react-display-name": "2.0.0",
|
||||
"babel-plugin-transform-react-constant-elements": "6.9.1",
|
||||
"babel-plugin-transform-react-remove-prop-types": "0.2.10",
|
||||
"babel-preset-latest": "6.16.0",
|
||||
"babel-preset-react": "6.16.0",
|
||||
"babel-preset-react-hmre": "1.1.1",
|
||||
"babel-preset-stage-1": "6.16.0",
|
||||
"babel-register": "6.16.3",
|
||||
"body-parser": "^1.12.4",
|
||||
"browserify": "^12.0.1",
|
||||
"browserify-shim": "^3.8.11",
|
||||
"browser-sync": "2.17.5",
|
||||
"chai": "3.5.0",
|
||||
"chalk": "1.1.3",
|
||||
"connect-history-api-fallback": "1.3.0",
|
||||
"coveralls": "2.11.14",
|
||||
"cross-env": "3.1.3",
|
||||
"css-loader": "0.25.0",
|
||||
"del": "^2.0.2",
|
||||
"enzyme": "2.5.1",
|
||||
"eslint": "^3.11.1",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-react": "^6.8.0",
|
||||
"eslint-watch": "2.1.14",
|
||||
"express": "^4.12.3",
|
||||
"gulp": "^3.8.11",
|
||||
"gulp-connect": "^4.0.0",
|
||||
"gulp-eslint": "^3.0.1",
|
||||
"gulp-header": "^1.2.2",
|
||||
"gulp-plumber": "^1.0.0",
|
||||
"gulp-react": "^3.0.1",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-streamify": "1.0.2",
|
||||
"gulp-uglify": "^1.1.0",
|
||||
"gulp-util": "^3.0.4",
|
||||
"extract-text-webpack-plugin": "1.0.1",
|
||||
"file-loader": "0.9.0",
|
||||
"html-webpack-plugin": "2.24.0",
|
||||
"isparta": "4.0.0",
|
||||
"istanbul": "0.4.4",
|
||||
"jest-babel-preprocessor": "^0.3.0",
|
||||
"jest-cli": "^0.8.2",
|
||||
"json-loader": "0.5.4",
|
||||
"mocha": "3.1.2",
|
||||
"mockdate": "1.0.4",
|
||||
"multer": "^1.1.0",
|
||||
"react": "^0.14.0",
|
||||
"react-addons-test-utils": "^0.14.3",
|
||||
"react-dom": "^0.14.0",
|
||||
"react-tools": "^0.13.3",
|
||||
"reactify": "^1.1.1",
|
||||
"tape": "^4.2.2",
|
||||
"vinyl-source-stream": "^1.1.0"
|
||||
"node-sass": "3.10.1",
|
||||
"npm-run-all": "3.1.1",
|
||||
"open": "0.0.5",
|
||||
"postcss-loader": "1.0.0",
|
||||
"prompt": "1.0.0",
|
||||
"react-addons-test-utils": ">=0.14.3",
|
||||
"react-tools": ">=0.13.3",
|
||||
"redux-immutable-state-invariant": "1.2.4",
|
||||
"replace": "0.3.0",
|
||||
"rimraf": "2.5.4",
|
||||
"sass-loader": "4.0.2",
|
||||
"sinon": "1.17.6",
|
||||
"sinon-chai": "2.8.0",
|
||||
"style-loader": "0.13.1",
|
||||
"url-loader": "0.5.7",
|
||||
"webpack": "^1.13.3",
|
||||
"webpack-bundle-analyzer": "^2.1.1",
|
||||
"webpack-dev-middleware": "^1.8.4",
|
||||
"webpack-hot-middleware": "^2.13.2",
|
||||
"webpack-md5-hash": "0.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
"debug": "gulp --debug",
|
||||
"dist": "gulp bundle-js --production --release && gulp bundle-js --development --release",
|
||||
"watch": "gulp",
|
||||
"start-message": "babel-node tools/startMessage.js",
|
||||
"prestart": "npm-run-all --parallel start-message remove-dist",
|
||||
"start": "npm-run-all --parallel test open:src lint:watch",
|
||||
"test": "jest",
|
||||
"lint": "eslint src"
|
||||
"lint": "esw webpack.config.* src tools --color",
|
||||
"lint:watch": "npm run lint -- --watch",
|
||||
"remove-dist": "rimraf ./dist",
|
||||
"clean-dist": "npm run remove-dist && mkdir dist",
|
||||
"prebuild": "npm run clean-dist && npm run lint && npm run test",
|
||||
"build": "babel-node tools/build.js && npm run open:dist",
|
||||
"build2": "babel-node tools/build.js",
|
||||
"analyze-bundle": "babel-node ./tools/analyzeBundle.js",
|
||||
"open:src": "babel-node tools/srcServer.js",
|
||||
"open:dist": "babel-node tools/distServer.js"
|
||||
},
|
||||
"jest": {
|
||||
"scriptPreprocessor": "<rootDir>/preprocessor.js",
|
||||
"scriptPreprocessor": "<rootDir>/node_modules/jest-babel-preprocessor/preprocessor.js",
|
||||
"unmockedModulePathPatterns": [
|
||||
"<rootDir>/node_modules/*"
|
||||
]
|
||||
},
|
||||
"browserify-shim": {
|
||||
"react": "global:React",
|
||||
"react/addons": "global:React",
|
||||
"react-dom": "global:ReactDOM"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
"browserify-shim"
|
||||
]
|
||||
},
|
||||
"main": "./lib/spreadsheet.js",
|
||||
"standalone": "React-Spreadsheet"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,11 @@ Clone the repository from GitHub and open the created folder:
|
|||
Install npm packages and compile JSX
|
||||
```bash
|
||||
npm install
|
||||
gulp
|
||||
npm start //for development with hot reloading
|
||||
npm lint //run Eslint
|
||||
npm test //run jest tests
|
||||
npm build //build for production
|
||||
npm open:dist //open against a local prod server
|
||||
```
|
||||
|
||||
Eslint is run automatically when you type 'gulp'. To check lint errors, do 'npm run lint'.
|
||||
|
|
|
@ -1,147 +1,119 @@
|
|||
"use strict";
|
||||
|
||||
const React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
import React, {PropTypes} from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
var Dispatcher = require('./dispatcher');
|
||||
var Helpers = require('./helpers');
|
||||
import Dispatcher from './dispatcher';
|
||||
import Helpers from './helpers';
|
||||
|
||||
var CellComponent = React.createClass({
|
||||
class CellComponent extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
/**
|
||||
* React "getInitialState" method, setting whether or not
|
||||
* the cell is being edited and its changing value
|
||||
*/
|
||||
getInitialState: function() {
|
||||
return {
|
||||
editing: this.props.editing,
|
||||
changedValue: this.props.value
|
||||
};
|
||||
},
|
||||
/**
|
||||
* React "getInitialState" method, setting whether or not
|
||||
* the cell is being edited and its changing value
|
||||
*/
|
||||
this.state = {
|
||||
editing: this.props.editing,
|
||||
changedValue: this.props.value
|
||||
};
|
||||
|
||||
/**
|
||||
* React "render" method, rendering the individual cell
|
||||
*/
|
||||
render: function() {
|
||||
var props = this.props,
|
||||
selected = (props.selected) ? 'selected' : '',
|
||||
ref = 'input_' + props.uid.join('_'),
|
||||
config = props.config || { emptyValueSymbol: ''},
|
||||
displayValue = (props.value === '' || !props.value) ? config.emptyValueSymbol : props.value,
|
||||
cellClasses = (props.cellClasses && props.cellClasses.length > 0) ? props.cellClasses + ' ' + selected : selected,
|
||||
cellContent;
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.handleHeadClick = this.handleHeadClick.bind(this);
|
||||
this.handleDoubleClick = this.handleDoubleClick.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
this.handleBlur = this.handleBlur.bind(this);
|
||||
|
||||
// Check if header - if yes, render it
|
||||
var header = this.renderHeader();
|
||||
if (header) {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
||||
// If not a header, check for editing and return
|
||||
if (props.selected && props.editing) {
|
||||
cellContent = (
|
||||
<input className="mousetrap"
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
ref={ref}
|
||||
defaultValue={this.props.value} />
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<td className={cellClasses} ref={props.uid.join('_')}>
|
||||
<div className="reactTableCell">
|
||||
{cellContent}
|
||||
<span onDoubleClick={this.handleDoubleClick} onClick={this.handleClick}>
|
||||
{displayValue}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* React "componentDidUpdate" method, ensuring correct input focus
|
||||
* @param {React previous properties} prevProps
|
||||
* @param {React previous state} prevState
|
||||
*/
|
||||
componentDidUpdate: function(prevProps, prevState) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.props.editing && this.props.selected) {
|
||||
var node = ReactDOM.findDOMNode(this.refs['input_' + this.props.uid.join('_')]);
|
||||
const node = ReactDOM.findDOMNode(this.refs['input_' + this.props.uid.join('_')]);
|
||||
node.focus();
|
||||
}
|
||||
|
||||
if (prevProps.selected && prevProps.editing && this.state.changedValue !== this.props.value) {
|
||||
this.props.onCellValueChange(this.props.uid, this.state.changedValue);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for individual cell, ensuring navigation and selection
|
||||
* @param {event} e
|
||||
*/
|
||||
handleClick: function (e) {
|
||||
var cellElement = ReactDOM.findDOMNode(this.refs[this.props.uid.join('_')]);
|
||||
handleClick(e) {
|
||||
let cellElement = ReactDOM.findDOMNode(this.refs[this.props.uid.join('_')]);
|
||||
this.props.handleSelectCell(this.props.uid, cellElement);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Click handler for individual cell if the cell is a header cell
|
||||
* @param {event} e
|
||||
*/
|
||||
handleHeadClick: function (e) {
|
||||
var cellElement = ReactDOM.findDOMNode(this.refs[this.props.uid.join('_')]);
|
||||
handleHeadClick(e) {
|
||||
let cellElement = ReactDOM.findDOMNode(this.refs[this.props.uid.join('_')]);
|
||||
Dispatcher.publish('headCellClicked', cellElement, this.props.spreadsheetId);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Double click handler for individual cell, ensuring navigation and selection
|
||||
* @param {event} e
|
||||
*/
|
||||
handleDoubleClick: function (e) {
|
||||
handleDoubleClick(e) {
|
||||
e.preventDefault();
|
||||
this.props.handleDoubleClickOnCell(this.props.uid);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Blur handler for individual cell
|
||||
* @param {event} e
|
||||
*/
|
||||
handleBlur: function (e) {
|
||||
var newValue = ReactDOM.findDOMNode(this.refs['input_' + this.props.uid.join('_')]).value;
|
||||
handleBlur(e) {
|
||||
let newValue = ReactDOM.findDOMNode(this.refs['input_' + this.props.uid.join('_')]).value;
|
||||
|
||||
this.props.onCellValueChange(this.props.uid, newValue, e);
|
||||
this.props.handleCellBlur(this.props.uid);
|
||||
Dispatcher.publish('cellBlurred', this.props.uid, this.props.spreadsheetId);
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Change handler for an individual cell, propagating the value change
|
||||
* @param {event} e
|
||||
*/
|
||||
handleChange: function (e) {
|
||||
var newValue = ReactDOM.findDOMNode(this.refs['input_' + this.props.uid.join('_')]).value;
|
||||
handleChange(e) {
|
||||
let newValue = ReactDOM.findDOMNode(this.refs['input_' + this.props.uid.join('_')]).value;
|
||||
|
||||
this.setState({changedValue: newValue});
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a header exists - if it does, it returns a header object
|
||||
* @return {false|react} [Either false if it's not a header cell, a react object if it is]
|
||||
*/
|
||||
renderHeader: function () {
|
||||
var props = this.props,
|
||||
renderHeader() {
|
||||
let props = this.props,
|
||||
selected = (props.selected) ? 'selected' : '',
|
||||
uid = props.uid,
|
||||
config = props.config || { emptyValueSymbol: ''},
|
||||
displayValue = (props.value === '' || !props.value) ? config.emptyValueSymbol : props.value,
|
||||
cellClasses = (props.cellClasses && props.cellClasses.length > 0) ? this.props.cellClasses + ' ' + selected : selected;
|
||||
cellClasses = (props.cellClasses && props.cellClasses.length > 0) ?
|
||||
this.props.cellClasses + ' ' + selected : selected;
|
||||
|
||||
// Cases
|
||||
var headRow = (uid[0] === 0),
|
||||
let headRow = (uid[0] === 0),
|
||||
headColumn = (uid[1] === 0),
|
||||
headRowAndEnabled = (config.hasHeadRow && uid[0] === 0),
|
||||
headColumnAndEnabled = (config.hasHeadColumn && uid[1] === 0)
|
||||
headColumnAndEnabled = (config.hasHeadColumn && uid[1] === 0);
|
||||
|
||||
// Head Row enabled, cell is in head row
|
||||
// Head Column enabled, cell is in head column
|
||||
|
@ -173,6 +145,65 @@ var CellComponent = React.createClass({
|
|||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = CellComponent;
|
||||
|
||||
/**
|
||||
* React "render" method, rendering the individual cell
|
||||
*/
|
||||
render() {
|
||||
let props = this.props,
|
||||
selected = (props.selected) ? 'selected' : '',
|
||||
ref = 'input_' + props.uid.join('_'),
|
||||
config = props.config || {
|
||||
emptyValueSymbol: ''
|
||||
},
|
||||
displayValue = (props.value === '' || !props.value) ? config.emptyValueSymbol : props.value,
|
||||
cellClasses = (props.cellClasses && props.cellClasses.length > 0) ? props.cellClasses + ' ' + selected : selected,
|
||||
cellContent;
|
||||
|
||||
// Check if header - if yes, render it
|
||||
const header = this.renderHeader();
|
||||
if (header) {
|
||||
return header;
|
||||
}
|
||||
|
||||
// If not a header, check for editing and return
|
||||
if (props.selected && props.editing) {
|
||||
cellContent = (
|
||||
<input className="mousetrap"
|
||||
onChange={this.handleChange}
|
||||
onBlur={this.handleBlur}
|
||||
ref={ref}
|
||||
defaultValue={this.props.value} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td className={cellClasses} ref={props.uid.join('_')}>
|
||||
<div className="reactTableCell">
|
||||
{cellContent}
|
||||
<span onDoubleClick={this.handleDoubleClick} onClick={this.handleClick}>
|
||||
{displayValue}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CellComponent.propTypes = {
|
||||
editing: PropTypes.bool,
|
||||
value: PropTypes.string,
|
||||
selected: PropTypes.bool,
|
||||
spreadsheetId: PropTypes.string,
|
||||
cellClasses: PropTypes.string,
|
||||
uid: PropTypes.array,
|
||||
onCellValueChange: PropTypes.func,
|
||||
handleSelectCell: PropTypes.func,
|
||||
handleDoubleClickOnCell: PropTypes.func,
|
||||
handleCellBlur: PropTypes.func
|
||||
};
|
||||
|
||||
|
||||
|
||||
export default CellComponent;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-type' content='text/html; charset=utf-8'>
|
||||
<title>React Spreadsheet Example</title>
|
||||
<link href='http://fonts.googleapis.com/css?family=Roboto:400,300' rel='stylesheet' type='text/css'>
|
||||
</head>
|
||||
<body>
|
||||
<div class="intro">
|
||||
<h2><a href="https://github.com/felixrieseberg/React-Spreadsheet-Component">React-Spreadsheet-Component</a></h2>
|
||||
<p>These are two rather simple examples of <a href="https://github.com/felixrieseberg/React-Spreadsheet-Component">React-Spreadsheet-Component</a>, a simple spreadsheet component in React (creative name, huh?). It's made with by Microsoft DX and released under the MIT License.</p>
|
||||
</div>
|
||||
<div class="example">
|
||||
<h3>Simple Example Spreadsheet</h3>
|
||||
<div id="exampleOne"></div>
|
||||
</div>
|
||||
<div class="example">
|
||||
<h3>Fancy Editable Spreadsheet</h3>
|
||||
<div id="exampleTwo"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,11 +1,13 @@
|
|||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var Spreadsheet = require('./lib/spreadsheet');
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Spreadsheet from './spreadsheet';
|
||||
import './styles/excel.css';
|
||||
import './styles/example.css';
|
||||
|
||||
|
||||
// Example One
|
||||
var exampleOne = {};
|
||||
let exampleOne = {};
|
||||
|
||||
exampleOne.initialData = {
|
||||
rows: [
|
||||
|
@ -31,7 +33,7 @@ exampleOne.config = {
|
|||
};
|
||||
|
||||
// Example Two
|
||||
var exampleTwo = {};
|
||||
let exampleTwo = {};
|
||||
exampleTwo.initialData = {
|
||||
rows: [
|
||||
['Customer', 'Job', 'Contact', 'City', 'Revenue'],
|
|
@ -1,23 +1,26 @@
|
|||
"use strict";
|
||||
|
||||
var React = require('react');
|
||||
import React, {PropTypes} from 'react';
|
||||
|
||||
var CellComponent = require('./cell');
|
||||
var Helpers = require('./helpers');
|
||||
import CellComponent from './cell';
|
||||
import Helpers from './helpers';
|
||||
|
||||
var RowComponent = React.createClass({
|
||||
class RowComponent extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
}
|
||||
/**
|
||||
* React Render method
|
||||
* @return {[JSX]} [JSX to render]
|
||||
*/
|
||||
render: function() {
|
||||
var config = this.props.config,
|
||||
render() {
|
||||
let config = this.props.config,
|
||||
cells = this.props.cells,
|
||||
columns = [],
|
||||
key, uid, selected, cellClasses, i;
|
||||
|
||||
if (!config.columns || cells.length === 0) {
|
||||
return console.error('Table can\'t be initialized without set number of columsn and no data!');
|
||||
|
||||
return console.error('Table can\'t be initialized without set number of columsn and no data!'); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
for (i = 0; i < cells.length; i = i + 1) {
|
||||
|
@ -27,23 +30,37 @@ var RowComponent = React.createClass({
|
|||
|
||||
key = 'row_' + this.props.uid + '_cell_' + i;
|
||||
uid = [this.props.uid, i];
|
||||
columns.push(<CellComponent key={key}
|
||||
columns.push(<CellComponent key={key}
|
||||
uid={uid}
|
||||
value={cells[i]}
|
||||
value={cells[i].toString()}
|
||||
config={config}
|
||||
cellClasses={cellClasses}
|
||||
onCellValueChange={this.props.onCellValueChange}
|
||||
onCellValueChange={this.props.onCellValueChange}
|
||||
handleSelectCell={this.props.handleSelectCell}
|
||||
handleDoubleClickOnCell={this.props.handleDoubleClickOnCell}
|
||||
handleCellBlur={this.props.handleCellBlur}
|
||||
spreadsheetId={this.props.spreadsheetId}
|
||||
selected={selected}
|
||||
selected={selected}
|
||||
editing={this.props.editing} />
|
||||
);
|
||||
}
|
||||
|
||||
return <tr>{columns}</tr>;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = RowComponent;
|
||||
RowComponent.propTypes = {
|
||||
config: PropTypes.object,
|
||||
editing: PropTypes.bool,
|
||||
cells: PropTypes.array,
|
||||
selected: PropTypes.array,
|
||||
uid: PropTypes.number,
|
||||
cellClasses: PropTypes.array,
|
||||
spreadsheetId: PropTypes.string,
|
||||
onCellValueChange: PropTypes.func,
|
||||
handleSelectCell: PropTypes.func,
|
||||
handleDoubleClickOnCell: PropTypes.func,
|
||||
handleCellBlur: PropTypes.func
|
||||
};
|
||||
|
||||
export default RowComponent;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var $ = require('jquery');
|
||||
import React, {PropTypes} from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import $ from 'jquery';
|
||||
|
||||
var RowComponent = require('./row');
|
||||
var Dispatcher = require('./dispatcher');
|
||||
var Helpers = require('./helpers');
|
||||
import RowComponent from './row';
|
||||
import Dispatcher from './dispatcher';
|
||||
import Helpers from './helpers';
|
||||
|
||||
var SpreadsheetComponent = React.createClass({
|
||||
spreadsheetId: null,
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
h2, h3 {
|
||||
color: #000
|
||||
}
|
||||
|
||||
h2 a {
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #F7F7F7;
|
||||
}
|
||||
|
||||
div.example {
|
||||
margin-bottom: 80px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
div.intro {
|
||||
margin-bottom: 80px;
|
||||
width: 700px;
|
||||
margin-left: 20px;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
div#exampleTwo td, div#exampleTwo th {
|
||||
width: 128px;
|
||||
height: 35px;
|
||||
border: none;
|
||||
text-align: left;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
div#exampleTwo th {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
div#exampleTwo span {
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
div#exampleTwo table {
|
||||
-webkit-box-shadow: 2px 2px 36px 0px rgba(0,0,0,0.39);
|
||||
-moz-box-shadow: 2px 2px 36px 0px rgba(0,0,0,0.39);
|
||||
box-shadow: 2px 2px 36px 0px rgba(0,0,0,0.39);
|
||||
border: none;
|
||||
padding: 8px;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
border: 1px solid #D7D7D7;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
div#exampleTwo table > tbody > tr:first-child > th {
|
||||
border-bottom: 1px solid #D8D8D8;
|
||||
border-right: none;
|
||||
text-transform: uppercase;
|
||||
text-align: left;
|
||||
height: 30px;
|
||||
font-weight: 300;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
div#exampleTwo table > tbody > tr > th {
|
||||
border-right: 1px solid #D8D8D8;
|
||||
}
|
||||
|
||||
div#exampleTwo table > tbody > tr > th:nth-child(2),
|
||||
div#exampleTwo table > tbody > tr > td:nth-child(2) {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
div#exampleTwo td input {
|
||||
background-color: #F0FAFF;
|
||||
}
|
||||
|
||||
div#exampleTwo tr:hover {
|
||||
background-color: #EEEEEE;
|
||||
}
|
||||
|
||||
div#exampleTwo input::selection {
|
||||
background: #F0FAFF;
|
||||
}
|
||||
|
||||
div#exampleTwo .selected {
|
||||
border-bottom: 2px solid #FF074E;
|
||||
}
|
||||
|
||||
div#exampleTwo .green {
|
||||
border-left: 13px solid #62B9A4;
|
||||
}
|
||||
|
||||
div#exampleTwo .purple {
|
||||
border-left: 13px solid #985DFF;
|
||||
}
|
||||
|
||||
div#exampleTwo .yellow {
|
||||
border-left: 13px solid #FF9E39;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// Dynamically set the webpack public path at runtime below
|
||||
// This magic global is used by webpack to set the public path at runtime.
|
||||
// The public path is set dynamically to avoid the following issues:
|
||||
// 1. https://github.com/coryhouse/react-slingshot/issues/205
|
||||
// 2. https://github.com/coryhouse/react-slingshot/issues/181
|
||||
// 3. https://github.com/coryhouse/react-slingshot/pull/125
|
||||
// Documentation: http://webpack.github.io/docs/configuration.html#output-publicpath
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_public_path__ = window.location.protocol + "//" + window.location.host + "/";
|
|
@ -0,0 +1 @@
|
|||
!browser-sync-ui/lib/plugins/history # need this for now because of https://github.com/yarnpkg/yarn/issues/1396#issuecomment-255965666
|
|
@ -0,0 +1,15 @@
|
|||
import webpack from 'webpack';
|
||||
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer';
|
||||
import config from '../webpack.config.prod';
|
||||
|
||||
config.plugins.push(new BundleAnalyzerPlugin());
|
||||
|
||||
const compiler = webpack(config);
|
||||
|
||||
compiler.run((error, stats) => {
|
||||
if (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
|
||||
console.log(stats); // eslint-disable-line no-console
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
// More info on Webpack's Node API here: https://webpack.github.io/docs/node.js-api.html
|
||||
// Allowing console calls below since this is a build file.
|
||||
/* eslint-disable no-console */
|
||||
import webpack from 'webpack';
|
||||
import config from '../webpack.config.prod';
|
||||
import {chalkError, chalkSuccess, chalkWarning, chalkProcessing} from './chalkConfig';
|
||||
|
||||
process.env.NODE_ENV = 'production'; // this assures React is built in prod mode and that the Babel dev config doesn't apply.
|
||||
|
||||
console.log(chalkProcessing('Generating minified bundle. This will take a moment...'));
|
||||
|
||||
webpack(config).run((error, stats) => {
|
||||
if (error) { // so a fatal error occurred. Stop here.
|
||||
console.log(chalkError(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const jsonStats = stats.toJson();
|
||||
|
||||
if (jsonStats.hasErrors) {
|
||||
return jsonStats.errors.map(error => console.log(chalkError(error)));
|
||||
}
|
||||
|
||||
if (jsonStats.hasWarnings) {
|
||||
console.log(chalkWarning('Webpack generated the following warnings: '));
|
||||
jsonStats.warnings.map(warning => console.log(chalkWarning(warning)));
|
||||
}
|
||||
|
||||
console.log(`Webpack stats: ${stats}`);
|
||||
|
||||
// if we got this far, the build succeeded.
|
||||
console.log(chalkSuccess('Your app is compiled in production mode in /dist. It\'s ready to roll!'));
|
||||
|
||||
return 0;
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
// Centralized configuration for chalk, which is used to add color to console.log statements.
|
||||
import chalk from 'chalk';
|
||||
export const chalkError = chalk.red;
|
||||
export const chalkSuccess = chalk.green;
|
||||
export const chalkWarning = chalk.yellow;
|
||||
export const chalkProcessing = chalk.blue;
|
|
@ -0,0 +1,27 @@
|
|||
// This file configures a web server for testing the production build
|
||||
// on your local machine.
|
||||
|
||||
import browserSync from 'browser-sync';
|
||||
import historyApiFallback from 'connect-history-api-fallback';
|
||||
import {chalkProcessing} from './chalkConfig';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
console.log(chalkProcessing('Opening production build...'));
|
||||
|
||||
// Run Browsersync
|
||||
browserSync({
|
||||
port: 3000,
|
||||
ui: {
|
||||
port: 3001
|
||||
},
|
||||
server: {
|
||||
baseDir: 'dist'
|
||||
},
|
||||
|
||||
files: [
|
||||
'src/*.html'
|
||||
],
|
||||
|
||||
middleware: [historyApiFallback()]
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
/* eslint-disable */
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
exec('node -v', function (err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
if (parseFloat(stdout.slice(1)) < 4) {
|
||||
throw new Error('React Slingshot requires node 4.0 or greater.');
|
||||
}
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
// This script removes demo app files
|
||||
import rimraf from 'rimraf';
|
||||
import fs from 'fs';
|
||||
import {chalkSuccess} from './chalkConfig';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const pathsToRemove = [
|
||||
'./src/actions/*',
|
||||
'./src/utils',
|
||||
'./src/components/*',
|
||||
'./src/constants/*',
|
||||
'./src/containers/*',
|
||||
'./src/images',
|
||||
'./src/reducers/*',
|
||||
'./src/store/store.spec.js',
|
||||
'./src/styles',
|
||||
'./src/routes.js',
|
||||
'./src/index.js',
|
||||
'./tools/removeDemo.js'
|
||||
];
|
||||
|
||||
const filesToCreate = [
|
||||
{
|
||||
path: './src/components/emptyTest.spec.js',
|
||||
content: '// Must have at least one test file in this directory or Mocha will throw an error.'
|
||||
},
|
||||
{
|
||||
path: './src/index.js',
|
||||
content: '// Set up your application entry point here...'
|
||||
},
|
||||
{
|
||||
path: './src/reducers/index.js',
|
||||
content: '// Set up your root reducer here...\n import { combineReducers } from \'redux\';\n export default combineReducers;'
|
||||
}
|
||||
];
|
||||
|
||||
function removePath(path, callback) {
|
||||
rimraf(path, error => {
|
||||
if (error) throw new Error(error);
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function createFile(file) {
|
||||
fs.writeFile(file.path, file.content, error => {
|
||||
if (error) throw new Error(error);
|
||||
});
|
||||
}
|
||||
|
||||
function removePackageJsonScriptEntry(scriptName) {
|
||||
const packageJsonPath = './package.json';
|
||||
let fileData = fs.readFileSync(packageJsonPath);
|
||||
let content = JSON.parse(fileData);
|
||||
delete content.scripts[scriptName];
|
||||
fs.writeFileSync(packageJsonPath,
|
||||
JSON.stringify(content, null, 2) + '\n');
|
||||
}
|
||||
|
||||
let numPathsRemoved = 0;
|
||||
pathsToRemove.map(path => {
|
||||
removePath(path, () => {
|
||||
numPathsRemoved++;
|
||||
if (numPathsRemoved === pathsToRemove.length) { // All paths have been processed
|
||||
// Now we can create files since we're done deleting.
|
||||
filesToCreate.map(file => createFile(file));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
removePackageJsonScriptEntry('remove-demo');
|
||||
|
||||
console.log(chalkSuccess('Demo app removed.'));
|
|
@ -0,0 +1,94 @@
|
|||
/* eslint-disable no-var */
|
||||
var rimraf = require('rimraf');
|
||||
var chalk = require('chalk');
|
||||
var replace = require("replace");
|
||||
var prompt = require("prompt");
|
||||
var prompts = require('./setupPrompts');
|
||||
|
||||
var chalkSuccess = chalk.green;
|
||||
var chalkProcessing = chalk.blue;
|
||||
var chalkWarn = chalk.red;
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
console.log(chalkSuccess('Dependencies installed.'));
|
||||
|
||||
prompt.start();
|
||||
|
||||
console.log(chalkWarn("WARNING: Preparing to delete local git repository..."));
|
||||
prompt.get([{name: 'deleteGit', description: "Delete the git repository? YES to continue or NO to skip."}], function(err, result) {
|
||||
var deleteGit;
|
||||
|
||||
if (err) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
deleteGit = result.deleteGit.toUpperCase();
|
||||
if (deleteGit === 'Y' || deleteGit === "YES") {
|
||||
// remove the original git repository
|
||||
rimraf('.git', error => {
|
||||
if (error) throw new Error(error);
|
||||
console.log(chalkSuccess('Original Git repository removed.\n'));
|
||||
|
||||
console.log(chalkProcessing('Updating package.json settings:'));
|
||||
|
||||
prompt.get(prompts, function(err, result) {
|
||||
// parse user responses
|
||||
// default values provided for fields that will cause npm to complain if left empty
|
||||
const responses = [
|
||||
{
|
||||
key: 'name',
|
||||
value: result.projectName || 'new-project'
|
||||
},
|
||||
{
|
||||
key: 'version',
|
||||
value: result.version || '0.1.0'
|
||||
},
|
||||
{
|
||||
key: 'author',
|
||||
value: result.author
|
||||
},
|
||||
{
|
||||
key: 'license',
|
||||
value: result.license || 'MIT'
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
value: result.description
|
||||
},
|
||||
// simply use an empty URL here to clear the existing repo URL
|
||||
{
|
||||
key: 'url',
|
||||
value: ''
|
||||
}
|
||||
];
|
||||
|
||||
// update package.json with the user's values
|
||||
responses.forEach(res => {
|
||||
replace({
|
||||
regex: `("${res.key}"): "(.*?)"`,
|
||||
replacement: `$1: "${res.value}"`,
|
||||
paths: ['package.json'],
|
||||
recursive: false,
|
||||
silent: true
|
||||
});
|
||||
});
|
||||
|
||||
// reset package.json 'keywords' field to empty state
|
||||
replace({
|
||||
regex: /"keywords": \[[\s\S]+\]/,
|
||||
replacement: `"keywords": []`,
|
||||
paths: ['package.json'],
|
||||
recursive: false,
|
||||
silent: true
|
||||
});
|
||||
|
||||
// remove all setup scripts from the 'tools' folder
|
||||
console.log(chalkSuccess('\nSetup complete! Cleaning up...\n'));
|
||||
rimraf('./tools/setup', error => {
|
||||
if (error) throw new Error(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
// This script displays an intro message for the setup script
|
||||
/* eslint-disable no-console */
|
||||
console.log('===========================');
|
||||
console.log('= React Slingshot Setup =');
|
||||
console.log('===========================\n');
|
||||
console.log('Installing dependencies. Please wait...');
|
|
@ -0,0 +1,26 @@
|
|||
// Define prompts for use with npm 'prompt' module in setup script
|
||||
module.exports = [
|
||||
{
|
||||
name: 'projectName',
|
||||
description: 'Project name (default: new-project)',
|
||||
pattern: /^[^._][a-z0-9\.\-_~]+$/,
|
||||
message: 'Limited to: lowercase letters, numbers, period, hyphen, ' +
|
||||
'underscore, and tilde; cannot begin with period or underscore.'
|
||||
},
|
||||
{
|
||||
name: 'version',
|
||||
description: 'Version (default: 0.1.0)'
|
||||
},
|
||||
{
|
||||
name: 'author',
|
||||
description: 'Author'
|
||||
},
|
||||
{
|
||||
name: 'license',
|
||||
description: 'License (default: MIT)'
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
description: 'Project description'
|
||||
}
|
||||
];
|
|
@ -0,0 +1,59 @@
|
|||
// This file configures the development web server
|
||||
// which supports hot reloading and synchronized testing.
|
||||
|
||||
// Require Browsersync along with webpack and middleware for it
|
||||
import browserSync from 'browser-sync';
|
||||
// Required for react-router browserHistory
|
||||
// see https://github.com/BrowserSync/browser-sync/issues/204#issuecomment-102623643
|
||||
import historyApiFallback from 'connect-history-api-fallback';
|
||||
import webpack from 'webpack';
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware';
|
||||
import webpackHotMiddleware from 'webpack-hot-middleware';
|
||||
import config from '../webpack.config.dev';
|
||||
|
||||
const bundler = webpack(config);
|
||||
|
||||
// Run Browsersync and use middleware for Hot Module Replacement
|
||||
browserSync({
|
||||
port: 3000,
|
||||
ui: {
|
||||
port: 3001
|
||||
},
|
||||
server: {
|
||||
baseDir: 'src',
|
||||
|
||||
middleware: [
|
||||
historyApiFallback(),
|
||||
|
||||
webpackDevMiddleware(bundler, {
|
||||
// Dev middleware can't access config, so we provide publicPath
|
||||
publicPath: config.output.publicPath,
|
||||
|
||||
// These settings suppress noisy webpack output so only errors are displayed to the console.
|
||||
noInfo: false,
|
||||
quiet: false,
|
||||
stats: {
|
||||
assets: false,
|
||||
colors: true,
|
||||
version: false,
|
||||
hash: false,
|
||||
timings: false,
|
||||
chunks: false,
|
||||
chunkModules: false
|
||||
},
|
||||
|
||||
// for other settings see
|
||||
// http://webpack.github.io/docs/webpack-dev-middleware.html
|
||||
}),
|
||||
|
||||
// bundler should be the same as above
|
||||
webpackHotMiddleware(bundler)
|
||||
]
|
||||
},
|
||||
|
||||
// no need to watch '*.js' here, webpack will take care of it for us,
|
||||
// including full page reloads if HMR won't work
|
||||
files: [
|
||||
'src/*.html'
|
||||
]
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
import {chalkSuccess} from './chalkConfig';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
console.log(chalkSuccess('Starting app in dev mode...'));
|
|
@ -0,0 +1,22 @@
|
|||
// Tests are placed alongside files under test.
|
||||
// This file does the following:
|
||||
// 1. Sets the environment to 'test' so that
|
||||
// dev-specific babel config in .babelrc doesn't run.
|
||||
// 2. Disables Webpack-specific features that Mocha doesn't understand.
|
||||
// 3. Registers babel for transpiling our code for testing.
|
||||
|
||||
// This assures the .babelrc dev config (which includes
|
||||
// hot module reloading code) doesn't apply for tests.
|
||||
// Setting NODE_ENV to test instead of production because setting it to production will suppress error messaging
|
||||
// and propType validation warnings.
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
// Disable webpack-specific features for tests since
|
||||
// Mocha doesn't know what to do with them.
|
||||
['.css', '.scss', '.png', '.jpg'].forEach(ext => {
|
||||
require.extensions[ext] = () => null;
|
||||
});
|
||||
|
||||
// Register babel so that it will transpile ES6 to ES5
|
||||
// before our tests run.
|
||||
require('babel-register')();
|
|
@ -0,0 +1,55 @@
|
|||
import webpack from 'webpack';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import path from 'path';
|
||||
|
||||
export default {
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx', '.json']
|
||||
},
|
||||
debug: true,
|
||||
devtool: 'eval-source-map', // more info:https://webpack.github.io/docs/build-performance.html#sourcemaps and https://webpack.github.io/docs/configuration.html#devtool
|
||||
noInfo: true, // set to false to see a list of every file being bundled.
|
||||
entry: [
|
||||
// must be first entry to properly set public path
|
||||
'./src/webpack-public-path',
|
||||
'webpack-hot-middleware/client?reload=true',
|
||||
path.resolve(__dirname, 'src/index.js') // Defining path seems necessary for this to work consistently on Windows machines.
|
||||
],
|
||||
target: 'web', // necessary per https://webpack.github.io/docs/testing.html#compile-and-test
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'), // Note: Physical files are only output by the production build task `npm run build`.
|
||||
publicPath: '/',
|
||||
filename: 'bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify('development'), // Tells React to build in either dev or prod modes. https://facebook.github.io/react/downloads.html (See bottom)
|
||||
__DEV__: true
|
||||
}),
|
||||
new webpack.HotModuleReplacementPlugin(),
|
||||
new webpack.NoErrorsPlugin(),
|
||||
new HtmlWebpackPlugin({ // Create HTML file that includes references to bundled CSS and JS.
|
||||
template: 'src/index.html',
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true
|
||||
},
|
||||
inject: true
|
||||
})
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel']},
|
||||
{test: /\.eot(\?v=\d+.\d+.\d+)?$/, loader: 'file'},
|
||||
{test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url?limit=10000&mimetype=application/font-woff"},
|
||||
{test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream'},
|
||||
{test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml'},
|
||||
{test: /\.(jpe?g|png|gif)$/i, loader: 'file?name=[name].[ext]'},
|
||||
{test: /\.ico$/, loader: 'file?name=[name].[ext]'},
|
||||
{test: /(\.css|\.scss)$/, loaders: ['style', 'css?sourceMap', 'postcss', 'sass?sourceMap']},
|
||||
{test: /\.json$/, loader: "json"}
|
||||
]
|
||||
},
|
||||
postcss: ()=> [autoprefixer]
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
// For info about this file refer to webpack and webpack-hot-middleware documentation
|
||||
// For info on how we're generating bundles with hashed filenames for cache busting: https://medium.com/@okonetchnikov/long-term-caching-of-static-assets-with-webpack-1ecb139adb95#.w99i89nsz
|
||||
import webpack from 'webpack';
|
||||
import ExtractTextPlugin from 'extract-text-webpack-plugin';
|
||||
import WebpackMd5Hash from 'webpack-md5-hash';
|
||||
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import path from 'path';
|
||||
|
||||
const GLOBALS = {
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
__DEV__: false
|
||||
};
|
||||
|
||||
export default {
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx', '.json']
|
||||
},
|
||||
debug: true,
|
||||
devtool: 'source-map', // more info:https://webpack.github.io/docs/build-performance.html#sourcemaps and https://webpack.github.io/docs/configuration.html#devtool
|
||||
noInfo: true, // set to false to see a list of every file being bundled.
|
||||
entry: path.resolve(__dirname, 'src/index'),
|
||||
target: 'web', // necessary per https://webpack.github.io/docs/testing.html#compile-and-test
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
publicPath: '/',
|
||||
filename: '[name].[chunkhash].js'
|
||||
},
|
||||
plugins: [
|
||||
// Hash the files using MD5 so that their names change when the content changes.
|
||||
new WebpackMd5Hash(),
|
||||
|
||||
// Optimize the order that items are bundled. This assures the hash is deterministic.
|
||||
new webpack.optimize.OccurenceOrderPlugin(),
|
||||
|
||||
// Tells React to build in prod mode. https://facebook.github.io/react/downloads.html
|
||||
new webpack.DefinePlugin(GLOBALS),
|
||||
|
||||
// Generate an external css file with a hash in the filename
|
||||
new ExtractTextPlugin('[name].[contenthash].css'),
|
||||
|
||||
// Generate HTML file that contains references to generated bundles. See here for how this works: https://github.com/ampedandwired/html-webpack-plugin#basic-usage
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'src/index.html',
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true
|
||||
},
|
||||
inject: true,
|
||||
// Note that you can add custom options here if you need to handle other custom logic in index.html
|
||||
// To track JavaScript errors via TrackJS, sign up for a free trial at TrackJS.com and enter your token below.
|
||||
trackJSToken: ''
|
||||
}),
|
||||
|
||||
// Eliminate duplicate packages when generating bundle
|
||||
new webpack.optimize.DedupePlugin(),
|
||||
|
||||
// Minify JS
|
||||
new webpack.optimize.UglifyJsPlugin()
|
||||
],
|
||||
module: {
|
||||
loaders: [
|
||||
{test: /\.jsx?$/, exclude: /node_modules/, loader: 'babel'},
|
||||
{test: /\.eot(\?v=\d+.\d+.\d+)?$/, loader: 'url?name=[name].[ext]'},
|
||||
{test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url?limit=10000&mimetype=application/font-woff&name=[name].[ext]"},
|
||||
{test: /\.ttf(\?v=\d+.\d+.\d+)?$/, loader: 'url?limit=10000&mimetype=application/octet-stream&name=[name].[ext]'},
|
||||
{test: /\.svg(\?v=\d+.\d+.\d+)?$/, loader: 'url?limit=10000&mimetype=image/svg+xml&name=[name].[ext]'},
|
||||
{test: /\.(jpe?g|png|gif)$/i, loader: 'file?name=[name].[ext]'},
|
||||
{test: /\.ico$/, loader: 'file?name=[name].[ext]'},
|
||||
{test: /(\.css|\.scss)$/, loader: ExtractTextPlugin.extract('css?sourceMap!postcss!sass?sourceMap')},
|
||||
{test: /\.json$/, loader: "json"}
|
||||
]
|
||||
},
|
||||
postcss: ()=> [autoprefixer]
|
||||
};
|
Загрузка…
Ссылка в новой задаче