Skip to content

Commit

Permalink
Merge pull request #463 from ONLYOFFICE/feature/mobile-main-button
Browse files Browse the repository at this point in the history
Feature/mobile main button
  • Loading branch information
AlexeySafronov authored Jan 20, 2022
2 parents 7e13b3a + 6c72436 commit 4d6b9fe
Show file tree
Hide file tree
Showing 15 changed files with 724 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ import ButtonMoveIcon from "../../../../public/images/button.move.react.svg";
import ButtonDuplicateIcon from "../../../../public/images/button.duplicate.react.svg";
import ButtonAlertIcon from "../../../../public/images/button.alert.react.svg";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
import ButtonPlusIcon from "../../../../public/images/actions.button.plus.react.svg";
import ButtonMinusIcon from "../../../../public/images/actions.button.minus.react.svg";

const StyledButtonAlertIcon = styled(ButtonAlertIcon)`
${commonIconsStyles}
`;
const FloatingButton = ({ id, className, style, ...rest }) => {
const { icon, alert, percent, onClick } = rest;
const { icon, alert, percent, onClick, color } = rest;

return (
<StyledCircleWrap
Expand All @@ -40,7 +42,7 @@ const FloatingButton = ({ id, className, style, ...rest }) => {
<div className="circle__fill"></div>
</div>

<StyledFloatingButton>
<StyledFloatingButton color={color}>
<IconBox>
{icon == "upload" ? (
<ButtonUploadIcon />
Expand All @@ -50,6 +52,10 @@ const FloatingButton = ({ id, className, style, ...rest }) => {
<ButtonTrashIcon />
) : icon == "move" ? (
<ButtonMoveIcon />
) : icon == "plus" ? (
<ButtonPlusIcon />
) : icon == "minus" ? (
<ButtonMinusIcon />
) : (
<ButtonDuplicateIcon />
)}
Expand All @@ -67,10 +73,19 @@ FloatingButton.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
icon: PropTypes.oneOf(["upload", "file", "trash", "move", "duplicate"]),
icon: PropTypes.oneOf([
"upload",
"file",
"trash",
"move",
"duplicate",
"plus",
"minus",
]),
alert: PropTypes.bool,
percent: PropTypes.number,
onClick: PropTypes.func,
color: PropTypes.string,
};

FloatingButton.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ const StyledFloatingButton = styled.div`
width: 48px;
height: 48px;
border-radius: 50%;
background: #fff;
background: ${(props) => (props.color ? props.color : "#fff")};
box-shadow: 0px 5px 20px rgba(0, 0, 0, 0.13);
text-align: center;
margin: 3px;
Expand Down
74 changes: 74 additions & 0 deletions packages/asc-web-components/main-button-mobile/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# MainButtonMobile

### Usage

```js
import MainButtonMobile from "@appserver/components/main-button-mobile";
```

```jsx
const actionOptions = [
{
key: "1",
label: "New document",
icon: "static/images/mobile.actions.document.react.svg",
},
{
key: "2",
label: "New presentation",
icon: "static/images/mobile.actions.presentation.react.svg",
},
];

const buttonOptions = [
{
key: "1",
label: "Import point",
},
{
key: "2",
label: "Import point",
},
];

const progressOptions = [
{
key: "1",
label: "Uploads",
percent: 30,
status: `8/10`,
open: true,
},
];

<MainButtonMobile
style={{
top: "90%",
left: "82%",
position: "fixed",
}}
manualWidth="320px"
title="Upload"
withButton={true}
actionOptions={actionOptions}
progressOptions={progressOptions}
isOpenButton={true}
buttonOptions={buttonOptions}
/>;
```

| Props | Type | Required | Values | Default | Description |
| ----------------- | :------------: | :------: | :----: | :-----: | -------------------------------------------------------------------------------------------------- |
| `style` | `obj`, `array` | - | - | - | Accepts css style |
| `actionOptions` | `obj` | - | - | - | Options for drop down items |
| `progressOptions` | `obj` | - | - | - | If you need display progress bar components |
| `buttonOptions` | `obj` | - | - | - | Menu that opens by clicking on the button |
| `onUploadClick` | `func` | - | - | - | The function that will be called after the button click |
| `withButton` | `bool` | - | - | - | Show button inside drop down |
| `isOpenButton` | `bool` | - | - | - | The parameter that is used with buttonOptions is needed to open the menu by clicking on the button |
| `title` | `string` | - | | - | The name of the button in the drop down |
| `percent` | `number` | - | - | - | Loading indicator |
| `manualWidth` | `string` | - | - | - | Required if you need to specify the exact width of the drop down component |
| `opened` | `bool` | - | - | - | Tells when the dropdown should be opened |
| `className` | `string` | - | - | - | Accepts class |
| `onClose` | `func` | - | - | - | if you need close drop down |
255 changes: 255 additions & 0 deletions packages/asc-web-components/main-button-mobile/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
import React, { useState, useRef, useEffect } from "react";
import PropTypes from "prop-types";
import {
StyledFloatingButton,
StyledDropDown,
StyledDropDownItem,
StyledContainerAction,
StyledProgressBarContainer,
StyledMobileProgressBar,
StyledProgressContainer,
StyledBar,
StyledButtonWrapper,
StyledButtonOptions,
} from "./styled-main-button";
import IconButton from "../icon-button";
import Button from "../button";
import Text from "../text";
import Scrollbar from "@appserver/components/scrollbar";
import { isMobile, isTablet } from "react-device-detect";

const ProgressBarMobile = ({
label,
status,
percent,
open,
onCancel,
icon,
onClick,
error,
}) => {
const uploadPercent = percent > 100 ? 100 : percent;

return (
<StyledProgressBarContainer isUploading={open}>
<Text onClick={onClick} className="progress-header" color="#657077">
{label}
</Text>
<Text className="progress_count" color="#657077">
{status}
</Text>
<IconButton onClick={onCancel} iconName={icon} size={14} />
<StyledMobileProgressBar>
<StyledBar uploadPercent={uploadPercent} error={error} />
</StyledMobileProgressBar>
</StyledProgressBarContainer>
);
};

ProgressBarMobile.propTypes = {
label: PropTypes.string,
status: PropTypes.string,
percent: PropTypes.number,
open: PropTypes.bool,
onCancel: PropTypes.func,
icon: PropTypes.string,
/** The function that will be called after the progress header click */
onClick: PropTypes.func,
/** If true the progress bar changes color */
error: PropTypes.bool,
};

const MainButtonMobile = (props) => {
const {
className,
style,
opened,
onUploadClick,
actionOptions,
progressOptions,
buttonOptions,
percent,
title,
withButton,
manualWidth,
isOpenButton,
onClose,
} = props;

const [isOpen, setIsOpen] = useState(opened);
const [height, setHeight] = useState("90vh");

const divRef = useRef();

useEffect(() => {
if (opened !== isOpen) {
setIsOpen(opened);
}
}, [opened]);

useEffect(() => {
let height = divRef.current.getBoundingClientRect().height;
height >= window.innerHeight ? setHeight("90vh") : setHeight(height + "px");
}, [isOpen, window.innerHeight]);

const ref = useRef();

const dropDownRef = useRef();

const toggle = (isOpen) => {
if (isOpen && onClose) {
onClose();
}
return setIsOpen(isOpen);
};

const onMainButtonClick = (e) => {
if (isOpen && ref.current.contains(e.target)) return;
toggle(!isOpen);
};

const outsideClick = (e) => {
if (isOpen && ref.current.contains(e.target)) return;
toggle(false);
};

const isUploading = progressOptions
? progressOptions.filter((option) => option.open)
: [];

const renderItems = () => {
return (
<div ref={divRef}>
<StyledContainerAction>
{actionOptions.map((option) => (
<StyledDropDownItem
key={option.key}
label={option.label}
className={option.className}
onClick={option.onClick}
icon={option.icon ? option.icon : ""}
/>
))}
</StyledContainerAction>
<StyledProgressContainer
isUploading={isUploading.length > 0 ? true : false}
isOpenButton={isOpenButton}
>
{progressOptions &&
progressOptions.map((option) => (
<ProgressBarMobile
key={option.key}
label={option.label}
icon={option.icon}
className={option.className}
percent={option.percent}
status={option.status}
open={option.open}
onCancel={option.onCancel}
error={option.error}
/>
))}
</StyledProgressContainer>
<StyledButtonOptions isOpenButton={isOpenButton}>
{isOpenButton && buttonOptions
? buttonOptions.map((option) =>
option.isSeparator ? (
<div key={option.key} className="separator-wrapper">
<div className="is-separator" />
</div>
) : (
<StyledDropDownItem
className={`drop-down-item-button ${
option.isSeparator ? "is-separator" : ""
}`}
key={option.key}
label={option.label}
onClick={option.onClick}
icon={option.icon ? option.icon : ""}
/>
)
)
: ""}
</StyledButtonOptions>
{withButton && (
<StyledButtonWrapper
isUploading={isUploading.length > 0 ? true : false}
isOpenButton={isOpenButton}
>
<Button
label={title}
className="action-mobile-button"
primary
size="large"
onClick={onUploadClick}
/>
</StyledButtonWrapper>
)}
</div>
);
};

const children = renderItems();

return (
<div ref={ref} className={className} style={style}>
<StyledFloatingButton
icon={isOpen ? "minus" : "plus"}
onClick={onMainButtonClick}
percent={percent}
color={"#ed7309"}
/>
<StyledDropDown
open={isOpen}
clickOutsideAction={outsideClick}
manualWidth={manualWidth || "400px"}
directionY="top"
directionX="right"
isMobile={isMobile || isTablet}
heightProp={height}
>
{isMobile || isTablet ? (
<Scrollbar
scrollclass="section-scroll"
stype="mediumBlack"
ref={dropDownRef}
>
{children}
</Scrollbar>
) : (
children
)}
</StyledDropDown>
</div>
);
};

MainButtonMobile.propTypes = {
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Options for drop down items */
actionOptions: PropTypes.array.isRequired,
/** If you need display progress bar components */
progressOptions: PropTypes.array,
/** Menu that opens by clicking on the button */
buttonOptions: PropTypes.array,
/** The function that will be called after the button click */
onUploadClick: PropTypes.func,
/** Show button inside drop down */
withButton: PropTypes.bool,
/** The parameter that is used with buttonOptions is needed to open the menu by clicking on the button */
isOpenButton: PropTypes.bool,
/** The name of the button in the drop down */
title: PropTypes.string,
/** Loading indicator */
percent: PropTypes.number,
/** Required if you need to specify the exact width of the drop down component */
manualWidth: PropTypes.string,
className: PropTypes.string,
/** Tells when the dropdown should be opened */
opened: PropTypes.bool,
/** If you need close drop down */
onClose: PropTypes.func,
};

export default MainButtonMobile;
Loading

0 comments on commit 4d6b9fe

Please sign in to comment.