Merge pull request #596 from ONLYOFFICE/feature/info-panel

Feature/info-panel
This commit is contained in:
Alexey Safronov 2022-04-06 14:27:39 -04:00 committed by GitHub
commit 0a28bd45d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 1572 additions and 205 deletions

View File

@ -159,7 +159,7 @@
"enabled": "true"
},
"thumbnail": {
"thumbnaillHeight": 156,
"thumbnaillWidth": 216
"thumbnaillHeight": 260,
"thumbnaillWidth": 360
}
}

View File

@ -1,5 +1,5 @@
{
"kafka": {
"BootstrapServers": ""
}
}
{
"kafka": {
"BootstrapServers": "localhost:9092"
}
}

View File

@ -13,103 +13,105 @@ import SortButton from "./sub-components/SortButton";
import { StyledFilterInput, StyledSearchInput } from "./StyledFilterInput";
const FilterInput = ({
t,
sectionWidth,
getFilterData,
getSortData,
getViewSettingsData,
getSelectedFilterData,
onFilter,
onSearch,
onSort,
onChangeViewAs,
viewAs,
placeholder,
contextMenuHeader,
headerLabel,
viewSelectorVisible,
isRecentFolder,
isFavoritesFolder,
...props
}) => {
const [viewSettings, setViewSettings] = React.useState([]);
const [selectedFilterData, setSelectedFilterData] = React.useState([]);
const FilterInput = React.memo(
({
t,
sectionWidth,
getFilterData,
getSortData,
getViewSettingsData,
getSelectedFilterData,
onFilter,
onSearch,
onSort,
onChangeViewAs,
viewAs,
placeholder,
contextMenuHeader,
headerLabel,
viewSelectorVisible,
isRecentFolder,
isFavoritesFolder,
...props
}) => {
const [viewSettings, setViewSettings] = React.useState([]);
const [selectedFilterData, setSelectedFilterData] = React.useState([]);
const [inputValue, setInputValue] = React.useState("");
const [inputValue, setInputValue] = React.useState("");
const getSelectedFilterDataAction = React.useCallback(async () => {
const data = await getSelectedFilterData();
const getSelectedFilterDataAction = React.useCallback(async () => {
const data = await getSelectedFilterData();
setSelectedFilterData(data);
setInputValue(!!data.inputValue ? data.inputValue : "");
}, [getSelectedFilterData]);
setSelectedFilterData(data);
setInputValue(!!data.inputValue ? data.inputValue : "");
}, [getSelectedFilterData]);
React.useEffect(() => {
getSelectedFilterDataAction();
}, [getSelectedFilterData]);
React.useEffect(() => {
getSelectedFilterDataAction();
}, [getSelectedFilterData]);
React.useEffect(() => {
getViewSettingsData && setViewSettings(getViewSettingsData());
}, [getViewSettingsData]);
React.useEffect(() => {
getViewSettingsData && setViewSettings(getViewSettingsData());
}, [getViewSettingsData]);
const onClearSearch = () => {
onSearch && onSearch();
};
const onClearSearch = () => {
onSearch && onSearch();
};
return (
<StyledFilterInput {...props} sectionWidth={sectionWidth}>
<StyledSearchInput
placeholder={placeholder}
value={inputValue}
onChange={onSearch}
onClearSearch={onClearSearch}
/>
<FilterButton
t={t}
selectedFilterData={selectedFilterData}
contextMenuHeader={contextMenuHeader}
getFilterData={getFilterData}
onFilter={onFilter}
headerLabel={headerLabel}
/>
{viewSettings &&
!isMobile &&
viewSelectorVisible &&
!isMobileUtils() &&
!isTabletUtils() ? (
<ViewSelector
style={{ marginLeft: "8px" }}
onChangeView={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
return (
<StyledFilterInput {...props} sectionWidth={sectionWidth}>
<StyledSearchInput
placeholder={placeholder}
value={inputValue}
onChange={onSearch}
onClearSearch={onClearSearch}
/>
) : (
<>
{(isMobile || isTabletUtils() || isMobileUtils()) && (
<SortButton
t={t}
selectedFilterData={selectedFilterData}
getSortData={getSortData}
onChangeViewAs={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
onSort={onSort}
viewSelectorVisible={viewSelectorVisible}
isRecentFolder={isRecentFolder}
isFavoritesFolder={isFavoritesFolder}
/>
)}
</>
)}
</StyledFilterInput>
);
};
<FilterButton
t={t}
selectedFilterData={selectedFilterData}
contextMenuHeader={contextMenuHeader}
getFilterData={getFilterData}
onFilter={onFilter}
headerLabel={headerLabel}
/>
{viewSettings &&
!isMobile &&
viewSelectorVisible &&
!isMobileUtils() &&
!isTabletUtils() ? (
<ViewSelector
style={{ marginLeft: "8px" }}
onChangeView={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
/>
) : (
<>
{(isMobile || isTabletUtils() || isMobileUtils()) && (
<SortButton
t={t}
selectedFilterData={selectedFilterData}
getSortData={getSortData}
onChangeViewAs={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
onSort={onSort}
viewSelectorVisible={viewSelectorVisible}
isRecentFolder={isRecentFolder}
isFavoritesFolder={isFavoritesFolder}
/>
)}
</>
)}
</StyledFilterInput>
);
}
);
FilterInput.defaultProps = {
viewSelectorVisible: false,
};
export default React.memo(FilterInput);
export default FilterInput;

View File

@ -30,6 +30,10 @@ const Navigation = ({
isRecycleBinFolder,
isEmptyFilesList,
clearTrash,
showFolderInfo,
isCurrentFolderInfo,
toggleInfoPanel,
isInfoPanelVisible,
...rest
}) => {
const [isOpen, setIsOpen] = React.useState(false);
@ -133,6 +137,8 @@ const Navigation = ({
isRecycleBinFolder={isRecycleBinFolder}
isEmptyFilesList={isEmptyFilesList}
clearTrash={clearTrash}
toggleInfoPanel={toggleInfoPanel}
isInfoPanelVisible={isInfoPanelVisible}
/>
</StyledContainer>
</>

View File

@ -3,16 +3,11 @@ import { isMobile, isMobileOnly } from "react-device-detect";
import { tablet, desktop, mobile } from "@appserver/components/utils/device";
const StyledContainer = styled.div`
padding: ${(props) => (props.isDropBox ? "14px 0 3px" : "14px 0 0px")};
width: fit-content;
width: 100% !important;
display: grid;
grid-template-columns: ${(props) =>
props.isRootFolder ? "1fr auto" : "29px 1fr auto"};
align-items: center;
grid-template-columns: ${(props) =>
props.isRootFolder ? "auto 1fr" : "29px auto 1fr"};
.arrow-button {
width: 17px;
@ -31,8 +26,7 @@ const StyledContainer = styled.div`
`}
@media ${mobile} {
width: 100%;
padding: ${(props) => (props.isDropBox ? "12px 0 5px" : "12px 0 0")};
height: 53px;
}
${isMobileOnly &&

View File

@ -5,6 +5,7 @@ import ContextMenuButton from "@appserver/components/context-menu-button";
import IconButton from "@appserver/components/icon-button";
import { isMobile } from "react-device-detect";
import { tablet } from "@appserver/components/utils/device";
import { Base } from "@appserver/components/themes";
const StyledContainer = styled.div`
margin-left: 20px;
@ -30,7 +31,8 @@ const StyledContainer = styled.div`
}
.option-button {
margin-right: 8px;
margin-left: auto;
margin-right: 15px;
min-width: 17px;
}
@ -39,6 +41,35 @@ const StyledContainer = styled.div`
}
`;
const StyledInfoPanelToggleWrapper = styled.div`
display: flex;
align-items: center;
align-self: center;
justify-content: center;
margin-left: ${({ isRootFolder }) => (isRootFolder ? "auto" : "none")};
.info-panel-toggle-bg {
height: 32px;
width: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleBgActive
: props.theme.infoPanel.sectionHeaderToggleBg};
path {
fill: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleIconActive
: props.theme.infoPanel.sectionHeaderToggleIcon};
}
}
`;
StyledInfoPanelToggleWrapper.defaultProps = { theme: Base };
const ControlButtons = ({
personal,
isDropBox,
@ -49,6 +80,8 @@ const ControlButtons = ({
isRecycleBinFolder,
isEmptyFilesList,
clearTrash,
isInfoPanelVisible,
toggleInfoPanel,
}) => {
return (
<StyledContainer isDropBox={isDropBox}>
@ -96,6 +129,20 @@ const ControlButtons = ({
) : (
<></>
)}
<StyledInfoPanelToggleWrapper
isRootFolder={isRootFolder}
isInfoPanelVisible={isInfoPanelVisible}
>
<div className="info-panel-toggle-bg">
<IconButton
className="info-panel-toggle"
iconName="images/panel.svg"
size="16"
isFill={true}
onClick={toggleInfoPanel}
/>
</div>
</StyledInfoPanelToggleWrapper>
</StyledContainer>
);
};

View File

@ -19,6 +19,10 @@ import SubSectionBody from "./sub-components/section-body";
import SubSectionBodyContent from "./sub-components/section-body-content";
import SubSectionBar from "./sub-components/section-bar";
import SubSectionPaging from "./sub-components/section-paging";
//import SectionToggler from "./sub-components/section-toggler";
import InfoPanel from "./sub-components/info-panel";
import SubInfoPanelBody from "./sub-components/info-panel-body";
import SubInfoPanelHeader from "./sub-components/info-panel-header";
import ReactResizeDetector from "react-resize-detector";
import FloatingButton from "../FloatingButton";
@ -36,8 +40,13 @@ const StyledMainBar = styled.div`
box-sizing: border-box;
margin-left: -20px;
width: calc(100vw - 256px);
max-width: calc(100vw - 256px);
/* width: calc(100vw - 256px);
max-width: calc(100vw - 256px); */
width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 657px)" : "calc(100vw - 256px)"};
max-width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 657px)" : "calc(100vw - 256px)"};
#bar-banner {
margin-bottom: -3px;
@ -123,12 +132,24 @@ function SectionPaging() {
}
SectionPaging.displayName = "SectionPaging";
function InfoPanelBody() {
return null;
}
InfoPanelBody.displayName = "InfoPanelBody";
function InfoPanelHeader() {
return null;
}
InfoPanelHeader.displayName = "InfoPanelHeader";
class Section extends React.Component {
static SectionHeader = SectionHeader;
static SectionFilter = SectionFilter;
static SectionBody = SectionBody;
static SectionBar = SectionBar;
static SectionPaging = SectionPaging;
static InfoPanelBody = InfoPanelBody;
static InfoPanelHeader = InfoPanelHeader;
constructor(props) {
super(props);
@ -208,6 +229,7 @@ class Section extends React.Component {
setMaintenanceExist,
snackbarExist,
showText,
infoPanelIsVisible,
} = this.props;
let sectionHeaderContent = null;
@ -215,6 +237,9 @@ class Section extends React.Component {
let sectionFilterContent = null;
let sectionPagingContent = null;
let sectionBodyContent = null;
let infoPanelBodyContent = null;
let infoPanelHeaderContent = null;
React.Children.forEach(children, (child) => {
const childType =
child && child.type && (child.type.displayName || child.type.name);
@ -235,6 +260,12 @@ class Section extends React.Component {
case SectionBody.displayName:
sectionBodyContent = child;
break;
case InfoPanelBody.displayName:
infoPanelBodyContent = child;
break;
case InfoPanelHeader.displayName:
infoPanelHeaderContent = child;
break;
default:
break;
}
@ -277,6 +308,7 @@ class Section extends React.Component {
maintenanceExist={maintenanceExist}
isSectionBarAvailable={isSectionBarAvailable}
isSectionHeaderAvailable={isSectionHeaderAvailable}
infoPanelIsVisible={infoPanelIsVisible}
>
{!isMobile && (
<StyledMainBar
@ -285,6 +317,7 @@ class Section extends React.Component {
className={"main-bar"}
showText={showText}
isSectionHeaderAvailable={isSectionHeaderAvailable}
infoPanelIsVisible={infoPanelIsVisible}
>
<SubSectionBar
setMaintenanceExist={setMaintenanceExist}
@ -302,6 +335,7 @@ class Section extends React.Component {
snackbarExist={snackbarExist}
className="section-header_header"
isHeaderVisible={isHeaderVisible}
infoPanelIsVisible={infoPanelIsVisible}
viewAs={viewAs}
showText={showText}
>
@ -322,6 +356,7 @@ class Section extends React.Component {
</SubSectionFilter>
</>
)}
{isSectionBodyAvailable && (
<>
<SubSectionBody
@ -341,6 +376,7 @@ class Section extends React.Component {
isSectionHeaderAvailable={
isSectionHeaderAvailable
}
infoPanelIsVisible={infoPanelIsVisible}
>
<SubSectionBar
setMaintenanceExist={setMaintenanceExist}
@ -358,6 +394,7 @@ class Section extends React.Component {
isHeaderVisible={isHeaderVisible}
viewAs={viewAs}
showText={showText}
infoPanelIsVisible={infoPanelIsVisible}
>
{sectionHeaderContent
? sectionHeaderContent.props.children
@ -372,11 +409,13 @@ class Section extends React.Component {
: null}
</SubSectionFilter>
)}
<SubSectionBodyContent>
{sectionBodyContent
? sectionBodyContent.props.children
: null}
</SubSectionBodyContent>
{isSectionPagingAvailable && (
<SubSectionPaging>
{sectionPagingContent
@ -387,6 +426,7 @@ class Section extends React.Component {
</SubSectionBody>
</>
)}
{!(isMobile || isMobileUtils() || isTabletUtils()) ? (
showPrimaryProgressBar && showSecondaryProgressBar ? (
<>
@ -428,6 +468,12 @@ class Section extends React.Component {
<></>
)}
</SectionContainer>
<InfoPanel>
<SubInfoPanelHeader>
{infoPanelHeaderContent}
</SubInfoPanelHeader>
<SubInfoPanelBody>{infoPanelBodyContent}</SubInfoPanelBody>
</InfoPanel>
</Provider>
)}
</ReactResizeDetector>
@ -501,12 +547,14 @@ Section.defaultProps = {
withBodyAutoFocus: false,
};
Section.InfoPanelHeader = InfoPanelHeader;
Section.InfoPanelBody = InfoPanelBody;
Section.SectionHeader = SectionHeader;
Section.SectionFilter = SectionFilter;
Section.SectionBody = SectionBody;
Section.SectionPaging = SectionPaging;
export default inject(({ auth }) => {
export default inject(({ auth, infoPanelStore }) => {
const { isLoaded, settingsStore } = auth;
const {
isHeaderVisible,
@ -523,6 +571,9 @@ export default inject(({ auth }) => {
showText,
} = settingsStore;
let infoPanelIsVisible = false;
if (infoPanelStore) infoPanelIsVisible = infoPanelStore.isVisible;
return {
isLoaded,
isTabletView,
@ -536,5 +587,7 @@ export default inject(({ auth }) => {
isDesktop: isDesktopClient,
showText,
infoPanelIsVisible: infoPanelIsVisible,
};
})(observer(Section));

View File

@ -0,0 +1,16 @@
import Scrollbar from "@appserver/components/scrollbar";
import React from "react";
const SubInfoPanelBody = ({ children }) => {
const content = children?.props?.children;
return (
<Scrollbar scrollclass="section-scroll" stype="mediumBlack">
{content}
</Scrollbar>
);
};
SubInfoPanelBody.displayName = "SubInfoPanelBody";
export default SubInfoPanelBody;

View File

@ -0,0 +1,58 @@
import IconButton from "@appserver/components/icon-button";
import Text from "@appserver/components/text";
import { Base } from "@appserver/components/themes";
import { tablet } from "@appserver/components/utils/device";
import { inject, observer } from "mobx-react";
import PropTypes from "prop-types";
import React from "react";
import styled from "styled-components";
const StyledInfoPanelHeader = styled.div`
width: 100%;
max-width: 100%;
height: 54px;
min-height: 54px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: ${(props) => `1px solid ${props.theme.infoPanel.borderColor}`};
.header-text {
margin-left: 20px;
}
`;
const SubInfoPanelHeader = ({ children, onHeaderCrossClick }) => {
const content = children?.props?.children;
return (
<StyledInfoPanelHeader>
<Text className="header-text" fontSize="21px" fontWeight="700">
{content}
</Text>
</StyledInfoPanelHeader>
);
};
SubInfoPanelHeader.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
PropTypes.any,
]),
toggleIsVisible: PropTypes.func,
};
StyledInfoPanelHeader.defaultProps = { theme: Base };
SubInfoPanelHeader.defaultProps = { theme: Base };
SubInfoPanelHeader.displayName = "SubInfoPanelHeader";
export default inject(({ infoPanelStore }) => {
let onHeaderCrossClick = () => {};
if (infoPanelStore) {
onHeaderCrossClick = infoPanelStore.onHeaderCrossClick;
}
return { onHeaderCrossClick };
})(observer(SubInfoPanelHeader));

View File

@ -0,0 +1,140 @@
import IconButton from "@appserver/components/icon-button";
import { Base } from "@appserver/components/themes";
import { isTablet, mobile, tablet } from "@appserver/components/utils/device";
import { inject } from "mobx-react";
import PropTypes from "prop-types";
import React, { useEffect } from "react";
import styled from "styled-components";
const StyledInfoPanelWrapper = styled.div.attrs(({ id }) => ({
id: id,
}))`
height: auto;
width: auto;
background: rgba(6, 22, 38, 0.2);
backdrop-filter: blur(18px);
@media ${tablet} {
z-index: 309;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
`;
const StyledInfoPanel = styled.div`
height: 100%;
width: 400px;
background-color: ${(props) => props.theme.infoPanel.backgroundColor};
border-left: ${(props) => `1px solid ${props.theme.infoPanel.borderColor}`};
display: flex;
flex-direction: column;
@media ${tablet} {
position: absolute;
border: none;
right: 0;
width: 480px;
max-width: calc(100vw - 69px);
}
@media ${mobile} {
bottom: 0;
height: 80%;
width: 100vw;
max-width: 100vw;
}
`;
const StyledCloseButtonWrapper = styled.div`
position: absolute;
display: none;
background-color: ${(props) => props.theme.infoPanel.closeButtonBg};
padding: ${(props) => props.theme.infoPanel.closeButtonWrapperPadding};
border-radius: 50%;
.info-panel-button {
svg {
width: ${(props) => props.theme.infoPanel.closeButtonSize};
height: ${(props) => props.theme.infoPanel.closeButtonSize};
}
path {
fill: ${(props) => props.theme.infoPanel.closeButtonIcon};
}
}
@media ${tablet} {
display: block;
top: 0;
left: 0;
margin-top: 18px;
margin-left: -34px;
}
@media ${mobile} {
right: 0;
left: auto;
margin-top: -34px;
margin-right: 10px;
}
`;
const InfoPanel = ({ children, isVisible, setIsVisible }) => {
if (!isVisible) return null;
const closeInfoPanel = () => setIsVisible(false);
useEffect(() => {
const onMouseDown = (e) => {
if (e.target.id === "InfoPanelWrapper") closeInfoPanel();
};
if (isTablet()) document.addEventListener("mousedown", onMouseDown);
return () => document.removeEventListener("mousedown", onMouseDown);
}, []);
return (
<StyledInfoPanelWrapper className="info-panel" id="InfoPanelWrapper">
<StyledInfoPanel>
<StyledCloseButtonWrapper>
<IconButton
onClick={closeInfoPanel}
iconName="/static/images/cross.react.svg"
className="info-panel-button"
/>
</StyledCloseButtonWrapper>
{children}
</StyledInfoPanel>
</StyledInfoPanelWrapper>
);
};
InfoPanel.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
PropTypes.any,
]),
isVisible: PropTypes.bool,
};
StyledInfoPanelWrapper.defaultProps = { theme: Base };
StyledCloseButtonWrapper.defaultProps = { theme: Base };
StyledInfoPanel.defaultProps = { theme: Base };
InfoPanel.defaultProps = { theme: Base };
export default inject(({ infoPanelStore }) => {
let isVisible = false;
let setIsVisible = () => {};
if (infoPanelStore) {
isVisible = infoPanelStore.isVisible;
setIsVisible = infoPanelStore.setIsVisible;
}
return {
isVisible,
setIsVisible,
};
})(InfoPanel);

View File

@ -38,8 +38,10 @@ const StyledSectionContainer = styled.section`
display: flex;
flex-direction: column;
width: 100%;
max-width: 100vw;
width: ${(props) =>
props.infoPanelIsVisible ? "calc(100% - 677px)" : "100%"};
max-width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 677px)" : "100vw"};
@media ${tablet} {
width: 100%;

View File

@ -16,8 +16,16 @@ const StyledSectionHeader = styled.div`
margin-right: 20px;
${NoUserSelect}
width: calc(100vw - 296px);
max-width: calc(100vw - 296px);
display: grid;
align-items: center;
/* width: calc(100vw - 296px);
max-width: calc(100vw - 296px); */
width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 696px)" : "calc(100vw - 296px)"};
max-width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 696px)" : "calc(100vw - 296px)"};
@media ${tablet} {
width: ${(props) =>

View File

@ -11,7 +11,6 @@ export const EmployeeActivationStatus = Object.freeze({
Pending: 2,
AutoGenerated: 4,
});
/**
* Enum for employee status.
* @readonly
@ -20,7 +19,6 @@ export const EmployeeStatus = Object.freeze({
Active: 1,
Disabled: 2,
});
/**
* Enum for employee type.
* @readonly
@ -29,7 +27,6 @@ export const EmployeeType = Object.freeze({
User: 1,
Guest: 2,
});
/**
* Enum for filter type.
* @readonly
@ -48,7 +45,6 @@ export const FilterType = Object.freeze({
ByExtension: 11,
MediaOnly: 12,
});
/**
* Enum for file type.
* @readonly
@ -63,7 +59,6 @@ export const FileType = Object.freeze({
Presentation: 6,
Document: 7,
});
/**
* Enum for file action.
* @readonly
@ -72,7 +67,6 @@ export const FileAction = Object.freeze({
Create: 0,
Rename: 1,
});
/**
* Enum for root folders type.
* @readonly
@ -90,7 +84,6 @@ export const FolderType = Object.freeze({
Templates: 12,
Privacy: 13,
});
export const ShareAccessRights = Object.freeze({
None: 0,
FullAccess: 1,
@ -102,7 +95,6 @@ export const ShareAccessRights = Object.freeze({
FormFilling: 7,
CustomFilter: 8,
});
export const ConflictResolveType = Object.freeze({
Skip: 0,
Overwrite: 1,
@ -127,7 +119,6 @@ export const providersData = Object.freeze({
icon: "/static/images/share.linkedin.react.svg",
},
});
export const LoaderStyle = {
title: "",
width: "100%",
@ -183,7 +174,6 @@ export const TenantTrustedDomainsType = Object.freeze({
Custom: 1,
All: 2,
});
export const PasswordLimitSpecialCharacters = "!@#$%^&*";
/**

View File

@ -7,11 +7,9 @@ html,
body {
height: 100%;
}
#root {
min-height: 100%;
position: relative;
.pageLoader {
position: fixed;
left: calc(50% - 20px);
@ -40,17 +38,14 @@ body {
body {
margin: 0;
}
body.loading * {
cursor: wait !important;
}
body.drag-cursor * {
cursor: url('data:image/svg+xml;utf8,<svg width="20" height="23" viewBox="0 0 20 23" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect width="20" height="23" fill="url(%23pattern0)"/><defs><pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"><use xlink:href="%23image0" transform="scale(0.05 0.0434783)"/></pattern><image id="image0" width="20" height="23" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAAzhJREFUOI2VlT1PI1cUhp/58DgZbMAOi2AcQZMFCSHREK3EKsoWNDRYCIkum7+Qf0BP4Y4K8QfSEtEgpUoatLuRiyDxUcSI2JKF8JCZAeyZe08Kj5GxTNgc6ejcO3Puc99zr86M4XkeqRmA05u4rvvGsqxfAEZGRt7Ytl3XWsv09JSampqODMNARNjb26PfjFKpBICIvLJt+1OhUMg+PDzI0tKSu7+/nxMR1tfXI9/3FcDd3d3p2dnZW0AAnUbpAc0+uOV53tfHx8evKpXK5P39fS6Xy5HP54njeOTg4GD06OhoNAzDb7LZ7DiQBay0MoYBtWVZHcdxGB8fJ45jAIIgwDRNSqUSExMTOI4zOjk5+cfY2NjrYVDTskyM7rSptRYA3/cJw/BxpyAIaLV8zs/PmZubsw4PD0srKys/z8/PfwtkUmEGgKm1RmtZAFZFuoqz2SzFYvERWCwWMc3uro7jsLi4yO7u7usgCH4F3CdKZ2dnv9vY2EjW1tZkZeWtxHEsnU5HfN8XERGttbRaLVFKSZIkcnNzIyIiFxcX4nleAswCXwFfACYLCwsiIhJFkbx7973c3t7K51i1WhXP80JgHigBecA2RaQDEIYhImAYTy7tWUvzjPQMM72Sba11W0ScKIqo1WokSYKI/BcLABHp5Rl9ji2iieMY13Upl8vYto3W+kVg9zKf5AmAnSQqD1AoFNjZ2QFAKfUiMJfLoZRyB5+blmX9Vq1WlVKK/+OXl5dxkiR/DQINIDMzM9M5OTl5UVXParUam5ubf56env4oIg9ACPjAnWlZlrRard8/fPj42eoymQyNRiMnIgpQQJJGbSqldBAEq+/f//D31dWVvASL45hms6kdx6mnkBhop1AxK5WKBpJ6vb66tbWVDIFI6iilCMOQcrlcv76+/qkPGKdjsbe3twG01rrRaNQz7XZ72Dl3B4ZBs9nE9303bYgeMCH9NtpRFGGapga+DMPoZnl52emDPGkbwzBQSiEi1ykg6VOnBxf0fgEuMJpG+5mLlhR0B/yTxpiBBdJXwn06t54Bkqp7SPMfW2ZQgQY6Kaw9WPKQ3F7Jj80/uKDX5OaQd89Bpc/5F/DGMQusl3V8AAAAAElFTkSuQmCC"/></defs></svg>')
6 6,
auto !important;
}
body.desktop {
user-select: none;
-moz-user-select: none;
@ -59,7 +54,6 @@ body.desktop {
-o-user-select: none;
mozuserselect: none;
}
#snackbar {
display: flex;
justify-content: center;

View File

@ -0,0 +1,3 @@
<svg width="17" height="15" viewBox="0 0 17 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 2.5H13.5C14.0523 2.5 14.5 2.94772 14.5 3.5V11.5C14.5 12.0523 14.0523 12.5 13.5 12.5H11.5V2.5ZM9 2.5H3.5C2.94772 2.5 2.5 2.94772 2.5 3.5V11.5C2.5 12.0523 2.94772 12.5 3.5 12.5H9V2.5ZM17 1.5C17 0.671573 16.3284 0 15.5 0H14.5H2.5H1.5C0.671573 0 0 0.671573 0 1.5V2.88462V12.1154V13.5C0 14.3284 0.671573 15 1.5 15H2.5H14.5H15.5C16.3284 15 17 14.3284 17 13.5V12.1154V2.88462V1.5Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 550 B

View File

@ -45,11 +45,19 @@ const StyledButton = styled(Button)`
padding-right: 8px;
}
.button-content {
@media ${tablet} {
flex-direction: column;
gap: 0px;
}
}
@media ${tablet} {
display: flex;
justify-content: center;
flex-direction: column;
height: 60px;
padding: 22px 12px 0 12px;
padding: 0px 12px;
.btnIcon {
padding: 0;
margin: 0 auto;
@ -57,7 +65,7 @@ const StyledButton = styled(Button)`
}
@media ${mobile} {
padding: 18px 16px 0 16px;
padding: 0 16px;
height: 50px;
font-size: 0;
line-height: 0;

View File

@ -7,7 +7,7 @@ import { isMobile } from "react-device-detect";
const StyledTableContainer = styled.div`
-moz-user-select: none;
width: calc(100% - 5px);
width: 100%;
max-width: 100%;
margin-top: -19px;
@ -79,7 +79,7 @@ const StyledTableGroupMenu = styled.div`
align-items: center;
width: 100%;
z-index: 199;
height: 52px;
height: 53px;
box-shadow: ${(props) => props.theme.tableContainer.groupMenu.boxShadow};
border-radius: 0px 0px 6px 6px;
margin: 0;
@ -125,6 +125,42 @@ const StyledTableGroupMenu = styled.div`
StyledTableGroupMenu.defaultProps = { theme: Base };
const StyledInfoPanelToggleWrapper = styled.div`
display: flex;
align-items: center;
align-self: center;
justify-content: center;
margin: 0 20px 0 auto;
height: 100%;
width: auto;
padding-left: 20px;
@media ${tablet} {
margin: 0 16px 0 auto;
}
.info-panel-toggle-bg {
height: 32px;
width: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleBgActive
: props.theme.infoPanel.sectionHeaderToggleBg};
path {
fill: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleIconActive
: props.theme.infoPanel.sectionHeaderToggleIcon};
}
}
`;
StyledInfoPanelToggleWrapper.defaultProps = { theme: Base };
const StyledTableHeader = styled.div`
position: fixed;
background: ${(props) => props.theme.tableContainer.header.background};
@ -302,6 +338,9 @@ const StyledScrollbar = styled(Scrollbar)`
.scroll-body {
display: flex;
}
.nav-thumb-vertical {
display: none !important;
}
.nav-thumb-horizontal {
${isMobile && "display: none !important"};
}
@ -318,6 +357,7 @@ export {
StyledTableCell,
StyledTableSettings,
StyledTableGroupMenu,
StyledInfoPanelToggleWrapper,
StyledEmptyTableContainer,
StyledScrollbar,
};

View File

@ -1,10 +1,15 @@
import React from "react";
import PropTypes from "prop-types";
import Checkbox from "../checkbox";
import { StyledTableGroupMenu, StyledScrollbar } from "./StyledTableContainer";
import {
StyledTableGroupMenu,
StyledScrollbar,
StyledInfoPanelToggleWrapper,
} from "./StyledTableContainer";
import ComboBox from "../combobox";
import GroupMenuItem from "./GroupMenuItem";
import { useTranslation } from "react-i18next";
import IconButton from "../icon-button";
const TableGroupMenu = (props) => {
const {
@ -14,15 +19,14 @@ const TableGroupMenu = (props) => {
onChange,
checkboxOptions,
checkboxMargin,
isInfoPanelVisible,
toggleInfoPanel,
...rest
} = props;
const onCheckboxChange = (e) => {
onChange && onChange(e.target && e.target.checked);
};
const { t } = useTranslation("Common");
return (
<>
<StyledTableGroupMenu
@ -54,11 +58,21 @@ const TableGroupMenu = (props) => {
<GroupMenuItem key={index} item={item} />
))}
</StyledScrollbar>
<StyledInfoPanelToggleWrapper isInfoPanelVisible={isInfoPanelVisible}>
<div className="info-panel-toggle-bg">
<IconButton
className="info-panel-toggle"
iconName="images/panel.svg"
size="16"
isFill={true}
onClick={toggleInfoPanel}
/>
</div>
</StyledInfoPanelToggleWrapper>
</StyledTableGroupMenu>
</>
);
};
TableGroupMenu.propTypes = {
isChecked: PropTypes.bool,
isIndeterminate: PropTypes.bool,
@ -68,5 +82,4 @@ TableGroupMenu.propTypes = {
onChange: PropTypes.func,
checkboxMargin: PropTypes.string,
};
export default TableGroupMenu;

View File

@ -117,7 +117,7 @@ class TableHeader extends React.Component {
const column2Width = this.getSubstring(widths[colIndex]);
const defaultColumn = document.getElementById("column_" + colIndex);
if (defaultColumn.dataset.defaultSize) return;
if (!defaultColumn || defaultColumn.dataset.defaultSize) return;
if (column2Width + offset >= defaultMinColumnSize) {
widths[+columnIndex] = newWidth + "px";
@ -236,8 +236,8 @@ class TableHeader extends React.Component {
const storageSize =
!resetColumnsSize && localStorage.getItem(columnStorageName);
const defaultSize = this.props.columns.find((col) => col.defaultSize)
?.defaultSize;
const defaultSize =
this.props.columns.find((col) => col.defaultSize)?.defaultSize || 0;
//TODO: Fixed columns size if something went wrong
if (storageSize) {
@ -262,9 +262,12 @@ class TableHeader extends React.Component {
const containerWidth = +container.clientWidth;
const oldWidth = tableContainer
.map((column) => this.getSubstring(column))
.reduce((x, y) => x + y);
const oldWidth =
tableContainer
.map((column) => this.getSubstring(column))
.reduce((x, y) => x + y) -
defaultSize -
settingsSize;
let str = "";
@ -278,7 +281,7 @@ class TableHeader extends React.Component {
const enable =
index == tableContainer.length - 1 ||
(column ? column.dataset.enable === "true" : item !== "0px");
const defaultSize = column && column.dataset.defaultSize;
const defaultColumnSize = column && column.dataset.defaultSize;
const isActiveNow = item === "0px" && enable;
if (isActiveNow && column) activeColumnIndex = index;
@ -301,18 +304,14 @@ class TableHeader extends React.Component {
} else if (item !== `${settingsSize}px`) {
const percent = (this.getSubstring(item) / oldWidth) * 100;
if (index == 1) {
const newItemWidth = (containerWidth * percent) / 100 + "px";
gridTemplateColumns.push(newItemWidth);
} else {
const newItemWidth = defaultSize
? `${defaultSize}px`
: percent === 0
? `${minColumnSize}px`
: (containerWidth * percent) / 100 + "px";
const newItemWidth = defaultColumnSize
? `${defaultColumnSize}px`
: percent === 0
? `${minColumnSize}px`
: ((containerWidth - defaultSize - settingsSize) * percent) / 100 +
"px";
gridTemplateColumns.push(newItemWidth);
}
gridTemplateColumns.push(newItemWidth);
} else {
gridTemplateColumns.push(item);
}
@ -351,7 +350,8 @@ class TableHeader extends React.Component {
const enableColumns = this.props.columns
.filter((x) => x.enable)
.filter((x) => !x.defaultSize);
.filter((x) => !x.defaultSize)
.filter((x) => !x.default);
const container = containerRef.current
? containerRef.current

View File

@ -1952,6 +1952,29 @@ const Base = {
},
},
infoPanel: {
sectionHeaderToggleIcon: gray,
sectionHeaderToggleIconActive: "#3B72A7",
sectionHeaderToggleBg: "transparent",
sectionHeaderToggleBgActive: grayLight,
backgroundColor: white,
borderColor: grayLightMid,
thumbnailBorderColor: grayLightMid,
textColor: black,
closeButtonWrapperPadding: "0px",
closeButtonIcon: white,
closeButtonSize: "17px",
closeButtonBg: "transparent",
accessGroupBg: grayLightMid,
accessGroupText: black,
showAccessUsersTextColor: gray,
showAccessPanelTextColor: "#3b72a7",
},
filesArticleBody: {
background: lightGrayishStrongBlue,
panelBackground: lightGrayishStrongBlue,

View File

@ -1954,6 +1954,29 @@ const Dark = {
},
},
infoPanel: {
sectionHeaderToggleIcon: "#858585",
sectionHeaderToggleIconActive: "#c4c4c4",
sectionHeaderToggleBg: "transparent",
sectionHeaderToggleBgActive: "#292929",
backgroundColor: black,
borderColor: "#292929",
thumbnailBorderColor: grayLightMid,
textColor: white,
closeButtonWrapperPadding: "6px",
closeButtonIcon: black,
closeButtonSize: "12px",
closeButtonBg: "#a2a2a2",
accessGroupBg: "#242424",
accessGroupText: white,
showAccessUsersTextColor: gray,
showAccessPanelTextColor: "#E06A1B",
},
filesArticleBody: {
background: black,
panelBackground: "#474747",

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM7 6V4H9V6H7ZM7 12V7H9V12H7Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@ -0,0 +1,3 @@
<svg width="17" height="15" viewBox="0 0 17 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 2.5H13.5C14.0523 2.5 14.5 2.94772 14.5 3.5V11.5C14.5 12.0523 14.0523 12.5 13.5 12.5H11.5V2.5ZM9 2.5H3.5C2.94772 2.5 2.5 2.94772 2.5 3.5V11.5C2.5 12.0523 2.94772 12.5 3.5 12.5H9V2.5ZM17 1.5C17 0.671573 16.3284 0 15.5 0H14.5H2.5H1.5C0.671573 0 0 0.671573 0 1.5V2.88462V12.1154V13.5C0 14.3284 0.671573 15 1.5 15H2.5H14.5H15.5C16.3284 15 17 14.3284 17 13.5V12.1154V2.88462V1.5Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 550 B

View File

@ -0,0 +1,15 @@
{
"Info": "Info",
"ViewDetails": "View Details",
"ItemsSelected": "Items selected",
"SystemProperties": "System properties",
"WhoHasAccess": "Who has access",
"Members": "members",
"OpenSharingSettings": "Open sharing settings",
"Location": "Location",
"FileExtension": "File extension",
"LastModifiedBy": "Last modified by",
"Versions": "Versions",
"Comments": "Comments"
}

View File

@ -0,0 +1,15 @@
{
"Info": "Информация",
"ViewDetails": "Просмотреть подробную информацию",
"ItemsSelected": "Выбрано элементов",
"SystemProperties": "Системные свойства",
"WhoHasAccess": "У кого есть доступ",
"Members": "участников",
"OpenSharingSettings": "Открыть настройки общего доступа",
"Location": "Местоположение",
"FileExtension": "Расширение файла",
"LastModifiedBy": "Автор последнего корректива",
"Versions": "Версии",
"Comments": "Комментарии"
}

View File

@ -82,6 +82,7 @@ export default function withFileActions(WrappedFileItem) {
if (mouseButton || e.currentTarget.tagName !== "DIV" || label) {
return e;
}
e.preventDefault();
setTooltipPosition(e.pageX, e.pageY);
setStartDrag(true);
@ -92,13 +93,14 @@ export default function withFileActions(WrappedFileItem) {
const { viewAs } = this.props;
if (
e.target.closest(".checkbox") ||
e.target.tagName === "INPUT" ||
e.target.tagName === "SPAN" ||
e.target.tagName === "A" ||
e.target.closest(".expandButton") ||
e.target.closest(".badges") ||
e.target.closest(".checkbox") ||
e.button !== 0 ||
e.target.closest(".expandButton") ||
e.target.querySelector(".expandButton") ||
e.target.closest(".badges") ||
e.target.closest(".not-selectable")
)
return;
@ -106,12 +108,10 @@ export default function withFileActions(WrappedFileItem) {
if (viewAs === "tile") {
if (e.target.closest(".edit-button") || e.target.tagName === "IMG")
return;
if (e.detail === 1) this.fileContextClick();
} else {
this.fileContextClick();
}
} else this.fileContextClick();
};
onFilesClick = (e) => {
const { item, openFileAction } = this.props;
if (
@ -247,6 +247,7 @@ export default function withFileActions(WrappedFileItem) {
activeFiles,
activeFolders,
} = filesStore;
const { startUpload } = uploadDataStore;
const { type, extension, id } = fileActionStore;

View File

@ -0,0 +1,32 @@
import Text from "@appserver/components/text";
import React from "react";
import { withTranslation } from "react-i18next";
import { ReactSVG } from "react-svg";
import { StyledTitle } from "./styles/styles.js";
const SeveralItems = (props) => {
const { t, selectedItems, getIcon, getFolderInfo } = props;
const itemsIcon = getIcon(24, ".file");
return (
<>
<StyledTitle>
<ReactSVG className="icon" src={itemsIcon} />
<Text className="text" fontWeight={600} fontSize="16px">
{`${t("ItemsSelected")}: ${selectedItems.length}`}
</Text>
</StyledTitle>
<div className="no-thumbnail-img-wrapper">
<img
size="96px"
className="no-thumbnail-img"
src="images/empty_screen.png"
/>
</div>
</>
);
};
export default withTranslation(["InfoPanel"])(SeveralItems);

View File

@ -0,0 +1,415 @@
import { FileType } from "@appserver/common/constants";
import { LANGUAGE } from "@appserver/common/constants";
import Link from "@appserver/components/link";
import Text from "@appserver/components/text";
import Tooltip from "@appserver/components/tooltip";
import React, { useEffect, useState } from "react";
import { ReactSVG } from "react-svg";
import {
StyledAccess,
StyledAccessItem,
StyledOpenSharingPanel,
StyledProperties,
StyledSubtitle,
StyledThumbnail,
StyledTitle,
} from "./styles/styles.js";
const moment = require("moment");
const SingleItem = (props) => {
const {
t,
selectedItem,
onSelectItem,
setSharingPanelVisible,
getFolderInfo,
getIcon,
getFolderIcon,
getShareUsers,
dontShowSize,
dontShowLocation,
dontShowAccess,
} = props;
let updateSubscription = true;
const [item, setItem] = useState({
id: "",
isFolder: false,
title: "",
iconUrl: "",
thumbnailUrl: "",
properties: [],
access: {
owner: {
img: "",
link: "",
},
others: [],
},
});
const updateItemsInfo = async (selectedItem) => {
const getItemIcon = (item, size) => {
return item.isFolder
? getFolderIcon(item.providerKey, size)
: getIcon(size, item.fileExst || ".file");
};
const getSingleItemProperties = (item) => {
const styledLink = (text, href) => (
<Link className="property-content" href={href} isHovered={true}>
{text}
</Link>
);
const styledText = (text) => (
<Text className="property-content">{text}</Text>
);
const parseAndFormatDate = (date) => {
return moment(date)
.locale(localStorage.getItem(LANGUAGE))
.format("DD.MM.YY hh:mm A");
};
const getItemType = (fileType) => {
switch (fileType) {
case FileType.Unknown:
return t("Common:Unknown");
case FileType.Archive:
return t("Common:Archive");
case FileType.Video:
return t("Common:Video");
case FileType.Audio:
return t("Common:Audio");
case FileType.Image:
return t("Common:Image");
case FileType.Spreadsheet:
return t("Home:Spreadsheet");
case FileType.Presentation:
return t("Home:Presentation");
case FileType.Document:
return t("Home:Document");
default:
return t("Home:Folder");
}
};
const itemSize = item.isFolder
? `${t("Translations:Folders")}: ${item.foldersCount} | ${t(
"Translations:Files"
)}: ${item.filesCount}`
: item.contentLength;
const itemType = getItemType(item.fileType);
let result = [
{
id: "Owner",
title: t("Common:Owner"),
content: styledLink(
item.createdBy?.displayName,
item.createdBy?.profileUrl
),
},
// {
// id: "Location",
// title: t("InfoPanel:Location"),
// content: styledText("..."),
// },
{
id: "Type",
title: t("Common:Type"),
content: styledText(itemType),
},
{
id: "Size",
title: t("Common:Size"),
content: styledText(itemSize),
},
{
id: "ByLastModifiedDate",
title: t("Home:ByLastModifiedDate"),
content: styledText(parseAndFormatDate(item.updated)),
},
{
id: "LastModifiedBy",
title: t("LastModifiedBy"),
content: styledLink(
item.updatedBy?.displayName,
item.updatedBy?.profileUrl
),
},
{
id: "ByCreationDate",
title: t("Home:ByCreationDate"),
content: styledText(parseAndFormatDate(item.created)),
},
];
if (item.isFolder) return result;
result.splice(3, 0, {
id: "FileExtension",
title: t("FileExtension"),
content: styledText(
item.fileExst ? item.fileExst.split(".")[1].toUpperCase() : "-"
),
});
result.push(
{
id: "Versions",
title: t("Versions"),
content: styledText(item.version),
},
{
id: "Comments",
title: t("Comments"),
content: styledText(item.comment),
}
);
return result;
};
const displayedItem = {
id: selectedItem.id,
isFolder: selectedItem.isFolder,
title: selectedItem.title,
iconUrl: getItemIcon(selectedItem, 32),
thumbnailUrl: selectedItem.thumbnailUrl || getItemIcon(selectedItem, 96),
properties: getSingleItemProperties(selectedItem),
access: {
owner: {
img: selectedItem.createdBy?.avatarSmall,
link: selectedItem.createdBy?.profileUrl,
},
others: [],
},
};
setItem(displayedItem);
await loadAsyncData(displayedItem, selectedItem);
};
const loadAsyncData = async (displayedItem, selectedItem) => {
if (!updateSubscription) return;
const updateLoadedItemProperties = async (displayedItem, selectedItem) => {
const parentFolderId = selectedItem.isFolder
? selectedItem.parentId
: selectedItem.folderId;
const noLocationProperties = [...displayedItem.properties].filter(
(dip) => dip.id !== "Location"
);
let result;
await getFolderInfo(parentFolderId)
.catch(() => {
result = noLocationProperties;
})
.then((data) => {
if (!data) {
result = noLocationProperties;
return;
}
result = [...displayedItem.properties].map((dip) =>
dip.id === "Location"
? {
id: "Location",
title: t("Location"),
content: (
<Link
className="property-content"
href={`/products/files/filter?folder=${parentFolderId}`}
isHovered={true}
>
{data.title}
</Link>
),
}
: dip
);
});
return result;
};
const updateLoadedItemAccess = async (selectedItem) => {
const accesses = await getShareUsers(
[selectedItem.isFolder ? selectedItem.parentId : selectedItem.folderId],
[selectedItem.id]
);
const result = {
owner: {},
others: [],
};
accesses.forEach((access) => {
let key = access.sharedTo.id,
img = access.sharedTo.avatarSmall,
link = access.sharedTo.profileUrl,
name = access.sharedTo.displayName || access.sharedTo.name,
{ manager } = access.sharedTo;
if (access.isOwner) result.owner = { key, img, link, name };
else {
if (access.sharedTo.email)
result.others.push({ key, type: "user", img, link, name });
else if (access.sharedTo.manager)
result.others.push({ key, type: "group", name, manager });
}
});
result.others = result.others.sort((a) => (a.type === "group" ? -1 : 1));
return result;
};
// const properties = await updateLoadedItemProperties(
// displayedItem,
// selectedItem
// );
if (dontShowAccess) {
setItem({
...displayedItem,
//properties: properties,
});
return;
}
const access = await updateLoadedItemAccess(selectedItem);
setItem({
...displayedItem,
// properties: properties,
access: access,
});
};
const openSharingPanel = () => {
const { id, isFolder } = item;
onSelectItem({ id, isFolder });
setSharingPanelVisible(true);
};
useEffect(() => {
if (selectedItem.id !== item.id && updateSubscription)
updateItemsInfo(selectedItem);
return () => (updateSubscription = false);
}, [selectedItem]);
return (
<>
<StyledTitle>
<ReactSVG className="icon" src={item.iconUrl} />
<Text className="text">{item.title}</Text>
</StyledTitle>
{selectedItem.thumbnailUrl ? (
<StyledThumbnail>
<img src={item.thumbnailUrl} alt="" />
</StyledThumbnail>
) : (
<div className="no-thumbnail-img-wrapper">
<ReactSVG className="no-thumbnail-img" src={item.thumbnailUrl} />
</div>
)}
<StyledSubtitle>
<Text fontWeight="600" fontSize="14px">
{t("SystemProperties")}
</Text>
</StyledSubtitle>
<StyledProperties>
{item.properties.map((p) => {
if (dontShowSize && p.id === "Size") return;
if (dontShowLocation && p.id === "Location") return;
return (
<div key={p.title} className="property">
<Text className="property-title">{p.title}</Text>
{p.content}
</div>
);
})}
</StyledProperties>
{!dontShowAccess && item.access && (
<>
<StyledSubtitle>
<Text fontWeight="600" fontSize="14px">
{t("WhoHasAccess")}
</Text>
</StyledSubtitle>
<StyledAccess>
<Tooltip
id="access-item-tooltip"
getContent={(dataTip) =>
dataTip ? <Text fontSize="13px">{dataTip}</Text> : null
}
/>
<StyledAccessItem>
<div
data-for="access-item-tooltip"
className="access-item-tooltip"
data-tip={item.access.owner.name}
>
<div className="item-user">
<a href={item.access.owner.link}>
<img src={item.access.owner.img} />
</a>
</div>
</div>
</StyledAccessItem>
{item.access.others.length > 0 && <div className="divider"></div>}
{item.access.others.map((item, i) => {
if (i < 3)
return (
<div key={item.key}>
<StyledAccessItem>
<div
data-for="access-item-tooltip"
data-tip={item.name}
className="access-item-tooltip"
>
{item.type === "user" ? (
<div className="item-user">
<a href={item.link}>
<img src={item.img} />
</a>
</div>
) : (
<div className="item-group">
<span>{item.name.substr(0, 2).toUpperCase()}</span>
</div>
)}
</div>
</StyledAccessItem>
</div>
);
})}
{item.access.others.length > 3 && (
<div className="show-more-users" onClick={openSharingPanel}>
{`+ ${item.access.others.length - 3} ${t("Members")}`}
</div>
)}
</StyledAccess>
<StyledOpenSharingPanel onClick={openSharingPanel}>
{t("OpenSharingSettings")}
</StyledOpenSharingPanel>
</>
)}
</>
);
};
export default SingleItem;

View File

@ -0,0 +1,109 @@
import { inject, observer } from "mobx-react";
import React, { useEffect, useState } from "react";
import { withTranslation } from "react-i18next";
import { withRouter } from "react-router";
import SeveralItems from "./SeveralItems";
import SingleItem from "./SingleItem";
import { StyledInfoRoomBody } from "./styles/styles.js";
import { Base } from "@appserver/components/themes";
const InfoPanelBodyContent = ({
t,
selectedFolder,
selectedItems,
getFolderInfo,
getIcon,
getFolderIcon,
getShareUsers,
onSelectItem,
setSharingPanelVisible,
isRecycleBinFolder,
isRecentFolder,
isFavoritesFolder,
}) => {
const singleItem = (item) => {
const dontShowLocation = item.isFolder && item.parentId === 0;
const dontShowSize = item.isFolder && (isFavoritesFolder || isRecentFolder);
const dontShowAccess =
isRecycleBinFolder ||
(item.isFolder && item.parentId === 0) ||
item.rootFolderId === 7 ||
(item.isFolder && item.pathParts && item.pathParts[0] === 7);
return (
<SingleItem
t={t}
selectedItem={item}
onSelectItem={onSelectItem}
setSharingPanelVisible={setSharingPanelVisible}
getFolderInfo={getFolderInfo}
getIcon={getIcon}
getFolderIcon={getFolderIcon}
getShareUsers={getShareUsers}
dontShowLocation={dontShowLocation}
dontShowSize={dontShowSize}
dontShowAccess={dontShowAccess}
/>
);
};
return (
<StyledInfoRoomBody>
<>
{selectedItems.length === 0 ? (
singleItem({
...selectedFolder,
isFolder: true,
})
) : selectedItems.length === 1 ? (
singleItem(selectedItems[0])
) : (
<SeveralItems selectedItems={selectedItems} getIcon={getIcon} />
)}
</>
</StyledInfoRoomBody>
);
};
InfoPanelBodyContent.defaultProps = { theme: Base };
export default inject(
({
filesStore,
settingsStore,
filesActionsStore,
dialogsStore,
treeFoldersStore,
selectedFolderStore,
}) => {
const { selection, getFolderInfo, getShareUsers } = filesStore;
const { getIcon, getFolderIcon } = settingsStore;
const { onSelectItem } = filesActionsStore;
const { setSharingPanelVisible } = dialogsStore;
const {
isRecycleBinFolder,
isRecentFolder,
isFavoritesFolder,
} = treeFoldersStore;
return {
selectedFolder: { ...selectedFolderStore },
selectedItems: [...selection],
getFolderInfo,
getShareUsers,
getIcon,
getFolderIcon,
onSelectItem,
setSharingPanelVisible,
isRecycleBinFolder,
isRecentFolder,
isFavoritesFolder,
};
}
)(
withRouter(
withTranslation(["InfoPanel", "Home", "Common", "Translations"])(
observer(InfoPanelBodyContent)
)
)
);

View File

@ -0,0 +1,234 @@
import styled from "styled-components";
import { Base } from "@appserver/components/themes";
const StyledInfoRoomBody = styled.div`
padding: 0px 0px 0 16px;
height: auto;
background-color: ${(props) => props.theme.infoPanel.backgroundColor};
color: ${(props) => props.theme.infoPanel.textColor};
.no-item {
text-align: center;
}
.no-thumbnail-img-wrapper {
height: auto;
width: 100%;
display: flex;
justify-content: center;
.no-thumbnail-img {
height: 96px;
width: 96px;
}
}
.current-folder-loader-wrapper {
width: 100%;
display: flex;
justify-content: center;
height: 96px;
margin-top: 116.56px;
}
`;
const StyledTitle = styled.div`
display: flex;
flex-wrap: no-wrap;
flex-direction: row;
align-items: center;
width: 100%;
height: 44px;
padding: 23px 0;
.icon {
display: flex;
align-items: center;
svg {
height: 32px;
width: 32px;
}
}
.text {
font-weight: 600;
font-size: 16px;
line-height: 22px;
max-height: 44px;
margin: 0 8px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
`;
const StyledThumbnail = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: auto;
img {
border: ${(props) => `solid 1px ${props.theme.infoPanel.borderColor}`};
border-radius: 6px;
//width: 100%;
width: auto;
max-width: 100%;
height: auto;
}
`;
const StyledSubtitle = styled.div`
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
padding: 24px 0;
`;
const StyledProperties = styled.div`
display: flex;
flex-direction: column;
width: 100%;
gap: 8px;
.property {
width: 100%;
display: grid;
grid-template-columns: 150px 1fr;
grid-column-gap: 24px;
.property-title {
font-size: 13px;
}
.property-content {
display: flex;
align-items: center;
font-weight: 600;
font-size: 13px;
}
}
`;
const StyledAccess = styled.div`
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 8px;
align-items: center;
.divider {
background: ${(props) => props.theme.infoPanel.borderColor};
margin: 2px 4px;
width: 1px;
height: 28px;
}
.show-more-users {
position: static;
width: 101px;
height: 16px;
left: 120px;
top: 8px;
padding-left: 1px;
font-family: "Open Sans";
font-style: normal;
font-weight: normal;
font-size: 12px;
line-height: 16px;
text-align: left;
color: ${(props) => props.theme.infoPanel.showAccessUsersTextColor};
flex: none;
order: 3;
flex-grow: 0;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
`;
const StyledAccessItem = styled.div`
width: 32px;
height: 32px;
border-radius: 50%;
.access-item-tooltip {
cursor: pointer;
width: 100%;
height: 100%;
.item-group {
border-radius: 50%;
background-color: ${(props) => props.theme.infoPanel.accessGroupBg};
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
span {
font-family: "Open Sans";
font-weight: 700;
font-size: 12px;
color: ${(props) => props.theme.infoPanel.accessGroupText};
line-height: 16px;
}
}
.item-user {
img {
border-radius: 50%;
width: 100%;
height: 100%;
}
}
}
`;
const StyledOpenSharingPanel = styled.div`
position: static;
width: auto;
height: 15px;
left: 0px;
top: 2px;
font-family: "Open Sans";
font-style: normal;
font-weight: 600;
font-size: 13px;
line-height: 15px;
color: ${(props) => props.theme.infoPanel.showAccessPanelTextColor};
display: flex;
margin: 16px 0px;
cursor: pointer;
text-decoration: underline;
text-decoration-style: dashed;
`;
StyledInfoRoomBody.defaultProps = { theme: Base };
StyledThumbnail.defaultProps = { theme: Base };
StyledAccess.defaultProps = { theme: Base };
StyledAccessItem.defaultProps = { theme: Base };
StyledOpenSharingPanel.defaultProps = { theme: Base };
export {
StyledInfoRoomBody,
StyledTitle,
StyledThumbnail,
StyledSubtitle,
StyledProperties,
StyledAccess,
StyledAccessItem,
StyledOpenSharingPanel,
};

View File

@ -0,0 +1,8 @@
import React from "react";
import { withTranslation } from "react-i18next";
const InfoPanelHeaderContent = ({ t }) => {
return <>{t("Info")}</>;
};
export default withTranslation(["InfoPanel"])(InfoPanelHeaderContent);

View File

@ -0,0 +1,2 @@
export { default as InfoPanelHeaderContent } from "./Header";
export { default as InfoPanelBodyContent } from "./Body";

View File

@ -97,8 +97,8 @@ const StyledSimpleFilesRow = styled(Row)`
}
.row_content {
${(props) => props.sectionWidth > 500 && `max-width: fit-content;`}
min-width: auto;
${(props) =>
props.sectionWidth > 500 && `max-width: fit-content;`}//min-width: auto;;
}
.badges {

View File

@ -146,15 +146,17 @@ const StyledFileTileTop = styled.div`
align-items: baseline;
height: 156px;
position: relative;
border-radius: 6px 6px 0 0;
.thumbnail-image {
pointer-events: none;
position: absolute;
height: 100%;
width: 100%;
object-fit: ${(props) => (props.isMedia ? "cover" : "none")};
object-fit: cover;
object-position: top;
z-index: 0;
border-radius: 6px 6px 0 0;
}
.temporary-icon > .injected-svg {

View File

@ -71,6 +71,7 @@ const SectionBodyContent = (props) => {
(e.target.closest(".scroll-body") &&
!e.target.closest(".files-item") &&
!e.target.closest(".not-selectable") &&
!e.target.closest(".info-panel") &&
!e.target.closest(".table-container_group-menu")) ||
e.target.closest(".files-main-button") ||
e.target.closest(".add-button") ||

View File

@ -18,7 +18,7 @@ import TableGroupMenu from "@appserver/components/table-container/TableGroupMenu
import Navigation from "@appserver/common/components/Navigation";
const StyledContainer = styled.div`
padding: 0 0 15px;
/* padding: 0 0 15px;
@media ${tablet} {
padding: 0 0 17px;
@ -36,7 +36,7 @@ const StyledContainer = styled.div`
${isMobileOnly &&
css`
padding: 0 0 13px;
`}
`} */
.table-container_group-menu {
${(props) =>
@ -317,6 +317,7 @@ class SectionHeaderContent extends React.Component {
const {
t,
tReady,
isInfoPanelVisible,
isRootFolder,
title,
canCreate,
@ -344,6 +345,8 @@ class SectionHeaderContent extends React.Component {
width={context.sectionWidth}
isRootFolder={isRootFolder}
canCreate={canCreate}
isRecycleBinFolder={isRecycleBinFolder}
title={title}
isTitle={title}
isDesktop={isDesktop}
isTabletView={isTabletView}
@ -357,6 +360,8 @@ class SectionHeaderContent extends React.Component {
isChecked={isHeaderChecked}
isIndeterminate={isHeaderIndeterminate}
headerMenu={headerMenu}
isInfoPanelVisible={this.props.isInfoPanelVisible}
toggleInfoPanel={this.props.toggleInfoPanel}
/>
) : (
<div className="header-container">
@ -383,6 +388,8 @@ class SectionHeaderContent extends React.Component {
isEmptyFilesList={isEmptyFilesList}
clearTrash={this.onEmptyTrashAction}
onBackToParentFolder={this.onBackToParentFolder}
toggleInfoPanel={this.props.toggleInfoPanel}
isInfoPanelVisible={this.props.isInfoPanelVisible}
/>
)}
</div>
@ -403,6 +410,7 @@ export default inject(
treeFoldersStore,
filesActionsStore,
settingsStore,
infoPanelStore,
}) => {
const {
setSelected,
@ -441,9 +449,10 @@ export default inject(
backToParentFolder,
} = filesActionsStore;
const { toggleIsVisible, isVisible } = infoPanelStore;
return {
showText: auth.settingsStore.showText,
isDesktop: auth.settingsStore.isDesktopClient,
isRootFolder: selectedFolderStore.parentId === 0,
title: selectedFolderStore.title,
@ -451,6 +460,8 @@ export default inject(
pathParts: selectedFolderStore.pathParts,
navigationPath: selectedFolderStore.navigationPath,
canCreate,
toggleInfoPanel: toggleIsVisible,
isInfoPanelVisible: isVisible,
isHeaderVisible,
isHeaderIndeterminate,
isHeaderChecked,

View File

@ -22,6 +22,7 @@ import {
SectionPagingContent,
Bar,
} from "./Section";
import { InfoPanelBodyContent, InfoPanelHeaderContent } from "./InfoPanel";
import { ArticleMainButtonContent } from "../../components/Article";
@ -287,6 +288,7 @@ class PureHome extends React.Component {
setMaintenanceExist,
snackbarExist,
} = this.props;
return (
<>
<MediaViewer />
@ -345,6 +347,14 @@ class PureHome extends React.Component {
</Consumer>
</Section.SectionBody>
<Section.InfoPanelHeader>
<InfoPanelHeaderContent />
</Section.InfoPanelHeader>
<Section.InfoPanelBody>
<InfoPanelBodyContent />
</Section.InfoPanelBody>
<Section.SectionPaging>
<SectionPagingContent tReady={tReady} />
</Section.SectionPaging>

View File

@ -22,6 +22,7 @@ class ContextOptionsStore {
uploadDataStore;
versionHistoryStore;
settingsStore;
infoPanelStore;
constructor(
authStore,
@ -32,7 +33,8 @@ class ContextOptionsStore {
treeFoldersStore,
uploadDataStore,
versionHistoryStore,
settingsStore
settingsStore,
infoPanelStore
) {
makeAutoObservable(this);
this.authStore = authStore;
@ -44,6 +46,7 @@ class ContextOptionsStore {
this.uploadDataStore = uploadDataStore;
this.versionHistoryStore = versionHistoryStore;
this.settingsStore = settingsStore;
this.infoPanelStore = infoPanelStore;
}
onOpenFolder = (item) => {
@ -335,6 +338,11 @@ class ContextOptionsStore {
return options;
};
onShowInfoPanel = () => {
const { setIsVisible } = this.infoPanelStore;
setIsVisible(true);
};
getFilesContextOptions = (item, t) => {
const { contextOptions } = item;
const isRootThirdPartyFolder =
@ -510,6 +518,13 @@ class ContextOptionsStore {
disabled: true,
},
...versionActions,
{
key: "show-info",
label: t("InfoPanel:ViewDetails"),
icon: "/static/images/info.outline.react.svg",
onClick: this.onShowInfoPanel,
disabled: false,
},
{
key: "block-unblock-version",
label: t("UnblockVersion"),

View File

@ -1,26 +1,26 @@
import { makeAutoObservable } from "mobx";
import {
removeFiles,
checkFileConflicts,
deleteFile,
deleteFolder,
finalizeVersion,
lockFile,
downloadFiles,
markAsRead,
checkFileConflicts,
removeShareFiles,
getSubfolders,
emptyTrash,
finalizeVersion,
getSubfolders,
lockFile,
markAsRead,
removeFiles,
removeShareFiles,
} from "@appserver/common/api/files";
import {
ConflictResolveType,
FileAction,
FileStatus,
} from "@appserver/common/constants";
import { makeAutoObservable } from "mobx";
import toastr from "studio/toastr";
import { TIMEOUT } from "../helpers/constants";
import { loopTreeFolders, checkProtocol } from "../helpers/files-helpers";
import toastr from "studio/toastr";
import { combineUrl } from "@appserver/common/utils";
import { AppServerConfig } from "@appserver/common/constants";
import config from "../../package.json";
@ -34,6 +34,7 @@ class FilesActionStore {
settingsStore;
dialogsStore;
mediaViewerDataStore;
infoPanelStore;
constructor(
authStore,
@ -43,7 +44,8 @@ class FilesActionStore {
selectedFolderStore,
settingsStore,
dialogsStore,
mediaViewerDataStore
mediaViewerDataStore,
infoPanelStore
) {
makeAutoObservable(this);
this.authStore = authStore;
@ -53,6 +55,7 @@ class FilesActionStore {
this.selectedFolderStore = selectedFolderStore;
this.settingsStore = settingsStore;
this.dialogsStore = dialogsStore;
this.infoPanelStore = infoPanelStore;
this.mediaViewerDataStore = mediaViewerDataStore;
}
@ -786,6 +789,7 @@ class FilesActionStore {
switch (option) {
case "share":
return isAccessedSelected && !personal; //isFavoritesFolder ||isRecentFolder
case "showInfo":
case "copy":
case "download":
return hasSelection;
@ -831,6 +835,8 @@ class FilesActionStore {
setDeleteDialogVisible,
} = this.dialogsStore;
const { toggleIsVisible } = this.infoPanelStore;
switch (option) {
case "share":
if (!this.isAvailableOption("share")) return null;
@ -915,6 +921,7 @@ class FilesActionStore {
const moveTo = this.getOption("moveTo", t);
const copy = this.getOption("copy", t);
const deleteOption = this.getOption("delete", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
@ -922,7 +929,8 @@ class FilesActionStore {
.set("downloadAs", downloadAs)
.set("moveTo", moveTo)
.set("copy", copy)
.set("delete", deleteOption);
.set("delete", deleteOption)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -932,12 +940,15 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const copy = this.getOption("copy", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
.set("download", download)
.set("downloadAs", downloadAs)
.set("copy", copy);
.set("copy", copy)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -948,6 +959,7 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const copy = this.getOption("copy", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
@ -960,7 +972,9 @@ class FilesActionStore {
setUnsubscribe(true);
setDeleteDialogVisible(true);
},
});
})
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -968,12 +982,15 @@ class FilesActionStore {
const moveTo = this.getOption("moveTo", t);
const deleteOption = this.getOption("delete", t);
const download = this.getOption("download", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("download", download)
.set("moveTo", moveTo)
.set("delete", deleteOption);
.set("delete", deleteOption)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -984,6 +1001,7 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const copy = this.getOption("copy", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
@ -999,7 +1017,9 @@ class FilesActionStore {
.then(() => toastr.success(t("RemovedFromFavorites")))
.catch((err) => toastr.error(err));
},
});
})
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -1012,6 +1032,7 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const deleteOption = this.getOption("delete", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("download", download)
@ -1021,9 +1042,12 @@ class FilesActionStore {
onClick: () => setMoveToPanelVisible(true),
iconUrl: "/static/images/move.react.svg",
})
.set("delete", deleteOption);
.set("delete", deleteOption)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
getHeaderMenu = (t) => {
const {
isFavoritesFolder,

View File

@ -1,23 +1,24 @@
import { makeAutoObservable, runInAction } from "mobx";
import api from "@appserver/common/api";
import {
FolderType,
FilterType,
FileType,
FileAction,
AppServerConfig,
FileAction,
FileType,
FilterType,
FolderType,
FileStatus,
} from "@appserver/common/constants";
import history from "@appserver/common/history";
import { loopTreeFolders } from "../helpers/files-helpers";
import config from "../../package.json";
import { combineUrl } from "@appserver/common/utils";
import { updateTempContent } from "@appserver/common/utils";
import { thumbnailStatuses } from "../helpers/constants";
import { isMobile } from "react-device-detect";
import { openDocEditor as openEditor } from "../helpers/utils";
import toastr from "studio/toastr";
import config from "../../package.json";
import { thumbnailStatuses } from "../helpers/constants";
import { loopTreeFolders } from "../helpers/files-helpers";
import { openDocEditor as openEditor } from "../helpers/utils";
const { FilesFilter } = api;
const storageViewAs = localStorage.getItem("viewAs");
@ -717,6 +718,7 @@ class FilesStore {
"version", //category
"finalize-version",
"show-version-history",
"show-info",
"block-unblock-version", //need split
"separator1",
"open-location",
@ -1004,6 +1006,7 @@ class FilesStore {
"separator0",
"sharing-settings",
"owner-change",
"show-info",
"link-for-portal-users",
"separator1",
"open-location",

View File

@ -0,0 +1,27 @@
import { makeAutoObservable } from "mobx";
class InfoPanelStore {
isVisible = false;
constructor() {
makeAutoObservable(this);
}
onHeaderCrossClick = () => {
this.isVisible = false;
};
toggleIsVisible = () => {
this.isVisible = !this.isVisible;
};
setVisible = () => {
this.isVisible = true;
};
setIsVisible = (bool) => {
this.isVisible = bool;
};
}
export default InfoPanelStore;

View File

@ -1,11 +1,11 @@
import { makeAutoObservable } from "mobx";
import api from "@appserver/common/api";
import axios from "axios";
import {
setFavoritesSetting,
setRecentSetting,
} from "@appserver/common/api/files";
import { FolderType } from "@appserver/common/constants";
import axios from "axios";
import { makeAutoObservable } from "mobx";
import { presentInArray } from "../helpers/files-helpers";
class SettingsStore {
@ -62,6 +62,10 @@ class SettingsStore {
this.treeFoldersStore = treeFoldersStore;
}
get infoPanelIsVisible() {
return this.infoPanelIsVisible;
}
setIsLoaded = (isLoaded) => {
this.settingsIsLoaded = isLoaded;
};

View File

@ -16,13 +16,12 @@ import selectedFilesStore from "./SelectedFilesStore";
import ContextOptionsStore from "./ContextOptionsStore";
import HotkeyStore from "./HotkeyStore";
import store from "studio/store";
import InfoPanelStore from "./InfoPanelStore";
const selectedFolderStore = new SelectedFolderStore(store.auth.settingsStore);
const treeFoldersStore = new TreeFoldersStore(selectedFolderStore);
const settingsStore = new SettingsStore(thirdPartyStore, treeFoldersStore);
const filesStore = new FilesStore(
store.auth,
store.auth.settingsStore,
@ -37,10 +36,8 @@ const mediaViewerDataStore = new MediaViewerDataStore(
filesStore,
settingsStore
);
const secondaryProgressDataStore = new SecondaryProgressDataStore();
const primaryProgressDataStore = new PrimaryProgressDataStore();
const dialogsStore = new DialogsStore(
store.auth,
treeFoldersStore,
@ -57,6 +54,8 @@ const uploadDataStore = new UploadDataStore(
settingsStore
);
const infoPanelStore = new InfoPanelStore();
const filesActionsStore = new FilesActionsStore(
store.auth,
uploadDataStore,
@ -65,11 +64,11 @@ const filesActionsStore = new FilesActionsStore(
selectedFolderStore,
settingsStore,
dialogsStore,
mediaViewerDataStore
mediaViewerDataStore,
infoPanelStore
);
const versionHistoryStore = new VersionHistoryStore(filesStore);
const contextOptionsStore = new ContextOptionsStore(
store.auth,
dialogsStore,
@ -79,7 +78,8 @@ const contextOptionsStore = new ContextOptionsStore(
treeFoldersStore,
uploadDataStore,
versionHistoryStore,
settingsStore
settingsStore,
infoPanelStore
);
const hotkeyStore = new HotkeyStore(
@ -105,6 +105,7 @@ const stores = {
selectedFilesStore,
contextOptionsStore,
hotkeyStore,
infoPanelStore,
};
export default stores;

View File

@ -0,0 +1,12 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_21120_56765)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 2C5.18629 2 2.5 4.68629 2.5 8C2.5 11.3137 5.18629 14 8.5 14C11.8137 14 14.5 11.3137 14.5 8C14.5 4.68629 11.8137 2 8.5 2ZM0.5 8C0.5 3.58172 4.08172 0 8.5 0C12.9183 0 16.5 3.58172 16.5 8C16.5 12.4183 12.9183 16 8.5 16C4.08172 16 0.5 12.4183 0.5 8Z" fill="#333333"/>
<circle cx="8.5" cy="5" r="1" fill="#333333"/>
<rect x="7.5" y="7" width="2" height="5" rx="1" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_21120_56765">
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 710 B