migrate markdown widget
This commit is contained in:
parent
3f47fe6dbf
commit
f1a2eb33b4
@ -27,15 +27,12 @@
|
|||||||
"gray-matter": "^3.0.6",
|
"gray-matter": "^3.0.6",
|
||||||
"history": "^4.7.2",
|
"history": "^4.7.2",
|
||||||
"immutable": "^3.7.6",
|
"immutable": "^3.7.6",
|
||||||
"is-hotkey": "^0.1.1",
|
|
||||||
"js-base64": "^2.1.9",
|
"js-base64": "^2.1.9",
|
||||||
"js-yaml": "^3.10.0",
|
"js-yaml": "^3.10.0",
|
||||||
"jwt-decode": "^2.1.0",
|
"jwt-decode": "^2.1.0",
|
||||||
"lib": "^3.0.2",
|
"lib": "^3.0.2",
|
||||||
"localforage": "^1.4.2",
|
"localforage": "^1.4.2",
|
||||||
"lodash": "^4.17.10",
|
"lodash": "^4.17.10",
|
||||||
"mdast-util-definitions": "^1.2.2",
|
|
||||||
"mdast-util-to-string": "^1.0.4",
|
|
||||||
"moment": "^2.11.2",
|
"moment": "^2.11.2",
|
||||||
"netlify-cms-editor-component-image": "2.0.0-alpha.0",
|
"netlify-cms-editor-component-image": "2.0.0-alpha.0",
|
||||||
"netlify-cms-lib-auth": "2.0.0-alpha.0",
|
"netlify-cms-lib-auth": "2.0.0-alpha.0",
|
||||||
@ -65,30 +62,14 @@
|
|||||||
"react-topbar-progress-indicator": "^2.0.0",
|
"react-topbar-progress-indicator": "^2.0.0",
|
||||||
"react-transition-group": "^2.2.1",
|
"react-transition-group": "^2.2.1",
|
||||||
"react-waypoint": "^7.1.0",
|
"react-waypoint": "^7.1.0",
|
||||||
"recompose": "^0.27.1",
|
|
||||||
"redux": "^3.3.1",
|
"redux": "^3.3.1",
|
||||||
"redux-notifications": "^4.0.1",
|
"redux-notifications": "^4.0.1",
|
||||||
"redux-optimist": "^0.0.2",
|
"redux-optimist": "^0.0.2",
|
||||||
"redux-thunk": "^1.0.3",
|
"redux-thunk": "^1.0.3",
|
||||||
"rehype-parse": "^3.1.0",
|
|
||||||
"rehype-remark": "^2.0.0",
|
|
||||||
"rehype-stringify": "^3.0.0",
|
|
||||||
"remark-parse": "^3.0.1",
|
|
||||||
"remark-rehype": "^2.0.0",
|
|
||||||
"remark-stringify": "^3.0.1",
|
|
||||||
"sanitize-filename": "^1.6.1",
|
"sanitize-filename": "^1.6.1",
|
||||||
"semaphore": "^1.0.5",
|
"semaphore": "^1.0.5",
|
||||||
"slate": "^0.30.0",
|
|
||||||
"slate-edit-list": "^0.10.1",
|
|
||||||
"slate-edit-table": "^0.12.0",
|
|
||||||
"slate-plain-serializer": "^0.4.0",
|
|
||||||
"slate-react": "0.10.11",
|
|
||||||
"slate-soft-break": "^0.6.0",
|
|
||||||
"toml-j0.4": "^1.1.1",
|
"toml-j0.4": "^1.1.1",
|
||||||
"tomlify-j0.4": "^3.0.0-alpha.0",
|
"tomlify-j0.4": "^3.0.0-alpha.0",
|
||||||
"unified": "^6.1.4",
|
|
||||||
"unist-builder": "^1.0.2",
|
|
||||||
"unist-util-visit-parents": "^1.1.1",
|
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"uuid": "^3.1.0",
|
"uuid": "^3.1.0",
|
||||||
"what-input": "^5.0.3"
|
"what-input": "^5.0.3"
|
||||||
|
@ -10,11 +10,11 @@ import { FileControl, FilePreview } from 'netlify-cms-widget-file';
|
|||||||
import { ImageControl, ImagePreview } from 'netlify-cms-widget-image';
|
import { ImageControl, ImagePreview } from 'netlify-cms-widget-image';
|
||||||
import { ListControl, ListPreview } from 'netlify-cms-widget-list';
|
import { ListControl, ListPreview } from 'netlify-cms-widget-list';
|
||||||
import { ObjectControl, ObjectPreview } from 'netlify-cms-widget-object';
|
import { ObjectControl, ObjectPreview } from 'netlify-cms-widget-object';
|
||||||
|
import { MarkdownControl, MarkdownPreview } from 'netlify-cms-widget-markdown';
|
||||||
import { StringControl, StringPreview } from 'netlify-cms-widget-string';
|
import { StringControl, StringPreview } from 'netlify-cms-widget-string';
|
||||||
// import { NumberControl, NumberPreview } from 'netlify-cms-widget-number';
|
// import { NumberControl, NumberPreview } from 'netlify-cms-widget-number';
|
||||||
// import { TextControl, TextPreview } from 'netlify-cms-widget-text';
|
// import { TextControl, TextPreview } from 'netlify-cms-widget-text';
|
||||||
// import { SelectControl, SelectPreview } from 'netlify-cms-widget-select';
|
// import { SelectControl, SelectPreview } from 'netlify-cms-widget-select';
|
||||||
// import { MarkdownControl, MarkdownPreview } from 'netlify-cms-widget-markdown';
|
|
||||||
// import { RelationControl, RelationPreview } from 'netlify-cms-widget-relation';
|
// import { RelationControl, RelationPreview } from 'netlify-cms-widget-relation';
|
||||||
import image from 'netlify-cms-editor-component-image';
|
import image from 'netlify-cms-editor-component-image';
|
||||||
|
|
||||||
@ -28,11 +28,11 @@ registerWidget('datetime', DateTimeControl, DateTimePreview);
|
|||||||
registerWidget('file', FileControl, FilePreview);
|
registerWidget('file', FileControl, FilePreview);
|
||||||
registerWidget('image', ImageControl, ImagePreview);
|
registerWidget('image', ImageControl, ImagePreview);
|
||||||
registerWidget('list', ListControl, ListPreview);
|
registerWidget('list', ListControl, ListPreview);
|
||||||
|
registerWidget('markdown', MarkdownControl, MarkdownPreview);
|
||||||
registerWidget('object', ObjectControl, ObjectPreview);
|
registerWidget('object', ObjectControl, ObjectPreview);
|
||||||
registerWidget('string', StringControl, StringPreview);
|
registerWidget('string', StringControl, StringPreview);
|
||||||
// registerWidget('text', TextControl, TextPreview);
|
// registerWidget('text', TextControl, TextPreview);
|
||||||
// registerWidget('number', NumberControl, NumberPreview);
|
// registerWidget('number', NumberControl, NumberPreview);
|
||||||
// registerWidget('markdown', MarkdownControl, MarkdownPreview);
|
|
||||||
// registerWidget('select', SelectControl, SelectPreview);
|
// registerWidget('select', SelectControl, SelectPreview);
|
||||||
// registerWidget('relation', RelationControl, RelationPreview);
|
// registerWidget('relation', RelationControl, RelationPreview);
|
||||||
registerEditorComponent(image);
|
registerEditorComponent(image);
|
||||||
|
@ -24,8 +24,6 @@ import {
|
|||||||
deleteUnpublishedEntry
|
deleteUnpublishedEntry
|
||||||
} from 'Actions/editorialWorkflow';
|
} from 'Actions/editorialWorkflow';
|
||||||
import { deserializeValues } from 'Lib/serializeEntryValues';
|
import { deserializeValues } from 'Lib/serializeEntryValues';
|
||||||
import { addAsset } from 'Actions/media';
|
|
||||||
import { openMediaLibrary, removeInsertedMedia } from 'Actions/mediaLibrary';
|
|
||||||
import { selectEntry, selectUnpublishedEntry, getAsset } from 'Reducers';
|
import { selectEntry, selectUnpublishedEntry, getAsset } from 'Reducers';
|
||||||
import { selectFields } from 'Reducers/collections';
|
import { selectFields } from 'Reducers/collections';
|
||||||
import { status } from 'Constants/publishModes';
|
import { status } from 'Constants/publishModes';
|
||||||
@ -40,7 +38,6 @@ const navigateToEntry = (collectionName, slug) => navigateCollection(`${collecti
|
|||||||
|
|
||||||
class Editor extends React.Component {
|
class Editor extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
addAsset: PropTypes.func.isRequired,
|
|
||||||
boundGetAsset: PropTypes.func.isRequired,
|
boundGetAsset: PropTypes.func.isRequired,
|
||||||
changeDraftField: PropTypes.func.isRequired,
|
changeDraftField: PropTypes.func.isRequired,
|
||||||
changeDraftFieldValidation: PropTypes.func.isRequired,
|
changeDraftFieldValidation: PropTypes.func.isRequired,
|
||||||
@ -49,14 +46,11 @@ class Editor extends React.Component {
|
|||||||
createEmptyDraft: PropTypes.func.isRequired,
|
createEmptyDraft: PropTypes.func.isRequired,
|
||||||
discardDraft: PropTypes.func.isRequired,
|
discardDraft: PropTypes.func.isRequired,
|
||||||
entry: ImmutablePropTypes.map,
|
entry: ImmutablePropTypes.map,
|
||||||
mediaPaths: ImmutablePropTypes.map.isRequired,
|
|
||||||
entryDraft: ImmutablePropTypes.map.isRequired,
|
entryDraft: ImmutablePropTypes.map.isRequired,
|
||||||
loadEntry: PropTypes.func.isRequired,
|
loadEntry: PropTypes.func.isRequired,
|
||||||
persistEntry: PropTypes.func.isRequired,
|
persistEntry: PropTypes.func.isRequired,
|
||||||
deleteEntry: PropTypes.func.isRequired,
|
deleteEntry: PropTypes.func.isRequired,
|
||||||
showDelete: PropTypes.bool.isRequired,
|
showDelete: PropTypes.bool.isRequired,
|
||||||
openMediaLibrary: PropTypes.func.isRequired,
|
|
||||||
removeInsertedMedia: PropTypes.func.isRequired,
|
|
||||||
fields: ImmutablePropTypes.list.isRequired,
|
fields: ImmutablePropTypes.list.isRequired,
|
||||||
slug: PropTypes.string,
|
slug: PropTypes.string,
|
||||||
newEntry: PropTypes.bool.isRequired,
|
newEntry: PropTypes.bool.isRequired,
|
||||||
@ -268,14 +262,10 @@ class Editor extends React.Component {
|
|||||||
entry,
|
entry,
|
||||||
entryDraft,
|
entryDraft,
|
||||||
fields,
|
fields,
|
||||||
mediaPaths,
|
|
||||||
boundGetAsset,
|
boundGetAsset,
|
||||||
collection,
|
collection,
|
||||||
changeDraftField,
|
changeDraftField,
|
||||||
changeDraftFieldValidation,
|
changeDraftFieldValidation,
|
||||||
openMediaLibrary,
|
|
||||||
addAsset,
|
|
||||||
removeInsertedMedia,
|
|
||||||
user,
|
user,
|
||||||
hasChanged,
|
hasChanged,
|
||||||
displayUrl,
|
displayUrl,
|
||||||
@ -303,12 +293,8 @@ class Editor extends React.Component {
|
|||||||
fields={fields}
|
fields={fields}
|
||||||
fieldsMetaData={entryDraft.get('fieldsMetaData')}
|
fieldsMetaData={entryDraft.get('fieldsMetaData')}
|
||||||
fieldsErrors={entryDraft.get('fieldsErrors')}
|
fieldsErrors={entryDraft.get('fieldsErrors')}
|
||||||
mediaPaths={mediaPaths}
|
|
||||||
onChange={changeDraftField}
|
onChange={changeDraftField}
|
||||||
onValidate={changeDraftFieldValidation}
|
onValidate={changeDraftFieldValidation}
|
||||||
onOpenMediaLibrary={openMediaLibrary}
|
|
||||||
onAddAsset={addAsset}
|
|
||||||
onRemoveInsertedMedia={removeInsertedMedia}
|
|
||||||
onPersist={this.handlePersistEntry}
|
onPersist={this.handlePersistEntry}
|
||||||
onDelete={this.handleDeleteEntry}
|
onDelete={this.handleDeleteEntry}
|
||||||
onDeleteUnpublishedChanges={this.handleDeleteUnpublishedChanges}
|
onDeleteUnpublishedChanges={this.handleDeleteUnpublishedChanges}
|
||||||
@ -339,7 +325,6 @@ function mapStateToProps(state, ownProps) {
|
|||||||
const fields = selectFields(collection, slug);
|
const fields = selectFields(collection, slug);
|
||||||
const entry = newEntry ? null : selectEntry(state, collectionName, slug);
|
const entry = newEntry ? null : selectEntry(state, collectionName, slug);
|
||||||
const boundGetAsset = getAsset.bind(null, state);
|
const boundGetAsset = getAsset.bind(null, state);
|
||||||
const mediaPaths = mediaLibrary.get('controlMedia');
|
|
||||||
const user = auth && auth.get('user');
|
const user = auth && auth.get('user');
|
||||||
const hasChanged = entryDraft.get('hasChanged');
|
const hasChanged = entryDraft.get('hasChanged');
|
||||||
const displayUrl = config.get('display_url');
|
const displayUrl = config.get('display_url');
|
||||||
@ -353,7 +338,6 @@ function mapStateToProps(state, ownProps) {
|
|||||||
collections,
|
collections,
|
||||||
newEntry,
|
newEntry,
|
||||||
entryDraft,
|
entryDraft,
|
||||||
mediaPaths,
|
|
||||||
boundGetAsset,
|
boundGetAsset,
|
||||||
fields,
|
fields,
|
||||||
slug,
|
slug,
|
||||||
@ -373,9 +357,6 @@ export default connect(
|
|||||||
{
|
{
|
||||||
changeDraftField,
|
changeDraftField,
|
||||||
changeDraftFieldValidation,
|
changeDraftFieldValidation,
|
||||||
openMediaLibrary,
|
|
||||||
removeInsertedMedia,
|
|
||||||
addAsset,
|
|
||||||
loadEntry,
|
loadEntry,
|
||||||
loadEntries,
|
loadEntries,
|
||||||
createDraftFromEntry,
|
createDraftFromEntry,
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled, { css, cx } from 'react-emotion';
|
import styled, { css, cx } from 'react-emotion';
|
||||||
import { partial, uniqueId } from 'lodash';
|
import { partial, uniqueId } from 'lodash';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import { colors, colorsRaw, transitions, lengths, borders } from 'netlify-cms-ui-default';
|
import { colors, colorsRaw, transitions, lengths, borders } from 'netlify-cms-ui-default';
|
||||||
import { resolveWidget } from 'Lib/registry';
|
import { resolveWidget, getEditorComponents } from 'Lib/registry';
|
||||||
|
import { addAsset } from 'Actions/media';
|
||||||
|
import { openMediaLibrary, removeInsertedMedia } from 'Actions/mediaLibrary';
|
||||||
|
import { getAsset } from 'Reducers';
|
||||||
import Widget from './Widget';
|
import Widget from './Widget';
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
@ -100,7 +104,7 @@ const ControlErrorsList = styled.ul`
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default class EditorControl extends React.Component {
|
class EditorControl extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
activeLabel: false,
|
activeLabel: false,
|
||||||
};
|
};
|
||||||
@ -112,11 +116,11 @@ export default class EditorControl extends React.Component {
|
|||||||
fieldsMetaData,
|
fieldsMetaData,
|
||||||
fieldsErrors,
|
fieldsErrors,
|
||||||
mediaPaths,
|
mediaPaths,
|
||||||
getAsset,
|
boundGetAsset,
|
||||||
onChange,
|
onChange,
|
||||||
onOpenMediaLibrary,
|
openMediaLibrary,
|
||||||
onAddAsset,
|
addAsset,
|
||||||
onRemoveInsertedMedia,
|
removeInsertedMedia,
|
||||||
onValidate,
|
onValidate,
|
||||||
processControlRef,
|
processControlRef,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -165,18 +169,34 @@ export default class EditorControl extends React.Component {
|
|||||||
metadata={metadata}
|
metadata={metadata}
|
||||||
onChange={(newValue, newMetadata) => onChange(fieldName, newValue, newMetadata)}
|
onChange={(newValue, newMetadata) => onChange(fieldName, newValue, newMetadata)}
|
||||||
onValidate={onValidate && partial(onValidate, fieldName)}
|
onValidate={onValidate && partial(onValidate, fieldName)}
|
||||||
onOpenMediaLibrary={onOpenMediaLibrary}
|
onOpenMediaLibrary={openMediaLibrary}
|
||||||
onRemoveInsertedMedia={onRemoveInsertedMedia}
|
onRemoveInsertedMedia={removeInsertedMedia}
|
||||||
onAddAsset={onAddAsset}
|
onAddAsset={addAsset}
|
||||||
getAsset={getAsset}
|
getAsset={boundGetAsset}
|
||||||
hasActiveStyle={this.state.styleActive}
|
hasActiveStyle={this.state.styleActive}
|
||||||
setActiveStyle={() => this.setState({ styleActive: true })}
|
setActiveStyle={() => this.setState({ styleActive: true })}
|
||||||
setInactiveStyle={() => this.setState({ styleActive: false })}
|
setInactiveStyle={() => this.setState({ styleActive: false })}
|
||||||
resolveWidget={resolveWidget}
|
resolveWidget={resolveWidget}
|
||||||
|
getEditorComponents={getEditorComponents}
|
||||||
ref={processControlRef && partial(processControlRef, fieldName)}
|
ref={processControlRef && partial(processControlRef, fieldName)}
|
||||||
editorControl={EditorControl}
|
editorControl={ConnectedEditorControl}
|
||||||
/>
|
/>
|
||||||
</ControlContainer>
|
</ControlContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
mediaPaths: state.mediaLibrary.get('controlMedia'),
|
||||||
|
boundGetAsset: getAsset.bind(null, state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
openMediaLibrary,
|
||||||
|
removeInsertedMedia,
|
||||||
|
addAsset,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ConnectedEditorControl = connect(mapStateToProps, mapDispatchToProps)(EditorControl);
|
||||||
|
|
||||||
|
export default ConnectedEditorControl;
|
||||||
|
@ -36,12 +36,7 @@ export default class ControlPane extends React.Component {
|
|||||||
entry,
|
entry,
|
||||||
fieldsMetaData,
|
fieldsMetaData,
|
||||||
fieldsErrors,
|
fieldsErrors,
|
||||||
mediaPaths,
|
|
||||||
getAsset,
|
|
||||||
onChange,
|
onChange,
|
||||||
onOpenMediaLibrary,
|
|
||||||
onAddAsset,
|
|
||||||
onRemoveInsertedMedia,
|
|
||||||
onValidate,
|
onValidate,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
@ -62,12 +57,7 @@ export default class ControlPane extends React.Component {
|
|||||||
value={entry.getIn(['data', field.get('name')])}
|
value={entry.getIn(['data', field.get('name')])}
|
||||||
fieldsMetaData={fieldsMetaData}
|
fieldsMetaData={fieldsMetaData}
|
||||||
fieldsErrors={fieldsErrors}
|
fieldsErrors={fieldsErrors}
|
||||||
mediaPaths={mediaPaths}
|
|
||||||
getAsset={getAsset}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onOpenMediaLibrary={onOpenMediaLibrary}
|
|
||||||
onAddAsset={onAddAsset}
|
|
||||||
onRemoveInsertedMedia={onRemoveInsertedMedia}
|
|
||||||
onValidate={onValidate}
|
onValidate={onValidate}
|
||||||
processControlRef={this.processControlRef}
|
processControlRef={this.processControlRef}
|
||||||
/>
|
/>
|
||||||
@ -83,11 +73,6 @@ ControlPane.propTypes = {
|
|||||||
fields: ImmutablePropTypes.list.isRequired,
|
fields: ImmutablePropTypes.list.isRequired,
|
||||||
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||||
fieldsErrors: ImmutablePropTypes.map.isRequired,
|
fieldsErrors: ImmutablePropTypes.map.isRequired,
|
||||||
mediaPaths: ImmutablePropTypes.map.isRequired,
|
|
||||||
getAsset: PropTypes.func.isRequired,
|
|
||||||
onOpenMediaLibrary: PropTypes.func.isRequired,
|
|
||||||
onAddAsset: PropTypes.func.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
onValidate: PropTypes.func.isRequired,
|
onValidate: PropTypes.func.isRequired,
|
||||||
onRemoveInsertedMedia: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,7 @@ export default class Widget extends Component {
|
|||||||
onRemoveInsertedMedia: PropTypes.func.isRequired,
|
onRemoveInsertedMedia: PropTypes.func.isRequired,
|
||||||
getAsset: PropTypes.func.isRequired,
|
getAsset: PropTypes.func.isRequired,
|
||||||
resolveWidget: PropTypes.func.isRequired,
|
resolveWidget: PropTypes.func.isRequired,
|
||||||
|
getEditorComponents: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
@ -192,6 +193,7 @@ export default class Widget extends Component {
|
|||||||
editorControl,
|
editorControl,
|
||||||
uniqueFieldId,
|
uniqueFieldId,
|
||||||
resolveWidget,
|
resolveWidget,
|
||||||
|
getEditorComponents,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return React.createElement(controlComponent, {
|
return React.createElement(controlComponent, {
|
||||||
field,
|
field,
|
||||||
@ -216,6 +218,7 @@ export default class Widget extends Component {
|
|||||||
hasActiveStyle,
|
hasActiveStyle,
|
||||||
editorControl,
|
editorControl,
|
||||||
resolveWidget,
|
resolveWidget,
|
||||||
|
getEditorComponents,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,6 @@ class EditorInterface extends Component {
|
|||||||
fields,
|
fields,
|
||||||
fieldsMetaData,
|
fieldsMetaData,
|
||||||
fieldsErrors,
|
fieldsErrors,
|
||||||
mediaPaths,
|
|
||||||
getAsset,
|
getAsset,
|
||||||
onChange,
|
onChange,
|
||||||
enableSave,
|
enableSave,
|
||||||
@ -162,9 +161,6 @@ class EditorInterface extends Component {
|
|||||||
onChangeStatus,
|
onChangeStatus,
|
||||||
onPublish,
|
onPublish,
|
||||||
onValidate,
|
onValidate,
|
||||||
onOpenMediaLibrary,
|
|
||||||
onAddAsset,
|
|
||||||
onRemoveInsertedMedia,
|
|
||||||
user,
|
user,
|
||||||
hasChanged,
|
hasChanged,
|
||||||
displayUrl,
|
displayUrl,
|
||||||
@ -188,13 +184,8 @@ class EditorInterface extends Component {
|
|||||||
fields={fields}
|
fields={fields}
|
||||||
fieldsMetaData={fieldsMetaData}
|
fieldsMetaData={fieldsMetaData}
|
||||||
fieldsErrors={fieldsErrors}
|
fieldsErrors={fieldsErrors}
|
||||||
mediaPaths={mediaPaths}
|
|
||||||
getAsset={getAsset}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onValidate={onValidate}
|
onValidate={onValidate}
|
||||||
onOpenMediaLibrary={onOpenMediaLibrary}
|
|
||||||
onAddAsset={onAddAsset}
|
|
||||||
onRemoveInsertedMedia={onRemoveInsertedMedia}
|
|
||||||
ref={c => this.controlPaneRef = c} // eslint-disable-line
|
ref={c => this.controlPaneRef = c} // eslint-disable-line
|
||||||
/>
|
/>
|
||||||
</ControlPaneContainer>
|
</ControlPaneContainer>
|
||||||
@ -283,10 +274,7 @@ EditorInterface.propTypes = {
|
|||||||
fields: ImmutablePropTypes.list.isRequired,
|
fields: ImmutablePropTypes.list.isRequired,
|
||||||
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||||
fieldsErrors: ImmutablePropTypes.map.isRequired,
|
fieldsErrors: ImmutablePropTypes.map.isRequired,
|
||||||
mediaPaths: ImmutablePropTypes.map.isRequired,
|
|
||||||
getAsset: PropTypes.func.isRequired,
|
getAsset: PropTypes.func.isRequired,
|
||||||
onOpenMediaLibrary: PropTypes.func.isRequired,
|
|
||||||
onAddAsset: PropTypes.func.isRequired,
|
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
onValidate: PropTypes.func.isRequired,
|
onValidate: PropTypes.func.isRequired,
|
||||||
onPersist: PropTypes.func.isRequired,
|
onPersist: PropTypes.func.isRequired,
|
||||||
@ -296,7 +284,6 @@ EditorInterface.propTypes = {
|
|||||||
onDeleteUnpublishedChanges: PropTypes.func.isRequired,
|
onDeleteUnpublishedChanges: PropTypes.func.isRequired,
|
||||||
onPublish: PropTypes.func.isRequired,
|
onPublish: PropTypes.func.isRequired,
|
||||||
onChangeStatus: PropTypes.func.isRequired,
|
onChangeStatus: PropTypes.func.isRequired,
|
||||||
onRemoveInsertedMedia: PropTypes.func.isRequired,
|
|
||||||
user: ImmutablePropTypes.map,
|
user: ImmutablePropTypes.map,
|
||||||
hasChanged: PropTypes.bool,
|
hasChanged: PropTypes.bool,
|
||||||
displayUrl: PropTypes.string,
|
displayUrl: PropTypes.string,
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
@import "./Object/Object.css";
|
|
||||||
@import "./List/List.css";
|
|
||||||
@import "./withMedia/withMedia.css";
|
|
||||||
@import "./Image/Image.css";
|
|
||||||
@import "./File/FileControl.css";
|
|
||||||
@import "./Markdown/Markdown.css";
|
|
||||||
@import "./Boolean/Boolean.css";
|
|
||||||
@import "./Relation/Relation.css";
|
|
||||||
@import "./DateTime/DateTime.css";
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--widgetNestDistance: 14px;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
@import "./MarkdownControl/RawEditor/index.css";
|
|
||||||
@import "./MarkdownControl/Toolbar/Toolbar.css";
|
|
||||||
@import "./MarkdownControl/Toolbar/ToolbarButton.css";
|
|
||||||
@import "./MarkdownControl/VisualEditor/index.css";
|
|
@ -1,15 +0,0 @@
|
|||||||
.nc-rawEditor-rawWrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-rawEditor-rawEditor {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-x: auto;
|
|
||||||
min-height: var(--richTextEditorMinHeight);
|
|
||||||
font-family: var(--fontFamilyMono);
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-top: 0;
|
|
||||||
margin-top: calc(-1 * var(--stickyDistanceBottom));
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
.nc-toolbar-Toolbar {
|
|
||||||
background-color: var(--textFieldBorderColor);
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 11px 14px;
|
|
||||||
min-height: 58px;
|
|
||||||
transition: background-color var(--transition), color var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-markdownWidget-toolbar-toggle {
|
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 14px;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-markdownWidget-toolbar-toggle-label {
|
|
||||||
display: inline-block;
|
|
||||||
text-align: center;
|
|
||||||
white-space: nowrap;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-markdownWidget-toolbar-toggle-label-active {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #3a69c7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-toolbar-ToolbarActive {
|
|
||||||
background-color: var(--colorActive);
|
|
||||||
color: var(--colorTextLight);
|
|
||||||
|
|
||||||
& .nc-markdownWidget-toolbar-toggle-label {
|
|
||||||
color: var(--colorTextLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
& .nc-markdownWidget-toolbar-toggle-background {
|
|
||||||
background-color: var(--textFieldBorderColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-toolbar-dropdown {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
.nc-toolbarButton-button {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 6px;
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
font-size: 16px;
|
|
||||||
color: inherit;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
cursor: auto;
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
& .nc-icon {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-toolbarButton-active {
|
|
||||||
color: #1e2532;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
.nc-visualEditor-shortcode {
|
|
||||||
border-radius: var(--borderRadius);
|
|
||||||
border: 2px solid var(--textFieldBorderColor);
|
|
||||||
margin: 12px 0;
|
|
||||||
padding: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-shortcode-topBar {
|
|
||||||
background-color: var(--textFieldBorderColor);
|
|
||||||
margin: calc(-1 * var(--widgetNestDistance)) calc(-1 * var(--widgetNestDistance)) 0;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-shortcode-collapsed {
|
|
||||||
background-color: var(--textFieldBorderColor);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-shortcode-collapsedTitle {
|
|
||||||
padding: 8px;
|
|
||||||
color: var(--controlLabelColor);
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
@import './Shortcode.css';
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--stickyDistanceBottom: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editorControlBar {
|
|
||||||
z-index: 1;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
margin-bottom: var(--stickyDistanceBottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-x: auto;
|
|
||||||
min-height: var(--richTextEditorMinHeight);
|
|
||||||
font-family: var(--fontFamilyPrimary);
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
border-top-right-radius: 0;
|
|
||||||
border-top: 0;
|
|
||||||
margin-top: calc(-1 * var(--stickyDistanceBottom));
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor h1 {
|
|
||||||
font-size: 32px;
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor h2 {
|
|
||||||
font-size: 24px;
|
|
||||||
margin-top: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor h3 {
|
|
||||||
font-size: 20px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor h4 {
|
|
||||||
font-size: 18px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor h5,
|
|
||||||
.nc-visualEditor-editor h6 {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor h1,
|
|
||||||
.nc-visualEditor-editor h2,
|
|
||||||
.nc-visualEditor-editor h3,
|
|
||||||
.nc-visualEditor-editor h4,
|
|
||||||
.nc-visualEditor-editor h5,
|
|
||||||
.nc-visualEditor-editor h6 {
|
|
||||||
font-weight: 700;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor p,
|
|
||||||
.nc-visualEditor-editor pre,
|
|
||||||
.nc-visualEditor-editor blockquote,
|
|
||||||
.nc-visualEditor-editor ul,
|
|
||||||
.nc-visualEditor-editor ol {
|
|
||||||
margin-top: 16px;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor a {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor hr {
|
|
||||||
border: 1px solid;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor li > p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor ul,
|
|
||||||
.nc-visualEditor-editor ol {
|
|
||||||
padding-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor pre > code {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
background-color: #000;
|
|
||||||
color: #ccc;
|
|
||||||
border-radius: var(--borderRadius);
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor code {
|
|
||||||
background-color: var(--colorBackground);
|
|
||||||
border-radius: var(--borderRadius);
|
|
||||||
padding: 0 2px;
|
|
||||||
font-size: 85%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor blockquote {
|
|
||||||
padding-left: 16px;
|
|
||||||
border-left: 3px solid var(--colorBackground);
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nc-visualEditor-editor td,
|
|
||||||
.nc-visualEditor-editor th {
|
|
||||||
border: 2px solid black;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
@ -7,14 +7,11 @@ import TextControl from './Text/TextControl';
|
|||||||
import TextPreview from './Text/TextPreview';
|
import TextPreview from './Text/TextPreview';
|
||||||
import SelectControl from './Select/SelectControl';
|
import SelectControl from './Select/SelectControl';
|
||||||
import SelectPreview from './Select/SelectPreview';
|
import SelectPreview from './Select/SelectPreview';
|
||||||
import MarkdownControl from './Markdown/MarkdownControl';
|
|
||||||
import MarkdownPreview from './Markdown/MarkdownPreview';
|
|
||||||
import RelationControl from './Relation/RelationControl';
|
import RelationControl from './Relation/RelationControl';
|
||||||
import RelationPreview from './Relation/RelationPreview';
|
import RelationPreview from './Relation/RelationPreview';
|
||||||
|
|
||||||
registerWidget('text', TextControl, TextPreview);
|
registerWidget('text', TextControl, TextPreview);
|
||||||
registerWidget('number', NumberControl, NumberPreview);
|
registerWidget('number', NumberControl, NumberPreview);
|
||||||
registerWidget('markdown', MarkdownControl, MarkdownPreview);
|
|
||||||
registerWidget('select', SelectControl, SelectPreview);
|
registerWidget('select', SelectControl, SelectPreview);
|
||||||
registerWidget('relation', RelationControl, RelationPreview);
|
registerWidget('relation', RelationControl, RelationPreview);
|
||||||
registerWidget('unknown', UnknownControl, UnknownPreview);
|
registerWidget('unknown', UnknownControl, UnknownPreview);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Map } from 'immutable';
|
import { Map } from 'immutable';
|
||||||
import { newEditorPlugin } from 'EditorWidgets/Markdown/MarkdownControl/plugins';
|
import EditorComponent from 'ValueObjects/EditorComponent'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global Registry Object
|
* Global Registry Object
|
||||||
@ -76,7 +76,7 @@ export function resolveWidget(name) {
|
|||||||
* Markdown Editor Custom Components
|
* Markdown Editor Custom Components
|
||||||
*/
|
*/
|
||||||
export function registerEditorComponent(component) {
|
export function registerEditorComponent(component) {
|
||||||
const plugin = newEditorPlugin(component);
|
const plugin = EditorComponent(component);
|
||||||
registry.editorComponents = registry.editorComponents.set(plugin.get('id'), plugin);
|
registry.editorComponents = registry.editorComponents.set(plugin.get('id'), plugin);
|
||||||
};
|
};
|
||||||
export function getEditorComponents() {
|
export function getEditorComponents() {
|
||||||
|
@ -36,7 +36,7 @@ class Plugin extends Component { // eslint-disable-line
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newEditorPlugin(config) {
|
export default function createEditorComponent(config) {
|
||||||
const configObj = new EditorComponent({
|
const configObj = new EditorComponent({
|
||||||
id: config.id || config.label.replace(/[^A-Z0-9]+/ig, '_'),
|
id: config.id || config.label.replace(/[^A-Z0-9]+/ig, '_'),
|
||||||
label: config.label,
|
label: config.label,
|
@ -42,6 +42,7 @@ const Toggle = ({
|
|||||||
renderBackground,
|
renderBackground,
|
||||||
onFocus,
|
onFocus,
|
||||||
onBlur,
|
onBlur,
|
||||||
|
className,
|
||||||
Container = ToggleContainer,
|
Container = ToggleContainer,
|
||||||
Background = ToggleBackground,
|
Background = ToggleBackground,
|
||||||
Handle = ToggleHandle,
|
Handle = ToggleHandle,
|
||||||
@ -53,6 +54,7 @@ const Toggle = ({
|
|||||||
aria-checked={on.toString()}
|
aria-checked={on.toString()}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
|
className={className}
|
||||||
{...getElementTogglerProps()}
|
{...getElementTogglerProps()}
|
||||||
>
|
>
|
||||||
<Background isActive={on}/>
|
<Background isActive={on}/>
|
||||||
|
@ -257,18 +257,13 @@ export default class ListControl extends React.Component {
|
|||||||
/>
|
/>
|
||||||
<NestedObjectLabel collapsed={collapsed}>{this.objectLabel(item)}</NestedObjectLabel>
|
<NestedObjectLabel collapsed={collapsed}>{this.objectLabel(item)}</NestedObjectLabel>
|
||||||
<ObjectControl
|
<ObjectControl
|
||||||
value={item}
|
|
||||||
field={field}
|
|
||||||
onChangeObject={this.handleChangeFor(index)}
|
|
||||||
getAsset={getAsset}
|
|
||||||
onOpenMediaLibrary={onOpenMediaLibrary}
|
|
||||||
mediaPaths={mediaPaths}
|
|
||||||
onAddAsset={onAddAsset}
|
|
||||||
onRemoveInsertedMedia={onRemoveInsertedMedia}
|
|
||||||
classNameWrapper={cx(
|
classNameWrapper={cx(
|
||||||
classNameWrapper,
|
classNameWrapper,
|
||||||
{ [styles.collapsedObjectControl]: collapsed },
|
{ [styles.collapsedObjectControl]: collapsed },
|
||||||
)}
|
)}
|
||||||
|
value={item}
|
||||||
|
field={field}
|
||||||
|
onChangeObject={this.handleChangeFor(index)}
|
||||||
editorControl={editorControl}
|
editorControl={editorControl}
|
||||||
resolveWidget={resolveWidget}
|
resolveWidget={resolveWidget}
|
||||||
forList
|
forList
|
||||||
|
53
packages/netlify-cms-widget-markdown/package.json
Normal file
53
packages/netlify-cms-widget-markdown/package.json
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"name": "netlify-cms-widget-markdown",
|
||||||
|
"description": "Widget for editing markdown in Netlify CMS.",
|
||||||
|
"version": "2.0.0-alpha.0",
|
||||||
|
"main": "dist/netlify-cms-widget-markdown.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"netlify",
|
||||||
|
"netlify-cms",
|
||||||
|
"widget",
|
||||||
|
"markdown",
|
||||||
|
"editor"
|
||||||
|
],
|
||||||
|
"sideEffects": false,
|
||||||
|
"scripts": {
|
||||||
|
"watch": "webpack -w",
|
||||||
|
"build": "webpack"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"is-hotkey": "^0.1.1",
|
||||||
|
"mdast-util-definitions": "^1.2.2",
|
||||||
|
"mdast-util-to-string": "^1.0.4",
|
||||||
|
"rehype-parse": "^3.1.0",
|
||||||
|
"rehype-remark": "^2.0.0",
|
||||||
|
"rehype-stringify": "^3.0.0",
|
||||||
|
"remark-parse": "^3.0.1",
|
||||||
|
"remark-rehype": "^2.0.0",
|
||||||
|
"remark-stringify": "^3.0.1",
|
||||||
|
"slate": "^0.30.0",
|
||||||
|
"slate-edit-list": "^0.10.1",
|
||||||
|
"slate-edit-table": "^0.12.0",
|
||||||
|
"slate-plain-serializer": "^0.4.0",
|
||||||
|
"slate-react": "0.10.11",
|
||||||
|
"slate-soft-break": "^0.6.0",
|
||||||
|
"unified": "^6.1.4",
|
||||||
|
"unist-builder": "^1.0.2",
|
||||||
|
"unist-util-visit-parents": "^1.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"webpack": "^4.16.1",
|
||||||
|
"webpack-cli": "^3.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"immutable": "^3.7.6",
|
||||||
|
"lodash": "^4.17.10",
|
||||||
|
"netlify-cms-ui-default": "^2.0.0-alpha.0",
|
||||||
|
"prop-types": "^15.5.10",
|
||||||
|
"react": "^16.4.1",
|
||||||
|
"react-dom": "^16.0.0",
|
||||||
|
"react-emotion": "^9.2.5",
|
||||||
|
"react-immutable-proptypes": "^2.1.0"
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import React from 'react';
|
import styled, { css, cx } from 'react-emotion';
|
||||||
import { Editor as Slate } from 'slate-react';
|
import { Editor as Slate } from 'slate-react';
|
||||||
import Plain from 'slate-plain-serializer';
|
import Plain from 'slate-plain-serializer';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import Toolbar from 'EditorWidgets/Markdown/MarkdownControl/Toolbar/Toolbar';
|
import { lengths, fonts } from 'netlify-cms-ui-default';
|
||||||
|
import { editorStyleVars, EditorControlBar } from '../styles';
|
||||||
|
import Toolbar from './Toolbar';
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
slateRaw: css`
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-x: auto;
|
||||||
|
min-height: ${lengths.richTextEditorMinHeight};
|
||||||
|
font-family: ${fonts.mono};
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-top: 0;
|
||||||
|
margin-top: -${editorStyleVars.stickyDistanceBottom};
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RawEditorContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
export default class RawEditor extends React.Component {
|
export default class RawEditor extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@ -53,23 +74,22 @@ export default class RawEditor extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
const { className, field } = this.props;
|
const { className, field } = this.props;
|
||||||
return (
|
return (
|
||||||
<div className="nc-rawEditor-rawWrapper">
|
<RawEditorContainer>
|
||||||
<div className="nc-visualEditor-editorControlBar">
|
<EditorControlBar>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
onToggleMode={this.handleToggleMode}
|
onToggleMode={this.handleToggleMode}
|
||||||
buttons={field.get('buttons')}
|
buttons={field.get('buttons')}
|
||||||
className="nc-markdownWidget-toolbarRaw"
|
|
||||||
disabled
|
disabled
|
||||||
rawMode
|
rawMode
|
||||||
/>
|
/>
|
||||||
</div>
|
</EditorControlBar>
|
||||||
<Slate
|
<Slate
|
||||||
className={`${className} nc-rawEditor-rawEditor`}
|
className={cx(className, styles.slateRaw)}
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
onPaste={this.handlePaste}
|
onPaste={this.handlePaste}
|
||||||
/>
|
/>
|
||||||
</div>
|
</RawEditorContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,16 +1,35 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import c from 'classnames';
|
|
||||||
import { Map } from 'immutable';
|
import { Map } from 'immutable';
|
||||||
import { connect } from 'react-redux';
|
import styled, { css } from 'react-emotion';
|
||||||
import { partial, capitalize } from 'lodash';
|
import { partial, capitalize } from 'lodash';
|
||||||
import { resolveWidget, getEditorComponents } from 'Lib/registry';
|
import { ListItemTopBar, components, colors, lengths } from 'netlify-cms-ui-default';
|
||||||
import { openMediaLibrary, removeInsertedMedia } from 'Actions/mediaLibrary';
|
import { getEditorControl, getEditorComponents } from './index';
|
||||||
import { addAsset } from 'Actions/media';
|
|
||||||
import { getAsset } from 'Reducers';
|
|
||||||
import { ListItemTopBar } from 'netlify-cms-ui-default';
|
|
||||||
import { getEditorControl } from '../index';
|
|
||||||
|
|
||||||
class Shortcode extends React.Component {
|
const ShortcodeContainer = styled.div`
|
||||||
|
${components.objectWidgetTopBarContainer};
|
||||||
|
border-radius: ${lengths.borderRadius};
|
||||||
|
border: 2px solid ${colors.textFieldBorder};
|
||||||
|
margin: 12px 0;
|
||||||
|
padding: 14px;
|
||||||
|
|
||||||
|
${props => props.collapsed && css`
|
||||||
|
background-color: ${colors.textFieldBorder};
|
||||||
|
cursor: pointer;
|
||||||
|
`}
|
||||||
|
`
|
||||||
|
|
||||||
|
const ShortcodeTopBar = styled(ListItemTopBar)`
|
||||||
|
background-color: ${colors.textFieldBorder};
|
||||||
|
margin: -14px -14px 0;
|
||||||
|
border-radius: 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ShortcodeTitle = styled.div`
|
||||||
|
padding: 8px;
|
||||||
|
color: ${colors.controlLabel};
|
||||||
|
`
|
||||||
|
|
||||||
|
export default class Shortcode extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -59,27 +78,11 @@ class Shortcode extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderControl = (shortcodeData, field, index) => {
|
renderControl = (shortcodeData, field, index) => {
|
||||||
const {
|
|
||||||
onAddAsset,
|
|
||||||
boundGetAsset,
|
|
||||||
mediaPaths,
|
|
||||||
onOpenMediaLibrary,
|
|
||||||
onRemoveInsertedMedia,
|
|
||||||
} = this.props;
|
|
||||||
if (field.get('widget') === 'hidden') return null;
|
if (field.get('widget') === 'hidden') return null;
|
||||||
const value = shortcodeData.get(field.get('name'));
|
const value = shortcodeData.get(field.get('name'));
|
||||||
const key = `field-${ field.get('name') }`;
|
const key = `field-${ field.get('name') }`;
|
||||||
const Control = getEditorControl();
|
const Control = getEditorControl();
|
||||||
const controlProps = {
|
const controlProps = { field, value, onChange: this.handleChange };
|
||||||
field,
|
|
||||||
value,
|
|
||||||
onAddAsset,
|
|
||||||
getAsset: boundGetAsset,
|
|
||||||
onChange: this.handleChange,
|
|
||||||
mediaPaths,
|
|
||||||
onOpenMediaLibrary,
|
|
||||||
onRemoveInsertedMedia,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key}>
|
<div key={key}>
|
||||||
@ -94,44 +97,19 @@ class Shortcode extends React.Component {
|
|||||||
const pluginId = node.data.get('shortcode');
|
const pluginId = node.data.get('shortcode');
|
||||||
const shortcodeData = Map(this.props.node.data.get('shortcodeData'));
|
const shortcodeData = Map(this.props.node.data.get('shortcodeData'));
|
||||||
const plugin = getEditorComponents().get(pluginId);
|
const plugin = getEditorComponents().get(pluginId);
|
||||||
const className = c(
|
|
||||||
'nc-objectControl-root',
|
|
||||||
'nc-visualEditor-shortcode',
|
|
||||||
{ 'nc-visualEditor-shortcode-collapsed': collapsed },
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div {...attributes} className={className} onClick={this.handleClick}>
|
<ShortcodeContainer collapsed={collapsed} {...attributes} onClick={this.handleClick}>
|
||||||
<ListItemTopBar
|
<ShortcodeTopBar
|
||||||
className="nc-visualEditor-shortcode-topBar"
|
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
onCollapseToggle={this.handleCollapseToggle}
|
onCollapseToggle={this.handleCollapseToggle}
|
||||||
onRemove={this.handleRemove}
|
onRemove={this.handleRemove}
|
||||||
/>
|
/>
|
||||||
{
|
{
|
||||||
collapsed
|
collapsed
|
||||||
? <div className="nc-visualEditor-shortcode-collapsedTitle">{capitalize(pluginId)}</div>
|
? <ShortcodeTitle>{capitalize(pluginId)}</ShortcodeTitle>
|
||||||
: plugin.get('fields').map(partial(this.renderControl, shortcodeData))
|
: plugin.get('fields').map(partial(this.renderControl, shortcodeData))
|
||||||
}
|
}
|
||||||
</div>
|
</ShortcodeContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => {
|
|
||||||
const { attributes, node, editor } = ownProps;
|
|
||||||
return {
|
|
||||||
mediaPaths: state.mediaLibrary.get('controlMedia'),
|
|
||||||
boundGetAsset: getAsset.bind(null, state),
|
|
||||||
attributes,
|
|
||||||
node,
|
|
||||||
editor,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
|
||||||
onAddAsset: addAsset,
|
|
||||||
onOpenMediaLibrary: openMediaLibrary,
|
|
||||||
onRemoveInsertedMedia: removeInsertedMedia,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Shortcode);
|
|
@ -1,11 +1,58 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { List } from 'immutable';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import c from 'classnames';
|
import styled, { css } from 'react-emotion';
|
||||||
import { Icon, Toggle, Dropdown, DropdownItem, DropdownButton } from 'netlify-cms-ui-default';
|
import { List } from 'immutable';
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Toggle,
|
||||||
|
Dropdown,
|
||||||
|
DropdownItem,
|
||||||
|
DropdownButton,
|
||||||
|
colors,
|
||||||
|
transitions,
|
||||||
|
} from 'netlify-cms-ui-default';
|
||||||
import ToolbarButton from './ToolbarButton';
|
import ToolbarButton from './ToolbarButton';
|
||||||
|
|
||||||
|
const ToolbarContainer = styled.div`
|
||||||
|
background-color: ${colors.textFieldBorder};
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 11px 14px;
|
||||||
|
min-height: 58px;
|
||||||
|
transition: background-color ${transitions.main}, color ${transitions.main};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ToolbarDropdownWrapper = styled.div`
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ToolbarToggle = styled.div`
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
margin: 0 10px;
|
||||||
|
`
|
||||||
|
|
||||||
|
const StyledToggle = ToolbarToggle.withComponent(Toggle);
|
||||||
|
|
||||||
|
const ToolbarToggleLabel = styled.span`
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 20px;
|
||||||
|
width: ${props => props.offPosition ? '62px' : '70px'};
|
||||||
|
|
||||||
|
${props => props.isActive && css`
|
||||||
|
font-weight: 600;
|
||||||
|
color: ${colors.active};
|
||||||
|
`}
|
||||||
|
`
|
||||||
|
|
||||||
export default class Toolbar extends React.Component {
|
export default class Toolbar extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
buttons: PropTypes.object,
|
buttons: PropTypes.object,
|
||||||
@ -16,7 +63,6 @@ export default class Toolbar extends React.Component {
|
|||||||
onAddAsset: PropTypes.func,
|
onAddAsset: PropTypes.func,
|
||||||
getAsset: PropTypes.func,
|
getAsset: PropTypes.func,
|
||||||
disabled: PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
className: PropTypes.string,
|
|
||||||
buttons: ImmutablePropTypes.list
|
buttons: ImmutablePropTypes.list
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,23 +93,12 @@ export default class Toolbar extends React.Component {
|
|||||||
getAsset,
|
getAsset,
|
||||||
disabled,
|
disabled,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
className,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { activePlugin } = this.state;
|
const { activePlugin } = this.state;
|
||||||
|
|
||||||
/**
|
|
||||||
* Because the toggle labels change font weight for active/inactive state,
|
|
||||||
* we need to set estimated widths for them to maintain position without
|
|
||||||
* moving other inline items on font weight change.
|
|
||||||
*/
|
|
||||||
const toggleOffLabel = 'Rich text';
|
|
||||||
const toggleOffLabelWidth = '62px';
|
|
||||||
const toggleOnLabel = 'Markdown';
|
|
||||||
const toggleOnLabelWidth = '70px';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={c(className, 'nc-toolbar-Toolbar')}>
|
<ToolbarContainer>
|
||||||
<div>
|
<div>
|
||||||
<ToolbarButton
|
<ToolbarButton
|
||||||
type="bold"
|
type="bold"
|
||||||
@ -155,7 +190,7 @@ export default class Toolbar extends React.Component {
|
|||||||
isHidden={this.isHidden('numbered-list')}
|
isHidden={this.isHidden('numbered-list')}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<div className="nc-toolbar-dropdown">
|
<ToolbarDropdownWrapper>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownTopOverlap="36px"
|
dropdownTopOverlap="36px"
|
||||||
renderButton={() => (
|
renderButton={() => (
|
||||||
@ -173,35 +208,14 @@ export default class Toolbar extends React.Component {
|
|||||||
<DropdownItem key={idx} label={plugin.get('label')} onClick={() => onSubmit(plugin.get('id'))} />
|
<DropdownItem key={idx} label={plugin.get('label')} onClick={() => onSubmit(plugin.get('id'))} />
|
||||||
))}
|
))}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
</ToolbarDropdownWrapper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<ToolbarToggle>
|
||||||
<div className="nc-markdownWidget-toolbar-toggle">
|
<ToolbarToggleLabel isActive={!rawMode} offPosition>Rich Text</ToolbarToggleLabel>
|
||||||
<span
|
<StyledToggle active={rawMode} onChange={onToggleMode}/>
|
||||||
style={{ width: toggleOffLabelWidth }}
|
<ToolbarToggleLabel isActive={rawMode}>Markdown</ToolbarToggleLabel>
|
||||||
className={c(
|
</ToolbarToggle>
|
||||||
'nc-markdownWidget-toolbar-toggle-label',
|
</ToolbarContainer>
|
||||||
{ 'nc-markdownWidget-toolbar-toggle-label-active': !rawMode },
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{toggleOffLabel}
|
|
||||||
</span>
|
|
||||||
<Toggle
|
|
||||||
active={rawMode}
|
|
||||||
onChange={onToggleMode}
|
|
||||||
className="nc-markdownWidget-toolbar-toggle"
|
|
||||||
classNameBackground="nc-markdownWidget-toolbar-toggle-background"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
style={{ width: toggleOnLabelWidth }}
|
|
||||||
className={c(
|
|
||||||
'nc-markdownWidget-toolbar-toggle-label',
|
|
||||||
{ 'nc-markdownWidget-toolbar-toggle-label-active': rawMode },
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{toggleOnLabel}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,24 +1,42 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import c from 'classnames';
|
import PropTypes from 'prop-types';
|
||||||
import { Icon } from 'netlify-cms-ui-default';
|
import styled from 'react-emotion';
|
||||||
|
import { Icon, buttons } from 'netlify-cms-ui-default';
|
||||||
|
|
||||||
|
const StyledToolbarButton = styled.button`
|
||||||
|
${buttons.button};
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
font-size: 16px;
|
||||||
|
color: ${props => props.isActive ? '#1e2532' : 'inherit'};
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: auto;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
${Icon} {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
const ToolbarButton = ({ type, label, icon, onClick, isActive, isHidden, disabled }) => {
|
const ToolbarButton = ({ type, label, icon, onClick, isActive, isHidden, disabled }) => {
|
||||||
const active = isActive && type && isActive(type);
|
|
||||||
|
|
||||||
if (isHidden) {
|
if (isHidden) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<StyledToolbarButton
|
||||||
className={c('nc-toolbarButton-button', { ['nc-toolbarButton-active']: active })}
|
isActive={isActive && type && isActive(type)}
|
||||||
onClick={e => onClick && onClick(e, type)}
|
onClick={e => onClick && onClick(e, type)}
|
||||||
title={label}
|
title={label}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{ icon ? <Icon type={icon}/> : label }
|
{ icon ? <Icon type={icon}/> : label }
|
||||||
</button>
|
</StyledToolbarButton>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -1,17 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import React, { Component } from 'react';
|
import styled, { css, cx } from 'react-emotion';
|
||||||
import { get, isEmpty, debounce } from 'lodash';
|
import { get, isEmpty, debounce } from 'lodash';
|
||||||
import { Map } from 'immutable';
|
import { Map } from 'immutable';
|
||||||
import { Value, Document, Block, Text } from 'slate';
|
import { Value, Document, Block, Text } from 'slate';
|
||||||
import { Editor as Slate } from 'slate-react';
|
import { Editor as Slate } from 'slate-react';
|
||||||
import { slateToMarkdown, markdownToSlate, htmlToSlate } from 'EditorWidgets/Markdown/serializers';
|
import { lengths, fonts } from 'netlify-cms-ui-default';
|
||||||
import { getEditorComponents } from 'Lib/registry';
|
import { slateToMarkdown, markdownToSlate, htmlToSlate } from '../serializers';
|
||||||
import Toolbar from 'EditorWidgets/Markdown/MarkdownControl/Toolbar/Toolbar';
|
import Toolbar from '../MarkdownControl/Toolbar';
|
||||||
import { renderNode, renderMark } from './renderers';
|
import { renderNode, renderMark } from './renderers';
|
||||||
import { validateNode } from './validators';
|
import { validateNode } from './validators';
|
||||||
import plugins, { EditListConfigured } from './plugins';
|
import plugins, { EditListConfigured } from './plugins';
|
||||||
import onKeyDown from './keys';
|
import onKeyDown from './keys';
|
||||||
|
import visualEditorStyles from './visualEditorStyles';
|
||||||
|
import { editorStyleVars, EditorControlBar } from '../styles';
|
||||||
|
|
||||||
|
const VisualEditorContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
`
|
||||||
|
|
||||||
const createEmptyRawDoc = () => {
|
const createEmptyRawDoc = () => {
|
||||||
const emptyText = Text.create('');
|
const emptyText = Text.create('');
|
||||||
@ -26,7 +33,7 @@ const createSlateValue = (rawValue) => {
|
|||||||
return Value.create({ document });
|
return Value.create({ document });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Editor extends Component {
|
export default class Editor extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onAddAsset: PropTypes.func.isRequired,
|
onAddAsset: PropTypes.func.isRequired,
|
||||||
getAsset: PropTypes.func.isRequired,
|
getAsset: PropTypes.func.isRequired,
|
||||||
@ -41,7 +48,6 @@ export default class Editor extends Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
value: createSlateValue(props.value),
|
value: createSlateValue(props.value),
|
||||||
shortcodePlugins: getEditorComponents(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,10 +179,11 @@ export default class Editor extends Component {
|
|||||||
|
|
||||||
|
|
||||||
handleDocumentChange = debounce(change => {
|
handleDocumentChange = debounce(change => {
|
||||||
|
const { onChange, getEditorComponents } = this.props;
|
||||||
const raw = change.value.document.toJSON();
|
const raw = change.value.document.toJSON();
|
||||||
const plugins = this.state.shortcodePlugins;
|
const plugins = getEditorComponents();
|
||||||
const markdown = slateToMarkdown(raw, plugins);
|
const markdown = slateToMarkdown(raw);
|
||||||
this.props.onChange(markdown);
|
onChange(markdown);
|
||||||
}, 150);
|
}, 150);
|
||||||
|
|
||||||
handleChange = change => {
|
handleChange = change => {
|
||||||
@ -191,11 +198,11 @@ export default class Editor extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { onAddAsset, getAsset, className, field } = this.props;
|
const { onAddAsset, getAsset, className, field, getEditorComponents } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="nc-visualEditor-wrapper">
|
<VisualEditorContainer>
|
||||||
<div className="nc-visualEditor-editorControlBar">
|
<EditorControlBar>
|
||||||
<Toolbar
|
<Toolbar
|
||||||
onMarkClick={this.handleMarkClick}
|
onMarkClick={this.handleMarkClick}
|
||||||
onBlockClick={this.handleBlockClick}
|
onBlockClick={this.handleBlockClick}
|
||||||
@ -204,15 +211,15 @@ export default class Editor extends Component {
|
|||||||
selectionHasBlock={this.selectionHasBlock}
|
selectionHasBlock={this.selectionHasBlock}
|
||||||
selectionHasLink={this.hasLinks}
|
selectionHasLink={this.hasLinks}
|
||||||
onToggleMode={this.handleToggle}
|
onToggleMode={this.handleToggle}
|
||||||
plugins={this.state.shortcodePlugins}
|
plugins={getEditorComponents()}
|
||||||
onSubmit={this.handlePluginAdd}
|
onSubmit={this.handlePluginAdd}
|
||||||
onAddAsset={onAddAsset}
|
onAddAsset={onAddAsset}
|
||||||
getAsset={getAsset}
|
getAsset={getAsset}
|
||||||
buttons={field.get('buttons')}
|
buttons={field.get('buttons')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</EditorControlBar>
|
||||||
<Slate
|
<Slate
|
||||||
className={`${className} nc-visualEditor-editor`}
|
className={cx(className, visualEditorStyles)}
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
renderNode={renderNode}
|
renderNode={renderNode}
|
||||||
renderMark={renderMark}
|
renderMark={renderMark}
|
||||||
@ -224,7 +231,7 @@ export default class Editor extends Component {
|
|||||||
ref={this.processRef}
|
ref={this.processRef}
|
||||||
spellCheck
|
spellCheck
|
||||||
/>
|
/>
|
||||||
</div>
|
</VisualEditorContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { fromJS } from 'immutable';
|
import { fromJS } from 'immutable';
|
||||||
import { markdownToSlate } from 'EditorWidgets/Markdown/serializers';
|
import { markdownToSlate } from '../../serializers';
|
||||||
|
|
||||||
const parser = markdownToSlate;
|
const parser = markdownToSlate;
|
||||||
|
|
@ -1,15 +1,15 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import c from 'classnames';
|
import PropTypes from 'prop-types';
|
||||||
import { markdownToRemark, remarkToMarkdown } from 'EditorWidgets/Markdown/serializers'
|
|
||||||
import RawEditor from './RawEditor';
|
import RawEditor from './RawEditor';
|
||||||
import VisualEditor from './VisualEditor';
|
import VisualEditor from './VisualEditor';
|
||||||
|
|
||||||
const MODE_STORAGE_KEY = 'cms.md-mode';
|
const MODE_STORAGE_KEY = 'cms.md-mode';
|
||||||
|
|
||||||
let editorControl;
|
let editorControl;
|
||||||
|
let _getEditorComponents = () => [];
|
||||||
|
|
||||||
export const getEditorControl = () => editorControl;
|
export const getEditorControl = () => editorControl;
|
||||||
|
export const getEditorComponents = () => _getEditorComponents();
|
||||||
|
|
||||||
export default class MarkdownControl extends React.Component {
|
export default class MarkdownControl extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@ -28,6 +28,7 @@ export default class MarkdownControl extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
editorControl = props.editorControl;
|
editorControl = props.editorControl;
|
||||||
|
_getEditorComponents = props.getEditorComponents;
|
||||||
this.state = { mode: localStorage.getItem(MODE_STORAGE_KEY) || 'visual' };
|
this.state = { mode: localStorage.getItem(MODE_STORAGE_KEY) || 'visual' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +46,8 @@ export default class MarkdownControl extends React.Component {
|
|||||||
getAsset,
|
getAsset,
|
||||||
value,
|
value,
|
||||||
classNameWrapper,
|
classNameWrapper,
|
||||||
field
|
field,
|
||||||
|
getEditorComponents,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { mode } = this.state;
|
const { mode } = this.state;
|
||||||
@ -59,6 +61,7 @@ export default class MarkdownControl extends React.Component {
|
|||||||
className={classNameWrapper}
|
className={classNameWrapper}
|
||||||
value={value}
|
value={value}
|
||||||
field={field}
|
field={field}
|
||||||
|
getEditorComponents={getEditorComponents}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { List } from 'immutable';
|
import { List } from 'immutable';
|
||||||
import cn from 'classnames';
|
|
||||||
import Shortcode from './Shortcode';
|
import Shortcode from './Shortcode';
|
||||||
|
|
||||||
/**
|
/**
|
@ -0,0 +1,106 @@
|
|||||||
|
import { css } from 'react-emotion';
|
||||||
|
import { colors, lengths, fonts } from 'netlify-cms-ui-default';
|
||||||
|
import { editorStyleVars } from '../styles';
|
||||||
|
|
||||||
|
export default css`
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-x: auto;
|
||||||
|
min-height: ${lengths.richTextEditorMinHeight};
|
||||||
|
font-family: ${fonts.primary};
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-top: 0;
|
||||||
|
margin-top: -${editorStyleVars.stickyDistanceBottom};
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 32px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, pre, blockquote, ul, ol {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: 1px solid;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre > code {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: #000;
|
||||||
|
color: #ccc;
|
||||||
|
border-radius: ${lengths.borderRadius};
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: ${colors.background};
|
||||||
|
border-radius: ${lengths.borderRadius};
|
||||||
|
padding: 0 2px;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding-left: 16px;
|
||||||
|
border-left: 3px solid ${colors.background};
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th {
|
||||||
|
border: 2px solid black;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
`;
|
@ -1,13 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { markdownToHtml } from 'EditorWidgets/Markdown/serializers';
|
import PropTypes from 'prop-types';
|
||||||
|
import { WidgetPreviewContainer } from 'netlify-cms-ui-default';
|
||||||
|
import { markdownToHtml } from './serializers';
|
||||||
|
|
||||||
const MarkdownPreview = ({ value, getAsset }) => {
|
const MarkdownPreview = ({ value, getAsset }) => {
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const html = markdownToHtml(value, getAsset);
|
const html = markdownToHtml(value, getAsset);
|
||||||
return <div className="nc-widgetPreview" dangerouslySetInnerHTML={{__html: html}}></div>;
|
return <WidgetPreviewContainer dangerouslySetInnerHTML={{__html: html}}/>
|
||||||
};
|
};
|
||||||
|
|
||||||
MarkdownPreview.propTypes = {
|
MarkdownPreview.propTypes = {
|
@ -4,7 +4,7 @@ import React from 'react';
|
|||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import { padStart } from 'lodash';
|
import { padStart } from 'lodash';
|
||||||
import MarkdownPreview from '../index';
|
import MarkdownPreview from '../index';
|
||||||
import { markdownToHtml } from 'EditorWidgets/Markdown/serializers';
|
import { markdownToHtml } from '../../serializers';
|
||||||
|
|
||||||
const parser = markdownToHtml;
|
const parser = markdownToHtml;
|
||||||
|
|
2
packages/netlify-cms-widget-markdown/src/index.js
Normal file
2
packages/netlify-cms-widget-markdown/src/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export MarkdownControl from './MarkdownControl'
|
||||||
|
export MarkdownPreview from './MarkdownPreview'
|
@ -7,7 +7,6 @@ import remarkToRehype from 'remark-rehype';
|
|||||||
import rehypeToHtml from 'rehype-stringify';
|
import rehypeToHtml from 'rehype-stringify';
|
||||||
import htmlToRehype from 'rehype-parse';
|
import htmlToRehype from 'rehype-parse';
|
||||||
import rehypeToRemark from 'rehype-remark';
|
import rehypeToRemark from 'rehype-remark';
|
||||||
import { getEditorComponents } from 'Lib/registry';
|
|
||||||
import remarkToRehypeShortcodes from './remarkRehypeShortcodes';
|
import remarkToRehypeShortcodes from './remarkRehypeShortcodes';
|
||||||
import rehypePaperEmoji from './rehypePaperEmoji';
|
import rehypePaperEmoji from './rehypePaperEmoji';
|
||||||
import remarkAssertParents from './remarkAssertParents';
|
import remarkAssertParents from './remarkAssertParents';
|
||||||
@ -21,6 +20,7 @@ import remarkEscapeMarkdownEntities from './remarkEscapeMarkdownEntities';
|
|||||||
import remarkStripTrailingBreaks from './remarkStripTrailingBreaks';
|
import remarkStripTrailingBreaks from './remarkStripTrailingBreaks';
|
||||||
import remarkAllowHtmlEntities from './remarkAllowHtmlEntities';
|
import remarkAllowHtmlEntities from './remarkAllowHtmlEntities';
|
||||||
import slateToRemark from './slateRemark';
|
import slateToRemark from './slateRemark';
|
||||||
|
import { getEditorComponents } from '../MarkdownControl';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This module contains all serializers for the Markdown widget.
|
* This module contains all serializers for the Markdown widget.
|
@ -1,5 +1,5 @@
|
|||||||
import { has, flow, partial, flatMap, flatten, map } from 'lodash';
|
import { has, flow, partial, flatMap, flatten, map } from 'lodash';
|
||||||
import { joinPatternSegments, combinePatterns, replaceWhen } from 'Lib/regexHelper';
|
import { joinPatternSegments, combinePatterns, replaceWhen } from '../regexHelper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reusable regular expressions segments.
|
* Reusable regular expressions segments.
|
12
packages/netlify-cms-widget-markdown/src/styles.js
Normal file
12
packages/netlify-cms-widget-markdown/src/styles.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import styled, { css } from 'react-emotion';
|
||||||
|
|
||||||
|
export const editorStyleVars = {
|
||||||
|
stickyDistanceBottom: '100px',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EditorControlBar = styled.div`
|
||||||
|
z-index: 1;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
margin-bottom: ${editorStyleVars.stickyDistanceBottom};
|
||||||
|
`
|
3
packages/netlify-cms-widget-markdown/webpack.config.js
Normal file
3
packages/netlify-cms-widget-markdown/webpack.config.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
const { getConfig } = require('../../scripts/webpack.js');
|
||||||
|
|
||||||
|
module.exports = getConfig();
|
@ -18,11 +18,6 @@ const styles = {
|
|||||||
export default class ObjectControl extends Component {
|
export default class ObjectControl extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onChangeObject: PropTypes.func.isRequired,
|
onChangeObject: PropTypes.func.isRequired,
|
||||||
onOpenMediaLibrary: PropTypes.func.isRequired,
|
|
||||||
mediaPaths: ImmutablePropTypes.map.isRequired,
|
|
||||||
onAddAsset: PropTypes.func.isRequired,
|
|
||||||
onRemoveInsertedMedia: PropTypes.func.isRequired,
|
|
||||||
getAsset: PropTypes.func.isRequired,
|
|
||||||
value: PropTypes.oneOfType([
|
value: PropTypes.oneOfType([
|
||||||
PropTypes.node,
|
PropTypes.node,
|
||||||
PropTypes.object,
|
PropTypes.object,
|
||||||
@ -58,17 +53,7 @@ export default class ObjectControl extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
controlFor(field, key) {
|
controlFor(field, key) {
|
||||||
const {
|
const { value, onChangeObject, editorControl: EditorControl, resolveWidget } = this.props;
|
||||||
onAddAsset,
|
|
||||||
onOpenMediaLibrary,
|
|
||||||
mediaPaths,
|
|
||||||
onRemoveInsertedMedia,
|
|
||||||
getAsset,
|
|
||||||
value,
|
|
||||||
onChangeObject,
|
|
||||||
editorControl: EditorControl,
|
|
||||||
resolveWidget,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (field.get('widget') === 'hidden') {
|
if (field.get('widget') === 'hidden') {
|
||||||
return null;
|
return null;
|
||||||
@ -79,17 +64,7 @@ export default class ObjectControl extends Component {
|
|||||||
const fieldValue = value && Map.isMap(value) ? value.get(fieldName) : value;
|
const fieldValue = value && Map.isMap(value) ? value.get(fieldName) : value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditorControl
|
<EditorControl key={key} field={field} value={fieldValue} onChange={onChangeObject}/>
|
||||||
key={key}
|
|
||||||
field={field}
|
|
||||||
value={fieldValue}
|
|
||||||
mediaPaths={mediaPaths}
|
|
||||||
getAsset={getAsset}
|
|
||||||
onChange={onChangeObject}
|
|
||||||
onOpenMediaLibrary={onOpenMediaLibrary}
|
|
||||||
onAddAsset={onAddAsset}
|
|
||||||
onRemoveInsertedMedia={onRemoveInsertedMedia}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
yarn.lock
23
yarn.lock
@ -1705,10 +1705,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1:
|
|||||||
escape-string-regexp "^1.0.5"
|
escape-string-regexp "^1.0.5"
|
||||||
supports-color "^5.3.0"
|
supports-color "^5.3.0"
|
||||||
|
|
||||||
change-emitter@^0.1.2:
|
|
||||||
version "0.1.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
|
|
||||||
|
|
||||||
character-entities-html4@^1.0.0:
|
character-entities-html4@^1.0.0:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610"
|
resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610"
|
||||||
@ -3123,7 +3119,7 @@ fb-watchman@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
bser "^2.0.0"
|
bser "^2.0.0"
|
||||||
|
|
||||||
fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.9:
|
fbjs@^0.8.16, fbjs@^0.8.9:
|
||||||
version "0.8.17"
|
version "0.8.17"
|
||||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3728,7 +3724,7 @@ hmac-drbg@^1.0.0:
|
|||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
minimalistic-crypto-utils "^1.0.1"
|
minimalistic-crypto-utils "^1.0.1"
|
||||||
|
|
||||||
hoist-non-react-statics@^2.1.0, hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
|
hoist-non-react-statics@^2.1.0, hoist-non-react-statics@^2.5.0:
|
||||||
version "2.5.5"
|
version "2.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
|
||||||
|
|
||||||
@ -6421,7 +6417,7 @@ react-is@^16.4.1:
|
|||||||
version "16.4.1"
|
version "16.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
|
||||||
|
|
||||||
react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
|
react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4:
|
||||||
version "3.0.4"
|
version "3.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
|
||||||
|
|
||||||
@ -6667,17 +6663,6 @@ realpath-native@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
util.promisify "^1.0.0"
|
util.promisify "^1.0.0"
|
||||||
|
|
||||||
recompose@^0.27.1:
|
|
||||||
version "0.27.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.27.1.tgz#1a49e931f183634516633bbb4f4edbfd3f38a7ba"
|
|
||||||
dependencies:
|
|
||||||
babel-runtime "^6.26.0"
|
|
||||||
change-emitter "^0.1.2"
|
|
||||||
fbjs "^0.8.1"
|
|
||||||
hoist-non-react-statics "^2.3.1"
|
|
||||||
react-lifecycles-compat "^3.0.2"
|
|
||||||
symbol-observable "^1.0.4"
|
|
||||||
|
|
||||||
redent@^1.0.0:
|
redent@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
|
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
|
||||||
@ -7712,7 +7697,7 @@ svg-inline-loader@^0.8.0:
|
|||||||
object-assign "^4.0.1"
|
object-assign "^4.0.1"
|
||||||
simple-html-tokenizer "^0.1.1"
|
simple-html-tokenizer "^0.1.1"
|
||||||
|
|
||||||
symbol-observable@^1.0.3, symbol-observable@^1.0.4:
|
symbol-observable@^1.0.3:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user