migrate file and image widgets

This commit is contained in:
Shawn Erquhart 2018-07-24 13:57:57 -04:00
parent e80a407cee
commit 2efd09ba94
14 changed files with 303 additions and 21 deletions

View File

@ -4,13 +4,13 @@ import { GitLabBackend } from 'netlify-cms-backend-gitlab';
import { GitGatewayBackend } from 'netlify-cms-backend-git-gateway'; import { GitGatewayBackend } from 'netlify-cms-backend-git-gateway';
import { TestBackend } from 'netlify-cms-backend-test'; import { TestBackend } from 'netlify-cms-backend-test';
import { BooleanControl } from 'netlify-cms-widget-boolean'; import { BooleanControl } from 'netlify-cms-widget-boolean';
import { StringControl, StringPreview } from 'netlify-cms-widget-string';
import { DateControl, DatePreview } from 'netlify-cms-widget-date'; import { DateControl, DatePreview } from 'netlify-cms-widget-date';
import { DateTimeControl, DateTimePreview } from 'netlify-cms-widget-datetime'; import { DateTimeControl, DateTimePreview } from 'netlify-cms-widget-datetime';
import { FileControl, FilePreview } from 'netlify-cms-widget-file';
import { ImageControl, ImagePreview } from 'netlify-cms-widget-image';
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 { ImageControl, ImagePreview } from 'netlify-cms-widget-image';
// import { FileControl, FilePreview } from 'netlify-cms-widget-file';
// 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 { MarkdownControl, MarkdownPreview } from 'netlify-cms-widget-markdown';
// import { ListControl, ListPreview } from 'netlify-cms-widget-list'; // import { ListControl, ListPreview } from 'netlify-cms-widget-list';
@ -25,13 +25,13 @@ registerBackend('test-repo', TestBackend);
registerWidget('boolean', BooleanControl); registerWidget('boolean', BooleanControl);
registerWidget('date', DateControl, DatePreview); registerWidget('date', DateControl, DatePreview);
registerWidget('datetime', DateTimeControl, DateTimePreview); registerWidget('datetime', DateTimeControl, DateTimePreview);
registerWidget('file', FileControl, FilePreview);
registerWidget('image', ImageControl, ImagePreview);
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('list', ListControl, ListPreview); // registerWidget('list', ListControl, ListPreview);
// registerWidget('markdown', MarkdownControl, MarkdownPreview); // registerWidget('markdown', MarkdownControl, MarkdownPreview);
// registerWidget('image', ImageControl, ImagePreview);
// registerWidget('file', FileControl, FilePreview);
// registerWidget('select', SelectControl, SelectPreview); // registerWidget('select', SelectControl, SelectPreview);
// registerWidget('object', ObjectControl, ObjectPreview); // registerWidget('object', ObjectControl, ObjectPreview);
// registerWidget('relation', RelationControl, RelationPreview); // registerWidget('relation', RelationControl, RelationPreview);

View File

@ -5,10 +5,6 @@ import NumberControl from './Number/NumberControl';
import NumberPreview from './Number/NumberPreview'; import NumberPreview from './Number/NumberPreview';
import TextControl from './Text/TextControl'; import TextControl from './Text/TextControl';
import TextPreview from './Text/TextPreview'; import TextPreview from './Text/TextPreview';
import ImageControl from './Image/ImageControl';
import ImagePreview from './Image/ImagePreview';
import FileControl from './File/FileControl';
import FilePreview from './File/FilePreview';
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 MarkdownControl from './Markdown/MarkdownControl';
@ -24,8 +20,6 @@ registerWidget('text', TextControl, TextPreview);
registerWidget('number', NumberControl, NumberPreview); registerWidget('number', NumberControl, NumberPreview);
registerWidget('list', ListControl, ListPreview); registerWidget('list', ListControl, ListPreview);
registerWidget('markdown', MarkdownControl, MarkdownPreview); registerWidget('markdown', MarkdownControl, MarkdownPreview);
registerWidget('image', ImageControl, ImagePreview);
registerWidget('file', FileControl, FilePreview);
registerWidget('select', SelectControl, SelectPreview); registerWidget('select', SelectControl, SelectPreview);
registerWidget('object', ObjectControl, ObjectPreview); registerWidget('object', ObjectControl, ObjectPreview);
registerWidget('relation', RelationControl, RelationPreview); registerWidget('relation', RelationControl, RelationPreview);

View File

