Web: Files: added OFORMs gallery
This commit is contained in:
parent
83ba8fa153
commit
8c782a7903
@ -456,3 +456,11 @@ export function toggleTipsSubscription() {
|
||||
};
|
||||
return request(options);
|
||||
}
|
||||
|
||||
export function getOforms(url) {
|
||||
const options = {
|
||||
method: "get",
|
||||
url,
|
||||
};
|
||||
return request(options);
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class SettingsStore {
|
||||
enabledJoin = false;
|
||||
urlLicense = "https://gnu.org/licenses/gpl-3.0.html";
|
||||
urlSupport = "https://helpdesk.onlyoffice.com/";
|
||||
urlOforms = "https://oforms.onlyoffice.com/data/def.json";
|
||||
logoUrl = combineUrl(proxyURL, "/static/images/nav.logo.opened.react.svg");
|
||||
customNames = {
|
||||
id: "Common",
|
||||
@ -422,6 +423,10 @@ class SettingsStore {
|
||||
this.theme = themes[theme];
|
||||
localStorage.setItem("theme", theme);
|
||||
};
|
||||
|
||||
getOforms = () => {
|
||||
return api.settings.getOforms(this.urlOforms);
|
||||
};
|
||||
}
|
||||
|
||||
export default SettingsStore;
|
||||
|
@ -0,0 +1,15 @@
|
||||
<svg width="150" height="150" viewBox="0 0 150 150" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_964_170806)">
|
||||
<path d="M71.4219 20.8414C72.779 18.6071 76.0175 18.595 77.3912 20.8191L144.306 129.161C145.746 131.493 144.069 134.5 141.328 134.5H21.1846H8.6095C5.88051 134.5 4.20137 131.516 5.61805 129.183L71.4219 20.8414Z" fill="url(#paint0_linear_964_170806)" stroke="#333333"/>
|
||||
<path d="M84.6797 70.3176C84.6797 74.9563 83.6221 80.7454 81.5068 87.6848C81.0244 89.2434 80.5605 90.802 80.1152 92.3606C79.6699 93.9192 79.1875 95.4963 78.668 97.092C77.5547 100.506 76.5342 102.157 75.6064 102.046C74.6045 101.898 73.4912 100.172 72.2666 96.8694C72.0439 96.3128 71.6729 95.2737 71.1533 93.7522C70.6709 92.2307 70.0215 90.2083 69.2051 87.6848C67.0898 80.968 66.0322 75.3274 66.0322 70.763C66.0322 62.8958 69.1494 58.8694 75.3838 58.6838C81.5811 58.5725 84.6797 62.4505 84.6797 70.3176ZM82.4531 111.954C82.4531 113.81 81.8408 115.443 80.6162 116.853C79.2803 118.374 77.5361 119.135 75.3838 119.135C73.1201 119.135 71.3389 118.43 70.04 117.02C68.8154 115.647 68.2031 113.995 68.2031 112.066C68.2031 110.099 68.8154 108.41 70.04 107C71.3018 105.59 73.0273 104.885 75.2168 104.885C77.5176 104.885 79.2988 105.59 80.5605 107C81.8223 108.448 82.4531 110.099 82.4531 111.954Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_964_170806" x1="-0.598706" y1="31.0226" x2="80.71" y2="121.735" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FFC671"/>
|
||||
<stop offset="1" stop-color="#FF6F3D"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_964_170806">
|
||||
<rect width="150" height="150" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,4 @@
|
||||
{
|
||||
"EmptyScreenHeader": "Failed to load form templates",
|
||||
"EmptyScreenDescription": "Please check your Internet connection and refresh the page or try later"
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
"EmptyScreenHeader": "Не удалось загрузить шаблоны форм",
|
||||
"EmptyScreenDescription": "Проверьте подключение к Интернету и обновите страницу или повторите попытку позже"
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { Provider as FilesProvider } from "mobx-react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { Switch } from "react-router-dom";
|
||||
import { Switch, withRouter } from "react-router-dom";
|
||||
import config from "../package.json";
|
||||
import PrivateRoute from "@appserver/common/components/PrivateRoute";
|
||||
import AppLoader from "@appserver/common/components/AppLoader";
|
||||
@ -28,6 +28,7 @@ import {
|
||||
ArticleHeaderContent,
|
||||
ArticleMainButtonContent,
|
||||
} from "./components/Article";
|
||||
import FormGallery from "./pages/FormGallery";
|
||||
|
||||
const { proxyURL } = AppServerConfig;
|
||||
const homepage = config.homepage;
|
||||
@ -40,6 +41,7 @@ const HISTORY_URL = combineUrl(PROXY_HOMEPAGE_URL, "/:fileId/history");
|
||||
const PRIVATE_ROOMS_URL = combineUrl(PROXY_HOMEPAGE_URL, "/private");
|
||||
const FILTER_URL = combineUrl(PROXY_HOMEPAGE_URL, "/filter");
|
||||
const MEDIA_VIEW_URL = combineUrl(PROXY_HOMEPAGE_URL, "/#preview");
|
||||
const FORM_GALLERY_URL = combineUrl(PROXY_HOMEPAGE_URL, "/form-gallery");
|
||||
|
||||
if (!window.AppServer) {
|
||||
window.AppServer = {};
|
||||
@ -56,8 +58,12 @@ window.AppServer.files = {
|
||||
|
||||
const Error404 = React.lazy(() => import("studio/Error404"));
|
||||
|
||||
const FilesArticle = React.memo(() => {
|
||||
return (
|
||||
const FilesArticle = React.memo(({ history }) => {
|
||||
const isFormGallery = history.location.pathname
|
||||
.split("/")
|
||||
.includes("form-gallery");
|
||||
|
||||
return !isFormGallery ? (
|
||||
<Article>
|
||||
<Article.Header>
|
||||
<ArticleHeaderContent />
|
||||
@ -69,6 +75,8 @@ const FilesArticle = React.memo(() => {
|
||||
<ArticleBodyContent />
|
||||
</Article.Body>
|
||||
</Article>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
});
|
||||
|
||||
@ -81,6 +89,7 @@ const FilesSection = React.memo(() => {
|
||||
<PrivateRoute exact path={HOME_URL} component={Home} />
|
||||
<PrivateRoute path={FILTER_URL} component={Home} />
|
||||
<PrivateRoute path={MEDIA_VIEW_URL} component={Home} />
|
||||
<PrivateRoute path={FORM_GALLERY_URL} component={FormGallery} />
|
||||
<PrivateRoute component={Error404Route} />
|
||||
</Switch>
|
||||
);
|
||||
@ -158,7 +167,7 @@ class FilesContent extends React.Component {
|
||||
return (
|
||||
<>
|
||||
<Panels />
|
||||
<FilesArticle />
|
||||
<FilesArticle history={this.props.history} />
|
||||
<FilesSection />
|
||||
</>
|
||||
);
|
||||
@ -182,7 +191,7 @@ const Files = inject(({ auth, filesStore }) => {
|
||||
auth.setProductVersion(config.version);
|
||||
},
|
||||
};
|
||||
})(withTranslation("Common")(observer(FilesContent)));
|
||||
})(withTranslation("Common")(observer(withRouter(FilesContent))));
|
||||
|
||||
export default () => (
|
||||
<FilesProvider {...stores}>
|
||||
|
@ -10,10 +10,13 @@ import {
|
||||
isTablet as isTabletUtils,
|
||||
} from "@appserver/components/utils/device";
|
||||
import Loaders from "@appserver/common/components/Loaders";
|
||||
import { FileAction } from "@appserver/common/constants";
|
||||
import { AppServerConfig, FileAction } from "@appserver/common/constants";
|
||||
import { encryptionUploadDialog } from "../../../helpers/desktop";
|
||||
import { withRouter } from "react-router";
|
||||
|
||||
import MobileView from "./MobileView";
|
||||
import { combineUrl } from "@appserver/common/utils";
|
||||
import config from "../../../../package.json";
|
||||
|
||||
const ArticleMainButtonContent = (props) => {
|
||||
const {
|
||||
@ -33,6 +36,8 @@ const ArticleMainButtonContent = (props) => {
|
||||
isRecentFolder,
|
||||
isCommonFolder,
|
||||
isRecycleBinFolder,
|
||||
history,
|
||||
hasOFORMFilesGallery,
|
||||
} = props;
|
||||
const inputFilesElement = React.useRef(null);
|
||||
const inputFolderElement = React.useRef(null);
|
||||
@ -93,6 +98,12 @@ const ArticleMainButtonContent = (props) => {
|
||||
|
||||
const onInputClick = React.useCallback((e) => (e.target.value = null), []);
|
||||
|
||||
const onShowGallery = () => {
|
||||
history.push(
|
||||
combineUrl(AppServerConfig.proxyURL, config.homepage, `/form-gallery`)
|
||||
);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const folderUpload = !isMobile
|
||||
? [
|
||||
@ -130,6 +141,13 @@ const ArticleMainButtonContent = (props) => {
|
||||
disabled: isPrivacy,
|
||||
key: "form-file",
|
||||
},
|
||||
hasOFORMFilesGallery && {
|
||||
className: "main-button_drop-down_sub",
|
||||
label: t("Common:OFORMsGallery"),
|
||||
onClick: onShowGallery,
|
||||
disabled: isPrivacy, //TODO: OFORM
|
||||
key: "form-gallery",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
@ -150,6 +168,14 @@ const ArticleMainButtonContent = (props) => {
|
||||
disabled: isPrivacy,
|
||||
key: "form-file",
|
||||
},
|
||||
hasOFORMFilesGallery && {
|
||||
className: "main-button_drop-down_sub",
|
||||
icon: "images/form.react.svg",
|
||||
label: t("Common:OFORMsGallery"),
|
||||
onClick: onShowGallery,
|
||||
disabled: isPrivacy, //TODO: OFORM
|
||||
key: "form-gallery",
|
||||
},
|
||||
];
|
||||
|
||||
const actions = [
|
||||
@ -281,6 +307,7 @@ export default inject(
|
||||
isLoading,
|
||||
fileActionStore,
|
||||
canCreate,
|
||||
hasOFORMFilesGallery,
|
||||
} = filesStore;
|
||||
const {
|
||||
isPrivacyFolder,
|
||||
@ -315,6 +342,11 @@ export default inject(
|
||||
isLoading,
|
||||
isLoaded,
|
||||
firstLoad,
|
||||
hasOFORMFilesGallery,
|
||||
};
|
||||
}
|
||||
)(withTranslation(["Article", "Common"])(observer(ArticleMainButtonContent)));
|
||||
)(
|
||||
withTranslation(["Article", "Common"])(
|
||||
observer(withRouter(ArticleMainButtonContent))
|
||||
)
|
||||
);
|
||||
|
30
products/ASC.Files/Client/src/pages/FormGallery/Body.js
Normal file
30
products/ASC.Files/Client/src/pages/FormGallery/Body.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import EmptyScreenContainer from "@appserver/components/empty-screen-container";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import TileContainer from "./TilesView/sub-components/TileContainer";
|
||||
import FileTile from "./TilesView/FileTile";
|
||||
|
||||
const SectionBodyContent = ({ oformFiles, hasOFORMFilesGallery, t }) => {
|
||||
//console.log("oformFiles", oformFiles);
|
||||
|
||||
return !hasOFORMFilesGallery ? (
|
||||
<EmptyScreenContainer
|
||||
imageSrc="images/empty_screen_form-gallery.react.svg"
|
||||
imageAlt="Empty Screen Filter image"
|
||||
headerText={t("EmptyScreenHeader")}
|
||||
descriptionText={t("EmptyScreenDescription")}
|
||||
/>
|
||||
) : (
|
||||
<TileContainer useReactWindow={false} className="tile-container">
|
||||
{oformFiles.map((item, index) => (
|
||||
<FileTile key={`${item.id}_${index}`} item={item} />
|
||||
))}
|
||||
</TileContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore }) => ({
|
||||
oformFiles: filesStore.oformFiles,
|
||||
hasOFORMFilesGallery: filesStore.hasOFORMFilesGallery,
|
||||
}))(withTranslation("FormGallery")(observer(SectionBodyContent)));
|
27
products/ASC.Files/Client/src/pages/FormGallery/Header.js
Normal file
27
products/ASC.Files/Client/src/pages/FormGallery/Header.js
Normal file
@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import IconButton from "@appserver/components/icon-button";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { StyledHeadline, StyledContainer } from "./StyledGallery";
|
||||
|
||||
const SectionHeaderContent = ({ t, history }) => {
|
||||
const onBackToFiles = () => history.goBack();
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<IconButton
|
||||
iconName="/static/images/arrow.path.react.svg"
|
||||
size="17"
|
||||
isFill
|
||||
onClick={onBackToFiles}
|
||||
className="arrow-button"
|
||||
/>
|
||||
|
||||
<StyledHeadline type="content" truncate>
|
||||
{t("Common:OFORMsGallery")}
|
||||
</StyledHeadline>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default withTranslation("Common")(withRouter(SectionHeaderContent));
|
@ -0,0 +1,59 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { isMobile, isMobileOnly } from "react-device-detect";
|
||||
import { tablet, mobile } from "@appserver/components/utils/device";
|
||||
import Headline from "@appserver/common/components/Headline";
|
||||
|
||||
const StyledHeadline = styled(Headline)`
|
||||
width: calc(100% + 1px);
|
||||
font-weight: 700;
|
||||
font-size: ${isMobile ? "21px !important" : "18px"};
|
||||
line-height: ${isMobile ? "28px !important" : "24px"};
|
||||
@media ${tablet} {
|
||||
font-size: 21px;
|
||||
line-height: 28px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
padding: ${(props) => (props.isDropBox ? "14px 0 3px" : "14px 0 0px")};
|
||||
|
||||
width: fit-content;
|
||||
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: ${(props) =>
|
||||
props.isRootFolder ? "1fr auto" : "29px 1fr auto"};
|
||||
|
||||
align-items: center;
|
||||
|
||||
.arrow-button {
|
||||
width: 17px;
|
||||
min-width: 17px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
width: 100%;
|
||||
padding: ${(props) => (props.isDropBox ? "16px 0 5px" : "16px 0 0px")};
|
||||
}
|
||||
|
||||
${isMobile &&
|
||||
css`
|
||||
width: 100% !important;
|
||||
padding: ${(props) =>
|
||||
props.isDropBox ? "16px 0 5px" : " 16px 0 0px"} !important;
|
||||
`}
|
||||
|
||||
@media ${mobile} {
|
||||
width: 100%;
|
||||
padding: ${(props) => (props.isDropBox ? "12px 0 5px" : "12px 0 0")};
|
||||
}
|
||||
|
||||
${isMobileOnly &&
|
||||
css`
|
||||
width: 100% !important;
|
||||
padding: ${(props) =>
|
||||
props.isDropBox ? "12px 0 5px" : "12px 0 0"} !important;
|
||||
`}
|
||||
`;
|
||||
|
||||
export { StyledHeadline, StyledContainer };
|
@ -0,0 +1,56 @@
|
||||
import React from "react";
|
||||
import { observer, inject } from "mobx-react";
|
||||
import Tile from "./sub-components/Tile";
|
||||
import { SimpleFilesTileContent } from "./StyledTileView";
|
||||
import Link from "@appserver/components/link";
|
||||
import { isDesktop } from "react-device-detect";
|
||||
|
||||
const FileTile = (props) => {
|
||||
const { item, getIcon } = props;
|
||||
const { fileExst, title } = item;
|
||||
|
||||
const src = getIcon(32, ".docxf");
|
||||
const temporaryIcon = getIcon(96, ".oform");
|
||||
|
||||
const { thumbnailUrl } = item;
|
||||
const element = <img className="react-svg-icon" src={src} />;
|
||||
|
||||
return (
|
||||
<div ref={props.selectableRef}>
|
||||
<Tile
|
||||
key={item.id}
|
||||
item={item}
|
||||
thumbnail={thumbnailUrl}
|
||||
element={element}
|
||||
temporaryIcon={temporaryIcon}
|
||||
>
|
||||
<SimpleFilesTileContent
|
||||
//sideColor={theme.filesSection.tilesView.sideColor}
|
||||
isFile={fileExst}
|
||||
>
|
||||
<Link
|
||||
className="item-file-name"
|
||||
containerWidth="100%"
|
||||
type="page"
|
||||
title={title}
|
||||
fontWeight="600"
|
||||
fontSize={isDesktop ? "13px" : "14px"}
|
||||
target="_blank"
|
||||
//{...linkStyles} //TODO
|
||||
//color={theme.filesSection.tilesView.color}
|
||||
isTextOverflow
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
</SimpleFilesTileContent>
|
||||
</Tile>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ settingsStore }) => {
|
||||
const { getIcon } = settingsStore;
|
||||
return {
|
||||
getIcon,
|
||||
};
|
||||
})(observer(FileTile));
|
@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import TileContainer from "./sub-components/TileContainer";
|
||||
import FileTile from "./FileTile";
|
||||
|
||||
const FilesTileContainer = ({ filesList, t }) => {
|
||||
return (
|
||||
<TileContainer
|
||||
className="tile-container"
|
||||
draggable
|
||||
useReactWindow={false}
|
||||
headingFolders={t("Folders")}
|
||||
headingFiles={t("Files")}
|
||||
>
|
||||
<div className="tile-item-wrapper file">
|
||||
{filesList.map((item, index) => (
|
||||
<FileTile key={`${item.id}_${index}`} item={item} />
|
||||
))}
|
||||
</div>
|
||||
</TileContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ filesStore }) => {
|
||||
const { filesList } = filesStore;
|
||||
|
||||
return {
|
||||
filesList,
|
||||
};
|
||||
})(observer(FilesTileContainer));
|
@ -0,0 +1,370 @@
|
||||
import styled, { css } from "styled-components";
|
||||
import { Base } from "@appserver/components/themes";
|
||||
import TileContent from "./sub-components/TileContent";
|
||||
import { tablet, desktop } from "@appserver/components/utils/device";
|
||||
|
||||
const FlexBoxStyles = css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
`;
|
||||
|
||||
const StyledTile = styled.div`
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border: ${(props) => props.theme.filesSection.tilesView.tile.border};
|
||||
border-radius: 6px;
|
||||
${(props) => props.showHotkeyBorder && "border-color: #2DA7DB"};
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
${(props) => props.showHotkeyBorder && "border-color: #2DA7DB"};
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.file-icon_container {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-left: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledFileTileTop = styled.div`
|
||||
${FlexBoxStyles};
|
||||
background: ${(props) =>
|
||||
props.theme.filesSection.tilesView.tile.backgroundColorTop};
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
height: 156px;
|
||||
position: relative;
|
||||
|
||||
.thumbnail-image {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 6px 6px 0 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.temporary-icon > .injected-svg {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledFileTileBottom = styled.div`
|
||||
${FlexBoxStyles};
|
||||
|
||||
border-top: 1px solid transparent;
|
||||
|
||||
padding: 9px 0;
|
||||
height: 62px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.tile-file-loader {
|
||||
padding-top: 4px;
|
||||
padding-left: 3px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledContent = styled.div`
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
display: -webkit-box;
|
||||
max-width: 400px;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
line-height: 19px;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledElement = styled.div`
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
margin-right: 4px;
|
||||
user-select: none;
|
||||
margin-top: 3px;
|
||||
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
`;
|
||||
|
||||
const StyledOptionButton = styled.div`
|
||||
display: block;
|
||||
|
||||
.expandButton > div:first-child {
|
||||
padding: 8px 21px 8px 12px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledOptionButton.defaultProps = { theme: Base };
|
||||
|
||||
const SimpleFilesTileContent = styled(TileContent)`
|
||||
.row-main-container {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.main-icons {
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.badge {
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.new-items {
|
||||
position: absolute;
|
||||
right: 29px;
|
||||
top: 19px;
|
||||
}
|
||||
|
||||
.badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.share-icon {
|
||||
margin-top: -4px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.favorite,
|
||||
.can-convert,
|
||||
.edit {
|
||||
svg:not(:root) {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
display: inline-flex;
|
||||
height: auto;
|
||||
|
||||
& > div {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const paddingCss = css`
|
||||
@media ${desktop} {
|
||||
margin-left: 1px;
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
margin-left: -1px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledGridWrapper = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(216px, 1fr));
|
||||
width: 100%;
|
||||
margin-bottom: ${(props) => (props.isFolders ? "23px" : 0)};
|
||||
box-sizing: border-box;
|
||||
${paddingCss};
|
||||
|
||||
grid-gap: 14px 16px;
|
||||
|
||||
@media ${tablet} {
|
||||
grid-gap: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTileContainer = styled.div`
|
||||
position: relative;
|
||||
|
||||
.tile-item-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
&.file {
|
||||
padding: 0;
|
||||
}
|
||||
&.folder {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tile-items-heading {
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
div {
|
||||
cursor: pointer !important;
|
||||
|
||||
.sort-combo-box {
|
||||
margin-right: 3px;
|
||||
.dropdown-container {
|
||||
top: 104%;
|
||||
bottom: auto;
|
||||
min-width: 200px;
|
||||
margin-top: 3px;
|
||||
|
||||
.option-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
min-width: 200px;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.option-item__icon {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
${(props) =>
|
||||
props.isDesc &&
|
||||
css`
|
||||
transform: rotate(180deg);
|
||||
`}
|
||||
|
||||
path {
|
||||
fill: ${(props) => props.theme.filterInput.sort.sortFill};
|
||||
}
|
||||
}
|
||||
|
||||
:hover {
|
||||
.option-item__icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-option-item {
|
||||
background: ${(props) =>
|
||||
props.theme.filterInput.sort.hoverBackground};
|
||||
cursor: auto;
|
||||
|
||||
.selected-option-item__icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.optionalBlock {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
|
||||
color: ${(props) => props.theme.filterInput.sort.tileSortColor};
|
||||
|
||||
.sort-icon {
|
||||
margin-right: 8px;
|
||||
svg {
|
||||
path {
|
||||
fill: ${(props) => props.theme.filterInput.sort.tileSortFill};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.combo-buttons_arrow-icon {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media ${tablet} {
|
||||
margin-right: -3px;
|
||||
}
|
||||
`;
|
||||
|
||||
StyledTileContainer.defaultProps = { theme: Base };
|
||||
|
||||
const truncateCss = css`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
const commonCss = css`
|
||||
margin: 0;
|
||||
font-family: "Open Sans";
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const StyledTileContent = styled.div`
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
`;
|
||||
|
||||
const MainContainerWrapper = styled.div`
|
||||
${commonCss};
|
||||
|
||||
display: flex;
|
||||
align-self: center;
|
||||
margin-right: auto;
|
||||
`;
|
||||
|
||||
const MainContainer = styled.div`
|
||||
height: 20px;
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
${truncateCss};
|
||||
}
|
||||
`;
|
||||
|
||||
export {
|
||||
StyledTile,
|
||||
StyledFileTileTop,
|
||||
StyledFileTileBottom,
|
||||
StyledContent,
|
||||
StyledElement,
|
||||
StyledOptionButton,
|
||||
SimpleFilesTileContent,
|
||||
StyledGridWrapper,
|
||||
StyledTileContainer,
|
||||
StyledTileContent,
|
||||
MainContainerWrapper,
|
||||
MainContainer,
|
||||
};
|
@ -0,0 +1,181 @@
|
||||
import ContextMenuButton from "@appserver/components/context-menu-button";
|
||||
import PropTypes from "prop-types";
|
||||
import React from "react";
|
||||
import ContextMenu from "@appserver/components/context-menu";
|
||||
import { isDesktop } from "react-device-detect";
|
||||
import Link from "@appserver/components/link";
|
||||
|
||||
import { ReactSVG } from "react-svg";
|
||||
|
||||
import {
|
||||
StyledTile,
|
||||
StyledFileTileTop,
|
||||
StyledFileTileBottom,
|
||||
StyledContent,
|
||||
StyledElement,
|
||||
StyledOptionButton,
|
||||
} from "../StyledTileView";
|
||||
|
||||
class Tile extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
errorLoadSrc: false,
|
||||
};
|
||||
|
||||
this.cm = React.createRef();
|
||||
this.tile = React.createRef();
|
||||
}
|
||||
|
||||
onError = () => {
|
||||
this.setState({
|
||||
errorLoadSrc: true,
|
||||
});
|
||||
};
|
||||
|
||||
getIconFile = () => {
|
||||
const { thumbnailClick, item, temporaryIcon } = this.props;
|
||||
|
||||
//const src = item.file_image;
|
||||
const src = temporaryIcon;
|
||||
//const svgLoader = () => <div style={{ width: "96px" }} />;
|
||||
|
||||
return (
|
||||
<ReactSVG
|
||||
className="temporary-icon"
|
||||
src={src} /* loading={svgLoader} */
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Link type="page" onClick={thumbnailClick}>
|
||||
<img
|
||||
src={src}
|
||||
//className="thumbnail-image"
|
||||
className="temporary-icon"
|
||||
alt="Thumbnail-img"
|
||||
onError={this.onError}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
onFileIconClick = () => {
|
||||
if (isDesktop) return;
|
||||
|
||||
const { onSelect, item } = this.props;
|
||||
onSelect && onSelect(true, item);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
children,
|
||||
contextButtonSpacerWidth,
|
||||
contextOptions,
|
||||
element,
|
||||
tileContextClick,
|
||||
|
||||
item,
|
||||
isActive,
|
||||
|
||||
title,
|
||||
getContextModel,
|
||||
showHotkeyBorder,
|
||||
} = this.props;
|
||||
|
||||
const renderElement = Object.prototype.hasOwnProperty.call(
|
||||
this.props,
|
||||
"element"
|
||||
);
|
||||
|
||||
const renderContext =
|
||||
Object.prototype.hasOwnProperty.call(item, "contextOptions") &&
|
||||
contextOptions.length > 0;
|
||||
|
||||
const getOptions = () => {
|
||||
tileContextClick && tileContextClick();
|
||||
return contextOptions;
|
||||
};
|
||||
|
||||
const onContextMenu = (e) => {
|
||||
tileContextClick && tileContextClick();
|
||||
if (!this.cm.current.menuRef.current) {
|
||||
this.tile.current.click(e); //TODO: need fix context menu to global
|
||||
}
|
||||
this.cm.current.show(e);
|
||||
};
|
||||
|
||||
const icon = this.getIconFile();
|
||||
|
||||
// const contextMenuHeader = {
|
||||
// icon: children[0].props.item.icon,
|
||||
// title: children[0].props.item.title,
|
||||
// };
|
||||
|
||||
return (
|
||||
<StyledTile
|
||||
ref={this.tile}
|
||||
{...this.props}
|
||||
onContextMenu={onContextMenu}
|
||||
isActive={isActive}
|
||||
isDesktop={isDesktop}
|
||||
showHotkeyBorder={showHotkeyBorder}
|
||||
>
|
||||
<StyledFileTileTop isActive={isActive}>{icon}</StyledFileTileTop>
|
||||
|
||||
<StyledFileTileBottom isActive={isActive}>
|
||||
<div className="file-icon_container">
|
||||
<div className="file-icon" onClick={this.onFileIconClick}>
|
||||
{element}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<StyledContent>{children}</StyledContent>
|
||||
<StyledOptionButton spacerWidth={contextButtonSpacerWidth}>
|
||||
{renderContext ? (
|
||||
<ContextMenuButton
|
||||
className="expandButton"
|
||||
directionX="right"
|
||||
getData={getOptions}
|
||||
isNew={true}
|
||||
onClick={onContextMenu}
|
||||
title={title}
|
||||
/>
|
||||
) : (
|
||||
<div className="expandButton" />
|
||||
)}
|
||||
<ContextMenu
|
||||
getContextModel={getContextModel}
|
||||
ref={this.cm}
|
||||
//header={contextMenuHeader}
|
||||
withBackdrop={true}
|
||||
/>
|
||||
</StyledOptionButton>
|
||||
</StyledFileTileBottom>
|
||||
</StyledTile>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Tile.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.element),
|
||||
PropTypes.element,
|
||||
]),
|
||||
className: PropTypes.string,
|
||||
contextButtonSpacerWidth: PropTypes.string,
|
||||
contextOptions: PropTypes.array,
|
||||
data: PropTypes.object,
|
||||
element: PropTypes.element,
|
||||
id: PropTypes.string,
|
||||
onSelect: PropTypes.func,
|
||||
tileContextClick: PropTypes.func,
|
||||
};
|
||||
|
||||
Tile.defaultProps = {
|
||||
contextButtonSpacerWidth: "32px",
|
||||
item: {},
|
||||
};
|
||||
|
||||
export default Tile;
|
@ -0,0 +1,106 @@
|
||||
import React, { memo } from "react";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
import { FixedSizeList as List, areEqual } from "react-window";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import CustomScrollbarsVirtualList from "@appserver/components/scrollbar";
|
||||
import { StyledGridWrapper, StyledTileContainer } from "../StyledTileView";
|
||||
|
||||
class TileContainer extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
contextOptions: [],
|
||||
};
|
||||
}
|
||||
|
||||
onRowContextClick = (options) => {
|
||||
if (Array.isArray(options)) {
|
||||
this.setState({
|
||||
contextOptions: options,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
renderTile = memo(({ data, index, style }) => {
|
||||
const options = data[index].props.contextOptions;
|
||||
|
||||
return (
|
||||
<div
|
||||
onContextMenu={this.onRowContextClick.bind(this, options)}
|
||||
style={style}
|
||||
>
|
||||
{data[index]}
|
||||
</div>
|
||||
);
|
||||
}, areEqual);
|
||||
|
||||
render() {
|
||||
const {
|
||||
itemHeight,
|
||||
children,
|
||||
useReactWindow,
|
||||
id,
|
||||
className,
|
||||
style,
|
||||
} = this.props;
|
||||
|
||||
const renderList = ({ height, width }) => (
|
||||
<List
|
||||
className="list"
|
||||
height={height}
|
||||
width={width}
|
||||
itemSize={itemHeight}
|
||||
itemCount={children.length}
|
||||
itemData={children}
|
||||
outerElementType={CustomScrollbarsVirtualList}
|
||||
>
|
||||
{this.renderTile}
|
||||
</List>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledTileContainer
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
useReactWindow={useReactWindow}
|
||||
>
|
||||
{useReactWindow ? (
|
||||
<AutoSizer>{renderList}</AutoSizer>
|
||||
) : (
|
||||
<StyledGridWrapper>
|
||||
{/* <div
|
||||
className="tile-item-wrapper file"
|
||||
// onContextMenu={this.onRowContextClick.bind(
|
||||
// this,
|
||||
// children.props.contextOptions
|
||||
// )}
|
||||
> */}
|
||||
{children}
|
||||
{/* </div> */}
|
||||
</StyledGridWrapper>
|
||||
)}
|
||||
</StyledTileContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TileContainer.propTypes = {
|
||||
itemHeight: PropTypes.number,
|
||||
manualHeight: PropTypes.string,
|
||||
children: PropTypes.any.isRequired,
|
||||
useReactWindow: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
};
|
||||
|
||||
TileContainer.defaultProps = {
|
||||
itemHeight: 50,
|
||||
useReactWindow: true,
|
||||
id: "rowContainer",
|
||||
};
|
||||
|
||||
export default withTranslation(["Home", "Common"])(TileContainer);
|
@ -0,0 +1,37 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import {
|
||||
StyledTileContent,
|
||||
MainContainerWrapper,
|
||||
MainContainer,
|
||||
} from "../StyledTileView";
|
||||
|
||||
const TileContent = (props) => {
|
||||
const { children, id, className, style, onClick } = props;
|
||||
|
||||
return (
|
||||
<StyledTileContent
|
||||
id={id}
|
||||
className={className}
|
||||
style={style}
|
||||
onClick={onClick}
|
||||
>
|
||||
<MainContainerWrapper
|
||||
mainContainerWidth={children.props && children.props.containerWidth}
|
||||
>
|
||||
<MainContainer className="row-main-container">{children}</MainContainer>
|
||||
</MainContainerWrapper>
|
||||
</StyledTileContent>
|
||||
);
|
||||
};
|
||||
|
||||
TileContent.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
sideColor: PropTypes.string,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
};
|
||||
|
||||
export default TileContent;
|
23
products/ASC.Files/Client/src/pages/FormGallery/index.js
Normal file
23
products/ASC.Files/Client/src/pages/FormGallery/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import Section from "@appserver/common/components/Section";
|
||||
|
||||
import SectionHeaderContent from "./Header";
|
||||
import SectionBodyContent from "./Body";
|
||||
|
||||
const FormGallery = () => {
|
||||
return (
|
||||
<Section
|
||||
// withBodyScroll
|
||||
// withBodyAutoFocus={!isMobile}
|
||||
>
|
||||
<Section.SectionHeader>
|
||||
<SectionHeaderContent />
|
||||
</Section.SectionHeader>
|
||||
<Section.SectionBody>
|
||||
<SectionBodyContent />
|
||||
</Section.SectionBody>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export default FormGallery;
|
@ -21,6 +21,8 @@ import toastr from "studio/toastr";
|
||||
const { FilesFilter } = api;
|
||||
const storageViewAs = localStorage.getItem("viewAs");
|
||||
|
||||
import OFORMsGallery from "./OFORMsGallery.json";
|
||||
|
||||
class FilesStore {
|
||||
authStore;
|
||||
settingsStore;
|
||||
@ -62,6 +64,7 @@ class FilesStore {
|
||||
headerBorder = false;
|
||||
|
||||
isPrevSettingsModule = false;
|
||||
oformFiles = [];
|
||||
|
||||
constructor(
|
||||
authStore,
|
||||
@ -287,10 +290,33 @@ class FilesStore {
|
||||
}
|
||||
}
|
||||
requests.push(getFilesSettings());
|
||||
//requests.push(this.getOforms());
|
||||
this.getOforms();
|
||||
|
||||
return Promise.all(requests).then(() => (this.isInit = true));
|
||||
};
|
||||
|
||||
getOforms = async () => {
|
||||
const { getOforms, culture } = this.settingsStore;
|
||||
console.log("culture", culture);
|
||||
//const oformData = await getOforms();
|
||||
const oformData = OFORMsGallery;
|
||||
|
||||
this.oformFiles = oformData.filter((f) =>
|
||||
f["file_country_access"].includes("US")
|
||||
); //TODO oformCulture
|
||||
|
||||
// const split = culture.split("-");
|
||||
// const oformCulture = split[1] ? split[1] : split[0].toUpperCase();
|
||||
// const oformData = jsonFile.filter(
|
||||
// (f) => f["file_country_access"].includes("US") //TODO oformCulture
|
||||
// );
|
||||
};
|
||||
|
||||
get hasOFORMFilesGallery() {
|
||||
return !!this.oformFiles.length;
|
||||
}
|
||||
|
||||
setFirstLoad = (firstLoad) => {
|
||||
this.firstLoad = firstLoad;
|
||||
};
|
||||
|
3908
products/ASC.Files/Client/src/store/OFORMsGallery.json
Normal file
3908
products/ASC.Files/Client/src/store/OFORMsGallery.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -153,5 +153,6 @@
|
||||
"Video": "Video",
|
||||
"View": "View",
|
||||
"ViewWeb": "View web version",
|
||||
"Warning": "Warning"
|
||||
"Warning": "Warning",
|
||||
"OFORMsGallery": "OFORMs gallery"
|
||||
}
|
||||
|
@ -153,5 +153,6 @@
|
||||
"Video": "Видео",
|
||||
"View": "Просмотр",
|
||||
"ViewWeb": "Просмотреть веб-версию",
|
||||
"Warning": "Внимание"
|
||||
"Warning": "Внимание",
|
||||
"OFORMsGallery": "Галерея OFORMs"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user