web: components: first version of the avatar editor

This commit is contained in:
NikolayRechkin 2019-09-05 09:23:05 +03:00
parent 676532c303
commit d54ba9f9d3
7 changed files with 339 additions and 1 deletions

View File

@ -33,6 +33,7 @@
"prop-types": "^15.7.2",
"rc-tree": "^2.1.2",
"react-autosize-textarea": "^7.0.0",
"react-avatar-edit": "^0.8.3",
"react-custom-scrollbars": "^4.2.1",
"react-datepicker": "^2.8.0",
"react-lifecycles-compat": "^3.0.4",

View File

@ -0,0 +1,218 @@
import React, { memo } from 'react'
import styled, { css } from 'styled-components'
import PropTypes from 'prop-types'
import ModalDialog from '../modal-dialog'
import Button from '../button'
import { Text } from '../text'
import Avatar from 'react-avatar-edit'
import { default as ASCAvatar } from '../avatar/index'
const StyledASCAvatar = styled(ASCAvatar)`
display: inline-block;
vertical-align: bottom;
`;
const StyledAvatarContainer = styled.div`
text-align: center;
div:first-child {
margin: 0 auto;
}
`;
class AvatarEditorBody extends React.Component {
constructor(props) {
super(props);
this.state = {
croppedImage: null,
src: this.props.image,
hasMaxSizeError: false
}
this.onCrop = this.onCrop.bind(this)
this.onClose = this.onClose.bind(this)
this.onBeforeFileLoad = this.onBeforeFileLoad.bind(this)
this.onFileLoad = this.onFileLoad.bind(this)
}
onClose() {
this.props.onCloseEditor();
this.setState({ croppedImage: null })
}
onCrop(croppedImage) {
this.props.onCropImage(croppedImage);
this.setState({ croppedImage })
}
onBeforeFileLoad(elem) {
if (elem.target.files[0].size > this.props.maxSize * 1000000) {
this.setState({
hasMaxSizeError: true
});
elem.target.value = "";
}else if(this.state.hasMaxSizeError){
this.setState({
hasMaxSizeError: false
});
};
}
onFileLoad(file){
let reader = new FileReader();
let _this = this;
reader.onloadend = () => {
_this.props.onFileLoad(reader.result);
};
reader.readAsDataURL(file)
}
render() {
return (
<StyledAvatarContainer>
<Avatar
width={400}
height={295}
imageWidth={400}
cropRadius={50}
onCrop={this.onCrop}
onClose={this.onClose}
onBeforeFileLoad={this.onBeforeFileLoad}
onFileLoad={this.onFileLoad}
label={this.props.label}
src={this.state.src}
/>
{this.state.croppedImage && (
<div>
<StyledASCAvatar
size='max'
role='user'
source={this.state.croppedImage}
editing={false}
/>
<StyledASCAvatar
size='big'
role='user'
source={this.state.croppedImage}
editing={false}
/>
</div>
)
}
{
this.state.hasMaxSizeError &&
<Text.Body as='span' color="#ED7309" isBold={true}>
{this.props.maxSizeErrorLabel}
</Text.Body>
}
</StyledAvatarContainer>
);
}
}
class AvatarEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
defaultImage: null,
croppedImage: null,
visible: props.value
};
this.onClose = this.onClose.bind(this);
this.onCropImage = this.onCropImage.bind(this);
this.onCloseEditor = this.onCloseEditor.bind(this);
this.onFileLoad = this.onFileLoad.bind(this);
this.onSaveButtonClick = this.onSaveButtonClick.bind(this);
}
onFileLoad(file){
this.setState({ defaultImage: file });
}
onSaveButtonClick() {
this.props.onSave({
defaultImage: this.state.defaultImage,
croppedImage: this.state.croppedImage
});
this.setState({ visible: false });
}
onCloseEditor() {
this.setState({
croppedImage: null
});
}
onCropImage(result) {
this.setState({
croppedImage: result
});
}
onClose() {
this.setState({ visible: false });
this.props.onClose();
}
componentDidUpdate(prevProps) {
if (this.props.visible !== prevProps.visible) {
this.setState({ visible: this.props.visible });
}
}
render() {
return (
<ModalDialog
visible={this.state.visible}
headerContent={this.props.headerLabel}
bodyContent={
<AvatarEditorBody
maxSize={this.props.maxSize}
image={this.props.image}
onCropImage={this.onCropImage}
onCloseEditor={this.onCloseEditor}
label={this.props.chooseFileLabel}
maxSizeErrorLabel={this.props.maxSizeErrorLabel}
onFileLoad={this.onFileLoad}
/>
}
footerContent={[
<Button
key="SaveBtn"
label={this.props.saveButtonLabel}
primary={true}
onClick={this.onSaveButtonClick}
/>,
<Button
key="CancelBtn"
label={this.props.cancelButtonLabel}
onClick={this.onClose}
style={{ marginLeft: "8px" }}
/>
]}
onClose={this.props.onClose}
/>
);
}
}
AvatarEditor.propTypes = {
visible: PropTypes.bool,
headerLabel: PropTypes.string,
chooseFileLabel: PropTypes.string,
saveButtonLabel: PropTypes.string,
maxSizeErrorLabel: PropTypes.string,
image: PropTypes.string,
cancelButtonLabel: PropTypes.string,
maxSize: PropTypes.number,
onSave: PropTypes.func,
onClose: PropTypes.func
};
AvatarEditor.defaultProps = {
visible: false,
maxSize: 1, //1MB
chooseFileLabel: 'Choose a file',
headerLabel: 'Edit Photo',
saveButtonLabel: 'Save',
cancelButtonLabel: 'Cancel',
maxSizeErrorLabel: 'File is too big'
};
export default AvatarEditor;

