@ -10,7 +10,7 @@ function isHidden(field) {
|
||||
export default class ControlPane extends Component {
|
||||
|
||||
controlFor(field) {
|
||||
const { entry, fieldsMetaData, getMedia, onChange, onAddMedia, onRemoveMedia } = this.props;
|
||||
const { entry, fieldsMetaData, getAsset, onChange, onAddAsset, onRemoveAsset } = this.props;
|
||||
const widget = resolveWidget(field.get('widget'));
|
||||
const fieldName = field.get('name');
|
||||
const value = entry.getIn(['data', fieldName]);
|
||||
@ -25,9 +25,9 @@ export default class ControlPane extends Component {
|
||||
value,
|
||||
metadata,
|
||||
onChange: (newValue, newMetadata) => onChange(fieldName, newValue, newMetadata),
|
||||
onAddMedia,
|
||||
onRemoveMedia,
|
||||
getMedia,
|
||||
onAddAsset,
|
||||
onRemoveAsset,
|
||||
getAsset,
|
||||
})
|
||||
}
|
||||
</div>
|
||||
@ -60,8 +60,8 @@ ControlPane.propTypes = {
|
||||
entry: ImmutablePropTypes.map.isRequired,
|
||||
fields: ImmutablePropTypes.list.isRequired,
|
||||
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onRemoveMedia: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -26,10 +26,10 @@ class EntryEditor extends Component {
|
||||
entry,
|
||||
fields,
|
||||
fieldsMetaData,
|
||||
getMedia,
|
||||
getAsset,
|
||||
onChange,
|
||||
onAddMedia,
|
||||
onRemoveMedia,
|
||||
onAddAsset,
|
||||
onRemoveAsset,
|
||||
onPersist,
|
||||
onCancelEdit,
|
||||
} = this.props;
|
||||
@ -53,10 +53,10 @@ class EntryEditor extends Component {
|
||||
entry={entry}
|
||||
fields={fields}
|
||||
fieldsMetaData={fieldsMetaData}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
onChange={onChange}
|
||||
onAddMedia={onAddMedia}
|
||||
onRemoveMedia={onRemoveMedia}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
/>
|
||||
|
||||
</div>
|
||||
@ -67,7 +67,7 @@ class EntryEditor extends Component {
|
||||
entry={entry}
|
||||
fields={fields}
|
||||
fieldsMetaData={fieldsMetaData}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
</div>
|
||||
</SplitPane>
|
||||
@ -91,11 +91,11 @@ EntryEditor.propTypes = {
|
||||
entry: ImmutablePropTypes.map.isRequired,
|
||||
fields: ImmutablePropTypes.list.isRequired,
|
||||
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onPersist: PropTypes.func.isRequired,
|
||||
onRemoveMedia: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
onCancelEdit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,7 @@ import htmlSyntax from 'markup-it/syntaxes/html';
|
||||
import reInline from 'markup-it/syntaxes/markdown/re/inline';
|
||||
import MarkupItReactRenderer from '../';
|
||||
|
||||
function getMedia(path) {
|
||||
function getAsset(path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ describe('MarkitupReactRenderer', () => {
|
||||
<MarkupItReactRenderer
|
||||
value="# Title"
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
const tree1 = component.html();
|
||||
@ -38,7 +38,7 @@ describe('MarkitupReactRenderer', () => {
|
||||
<MarkupItReactRenderer
|
||||
value="# Title"
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
const syntax1 = component.instance().props.syntax;
|
||||
@ -83,7 +83,7 @@ Text with **bold** & _em_ elements
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -98,7 +98,7 @@ Text with **bold** & _em_ elements
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -123,7 +123,7 @@ Text with **bold** & _em_ elements
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -143,7 +143,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -157,7 +157,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -169,7 +169,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -197,7 +197,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -241,7 +241,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
|
||||
value={value}
|
||||
syntax={myMarkdownSyntax}
|
||||
schema={myCustomSchema}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
@ -255,7 +255,7 @@ I get 10 times more traffic from [Google] [1] than from [Yahoo] [2] or [MSN] [3]
|
||||
<MarkupItReactRenderer
|
||||
value={value}
|
||||
syntax={htmlSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
);
|
||||
expect(component.html()).toMatchSnapshot();
|
||||
|
@ -13,9 +13,7 @@ const defaultSchema = {
|
||||
[BLOCKS.BLOCKQUOTE]: 'blockquote',
|
||||
[BLOCKS.PARAGRAPH]: 'p',
|
||||
[BLOCKS.FOOTNOTE]: 'footnote',
|
||||
[BLOCKS.HTML]: ({ token }) => {
|
||||
return <div dangerouslySetInnerHTML={{ __html: token.get('raw') }} />;
|
||||
},
|
||||
[BLOCKS.HTML]: ({ token }) => <div dangerouslySetInnerHTML={{ __html: token.get('raw') }} />,
|
||||
[BLOCKS.HR]: 'hr',
|
||||
[BLOCKS.HEADING_1]: 'h1',
|
||||
[BLOCKS.HEADING_2]: 'h2',
|
||||
@ -63,10 +61,10 @@ export default class MarkupItReactRenderer extends React.Component {
|
||||
}
|
||||
|
||||
sanitizeProps(props) {
|
||||
const { getMedia } = this.props;
|
||||
const { getAsset } = this.props;
|
||||
|
||||
if (props.image) {
|
||||
props = Object.assign({}, props, { src: getMedia(props.image).toString() });
|
||||
props = Object.assign({}, props, { src: getAsset(props.image).toString() });
|
||||
}
|
||||
|
||||
return omit(props, notAllowedAttributes);
|
||||
@ -115,7 +113,7 @@ export default class MarkupItReactRenderer extends React.Component {
|
||||
|
||||
|
||||
render() {
|
||||
const { value, schema, getMedia } = this.props;
|
||||
const { value, schema, getAsset } = this.props;
|
||||
const content = this.parser.toContent(value);
|
||||
return this.renderToken({ ...defaultSchema, ...schema }, content.get('token'));
|
||||
}
|
||||
@ -128,5 +126,5 @@ MarkupItReactRenderer.propTypes = {
|
||||
PropTypes.string,
|
||||
PropTypes.func,
|
||||
])),
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -24,6 +24,6 @@ Preview.propTypes = {
|
||||
collection: ImmutablePropTypes.map.isRequired,
|
||||
entry: ImmutablePropTypes.map.isRequired,
|
||||
fields: ImmutablePropTypes.list.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
widgetFor: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ export default class PreviewPane extends React.Component {
|
||||
}
|
||||
|
||||
widgetFor = (name) => {
|
||||
const { fields, entry, fieldsMetaData, getMedia } = this.props;
|
||||
const { fields, entry, fieldsMetaData, getAsset } = this.props;
|
||||
const field = fields.find(f => f.get('name') === name);
|
||||
let value = entry.getIn(['data', field.get('name')]);
|
||||
const metadata = fieldsMetaData.get(field.get('name'));
|
||||
@ -46,7 +46,7 @@ export default class PreviewPane extends React.Component {
|
||||
value,
|
||||
field,
|
||||
metadata,
|
||||
getMedia,
|
||||
getAsset,
|
||||
});
|
||||
};
|
||||
|
||||
@ -72,6 +72,7 @@ export default class PreviewPane extends React.Component {
|
||||
|
||||
renderPreview() {
|
||||
const { entry, collection } = this.props;
|
||||
if (!entry || !entry.get('data')) return;
|
||||
const component = registry.getPreviewTemplate(selectTemplateName(collection, entry.get('slug'))) || Preview;
|
||||
|
||||
this.inferFields();
|
||||
@ -104,5 +105,5 @@ PreviewPane.propTypes = {
|
||||
fields: ImmutablePropTypes.list.isRequired,
|
||||
entry: ImmutablePropTypes.map.isRequired,
|
||||
fieldsMetaData: ImmutablePropTypes.map.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -13,6 +13,8 @@ import MarkdownControl from './Widgets/MarkdownControl';
|
||||
import MarkdownPreview from './Widgets/MarkdownPreview';
|
||||
import ImageControl from './Widgets/ImageControl';
|
||||
import ImagePreview from './Widgets/ImagePreview';
|
||||
import FileControl from './Widgets/FileControl';
|
||||
import FilePreview from './Widgets/FilePreview';
|
||||
import DateControl from './Widgets/DateControl';
|
||||
import DatePreview from './Widgets/DatePreview';
|
||||
import DateTimeControl from './Widgets/DateTimeControl';
|
||||
@ -31,6 +33,7 @@ registry.registerWidget('number', NumberControl, NumberPreview);
|
||||
registry.registerWidget('list', ListControl, ListPreview);
|
||||
registry.registerWidget('markdown', MarkdownControl, MarkdownPreview);
|
||||
registry.registerWidget('image', ImageControl, ImagePreview);
|
||||
registry.registerWidget('file', FileControl, FilePreview);
|
||||
registry.registerWidget('date', DateControl, DatePreview);
|
||||
registry.registerWidget('datetime', DateTimeControl, DateTimePreview);
|
||||
registry.registerWidget('select', SelectControl, SelectPreview);
|
||||
|
122
src/components/Widgets/FileControl.js
Normal file
122
src/components/Widgets/FileControl.js
Normal file
@ -0,0 +1,122 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { truncateMiddle } from '../../lib/textHelper';
|
||||
import { Loader } from '../UI';
|
||||
import AssetProxy, { createAssetProxy } from '../../valueObjects/AssetProxy';
|
||||
|
||||
const MAX_DISPLAY_LENGTH = 50;
|
||||
|
||||
export default class FileControl extends React.Component {
|
||||
state = {
|
||||
processing: false,
|
||||
};
|
||||
|
||||
handleFileInputRef = (el) => {
|
||||
this._fileInput = el;
|
||||
};
|
||||
|
||||
handleClick = (e) => {
|
||||
this._fileInput.click();
|
||||
};
|
||||
|
||||
handleDragEnter = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
handleDragOver = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
handleChange = (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
const fileList = e.dataTransfer ? e.dataTransfer.files : e.target.files;
|
||||
const files = [...fileList];
|
||||
const imageType = /^image\//;
|
||||
|
||||
// Return the first file on the list
|
||||
const file = files[0];
|
||||
|
||||
this.props.onRemoveAsset(this.props.value);
|
||||
if (file) {
|
||||
this.setState({ processing: true });
|
||||
createAssetProxy(file.name, file, false, this.props.field.get('private', false))
|
||||
.then((assetProxy) => {
|
||||
this.setState({ processing: false });
|
||||
this.props.onAddAsset(assetProxy);
|
||||
this.props.onChange(assetProxy.public_path);
|
||||
});
|
||||
} else {
|
||||
this.props.onChange(null);
|
||||
}
|
||||
};
|
||||
|
||||
renderFileName = () => {
|
||||
if (!this.props.value) return null;
|
||||
if (this.value instanceof AssetProxy) {
|
||||
return truncateMiddle(this.props.value.path, MAX_DISPLAY_LENGTH);
|
||||
} else {
|
||||
return truncateMiddle(this.props.value, MAX_DISPLAY_LENGTH);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { processing } = this.state;
|
||||
const fileName = this.renderFileName();
|
||||
if (processing) {
|
||||
return (
|
||||
<div style={styles.imageUpload}>
|
||||
<span style={styles.message}>
|
||||
<Loader active />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={styles.imageUpload}
|
||||
onDragEnter={this.handleDragEnter}
|
||||
onDragOver={this.handleDragOver}
|
||||
onDrop={this.handleChange}
|
||||
>
|
||||
<span style={styles.message} onClick={this.handleClick}>
|
||||
{fileName ? fileName : 'Tip: Click here to select a file to upload, or drag an image directly into this box from your desktop'}
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
onChange={this.handleChange}
|
||||
style={styles.input}
|
||||
ref={this.handleFileInputRef}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = {
|
||||
input: {
|
||||
display: 'none',
|
||||
},
|
||||
message: {
|
||||
padding: '20px',
|
||||
display: 'block',
|
||||
fontSize: '12px',
|
||||
},
|
||||
imageUpload: {
|
||||
backgroundColor: '#fff',
|
||||
textAlign: 'center',
|
||||
color: '#999',
|
||||
border: '1px dashed #eee',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
};
|
||||
|
||||
FileControl.propTypes = {
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
field: PropTypes.object,
|
||||
};
|
15
src/components/Widgets/FilePreview.js
Normal file
15
src/components/Widgets/FilePreview.js
Normal file
@ -0,0 +1,15 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import previewStyle from './defaultPreviewStyle';
|
||||
|
||||
export default function FilePreview({ value, getAsset }) {
|
||||
return (<div style={previewStyle}>
|
||||
{ value ?
|
||||
<a href={getAsset(value)}>{ value }</a>
|
||||
: null}
|
||||
</div>);
|
||||
}
|
||||
|
||||
FilePreview.propTypes = {
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
};
|
@ -1,10 +1,15 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { truncateMiddle } from '../../lib/textHelper';
|
||||
import MediaProxy from '../../valueObjects/MediaProxy';
|
||||
import { Loader } from '../UI';
|
||||
import AssetProxy, { createAssetProxy } from '../../valueObjects/AssetProxy';
|
||||
|
||||
const MAX_DISPLAY_LENGTH = 50;
|
||||
|
||||
export default class ImageControl extends React.Component {
|
||||
state = {
|
||||
processing: false,
|
||||
};
|
||||
|
||||
handleFileInputRef = (el) => {
|
||||
this._fileInput = el;
|
||||
};
|
||||
@ -38,11 +43,15 @@ export default class ImageControl extends React.Component {
|
||||
}
|
||||
});
|
||||
|
||||
this.props.onRemoveMedia(this.props.value);
|
||||
this.props.onRemoveAsset(this.props.value);
|
||||
if (file) {
|
||||
const mediaProxy = new MediaProxy(file.name, file);
|
||||
this.props.onAddMedia(mediaProxy);
|
||||
this.props.onChange(mediaProxy.public_path);
|
||||
this.setState({ processing: true });
|
||||
createAssetProxy(file.name, file)
|
||||
.then((assetProxy) => {
|
||||
this.setState({ processing: false });
|
||||
this.props.onAddAsset(assetProxy);
|
||||
this.props.onChange(assetProxy.public_path);
|
||||
});
|
||||
} else {
|
||||
this.props.onChange(null);
|
||||
}
|
||||
@ -50,7 +59,7 @@ export default class ImageControl extends React.Component {
|
||||
|
||||
renderImageName = () => {
|
||||
if (!this.props.value) return null;
|
||||
if (this.value instanceof MediaProxy) {
|
||||
if (this.value instanceof AssetProxy) {
|
||||
return truncateMiddle(this.props.value.path, MAX_DISPLAY_LENGTH);
|
||||
} else {
|
||||
return truncateMiddle(this.props.value, MAX_DISPLAY_LENGTH);
|
||||
@ -58,14 +67,25 @@ export default class ImageControl extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { processing } = this.state;
|
||||
const imageName = this.renderImageName();
|
||||
if (processing) {
|
||||
return (
|
||||
<div style={styles.imageUpload}>
|
||||
<span style={styles.message}>
|
||||
<Loader active />
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
style={styles.imageUpload}
|
||||
onDragEnter={this.handleDragEnter}
|
||||
onDragOver={this.handleDragOver}
|
||||
onDrop={this.handleChange}
|
||||
>
|
||||
<span style={styles.imageUpload} onClick={this.handleClick}>
|
||||
<span style={styles.message} onClick={this.handleClick}>
|
||||
{imageName ? imageName : 'Tip: Click here to upload an image from your file browser, or drag an image directly into this box from your desktop'}
|
||||
</span>
|
||||
<input
|
||||
@ -84,21 +104,23 @@ const styles = {
|
||||
input: {
|
||||
display: 'none',
|
||||
},
|
||||
message: {
|
||||
padding: '20px',
|
||||
display: 'block',
|
||||
fontSize: '12px',
|
||||
},
|
||||
imageUpload: {
|
||||
backgroundColor: '#fff',
|
||||
textAlign: 'center',
|
||||
color: '#999',
|
||||
padding: '20px',
|
||||
display: 'block',
|
||||
border: '1px dashed #eee',
|
||||
cursor: 'pointer',
|
||||
fontSize: '12px',
|
||||
},
|
||||
};
|
||||
|
||||
ImageControl.propTypes = {
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onRemoveMedia: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import previewStyle, { imagePreviewStyle } from './defaultPreviewStyle';
|
||||
|
||||
export default function ImagePreview({ value, getMedia }) {
|
||||
export default function ImagePreview({ value, getAsset }) {
|
||||
return (<div style={previewStyle}>
|
||||
{ value ?
|
||||
<img
|
||||
src={getMedia(value)}
|
||||
src={getAsset(value)}
|
||||
style={imagePreviewStyle}
|
||||
role="presentation"
|
||||
/>
|
||||
@ -14,6 +14,6 @@ export default function ImagePreview({ value, getMedia }) {
|
||||
}
|
||||
|
||||
ImagePreview.propTypes = {
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
};
|
||||
|
@ -29,9 +29,9 @@ export default class ListControl extends Component {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
field: PropTypes.node,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
onRemoveMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -125,7 +125,7 @@ export default class ListControl extends Component {
|
||||
};
|
||||
|
||||
renderItem(item, index) {
|
||||
const { value, field, getMedia, onAddMedia, onRemoveMedia } = this.props;
|
||||
const { value, field, getAsset, onAddAsset, onRemoveAsset } = this.props;
|
||||
const { itemStates } = this.state;
|
||||
const collapsed = itemStates.getIn([index, 'collapsed']);
|
||||
const classNames = [styles.item, collapsed ? styles.collapsed : styles.expanded];
|
||||
@ -145,9 +145,9 @@ export default class ListControl extends Component {
|
||||
value={item}
|
||||
field={field}
|
||||
onChange={this.handleChangeFor(index)}
|
||||
getMedia={getMedia}
|
||||
onAddMedia={onAddMedia}
|
||||
onRemoveMedia={onRemoveMedia}
|
||||
getAsset={getAsset}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
/>
|
||||
</div>
|
||||
<button className={styles.toggleButton} onClick={this.handleToggle(index)}>
|
||||
|
@ -4,13 +4,13 @@ import previewStyle from './defaultPreviewStyle';
|
||||
|
||||
export default class ListPreview extends Component {
|
||||
widgetFor = (field, value) => {
|
||||
const { getMedia } = this.props;
|
||||
const { getAsset } = this.props;
|
||||
const widget = resolveWidget(field.get('widget'));
|
||||
return (<div key={field.get('name')}>{React.createElement(widget.preview, {
|
||||
key: field.get('name'),
|
||||
value: value && value.get(field.get('name')),
|
||||
field,
|
||||
getMedia,
|
||||
getAsset,
|
||||
})}</div>);
|
||||
};
|
||||
|
||||
@ -32,5 +32,5 @@ export default class ListPreview extends Component {
|
||||
ListPreview.propTypes = {
|
||||
value: PropTypes.node,
|
||||
field: PropTypes.node,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -12,8 +12,8 @@ class MarkdownControl extends React.Component {
|
||||
static propTypes = {
|
||||
editor: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
switchVisualMode: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
};
|
||||
@ -33,17 +33,17 @@ class MarkdownControl extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onChange, onAddMedia, onRemoveMedia, getMedia, value } = this.props;
|
||||
const { onChange, onAddAsset, onRemoveAsset, getAsset, value } = this.props;
|
||||
const { mode } = this.state;
|
||||
if (mode === 'visual') {
|
||||
return (
|
||||
<div className="cms-editor-visual">
|
||||
<VisualEditor
|
||||
onChange={onChange}
|
||||
onAddMedia={onAddMedia}
|
||||
onRemoveMedia={onRemoveMedia}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
onMode={this.handleMode}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
@ -54,10 +54,10 @@ class MarkdownControl extends React.Component {
|
||||
<div className="cms-editor-raw">
|
||||
<RawEditor
|
||||
onChange={onChange}
|
||||
onAddMedia={onAddMedia}
|
||||
onRemoveMedia={onRemoveMedia}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
onMode={this.handleMode}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
|
@ -10,9 +10,9 @@ export default class BlockMenu extends Component {
|
||||
selectionPosition: PropTypes.object,
|
||||
plugins: PropTypes.object.isRequired,
|
||||
onBlock: PropTypes.func.isRequired,
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
onRemoveMedia: PropTypes.func.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
@ -67,7 +67,7 @@ export default class BlockMenu extends Component {
|
||||
};
|
||||
|
||||
controlFor(field) {
|
||||
const { onAddMedia, onRemoveMedia, getMedia } = this.props;
|
||||
const { onAddAsset, onRemoveAsset, getAsset } = this.props;
|
||||
const { pluginData } = this.state;
|
||||
const widget = resolveWidget(field.get('widget') || 'string');
|
||||
const value = pluginData.get(field.get('name'));
|
||||
@ -84,9 +84,9 @@ export default class BlockMenu extends Component {
|
||||
pluginData: pluginData.set(field.get('name'), val),
|
||||
});
|
||||
},
|
||||
onAddMedia,
|
||||
onRemoveMedia,
|
||||
getMedia,
|
||||
onAddAsset,
|
||||
onRemoveAsset,
|
||||
getAsset,
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@ import markdownSyntax from 'markup-it/syntaxes/markdown';
|
||||
import htmlSyntax from 'markup-it/syntaxes/html';
|
||||
import CaretPosition from 'textarea-caret-position';
|
||||
import registry from '../../../../lib/registry';
|
||||
import MediaProxy from '../../../../valueObjects/MediaProxy';
|
||||
import { createAssetProxy } from '../../../../valueObjects/AssetProxy';
|
||||
import Toolbar from '../Toolbar';
|
||||
import BlockMenu from '../BlockMenu';
|
||||
import styles from './index.css';
|
||||
@ -271,12 +271,16 @@ export default class RawEditor extends React.Component {
|
||||
|
||||
if (e.dataTransfer.files && e.dataTransfer.files.length) {
|
||||
data = Array.from(e.dataTransfer.files).map((file) => {
|
||||
const mediaProxy = new MediaProxy(file.name, file);
|
||||
this.props.onAddMedia(mediaProxy);
|
||||
const link = `[${ file.name }](${ mediaProxy.public_path })`;
|
||||
const link = `[Uploading ${ file.name }...]()`;
|
||||
if (file.type.split('/')[0] === 'image') {
|
||||
return `!${ link }`;
|
||||
}
|
||||
|
||||
createAssetProxy(file.name, file)
|
||||
.then((assetProxy) => {
|
||||
this.props.onAddAsset(assetProxy);
|
||||
// TODO: Change the link text
|
||||
});
|
||||
return link;
|
||||
}).join('\n\n');
|
||||
} else {
|
||||
@ -304,7 +308,7 @@ export default class RawEditor extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onAddMedia, onRemoveMedia, getMedia } = this.props;
|
||||
const { onAddAsset, onRemoveAsset, getAsset } = this.props;
|
||||
const { showToolbar, showBlockMenu, plugins, selectionPosition, dragging } = this.state;
|
||||
const classNames = [styles.root];
|
||||
if (dragging) {
|
||||
@ -333,9 +337,9 @@ export default class RawEditor extends React.Component {
|
||||
selectionPosition={selectionPosition}
|
||||
plugins={plugins}
|
||||
onBlock={this.handleBlock}
|
||||
onAddMedia={onAddMedia}
|
||||
onRemoveMedia={onRemoveMedia}
|
||||
getMedia={getMedia}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
<textarea
|
||||
ref={this.handleRef}
|
||||
@ -344,15 +348,15 @@ export default class RawEditor extends React.Component {
|
||||
onChange={this.handleChange}
|
||||
onSelect={this.handleSelection}
|
||||
/>
|
||||
<div className={styles.shim}/>
|
||||
<div className={styles.shim} />
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
|
||||
RawEditor.propTypes = {
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
onRemoveMedia: PropTypes.func.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onMode: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
|
@ -11,7 +11,7 @@ import { keymap } from 'prosemirror-keymap';
|
||||
import { schema, defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||
import { baseKeymap, setBlockType, toggleMark } from 'prosemirror-commands';
|
||||
import registry from '../../../../lib/registry';
|
||||
import MediaProxy from '../../../../valueObjects/MediaProxy';
|
||||
import { createAssetProxy } from '../../../../valueObjects/AssetProxy';
|
||||
import { buildKeymap } from './keymap';
|
||||
import createMarkdownParser from './parser';
|
||||
import Toolbar from '../Toolbar';
|
||||
@ -89,7 +89,7 @@ function createSerializer(schema, plugins) {
|
||||
plugins.forEach((plugin) => {
|
||||
serializer.nodes[`plugin_${ plugin.get('id') }`] = (state, node) => {
|
||||
const toBlock = plugin.get('toBlock');
|
||||
state.write(toBlock.call(plugin, node.attrs) + '\n\n');
|
||||
state.write(`${ toBlock.call(plugin, node.attrs) }\n\n`);
|
||||
};
|
||||
});
|
||||
return serializer;
|
||||
@ -239,17 +239,19 @@ export default class Editor extends Component {
|
||||
|
||||
if (e.dataTransfer.files && e.dataTransfer.files.length) {
|
||||
Array.from(e.dataTransfer.files).forEach((file) => {
|
||||
const mediaProxy = new MediaProxy(file.name, file);
|
||||
this.props.onAddMedia(mediaProxy);
|
||||
if (file.type.split('/')[0] === 'image') {
|
||||
nodes.push(
|
||||
schema.nodes.image.create({ src: mediaProxy.public_path, alt: file.name })
|
||||
);
|
||||
} else {
|
||||
nodes.push(
|
||||
schema.marks.link.create({ href: mediaProxy.public_path, title: file.name })
|
||||
);
|
||||
}
|
||||
createAssetProxy(file.name, file)
|
||||
.then((assetProxy) => {
|
||||
this.props.onAddAsset(assetProxy);
|
||||
if (file.type.split('/')[0] === 'image') {
|
||||
nodes.push(
|
||||
schema.nodes.image.create({ src: assetProxy.public_path, alt: file.name })
|
||||
);
|
||||
} else {
|
||||
nodes.push(
|
||||
schema.marks.link.create({ href: assetProxy.public_path, title: file.name })
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
nodes.push(schema.nodes.paragraph.create({}, e.dataTransfer.getData('text/plain')));
|
||||
@ -265,7 +267,7 @@ export default class Editor extends Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { onAddMedia, onRemoveMedia, getMedia } = this.props;
|
||||
const { onAddAsset, onRemoveAsset, getAsset } = this.props;
|
||||
const { plugins, showToolbar, showBlockMenu, selectionPosition, dragging } = this.state;
|
||||
const classNames = [styles.editor];
|
||||
if (dragging) {
|
||||
@ -294,9 +296,9 @@ export default class Editor extends Component {
|
||||
selectionPosition={selectionPosition}
|
||||
plugins={plugins}
|
||||
onBlock={this.handleBlock}
|
||||
onAddMedia={onAddMedia}
|
||||
onRemoveMedia={onRemoveMedia}
|
||||
getMedia={getMedia}
|
||||
onAddAsset={onAddAsset}
|
||||
onRemoveAsset={onRemoveAsset}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
<div ref={this.handleRef} />
|
||||
<div className={styles.shim} />
|
||||
@ -305,9 +307,9 @@ export default class Editor extends Component {
|
||||
}
|
||||
|
||||
Editor.propTypes = {
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
onRemoveMedia: PropTypes.func.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
onRemoveAsset: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onMode: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
|
@ -3,7 +3,7 @@ import { getSyntaxes } from './richText';
|
||||
import MarkupItReactRenderer from '../MarkupItReactRenderer/index';
|
||||
import previewStyle from './defaultPreviewStyle';
|
||||
|
||||
const MarkdownPreview = ({ value, getMedia }) => {
|
||||
const MarkdownPreview = ({ value, getAsset }) => {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
@ -11,7 +11,7 @@ const MarkdownPreview = ({ value, getMedia }) => {
|
||||
const schema = {
|
||||
'mediaproxy': ({ token }) => ( // eslint-disable-line
|
||||
<img
|
||||
src={getMedia(token.getIn(['data', 'src']))}
|
||||
src={getAsset(token.getIn(['data', 'src']))}
|
||||
alt={token.getIn(['data', 'alt'])}
|
||||
/>
|
||||
),
|
||||
@ -24,14 +24,14 @@ const MarkdownPreview = ({ value, getMedia }) => {
|
||||
value={value}
|
||||
syntax={markdown}
|
||||
schema={schema}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
MarkdownPreview.propTypes = {
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
|
@ -7,14 +7,14 @@ import styles from './ObjectControl.css';
|
||||
export default class ObjectControl extends Component {
|
||||
static propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onAddMedia: PropTypes.func.isRequired,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
onAddAsset: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
value: PropTypes.node,
|
||||
field: PropTypes.node,
|
||||
};
|
||||
|
||||
controlFor(field) {
|
||||
const { onAddMedia, onRemoveMedia, getMedia, value, onChange } = this.props;
|
||||
const { onAddAsset, onRemoveAsset, getAsset, value, onChange } = this.props;
|
||||
const widget = resolveWidget(field.get('widget') || 'string');
|
||||
const fieldValue = value && Map.isMap(value) ? value.get(field.get('name')) : value;
|
||||
|
||||
@ -28,9 +28,9 @@ export default class ObjectControl extends Component {
|
||||
onChange: (val, metadata) => {
|
||||
onChange((value || Map()).set(field.get('name'), val), metadata);
|
||||
},
|
||||
onAddMedia,
|
||||
onRemoveMedia,
|
||||
getMedia,
|
||||
onAddAsset,
|
||||
onRemoveAsset,
|
||||
getAsset,
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@ import previewStyle from './defaultPreviewStyle';
|
||||
|
||||
export default class ObjectPreview extends Component {
|
||||
widgetFor = (field) => {
|
||||
const { value, getMedia } = this.props;
|
||||
const { value, getAsset } = this.props;
|
||||
const widget = resolveWidget(field.get('widget'));
|
||||
return (
|
||||
<div key={field.get('name')}>
|
||||
@ -12,7 +12,7 @@ export default class ObjectPreview extends Component {
|
||||
key: field.get('name'),
|
||||
value: value && value.get(field.get('name')),
|
||||
field,
|
||||
getMedia,
|
||||
getAsset,
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
@ -29,5 +29,5 @@ export default class ObjectPreview extends Component {
|
||||
ObjectPreview.propTypes = {
|
||||
value: PropTypes.node,
|
||||
field: PropTypes.node,
|
||||
getMedia: PropTypes.func.isRequired,
|
||||
getAsset: PropTypes.func.isRequired,
|
||||
};
|
||||
|
@ -59,8 +59,8 @@ function processEditorPlugins(plugins) {
|
||||
processedPlugins = plugins;
|
||||
}
|
||||
|
||||
function processMediaProxyPlugins(getMedia) {
|
||||
const mediaProxyRule = MarkupIt.Rule('mediaproxy').regExp(reInline.link, (state, match) => {
|
||||
function processAssetProxyPlugins(getAsset) {
|
||||
const assetProxyRule = MarkupIt.Rule('assetproxy').regExp(reInline.link, (state, match) => {
|
||||
if (match[0].charAt(0) !== '!') {
|
||||
// Return if this is not an image
|
||||
return;
|
||||
@ -76,7 +76,7 @@ function processMediaProxyPlugins(getMedia) {
|
||||
data: imgData,
|
||||
};
|
||||
});
|
||||
const mediaProxyMarkdownRule = mediaProxyRule.toText((state, token) => {
|
||||
const assetProxyMarkdownRule = assetProxyRule.toText((state, token) => {
|
||||
const data = token.getData();
|
||||
const alt = data.get('alt', '');
|
||||
const src = data.get('src', '');
|
||||
@ -88,25 +88,25 @@ function processMediaProxyPlugins(getMedia) {
|
||||
return ``;
|
||||
}
|
||||
});
|
||||
const mediaProxyHTMLRule = mediaProxyRule.toText((state, token) => {
|
||||
const assetProxyHTMLRule = assetProxyRule.toText((state, token) => {
|
||||
const data = token.getData();
|
||||
const alt = data.get('alt', '');
|
||||
const src = data.get('src', '');
|
||||
return `<img src=${ getMedia(src) } alt=${ alt } />`;
|
||||
return `<img src=${ getAsset(src) } alt=${ alt } />`;
|
||||
});
|
||||
|
||||
nodes.mediaproxy = (props) => {
|
||||
nodes.assetproxy = (props) => {
|
||||
/* eslint react/prop-types: 0 */
|
||||
const { node, state } = props;
|
||||
const isFocused = state.selection.hasEdgeIn(node);
|
||||
const className = isFocused ? 'active' : null;
|
||||
const src = node.data.get('src');
|
||||
return (
|
||||
<img {...props.attributes} src={getMedia(src)} className={className} />
|
||||
<img {...props.attributes} src={getAsset(src)} className={className} />
|
||||
);
|
||||
};
|
||||
augmentedMarkdownSyntax = augmentedMarkdownSyntax.addInlineRules(mediaProxyMarkdownRule);
|
||||
augmentedHTMLSyntax = augmentedHTMLSyntax.addInlineRules(mediaProxyHTMLRule);
|
||||
augmentedMarkdownSyntax = augmentedMarkdownSyntax.addInlineRules(assetProxyMarkdownRule);
|
||||
augmentedHTMLSyntax = augmentedHTMLSyntax.addInlineRules(assetProxyHTMLRule);
|
||||
}
|
||||
|
||||
function getPlugins() {
|
||||
@ -121,9 +121,9 @@ function getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
function getSyntaxes(getMedia) {
|
||||
if (getMedia) {
|
||||
processMediaProxyPlugins(getMedia);
|
||||
function getSyntaxes(getAsset) {
|
||||
if (getAsset) {
|
||||
processAssetProxyPlugins(getAsset);
|
||||
}
|
||||
return { markdown: augmentedMarkdownSyntax, html: augmentedHTMLSyntax };
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ const htmlContent = `
|
||||
</ol>
|
||||
`;
|
||||
|
||||
function getMedia(path) {
|
||||
function getAsset(path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
@ -28,13 +28,13 @@ storiesOf('MarkupItReactRenderer', module)
|
||||
<MarkupItReactRenderer
|
||||
value={mdContent}
|
||||
syntax={markdownSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
|
||||
)).add('HTML', () => (
|
||||
<MarkupItReactRenderer
|
||||
value={htmlContent}
|
||||
syntax={htmlSyntax}
|
||||
getMedia={getMedia}
|
||||
getAsset={getAsset}
|
||||
/>
|
||||
));
|
||||
|
Reference in New Issue
Block a user