Merge branch 'feature/virtual-rooms-1.2' into feature/modal-redesign

This commit is contained in:
mushka 2022-04-19 14:18:53 +03:00
commit 0cafa97440
234 changed files with 4347 additions and 3463 deletions

View File

@ -1,5 +1,5 @@
REM echo ######## Set variables ########
set "msbuild4="C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe""
set "publisher="Ascensio System SIA""
REM echo ######## Extracting and preparing files to build ########
%sevenzip% x build\install\win\nginx-1.21.1.zip -o"build\install\win\Files" -y
@ -29,7 +29,7 @@ del /f /q "build\install\win\kafka-zookeeper\zookeeper\conf\zoo_sample.cfg"
rmdir build\install\win\publish /s /q
REM echo ######## Build Utils ########
%msbuild4% build\install\win\CustomActions\C#\Utils\Utils.csproj
%msbuild% build\install\win\CustomActions\C#\Utils\Utils.csproj
copy build\install\win\CustomActions\C#\Utils\bin\Debug\Utils.CA.dll build\install\win\Utils.CA.dll /y
rmdir build\install\win\CustomActions\C#\Utils\bin /s /q
rmdir build\install\win\CustomActions\C#\Utils\obj /s /q
@ -80,7 +80,7 @@ copy "build\install\win\publish\Apache ZooKeeper.msi" "build\install\win\Apache
copy "build\install\win\publish\Apache Kafka.msi" "build\install\win\Apache Kafka.msi" /y
REM echo ######## Build MySQL Server Installer ########
iscc "build\install\win\MySQL Server Installer Runner.iss"
iscc /Qp /S"byparam="signtool" sign /a /n "%publisher%" /t http://timestamp.digicert.com $f" "build\install\win\MySQL Server Installer Runner.iss"
REM echo ######## Build AppServer package ########
%AdvancedInstaller% /edit build\install\win\AppServer.aip /SetVersion %BUILD_VERSION%.%BUILD_NUMBER%

View File

