Merge branch 'master' into refactoring/text-heading
This commit is contained in:
commit
6b6c4569cf
@ -23,7 +23,7 @@ import {
|
||||
updateUserStatus,
|
||||
fetchPeople
|
||||
} from "../../../../../store/people/actions";
|
||||
import { fetchProfile } from "../../../../../store/profile/actions";
|
||||
import { fetchProfile, getUserPhoto } from "../../../../../store/profile/actions";
|
||||
import styled from "styled-components";
|
||||
import { store, api, constants } from "asc-web-common";
|
||||
const { isAdmin, isMe } = store.auth.selectors;
|
||||
@ -82,9 +82,7 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
},
|
||||
avatar: {
|
||||
tmpFile: "",
|
||||
image: profile.avatarDefault
|
||||
? "data:image/png;base64," + profile.avatarDefault
|
||||
: null,
|
||||
image: null,
|
||||
defaultWidth: 0,
|
||||
defaultHeight: 0
|
||||
}
|
||||
@ -94,24 +92,31 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
};
|
||||
|
||||
openAvatarEditor = () => {
|
||||
let avatarDefault = this.state.profile.avatarDefault
|
||||
? "data:image/png;base64," + this.state.profile.avatarDefault
|
||||
: null;
|
||||
let _this = this;
|
||||
if (avatarDefault !== null) {
|
||||
let img = new Image();
|
||||
img.onload = function() {
|
||||
_this.setState({
|
||||
avatar: {
|
||||
defaultWidth: img.width,
|
||||
defaultHeight: img.height
|
||||
}
|
||||
});
|
||||
};
|
||||
img.src = avatarDefault;
|
||||
}
|
||||
this.setState({
|
||||
visibleAvatarEditor: true
|
||||
getUserPhoto(this.state.profile.id).then(userPhotoData => {
|
||||
if(userPhotoData.original){
|
||||
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
|
||||
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
|
||||
this.setState({
|
||||
avatar: {
|
||||
tmpFile: this.state.avatar.tmpFile,
|
||||
defaultWidth: avatarDefaultSizes[1],
|
||||
defaultHeight: avatarDefaultSizes[2],
|
||||
image: userPhotoData.original ? userPhotoData.original.indexOf('default_user_photo') !== -1 ? null : userPhotoData.original : null
|
||||
},
|
||||
visibleAvatarEditor: true
|
||||
});
|
||||
}else{
|
||||
this.setState({
|
||||
avatar: {
|
||||
tmpFile: this.state.avatar.tmpFile,
|
||||
defaultWidth: 0,
|
||||
defaultHeight: 0,
|
||||
image: null
|
||||
},
|
||||
visibleAvatarEditor: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -52,6 +52,7 @@ class PasswordField extends React.Component {
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
spacing='33px'
|
||||
/>
|
||||
<PasswordInput
|
||||
inputName={inputName}
|
||||
|
@ -40,6 +40,7 @@ class RadioField extends React.Component {
|
||||
isDisabled={radioIsDisabled}
|
||||
onClick={radioOnChange}
|
||||
className="radio-group"
|
||||
spacing='33px'
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
|
@ -4,7 +4,7 @@ import { connect } from 'react-redux'
|
||||
import { Avatar, Button, Textarea, toastr, AvatarEditor, Text } from 'asc-web-components'
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
import { toEmployeeWrapper, getUserRole, getUserContactsPattern, getUserContacts, mapGroupsToGroupSelectorOptions, mapGroupSelectorOptionsToGroups, filterGroupSelectorOptions } from "../../../../../store/people/selectors";
|
||||
import { createProfile } from '../../../../../store/profile/actions';
|
||||
import { createProfile, getUserPhoto } from '../../../../../store/profile/actions';
|
||||
import { MainContainer, AvatarContainer, MainFieldsContainer } from './FormFields/Form'
|
||||
import TextField from './FormFields/TextField'
|
||||
import PasswordField from './FormFields/PasswordField'
|
||||
@ -69,19 +69,17 @@ class CreateUserForm extends React.Component {
|
||||
}
|
||||
|
||||
openAvatarEditor(){
|
||||
let avatarDefault = this.state.profile.avatarDefault ? "data:image/png;base64," + this.state.profile.avatarDefault : null;
|
||||
let _this = this;
|
||||
if(avatarDefault !== null){
|
||||
let img = new Image();
|
||||
img.onload = function () {
|
||||
_this.setState({
|
||||
avatar:{
|
||||
defaultWidth: img.width,
|
||||
defaultHeight: img.height
|
||||
}
|
||||
})
|
||||
};
|
||||
img.src = avatarDefault;
|
||||
let avatarDefault = this.state.avatar.image;
|
||||
let avatarDefaultSizes = /_orig_(\d*)-(\d*)./g.exec(this.state.avatar.image);
|
||||
if (avatarDefault !== null && avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
|
||||
this.setState({
|
||||
avatar: {
|
||||
tmpFile: this.state.avatar.tmpFile,
|
||||
image: this.state.avatar.image,
|
||||
defaultWidth: avatarDefaultSizes[1],
|
||||
defaultHeight: avatarDefaultSizes[2]
|
||||
}
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
visibleAvatarEditor: true,
|
||||
@ -149,7 +147,21 @@ class CreateUserForm extends React.Component {
|
||||
});
|
||||
var allOptions = mapGroupsToGroupSelectorOptions(props.groups);
|
||||
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
|
||||
|
||||
getUserPhoto(profile.id).then(userPhotoData => {
|
||||
if(userPhotoData.original){
|
||||
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
|
||||
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
|
||||
this.setState({
|
||||
avatar: {
|
||||
tmpFile: this.state.avatar.tmpFile,
|
||||
defaultWidth: avatarDefaultSizes[1],
|
||||
defaultHeight: avatarDefaultSizes[2],
|
||||
image: userPhotoData.original ? userPhotoData.original.indexOf('default_user_photo') !== -1 ? null : userPhotoData.original : null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
visibleAvatarEditor: false,
|
||||
croppedAvatarImage: "",
|
||||
@ -169,7 +181,7 @@ class CreateUserForm extends React.Component {
|
||||
},
|
||||
avatar: {
|
||||
tmpFile:"",
|
||||
image: profile.avatarDefault ? "data:image/png;base64," + profile.avatarDefault : null,
|
||||
image: null,
|
||||
defaultWidth: 0,
|
||||
defaultHeight: 0,
|
||||
x: 0,
|
||||
|
@ -4,7 +4,7 @@ import { connect } from 'react-redux'
|
||||
import { Avatar, Button, Textarea, Text, toastr, ModalDialog, TextInput, AvatarEditor, Link } from 'asc-web-components'
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
import { toEmployeeWrapper, getUserRole, getUserContactsPattern, getUserContacts, mapGroupsToGroupSelectorOptions, mapGroupSelectorOptionsToGroups, filterGroupSelectorOptions } from "../../../../../store/people/selectors";
|
||||
import { updateProfile } from '../../../../../store/profile/actions';
|
||||
import { updateProfile, getUserPhoto } from '../../../../../store/profile/actions'
|
||||
import { MainContainer, AvatarContainer, MainFieldsContainer } from './FormFields/Form'
|
||||
import TextField from './FormFields/TextField'
|
||||
import TextChangeField from './FormFields/TextChangeField'
|
||||
@ -82,6 +82,22 @@ class UpdateUserForm extends React.Component {
|
||||
var allOptions = mapGroupsToGroupSelectorOptions(props.groups);
|
||||
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
|
||||
|
||||
getUserPhoto(profile.id).then(userPhotoData => {
|
||||
if(userPhotoData.original){
|
||||
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
|
||||
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
|
||||
this.setState({
|
||||
avatar: {
|
||||
tmpFile: this.state.avatar.tmpFile,
|
||||
defaultWidth: avatarDefaultSizes[1],
|
||||
defaultHeight: avatarDefaultSizes[2],
|
||||
image: userPhotoData.original ? userPhotoData.original.indexOf('default_user_photo') !== -1 ? null : userPhotoData.original : null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const newState = {
|
||||
isLoading: false,
|
||||
errors: {
|
||||
@ -105,7 +121,7 @@ class UpdateUserForm extends React.Component {
|
||||
},
|
||||
avatar: {
|
||||
tmpFile:"",
|
||||
image: profile.avatarDefault ? "data:image/png;base64," + profile.avatarDefault : null,
|
||||
image: null,
|
||||
defaultWidth: 0,
|
||||
defaultHeight: 0
|
||||
}
|
||||
@ -323,19 +339,17 @@ class UpdateUserForm extends React.Component {
|
||||
}
|
||||
|
||||
openAvatarEditor(){
|
||||
let avatarDefault = this.state.profile.avatarDefault ? "data:image/png;base64," + this.state.profile.avatarDefault : null;
|
||||
let _this = this;
|
||||
if(avatarDefault !== null){
|
||||
let img = new Image();
|
||||
img.onload = function () {
|
||||
_this.setState({
|
||||
avatar:{
|
||||
defaultWidth: img.width,
|
||||
defaultHeight: img.height
|
||||
}
|
||||
})
|
||||
};
|
||||
img.src = avatarDefault;
|
||||
let avatarDefault = this.state.avatar.image;
|
||||
let avatarDefaultSizes = /_orig_(\d*)-(\d*)./g.exec(this.state.avatar.image);
|
||||
if (avatarDefault !== null && avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
|
||||
this.setState({
|
||||
avatar: {
|
||||
tmpFile: this.state.avatar.tmpFile,
|
||||
image: this.state.avatar.image,
|
||||
defaultWidth: avatarDefaultSizes[1],
|
||||
defaultHeight: avatarDefaultSizes[2]
|
||||
}
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
visibleAvatarEditor: true,
|
||||
|
@ -93,3 +93,6 @@ export function updateProfileCulture(id, culture) {
|
||||
};
|
||||
};
|
||||
|
||||
export function getUserPhoto(id) {
|
||||
return api.people.getUserPhoto(id);
|
||||
};
|
||||
|
@ -747,109 +747,7 @@ namespace ASC.Employee.Core.Controllers
|
||||
return new ThumbnailsDataWrapper(user.ID, UserPhotoManager);
|
||||
}
|
||||
|
||||
public FormFile Base64ToImage(string base64String, string fileName)
|
||||
{
|
||||
byte[] imageBytes = Convert.FromBase64String(base64String);
|
||||
MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);
|
||||
|
||||
ms.Write(imageBytes, 0, imageBytes.Length);
|
||||
return new FormFile(ms, 0, ms.Length, fileName, fileName);
|
||||
}
|
||||
|
||||
[Create("{userid}/photo/cropped")]
|
||||
public People.Models.FileUploadResult UploadCroppedMemberPhoto(string userid, UploadCroppedPhotoModel model)
|
||||
{
|
||||
var result = new People.Models.FileUploadResult();
|
||||
|
||||
try
|
||||
{
|
||||
Guid userId;
|
||||
try
|
||||
{
|
||||
userId = new Guid(userid);
|
||||
}
|
||||
catch
|
||||
{
|
||||
userId = SecurityContext.CurrentAccount.ID;
|
||||
}
|
||||
|
||||
PermissionContext.DemandPermissions(new UserSecurityProvider(userId), Constants.Action_EditUser);
|
||||
|
||||
var userPhoto = Base64ToImage(model.base64CroppedImage, "userPhoto_" + userId.ToString());
|
||||
var defaultUserPhoto = Base64ToImage(model.base64DefaultImage, "defaultPhoto" + userId.ToString());
|
||||
|
||||
if (userPhoto.Length > SetupInfo.MaxImageUploadSize)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = FileSizeComment.FileImageSizeExceptionString;
|
||||
return result;
|
||||
}
|
||||
|
||||
var data = new byte[userPhoto.Length];
|
||||
using var inputStream = userPhoto.OpenReadStream();
|
||||
|
||||
var br = new BinaryReader(inputStream);
|
||||
br.Read(data, 0, (int)userPhoto.Length);
|
||||
br.Close();
|
||||
|
||||
var defaultData = new byte[defaultUserPhoto.Length];
|
||||
using var defaultInputStream = defaultUserPhoto.OpenReadStream();
|
||||
|
||||
var defaultBr = new BinaryReader(defaultInputStream);
|
||||
defaultBr.Read(defaultData, 0, (int)defaultUserPhoto.Length);
|
||||
defaultBr.Close();
|
||||
|
||||
CheckImgFormat(data);
|
||||
|
||||
if (model.Autosave)
|
||||
{
|
||||
if (data.Length > SetupInfo.MaxImageUploadSize)
|
||||
throw new ImageSizeLimitException();
|
||||
|
||||
var mainPhoto = UserPhotoManager.SaveOrUpdateCroppedPhoto(userId, data, defaultData);
|
||||
|
||||
result.Data =
|
||||
new
|
||||
{
|
||||
main = mainPhoto,
|
||||
retina = UserPhotoManager.GetRetinaPhotoURL(userId),
|
||||
max = UserPhotoManager.GetMaxPhotoURL(userId),
|
||||
big = UserPhotoManager.GetBigPhotoURL(userId),
|
||||
medium = UserPhotoManager.GetMediumPhotoURL(userId),
|
||||
small = UserPhotoManager.GetSmallPhotoURL(userId),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Data = UserPhotoManager.SaveTempPhoto(data, SetupInfo.MaxImageUploadSize, UserPhotoManager.OriginalFotoSize.Width, UserPhotoManager.OriginalFotoSize.Height);
|
||||
}
|
||||
|
||||
result.Success = true;
|
||||
|
||||
}
|
||||
catch (UnknownImageFormatException)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = PeopleResource.ErrorUnknownFileImageType;
|
||||
}
|
||||
catch (ImageWeightLimitException)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = PeopleResource.ErrorImageWeightLimit;
|
||||
}
|
||||
catch (ImageSizeLimitException)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = PeopleResource.ErrorImageSizetLimit;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result.Success = false;
|
||||
result.Message = ex.Message.HtmlEncode();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
[Create("{userid}/photo")]
|
||||
public People.Models.FileUploadResult UploadMemberPhoto(string userid, IFormCollection model)
|
||||
{
|
||||
@ -1002,6 +900,7 @@ namespace ASC.Employee.Core.Controllers
|
||||
var settings = new UserPhotoThumbnailSettings(thumbnailsModel.X, thumbnailsModel.Y, thumbnailsModel.Width, thumbnailsModel.Height);
|
||||
SettingsManager.SaveForUser(settings, user.ID);
|
||||
|
||||
UserPhotoManager.RemovePhoto(user.ID);
|
||||
UserPhotoManager.SaveOrUpdatePhoto(user.ID, data);
|
||||
UserPhotoManager.RemoveTempPhoto(fileName);
|
||||
}
|
||||
|
@ -89,9 +89,6 @@ namespace ASC.Web.Api.Models
|
||||
[DataMember(Order = 20)]
|
||||
public string AvatarMax { get; set; }
|
||||
|
||||
[DataMember(Order = 20)]
|
||||
public string AvatarDefault { get; set; }
|
||||
|
||||
[DataMember(Order = 20)]
|
||||
public string AvatarMedium { get; set; }
|
||||
|
||||
@ -252,11 +249,6 @@ namespace ASC.Web.Api.Models
|
||||
|
||||
var userInfoLM = userInfo.LastModified.GetHashCode();
|
||||
|
||||
if (Context.Check("avatarDefault"))
|
||||
{
|
||||
result.AvatarDefault = Convert.ToBase64String(UserManager.GetUserPhoto(userInfo.ID));
|
||||
}
|
||||
|
||||
if (Context.Check("avatarMax"))
|
||||
{
|
||||
result.AvatarMax = UserPhotoManager.GetMaxPhotoURL(userInfo.ID, out var isdef) + (isdef ? "" : $"?_={userInfoLM}");
|
||||
|
@ -8,12 +8,6 @@ namespace ASC.People.Models
|
||||
public List<IFormFile> Files { get; set; }
|
||||
public bool Autosave { get; set; }
|
||||
}
|
||||
public class UploadCroppedPhotoModel
|
||||
{
|
||||
public string base64CroppedImage { get; set; }
|
||||
public string base64DefaultImage { get; set; }
|
||||
public bool Autosave { get; set; }
|
||||
}
|
||||
|
||||
public class FileUploadResult
|
||||
{
|
||||
|
@ -11,14 +11,6 @@ const ProjectsContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.display-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div label:not(:first-child) {
|
||||
margin: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const RadioButtonContainer = styled.div`
|
||||
@ -104,7 +96,8 @@ class PureModulesSettings extends Component {
|
||||
})
|
||||
}
|
||||
]}
|
||||
className="display-block"
|
||||
orientation='vertical'
|
||||
spacing='10px'
|
||||
/>
|
||||
</RadioButtonContainer>
|
||||
<ProjectsBody>
|
||||
|
@ -21,6 +21,12 @@ export function getUserList(filter = Filter.getDefault()) {
|
||||
url: `/people/${userName || '@self'}.json`
|
||||
});
|
||||
}
|
||||
export function getUserPhoto(userId) {
|
||||
return request({
|
||||
method: "get",
|
||||
url: `/people/${userId}/photo`
|
||||
});
|
||||
}
|
||||
|
||||
export function createUser(data, confirmKey = null) {
|
||||
const options = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-components",
|
||||
"version": "1.0.220",
|
||||
"version": "1.0.222",
|
||||
"description": "Ascensio System SIA component library",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "dist/asc-web-components.js",
|
||||
|
@ -74,6 +74,9 @@ class AvatarEditor extends React.Component {
|
||||
if (this.props.visible !== prevProps.visible) {
|
||||
this.setState({ visible: this.props.visible });
|
||||
}
|
||||
if (this.props.image !== prevProps.image) {
|
||||
this.setState({ isContainsFile: !!this.props.image });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -159,10 +159,12 @@ class AvatarEditorBody extends React.Component {
|
||||
this.props.deleteImage();
|
||||
}
|
||||
onImageChange() {
|
||||
this.setState({
|
||||
croppedImage: this.setEditorRef.current.getImage().toDataURL()
|
||||
});
|
||||
this.props.onImageChange(this.setEditorRef.current.getImage().toDataURL());
|
||||
if(this.setEditorRef.current !== null){
|
||||
this.setState({
|
||||
croppedImage: this.setEditorRef.current.getImage().toDataURL()
|
||||
});
|
||||
this.props.onImageChange(this.setEditorRef.current.getImage().toDataURL());
|
||||
}
|
||||
}
|
||||
dist = 0
|
||||
scaling = false
|
||||
@ -247,6 +249,13 @@ class AvatarEditorBody extends React.Component {
|
||||
height: this.setEditorRef.current.getImage().height
|
||||
});
|
||||
}
|
||||
componentDidUpdate(prevProps){
|
||||
if(prevProps.image !== this.props.image){
|
||||
this.setState({
|
||||
image: this.props.image
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
|
@ -16,9 +16,6 @@ const StyledComboBox = styled(ComboBox)`
|
||||
float: left;
|
||||
width: 20%;
|
||||
margin-left: 8px;
|
||||
.display-block{
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media ${mobile} {
|
||||
width: 50px;
|
||||
@ -90,11 +87,11 @@ class SortComboBox extends React.Component {
|
||||
<>
|
||||
<DropDownItem noHover >
|
||||
<RadioButtonGroup
|
||||
className="display-block"
|
||||
orientation='vertical'
|
||||
onClick={this.onChangeSortDirection}
|
||||
isDisabled={this.props.isDisabled}
|
||||
selected={this.state.sortDirection.toString()}
|
||||
spacing={0}
|
||||
spacing='0px'
|
||||
name={'direction'}
|
||||
options={sortDirectionArray}
|
||||
/>
|
||||
@ -102,11 +99,11 @@ class SortComboBox extends React.Component {
|
||||
<DropDownItem isSeparator />
|
||||
<DropDownItem noHover >
|
||||
<RadioButtonGroup
|
||||
className="display-block"
|
||||
orientation='vertical'
|
||||
onClick={this.onChangeSortId}
|
||||
isDisabled={this.props.isDisabled}
|
||||
selected={this.props.selectedOption.key}
|
||||
spacing={0}
|
||||
spacing='0px'
|
||||
name={'sort'}
|
||||
options={sortArray}
|
||||
/>
|
||||
|
@ -22,14 +22,16 @@ import { RadioButtonGroup } from "asc-web-components";
|
||||
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------ | :------------: | :------: | :----: | :-----: | ---------------------------------------------------------------------------------------- |
|
||||
| `className` | `string` | - | - | - | Accepts class |
|
||||
| `id` | `string` | - | - | - | Accepts id |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Disabling all radiobutton in group |
|
||||
| `name` | `string` | ✅ | - | - | Used as HTML `name` property for `<input>` tag. Used for identification RadioButtonGroup |
|
||||
| `onClick` | `func` | - | - | - | Allow you to handle clicking events on `<RadioButton />` component |
|
||||
| `options` | `arrayOf` | ✅ | - | - | Array of objects, contains props for each `<RadioButton />` component |
|
||||
| `selected` | `string` | ✅ | - | - | Value of the selected radiobutton |
|
||||
| `spacing` | `number` | - | - | `33` | Margin (in px) between radiobutton |
|
||||
| `style` | `obj`, `array` | - | - | - | Accepts css style |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------- | :------------: | :------: | :----------------------: | :----------: | ---------------------------------------------------------------------------------------- |
|
||||
| `className` | `string` | - | - | - | Accepts class |
|
||||
| `id` | `string` | - | - | - | Accepts id |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Disabling all radiobutton in group |
|
||||
| `name` | `string` | ✅ | - | - | Used as HTML `name` property for `<input>` tag. Used for identification RadioButtonGroup |
|
||||
| `onClick` | `func` | - | - | - | Allow you to handle clicking events on `<RadioButton />` component |
|
||||
| `options` | `arrayOf` | ✅ | - | - | Array of objects, contains props for each `<RadioButton />` component |
|
||||
| `selected` | `string` | ✅ | - | - | Value of the selected radiobutton |
|
||||
| `style` | `obj`, `array` | - | - | - | Accepts css style |
|
||||
| `orientation` | `oneOf` | - | `vertical`, `horizontal` | `horizontal` | Position of radiobuttons |
|
||||
| `spacing` | `string` | - | - | `15px` | Margin between radiobutton. If orientation `horizontal`, it is `margin-left`(apply for all radiobuttons, except first), if orientation `vertical`, it is `margin-bottom`(apply for all radiobuttons, except last) |
|
||||
| `width` | `string` | - | - | `100%` | Width of RadioButtonGroup container |
|
||||
|
@ -1,11 +1,17 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import RadioButton from '../radio-button';
|
||||
import styled from 'styled-components';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
// eslint-disable-next-line react/prop-types, no-unused-vars
|
||||
const ClearDiv = ({ orientation, width, ...props }) => <div {...props} />
|
||||
const StyledDiv = styled(ClearDiv)`
|
||||
${props =>
|
||||
(props.orientation === 'horizontal' && css`display: flex;`) ||
|
||||
(props.orientation === 'vertical' && css`display: block;`)};
|
||||
|
||||
width: ${props => props.width};
|
||||
`;
|
||||
|
||||
class RadioButtonGroup extends React.Component {
|
||||
|
||||
@ -38,6 +44,8 @@ class RadioButtonGroup extends React.Component {
|
||||
id={this.props.id}
|
||||
className={this.props.className}
|
||||
style={this.props.style}
|
||||
orientation={this.props.orientation}
|
||||
width={this.props.width}
|
||||
>
|
||||
{options.map(option => {
|
||||
return (
|
||||
@ -54,6 +62,7 @@ class RadioButtonGroup extends React.Component {
|
||||
isDisabled={this.props.isDisabled || option.disabled}
|
||||
label={option.label}
|
||||
spacing={this.props.spacing}
|
||||
orientation={this.props.orientation}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -74,16 +83,20 @@ RadioButtonGroup.propTypes = {
|
||||
disabled: PropTypes.bool
|
||||
})).isRequired,
|
||||
selected: PropTypes.string.isRequired,
|
||||
spacing: PropTypes.number,
|
||||
spacing: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
orientation: PropTypes.oneOf(['horizontal', 'vertical']),
|
||||
width: PropTypes.string,
|
||||
}
|
||||
|
||||
RadioButtonGroup.defaultProps = {
|
||||
isDisabled: false,
|
||||
selected: undefined,
|
||||
spacing: 33
|
||||
spacing: '15px',
|
||||
orientation: 'horizontal',
|
||||
width: '100%'
|
||||
}
|
||||
|
||||
export default RadioButtonGroup;
|
||||
|
@ -3,17 +3,18 @@ import { storiesOf } from '@storybook/react';
|
||||
import withReadme from 'storybook-readme/with-readme';
|
||||
import RadioButtonGroup from '.';
|
||||
import Section from '../../../.storybook/decorators/section';
|
||||
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs/react';
|
||||
import { withKnobs, text, boolean, select } from '@storybook/addon-knobs/react';
|
||||
import Readme from './README.md';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { optionsKnob as options } from '@storybook/addon-knobs';
|
||||
|
||||
|
||||
storiesOf('Components|Input', module)
|
||||
.addDecorator(withKnobs)
|
||||
.addDecorator(withReadme(Readme))
|
||||
.add('radio button group', () => {
|
||||
|
||||
.addDecorator(withKnobs)
|
||||
.addDecorator(withReadme(Readme))
|
||||
.add('radio button group', () => {
|
||||
|
||||
const orientation = ['horizontal', 'vertical'];
|
||||
const values = ['first', 'second', 'third'];
|
||||
const valuesMultiSelect = {
|
||||
radio1: 'radio1',
|
||||
@ -50,9 +51,11 @@ storiesOf('Components|Input', module)
|
||||
action('onChange')(e);
|
||||
console.log('Value of selected radiobutton: ', e.target.value);
|
||||
}}
|
||||
orientation={select('orientation', orientation, 'horizontal')}
|
||||
width={text('width', '100%')}
|
||||
isDisabled={boolean('isDisabled', false)}
|
||||
selected={values[0]}
|
||||
spacing={number('spacing', 33)}
|
||||
spacing={text('spacing', '15px')}
|
||||
name={text('name', 'group')}
|
||||
options={children}
|
||||
/>
|
||||
|
@ -16,14 +16,17 @@ import { RadioButton } from "asc-web-components";
|
||||
|
||||
`<RadioButtonGroup />` props supersede RadioButton props
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------ | :------------: | :------: | :----: | :-----: | ----------------------------------------------------------------------------------------- |
|
||||
| `className` | `string` | - | - | - | Accepts class |
|
||||
| `id` | `string` | - | - | - | Accepts id |
|
||||
| `isChecked` | `bool` | - | - | `false` | Used as HTML `checked` property for each `<input>` tag |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Used as HTML `disabled` property for each `<input>` tag |
|
||||
| `label` | `string` | - | - | - | Name of the radiobutton. If missed, `value` will be used |
|
||||
| `name` | `string` | ✅ | - | - | Used as HTML `name` property for `<input>` tag. |
|
||||
| `onClick` | `func` | - | - | - | Allow you to handle clicking events on component |
|
||||
| `style` | `obj`, `array` | - | - | - | Accepts css style |
|
||||
| `value` | `string` | ✅ | - | - | Used as HTML `value` property for `<input>` tag. Used for identification each radiobutton |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------- | :------------: | :------: | :----------------------: | :----------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `className` | `string` | - | - | - | Accepts class |
|
||||
| `id` | `string` | - | - | - | Accepts id |
|
||||
| `isChecked` | `bool` | - | - | `false` | Used as HTML `checked` property for each `<input>` tag |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Used as HTML `disabled` property for each `<input>` tag |
|
||||
| `label` | `string` | - | - | - | Name of the radiobutton. If missed, `value` will be used |
|
||||
| `name` | `string` | ✅ | - | - | Used as HTML `name` property for `<input>` tag. |
|
||||
| `onClick` | `func` | - | - | - | Allow you to handle clicking events on component |
|
||||
| `style` | `obj`, `array` | - | - | - | Accepts css style |
|
||||
| `value` | `string` | ✅ | - | - | Used as HTML `value` property for `<input>` tag. Used for identification each radiobutton |
|
||||
| `orientation` | `oneOf` | - | `vertical`, `horizontal` | `horizontal` | Position of radiobuttons |
|
||||
| `spacing` | `number` | - | - | `33` | Margin (in px) between radiobutton. If orientation `horizontal`, it is `margin-left`(apply for all radiobuttons, except first), if orientation `vertical`, it is `margin-bottom`(apply for all radiobuttons, except last) |
|
||||
| `width` | `string` | - | - | `100%` | Width of RadioButtonGroup container |
|
||||
|
@ -10,7 +10,7 @@ const internalCircleDisabledColor = '#D0D5DA';
|
||||
const externalCircleDisabledColor = '#eceef1';
|
||||
|
||||
// eslint-disable-next-line react/prop-types, no-unused-vars
|
||||
const ClearLabel = ({ spacing, isDisabled, ...props }) => <label {...props} />
|
||||
const ClearLabel = ({ spacing, isDisabled, orientation, ...props }) => <label {...props} />
|
||||
const Label = styled(ClearLabel)`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -47,7 +47,13 @@ const Label = styled(ClearLabel)`
|
||||
`}
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: ${props => props.spacing}px;
|
||||
${props =>
|
||||
(props.orientation === 'horizontal' && css`margin-left: ${props.spacing};`)};
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
${props =>
|
||||
(props.orientation === 'vertical' && css`margin-bottom: ${props.spacing};`)};
|
||||
}
|
||||
`;
|
||||
|
||||
@ -99,6 +105,7 @@ class RadioButton extends React.Component {
|
||||
|
||||
return (
|
||||
<Label
|
||||
orientation={this.props.orientation}
|
||||
spacing={this.props.spacing}
|
||||
isDisabled={this.props.isDisabled}
|
||||
id={this.props.id}
|
||||
@ -133,10 +140,11 @@ RadioButton.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
onClick: PropTypes.func,
|
||||
value: PropTypes.string.isRequired,
|
||||
spacing: PropTypes.number,
|
||||
spacing: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
orientation: PropTypes.oneOf(['horizontal', 'vertical'])
|
||||
}
|
||||
|
||||
RadioButton.defaultProps = {
|
||||
|
@ -516,10 +516,6 @@ namespace ASC.Web.Core.Users
|
||||
{
|
||||
return SaveOrUpdatePhoto(userID, data, -1, OriginalFotoSize, true, out _);
|
||||
}
|
||||
public string SaveOrUpdateCroppedPhoto(Guid userID, byte[] data, byte[] defaultData)
|
||||
{
|
||||
return SaveOrUpdateCroppedPhoto(userID, data, defaultData, -1, OriginalFotoSize, true, out _);
|
||||
}
|
||||
|
||||
public void RemovePhoto(Guid idUser)
|
||||
{
|
||||
@ -543,49 +539,7 @@ namespace ASC.Web.Core.Users
|
||||
UserManager.SaveUserPhoto(userID, data);
|
||||
SetUserPhotoThumbnailSettings(userID, width, height);
|
||||
UserPhotoManagerCache.ClearCache(userID);
|
||||
}
|
||||
|
||||
var store = GetDataStore();
|
||||
|
||||
var photoUrl = GetDefaultPhotoAbsoluteWebPath();
|
||||
if (data != null && data.Length > 0)
|
||||
{
|
||||
using (var stream = new MemoryStream(data))
|
||||
{
|
||||
photoUrl = store.Save(fileName, stream).ToString();
|
||||
}
|
||||
//Queue resizing
|
||||
SizePhoto(userID, data, -1, SmallFotoSize, true);
|
||||
SizePhoto(userID, data, -1, MediumFotoSize, true);
|
||||
SizePhoto(userID, data, -1, BigFotoSize, true);
|
||||
SizePhoto(userID, data, -1, MaxFotoSize, true);
|
||||
SizePhoto(userID, data, -1, RetinaFotoSize, true);
|
||||
}
|
||||
return photoUrl;
|
||||
}
|
||||
private string SaveOrUpdateCroppedPhoto(Guid userID, byte[] data, byte[] defaultData, long maxFileSize, Size size, bool saveInCoreContext, out string fileName)
|
||||
{
|
||||
data = TryParseImage(data, maxFileSize, size, out var imgFormat, out var width, out var height);
|
||||
|
||||
var widening = CommonPhotoManager.GetImgFormatName(imgFormat);
|
||||
fileName = string.Format("{0}_orig_{1}-{2}.{3}", userID, width, height, widening);
|
||||
|
||||
if (saveInCoreContext)
|
||||
{
|
||||
UserManager.SaveUserPhoto(userID, defaultData);
|
||||
|
||||
var max = Math.Max(Math.Max(width, height), SmallFotoSize.Width);
|
||||
var min = Math.Max(Math.Min(width, height), SmallFotoSize.Width);
|
||||
|
||||
var pos = (max - min) / 2;
|
||||
|
||||
var settings = new UserPhotoThumbnailSettings(
|
||||
width >= height ? new Point(pos, 0) : new Point(0, pos),
|
||||
new Size(min, min));
|
||||
|
||||
SettingsManager.SaveForUser(settings, userID);
|
||||
|
||||
UserPhotoManagerCache.ClearCache(userID);
|
||||
}
|
||||
|
||||
var store = GetDataStore();
|
||||
|
Loading…
Reference in New Issue
Block a user