Merge branch 'react-pr' of https://github.com/netlify/netlify-cms into react-pr
This commit is contained in:
commit
4108b88315
28
package.json
28
package.json
@ -4,13 +4,23 @@
|
|||||||
"description": "Netlify CMS lets content editors work on structured content stored in git",
|
"description": "Netlify CMS lets content editors work on structured content stored in git",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "webpack-dev-server -d --inline --config webpack.config.js",
|
"start": "webpack-dev-server --config webpack.config.js",
|
||||||
"test": "NODE_ENV=test mocha --recursive --compilers js:babel-register --require ./test/setup.js",
|
"test": "NODE_ENV=test mocha --recursive --compilers js:babel-register --require ./test/setup.js",
|
||||||
"test:watch": "npm test -- --watch",
|
"test:watch": "npm test -- --watch",
|
||||||
"build": "webpack --config webpack.config.js",
|
"build": "webpack --config webpack.config.js",
|
||||||
"storybook": "start-storybook -p 9001",
|
"storybook": "start-storybook -p 9001",
|
||||||
"storybook-build": "build-storybook -o dist"
|
"storybook-build": "build-storybook -o dist",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"lint:fix": "npm run lint -- --fix",
|
||||||
|
"lint:staged": "lint-staged"
|
||||||
},
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.@(js|jsx)": [
|
||||||
|
"eslint --fix",
|
||||||
|
"git add"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"pre-commit": "lint:staged",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"netlify",
|
"netlify",
|
||||||
"cms"
|
"cms"
|
||||||
@ -21,7 +31,7 @@
|
|||||||
"@kadira/storybook": "^1.36.0",
|
"@kadira/storybook": "^1.36.0",
|
||||||
"autoprefixer": "^6.3.3",
|
"autoprefixer": "^6.3.3",
|
||||||
"babel-core": "^6.5.1",
|
"babel-core": "^6.5.1",
|
||||||
"babel-eslint": "^4.1.8",
|
"babel-eslint": "^6.1.2",
|
||||||
"babel-loader": "^6.2.2",
|
"babel-loader": "^6.2.2",
|
||||||
"babel-plugin-lodash": "^3.2.0",
|
"babel-plugin-lodash": "^3.2.0",
|
||||||
"babel-plugin-transform-class-properties": "^6.5.2",
|
"babel-plugin-transform-class-properties": "^6.5.2",
|
||||||
@ -32,24 +42,24 @@
|
|||||||
"babel-register": "^6.5.2",
|
"babel-register": "^6.5.2",
|
||||||
"babel-runtime": "^6.5.0",
|
"babel-runtime": "^6.5.0",
|
||||||
"css-loader": "^0.23.1",
|
"css-loader": "^0.23.1",
|
||||||
"eslint": "^1.10.3",
|
"eslint": "^3.5.0",
|
||||||
"eslint-loader": "^1.2.1",
|
|
||||||
"eslint-plugin-react": "^5.1.1",
|
"eslint-plugin-react": "^5.1.1",
|
||||||
"exports-loader": "^0.6.3",
|
"exports-loader": "^0.6.3",
|
||||||
"extract-text-webpack-plugin": "^1.0.1",
|
|
||||||
"express": "^4.13.4",
|
|
||||||
"file-loader": "^0.8.5",
|
"file-loader": "^0.8.5",
|
||||||
"immutable": "^3.7.6",
|
"immutable": "^3.7.6",
|
||||||
"imports-loader": "^0.6.5",
|
"imports-loader": "^0.6.5",
|
||||||
"js-yaml": "^3.5.3",
|
"js-yaml": "^3.5.3",
|
||||||
|
"lint-staged": "^3.0.2",
|
||||||
"mocha": "^2.4.5",
|
"mocha": "^2.4.5",
|
||||||
"moment": "^2.11.2",
|
"moment": "^2.11.2",
|
||||||
"normalizr": "^2.0.0",
|
"normalizr": "^2.0.0",
|
||||||
"postcss-cssnext": "^2.7.0",
|
"postcss-cssnext": "^2.7.0",
|
||||||
"postcss-import": "^8.1.2",
|
"postcss-import": "^8.1.2",
|
||||||
"postcss-loader": "^0.9.1",
|
"postcss-loader": "^0.9.1",
|
||||||
|
"pre-commit": "^1.1.3",
|
||||||
"react": "^15.1.0",
|
"react": "^15.1.0",
|
||||||
"react-dom": "^15.1.0",
|
"react-dom": "^15.1.0",
|
||||||
|
"react-hot-loader": "^3.0.0-beta.2",
|
||||||
"react-immutable-proptypes": "^1.6.0",
|
"react-immutable-proptypes": "^1.6.0",
|
||||||
"react-lazy-load": "^3.0.3",
|
"react-lazy-load": "^3.0.3",
|
||||||
"react-pure-render": "^1.0.2",
|
"react-pure-render": "^1.0.2",
|
||||||
@ -60,8 +70,8 @@
|
|||||||
"redux-thunk": "^1.0.3",
|
"redux-thunk": "^1.0.3",
|
||||||
"style-loader": "^0.13.0",
|
"style-loader": "^0.13.0",
|
||||||
"url-loader": "^0.5.7",
|
"url-loader": "^0.5.7",
|
||||||
"webpack": "^1.12.13",
|
"webpack": "^1.13.2",
|
||||||
"webpack-dev-server": "^1.14.1",
|
"webpack-dev-server": "^1.15.1",
|
||||||
"webpack-postcss-tools": "^1.1.1",
|
"webpack-postcss-tools": "^1.1.1",
|
||||||
"whatwg-fetch": "^1.0.0"
|
"whatwg-fetch": "^1.0.0"
|
||||||
},
|
},
|
||||||
|
@ -21,9 +21,9 @@ export default class AuthenticationPage extends React.Component {
|
|||||||
auth = new Authenticator();
|
auth = new Authenticator();
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.authenticate({provider: 'github', scope: 'repo'}, (err, data) => {
|
auth.authenticate({ provider: 'github', scope: 'repo' }, (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.setState({loginError: err.toString()});
|
this.setState({ loginError: err.toString() });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.props.onLogin(data);
|
this.props.onLogin(data);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Authenticator from '../../lib/netlify-auth';
|
|
||||||
|
|
||||||
export default class AuthenticationPage extends React.Component {
|
export default class AuthenticationPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -14,8 +13,8 @@ export default class AuthenticationPage extends React.Component {
|
|||||||
|
|
||||||
handleLogin(e) {
|
handleLogin(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const {email, password} = this.state;
|
const { email, password } = this.state;
|
||||||
this.setState({authenticating: true});
|
this.setState({ authenticating: true });
|
||||||
fetch(`${AuthenticationPage.url}/token`, {
|
fetch(`${AuthenticationPage.url}/token`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: 'grant_type=client_credentials',
|
body: 'grant_type=client_credentials',
|
||||||
@ -27,18 +26,18 @@ export default class AuthenticationPage extends React.Component {
|
|||||||
console.log(response);
|
console.log(response);
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return response.json().then((data) => {
|
return response.json().then((data) => {
|
||||||
this.props.onLogin(Object.assign({email}, data));
|
this.props.onLogin(Object.assign({ email }, data));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
response.json().then((data) => {
|
response.json().then((data) => {
|
||||||
this.setState({loginError: data.msg});
|
this.setState({ loginError: data.msg });
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(key) {
|
handleChange(key) {
|
||||||
return (e) => {
|
return (e) => {
|
||||||
this.setState({[key]: e.target.value});
|
this.setState({ [key]: e.target.value });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ export default class AuthenticationPage extends React.Component {
|
|||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {email: ''};
|
this.state = { email: '' };
|
||||||
this.handleLogin = this.handleLogin.bind(this);
|
this.handleLogin = this.handleLogin.bind(this);
|
||||||
this.handleEmailChange = this.handleEmailChange.bind(this);
|
this.handleEmailChange = this.handleEmailChange.bind(this);
|
||||||
}
|
}
|
||||||
@ -18,7 +18,7 @@ export default class AuthenticationPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleEmailChange(e) {
|
handleEmailChange(e) {
|
||||||
this.setState({email: e.target.value});
|
this.setState({ email: e.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -27,14 +27,14 @@ export default class EntryEditor extends React.Component {
|
|||||||
calculateHeight() {
|
calculateHeight() {
|
||||||
const height = window.innerHeight - 54;
|
const height = window.innerHeight - 54;
|
||||||
console.log('setting height to %s', height);
|
console.log('setting height to %s', height);
|
||||||
this.setState({height});
|
this.setState({ height });
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { collection, entry, getMedia, onChange, onAddMedia, onRemoveMedia, onPersist } = this.props;
|
const { collection, entry, getMedia, onChange, onAddMedia, onRemoveMedia, onPersist } = this.props;
|
||||||
const {height} = this.state;
|
const { height } = this.state;
|
||||||
|
|
||||||
return <div className={styles.entryEditor} style={{height}}>
|
return <div className={styles.entryEditor} style={{ height }}>
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<div className={styles.controlPane}>
|
<div className={styles.controlPane}>
|
||||||
<ControlPane
|
<ControlPane
|
||||||
|
@ -55,7 +55,7 @@ export default class PreviewPane extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderPreview() {
|
renderPreview() {
|
||||||
const props = Object.assign({}, this.props, {widgetFor: this.widgetFor});
|
const props = Object.assign({}, this.props, { widgetFor: this.widgetFor });
|
||||||
const component = registry.getPreviewTemplate(props.collection.get('name')) || Preview;
|
const component = registry.getPreviewTemplate(props.collection.get('name')) || Preview;
|
||||||
|
|
||||||
render(React.createElement(component, props), this.previewEl);
|
render(React.createElement(component, props), this.previewEl);
|
||||||
|
@ -6,7 +6,7 @@ const availableIcons = [
|
|||||||
'bold', 'italic', 'list', 'font', 'text-height', 'text-width', 'align-left', 'align-center', 'align-right',
|
'bold', 'italic', 'list', 'font', 'text-height', 'text-width', 'align-left', 'align-center', 'align-right',
|
||||||
'align-justify', 'indent-left', 'indent-right', 'list-bullet', 'list-numbered', 'strike', 'underline', 'table',
|
'align-justify', 'indent-left', 'indent-right', 'list-bullet', 'list-numbered', 'strike', 'underline', 'table',
|
||||||
'superscript', 'subscript', 'header', 'h1', 'h2', 'paragraph', 'link', 'unlink', 'quote-left', 'quote-right', 'code',
|
'superscript', 'subscript', 'header', 'h1', 'h2', 'paragraph', 'link', 'unlink', 'quote-left', 'quote-right', 'code',
|
||||||
'picture','video',
|
'picture', 'video',
|
||||||
// Entypo
|
// Entypo
|
||||||
'note', 'note-beamed',
|
'note', 'note-beamed',
|
||||||
'music',
|
'music',
|
||||||
@ -199,7 +199,7 @@ const iconPropType = (props, propName) => {
|
|||||||
|
|
||||||
const noop = function() {};
|
const noop = function() {};
|
||||||
|
|
||||||
export default function Icon({ style, className = '', type, onClick = noop}) {
|
export default function Icon({ style, className = '', type, onClick = noop }) {
|
||||||
return <span className={`${styles.root} ${styles[type]} ${className}`} style={style} onClick={onClick} />;
|
return <span className={`${styles.root} ${styles[type]} ${className}`} style={style} onClick={onClick} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class MarkdownControl extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
{ this.renderEditor() }
|
{this.renderEditor()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -62,4 +62,4 @@ export const SCHEMA = {
|
|||||||
borderRadius: '4px'
|
borderRadius: '4px'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
@ -46,7 +46,7 @@ function processEditorPlugins(plugins) {
|
|||||||
<div {...props.attributes} className={className}>
|
<div {...props.attributes} className={className}>
|
||||||
<div className="plugin_icon" contentEditable={false}><Icon type={plugin.icon}/></div>
|
<div className="plugin_icon" contentEditable={false}><Icon type={plugin.icon}/></div>
|
||||||
<div className="plugin_fields" contentEditable={false}>
|
<div className="plugin_fields" contentEditable={false}>
|
||||||
{ plugin.fields.map(field => `${field.label}: “${node.data.get(field.name)}”`) }
|
{plugin.fields.map(field => `${field.label}: “${node.data.get(field.name)}”`)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -39,4 +39,4 @@ storiesOf('Card', module)
|
|||||||
<p>header and footer elements are also not subject to margin</p>
|
<p>header and footer elements are also not subject to margin</p>
|
||||||
<footer style={styles.footer}>© Thousand Cats Corp</footer>
|
<footer style={styles.footer}>© Thousand Cats Corp</footer>
|
||||||
</Card>
|
</Card>
|
||||||
))
|
));
|
||||||
|
@ -57,7 +57,7 @@ class EntryPage extends React.Component {
|
|||||||
const {
|
const {
|
||||||
entry, entryDraft, boundGetMedia, collection, changeDraft, addMedia, removeMedia
|
entry, entryDraft, boundGetMedia, collection, changeDraft, addMedia, removeMedia
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (entryDraft == null || entryDraft.get('entry') == undefined || entry && entry.get('isFetching')) {
|
if (entryDraft == null || entryDraft.get('entry') == undefined || entry && entry.get('isFetching')) {
|
||||||
return <div>Loading...</div>;
|
return <div>Loading...</div>;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,12 @@ class FindBar extends Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this._compiledCommands = [];
|
this._compiledCommands = [];
|
||||||
this._searchCommand = { search: true, regexp:`(?:${SEARCH})?(.*)`, param:{ name:'searchTerm', display:'' }, token: SEARCH };
|
this._searchCommand = {
|
||||||
|
search: true,
|
||||||
|
regexp: `(?:${SEARCH})?(.*)`,
|
||||||
|
param: { name: 'searchTerm', display: '' },
|
||||||
|
token: SEARCH
|
||||||
|
};
|
||||||
this.state = {
|
this.state = {
|
||||||
value: '',
|
value: '',
|
||||||
placeholder: PLACEHOLDER,
|
placeholder: PLACEHOLDER,
|
||||||
@ -68,7 +73,7 @@ class FindBar extends Component {
|
|||||||
|
|
||||||
if (match && match[1]) {
|
if (match && match[1]) {
|
||||||
regexp += '(.*)';
|
regexp += '(.*)';
|
||||||
param = { name:match[1], display:match[2] || this._camelCaseToSpace(match[1]) };
|
param = { name: match[1], display: match[2] || this._camelCaseToSpace(match[1]) };
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.assign({}, command, {
|
return Object.assign({}, command, {
|
||||||
@ -144,6 +149,7 @@ class FindBar extends Component {
|
|||||||
getSuggestions() {
|
getSuggestions() {
|
||||||
return this._getSuggestions(this.state.value, this.state.activeScope, this._compiledCommands, this.props.defaultCommands);
|
return this._getSuggestions(this.state.value, this.state.activeScope, this._compiledCommands, this.props.defaultCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memoized version
|
// Memoized version
|
||||||
_getSuggestions(value, scope, commands, defaultCommands) {
|
_getSuggestions(value, scope, commands, defaultCommands) {
|
||||||
if (scope) return []; // No autocomplete for scoped input
|
if (scope) return []; // No autocomplete for scoped input
|
||||||
@ -152,7 +158,7 @@ class FindBar extends Component {
|
|||||||
.filter(command => defaultCommands.indexOf(command.id) !== -1)
|
.filter(command => defaultCommands.indexOf(command.id) !== -1)
|
||||||
.map(result => (
|
.map(result => (
|
||||||
Object.assign({}, result, { string: result.token }
|
Object.assign({}, result, { string: result.token }
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = fuzzy.filter(value, commands, {
|
const results = fuzzy.filter(value, commands, {
|
||||||
@ -162,8 +168,8 @@ class FindBar extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const returnResults = results.slice(0, 4).map(result => (
|
const returnResults = results.slice(0, 4).map(result => (
|
||||||
Object.assign({}, result.original, { string:result.string }
|
Object.assign({}, result.original, { string: result.string }
|
||||||
)));
|
)));
|
||||||
returnResults.push(this._searchCommand);
|
returnResults.push(this._searchCommand);
|
||||||
|
|
||||||
return returnResults;
|
return returnResults;
|
||||||
@ -178,7 +184,7 @@ class FindBar extends Component {
|
|||||||
index = (
|
index = (
|
||||||
highlightedIndex === this.getSuggestions().length - 1 ||
|
highlightedIndex === this.getSuggestions().length - 1 ||
|
||||||
this.state.isOpen === false
|
this.state.isOpen === false
|
||||||
) ? 0 : highlightedIndex + 1;
|
) ? 0 : highlightedIndex + 1;
|
||||||
this.setState({
|
this.setState({
|
||||||
highlightedIndex: index,
|
highlightedIndex: index,
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
@ -290,7 +296,7 @@ class FindBar extends Component {
|
|||||||
let children;
|
let children;
|
||||||
if (!command.search) {
|
if (!command.search) {
|
||||||
children = (
|
children = (
|
||||||
<span><span dangerouslySetInnerHTML={{__html: command.string}} /></span>
|
<span><span dangerouslySetInnerHTML={{ __html: command.string }}/></span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
children = (
|
children = (
|
||||||
@ -299,7 +305,8 @@ class FindBar extends Component {
|
|||||||
<span><Icon type="search"/>Search... </span> :
|
<span><Icon type="search"/>Search... </span> :
|
||||||
<span className={styles.faded}><Icon type="search"/>Search for: </span>
|
<span className={styles.faded}><Icon type="search"/>Search for: </span>
|
||||||
}
|
}
|
||||||
<strong>{this.state.value}</strong></span>
|
<strong>{this.state.value}</strong>
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@ -317,7 +324,7 @@ class FindBar extends Component {
|
|||||||
return commands.length === 0 ? null : (
|
return commands.length === 0 ? null : (
|
||||||
<div className={styles.menu}>
|
<div className={styles.menu}>
|
||||||
<div className={styles.suggestions}>
|
<div className={styles.suggestions}>
|
||||||
{ commands }
|
{commands}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.history}>
|
<div className={styles.history}>
|
||||||
Your past searches and commands
|
Your past searches and commands
|
||||||
@ -328,7 +335,7 @@ class FindBar extends Component {
|
|||||||
|
|
||||||
renderActiveScope() {
|
renderActiveScope() {
|
||||||
if (this.state.activeScope === SEARCH) {
|
if (this.state.activeScope === SEARCH) {
|
||||||
return <div className={styles.inputScope}><Icon type="search"/> </div>;
|
return <div className={styles.inputScope}><Icon type="search"/></div>;
|
||||||
} else {
|
} else {
|
||||||
return <div className={styles.inputScope}>{this.state.activeScope}</div>;
|
return <div className={styles.inputScope}>{this.state.activeScope}</div>;
|
||||||
}
|
}
|
||||||
@ -358,6 +365,7 @@ class FindBar extends Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FindBar.propTypes = {
|
FindBar.propTypes = {
|
||||||
commands: PropTypes.arrayOf(PropTypes.shape({
|
commands: PropTypes.arrayOf(PropTypes.shape({
|
||||||
id: PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
|
@ -41,6 +41,6 @@ export default class YAML {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toFile(data) {
|
toFile(data) {
|
||||||
return yaml.safeDump(data, {schema: OutputSchema});
|
return yaml.safeDump(data, { schema: OutputSchema });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ html {
|
|||||||
-ms-text-size-adjust: 100%;
|
-ms-text-size-adjust: 100%;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: Roboto,"Helvetica Neue",HelveticaNeue,Helvetica,Arial,sans-serif;
|
font-family: Roboto, "Helvetica Neue", HelveticaNeue, Helvetica, Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
*, *:before, *:after {
|
*, *:before, *:after {
|
||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
@ -13,15 +14,15 @@ body {
|
|||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: #f2f5f4;
|
background-color: #f2f5f4;
|
||||||
color:#7c8382;
|
color: #7c8382;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
background-color: #272e30;
|
background-color: #272e30;
|
||||||
box-shadow: 0 1px 2px 0 rgba(0,0,0,0.22);
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.22);
|
||||||
height: 54px;
|
height: 54px;
|
||||||
border-bottom:2px solid #3ab7a5;
|
border-bottom: 2px solid #3ab7a5;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
@ -35,23 +36,24 @@ h1, h2, h3, h4, h5, h6, p {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1{
|
h1 {
|
||||||
color: #3ab7a5;
|
color: #3ab7a5;
|
||||||
border-bottom: 1px solid #3ab7a5;
|
border-bottom: 1px solid #3ab7a5;
|
||||||
margin: 30px auto 25px;
|
margin: 30px auto 25px;
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
header input{
|
header input {
|
||||||
margin-bottom:0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
button{
|
|
||||||
|
button {
|
||||||
border: 1px solid #3ab7a5;
|
border: 1px solid #3ab7a5;
|
||||||
padding: 3px 20px;
|
padding: 3px 20px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
background-color:#fff;
|
background-color: #fff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +122,7 @@ button{
|
|||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
z-index: 99999 !important;
|
z-index: 99999 !important;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,.1);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, .1);
|
||||||
border: 1px solid #f9f9f9;
|
border: 1px solid #f9f9f9;
|
||||||
}
|
}
|
||||||
& .rdtOpen .rdtPicker {
|
& .rdtOpen .rdtPicker {
|
||||||
@ -217,10 +219,10 @@ button{
|
|||||||
& .rdtNext span {
|
& .rdtNext span {
|
||||||
display: block;
|
display: block;
|
||||||
-webkit-touch-callout: none; /* iOS Safari */
|
-webkit-touch-callout: none; /* iOS Safari */
|
||||||
-webkit-user-select: none; /* Chrome/Safari/Opera */
|
-webkit-user-select: none; /* Chrome/Safari/Opera */
|
||||||
-khtml-user-select: none; /* Konqueror */
|
-khtml-user-select: none; /* Konqueror */
|
||||||
-moz-user-select: none; /* Firefox */
|
-moz-user-select: none; /* Firefox */
|
||||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,10 +295,10 @@ button{
|
|||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
-webkit-touch-callout: none; /* iOS Safari */
|
-webkit-touch-callout: none; /* iOS Safari */
|
||||||
-webkit-user-select: none; /* Chrome/Safari/Opera */
|
-webkit-user-select: none; /* Chrome/Safari/Opera */
|
||||||
-khtml-user-select: none; /* Konqueror */
|
-khtml-user-select: none; /* Konqueror */
|
||||||
-moz-user-select: none; /* Firefox */
|
-moz-user-select: none; /* Firefox */
|
||||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
& .rdtCounter .rdtBtn:hover {
|
& .rdtCounter .rdtBtn:hover {
|
||||||
|
32
src/index.js
32
src/index.js
@ -1,31 +1,33 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { AppContainer } from 'react-hot-loader'
|
||||||
import { Router } from 'react-router';
|
import Root from './root'
|
||||||
import registry from './lib/registry';
|
import registry from './lib/registry';
|
||||||
import configureStore from './store/configureStore';
|
|
||||||
import routes from './routing/routes';
|
|
||||||
import history, { syncHistory } from './routing/history';
|
|
||||||
import 'file?name=index.html!../example/index.html';
|
import 'file?name=index.html!../example/index.html';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
const store = configureStore();
|
// Create mount element dynamically
|
||||||
|
|
||||||
// Create an enhanced history that syncs navigation events with the store
|
|
||||||
syncHistory(store);
|
|
||||||
|
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.id = 'root';
|
el.id = 'root';
|
||||||
document.body.appendChild(el);
|
document.body.appendChild(el);
|
||||||
|
|
||||||
render((
|
render((
|
||||||
<Provider store={store}>
|
<AppContainer>
|
||||||
<Router history={history}>
|
<Root />
|
||||||
{routes}
|
</AppContainer>
|
||||||
</Router>
|
|
||||||
</Provider>
|
|
||||||
), el);
|
), el);
|
||||||
|
|
||||||
|
if (module.hot) {
|
||||||
|
module.hot.accept('./root', () => {
|
||||||
|
const NextRoot = require('./root').default;
|
||||||
|
render((
|
||||||
|
<AppContainer>
|
||||||
|
<NextRoot />
|
||||||
|
</AppContainer>
|
||||||
|
), el);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
window.CMS = {};
|
window.CMS = {};
|
||||||
console.log('reg: ', registry);
|
console.log('reg: ', registry);
|
||||||
for (const method in registry) {
|
for (const method in registry) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import {List} from 'immutable';
|
import { List } from 'immutable';
|
||||||
import {newEditorPlugin} from '../components/Widgets/MarkdownControlElements/plugins';
|
import { newEditorPlugin } from '../components/Widgets/MarkdownControlElements/plugins';
|
||||||
|
|
||||||
const _registry = {
|
const _registry = {
|
||||||
templates: {},
|
templates: {},
|
||||||
|
8
src/reducers/combinedReducer.js
Normal file
8
src/reducers/combinedReducer.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { combineReducers } from 'redux';
|
||||||
|
import { routerReducer } from 'react-router-redux';
|
||||||
|
import reducers from '.';
|
||||||
|
|
||||||
|
export default combineReducers({
|
||||||
|
...reducers,
|
||||||
|
routing: routerReducer
|
||||||
|
});
|
21
src/root.js
Normal file
21
src/root.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { Router } from 'react-router';
|
||||||
|
import routes from './routing/routes';
|
||||||
|
import history, { syncHistory } from './routing/history';
|
||||||
|
import configureStore from './store/configureStore';
|
||||||
|
|
||||||
|
const store = configureStore();
|
||||||
|
|
||||||
|
// Create an enhanced history that syncs navigation events with the store
|
||||||
|
syncHistory(store);
|
||||||
|
|
||||||
|
const Root = () => (
|
||||||
|
<Provider store={store}>
|
||||||
|
<Router history={history}>
|
||||||
|
{routes}
|
||||||
|
</Router>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Root;
|
@ -1,18 +1,20 @@
|
|||||||
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
|
import { createStore, applyMiddleware, compose } from 'redux';
|
||||||
import thunkMiddleware from 'redux-thunk';
|
import thunkMiddleware from 'redux-thunk';
|
||||||
import { routerReducer } from 'react-router-redux';
|
import reducer from '../reducers/combinedReducer';
|
||||||
import reducers from '../reducers';
|
|
||||||
|
|
||||||
const reducer = combineReducers({
|
export default function configureStore(initialState) {
|
||||||
...reducers,
|
const store = createStore(reducer, initialState, compose(
|
||||||
routing: routerReducer
|
applyMiddleware(thunkMiddleware),
|
||||||
});
|
window.devToolsExtension ? window.devToolsExtension() : f => f
|
||||||
|
));
|
||||||
|
|
||||||
const createStoreWithMiddleware = compose(
|
if (module.hot) {
|
||||||
applyMiddleware(thunkMiddleware),
|
// Enable Webpack hot module replacement for reducers
|
||||||
window.devToolsExtension ? window.devToolsExtension() : (f) => f
|
module.hot.accept('../reducers/combinedReducer', () => {
|
||||||
)(createStore);
|
const nextReducer = require('../reducers/combinedReducer') // eslint-disable-line
|
||||||
|
store.replaceReducer(nextReducer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default (initialState) => (
|
return store;
|
||||||
createStoreWithMiddleware(reducer, initialState)
|
}
|
||||||
);
|
|
||||||
|
@ -16,15 +16,15 @@ describe('auth', () => {
|
|||||||
expect(
|
expect(
|
||||||
auth(undefined, authenticating())
|
auth(undefined, authenticating())
|
||||||
).toEqual(
|
).toEqual(
|
||||||
Immutable.Map({isFetching: true})
|
Immutable.Map({ isFetching: true })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle authentication', () => {
|
it('should handle authentication', () => {
|
||||||
expect(
|
expect(
|
||||||
auth(undefined, authenticate({email: 'joe@example.com'}))
|
auth(undefined, authenticate({ email: 'joe@example.com' }))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
Immutable.fromJS({user: {email: 'joe@example.com'}})
|
Immutable.fromJS({ user: { email: 'joe@example.com' } })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,39 +15,39 @@ describe('collections', () => {
|
|||||||
|
|
||||||
it('should load the collections from the config', () => {
|
it('should load the collections from the config', () => {
|
||||||
expect(
|
expect(
|
||||||
collections(undefined, configLoaded({collections: [
|
collections(undefined, configLoaded({ collections: [
|
||||||
{name: 'posts', folder: '_posts', fields: [{name: 'title', widget: 'string'}]}
|
{ name: 'posts', folder: '_posts', fields: [{ name: 'title', widget: 'string' }] }
|
||||||
]}))
|
] }))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
OrderedMap({
|
OrderedMap({
|
||||||
posts: fromJS({name: 'posts', folder: '_posts', fields: [{name: 'title', widget: 'string'}]})
|
posts: fromJS({ name: 'posts', folder: '_posts', fields: [{ name: 'title', widget: 'string' }] })
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should mark entries as loading', () => {
|
it('should mark entries as loading', () => {
|
||||||
const state = OrderedMap({
|
const state = OrderedMap({
|
||||||
'posts': Map({name: 'posts'})
|
'posts': Map({ name: 'posts' })
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
collections(state, entriesLoading(Map({name: 'posts'})))
|
collections(state, entriesLoading(Map({ name: 'posts' })))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
OrderedMap({
|
OrderedMap({
|
||||||
'posts': Map({name: 'posts', isFetching: true})
|
'posts': Map({ name: 'posts', isFetching: true })
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle loaded entries', () => {
|
it('should handle loaded entries', () => {
|
||||||
const state = OrderedMap({
|
const state = OrderedMap({
|
||||||
'posts': Map({name: 'posts'})
|
'posts': Map({ name: 'posts' })
|
||||||
});
|
});
|
||||||
const entries = [{slug: 'a', path: ''}, {slug: 'b', title: 'B'}];
|
const entries = [{ slug: 'a', path: '' }, { slug: 'b', title: 'B' }];
|
||||||
expect(
|
expect(
|
||||||
collections(state, entriesLoaded(Map({name: 'posts'}), entries))
|
collections(state, entriesLoaded(Map({ name: 'posts' }), entries))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
OrderedMap({
|
OrderedMap({
|
||||||
'posts': fromJS({name: 'posts', entries: entries})
|
'posts': fromJS({ name: 'posts', entries: entries })
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -14,9 +14,9 @@ describe('config', () => {
|
|||||||
|
|
||||||
it('should handle an update', () => {
|
it('should handle an update', () => {
|
||||||
expect(
|
expect(
|
||||||
config(Immutable.Map({'a': 'b', 'c': 'd'}), configLoaded({'a': 'changed', 'e': 'new'}))
|
config(Immutable.Map({ 'a': 'b', 'c': 'd' }), configLoaded({ 'a': 'changed', 'e': 'new' }))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
Immutable.Map({'a': 'changed', 'e': 'new'})
|
Immutable.Map({ 'a': 'changed', 'e': 'new' })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -24,15 +24,15 @@ describe('config', () => {
|
|||||||
expect(
|
expect(
|
||||||
config(undefined, configLoading())
|
config(undefined, configLoading())
|
||||||
).toEqual(
|
).toEqual(
|
||||||
Immutable.Map({isFetching: true})
|
Immutable.Map({ isFetching: true })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle an error', () => {
|
it('should handle an error', () => {
|
||||||
expect(
|
expect(
|
||||||
config(Immutable.Map({isFetching: true}), configFailed(new Error('Config could not be loaded')))
|
config(Immutable.Map({ isFetching: true }), configFailed(new Error('Config could not be loaded')))
|
||||||
).toEqual(
|
).toEqual(
|
||||||
Immutable.Map({error: 'Error: Config could not be loaded'})
|
Immutable.Map({ error: 'Error: Config could not be loaded' })
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
/* global module, __dirname, require */
|
/* global module, __dirname, require */
|
||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
|
const HOST = 'localhost';
|
||||||
|
const PORT = '8080';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
@ -13,7 +15,7 @@ module.exports = {
|
|||||||
{ test: /\.json$/, loader: 'json-loader' },
|
{ test: /\.json$/, loader: 'json-loader' },
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
loader: ExtractTextPlugin.extract("style", "css?modules&importLoaders=1&&localIdentName=cms__[name]__[local]!postcss"),
|
loader: 'style!css?modules&importLoaders=1&&localIdentName=cms__[name]__[local]!postcss',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
loader: 'babel',
|
loader: 'babel',
|
||||||
@ -22,7 +24,13 @@ module.exports = {
|
|||||||
query: {
|
query: {
|
||||||
cacheDirectory: true,
|
cacheDirectory: true,
|
||||||
presets: ['react', 'es2015'],
|
presets: ['react', 'es2015'],
|
||||||
plugins: ['transform-class-properties', 'transform-object-assign', 'transform-object-rest-spread', 'lodash']
|
plugins: [
|
||||||
|
'transform-class-properties',
|
||||||
|
'transform-object-assign',
|
||||||
|
'transform-object-rest-spread',
|
||||||
|
'lodash',
|
||||||
|
'react-hot-loader/babel'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -34,7 +42,9 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
new ExtractTextPlugin('cms.css', { allChunks: true }),
|
new webpack.optimize.OccurenceOrderPlugin(),
|
||||||
|
new webpack.HotModuleReplacementPlugin(),
|
||||||
|
new webpack.NoErrorsPlugin(),
|
||||||
new webpack.ProvidePlugin({
|
new webpack.ProvidePlugin({
|
||||||
'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
|
'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
|
||||||
})
|
})
|
||||||
@ -42,16 +52,23 @@ module.exports = {
|
|||||||
|
|
||||||
context: path.join(__dirname, 'src'),
|
context: path.join(__dirname, 'src'),
|
||||||
entry: {
|
entry: {
|
||||||
cms: './index',
|
cms: [
|
||||||
|
'webpack/hot/dev-server',
|
||||||
|
`webpack-dev-server/client?http://${HOST}:${PORT}/`,
|
||||||
|
'react-hot-loader/patch',
|
||||||
|
'./index'
|
||||||
|
],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
filename: '[name].js'
|
filename: '[name].js',
|
||||||
|
publicPath: `http://${HOST}:${PORT}/`,
|
||||||
},
|
},
|
||||||
externals: [/^vendor\/.+\.js$/],
|
externals: [/^vendor\/.+\.js$/],
|
||||||
devServer: {
|
devServer: {
|
||||||
|
hot: true,
|
||||||
contentBase: 'example/',
|
contentBase: 'example/',
|
||||||
historyApiFallback: true,
|
historyApiFallback: true,
|
||||||
devTool: 'source-map'
|
devTool: 'cheap-module-source-map'
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user