@ -175,7 +175,7 @@ namespace ASC.Core
public UserInfo GetUserBySid(string sid)
{
return GetUsersInternal()
.FirstOrDefault(u => u.Sid != null && string.Equals(u.Sid , sid, StringComparison.CurrentCultureIgnoreCase)) ?? Constants.LostUser;
.FirstOrDefault(u => u.Sid != null && string.Equals(u.Sid, sid, StringComparison.CurrentCultureIgnoreCase)) ?? Constants.LostUser;
}
public UserInfo GetSsoUserByNameId(string nameId)
@ -518,6 +518,11 @@ namespace ASC.Core
{
var group = UserService.GetGroup(Tenant.TenantId, groupID);
if (group == null)
{
group = ToGroup(Constants.BuildinGroups.FirstOrDefault(r => r.ID == groupID));
}
return new GroupInfo
{
ID = group.Id,

View File

@ -321,11 +321,11 @@ namespace ASC.Core.Notify.Signalr
}
}
public void StopEdit<T>(T fileId, string room, string data)
public void StopEdit<T>(T fileId, string room)
{
try
{
MakeRequest("stop-edit", new { room, fileId, data });
MakeRequest("stop-edit", new { room, fileId });
}
catch (Exception error)
{

View File

@ -275,6 +275,7 @@ namespace ASC.Security.Cryptography
case ConfirmType.PhoneAuth:
case ConfirmType.TfaActivation:
case ConfirmType.TfaAuth:
case ConfirmType.Auth:
checkKeyResult = Provider.ValidateEmailKey(email + type, key, Provider.ValidAuthKeyInterval);
break;

View File

@ -43,7 +43,7 @@ namespace ASC.IPSecurity
public IPRestrictionsServiceCache(ICacheNotify<IPRestrictionItem> notify, ICache cache)
{
Cache = cache;
notify.Subscribe((r) => Cache.Remove(GetCacheKey(r.TenantId)), CacheNotifyAction.Any);
notify.Subscribe((r) => Cache.Remove(GetCacheKey(r.TenantId)), CacheNotifyAction.InsertOrUpdate);
Notify = notify;
}

View File

@ -101,9 +101,9 @@
filesIO.to(room).emit("s:start-edit-file", fileId);
}
function stopEdit({ fileId, room, data } = {}) {
function stopEdit({ fileId, room } = {}) {
logger.info(`stop edit file ${fileId} in room ${room}`);
filesIO.to(room).emit("s:stop-edit-file", fileId, data);
filesIO.to(room).emit("s:stop-edit-file", fileId);
}
function modifyFolder(room, cmd, id, type, data) {

View File

@ -554,7 +554,8 @@ namespace ASC.ElasticSearch
while (!string.IsNullOrEmpty(name = TryGetName(expression, out var member)))
{
sourceExprText = "." + name + sourceExprText;
sourceExprText = "." + name + sourceExprText;
expression = member.Expression;
}
if (isList)

View File

@ -3,6 +3,7 @@ import axios from "axios";
import FilesFilter from "./filter";
import { FolderType } from "../../constants";
import find from "lodash/find";
import { getFolderOptions } from "../../utils";
export function openEdit(fileId, version, doc, view) {
const params = []; // doc ? `?doc=${doc}` : "";
@ -48,19 +49,7 @@ export function getFolderPath(folderId) {
}
export function getFolder(folderId, filter) {
if (folderId && typeof folderId === "string") {
folderId = encodeURIComponent(folderId.replace(/\\\\/g, "\\"));
}
const params =
filter && filter instanceof FilesFilter
? `${folderId}?${filter.toApiUrlParams()}`
: folderId;
const options = {
method: "get",
url: `/files/${params}`,
};
const options = getFolderOptions(folderId, filter);
return request(options);
}
@ -181,6 +170,38 @@ export function getFoldersTree() {
);
}
export function getCommonFoldersTree() {
const index = 1;
return request({ method: "get", url: "/files/@common" }).then(
(commonFolders) => {
return [
{
id: commonFolders.current.id,
key: `0-${index}`,
parentId: commonFolders.current.parentId,
title: commonFolders.current.title,
rootFolderType: +commonFolders.current.rootFolderType,
rootFolderName: "@common",
pathParts: commonFolders.pathParts,
foldersCount: commonFolders.current.foldersCount,
newItems: commonFolders.new,
},
];
}
);
}
export function getThirdPartyCommonFolderTree() {
return request({ method: "get", url: "/files/thirdparty/common" }).then(
(commonThirdPartyArray) => {
commonThirdPartyArray.map((currentValue, index) => {
commonThirdPartyArray[index].key = `0-${index}`;
});
return commonThirdPartyArray;
}
);
}
export function getMyFolderList(filter = FilesFilter.getDefault()) {
const options = {
method: "get",

View File

@ -415,13 +415,6 @@ export function validateTfaCode(code) {
});
}
export function getCommonThirdPartyList() {
const options = {
method: "get",
url: "/files/thirdparty/common",
};
return request(options);
}
export function getBackupStorage() {
const options = {
method: "get",

View File

@ -31,7 +31,7 @@ const Article = ({
toggleShowText,
toggleArticleOpen,
setIsMobileArticle,
isLoading,
isLoadedPage,
children,
...rest
}) => {
@ -117,7 +117,7 @@ const Article = ({
handleWrapperClass="resizable-border not-selectable"
>
<SubArticleHeader
isLoading={isLoading}
isLoadedPage={isLoadedPage}
showText={showText}
onClick={toggleShowText}
>
@ -128,7 +128,7 @@ const Article = ({
{articleMainButtonContent.props.children}
</SubArticleMainButton>
) : null}
<SubArticleBody isLoading={isLoading} showText={showText}>
<SubArticleBody showText={showText}>
{articleBodyContent ? articleBodyContent.props.children : null}
</SubArticleBody>
</Resizable>

View File

@ -1,10 +1,8 @@
import React from "react";
import Scrollbar from "@appserver/components/scrollbar";
import LoaderArticleBody from "./article-body-loader";
const ArticleBody = ({ children, isLoading = false }) => {
return isLoading ? (
<LoaderArticleBody />
) : (
const ArticleBody = ({ children }) => {
return (
<Scrollbar className="article-body__scrollbar" stype="mediumBlack">
{children}
</Scrollbar>

View File

@ -1,9 +1,9 @@
import React from "react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import Loaders from "@appserver/common/components/Loaders";
import { isTablet as isTabletUtils } from "@appserver/components/utils/device";
import { isTablet } from "react-device-detect";
import { inject, observer } from "mobx-react";
import {
StyledArticleHeader,
StyledHeading,
@ -15,12 +15,22 @@ const ArticleHeader = ({
showText,
children,
onClick,
isLoading = false,
isLoadedPage,
isLoaded,
tReady,
setIsLoadedArticleHeader,
...rest
}) => {
const heightLoader = isTabletUtils() || isTablet ? "20px" : "32px";
const isLoadedSetting = isLoaded;
const commonSettings = location.pathname.includes("common");
return isLoading ? (
useEffect(() => {
if (isLoadedSetting) setIsLoadedArticleHeader(isLoadedSetting);
}, [isLoadedSetting]);
const heightLoader = isTabletUtils() || isTablet ? "20px" : "32px";
const showLoader = commonSettings ? !isLoadedPage : false;
return showLoader ? (
<StyledArticleHeader>
<Loaders.ArticleHeader height={heightLoader} className="loader" />
</StyledArticleHeader>
@ -45,4 +55,11 @@ ArticleHeader.propTypes = {
ArticleHeader.displayName = "Header";
export default React.memo(ArticleHeader);
export default inject(({ common }) => {
const { isLoaded, setIsLoadedArticleHeader } = common;
return {
isLoaded,
setIsLoadedArticleHeader,
};
})(observer(ArticleHeader));

View File

@ -9,10 +9,10 @@ const StyledFilter = styled.div`
grid-template-rows: 1fr;
grid-column-gap: 8px;
${isMobile &&
/* ${isMobile &&
css`
margin-top: -22px;
`}
`} */
@media ${mobile} {
grid-template-columns: 1fr 50px;

View File

@ -3,7 +3,14 @@ import PropTypes from "prop-types";
import { StyledRow } from "./StyledListLoader";
import RectangleLoader from "../RectangleLoader";
const ListItemLoader = ({ id, className, style, isRectangle, ...rest }) => {
const ListItemLoader = ({
id,
className,
style,
withoutFirstRectangle,
withoutLastRectangle,
...rest
}) => {
const {
title,
borderRadius,
@ -16,8 +23,14 @@ const ListItemLoader = ({ id, className, style, isRectangle, ...rest }) => {
} = rest;
return (
<StyledRow id={id} className={className} style={style}>
{isRectangle && (
<StyledRow
id={id}
className={className}
style={style}
withoutFirstRectangle={withoutFirstRectangle}
withoutLastRectangle={withoutLastRectangle}
>
{!withoutFirstRectangle && (
<RectangleLoader
title={title}
width="16"
@ -60,18 +73,20 @@ const ListItemLoader = ({ id, className, style, isRectangle, ...rest }) => {
animate={animate}
/>
<RectangleLoader
title={title}
width="16"
height="16"
borderRadius={borderRadius}
backgroundColor={backgroundColor}
foregroundColor={foregroundColor}
backgroundOpacity={backgroundOpacity}
foregroundOpacity={foregroundOpacity}
speed={speed}
animate={animate}
/>
{!withoutLastRectangle && (
<RectangleLoader
title={title}
width="16"
height="16"
borderRadius={borderRadius}
backgroundColor={backgroundColor}
foregroundColor={foregroundColor}
backgroundOpacity={backgroundOpacity}
foregroundOpacity={foregroundOpacity}
speed={speed}
animate={animate}
/>
)}
</StyledRow>
);
};
@ -80,14 +95,16 @@ ListItemLoader.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
isRectangle: PropTypes.bool,
withoutFirstRectangle: PropTypes.bool,
withoutLastRectangle: PropTypes.bool,
};
ListItemLoader.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
isRectangle: true,
withoutFirstRectangle: false,
withoutLastRectangle: false,
};
export default ListItemLoader;

View File

@ -9,21 +9,26 @@ const StyledRow = styled.div`
width: 100%;
display: grid;
grid-template-columns: 16px 32px 1fr 16px;
${(props) =>
props.withoutFirstRectangle &&
props.withoutLastRectangle &&
"grid-template-columns: 32px 1fr"};
${(props) =>
props.withoutFirstRectangle &&
!props.withoutLastRectangle &&
"grid-template-columns: 32px 1fr 16px"};
grid-template-rows: 1fr;
grid-column-gap: 8px;
margin-bottom: 16px;
justify-items: center;
align-items: center;
.list-loader_rectangle {
padding-right: 4px;
}
.list-loader_rectangle-content {
width: 32px;
height: 32px;
}
.list-loader_rectangle-row {
margin-right: auto;
max-width: 167px;

View File

@ -0,0 +1,24 @@
import styled from "styled-components";
import { desktop } from "@appserver/components/utils/device";
const StyledTreeFolder = styled.div`
padding-right: 16px;
`;
const StyledLoader = styled.div`
width: 100%;
display: grid;
grid-template-columns: 8px 16px 1fr;
grid-template-rows: 1fr;
grid-column-gap: 6px;
margin-bottom: 8px;
box-sizing: border-box;
.tree-node-loader_additional-rectangle {
padding-top: 4px;
}
${(props) => props.paddingLeft && `padding-left: ${props.paddingLeft}`};
`;
export { StyledLoader, StyledTreeFolder };

View File

@ -0,0 +1,51 @@
import React from "react";
import PropTypes from "prop-types";
import { StyledTreeFolder, StyledLoader } from "./StyledTreeFolder";
import TreeNodeLoader from "../TreeNodeLoader";
const NewTreeFolderLoader = ({ id, className, style, ...rest }) => {
return (
<StyledTreeFolder id={id} className={className} style={style}>
<StyledLoader>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"16px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"32px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"32px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"32px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"16px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
<StyledLoader paddingLeft={"16px"}>
<TreeNodeLoader {...rest} withRectangle />
</StyledLoader>
</StyledTreeFolder>
);
};
NewTreeFolderLoader.propTypes = {
id: PropTypes.string,
className: PropTypes.string,
style: PropTypes.object,
};
NewTreeFolderLoader.defaultProps = {
id: undefined,
className: undefined,
style: undefined,
};
export default NewTreeFolderLoader;

View File

@ -11,6 +11,7 @@ const TreeNodeLoader = ({
foregroundOpacity,
speed,
animate,
withRectangle = false,
}) => {
return (
<>
@ -25,6 +26,23 @@ const TreeNodeLoader = ({
speed={speed}
animate={animate}
/>
{withRectangle && (
<RectangleLoader
title={title}
width="16"
height="16"
borderRadius={borderRadius}
backgroundColor={backgroundColor}
foregroundColor={foregroundColor}
backgroundOpacity={backgroundOpacity}
foregroundOpacity={foregroundOpacity}
speed={speed}
animate={animate}
className="tree-node-loader_additional-rectangle"
/>
)}
<RectangleLoader
title={title}
width="100%"

View File

@ -7,6 +7,7 @@ import ArticleButton from "./ArticleButtonLoader";
import ArticleFolder from "./ArticleFolderLoader";
import ArticleGroup from "./ArticleGroupsLoader";
import TreeFolders from "./TreeFolderLoader";
import NewTreeFolders from "./NewTreeFolderLoader";
import TreeSettingsLoader from "./TreeSettingsLoader";
import Row from "./RowLoader";
import Rows from "./RowsLoader";
@ -46,4 +47,5 @@ export default {
ArticleFolder,
ArticleGroup,
ListLoader,
NewTreeFolders,
};

View File

@ -250,8 +250,13 @@ class SettingsStore {
this.isLoaded = isLoaded;
};
setCultures = (cultures) => {
this.cultures = cultures;
};
getPortalCultures = async () => {
this.cultures = await api.settings.getPortalCultures();
const cultures = await api.settings.getPortalCultures();
this.setCultures(cultures);
};
setIsEncryptionSupport = (isEncryptionSupport) => {

View File

@ -42,10 +42,8 @@ class TfaStore {
this.backupCodes = codes;
};
getTfaConfirmLink = async (res) => {
if (res) {
return await api.settings.getTfaConfirmLink();
}
getTfaConfirmLink = async () => {
return await api.settings.getTfaConfirmLink();
};
getSecretKeyAndQR = async (confirmKey) => {

View File

@ -324,3 +324,22 @@ export function convertLanguage(key) {
return key;
}
import FilesFilter from "../api/files/filter";
export function getFolderOptions(folderId, filter) {
if (folderId && typeof folderId === "string") {
folderId = encodeURIComponent(folderId.replace(/\\\\/g, "\\"));
}
const params =
filter && filter instanceof FilesFilter
? `${folderId}?${filter.toApiUrlParams()}`
: folderId;
const options = {
method: "get",
url: `/files/${params}`,
};
return options;
}

View File

@ -37,7 +37,14 @@ Button.propTypes = {
/** Size of button.
The normal size equals 36px and 40px in height on the Desktop and Touchcreen devices. */
size: PropTypes.oneOf(["extraSmall", "small", "normal", "medium"]),
size: PropTypes.oneOf([
"extraSmall",
"small",
"normal",
"medium",
"normalDesktop",
"normalTouchscreen",
]),
/** Scale width of button to 100% */
scale: PropTypes.bool,
/** Icon node element */

View File

@ -156,8 +156,7 @@ const StyledButton = styled(ButtonWrapper).attrs((props) => ({
: props.theme.button.border.base};
${(props) => props.scale && `width: 100%;`};
min-width: ${(props) =>
props.minwidth ? props.minwidth : props.theme.button.minWidth[props.size]};
min-width: ${(props) => props.minwidth && props.minwidth};
padding: ${(props) => `${props.theme.button.padding[props.size]}`};
@ -214,6 +213,8 @@ const StyledButton = styled(ButtonWrapper).attrs((props) => ({
}
.button-content {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;

View File

@ -127,7 +127,6 @@ const MainButtonMobile = (props) => {
};
const onMainButtonClick = (e) => {
if (isOpen && ref.current.contains(e.target)) return;
toggle(!isOpen);
};
@ -191,8 +190,8 @@ const MainButtonMobile = (props) => {
/>
))}
</StyledProgressContainer>
<StyledButtonOptions isOpenButton={isOpenButton}>
{isOpenButton && buttonOptions
<StyledButtonOptions>
{buttonOptions
? buttonOptions.map((option) =>
option.isSeparator ? (
<div key={option.key} className="separator-wrapper">
@ -213,20 +212,6 @@ const MainButtonMobile = (props) => {
)
: ""}
</StyledButtonOptions>
{withButton && (
<StyledButtonWrapper
isUploading={isUploading}
isOpenButton={isOpenButton}
>
<Button
label={title}
className="action-mobile-button"
primary
size="medium"
onClick={onUploadClick}
/>
</StyledButtonWrapper>
)}
</div>
);
};

View File

@ -134,7 +134,6 @@ const StyledDropDownItem = styled(DropDownItem)`
`;
const StyledButtonOptions = styled.div`
display: ${(props) => !props.isOpenButton && "none"};
padding: 16px 0;
background-color: ${(props) =>
props.theme.mainButtonMobile.buttonOptions.backgroundColor};

View File

@ -162,6 +162,7 @@ class ModalDialog extends React.Component {
className={className}
isLarge={isLarge}
zIndex={zIndex}
<<<<<<< HEAD
autoMaxHeight={autoMaxHeight}
autoMaxWidth={autoMaxWidth}
fadeType={this.state.fadeType}
@ -174,6 +175,48 @@ class ModalDialog extends React.Component {
visible={visible}
modalSwipeOffset={this.state.modalSwipeOffset}
/>
=======
withBackground={true}
isModalDialog
>
<Dialog
width={width}
className={`${className} not-selectable`}
id={id}
style={style}
>
<Content
contentHeight={contentHeight}
contentWidth={contentWidth}
displayType={this.state.displayType}
>
{isLoading ? (
<Loaders.DialogLoader bodyHeight={modalLoaderBodyHeight} />
) : (
<>
<StyledHeader>
<Heading className="heading" size="medium" truncate={true}>
{header ? header.props.children : null}
</Heading>
{!withoutCloseButton && (
<CloseButton
className="modal-dialog-button_close"
onClick={onClose}
></CloseButton>
)}
</StyledHeader>
<BodyBox paddingProp={modalBodyPadding}>
{body ? body.props.children : null}
</BodyBox>
<Box className="modal-dialog-modal-footer">
{footer ? footer.props.children : null}
</Box>
</>
)}
</Content>
</Dialog>
</Backdrop>
>>>>>>> feature/virtual-rooms-1.2
) : (
<ModalAside
id={id}

View File

@ -108,7 +108,6 @@ RadioButtonGroup.defaultProps = {
selected: undefined,
spacing: "15px",
orientation: "horizontal",
width: "100%",
};
export default RadioButtonGroup;

View File

@ -17,7 +17,7 @@ const StyledDiv = styled(ClearDiv)`
`) ||
(props.orientation === "vertical" &&
css`
display: block;
display: inline-block;
`)};
width: ${(props) => props.width};

View File

@ -47,6 +47,7 @@ class SaveCancelButtons extends React.Component {
isFirstWelcomePageSettings,
className,
id,
isSaving,
} = this.props;
const cancelButtonDisabled =
@ -69,11 +70,12 @@ class SaveCancelButtons extends React.Component {
onClick={onSaveClick}
label={saveButtonLabel}
minwidth={displaySettings && "auto"}
isLoading={isSaving}
/>
<Button
className="cancel-button"
size="normal"
isDisabled={cancelButtonDisabled}
isDisabled={cancelButtonDisabled || isSaving}
onClick={onCancelClick}
label={cancelButtonLabel}
minwidth={displaySettings && "auto"}
@ -109,6 +111,7 @@ SaveCancelButtons.propTypes = {
hasScroll: PropTypes.bool,
minwidth: PropTypes.string,
isFirstWelcomePageSettings: PropTypes.string,
isSaving: PropTypes.bool,
};
SaveCancelButtons.defaultProps = {

View File

@ -5,7 +5,7 @@ import StyledButton from "./styled-selector-add-button";
import IconButton from "../icon-button";
const SelectorAddButton = (props) => {
const { isDisabled, title, className, id, style } = props;
const { isDisabled, title, className, id, style, iconName } = props;
const onClick = (e) => {
!isDisabled && props.onClick && props.onClick(e);
@ -22,7 +22,7 @@ const SelectorAddButton = (props) => {
>
<IconButton
size={14}
iconName="/static/images/actions.header.touch.react.svg"
iconName={iconName}
isFill={true}
isDisabled={isDisabled}
isClickable={!isDisabled}
@ -44,10 +44,13 @@ SelectorAddButton.propTypes = {
id: PropTypes.string,
/** Accepts css style */
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
/** Specifies the icon name */
iconName: PropTypes.string,
};
SelectorAddButton.defaultProps = {
isDisabled: false,
iconName: "/static/images/actions.header.touch.react.svg",
};
export default SelectorAddButton;

View File

@ -12,8 +12,9 @@ import {
StyledSubmenuItems,
StyledSubmenuItemText,
} from "./styled-submenu";
import LoaderSubmenu from "./loader";
const Submenu = ({ data, startSelect = 0, onSelect, ...rest }) => {
const Submenu = ({ data, startSelect = 0, onSelect, isLoading, ...rest }) => {
if (!data) return null;
const [currentItem, setCurrentItem] = useState(
@ -74,29 +75,40 @@ const Submenu = ({ data, startSelect = 0, onSelect, ...rest }) => {
return (
<StyledSubmenu {...rest}>
<StyledSubmenuItems ref={submenuItemsRef} role="list">
{data.map((d) => {
const isActive = d.id === currentItem.id;
{isLoading ? (
<LoaderSubmenu />
) : (
<>
<StyledSubmenuItems ref={submenuItemsRef} role="list">
{data.map((d) => {
const isActive = d.id === currentItem.id;
return (
<StyledSubmenuItem key={d.id} id={d.id} onClick={selectSubmenuItem}>
<StyledSubmenuItemText>
<Text
color={isActive ? "#316DAA" : "#657077"}
fontSize="13px"
fontWeight="600"
truncate={false}
return (
<StyledSubmenuItem
key={d.id}
id={d.id}
onClick={selectSubmenuItem}
>
{d.name}
</Text>
</StyledSubmenuItemText>
<StyledSubmenuItemLabel color={isActive ? "#316DAA" : "none"} />
</StyledSubmenuItem>
);
})}
</StyledSubmenuItems>
<StyledSubmenuBottomLine />
<StyledSubmenuItemText>
<Text
color={isActive ? "#316DAA" : "#657077"}
fontSize="13px"
fontWeight="600"
truncate={false}
>
{d.name}
</Text>
</StyledSubmenuItemText>
<StyledSubmenuItemLabel
color={isActive ? "#316DAA" : "none"}
/>
</StyledSubmenuItem>
);
})}
</StyledSubmenuItems>
<StyledSubmenuBottomLine />
</>
)}
<StyledSubmenuContentWrapper>
{currentItem.content}
</StyledSubmenuContentWrapper>
@ -108,6 +120,7 @@ Submenu.propTypes = {
data: PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
startSelect: PropTypes.oneOfType([PropTypes.object, PropTypes.number]),
onSelect: PropTypes.func,
isLoading: PropTypes.bool,
};
export default Submenu;

View File

@ -1,5 +1,5 @@
import React from "react";
import styled from "styled-components";
import styled, { css } from "styled-components";
import Loaders from "@appserver/common/components/Loaders";
import { isTablet } from "react-device-detect";

View File

@ -168,7 +168,7 @@ class TabContainer extends Component {
selected={activeTab === index}
isDisabled={isDisabled}
>
<Text className="title_style" fontSize="13px">
<Text fontWeight={600} className="title_style" fontSize="13px">
{item.title}
</Text>
</Label>

View File

@ -128,14 +128,6 @@ const Base = {
medium: "0 32px",
},
minWidth: {
extraSmall: "none",
small: "100px",
normalDesktop: "100px",
normalTouchscreen: "100px",
medium: "100px",
},
color: {
base: black,
baseHover: black,
@ -520,6 +512,7 @@ const Base = {
minHeight: "47px",
width: "100%",
borderBottom: globalColors.grayLightMid,
backgroundColor: globalColors.lightHover,
minWidth: "160px",
overflow: "hidden",
textOverflow: "ellipsis",
@ -1904,7 +1897,6 @@ const Base = {
menuContainer: {
background: "linear-gradient(200.71deg, #2274aa 0%, #0f4071 100%)",
arrowTop: "#206FA4",
color: white,
},
@ -2416,6 +2408,13 @@ const Base = {
border: `1px solid ${grayMid}`,
borderBottom: `1px solid ${grayLightMid}`,
tile: {
background: globalColors.lightHover,
itemBackground: white,
itemBorder: grayMid,
itemActiveBorder: blueMain,
},
fill: gray,
hoverFill: grayMain,
},

View File

@ -127,14 +127,6 @@ const Dark = {
medium: "0 32px",
},
minWidth: {
extraSmall: "none",
small: "100px",
normalDesktop: "100px",
normalTouchscreen: "100px",
medium: "100px",
},
color: {
base: "#CCCCCC",
baseHover: "#FAFAFA",
@ -517,6 +509,7 @@ const Dark = {
minHeight: "47px",
width: "100%",
borderBottom: "#474747",
backgroundColor: globalColors.veryDarkGrey,
minWidth: "160px",
overflow: "hidden",
textOverflow: "ellipsis",
@ -1902,10 +1895,8 @@ const Dark = {
},
menuContainer: {
background:
"linear-gradient(226.22deg, #EBB67A 0.24%, #E9AC6B 11.61%, #E8A25D 22.98%, #E69850 34.34%, #E58D42 45.71%, #E38235 57.08%, #E27628 68.44%, #E06A1B 79.81%)",
arrowTop: "#EAB274",
color: "#22221f",
background: "linear-gradient(226.22deg, #606060 0.24%, #1F1F1F 79.81%)",
color: "rgba(255, 255, 255, 0.92)",
},
article: {
@ -2426,6 +2417,13 @@ const Dark = {
border: "1px solid #474747",
borderBottom: "1px solid #474747",
tile: {
background: globalColors.black,
itemBackground: "#242424",
itemBorder: gray,
itemActiveBorder: "#eeeeee",
},
fill: "#858585",
hoverFill: "#eeeeee",
},

View File

@ -22,6 +22,7 @@ const StyledTreeMenu = styled(Tree)`
.rc-tree-switcher {
margin-left: 0 !important;
padding-left: 16px;
margin-top: 1px;
}
@ -30,7 +31,7 @@ const StyledTreeMenu = styled(Tree)`
width: 18px;
height: 16px;
padding: 0;
margin-top: 4px;
margin-top: -4px;
}
${(props) =>
@ -44,22 +45,22 @@ const StyledTreeMenu = styled(Tree)`
margin-right: 6px !important;
}
.rc-tree-node-content-wrapper {
position: static !important;
margin-bottom: ${(props) => +props.gapBetweenNodes - 16 + "px;"};
padding-top: 4px;
}
${(props) =>
!props.isFullFillSelection &&
css`
span.rc-tree-node-selected {
width: min-content !important;
padding-right: 4px;
// width: min-content !important;
// padding-right: 4px;
max-width: 98%;
}
`}
& .rc-tree-node-selected .rc-tree-title {
${(props) => !props.isFullFillSelection && "width: calc(100% - 26px);"}
${(props) => !props.isFullFillSelection && "width: calc(100% - 16px);"}
margin-top: 2px;
}
&:not(.rc-tree-show-line) .rc-tree-switcher-noop {
@ -84,12 +85,34 @@ const StyledTreeMenu = styled(Tree)`
.rc-tree-child-tree-open {
display: block;
${(props) => props.disableSwitch && "margin: 0 0 25px 0;"}
margin-left: ${(props) => (props.disableSwitch ? "27px" : "8px")};
li:first-child {
margin-top: ${(props) => (props.disableSwitch ? "10px" : "6px")};
margin-top: ${(props) => (props.disableSwitch ? "10px" : "8px")};
margin-bottom: 8px;
margin-left: 0;
}
li {
padding-left: 16px;
}
}
.rc-tree-treenode-selected {
::after {
position: absolute;
display: block;
top: 0px;
width: 100%;
width: ${(props) => props.widthAdditional};
height: 36px;
background-color: #f3f4f4;
content: "";
z-index: 1;
right: -${(props) => props.multiplicationFactor - 4}px;
}
.span.rc-tree-node-selected {
}
}
.rc-tree-treenode-disabled > span:not(.rc-tree-switcher),
.rc-tree-treenode-disabled > a,
.rc-tree-treenode-disabled > a span {
@ -136,12 +159,12 @@ const StyledTreeMenu = styled(Tree)`
: ``}
@media (max-width: 1024px) {
margin-top: 20px !important;
.rc-tree-node-content-wrapper {
/* .rc-tree-node-content-wrapper {
margin-bottom: ${(props) =>
props.gapBetweenNodesTablet
? +props.gapBetweenNodesTablet - 16 + "px;"
: +props.gapBetweenNodes - 16 + "px;"};
}
props.gapBetweenNodesTablet
? +props.gapBetweenNodesTablet - 16 + "px;"
: +props.gapBetweenNodes - 16 + "px;"};
} */
& > li > .rc-tree-child-tree {
margin-left: 4px;
}
@ -151,7 +174,6 @@ const StyledTreeMenu = styled(Tree)`
StyledTreeMenu.defaultProps = { theme: Base };
const TreeMenu = React.forwardRef((props, ref) => {
//console.log("TreeMenu render");
const {
defaultExpandAll,
defaultExpandParent,
@ -193,6 +215,7 @@ const TreeMenu = React.forwardRef((props, ref) => {
gapBetweenNodesTablet,
isEmptyRootNode,
theme,
childrenCount,
} = props;
const expandedKeysProp = expandedKeys ? { expandedKeys: expandedKeys } : {};
@ -277,6 +300,10 @@ const TreeMenu = React.forwardRef((props, ref) => {
gapBetweenNodes={gapBetweenNodes}
gapBetweenNodesTablet={gapBetweenNodesTablet}
isEmptyRootNode={isEmptyRootNode}
widthAdditional={`calc(100% + ${
(childrenCount ? childrenCount : 1) * 32
}px)`}
multiplicationFactor={32}
>
{modifiedChildren}
</StyledTreeMenu>

View File

@ -123,7 +123,8 @@ const TreeNodeMenu = styled(TreeNode)`
${NoUserSelect}
.rc-tree-node-selected {
max-width: ${(props) => (props.newItems > 999 ? "71%" : "102%")} !important;
max-width: ${(props) => (props.newItems > 999 ? "71%" : "87%")} !important;
width: calc(100% - 10px) !important;
}
${(props) =>
@ -182,9 +183,12 @@ const TreeNodeMenu = styled(TreeNode)`
padding: 0;
}
.rc-tree-node-content-wrapper {
width: ${(props) => (props.disableSwitch ? "90%" : "108%")};
// width: ${(props) => (props.disableSwitch ? "90%" : "108%")};
width: calc(100% - 16px);
max-width: 87%;
display: inline-block;
position: relative;
z-index: 2;
/*min-width: ${(props) => (props.disableSwitch ? "160px" : "190px")};*/
// overflow: hidden;
@ -220,6 +224,7 @@ const TreeNodeMenu = styled(TreeNode)`
svg {
height: 16px;
margin-top: 10px;
}
}
span.rc-tree-iconEle {
@ -242,6 +247,8 @@ const TreeNodeMenu = styled(TreeNode)`
vertical-align: 1px;
height: 24px;
width: 8px;
z-index: 2;
position: relative;
}
span.rc-tree-switcher.rc-tree-icon__customize,
span.rc-tree-checkbox.rc-tree-icon__customize,
@ -324,7 +331,7 @@ const TreeNodeMenu = styled(TreeNode)`
? props.icon
? props.newItems > 999
? "calc(100% - 104px)"
: "calc(100% - 44px)"
: "calc(100% - 16px)"
: "calc(100% - 20px)"
: "100%"};
white-space: nowrap;
@ -333,13 +340,14 @@ const TreeNodeMenu = styled(TreeNode)`
color: ${(props) => props.theme.treeNode.title.color};
padding-left: ${(props) =>
props.icon || props.disableSwitch ? "0" : "20px"};
margin-top: 2px;
}
span.rc-tree-title:first-child {
max-width: 100%;
}
.rc-tree-node-selected {
background: ${(props) => props.theme.treeNode.selected.background};
//background: ${(props) => props.theme.treeNode.selected.background};
mix-blend-mode: normal;
border-radius: ${(props) => props.theme.treeNode.selected.borderRadius};
z-index: 0;
@ -354,6 +362,9 @@ const TreeNodeMenu = styled(TreeNode)`
props.theme.treeNode.selected.hoverBackgroundColor};
}
overflow: visible;
z-index: 2;
position: relative;
padding-top: 4px;
}
.newItem {

View File

@ -14,6 +14,8 @@ const globalColors = {
veryLightGrey: "#CACACA",
darkSilver: "#bbb",
silver: "#CCCCCC",
lightHover: "#F3F4F4",
veryDarkGrey: "#3D3D3D",
blueMain: "#2DA7DB",
blueHover: "#3DB8EC",

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{file}} adlı fayl artıq {{folder}} qovluğunda mövcuddur. ",
"ConflictResolveDescriptionFiles": "{{filesCount}} sənəd sayı artıq '{{folder}}' qovluğunda mövcuddur.",
"ConflictResolveDescription": "<1>{{file}}</1> adlı fayl artıq <1>{{folder}}</1> qovluğunda mövcuddur. ",
"ConflictResolveDescriptionFiles": "{{filesCount}} sənəd sayı artıq <1>{{folder}}<1> qovluğunda mövcuddur.",
"ConflictResolveSelectAction": "Xahiş edirik əməliyyatı seçin:",
"ConflictResolveTitle": "Üzərinə yazmanın təsdiqlənməsi",
"CreateDescription": "Qovluqda iki fərqli fayl olacaq. ",
@ -9,4 +9,4 @@
"OverwriteTitle": "Versiya yenilənməsi ilə üzərinə yazma",
"SkipDescription": "Heç bir fayl köçürülmədi. Orijinal fayl köçürülən yerdə dəyişikliksiz qaldı.",
"SkipTitle": "Atlamaq"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Файл с име {{file}} вече съществува в папка {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документа с едно и също име вече съществуват в папката '{{folder}}'.",
"ConflictResolveDescription": "Файл с име <1>{{file}}</1> вече съществува в папка <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документа с едно и също име вече съществуват в папката <1>{{folder}}<1>.",
"ConflictResolveSelectAction": "Моля, изберете действие:",
"ConflictResolveTitle": "Потвърждение за презаписване",
"CreateDescription": "Ще има два различни файла в папката.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Препишете с актуализацията на версията",
"SkipDescription": "Никой файл няма да бъде копиран. Оригиналният файл ще бъде задържан в целевата папка.",
"SkipTitle": "Пропусни"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Soubor s názvem {{file}} již existuje ve složce {{folder}}.",
"ConflictResolveDescriptionFiles": "Ve složce '{{folder}}' již existuje {{filesCount}} dokumentů se stejným názvem.",
"ConflictResolveDescription": "Soubor s názvem <1>{{file}}</1> již existuje ve složce <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "Ve složce <1>{{folder}}<1> již existuje {{filesCount}} dokumentů se stejným názvem.",
"ConflictResolveSelectAction": "Prosím, vyberte akci:",
"ConflictResolveTitle": "Potvrzení o přepsání",
"CreateDescription": "Ve složce budou dva různé soubory.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Přepsat aktualizací verze",
"SkipDescription": "Žádný soubor nebude zkopírován. Původní soubor bude zachován v cílové složce.",
"SkipTitle": "Přeskočit"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Die Datei {{file}} existiert bereits im Ordner {{folder}}",
"ConflictResolveDescriptionFiles": "im Ordner '{{folder}}' sind bereits {{filesCount}} Dokumente mit dem gleichen Namen vorhanden",
"ConflictResolveDescription": "Die Datei <1>{{file}}</1> existiert bereits im Ordner <1>{{folder}}</1>",
"ConflictResolveDescriptionFiles": "im Ordner <1>{{folder}}</1> sind bereits {{filesCount}} Dokumente mit dem gleichen Namen vorhanden",
"ConflictResolveSelectAction": "Bitte wählen Sie die Aktion aus:",
"ConflictResolveTitle": "Bestätigung überschreiben",
"CreateDescription": "Es werden zwei verschiedene Dateien in dem Ordner vorhanden sein.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Überschreiben mit Versions-Update",
"SkipDescription": "Es wird keine Datei kopiert. Die Originaldatei wird im Zielordner beibehalten.",
"SkipTitle": "Überspringen"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Το αρχείο με το όνομα {{file}} υπάρχει ήδη στον φάκελο {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} έγγραφα με το ίδιο όνομα υπάρχουν ήδη στον φάκελο {{folder}}",
"ConflictResolveDescription": "Το αρχείο με το όνομα <1>{{file}}</1> υπάρχει ήδη στον φάκελο <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} έγγραφα με το ίδιο όνομα υπάρχουν ήδη στον φάκελο <1>{{folder}}</1>",
"ConflictResolveSelectAction": "Επιλέξτε την ενέργεια",
"ConflictResolveTitle": "Επιβεβαίωση αντικατάστασης",
"CreateDescription": "Θα υπάρχουν δύο διαφορετικά αρχεία στον φάκελο.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Αντικατάσταση με ενημέρωση έκδοσης",
"SkipDescription": "Δεν θα αντιγραφεί κανένα αρχείο. Το αρχικό αρχείο θα διατηρηθεί στον φάκελο προορισμού.",
"SkipTitle": "Παράλειψη"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "The file with the name {{file}} already exists in the folder {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documents with the same name already exist in the folder '{{folder}}'.",
"ConflictResolveDescription": "The file with the name <1>{{file}}</1> already exists in the folder <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documents with the same name already exist in the folder <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Please select the action:",
"ConflictResolveTitle": "Overwrite confirmation",
"CreateDescription": "There will be two different files in the folder.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Overwrite with version update",
"SkipDescription": "No file will be copied. The original file will be retained in the destination folder.",
"SkipTitle": "Skip"
}
}

View File

@ -1,3 +1,4 @@
{
"NotAvailableFolder": "No folders available"
}
"NotAvailableFolder": "No folders available",
"FolderContents": "The contents of the '{{folderTitle}}' folder"
}

View File

@ -19,5 +19,6 @@
"StoringFileVersion": "Storing file versions",
"ThirdPartyBtn": "Allow users to connect third-party storages",
"ThirdPartySettings": "Connected clouds",
"UpdateOrCreate": "Update the file version for the existing file with the same name. Otherwise, a copy of the file will be created."
"UpdateOrCreate": "Update the file version for the existing file with the same name. Otherwise, a copy of the file will be created.",
"Clouds": "Clouds"
}

View File

@ -4,6 +4,7 @@
"ButtonShareAccess": "Sharing Settings",
"ConnectingAccount": "Connecting account",
"Copy": "Copy",
"CopyHere": "Copy here",
"CreateMasterFormFromFile": "Create Form Template from file",
"DeleteFromTrash": "Selected elements were successfully deleted from Trash",
"DeleteOperation": "Deleting",
@ -11,6 +12,7 @@
"DeleteThirdParty": "Delete third-party",
"DownloadApps": "Download applications",
"DownloadAs": "Download as",
"Documents": "Documents",
"EncryptedFileSaving": "Saving encrypted file",
"Files": "Files",
"FileProtected": "The file is password protected",
@ -33,6 +35,7 @@
"MobileMac": "Download ONLYOFFICE Desktop Editors for Mac OS",
"MobileWin": "Download ONLYOFFICE Desktop Editors for Windows",
"Move": "Move",
"MoveHere": "Move here",
"MoveToOperation": "Moving",
"NewForm": "Form template",
"NewFormFile": "Form from text file",

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "El archivo con el nombre {{file}} ya existe en la carpeta {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos con el mismo nombre ya existen en la carpeta '{{folder}}'.",
"ConflictResolveDescription": "El archivo con el nombre <1>{{file}}</1> ya existe en la carpeta <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos con el mismo nombre ya existen en la carpeta <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Por favor, seleccione la acción:",
"ConflictResolveTitle": "Confirmación de sobrescritura",
"CreateDescription": "Habrá dos archivos diferentes en la carpeta.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Sobrescribir con la actualización de la versión",
"SkipDescription": "No se copiará ningún archivo. El archivo original se conservará en la carpeta de destino.",
"SkipTitle": "Omitir"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Tiedosto, jonka nimi on {{file}}, on jo kansiossa {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} samannimistä asiakirjaa on jo olemassa kansiossa {{folder}}.",
"ConflictResolveDescription": "Tiedosto, jonka nimi on <1>{{file}}</1>, on jo kansiossa <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} samannimistä asiakirjaa on jo olemassa kansiossa <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Valitse toiminto:",
"ConflictResolveTitle": "Korvaamisen vahvistus",
"CreateDescription": "Kansiossa tulee olemaan kaksi eri tiedostoa.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Korvaa versiopäivityksellä",
"SkipDescription": "Mitään tiedostoa ei kopioida. Alkuperäinen tiedosto säilytetään kohdekansiossa.",
"SkipTitle": "Ohita"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Le fichier portant le nom {{file}} existe déjà dans le dossier {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} les documents portant le même nom existent déjà dans le dossier '{{folder}}'.",
"ConflictResolveDescription": "Le fichier portant le nom <1>{{file}}</1> existe déjà dans le dossier <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} les documents portant le même nom existent déjà dans le dossier <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Veuillez sélectionner une action :",
"ConflictResolveTitle": "Confirmation de l'écrasement",
"CreateDescription": "Il y aura deux fichiers différents dans le dossier.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Écraser avec la mise à jour de la version",
"SkipDescription": "Aucun fichier ne sera copié. Le fichier d'origine sera conservé dans le dossier de destination.",
"SkipTitle": "Sauter"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Il file con il nome {{file}} già esiste nella cartella {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenti con lo stesso nome esistono già nella cartella '{{folder}}'.",
"ConflictResolveDescription": "Il file con il nome <1>{{file}}</1> già esiste nella cartella <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenti con lo stesso nome esistono già nella cartella <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Ti preghiamo di selezionare l'azione:",
"ConflictResolveTitle": "Confermare sovrascrittura",
"CreateDescription": "Ci saranno due file diversi nella cartella.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Sovrascrivere con l'aggiornamento della versione",
"SkipDescription": "Nessun file verrà copiato. Il file originale verrà conservato nella cartella di destinazione.",
"SkipTitle": "Saltare"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "フォルダ{{folder}}内に{{file}}という名前のファイルがすでに存在しています。",
"ConflictResolveDescriptionFiles": "{{filesCount}}フォルダ'{{folder}}'に同名のドキュメントがすでに存在しています。",
"ConflictResolveDescription": "フォルダ<1>{{folder}}</1>内に<1>{{file}}</1>という名前のファイルがすでに存在しています。",
"ConflictResolveDescriptionFiles": "{{filesCount}}フォルダ<1>{{folder}}</1>に同名のドキュメントがすでに存在しています。",
"ConflictResolveSelectAction": "アクションをお選びください:",
"ConflictResolveTitle": "上書き確認",
"CreateDescription": "フォルダ内には2種類のファイルが存在します。",
@ -9,4 +9,4 @@
"OverwriteTitle": "バージョンアップによる上書き",
"SkipDescription": "ファイルはコピーされません。元のファイルはコピー先のフォルダーに保持されます。",
"SkipTitle": "スキップ"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{file}} 이름의 파일이 {{folder}} 폴더에 이미 존재합니다.",
"ConflictResolveDescriptionFiles": "같은 이름의 문서 {{filesCount}}개가 '{{folder}}' 폴더에 이미 존재합니다.",
"ConflictResolveDescription": "<1>{{file}}</1> 이름의 파일이 <1>{{folder}}</1> 폴더에 이미 존재합니다.",
"ConflictResolveDescriptionFiles": "같은 이름의 문서 {{filesCount}}개가 <1>{{folder}}</1> 폴더에 이미 존재합니다.",
"ConflictResolveSelectAction": "작업 선택:",
"ConflictResolveTitle": "덮어쓰기",
"CreateDescription": "폴더에 두 개의 다른 파일이 저장됩니다.",
@ -9,4 +9,4 @@
"OverwriteTitle": "버전 업데이트로 덮어쓰기",
"SkipDescription": "파일이 복사되지 않습니다. 원본 파일이 대상 폴더에 유지됩니다.",
"SkipTitle": "건너뛰기"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "ຊື່ໄຟລ໌ {{file}} ມີຢູ່ໃນໂຟນເດີ {{folder}} ແລ້ວ.",
"ConflictResolveDescriptionFiles": "{{filesCount}} ມີເອກະສານຊື່ດ່ຽວກັນໃນໂໄນເດີ້ແລ້ວ '{{folder}}'.",
"ConflictResolveDescription": "ຊື່ໄຟລ໌ <1>{{file}}</1> ມີຢູ່ໃນໂຟນເດີ <1>{{folder}}</1> ແລ້ວ.",
"ConflictResolveDescriptionFiles": "{{filesCount}} ມີເອກະສານຊື່ດ່ຽວກັນໃນໂໄນເດີ້ແລ້ວ <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "ກະລຸນາເລືອກການດຳເນີນການ",
"ConflictResolveTitle": "ຢືນຢັນການທັບ",
"CreateDescription": "ໃນໂຟນເດີ້ຈະມີ 2 ໄຟລ໌ແຕກຕ່າງກັນ.",
@ -9,4 +9,4 @@
"OverwriteTitle": "ອັບເດດທັບເວີຊັ່ນເກົ່າ",
"SkipDescription": "ໄຟລ໌ຈະບໍ່ຖືກສຳເນົາ ໄຟລ໌ຕົ້ນສະບັບຈະຖືກເກັບໄວ້ໃນໂຟນເດີ້.",
"SkipTitle": "ຂ້າມ"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Fails ar nosaukumu {{file}} jau pastāv mapē {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumenti ar tādu pašu nosaukumu jau pastāv mapē '{{folder}}'.",
"ConflictResolveDescription": "Fails ar nosaukumu <1>{{file}}</1> jau pastāv mapē <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumenti ar tādu pašu nosaukumu jau pastāv mapē <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Lūdzu, atlasiet darbību:",
"ConflictResolveTitle": "Pārrakstiet apstiprinājumu",
"CreateDescription": "Mapē būs divi dažādi faili.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Overwrite with version update",
"SkipDescription": "Fails tiks pievienots failam ar tādu pašu nosaukumu kā versijai.",
"SkipTitle": "Izlaist"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Het bestand met de naam {{file}} bestaat al in de map {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenten met dezelfde naam al bestaan in de map '{{folder}}'.",
"ConflictResolveDescription": "Het bestand met de naam <1>{{file}}</1> bestaat al in de map <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documenten met dezelfde naam al bestaan in de map <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Kies de actie:",
"ConflictResolveTitle": "Overschrijf bevestiging",
"CreateDescription": "Er zullen twee verschillende bestanden in de map staan.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Overschrijven met versie update",
"SkipDescription": "Er wordt geen bestand gekopieerd. Het originele bestand blijft bewaard in de doelmap.",
"SkipTitle": "Overslaan"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Plik o nazwie {{file}} istnieje już w folderze {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokument(y/ów) o tej samej nazwie istnieje(-ją) już w folderze '{{folder}}'.",
"ConflictResolveDescription": "Plik o nazwie <1>{{file}}</1> istnieje już w folderze <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokument(y/ów) o tej samej nazwie istnieje(-ją) już w folderze <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Wybierz działanie:",
"ConflictResolveTitle": "Potwierdź zastąpienie",
"CreateDescription": "W folderze będą dwa różne pliki.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Zastąp i zaktualizuj wersję",
"SkipDescription": "Żaden plik nie zostanie skopiowany. Oryginalny plik zostanie zachowany w folderze docelowym.",
"SkipTitle": "Pomiń"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "O arquivo com o nome {{file}} já existe na pasta {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} Os documentos com o mesmo nome já existem na pasta '{{folder}}.",
"ConflictResolveDescription": "O arquivo com o nome <1>{{file}}</1> já existe na pasta <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} Os documentos com o mesmo nome já existem na pasta <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Selecione a ação:",
"ConflictResolveTitle": "Confirmação de substituição",
"CreateDescription": "Haverão dois arquivos diferentes na pasta.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Substituir por versão atualizada",
"SkipDescription": "Nenhum arquivo será copiado. O arquivo original será mantido na pasta de destino.",
"SkipTitle": "Pular"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "O ficheiro com o nome {{file}} já existe na pasta {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos com o mesmo nome já existem na pasta '{{folder}}.",
"ConflictResolveDescription": "O ficheiro com o nome <1>{{file}}</1> já existe na pasta <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} documentos com o mesmo nome já existem na pasta <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Selecione a ação:",
"ConflictResolveTitle": "Confirmação de substituição",
"CreateDescription": "Haverá dois ficheiros diferentes na pasta.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Substituir por versão atualizada",
"SkipDescription": "Nenhum ficheiro será copiado. O ficheiro original será mantido na pasta de destino.",
"SkipTitle": "Ignorar"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Fișierul cu numele {{file}} există deja în dosarul {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} fișiere cu același nume exista deja în folderul'{{folder}}'.",
"ConflictResolveDescription": "Fișierul cu numele <1>{{file}}</1> există deja în dosarul <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} fișiere cu același nume exista deja în folderul<1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Selectați acțiune:",
"ConflictResolveTitle": "Confirmarea suprascrierii",
"CreateDescription": "Două fișiere diferite vor fi salvate în dosarul.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Suprascriere la o versiune actualizată",
"SkipDescription": "Niciun fișier n-o să fie copiat. Fișierul de origine rămâne în dosarul de destinație.",
"SkipTitle": "Treci mai departe"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Файл с именем {{file}} уже существует в папке {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документов с одинаковыми именами уже существуют в папке '{{folder}}'.",
"ConflictResolveDescription": "Файл с именем <1>{{file}}</1> уже существует в папке <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} документов с одинаковыми именами уже существуют в папке <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Пожалуйста, выберите действие:",
"ConflictResolveTitle": "Подтверждение перезаписи",
"CreateDescription": "В папке будет два разных файла.",

View File

@ -1,3 +1,4 @@
{
"NotAvailableFolder": "Нет доступных папок"
}
"NotAvailableFolder": "Нет доступных папок",
"FolderContents": "Содержимое папки '{{folderTitle}}'"
}

View File

@ -19,5 +19,6 @@
"StoringFileVersion": "Хранение версий файлов",
"ThirdPartyBtn": "Разрешить пользователям подключать сторонние хранилища",
"ThirdPartySettings": "Подключенные облака",
"UpdateOrCreate": "Обновлять версию файла для существующего файла с таким же именем. В противном случае будет создаваться копия файла."
"UpdateOrCreate": "Обновлять версию файла для существующего файла с таким же именем. В противном случае будет создаваться копия файла.",
"Clouds": "Облака"
}

View File

@ -4,6 +4,7 @@
"ButtonShareAccess": "Настройка доступа",
"ConnectingAccount": "Подключение аккаунта",
"Copy": "Копировать",
"CopyHere": "Копировать сюда",
"CreateMasterFormFromFile": "Создать шаблон формы из файла",
"DeleteFromTrash": "Выбранные элементы успешно удалены из корзины",
"DeleteOperation": "Удаление",
@ -11,6 +12,7 @@
"DeleteThirdParty": "Отключить сторонний ресурс",
"DownloadApps": "Скачать приложения",
"DownloadAs": "Скачать как",
"Documents": "Документы",
"EncryptedFileSaving": "Сохранение зашифрованного файла",
"Files": "Файлы",
"FileProtected": "Файл защищен с помощью пароля",
@ -33,6 +35,7 @@
"MobileMac": "Скачать десктопные редакторы ONLYOFFICE для Mac OS",
"MobileWin": "Скачать десктопные редакторы ONLYOFFICE для Windows",
"Move": "Переместить",
"MoveHere": "Переместить сюда",
"MoveToOperation": "Перемещение",
"NewForm": "Шаблон формы",
"NewFormFile": "Форма из текстового файла",

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Súbor s názvom {{file}} už v priečinku {{folder}} existuje.",
"ConflictResolveDescriptionFiles": "Dokumenty {{filesCount}} s rovnakými názvami už v priečinku '{{folder}}' existujú.",
"ConflictResolveDescription": "Súbor s názvom <1>{{file}}</1> už v priečinku <1>{{folder}}</1> existuje.",
"ConflictResolveDescriptionFiles": "Dokumenty {{filesCount}} s rovnakými názvami už v priečinku <1>{{folder}}</1> existujú.",
"ConflictResolveSelectAction": "Vyberte akciu:",
"ConflictResolveTitle": "Potvrdenie prepísania",
"CreateDescription": "V priečinku budú dva rôzne súbory.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Prepísať a aktualizovať verziu",
"SkipDescription": "Žiadny súbor nebude skopírovaný. Pôvodný súbor zostane v cieľovom priečinku.",
"SkipTitle": "Preskočiť"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Datoteka z imenom {{file}} že obstaja v mapi {{folder}}.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumentov z enakim imenom že obstaja v mapi '{{folder}}'.",
"ConflictResolveDescription": "Datoteka z imenom <1>{{file}}</1> že obstaja v mapi <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "{{filesCount}} dokumentov z enakim imenom že obstaja v mapi <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Prosim, izberite akcijo:",
"ConflictResolveTitle": "Prepišite potrditev",
"CreateDescription": "V mapi bosta dve različni datoteki.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Prepišite s posodobitvijo različice",
"SkipDescription": "Nobena datoteka ne bo kopirana. Izvirna datoteka bo shranjena v ciljni mapi.",
"SkipTitle": "Preskoči"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{file}} isimli dosya {{folder}} klasöründe zaten mevcut.",
"ConflictResolveDescriptionFiles": "{{filesCount}} belgeleri, '{{folder}}' klasöründe aynı isimle zaten mevcut.",
"ConflictResolveDescription": "<1>{{file}}</1> isimli dosya <1>{{folder}}</1> klasöründe zaten mevcut.",
"ConflictResolveDescriptionFiles": "{{filesCount}} belgeleri, <1>{{folder}}</1> klasöründe aynı isimle zaten mevcut.",
"ConflictResolveSelectAction": "Lütfen işlemi seçin:",
"ConflictResolveTitle": "Üstüne yazma onayı",
"CreateDescription": "Klasörde iki farklı dosya olacaktır.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Sürüm güncellemesiyle üstüne yaz",
"SkipDescription": "Hiçbir dosya kopyalanmayacaktır. Orijinal dosya hedef klasörde tutulacaktır.",
"SkipTitle": "Atla"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "Файл із іменем {{file}} уже існує в папці {{folder}}.",
"ConflictResolveDescriptionFiles": "Документи ({{filesCount}}) з таким ім'ям вже існують у папці {{folder}}.",
"ConflictResolveDescription": "Файл із іменем <1>{{file}}<1> уже існує в папці <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": "Документи ({{filesCount}}) з таким ім'ям вже існують у папці <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Оберіть дію:",
"ConflictResolveTitle": "Підтвердження перезапису",
"CreateDescription": "У папці буде два різних файли.",
@ -9,4 +9,4 @@
"OverwriteTitle": "Перезапис з оновленням версії",
"SkipDescription": "Жоден файл не буде скопійовано. Оригінальний файл буде збережено у папці призначення.",
"SkipTitle": "Пропустити"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": " Tệp có tên {{file}} đã tồn tại trong thư mục {{folder}}.",
"ConflictResolveDescriptionFiles": " {{filesCount}} tài liệu có cùng tên đã tồn tại trong thư mục '{{folder}}.",
"ConflictResolveDescription": " Tệp có tên <1>{{file}}</1> đã tồn tại trong thư mục <1>{{folder}}</1>.",
"ConflictResolveDescriptionFiles": " {{filesCount}} tài liệu có cùng tên đã tồn tại trong thư mục <1>{{folder}}</1>.",
"ConflictResolveSelectAction": "Vui lòng chọn hành động:",
"ConflictResolveTitle": "Xác nhận ghi đè",
"CreateDescription": "Sẽ có hai tệp khác nhau trong thư mục.",
@ -9,4 +9,4 @@
"OverwriteTitle": " Ghi đè bằng cập nhật phiên bản",
"SkipDescription": "Không có tệp nào được sao chép. Tệp gốc sẽ được giữ lại trong thư mục đích.",
"SkipTitle": "Bỏ qua"
}
}

View File

@ -1,6 +1,6 @@
{
"ConflictResolveDescription": "{{folder}}文件夹中已有名称为{{file}}的文件。",
"ConflictResolveDescriptionFiles": "{{folder}}文件夹中已有{{filesCount}}个名称相同的文档。",
"ConflictResolveDescription": "<1>{{folder}}</1>文件夹中已有名称为<1>{{file}}</1>的文件。",
"ConflictResolveDescriptionFiles": "<1>{{folder}}</1>文件夹中已有{{filesCount}}个名称相同的文档。",
"ConflictResolveSelectAction": "请选择操作:",
"ConflictResolveTitle": "覆盖确认",
"CreateDescription": "文件夹中将有两种不同文件。",
@ -9,4 +9,4 @@
"OverwriteTitle": "使用版本更新覆盖",
"SkipDescription": "不会复制任何文件。原始文件将在目标文件夹中保留。",
"SkipTitle": "跳过"
}
}

View File

@ -11,6 +11,16 @@ export const StyledIcon = styled(IconButton)`
${commonIconsStyles}
`;
const StyledEditIcon = styled(IconButton)`
${commonIconsStyles}
svg {
path {
fill: ${(props) => props.theme.filesSection.rowView.editingIconColor};
}
}
`;
const StyledWrapper = styled.div`
display: flex;
justify-content: center;
@ -129,7 +139,7 @@ const Badges = ({
/>
)}
{isEditing && (
<StyledIcon
<StyledEditIcon
iconName={iconEdit}
className="badge icons-group is-editing tablet-badge tablet-edit"
size={sizeBadge}

View File

@ -53,15 +53,30 @@ const EditingWrapper = styled.div`
border-bottom: ${(props) => props.theme.filesEditingWrapper.borderBottom};
padding-bottom: 4px;
margin-top: 4px;
/* margin-left: -4px; */
`}
${(props) =>
props.viewAs === "tile" &&
`margin-right: 10px !important; margin-left: 8px;`}
css`
position: absolute;
width: calc(100% - 18px);
z-index: 1;
gap: 4px;
background-color: ${(props) =>
props.theme.filesEditingWrapper.tile.background};
border: ${(props) => props.theme.filesEditingWrapper.border};
border-radius: 0 0 6px 6px;
height: 43px;
bottom: 0;
left: 0;
right: 0;
padding: 9px 8px 9px 8px;
`}
@media ${tablet} {
height: 56px;
}
@ -96,26 +111,37 @@ const EditingWrapper = styled.div`
`}
${(props) => props.viewAs === "table" && `padding-left: 12px`}
${(props) =>
props.viewAs === "tile" &&
css`
background: #fff;
border: ${(props) =>
`1px solid ${props.theme.filesEditingWrapper.tile.itemBorder}`};
&:focus {
border: ${(props) =>
`1px solid ${props.theme.filesEditingWrapper.tile.itemActiveBorder}`};
}
`};
}
.edit-button {
margin-left: 8px;
height: 32px;
padding: 8px 7px 7px 7px;
padding: 0px 7px 0px 7px;
${(props) =>
props.viewAs === "tile" &&
css`
margin-left: 0px;
background: none;
border: 1px solid transparent;
background: #fff;
border: ${(props) =>
`1px solid ${props.theme.filesEditingWrapper.tile.itemBorder}`};
:hover {
border: ${(props) => props.theme.filesEditingWrapper.border};
}
&:last-child {
margin-left: 2px;
&:hover {
border: ${(props) =>
`1px solid ${props.theme.filesEditingWrapper.tile.itemActiveBorder}`};
}
`};
@ -125,22 +151,23 @@ const EditingWrapper = styled.div`
width: 24px;
height: 24px;
border: 1px transparent;
padding: 4px 0 0 0;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
:hover {
&:hover {
border: ${(props) => props.theme.filesEditingWrapper.border};
}
`}
}
.edit-ok-icon {
margin-top: -6px;
width: 16px;
height: 16px;
}
.edit-cancel-icon {
margin-top: -6px;
width: 14px;
height: 14px;
padding: 1px;

View File

@ -89,15 +89,15 @@ const Panels = (props) => {
selectFileDialogVisible && (
<SelectFileDialog
key="select-file-dialog"
resetTreeFolders
//resetTreeFolders
onSelectFile={createMasterForm}
isPanelVisible={selectFileDialogVisible}
onClose={onClose}
foldersType="exceptPrivacyTrashFolders"
ByExtension
searchParam={".docx"}
headerName={t("Translations:CreateMasterFormFromFile")}
titleFilesList={t("SelectFile:SelectDOCXFormat")}
dialogName={t("Translations:CreateMasterFormFromFile")}
filesListTitle={t("SelectFile:SelectDOCXFormat")}
creationButtonPrimary
withSubfolders={false}
/>

View File

@ -26,12 +26,12 @@ const StyledTreeMenu = styled(TreeMenu)`
background: ${(props) => !props.dragging && "none !important"};
}
.rc-tree-node-selected {
/* .rc-tree-node-selected {
background: ${(props) =>
!props.isPanel
? props.theme.filesArticleBody.background
: props.theme.filesArticleBody.panelBackground} !important;
}
!props.isPanel
? props.theme.filesArticleBody.background
: props.theme.filesArticleBody.panelBackground} !important;
} */
.rc-tree-treenode-disabled > span:not(.rc-tree-switcher),
.rc-tree-treenode-disabled > a,
@ -453,7 +453,7 @@ class TreeFolders extends React.Component {
isLoading,
onSelect,
dragging,
expandedKeys,
expandedPanelKeys,
treeFolders,
data,
@ -475,7 +475,7 @@ class TreeFolders extends React.Component {
onSelect={onSelect}
selectedKeys={selectedKeys}
loadData={this.onLoadData}
expandedKeys={expandedPanelKeys ? expandedPanelKeys : expandedKeys}
expandedKeys={expandedPanelKeys}
onExpand={this.onExpand}
onDragOver={this.onDragOver}
onDragLeave={this.onDragLeave}
@ -484,6 +484,7 @@ class TreeFolders extends React.Component {
gapBetweenNodes="22"
gapBetweenNodesTablet="26"
isFullFillSelection={false}
childrenCount={expandedPanelKeys?.length}
>
{this.getItems(data || treeFolders)}
</StyledTreeMenu>
@ -515,7 +516,6 @@ export default inject(
myFolderId,
commonFolderId,
isPrivacyFolder,
expandedKeys,
setExpandedKeys,
setExpandedPanelKeys,
getSubfolders,
@ -532,7 +532,6 @@ export default inject(
commonId: commonFolderId,
isPrivacy: isPrivacyFolder,
draggableItems: dragging ? selection : null,
expandedKeys,
treeFolders,
isLoading,
selectedKeys: useDefaultSelectedKeys
@ -547,8 +546,4 @@ export default inject(
getSubfolders,
};
}
)(
withTranslation(["Home", "Common"])(
withLoader(observer(TreeFolders))(<Loaders.TreeFolders />)
)
);
)(withTranslation(["Home", "Common"])(observer(TreeFolders)));

View File

@ -1,79 +1,50 @@
import React from "react";
import { inject, observer } from "mobx-react";
import { useTranslation } from "react-i18next";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import Scrollbar from "@appserver/components/scrollbar";
import TreeFolders from "./TreeFolders";
import { StyledSelectFolderPanel } from "../panels/StyledPanels";
import { StyledTree } from "../panels/SelectionPanel/StyledSelectionPanel";
const FolderTreeBody = ({
isLoadingData,
expandedKeys,
folderList,
folderTree,
onSelect,
withoutProvider,
certainFolders,
isAvailable,
filter,
selectedKeys,
heightContent,
displayType,
isHeaderChildren,
theme,
isDisableTree,
}) => {
const { t } = useTranslation(["SelectFolder", "Common"]);
return (
<>
{!isLoadingData ? (
isAvailable ? (
<StyledSelectFolderPanel
heightContent={heightContent}
displayType={displayType}
isHeaderChildren={isHeaderChildren}
>
<div className="select-folder-dialog_tree-folder">
<Scrollbar id="folder-tree-scroll-bar">
<TreeFolders
isPanel={true}
expandedPanelKeys={expandedKeys}
data={folderList}
filter={filter}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
selectedKeys={selectedKeys}
needUpdate={false}
/>
</Scrollbar>
</div>
</StyledSelectFolderPanel>
) : (
<StyledSelectFolderPanel
heightContent={heightContent}
isHeaderChildren={isHeaderChildren}
>
<div className="tree-folder-empty-list select-folder-dialog_tree-folder">
<Text as="span">{t("NotAvailableFolder")}</Text>
</div>
</StyledSelectFolderPanel>
)
) : (
<StyledSelectFolderPanel heightContent={heightContent}>
<div className="tree-folder-Loader" key="loader">
<Loader
type="oval"
size="16px"
style={{
display: "inline",
marginRight: "10px",
marginTop: "16px",
}}
/>
<Text as="span">{`${t("Common:LoadingProcessing")} ${t(
"Common:LoadingDescription"
)}`}</Text>
{isAvailable ? (
<StyledTree theme={theme}>
<div className="selection-panel_tree-folder">
<Scrollbar id="folder-tree-scroll-bar">
<TreeFolders
isPanel={true}
expandedPanelKeys={expandedKeys}
data={folderTree}
filter={filter}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
selectedKeys={selectedKeys}
disabled={isDisableTree}
needUpdate={false}
/>
</Scrollbar>
</div>
</StyledSelectFolderPanel>
</StyledTree>
) : (
<StyledTree>
<div className="selection-panel_empty-folder">
<Text as="span">{t("NotAvailableFolder")}</Text>
</div>
</StyledTree>
)}
</>
);
@ -88,8 +59,15 @@ export default inject(
({ filesStore, treeFoldersStore, selectedFolderStore }) => {
const { filter, isLoading } = filesStore;
const { expandedPanelKeys } = treeFoldersStore;
const expandedKeysProp = expandedPanelKeys
? expandedPanelKeys
: selectedFolderStore.pathParts;
const expandedKeys = expandedKeysProp?.map((item) => item.toString());
!expandedPanelKeys && expandedKeys?.pop();
return {
expandedKeys: expandedPanelKeys ? expandedPanelKeys : null,
expandedKeys: expandedKeys,
filter,
isLoading,
};

View File

@ -4,7 +4,7 @@ import ModalDialog from "@appserver/components/modal-dialog";
import RadioButtonGroup from "@appserver/components/radio-button-group";
import Button from "@appserver/components/button";
import Text from "@appserver/components/text";
import { withTranslation } from "react-i18next";
import { withTranslation, Trans } from "react-i18next";
import { inject, observer } from "mobx-react";
import { ConflictResolveType } from "@appserver/common/constants";
import toastr from "studio/toastr";
@ -30,6 +30,8 @@ const StyledModalDialog = styled(ModalDialog)`
svg {
overflow: visible;
margin-right: 8px;
margin-top: 3px
}
.radio-option-title {
@ -49,7 +51,20 @@ const StyledModalDialog = styled(ModalDialog)`
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 10px;
width: 90%;
width: 100%;
}
.button-dialog-accept {
margin-right: 8px;
}
.modal-dialog-aside-footer, .modal-dialog-modal-footer {
border-top: ${(props) => props.theme.button.border.baseDisabled};
margin-left: -16px;
margin-right: -16px;
padding-left: 16px;
padding-right: 16px;
padding-top: 16px;
}
}
`;
@ -67,6 +82,7 @@ const ConflictResolveDialog = (props) => {
setMoveToPanelVisible,
setCopyPanelVisible,
setThirdPartyMoveDialogVisible,
theme,
} = props;
const {
@ -242,7 +258,7 @@ const ConflictResolveDialog = (props) => {
);
};
export default inject(({ dialogsStore, uploadDataStore, filesStore }) => {
export default inject(({ auth, dialogsStore, uploadDataStore, filesStore }) => {
const {
conflictResolveDialogVisible: visible,
setConflictResolveDialogVisible,
@ -255,8 +271,10 @@ export default inject(({ dialogsStore, uploadDataStore, filesStore }) => {
const { itemOperationToFolder } = uploadDataStore;
const { activeFiles, setActiveFiles } = filesStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
theme,
items,
visible,
conflictResolveDialogData,

View File

@ -8,88 +8,78 @@ import ModalDialog from "@appserver/components/modal-dialog";
import Text from "@appserver/components/text";
import Link from "@appserver/components/link";
import { connectedCloudsTitleTranslation } from "../../../helpers/utils";
import NoUserSelect from "@appserver/components/utils/commonStyles";
import { Base } from "@appserver/components/themes";
import Button from "@appserver/components/button";
import SelectorAddButton from "@appserver/components/selector-add-button";
import { isMobile } from "react-device-detect";
const StyledServicesBlock = styled.div`
display: grid;
column-gap: 55px;
row-gap: 20px;
justify-content: center;
align-items: center;
grid-template-columns: repeat(auto-fill, 158px);
padding-top: 24px;
.service-item {
border: ${(props) => props.theme.filesThirdPartyDialog.border};
width: 158px;
height: 40px;
:hover {
cursor: pointer;
}
const StyledModalDialog = styled(ModalDialog)`
.modal-dialog-aside-body {
margin-right: -16px;
}
.service-item__svg {
${NoUserSelect}
border: ${(props) => props.theme.filesThirdPartyDialog.border};
width: 158px;
height: 40px;
.service-block {
padding-top: 20px;
display: grid;
grid-gap: 16px;
display: flex;
justify-content: center;
align-item: center;
.service-item-container {
display: flex;
:hover {
cursor: pointer;
}
.service-name-container {
display: flex;
align-items: center;
${(props) =>
!props.theme.isBase &&
css`
svg {
rect {
fill: #333333;
}
path {
fill: #ffffff;
opacity: 0.16;
.service-item__svg {
width: 24px;
height: 24px;
margin-right: 8px;
}
.kDrive {
svg {
path:nth-child(7) {
opacity: 0.5 !important;
}
path:nth-child(8) {
opacity: 0.8 !important;
}
path:nth-child(9) {
opacity: 0.8 !important;
}
path:nth-child(10) {
opacity: 0.16 !important;
}
path:nth-child(11) {
opacity: 0.16 !important;
}
}
}
`}
}
}
.kDrive {
svg {
path:nth-child(7) {
opacity: 0.5 !important;
}
path:nth-child(8) {
opacity: 0.8 !important;
}
path:nth-child(9) {
opacity: 0.8 !important;
}
path:nth-child(10) {
opacity: 0.16 !important;
}
path:nth-child(11) {
opacity: 0.16 !important;
.service-btn {
margin-left: auto;
svg {
path {
fill: #333;
}
}
}
}
}
.service-text {
display: flex;
align-items: center;
justify-content: center;
}
`;
StyledServicesBlock.defaultProps = { theme: Base };
StyledModalDialog.defaultProps = { theme: Base };
const ServiceItem = (props) => {
const { capability, t, className, ...rest } = props;
const {
t,
capability,
className,
getThirdPartyIcon,
serviceName,
onClick,
} = props;
const capabilityName = capability[0];
const capabilityLink = capability.length > 1 ? capability[1] : "";
@ -100,13 +90,38 @@ const ServiceItem = (props) => {
"data-key": capabilityName,
};
const src = getThirdPartyIcon(capabilityName);
return (
<ReactSVG
{...dataProps}
{...rest}
className={`service-item__svg ${className}`}
alt=""
/>
<div className="service-item-container">
<div className="service-name-container">
<ReactSVG
src={src}
className={`service-item__svg ${className}`}
alt=""
/>
<Text fontWeight={600} fontSize="14px">
{serviceName ? serviceName : capabilityName}
</Text>
</div>
{isMobile ? (
<SelectorAddButton
onClick={onClick}
iconName="/static/images/actions.plus.icon.react.svg"
className="service-btn"
title={t("Common:Connect")}
{...dataProps}
/>
) : (
<Button
size="small"
className="service-btn"
label={t("Common:Connect")}
onClick={onClick}
{...dataProps}
/>
)}
</div>
);
};
@ -133,6 +148,7 @@ const ThirdPartyDialog = (props) => {
getOAuthToken,
setConnectDialogVisible,
setConnectItem,
getThirdPartyIcon,
} = props;
const onClose = () => {
@ -178,13 +194,8 @@ const ThirdPartyDialog = (props) => {
setThirdPartyDialogVisible(false);
};
const yandexLogoUrl =
i18n && i18n.language === "ru-RU"
? "images/services/logo_yandex_ru.svg"
: "images/services/logo_yandex_en.svg";
return (
<ModalDialog
<StyledModalDialog
isLoading={!tReady}
visible={visible}
scale={false}
@ -209,100 +220,110 @@ const ThirdPartyDialog = (props) => {
</Trans>
)}
</Text>
<StyledServicesBlock>
<div className="service-block">
{googleConnectItem && (
<ServiceItem
t={t}
capability={googleConnectItem}
onClick={onShowService}
src="images/services/logo_google-drive.svg"
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{boxConnectItem && (
<ServiceItem
t={t}
capability={boxConnectItem}
onClick={onShowService}
src="images/services/logo_box.svg"
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{dropboxConnectItem && (
<ServiceItem
t={t}
capability={dropboxConnectItem}
onClick={onShowService}
src="images/services/logo_dropbox.svg"
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{sharePointConnectItem && (
<ServiceItem
t={t}
capability={sharePointConnectItem}
onClick={onShowService}
src={"images/services/logo_sharepoint.svg"}
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{oneDriveConnectItem && (
<ServiceItem
t={t}
capability={oneDriveConnectItem}
onClick={onShowService}
src="images/services/logo_onedrive.svg"
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{sharePointConnectItem && (
{/* {sharePointConnectItem && (
<ServiceItem
t={t}
capability={sharePointConnectItem}
onClick={onShowService}
src={"images/services/logo_onedrive-for-business.svg"}
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
)} */}
{nextCloudConnectItem && (
<ServiceItem
t={t}
serviceName="Nextcloud"
capability={webDavConnectItem}
onClick={onShowService}
src="images/services/logo_nextcloud.svg"
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{ownCloudConnectItem && (
<ServiceItem
t={t}
serviceName="ownCloud"
capability={webDavConnectItem}
onClick={onShowService}
src="images/services/logo_owncloud.svg"
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{kDriveConnectItem && (
<ServiceItem
t={t}
capability={kDriveConnectItem}
onClick={onShowService}
className={"kDrive"}
src="images/services/logo_kdrive.svg"
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{yandexConnectItem && (
<ServiceItem
t={t}
capability={yandexConnectItem}
onClick={onShowService}
src={yandexLogoUrl}
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
{webDavConnectItem && (
<Text
<ServiceItem
t={t}
serviceName={t("ConnextOtherAccount")}
capability={webDavConnectItem}
onClick={onShowService}
className="service-item service-text"
data-title={webDavConnectItem[0]}
data-key={webDavConnectItem[0]}
noSelect
>
{t("ConnextOtherAccount")}
</Text>
getThirdPartyIcon={getThirdPartyIcon}
/>
)}
</StyledServicesBlock>
</div>
</ModalDialog.Body>
</ModalDialog>
</StyledModalDialog>
);
};
@ -319,6 +340,7 @@ export default inject(({ auth, settingsStore, dialogsStore }) => {
webDavConnectItem,
sharePointConnectItem,
openConnectWindow,
getThirdPartyIcon,
} = settingsStore.thirdPartyStore;
const {
setThirdPartyDialogVisible,
@ -348,5 +370,10 @@ export default inject(({ auth, settingsStore, dialogsStore }) => {
setThirdPartyDialogVisible,
getOAuthToken,
openConnectWindow,
getThirdPartyIcon,
};
})(withTranslation(["Settings", "Translations"])(observer(ThirdPartyDialog)));
})(
withTranslation(["Settings", "Translations, Common"])(
observer(ThirdPartyDialog)
)
);

View File

@ -1,19 +1,11 @@
import React, { useState } from "react";
import React, { useState, useEffect } from "react";
import { withRouter } from "react-router";
import ModalDialog from "@appserver/components/modal-dialog";
import { withTranslation } from "react-i18next";
import TreeFolders from "../../FolderTreeBody/TreeFolders";
import { inject, observer } from "mobx-react";
import toastr from "studio/toastr";
import Button from "@appserver/components/button";
import styled from "styled-components";
const StyledModalDialog = styled(ModalDialog)`
.modal-dialog-aside-footer {
width: 90%;
}
`;
import SelectFolderDialog from "../SelectFolderDialog";
let operationData, fileWithConflicts, timerId;
const OperationsPanelComponent = (props) => {
const {
t,
@ -37,18 +29,34 @@ const OperationsPanelComponent = (props) => {
checkFileConflicts,
setThirdPartyMoveDialogVisible,
parentFolderId,
conflictResolveDialogVisible,
clearActiveOperations,
} = props;
const zIndex = 310;
const deleteAfter = false; // TODO: get from settings
const expandedKeys = props.expandedKeys.map((item) => item.toString());
const [isLoading, setIsLoading] = useState(false);
const [selectedFolder, setSelectedFolder] = useState(null);
const [folderTitle, setFolderTitle] = useState(null);
const [providerKey, setProviderKey] = useState(null);
const [intermediateHidden, setIntermediateHidden] = useState(false);
useEffect(() => {
if (conflictResolveDialogVisible === false) {
intermediateHidden && setIntermediateHidden(false);
} else {
isLoading && setIsLoading(false);
}
}, [conflictResolveDialogVisible]);
useEffect(() => {
intermediateHidden &&
setConflictDialogData(fileWithConflicts, operationData);
}, [intermediateHidden]);
useEffect(() => {
return () => {
clearTimeout(timerId);
timerId = null;
};
});
const onClose = () => {
if (isCopy) {
setCopyPanelVisible(false);
@ -59,7 +67,7 @@ const OperationsPanelComponent = (props) => {
setExpandedPanelKeys(null);
};
const onSubmit = () => {
const onSubmit = (selectedFolder, folderTitle, providerKey) => {
if (currentFolderId === selectedFolder) {
return;
}
@ -76,12 +84,6 @@ const OperationsPanelComponent = (props) => {
}
};
const onSelect = (folder, treeNode) => {
setProviderKey(treeNode.node.props.providerKey);
setFolderTitle(treeNode.node.props.title);
setSelectedFolder(isNaN(+folder[0]) ? folder[0] : +folder[0]);
};
const startOperation = async (isCopy, destFolderId, folderTitle) => {
const isProviderFolder = selection.find((x) => !x.providerKey);
const items =
@ -111,7 +113,7 @@ const OperationsPanelComponent = (props) => {
if (!folderIds.length && !fileIds.length) return;
const operationData = {
operationData = {
destFolderId,
folderIds,
fileIds,
@ -124,69 +126,60 @@ const OperationsPanelComponent = (props) => {
},
};
setIsLoading(true);
checkFileConflicts(destFolderId, folderIds, fileIds).then(
async (conflicts) => {
if (!timerId)
timerId = setTimeout(() => {
setIsLoading(true);
}, 500);
checkFileConflicts(destFolderId, folderIds, fileIds)
.then(async (conflicts) => {
if (conflicts.length) {
setConflictDialogData(conflicts, operationData);
fileWithConflicts = conflicts;
setIntermediateHidden(true);
setIsLoading(false);
} else {
setIsLoading(false);
onClose();
await itemOperationToFolder(operationData);
}
}
);
})
.catch((e) => {
toastr.error(e);
setIsLoading(false);
clearActiveOperations(fileIds, folderIds);
})
.finally(() => {
clearTimeout(timerId);
timerId = null;
});
};
//console.log("Operations panel render");
// console.log("Operations panel render", expandedKeys);
const isVisible = intermediateHidden ? false : visible;
return (
<StyledModalDialog
visible={visible}
displayType="aside"
zIndex={zIndex}
<SelectFolderDialog
isDisableTree={isLoading}
foldersType="exceptSortedByTags"
isPanelVisible={isVisible}
onSubmit={onSubmit}
onClose={onClose}
isLoading={!tReady}
className="operations-panel-dialog"
>
<ModalDialog.Header>
{isRecycleBin
? t("Translations:Restore")
id={isRecycleBin ? null : currentFolderId}
withoutImmediatelyClose
dialogName={
isRecycleBin
? t("Common:Restore")
: isCopy
? t("Translations:Copy")
: t("Translations:Move")}
</ModalDialog.Header>
<ModalDialog.Body>
<TreeFolders
isPanel={true}
expandedPanelKeys={expandedKeys}
data={operationsFolders}
filter={filter}
onSelect={onSelect}
needUpdate={false}
disabled={isLoading || isLoading}
selectedKeys={[selectedFolder + ""]}
/>
</ModalDialog.Body>
<ModalDialog.Footer>
<Button
scale
key="OkButton"
label={
isRecycleBin
? t("Translations:Restore")
: isCopy
? t("Translations:Copy")
: t("Translations:Move")
}
size="small"
primary
onClick={onSubmit}
isLoading={isLoading}
isDisabled={!selectedFolder || isLoading}
/>
</ModalDialog.Footer>
</StyledModalDialog>
: t("Home:MoveTo")
}
buttonName={
isRecycleBin
? t("Common:RestoreHere")
: isCopy
? t("Translations:CopyHere")
: t("Translations:MoveHere")
}
></SelectFolderDialog>
);
};
@ -194,6 +187,7 @@ const OperationsPanel = withTranslation([
"OperationsPanel",
"Translations",
"Common",
"Home",
])(OperationsPanelComponent);
export default inject(
@ -213,10 +207,9 @@ export default inject(
isRecycleBinFolder,
operationsFolders,
setExpandedPanelKeys,
expandedPanelKeys,
} = treeFoldersStore;
const { setConflictDialogData, checkFileConflicts } = filesActionsStore;
const { itemOperationToFolder } = uploadDataStore;
const { itemOperationToFolder, clearActiveOperations } = uploadDataStore;
const {
moveToPanelVisible,
@ -227,6 +220,7 @@ export default inject(
setDestFolderId,
setThirdPartyMoveDialogVisible,
setIsFolderActions,
conflictResolveDialogVisible,
} = dialogsStore;
const selections = selection.length ? selection : [bufferSelection];
@ -237,9 +231,6 @@ export default inject(
const provider = selections.find((x) => x.providerKey);
return {
expandedKeys: expandedPanelKeys
? expandedPanelKeys
: selectedFolderStore.pathParts,
currentFolderId: selectedFolderStore.id,
parentFolderId: selectedFolderStore.parentId,
isRecycleBin: isRecycleBinFolder,
@ -259,6 +250,8 @@ export default inject(
setExpandedPanelKeys,
itemOperationToFolder,
checkFileConflicts,
conflictResolveDialogVisible,
clearActiveOperations,
};
}
)(withRouter(observer(OperationsPanel)));

View File

@ -1,170 +1,124 @@
import React, { useState } from "react";
import { StyledAsidePanel, StyledSelectFilePanel } from "../StyledPanels";
import React from "react";
import { StyledAsideBody } from "../SelectionPanel/StyledSelectionPanel";
import Text from "@appserver/components/text";
import SelectFolderInput from "../SelectFolderInput";
import FilesListBody from "./FilesListBody";
import Button from "@appserver/components/button";
import Loaders from "@appserver/common/components/Loaders";
import EmptyContainer from "../../EmptyContainer/EmptyContainer";
import ModalDialog from "@appserver/components/modal-dialog";
const DISPLAY_TYPE = "aside";
import FilesListWrapper from "../SelectionPanel/FilesListWrapper";
const SelectFileDialogAsideView = ({
t,
theme,
isPanelVisible,
zIndex,
onClose,
isVisible,
withoutProvider,
foldersType,
onSelectFile,
onClickInput,
onCloseSelectFolderDialog,
onSelectFolder,
filesList,
files,
hasNextPage,
isNextPageLoading,
loadNextPage,
selectedFolder,
titleFilesList,
loadingText,
selectedFile,
onClickSave,
onSetFileName,
fileName,
displayType,
isTranslationsReady,
passedId,
headerName,
isAvailableFolderList,
filesListTitle,
dialogName,
primaryButtonName,
theme,
onButtonClick,
folderId,
onSelectFolder,
resultingFolderTree,
footer,
fileId,
foldersType,
onCloseSelectFolderDialog,
onClickInput,
isFolderPanelVisible,
maxInputWidth,
newFilter,
}) => {
const [isLoadingData, setIsLoadingData] = useState(false);
const onSetLoadingData = (loading) => {
setIsLoadingData(loading);
};
const isHeaderChildren = !!titleFilesList;
const onMouseEvent = (event) => {
event.stopPropagation();
};
return (
<StyledAsidePanel
visible={isPanelVisible}
onMouseUp={onMouseEvent}
onMouseDown={onMouseEvent}
>
<div onMouseUp={onMouseEvent} onMouseDown={onMouseEvent}>
<ModalDialog
visible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
contentHeight="100%"
displayType={DISPLAY_TYPE}
contentPaddingBottom="0px"
displayType="aside"
withoutBodyScroll
>
<ModalDialog.Header>
{headerName ? headerName : t("SelectFile")}
</ModalDialog.Header>
<ModalDialog.Header>{dialogName}</ModalDialog.Header>
<ModalDialog.Body className="select-file_body-modal-dialog">
<StyledSelectFilePanel
theme={theme}
isHeaderChildren={isHeaderChildren}
displayType={DISPLAY_TYPE}
>
<div className="select-file-dialog_aside-body_wrapper">
<div className="select-file-dialog_aside-children"></div>
<div className="select-file-dialog_aside_body">
<StyledAsideBody theme={theme}>
<div className="selection-panel_aside-body">
<div className="selection-panel_folder-info">
<Text
className="selection-panel_folder-selection-title"
fontWeight={600}
>
{t("Translations:FolderSelection")}
</Text>
<SelectFolderInput
theme={theme}
onClickInput={onClickInput}
onClose={onCloseSelectFolderDialog}
onSelectFolder={onSelectFolder}
onSetLoadingData={onSetLoadingData}
isPanelVisible={isVisible}
isPanelVisible={isFolderPanelVisible}
foldersType={foldersType}
isNeedArrowIcon
withoutProvider={withoutProvider}
isSetFolderImmediately
selectedFolderId={selectedFolder}
id={passedId}
onSetFileName={onSetFileName}
fileName={fileName}
displayType={displayType}
dialogWithFiles
showButtons
selectionButtonPrimary
id={folderId}
onSelectFile={onSelectFile}
displayType="aside"
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
files={files}
folderTree={resultingFolderTree}
isFolderTreeLoading={!!!resultingFolderTree}
isNeedArrowIcon
maxInputWidth={maxInputWidth}
/>
{titleFilesList && (
<Text className="modal-dialog-filter-title">
{titleFilesList}
</Text>
)}
<div className="select-file-dialog_aside_body-files_list">
{selectedFolder && !isLoadingData ? (
<FilesListBody
theme={theme}
filesList={filesList}
onSelectFile={onSelectFile}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={loadNextPage}
selectedFolder={selectedFolder}
displayType={DISPLAY_TYPE}
loadingText={loadingText}
selectedFile={selectedFile}
/>
) : isAvailableFolderList ? (
<div key="loader" className="panel-loader-wrapper">
<Loaders.Rows
theme={theme}
style={{
marginBottom: "24px",
marginTop: "20px",
}}
count={12}
/>
</div>
) : (
<div className="select-file-dialog_empty-container">
<EmptyContainer
theme={theme}
headerText={t("Home:EmptyFolderHeader")}
imageSrc="/static/images/empty_screen.png"
/>
</div>
)}
<Text color="#A3A9AE" className="selection-panel_aside-title">
{filesListTitle}
</Text>
</div>
<div className="selection-panel_files">
<FilesListWrapper
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType="aside"
folderSelection={false}
fileId={fileId}
newFilter={newFilter}
/>
</div>
<div className="selection-panel_aside-footer">
{footer}
<div className="selection-panel_aside-buttons">
<Button
theme={theme}
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={!fileId}
/>
<Button
theme={theme}
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
/>
</div>
</div>
</div>
</StyledSelectFilePanel>
</StyledAsideBody>
</ModalDialog.Body>
<ModalDialog.Footer theme={theme}>
<StyledSelectFilePanel
theme={theme}
isHeaderChildren={isHeaderChildren}
>
<div className="select-file-dialog-aside_buttons">
<Button
theme={theme}
className="select-file-dialog-buttons-save"
primary
size="normal"
label={primaryButtonName}
onClick={onClickSave}
isDisabled={selectedFile.length === 0}
/>
<Button
size="normal"
theme={theme}
label={t("Common:CancelButton")}
onClick={onClose}
/>
</div>
</StyledSelectFilePanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
</div>
);
};
export default SelectFileDialogAsideView;

View File

@ -1,85 +0,0 @@
import React from "react";
import { StyledFilesList } from "../StyledPanels";
import { ReactSVG } from "react-svg";
import { inject, observer } from "mobx-react";
import Text from "@appserver/components/text";
import Checkbox from "@appserver/components/checkbox";
import RadioButton from "@appserver/components/radio-button";
const FilesListRow = ({
displayType,
needRowSelection,
index,
onSelectFile,
fileName,
children,
fileExst,
iconSrc,
isMultiSelect, // it will be needed
isChecked,
noCheckBox,
theme,
}) => {
return (
<StyledFilesList
displayType={displayType}
theme={theme}
needRowSelection={needRowSelection}
isChecked={isChecked}
noCheckBox={noCheckBox}
>
<div
data-index={index}
className="modal-dialog_file-name"
onClick={onSelectFile}
>
{isMultiSelect ? ( // it will be needed
<Checkbox
theme={theme}
label=""
isChecked={isChecked}
className="select-file-dialog_checked"
/>
) : (
<RadioButton
theme={theme}
fontSize="13px"
fontWeight="400"
name={`${index}`}
label=""
isChecked={isChecked}
onClick={onSelectFile}
value=""
className="select-file-dialog_checked"
/>
)}
<ReactSVG src={iconSrc} className="select-file-dialog_icon" />
<div data-index={index} className="files-list_full-name">
<Text theme={theme} data-index={index} className="entry-title">
{fileName}
<Text
theme={theme}
data-index={index}
className="file-exst"
as="span"
>
{fileExst}
</Text>
</Text>
</div>
<div className="files-list_file-children_wrapper">{children}</div>
</div>
</StyledFilesList>
);
};
FilesListRow.defaultProps = {
isMultiSelect: false,
};
export default inject(({ settingsStore }, { fileExst }) => {
const iconSrc = settingsStore.getIconSrc(fileExst, 24);
return {
iconSrc,
};
})(observer(FilesListRow));

View File

@ -0,0 +1,44 @@
import React from "react";
import { Provider as MobxProvider, inject, observer } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import SelectFileDialog from "./index";
import stores from "../../../store/index";
import store from "studio/store";
import i18n from "./i18n";
const { auth: authStore } = store;
class SelectFileDialogBody extends React.Component {
componentDidMount() {
const { settings, setFilesSettings } = this.props;
settings && setFilesSettings(settings);
}
render() {
return <SelectFileDialog {...this.props} />;
}
}
const SelectFileWrapper = inject(({ settingsStore }) => {
const { setFilesSettings } = settingsStore;
return {
setFilesSettings,
};
})(observer(SelectFileDialogBody));
class SelectFileDialogWrapper extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFileWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFileDialogWrapper;

View File

@ -1,59 +1,28 @@
import React from "react";
import { inject, observer, Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import PropTypes from "prop-types";
import throttle from "lodash/throttle";
import stores from "../../../store/index";
import i18n from "./i18n";
import SelectFileDialogModalView from "./ModalView";
import SelectFileDialogAsideView from "./AsideView";
import utils from "@appserver/components/utils";
//import SelectFolderDialog from "../SelectFolderDialog";
import { getFolder } from "@appserver/common/api/files";
import { FilterType } from "@appserver/common/constants";
import SelectionPanel from "../SelectionPanel/SelectionPanelBody";
import toastr from "studio/toastr";
const { desktop } = utils.device;
import store from "studio/store";
const { auth: authStore } = store;
class SelectFileDialogBody extends React.Component {
class SelectFileDialog extends React.Component {
constructor(props) {
super(props);
const {
folderId,
storeFolderId,
fileInfo,
filter,
creationButtonPrimary,
t,
} = props;
this.buttonName = creationButtonPrimary
? t("Common:Create")
: t("Common:SaveButton");
const { filter } = props;
this.state = {
isVisible: false,
selectedFolder: storeFolderId || "",
passedId: folderId,
selectedFile: fileInfo || "",
fileName: (fileInfo && fileInfo.title) || "",
filesList: [],
hasNextPage: true,
isNextPageLoading: false,
files: [],
displayType: this.getDisplayType(),
page: 0,
filterParams: this.getFilterParameters(),
isAvailableFolderList: true,
};
this.throttledResize = throttle(this.setDisplayType, 300);
this.newFilter = filter.clone();
this._isLoadNextPage = false;
}
getFilterParameters = () => {
@ -101,36 +70,79 @@ class SelectFileDialogBody extends React.Component {
};
setFilter = () => {
const { filterParams } = this.state;
const { withSubfolders = true } = this.props;
const filterParams = this.getFilterParameters();
this.newFilter.filterType = filterParams.filterType;
this.newFilter.search = filterParams.filterValue;
this.newFilter.withSubfolders = withSubfolders;
};
componentDidMount() {
authStore.init(true); // it will work if authStore is not initialized
async componentDidMount() {
const {
treeFolders,
foldersType,
id,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
treeFromInput,
displayType,
setFolderId,
folderId,
} = this.props;
!displayType && window.addEventListener("resize", this.throttledResize);
window.addEventListener("resize", this.throttledResize);
this.setFilter();
let resultingFolderTree, resultingId;
try {
[
resultingFolderTree,
resultingId,
] = await SelectionPanel.getBasicFolderInfo(
treeFolders,
foldersType,
folderId,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
true
);
} catch (e) {
toastr.error(e);
return;
}
const tree = treeFromInput ? treeFromInput : resultingFolderTree;
if (tree.length === 0) {
this.setState({ isAvailable: false });
onSelectFolder(null);
return;
}
setFolderId(resultingId);
this.setState({
resultingFolderTree: tree,
});
}
componentWillUnmount() {
const {
resetTreeFolders,
setExpandedPanelKeys,
setDefaultSelectedFolder,
setSelectedFolder,
setFolderId,
setFile,
setExpandedPanelKeys,
withoutResetFolderTree,
} = this.props;
this.throttledResize && this.throttledResize.cancel();
window.removeEventListener("resize", this.throttledResize);
if (resetTreeFolders) {
if (!withoutResetFolderTree) {
setExpandedPanelKeys(null);
//setSelectedFolder(null);
setFolderId(null);
setFile(null);
}
@ -161,192 +173,123 @@ class SelectFileDialogBody extends React.Component {
});
};
onSelectFolder = (id) => {
const { setFolderId } = this.props;
onSelectFolder = (folder) => {
const { displayType } = this.state;
const { setFolderId, setFile, folderId } = this.props;
const id = displayType === "aside" ? folder : folder[0];
if (id) {
if (id !== folderId) {
setFolderId(id);
this.setState({
selectedFolder: id,
hasNextPage: true,
filesList: [],
page: 0,
});
} else
this.setState({
isAvailableFolderList: false,
});
setFile(null);
}
};
onSelectFile = (e) => {
const { filesList } = this.state;
onSelectFile = (item, index) => {
const { setFile } = this.props;
const index = e.target.dataset.index || e.target.name;
if (!index) return;
setFile(filesList[+index]);
this.setState({
selectedFile: filesList[+index],
fileName: filesList[+index].title,
});
setFile(item);
};
onClickSave = () => {
const { onSetFileName, onClose, onSelectFile } = this.props;
const { fileName, selectedFile } = this.state;
const {
onSetFileNameAndLocation,
onClose,
onSelectFile,
fileInfo,
folderId,
} = this.props;
onSetFileName && onSetFileName(fileName);
onSelectFile && onSelectFile(selectedFile);
const fileName = fileInfo.title;
onSetFileNameAndLocation && onSetFileNameAndLocation(fileName, folderId);
onSelectFile && onSelectFile(fileInfo);
onClose && onClose();
};
loadNextPage = () => {
// const {
// setSelectedNode,
// setSelectedFolder,
// setExpandedPanelKeys,
// } = this.props;
const { selectedFolder, page } = this.state;
if (this._isLoadNextPage) return;
this._isLoadNextPage = true;
const pageCount = 30;
this.newFilter.page = page;
this.newFilter.pageCount = pageCount;
this.setState({ isNextPageLoading: true }, () => {
getFolder(selectedFolder, this.newFilter)
.then((data) => {
let newFilesList = page
? this.state.filesList.concat(data.files)
: data.files;
//TODO: it will need if passed the folder id - need to come up with a different solution.
// const newPathParts = SelectFolderDialog.convertPathParts(
//
// data.pathParts
// );
//setExpandedPanelKeys(newPathParts);
// setSelectedNode([selectedFolder + ""]);
// setSelectedFolder({
// folders: data.folders,
// ...data.current,
// pathParts: newPathParts,
// ...{ new: data.new },
// });
this.setState({
hasNextPage: newFilesList.length < data.total,
isNextPageLoading: false,
filesList: newFilesList,
page: page + 1,
});
})
.catch((error) => console.log(error))
.finally(() => (this._isLoadNextPage = false));
});
};
render() {
const {
t,
isPanelVisible,
onClose,
zIndex,
foldersType,
withoutProvider,
titleFilesList,
loadingLabel,
folderId,
onSetFileName,
tReady,
headerName,
foldersList,
filesListTitle,
theme,
header,
footer,
dialogName,
creationButtonPrimary,
maxInputWidth,
folderId,
fileInfo,
} = this.props;
const {
isVisible,
filesList,
hasNextPage,
isNextPageLoading,
selectedFolder,
displayType,
selectedFile,
fileName,
passedId,
isAvailableFolderList,
resultingFolderTree,
isLoadingData,
} = this.state;
const loadingText = loadingLabel
? loadingLabel
: `${t("Common:LoadingProcessing")} ${t("Common:LoadingDescription")}`;
const buttonName = creationButtonPrimary
? t("Common:Create")
: t("Common:SaveButton");
const name = dialogName ? dialogName : t("Common:SelectFile");
// console.log("Render file-component");
return displayType === "aside" ? (
<SelectFileDialogAsideView
theme={theme}
t={t}
theme={theme}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
isFolderPanelVisible={isVisible}
onClose={onClose}
isVisible={isVisible}
withoutProvider={withoutProvider}
foldersType={foldersType}
filesList={filesList}
onSelectFile={this.onSelectFile}
onClickInput={this.onClickInput}
onClickSave={this.onClickSave}
onCloseSelectFolderDialog={this.onCloseSelectFolderDialog}
onSelectFolder={this.onSelectFolder}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this.loadNextPage}
selectedFolder={selectedFolder}
headerName={headerName}
loadingText={loadingText}
selectedFile={selectedFile}
folderId={folderId}
onSetFileName={onSetFileName}
fileName={fileName}
displayType={displayType}
isTranslationsReady={tReady}
passedId={passedId}
titleFilesList={titleFilesList}
isAvailableFolderList={isAvailableFolderList}
primaryButtonName={this.buttonName}
resultingFolderTree={resultingFolderTree}
onButtonClick={this.onClickSave}
header={header}
dialogName={name}
footer={footer}
isLoadingData={isLoadingData}
primaryButtonName={buttonName}
isAvailable={isAvailableFolderList}
onSelectFolder={this.onSelectFolder}
onSelectFile={this.onSelectFile}
filesListTitle={filesListTitle}
fileId={fileInfo?.id}
newFilter={this.newFilter}
foldersType={foldersType}
onClickInput={this.onClickInput}
onCloseSelectFolderDialog={this.onCloseSelectFolderDialog}
maxInputWidth={maxInputWidth}
/>
) : (
<SelectFileDialogModalView
theme={theme}
<SelectionPanel
t={t}
theme={theme}
isPanelVisible={isPanelVisible}
onClose={onClose}
withoutProvider={withoutProvider}
folderId={folderId}
resultingFolderTree={resultingFolderTree}
onButtonClick={this.onClickSave}
header={header}
dialogName={name}
footer={footer}
isLoadingData={isLoadingData}
primaryButtonName={buttonName}
isAvailable={isAvailableFolderList}
onSelectFolder={this.onSelectFolder}
onSelectFile={this.onSelectFile}
foldersType={foldersType}
onClickSave={this.onClickSave}
filesList={filesList}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this.loadNextPage}
selectedFolder={selectedFolder}
withoutProvider={withoutProvider}
headerName={headerName}
loadingText={loadingText}
selectedFile={selectedFile}
folderId={folderId}
passedId={passedId}
titleFilesList={titleFilesList}
primaryButtonName={this.buttonName}
foldersList={foldersList}
filesListTitle={filesListTitle}
fileId={fileInfo?.id}
newFilter={this.newFilter}
/>
);
}
}
SelectFileDialogBody.propTypes = {
SelectFileDialog.propTypes = {
onClose: PropTypes.func.isRequired,
isPanelVisible: PropTypes.bool.isRequired,
onSelectFile: PropTypes.func.isRequired,
@ -356,67 +299,59 @@ SelectFileDialogBody.propTypes = {
"exceptSortedByTags",
"exceptPrivacyTrashFolders",
]),
folderId: PropTypes.string,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
withoutProvider: PropTypes.bool,
creationButtonPrimary: PropTypes.bool,
ignoreSelectedFolderTree: PropTypes.bool,
headerName: PropTypes.string,
titleFilesList: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
zIndex: PropTypes.number,
filesListTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
withoutResetFolderTree: PropTypes.bool,
};
SelectFileDialogBody.defaultProps = {
folderId: "",
titleFilesList: "",
SelectFileDialog.defaultProps = {
id: "",
filesListTitle: "",
withoutProvider: false,
zIndex: 310,
creationButtonPrimary: false,
ignoreSelectedFolderTree: false,
withoutResetFolderTree: false,
};
const SelectFileDialogWrapper = inject(
export default inject(
({
auth,
filesStore,
selectedFilesStore,
treeFoldersStore,
selectedFolderStore,
selectFileDialogStore,
}) => {
const {
folderId: storeFolderId,
folderId: id,
fileInfo,
setFolderId,
setFile,
} = selectedFilesStore;
} = selectFileDialogStore;
const { setSelectedNode, setExpandedPanelKeys } = treeFoldersStore;
const { treeFolders, setExpandedPanelKeys } = treeFoldersStore;
const { filter } = filesStore;
const { setSelectedFolder, id } = selectedFolderStore;
const { id: storeFolderId } = selectedFolderStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
const folderId = id ? id : storeFolderId;
return {
storeFolderId: storeFolderId || id,
fileInfo,
setFile,
setFolderId,
setSelectedFolder,
setSelectedNode,
filter,
treeFolders,
storeFolderId,
folderId,
theme: theme,
setExpandedPanelKeys,
};
}
)(
observer(
withTranslation(["SelectFile", "Common", "Translations"])(
SelectFileDialogBody
)
withTranslation(["SelectFile", "Common", "Translations"])(SelectFileDialog)
)
);
class SelectFileDialog extends React.Component {
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFileDialogWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFileDialog;

View File

@ -0,0 +1,28 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import stores from "../../../store/index";
import store from "studio/store";
import SelectFileInput from "./index";
import i18n from "./i18n";
const { auth: authStore } = store;
const SelectFileModalWrapper = (props) => <SelectFileInput {...props} />;
class SelectFileInputWrapper extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFileModalWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFileInputWrapper;

View File

@ -0,0 +1,35 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
import { loadLanguagePath } from "@appserver/common/utils";
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
fallbackLng: "en",
load: "currentOnly",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFile"],
defaultNS: "SelectFile",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -1,7 +1,6 @@
import React from "react";
import { Provider as MobxProvider, inject, observer } from "mobx-react";
import PropTypes from "prop-types";
import stores from "../../../store/index";
import SelectFileDialog from "../SelectFileDialog";
import StyledComponent from "./StyledSelectFileInput";
@ -13,6 +12,7 @@ class SelectFileInputBody extends React.PureComponent {
this.state = {
fileName: "",
folderId: "",
};
}
@ -20,76 +20,46 @@ class SelectFileInputBody extends React.PureComponent {
this.props.setFirstLoad(false);
}
onSetFileName = (fileName) => {
onSetFileNameAndLocation = (fileName, id) => {
this.setState({
fileName: fileName,
folderId: id,
});
};
render() {
const {
name,
onClickInput,
isPanelVisible,
withoutProvider,
onClose,
isError,
isDisabled,
foldersType,
withSubfolders,
onSelectFile,
folderId,
headerName,
isImageOnly,
isArchiveOnly,
isDocumentsOnly,
searchParam,
isPresentationOnly,
isTablesOnly,
isMediaOnly,
loadingLabel,
titleFilesList,
zIndex,
fontSizeInput,
hasError,
t,
placeholder,
maxInputWidth,
foldersList,
maxFolderInputWidth,
isPanelVisible,
isDisabled,
isError,
...rest
} = this.props;
const { fileName } = this.state;
const { fileName, folderId } = this.state;
return (
<StyledComponent maxInputWidth={maxInputWidth}>
<SimpleFileInput
name={name}
className="select-file_file-input"
textField={fileName}
isDisabled={isDisabled}
isError={isError}
onClickInput={onClickInput}
fontSizeInput={fontSizeInput}
maxInputWidth={maxInputWidth}
/>
{isPanelVisible && (
<SelectFileDialog
zIndex={zIndex}
onClose={onClose}
{...rest}
id={folderId}
isPanelVisible={isPanelVisible}
foldersType={foldersType}
onSetFileName={this.onSetFileName}
withoutProvider={withoutProvider}
withSubfolders={withSubfolders}
onSelectFile={onSelectFile}
folderId={folderId}
headerName={headerName}
searchParam={searchParam}
isImageOnly={isImageOnly}
isArchiveOnly={isArchiveOnly}
isDocumentsOnly={isDocumentsOnly}
isPresentation={isPresentationOnly}
isTables={isTablesOnly}
isMediaOnly={isMediaOnly}
loadingLabel={loadingLabel}
titleFilesList={titleFilesList}
foldersList={foldersList}
onSetFileNameAndLocation={this.onSetFileNameAndLocation}
maxInputWidth={maxFolderInputWidth}
/>
)}
</StyledComponent>
@ -99,13 +69,13 @@ class SelectFileInputBody extends React.PureComponent {
SelectFileInputBody.propTypes = {
onClickInput: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
hasError: PropTypes.bool,
placeholder: PropTypes.string,
};
SelectFileInputBody.defaultProps = {
withoutProvider: false,
isDisabled: false,
zIndex: 310,
hasError: false,
placeholder: "",
};
const SelectFileInputBodyWrapper = inject(({ filesStore }) => {

View File

@ -1,118 +1,122 @@
import React from "react";
import IconButton from "@appserver/components/icon-button";
import FolderTreeBody from "../../FolderTreeBody";
import { StyledAsidePanel, StyledSelectFolderPanel } from "../StyledPanels";
import Button from "@appserver/components/button";
import ModalDialog from "@appserver/components/modal-dialog";
import {
StyledAsideBody,
StyledAsideHeader,
} from "../SelectionPanel/StyledSelectionPanel";
import Text from "@appserver/components/text";
import Loaders from "@appserver/common/components/Loaders";
import styled, { css } from "styled-components";
const DISPLAY_TYPE = "aside";
const StyledModalDialog = styled(ModalDialog)`
.modal-dialog-aside-body {
padding-top: 12px;
}
`;
const SelectFolderDialogAsideView = ({
theme,
t,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
asideHeightContent,
isAvailable,
certainFolders,
folderId,
isLoadingData,
folderList,
onSelect,
resultingFolderTree,
onSelectFolder,
footer,
showButtons,
onSave,
headerName,
onButtonClick,
dialogName,
header,
canCreate,
isLoading,
primaryButtonName,
noTreeSwitcher,
isDisableTree,
isDisableButton,
}) => {
return (
<StyledAsidePanel theme={theme} visible={isPanelVisible}>
<ModalDialog
theme={theme}
visible={isPanelVisible}
zIndex={zIndex}
contentHeight="100%"
contentPaddingBottom={footer && showButtons ? "100px" : "40px"}
onClose={onClose}
withoutBodyScroll
displayType="aside"
>
<ModalDialog.Header theme={theme}>
<StyledSelectFolderPanel theme={theme}>
<div className="select-folder-dialog_header">
{isNeedArrowIcon && (
<IconButton
theme={theme}
className="select-folder-dialog_header-icon"
size="16"
iconName="/static/images/arrow.path.react.svg"
onClick={onClose}
// color={theme.filesPanels.selectFolder.color}
/>
)}
{headerName ? headerName : t("Translations:FolderSelection")}
<StyledModalDialog
theme={theme}
visible={isPanelVisible}
contentHeight="100%"
contentPaddingBottom="0px"
onClose={onClose}
withoutBodyScroll
displayType="aside"
>
<ModalDialog.Header theme={theme}>
<StyledAsideHeader>
{isNeedArrowIcon && (
<IconButton
theme={theme}
className="selection-panel_aside-header-icon"
size="16"
iconName="/static/images/arrow.path.react.svg"
onClick={onClose}
/>
)}
{dialogName ? dialogName : t("Translations:FolderSelection")}
</StyledAsideHeader>
</ModalDialog.Header>
<ModalDialog.Body theme={theme}>
<StyledAsideBody theme={theme} header={!!header} footer={!!footer}>
<div className="selection-panel_aside-body">
<div className="selection-panel_aside-header">
<div>{header}</div>
<Text fontWeight="700" fontSize="18px">
{t("Translations:Documents")}
</Text>
</div>
</StyledSelectFolderPanel>
</ModalDialog.Header>
<ModalDialog.Body theme={theme}>
<StyledSelectFolderPanel
theme={theme}
displayType={DISPLAY_TYPE}
showButtons={showButtons}
isFooter={!!footer}
noTreeSwitcher={noTreeSwitcher}
>
<div className="select-folder-dialog_aside_body">
<div>{header} </div>
<FolderTreeBody
theme={theme}
isLoadingData={isLoadingData}
folderList={folderList}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
isAvailable={isAvailable}
selectedKeys={[folderId]}
heightContent={asideHeightContent}
displayType={DISPLAY_TYPE}
/>
<div className="selection-panel_aside-tree">
{folderId && resultingFolderTree ? (
<FolderTreeBody
theme={theme}
folderTree={resultingFolderTree}
onSelect={onSelectFolder}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
selectedKeys={[`${folderId}`]}
isDisableTree={isDisableTree}
displayType="aside"
/>
) : (
<Loaders.NewTreeFolders />
)}
</div>
</StyledSelectFolderPanel>
</ModalDialog.Body>
<ModalDialog.Footer theme={theme}>
<StyledSelectFolderPanel theme={theme}>
{footer}
{showButtons && (
<div className="select-folder-dialog-modal_buttons">
<div className="selection-panel_aside-footer">
<div>{footer}</div>
<div className="selection-panel_aside-buttons">
<Button
theme={theme}
className="select-folder-dialog-buttons-save"
primary
size="normal"
size="normalTouchscreen"
label={primaryButtonName}
onClick={onSave}
isDisabled={isLoadingData || !isAvailable || !canCreate}
onClick={onButtonClick}
isDisabled={
isDisableButton ||
isDisableTree ||
isLoadingData ||
!isAvailable
}
/>
<Button
size="normal"
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData || isLoading}
isDisabled={isLoadingData}
/>
</div>
)}
</StyledSelectFolderPanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
</div>
</div>
</StyledAsideBody>
</ModalDialog.Body>
</StyledModalDialog>
);
};
export default SelectFolderDialogAsideView;

View File

@ -1,95 +0,0 @@
import React from "react";
import ModalDialog from "@appserver/components/modal-dialog";
import { StyledAsidePanel, StyledSelectFolderPanel } from "../StyledPanels";
import FolderTreeBody from "../../FolderTreeBody";
import Button from "@appserver/components/button";
const SelectFolderDialogModalView = ({
t,
theme,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
modalHeightContent,
isAvailable,
certainFolders,
folderId,
isLoadingData,
folderList,
onSelect,
header,
footer,
headerName,
showButtons,
onSave,
canCreate,
isLoading,
primaryButtonName,
noTreeSwitcher,
}) => {
return (
<StyledAsidePanel theme={theme} visible={isPanelVisible}>
<ModalDialog
theme={theme}
visible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
displayType="modal"
{...(!header && !footer && !showButtons && { contentHeight: "416px" })}
>
<ModalDialog.Header theme={theme}>
{headerName ? headerName : t("Translations:FolderSelection")}
</ModalDialog.Header>
<ModalDialog.Body theme={theme}>
<StyledSelectFolderPanel
theme={theme}
isNeedArrowIcon={isNeedArrowIcon}
noTreeSwitcher={noTreeSwitcher}
>
<div className="select-folder-modal-dialog-header">{header} </div>
<FolderTreeBody
theme={theme}
isLoadingData={isLoadingData}
folderList={folderList}
onSelect={onSelect}
withoutProvider={withoutProvider}
certainFolders={certainFolders}
isAvailable={isAvailable}
selectedKeys={[folderId]}
heightContent={modalHeightContent}
/>
</StyledSelectFolderPanel>
</ModalDialog.Body>
<ModalDialog.Footer theme={theme}>
<StyledSelectFolderPanel theme={theme}>
{footer}
{showButtons && (
<div className="select-folder-dialog-modal_buttons">
<Button
theme={theme}
className="select-folder-dialog-buttons-save"
primary
size="small"
label={primaryButtonName}
onClick={onSave}
isDisabled={isLoadingData || !isAvailable || !canCreate}
/>
<Button
size="small"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData || isLoading}
/>
</div>
)}
</StyledSelectFolderPanel>
</ModalDialog.Footer>
</ModalDialog>
</StyledAsidePanel>
);
};
export default SelectFolderDialogModalView;

View File

@ -0,0 +1,28 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import stores from "../../../store/index";
import store from "studio/store";
import SelectFolderDialog from "./index";
import i18n from "./i18n";
const { auth: authStore } = store;
const SelectFolderModalWrapper = (props) => <SelectFolderDialog {...props} />;
class SelectFolderModal extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFolderModalWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFolderModal;

View File

@ -24,7 +24,7 @@ newInstance.use(Backend).init({
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFolder", "Common", "Translations"],
ns: ["SelectFolder"],
defaultNS: "SelectFolder",
react: {
@ -32,4 +32,4 @@ newInstance.use(Backend).init({
},
});
export default newInstance;
export default newInstance;

View File

@ -1,377 +1,113 @@
import React from "react";
import { inject, observer, Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import PropTypes from "prop-types";
import throttle from "lodash/throttle";
import { getCommonThirdPartyList } from "@appserver/common/api/settings";
import {
getCommonFolderList,
getFolder,
getFolderPath,
getFoldersTree,
} from "@appserver/common/api/files";
import SelectFolderInput from "../SelectFolderInput";
import i18n from "./i18n";
import SelectFolderDialogAsideView from "./AsideView";
import SelectFolderDialogModalView from "./ModalView";
import stores from "../../../store/index";
import utils from "@appserver/components/utils";
import { FolderType } from "@appserver/common/constants";
import { isArrayEqual } from "@appserver/components/utils/array";
import store from "studio/store";
import toastr from "studio/toastr";
import {
exceptSortedByTagsFolders,
exceptPrivacyTrashFolders,
} from "./ExceptionFoldersConstants";
const { auth: authStore } = store;
import SelectionPanel from "../SelectionPanel/SelectionPanelBody";
import { FilterType } from "@appserver/common/constants";
const { desktop } = utils.device;
let pathName = "";
let folderList;
class SelectFolderModalDialog extends React.Component {
class SelectFolderDialog extends React.Component {
constructor(props) {
super(props);
const {
isSetFolderImmediately,
id,
displayType,
selectionButtonPrimary,
t,
} = this.props;
const isNeedFolder = id ? true : isSetFolderImmediately;
this.buttonName = selectionButtonPrimary
? t("Common:Select")
: t("Common:SaveButton");
const { id, displayType, filter } = this.props;
this.newFilter = filter.clone();
this.newFilter.filterType = FilterType.FilesOnly;
this.newFilter.withSubfolders = false;
this.state = {
isLoadingData: false,
isLoading: false,
isAvailable: true,
certainFolders: true,
folderId: "",
displayType: displayType || this.getDisplayType(),
isSetFolderImmediately: isNeedFolder,
canCreate: true,
isAvailable: true,
};
this.throttledResize = throttle(this.setDisplayType, 300);
this.folderTitle = "";
this.noTreeSwitcher = false;
}
componentDidMount() {
const { onSetLoadingData, onSetLoadingInput, displayType } = this.props;
authStore.init(true); // it will work if authStore is not initialized
async componentDidMount() {
const {
treeFolders,
foldersType,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
displayType,
isNeedArrowIcon = false,
folderTree,
setFolderId,
withInput,
id,
storeFolderId,
} = this.props;
!displayType && window.addEventListener("resize", this.throttledResize);
this.setState({ isLoadingData: true }, function () {
onSetLoadingData && onSetLoadingData(true);
onSetLoadingInput && onSetLoadingInput(true);
this.trySwitch();
const initialFolderId = withInput ? id : storeFolderId;
let resultingFolderTree, resultingId;
if (!withInput && !isNeedArrowIcon) {
try {
[
resultingFolderTree,
resultingId,
] = await SelectionPanel.getBasicFolderInfo(
treeFolders,
foldersType,
initialFolderId,
onSetBaseFolderPath,
onSelectFolder,
foldersList
);
} catch (e) {
toastr.error(e);
return;
}
}
const tree =
isNeedArrowIcon || withInput ? folderTree : resultingFolderTree;
if (tree.length === 0) {
this.setState({ isAvailable: false });
onSelectFolder(null);
return;
}
const resId = isNeedArrowIcon || withInput ? id : resultingId;
onSelectFolder && onSelectFolder(resId);
isNeedArrowIcon && onSetBaseFolderPath(resId);
setFolderId(resId);
this.setState({
resultingFolderTree: tree,
});
}
componentDidUpdate(prevProps) {
const {
storeFolderId,
canCreate,
showButtons,
selectionButtonPrimary,
isReset,
} = this.props;
if (
showButtons &&
!selectionButtonPrimary &&
storeFolderId !== prevProps.storeFolderId
) {
this.setState({
canCreate: canCreate,
isLoading: false,
});
}
const { isReset } = this.props;
if (isReset && isReset !== prevProps.isReset) {
this.onResetInfo();
}
}
trySwitch = async () => {
const {
folderPath,
onSelectFolder,
onSetBaseFolderPath,
foldersType,
id,
selectedFolderId,
foldersList,
} = this.props;
switch (foldersType) {
case "exceptSortedByTags":
try {
const foldersTree = await getFoldersTree();
[folderList, this.noTreeSwitcher] = SelectFolderDialog.convertFolders(
foldersTree,
exceptSortedByTagsFolders
);
this.setBaseSettings();
} catch (err) {
console.error("error", err);
this.loadersCompletes();
}
break;
case "exceptPrivacyTrashFolders":
try {
const foldersTree = await getFoldersTree();
[folderList, this.noTreeSwitcher] = SelectFolderDialog.convertFolders(
foldersTree,
exceptPrivacyTrashFolders
);
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "common":
try {
folderList = await SelectFolderDialog.getCommonFolders();
folderPath.length === 0 &&
!selectedFolderId &&
onSelectFolder &&
onSelectFolder(`${id ? id : folderList[0].id}`);
this.setState({
folderId: `${
selectedFolderId ? selectedFolderId : id ? id : folderList[0].id
}`,
});
!id &&
!selectedFolderId &&
onSetBaseFolderPath &&
onSetBaseFolderPath(folderList[0].title);
this.setFolderInfo();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "third-party":
try {
folderList = foldersList
? foldersList
: await SelectFolderDialog.getCommonThirdPartyList();
this.setBaseSettings();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
}
};
loadersCompletes = () => {
const {
onSetLoadingData,
onSetLoadingInput,
} = this.props;
onSetLoadingData && onSetLoadingData(false);
onSetLoadingInput && onSetLoadingInput(false);
this.setState({
isLoadingData: false,
});
};
setBaseSettings = async () => {
const { isSetFolderImmediately } = this.state;
const {
onSelectFolder,
onSetBaseFolderPath,
id,
selectedFolderId,
showButtons,
} = this.props;
if (folderList.length === 0) {
this.setState({ isAvailable: false });
onSelectFolder(null);
this.loadersCompletes();
return;
}
!id && showButtons && this.setFolderToTree(folderList[0].id);
isSetFolderImmediately &&
!selectedFolderId &&
onSelectFolder &&
onSelectFolder(
`${selectedFolderId ? selectedFolderId : id ? id : folderList[0].id}`
);
isSetFolderImmediately &&
this.setState({
folderId: `${
selectedFolderId ? selectedFolderId : id ? id : folderList[0].id
}`,
});
if (onSetBaseFolderPath) {
try {
this.folderTitle = await SelectFolderDialog.getFolderPath(
id ? id : folderList[0].id
);
!id &&
!selectedFolderId &&
isSetFolderImmediately &&
onSetBaseFolderPath(this.folderTitle);
} catch (err) {
console.error(err);
}
}
this.setFolderInfo();
};
setFolderInfo = () => {
const {
id,
onSetFileName,
fileName,
selectedFolderId,
dialogWithFiles,
onSetBaseFolderPath,
} = this.props;
fileName && onSetFileName && onSetFileName(fileName);
if (!id && !selectedFolderId) {
this.loadersCompletes();
return;
}
if (selectedFolderId) {
onSetBaseFolderPath
? this.setBaseFolderPath(selectedFolderId)
: this.loadersCompletes();
}
if (id && !selectedFolderId) {
if (!dialogWithFiles) this.setSelectedFolder(id);
else {
this.setBaseFolderPath(id);
}
}
};
setBaseFolderPath = () => {
const { onSetBaseFolderPath, selectedFolderId } = this.props;
SelectFolderDialog.getFolderPath(selectedFolderId)
.then((folderPath) => (this.folderTitle = folderPath))
.then(() => onSetBaseFolderPath(this.folderTitle))
.catch((error) => console.log("error", error))
.finally(() => {
this.loadersCompletes();
});
};
setSelectedFolder = async (id) => {
const { onSetBaseFolderPath } = this.props;
let folder,
folderPath,
requests = [];
requests.push(getFolder(id));
if (onSetBaseFolderPath) {
requests.push(getFolderPath(id));
}
try {
[folder, folderPath] = await Promise.all(requests);
} catch (e) {
console.error(e);
}
folder && this.setFolderObjectToTree(id, folder);
if (onSetBaseFolderPath && folderPath) {
this.folderTitle = SelectFolderInput.setFullFolderPath(folderPath);
onSetBaseFolderPath(this.folderTitle);
}
this.loadersCompletes();
};
setFolderToTree = (id) => {
getFolder(id)
.then((data) => {
this.setFolderObjectToTree(id, data);
})
.catch((error) => console.log("error", error));
};
setFolderObjectToTree = (id, data) => {
const {
setSelectedNode,
setSelectedFolder,
selectionButtonPrimary,
setExpandedPanelKeys,
onSetBaseFolderPath,
} = this.props;
const isInput = !!onSetBaseFolderPath;
if (!selectionButtonPrimary || isInput) {
//TODO: it need for canCreate function now, will need when passed the folder id - need to come up with a different solution.
setSelectedNode([id + ""]);
const newPathParts = SelectFolderDialog.convertPathParts(data.pathParts);
isInput && setExpandedPanelKeys(newPathParts);
setSelectedFolder({
folders: data.folders,
...data.current,
pathParts: newPathParts,
...{ new: data.new },
});
}
};
componentWillUnmount() {
const {
setExpandedPanelKeys,
resetTreeFolders,
setSelectedFolder,
dialogWithFiles,
} = this.props;
const { setFolderTitle, setProviderKey, setFolderId } = this.props;
console.log("componentWillUnmount");
if (this.throttledResize) {
this.throttledResize && this.throttledResize.cancel();
window.removeEventListener("resize", this.throttledResize);
}
if (resetTreeFolders && !dialogWithFiles) {
setExpandedPanelKeys(null);
setSelectedFolder(null);
}
setFolderTitle("");
setProviderKey(null);
setFolderId(null);
}
getDisplayType = () => {
const displayType =
@ -386,132 +122,52 @@ class SelectFolderModalDialog extends React.Component {
this.setState({ displayType: displayType });
};
onSelect = async (folder) => {
const {
onSelectFolder,
onClose,
showButtons,
onSetFullPath,
selectionButtonPrimary,
onSetLoadingData,
onSetLoadingInput,
} = this.props;
const { folderId } = this.state;
onSelect = async (folder, treeNode) => {
const { setFolderId, folderId } = this.props;
let requests = [];
if (+folderId === +folder[0]) return;
if (isArrayEqual([folder[0]], [folderId])) {
return;
}
onSetLoadingData && onSetLoadingData(true);
onSetLoadingInput && onSetLoadingInput(true);
this.setState({
folderId: folder[0],
});
let folderInfo, folderPath;
if (showButtons && !selectionButtonPrimary) {
this.setState({
isLoading: true,
canCreate: false,
});
}
try {
if (showButtons && onSetFullPath) {
requests.push(getFolder(folder[0]), getFolderPath(folder));
[folderInfo, folderPath] = await Promise.all(requests);
} else {
showButtons
? (folderInfo = await getFolder(folder[0]))
: (folderPath = await getFolderPath(folder));
}
if (folderInfo) {
this.setFolderObjectToTree(folder[0], folderInfo);
}
if (folderPath) {
pathName = SelectFolderInput.setFullFolderPath(folderPath);
onSetFullPath && onSetFullPath(pathName);
}
} catch (e) {
console.error(e);
toastr.error();
if (showButtons) {
this.setState({
isLoading: false,
canCreate: true,
});
onClose && onClose();
}
}
onSelectFolder && onSelectFolder(folder[0]);
!showButtons && onClose && onClose();
this.loadersCompletes();
setFolderId(folder[0]);
};
onSave = (e) => {
const { onClose, onSave } = this.props;
const { folderId } = this.state;
onSave && onSave(e, folderId);
onClose = () => {
const { setExpandedPanelKeys, onClose, treeFolders } = this.props;
if (!treeFolders.length) {
setExpandedPanelKeys(null);
}
onClose && onClose();
};
onCloseAside = () => {
const { onClose } = this.props;
onClose && onClose();
};
onButtonClick = (e) => {
const {
onSave,
onSetNewFolderPath,
onSelectFolder,
withoutImmediatelyClose,
onSubmit,
providerKey,
folderTitle,
folderId,
} = this.props;
onSubmit && onSubmit(folderId, folderTitle, providerKey);
onSave && onSave(e, folderId);
onSetNewFolderPath && onSetNewFolderPath(folderId);
onSelectFolder && onSelectFolder(folderId);
!withoutImmediatelyClose && this.onClose();
};
onResetInfo = async () => {
const { id, foldersType, onSelectFolder } = this.props;
switch (foldersType) {
case "common":
try {
if (!id) {
folderList = await SelectFolderDialog.getCommonFolders();
}
onSelectFolder && onSelectFolder(`${id ? id : folderList[0].id}`);
this.setState({
folderId: `${id ? id : folderList[0].id}`,
});
this.setFolderToTree(id ? id : folderList[0].id);
this.loadersCompletes();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
case "third-party":
try {
if (!id) {
folderList = await SelectFolderDialog.getCommonThirdPartyList();
}
onSelectFolder && onSelectFolder(`${id ? id : folderList[0].id}`);
this.setState({
folderId: `${id ? id : folderList[0].id}`,
});
this.setFolderToTree(id ? id : folderList[0].id);
this.loadersCompletes();
} catch (err) {
console.error(err);
this.loadersCompletes();
}
break;
}
const { id, setFolderId } = this.props;
setFolderId(id);
};
render() {
@ -520,84 +176,88 @@ class SelectFolderModalDialog extends React.Component {
theme,
isPanelVisible,
zIndex,
onClose,
withoutProvider,
isNeedArrowIcon,
modalHeightContent,
asideHeightContent,
isNeedArrowIcon, //for aside view when selected file
header,
headerName,
dialogName,
footer,
showButtons,
buttonName,
isDisableTree,
folderId,
folderTitle,
expandedKeys,
isDisableButton,
} = this.props;
const {
isAvailable,
certainFolders,
folderId,
displayType,
isLoadingData,
canCreate,
isLoading,
isAvailable,
resultingFolderTree,
} = this.state;
const primaryButtonName = buttonName
? buttonName
: t("Common:SaveHereButton");
const name = dialogName ? dialogName : t("Common:SaveButton");
//console.log("Render Folder Component?", this.state);
return displayType === "aside" ? (
<SelectFolderDialogAsideView
theme={theme}
t={t}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
onClose={this.onCloseAside}
withoutProvider={withoutProvider}
isNeedArrowIcon={isNeedArrowIcon}
asideHeightContent={asideHeightContent}
isAvailable={isAvailable}
certainFolders={certainFolders}
certainFolders={true}
folderId={folderId}
folderList={folderList}
onSelect={this.onSelect}
onSave={this.onSave}
resultingFolderTree={resultingFolderTree}
onSelectFolder={this.onSelect}
onButtonClick={this.onButtonClick}
header={header}
headerName={headerName}
dialogName={isNeedArrowIcon ? t("Translations:FolderSelection") : name}
footer={footer}
showButtons={showButtons}
isLoadingData={isLoadingData}
canCreate={canCreate}
isLoading={isLoading}
primaryButtonName={this.buttonName}
noTreeSwitcher={this.noTreeSwitcher}
primaryButtonName={
isNeedArrowIcon ? t("Common:Select") : primaryButtonName
}
isAvailable={isAvailable}
isDisableTree={isDisableTree}
isDisableButton={isDisableButton}
/>
) : (
<SelectFolderDialogModalView
<SelectionPanel
t={t}
theme={theme}
isPanelVisible={isPanelVisible}
zIndex={zIndex}
onClose={onClose}
onClose={this.onClose}
withoutProvider={withoutProvider}
modalHeightContent={modalHeightContent}
isAvailable={isAvailable}
certainFolders={certainFolders}
folderId={folderId}
folderList={folderList}
onSelect={this.onSelect}
onSave={this.onSave}
resultingFolderTree={resultingFolderTree}
onButtonClick={this.onButtonClick}
header={header}
headerName={headerName}
dialogName={name}
footer={footer}
showButtons={showButtons}
canCreate={canCreate}
isLoadingData={isLoadingData}
isLoading={isLoading}
primaryButtonName={this.buttonName}
noTreeSwitcher={this.noTreeSwitcher}
primaryButtonName={primaryButtonName}
isAvailable={isAvailable}
onSelectFolder={this.onSelect}
folderTitle={folderTitle}
expandedKeys={expandedKeys}
isDisableTree={isDisableTree}
folderSelection
newFilter={this.newFilter}
isDisableButton={isDisableButton}
/>
);
}
}
SelectFolderModalDialog.propTypes = {
SelectFolderDialog.propTypes = {
onSelectFolder: PropTypes.func,
onClose: PropTypes.func.isRequired,
onClose: PropTypes.func,
isPanelVisible: PropTypes.bool.isRequired,
foldersType: PropTypes.oneOf([
"common",
@ -606,153 +266,60 @@ SelectFolderModalDialog.propTypes = {
"exceptPrivacyTrashFolders",
]),
displayType: PropTypes.oneOf(["aside", "modal"]),
id: PropTypes.string,
zIndex: PropTypes.number,
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
withoutProvider: PropTypes.bool,
isNeedArrowIcon: PropTypes.bool,
dialogWithFiles: PropTypes.bool,
showButtons: PropTypes.bool,
selectionButtonPrimary: PropTypes.bool,
modalHeightContent: PropTypes.string,
asideHeightContent: PropTypes.string,
withoutImmediatelyClose: PropTypes.bool,
isDisableTree: PropTypes.bool,
};
SelectFolderModalDialog.defaultProps = {
isSetFolderImmediately: false,
dialogWithFiles: false,
isNeedArrowIcon: false,
SelectFolderDialog.defaultProps = {
id: "",
modalHeightContent: "291px",
asideHeightContent: "100%",
zIndex: 310,
withoutProvider: false,
folderPath: "",
showButtons: false,
selectionButtonPrimary: false,
withoutImmediatelyClose: false,
isDisableTree: false,
};
const SelectFolderDialogWrapper = inject(
export default inject(
({
treeFoldersStore,
selectedFolderStore,
selectedFilesStore,
selectFolderDialogStore,
filesStore,
auth,
}) => {
const { setSelectedNode, setExpandedPanelKeys } = treeFoldersStore;
const { canCreate } = filesStore;
const { setSelectedFolder, id } = selectedFolderStore;
const { setFolderId, setFile } = selectedFilesStore;
const { treeFolders, setExpandedPanelKeys } = treeFoldersStore;
const { filter } = filesStore;
const { id } = selectedFolderStore;
const {
setFolderId,
setFolderTitle,
setProviderKey,
providerKey,
folderTitle,
folderId,
} = selectFolderDialogStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
theme: auth.settingsStore.theme,
setSelectedFolder,
setSelectedNode,
canCreate,
theme: theme,
storeFolderId: id,
providerKey,
folderTitle,
folderId,
setExpandedPanelKeys,
setFolderId,
setFile,
setFolderTitle,
setProviderKey,
treeFolders,
filter,
};
}
)(
observer(
withTranslation(["SelectFolder", "Common", "Translations"])(
SelectFolderModalDialog
SelectFolderDialog
)
)
);
class SelectFolderDialog extends React.Component {
static getCommonThirdPartyList = async () => {
const commonThirdPartyArray = await getCommonThirdPartyList();
commonThirdPartyArray.map((currentValue, index) => {
commonThirdPartyArray[index].key = `0-${index}`;
});
return commonThirdPartyArray;
};
static getCommonFolders = async () => {
const commonFolders = await getCommonFolderList();
const convertedData = {
id: commonFolders.current.id,
key: 0 - 1,
parentId: commonFolders.current.parentId,
title: commonFolders.current.title,
rootFolderType: +commonFolders.current.rootFolderType,
rootFolderName: "@common",
folders: commonFolders.folders.map((folder) => {
return {
id: folder.id,
title: folder.title,
access: folder.access,
foldersCount: folder.foldersCount,
rootFolderType: folder.rootFolderType,
providerKey: folder.providerKey,
newItems: folder.new,
};
}),
pathParts: commonFolders.pathParts,
foldersCount: commonFolders.current.foldersCount,
newItems: commonFolders.new,
};
return [convertedData];
};
static getFolderPath = async (folderId) => {
const foldersArray = await getFolderPath(folderId);
const convertFoldersArray = SelectFolderInput.setFullFolderPath(
foldersArray
);
return convertFoldersArray;
};
static convertPathParts = (pathParts) => {
let newPathParts = [];
for (let i = 0; i < pathParts.length - 1; i++) {
if (typeof pathParts[i] === "number") {
newPathParts.push(String(pathParts[i]));
} else {
newPathParts.push(pathParts[i]);
}
}
return newPathParts;
};
static convertFolders = (folders, arrayOfExceptions) => {
let newArray = [];
let noSubfoldersCount = 0;
let needHideSwitcher = false;
for (let i = 0; i < folders.length; i++) {
if (!arrayOfExceptions.includes(folders[i].rootFolderType)) {
newArray.push(folders[i]);
if (
folders[i].foldersCount === 0 ||
folders[i].rootFolderType === FolderType.Privacy
) {
noSubfoldersCount += 1;
}
}
}
if (newArray.length === noSubfoldersCount) {
needHideSwitcher = true;
}
return [newArray, needHideSwitcher];
};
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFolderDialogWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFolderDialog;

View File

@ -0,0 +1,28 @@
import React from "react";
import { Provider as MobxProvider } from "mobx-react";
import { I18nextProvider } from "react-i18next";
import stores from "../../../store/index";
import store from "studio/store";
import SelectFolderInput from "./index";
import i18n from "./i18n";
const { auth: authStore } = store;
const SelectFolderModalWrapper = (props) => <SelectFolderInput {...props} />;
class SelectFolderInputWrapper extends React.Component {
componentDidMount() {
authStore.init(true);
}
render() {
return (
<MobxProvider auth={authStore} {...stores}>
<I18nextProvider i18n={i18n}>
<SelectFolderModalWrapper {...this.props} />
</I18nextProvider>
</MobxProvider>
);
}
}
export default SelectFolderInputWrapper;

View File

@ -0,0 +1,35 @@
import i18n from "i18next";
import Backend from "i18next-http-backend";
import { LANGUAGE } from "@appserver/common/constants";
import config from "../../../../package.json";
import { loadLanguagePath } from "@appserver/common/utils";
const newInstance = i18n.createInstance();
newInstance.use(Backend).init({
lng: localStorage.getItem(LANGUAGE) || "en",
fallbackLng: "en",
load: "currentOnly",
//debug: true,
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
format: function (value, format) {
if (format === "lowercase") return value.toLowerCase();
return value;
},
},
backend: {
loadPath: loadLanguagePath(config.homepage),
},
ns: ["SelectFolder"],
defaultNS: "SelectFolder",
react: {
useSuspense: false,
},
});
export default newInstance;

View File

@ -1,204 +1,229 @@
import React from "react";
import { Provider as MobxProvider, inject, observer } from "mobx-react";
import { inject, observer } from "mobx-react";
import PropTypes from "prop-types";
import stores from "../../../store/index";
import SelectFolderDialog from "../SelectFolderDialog/index";
import StyledComponent from "./StyledSelectFolderInput";
import { getFolderPath } from "@appserver/common/api/files";
import toastr from "@appserver/components/toast/toastr";
import SelectFolderDialog from "../SelectFolderDialog";
import SimpleFileInput from "../../SimpleFileInput";
let path = "";
class SelectFolderInputBody extends React.PureComponent {
import { withTranslation } from "react-i18next";
import SelectionPanel from "../SelectionPanel/SelectionPanelBody";
class SelectFolderInput extends React.PureComponent {
constructor(props) {
super(props);
const { id, foldersType } = this.props;
const isNeedLoader =
!!id || foldersType !== "third-party" || foldersType === "common";
this.state = {
isLoading: false,
isLoading: isNeedLoader,
baseFolderPath: "",
fullFolderPath: "",
newFolderPath: "",
resultingFolderTree: [],
baseId: "",
};
}
componentDidMount() {
const { folderPath, setFirstLoad } = this.props;
if (folderPath.length !== 0) {
this.setState({
fullFolderPath: folderPath,
});
}
async componentDidMount() {
const {
setFirstLoad,
treeFolders,
foldersType,
id,
onSelectFolder,
foldersList,
} = this.props;
setFirstLoad(false);
let resultingFolderTree, resultingId;
try {
[
resultingFolderTree,
resultingId,
] = await SelectionPanel.getBasicFolderInfo(
treeFolders,
foldersType,
id,
this.onSetBaseFolderPath,
onSelectFolder,
foldersList
);
} catch (e) {
toastr.error(e);
return;
}
this.setState({
resultingFolderTree,
baseId: resultingId,
});
}
componentDidUpdate(prevProps) {
const { isSuccessSave, isReset } = this.props;
const { fullFolderPath, baseFolderPath } = this.state;
const { isSuccessSave, isReset, setFolderId, id } = this.props;
const { newFolderPath, baseFolderPath, baseId } = this.state;
if (isSuccessSave && isSuccessSave !== prevProps.isSuccessSave) {
fullFolderPath &&
newFolderPath &&
this.setState({
baseFolderPath: fullFolderPath,
baseFolderPath: newFolderPath,
});
}
if (isReset && isReset !== prevProps.isReset) {
setFolderId(baseId !== id && id ? id : baseId);
this.setState({
fullFolderPath: baseFolderPath,
newFolderPath: baseFolderPath,
});
}
}
setFolderPath = async (folderId) => {
const foldersArray = await getFolderPath(folderId);
onSetFullPath = (pathName) => {
const convertFolderPath = (foldersArray) => {
let path = "";
if (foldersArray.length > 1) {
for (let item of foldersArray) {
if (!path) {
path = path + `${item.title}`;
} else path = path + " " + "/" + " " + `${item.title}`;
}
} else {
for (let item of foldersArray) {
path = `${item.title}`;
}
}
return path;
};
const convertFoldersArray = convertFolderPath(foldersArray);
return convertFoldersArray;
};
onSetNewFolderPath = async (folderId) => {
let timerId = setTimeout(() => {
this.setState({ isLoading: true });
}, 500);
try {
const convertFoldersArray = await this.setFolderPath(folderId);
clearTimeout(timerId);
timerId = null;
this.setState({
newFolderPath: convertFoldersArray,
isLoading: false,
});
} catch (e) {
toastr.error(e);
clearTimeout(timerId);
timerId = null;
this.setState({
isLoading: false,
});
}
};
onSetBaseFolderPath = async (folderId) => {
try {
const convertFoldersArray = await this.setFolderPath(folderId);
this.setState({
baseFolderPath: convertFoldersArray,
isLoading: false,
});
} catch (e) {
toastr.error(e);
this.setState({
isLoading: false,
});
}
};
onSetLoadingInput = (isLoading) => {
this.setState({
fullFolderPath: pathName,
isLoading,
});
};
onSetBaseFolderPath = (pathName) => {
this.setState({
baseFolderPath: pathName,
});
};
onSetLoadingInput = (loading) => {
this.setState({
isLoading: loading,
});
};
render() {
const {
name,
isLoading,
baseFolderPath,
newFolderPath,
baseId,
resultingFolderTree,
} = this.state;
const {
onClickInput,
isPanelVisible,
withoutProvider,
onClose,
isError,
isSavingProcess,
isDisabled,
onSelectFolder,
onSetLoadingData,
foldersType,
folderPath,
isNeedArrowIcon,
isSetFolderImmediately,
id,
selectedFolderId,
displayType,
dialogWithFiles,
modalHeightContent,
asideHeightContent,
zIndex,
showButtons,
header,
headerName,
footer,
selectionButtonPrimary,
fontSizeInput,
t,
placeholder,
maxInputWidth,
isReset,
foldersList,
isDisabled,
isPanelVisible,
id,
theme,
isFolderTreeLoading = false,
...rest
} = this.props;
const { isLoading, baseFolderPath, fullFolderPath } = this.state;
const passedId = baseId !== id && id ? id : baseId;
return (
<StyledComponent maxInputWidth={maxInputWidth} theme={theme}>
<StyledComponent maxWidth={maxInputWidth}>
<SimpleFileInput
theme={theme}
name={name}
className="select-folder_file-input"
textField={fullFolderPath || baseFolderPath}
isDisabled={isLoading || isSavingProcess || isDisabled}
textField={newFolderPath || baseFolderPath}
isError={isError}
onClickInput={onClickInput}
fontSizeInput={fontSizeInput}
maxInputWidth={maxInputWidth}
placeholder={placeholder}
isDisabled={isFolderTreeLoading || isDisabled || isLoading}
/>
<SelectFolderDialog
theme={theme}
zIndex={zIndex}
isPanelVisible={isPanelVisible}
onClose={onClose}
folderPath={folderPath}
onSelectFolder={onSelectFolder}
onSetLoadingData={onSetLoadingData}
foldersType={foldersType}
withoutProvider={withoutProvider}
onSetFullPath={this.onSetFullPath}
onSetBaseFolderPath={this.onSetBaseFolderPath}
onSetLoadingInput={this.onSetLoadingInput}
isNeedArrowIcon={isNeedArrowIcon}
isSetFolderImmediately={isSetFolderImmediately}
id={id}
selectedFolderId={selectedFolderId}
displayType={displayType}
dialogWithFiles={dialogWithFiles}
modalHeightContent={modalHeightContent}
asideHeightContent={asideHeightContent}
showButtons={showButtons}
header={header}
headerName={headerName}
footer={footer}
selectionButtonPrimary={selectionButtonPrimary}
isReset={isReset}
foldersList={foldersList}
/>
{!isFolderTreeLoading && isPanelVisible && (
<SelectFolderDialog
{...rest}
withInput={true}
folderTree={resultingFolderTree}
id={passedId}
isPanelVisible={isPanelVisible}
onSetBaseFolderPath={this.onSetBaseFolderPath}
onSetNewFolderPath={this.onSetNewFolderPath}
/>
)}
</StyledComponent>
);
}
}
SelectFolderInputBody.propTypes = {
SelectFolderInput.propTypes = {
onClickInput: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
onSelectFolder: PropTypes.func.isRequired,
onSetLoadingData: PropTypes.func,
isPanelVisible: PropTypes.bool.isRequired,
name: PropTypes.string,
withoutProvider: PropTypes.bool,
isError: PropTypes.bool,
isSavingProcess: PropTypes.bool,
hasError: PropTypes.bool,
isDisabled: PropTypes.bool,
placeholder: PropTypes.string,
};
SelectFolderInputBody.defaultProps = {
withoutProvider: false,
SelectFolderInput.defaultProps = {
hasError: false,
isDisabled: false,
isError: false,
folderPath: "",
placeholder: "",
};
const SelectFolderInputBodyWrapper = inject(({ filesStore }) => {
const { setFirstLoad } = filesStore;
return {
setFirstLoad,
};
})(observer(SelectFolderInputBody));
class SelectFolderInput extends React.Component {
static setFullFolderPath = (foldersArray) => {
path = "";
if (foldersArray.length > 1) {
for (let item of foldersArray) {
if (!path) {
path = path + `${item.title}`;
} else path = path + " " + "/" + " " + `${item.title}`;
}
} else {
for (let item of foldersArray) {
path = `${item.title}`;
}
}
return path;
};
render() {
return (
<MobxProvider {...stores}>
<SelectFolderInputBodyWrapper {...this.props} />
</MobxProvider>
);
export default inject(
({ filesStore, treeFoldersStore, selectFolderDialogStore }) => {
const { setFirstLoad } = filesStore;
const { treeFolders } = treeFoldersStore;
const { setFolderId } = selectFolderDialogStore;
return {
setFirstLoad,
treeFolders,
setFolderId,
};
}
}
export default SelectFolderInput;
)(observer(withTranslation("Translations")(SelectFolderInput)));

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef } from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import Loader from "@appserver/components/loader";
import Text from "@appserver/components/text";
import { useTranslation, withTranslation } from "react-i18next";
@ -9,152 +9,163 @@ import { FixedSizeList as List } from "react-window";
import { inject, observer } from "mobx-react";
import FilesListRow from "./FilesListRow";
import EmptyContainer from "../../EmptyContainer/EmptyContainer";
import i18n from "./i18n";
import { StyledItemsLoader } from "../SelectionPanel/StyledSelectionPanel";
import Loaders from "@appserver/common/components/Loaders";
import { I18nextProvider } from "react-i18next";
let countLoad;
let countLoad, timerId;
const FilesListBody = ({
filesList,
files,
onSelectFile,
loadNextPage,
hasNextPage,
isNextPageLoading,
displayType,
viewer,
listHeight,
needRowSelection,
loadingText,
selectedFolder,
isMultiSelect,
selectedFile,
folderId,
fileId,
theme,
page,
folderSelection,
getIcon,
}) => {
const { t } = useTranslation(["SelectFile", "Common"]);
const [isLoading, setIsLoading] = useState(false);
const filesListRef = useRef(null);
if (page === 0) {
countLoad = 0;
}
useEffect(() => {
countLoad = 0;
return () => {
clearTimeout(timerId);
timerId = null;
};
}, []);
useEffect(() => {
if (filesListRef && filesListRef.current) {
filesListRef.current.resetloadMoreItemsCache(true);
}
}, [selectedFolder, displayType]);
}, [folderId, page, displayType]);
useEffect(() => {
if (isNextPageLoading) {
timerId = setTimeout(() => {
setIsLoading(true);
}, 500);
} else {
isLoading && setIsLoading(false);
clearTimeout(timerId);
timerId = null;
}
}, [isNextPageLoading]);
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const itemCount = hasNextPage ? files.length + 1 : files.length;
// Every row is loaded except for our loading indicator row.
const isItemLoaded = useCallback(
(index) => {
return !hasNextPage || index < filesList.length;
const isLoaded = !hasNextPage || index < files.length;
if (isLoaded) {
clearTimeout(timerId);
timerId = null;
}
return isLoaded;
},
[filesList, hasNextPage]
[files, hasNextPage]
);
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const itemCount = hasNextPage ? filesList.length + 1 : filesList.length;
const loadMoreItems = useCallback(() => {
if (folderId && page == 0 && isNextPageLoading) {
loadNextPage && loadNextPage();
return;
}
if (isNextPageLoading) return;
countLoad++;
loadNextPage && loadNextPage();
}, [isNextPageLoading, filesList, displayType]);
const renderPageLoader = useCallback(
(style) => {
return (
<div style={style}>
<div
key="loader"
className="panel-loader-wrapper loader-wrapper_margin"
>
<Loader
theme={theme}
type="oval"
size="16px"
className="panel-loader"
/>
<Text theme={theme} as="span">
{loadingText}
</Text>
</div>
</div>
);
},
[loadingText]
);
folderId && loadNextPage && loadNextPage();
}, [isNextPageLoading, files, displayType, folderId]);
const renderPageLoader = (style) => {
return (
<div style={style}>
<StyledItemsLoader key="loader">
<Loader
theme={theme}
type="oval"
size="16px"
className="panel-loader"
/>
<Text theme={theme} as="span">
{t("Common:LoadingProcessing")} {t("Common:LoadingDescription")}
</Text>
</StyledItemsLoader>
</div>
);
};
const renderFirstLoader = useCallback(
(style) => {
return (
<div style={style}>
<div
key="loader"
className="panel-loader-wrapper loader-wrapper_margin"
>
<Loaders.Rows
theme={theme}
style={{
marginBottom: displayType === "aside" ? "24px" : "26px",
marginTop: displayType === "aside" ? "8px" : "10px",
}}
count={displayType === "aside" ? 12 : 5}
/>
<div className="selection-panel_loader" key="loader">
{isLoading ? <Loaders.ListLoader withoutFirstRectangle /> : <></>}
</div>
</div>
);
},
[loadingText]
[isLoading]
);
const isFileChecked = useCallback(
(file) => {
const checked = selectedFile ? file.id === selectedFile.id : false;
(id) => {
const checked = fileId ? id === fileId : false;
return checked;
},
[selectedFile]
[fileId]
);
const Item = useCallback(
({ index, style }) => {
const isLoaded = isItemLoaded(index);
if (!isLoaded) {
if (countLoad >= 1) return renderPageLoader(style);
if (!isLoaded || !folderId) {
if (countLoad >= 1) {
return renderPageLoader(style);
}
return renderFirstLoader(style);
}
const file = filesList[index];
const fileName = file.title;
const fileExst = file.fileExst;
const modifyFileName = fileName.substring(
0,
fileName.indexOf(`${fileExst}`)
);
const fileOwner =
file.createdBy &&
((viewer.id === file.createdBy.id && t("Common:MeLabel")) ||
file.createdBy.displayName);
const isChecked = isFileChecked(file);
const item = files[index];
const isChecked = folderSelection ? false : isFileChecked(item.id);
return (
<div style={style}>
<FilesListRow
theme={theme}
displayType={displayType}
needRowSelection={needRowSelection}
index={index}
onSelectFile={onSelectFile}
fileName={modifyFileName}
fileExst={fileExst}
isMultiSelect={isMultiSelect}
item={item}
isChecked={isChecked}
>
<Text data-index={index} className="files-list_file-owner">
{fileOwner}
</Text>
</FilesListRow>
folderSelection={folderSelection}
icon={getIcon(
32,
item.fileExst,
item.providerKey,
item.contentLength
)}
/>
</div>
);
},
[filesList, selectedFile, displayType, renderFirstLoader, renderPageLoader]
[files, fileId, displayType, renderFirstLoader, renderPageLoader]
);
return (
<div className="files-list-body">
<div className="selection-panel_files-list-body">
<AutoSizer>
{({ width, height }) => (
<InfiniteLoader
@ -167,9 +178,9 @@ const FilesListBody = ({
{({ onItemsRendered, ref }) => (
<List
theme={theme}
height={displayType === "aside" ? height : listHeight}
height={height}
itemCount={itemCount}
itemSize={displayType === "aside" ? 56 : 50}
itemSize={48}
onItemsRendered={onItemsRendered}
ref={ref}
width={width + 8}
@ -187,7 +198,7 @@ const FilesListBody = ({
<EmptyContainer
theme={theme}
headerText={t("Home:EmptyFolderHeader")}
imageSrc="/static/images/empty_screen.png"
imageSrc="/static/images/empty.screen.react.svg"
/>
</div>
)}
@ -198,21 +209,12 @@ FilesListBody.defaultProps = {
listHeight: 300,
isMultiSelect: false,
};
const FilesListBodyWrapper = inject(({ auth }) => {
export default inject(({ auth, settingsStore }) => {
const { user } = auth.userStore;
const { getIcon } = settingsStore;
return {
viewer: user,
getIcon,
};
})(observer(withTranslation(["Common", "Home"])(FilesListBody)));
class FilesList extends React.Component {
render() {
return (
<I18nextProvider i18n={i18n}>
<FilesListBodyWrapper {...this.props} />
</I18nextProvider>
);
}
}
export default FilesList;

View File

@ -0,0 +1,54 @@
import React from "react";
import { StyledRow } from "./StyledSelectionPanel";
import Text from "@appserver/components/text";
import RadioButton from "@appserver/components/radio-button";
import ItemIcon from "../../ItemIcon";
const FilesListRow = ({
displayType,
index,
onSelectFile,
isChecked,
theme,
folderSelection,
icon,
item,
}) => {
const { id, fileExst, title } = item;
const element = <ItemIcon id={id} icon={icon} fileExst={fileExst} />;
const onFileClick = () => {
onSelectFile && onSelectFile(item, index);
};
return (
<StyledRow
displayType={displayType}
theme={theme}
isChecked={isChecked}
folderSelection={folderSelection}
>
<div className="selection-panel_icon">{element}</div>
<div className="selection-panel_text">
<Text fontSize="14px" fontWeight={600}>
{title}
</Text>
</div>
<div className="selection-panel_checkbox">
{!folderSelection && (
<RadioButton
//theme={theme}
fontSize="13px"
fontWeight="400"
name={`${index}`}
label=""
isChecked={isChecked}
onClick={onFileClick}
value=""
className="select-file-dialog_checked"
/>
)}
</div>
</StyledRow>
);
};
export default FilesListRow;

View File

@ -0,0 +1,151 @@
import React from "react";
import { inject, observer } from "mobx-react";
import toastr from "studio/toastr";
import FilesListBody from "./FilesListBody";
import axios from "axios";
import { combineUrl, getFolderOptions } from "@appserver/common/utils";
import AppServerConfig from "@appserver/common/constants/AppServerConfig";
class FilesListWrapper extends React.Component {
constructor(props) {
super(props);
const { newFilter } = this.props;
this.newFilter = newFilter;
this.state = {
isNextPageLoading: false,
page: 0,
hasNextPage: true,
files: [],
};
}
componentDidUpdate(prevProps) {
const { folderId } = this.props;
const { isNextPageLoading } = this.state;
if (folderId !== prevProps.folderId) {
if (isNextPageLoading) {
this.source.cancel();
this._isLoadNextPage = false;
this.setState({
isNextPageLoading: false,
});
}
this.setState({
page: 0,
files: [],
hasNextPage: true,
});
}
}
_loadNextPage = () => {
const { files, page } = this.state;
const {
folderId,
setFolderTitle,
setProviderKey,
setFolderId,
folderSelection,
} = this.props;
if (this._isLoadNextPage) return;
const pageCount = 30;
this.newFilter.page = page;
this.newFilter.pageCount = pageCount;
this._isLoadNextPage = true;
this.setState({ isNextPageLoading: true }, async () => {
try {
this.CancelToken = axios.CancelToken;
this.source = this.CancelToken.source();
const options = getFolderOptions(folderId, this.newFilter);
const response = await axios
.get(combineUrl(AppServerConfig.apiPrefixURL, options.url), {
cancelToken: this.source.token,
})
.catch((thrown) => {
if (axios.isCancel(thrown)) {
console.log("Request canceled", thrown.message);
} else {
console.error(thrown);
}
return;
});
if (!response) return;
const data = response.data.response;
if (page === 0 && folderSelection) {
setFolderTitle(data.current.title);
setProviderKey(data.current.providerKey);
setFolderId(folderId);
}
const finalData = [...data.files];
const newFilesList = [...files].concat(finalData);
const hasNextPage = newFilesList.length < data.total - 1;
this._isLoadNextPage = false;
this.setState((state) => ({
hasNextPage: hasNextPage,
isNextPageLoading: false,
page: state.page + 1,
files: newFilesList,
}));
} catch (e) {
toastr.error(e);
}
});
};
render() {
const {
t,
theme,
onSelectFile,
folderSelection = false,
fileId,
folderId,
} = this.props;
const { hasNextPage, isNextPageLoading, files, page } = this.state;
return (
<FilesListBody
theme={theme}
files={files}
onSelectFile={onSelectFile}
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
loadNextPage={this._loadNextPage}
folderId={folderId}
displayType={"modal"}
folderSelection={folderSelection}
fileId={fileId}
page={page}
/>
);
}
}
export default inject(
({ selectedFolderStore, selectFolderDialogStore, auth }) => {
const { id } = selectedFolderStore;
const {
setFolderId,
setFolderTitle,
setProviderKey,
} = selectFolderDialogStore;
const { settingsStore } = auth;
const { theme } = settingsStore;
return {
theme: theme,
storeFolderId: id,
setFolderId,
setFolderTitle,
setProviderKey,
};
}
)(observer(FilesListWrapper));

View File

@ -0,0 +1,278 @@
import React from "react";
import ModalDialog from "@appserver/components/modal-dialog";
import FolderTreeBody from "../../FolderTreeBody";
import Button from "@appserver/components/button";
import FilesListWrapper from "./FilesListWrapper";
import {
getCommonFoldersTree,
getFolder,
getFoldersTree,
getThirdPartyCommonFolderTree,
} from "@appserver/common/api/files";
import toastr from "studio/toastr";
import {
exceptSortedByTagsFolders,
exceptPrivacyTrashFolders,
} from "./ExceptionFoldersConstants";
import { StyledBody, StyledModalDialog } from "./StyledSelectionPanel";
import Text from "@appserver/components/text";
import Loaders from "@appserver/common/components/Loaders";
const SelectionPanelBody = ({
t,
isPanelVisible,
onClose,
withoutProvider,
onSelectFile,
filesListTitle,
dialogName,
primaryButtonName,
theme,
isLoading,
onButtonClick,
folderId,
onSelectFolder,
resultingFolderTree,
isAvailable,
footer,
header,
folderSelection = false,
folderTitle,
fileId,
canCreate = true,
isLoadingData,
expandedKeys,
isDisableTree,
page,
newFilter,
isDisableButton,
}) => {
const onMouseEvent = (event) => {
event.stopPropagation();
};
return (
<div onMouseUp={onMouseEvent} onMouseDown={onMouseEvent}>
<StyledModalDialog
theme={theme}
visible={isPanelVisible}
onClose={onClose}
className="select-file-modal-dialog"
style={{ maxWidth: "773px" }}
displayType="modal"
modalBodyPadding="0px"
isLoading={isLoading}
>
<ModalDialog.Header theme={theme}>{dialogName}</ModalDialog.Header>
<ModalDialog.Body
theme={theme}
className="select-file_body-modal-dialog"
>
<StyledBody header={!!header} footer={!!footer}>
<div className="selection-panel_body">
<div className="selection-panel_tree-body">
<Text
fontWeight="700"
fontSize="18px"
className="selection-panel_folder-title"
>
{t("Translations:Documents")}
</Text>
{folderId && resultingFolderTree ? (
<FolderTreeBody
theme={theme}
folderTree={resultingFolderTree}
onSelect={onSelectFolder}
withoutProvider={withoutProvider}
certainFolders
isAvailable={isAvailable}
selectedKeys={[`${folderId}`]}
expandedKeys={expandedKeys}
isDisableTree={isDisableTree}
displayType="modal"
/>
) : (
<Loaders.NewTreeFolders />
)}
</div>
<div className="selection-panel_files-body">
<>
<div className="selection-panel_files-header">
{header}
<Text
color="#A3A9AE"
theme={theme}
className="selection-panel_title"
>
{folderSelection
? t("FolderContents", { folderTitle })
: filesListTitle}
</Text>
</div>
<FilesListWrapper
theme={theme}
onSelectFile={onSelectFile}
folderId={folderId}
displayType={"modal"}
folderSelection={folderSelection}
newFilter={newFilter}
fileId={fileId}
/>
</>
</div>
<div className="selection-panel_footer">
<div>{footer}</div>
<div className="selection-panel_buttons">
<Button
theme={theme}
className="select-file-modal-dialog-buttons-save"
primary
size="normalTouchscreen"
label={primaryButtonName}
onClick={onButtonClick}
isDisabled={
isDisableButton ||
isDisableTree ||
isLoadingData ||
(!fileId && !folderSelection) ||
!canCreate
}
isLoading={isDisableTree}
/>
<Button
theme={theme}
className="modal-dialog-button"
size="normalTouchscreen"
label={t("Common:CancelButton")}
onClick={onClose}
isDisabled={isLoadingData}
/>
</div>
</div>
</div>
</StyledBody>
</ModalDialog.Body>
</StyledModalDialog>
</div>
);
};
class SelectionPanel extends React.Component {
static getFolderPath = async (id) => {
try {
const data = await getFolder(id);
const newPathParts = data.pathParts.map((item) => item.toString());
+newPathParts[newPathParts.length - 1] === +id && newPathParts.pop();
return newPathParts;
} catch (e) {
toastr.error(e);
return null;
}
};
static getBasicFolderInfo = async (
treeFolders,
foldersType,
id,
onSetBaseFolderPath,
onSelectFolder,
foldersList,
isFilesPanel = false
) => {
const getRequestFolderTree = () => {
switch (foldersType) {
case "exceptSortedByTags":
case "exceptPrivacyTrashFolders":
try {
return getFoldersTree();
} catch (err) {
console.error(err);
}
break;
case "common":
try {
return getCommonFoldersTree();
} catch (err) {
console.error(err);
}
break;
case "third-party":
try {
return getThirdPartyCommonFolderTree();
} catch (err) {
console.error(err);
}
break;
}
};
const filterFoldersTree = (folders, arrayOfExceptions) => {
let newArray = [];
for (let i = 0; i < folders.length; i++) {
if (!arrayOfExceptions.includes(folders[i].rootFolderType)) {
newArray.push(folders[i]);
}
}
return newArray;
};
const getExceptionsFolders = (treeFolders) => {
switch (foldersType) {
case "exceptSortedByTags":
return filterFoldersTree(treeFolders, exceptSortedByTagsFolders);
case "exceptPrivacyTrashFolders":
return filterFoldersTree(treeFolders, exceptPrivacyTrashFolders);
}
};
let requestedTreeFolders, filteredTreeFolders;
const treeFoldersLength = treeFolders.length;
if (treeFoldersLength === 0) {
try {
requestedTreeFolders = foldersList
? foldersList
: await getRequestFolderTree();
} catch (e) {
toastr.error(e);
return;
}
}
const foldersTree =
treeFoldersLength > 0 ? treeFolders : requestedTreeFolders;
const passedId = id ? id : foldersTree[0].id;
if (foldersType === "third-party") {
isFilesPanel && onSetBaseFolderPath && onSetBaseFolderPath(passedId);
} else {
onSetBaseFolderPath && onSetBaseFolderPath(passedId);
}
onSelectFolder && onSelectFolder(passedId);
if (
foldersType === "exceptSortedByTags" ||
foldersType === "exceptPrivacyTrashFolders"
) {
filteredTreeFolders = getExceptionsFolders(foldersTree);
}
return [filteredTreeFolders || foldersTree, passedId];
};
render() {
return <SelectionPanelBody {...this.props} />;
}
}
export default SelectionPanel;

View File

@ -0,0 +1,284 @@
import styled, { css } from "styled-components";
import ModalDialog from "@appserver/components/modal-dialog";
const commonStyles = css`
.empty-folder_container {
grid-template-areas:
"img img"
"headerText headerText";
grid-template-rows: 72px 1fr;
padding-bottom: 0;
.ec-image {
margin: auto;
}
.ec-header {
margin: auto;
}
}
`;
const StyledModalDialog = styled(ModalDialog)`
.heading {
line-height: 52px;
font-size: 21px;
}
.modal-dialog-aside-header {
height: 53px;
}
`;
const StyledBody = styled.div`
.selection-panel_body {
height: 495px;
display: grid;
grid-template-columns: 245px 1fr;
grid-template-areas: "tree files" "footer footer";
grid-template-rows: auto max-content;
margin-right: -4px;
.selection-panel_files-body {
grid-area: files;
display: grid;
grid-template-rows: max-content auto;
}
.selection-panel_files-list-body {
height: 100%;
}
.selection-panel_tree-body {
grid-area: tree;
height: 100%;
border-right: 1px solid #eceef1;
display: grid;
grid-template-rows: max-content auto;
.selection-panel_folder-title {
padding: 12px 20px 14px 0px;
}
.selection-panel_tree-folder {
margin-left: -12px;
}
.span.rc-tree-switcher {
padding-left: 16px;
}
}
.selection-panel_files-header {
padding: 16px;
word-break: break-word;
.selection-panel_title {
${(props) => props.header && "padding-top: 16px"};
}
}
.selection-panel_footer {
grid-area: footer;
border-top: 1px solid ${(props) => props.theme.row.borderBottom};
margin-left: -13px;
margin-right: -7px;
padding-left: 16px;
padding-top: 16px;
.selection-panel_buttons {
${(props) => props.footer && "margin-top:16px"};
button:first-child {
margin-right: 8px;
}
}
}
}
${commonStyles}
`;
const StyledRow = styled.div`
display: grid;
grid-template-columns: 32px auto 32px;
grid-gap: 8px;
position: relative;
height: 48px;
width: calc(100% - 16px);
padding-left: 16px;
${(props) =>
props.isChecked && `background: ${props.theme.row.backgroundColor}`};
.selection-panel_clicked-area {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.selection-panel_text {
margin-top: auto;
margin-bottom: 16px;
overflow: hidden;
p {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
.selection-panel_icon,
.selection-panel_checkbox {
margin: auto 0;
}
.selection-panel_icon {
svg {
path {
fill: #a3a9ae;
}
}
}
${(props) =>
props.folderSelection &&
css`
.selection-panel_icon {
::after {
position: absolute;
display: block;
background-color: #f3f4f4;
border-top-right-radius: 50%;
left: 18px;
top: 6px;
width: 27px;
height: 32px;
content: "";
opacity: 0.7;
}
}
.selection-panel_text p {
color: ${(props) => props.theme.text.disableColor};
}
`}
`;
const StyledAsideBody = styled.div`
height: 100%;
.selection-panel_aside-body {
height: calc(100% - 32px);
display: grid;
grid-template-rows: max-content auto max-content;
}
.selection-panel_files-list-body {
height: 100%;
margin-left: -16px;
margin-right: -6px;
}
.selection-panel_files {
height: 100%;
}
.selection-panel_aside-header {
div:first-child {
${(props) => props.header && " margin-bottom: 12px;"}
}
}
.selection-panel_aside-folder-title {
margin-top: 12px;
}
.selection-panel_folder-selection-title {
margin-bottom: 4px;
}
.selection-panel_aside-tree {
margin-top: 12px;
margin-left: -16px;
margin-right: -16px;
}
.selection-panel_aside-title {
padding-bottom: 16px;
}
.selection-panel_aside-footer {
border-top: 1px solid ${(props) => props.theme.row.borderBottom};
margin-left: -13px;
margin-right: -13px;
padding-left: 16px;
padding-right: 16px;
padding-top: 16px;
padding-bottom: 12px;
.selection-panel_aside-buttons {
${(props) => props.footer && "margin-top:16px"};
display: grid;
grid-template-columns: 1fr 1fr;
button:first-child {
margin-right: 8px;
}
}
}
${commonStyles}
`;
const StyledAsideHeader = styled.div`
display: flex;
align-items: center;
.selection-panel_aside-header-icon {
margin-right: 16px;
}
`;
const StyledTree = styled.div`
height: 100%;
.files-tree-menu {
margin-top: 0 !important;
margin-bottom: 22px;
}
.selection-panel_tree-folder {
height: 100%;
}
#folder-tree-scroll-bar {
.nav-thumb-horizontal {
height: 0px !important;
}
${(props) =>
props.displayType === "modal" &&
css`
.nav-thumb-vertical {
margin-left: 4px !important;
width: 4px !important;
}
`}
.scroll-body {
overflow-x: hidden !important;
}
}
.selection-panel_empty-folder {
margin-top: 12px;
margin-left: 12px;
}
`;
const StyledItemsLoader = styled.div`
display: flex;
.panel-loader {
margin-left: 16px;
margin-right: 8px;
}
`;
export {
StyledBody,
StyledRow,
StyledAsideBody,
StyledAsideHeader,
StyledTree,
StyledModalDialog,
StyledItemsLoader,
};

Some files were not shown because too many files have changed in this diff Show More