Merge pull request #601 from ONLYOFFICE/feature/catalog-item-link

Feature/catalog item link
This commit is contained in:
Alexey Safronov 2024-08-28 16:59:07 +04:00 committed by GitHub
commit db6c6f95de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 137 additions and 190 deletions

View File

@ -39,6 +39,7 @@ const PureAccountsItem = ({
onClick,
t,
currentColorScheme,
getLinkData,
}) => {
const onClickAction = React.useCallback(
(e, id) => {
@ -51,10 +52,13 @@ const PureAccountsItem = ({
const title = t("Accounts");
const linkData = getLinkData("accounts");
return (
<ArticleItem
key="accounts"
text={title}
linkData={linkData}
title={title}
icon={icon}
showText={showText}

View File

@ -75,6 +75,7 @@ const Item = ({
iconBadge,
folderId,
currentColorScheme,
getLinkData,
}) => {
const [isDragActive, setIsDragActive] = useState(false);
@ -126,18 +127,24 @@ const Item = ({
(e, folderId) => {
setBufferSelection(null);
onClick &&
onClick(
e,
folderId,
item.title,
item.rootFolderType,
item.security.Create,
);
onClick?.(
e,
folderId,
item.title,
item.rootFolderType,
item.security.Create,
);
},
[onClick, item.title, item.rootFolderType],
);
const linkData = getLinkData(
item.id,
item.title,
item.rootFolderType,
item.security.Create,
);
return (
<StyledDragAndDrop
key={item.id}
@ -170,6 +177,7 @@ const Item = ({
onClickBadge={onBadgeClick}
iconBadge={iconBadge}
badgeTitle={labelBadge ? "" : t("EmptyRecycleBin")}
linkData={linkData}
$currentColorScheme={currentColorScheme}
/>
</StyledDragAndDrop>
@ -215,6 +223,8 @@ const Items = ({
currentDeviceType,
folderAccess,
currentColorScheme,
getLinkData,
}) => {
const getEndOfBlock = React.useCallback((item) => {
switch (item.key) {
@ -329,6 +339,7 @@ const Items = ({
getEndOfBlock={getEndOfBlock}
showText={showText}
onClick={onClick}
getLinkData={getLinkData}
onMoveTo={isTrash ? onRemove : onMoveTo}
onBadgeClick={isTrash ? onEmptyTrashAction : onBadgeClick}
showDragItems={showDragItems}
@ -341,16 +352,6 @@ const Items = ({
);
});
/*if (!firstLoad && !isVisitor)
items.splice(
3,
0,
<SettingsItem
key="settings-item"
onClick={onClick}
isActive={activeItemId === "settings"}
/>
);*/
if (!isVisitor && !isCollaborator)
items.splice(
3,
@ -358,6 +359,7 @@ const Items = ({
<AccountsItem
key="accounts-item"
onClick={onClick}
getLinkData={getLinkData}
isActive={activeItemId === "accounts"}
/>,
);
@ -375,6 +377,7 @@ const Items = ({
dragging,
getFolderIcon,
onClick,
getLinkData,
onMoveTo,
getEndOfBlock,
onBadgeClick,

View File

@ -1,72 +0,0 @@
// (c) Copyright Ascensio System SIA 2009-2024
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
import React from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import { ArticleItem } from "@docspace/shared/components/article-item";
import CatalogSettingsReactSvgUrl from "PUBLIC_DIR/images/catalog.settings.react.svg?url";
const PureSettingsItem = ({
t,
showText,
isActive,
onClick,
currentColorScheme,
}) => {
const onClickAction = React.useCallback(() => {
onClick && onClick("settings");
}, [onClick]);
const title = t("Common:Settings");
return (
<ArticleItem
key="settings"
text={title}
title={title}
icon={CatalogSettingsReactSvgUrl}
showText={showText}
onClick={onClickAction}
isActive={isActive}
folderId="document_catalog-settings"
$currentColorScheme={currentColorScheme}
/>
);
};
const SettingsItem = withTranslation(["FilesSettings", "Common"])(
PureSettingsItem,
);
export default inject(({ settingsStore }) => {
return {
showText: settingsStore.showText,
currentColorScheme: settingsStore.currentColorScheme,
};
})(observer(SettingsItem));

View File

@ -28,7 +28,7 @@ import React from "react";
import { inject, observer } from "mobx-react";
import { withTranslation } from "react-i18next";
import { useNavigate, useLocation } from "react-router-dom";
import { useLocation } from "react-router-dom";
import { DeviceType, RoomSearchArea } from "@docspace/shared/enums";
import Items from "./Items";
@ -43,9 +43,7 @@ import { getCategoryUrl } from "SRC_DIR/helpers/utils";
import { CategoryType } from "SRC_DIR/helpers/constants";
import { ArticleFolderLoader } from "@docspace/shared/skeletons/article";
import { MEDIA_VIEW_URL } from "@docspace/shared/constants";
import { combineUrl } from "@docspace/shared/utils/combineUrl";
import { showProgress } from "@docspace/shared/utils/common";
import { openingNewTab } from "@docspace/shared/utils/openingNewTab";
const ArticleBodyContent = (props) => {
const {
@ -77,18 +75,13 @@ const ArticleBodyContent = (props) => {
isFrame,
} = props;
const navigate = useNavigate();
const location = useLocation();
const [disableBadgeClick, setDisableBadgeClick] = React.useState(false);
const [activeItemId, setActiveItemId] = React.useState(null);
const isAccounts = location.pathname.includes("accounts/filter");
const onClick = React.useCallback(
(e, folderId, title, rootFolderType, canCreate) => {
const { toggleArticleOpen } = props;
const getLinkData = React.useCallback(
(folderId, title, rootFolderType, canCreate) => {
let params = null;
let path = ``;
@ -100,8 +93,6 @@ const ArticleBodyContent = (props) => {
canCreate,
};
let withTimer = !!selectedFolderId;
switch (folderId) {
case myFolderId:
const myFilter = FilesFilter.getDefault();
@ -155,17 +146,7 @@ const ArticleBodyContent = (props) => {
params = accountsFilter.toUrlParams();
path = getCategoryUrl(CategoryType.Accounts);
withTimer = false;
break;
case "settings":
path = getCategoryUrl(CategoryType.Settings);
navigate(path);
if (currentDeviceType === DeviceType.mobile) {
toggleArticleOpen();
}
return;
case roomsFolderId:
default:
const roomsFilter = RoomsFilter.getDefault(
@ -181,16 +162,33 @@ const ArticleBodyContent = (props) => {
path += `?${params}&date=${new Date().getTime()}`;
if (openingNewTab(path, e)) return;
return { path, state };
},
[
roomsFolderId,
archiveFolderId,
myFolderId,
recycleBinFolderId,
activeItemId,
],
);
if (folderId === "accounts" || folderId === "settings") clearFiles();
const onClick = React.useCallback(
(e, folderId) => {
if (e?.ctrlKey || e?.metaKey || e?.shiftKey || e?.button) return;
setSelection && setSelection([]);
const { toggleArticleOpen } = props;
const isAccountsClick = folderId === "accounts";
let withTimer = isAccountsClick ? false : !!selectedFolderId;
if (isAccountsClick) clearFiles();
setSelection?.([]);
setIsLoading(true, withTimer);
navigate(path, { state });
if (currentDeviceType === DeviceType.mobile) {
toggleArticleOpen();
}
@ -202,7 +200,7 @@ const ArticleBodyContent = (props) => {
recycleBinFolderId,
activeItemId,
selectedFolderId,
isAccounts,
setSelection,
],
);
@ -281,6 +279,7 @@ const ArticleBodyContent = (props) => {
<Items
onClick={onClick}
onBadgeClick={onShowNewFilesPanel}
getLinkData={getLinkData}
showText={showText}
onHide={toggleArticleOpen}
activeItemId={activeItemId}

View File

@ -173,26 +173,19 @@ const ArticleBodyContent = (props) => {
selectedKeys,
]);
const onSelect = (value, e) => {
if (isArrayEqual([value], selectedKeys)) {
return;
}
const settingsPath = `/portal-settings${getSelectedLinkByKey(
const getLinkData = (value) => {
const path = `/portal-settings${getSelectedLinkByKey(
value + "-0",
settingsTree,
)}`;
if (openingNewTab(settingsPath, e)) return;
// setSelectedKeys([value + "-0"]);
return { path, state: {} };
};
const onSelect = (value, e) => {
if (currentDeviceType === DeviceType.mobile) {
toggleArticleOpen();
}
if (settingsPath === location.pathname) return;
navigate(`${settingsPath}`);
};
const mapKeys = (tKey) => {
@ -303,6 +296,7 @@ const ArticleBodyContent = (props) => {
const patternSearching = selectedKeys[0].split("-");
const selectedKey = patternSearching[0];
const title = mapKeys(item.tKey);
const linkData = getLinkData(item.key);
items.push(
<ArticleItem
@ -315,6 +309,7 @@ const ArticleBodyContent = (props) => {
value={item.link}
isActive={item.key === selectedKey}
onClick={(e) => onSelect(item.key, e)}
linkData={linkData}
folderId={item.id}
style={{
marginTop: `${item.key.includes(9) ? "16px" : "0"}`,

View File

@ -68,18 +68,11 @@ const ArticleBodyContent = () => {
}, []);
const onClickItem = (item: TSettingsTreeItem, e: React.MouseEvent) => {
const path = item.link;
const url = combineUrl(PROXY_BASE_URL, path);
if (openingNewTab(url, e)) return;
setSelectedKey(item?.key);
if (isMobileOnly || isMobile()) {
toggleArticleOpen();
}
navigate(path);
};
const catalogItems = () => {
@ -108,6 +101,7 @@ const ArticleBodyContent = () => {
onClick={(e) => onClickItem(item, e)}
folderId={item.id}
$currentColorScheme={currentColorScheme}
linkData={{ path: item.link, state: {} }}
/>
);
});

View File

@ -26,6 +26,7 @@
import React from "react";
import { ReactSVG } from "react-svg";
import { Link } from "react-router-dom";
import { Text } from "../text";
@ -69,6 +70,7 @@ export const ArticleItemPure = (props: ArticleItemProps) => {
badgeTitle,
$currentColorScheme,
title,
linkData,
} = props;
const onClickAction = (e: React.MouseEvent) => {
@ -105,62 +107,68 @@ export const ArticleItemPure = (props: ArticleItemProps) => {
const renderItem = () => {
return (
<StyledArticleItemTheme
className={className}
style={style}
showText={showText}
isEndOfBlock={isEndOfBlock}
isActive={isActive}
data-testid="article-item"
$currentColorScheme={$currentColorScheme}
title={tooltipTitle}
<Link
style={{ textDecoration: "none" }}
to={linkData?.path}
state={linkData?.state}
>
<StyledArticleItemSibling
id={folderId}
<StyledArticleItemTheme
className={className}
style={style}
showText={showText}
isEndOfBlock={isEndOfBlock}
isActive={isActive}
isDragging={isDragging}
isDragActive={isDragActive}
onClick={onClickAction}
onMouseUp={onMouseUpAction}
onMouseDown={onMouseDown}
/>
<StyledArticleItemImg isActive={isActive}>
<ReactSVG className="icon" src={icon} />
{!showText && (
<>
{showInitial && (
<StyledArticleItemInitialText>
{getInitial(text)}
</StyledArticleItemInitialText>
)}
{showBadge && !iconBadge && (
<StyledArticleItemBadgeWrapper
onClick={onClickBadgeAction}
showText={showText}
/>
)}
</>
)}
</StyledArticleItemImg>
{showText && (
<StyledArticleItemText isActive={isActive} noSelect>
{text}
</StyledArticleItemText>
)}
{showBadge && showText && (
<StyledArticleItemBadgeWrapper
showText={showText}
onClick={onClickBadgeAction}
title={badgeTitle}
>
{!iconBadge ? (
<Badge className="catalog-item__badge" label={labelBadge} />
) : (
<ReactSVG className="catalog-item__icon" src={iconBadge} />
data-testid="article-item"
$currentColorScheme={$currentColorScheme}
title={tooltipTitle}
>
<StyledArticleItemSibling
id={folderId}
isActive={isActive}
isDragging={isDragging}
isDragActive={isDragActive}
onClick={onClickAction}
onMouseUp={onMouseUpAction}
onMouseDown={onMouseDown}
/>
<StyledArticleItemImg isActive={isActive}>
<ReactSVG className="icon" src={icon} />
{!showText && (
<>
{showInitial && (
<StyledArticleItemInitialText>
{getInitial(text)}
</StyledArticleItemInitialText>
)}
{showBadge && !iconBadge && (
<StyledArticleItemBadgeWrapper
onClick={onClickBadgeAction}
showText={showText}
/>
)}
</>
)}
</StyledArticleItemBadgeWrapper>
)}
</StyledArticleItemTheme>
</StyledArticleItemImg>
{showText && (
<StyledArticleItemText isActive={isActive} noSelect>
{text}
</StyledArticleItemText>
)}
{showBadge && showText && (
<StyledArticleItemBadgeWrapper
showText={showText}
onClick={onClickBadgeAction}
title={badgeTitle}
>
{!iconBadge ? (
<Badge className="catalog-item__badge" label={labelBadge} />
) : (
<ReactSVG className="catalog-item__icon" src={iconBadge} />
)}
</StyledArticleItemBadgeWrapper>
)}
</StyledArticleItemTheme>
</Link>
);
};

View File

@ -26,6 +26,21 @@
import { TColorScheme } from "../../themes";
export type TArticleLinkDataState =
| {
title: string;
isRoot: boolean;
isPublicRoomType: boolean;
rootFolderType: number;
canCreate: boolean;
}
| {};
export type TArticleLinkData = {
path: string;
state: TArticleLinkDataState;
};
export interface ArticleItemProps {
/** Accepts className */
className?: string;
@ -70,4 +85,5 @@ export interface ArticleItemProps {
badgeTitle?: string;
$currentColorScheme?: TColorScheme;
title?: string;
linkData: TArticleLinkData;
}