Merge branch 'master' into feature/layout-scrolling
This commit is contained in:
commit
4d2db41258
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@
|
||||
/packages/asc-web-components
|
||||
/products/ASC.People/Data/
|
||||
Data/
|
||||
Logs/
|
||||
|
@ -9,7 +9,7 @@
|
||||
"ConnectionStrings": {
|
||||
"default": {
|
||||
"name": "default",
|
||||
"connectionString": "Server=172.18.0.5;Port=3306;Database=onlyoffice;User ID=onlyoffice_user;Password=onlyoffice_pass;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none",
|
||||
"connectionString": "Server=172.18.0.3;Port=3306;Database=onlyoffice;User ID=onlyoffice_user;Password=onlyoffice_pass;Pooling=true;Character Set=utf8;AutoEnlist=false;SSL Mode=none",
|
||||
"providerName": "MySql.Data.MySqlClient"
|
||||
}
|
||||
},
|
||||
|
@ -10,6 +10,7 @@ import ProfileAction from './components/pages/ProfileAction';
|
||||
import GroupAction from './components/pages/GroupAction';
|
||||
import { Error404 } from "./components/pages/Error";
|
||||
import Reassign from './components/pages/Reassign';
|
||||
import Import from './components/pages/Import';
|
||||
import history from './history';
|
||||
|
||||
/*const Profile = lazy(() => import("./components/pages/Profile"));
|
||||
@ -56,6 +57,11 @@ const App = ({ settings }) => {
|
||||
component={Reassign}
|
||||
restricted
|
||||
/>
|
||||
<PrivateRoute
|
||||
path={`${homepage}/import`}
|
||||
component={Import}
|
||||
restricted
|
||||
/>
|
||||
<PrivateRoute component={Error404} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
|
@ -72,7 +72,7 @@ class PureArticleMainButtonContent extends React.Component {
|
||||
<DropDownItem
|
||||
icon="ImportIcon"
|
||||
label={t('ImportPeople')}
|
||||
onClick={this.onNotImplementedClick.bind(this, "Import people action")}
|
||||
onClick={this.onDropDownItemClick.bind(this, `${settings.homepage}/import`)}
|
||||
/>
|
||||
</MainButton>
|
||||
{dialogVisible &&
|
||||
|
@ -0,0 +1,254 @@
|
||||
import React, { memo } from "react";
|
||||
import { withRouter } from "react-router";
|
||||
// import { useTranslation } from 'react-i18next';
|
||||
import { connect } from "react-redux";
|
||||
import styled from 'styled-components';
|
||||
import { Text, Button, Avatar, toastr } from 'asc-web-components';
|
||||
import { toEmployeeWrapper } from "../../../../../store/people/selectors";
|
||||
import { FixedSizeList as List, areEqual } from 'react-window';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
const LoadCsvWrapper = styled.div`
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
max-width: 1024px;
|
||||
`;
|
||||
|
||||
const SelectSourceWrapper = styled.div`
|
||||
margin-top: 24px;
|
||||
`;
|
||||
|
||||
const StyledFileInput = styled.div`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
height: 36px;
|
||||
|
||||
input[type="file"] {
|
||||
height: 100%;
|
||||
font-size: 200px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledFieldContainer = styled.div`
|
||||
position: relative;
|
||||
|
||||
@media (min-width:768px) {
|
||||
margin-top: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledAvatar = styled.div`
|
||||
float: left;
|
||||
|
||||
@media (max-width:768px) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledField = styled.div`
|
||||
line-height: 14px;
|
||||
min-width: 100px;
|
||||
padding: 4px 16px;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@media (min-width:768px) {
|
||||
margin-top: 4px;
|
||||
display: inline-block !important;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledProgress = styled.div`
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
width: ${props => props.completed && `${props.completed}%`};
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
background-color: #7ACE9B;
|
||||
opacity: 0.3;
|
||||
border-radius: 3px;
|
||||
z-index: -1;
|
||||
transition-property: width;
|
||||
transition-duration: 1s;
|
||||
`;
|
||||
|
||||
class ImportRow extends React.PureComponent {
|
||||
render() {
|
||||
const { items, completed } = this.props;
|
||||
|
||||
const fullName = items[0] + ' ' + items[1];
|
||||
const firstName = items[0];
|
||||
const lastName = items[1];
|
||||
const email = items[2];
|
||||
|
||||
return (
|
||||
<StyledFieldContainer key={email}>
|
||||
<StyledAvatar>
|
||||
<Avatar size='small' role='user' userName={fullName} />
|
||||
</StyledAvatar>
|
||||
<StyledField style={{ display: 'inline-block' }}>{firstName}</StyledField>
|
||||
<StyledField style={{ display: 'inline-block' }}>{lastName}</StyledField>
|
||||
<StyledField style={{ display: 'block' }}>{email}</StyledField>
|
||||
<StyledProgress completed={completed} />
|
||||
</StyledFieldContainer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SectionBodyContent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
splittedLines: [],
|
||||
completion: 0
|
||||
}
|
||||
}
|
||||
|
||||
getAsText = (fileToRead) => {
|
||||
let reader = new FileReader();
|
||||
reader.readAsText(fileToRead);
|
||||
reader.onload = this.loadHandler;
|
||||
reader.onerror = this.errorHandler;
|
||||
}
|
||||
|
||||
loadHandler = (event) => {
|
||||
const csv = event.target.result;
|
||||
this.processData(csv);
|
||||
}
|
||||
|
||||
errorHandler = (event) => {
|
||||
if (event.target.error.name === 'NotReadableError') {
|
||||
alert(event.target.error.name);
|
||||
}
|
||||
}
|
||||
|
||||
processData = (csv) => {
|
||||
const allTextLines = csv.split(/\r\n|\n/);
|
||||
const splittedLines = allTextLines.map(line => line.split(','));
|
||||
const filteredLines = splittedLines.filter(line => (line[0].length > 0) && line);
|
||||
|
||||
this.setState({
|
||||
splittedLines: filteredLines
|
||||
});
|
||||
}
|
||||
|
||||
createRows = rows => rows.map(data => {
|
||||
return (
|
||||
<ImportRow items={data} />
|
||||
);
|
||||
});
|
||||
|
||||
renderRow = memo(({ data, index, style }) => {
|
||||
return (
|
||||
<div style={style}>
|
||||
{data[index]}
|
||||
</div>
|
||||
)
|
||||
}, areEqual);
|
||||
|
||||
prepareUsersData = data => {
|
||||
const { splittedLines } = this.state;
|
||||
const rawUsers = splittedLines || data;
|
||||
|
||||
const preparedUsers = rawUsers.map(user => {
|
||||
const userObj = {};
|
||||
|
||||
userObj.firstName = user[0];
|
||||
userObj.lastName = user[1];
|
||||
userObj.email = user[2];
|
||||
|
||||
return toEmployeeWrapper(userObj);
|
||||
});
|
||||
|
||||
return preparedUsers;
|
||||
}
|
||||
|
||||
runImport = users => {
|
||||
// Use with axios
|
||||
}
|
||||
|
||||
onImportClick = () => {
|
||||
const users = this.prepareUsersData();
|
||||
this.runImport(users);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { splittedLines, completion } = this.state;
|
||||
// const { t } = useTranslation();
|
||||
const rows = this.createRows(splittedLines);
|
||||
|
||||
const renderList = ({ height, width }) => {
|
||||
const itemHeight = (width < 768) ? 56 : 36;
|
||||
return (
|
||||
<List
|
||||
className="List"
|
||||
height={height}
|
||||
width={width}
|
||||
itemSize={itemHeight}
|
||||
itemCount={rows.length}
|
||||
itemData={rows}
|
||||
>
|
||||
{this.renderRow}
|
||||
</List>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text.Body fontSize={18} >
|
||||
Functionality at development stage.
|
||||
</Text.Body>
|
||||
<br />
|
||||
<Text.Body fontSize={14} >
|
||||
Files are formatted according to CSV RFC rules. <br />
|
||||
Column Order: FirstName, LastName, Email. <br />
|
||||
Comma delimiter, strings in unix format. <br />
|
||||
</Text.Body>
|
||||
<SelectSourceWrapper>
|
||||
<StyledFileInput>
|
||||
<Button size='big' primary={true} scale={true} label="Upload CSV" isDisabled={true} />
|
||||
{false &&
|
||||
<input type="file" name="file" onChange={(e) => this.getAsText(e.target.files[0])} accept='.csv' />
|
||||
}
|
||||
</StyledFileInput>
|
||||
</SelectSourceWrapper>
|
||||
<br />
|
||||
<Text.Body fontSize={14} >
|
||||
Ready for import: {`${splittedLines.length} of ${splittedLines.length}`}
|
||||
</Text.Body>
|
||||
<div style={{ position: 'relative', width: '100%', height: '30px' }}>
|
||||
<StyledProgress completed={completion} />
|
||||
</div>
|
||||
<LoadCsvWrapper>
|
||||
<AutoSizer>
|
||||
{renderList}
|
||||
</AutoSizer>
|
||||
</LoadCsvWrapper>
|
||||
<StyledFileInput>
|
||||
<Button size='big' primary={true} scale={true} onClick={this.onImportClick} isDisabled={true} label="Start import" />
|
||||
</StyledFileInput>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
}
|
||||
)(withRouter(SectionBodyContent));
|
@ -0,0 +1,45 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Text, IconButton } from "asc-web-components";
|
||||
import { withRouter } from "react-router";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const wrapperStyle = {
|
||||
display: "flex",
|
||||
alignItems: "center"
|
||||
};
|
||||
|
||||
const textStyle = {
|
||||
marginLeft: "16px",
|
||||
marginRight: "16px"
|
||||
};
|
||||
|
||||
const SectionHeaderContent = props => {
|
||||
const { history, settings } = props;
|
||||
//const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div style={wrapperStyle}>
|
||||
<div style={{ width: "16px" }}>
|
||||
<IconButton
|
||||
iconName={"ArrowPathIcon"}
|
||||
color="#A3A9AE"
|
||||
size="16"
|
||||
onClick={() => history.push(settings.homepage)}
|
||||
/>
|
||||
</div>
|
||||
<Text.ContentHeader truncate={true} style={textStyle}>
|
||||
Add users to the portal
|
||||
</Text.ContentHeader>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
profile: state.profile.targetUser,
|
||||
settings: state.auth.settings
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(withRouter(SectionHeaderContent));
|
@ -0,0 +1,2 @@
|
||||
export { default as SectionHeaderContent } from './Header';
|
||||
export { default as SectionBodyContent } from './Body';
|
@ -0,0 +1,50 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/Reassign/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
}
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,75 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
// import PropTypes from "prop-types";
|
||||
import { PageLayout } from "asc-web-components";
|
||||
import { ArticleHeaderContent, ArticleMainButtonContent, ArticleBodyContent } from '../../Article';
|
||||
// import { SectionHeaderContent } from './Section';
|
||||
// import { fetchProfile } from '../../../store/profile/actions';
|
||||
import i18n from "./i18n";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import { SectionHeaderContent, SectionBodyContent } from './Section';
|
||||
|
||||
class Import extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
// const { match, fetchProfile } = this.props;
|
||||
// const { userId } = match.params;
|
||||
|
||||
// if (userId) {
|
||||
// fetchProfile(userId);
|
||||
// }
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
// const { match, fetchProfile } = this.props;
|
||||
// const { userId } = match.params;
|
||||
// const prevUserId = prevProps.match.params.userId;
|
||||
|
||||
// if (userId !== undefined && userId !== prevUserId) {
|
||||
// fetchProfile(userId);
|
||||
// }
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("Import render")
|
||||
|
||||
// let loaded = false;
|
||||
// const { profile, match } = this.props;
|
||||
// const { userId, type } = match.params;
|
||||
|
||||
// if (type) {
|
||||
// loaded = true;
|
||||
// } else if (profile) {
|
||||
// loaded = profile.userName === userId || profile.id === userId;
|
||||
// }
|
||||
|
||||
return (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<PageLayout
|
||||
articleHeaderContent={<ArticleHeaderContent />}
|
||||
articleMainButtonContent={<ArticleMainButtonContent />}
|
||||
articleBodyContent={<ArticleBodyContent />}
|
||||
sectionHeaderContent={<SectionHeaderContent />}
|
||||
sectionBodyContent={<SectionBodyContent />}
|
||||
/>
|
||||
</I18nextProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Import.propTypes = {
|
||||
// match: PropTypes.object.isRequired,
|
||||
// profile: PropTypes.object,
|
||||
// fetchProfile: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
// profile: state.profile.targetUser
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
})(Import);
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"ReassignmentData": "Reassignment of data",
|
||||
"ReassignsToUser": "Employee to whom the data will be transferred —",
|
||||
"ChooseUser": "Choose user",
|
||||
"ReassignsTransferedListHdr": "Will be transferred:",
|
||||
"ReassignsTransferedListItem1": "General documents and personal documents that are available to other portal users;",
|
||||
"ReassignsTransferedListItem2": "Open projects, milestones and tasks;",
|
||||
"ReassignsTransferedListItem3": "Contacts, open tasks, unclosed opportunities and CRM cases;",
|
||||
"NotBeUndone": "Note: this action cannot be undone.",
|
||||
"ReassignsReadMore": "More about data transfer",
|
||||
"DeleteProfileAfterReassignment": "Delete profile when reassignment will be finished",
|
||||
"CancelButton": "Cancel",
|
||||
"ReassignButton": "Reassign",
|
||||
|
||||
"LDAPLbl": "LDAP"
|
||||
}
|
@ -347,7 +347,13 @@ class ProfileInfo extends React.PureComponent {
|
||||
className='language-combo'
|
||||
/>
|
||||
<TooltipIcon>
|
||||
<HelpButton place="bottom" offsetLeft={50} offsetRight={0} tooltipContent={tooltipLanguage} />
|
||||
<HelpButton
|
||||
place="bottom"
|
||||
offsetLeft={50}
|
||||
offsetRight={0}
|
||||
tooltipContent={tooltipLanguage}
|
||||
HelpButtonHeaderContent={t('Language')}
|
||||
/>
|
||||
</TooltipIcon>
|
||||
|
||||
</InfoItemValue>
|
||||
|
@ -21,7 +21,8 @@ class RadioField extends React.Component {
|
||||
radioIsDisabled,
|
||||
radioOnChange,
|
||||
|
||||
tooltipContent
|
||||
tooltipContent,
|
||||
HelpButtonHeaderContent
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -30,6 +31,7 @@ class RadioField extends React.Component {
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
tooltipContent={tooltipContent}
|
||||
HelpButtonHeaderContent={HelpButtonHeaderContent}
|
||||
>
|
||||
<RadioButtonGroup
|
||||
name={radioName}
|
||||
|
@ -32,7 +32,8 @@ class TextChangeField extends React.Component {
|
||||
buttonOnClick,
|
||||
buttonTabIndex,
|
||||
|
||||
tooltipContent
|
||||
tooltipContent,
|
||||
HelpButtonHeaderContent
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -41,6 +42,7 @@ class TextChangeField extends React.Component {
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
tooltipContent={tooltipContent}
|
||||
HelpButtonHeaderContent={HelpButtonHeaderContent}
|
||||
>
|
||||
<InputContainer>
|
||||
<TextInput
|
||||
|
@ -21,7 +21,8 @@ class TextField extends React.Component {
|
||||
inputOnChange,
|
||||
inputAutoFocussed,
|
||||
inputTabIndex,
|
||||
tooltipContent
|
||||
tooltipContent,
|
||||
HelpButtonHeaderContent
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -30,6 +31,7 @@ class TextField extends React.Component {
|
||||
hasError={hasError}
|
||||
labelText={labelText}
|
||||
tooltipContent={tooltipContent}
|
||||
HelpButtonHeaderContent={HelpButtonHeaderContent}
|
||||
>
|
||||
<TextInput
|
||||
name={inputName}
|
||||
|
@ -356,6 +356,7 @@ class CreateUserForm extends React.Component {
|
||||
inputOnChange={this.onInputChange}
|
||||
inputTabIndex={3}
|
||||
|
||||
HelpButtonHeaderContent={t("Mail")}
|
||||
tooltipContent={
|
||||
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
|
||||
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications. <p className="tooltip_email" style={{marginTop: "1rem", marginBottom: "1rem"}} >You can create a new mail on the domain as the primary. In this case, you must set a one-time password so that the user can log in to the portal for the first time.</p> The main e-mail can be used as a login when logging in to the portal.
|
||||
|
@ -506,6 +506,7 @@ class UpdateUserForm extends React.Component {
|
||||
buttonOnClick={this.onEmailChange}
|
||||
buttonTabIndex={1}
|
||||
|
||||
HelpButtonHeaderContent={t("Mail")}
|
||||
tooltipContent={
|
||||
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
|
||||
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications. <p style={{height: "0", visibility: "hidden"}}>You can create a new mail on the domain as the primary. In this case, you must set a one-time password so that the user can log in to the portal for the first time.</p> The main e-mail can be used as a login when logging in to the portal.
|
||||
@ -583,6 +584,7 @@ class UpdateUserForm extends React.Component {
|
||||
radioOnChange={this.onUserTypeChange}
|
||||
|
||||
tooltipContent={tooltipTypeContent}
|
||||
HelpButtonHeaderContent={t('UserType')}
|
||||
/>
|
||||
<DateField
|
||||
calendarHeaderContent={t("CalendarSelectDate")}
|
||||
|
@ -13,6 +13,7 @@ const ActivateEmailForm = lazy(() => import("./sub-components/activateEmail"));
|
||||
const ChangeEmailForm = lazy(() => import("./sub-components/changeEmail"));
|
||||
const ChangePhoneForm = lazy(() => import("./sub-components/changePhone"));
|
||||
const ProfileRemoveForm = lazy(() => import("./sub-components/profileRemove"));
|
||||
const ChangeOwnerForm = lazy(() => import("./sub-components/changeOwner"));
|
||||
const Error404 = lazy(() => import("../Error"));
|
||||
|
||||
const Confirm = ({ match, language }) => {
|
||||
@ -61,6 +62,11 @@ const Confirm = ({ match, language }) => {
|
||||
path={`${match.path}/PhoneActivation`}
|
||||
component={ChangePhoneForm}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${match.path}/ownerchange`}
|
||||
component={ChangeOwnerForm}
|
||||
/>
|
||||
<Route component={Error404} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
|
@ -22,5 +22,9 @@
|
||||
"DeleteProfileConfirmation": "Attention! You are about to delete your account.",
|
||||
"DeleteProfileConfirmationInfo": "By clicking the \"Delete my account\" button you agree with our Privacy policy.",
|
||||
"DeleteProfileSuccessMessage": "Your account has been successfully deleted.",
|
||||
"DeleteProfileSuccessMessageInfo": "See our Privacy policy to learn more about deleting your account and data accociated with it."
|
||||
"DeleteProfileSuccessMessageInfo": "See our Privacy policy to learn more about deleting your account and data accociated with it.",
|
||||
"ConfirmOwnerPortalTitle": "Please confirm that you want to change portal owner to {{newOwner}}",
|
||||
"SaveButton": "Save",
|
||||
"CancelButton": "Cancel",
|
||||
"ConfirmOwnerPortalSuccessMessage": "Владелец портала был успешно изменен. {0} Через 10 секунд вы будете перенаправлены {1} сюда {2}"
|
||||
}
|
@ -17,5 +17,9 @@
|
||||
"PasswordCustomMode": "Пароль",
|
||||
"ImportContactsOkButton": "OK",
|
||||
"LoadingProcessing": "Загрузка...",
|
||||
"ChangePasswordSuccess": "Пароль был успешно изменен"
|
||||
"ChangePasswordSuccess": "Пароль был успешно изменен",
|
||||
"ConfirmOwnerPortalTitle": "Пожалуйста, подтвердите, что Вы хотите изменить владельца портала на {{newOwner}}",
|
||||
"SaveButton": "Сохранить",
|
||||
"CancelButton": "Отмена",
|
||||
"ConfirmOwnerPortalSuccessMessage": "Portal owner has been successfully changed. {0}In 10 seconds you will be redirected {1}here{2}"
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { Container, Row, Col, Card, CardTitle, CardImg } from "reactstrap";
|
||||
import { Button, PageLayout, Text, toastr } from "asc-web-components";
|
||||
//import { } from "../../../../../src/store/auth/actions";
|
||||
|
||||
const BodyStyle = styled(Container)`
|
||||
margin-top: 70px;
|
||||
|
||||
.buttons-style {
|
||||
margin-top: 20px;
|
||||
min-width: 110px;
|
||||
}
|
||||
.button-style {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.confirm_text {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.password-card {
|
||||
border: none;
|
||||
.card-img {
|
||||
max-width: 216px;
|
||||
max-height: 35px;
|
||||
}
|
||||
.card-title {
|
||||
word-wrap: break-word;
|
||||
margin: 8px 0;
|
||||
text-align: left;
|
||||
font-size: 24px;
|
||||
color: #116d9d;
|
||||
}
|
||||
}
|
||||
|
||||
.row_display {
|
||||
display: flex;
|
||||
}
|
||||
.confirm-text {
|
||||
margin-top: 32px;
|
||||
}
|
||||
`;
|
||||
|
||||
class Form extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { showButtons: true };
|
||||
}
|
||||
|
||||
onAcceptClick = () => {
|
||||
this.setState({ showButtons: false });
|
||||
toastr.success("Accept click");
|
||||
setTimeout(this.onRedirect, 10000);
|
||||
};
|
||||
|
||||
onRedirect = () => {
|
||||
this.props.history.push("/");
|
||||
};
|
||||
|
||||
onCancelClick = () => {
|
||||
this.props.history.push("/");
|
||||
};
|
||||
|
||||
render() {
|
||||
const { t, greetingTitle } = this.props;
|
||||
const mdOptions = { size: 6, offset: 2 };
|
||||
|
||||
return (
|
||||
<BodyStyle>
|
||||
<Row className="password-row">
|
||||
<Col sm="12" md={mdOptions}>
|
||||
<Card className="password-card">
|
||||
<CardImg
|
||||
className="card-img"
|
||||
src="images/dark_general.png"
|
||||
alt="Logo"
|
||||
top
|
||||
/>
|
||||
<CardTitle className="card-title">{greetingTitle}</CardTitle>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col sm="12" md={{ size: 12, offset: 2 }}>
|
||||
<Text.Body className="confirm_text" fontSize={18}>
|
||||
{t("ConfirmOwnerPortalTitle", { newOwner: "NEW OWNER" })}
|
||||
</Text.Body>
|
||||
</Col>
|
||||
</Row>
|
||||
{this.state.showButtons ? (
|
||||
<Row>
|
||||
<Col className="row_display" sm="12" md={mdOptions}>
|
||||
<Button
|
||||
className="button-style buttons-style"
|
||||
primary
|
||||
size="big"
|
||||
label={t("SaveButton")}
|
||||
tabIndex={2}
|
||||
isDisabled={false}
|
||||
onClick={this.onAcceptClick}
|
||||
/>
|
||||
<Button
|
||||
className="buttons-style"
|
||||
size="big"
|
||||
label={t("CancelButton")}
|
||||
tabIndex={2}
|
||||
isDisabled={false}
|
||||
onClick={this.onCancelClick}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Row>
|
||||
<Col sm="12" md={{ size: 12, offset: 2 }}>
|
||||
<Text.Body className="confirm-text" fontSize={12}>
|
||||
{t("ConfirmOwnerPortalSuccessMessage")}
|
||||
</Text.Body>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
</BodyStyle>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Form.propTypes = {};
|
||||
|
||||
Form.defaultProps = {};
|
||||
|
||||
const ChangePasswordForm = props => (
|
||||
<PageLayout sectionBodyContent={<Form {...props} />} />
|
||||
);
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return { greetingTitle: state.auth.settings.greetingSettings };
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{}
|
||||
)(withRouter(withTranslation()(ChangePasswordForm)));
|
@ -273,6 +273,7 @@ const Form = props => {
|
||||
/>
|
||||
<TooltipStyle>
|
||||
<HelpButton
|
||||
HelpButtonHeaderContent={t('CookieSettingsTitle')}
|
||||
tooltipContent={
|
||||
<Text.Body fontSize={12}>{t("RememberHelper")}</Text.Body>
|
||||
}
|
||||
|
@ -11,5 +11,6 @@
|
||||
"SendButton": "Send",
|
||||
"CancelButton": "Cancel",
|
||||
"Remember": "Remember",
|
||||
"RememberHelper": "The default session lifetime is 20 minutes. Check this option to set it to 1 year. To set your own value, go to the settings."
|
||||
"RememberHelper": "The default session lifetime is 20 minutes. Check this option to set it to 1 year. To set your own value, go to the settings.",
|
||||
"CookieSettingsTitle": "Session Lifetime"
|
||||
}
|
@ -11,5 +11,6 @@
|
||||
"SendButton": "Отправить",
|
||||
"CancelButton": "Отмена",
|
||||
"Remember": "Запомнить",
|
||||
"RememberHelper": "Время существования сессии по умолчанию составляет 20 минут. Отметьте эту опцию, чтобы установить значение 1 год. Чтобы задать собственное значение, перейдите в настройки."
|
||||
"RememberHelper": "Время существования сессии по умолчанию составляет 20 минут. Отметьте эту опцию, чтобы установить значение 1 год. Чтобы задать собственное значение, перейдите в настройки.",
|
||||
"CookieSettingsTitle": "Время жизни сессии"
|
||||
}
|
@ -177,6 +177,7 @@ class Customization extends React.Component {
|
||||
className='margin-top'
|
||||
labelText={`${t("Language")}:`}
|
||||
tooltipContent={tooltipLanguage}
|
||||
HelpButtonHeaderContent={t("Language")}
|
||||
isVertical={true}>
|
||||
<ComboBox
|
||||
id='comboBoxLanguage'
|
||||
|
@ -73,7 +73,6 @@ class PureAccessRights extends Component {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
const { isLoading, selectedTab } = this.state;
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { I18nextProvider, withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import {
|
||||
changeAdmins,
|
||||
getUpdateListAdmin,
|
||||
fetchPeople
|
||||
} from "../../../../../../store/settings/actions";
|
||||
import {
|
||||
@ -22,11 +23,20 @@ import {
|
||||
toastr,
|
||||
FilterInput,
|
||||
Button,
|
||||
RequestLoader
|
||||
RequestLoader,
|
||||
Loader,
|
||||
EmptyScreenContainer,
|
||||
Icons
|
||||
} from "asc-web-components";
|
||||
import { getUserRole } from "../../../../../../store/settings/selectors";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
|
||||
const AdminsContainer = styled.div`
|
||||
.hidden-icon {
|
||||
position: fixed;
|
||||
visibility: hidden;
|
||||
}
|
||||
`;
|
||||
const ToggleContentContainer = styled.div`
|
||||
.buttons_container {
|
||||
display: flex;
|
||||
@ -53,7 +63,6 @@ const ToggleContentContainer = styled.div`
|
||||
}
|
||||
|
||||
.filter_container {
|
||||
margin-bottom: 50px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
@ -68,17 +77,15 @@ class PureAdminsSettings extends Component {
|
||||
allOptions: [],
|
||||
options: [],
|
||||
isLoading: false,
|
||||
showLoader: true,
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { admins, options } = this.props;
|
||||
const { admins, options, fetchPeople } = this.props;
|
||||
|
||||
if (isEmpty(admins, true) || isEmpty(options, true)) {
|
||||
const { fetchPeople } = this.props;
|
||||
|
||||
this.onLoading(true);
|
||||
const newFilter = this.onAdminsFilter();
|
||||
fetchPeople(newFilter)
|
||||
.catch(error => {
|
||||
@ -86,10 +93,12 @@ class PureAdminsSettings extends Component {
|
||||
})
|
||||
.finally(() =>
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
showLoader: false,
|
||||
allOptions: this.props.options
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.setState({ showLoader: false });
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,30 +162,32 @@ class PureAdminsSettings extends Component {
|
||||
};
|
||||
|
||||
onChangePage = pageItem => {
|
||||
const { filter, fetchPeople } = this.props;
|
||||
const { filter, getUpdateListAdmin } = this.props;
|
||||
|
||||
const newFilter = filter.clone();
|
||||
newFilter.page = pageItem.key;
|
||||
this.onLoading(true);
|
||||
fetchPeople(newFilter)
|
||||
|
||||
getUpdateListAdmin(newFilter)
|
||||
.catch(res => console.log(res))
|
||||
.finally(() => this.onLoading(false));
|
||||
};
|
||||
|
||||
onChangePageSize = pageItem => {
|
||||
const { filter, fetchPeople } = this.props;
|
||||
const { filter, getUpdateListAdmin } = this.props;
|
||||
|
||||
const newFilter = filter.clone();
|
||||
newFilter.page = 0;
|
||||
newFilter.pageCount = pageItem.key;
|
||||
this.onLoading(true);
|
||||
fetchPeople(newFilter)
|
||||
|
||||
getUpdateListAdmin(newFilter)
|
||||
.catch(res => console.log(res))
|
||||
.finally(() => this.onLoading(false));
|
||||
};
|
||||
|
||||
onPrevClick = e => {
|
||||
const { filter, fetchPeople } = this.props;
|
||||
const { filter, getUpdateListAdmin } = this.props;
|
||||
|
||||
if (!filter.hasPrev()) {
|
||||
e.preventDefault();
|
||||
@ -185,13 +196,13 @@ class PureAdminsSettings extends Component {
|
||||
const newFilter = filter.clone();
|
||||
newFilter.page--;
|
||||
this.onLoading(true);
|
||||
fetchPeople(newFilter)
|
||||
getUpdateListAdmin(newFilter)
|
||||
.catch(res => console.log(res))
|
||||
.finally(() => this.onLoading(false));
|
||||
};
|
||||
|
||||
onNextClick = e => {
|
||||
const { filter, fetchPeople } = this.props;
|
||||
const { filter, getUpdateListAdmin } = this.props;
|
||||
|
||||
if (!filter.hasNext()) {
|
||||
e.preventDefault();
|
||||
@ -200,7 +211,8 @@ class PureAdminsSettings extends Component {
|
||||
const newFilter = filter.clone();
|
||||
newFilter.page++;
|
||||
this.onLoading(true);
|
||||
fetchPeople(newFilter)
|
||||
|
||||
getUpdateListAdmin(newFilter)
|
||||
.catch(res => console.log(res))
|
||||
.finally(() => this.onLoading(false));
|
||||
};
|
||||
@ -220,7 +232,7 @@ class PureAdminsSettings extends Component {
|
||||
};
|
||||
|
||||
onFilter = data => {
|
||||
const { filter, fetchPeople } = this.props;
|
||||
const { filter, getUpdateListAdmin } = this.props;
|
||||
|
||||
const search = data.inputValue || null;
|
||||
const sortBy = data.sortId;
|
||||
@ -235,11 +247,23 @@ class PureAdminsSettings extends Component {
|
||||
newFilter.role = "admin";
|
||||
newFilter.search = search;
|
||||
this.onLoading(true);
|
||||
fetchPeople(newFilter)
|
||||
|
||||
getUpdateListAdmin(newFilter)
|
||||
.catch(res => console.log(res))
|
||||
.finally(this.onLoading(false));
|
||||
};
|
||||
|
||||
onResetFilter = () => {
|
||||
const { getUpdateListAdmin, filter } = this.props;
|
||||
|
||||
const newFilter = filter.clone(true);
|
||||
|
||||
this.onLoading(true);
|
||||
getUpdateListAdmin(newFilter)
|
||||
.catch(res => console.log(res))
|
||||
.finally(() => this.onLoading(false));
|
||||
};
|
||||
|
||||
filterUserSelectorOptions = (options, template) =>
|
||||
options.filter(option => option.label.indexOf(template) > -1);
|
||||
|
||||
@ -304,7 +328,8 @@ class PureAdminsSettings extends Component {
|
||||
options,
|
||||
selectedOptions,
|
||||
isLoading,
|
||||
showFullAdminSelector
|
||||
showFullAdminSelector,
|
||||
showLoader
|
||||
} = this.state;
|
||||
|
||||
const countElements = filter.total;
|
||||
@ -312,167 +337,198 @@ class PureAdminsSettings extends Component {
|
||||
console.log("Admins render_");
|
||||
|
||||
return (
|
||||
<>
|
||||
<RequestLoader
|
||||
visible={isLoading}
|
||||
zIndex={256}
|
||||
loaderSize={16}
|
||||
loaderColor={"#999"}
|
||||
label={`${t("LoadingProcessing")} ${t("LoadingDescription")}`}
|
||||
fontSize={12}
|
||||
fontColor={"#999"}
|
||||
className="page_loader"
|
||||
/>
|
||||
/*TODO: delete after resolve icon button problem*/
|
||||
<AdminsContainer>
|
||||
<IconButton className="hidden-icon" iconName="SearchIcon" />
|
||||
|
||||
<ToggleContentContainer>
|
||||
<div className="buttons_container">
|
||||
<Button
|
||||
className="button_style"
|
||||
size="medium"
|
||||
primary={true}
|
||||
label="Set people admin"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onShowGroupSelector}
|
||||
{showLoader ? (
|
||||
<Loader className="pageLoader" type="rombs" size={40} />
|
||||
) : (
|
||||
<>
|
||||
<RequestLoader
|
||||
visible={isLoading}
|
||||
zIndex={256}
|
||||
loaderSize={16}
|
||||
loaderColor={"#999"}
|
||||
label={`${t("LoadingProcessing")} ${t("LoadingDescription")}`}
|
||||
fontSize={12}
|
||||
fontColor={"#999"}
|
||||
className="page_loader"
|
||||
/>
|
||||
<div style={{ right: 180 }} className="advanced-selector">
|
||||
<AdvancedSelector
|
||||
displayType="dropdown"
|
||||
isOpen={showSelector}
|
||||
placeholder="placeholder"
|
||||
options={options}
|
||||
onSearchChanged={this.onSearchUsers}
|
||||
//groups={groups}
|
||||
isMultiSelect={true}
|
||||
buttonLabel="Add members"
|
||||
onSelect={this.onSelect}
|
||||
onCancel={this.onShowGroupSelector}
|
||||
onAddNewClick={() => console.log("onAddNewClick")}
|
||||
selectAllLabel="selectorSelectAllText"
|
||||
selectedOptions={selectedOptions}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="medium"
|
||||
primary={true}
|
||||
label="Set portal admin"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onShowFullAdminGroupSelector}
|
||||
/>
|
||||
<div style={{ right: 160 }} className="advanced-selector">
|
||||
<AdvancedSelector
|
||||
displayType="dropdown"
|
||||
isOpen={showFullAdminSelector}
|
||||
placeholder="placeholder"
|
||||
options={options}
|
||||
onSearchChanged={this.onSearchUsers}
|
||||
//groups={groups}
|
||||
isMultiSelect={true}
|
||||
buttonLabel="Add members"
|
||||
onSelect={this.onSelectFullAdmin}
|
||||
onCancel={this.onShowFullAdminGroupSelector}
|
||||
onAddNewClick={() => console.log("onAddNewClick")}
|
||||
selectAllLabel="selectorSelectAllText"
|
||||
selectedOptions={selectedOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{countElements > 25 ? (
|
||||
<FilterInput
|
||||
className="filter_container"
|
||||
getFilterData={() => []}
|
||||
getSortData={this.getSortData}
|
||||
onFilter={this.onFilter}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="wrapper">
|
||||
<RowContainer manualHeight={`${admins.length * 50}px`}>
|
||||
{admins.map(user => {
|
||||
const element = (
|
||||
<Avatar
|
||||
size="small"
|
||||
role={getUserRole(user)}
|
||||
userName={user.displayName}
|
||||
source={user.avatar}
|
||||
<ToggleContentContainer>
|
||||
<div className="buttons_container">
|
||||
<Button
|
||||
className="button_style"
|
||||
size="medium"
|
||||
primary={true}
|
||||
label="Set people admin"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onShowGroupSelector}
|
||||
/>
|
||||
<div style={{ right: 180 }} className="advanced-selector">
|
||||
<AdvancedSelector
|
||||
displayType="dropdown"
|
||||
isOpen={showSelector}
|
||||
placeholder="placeholder"
|
||||
options={options}
|
||||
onSearchChanged={this.onSearchUsers}
|
||||
//groups={groups}
|
||||
isMultiSelect={true}
|
||||
buttonLabel="Add members"
|
||||
onSelect={this.onSelect}
|
||||
onCancel={this.onShowGroupSelector}
|
||||
onAddNewClick={() => console.log("onAddNewClick")}
|
||||
selectAllLabel="selectorSelectAllText"
|
||||
selectedOptions={selectedOptions}
|
||||
/>
|
||||
);
|
||||
const nameColor =
|
||||
user.status === "pending" ? "#A3A9AE" : "#333333";
|
||||
</div>
|
||||
|
||||
return (
|
||||
<Row
|
||||
key={user.id}
|
||||
status={user.status}
|
||||
data={user}
|
||||
element={element}
|
||||
>
|
||||
<RowContent disableSideInfo={true}>
|
||||
<Link
|
||||
containerWidth="120px"
|
||||
type="page"
|
||||
title={user.displayName}
|
||||
isBold={true}
|
||||
fontSize={15}
|
||||
color={nameColor}
|
||||
href={user.profileUrl}
|
||||
>
|
||||
{user.displayName}
|
||||
</Link>
|
||||
<div style={{ maxWidth: 120 }} />
|
||||
<Button
|
||||
size="medium"
|
||||
primary={true}
|
||||
label="Set portal admin"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onShowFullAdminGroupSelector}
|
||||
/>
|
||||
<div style={{ right: 160 }} className="advanced-selector">
|
||||
<AdvancedSelector
|
||||
displayType="dropdown"
|
||||
isOpen={showFullAdminSelector}
|
||||
placeholder="placeholder"
|
||||
options={options}
|
||||
onSearchChanged={this.onSearchUsers}
|
||||
//groups={groups}
|
||||
isMultiSelect={true}
|
||||
buttonLabel="Add members"
|
||||
onSelect={this.onSelectFullAdmin}
|
||||
onCancel={this.onShowFullAdminGroupSelector}
|
||||
onAddNewClick={() => console.log("onAddNewClick")}
|
||||
selectAllLabel="selectorSelectAllText"
|
||||
selectedOptions={selectedOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Text.Body>
|
||||
{user.isAdmin ? "Full access" : "People module admin"}
|
||||
</Text.Body>
|
||||
|
||||
{!user.isOwner ? (
|
||||
<IconButton
|
||||
className="remove_icon"
|
||||
size="16"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onChangeAdmin.bind(
|
||||
this,
|
||||
[user.id],
|
||||
false,
|
||||
"00000000-0000-0000-0000-000000000000"
|
||||
)}
|
||||
iconName={"CatalogTrashIcon"}
|
||||
isFill={true}
|
||||
isClickable={false}
|
||||
/>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
</RowContent>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</RowContainer>
|
||||
</div>
|
||||
|
||||
{countElements > 25 ? (
|
||||
<div className="wrapper">
|
||||
<Paging
|
||||
previousLabel={t("PreviousPage")}
|
||||
nextLabel={t("NextPage")}
|
||||
openDirection="top"
|
||||
countItems={this.countItems()}
|
||||
pageItems={this.pageItems()}
|
||||
displayItems={false}
|
||||
selectedPageItem={this.selectedPageItem()}
|
||||
selectedCountItem={this.selectedCountItem()}
|
||||
onSelectPage={this.onChangePage}
|
||||
onSelectCount={this.onChangePageSize}
|
||||
previousAction={this.onPrevClick}
|
||||
nextAction={this.onNextClick}
|
||||
disablePrevious={!filter.hasPrev()}
|
||||
disableNext={!filter.hasNext()}
|
||||
<FilterInput
|
||||
className="filter_container"
|
||||
getFilterData={() => []}
|
||||
getSortData={this.getSortData}
|
||||
onFilter={this.onFilter}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</ToggleContentContainer>
|
||||
</>
|
||||
|
||||
{admins.length > 0 ? (
|
||||
<div className="wrapper">
|
||||
<RowContainer manualHeight={`${admins.length * 50}px`}>
|
||||
{admins.map(user => {
|
||||
const element = (
|
||||
<Avatar
|
||||
size="small"
|
||||
role={getUserRole(user)}
|
||||
userName={user.displayName}
|
||||
source={user.avatar}
|
||||
/>
|
||||
);
|
||||
const nameColor =
|
||||
user.status === "pending" ? "#A3A9AE" : "#333333";
|
||||
|
||||
return (
|
||||
<Row
|
||||
key={user.id}
|
||||
status={user.status}
|
||||
data={user}
|
||||
element={element}
|
||||
>
|
||||
<RowContent disableSideInfo={true}>
|
||||
<Link
|
||||
containerWidth="120px"
|
||||
type="page"
|
||||
title={user.displayName}
|
||||
isBold={true}
|
||||
fontSize={15}
|
||||
color={nameColor}
|
||||
href={user.profileUrl}
|
||||
>
|
||||
{user.displayName}
|
||||
</Link>
|
||||
<div style={{ maxWidth: 120 }} />
|
||||
|
||||
<Text.Body>
|
||||
{user.isAdmin
|
||||
? "Full access"
|
||||
: "People module admin"}
|
||||
</Text.Body>
|
||||
|
||||
{!user.isOwner ? (
|
||||
<IconButton
|
||||
className="remove_icon"
|
||||
size="16"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onChangeAdmin.bind(
|
||||
this,
|
||||
[user.id],
|
||||
false,
|
||||
"00000000-0000-0000-0000-000000000000"
|
||||
)}
|
||||
iconName={"CatalogTrashIcon"}
|
||||
isFill={true}
|
||||
isClickable={false}
|
||||
/>
|
||||
) : (
|
||||
<div />
|
||||
)}
|
||||
</RowContent>
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</RowContainer>
|
||||
</div>
|
||||
) : countElements > 25 ? (
|
||||
<div className="wrapper">
|
||||
<Paging
|
||||
previousLabel={t("PreviousPage")}
|
||||
nextLabel={t("NextPage")}
|
||||
openDirection="top"
|
||||
countItems={this.countItems()}
|
||||
pageItems={this.pageItems()}
|
||||
displayItems={false}
|
||||
selectedPageItem={this.selectedPageItem()}
|
||||
selectedCountItem={this.selectedCountItem()}
|
||||
onSelectPage={this.onChangePage}
|
||||
onSelectCount={this.onChangePageSize}
|
||||
previousAction={this.onPrevClick}
|
||||
nextAction={this.onNextClick}
|
||||
disablePrevious={!filter.hasPrev()}
|
||||
disableNext={!filter.hasNext()}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc="products/people/images/empty_screen_filter.png"
|
||||
imageAlt="Empty Screen Filter image"
|
||||
headerText={t("NotFoundTitle")}
|
||||
descriptionText={t("NotFoundDescription")}
|
||||
buttons={
|
||||
<>
|
||||
<Icons.CrossIcon
|
||||
size="small"
|
||||
style={{ marginRight: "4px" }}
|
||||
/>
|
||||
<Link
|
||||
type="action"
|
||||
isHovered={true}
|
||||
onClick={this.onResetFilter}
|
||||
>
|
||||
{t("ClearButton")}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</ToggleContentContainer>
|
||||
</>
|
||||
)}
|
||||
</AdminsContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -522,6 +578,8 @@ AdminsSettings.propTypes = {
|
||||
options: PropTypes.arrayOf(PropTypes.object)
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, { changeAdmins, fetchPeople })(
|
||||
withRouter(AdminsSettings)
|
||||
);
|
||||
export default connect(mapStateToProps, {
|
||||
changeAdmins,
|
||||
fetchPeople,
|
||||
getUpdateListAdmin
|
||||
})(withRouter(AdminsSettings));
|
||||
|
@ -5,18 +5,39 @@ import { withRouter } from "react-router";
|
||||
import i18n from "../../../i18n";
|
||||
import { I18nextProvider, withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { getPortalOwner } from "../../../../../../store/settings/actions";
|
||||
import {
|
||||
getPortalOwner,
|
||||
getUsersOptions
|
||||
} from "../../../../../../store/settings/actions";
|
||||
import {
|
||||
Text,
|
||||
Avatar,
|
||||
Link,
|
||||
toastr,
|
||||
Button,
|
||||
RequestLoader
|
||||
RequestLoader,
|
||||
AdvancedSelector,
|
||||
Loader
|
||||
} from "asc-web-components";
|
||||
import { isArrayEqual } from "../../../utils/isArrayEqual";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
|
||||
const OwnerContainer = styled.div`
|
||||
.link_style {
|
||||
margin-right: 16px;
|
||||
}
|
||||
.text-body_wrapper {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.advanced-selector {
|
||||
position: relative;
|
||||
}
|
||||
.text-body_inline {
|
||||
display: inline-flex;
|
||||
}
|
||||
.button_offset {
|
||||
margin-right: 16px;
|
||||
}
|
||||
`;
|
||||
const HeaderContainer = styled.div`
|
||||
margin: 40px 0 16px 0;
|
||||
`;
|
||||
@ -60,44 +81,85 @@ class PureOwnerSettings extends Component {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isLoading: false
|
||||
isLoading: false,
|
||||
showSelector: false,
|
||||
options: [],
|
||||
allOptions: [],
|
||||
showLoader: true,
|
||||
selectedOwner: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { owner, getPortalOwner, ownerId } = this.props;
|
||||
const {
|
||||
owner,
|
||||
getPortalOwner,
|
||||
ownerId,
|
||||
options,
|
||||
getUsersOptions
|
||||
} = this.props;
|
||||
|
||||
if (isEmpty(owner, true)) {
|
||||
this.onLoading(true);
|
||||
getPortalOwner(ownerId)
|
||||
.catch(error => {
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(() => this.onLoading(false));
|
||||
.finally(() => this.setState({ showLoader: false }));
|
||||
}
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { owner, ownerId } = this.props;
|
||||
if (
|
||||
ownerId === nextProps.ownerId &&
|
||||
this.state.isLoading === nextState.isLoading &&
|
||||
isArrayEqual(owner, nextProps.owner)
|
||||
) {
|
||||
return false;
|
||||
if (isEmpty(options, true)) {
|
||||
getUsersOptions()
|
||||
.catch(error => {
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(() =>
|
||||
this.setState({
|
||||
showLoader: false,
|
||||
allOptions: this.props.options
|
||||
})
|
||||
);
|
||||
}
|
||||
return true;
|
||||
this.setState({ showLoader: false });
|
||||
}
|
||||
|
||||
onChangeOwner = () => {
|
||||
toastr.warning("onChangeOwner");
|
||||
const { t, owner } = this.props;
|
||||
toastr.success(t("DnsChangeMsg", { email: owner.email }));
|
||||
};
|
||||
|
||||
onLoading = status => this.setState({ isLoading: status });
|
||||
|
||||
onShowSelector = status => {
|
||||
this.setState({
|
||||
options: this.props.options,
|
||||
showSelector: status
|
||||
});
|
||||
};
|
||||
|
||||
onSearchUsers = template => {
|
||||
const options = this.filterUserSelectorOptions(
|
||||
this.state.allOptions,
|
||||
template
|
||||
);
|
||||
this.setState({ options: options });
|
||||
};
|
||||
|
||||
onSelect = selected => {
|
||||
this.onShowSelector(false);
|
||||
this.setState({ selectedOwner: selected });
|
||||
};
|
||||
|
||||
filterUserSelectorOptions = (options, template) =>
|
||||
options.filter(option => option.label.indexOf(template) > -1);
|
||||
|
||||
render() {
|
||||
const { t, owner } = this.props;
|
||||
const { isLoading } = this.state;
|
||||
const {
|
||||
isLoading,
|
||||
showLoader,
|
||||
showSelector,
|
||||
options,
|
||||
selectedOwner
|
||||
} = this.state;
|
||||
|
||||
const OwnerOpportunities = t("AccessRightsOwnerOpportunities").split("|");
|
||||
|
||||
@ -105,59 +167,110 @@ class PureOwnerSettings extends Component {
|
||||
|
||||
return (
|
||||
<>
|
||||
<RequestLoader
|
||||
visible={isLoading}
|
||||
zIndex={256}
|
||||
loaderSize={16}
|
||||
loaderColor={"#999"}
|
||||
label={`${t("LoadingProcessing")} ${t("LoadingDescription")}`}
|
||||
fontSize={12}
|
||||
fontColor={"#999"}
|
||||
className="page_loader"
|
||||
/>
|
||||
<HeaderContainer>
|
||||
<Text.Body fontSize={18}>{t("PortalOwner")}</Text.Body>
|
||||
</HeaderContainer>
|
||||
|
||||
<BodyContainer>
|
||||
<AvatarContainer>
|
||||
<Avatar
|
||||
className="avatar_wrapper"
|
||||
size="big"
|
||||
role="owner"
|
||||
userName={owner.userName}
|
||||
source={owner.avatar}
|
||||
{showLoader ? (
|
||||
<Loader className="pageLoader" type="rombs" size={40} />
|
||||
) : (
|
||||
<OwnerContainer>
|
||||
<RequestLoader
|
||||
visible={isLoading}
|
||||
zIndex={256}
|
||||
loaderSize={16}
|
||||
loaderColor={"#999"}
|
||||
label={`${t("LoadingProcessing")} ${t("LoadingDescription")}`}
|
||||
fontSize={12}
|
||||
fontColor={"#999"}
|
||||
className="page_loader"
|
||||
/>
|
||||
<div className="avatar_body">
|
||||
<Text.Body className="avatar_text" fontSize={16} isBold={true}>
|
||||
{owner.displayName}
|
||||
</Text.Body>
|
||||
{owner.groups &&
|
||||
owner.groups.map(group => (
|
||||
<Link fontSize={12} key={group.id} href={owner.profileUrl}>
|
||||
{group.name}
|
||||
</Link>
|
||||
))}
|
||||
<HeaderContainer>
|
||||
<Text.Body fontSize={18}>{t("PortalOwner")}</Text.Body>
|
||||
</HeaderContainer>
|
||||
|
||||
<BodyContainer>
|
||||
<AvatarContainer>
|
||||
<Avatar
|
||||
className="avatar_wrapper"
|
||||
size="big"
|
||||
role="owner"
|
||||
userName={owner.userName}
|
||||
source={owner.avatar}
|
||||
/>
|
||||
<div className="avatar_body">
|
||||
<Text.Body
|
||||
className="avatar_text"
|
||||
fontSize={16}
|
||||
isBold={true}
|
||||
>
|
||||
{owner.displayName}
|
||||
</Text.Body>
|
||||
{owner.groups &&
|
||||
owner.groups.map(group => (
|
||||
<Link
|
||||
fontSize={12}
|
||||
key={group.id}
|
||||
href={owner.profileUrl}
|
||||
>
|
||||
{group.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</AvatarContainer>
|
||||
<ProjectsBody>
|
||||
<Text.Body className="portal_owner" fontSize={12}>
|
||||
{t("AccessRightsOwnerCan")}:
|
||||
</Text.Body>
|
||||
<Text.Body fontSize={12}>
|
||||
{OwnerOpportunities.map((item, key) => (
|
||||
<li key={key}>{item};</li>
|
||||
))}
|
||||
</Text.Body>
|
||||
</ProjectsBody>
|
||||
</BodyContainer>
|
||||
|
||||
<Text.Body fontSize={12} className="text-body_wrapper">
|
||||
{t("AccessRightsChangeOwnerText")}
|
||||
</Text.Body>
|
||||
|
||||
<Link
|
||||
className="link_style"
|
||||
isHovered={true}
|
||||
onClick={this.onShowSelector.bind(this, !showSelector)}
|
||||
>
|
||||
{selectedOwner ? selectedOwner.label : t("ChooseOwner")}
|
||||
</Link>
|
||||
|
||||
<Button
|
||||
className="button_offset"
|
||||
size="medium"
|
||||
primary={true}
|
||||
label="Change portal owner"
|
||||
isDisabled={!isLoading ? selectedOwner === null : false}
|
||||
onClick={this.onChangeOwner}
|
||||
/>
|
||||
<Text.Body
|
||||
className="text-body_inline"
|
||||
fontSize={12}
|
||||
color="#A3A9AE"
|
||||
>
|
||||
{t("AccessRightsChangeOwnerConfirmText")}
|
||||
</Text.Body>
|
||||
|
||||
<div className="advanced-selector">
|
||||
<AdvancedSelector
|
||||
displayType="dropdown"
|
||||
isOpen={showSelector}
|
||||
placeholder="placeholder"
|
||||
options={options}
|
||||
onSearchChanged={this.onSearchUsers}
|
||||
//groups={groups}
|
||||
buttonLabel="Add members"
|
||||
onSelect={this.onSelect}
|
||||
onCancel={this.onShowSelector.bind(this, false)}
|
||||
onAddNewClick={() => console.log("onAddNewClick")}
|
||||
selectAllLabel="selectorSelectAllText"
|
||||
/>
|
||||
</div>
|
||||
</AvatarContainer>
|
||||
<ProjectsBody>
|
||||
<Text.Body className="portal_owner" fontSize={12}>
|
||||
{t("AccessRightsOwnerCan")}:
|
||||
</Text.Body>
|
||||
<Text.Body fontSize={12}>
|
||||
{OwnerOpportunities.map((item, key) => (
|
||||
<li key={key}>{item};</li>
|
||||
))}
|
||||
</Text.Body>
|
||||
</ProjectsBody>
|
||||
</BodyContainer>
|
||||
<Button
|
||||
size="medium"
|
||||
primary={true}
|
||||
label="Change portal owner"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onChangeOwner}
|
||||
/>
|
||||
</OwnerContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -178,9 +291,12 @@ const OwnerSettings = props => {
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const { owner, users } = state.settings.security.accessRight;
|
||||
|
||||
return {
|
||||
ownerId: state.auth.settings.ownerId,
|
||||
owner: state.settings.security.accessRight.owner
|
||||
owner,
|
||||
options: users
|
||||
};
|
||||
}
|
||||
|
||||
@ -192,6 +308,6 @@ OwnerSettings.propTypes = {
|
||||
owner: PropTypes.object
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, { getPortalOwner })(
|
||||
export default connect(mapStateToProps, { getPortalOwner, getUsersOptions })(
|
||||
withRouter(OwnerSettings)
|
||||
);
|
||||
|
@ -58,8 +58,13 @@
|
||||
"LogoDocsEditor": "Logo for the editors header",
|
||||
"ChangeLogoButton": "Change Logo",
|
||||
"BrowserNoCanvasSupport": "Your browser does not support the HTML5 canvas tag.",
|
||||
|
||||
|
||||
"AccessRightsChangeOwnerText": "To change the Portal owner please choose the Name of the new Portal owner below.",
|
||||
"ChooseOwner": "Choose owner",
|
||||
"DnsChangeMsg": "A link to confirm the operation has been sent to {{email}} (the email address of the portal owner).",
|
||||
"AccessRightsChangeOwnerConfirmText": "Changes will be applied after the confirmation via email.",
|
||||
"NotFoundTitle": "No results matching your search could be found",
|
||||
"NotFoundDescription": "No people matching your filter can be displayed in this section. Please select other filter options or clear filter to view all the people in this section.",
|
||||
"ClearButton": "Reset filter",
|
||||
|
||||
|
||||
"ViewProfilesAndGroups": "View profiles and groups",
|
||||
|
@ -57,6 +57,13 @@
|
||||
"LogoDocsEditor": "Логотип для шапки редакторов",
|
||||
"ChangeLogoButton": "Сменить логотип",
|
||||
"BrowserNoCanvasSupport": "Ваш браузер не поддерживает тег HTML5 canvas.",
|
||||
"AccessRightsChangeOwnerText": "Чтобы сменить владельца портала, выберите имя нового владельца портала ниже.",
|
||||
"ChooseOwner": "Выбрать владельца",
|
||||
"DnsChangeMsg": "Ссылка для подтверждения операции была отправлена на {{email}} (адрес электронной почты владельца портала).",
|
||||
"AccessRightsChangeOwnerConfirmText": "Изменения будут применены после подтверждения по электронной почте.",
|
||||
"NotFoundTitle": "Результатов, соответствующих заданным критериям, не найдено",
|
||||
"NotFoundDescription": "В данном разделе нет людей, соответствующих фильтру. Пожалуйста, выберите другие параметры или очистите фильтр, чтобы просмотреть всех людей в этом разделе.",
|
||||
"ClearButton": "Сбросить фильтр",
|
||||
|
||||
|
||||
"ViewProfilesAndGroups": "Просматривать профили и группы",
|
||||
|
@ -27,7 +27,8 @@
|
||||
"PasswordRecoveryTitle",
|
||||
"MessageSendPasswordRecoveryInstructionsOnEmail",
|
||||
"RememberHelper",
|
||||
"LoadingDescription"
|
||||
"LoadingDescription",
|
||||
"CookieSettingsTitle"
|
||||
]
|
||||
},
|
||||
"Confirm": {
|
||||
@ -52,7 +53,9 @@
|
||||
"DeleteProfileConfirmation",
|
||||
"DeleteProfileConfirmationInfo",
|
||||
"ChangePasswordSuccess",
|
||||
"Remember"
|
||||
"Remember",
|
||||
"ConfirmOwnerPortalTitle",
|
||||
"ConfirmOwnerPortalSuccessMessage"
|
||||
]
|
||||
},
|
||||
"Settings": {
|
||||
@ -94,7 +97,10 @@
|
||||
"RestoreDefaultButton",
|
||||
"SuccessfullySaveGreetingSettingsMessage",
|
||||
"ChangeLogoButton",
|
||||
"Employees"
|
||||
"Employees",
|
||||
"AccessRightsChangeOwnerText",
|
||||
"DnsChangeMsg",
|
||||
"AccessRightsChangeOwnerConfirmText"
|
||||
],
|
||||
"FeedResource": [
|
||||
"ProjectsProduct",
|
||||
@ -122,6 +128,9 @@
|
||||
"UserControlsCommonResource": [
|
||||
"NextPage",
|
||||
"PreviousPage"
|
||||
],
|
||||
"ResourceJS": [
|
||||
"ChooseOwner"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -6,15 +6,23 @@ import Filter from "./filter";
|
||||
export const SET_USERS = "SET_USERS";
|
||||
export const SET_ADMINS = "SET_ADMINS";
|
||||
export const SET_OWNER = "SET_OWNER";
|
||||
export const SET_OPTIONS = "SET_OPTIONS";
|
||||
export const SET_FILTER = "SET_FILTER";
|
||||
export const SET_LOGO_TEXT = "SET_LOGO_TEXT";
|
||||
export const SET_LOGO_SIZES = "SET_LOGO_SIZES";
|
||||
export const SET_LOGO_URLS = "SET_LOGO_URLS";
|
||||
|
||||
export function setUsers(options) {
|
||||
export function setOptions(options) {
|
||||
return {
|
||||
type: SET_OPTIONS,
|
||||
options
|
||||
};
|
||||
}
|
||||
|
||||
export function setUsers(users) {
|
||||
return {
|
||||
type: SET_USERS,
|
||||
options
|
||||
users
|
||||
};
|
||||
}
|
||||
|
||||
@ -81,7 +89,7 @@ export function changeAdmins(userIds, productId, isAdmin, filter) {
|
||||
const newOptions = getSelectorOptions(options);
|
||||
filterData.total = admins.total;
|
||||
|
||||
dispatch(setUsers(newOptions));
|
||||
dispatch(setOptions(newOptions));
|
||||
dispatch(setAdmins(admins.items));
|
||||
dispatch(setFilter(filterData));
|
||||
})
|
||||
@ -102,45 +110,66 @@ export function fetchPeople(filter) {
|
||||
}
|
||||
|
||||
return dispatch => {
|
||||
return axios
|
||||
.all([api.getUserList(filterData), api.getListAdmins(filterData)])
|
||||
.then(
|
||||
axios.spread((users, admins) => {
|
||||
const options = getUserOptions(users.items, admins.items);
|
||||
const newOptions = getSelectorOptions(options);
|
||||
filterData.total = admins.total;
|
||||
return axios.all([api.getUserList(), api.getListAdmins(filterData)]).then(
|
||||
axios.spread((users, admins) => {
|
||||
const options = getUserOptions(users.items, admins.items);
|
||||
const newOptions = getSelectorOptions(options);
|
||||
const usersOptions = getSelectorOptions(users.items);
|
||||
filterData.total = admins.total;
|
||||
|
||||
dispatch(setAdmins(admins.items));
|
||||
dispatch(setUsers(newOptions));
|
||||
dispatch(setFilter(filterData));
|
||||
})
|
||||
);
|
||||
dispatch(setUsers(usersOptions));
|
||||
dispatch(setAdmins(admins.items));
|
||||
dispatch(setOptions(newOptions));
|
||||
dispatch(setFilter(filterData));
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export function getUpdateListAdmin(filter) {
|
||||
let filterData = filter && filter.clone();
|
||||
if (!filterData) {
|
||||
filterData = Filter.getDefault();
|
||||
}
|
||||
return dispatch => {
|
||||
return api.getListAdmins(filterData).then(admins => {
|
||||
filterData.total = admins.total;
|
||||
|
||||
dispatch(setAdmins(admins.items));
|
||||
dispatch(setFilter(filterData));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function getUsersOptions() {
|
||||
return dispatch => {
|
||||
return api.getUserList().then(users => {
|
||||
const usersOptions = getSelectorOptions(users.items);
|
||||
dispatch(setUsers(usersOptions));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function getWhiteLabelLogoText() {
|
||||
return dispatch => {
|
||||
return api.getLogoText()
|
||||
.then(res => {
|
||||
return api.getLogoText().then(res => {
|
||||
dispatch(setLogoText(res));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function getWhiteLabelLogoSizes() {
|
||||
return dispatch => {
|
||||
return api.getLogoSizes()
|
||||
.then(res => {
|
||||
return api.getLogoSizes().then(res => {
|
||||
dispatch(setLogoSizes(res));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export function getWhiteLabelLogoUrls() {
|
||||
return dispatch => {
|
||||
return api.getLogoUrls()
|
||||
.then(res => {
|
||||
return api.getLogoUrls().then(res => {
|
||||
dispatch(setLogoUrls(Object.values(res)));
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { SET_USERS, SET_ADMINS, SET_OWNER, SET_FILTER, SET_LOGO_TEXT, SET_LOGO_SIZES, SET_LOGO_URLS } from "./actions";
|
||||
import { SET_USERS, SET_ADMINS, SET_OWNER, SET_OPTIONS, SET_FILTER, SET_LOGO_TEXT, SET_LOGO_SIZES, SET_LOGO_URLS } from "./actions";
|
||||
import Filter from "./filter";
|
||||
|
||||
const initialState = {
|
||||
@ -13,6 +13,7 @@ const initialState = {
|
||||
security: {
|
||||
accessRight: {
|
||||
options: [],
|
||||
users: [],
|
||||
admins: [],
|
||||
owner: {},
|
||||
filter: Filter.getDefault()
|
||||
@ -22,7 +23,7 @@ const initialState = {
|
||||
|
||||
const peopleReducer = (state = initialState, action) => {
|
||||
switch (action.type) {
|
||||
case SET_USERS:
|
||||
case SET_OPTIONS:
|
||||
return Object.assign({}, state, {
|
||||
security: Object.assign({}, state.security, {
|
||||
accessRight: Object.assign({}, state.security.accessRight, {
|
||||
@ -30,6 +31,14 @@ const peopleReducer = (state = initialState, action) => {
|
||||
})
|
||||
})
|
||||
});
|
||||
case SET_USERS:
|
||||
return Object.assign({}, state, {
|
||||
security: Object.assign({}, state.security, {
|
||||
accessRight: Object.assign({}, state.security.accessRight, {
|
||||
users: action.users
|
||||
})
|
||||
})
|
||||
});
|
||||
case SET_ADMINS:
|
||||
return Object.assign({}, state, {
|
||||
security: Object.assign({}, state.security, {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-components",
|
||||
"version": "1.0.170",
|
||||
"version": "1.0.177",
|
||||
"description": "Ascensio System SIA component library",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "dist/asc-web-components.js",
|
||||
|
@ -1,43 +1,48 @@
|
||||
# Avatar Editor
|
||||
|
||||
## Usage
|
||||
Used to display user avatar editor on page.
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { AvatarEditor } from 'asc-web-components';
|
||||
import { AvatarEditor } from "asc-web-components";
|
||||
```
|
||||
|
||||
#### Description
|
||||
|
||||
Required to display user avatar editor on page.
|
||||
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
```jsx
|
||||
<AvatarEditor
|
||||
visible={this.state.isOpen}
|
||||
onClose={() =>{}}
|
||||
onSave={(data) =>{console.log(data.isUpdate, data.croppedImageInfo)}}
|
||||
onImageChange={(data) =>{console.log(croppedImageInfo)}
|
||||
/>
|
||||
visible={true}
|
||||
onClose={() => {}}
|
||||
onSave={() => {})}
|
||||
onDeleteImage={() => {})}
|
||||
onImageChange={() => {})}
|
||||
onLoadFile={() => {}}
|
||||
headerLabel="Edit Photo"
|
||||
chooseFileLabel="Drop files here, or click to select files"
|
||||
saveButtonLabel="Save"
|
||||
maxSizeFileError="Maximum file size exceeded"
|
||||
unknownTypeError="Unknown image file type"
|
||||
unknownError="Error"
|
||||
displayType="auto"
|
||||
/>
|
||||
```
|
||||
|
||||
#### Properties
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------ | -------- | :------: | -------------------------------- | ----------------------------- | ----------------------------------------------------- |
|
||||
| `visible` | `bool` | - | | `false` | Display avatar editor or not |
|
||||
| `image` | `string/file`| - | | | The URL of the image to use, or a File |
|
||||
| `accept` | `array` | - | | `['image/png', 'image/jpeg']` | Accepted file types |
|
||||
| `displayType` | `oneOf` | - | `auto`, `modal`, `aside` | `auto` | |
|
||||
| `chooseFileLabel` | `string` | - | | `Choose a file` | |
|
||||
| `headerLabel` | `string` | - | | `Edit Photo` | |
|
||||
| `saveButtonLabel` | `string` | - | | `Save` | |
|
||||
| `maxSizeFileError` | `string` | - | | `Maximum file size exceeded` | |
|
||||
| `unknownTypeError` | `string` | - | | `Unknown image file type` | |
|
||||
| `unknownError` | `string` | - | | `Error` | |
|
||||
| `maxSize` | `number` | - | | `1` | Max size of image |
|
||||
| `onSave` | `function` | - | | | |
|
||||
| `onClose` | `function` | - | | | |
|
||||
| `onDeleteImage` | `function` | - | | | |
|
||||
| `onLoadFile` | `function` | - | | | |
|
||||
| `onImageChange` | `function` | - | | | |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------ | :-------------: | :------: | :----------------------: | :---------------------------: | ---------------------------------------- |
|
||||
| `visible` | `bool` | - | - | `false` | Display avatar editor |
|
||||
| `image` | `string`,`file` | - | - | - | The URL of the image to use, or a File |
|
||||
| `accept` | `array` | - | - | `['image/png', 'image/jpeg']` | Accepted file types |
|
||||
| `displayType` | `oneOf` | - | `auto`, `modal`, `aside` | `auto` | Display type |
|
||||
| `chooseFileLabel` | `string` | - | - | `Choose a file` | Translation string for file selection |
|
||||
| `headerLabel` | `string` | - | - | `Edit Photo` | Translation string for title |
|
||||
| `saveButtonLabel` | `string` | - | - | `Save` | Translation string for save button |
|
||||
| `maxSizeFileError` | `string` | - | - | `Maximum file size exceeded` | Translation string for size warning |
|
||||
| `unknownTypeError` | `string` | - | - | `Unknown image file type` | Translation string for file type warning |
|
||||
| `unknownError` | `string` | - | - | `Error` | Translation string for warning |
|
||||
| `maxSize` | `number` | - | - | `1` | Max size of image |
|
||||
| `onSave` | `function` | - | - | - | Save event |
|
||||
| `onClose` | `function` | - | - | - | Closing event |
|
||||
| `onDeleteImage` | `function` | - | - | - | Image deletion event |
|
||||
| `onLoadFile` | `function` | - | - | - | Image upload event |
|
||||
| `onImageChange` | `function` | - | - | - | Image change event |
|
||||
|
@ -1,29 +1,33 @@
|
||||
# Avatar
|
||||
|
||||
## Usage
|
||||
Used to display an avatar or brand.
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { Avatar } from "asc-web-components";
|
||||
```
|
||||
|
||||
#### Description
|
||||
|
||||
Required to display user avatar on page.
|
||||
|
||||
If you want to create an avatar with initials, only *first letter of first two words* of line is used.
|
||||
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
<Avatar size="max" role="admin" source="" userName="" editing={false} />
|
||||
```jsx
|
||||
<Avatar
|
||||
size="max"
|
||||
role="admin"
|
||||
source=""
|
||||
userName=""
|
||||
editing={false}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Properties
|
||||
If you want to create an avatar with initials, only _first letter of first two words_ of line is used.
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ---------- | -------- | :------: | --------------------------------- | -------- | ------------------------------------------ |
|
||||
| `size` | `oneOf` | - | `max`, `big`, `medium`, `small` | `medium` | Tells what size avatar should be displayed |
|
||||
| `role` | `oneOf` | - | `owner`, `admin`, `guest`, `user` | - | Adds a user role table |
|
||||
| `source` | `string` | - | - | - | Avatar image source |
|
||||
| `userName` | `string` | - | - | - | Need to create an avatar with initials |
|
||||
| `editing` | `bool` | - | - | `false` | Displays avatar edit layer |
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------ | :------: | :------: | :-------------------------------: | :----------: | -------------------------------------------------------- |
|
||||
| `size` | `oneOf` | - | `max`, `big`, `medium`, `small` | `medium` | Size of avatar |
|
||||
| `role` | `oneOf` | - | `owner`, `admin`, `guest`, `user` | `user` | Adds a user role table |
|
||||
| `source` | `string` | - | - | - | The address of the image for an image avatar |
|
||||
| `userName` | `string` | - | - | - | Need to create an avatar with initials |
|
||||
| `editing` | `bool` | - | - | `false` | Displays avatar edit layer |
|
||||
| `editLabel` | `string` | - | - | `Edit photo` | Label for editing layer |
|
||||
| `editAction` | `func` | - | - | - | Function called when the avatar change button is pressed |
|
||||
|
@ -1,20 +1,23 @@
|
||||
# Backdrop
|
||||
|
||||
#### Description
|
||||
|
||||
Background for displaying modal dialogs
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { Backdrop } from 'asc-web-components';
|
||||
|
||||
<Backdrop visible={false} />
|
||||
import { Backdrop } from "asc-web-components";
|
||||
```
|
||||
|
||||
#### Properties
|
||||
```jsx
|
||||
<Backdrop
|
||||
visible={true}
|
||||
zIndex={1}
|
||||
/>
|
||||
```
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| --------------- | ------------------------- | :------: | -------| ------- | ------------------------------------------------ |
|
||||
| `visible` | `bool` | | | false | Display or not |
|
||||
| `zIndex` | `number` | | | 100 | CSS z-index |
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| --------- | :------: | :------: | :----: | :-----: | -------------- |
|
||||
| `visible` | `bool` | - | - | `false` | Display or not |
|
||||
| `zIndex` | `number` | - | - | `100` | CSS z-index |
|
||||
|
@ -1,27 +1,37 @@
|
||||
# Badge
|
||||
|
||||
## Usage
|
||||
Used for buttons, numbers or status markers next to icons.
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { Badge } from 'asc-web-components';
|
||||
|
||||
<Badge />;
|
||||
import { Badge } from "asc-web-components";
|
||||
```
|
||||
|
||||
#### Description
|
||||
```jsx
|
||||
<Badge
|
||||
number={10}
|
||||
backgroundColor="#ED7309"
|
||||
color="#FFFFFF"
|
||||
fontSize="11px"
|
||||
fontWeight={800}
|
||||
borderRadius="11px"
|
||||
padding="0 5px"
|
||||
maxWidth="50px"
|
||||
onClick={() => {}}
|
||||
/>
|
||||
```
|
||||
|
||||
Notify badge
|
||||
### Properties
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ----------------- | -------- | :------: | ------ | --------- | --------------------- |
|
||||
| `number` | `number` | | | 0 | Number value |
|
||||
| `backgroundColor` | `string` | | | '#ED7309' | CSS background-color |
|
||||
| `color` | `string` | | | '#FFFFFF' | CSS color |
|
||||
| `fontSize` | `string` | | | '11px' | CSS font-size |
|
||||
| `fontWeight` | `number` | | | '800' | CSS font-weight |
|
||||
| `borderRadius` | `string` | | | '11px' | CSS border-radius |
|
||||
| `padding` | `string` | | | '0 5px' | CSS padding |
|
||||
| `maxWidth` | `string` | | | '50px' | CSS max-width |
|
||||
| `onClick` | `func` | | | | onClick event |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ----------------- | :------: | :------: | :----: | :-------: | -------------------- |
|
||||
| `number` | `number` | - | - | `0` | Number value |
|
||||
| `backgroundColor` | `string` | - | - | `#ED7309` | CSS background-color |
|
||||
| `color` | `string` | - | - | `#FFFFFF` | CSS color |
|
||||
| `fontSize` | `string` | - | - | `11px` | CSS font-size |
|
||||
| `fontWeight` | `number` | - | - | `800` | CSS font-weight |
|
||||
| `borderRadius` | `string` | - | - | `11px` | CSS border-radius |
|
||||
| `padding` | `string` | - | - | `0 5px` | CSS padding |
|
||||
| `maxWidth` | `string` | - | - | `50px` | CSS max-width |
|
||||
| `onClick` | `func` | - | - | - | onClick event |
|
||||
|
@ -1,30 +1,34 @@
|
||||
# Buttons: Button
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { Button } from 'asc-web-components';
|
||||
```
|
||||
|
||||
#### Description
|
||||
# Button
|
||||
|
||||
Button is used for a action on a page.
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
```js
|
||||
<Button size='base' isDisabled={false} onClick={() => alert('Button clicked')} label="OK" />
|
||||
import { Button } from "asc-web-components";
|
||||
```
|
||||
|
||||
#### Properties
|
||||
```jsx
|
||||
<Button
|
||||
size="base"
|
||||
isDisabled={false}
|
||||
onClick={() => alert("Button clicked")}
|
||||
label="OK"
|
||||
/>
|
||||
```
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------ | -------- | :------: | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `label` | `string` | - | - | - | Button text |
|
||||
| `primary` | `bool` | - | - | - | Tells when the button should be primary |
|
||||
| `size` | `oneOf` | - | `base`, `middle`, `big` | `base` | Size of button |
|
||||
| `scale` | `bool` | - | - | false | Scale width of button to 100% |
|
||||
| `isDisabled` | `bool` | - | - | - | Tells when the button should present a disabled state |
|
||||
| `isLoading` | `bool` | - | - | - | Tells when the button should show loader icon |
|
||||
| `onClick` | `func` | - | - | - | What the button will trigger when clicked |
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------ | :------: | :------: | :---------------------: | :-----: | ----------------------------------------------------- |
|
||||
| `label` | `string` | - | - | - | Button text |
|
||||
| `primary` | `bool` | - | - | `false` | Tells when the button should be primary |
|
||||
| `size` | `oneOf` | - | `base`, `middle`, `big` | `base` | Size of button |
|
||||
| `scale` | `bool` | - | - | `false` | Scale width of button to 100% |
|
||||
| `icon` | `node` | - | - | `null` | Icon node element |
|
||||
| `tabIndex` | `number` | - | - | `-1` | Button tab index |
|
||||
| `isHovered` | `bool` | - | - | `false` | Tells when the button should present a hovered state |
|
||||
| `isClicked` | `bool` | - | - | `false` | Tells when the button should present a clicked state |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Tells when the button should present a disabled state |
|
||||
| `isLoading` | `bool` | - | - | `false` | Tells when the button should show loader icon |
|
||||
| `onClick` | `func` | - | - | - | What the button will trigger when clicked |
|
||||
|
@ -1,14 +1,14 @@
|
||||
# Calendar
|
||||
|
||||
#### Description
|
||||
Used to display custom calendar
|
||||
|
||||
Custom calendar
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { Calendar } from "asc-web-components";
|
||||
```
|
||||
|
||||
```jsx
|
||||
<Calendar
|
||||
onChange={date => {
|
||||
console.log("Selected date:", date);
|
||||
@ -20,19 +20,19 @@ import { Calendar } from "asc-web-components";
|
||||
minDate={new Date("1970/01/01")}
|
||||
maxDate={new Date("3000/01/01")}
|
||||
locale="ru"
|
||||
/>;
|
||||
/>
|
||||
```
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------- | -------- | :------: | ------ | ------------------------ | ------------------------------------------------------------ |
|
||||
| `onChange` | `func` | - | - | - | Function called when the user select a day |
|
||||
| `isDisabled` | `bool` | - | - | - | Disabled react-calendar |
|
||||
| `themeColor` | `string` | - | - | `#ED7309` | Color of the selected day |
|
||||
| `selectedDate` | `date` | - | - | (today) | Selected date value |
|
||||
| `openToDate` | `date` | - | - | (today) | The beginning of a period that shall be displayed by default |
|
||||
| `minDate` | `date` | - | - | `new Date("1970/01/01")` | Minimum date that the user can select. |
|
||||
| `maxDate` | `date` | - | - | `new Date("3000/01/01")` | Maximum date that the user can select. |
|
||||
| `locale` | `string` | - | - | User's browser settings | Browser locale |
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------- | :------: | :------: | :-----------: | :-----------------------: | ------------------------------------------------------------ |
|
||||
| `onChange` | `func` | - | - | - | Function called when the user select a day |
|
||||
| `isDisabled` | `bool` | - | - | - | Disabled react-calendar |
|
||||
| `themeColor` | `string` | - | - | `#ED7309` | Color of the selected day |
|
||||
| `selectedDate` | `date` | - | - | `new Date()` | Selected date value |
|
||||
| `openToDate` | `date` | - | - | `new Date()` | The beginning of a period that shall be displayed by default |
|
||||
| `minDate` | `date` | - | - | `new Date("1970/01/01")` | Minimum date that the user can select. |
|
||||
| `maxDate` | `date` | - | - | `new Date("3000/01/01")` | Maximum date that the user can select. |
|
||||
| `locale` | `string` | - | - | `User's browser settings` | Browser locale |
|
||||
| `size` | `oneOf` | - | `base`, `big` | `base` | Calendar size |
|
||||
|
@ -1,26 +1,35 @@
|
||||
# Checkbox
|
||||
|
||||
#### Description
|
||||
|
||||
Custom checkbox input
|
||||
|
||||
#### Usage
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { Checkbox } from 'asc-web-components';
|
||||
|
||||
<Checkbox value="text" onChange={event => alert(event.target.value)}/>
|
||||
import { Checkbox } from "asc-web-components";
|
||||
```
|
||||
|
||||
#### Properties
|
||||
```jsx
|
||||
<Checkbox
|
||||
id="id"
|
||||
name="name"
|
||||
value="value"
|
||||
label="label"
|
||||
isChecked={false}
|
||||
isIndeterminate={false}
|
||||
isDisabled={false}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
```
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ---------------------- | -------- | :------: | ---------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| `id` | `string` | - | - | - | Used as HTML `id` property
|
||||
| `name` | `string` | - | - | - | Used as HTML `name` property
|
||||
| `value` | `string` | - | - | - | Value of the input
|
||||
| `label` | `string` | - | - | - | Label of the input
|
||||
| `isChecked` | `bool` | - | - | - | The checked property sets the checked state of a checkbox.
|
||||
| `isIndeterminate` | `bool` | - | - | - | If true, this state is shown as a rectangle in the checkbox
|
||||
| `isDisabled` | `bool` | - | - | - | Disables the Checkbox input
|
||||
| `onChange` | `func` | ✅ | - | - | Will be triggered whenever an CheckboxInput is clicked
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ----------------- | :------: | :------: | :----: | :-----: | ----------------------------------------------------------- |
|
||||
| `id` | `string` | - | - | - | Used as HTML `id` property |
|
||||
| `name` | `string` | - | - | - | Used as HTML `name` property |
|
||||
| `value` | `string` | - | - | - | Value of the input |
|
||||
| `label` | `string` | - | - | - | Label of the input |
|
||||
| `isChecked` | `bool` | - | - | `false` | The checked property sets the checked state of a checkbox |
|
||||
| `isIndeterminate` | `bool` | - | - | - | If true, this state is shown as a rectangle in the checkbox |
|
||||
| `isDisabled` | `bool` | - | - | - | Disables the Checkbox input |
|
||||
| `onChange` | `func` | ✅ | - | - | Will be triggered whenever an CheckboxInput is clicked |
|
||||
|
@ -1,9 +1,42 @@
|
||||
# ComboBox
|
||||
|
||||
#### Description
|
||||
|
||||
Custom combo box input
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { ComboBox } from "asc-web-components";
|
||||
```
|
||||
|
||||
```js
|
||||
const options = [
|
||||
{
|
||||
key: 1,
|
||||
icon: "CatalogEmployeeIcon", // optional item
|
||||
label: "Option 1",
|
||||
disabled: false, // optional item
|
||||
onClick: clickFunction // optional item
|
||||
}
|
||||
];
|
||||
```
|
||||
|
||||
```jsx
|
||||
<ComboBox
|
||||
options={options}
|
||||
isDisabled={false}
|
||||
selectedOption={{
|
||||
key: 0,
|
||||
label: "Select"
|
||||
}}
|
||||
dropDownMaxHeight={200}
|
||||
noBorder={false}
|
||||
scale={true}
|
||||
scaledOptions={true}
|
||||
size="content"
|
||||
onSelect={option => console.log("selected", option)}
|
||||
/>
|
||||
```
|
||||
|
||||
Options have options:
|
||||
|
||||
- key - Item key, may be a string or a number
|
||||
@ -39,7 +72,9 @@ const advancedOptions = (
|
||||
</DropDownItem>
|
||||
</>
|
||||
);
|
||||
```
|
||||
|
||||
```jsx
|
||||
<ComboBox
|
||||
options={[]} // An empty array will enable advancedOptions
|
||||
advancedOptions={advancedOptions}
|
||||
@ -54,55 +89,23 @@ const advancedOptions = (
|
||||
directionX="right"
|
||||
>
|
||||
<Icons.NavLogoIcon size="medium" key="comboIcon" />
|
||||
</ComboBox>;
|
||||
</ComboBox>
|
||||
```
|
||||
|
||||
#### Usage
|
||||
### Properties
|
||||
|
||||
```js
|
||||
import { ComboBox } from 'asc-web-components';
|
||||
|
||||
const options = [
|
||||
{
|
||||
key: 1,
|
||||
icon: 'CatalogEmployeeIcon', // optional item
|
||||
label: 'Option 1',
|
||||
disabled: false, // optional item
|
||||
onClick: clickFunction // optional item
|
||||
},
|
||||
...
|
||||
];
|
||||
|
||||
<ComboBox
|
||||
options={options}
|
||||
isDisabled={false}
|
||||
selectedOption={{
|
||||
key: 0,
|
||||
label: 'Select'
|
||||
}}
|
||||
dropDownMaxHeight={200}
|
||||
noBorder={false}
|
||||
scale={true}
|
||||
scaledOptions={true}
|
||||
size='content'
|
||||
onSelect={option => console.log('selected', option)}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------- | --------- | :------: | ------------------------------------------ | ------- | ----------------------------------------------------------- |
|
||||
| `options` | `array` | ✅ | - | - | Combo box options |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Indicates that component is disabled |
|
||||
| `noBorder` | `bool` | - | - | `false` | Indicates that component is displayed without borders |
|
||||
| `selectedOption` | `object` | ✅ | - | - | Selected option |
|
||||
| `onSelect` | `func` | - | - | - | Will be triggered whenever an ComboBox is selected option |
|
||||
| `dropDownMaxHeight` | `number` | - | - | - | Height of Dropdown |
|
||||
| `scaled` | `bool` | - | - | `true` | Indicates that component is scaled by parent |
|
||||
| `scaledOptions` | `bool` | - | - | `false` | Indicates that component`s options is scaled by ComboButton |
|
||||
| `size` | `oneOf` | - | `base`, `middle`, `big`, `huge`, `content` | `base` | Select component width, one of default |
|
||||
| `advancedOptions` | `element` | - | - | - | If you need display options not basic options |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------- | :-------: | :------: | :----------------------------------------: | :-----: | ----------------------------------------------------------- |
|
||||
| `options` | `array` | ✅ | - | - | Combo box options |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Indicates that component is disabled |
|
||||
| `noBorder` | `bool` | - | - | `false` | Indicates that component is displayed without borders |
|
||||
| `selectedOption` | `object` | ✅ | - | - | Selected option |
|
||||
| `onSelect` | `func` | - | - | - | Will be triggered whenever an ComboBox is selected option |
|
||||
| `dropDownMaxHeight` | `number` | - | - | - | Height of Dropdown |
|
||||
| `scaled` | `bool` | - | - | `true` | Indicates that component is scaled by parent |
|
||||
| `scaledOptions` | `bool` | - | - | `false` | Indicates that component`s options is scaled by ComboButton |
|
||||
| `size` | `oneOf` | - | `base`, `middle`, `big`, `huge`, `content` | `base` | Select component width, one of default |
|
||||
| `advancedOptions` | `element` | - | - | - | If you need display options not basic options |
|
||||
|
||||
## ComboButton
|
||||
|
||||
@ -113,7 +116,7 @@ const options = [
|
||||
To create designs using combobox logic, there is a child component ComboButton.
|
||||
This is an independent element that responds to changes in parameters and serves only to demonstrate set values.
|
||||
|
||||
```js
|
||||
```jsx
|
||||
<ComboButton
|
||||
noBorder={false}
|
||||
isDisabled={false}
|
||||
@ -132,19 +135,19 @@ This is an independent element that responds to changes in parameters and serves
|
||||
/>
|
||||
```
|
||||
|
||||
#### Properties
|
||||
### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------------- | -------- | :------: | -------------------------------- | ---------------- | -------------------------------------------------------- |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Indicates that component is disabled |
|
||||
| `noBorder` | `bool` | - | - | `false` | Indicates that component is displayed without borders |
|
||||
| `selectedOption` | `object` | - | - | - | Selected option |
|
||||
| `withOptions` | `bool` | - | - | `true` | Lets you style as ComboBox with options |
|
||||
| `optionsLength` | `number` | - | - | - | Lets you style as ComboBox with options |
|
||||
| `withAdvancedOptions` | `bool` | - | - | `false` | Lets you style as a ComboBox with advanced options |
|
||||
| `innerContainer` | `node` | - | - | - | Allows displaying third-party element inside ComboButton |
|
||||
| `innerContainerClassName` | `string` | - | - | `innerContainer` | Required to access third-party container |
|
||||
| `isOpen` | `bool` | - | - | `false` | Lets you style as ComboBox arrow |
|
||||
| `scaled` | `bool` | - | - | `false` | Indicates that component is scaled by parent |
|
||||
| `size` | `oneOf` | - | `base`, `...`, `huge`, `content` | `content` | Select component width, one of default |
|
||||
| `onClick` | `func` | - | - | - | Will be triggered whenever an ComboButton is clicked |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------------- | :------: | :------: | :------------------------------: | :--------------: | -------------------------------------------------------- |
|
||||
| `isDisabled` | `bool` | - | - | `false` | Indicates that component is disabled |
|
||||
| `noBorder` | `bool` | - | - | `false` | Indicates that component is displayed without borders |
|
||||
| `selectedOption` | `object` | - | - | - | Selected option |
|
||||
| `withOptions` | `bool` | - | - | `true` | Lets you style as ComboBox with options |
|
||||
| `optionsLength` | `number` | - | - | - | Lets you style as ComboBox with options |
|
||||
| `withAdvancedOptions` | `bool` | - | - | `false` | Lets you style as a ComboBox with advanced options |
|
||||
| `innerContainer` | `node` | - | - | - | Allows displaying third-party element inside ComboButton |
|
||||
| `innerContainerClassName` | `string` | - | - | `innerContainer` | Required to access third-party container |
|
||||
| `isOpen` | `bool` | - | - | `false` | Lets you style as ComboBox arrow |
|
||||
| `scaled` | `bool` | - | - | `false` | Indicates that component is scaled by parent |
|
||||
| `size` | `oneOf` | - | `base`, `...`, `huge`, `content` | `content` | Select component width, one of default |
|
||||
| `onClick` | `func` | - | - | - | Will be triggered whenever an ComboButton is clicked |
|
||||
|
@ -1,30 +1,29 @@
|
||||
# ContextMenu
|
||||
|
||||
## Usage
|
||||
ContextMenu is used for a call context actions on a page.
|
||||
|
||||
> Implemented as part of RowContainer component.
|
||||
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { ContextMenu } from "asc-web-components";
|
||||
```
|
||||
|
||||
#### Description
|
||||
|
||||
ContextMenu is used for a call context actions on a page.
|
||||
|
||||
Implemented as part of RowContainer component.
|
||||
|
||||
#### Usage
|
||||
```jsx
|
||||
<ContextMenu
|
||||
targetAreaId="rowContainer"
|
||||
options={[]}
|
||||
/>
|
||||
```
|
||||
|
||||
For use within separate component it is necessary to determine active zone and events for calling and transferring options in menu.
|
||||
|
||||
In particular case, state is created containing options for particular Row element and passed to component when called.
|
||||
|
||||
```js
|
||||
<ContextMenu targetAreaId="rowContainer" options={[]} />
|
||||
```
|
||||
### Properties
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------- | -------- | :------: | ------ | ------- | ------------------------ |
|
||||
| `options` | `array` | - | - | [] | DropDownItems collection |
|
||||
| `targetAreaId` | `string` | - | - | - | Id of container apply to |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------- | :------: | :------: | :----: | :-----: | ------------------------ |
|
||||
| `options` | `array` | - | - | `[ ]` | DropDownItems collection |
|
||||
| `targetAreaId` | `string` | - | - | - | Id of container apply to |
|
||||
|
@ -27,16 +27,18 @@ import { DatePicker } from "asc-web-components";
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------- | -------- | :------: | ------ | ------------------------ | ------------------------------------------ |
|
||||
| `onChange` | `func` | - | - | - | Function called when the user select a day |
|
||||
| `isDisabled` | `bool` | - | - | - | Disabled react-calendar |
|
||||
| `themeColor` | `string` | - | - | `#ED7309` | Color of the selected day |
|
||||
| `selectedDate` | `date` | - | - | (today) | Selected date value |
|
||||
| `minDate` | `date` | - | - | `new Date("1970/01/01")` | Minimum date that the user can select. |
|
||||
| `maxDate` | `date` | - | - | `new Date("3000/01/01")` | Maximum date that the user can select. |
|
||||
| `locale` | `string` | - | - | User's browser settings | Browser locale |
|
||||
| `scaled` | `bool` | - | | - | Selected calendar size |
|
||||
| `isReadOnly` | `bool` | - | | - | Set input type is read only |
|
||||
| `hasError` | `bool` | - | | - | Set error date-input style |
|
||||
| `isOpen` | `bool` | - | | - | Opens calendar |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ----------------------- | -------- | :------: | ----------------------- | ------------------------ | -------------------------------------------------- |
|
||||
| `onChange` | `func` | - | - | - | Function called when the user select a day |
|
||||
| `isDisabled` | `bool` | - | - | - | Disabled react-calendar |
|
||||
| `themeColor` | `string` | - | - | `#ED7309` | Color of the selected day |
|
||||
| `selectedDate` | `date` | - | - | (today) | Selected date value |
|
||||
| `minDate` | `date` | - | - | `new Date("1970/01/01")` | Minimum date that the user can select. |
|
||||
| `maxDate` | `date` | - | - | `new Date("3000/01/01")` | Maximum date that the user can select. |
|
||||
| `locale` | `string` | - | - | User's browser settings | Browser locale |
|
||||
| `scaled` | `bool` | - | | - | Selected calendar size |
|
||||
| `isReadOnly` | `bool` | - | | - | Set input type is read only |
|
||||
| `hasError` | `bool` | - | | - | Set error date-input style |
|
||||
| `isOpen` | `bool` | - | | - | Opens calendar |
|
||||
| `displayType` | `oneOf` | - | `dropdown, aside, auto` | `auto` | Calendar display type |
|
||||
| `calendarHeaderContent` | `string` | - | - | - | Calendar header content (calendar opened in aside) |
|
||||
|
@ -10,68 +10,67 @@ import GroupButton from '../group-button';
|
||||
const rowStyle = { marginTop: 8 };
|
||||
|
||||
storiesOf('Components| DropDown', module)
|
||||
.addDecorator(withReadme(Readme))
|
||||
.add('base', () => (
|
||||
<Container>
|
||||
<Row style={rowStyle}>
|
||||
<Col xs="4">Only dropdown</Col>
|
||||
<Col xs="2"/>
|
||||
<Col>With Button</Col>
|
||||
</Row>
|
||||
<Row style={rowStyle}>
|
||||
<Col xs="4">
|
||||
Without active button
|
||||
.addDecorator(withReadme(Readme))
|
||||
.add('base', () => (
|
||||
<Container>
|
||||
<Row style={rowStyle}>
|
||||
<Col xs="4">Only dropdown</Col>
|
||||
<Col xs="2" />
|
||||
<Col>With Button</Col>
|
||||
</Row>
|
||||
<Row style={rowStyle}>
|
||||
<Col xs="4">
|
||||
Without active button
|
||||
<DropDown opened={true}>
|
||||
<DropDownItem
|
||||
isHeader
|
||||
label='Category 1'
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 1'
|
||||
onClick={() => console.log('Button 1 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 2'
|
||||
onClick={() => console.log('Button 2 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 3'
|
||||
onClick={() => console.log('Button 3 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 4'
|
||||
onClick={() => console.log('Button 4 clicked')}
|
||||
disabled={true}
|
||||
/>
|
||||
<DropDownItem isSeparator />
|
||||
<DropDownItem
|
||||
label='Button 5'
|
||||
onClick={() => console.log('Button 5 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 6'
|
||||
onClick={() => console.log('Button 6 clicked')}
|
||||
/>
|
||||
</DropDown>
|
||||
</Col>
|
||||
<Col xs="2"/>
|
||||
<Col>
|
||||
<GroupButton label='Dropdown demo' isDropdown={true}>
|
||||
<DropDownItem
|
||||
label='Button 1'
|
||||
onClick={() => console.log('Button 1 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 2'
|
||||
onClick={() => console.log('Button 2 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 3'
|
||||
onClick={() => console.log('Button 3 clicked')}
|
||||
/>
|
||||
</GroupButton>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
);
|
||||
<DropDownItem
|
||||
isHeader
|
||||
label='Category 1'
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 1'
|
||||
onClick={() => console.log('Button 1 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 2'
|
||||
onClick={() => console.log('Button 2 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 3'
|
||||
onClick={() => console.log('Button 3 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 4'
|
||||
onClick={() => console.log('Button 4 clicked')}
|
||||
disabled={true}
|
||||
/>
|
||||
<DropDownItem isSeparator />
|
||||
<DropDownItem
|
||||
label='Button 5'
|
||||
onClick={() => console.log('Button 5 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 6'
|
||||
onClick={() => console.log('Button 6 clicked')}
|
||||
/>
|
||||
</DropDown>
|
||||
</Col>
|
||||
<Col xs="2" />
|
||||
<Col>
|
||||
<GroupButton label='Dropdown demo' isDropdown={true}>
|
||||
<DropDownItem
|
||||
label='Button 1'
|
||||
onClick={() => console.log('Button 1 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 2'
|
||||
onClick={() => console.log('Button 2 clicked')}
|
||||
/>
|
||||
<DropDownItem
|
||||
label='Button 3'
|
||||
onClick={() => console.log('Button 3 clicked')}
|
||||
/>
|
||||
</GroupButton>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
));
|
@ -68,40 +68,61 @@ class DropDown extends React.PureComponent {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
width: 100
|
||||
width: 100,
|
||||
directionX: props.directionX,
|
||||
directionY: props.directionY
|
||||
};
|
||||
|
||||
this.dropDown = React.createRef();
|
||||
}
|
||||
|
||||
setDropDownWidthState = () => {
|
||||
if (this.dropDown.current) {
|
||||
this.setState({
|
||||
width: this.dropDown.current.offsetWidth
|
||||
});
|
||||
}
|
||||
this.dropDownRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.setDropDownWidthState();
|
||||
this.checkPosition();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.opened !== prevProps.opened || this.props.isOpen !== prevProps.isOpen) {
|
||||
this.setDropDownWidthState();
|
||||
this.checkPosition();
|
||||
}
|
||||
}
|
||||
|
||||
checkPosition = () => {
|
||||
if (this.dropDownRef.current){
|
||||
const rects = this.dropDownRef.current.getBoundingClientRect();
|
||||
const container = {width: window.innerWidth, height: window.innerHeight};
|
||||
|
||||
const left = rects.x < rects.width;
|
||||
const right = rects.x + rects.width > container.width;
|
||||
|
||||
const top = rects.y + rects.height > container.height;
|
||||
const bottom = rects.y < rects.height;
|
||||
|
||||
let newDirection = {};
|
||||
|
||||
newDirection.directionX = left ? 'left' : right ? 'right' : this.props.directionX;
|
||||
newDirection.directionY = top ? 'top' : bottom ? 'bottom' : this.props.directionY;
|
||||
|
||||
this.setState({
|
||||
directionX: newDirection.directionX,
|
||||
directionY: newDirection.directionY,
|
||||
width: rects.width
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {maxHeight, withArrow, directionX, children} = this.props;
|
||||
const {maxHeight, withArrow, children} = this.props;
|
||||
const {directionX, directionY} = this.state;
|
||||
const fullHeight = children && children.length * 36;
|
||||
const calculatedHeight = ((fullHeight > 0) && (fullHeight < maxHeight)) ? fullHeight : maxHeight;
|
||||
const dropDownMaxHeightProp = maxHeight ? { height: calculatedHeight + 'px' } : {};
|
||||
//console.log("DropDown render");
|
||||
return (
|
||||
<StyledDropdown
|
||||
ref={this.dropDown}
|
||||
ref={this.dropDownRef}
|
||||
{...this.props}
|
||||
directionX={directionX}
|
||||
directionY={directionY}
|
||||
{...dropDownMaxHeightProp}
|
||||
>
|
||||
{withArrow && <Arrow directionX={directionX} />}
|
||||
|
@ -20,10 +20,11 @@ Responsive form field container
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ---------------- | ------------------ | :------: | ------ | ------- | -------------------------------------------- |
|
||||
| `isVertical` | `bool` | - | - | false | Vertical or horizontal alignment |
|
||||
| `isRequired` | `bool` | - | - | false | Indicates that the field is required to fill |
|
||||
| `hasError` | `bool` | - | - | false | Indicates that the field is incorrect |
|
||||
| `labelText` | `string` | - | - | - | Field label text |
|
||||
| `tooltipContent` | `object or string` | ✅ | - | - | Tooltip content |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------------- | ------------------ | :------: | ------ | ------- | ------------------------------------------------ |
|
||||
| `isVertical` | `bool` | - | - | false | Vertical or horizontal alignment |
|
||||
| `isRequired` | `bool` | - | - | false | Indicates that the field is required to fill |
|
||||
| `hasError` | `bool` | - | - | false | Indicates that the field is incorrect |
|
||||
| `labelText` | `string` | - | - | - | Field label text |
|
||||
| `tooltipContent` | `object or string` | ✅ | - | - | Tooltip content |
|
||||
| `HelpButtonHeaderContent` | `string` | - | - | - | Tooltip header content (tooltip opened in aside) |
|
||||
|
@ -71,7 +71,8 @@ class FieldContainer extends React.Component {
|
||||
labelText,
|
||||
children,
|
||||
tooltipContent,
|
||||
place
|
||||
place,
|
||||
HelpButtonHeaderContent
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -88,6 +89,7 @@ class FieldContainer extends React.Component {
|
||||
<HelpButton
|
||||
tooltipContent={tooltipContent}
|
||||
place={place}
|
||||
HelpButtonHeaderContent={HelpButtonHeaderContent}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -111,7 +113,8 @@ FieldContainer.propTypes = {
|
||||
PropTypes.node
|
||||
]),
|
||||
tooltipContent: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
place: PropTypes.string
|
||||
place: PropTypes.string,
|
||||
HelpButtonHeaderContent: PropTypes.string
|
||||
};
|
||||
|
||||
FieldContainer.defaultProps ={
|
||||
|
@ -51,7 +51,9 @@ HelpButton is used for a action on a page.
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ---------------- | ------------------ | :------: | -------------------------- | ------- | ----------------- |
|
||||
| `tooltipContent` | `object or string` | ✅ | - | - | Tooltip content |
|
||||
| `place` | `string` | - | `top, right, bottom, left` | `top` | Tooltip placement |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------------- | ------------------ | :------: | -------------------------- | ------- | ------------------------------------------------ |
|
||||
| `tooltipContent` | `object or string` | ✅ | - | - | Tooltip content |
|
||||
| `place` | `string` | - | `top, right, bottom, left` | `top` | Tooltip placement |
|
||||
| `displayType` | `oneOf` | - | `dropdown, aside, auto` | `auto` | Tooltip display type |
|
||||
| `HelpButtonHeaderContent` | `string` | - | - | - | Tooltip header content (tooltip opened in aside) |
|
||||
|
@ -27,6 +27,7 @@ storiesOf("Components|Buttons", module)
|
||||
<Section>
|
||||
<IconButtons>
|
||||
<HelpButton
|
||||
displayType="dropdown"
|
||||
tooltipContent={
|
||||
<Text.Body fontSize={13}>
|
||||
Paste you tooltip content here
|
||||
@ -34,6 +35,8 @@ storiesOf("Components|Buttons", module)
|
||||
}
|
||||
/>
|
||||
<HelpButton
|
||||
displayType="aside"
|
||||
HelpButtonHeaderContent="Aside position HelpButton"
|
||||
tooltipContent={
|
||||
<Text.Body>
|
||||
You tooltip content with{" "}
|
||||
@ -47,10 +50,12 @@ storiesOf("Components|Buttons", module)
|
||||
}
|
||||
/>
|
||||
<HelpButton
|
||||
displayType="auto"
|
||||
HelpButtonHeaderContent="Auto position HelpButton"
|
||||
tooltipContent={
|
||||
<>
|
||||
<p>You can put every thing here</p>
|
||||
<ul style={{marginBottom: 0}}>
|
||||
<ul style={{ marginBottom: 0 }}>
|
||||
<li>Word</li>
|
||||
<li>Chart</li>
|
||||
<li>Else</li>
|
||||
|
@ -4,15 +4,47 @@ import IconButton from "../icon-button";
|
||||
import Tooltip from "../tooltip";
|
||||
import { handleAnyClick } from "../../utils/event";
|
||||
import uniqueId from "lodash/uniqueId";
|
||||
import Aside from "../layout/sub-components/aside";
|
||||
import { desktop } from "../../utils/device";
|
||||
import Backdrop from "../backdrop";
|
||||
import { Text } from "../text";
|
||||
import throttle from "lodash/throttle";
|
||||
import styled from "styled-components";
|
||||
|
||||
const Content = styled.div`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
padding: 0 16px 16px;
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
`;
|
||||
|
||||
const Body = styled.div`
|
||||
position: relative;
|
||||
padding: 16px 0;
|
||||
`;
|
||||
|
||||
const HeaderText = styled(Text.ContentHeader)`
|
||||
max-width: 500px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
class HelpButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { isOpen: false };
|
||||
this.state = { isOpen: false, displayType: this.getTypeByWidth() };
|
||||
this.ref = React.createRef();
|
||||
this.refTooltip = React.createRef();
|
||||
this.id = uniqueId();
|
||||
|
||||
this.throttledResize = throttle(this.resize, 300);
|
||||
}
|
||||
|
||||
afterShow = () => {
|
||||
@ -41,12 +73,60 @@ class HelpButton extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
onClose = () => {
|
||||
this.setState({ isOpen: false });
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("resize", this.throttledResize);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
handleAnyClick(false, this.handleClick);
|
||||
window.removeEventListener("resize", this.throttledResize);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (this.props.displayType !== prevProps.displayType) {
|
||||
this.setState({ displayType: this.getTypeByWidth() });
|
||||
}
|
||||
if (this.state.isOpen && this.state.displayType === "aside") {
|
||||
window.addEventListener("popstate", this.popstate, false);
|
||||
}
|
||||
}
|
||||
|
||||
popstate = () => {
|
||||
window.removeEventListener("popstate", this.popstate, false);
|
||||
this.onClose();
|
||||
window.history.go(1);
|
||||
};
|
||||
|
||||
resize = () => {
|
||||
if (this.props.displayType !== "auto") return;
|
||||
const type = this.getTypeByWidth();
|
||||
if (type === this.state.displayType) return;
|
||||
this.setState({ displayType: type });
|
||||
};
|
||||
|
||||
getTypeByWidth = () => {
|
||||
if (this.props.displayType !== "auto") return this.props.displayType;
|
||||
return window.innerWidth < desktop.match(/\d+/)[0] ? "aside" : "dropdown";
|
||||
};
|
||||
|
||||
onClick = () => {
|
||||
this.setState({isOpen: !this.state.isOpen});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tooltipContent, place, offsetRight, offsetLeft } = this.props;
|
||||
const { isOpen, displayType } = this.state;
|
||||
const {
|
||||
tooltipContent,
|
||||
place,
|
||||
offsetRight,
|
||||
offsetLeft,
|
||||
zIndex,
|
||||
HelpButtonHeaderContent
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div ref={this.ref}>
|
||||
@ -56,19 +136,38 @@ class HelpButton extends React.Component {
|
||||
isClickable={true}
|
||||
iconName="QuestionIcon"
|
||||
size={13}
|
||||
onClick={this.onClick}
|
||||
/>
|
||||
<Tooltip
|
||||
id={this.id}
|
||||
reference={this.refTooltip}
|
||||
effect="solid"
|
||||
place={place}
|
||||
offsetRight={offsetRight}
|
||||
offsetLeft={offsetLeft}
|
||||
afterShow={this.afterShow}
|
||||
afterHide={this.afterHide}
|
||||
>
|
||||
{tooltipContent}
|
||||
</Tooltip>
|
||||
{displayType === "dropdown" ? (
|
||||
<Tooltip
|
||||
id={this.id}
|
||||
reference={this.refTooltip}
|
||||
effect="solid"
|
||||
place={place}
|
||||
offsetRight={offsetRight}
|
||||
offsetLeft={offsetLeft}
|
||||
afterShow={this.afterShow}
|
||||
afterHide={this.afterHide}
|
||||
>
|
||||
{tooltipContent}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<>
|
||||
<Backdrop onClick={this.onClose} visible={isOpen} zIndex={zIndex} />
|
||||
<Aside visible={isOpen} scale={false} zIndex={zIndex}>
|
||||
<Content>
|
||||
<Header>
|
||||
<HeaderText>
|
||||
<Text.Body isBold={true} fontSize={21}>
|
||||
{HelpButtonHeaderContent}
|
||||
</Text.Body>
|
||||
</HeaderText>
|
||||
</Header>
|
||||
<Body>{tooltipContent}</Body>
|
||||
</Content>
|
||||
</Aside>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -84,13 +183,18 @@ HelpButton.propTypes = {
|
||||
tooltipMaxWidth: PropTypes.number,
|
||||
tooltipId: PropTypes.string,
|
||||
place: PropTypes.string,
|
||||
offsetLeft: PropTypes.number
|
||||
offsetLeft: PropTypes.number,
|
||||
zIndex: PropTypes.number,
|
||||
displayType: PropTypes.oneOf(["dropdown", "aside", "auto"]),
|
||||
HelpButtonHeaderContent: PropTypes.string
|
||||
};
|
||||
|
||||
HelpButton.defaultProps = {
|
||||
place: "top",
|
||||
offsetRight: 120,
|
||||
offsetLeft: 0
|
||||
}
|
||||
offsetLeft: 0,
|
||||
zIndex: 310,
|
||||
displayType: "auto"
|
||||
};
|
||||
|
||||
export default HelpButton;
|
||||
|
@ -14,7 +14,7 @@ const getSizeStyle = size => {
|
||||
return `
|
||||
&:not(:root) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
height: 100%;
|
||||
}
|
||||
`;
|
||||
case 'small':
|
||||
|
@ -61,8 +61,6 @@ import OrigCopyIcon from './copy.react.svg';
|
||||
import OrigShareEmailIcon from './share.e-mail.react.svg';
|
||||
import OrigShareGooglePlusIcon from './share.google.plus.react.svg';
|
||||
import OrigSendClockIcon from './send.clock.react.svg';
|
||||
import OrigShareFacebookIcon from './share.facebook.react.svg';
|
||||
import OrigShareTwitterIcon from './share.twitter.react.svg';
|
||||
import OrigAccessNoneIcon from './access.none.react.svg';
|
||||
import OrigTimeTrackingNotBilledIcon from './time.tracking.not.billed.react.svg';
|
||||
import OrigAccessFormIcon from './access.form.react.svg';
|
||||
@ -141,6 +139,10 @@ import OrigToggleButtonIcon from './toggle.button.react.svg';
|
||||
|
||||
import OrigQuestionIcon from './question.react.svg';
|
||||
|
||||
import OrigShareGoogleIcon from './share.google.react.svg';
|
||||
import OrigShareFacebookIcon from './share.facebook.react.svg';
|
||||
import OrigShareTwitterIcon from './share.twitter.react.svg';
|
||||
import OrigShareLinkedInIcon from './share.linkedin.react.svg';
|
||||
|
||||
export const AZSortingIcon = createStyledIcon(
|
||||
OrigAZSortingIcon,
|
||||
@ -619,18 +621,10 @@ export const ShareEmailIcon = createStyledIcon(
|
||||
OrigShareEmailIcon,
|
||||
'ShareEmailIcon'
|
||||
);
|
||||
export const ShareFacebookIcon = createStyledIcon(
|
||||
OrigShareFacebookIcon,
|
||||
'ShareFacebookIcon'
|
||||
);
|
||||
export const ShareGooglePlusIcon = createStyledIcon(
|
||||
OrigShareGooglePlusIcon,
|
||||
'ShareGooglePlusIcon'
|
||||
);
|
||||
export const ShareTwitterIcon = createStyledIcon(
|
||||
OrigShareTwitterIcon,
|
||||
'ShareTwitterIcon'
|
||||
);
|
||||
export const SkypeIcon = createStyledIcon(
|
||||
OrigSkypeIcon,
|
||||
'SkypeIcon'
|
||||
@ -692,4 +686,23 @@ export const QuestionIcon = createStyledIcon(
|
||||
OrigQuestionIcon,
|
||||
'ToggleButtonIcon',
|
||||
"rect"
|
||||
);
|
||||
|
||||
export const ShareGoogleIcon = createStyledIcon(
|
||||
OrigShareGoogleIcon,
|
||||
'ShareGoogleIcon'
|
||||
);
|
||||
|
||||
export const ShareFacebookIcon = createStyledIcon(
|
||||
OrigShareFacebookIcon,
|
||||
'ShareFacebookIcon'
|
||||
);
|
||||
export const ShareTwitterIcon = createStyledIcon(
|
||||
OrigShareTwitterIcon,
|
||||
'ShareTwitterIcon'
|
||||
);
|
||||
|
||||
export const ShareLinkedInIcon = createStyledIcon(
|
||||
OrigShareLinkedInIcon,
|
||||
'ShareLinkedInIcon'
|
||||
);
|
@ -1,3 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 0H2C0.897 0 0 0.897 0 2V14C0 15.103 0.897 16 2 16H8V10.1848H5.81641V8H8V5.90666C8 4.24966 9.343 2.90666 11 2.90666H13.0877V5.09417H11.912C11.36 5.09417 10.912 5.26478 10.912 5.81678V8H13.5L12.5 10.1848H10.912V16H14C15.103 16 16 15.103 16 14V2C16 0.897 15.103 0 14 0Z" fill="#A3A9AE"/>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.75 0H2.25C1.00912 0 0 1.00912 0 2.25V15.75C0 16.9909 1.00912 18 2.25 18H9V11.4579H6.54346V9H9V6.64499C9 4.78087 10.5109 3.26999 12.375 3.26999H14.7237V5.73094H13.401C12.78 5.73094 12.276 5.92288 12.276 6.54388V9H15.1875L14.0625 11.4579H12.276V18H15.75C16.9909 18 18 16.9909 18 15.75V2.25C18 1.00912 16.9909 0 15.75 0Z" fill="#4469B0"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 441 B After Width: | Height: | Size: 494 B |
@ -0,0 +1,6 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.64 9.20419C17.64 8.56601 17.5827 7.95237 17.4764 7.36328H9V10.8446H13.8436C13.635 11.9696 13.0009 12.9228 12.0477 13.561V15.8192H14.9564C16.6582 14.2524 17.64 11.9451 17.64 9.20419Z" fill="#4285F4"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 18C11.43 18 13.4673 17.1941 14.9564 15.8195L12.0477 13.5613C11.2418 14.1013 10.2109 14.4204 9 14.4204C6.65591 14.4204 4.67182 12.8372 3.96409 10.71H0.957275V13.0418C2.43818 15.9831 5.48182 18 9 18Z" fill="#34A853"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.96409 10.7098C3.78409 10.1698 3.68182 9.59301 3.68182 8.99983C3.68182 8.40664 3.78409 7.82983 3.96409 7.28983V4.95801H0.957273C0.347727 6.17301 0 7.54755 0 8.99983C0 10.4521 0.347727 11.8266 0.957273 13.0416L3.96409 10.7098Z" fill="#FBBC05"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 3.57955C10.3214 3.57955 11.5077 4.03364 12.4405 4.92545L15.0218 2.34409C13.4632 0.891818 11.4259 0 9 0C5.48182 0 2.43818 2.01682 0.957275 4.95818L3.96409 7.29C4.67182 5.16273 6.65591 3.57955 9 3.57955Z" fill="#EA4335"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,4 @@
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.75036 8.05344C9.74586 8.06301 9.73967 8.07201 9.73292 8.08044H9.75036V8.05344Z" fill="#1276B3"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.25 0C1.00736 0 0 1.00736 0 2.25V15.75C0 16.9926 1.00736 18 2.25 18H15.75C16.9926 18 18 16.9926 18 15.75V2.25C18 1.00736 16.9926 0 15.75 0H2.25ZM2.89855 6.91044H5.5828V14.9846H2.89855V6.91044ZM4.25755 3.01794C5.17611 3.01794 5.74142 3.62094 5.7583 4.41407C5.7583 5.18751 5.17611 5.80851 4.24011 5.80851H4.22267C3.32211 5.80851 2.73992 5.18751 2.73992 4.41407C2.73992 3.62094 3.34011 3.01794 4.25755 3.01794ZM12.1697 6.72144C13.9354 6.72144 15.26 7.87513 15.26 10.3546L15.2595 14.9846H12.5758V10.6646C12.5758 9.57782 12.1882 8.83869 11.2157 8.83869C10.4754 8.83869 10.0333 9.34044 9.83867 9.82082C9.7678 9.99463 9.75036 10.2314 9.75036 10.4756V14.9846H7.06723C7.06723 14.9846 7.10155 7.66757 7.06723 6.91044H9.75036V8.05344C10.1087 7.50388 10.746 6.72144 12.1697 6.72144Z" fill="#1276B3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -1,3 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.4 0H1.6C0.72 0 0 0.72 0 1.6V14.4C0 15.28 0.72 16 1.6 16H14.4C15.28 16 16 15.28 16 14.4V1.6C16 0.72 15.28 0 14.4 0ZM12.56 5.84C12.48 9.52 10.16 12.08 6.64 12.24C5.2 12.32 4.16 11.84 3.2 11.28C4.24 11.44 5.6 11.04 6.32 10.4C5.28 10.32 4.64 9.76 4.32 8.88C4.64 8.96 4.96 8.88 5.2 8.88C4.24 8.56 3.6 8 3.52 6.72C3.76 6.88 4.08 6.96 4.4 6.96C3.68 6.56 3.2 5.04 3.76 4.08C4.8 5.2 6.08 6.16 8.16 6.32C7.6 4.08 10.64 2.88 11.84 4.4C12.4 4.32 12.8 4.08 13.2 3.92C13.04 4.48 12.72 4.8 12.32 5.12C12.72 5.04 13.12 4.96 13.44 4.8C13.36 5.2 12.96 5.52 12.56 5.84Z" fill="#A3A9AE"/>
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.2 0H1.8C0.81 0 0 0.81 0 1.8V16.2C0 17.19 0.81 18 1.8 18H16.2C17.19 18 18 17.19 18 16.2V1.8C18 0.81 17.19 0 16.2 0ZM14.13 6.57002C14.04 10.71 11.43 13.59 7.47 13.77C5.85 13.86 4.68 13.32 3.6 12.69C4.77 12.8701 6.3 12.4201 7.11 11.7C5.94 11.61 5.22 10.98 4.86 9.99002C5.22 10.08 5.58 9.99002 5.85 9.99002C4.77 9.63002 4.05 9.00002 3.96 7.56002C4.23 7.74002 4.59 7.83002 4.95 7.83002C4.14 7.38002 3.6 5.67002 4.23 4.59002C5.4 5.85002 6.84 6.93002 9.18 7.11002C8.55 4.59002 11.97 3.24002 13.32 4.95002C13.95 4.86002 14.4 4.59002 14.85 4.41002C14.67 5.04002 14.31 5.40002 13.86 5.76002C14.31 5.67002 14.76 5.58002 15.12 5.40002C15.03 5.85002 14.58 6.21002 14.13 6.57002Z" fill="#2AA3EF"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 725 B After Width: | Height: | Size: 842 B |
@ -0,0 +1,29 @@
|
||||
# Buttons: SocialButton
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { SocialButton } from 'asc-web-components';
|
||||
|
||||
```
|
||||
|
||||
#### Description
|
||||
|
||||
Button is used for sign up with help social networks.
|
||||
|
||||
#### Usage
|
||||
|
||||
```js
|
||||
<SocialButton iconName={"SocialButtonGoogleIcon"} label={"Sign up with Google"}/>
|
||||
```
|
||||
|
||||
#### Properties
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| ------------------ | -------- | :------: | --------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `label` | `string` | - | - | - | Button text |
|
||||
| `iconName` | `string` | - | - | SocialButtonGoogleIcon | Icon of button |
|
||||
| `isDisabled` | `bool` | - | - | false | Tells when the button should present a disabled state |
|
||||
| `onClick` | `func` | - | - | - | What the button will trigger when clicked |
|
||||
|
||||
|
137
web/ASC.Web.Components/src/components/social-button/index.js
Normal file
137
web/ASC.Web.Components/src/components/social-button/index.js
Normal file
@ -0,0 +1,137 @@
|
||||
import React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Text } from '../text';
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { Icons } from "../icons";
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const ButtonWrapper = ({label, iconName, isDisabled, ...props}) => <button type="button" {...props}></button>;
|
||||
|
||||
ButtonWrapper.propTypes = {
|
||||
label: PropTypes.string,
|
||||
iconName: PropTypes.string,
|
||||
tabIndex: PropTypes.number,
|
||||
isDisabled: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
const StyledSocialButton = styled(ButtonWrapper).attrs((props) => ({
|
||||
disabled: props.isDisabled ? 'disabled' : '',
|
||||
tabIndex: props.tabIndex
|
||||
}))`
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
margin: 20px 0 0 20px;
|
||||
padding: 0;
|
||||
border-radius: 2px;
|
||||
width: 201px;
|
||||
height: 40px;
|
||||
text-align: left;
|
||||
touch-callout: none;
|
||||
-o-touch-callout: none;
|
||||
-moz-touch-callout: none;
|
||||
-webkit-touch-callout: none;
|
||||
stroke: none;
|
||||
|
||||
&:focus {
|
||||
outline: none
|
||||
}
|
||||
|
||||
${props =>
|
||||
!props.isDisabled ?
|
||||
css`
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.24), 0px 0px 1px rgba(0, 0, 0, 0.12);
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
|
||||
:hover, :active {
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.24), 0px 0px 2px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
:hover {
|
||||
background: #FFFFFF;
|
||||
}
|
||||
|
||||
:active {
|
||||
background: #EEEEEE;
|
||||
border: none;
|
||||
}
|
||||
`
|
||||
:
|
||||
css`
|
||||
box-shadow: none;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
color: rgba(0, 0, 0, 0.4);
|
||||
|
||||
svg path {
|
||||
fill: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
.social_button_text {
|
||||
position: absolute;
|
||||
width: 142px;
|
||||
height: 16px;
|
||||
margin: 12px 9px 12px 10px;
|
||||
font-family: Roboto, 'Open Sans', sans-serif, Arial;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.21875px;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin: 11px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
min-width: 18px;
|
||||
min-height: 18px;
|
||||
}
|
||||
`;
|
||||
|
||||
class SocialButton extends React.Component {
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return !isEqual(this.props, nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {label, iconName} = this.props;
|
||||
return (
|
||||
<StyledSocialButton {...this.props}>
|
||||
{React.createElement(Icons[iconName], {})}
|
||||
|
||||
{label && (
|
||||
<Text.Body as="span" className="social_button_text">{label}</Text.Body>
|
||||
)}
|
||||
</StyledSocialButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SocialButton.propTypes = {
|
||||
label: PropTypes.string,
|
||||
iconName: PropTypes.string,
|
||||
tabIndex: PropTypes.number,
|
||||
isDisabled: PropTypes.bool
|
||||
};
|
||||
|
||||
SocialButton.defaultProps = {
|
||||
label: '',
|
||||
iconName: 'SocialButtonGoogleIcon',
|
||||
tabIndex: -1,
|
||||
isDisabled: false
|
||||
};
|
||||
|
||||
export default SocialButton;
|
@ -0,0 +1,27 @@
|
||||
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 SocialButton from '.';
|
||||
|
||||
storiesOf('Components|Buttons|SocialButtons', module)
|
||||
.addDecorator(withKnobs)
|
||||
.addDecorator(withReadme(Readme))
|
||||
.add('social button', () => {
|
||||
const socialNetworks = ['ShareGoogleIcon',
|
||||
'ShareFacebookIcon',
|
||||
'ShareTwitterIcon',
|
||||
'ShareLinkedInIcon'];
|
||||
const iconName = select("iconName", ['', ...socialNetworks], 'ShareGoogleIcon');
|
||||
|
||||
return (
|
||||
<SocialButton
|
||||
label={text('label', 'Base SocialButton')}
|
||||
iconName={iconName}
|
||||
isDisabled={boolean('isDisabled', false)}
|
||||
onClick={action('clicked')}
|
||||
/>
|
||||
)
|
||||
});
|
@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import SocialButton from '.';
|
||||
|
||||
describe('<SocialButton />', () => {
|
||||
it('renders without error', () => {
|
||||
const wrapper = mount(
|
||||
<SocialButton iconName={'ShareGoogleIcon'} label={"Test Caption"}/>
|
||||
);
|
||||
|
||||
expect(wrapper).toExist();
|
||||
});
|
||||
|
||||
it('not re-render test', () => {
|
||||
// const onClick= () => alert('SocialButton clicked');
|
||||
|
||||
const wrapper = shallow(<SocialButton iconName={'ShareGoogleIcon'} label={"Test Caption"}/>).instance();
|
||||
|
||||
const shouldUpdate = wrapper.shouldComponentUpdate(wrapper.props);
|
||||
|
||||
expect(shouldUpdate).toBe(false);
|
||||
});
|
||||
|
||||
it('disabled click test', () => {
|
||||
const testClick = jest.fn();
|
||||
|
||||
const wrapper = mount(<SocialButton iconName={'ShareGoogleIcon'} label={"Test Caption"} onClick={testClick} isDisabled={true}/>);
|
||||
|
||||
wrapper.simulate('click');
|
||||
|
||||
expect(testClick).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('click test', () => {
|
||||
const testClick = jest.fn();
|
||||
|
||||
const wrapper = mount(<SocialButton iconName={'ShareGoogleIcon'} label={"Test Caption"} onClick={testClick} isDisabled={false}/>);
|
||||
|
||||
wrapper.simulate('click');
|
||||
|
||||
expect(testClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
@ -21,6 +21,13 @@ const Label = styled.div`
|
||||
text-align: center;
|
||||
margin: 7px 15px 7px 15px;
|
||||
overflow: hidden;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
${props => (props.isDisabled ? `pointer-events: none;` : ``)}
|
||||
|
Loading…
Reference in New Issue
Block a user