@orfeas126/box-ui-elements
Version:
Box UI Elements
399 lines (396 loc) • 15.8 kB
JavaScript
const _excluded = ["collaborationRestrictionWarning", "intl", "inviteButtonProps", "isEligibleForReferAFriendProgram", "itemName", "itemType", "submissionError", "submitting"];
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var s = Object.getOwnPropertySymbols(e); for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import omit from 'lodash/omit';
import throttle from 'lodash/throttle';
import { FormattedMessage, injectIntl } from 'react-intl';
import { UpgradeBadge } from '../../components/badge';
import Button from '../../components/button';
import InlineNotice from '../../components/inline-notice';
import PrimaryButton from '../../components/primary-button';
import Select from '../../components/select';
import TextArea from '../../components/text-area';
import { Link } from '../../components/link';
import { Modal, ModalActions } from '../../components/modal';
import ContactDatalistItem from '../../components/contact-datalist-item';
import PillSelectorDropdown from '../../components/pill-selector-dropdown';
import parseEmails from '../../utils/parseEmails';
import { RESIN_TAG_TARGET } from '../../common/variables';
import PermissionFlyout from './PermissionFlyout';
import ReferAFriendAd from './ReferAFriendAd';
import messages from './messages';
import commonMessages from '../../common/messages';
import './InviteCollaboratorsModal.scss';
const COLLAB_ROLE_FOR_FILE = 'Editor';
const INPUT_DELAY = 250;
/**
* Returns true if the given value is a substring of the searchString
* @param {String} value
* @param {String} searchString
* @return {boolean}
*/
const isSubstring = (value, searchString) => value && value.toLowerCase().indexOf(searchString.toLowerCase()) !== -1;
class InviteCollaboratorsModal extends Component {
constructor(props) {
super(props);
_defineProperty(this, "getSelectorOptions", () => {
const {
contacts
} = this.props;
const {
pillSelectorInputValue,
selectedOptions
} = this.state;
if (pillSelectorInputValue !== '') {
return contacts.filter(
// filter contacts whose name or email don't match input value
({
name,
email
}) => isSubstring(name, pillSelectorInputValue) || isSubstring(email, pillSelectorInputValue)).filter(
// filter contacts who have already been selected
({
email,
id
}) => !selectedOptions.find(({
value
}) => value === email || value === id)).map(({
email,
id,
name,
type
}) => ({
// map to standardized DatalistItem format
// TODO: refactor this so inline conversions aren't required at every usage
email,
id,
text: name,
type,
value: email || id // if email doesn't exist, contact is a group, use id
}));
}
// return empty selector options if input value is empty
return [];
});
_defineProperty(this, "closeModal", () => {
this.setState({
pillSelectorError: '',
pillSelectorInputValue: '',
selectedOptions: []
});
this.props.onRequestClose();
});
_defineProperty(this, "sendInvites", () => {
const {
intl,
sendInvites
} = this.props;
const {
message,
permission,
pillSelectorError,
selectedOptions
} = this.state;
if (pillSelectorError) {
// Block submission if there's a validation error
return;
}
if (selectedOptions.length === 0) {
// Block submission if no pills are selected
this.setState({
pillSelectorError: intl.formatMessage(commonMessages.requiredFieldError)
});
return;
}
const emails = [];
const groupIDs = [];
selectedOptions.forEach(({
type,
value
}) => {
if (type === 'group') {
groupIDs.push(value);
} else {
emails.push(value);
}
});
const params = {
emails: emails.join(','),
groupIDs: groupIDs.join(','),
emailMessage: message,
permission: permission || COLLAB_ROLE_FOR_FILE,
numsOfInvitees: emails.length,
numOfInviteeGroups: groupIDs.length
};
sendInvites(params).catch(error => {
// Remove invited emails from selected pills
const invitedEmails = error.invitedEmails || [];
this.filterInvitedEmails(invitedEmails);
});
});
_defineProperty(this, "filterInvitedEmails", invitedEmails => {
this.setState({
selectedOptions: this.state.selectedOptions.filter(({
value
}) => !invitedEmails.includes(value))
});
});
_defineProperty(this, "handlePillSelectorInput", throttle(value => {
const {
onUserInput
} = this.props;
if (onUserInput) {
onUserInput(value);
}
// As user is typing, reset error
this.setState({
pillSelectorError: '',
pillSelectorInputValue: value
});
}, INPUT_DELAY));
_defineProperty(this, "handlePillSelect", pills => {
this.setState({
selectedOptions: [...this.state.selectedOptions, ...pills]
});
});
_defineProperty(this, "handlePillRemove", (option, index) => {
const selectedOptions = this.state.selectedOptions.slice();
selectedOptions.splice(index, 1);
this.setState({
selectedOptions
});
});
_defineProperty(this, "validateForError", text => {
const {
intl
} = this.props;
let pillSelectorError = '';
if (text && !this.validator(text)) {
pillSelectorError = intl.formatMessage(commonMessages.invalidEmailError);
}
this.setState({
pillSelectorError
});
});
_defineProperty(this, "validator", text => {
// email input validation
const pattern = /^[^\s<>@,]+@[^\s<>@,/\\]+\.[^\s<>@,]+$/i;
return pattern.test(text);
});
_defineProperty(this, "handlePermissionChange", ({
target
}) => {
const {
value
} = target;
this.setState({
permission: value
});
});
_defineProperty(this, "handleMessageChange", ({
target
}) => {
const {
value
} = target;
this.setState({
message: value
});
});
const {
defaultPersonalMessage,
inviteePermissions
} = props;
this.state = {
message: defaultPersonalMessage || '',
permission: inviteePermissions ? inviteePermissions[0].value : COLLAB_ROLE_FOR_FILE,
pillSelectorError: '',
pillSelectorInputValue: '',
selectedOptions: []
};
}
renderFileCollabComponents() {
return /*#__PURE__*/React.createElement("div", {
className: "invite-file-editors"
}, /*#__PURE__*/React.createElement(FormattedMessage, messages.inviteFileEditorsLabel));
}
renderPermissionsSection() {
const {
inviteePermissions
} = this.props;
return /*#__PURE__*/React.createElement("div", {
className: "invite-permissions-container"
}, /*#__PURE__*/React.createElement(Select, {
className: "select-container-medium",
"data-resin-target": "selectpermission",
label: /*#__PURE__*/React.createElement(FormattedMessage, messages.inviteePermissionsFieldLabel),
name: "invite-permission",
onChange: this.handlePermissionChange
}, inviteePermissions.map(({
value,
disabled = false,
text
}) => /*#__PURE__*/React.createElement("option", {
key: value,
"data-resin-option": value,
disabled: disabled,
value: value
}, text))), /*#__PURE__*/React.createElement(PermissionFlyout, null));
}
renderFolderCollabComponents() {
const {
defaultPersonalMessage,
inviteePermissions,
showUpgradeOptions
} = this.props;
return /*#__PURE__*/React.createElement("div", null, inviteePermissions && this.renderPermissionsSection(), showUpgradeOptions && /*#__PURE__*/React.createElement(Link, {
className: "upgrade-link",
href: "/upgrade"
}, /*#__PURE__*/React.createElement(UpgradeBadge, null), /*#__PURE__*/React.createElement(FormattedMessage, messages.upgradeGetMoreAccessControls)), defaultPersonalMessage && /*#__PURE__*/React.createElement(TextArea, {
cols: "30",
"data-resin-feature": "personalmessage",
"data-resin-target": "message",
defaultValue: defaultPersonalMessage,
label: /*#__PURE__*/React.createElement(FormattedMessage, messages.personalMessageLabel),
name: "collab-message",
onChange: this.handleMessageChange,
rows: "4"
}));
}
render() {
const _this$props = this.props,
{
collaborationRestrictionWarning,
intl,
inviteButtonProps,
isEligibleForReferAFriendProgram,
itemName,
itemType,
submissionError,
submitting
} = _this$props,
rest = _objectWithoutProperties(_this$props, _excluded);
const {
pillSelectorError,
selectedOptions
} = this.state;
const modalProps = omit(rest, ['contacts', 'defaultPersonalMessage', 'inviteePermissions', 'itemTypedID', 'onRequestClose', 'onUserInput', 'sendInvites', 'showUpgradeOptions']);
const selectorOptions = this.getSelectorOptions();
const title = /*#__PURE__*/React.createElement(FormattedMessage, _extends({}, messages.inviteCollaboratorsModalTitle, {
values: {
itemName
}
}));
const groupLabel = /*#__PURE__*/React.createElement(FormattedMessage, messages.groupLabel);
return /*#__PURE__*/React.createElement(Modal, _extends({
className: "invite-collaborators-modal",
closeButtonProps: {
[RESIN_TAG_TARGET]: 'close'
},
"data-resin-component": "modal",
"data-resin-feature": "invitecollaborators",
onRequestClose: this.closeModal,
title: title
}, modalProps), submissionError && /*#__PURE__*/React.createElement(InlineNotice, {
type: "error"
}, submissionError), collaborationRestrictionWarning && /*#__PURE__*/React.createElement(InlineNotice, {
type: "warning"
}, collaborationRestrictionWarning), /*#__PURE__*/React.createElement(PillSelectorDropdown, {
allowCustomPills: true,
error: pillSelectorError,
label: /*#__PURE__*/React.createElement(FormattedMessage, messages.inviteFieldLabel),
onInput: this.handlePillSelectorInput,
onRemove: this.handlePillRemove,
onSelect: this.handlePillSelect,
parseItems: parseEmails,
placeholder: intl.formatMessage(commonMessages.pillSelectorPlaceholder),
selectedOptions: selectedOptions,
selectorOptions: selectorOptions,
validateForError: this.validateForError,
validator: this.validator
}, selectorOptions.map(({
email,
text,
value
}) => /*#__PURE__*/React.createElement(ContactDatalistItem, {
key: value,
name: text,
subtitle: email || groupLabel
}))), itemType === 'file' ? this.renderFileCollabComponents() : this.renderFolderCollabComponents(), isEligibleForReferAFriendProgram && /*#__PURE__*/React.createElement(ReferAFriendAd, null), /*#__PURE__*/React.createElement(ModalActions, null, /*#__PURE__*/React.createElement(Button, {
"data-resin-target": "cancel",
isDisabled: submitting,
onClick: this.closeModal,
type: "button"
}, /*#__PURE__*/React.createElement(FormattedMessage, messages.inviteCollaboratorsModalCancelButton)), /*#__PURE__*/React.createElement(PrimaryButton, _extends({}, inviteButtonProps, {
isDisabled: submitting,
isLoading: submitting,
onClick: this.sendInvites
}), /*#__PURE__*/React.createElement(FormattedMessage, messages.inviteCollaboratorsModalSendInvitesButton))));
}
}
_defineProperty(InviteCollaboratorsModal, "propTypes", {
/** Message warning about restrictions regarding inviting collaborators to the item */
collaborationRestrictionWarning: PropTypes.node,
/** An array of contacts for the pill selector dropdown */
contacts: PropTypes.arrayOf(PropTypes.shape({
email: PropTypes.string,
id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isrequired,
name: PropTypes.string.isRequired,
type: PropTypes.string.isRequired
})).isRequired,
/**
* Default personal message for inviting collaborators to the item.
* Do not include if no personal message textarea should show up.
* Only applicable to non-file item types.
* */
defaultPersonalMessage: PropTypes.node,
intl: PropTypes.any,
/** Props for the invite button */
inviteButtonProps: PropTypes.object,
/** An array of invitee permissions */
inviteePermissions: PropTypes.arrayOf(PropTypes.shape({
disabled: PropTypes.bool,
text: PropTypes.string.isRequired,
value: PropTypes.string.isRequired
})),
/** If true, will render a link to the refer-a-friend reward center */
isEligibleForReferAFriendProgram: PropTypes.bool,
/** The name of the item to invite collaborators for */
itemName: PropTypes.string.isRequired,
/** The type of the item to invite collaborators for (e.g. "file", "folder") */
itemType: PropTypes.string.isRequired,
/** The typed id of the item to invite collaborators for */
itemTypedID: PropTypes.string.isRequired,
/** Handler function for closing the modal */
onRequestClose: PropTypes.func.isRequired,
/** Handler function whenever the user types, e.g. to fetch contacts. */
onUserInput: PropTypes.func,
/**
* Function to send collab invitations based on the given parameters object.
* This function should return a Promise.
*/
sendInvites: PropTypes.func.isRequired,
/**
* Flag to show link to upgrade and get more access controls.
* Only applicable to non-file item types.
*/
showUpgradeOptions: PropTypes.bool,
/** Message indicating an error occurred while sending the invites. */
submissionError: PropTypes.node,
/**
* Flag indicating whether the send invites request is submitting.
*/
submitting: PropTypes.bool
});
_defineProperty(InviteCollaboratorsModal, "defaultProps", {
inviteButtonProps: {}
});
export { InviteCollaboratorsModal as InviteCollaboratorsModalBase };
export default injectIntl(InviteCollaboratorsModal);
//# sourceMappingURL=InviteCollaboratorsModal.js.map