View File

@ -56,7 +56,7 @@ const RoleWrapper = styled.div`
`;
const ImageStyled = styled.img`
max-width: 100%;
width: 100%;
height: auto;
border-radius: 50%;

View File

@ -12,6 +12,7 @@ export { default as GroupButtonsMenu } from './components/group-buttons-menu'
export { default as TreeMenu } from './components/tree-menu'
export { default as TreeNode } from './components/tree-menu-node'
export { default as Avatar } from './components/avatar'
export { default as AvatarEditor } from './components/avatar-editor'
export { default as RequestLoader } from './components/request-loader'
export { default as MainButton } from './components/main-button'
export { default as ContextMenuButton } from './components/context-menu-button'

View File

@ -4976,6 +4976,11 @@ kind-of@^6.0.0, kind-of@^6.0.2:
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==
konva@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/konva/-/konva-2.5.1.tgz#cca611a9522e831e54cf57c508a1aed3f0ceac25"
integrity sha512-YdHEWqmbWPieqIZuLx7JFGm9Ui08hSUaSJ2k2Ml8o5giFgJ0WmxAS0DPXIM+Ty2ADRagOHZfXSJ/skwYqqlwgQ==
lazy-cache@^0.2.3:
version "0.2.7"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65"
@ -6777,6 +6782,13 @@ react-autosize-textarea@^7.0.0:
line-height "^0.3.1"
prop-types "^15.5.6"
react-avatar-edit@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/react-avatar-edit/-/react-avatar-edit-0.8.3.tgz#0ebf21391328fc255429bdfbc782f795827109bf"
integrity sha512-QEedh6DjDCSI7AUsUHHtfhxApCWC5hJAoywxUA5PtUdw03iIjEurgVqPOIt1UBHhU/Zk/9amElRF3oepN9JZSg==
dependencies:
konva "2.5.1"
react-custom-scrollbars@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/react-custom-scrollbars/-/react-custom-scrollbars-4.2.1.tgz#830fd9502927e97e8a78c2086813899b2a8b66db"

View File

@ -0,0 +1,34 @@
# Avatar Editor
## Usage
```js
import { AvatarEditor } from 'asc-web-components';
```
#### Description
Required to display user avatar editor on page.
#### Usage
```js
<AvatarEditor
visible={true}
onSave={(data) =>{console.log(data.croppedImage, data.defaultImage)}}
/>
```
#### Properties
| Props | Type | Required | Values | Default | Description |
| ------------------ | -------- | :------: | ----------------------------------------- | ------------------ | ----------------------------------------------------- |
| `visible` | `bool` | - | | `false` | Display avatar editor or not |
| `chooseFileLabel` | `string` | - | | `Choose a file` | |
| `headerLabel` | `string` | - | | `Edit Photo` | |
| `saveButtonLabel` | `string` | - | | `Save` | |
| `cancelButtonLabel` | `string` | - | | `Cancel` | |
| `maxSizeErrorLabel` | `string` | - | | `File is too big` | |
| `maxSize` | `number` | - | | `1` | Max size of image |
| `onSave` | `function` | - | | | |
| `onClose` | `function` | - | | | |

View File

@ -0,0 +1,72 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { withKnobs, boolean, text, select } from '@storybook/addon-knobs/react';
import withReadme from 'storybook-readme/with-readme';
import Readme from './README.md';
import { AvatarEditor, Avatar } from 'asc-web-components';
import Section from '../../.storybook/decorators/section';
class AvatarEditorStory extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false,
userImage: null
}
this.openEditor = this.openEditor.bind(this);
this.onClose = this.onClose.bind(this);
this.onSave = this.onSave.bind(this);
}
onSave(result){
action('onSave')(result);
this.setState({
userImage: result.croppedImage,
isOpen: false
})
}
openEditor(){
this.setState({
isOpen: true
})
}
onClose(){
action('onClose');
this.setState({
isOpen: false
})
}
render(){
return(
<div>
<Avatar
size='max'
role='user'
source={this.state.userImage }
editing={true}
editAction={this.openEditor}
/>
<AvatarEditor
visible={this.state.isOpen}
onClose={this.onClose}
onSave={this.onSave}
/>
</div>
)
}
}
storiesOf('Components|AvatarEditor', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('avatar editor', () => {
return (
<Section>
<AvatarEditorStory />
</Section>
);
});