Merge branch 'release/v1.1.0' of github.com:ONLYOFFICE/AppServer into release/v1.1.0

This commit is contained in:
Tatiana Lopaeva 2021-11-19 18:18:13 +03:00
commit 56ecbc95ea
9 changed files with 263 additions and 308 deletions

View File

@ -369,8 +369,10 @@ class ContextMenu extends Component {
position(event) { position(event) {
if (event) { if (event) {
let left = event.pageX + 1; const rects = this.props.containerRef?.current.getBoundingClientRect();
let top = event.pageY + 1;
let left = rects ? rects.left : event.pageX + 1;
let top = rects ? rects.top : event.pageY + 1;
let width = this.menuRef.current.offsetParent let width = this.menuRef.current.offsetParent
? this.menuRef.current.offsetWidth ? this.menuRef.current.offsetWidth
: DomHelpers.getHiddenElementOuterWidth(this.menuRef.current); : DomHelpers.getHiddenElementOuterWidth(this.menuRef.current);
@ -399,6 +401,14 @@ class ContextMenu extends Component {
top = document.body.scrollTop; top = document.body.scrollTop;
} }
if (this.props.containerRef) {
top += rects.height + 4;
if (this.props.scaled) {
this.menuRef.current.style.width = rects.width + "px";
}
}
this.menuRef.current.style.left = left + "px"; this.menuRef.current.style.left = left + "px";
this.menuRef.current.style.top = top + "px"; this.menuRef.current.style.top = top + "px";
} }
@ -583,6 +593,10 @@ ContextMenu.propTypes = {
onShow: PropTypes.func, onShow: PropTypes.func,
/** Callback to invoke when a popup menu is hidden */ /** Callback to invoke when a popup menu is hidden */
onHide: PropTypes.func, onHide: PropTypes.func,
/** If you want to display relative to another component */
containerRef: PropTypes.any,
/** Scale with by container component*/
scaled: PropTypes.bool,
}; };
ContextMenu.defaultProps = { ContextMenu.defaultProps = {
@ -596,6 +610,8 @@ ContextMenu.defaultProps = {
appendTo: null, appendTo: null,
onShow: null, onShow: null,
onHide: null, onHide: null,
scaled: false,
containerRef: null,
}; };
export default ContextMenu; export default ContextMenu;

View File

@ -34,7 +34,7 @@ const StyledContextMenu = styled.div`
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin-left: 8px; margin-left: 4px;
margin-top: -4px; margin-top: -4px;
} }

View File

@ -1,104 +1,85 @@
import React from "react"; import React, { useRef, useState } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { ReactSVG } from "react-svg"; import { ReactSVG } from "react-svg";
import { handleAnyClick } from "../utils/event";
import Text from "../text"; import Text from "../text";
import { import {
StyledSecondaryButton, StyledSecondaryButton,
StyledMainButton, StyledMainButton,
StyledDropDown,
GroupMainButton, GroupMainButton,
} from "./styled-main-button"; } from "./styled-main-button";
import ContextMenu from "../context-menu";
class MainButton extends React.PureComponent { const MainButton = (props) => {
constructor(props) { const {
super(props); text,
model,
iconName,
isDropdown,
isDisabled,
clickAction,
clickActionSecondary,
} = props;
this.ref = React.createRef(); const ref = useRef();
const menuRef = useRef(null);
this.state = { const [isOpen, setIsOpen] = useState(props.opened);
isOpen: props.opened,
};
if (props.opened) handleAnyClick(true, this.handleClick); const stopAction = (e) => e.preventDefault();
}
handleClick = (e) => { const toggle = (e, isOpen) => {
if (this.state.isOpen && this.ref.current.contains(e.target)) return; isOpen ? menuRef.current.show(e) : menuRef.current.hide(e);
this.toggle(false);
setIsOpen(isOpen);
}; };
stopAction = (e) => e.preventDefault(); const onHide = () => {
setIsOpen(false);
};
toggle = (isOpen) => this.setState({ isOpen: isOpen }); const onMainButtonClick = (e) => {
if (!isDisabled) {
componentWillUnmount() { if (!isDropdown) {
handleAnyClick(false, this.handleClick); clickAction && clickAction(e);
}
componentDidUpdate(prevProps, prevState) {
// Store prevId in state so we can compare when props change.
// Clear out previously-loaded data (so we don't render stale stuff).
if (this.props.opened !== prevProps.opened) {
this.toggle(this.props.opened);
}
if (this.state.isOpen !== prevState.isOpen) {
handleAnyClick(this.state.isOpen, this.handleClick);
}
}
onMainButtonClick = (e) => {
if (!this.props.isDisabled) {
if (!this.props.isDropdown) {
this.props.clickAction && this.props.clickAction();
} else { } else {
this.toggle(!this.state.isOpen); toggle(e, !isOpen);
} }
} else { } else {
this.stopAction(e); stopAction(e);
} }
}; };
onDropDownClick = () => { const onSecondaryButtonClick = (e) => {
this.props.onClick && this.props.onClick(); if (!isDisabled) {
this.toggle(!this.state.isOpen); clickActionSecondary && clickActionSecondary();
};
onSecondaryButtonClick = (e) => {
if (!this.props.isDisabled) {
this.props.clickActionSecondary && this.props.clickActionSecondary();
} else { } else {
this.stopAction(e); stopAction(e);
} }
}; };
render() { const sideIcon = <ReactSVG src={iconName} width="16px" height="16px" />;
//console.log("MainButton render");
return ( return (
<GroupMainButton {...this.props} ref={this.ref}> <GroupMainButton {...props} ref={ref}>
<StyledMainButton {...this.props} onClick={this.onMainButtonClick}> <StyledMainButton {...props} onClick={onMainButtonClick}>
<Text className="main-button_text">{this.props.text}</Text> <Text className="main-button_text">{text}</Text>
</StyledMainButton> </StyledMainButton>
{this.props.isDropdown ? ( {isDropdown ? (
<StyledDropDown <ContextMenu
open={this.state.isOpen} model={model}
clickOutsideAction={this.handleClick} containerRef={ref}
{...this.props} ref={menuRef}
onClick={this.onDropDownClick} onHide={onHide}
/> scaled={true}
) : ( />
<StyledSecondaryButton ) : (
{...this.props} <StyledSecondaryButton {...props} onClick={onSecondaryButtonClick}>
onClick={this.onSecondaryButtonClick} {iconName && sideIcon}
> </StyledSecondaryButton>
{this.props.iconName && <ReactSVG src={this.props.iconName} />} )}
</StyledSecondaryButton> </GroupMainButton>
)} );
</GroupMainButton> };
);
}
}
MainButton.propTypes = { MainButton.propTypes = {
/** Button text */ /** Button text */
@ -115,14 +96,14 @@ MainButton.propTypes = {
iconName: PropTypes.string, iconName: PropTypes.string,
/** Open DropDown */ /** Open DropDown */
opened: PropTypes.bool, //TODO: Make us whole opened: PropTypes.bool, //TODO: Make us whole
/** DropDown component click action */
onClick: PropTypes.func,
/** Accepts class */ /** Accepts class */
className: PropTypes.string, className: PropTypes.string,
/** Accepts id */ /** Accepts id */
id: PropTypes.string, id: PropTypes.string,
/** Accepts css style */ /** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Menu data model */
model: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
}; };
MainButton.defaultProps = { MainButton.defaultProps = {

View File

@ -1,11 +1,9 @@
import React from "react"; import React from "react";
import MainButton from "."; import MainButton from ".";
import DropDownItem from "../drop-down-item";
export default { export default {
title: "Components/MainButton", title: "Components/MainButton",
component: MainButton, component: MainButton,
subcomponents: { DropDownItem },
parameters: { docs: { description: { component: "Components/MainButton" } } }, parameters: { docs: { description: { component: "Components/MainButton" } } },
clickAction: { action: "clickAction" }, clickAction: { action: "clickAction" },
clickActionSecondary: { action: "clickActionSecondary" }, clickActionSecondary: { action: "clickActionSecondary" },
@ -26,37 +24,51 @@ const Template = ({
clickActionSecondary(e, credentials); clickActionSecondary(e, credentials);
}; };
let icon = !args.isDropdown const itemsModel = [
? { {
iconName: "static/images/people.react.svg", label: "New document",
} icon: "/static/images/catalog.folder.react.svg",
: {}; },
{
label: "New spreadsheet",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "New presentation",
icon: "/static/images/catalog.folder.react.svg",
},
{
label: "Master form",
icon: "/static/images/catalog.folder.react.svg",
items: [
{
label: "From blank",
},
{
label: "From an existing text file",
},
],
},
{
label: "New folder",
icon: "/static/images/catalog.folder.react.svg",
},
{ separator: true },
{
label: "Upload",
icon: "/static/images/catalog.folder.react.svg",
},
];
return ( return (
<div style={{ width: "280px" }}> <div style={{ width: "280px" }}>
<MainButton <MainButton
{...args} {...args}
clickAction={clickMainButtonHandler} clickAction={clickMainButtonHandler}
clickActionSecondary={clickSecondaryButtonHandler} clickActionSecondary={clickSecondaryButtonHandler}
model={itemsModel}
iconName iconName
{...icon} ></MainButton>
>
<DropDownItem
icon="static/images/catalog.employee.react.svg"
label="New employee"
onClick={() => clickItem("New employee clicked")}
/>
<DropDownItem
icon="static/images/catalog.departments.react.svg"
label="New department"
onClick={() => clickItem("New department clicked")}
/>
<DropDownItem isSeparator />
<DropDownItem
icon="static/images/invitation.link.react.svg"
label="Invitation link"
onClick={() => clickItem("Invitation link clicked")}
/>
</MainButton>
</div> </div>
); );
}; };
@ -68,86 +80,3 @@ Default.args = {
text: "Actions", text: "Actions",
iconName: "static/images/people.react.svg", iconName: "static/images/people.react.svg",
}; };
/*
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import { text, boolean, withKnobs, select } from "@storybook/addon-knobs/react";
import Section from "../../../.storybook/decorators/section";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
const iconNames = Object.keys(Icons);
function ClickMainButton(e, credentials) {
console.log("ClickMainButton", e, credentials);
}
function ClickSecondaryButton(e, credentials) {
console.log("ClickSecondaryButton", e, credentials);
}
storiesOf("Components|MainButton", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add("base", () => {
let isDropdown = boolean("isDropdown", true);
let icon = !isDropdown
? {
iconName: `${select(
"iconName",
iconNames,
"static/images/people.react.svg"
)}`,
}
: {};
return (
<Section>
<MainButton
isDisabled={boolean("isDisabled", false)}
isDropdown={isDropdown}
text={text("text", "Actions")}
clickAction={ClickMainButton}
clickActionSecondary={ClickSecondaryButton}
{...icon}
>
<DropDownItem
icon="static/images/catalog.employee.react.svg"
label="New employee"
onClick={() => action("New employee clicked")}
/>
<DropDownItem
icon="CatalogGuestIcon"
label="New quest"
onClick={() => action("New quest clicked")}
/>
<DropDownItem
icon="static/images/catalog.departments.react.svg"
label="New department"
onClick={() => action("New department clicked")}
/>
<DropDownItem isSeparator />
<DropDownItem
icon="static/images/invitation.link.react.svg"
label="Invitation link"
onClick={() => action("Invitation link clicked")}
/>
<DropDownItem
icon="PlaneIcon"
label="Invite again"
onClick={() => action("Invite again clicked")}
/>
<DropDownItem
icon="ImportIcon"
label="Import people"
onClick={() => action("Import people clicked")}
/>
</MainButton>
</Section>
);
});
*/

View File

@ -7,5 +7,7 @@
"UploadFolder": "Upload folder", "UploadFolder": "Upload folder",
"NewForm": "Master form", "NewForm": "Master form",
"NewFormFile": "Master form from file", "NewFormFile": "Master form from file",
"CreateMasterFormFromFile": "Create Master Form from file" "CreateMasterFormFromFile": "Create Master Form from file",
"SubNewForm": "From blank",
"SubNewFormFile": "From an existing text file"
} }

View File

@ -372,7 +372,7 @@ export default function withContextOptions(WrappedComponent) {
return { return {
key: option, key: option,
label: t("Common:FillFormButton"), label: t("Common:FillFormButton"),
icon: "/static/images/access.edit.form.react.svg", icon: "/static/images/form.fill.rect.svg",
onClick: this.onClickLinkFillForm, onClick: this.onClickLinkFillForm,
disabled: false, disabled: false,
}; };

View File

@ -16,7 +16,7 @@ import withLoader from "../../../HOCs/withLoader";
class ArticleMainButtonContent extends React.Component { class ArticleMainButtonContent extends React.Component {
onCreate = (e) => { onCreate = (e) => {
// this.goToHomePage(); // this.goToHomePage();
const format = e.currentTarget.dataset.format || null; const format = e.action || null;
this.props.setAction({ this.props.setAction({
type: FileAction.Create, type: FileAction.Create,
extension: format, extension: format,
@ -79,68 +79,104 @@ class ArticleMainButtonContent extends React.Component {
isPrivacy, isPrivacy,
} = this.props; } = this.props;
const folderUpload = !isMobile
? [
{
className: "main-button_drop-down",
icon: "images/actions.upload.react.svg",
label: t("UploadFolder"),
disabled: isPrivacy,
onClick: this.onUploadFolderClick,
},
]
: [];
const formActions = !isMobile
? [
{
className: "main-button_drop-down",
icon: "images/form.react.svg",
label: t("NewForm"),
items: [
{
className: "main-button_drop-down_sub",
label: t("SubNewForm"),
onClick: this.onCreate,
action: "docxf",
},
{
className: "main-button_drop-down_sub",
label: t("SubNewFormFile"),
onClick: this.onShowSelectFileDialog,
},
],
},
]
: [
{
className: "main-button_drop-down_sub",
icon: "images/form.react.svg",
label: t("SubNewForm"),
onClick: this.onCreate,
action: "docxf",
},
{
className: "main-button_drop-down_sub",
icon: "images/form.file.react.svg",
label: t("SubNewFormFile"),
onClick: this.onShowSelectFileDialog,
},
];
const menuModel = [
{
className: "main-button_drop-down",
icon: "images/actions.documents.react.svg",
label: t("NewDocument"),
onClick: this.onCreate,
action: "docx",
},
{
className: "main-button_drop-down",
icon: "images/spreadsheet.react.svg",
label: t("NewSpreadsheet"),
onClick: this.onCreate,
action: "xlsx",
},
{
className: "main-button_drop-down",
icon: "images/actions.presentation.react.svg",
label: t("NewPresentation"),
onClick: this.onCreate,
action: "pptx",
},
...formActions,
{
className: "main-button_drop-down",
icon: "images/catalog.folder.react.svg",
label: t("NewFolder"),
onClick: this.onCreate,
},
{
isSeparator: true,
},
{
className: "main-button_drop-down",
icon: "images/actions.upload.react.svg",
label: t("UploadFiles"),
onClick: this.onUploadFileClick,
},
...folderUpload,
];
return ( return (
<MainButton <>
isDisabled={isDisabled ? isDisabled : !canCreate} <MainButton
isDropdown={true} isDisabled={isDisabled ? isDisabled : !canCreate}
text={t("Common:Actions")} isDropdown={true}
> text={t("Common:Actions")}
<DropDownItem model={menuModel}
className="main-button_drop-down"
icon="images/actions.documents.react.svg"
label={t("NewDocument")}
onClick={this.onCreate}
data-format="docx"
/> />
<DropDownItem
className="main-button_drop-down"
icon="images/spreadsheet.react.svg"
label={t("NewSpreadsheet")}
onClick={this.onCreate}
data-format="xlsx"
/>
<DropDownItem
className="main-button_drop-down"
icon="images/actions.presentation.react.svg"
label={t("NewPresentation")}
onClick={this.onCreate}
data-format="pptx"
/>
<DropDownItem
className="main-button_drop-down"
icon="images/form.react.svg"
label={t("NewForm")}
onClick={this.onCreate}
data-format="docxf"
/>
<DropDownItem
className="main-button_drop-down"
icon="images/form.file.react.svg"
label={t("NewFormFile")}
onClick={this.onShowSelectFileDialog}
/>
<DropDownItem
className="main-button_drop-down"
icon="images/catalog.folder.react.svg"
label={t("NewFolder")}
onClick={this.onCreate}
/>
<DropDownItem isSeparator />
<DropDownItem
className="main-button_drop-down"
icon="images/actions.upload.react.svg"
label={t("UploadFiles")}
onClick={this.onUploadFileClick}
/>
{!isMobile && (
<DropDownItem
className="main-button_drop-down"
icon="images/actions.upload.react.svg"
label={t("UploadFolder")}
disabled={isPrivacy}
onClick={this.onUploadFolderClick}
/>
)}
<input <input
id="customFileInput" id="customFileInput"
className="custom-file-input" className="custom-file-input"
@ -162,7 +198,7 @@ class ArticleMainButtonContent extends React.Component {
ref={(input) => (this.inputFolderElement = input)} ref={(input) => (this.inputFolderElement = input)}
style={{ display: "none" }} style={{ display: "none" }}
/> />
</MainButton> </>
); );
} }
} }

View File

@ -69,67 +69,55 @@ class ArticleMainButtonContent extends React.Component {
const { dialogVisible } = this.state; const { dialogVisible } = this.state;
const menuModel = [
{
icon: combineUrl(
AppServerConfig.proxyURL,
homepage,
"/images/add.employee.react.svg"
),
label: userCaption,
onClick: this.goToEmployeeCreate,
},
{
icon: combineUrl(
AppServerConfig.proxyURL,
homepage,
"/images/add.guest.react.svg"
),
label: guestCaption,
onClick: this.goToGuestCreate,
},
{
icon: combineUrl(
AppServerConfig.proxyURL,
homepage,
"/images/add.department.react.svg"
),
label: groupCaption,
onClick: this.goToGroupCreate,
},
{
isSeparator: true,
},
{
icon: combineUrl(
AppServerConfig.proxyURL,
"/static/images/invitation.link.react.svg"
),
label: t("Translations:InviteLinkTitle"),
onClick: this.onInvitationDialogClick,
},
];
return isAdmin ? ( return isAdmin ? (
<> <>
<MainButton <MainButton
isDisabled={false} isDisabled={false}
isDropdown={true} isDropdown={true}
text={t("Common:Actions")} text={t("Common:Actions")}
> model={menuModel}
<DropDownItem />
icon={combineUrl(
AppServerConfig.proxyURL,
homepage,
"/images/add.employee.react.svg"
)}
label={userCaption}
onClick={this.goToEmployeeCreate}
/>
<DropDownItem
icon={combineUrl(
AppServerConfig.proxyURL,
homepage,
"/images/add.guest.react.svg"
)}
label={guestCaption}
onClick={this.goToGuestCreate}
/>
<DropDownItem
icon={combineUrl(
AppServerConfig.proxyURL,
homepage,
"/images/add.department.react.svg"
)}
label={groupCaption}
onClick={this.goToGroupCreate}
/>
<DropDownItem isSeparator />
<DropDownItem
icon={combineUrl(
AppServerConfig.proxyURL,
"/static/images/invitation.link.react.svg"
)}
label={t("Translations:InviteLinkTitle")}
onClick={this.onInvitationDialogClick}
/>
{/* <DropDownItem
icon="images/plane.react.svg"
label={t("SendInvitesAgain")}
onClick={this.onNotImplementedClick.bind(this, t("SendInvitesAgain"))}
/> */}
{false && (
<DropDownItem
icon={combineUrl(
AppServerConfig.proxyURL,
homepage,
"/images/import.react.svg"
)}
label={t("ImportPeople")}
onClick={this.onImportClick}
/>
)}
</MainButton>
{dialogVisible && ( {dialogVisible && (
<InviteDialog <InviteDialog
visible={dialogVisible} visible={dialogVisible}

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 1C0.447715 1 0 1.44772 0 2V6C0 6.55228 0.447715 7 1 7H15C15.5523 7 16 6.55228 16 6V2C16 1.44772 15.5523 1 15 1H1ZM1 9C0.447715 9 0 9.44771 0 10C0 10.5523 0.447715 11 1 11H15C15.5523 11 16 10.5523 16 10C16 9.44771 15.5523 9 15 9H1ZM6 14C6 13.4477 6.44772 13 7 13H15C15.5523 13 16 13.4477 16 14C16 14.5523 15.5523 15 15 15H7C6.44772 15 6 14.5523 6 14ZM2.5 3C2.22386 3 2 3.22386 2 3.5V4.5C2 4.77614 2.22386 5 2.5 5H13.5C13.7761 5 14 4.77614 14 4.5V3.5C14 3.22386 13.7761 3 13.5 3H2.5Z" fill="#657077"/>
</svg>

After

Width:  |  Height:  |  Size: 655 B