Merge branch 'master' into feature/social-button
This commit is contained in:
commit
3a1597c72d
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"
|
||||
}
|
@ -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)));
|
@ -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,12 +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";
|
||||
|
||||
import { isArrayEqual } from "../../../utils/isArrayEqual";
|
||||
|
||||
const AdminsContainer = styled.div`
|
||||
.hidden-icon {
|
||||
position: fixed;
|
||||
visibility: hidden;
|
||||
}
|
||||
`;
|
||||
const ToggleContentContainer = styled.div`
|
||||
.buttons_container {
|
||||
display: flex;
|
||||
@ -54,7 +63,6 @@ const ToggleContentContainer = styled.div`
|
||||
}
|
||||
|
||||
.filter_container {
|
||||
margin-bottom: 50px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
`;
|
||||
@ -66,41 +74,32 @@ class PureAdminsSettings extends Component {
|
||||
this.state = {
|
||||
showSelector: false,
|
||||
showFullAdminSelector: false,
|
||||
allOptions: [],
|
||||
options: [],
|
||||
isLoading: false,
|
||||
showLoader: true,
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { fetchPeople } = this.props;
|
||||
this.onLoading(true);
|
||||
const { admins, options, fetchPeople } = this.props;
|
||||
|
||||
const newFilter = this.onAdminsFilter();
|
||||
|
||||
fetchPeople(newFilter)
|
||||
.catch(error => {
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(() => this.onLoading(false));
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { admins, options } = this.props;
|
||||
const { isLoading, showSelector, showFullAdminSelector } = this.state;
|
||||
|
||||
//options, selectedOptions ???
|
||||
|
||||
if (
|
||||
isArrayEqual(admins, nextProps.admins) &&
|
||||
isArrayEqual(options, nextProps.options) &&
|
||||
isLoading === nextState.isLoading &&
|
||||
showSelector === nextState.showSelector &&
|
||||
showFullAdminSelector === nextState.showFullAdminSelector
|
||||
) {
|
||||
return false;
|
||||
if (isEmpty(admins, true) || isEmpty(options, true)) {
|
||||
const newFilter = this.onAdminsFilter();
|
||||
fetchPeople(newFilter)
|
||||
.catch(error => {
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(() =>
|
||||
this.setState({
|
||||
showLoader: false,
|
||||
allOptions: this.props.options
|
||||
})
|
||||
);
|
||||
} else {
|
||||
this.setState({ showLoader: false });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
onChangeAdmin = (userIds, isAdmin, productId) => {
|
||||
@ -117,12 +116,13 @@ class PureAdminsSettings extends Component {
|
||||
});
|
||||
};
|
||||
|
||||
onShowGroupSelector = () =>
|
||||
onShowGroupSelector = () => {
|
||||
this.setState({
|
||||
showSelector: !this.state.showSelector,
|
||||
options: this.props.options,
|
||||
selectedOptions: []
|
||||
});
|
||||
};
|
||||
|
||||
onShowFullAdminGroupSelector = () => {
|
||||
this.setState({
|
||||
@ -154,37 +154,40 @@ class PureAdminsSettings extends Component {
|
||||
};
|
||||
|
||||
onSearchUsers = template => {
|
||||
//dispatch
|
||||
this.setState({
|
||||
options: this.filterUserSelectorOptions(this.props.options, template)
|
||||
});
|
||||
const options = this.filterUserSelectorOptions(
|
||||
this.state.allOptions,
|
||||
template
|
||||
);
|
||||
this.setState({ options: options });
|
||||
};
|
||||
|
||||
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();
|
||||
@ -193,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();
|
||||
@ -208,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));
|
||||
};
|
||||
@ -228,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;
|
||||
@ -243,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);
|
||||
|
||||
@ -312,182 +328,201 @@ class PureAdminsSettings extends Component {
|
||||
options,
|
||||
selectedOptions,
|
||||
isLoading,
|
||||
showFullAdminSelector
|
||||
showFullAdminSelector,
|
||||
showLoader
|
||||
} = this.state;
|
||||
|
||||
const countElements = filter.total;
|
||||
|
||||
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"
|
||||
/>
|
||||
{!isLoading
|
||||
? (console.log("123123123"),
|
||||
(
|
||||
<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}
|
||||
/>
|
||||
</div>
|
||||
return admins.length > 0 ? (
|
||||
/*TODO: delete after resolve icon button problem*/
|
||||
<AdminsContainer>
|
||||
<IconButton className="hidden-icon" iconName="SearchIcon" />
|
||||
|
||||
<Button
|
||||
size="medium"
|
||||
primary={true}
|
||||
label="Set portal admin"
|
||||
isDisabled={isLoading}
|
||||
onClick={this.onShowFullAdminGroupSelector}
|
||||
{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"
|
||||
/>
|
||||
|
||||
<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}
|
||||
/>
|
||||
<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}
|
||||
<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}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FilterInput
|
||||
className="filter_container"
|
||||
getFilterData={() => []}
|
||||
getSortData={this.getSortData}
|
||||
onFilter={this.onFilter}
|
||||
/>
|
||||
|
||||
<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">
|
||||
<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>
|
||||
<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>
|
||||
|
||||
{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>
|
||||
) : null}
|
||||
</ToggleContentContainer>
|
||||
))
|
||||
: null}
|
||||
</>
|
||||
);
|
||||
) : null}
|
||||
</ToggleContentContainer>
|
||||
</>
|
||||
)}
|
||||
</AdminsContainer>
|
||||
) : !showLoader ? (
|
||||
<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>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -536,6 +571,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 isEmpty from "lodash/isEmpty";
|
||||
|
||||
import { isArrayEqual } from "../../../utils/isArrayEqual";
|
||||
|
||||
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,42 +81,85 @@ class PureOwnerSettings extends Component {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isLoading: false
|
||||
isLoading: false,
|
||||
showSelector: false,
|
||||
options: [],
|
||||
allOptions: [],
|
||||
showLoader: true,
|
||||
selectedOwner: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { getPortalOwner, ownerId } = this.props;
|
||||
this.onLoading(true);
|
||||
const {
|
||||
owner,
|
||||
getPortalOwner,
|
||||
ownerId,
|
||||
options,
|
||||
getUsersOptions
|
||||
} = this.props;
|
||||
|
||||
getPortalOwner(ownerId)
|
||||
.catch(error => {
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(() => this.onLoading(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(owner, true)) {
|
||||
getPortalOwner(ownerId)
|
||||
.catch(error => {
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(() => this.setState({ showLoader: false }));
|
||||
}
|
||||
return true;
|
||||
if (isEmpty(options, true)) {
|
||||
getUsersOptions()
|
||||
.catch(error => {
|
||||
toastr.error(error);
|
||||
})
|
||||
.finally(() =>
|
||||
this.setState({
|
||||
showLoader: false,
|
||||
allOptions: this.props.options
|
||||
})
|
||||
);
|
||||
}
|
||||
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("|");
|
||||
|
||||
@ -103,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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -176,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
|
||||
};
|
||||
}
|
||||
|
||||
@ -190,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": "Просматривать профили и группы",
|
||||
|
@ -52,7 +52,9 @@
|
||||
"DeleteProfileConfirmation",
|
||||
"DeleteProfileConfirmationInfo",
|
||||
"ChangePasswordSuccess",
|
||||
"Remember"
|
||||
"Remember",
|
||||
"ConfirmOwnerPortalTitle",
|
||||
"ConfirmOwnerPortalSuccessMessage"
|
||||
]
|
||||
},
|
||||
"Settings": {
|
||||
@ -94,7 +96,10 @@
|
||||
"RestoreDefaultButton",
|
||||
"SuccessfullySaveGreetingSettingsMessage",
|
||||
"ChangeLogoButton",
|
||||
"Employees"
|
||||
"Employees",
|
||||
"AccessRightsChangeOwnerText",
|
||||
"DnsChangeMsg",
|
||||
"AccessRightsChangeOwnerConfirmText"
|
||||
],
|
||||
"FeedResource": [
|
||||
"ProjectsProduct",
|
||||
@ -122,6 +127,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.175",
|
||||
"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} />}
|
||||
|
@ -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':
|
||||
|
@ -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