feat: upgrade to Emotion 10 (#2166)
This commit is contained in:
@ -19,44 +19,44 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^6.4.0",
|
||||
"ajv-errors": "^1.0.0",
|
||||
"copy-text-to-clipboard": "^1.0.4",
|
||||
"@emotion/core": "^10.0.9",
|
||||
"@emotion/styled": "^10.0.9",
|
||||
"ajv": "^6.10.0",
|
||||
"ajv-errors": "^1.0.1",
|
||||
"copy-text-to-clipboard": "^2.0.0",
|
||||
"diacritics": "^1.3.0",
|
||||
"emotion": "^9.2.6",
|
||||
"fuzzy": "^0.1.1",
|
||||
"gotrue-js": "^0.9.15",
|
||||
"gray-matter": "^4.0.1",
|
||||
"gotrue-js": "^0.9.24",
|
||||
"gray-matter": "^4.0.2",
|
||||
"history": "^4.7.2",
|
||||
"immutable": "^3.7.6",
|
||||
"js-base64": "^2.1.9",
|
||||
"js-yaml": "^3.10.0",
|
||||
"js-base64": "^2.5.1",
|
||||
"js-yaml": "^3.12.2",
|
||||
"jwt-decode": "^2.1.0",
|
||||
"lodash": "^4.17.10",
|
||||
"moment": "^2.11.2",
|
||||
"lodash": "^4.17.11",
|
||||
"moment": "^2.24.0",
|
||||
"netlify-cms-editor-component-image": "^2.2.0",
|
||||
"netlify-cms-lib-auth": "^2.0.5",
|
||||
"netlify-cms-lib-util": "^2.1.2",
|
||||
"netlify-cms-ui-default": "^2.4.1-alpha.0",
|
||||
"node-polyglot": "^2.3.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.8.1",
|
||||
"react-dnd": "^7.0.0",
|
||||
"react-dnd-html5-backend": "^7.0.0",
|
||||
"react-dom": "^16.8.1",
|
||||
"react-emotion": "^9.2.5",
|
||||
"react-frame-component": "^4.0.2",
|
||||
"react-hot-loader": "^4.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.4",
|
||||
"react-dnd": "^7.3.2",
|
||||
"react-dnd-html5-backend": "^7.2.0",
|
||||
"react-dom": "^16.8.4",
|
||||
"react-frame-component": "^4.1.0",
|
||||
"react-hot-loader": "^4.8.0",
|
||||
"react-immutable-proptypes": "^2.1.0",
|
||||
"react-is": "16.3.1",
|
||||
"react-modal": "^3.1.5",
|
||||
"react-is": "16.8.4",
|
||||
"react-modal": "^3.8.1",
|
||||
"react-polyglot": "^0.2.6",
|
||||
"react-redux": "^5.1.1",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-router-redux": "^5.0.0-alpha.8",
|
||||
"react-scroll-sync": "^0.6.0",
|
||||
"react-sortable-hoc": "^0.6.8",
|
||||
"react-split-pane": "^0.1.82",
|
||||
"react-split-pane": "^0.1.85",
|
||||
"react-topbar-progress-indicator": "^2.0.0",
|
||||
"react-waypoint": "^8.1.0",
|
||||
"redux": "^4.0.1",
|
||||
@ -68,13 +68,13 @@
|
||||
"toml-j0.4": "^1.1.1",
|
||||
"tomlify-j0.4": "^3.0.0-alpha.0",
|
||||
"url": "^0.11.0",
|
||||
"what-input": "^5.0.3"
|
||||
"what-input": "^5.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^1.0.0",
|
||||
"css-loader": "^2.1.1",
|
||||
"to-string-loader": "^1.1.5",
|
||||
"webpack": "^4.16.1",
|
||||
"webpack-cli": "^3.1.0"
|
||||
"webpack": "^4.29.6",
|
||||
"webpack-cli": "^3.2.3"
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import { Map } from 'immutable';
|
||||
import { stripIndent } from 'common-tags';
|
||||
import moment from 'moment';
|
||||
import fuzzy from 'fuzzy';
|
||||
import { localForage } from 'netlify-cms-lib-util';
|
||||
import { resolveFormat } from 'Formats/formats';
|
||||
import { selectIntegration } from 'Reducers/integrations';
|
||||
import {
|
||||
@ -19,7 +18,7 @@ import {
|
||||
import { createEntry } from 'ValueObjects/Entry';
|
||||
import { sanitizeSlug } from 'Lib/urlHelper';
|
||||
import { getBackend } from 'Lib/registry';
|
||||
import { Cursor, CURSOR_COMPATIBILITY_SYMBOL } from 'netlify-cms-lib-util';
|
||||
import { localForage, Cursor, CURSOR_COMPATIBILITY_SYMBOL } from 'netlify-cms-lib-util';
|
||||
import { EDITORIAL_WORKFLOW, status } from 'Constants/publishModes';
|
||||
|
||||
class LocalStorageAuthStore {
|
||||
|
22
packages/netlify-cms-core/src/bootstrap.js
vendored
22
packages/netlify-cms-core/src/bootstrap.js
vendored
@ -8,6 +8,7 @@ import store from 'Redux';
|
||||
import { mergeConfig } from 'Actions/config';
|
||||
import { getPhrases } from 'Constants/defaultPhrases';
|
||||
import { I18n } from 'react-polyglot';
|
||||
import { GlobalStyles } from 'netlify-cms-ui-default';
|
||||
import { ErrorBoundary } from 'UI';
|
||||
import App from 'App/App';
|
||||
import 'EditorWidgets';
|
||||
@ -62,15 +63,18 @@ function bootstrap(opts = {}) {
|
||||
* Create connected root component.
|
||||
*/
|
||||
const Root = () => (
|
||||
<I18n locale={'en'} messages={getPhrases()}>
|
||||
<ErrorBoundary showBackup>
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Route component={App} />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
</I18n>
|
||||
<>
|
||||
<GlobalStyles />
|
||||
<I18n locale={'en'} messages={getPhrases()}>
|
||||
<ErrorBoundary showBackup>
|
||||
<Provider store={store}>
|
||||
<ConnectedRouter history={history}>
|
||||
<Route component={App} />
|
||||
</ConnectedRouter>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
</I18n>
|
||||
</>
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@ import React from 'react';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { translate } from 'react-polyglot';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { connect } from 'react-redux';
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
import { Notifs } from 'redux-notifications';
|
||||
|
@ -1,7 +1,9 @@
|
||||
/** @jsx jsx */
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { jsx, css } from '@emotion/core';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import {
|
||||
@ -22,15 +24,20 @@ const styles = {
|
||||
`,
|
||||
};
|
||||
|
||||
const AppHeader = styled.header`
|
||||
${shadows.dropMain};
|
||||
position: sticky;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
background-color: ${colors.foreground};
|
||||
z-index: 300;
|
||||
height: ${lengths.topBarHeight};
|
||||
`;
|
||||
const AppHeader = props => (
|
||||
<header
|
||||
css={css`
|
||||
${shadows.dropMain};
|
||||
position: sticky;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
background-color: ${colors.foreground};
|
||||
z-index: 300;
|
||||
height: ${lengths.topBarHeight};
|
||||
`}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
const AppHeaderContent = styled.div`
|
||||
display: flex;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { lengths } from 'netlify-cms-ui-default';
|
||||
import PropTypes from 'prop-types';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { connect } from 'react-redux';
|
||||
import { lengths } from 'netlify-cms-ui-default';
|
||||
import { getNewEntryUrl } from 'Lib/urlHelper';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Icon, components, buttons, shadows, colors } from 'netlify-cms-ui-default';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { resolvePath } from 'netlify-cms-lib-util';
|
||||
import { colors, colorsRaw, components, lengths } from 'netlify-cms-ui-default';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import Waypoint from 'react-waypoint';
|
||||
import { Map } from 'immutable';
|
||||
import { Cursor } from 'netlify-cms-lib-util';
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { css } from '@emotion/core';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { Icon, components, colors, colorsRaw, lengths } from 'netlify-cms-ui-default';
|
||||
|
@ -31,8 +31,7 @@ import { loadDeployPreview } from 'Actions/deploys';
|
||||
import { deserializeValues } from 'Lib/serializeEntryValues';
|
||||
import { selectEntry, selectUnpublishedEntry, selectDeployPreview, getAsset } from 'Reducers';
|
||||
import { selectFields } from 'Reducers/collections';
|
||||
import { status } from 'Constants/publishModes';
|
||||
import { EDITORIAL_WORKFLOW } from 'Constants/publishModes';
|
||||
import { status, EDITORIAL_WORKFLOW } from 'Constants/publishModes';
|
||||
import EditorInterface from './EditorInterface';
|
||||
import withWorkflow from './withWorkflow';
|
||||
|
||||
|
@ -1,16 +1,17 @@
|
||||
/** @jsx jsx */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { translate } from 'react-polyglot';
|
||||
import styled, { css, cx } from 'react-emotion';
|
||||
import { jsx, ClassNames, Global, css as coreCss } from '@emotion/core';
|
||||
import styled from '@emotion/styled';
|
||||
import { partial, uniqueId } from 'lodash';
|
||||
import { connect } from 'react-redux';
|
||||
import { colors, colorsRaw, transitions, lengths, borders } from 'netlify-cms-ui-default';
|
||||
import { resolveWidget, getEditorComponents } from 'Lib/registry';
|
||||
import { clearFieldErrors } from 'Actions/entries';
|
||||
import { clearFieldErrors, loadEntry } from 'Actions/entries';
|
||||
import { addAsset } from 'Actions/media';
|
||||
import { query, clearSearch } from 'Actions/search';
|
||||
import { loadEntry } from 'Actions/entries';
|
||||
import {
|
||||
openMediaLibrary,
|
||||
removeInsertedMedia,
|
||||
@ -20,8 +21,13 @@ import {
|
||||
import { getAsset } from 'Reducers';
|
||||
import Widget from './Widget';
|
||||
|
||||
const styles = {
|
||||
label: css`
|
||||
/**
|
||||
* This is a necessary bridge as we are still passing classnames to widgets
|
||||
* for styling. Once that changes we can stop storing raw style strings like
|
||||
* this.
|
||||
*/
|
||||
const styleStrings = {
|
||||
label: `
|
||||
color: ${colors.controlLabel};
|
||||
background-color: ${colors.textFieldBorder};
|
||||
display: inline-block;
|
||||
@ -55,15 +61,15 @@ const styles = {
|
||||
background-color: #fff;
|
||||
}
|
||||
`,
|
||||
labelActive: css`
|
||||
labelActive: `
|
||||
background-color: ${colors.active};
|
||||
color: ${colors.textLight};
|
||||
`,
|
||||
labelError: css`
|
||||
labelError: `
|
||||
background-color: ${colors.errorText};
|
||||
color: ${colorsRaw.white};
|
||||
`,
|
||||
widget: css`
|
||||
widget: `
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: ${lengths.inputPadding};
|
||||
@ -85,10 +91,10 @@ const styles = {
|
||||
height: 58px;
|
||||
}
|
||||
`,
|
||||
widgetActive: css`
|
||||
widgetActive: `
|
||||
border-color: ${colors.active};
|
||||
`,
|
||||
widgetError: css`
|
||||
widgetError: `
|
||||
border-color: ${colors.errorText};
|
||||
`,
|
||||
};
|
||||
@ -96,7 +102,7 @@ const styles = {
|
||||
const ControlContainer = styled.div`
|
||||
margin-top: 16px;
|
||||
|
||||
&:first-child {
|
||||
&:first-of-type {
|
||||
margin-top: 36px;
|
||||
}
|
||||
`;
|
||||
@ -117,8 +123,8 @@ export const ControlHint = styled.p`
|
||||
margin-bottom: 0;
|
||||
padding: 3px 0;
|
||||
font-size: 12px;
|
||||
color: ${({ active, error }) =>
|
||||
error ? colors.errorText : active ? colors.active : colors.controlLabel};
|
||||
color: ${props =>
|
||||
props.error ? colors.errorText : props.active ? colors.active : colors.controlLabel};
|
||||
transition: color ${transitions.main};
|
||||
`;
|
||||
|
||||
@ -191,75 +197,108 @@ class EditorControl extends React.Component {
|
||||
const metadata = fieldsMetaData && fieldsMetaData.get(fieldName);
|
||||
const errors = fieldsErrors && fieldsErrors.get(this.uniqueFieldId);
|
||||
return (
|
||||
<ControlContainer>
|
||||
<ControlErrorsList>
|
||||
{errors &&
|
||||
errors.map(
|
||||
error =>
|
||||
error.message &&
|
||||
typeof error.message === 'string' && (
|
||||
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>{error.message}</li>
|
||||
),
|
||||
<ClassNames>
|
||||
{({ css, cx }) => (
|
||||
<ControlContainer>
|
||||
{widget.globalStyles && <Global styles={coreCss`${widget.globalStyles}`} />}
|
||||
<ControlErrorsList>
|
||||
{errors &&
|
||||
errors.map(
|
||||
error =>
|
||||
error.message &&
|
||||
typeof error.message === 'string' && (
|
||||
<li key={error.message.trim().replace(/[^a-z0-9]+/gi, '-')}>
|
||||
{error.message}
|
||||
</li>
|
||||
),
|
||||
)}
|
||||
</ControlErrorsList>
|
||||
<label
|
||||
className={cx(
|
||||
css`
|
||||
${styleStrings.label};
|
||||
`,
|
||||
this.state.styleActive &&
|
||||
css`
|
||||
${styleStrings.labelActive};
|
||||
`,
|
||||
!!errors &&
|
||||
css`
|
||||
${styleStrings.labelError};
|
||||
`,
|
||||
)}
|
||||
htmlFor={this.uniqueFieldId}
|
||||
>
|
||||
{`${field.get('label', field.get('name'))}${isFieldOptional ? ' (optional)' : ''}`}
|
||||
</label>
|
||||
<Widget
|
||||
classNameWrapper={cx(
|
||||
css`
|
||||
${styleStrings.widget};
|
||||
`,
|
||||
{
|
||||
[css`
|
||||
${styleStrings.widgetActive};
|
||||
`]: this.state.styleActive,
|
||||
},
|
||||
{
|
||||
[css`
|
||||
${styleStrings.widgetError};
|
||||
`]: !!errors,
|
||||
},
|
||||
)}
|
||||
classNameWidget={css`
|
||||
${styleStrings.widget};
|
||||
`}
|
||||
classNameWidgetActive={css`
|
||||
${styleStrings.widgetActive};
|
||||
`}
|
||||
classNameLabel={css`
|
||||
${styleStrings.label};
|
||||
`}
|
||||
classNameLabelActive={css`
|
||||
${styleStrings.labelActive};
|
||||
`}
|
||||
controlComponent={widget.control}
|
||||
field={field}
|
||||
uniqueFieldId={this.uniqueFieldId}
|
||||
value={value}
|
||||
mediaPaths={mediaPaths}
|
||||
metadata={metadata}
|
||||
onChange={(newValue, newMetadata) => onChange(fieldName, newValue, newMetadata)}
|
||||
onValidate={onValidate && partial(onValidate, this.uniqueFieldId)}
|
||||
onOpenMediaLibrary={openMediaLibrary}
|
||||
onClearMediaControl={clearMediaControl}
|
||||
onRemoveMediaControl={removeMediaControl}
|
||||
onRemoveInsertedMedia={removeInsertedMedia}
|
||||
onAddAsset={addAsset}
|
||||
getAsset={boundGetAsset}
|
||||
hasActiveStyle={this.state.styleActive}
|
||||
setActiveStyle={() => this.setState({ styleActive: true })}
|
||||
setInactiveStyle={() => this.setState({ styleActive: false })}
|
||||
resolveWidget={resolveWidget}
|
||||
getEditorComponents={getEditorComponents}
|
||||
ref={processControlRef && partial(processControlRef, field)}
|
||||
controlRef={controlRef}
|
||||
editorControl={ConnectedEditorControl}
|
||||
query={query}
|
||||
loadEntry={loadEntry}
|
||||
queryHits={queryHits}
|
||||
clearSearch={clearSearch}
|
||||
clearFieldErrors={clearFieldErrors}
|
||||
isFetching={isFetching}
|
||||
fieldsErrors={fieldsErrors}
|
||||
onValidateObject={onValidateObject}
|
||||
t={t}
|
||||
/>
|
||||
{fieldHint && (
|
||||
<ControlHint active={this.state.styleActive} error={!!errors}>
|
||||
{fieldHint}
|
||||
</ControlHint>
|
||||
)}
|
||||
</ControlErrorsList>
|
||||
<label
|
||||
className={cx(
|
||||
styles.label,
|
||||
{ [styles.labelActive]: this.state.styleActive },
|
||||
{ [styles.labelError]: !!errors },
|
||||
)}
|
||||
htmlFor={this.uniqueFieldId}
|
||||
>
|
||||
{`${field.get('label', field.get('name'))}${isFieldOptional ? ' (optional)' : ''}`}
|
||||
</label>
|
||||
<Widget
|
||||
classNameWrapper={cx(
|
||||
styles.widget,
|
||||
{ [styles.widgetActive]: this.state.styleActive },
|
||||
{ [styles.widgetError]: !!errors },
|
||||
)}
|
||||
classNameWidget={styles.widget}
|
||||
classNameWidgetActive={styles.widgetActive}
|
||||
classNameLabel={styles.label}
|
||||
classNameLabelActive={styles.labelActive}
|
||||
controlComponent={widget.control}
|
||||
field={field}
|
||||
uniqueFieldId={this.uniqueFieldId}
|
||||
value={value}
|
||||
mediaPaths={mediaPaths}
|
||||
metadata={metadata}
|
||||
onChange={(newValue, newMetadata) => onChange(fieldName, newValue, newMetadata)}
|
||||
onValidate={onValidate && partial(onValidate, this.uniqueFieldId)}
|
||||
onOpenMediaLibrary={openMediaLibrary}
|
||||
onClearMediaControl={clearMediaControl}
|
||||
onRemoveMediaControl={removeMediaControl}
|
||||
onRemoveInsertedMedia={removeInsertedMedia}
|
||||
onAddAsset={addAsset}
|
||||
getAsset={boundGetAsset}
|
||||
hasActiveStyle={this.state.styleActive}
|
||||
setActiveStyle={() => this.setState({ styleActive: true })}
|
||||
setInactiveStyle={() => this.setState({ styleActive: false })}
|
||||
resolveWidget={resolveWidget}
|
||||
getEditorComponents={getEditorComponents}
|
||||
ref={processControlRef && partial(processControlRef, field)}
|
||||
controlRef={controlRef}
|
||||
editorControl={ConnectedEditorControl}
|
||||
query={query}
|
||||
loadEntry={loadEntry}
|
||||
queryHits={queryHits}
|
||||
clearSearch={clearSearch}
|
||||
clearFieldErrors={clearFieldErrors}
|
||||
isFetching={isFetching}
|
||||
fieldsErrors={fieldsErrors}
|
||||
onValidateObject={onValidateObject}
|
||||
t={t}
|
||||
/>
|
||||
{fieldHint && (
|
||||
<ControlHint active={this.state.styleActive} error={!!errors}>
|
||||
{fieldHint}
|
||||
</ControlHint>
|
||||
</ControlContainer>
|
||||
)}
|
||||
</ControlContainer>
|
||||
</ClassNames>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,14 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from 'react-emotion';
|
||||
import EditorControl, { ControlHint } from './EditorControl';
|
||||
import styled from '@emotion/styled';
|
||||
import EditorControl from './EditorControl';
|
||||
|
||||
const ControlPaneContainer = styled.div`
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 16px;
|
||||
|
||||
p:not(${ControlHint}) {
|
||||
font-size: 16px;
|
||||
}
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
export default class ControlPane extends React.Component {
|
||||
@ -56,21 +53,20 @@ export default class ControlPane extends React.Component {
|
||||
|
||||
return (
|
||||
<ControlPaneContainer>
|
||||
{fields.map(
|
||||
(field, i) =>
|
||||
field.get('widget') === 'hidden' ? null : (
|
||||
<EditorControl
|
||||
key={i}
|
||||
field={field}
|
||||
value={entry.getIn(['data', field.get('name')])}
|
||||
fieldsMetaData={fieldsMetaData}
|
||||
fieldsErrors={fieldsErrors}
|
||||
onChange={onChange}
|
||||
onValidate={onValidate}
|
||||
processControlRef={this.controlRef.bind(this)}
|
||||
controlRef={this.controlRef}
|
||||
/>
|
||||
),
|
||||
{fields.map((field, i) =>
|
||||
field.get('widget') === 'hidden' ? null : (
|
||||
<EditorControl
|
||||
key={i}
|
||||
field={field}
|
||||
value={entry.getIn(['data', field.get('name')])}
|
||||
fieldsMetaData={fieldsMetaData}
|
||||
fieldsErrors={fieldsErrors}
|
||||
onChange={onChange}
|
||||
onValidate={onValidate}
|
||||
processControlRef={this.controlRef.bind(this)}
|
||||
controlRef={this.controlRef}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
</ControlPaneContainer>
|
||||
);
|
||||
|
@ -1,7 +1,8 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled, { css, injectGlobal } from 'react-emotion';
|
||||
import { css, Global } from '@emotion/core';
|
||||
import styled from '@emotion/styled';
|
||||
import SplitPane from 'react-split-pane';
|
||||
import { colors, colorsRaw, components, transitions } from 'netlify-cms-ui-default';
|
||||
import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
|
||||
@ -25,33 +26,33 @@ const styles = {
|
||||
`,
|
||||
};
|
||||
|
||||
injectGlobal`
|
||||
/**
|
||||
* React Split Pane
|
||||
*/
|
||||
.Resizer.vertical {
|
||||
width: 21px;
|
||||
cursor: col-resize;
|
||||
position: relative;
|
||||
transition: background-color ${transitions.main};
|
||||
const ReactSplitPaneGlobalStyles = () => (
|
||||
<Global
|
||||
styles={css`
|
||||
.Resizer.vertical {
|
||||
width: 21px;
|
||||
cursor: col-resize;
|
||||
position: relative;
|
||||
transition: background-color ${transitions.main};
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
left: 10px;
|
||||
background-color: ${colors.textFieldBorder};
|
||||
display: block;
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
left: 10px;
|
||||
background-color: ${colors.textFieldBorder};
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background-color: ${colorsRaw.GrayLight};
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
&:hover,
|
||||
&:active {
|
||||
background-color: ${colorsRaw.GrayLight};
|
||||
}
|
||||
}
|
||||
`}
|
||||
/>
|
||||
);
|
||||
|
||||
const StyledSplitPane = styled(SplitPane)`
|
||||
${styles.splitPane};
|
||||
@ -195,6 +196,7 @@ class EditorInterface extends Component {
|
||||
const editorWithPreview = (
|
||||
<ScrollSync enabled={this.state.scrollSyncEnabled}>
|
||||
<div>
|
||||
<ReactSplitPaneGlobalStyles />
|
||||
<StyledSplitPane
|
||||
maxSize={-100}
|
||||
defaultSize="50%"
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
function isVisible(field) {
|
||||
return field.get('widget') !== 'hidden';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { List, Map } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Frame from 'react-frame-component';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { Icon, colors, colorsRaw, shadows, buttons } from 'netlify-cms-ui-default';
|
||||
|
||||
const EditorToggleButton = styled.button`
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import { css } from '@emotion/core';
|
||||
import styled from '@emotion/styled';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { Map } from 'immutable';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { colors } from 'netlify-cms-ui-default';
|
||||
|
||||
const EmptyMessageContainer = styled.div`
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import { css } from '@emotion/core';
|
||||
import styled from '@emotion/styled';
|
||||
import { FileUploadButton } from 'UI';
|
||||
import { buttons, shadows } from 'netlify-cms-ui-default';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { colors, borders, lengths, shadows, effects } from 'netlify-cms-ui-default';
|
||||
|
||||
const IMAGE_HEIGHT = 160;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import Waypoint from 'react-waypoint';
|
||||
import MediaLibraryCard from './MediaLibraryCard';
|
||||
import { Map } from 'immutable';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { Icon, shadows, colors, buttons } from 'netlify-cms-ui-default';
|
||||
|
||||
const CloseButton = styled.button`
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { Modal } from 'UI';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { Icon, lengths, colors } from 'netlify-cms-ui-default';
|
||||
|
||||
const SearchContainer = styled.div`
|
||||
|
@ -1,42 +1,42 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { translate } from 'react-polyglot';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import copyToClipboard from 'copy-text-to-clipboard';
|
||||
import { localForage } from 'netlify-cms-lib-util';
|
||||
import { buttons, colors } from 'netlify-cms-ui-default';
|
||||
|
||||
const ISSUE_URL = 'https://github.com/netlify/netlify-cms/issues/new?template=bug_report.md';
|
||||
|
||||
const styles = {
|
||||
errorBoundary: css`
|
||||
padding: 40px;
|
||||
const ErrorBoundaryContainer = styled.div`
|
||||
padding: 40px;
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: ${colors.text};
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
strong {
|
||||
color: ${colors.textLead};
|
||||
font-weight: 500;
|
||||
}
|
||||
strong {
|
||||
color: ${colors.textLead};
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
hr {
|
||||
width: 200px;
|
||||
margin: 30px 0;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background-color: ${colors.text};
|
||||
}
|
||||
`,
|
||||
errorText: css`
|
||||
color: ${colors.errorText};
|
||||
`,
|
||||
};
|
||||
hr {
|
||||
width: 200px;
|
||||
margin: 30px 0;
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background-color: ${colors.text};
|
||||
}
|
||||
|
||||
a {
|
||||
color: ${colors.text};
|
||||
}
|
||||
`;
|
||||
|
||||
const CopyButton = styled.button`
|
||||
${buttons.button};
|
||||
@ -104,16 +104,11 @@ class ErrorBoundary extends React.Component {
|
||||
return this.props.children;
|
||||
}
|
||||
return (
|
||||
<div className={styles.errorBoundary}>
|
||||
<h1 className={styles.errorBoundaryText}>{t('ui.errorBoundary.title')}</h1>
|
||||
<ErrorBoundaryContainer>
|
||||
<h1>{t('ui.errorBoundary.title')}</h1>
|
||||
<p>
|
||||
<span>{t('ui.errorBoundary.details')}</span>
|
||||
<a
|
||||
href={ISSUE_URL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.errorBoundaryText}
|
||||
>
|
||||
<a href={ISSUE_URL} target="_blank" rel="noopener noreferrer">
|
||||
{t('ui.errorBoundary.reportIt')}
|
||||
</a>
|
||||
</p>
|
||||
@ -121,7 +116,7 @@ class ErrorBoundary extends React.Component {
|
||||
<h2>{t('ui.errorBoundary.detailsHeading')}</h2>
|
||||
<p>{errorMessage}</p>
|
||||
{backup && showBackup && <RecoveredEntry entry={backup} t={t} />}
|
||||
</div>
|
||||
</ErrorBoundaryContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,21 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { css, cx, injectGlobal } from 'react-emotion';
|
||||
import { css, Global, ClassNames } from '@emotion/core';
|
||||
import ReactModal from 'react-modal';
|
||||
import { transitions, shadows, lengths } from 'netlify-cms-ui-default';
|
||||
|
||||
injectGlobal`
|
||||
.ReactModal__Body--open {
|
||||
overflow: hidden;
|
||||
}
|
||||
`;
|
||||
const ReactModalGlobalStyles = () => (
|
||||
<Global
|
||||
styles={css`
|
||||
.ReactModal__Body--open {
|
||||
overflow: hidden;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
);
|
||||
|
||||
const styles = {
|
||||
modalBody: css`
|
||||
const styleStrings = {
|
||||
modalBody: `
|
||||
${shadows.dropDeep};
|
||||
background-color: #fff;
|
||||
border-radius: ${lengths.borderRadius};
|
||||
@ -24,7 +28,7 @@ const styles = {
|
||||
outline: none;
|
||||
}
|
||||
`,
|
||||
overlay: css`
|
||||
overlay: `
|
||||
z-index: 99999;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -38,11 +42,11 @@ const styles = {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
transition: background-color ${transitions.main}, opacity ${transitions.main};
|
||||
`,
|
||||
overlayAfterOpen: css`
|
||||
overlayAfterOpen: `
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
opacity: 1;
|
||||
`,
|
||||
overlayBeforeClose: css`
|
||||
overlayBeforeClose: `
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
opacity: 0;
|
||||
`,
|
||||
@ -63,23 +67,41 @@ export class Modal extends React.Component {
|
||||
render() {
|
||||
const { isOpen, children, className, onClose } = this.props;
|
||||
return (
|
||||
<ReactModal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={onClose}
|
||||
closeTimeoutMS={300}
|
||||
className={{
|
||||
base: cx(styles.modalBody, className),
|
||||
afterOpen: '',
|
||||
beforeClose: '',
|
||||
}}
|
||||
overlayClassName={{
|
||||
base: styles.overlay,
|
||||
afterOpen: styles.overlayAfterOpen,
|
||||
beforeClose: styles.overlayBeforeClose,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ReactModal>
|
||||
<>
|
||||
<ReactModalGlobalStyles />
|
||||
<ClassNames>
|
||||
{({ css, cx }) => (
|
||||
<ReactModal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={onClose}
|
||||
closeTimeoutMS={300}
|
||||
className={{
|
||||
base: cx(
|
||||
css`
|
||||
${styleStrings.modalBody};
|
||||
`,
|
||||
className,
|
||||
),
|
||||
afterOpen: '',
|
||||
beforeClose: '',
|
||||
}}
|
||||
overlayClassName={{
|
||||
base: css`
|
||||
${styleStrings.overlay};
|
||||
`,
|
||||
afterOpen: css`
|
||||
${styleStrings.overlayAfterOpen};
|
||||
`,
|
||||
beforeClose: css`
|
||||
${styleStrings.overlayBeforeClose};
|
||||
`,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ReactModal>
|
||||
)}
|
||||
</ClassNames>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import { css } from '@emotion/core';
|
||||
import styled from '@emotion/styled';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { Icon, Dropdown, DropdownItem, DropdownButton, colors } from 'netlify-cms-ui-default';
|
||||
import { stripProtocol } from 'Lib/urlHelper';
|
||||
|
@ -1,18 +1,24 @@
|
||||
/** @jsx jsx */
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { css, injectGlobal, cx } from 'react-emotion';
|
||||
import { jsx, css, Global } from '@emotion/core';
|
||||
import { translate } from 'react-polyglot';
|
||||
import reduxNotificationsStyles from 'redux-notifications/lib/styles.css';
|
||||
import { shadows, colors, lengths } from 'netlify-cms-ui-default';
|
||||
|
||||
injectGlobal`
|
||||
${reduxNotificationsStyles};
|
||||
const ReduxNotificationsGlobalStyles = () => (
|
||||
<Global
|
||||
styles={css`
|
||||
${reduxNotificationsStyles};
|
||||
|
||||
.notif__container {
|
||||
z-index: 10000;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
`;
|
||||
.notif__container {
|
||||
z-index: 10000;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
);
|
||||
|
||||
const styles = {
|
||||
toast: css`
|
||||
@ -43,7 +49,8 @@ const styles = {
|
||||
};
|
||||
|
||||
const Toast = ({ kind, message, t }) => (
|
||||
<div className={cx(styles.toast, styles[kind])}>
|
||||
<div css={[styles.toast, styles[kind]]}>
|
||||
<ReduxNotificationsGlobalStyles />
|
||||
{t(message.key, { details: message.details })}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled from 'react-emotion';
|
||||
import styled from '@emotion/styled';
|
||||
import { OrderedMap } from 'immutable';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import { css } from '@emotion/core';
|
||||
import styled from '@emotion/styled';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { components, colors, colorsRaw, transitions, buttons } from 'netlify-cms-ui-default';
|
||||
|
@ -1,7 +1,9 @@
|
||||
/** @jsx jsx */
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import styled, { css, cx } from 'react-emotion';
|
||||
import { jsx, css } from '@emotion/core';
|
||||
import styled from '@emotion/styled';
|
||||
import moment from 'moment';
|
||||
import { translate } from 'react-polyglot';
|
||||
import { colors, lengths } from 'netlify-cms-ui-default';
|
||||
@ -16,22 +18,16 @@ const WorkflowListContainer = styled.div`
|
||||
`;
|
||||
|
||||
const styles = {
|
||||
column: css`
|
||||
margin: 0 20px;
|
||||
transition: background-color 0.5s ease;
|
||||
border: 2px dashed transparent;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&:not(:first-child):not(:last-child) {
|
||||
columnPosition: idx =>
|
||||
(idx === 0 &&
|
||||
css`
|
||||
margin-left: 0;
|
||||
`) ||
|
||||
(idx === 2 &&
|
||||
css`
|
||||
margin-right: 0;
|
||||
`) ||
|
||||
css`
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
@ -50,7 +46,14 @@ const styles = {
|
||||
&:after {
|
||||
right: -23px;
|
||||
}
|
||||
}
|
||||
`,
|
||||
column: css`
|
||||
margin: 0 20px;
|
||||
transition: background-color 0.5s ease;
|
||||
border: 2px dashed transparent;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
`,
|
||||
columnHovered: css`
|
||||
border-color: ${colors.active};
|
||||
@ -140,11 +143,12 @@ class WorkflowList extends React.Component {
|
||||
this.props.handlePublish(collection, slug);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
renderColumns = (entries, column) => {
|
||||
if (!entries) return null;
|
||||
|
||||
if (!column) {
|
||||
return entries.entrySeq().map(([currColumn, currEntries]) => (
|
||||
return entries.entrySeq().map(([currColumn, currEntries], idx) => (
|
||||
<DropTarget
|
||||
namespace={DNDNamespace}
|
||||
key={currColumn}
|
||||
@ -152,16 +156,24 @@ class WorkflowList extends React.Component {
|
||||
>
|
||||
{(connect, { isHovered }) =>
|
||||
connect(
|
||||
<div className={cx(styles.column, { [styles.columnHovered]: isHovered })}>
|
||||
<ColumnHeader name={currColumn}>
|
||||
{getColumnHeaderText(currColumn, this.props.t)}
|
||||
</ColumnHeader>
|
||||
<ColumnCount>
|
||||
{this.props.t('workflow.workflowList.currentEntries', {
|
||||
smart_count: currEntries.size,
|
||||
})}
|
||||
</ColumnCount>
|
||||
{this.renderColumns(currEntries, currColumn)}
|
||||
<div style={{ height: '100%' }}>
|
||||
<div
|
||||
css={[
|
||||
styles.column,
|
||||
styles.columnPosition(idx),
|
||||
isHovered && styles.columnHovered,
|
||||
]}
|
||||
>
|
||||
<ColumnHeader name={currColumn}>
|
||||
{getColumnHeaderText(currColumn, this.props.t)}
|
||||
</ColumnHeader>
|
||||
<ColumnCount>
|
||||
{this.props.t('workflow.workflowList.currentEntries', {
|
||||
smart_count: currEntries.size,
|
||||
})}
|
||||
</ColumnCount>
|
||||
{this.renderColumns(currEntries, currColumn)}
|
||||
</div>
|
||||
</div>,
|
||||
)
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ describe('config', () => {
|
||||
media_folder: 'baz',
|
||||
collections: [],
|
||||
});
|
||||
}).toThrowError("'collections' should NOT have less than 1 items");
|
||||
}).toThrowError("'collections' should NOT have fewer than 1 items");
|
||||
});
|
||||
|
||||
it('should throw if collections is an array with a single null element in config', () => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Map } from 'immutable';
|
||||
import { oneLine } from 'common-tags';
|
||||
import EditorComponent from 'ValueObjects/EditorComponent';
|
||||
|
||||
/**
|
||||
@ -59,10 +60,39 @@ export function getPreviewTemplate(name) {
|
||||
* Editor Widgets
|
||||
*/
|
||||
export function registerWidget(name, control, preview) {
|
||||
// A registered widget control can be reused by a new widget, allowing
|
||||
// multiple copies with different previews.
|
||||
const newControl = typeof control === 'string' ? registry.widgets[control].control : control;
|
||||
registry.widgets[name] = { control: newControl, preview };
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(widget => {
|
||||
if (typeof widget !== 'object') {
|
||||
console.error(`Cannot register widget: ${widget}`);
|
||||
} else {
|
||||
registerWidget(widget);
|
||||
}
|
||||
});
|
||||
} else if (typeof name === 'string') {
|
||||
// A registered widget control can be reused by a new widget, allowing
|
||||
// multiple copies with different previews.
|
||||
const newControl = typeof control === 'string' ? registry.widgets[control].control : control;
|
||||
registry.widgets[name] = { control: newControl, preview };
|
||||
} else if (typeof name === 'object') {
|
||||
const {
|
||||
name: widgetName,
|
||||
controlComponent: control,
|
||||
previewComponent: preview,
|
||||
globalStyles,
|
||||
} = name;
|
||||
if (registry.widgets[widgetName]) {
|
||||
console.error(oneLine`
|
||||
Multiple widgets registered with name "${widgetName}". Only the last widget registered with
|
||||
this name will be used.
|
||||
`);
|
||||
}
|
||||
if (!control) {
|
||||
throw Error(`Widget "${widgetName}" registered without \`controlComponent\`.`);
|
||||
}
|
||||
registry.widgets[widgetName] = { control, preview, globalStyles };
|
||||
} else {
|
||||
console.error('`registerWidget` failed, called with incorrect arguments.');
|
||||
}
|
||||
}
|
||||
export function getWidget(name) {
|
||||
return registry.widgets[name];
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Map } from 'immutable';
|
||||
/*
|
||||
* Reducer for some global UI state that we want to share between components
|
||||
* */
|
||||
* Reducer for some global UI state that we want to share between components
|
||||
* */
|
||||
const globalUI = (state = Map({ isFetching: false }), action) => {
|
||||
// Generic, global loading indicator
|
||||
if (action.type.indexOf('REQUEST') > -1) {
|
||||
|
Reference in New Issue
Block a user