From 94581bdd39970c98cfe1b5b0aebd0580cfcd2b9f Mon Sep 17 00:00:00 2001 From: NikolayRechkin Date: Mon, 9 Dec 2019 15:59:34 +0300 Subject: [PATCH 1/8] People: deleted unused code --- .../Server/Controllers/PeopleController.cs | 103 +----------------- .../Server/Models/EmployeeWraperFull.cs | 8 -- .../Server/Models/UploadPhotoModel.cs | 6 - web/ASC.Web.Core/Users/UserPhotoManager.cs | 46 -------- 4 files changed, 1 insertion(+), 162 deletions(-) diff --git a/products/ASC.People/Server/Controllers/PeopleController.cs b/products/ASC.People/Server/Controllers/PeopleController.cs index 934042a2ab..b3926ccb23 100644 --- a/products/ASC.People/Server/Controllers/PeopleController.cs +++ b/products/ASC.People/Server/Controllers/PeopleController.cs @@ -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); } diff --git a/products/ASC.People/Server/Models/EmployeeWraperFull.cs b/products/ASC.People/Server/Models/EmployeeWraperFull.cs index 4cca18bf71..e78cf6dea1 100644 --- a/products/ASC.People/Server/Models/EmployeeWraperFull.cs +++ b/products/ASC.People/Server/Models/EmployeeWraperFull.cs @@ -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}"); diff --git a/products/ASC.People/Server/Models/UploadPhotoModel.cs b/products/ASC.People/Server/Models/UploadPhotoModel.cs index e26ea7b92e..2f1661bddc 100644 --- a/products/ASC.People/Server/Models/UploadPhotoModel.cs +++ b/products/ASC.People/Server/Models/UploadPhotoModel.cs @@ -8,12 +8,6 @@ namespace ASC.People.Models public List 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 { diff --git a/web/ASC.Web.Core/Users/UserPhotoManager.cs b/web/ASC.Web.Core/Users/UserPhotoManager.cs index 5786a4493d..ebc39a6d8a 100644 --- a/web/ASC.Web.Core/Users/UserPhotoManager.cs +++ b/web/ASC.Web.Core/Users/UserPhotoManager.cs @@ -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(); From 8db8e25fa7036d388036396b7d28c78b4fef9e1b Mon Sep 17 00:00:00 2001 From: NikolayRechkin Date: Mon, 9 Dec 2019 16:00:45 +0300 Subject: [PATCH 2/8] web: components: fixed avatar editor --- .../src/components/avatar-editor/index.js | 3 +++ .../sub-components/avatar-editor-body.js | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/web/ASC.Web.Components/src/components/avatar-editor/index.js b/web/ASC.Web.Components/src/components/avatar-editor/index.js index 4f985a53d1..0202ac424f 100644 --- a/web/ASC.Web.Components/src/components/avatar-editor/index.js +++ b/web/ASC.Web.Components/src/components/avatar-editor/index.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() { diff --git a/web/ASC.Web.Components/src/components/avatar-editor/sub-components/avatar-editor-body.js b/web/ASC.Web.Components/src/components/avatar-editor/sub-components/avatar-editor-body.js index 6dc27bb7c7..8c8acf9d19 100644 --- a/web/ASC.Web.Components/src/components/avatar-editor/sub-components/avatar-editor-body.js +++ b/web/ASC.Web.Components/src/components/avatar-editor/sub-components/avatar-editor-body.js @@ -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 (
Date: Mon, 9 Dec 2019 16:07:04 +0300 Subject: [PATCH 3/8] web: people: changed the use of the avatar editor --- .../pages/Profile/Section/Header/index.js | 49 ++++++++++--------- .../Section/Body/createUserForm.js | 44 +++++++++++------ .../Section/Body/updateUserForm.js | 44 +++++++++++------ .../Client/src/store/profile/actions.js | 3 ++ web/ASC.Web.Common/src/api/people/index.js | 6 +++ 5 files changed, 93 insertions(+), 53 deletions(-) diff --git a/products/ASC.People/Client/src/components/pages/Profile/Section/Header/index.js b/products/ASC.People/Client/src/components/pages/Profile/Section/Header/index.js index 3f5659b6da..73aee4afc8 100644 --- a/products/ASC.People/Client/src/components/pages/Profile/Section/Header/index.js +++ b/products/ASC.People/Client/src/components/pages/Profile/Section/Header/index.js @@ -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 + }); + } + } }); }; diff --git a/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/createUserForm.js b/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/createUserForm.js index 087ebf5048..de5f101dfc 100644 --- a/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/createUserForm.js +++ b/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/createUserForm.js @@ -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, diff --git a/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/updateUserForm.js b/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/updateUserForm.js index 5d43884805..196d13ca2a 100644 --- a/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/updateUserForm.js +++ b/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/updateUserForm.js @@ -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, diff --git a/products/ASC.People/Client/src/store/profile/actions.js b/products/ASC.People/Client/src/store/profile/actions.js index ec305ff8e0..be338c4f51 100644 --- a/products/ASC.People/Client/src/store/profile/actions.js +++ b/products/ASC.People/Client/src/store/profile/actions.js @@ -98,3 +98,6 @@ export function getInvitationLink(isGuest = false) { return api.portal.getInvitationLink(isGuest); } }; +export function getUserPhoto(id) { + return api.people.getUserPhoto(id); +}; diff --git a/web/ASC.Web.Common/src/api/people/index.js b/web/ASC.Web.Common/src/api/people/index.js index 798a74f359..3f917ec32f 100644 --- a/web/ASC.Web.Common/src/api/people/index.js +++ b/web/ASC.Web.Common/src/api/people/index.js @@ -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 = { From a739f1ee8afd2fa0f47215e901c76f22b7a75fcd Mon Sep 17 00:00:00 2001 From: NikolayRechkin Date: Mon, 9 Dec 2019 16:10:48 +0300 Subject: [PATCH 4/8] revert appsettings.json --- config/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/appsettings.json b/config/appsettings.json index 402311dc2b..0cf8a54373 100644 --- a/config/appsettings.json +++ b/config/appsettings.json @@ -59,7 +59,7 @@ "ConnectionStrings": { "default": { "name": "default", - "connectionString": "Server=localhost;Database=test;User ID=root;Password=root;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none", + "connectionString": "Server=localhost;Database=onlyoffice;User ID=dev;Password=dev;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none", "providerName": "MySql.Data.MySqlClient" } }, From f6f12dea460df840c74416300592aecc426762ee Mon Sep 17 00:00:00 2001 From: NikolayRechkin Date: Tue, 10 Dec 2019 09:16:21 +0300 Subject: [PATCH 5/8] web: people: small fix --- products/ASC.People/Client/src/store/profile/actions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/products/ASC.People/Client/src/store/profile/actions.js b/products/ASC.People/Client/src/store/profile/actions.js index 70edf9aa86..d9c56a0f13 100644 --- a/products/ASC.People/Client/src/store/profile/actions.js +++ b/products/ASC.People/Client/src/store/profile/actions.js @@ -93,7 +93,6 @@ export function updateProfileCulture(id, culture) { }; }; -}; export function getUserPhoto(id) { return api.people.getUserPhoto(id); }; From d9d50ee364594d95dc8c83ea7afb0446aaaf0b5f Mon Sep 17 00:00:00 2001 From: NikolayRechkin Date: Tue, 10 Dec 2019 09:17:07 +0300 Subject: [PATCH 6/8] web: components: bump version --- web/ASC.Web.Components/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/ASC.Web.Components/package.json b/web/ASC.Web.Components/package.json index 5a16f2bca8..fb19e455a9 100644 --- a/web/ASC.Web.Components/package.json +++ b/web/ASC.Web.Components/package.json @@ -1,6 +1,6 @@ { "name": "asc-web-components", - "version": "1.0.220", + "version": "1.0.221", "description": "Ascensio System SIA component library", "license": "AGPL-3.0", "main": "dist/asc-web-components.js", From b0b1b4512771c5158fccdde0257287104f6b436f Mon Sep 17 00:00:00 2001 From: Daniil Senkiv Date: Tue, 10 Dec 2019 09:28:45 +0300 Subject: [PATCH 7/8] Web.components: RadioButtonGroup: added new props: width, orientation, changed proptype spacing from number to string, applied changes to other components --- .../Section/Body/FormFields/PasswordField.js | 1 + .../Section/Body/FormFields/RadioField.js | 1 + .../security/sub-components/modules.js | 11 ++------ .../components/filter-input/sort-combobox.js | 11 +++----- .../components/radio-button-group/README.md | 24 +++++++++-------- .../components/radio-button-group/index.js | 27 ++++++++++++++----- .../radio-button-group.stories.js | 15 ++++++----- .../src/components/radio-button/README.md | 25 +++++++++-------- .../src/components/radio-button/index.js | 16 ++++++++--- 9 files changed, 76 insertions(+), 55 deletions(-) diff --git a/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/FormFields/PasswordField.js b/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/FormFields/PasswordField.js index 7436f6eae1..a99b7924ab 100644 --- a/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/FormFields/PasswordField.js +++ b/products/ASC.People/Client/src/components/pages/ProfileAction/Section/Body/FormFields/PasswordField.js @@ -52,6 +52,7 @@ class PasswordField extends React.Component { isDisabled={radioIsDisabled} onClick={radioOnChange} className="radio-group" + spacing='33px' /> ); diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/modules.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/modules.js index 2cf1f16688..b22ede9f8e 100644 --- a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/modules.js +++ b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/modules.js @@ -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' /> diff --git a/web/ASC.Web.Components/src/components/filter-input/sort-combobox.js b/web/ASC.Web.Components/src/components/filter-input/sort-combobox.js index 1acd7cab17..4fd5150d42 100644 --- a/web/ASC.Web.Components/src/components/filter-input/sort-combobox.js +++ b/web/ASC.Web.Components/src/components/filter-input/sort-combobox.js @@ -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 { <> @@ -102,11 +99,11 @@ class SortComboBox extends React.Component { diff --git a/web/ASC.Web.Components/src/components/radio-button-group/README.md b/web/ASC.Web.Components/src/components/radio-button-group/README.md index 361df23e2a..4f09b295d9 100644 --- a/web/ASC.Web.Components/src/components/radio-button-group/README.md +++ b/web/ASC.Web.Components/src/components/radio-button-group/README.md @@ -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 `` tag. Used for identification RadioButtonGroup | -| `onClick` | `func` | - | - | - | Allow you to handle clicking events on `` component | -| `options` | `arrayOf` | ✅ | - | - | Array of objects, contains props for each `` 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 `` tag. Used for identification RadioButtonGroup | +| `onClick` | `func` | - | - | - | Allow you to handle clicking events on `` component | +| `options` | `arrayOf` | ✅ | - | - | Array of objects, contains props for each `` 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 | diff --git a/web/ASC.Web.Components/src/components/radio-button-group/index.js b/web/ASC.Web.Components/src/components/radio-button-group/index.js index 17694a55da..fcbe0ca394 100644 --- a/web/ASC.Web.Components/src/components/radio-button-group/index.js +++ b/web/ASC.Web.Components/src/components/radio-button-group/index.js @@ -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 }) =>
+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; diff --git a/web/ASC.Web.Components/src/components/radio-button-group/radio-button-group.stories.js b/web/ASC.Web.Components/src/components/radio-button-group/radio-button-group.stories.js index 663c97b398..bf46e3df87 100644 --- a/web/ASC.Web.Components/src/components/radio-button-group/radio-button-group.stories.js +++ b/web/ASC.Web.Components/src/components/radio-button-group/radio-button-group.stories.js @@ -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} /> diff --git a/web/ASC.Web.Components/src/components/radio-button/README.md b/web/ASC.Web.Components/src/components/radio-button/README.md index e2cc18b367..a263f736f9 100644 --- a/web/ASC.Web.Components/src/components/radio-button/README.md +++ b/web/ASC.Web.Components/src/components/radio-button/README.md @@ -16,14 +16,17 @@ import { RadioButton } from "asc-web-components"; `` 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 `` tag | -| `isDisabled` | `bool` | - | - | `false` | Used as HTML `disabled` property for each `` tag | -| `label` | `string` | - | - | - | Name of the radiobutton. If missed, `value` will be used | -| `name` | `string` | ✅ | - | - | Used as HTML `name` property for `` 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 `` 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 `` tag | +| `isDisabled` | `bool` | - | - | `false` | Used as HTML `disabled` property for each `` tag | +| `label` | `string` | - | - | - | Name of the radiobutton. If missed, `value` will be used | +| `name` | `string` | ✅ | - | - | Used as HTML `name` property for `` 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 `` 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 | diff --git a/web/ASC.Web.Components/src/components/radio-button/index.js b/web/ASC.Web.Components/src/components/radio-button/index.js index f06bc5e6dd..64b678891a 100644 --- a/web/ASC.Web.Components/src/components/radio-button/index.js +++ b/web/ASC.Web.Components/src/components/radio-button/index.js @@ -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 }) =>