@ -1,10 +1,3 @@
export function truncateMiddle(string = "", size) {
if (string.length <= size) {
return string;
}
return `${ string.substring(0, size / 2) }\u2026${ string.substring(string.length - size / 2 + 1, string.length) }`;
}
export function stringToRGB(str) { export function stringToRGB(str) {
if (!str) return "000000"; if (!str) return "000000";
let hash = 0; let hash = 0;

View File

@ -214,10 +214,15 @@ const components = {
border-top: 6px solid currentColor; border-top: 6px solid currentColor;
border-radius: 2px; border-radius: 2px;
`, `,
textBadge: css`
${textBadge};
color: ${colors.infoText};
background-color: ${colors.infoBackground};
`,
textBadgeSuccess: css` textBadgeSuccess: css`
${textBadge}; ${textBadge};
color: ${colorsRaw.green}; color: ${colors.successText};
background-color: ${colorsRaw.greenLight}; background-color: ${colors.successBackground};
`, `,
textBadgeDanger: css` textBadgeDanger: css`
${textBadge}; ${textBadge};

View File

@ -1,2 +1,2 @@
export DateTimeControl from './DateTimeControl'; export DateTimeControl from './DateTimeControl';
export DateTimePreview from 'netlify-cms-widget-date'; export { DatePreview as DateTimePreview } from 'netlify-cms-widget-date';

View File

@ -0,0 +1,34 @@
{
"name": "netlify-cms-widget-file",
"description": "Widget for uploading files in Netlify CMS.",
"version": "2.0.0-alpha.0",
"main": "dist/netlify-cms-widget-file.js",
"license": "MIT",
"keywords": [
"netlify",
"netlify-cms",
"widget",
"file",
"upload",
"file-upload"
],
"sideEffects": false,
"scripts": {
"watch": "webpack -w",
"build": "webpack"
},
"dependencies": {
"uuid": "^3.3.2"
},
"devDependencies": {
"webpack": "^4.16.1",
"webpack-cli": "^3.1.0"
},
"peerDependencies": {
"netlify-cms-ui-default": "^2.0.0-alpha.0",
"prop-types": "^15.5.10",
"react": "^16.4.1",
"react-emotion": "^9.2.6",
"react-immutable-proptypes": "^2.1.0"
}
}

View File

@ -0,0 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import { WidgetPreviewContainer } from 'netlify-cms-ui-default';
const FilePreview = ({ value, getAsset }) => (
<WidgetPreviewContainer>
{ value ? <a href={getAsset(value)}>{ value }</a> : null}
</WidgetPreviewContainer>
);
FilePreview.propTypes = {
getAsset: PropTypes.func.isRequired,
value: PropTypes.node,
};
export default FilePreview;

View File

@ -0,0 +1,5 @@
import withFileControl from './withFileControl';
export { withFileControl };
export const FileControl = withFileControl();
export FilePreview from './FilePreview';

View File

@ -0,0 +1,167 @@
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import styled from 'react-emotion';
import uuid from 'uuid/v4';
import { lengths, components, buttons } from 'netlify-cms-ui-default';
const MAX_DISPLAY_LENGTH = 50;
const FileContent = styled.div`
display: flex;
`
const ImageWrapper = styled.div`
width: 155px;
height: 100px;
margin-right: 20px;
`
const Image = styled.img`
width: 100%;
height: 100%;
object-fit: cover;
border-radius: ${lengths.borderRadius};
`
const FileInfo = styled.div`
button:not(:first-child) {
margin-top: 12px;
}
`
const FileName = styled.span`
display: block;
font-size: 16px;
margin-bottom: 20px;
`
const FileWidgetButton = styled.button`
${buttons.button};
${components.textBadge};
display: block;
`
const FileWidgetButtonRemove = styled.button`
${buttons.button};
${components.textBadgeDanger};
display: block;
`
export default function withFileControl({ forImage } = {}) {
return class extends React.Component {
static propTypes = {
field: PropTypes.object.isRequired,
getAsset: PropTypes.func.isRequired,
mediaPaths: ImmutablePropTypes.map.isRequired,
onAddAsset: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onRemoveInsertedMedia: PropTypes.func.isRequired,
onOpenMediaLibrary: PropTypes.func.isRequired,
classNameWrapper: PropTypes.string.isRequired,
value: PropTypes.node,
};
static defaultProps = {
value: '',
};
constructor(props) {
super(props);
this.controlID = uuid();
}
shouldComponentUpdate(nextProps) {
/**
* Always update if the value changes.
*/
if (this.props.value !== nextProps.value) {
return true;
}
/**
* If there is a media path for this control in the state object, and that
* path is different than the value in `nextProps`, update.
*/
const mediaPath = nextProps.mediaPaths.get(this.controlID);
if (mediaPath && (nextProps.value !== mediaPath)) {
return true;
}
return false;
}
componentWillReceiveProps(nextProps) {
const { mediaPaths, value, onRemoveInsertedMedia, onChange } = nextProps;
const mediaPath = mediaPaths.get(this.controlID);
if (mediaPath && mediaPath !== value) {
onChange(mediaPath);
} else if (mediaPath && mediaPath === value) {
onRemoveInsertedMedia(this.controlID);
}
}
handleChange = e => {
const { field, onOpenMediaLibrary } = this.props;
e.preventDefault();
return onOpenMediaLibrary({
controlID: this.controlID,
forImage,
privateUpload: field.get('private'),
});
};
handleRemove = e => {
e.preventDefault();
return this.props.onChange('');
};
renderFileName = () => {
const { value, classNameWrapper } = this.props;
const size = MAX_DISPLAY_LENGTH;
if (!value || value.length <= size) {
return value;
}
return `${ value.substring(0, size / 2) }\u2026${ value.substring(value.length - size / 2 + 1, value.length) }`;
};
renderSelection = (subject) => {
const fileName = this.renderFileName();
const { getAsset, value } = this.props;
return (
<FileContent>
{ forImage ? <ImageWrapper><Image src={getAsset(value)}/></ImageWrapper> : null }
<FileInfo>
<FileName>{fileName}</FileName>
<FileWidgetButton onClick={this.handleChange}>
Choose different {subject}
</FileWidgetButton>
<FileWidgetButtonRemove onClick={this.handleRemove}>
Remove {subject}
</FileWidgetButtonRemove>
</FileInfo>
</FileContent>
);
};
renderNoSelection = (subject, article) => (
<FileWidgetButton onClick={this.handleChange}>
Choose {article} {subject}
</FileWidgetButton>
);
render() {
const { value, classNameWrapper } = this.props;
const subject = forImage ? 'image' : 'file';
const article = forImage ? 'an' : 'a';
return (
<div className={classNameWrapper}>
<span>
{ value ? this.renderSelection(subject) : this.renderNoSelection(subject, article) }
</span>
</div>
);
}
}
};

View File

@ -0,0 +1,3 @@
const { getConfig } = require('../../scripts/webpack.js');
module.exports = getConfig();

View File

@ -0,0 +1,36 @@
{
"name": "netlify-cms-widget-image",
"description": "Widget for uploading images in Netlify CMS.",
"version": "2.0.0-alpha.0",
"main": "dist/netlify-cms-widget-image.js",
"license": "MIT",
"keywords": [
"netlify",
"netlify-cms",
"widget",
"image",
"upload",
"image-upload"
],
"sideEffects": false,
"scripts": {
"watch": "webpack -w",
"build": "webpack"
},
"dependencies": {
"netlify-cms-widget-file": "^2.0.0-alpha.0"
},
"devDependencies": {
"webpack": "^4.16.1",
"webpack-cli": "^3.1.0"
},
"peerDependencies": {
"netlify-cms-ui-default": "^2.0.0-alpha.0",
"prop-types": "^15.5.10",
"react": "^16.4.1",
"react-emotion": "^9.2.6"
},
"localExternals": [
"netlify-cms-widget-file"
]
}

View File

@ -0,0 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'react-emotion';
import { WidgetPreviewContainer } from 'netlify-cms-ui-default';
const Image = styled.img`
max-width: 100%;
height: auto;
`
const ImagePreview = ({ value, getAsset }) => (
<WidgetPreviewContainer>
{ value ? <Image src={getAsset(value)} role="presentation"/> : null}
</WidgetPreviewContainer>
);
ImagePreview.propTypes = {
getAsset: PropTypes.func.isRequired,
value: PropTypes.node,
};
export default ImagePreview;

View File

@ -0,0 +1,4 @@
import { withFileControl } from 'netlify-cms-widget-file';
export const ImageControl = withFileControl({ forImage: true });
export ImagePreview from './ImagePreview';

View File

@ -0,0 +1,3 @@
const { getConfig } = require('../../scripts/webpack.js');
module.exports = getConfig();