From 271b9c21a1b231be53dda67309f62ab1fada1573 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Mon, 13 May 2024 14:28:35 +0300 Subject: [PATCH 01/33] Shared: fix storybook start --- packages/shared/.storybook/main.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/shared/.storybook/main.js b/packages/shared/.storybook/main.js index af58575c23..b7eb5c0482 100644 --- a/packages/shared/.storybook/main.js +++ b/packages/shared/.storybook/main.js @@ -44,6 +44,16 @@ module.exports = { docs: { autodocs: true, }, + typescript: { + check: false, + checkOptions: {}, + reactDocgen: false, + reactDocgenTypescriptOptions: { + shouldExtractLiteralValuesFromEnum: true, + propFilter: (prop) => + prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, + }, + }, }; function getAbsolutePath(value) { From 557b43feb9c63ff7bde62363157a3aedff5af759 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Mon, 13 May 2024 14:28:56 +0300 Subject: [PATCH 02/33] Shared:Components:Selector: add NewItem and InputItem --- .../components/selector/Selector.stories.tsx | 10 +++ .../components/selector/Selector.styled.ts | 6 +- .../components/selector/Selector.types.ts | 32 +++++++++ .../selector/sub-components/InputItem.tsx | 0 .../selector/sub-components/Item.tsx | 8 +++ .../selector/sub-components/NewItem.tsx | 66 +++++++++++++++++++ 6 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 packages/shared/components/selector/sub-components/InputItem.tsx create mode 100644 packages/shared/components/selector/sub-components/NewItem.tsx diff --git a/packages/shared/components/selector/Selector.stories.tsx b/packages/shared/components/selector/Selector.stories.tsx index f811fd78d6..7561e6b63f 100644 --- a/packages/shared/components/selector/Selector.stories.tsx +++ b/packages/shared/components/selector/Selector.stories.tsx @@ -89,6 +89,16 @@ function makeName() { const getItems = (count: number) => { const items: TSelectorItem[] = []; + console.log("call"); + + items.push({ + key: "create_new", + id: "create_new_item", + label: "New folder", + isCreateNewItem: true, + onCreateClick: () => {}, + }); + for (let i = 0; i < count / 2; i += 1) { const label = makeName(); items.push({ diff --git a/packages/shared/components/selector/Selector.styled.ts b/packages/shared/components/selector/Selector.styled.ts index 2b3fa130c2..e7841acabe 100644 --- a/packages/shared/components/selector/Selector.styled.ts +++ b/packages/shared/components/selector/Selector.styled.ts @@ -186,6 +186,7 @@ const StyledItem = styled.div<{ isSelected: boolean | undefined; isDisabled?: boolean; isMultiSelect: boolean; + noHover?: boolean; }>` display: flex; align-items: center; @@ -245,12 +246,13 @@ const StyledItem = styled.div<{ ` : css` ${props.isSelected && !props.isMultiSelect && selectedCss} - @media (hover: hover) { + ${!props.noHover && + ` @media (hover: hover) { &:hover { cursor: pointer; background: ${props.theme.selector.item.hoverBackground}; } - } + }`} `} `; diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index 06de7293b6..7f7b4f7a56 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -435,6 +435,8 @@ type TSelectorItemType = security?: undefined; isGroup?: undefined; name?: undefined; + isCreateNewItem?: undefined; + onCreateClick?: undefined; } | { email?: undefined; @@ -455,6 +457,8 @@ type TSelectorItemType = security?: TFileSecurity; isGroup?: undefined; name?: undefined; + isCreateNewItem?: undefined; + onCreateClick?: undefined; } | { email?: undefined; @@ -475,6 +479,8 @@ type TSelectorItemType = security?: TRoomSecurity; isGroup?: undefined; name?: undefined; + isCreateNewItem?: undefined; + onCreateClick?: undefined; } | { email?: undefined; @@ -495,6 +501,8 @@ type TSelectorItemType = security?: TFolderSecurity; isGroup?: undefined; name?: undefined; + isCreateNewItem?: undefined; + onCreateClick?: undefined; } | { email?: undefined; @@ -515,6 +523,30 @@ type TSelectorItemType = security?: TFolderSecurity; isGroup: true; name: string; + isCreateNewItem?: undefined; + onCreateClick?: undefined; + } + | { + email?: undefined; + fileExst?: undefined; + roomType?: undefined; + shared?: undefined; + isOwner?: undefined; + isAdmin?: undefined; + isVisitor?: undefined; + isCollaborator?: undefined; + isRoomAdmin?: undefined; + access?: undefined; + isFolder?: undefined; + parentId?: undefined; + rootFolderType?: undefined; + filesCount?: undefined; + foldersCount?: undefined; + security?: undefined; + isGroup?: undefined; + name?: undefined; + isCreateNewItem: boolean; + onCreateClick: VoidFunction; }; export type TSelectorItem = TSelectorItemLogo & diff --git a/packages/shared/components/selector/sub-components/InputItem.tsx b/packages/shared/components/selector/sub-components/InputItem.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/shared/components/selector/sub-components/Item.tsx b/packages/shared/components/selector/sub-components/Item.tsx index bc67babf1f..49352bbbcc 100644 --- a/packages/shared/components/selector/sub-components/Item.tsx +++ b/packages/shared/components/selector/sub-components/Item.tsx @@ -36,6 +36,7 @@ import { RoomIcon } from "../../room-icon"; import { StyledItem } from "../Selector.styled"; import { ItemProps, Data, TSelectorItem } from "../Selector.types"; import { RoomsType } from "../../../enums"; +import NewItem from "./NewItem"; const compareFunction = (prevProps: ItemProps, nextProps: ItemProps) => { const prevData = prevProps.data; @@ -90,8 +91,15 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { email, isGroup, disabledText, + isCreateNewItem, + onCreateClick, } = item; + if (isCreateNewItem) + return ( + + ); + const showPlanetIcon = (item.roomType === RoomsType.PublicRoom || item.roomType === RoomsType.CustomRoom) && diff --git a/packages/shared/components/selector/sub-components/NewItem.tsx b/packages/shared/components/selector/sub-components/NewItem.tsx new file mode 100644 index 0000000000..dd6a68626c --- /dev/null +++ b/packages/shared/components/selector/sub-components/NewItem.tsx @@ -0,0 +1,66 @@ +// (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 { Text } from "../../text"; +import { SelectorAddButton } from "../../selector-add-button"; + +import { StyledItem } from "../Selector.styled"; + +const NewItem = ({ + label, + style, + onCreateClick, +}: { + label: string; + style: React.CSSProperties; + onCreateClick: VoidFunction; +}) => { + return ( + + + + {label} + + + ); +}; + +export default NewItem; From 27679f7b5e5ec718708130de010064254d0b303f Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 14:27:02 +0300 Subject: [PATCH 03/33] Shared:Selector: refactoring types --- .../components/selector/Selector.styled.ts | 5 +- .../components/selector/Selector.types.ts | 342 +++++++----------- .../selector/sub-components/Body.tsx | 29 +- .../selector/sub-components/BreadCrumbs.tsx | 19 +- .../selector/sub-components/Item.tsx | 12 +- .../selector/sub-components/SelectAll.tsx | 67 ++-- packages/shared/selectors/Groups/index.tsx | 1 - packages/shared/types/index.ts | 2 + 8 files changed, 212 insertions(+), 265 deletions(-) diff --git a/packages/shared/components/selector/Selector.styled.ts b/packages/shared/components/selector/Selector.styled.ts index e7841acabe..477725c895 100644 --- a/packages/shared/components/selector/Selector.styled.ts +++ b/packages/shared/components/selector/Selector.styled.ts @@ -327,7 +327,10 @@ const StyledBreadCrumbs = styled.div<{ StyledBreadCrumbs.defaultProps = { theme: Base }; -const StyledItemText = styled(Text)<{ isCurrent: boolean; isLoading: boolean }>` +const StyledItemText = styled(Text)<{ + isCurrent: boolean; + isLoading?: boolean; +}>` ${(props) => !props.isCurrent && css` diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index 7f7b4f7a56..e3d14e78c9 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -26,10 +26,14 @@ import React from "react"; import { RoomsType, ShareAccessRights } from "../../enums"; -import { AvatarRole } from "../avatar"; +import { MergeTypes } from "../../types"; + import { TFileSecurity, TFolderSecurity } from "../../api/files/types"; import { TRoomSecurity } from "../../api/rooms/types"; + +import { AvatarRole } from "../avatar"; import { TSubmenuItem } from "../submenu"; + import { SelectorAccessRightsMode } from "./Selector.enums"; // header @@ -56,12 +60,12 @@ export type TSelectorHeader = | { withHeader?: undefined; headerProps?: undefined }; // bread crumbs - export type TBreadCrumb = { id: string | number; label: string; isRoom?: boolean; minWidth?: string; + roomType?: RoomsType; onClick?: ({ e, open, @@ -71,49 +75,41 @@ export type TBreadCrumb = { open: boolean; item: TBreadCrumb; }) => void; - roomType?: RoomsType; }; -export interface BreadCrumbsProps { - breadCrumbs: TBreadCrumb[]; - onSelectBreadCrumb: (item: TBreadCrumb) => void; - isLoading: boolean; -} +export type TDisplayedItem = { + id: string | number; + label: string; + isArrow: boolean; + isList: boolean; + isRoom?: boolean; + listItems?: TBreadCrumb[]; +}; export type TSelectorBreadCrumbs = | { withBreadCrumbs: true; - breadCrumbs: TBreadCrumb[]; - onSelectBreadCrumb: (item: TBreadCrumb) => void; - breadCrumbsLoader: React.ReactNode; isBreadCrumbsLoading: boolean; + breadCrumbs: TBreadCrumb[]; + breadCrumbsLoader: React.ReactNode; + + onSelectBreadCrumb: (item: TBreadCrumb) => void; } | { withBreadCrumbs?: undefined; - breadCrumbs?: undefined; - onSelectBreadCrumb?: undefined; - breadCrumbsLoader?: undefined; isBreadCrumbsLoading?: undefined; + breadCrumbs?: undefined; + breadCrumbsLoader?: undefined; + + onSelectBreadCrumb?: undefined; }; // tabs - export type TWithTabs = | { withTabs: true; tabsData: TSubmenuItem[]; activeTabId: string } | { withTabs?: undefined; tabsData?: undefined; activeTabId?: undefined }; // select all - -export interface SelectAllProps { - label: string; - icon: string; - onSelectAll: () => void; - isChecked: boolean; - isIndeterminate: boolean; - isLoading: boolean; - rowLoader: React.ReactNode; -} - export type TSelectorSelectAll = { isAllIndeterminate?: boolean; isAllChecked?: boolean; @@ -389,178 +385,127 @@ export type FooterProps = TSelectorFooterSubmitButton & requestRunning?: boolean; }; -type TSelectorItemLogo = - | { - avatar: string; - color?: undefined; - hasAvatar?: boolean; - icon?: undefined; - iconOriginal?: string; - role?: AvatarRole; - } - | { - avatar?: undefined; - color: string; - hasAvatar?: undefined; - icon?: undefined; - iconOriginal?: string; - role?: undefined; - } - | { - avatar?: undefined; - color?: undefined; - hasAvatar?: undefined; - icon: string; - iconOriginal: string; - role?: undefined; - }; +type TSelectorItemEmpty = { + avatar?: undefined; + color?: undefined; + hasAvatar?: undefined; + icon?: undefined; + iconOriginal?: undefined; + role?: undefined; + email?: undefined; + isOwner?: undefined; + isAdmin?: undefined; + isVisitor?: undefined; + isCollaborator?: undefined; + isRoomAdmin?: undefined; + access?: undefined; + fileExst?: undefined; + shared?: undefined; + parentId?: undefined; + rootFolderType?: undefined; + security?: undefined; + isFolder?: undefined; + filesCount?: undefined; + foldersCount?: undefined; + roomType?: undefined; + isGroup?: undefined; + name?: undefined; + isCreateNewItem?: undefined; + onCreateClick?: undefined; +}; + +export type TSelectorItemUser = MergeTypes< + TSelectorItemEmpty, + { + email: string; + isOwner: boolean; + isAdmin: boolean; + isVisitor: boolean; + isCollaborator: boolean; + isRoomAdmin: boolean; + avatar: string; + hasAvatar: boolean; + role: AvatarRole; + + access?: ShareAccessRights | string | number; + } +>; + +export type TSelectorItemFile = MergeTypes< + TSelectorItemEmpty, + { + fileExst: string; + parentId: string | number; + rootFolderType: string | number; + security: TFileSecurity; + icon: string; + } +>; + +export type TSelectorItemFolder = MergeTypes< + TSelectorItemEmpty, + { + isFolder: boolean; + parentId: string | number; + rootFolderType: string | number; + filesCount: number; + foldersCount: number; + security: TFolderSecurity; + icon: string; + } +>; + +export type TSelectorItemRoom = MergeTypes< + TSelectorItemEmpty, + { + isFolder: boolean; + roomType: RoomsType; + shared: boolean; + parentId: string | number; + rootFolderType: string | number; + filesCount: number; + foldersCount: number; + security: TRoomSecurity; + icon?: string; + color?: string; + iconOriginal?: string; + } +>; + +export type TSelectorItemGroup = MergeTypes< + TSelectorItemEmpty, + { + isGroup: boolean; + name: string; + } +>; + +export type TSelectorItemNew = MergeTypes< + TSelectorItemEmpty, + { + isCreateNewItem: boolean; + onCreateClick: VoidFunction; + } +>; type TSelectorItemType = - | { - email: string; - fileExst?: undefined; - roomType?: undefined; - shared?: undefined; - isOwner: boolean; - isAdmin: boolean; - isVisitor: boolean; - isCollaborator: boolean; - isRoomAdmin: boolean; - access?: ShareAccessRights | string | number; - isFolder?: undefined; - parentId?: undefined; - rootFolderType?: undefined; - filesCount?: undefined; - foldersCount?: undefined; - security?: undefined; - isGroup?: undefined; - name?: undefined; - isCreateNewItem?: undefined; - onCreateClick?: undefined; - } - | { - email?: undefined; - fileExst: string; - roomType?: undefined; - shared?: boolean; - isOwner?: undefined; - isAdmin?: undefined; - isVisitor?: undefined; - isCollaborator?: undefined; - isRoomAdmin?: undefined; - access?: undefined; - isFolder?: undefined; - parentId?: string | number; - rootFolderType?: string | number; - filesCount?: undefined; - foldersCount?: undefined; - security?: TFileSecurity; - isGroup?: undefined; - name?: undefined; - isCreateNewItem?: undefined; - onCreateClick?: undefined; - } - | { - email?: undefined; - fileExst?: undefined; - roomType: RoomsType; - shared?: boolean; - isOwner?: undefined; - isAdmin?: undefined; - isVisitor?: undefined; - isCollaborator?: undefined; - isRoomAdmin?: undefined; - access?: undefined; - isFolder: boolean; - parentId?: string | number; - rootFolderType?: string | number; - filesCount?: number; - foldersCount?: number; - security?: TRoomSecurity; - isGroup?: undefined; - name?: undefined; - isCreateNewItem?: undefined; - onCreateClick?: undefined; - } - | { - email?: undefined; - fileExst?: undefined; - roomType?: undefined; - shared?: boolean; - isOwner?: undefined; - isAdmin?: undefined; - isVisitor?: undefined; - isCollaborator?: undefined; - isRoomAdmin?: undefined; - access?: undefined; - isFolder: boolean; - parentId?: string | number; - rootFolderType?: string | number; - filesCount?: number; - foldersCount?: number; - security?: TFolderSecurity; - isGroup?: undefined; - name?: undefined; - isCreateNewItem?: undefined; - onCreateClick?: undefined; - } - | { - email?: undefined; - fileExst?: undefined; - roomType?: undefined; - shared?: boolean; - isOwner?: undefined; - isAdmin?: undefined; - isVisitor?: undefined; - isCollaborator?: undefined; - isRoomAdmin?: undefined; - access?: undefined; - isFolder?: undefined; - parentId?: string | number; - rootFolderType?: string | number; - filesCount?: number; - foldersCount?: number; - security?: TFolderSecurity; - isGroup: true; - name: string; - isCreateNewItem?: undefined; - onCreateClick?: undefined; - } - | { - email?: undefined; - fileExst?: undefined; - roomType?: undefined; - shared?: undefined; - isOwner?: undefined; - isAdmin?: undefined; - isVisitor?: undefined; - isCollaborator?: undefined; - isRoomAdmin?: undefined; - access?: undefined; - isFolder?: undefined; - parentId?: undefined; - rootFolderType?: undefined; - filesCount?: undefined; - foldersCount?: undefined; - security?: undefined; - isGroup?: undefined; - name?: undefined; - isCreateNewItem: boolean; - onCreateClick: VoidFunction; - }; + | TSelectorItemUser + | TSelectorItemFile + | TSelectorItemFolder + | TSelectorItemRoom + | TSelectorItemGroup + | TSelectorItemNew; -export type TSelectorItem = TSelectorItemLogo & - TSelectorItemType & { - key?: string; - id?: string | number; - label: string; - displayName?: string; +export type TSelectorItem = TSelectorItemType & { + label: string; - isSelected?: boolean; - - isDisabled?: boolean; - disabledText?: string; - }; + key?: string; + id?: string | number; + displayName?: string; + isSelected?: boolean; + isDisabled?: boolean; + disabledText?: string; +}; export type Data = { items: TSelectorItem[]; @@ -581,12 +526,3 @@ export interface ItemProps { style: React.CSSProperties; data: Data; } - -export type TDisplayedItem = { - id: string | number; - label: string; - isArrow: boolean; - isList: boolean; - isRoom?: boolean; - listItems?: TBreadCrumb[]; -}; diff --git a/packages/shared/components/selector/sub-components/Body.tsx b/packages/shared/components/selector/sub-components/Body.tsx index 7ebbb2e13f..413fd60c45 100644 --- a/packages/shared/components/selector/sub-components/Body.tsx +++ b/packages/shared/components/selector/sub-components/Body.tsx @@ -184,9 +184,11 @@ const Body = ({ breadCrumbsLoader ) : ( ) ) : null} @@ -232,17 +234,20 @@ const Body = ({ {!!descriptionText && ( {descriptionText} )} - {isMultiSelect && withSelectAll && !isSearch && ( - - )} + {isMultiSelect && withSelectAll && !isSearch ? ( + isLoading ? ( + rowLoader + ) : ( + + ) + ) : null} {bodyHeight && ( { +}: TSelectorBreadCrumbs) => { const [displayedItems, setDisplayedItems] = React.useState( [], ); const onClickItem = React.useCallback( ({ item }: { item: TBreadCrumb }) => { - if (isLoading) return; + if (isBreadCrumbsLoading) return; - onSelectBreadCrumb(item); + onSelectBreadCrumb?.(item); }, - [isLoading, onSelectBreadCrumb], + [isBreadCrumbsLoading, onSelectBreadCrumb], ); const calculateDisplayedItems = React.useCallback( @@ -214,11 +214,12 @@ const BreadCrumbs = ({ noSelect truncate isCurrent={index === displayedItems.length - 1} - isLoading={isLoading || false} + isLoading={isBreadCrumbsLoading} onClick={() => { - if (index === displayedItems.length - 1 || isLoading) return; + if (index === displayedItems.length - 1 || isBreadCrumbsLoading) + return; - onSelectBreadCrumb({ + onSelectBreadCrumb?.({ id: item.id, label: item.label, isRoom: item.isRoom, diff --git a/packages/shared/components/selector/sub-components/Item.tsx b/packages/shared/components/selector/sub-components/Item.tsx index 49352bbbcc..251ba24f3a 100644 --- a/packages/shared/components/selector/sub-components/Item.tsx +++ b/packages/shared/components/selector/sub-components/Item.tsx @@ -82,6 +82,8 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { const { label, + isCreateNewItem, + onCreateClick, avatar, icon, role, @@ -91,14 +93,13 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { email, isGroup, disabledText, - isCreateNewItem, - onCreateClick, } = item; - if (isCreateNewItem) + if (isCreateNewItem) { return ( ); + } const showPlanetIcon = (item.roomType === RoomsType.PublicRoom || @@ -109,7 +110,10 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { const currentRole = role || AvatarRole.user; - const typeLabel = getUserTypeLabel(role, t); + const typeLabel = getUserTypeLabel( + role as "owner" | "admin" | "user" | "collaborator" | "manager", + t, + ); const onChangeAction = () => { onSelect?.(item, false); diff --git a/packages/shared/components/selector/sub-components/SelectAll.tsx b/packages/shared/components/selector/sub-components/SelectAll.tsx index 4d11bf4625..aec954ff31 100644 --- a/packages/shared/components/selector/sub-components/SelectAll.tsx +++ b/packages/shared/components/selector/sub-components/SelectAll.tsx @@ -31,18 +31,22 @@ import { Text } from "../../text"; import { Checkbox } from "../../checkbox"; import { StyledSelectAll } from "../Selector.styled"; -import { SelectAllProps } from "../Selector.types"; +import { TSelectorSelectAll } from "../Selector.types"; const SelectAll = React.memo( ({ - label, - icon, + withSelectAll, + + selectAllLabel, + selectAllIcon, + + isAllChecked, + isAllIndeterminate, + onSelectAll, - isChecked, - isIndeterminate, - isLoading, - rowLoader, - }: SelectAllProps) => { + }: TSelectorSelectAll) => { + if (!withSelectAll) return; + const onClick = (e: React.MouseEvent) => { if (e.target instanceof HTMLElement && e.target.closest(".checkbox")) return; @@ -52,35 +56,28 @@ const SelectAll = React.memo( return ( - {isLoading ? ( - rowLoader - ) : ( - <> - + - - {label} - + + {selectAllLabel} + - - - )} + ); }, diff --git a/packages/shared/selectors/Groups/index.tsx b/packages/shared/selectors/Groups/index.tsx index 8e29ec8bac..d0795434e9 100644 --- a/packages/shared/selectors/Groups/index.tsx +++ b/packages/shared/selectors/Groups/index.tsx @@ -121,7 +121,6 @@ const GroupsSelector = (props: GroupsSelectorProps) => { label: group.name, name: group.name, isGroup: true, - avatar: "", })); if (isFirstLoad.current) { diff --git a/packages/shared/types/index.ts b/packages/shared/types/index.ts index c83f5d33bc..37f4831c10 100644 --- a/packages/shared/types/index.ts +++ b/packages/shared/types/index.ts @@ -55,6 +55,8 @@ export type NonFunctionProperties = Pick< NonFunctionPropertyNames >; +export type MergeTypes = Omit & MergedType; + export type TPathParts = { id: number; title: string; From 2a04f2caf20df058b95890ea257b37719d7d8f2a Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 14:44:43 +0300 Subject: [PATCH 04/33] Shared: fix ts and eslint problems --- .../article-item/ArticleItem.types.ts | 2 +- .../campaigns-banner/CampaignsBanner.test.tsx | 1 + .../components/docspace-logo/DocspaceLogo.tsx | 2 +- .../components/email-input/EmailInput.tsx | 2 +- .../shared/components/filter/Filter.styled.ts | 1 - .../components/input-block/InputBlock.tsx | 1 - .../DesktopDetails/DesktopDetails.type.ts | 2 +- .../section/sub-components/InfoPanel.tsx | 3 +-- .../components/selector/Selector.stories.tsx | 24 +++++-------------- .../components/selector/Selector.types.ts | 3 ++- .../share/sub-components/LinkRow.tsx | 4 ++-- .../social-button/SocialButton.types.ts | 3 +++ .../components/textarea/Textarea.styled.tsx | 2 ++ 13 files changed, 21 insertions(+), 29 deletions(-) diff --git a/packages/shared/components/article-item/ArticleItem.types.ts b/packages/shared/components/article-item/ArticleItem.types.ts index 20ec210f17..77f0b848e2 100644 --- a/packages/shared/components/article-item/ArticleItem.types.ts +++ b/packages/shared/components/article-item/ArticleItem.types.ts @@ -40,7 +40,7 @@ export interface ArticleItemProps { /** Sets the catalog item to display text */ showText?: boolean; /** Invokes a function upon clicking on a catalog item */ - onClick?: (id?: string) => void; + onClick?: (e: React.MouseEvent, id?: string) => void; /** Invokes a function upon dragging and dropping a catalog item */ onDrop?: (id?: string, text?: string) => void; /** Tells when the catalog item should display initial on icon, text should be hidden */ diff --git a/packages/shared/components/campaigns-banner/CampaignsBanner.test.tsx b/packages/shared/components/campaigns-banner/CampaignsBanner.test.tsx index 59cb0ef958..8ecacd0870 100644 --- a/packages/shared/components/campaigns-banner/CampaignsBanner.test.tsx +++ b/packages/shared/components/campaigns-banner/CampaignsBanner.test.tsx @@ -37,6 +37,7 @@ describe("", () => { render( null} diff --git a/packages/shared/components/docspace-logo/DocspaceLogo.tsx b/packages/shared/components/docspace-logo/DocspaceLogo.tsx index f95acf4449..522d56a4c1 100644 --- a/packages/shared/components/docspace-logo/DocspaceLogo.tsx +++ b/packages/shared/components/docspace-logo/DocspaceLogo.tsx @@ -63,7 +63,7 @@ const DocspaceLogo = ({ const logo = getLogoUrl(logoSize, !theme.isBase); return ( - + {logo && ( { const items: TSelectorItem[] = []; - console.log("call"); - items.push({ key: "create_new", id: "create_new_item", @@ -110,21 +109,10 @@ const getItems = (count: number) => { isAdmin: false, isVisitor: false, isCollaborator: false, + isRoomAdmin: false, avatar: "", - }); - } - - for (let i = 0; i < count / 2; i += 1) { - const label = makeName(); - - items.push({ - key: `room_${i}`, - id: `room_${i}`, - label: `${label} ${i}`, - icon: CustomSvgUrl, - shared: false, - isFolder: true, - roomType: RoomsType.CustomRoom, + role: AvatarRole.owner, + hasAvatar: false, }); } diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index e3d14e78c9..1a782e613b 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -451,7 +451,8 @@ export type TSelectorItemFolder = MergeTypes< filesCount: number; foldersCount: number; security: TFolderSecurity; - icon: string; + icon?: string; + avatar?: string; } >; diff --git a/packages/shared/components/share/sub-components/LinkRow.tsx b/packages/shared/components/share/sub-components/LinkRow.tsx index 46931f87c5..6b36ac4b63 100644 --- a/packages/shared/components/share/sub-components/LinkRow.tsx +++ b/packages/shared/components/share/sub-components/LinkRow.tsx @@ -40,13 +40,13 @@ import { ComboBox, ComboBoxSize, TOption } from "../../combobox"; import { IconButton } from "../../icon-button"; import { toastr } from "../../toast"; import { Loader, LoaderTypes } from "../../loader"; +import { Text } from "../../text"; import { StyledLinkRow, StyledSquare } from "../Share.styled"; import { getShareOptions, getAccessOptions } from "../Share.helpers"; import { LinkRowProps } from "../Share.types"; import ExpiredComboBox from "./ExpiredComboBox"; -import { Text } from "@docspace/shared/components/text"; const LinkRow = ({ onAddClick, @@ -149,7 +149,7 @@ const LinkRow = ({ scaledOptions={false} showDisabledItems size={ComboBoxSize.content} - fillIcon={true} + fillIcon modernView type="onlyIcon" isDisabled={isExpiredLink || isLoaded} diff --git a/packages/shared/components/social-button/SocialButton.types.ts b/packages/shared/components/social-button/SocialButton.types.ts index 43ed035e89..49748c75c3 100644 --- a/packages/shared/components/social-button/SocialButton.types.ts +++ b/packages/shared/components/social-button/SocialButton.types.ts @@ -58,5 +58,8 @@ export interface SocialButtonProps extends Partial { /** Button icon */ iconName?: string; + "data-url"?: string; + "data-providername"?: string; + IconComponent?: JSX.ElementType; } diff --git a/packages/shared/components/textarea/Textarea.styled.tsx b/packages/shared/components/textarea/Textarea.styled.tsx index 2e8896eaff..b20c0b858b 100644 --- a/packages/shared/components/textarea/Textarea.styled.tsx +++ b/packages/shared/components/textarea/Textarea.styled.tsx @@ -50,6 +50,8 @@ const ClearScrollbar = ({ hasError?: boolean; heightTextAreaProp?: string; ref?: React.Ref; + isFullHeight?: boolean; + fullHeight?: number; // @ts-expect-error error from custom scrollbar } & ScrollbarProps) => ; From 7c1e90d2e08254840e031591658edd237630713a Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 14:51:06 +0300 Subject: [PATCH 05/33] Shared:Selector: fix stories --- packages/shared/components/selector/Selector.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared/components/selector/Selector.stories.tsx b/packages/shared/components/selector/Selector.stories.tsx index f78cedc972..a78de45e9b 100644 --- a/packages/shared/components/selector/Selector.stories.tsx +++ b/packages/shared/components/selector/Selector.stories.tsx @@ -98,7 +98,7 @@ const getItems = (count: number) => { onCreateClick: () => {}, }); - for (let i = 0; i < count / 2; i += 1) { + for (let i = 0; i < count; i += 1) { const label = makeName(); items.push({ key: `${label} ${i}`, From ed41266757af6637c77fe3add6febf34c266af8b Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 16:33:31 +0300 Subject: [PATCH 06/33] Shared:Selectors: refactoring --- packages/shared/selectors/People/index.tsx | 4 +-- .../selectors/Room/RoomSelector.types.ts | 2 +- .../selectors/Room/RoomSelector.utils.ts | 32 +++++++++++++++++-- packages/shared/selectors/Room/index.tsx | 4 +-- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/packages/shared/selectors/People/index.tsx b/packages/shared/selectors/People/index.tsx index be6b82f925..5dd7eda4f5 100644 --- a/packages/shared/selectors/People/index.tsx +++ b/packages/shared/selectors/People/index.tsx @@ -86,7 +86,7 @@ const toListItem = ( ? t("Common:Disabled") : ""; - const i = { + const i: TSelectorItem = { id: userId, email, avatar: userAvatar, @@ -100,7 +100,7 @@ const toListItem = ( hasAvatar, isDisabled: isInvited || isDisabled, disabledText, - } as TSelectorItem; + }; return i; }; diff --git a/packages/shared/selectors/Room/RoomSelector.types.ts b/packages/shared/selectors/Room/RoomSelector.types.ts index f37de4d20c..bfa38caf8f 100644 --- a/packages/shared/selectors/Room/RoomSelector.types.ts +++ b/packages/shared/selectors/Room/RoomSelector.types.ts @@ -42,7 +42,7 @@ export type RoomSelectorProps = TSelectorHeader & onSubmit: (items: TSelectorItem[]) => void | Promise; roomType?: RoomsType; - excludeItems?: number[]; + excludeItems?: (number | string | undefined)[]; setIsDataReady?: (value: boolean) => void; submitButtonLabel?: string; withSearch?: boolean; diff --git a/packages/shared/selectors/Room/RoomSelector.utils.ts b/packages/shared/selectors/Room/RoomSelector.utils.ts index 2e039dbd86..8cf861784a 100644 --- a/packages/shared/selectors/Room/RoomSelector.utils.ts +++ b/packages/shared/selectors/Room/RoomSelector.utils.ts @@ -25,16 +25,42 @@ // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode import { TRoom } from "../../api/rooms/types"; +import { TSelectorItem } from "../../components/selector"; export const convertToItems = (folders: TRoom[]) => { - const items = folders.map((folder) => { - const { id, title, roomType, logo, shared } = folder; + const items: TSelectorItem[] = folders.map((folder) => { + const { + id, + title, + roomType, + logo, + shared, + parentId, + filesCount, + foldersCount, + rootFolderType, + security, + } = folder; const icon = logo.medium; const iconOriginal = logo.original; const color = logo.color; - return { id, label: title, icon, iconOriginal, color, roomType, shared }; + return { + id, + label: title, + icon, + iconOriginal, + color, + roomType, + shared, + isFolder: true, + parentId, + filesCount, + foldersCount, + rootFolderType, + security, + }; }); return items; diff --git a/packages/shared/selectors/Room/index.tsx b/packages/shared/selectors/Room/index.tsx index 7b4f95cd97..aa73fc615c 100644 --- a/packages/shared/selectors/Room/index.tsx +++ b/packages/shared/selectors/Room/index.tsx @@ -159,10 +159,10 @@ const RoomSelector = ({ if (isFirstLoad) { setTotal(totalCount); - setItems([...rooms] as TSelectorItem[]); + setItems([...rooms]); } else { setItems((prevItems) => { - const newItems = [...rooms] as TSelectorItem[]; + const newItems = [...rooms]; return [...prevItems, ...newItems]; }); From e77b80f2275a19d7ac78506bb7313ef62ba631be Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 17:03:58 +0300 Subject: [PATCH 07/33] Add images for selector input item --- public/images/selector.input.accept.svg | 3 +++ public/images/selector.input.cancel.svg | 10 ++++++++++ 2 files changed, 13 insertions(+) create mode 100644 public/images/selector.input.accept.svg create mode 100644 public/images/selector.input.cancel.svg diff --git a/public/images/selector.input.accept.svg b/public/images/selector.input.accept.svg new file mode 100644 index 0000000000..3d5add8ba3 --- /dev/null +++ b/public/images/selector.input.accept.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/selector.input.cancel.svg b/public/images/selector.input.cancel.svg new file mode 100644 index 0000000000..1c0db52fe6 --- /dev/null +++ b/public/images/selector.input.cancel.svg @@ -0,0 +1,10 @@ + + + + + + + + + + From 73fd154d6d674c5c877cdfe5168eac18f59cec96 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 17:04:16 +0300 Subject: [PATCH 08/33] Shared:Storybook: add current color scheme --- packages/shared/.storybook/globals/theme-wrapper.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/shared/.storybook/globals/theme-wrapper.js b/packages/shared/.storybook/globals/theme-wrapper.js index 168b4e00d4..099b86f2a1 100644 --- a/packages/shared/.storybook/globals/theme-wrapper.js +++ b/packages/shared/.storybook/globals/theme-wrapper.js @@ -2,7 +2,16 @@ import PropTypes from "prop-types"; import { ThemeProvider } from "../../components/theme-provider"; const ThemeWrapper = ({ theme, children }) => { - return {children}; + return ( + + {children} + + ); }; ThemeWrapper.propTypes = { From 679fbbdc26a2fb9319e27c9fdde13b504de1b9e9 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 17:04:37 +0300 Subject: [PATCH 09/33] Shared:Components:SelectorAddButton: add action state --- .../SelectorAddButton.styled.ts | 52 ++++++++++++++++++- .../selector-add-button/SelectorAddButton.tsx | 2 + .../SelectorAddButton.types.tsx | 2 + 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/shared/components/selector-add-button/SelectorAddButton.styled.ts b/packages/shared/components/selector-add-button/SelectorAddButton.styled.ts index f1da13cbad..d7d445c549 100644 --- a/packages/shared/components/selector-add-button/SelectorAddButton.styled.ts +++ b/packages/shared/components/selector-add-button/SelectorAddButton.styled.ts @@ -27,7 +27,7 @@ import styled, { css } from "styled-components"; import { Base } from "../../themes"; -const StyledButton = styled.div<{ isDisabled?: boolean }>` +const StyledButton = styled.div<{ isDisabled?: boolean; isAction?: boolean }>` display: inline-block; background: ${(props) => props.theme.selectorAddButton.background}; border: ${(props) => props.theme.selectorAddButton.border}; @@ -79,6 +79,56 @@ const StyledButton = styled.div<{ isDisabled?: boolean }>` } -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + + ${(props) => + props.isAction && + css` + // convert into 0.1 opacity + background-color: ${props.theme.currentColorScheme?.main.accent}1A; + + svg { + path { + ${!props.isDisabled && + css` + fill: ${props.theme.currentColorScheme?.main.accent}; + `} + } + } + + :hover { + background-color: ${props.theme.currentColorScheme?.main.accent}1A; + + svg { + path { + ${!props.isDisabled && + css` + fill: ${props.theme.currentColorScheme?.main.accent}; + opacity: 0.85; + `} + } + } + } + + :active { + background-color: ${props.theme.currentColorScheme?.main.accent}1A; + svg { + path { + ${!props.isDisabled && + css` + fill: ${props.theme.currentColorScheme?.main.accent}; + opacity: 1; + filter: ${props.theme.isBase + ? "brightness(90%)" + : "brightness(82%)"}; + `} + } + } + } + + div { + opacity: 1; + } + `} `; StyledButton.defaultProps = { theme: Base }; diff --git a/packages/shared/components/selector-add-button/SelectorAddButton.tsx b/packages/shared/components/selector-add-button/SelectorAddButton.tsx index 1914c20938..a6a90c4370 100644 --- a/packages/shared/components/selector-add-button/SelectorAddButton.tsx +++ b/packages/shared/components/selector-add-button/SelectorAddButton.tsx @@ -36,6 +36,7 @@ import { SelectorAddButtonProps } from "./SelectorAddButton.types"; const SelectorAddButton = (props: SelectorAddButtonProps) => { const { isDisabled = false, + isAction, title, className, id, @@ -52,6 +53,7 @@ const SelectorAddButton = (props: SelectorAddButtonProps) => { Date: Wed, 22 May 2024 17:04:56 +0300 Subject: [PATCH 10/33] Shared:Components:Selector: add NewItem and InputItem --- .../components/selector/Selector.stories.tsx | 10 +++ .../components/selector/Selector.styled.ts | 32 +++++++ .../components/selector/Selector.types.ts | 17 +++- .../selector/sub-components/InputItem.tsx | 86 +++++++++++++++++++ .../selector/sub-components/Item.tsx | 16 ++++ .../selector/sub-components/NewItem.tsx | 2 +- packages/shared/themes/base.ts | 3 + packages/shared/themes/dark.ts | 3 + 8 files changed, 167 insertions(+), 2 deletions(-) diff --git a/packages/shared/components/selector/Selector.stories.tsx b/packages/shared/components/selector/Selector.stories.tsx index a78de45e9b..c1c8906d19 100644 --- a/packages/shared/components/selector/Selector.stories.tsx +++ b/packages/shared/components/selector/Selector.stories.tsx @@ -98,6 +98,16 @@ const getItems = (count: number) => { onCreateClick: () => {}, }); + items.push({ + key: "input_item", + id: "input_item", + label: "", + isInputItem: true, + defaultInputValue: "New folder", + onAcceptInput: () => {}, + onCancelInput: () => {}, + }); + for (let i = 0; i < count; i += 1) { const label = makeName(); items.push({ diff --git a/packages/shared/components/selector/Selector.styled.ts b/packages/shared/components/selector/Selector.styled.ts index 477725c895..296696217c 100644 --- a/packages/shared/components/selector/Selector.styled.ts +++ b/packages/shared/components/selector/Selector.styled.ts @@ -445,6 +445,36 @@ const StyledInfo = styled.div` } `; +const StyledInputWrapper = styled.div` + width: 32px; + height: 32px; + + margin-inline-start: 8px; + + border: 1px solid ${(props) => props.theme.selector.item.inputButtonBorder}; + border-radius: 3px; + + display: flex; + align-items: center; + justify-content: center; + + box-sizing: border-box; + + :hover { + div { + cursor: pointer; + } + cursor: pointer; + + border-color: ${(props) => + props.theme.selector.item.inputButtonBorderHover}; + + path { + fill: ${(props) => props.theme.selector.item.inputButtonBorderHover}; + } + } +`; + StyledSelector.defaultProps = { theme: Base }; StyledHeader.defaultProps = { theme: Base }; StyledBody.defaultProps = { theme: Base }; @@ -454,6 +484,7 @@ StyledEmptyScreen.defaultProps = { theme: Base }; StyledArrowRightSvg.defaultProps = { theme: Base }; StyledComboBox.defaultProps = { theme: Base }; StyledInfo.defaultProps = { theme: Base }; +StyledInputWrapper.defaultProps = { theme: Base }; export { StyledSelector, @@ -473,4 +504,5 @@ export { StyledTabs, StyledInfo, StyledAccessSelector, + StyledInputWrapper, }; diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index 1a782e613b..074eb7950e 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -412,6 +412,10 @@ type TSelectorItemEmpty = { name?: undefined; isCreateNewItem?: undefined; onCreateClick?: undefined; + isInputItem?: undefined; + defaultInputValue?: undefined; + onAcceptInput?: undefined; + onCancelInput?: undefined; }; export type TSelectorItemUser = MergeTypes< @@ -489,13 +493,24 @@ export type TSelectorItemNew = MergeTypes< } >; +export type TSelectorItemInput = MergeTypes< + TSelectorItemEmpty, + { + isInputItem: boolean; + defaultInputValue: string; + onAcceptInput: (value: string) => void; + onCancelInput: VoidFunction; + } +>; + type TSelectorItemType = | TSelectorItemUser | TSelectorItemFile | TSelectorItemFolder | TSelectorItemRoom | TSelectorItemGroup - | TSelectorItemNew; + | TSelectorItemNew + | TSelectorItemInput; export type TSelectorItem = TSelectorItemType & { label: string; diff --git a/packages/shared/components/selector/sub-components/InputItem.tsx b/packages/shared/components/selector/sub-components/InputItem.tsx index e69de29bb2..90d5cb8bb9 100644 --- a/packages/shared/components/selector/sub-components/InputItem.tsx +++ b/packages/shared/components/selector/sub-components/InputItem.tsx @@ -0,0 +1,86 @@ +// (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 AcceptIconSvgUrl from "PUBLIC_DIR/images/selector.input.accept.svg?url"; +import CancelIconSvgUrl from "PUBLIC_DIR/images/selector.input.cancel.svg?url"; + +import { InputSize, InputType, TextInput } from "../../text-input"; +import { IconButton } from "../../icon-button"; + +import { StyledInputWrapper, StyledItem } from "../Selector.styled"; + +const InputItem = ({ + defaultInputValue, + onAcceptInput, + onCancelInput, + style, +}: { + defaultInputValue: string; + onAcceptInput: (value: string) => void; + onCancelInput: VoidFunction; + style: React.CSSProperties; +}) => { + const [value, setValue] = React.useState(""); + + const onAcceptInputAction = () => { + onAcceptInput(value); + }; + + const onChange = (e: React.ChangeEvent) => { + const newVal = e.target.value; + + setValue(newVal); + }; + + return ( + + + + + + + + + + ); +}; + +export default InputItem; diff --git a/packages/shared/components/selector/sub-components/Item.tsx b/packages/shared/components/selector/sub-components/Item.tsx index 251ba24f3a..6d4075168e 100644 --- a/packages/shared/components/selector/sub-components/Item.tsx +++ b/packages/shared/components/selector/sub-components/Item.tsx @@ -37,6 +37,7 @@ import { StyledItem } from "../Selector.styled"; import { ItemProps, Data, TSelectorItem } from "../Selector.types"; import { RoomsType } from "../../../enums"; import NewItem from "./NewItem"; +import InputItem from "./InputItem"; const compareFunction = (prevProps: ItemProps, nextProps: ItemProps) => { const prevData = prevProps.data; @@ -84,6 +85,10 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { label, isCreateNewItem, onCreateClick, + isInputItem, + defaultInputValue, + onAcceptInput, + onCancelInput, avatar, icon, role, @@ -95,6 +100,17 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { disabledText, } = item; + if (isInputItem) { + return ( + + ); + } + if (isCreateNewItem) { return ( diff --git a/packages/shared/components/selector/sub-components/NewItem.tsx b/packages/shared/components/selector/sub-components/NewItem.tsx index dd6a68626c..e396cf56c2 100644 --- a/packages/shared/components/selector/sub-components/NewItem.tsx +++ b/packages/shared/components/selector/sub-components/NewItem.tsx @@ -48,7 +48,7 @@ const NewItem = ({ isMultiSelect={false} noHover > - + { item: { hoverBackground: grayLight, selectedBackground: lightHover, + + inputButtonBorder: "#D0D5DA", + inputButtonBorderHover: grayMain, }, emptyScreen: { diff --git a/packages/shared/themes/dark.ts b/packages/shared/themes/dark.ts index 363891c81f..f5b0f55f32 100644 --- a/packages/shared/themes/dark.ts +++ b/packages/shared/themes/dark.ts @@ -2370,6 +2370,9 @@ const Dark: TTheme = { item: { hoverBackground: "#3d3d3d", selectedBackground: "#3d3d3d", + + inputButtonBorder: "#474747", + inputButtonBorderHover: grayMaxLight, }, emptyScreen: { From 1cc2500a63e4e011e4c43fb0b6f70308fe6421ae Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Wed, 22 May 2024 18:09:57 +0300 Subject: [PATCH 11/33] Shared:Components:Selector: add icon for input item --- .../components/selector/Selector.stories.tsx | 2 ++ .../components/selector/Selector.styled.ts | 4 +++ .../components/selector/Selector.types.ts | 3 ++ .../selector/sub-components/InputItem.tsx | 35 +++++++++++++++++-- .../selector/sub-components/Item.tsx | 2 ++ 5 files changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/shared/components/selector/Selector.stories.tsx b/packages/shared/components/selector/Selector.stories.tsx index c1c8906d19..9b1c183eab 100644 --- a/packages/shared/components/selector/Selector.stories.tsx +++ b/packages/shared/components/selector/Selector.stories.tsx @@ -29,6 +29,7 @@ import styled from "styled-components"; import { Meta, StoryObj } from "@storybook/react"; import ArchiveSvgUrl from "PUBLIC_DIR/images/room.archive.svg?url"; +import FolderSvgUrl from "PUBLIC_DIR/images/icons/32/folder.svg?url"; import EmptyScreenFilter from "PUBLIC_DIR/images/empty_screen_filter.png"; import { AvatarRole } from "../avatar"; @@ -103,6 +104,7 @@ const getItems = (count: number) => { id: "input_item", label: "", isInputItem: true, + icon: FolderSvgUrl, defaultInputValue: "New folder", onAcceptInput: () => {}, onCancelInput: () => {}, diff --git a/packages/shared/components/selector/Selector.styled.ts b/packages/shared/components/selector/Selector.styled.ts index 296696217c..17458f8d43 100644 --- a/packages/shared/components/selector/Selector.styled.ts +++ b/packages/shared/components/selector/Selector.styled.ts @@ -222,6 +222,10 @@ const StyledItem = styled.div<{ `} } + .input-component { + margin-inline-start: 8px; + } + .checkbox { svg { margin-inline-end: 0px; diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index 074eb7950e..db2c29f286 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -498,6 +498,9 @@ export type TSelectorItemInput = MergeTypes< { isInputItem: boolean; defaultInputValue: string; + icon?: string; + color?: string; + onAcceptInput: (value: string) => void; onCancelInput: VoidFunction; } diff --git a/packages/shared/components/selector/sub-components/InputItem.tsx b/packages/shared/components/selector/sub-components/InputItem.tsx index 90d5cb8bb9..db4a8661ef 100644 --- a/packages/shared/components/selector/sub-components/InputItem.tsx +++ b/packages/shared/components/selector/sub-components/InputItem.tsx @@ -31,6 +31,7 @@ import CancelIconSvgUrl from "PUBLIC_DIR/images/selector.input.cancel.svg?url"; import { InputSize, InputType, TextInput } from "../../text-input"; import { IconButton } from "../../icon-button"; +import { RoomIcon } from "../../room-icon"; import { StyledInputWrapper, StyledItem } from "../Selector.styled"; @@ -39,13 +40,20 @@ const InputItem = ({ onAcceptInput, onCancelInput, style, + + color, + icon, }: { defaultInputValue: string; onAcceptInput: (value: string) => void; onCancelInput: VoidFunction; style: React.CSSProperties; + + color?: string; + icon?: string; }) => { - const [value, setValue] = React.useState(""); + const [value, setValue] = React.useState(defaultInputValue); + const inputRef = React.useRef(null); const onAcceptInputAction = () => { onAcceptInput(value); @@ -57,6 +65,13 @@ const InputItem = ({ setValue(newVal); }; + React.useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, []); + return ( + {color ? ( + + ) : icon ? ( + + ) : null} diff --git a/packages/shared/components/selector/sub-components/Item.tsx b/packages/shared/components/selector/sub-components/Item.tsx index 6d4075168e..08dc90cfb5 100644 --- a/packages/shared/components/selector/sub-components/Item.tsx +++ b/packages/shared/components/selector/sub-components/Item.tsx @@ -107,6 +107,8 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { onAcceptInput={onAcceptInput} onCancelInput={onCancelInput} style={style} + color={color} + icon={icon} /> ); } From 594e3eba89e715ce726ed04b92a5e2f68ea09a39 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Thu, 23 May 2024 14:44:57 +0300 Subject: [PATCH 12/33] Shared:Selectors:Files: add create folder --- .../src/components/FilesSelector/index.tsx | 27 +++++-- packages/shared/api/files/index.ts | 5 +- .../selectors/Files/FilesSelector.types.ts | 4 + .../selectors/Files/hooks/useFilesHelper.ts | 81 ++++++++++++++++++- .../selectors/Files/hooks/useSocketHelper.ts | 26 +++++- packages/shared/selectors/Files/index.tsx | 3 + 6 files changed, 129 insertions(+), 17 deletions(-) diff --git a/packages/client/src/components/FilesSelector/index.tsx b/packages/client/src/components/FilesSelector/index.tsx index 9e21878dd2..0ce43b4598 100644 --- a/packages/client/src/components/FilesSelector/index.tsx +++ b/packages/client/src/components/FilesSelector/index.tsx @@ -34,6 +34,7 @@ import FilesSelector from "@docspace/shared/selectors/Files"; import { toastr } from "@docspace/shared/components/toast"; import { SettingsStore } from "@docspace/shared/store/SettingsStore"; import { + TFile, TFileSecurity, TFolder, TFolderSecurity, @@ -41,7 +42,7 @@ import { import { TBreadCrumb } from "@docspace/shared/components/selector/Selector.types"; import { TData } from "@docspace/shared/components/toast/Toast.type"; import { TSelectedFileInfo } from "@docspace/shared/selectors/Files/FilesSelector.types"; -import { TRoomSecurity } from "@docspace/shared/api/rooms/types"; +import { TRoom, TRoomSecurity } from "@docspace/shared/api/rooms/types"; import { TTranslation } from "@docspace/shared/types"; import SelectedFolderStore from "SRC_DIR/store/SelectedFolderStore"; @@ -55,6 +56,8 @@ import InfoPanelStore from "SRC_DIR/store/InfoPanelStore"; import { FilesSelectorProps } from "./FilesSelector.types"; import { getAcceptButtonLabel, getHeaderLabel, getIsDisabled } from "./utils"; +const disabledItems: (string | number)[] = []; + const FilesSelectorWrapper = ({ isPanelVisible = false, // withoutImmediatelyClose = false, @@ -87,7 +90,7 @@ const FilesSelectorWrapper = ({ treeFolders, selection, - disabledItems, + // disabledItems, setConflictDialogData, checkFileConflicts, itemOperationToFolder, @@ -169,8 +172,10 @@ const FilesSelectorWrapper = ({ onCloseAction(); }; - const getFilesArchiveError = (name: string) => - t("Common:ArchivedRoomAction", { name }); + const getFilesArchiveError = React.useCallback( + (name: string) => t("Common:ArchivedRoomAction", { name }), + [t], + ); const onAccept = async ( selectedItemId: string | number | undefined, @@ -375,6 +380,9 @@ const FilesSelectorWrapper = ({ isMove || isCopy || isRestore ? "select-file-modal-cancel" : "" } getFilesArchiveError={getFilesArchiveError} + withCreateFolder={ + (isMove || isCopy || isRestore || isRestoreAll) ?? false + } /> ); }; @@ -471,10 +479,13 @@ export default inject( ? selections : selections.filter((f) => f && !f?.isEditing); - const disabledItems: (string | number)[] = []; - - selectionsWithoutEditing.forEach((item) => { - if ((item?.isFolder || item?.parentId) && item?.id) { + selectionsWithoutEditing.forEach((item: TFile | TFolder | TRoom) => { + if ( + (("isFolder" in item && item?.isFolder) || + ("parentId" in item && item?.parentId)) && + item?.id && + !disabledItems.includes(item.id) + ) { disabledItems.push(item.id); } }); diff --git a/packages/shared/api/files/index.ts b/packages/shared/api/files/index.ts index 1aa3136fd4..2addde50f4 100644 --- a/packages/shared/api/files/index.ts +++ b/packages/shared/api/files/index.ts @@ -337,7 +337,10 @@ export async function getTrashFolderList() { // return request(options); // } -export async function createFolder(parentFolderId: number, title: string) { +export async function createFolder( + parentFolderId: number | string, + title: string, +) { const data = { title }; const options: AxiosRequestConfig = { method: "post", diff --git a/packages/shared/selectors/Files/FilesSelector.types.ts b/packages/shared/selectors/Files/FilesSelector.types.ts index cf5165b351..671a362789 100644 --- a/packages/shared/selectors/Files/FilesSelector.types.ts +++ b/packages/shared/selectors/Files/FilesSelector.types.ts @@ -62,6 +62,7 @@ export type UseSocketHelperProps = { disabledItems: (string | number)[]; filterParam?: string; getIcon: (fileExst: string) => string; + withCreateFolder: boolean; }; export type UseRoomsHelperProps = { @@ -119,6 +120,7 @@ export type UseFilesHelpersProps = { getFilesArchiveError: (name: string) => string; isInit: boolean; setIsFirstLoad: (value: boolean) => void; + withCreateFolder: boolean; }; export type TSelectedFileInfo = { @@ -199,4 +201,6 @@ export type FilesSelectorProps = ( isPanelVisible: boolean; currentDeviceType: DeviceType; getFilesArchiveError: (name: string) => string; + + withCreateFolder: boolean; }; diff --git a/packages/shared/selectors/Files/hooks/useFilesHelper.ts b/packages/shared/selectors/Files/hooks/useFilesHelper.ts index 4b58368c66..1a71902e6b 100644 --- a/packages/shared/selectors/Files/hooks/useFilesHelper.ts +++ b/packages/shared/selectors/Files/hooks/useFilesHelper.ts @@ -25,8 +25,16 @@ // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode import React from "react"; +import { useTranslation } from "react-i18next"; -import { getFolder, getFolderInfo, getSettingsFiles } from "../../../api/files"; +import FolderSvgUrl from "PUBLIC_DIR/images/icons/32/folder.svg?url"; + +import { + createFolder, + getFolder, + getFolderInfo, + getSettingsFiles, +} from "../../../api/files"; import FilesFilter from "../../../api/files/filter"; import { ApplyFilterOption, @@ -75,7 +83,10 @@ const useFilesHelper = ({ isInit, setIsInit, setIsFirstLoad, + withCreateFolder, }: UseFilesHelpersProps) => { + const { t } = useTranslation(["Common"]); + const requestRunning = React.useRef(false); const initRef = React.useRef(isInit); const firstLoadRef = React.useRef(isFirstLoad); @@ -93,6 +104,55 @@ const useFilesHelper = ({ initRef.current = isInit; }, [isInit]); + const onCancelInput = React.useCallback(() => { + if (!withCreateFolder) return; + + setItems((value) => { + if (!value[1].isInputItem) return value; + + const newValue = [...value]; + + newValue.splice(1, 1); + + return newValue; + }); + }, [setItems, withCreateFolder]); + + const onAcceptInput = React.useCallback( + async (value: string) => { + if (!withCreateFolder || !selectedItemId) return; + + await createFolder(selectedItemId, value); + + onCancelInput(); + }, + [onCancelInput, selectedItemId, withCreateFolder], + ); + + const addInputItem = React.useCallback(() => { + if (!withCreateFolder) return; + + const inputItem: TSelectorItem = { + label: "", + id: "new-folder-input", + isInputItem: true, + onAcceptInput, + onCancelInput, + defaultInputValue: t("NewFolder"), + icon: FolderSvgUrl, + }; + + setItems((value) => { + if (value[1].isInputItem) return value; + + const newValue = [...value]; + + newValue.splice(1, 0, inputItem); + + return newValue; + }); + }, [onAcceptInput, onCancelInput, setItems, t, withCreateFolder]); + const getFileList = React.useCallback( async (startIndex: number) => { if (requestRunning.current) return; @@ -310,7 +370,18 @@ const useFilesHelper = ({ } if (firstLoadRef.current || startIndex === 0) { - setTotal(total); + if (withCreateFolder) { + setTotal(total + 1); + itemList.unshift({ + isCreateNewItem: true, + label: "New folder", + id: "create-folder-item", + key: "create-folder-item", + onCreateClick: addInputItem, + }); + } else { + setTotal(total); + } setItems(itemList); } else { setItems((prevState) => { @@ -361,8 +432,8 @@ const useFilesHelper = ({ setIsNextPageLoading, searchValue, filterParam, - isUserOnly, selectedItemId, + isUserOnly, getRootData, setSelectedItemSecurity, getIcon, @@ -380,8 +451,10 @@ const useFilesHelper = ({ setIsBreadCrumbsLoading, roomsFolderId, setIsSelectedParentFolder, - setTotal, + withCreateFolder, setItems, + setTotal, + addInputItem, rootThirdPartyId, ], ); diff --git a/packages/shared/selectors/Files/hooks/useSocketHelper.ts b/packages/shared/selectors/Files/hooks/useSocketHelper.ts index 9439f064b0..e9784dc369 100644 --- a/packages/shared/selectors/Files/hooks/useSocketHelper.ts +++ b/packages/shared/selectors/Files/hooks/useSocketHelper.ts @@ -43,6 +43,7 @@ const useSocketHelper = ({ socketSubscribers, disabledItems, filterParam, + withCreateFolder, setItems, setBreadCrumbs, setTotal, @@ -109,9 +110,9 @@ const useSocketHelper = ({ if (opt?.type === "file" && "folderId" in data) { item = convertFilesToItems([data], getIcon, filterParam)[0]; - } else if (opt?.type === "folder" && "roomType" in data) { + } else if (opt?.type === "folder" && !("folderId" in data)) { item = - data.roomType && "tags" in data + "roomType" in data && data.roomType && "tags" in data ? convertRoomsToItems([data])[0] : convertFoldersToItems([data], disabledItems, filterParam)[0]; } @@ -122,6 +123,18 @@ const useSocketHelper = ({ if (opt.type === "folder") { setTotal((v) => v + 1); + if (withCreateFolder) { + const newValue = [...value]; + + let idx = 1; + + if (value[1].isInputItem) idx = 2; + + newValue.splice(idx, 0, item); + + return newValue; + } + return [item, ...value]; } @@ -129,7 +142,12 @@ const useSocketHelper = ({ let idx = 0; for (let i = 0; i < value.length - 1; i += 1) { - if (!value[i].isFolder) break; + if ( + !value[i].isFolder && + !value[i].isCreateNewItem && + !value[i].isInputItem + ) + break; idx = i + 1; } @@ -146,7 +164,7 @@ const useSocketHelper = ({ return value; }); }, - [disabledItems, filterParam, getIcon, setItems, setTotal], + [disabledItems, filterParam, getIcon, setItems, setTotal, withCreateFolder], ); const updateItem = React.useCallback( diff --git a/packages/shared/selectors/Files/index.tsx b/packages/shared/selectors/Files/index.tsx index 713334d467..ba93b305ef 100644 --- a/packages/shared/selectors/Files/index.tsx +++ b/packages/shared/selectors/Files/index.tsx @@ -107,6 +107,7 @@ const FilesSelector = ({ withBreadCrumbs: withBreadCrumbsProp, filesSettings, cancelButtonLabel, + withCreateFolder, }: FilesSelectorProps) => { const theme = useTheme(); const { t } = useTranslation(["Common"]); @@ -148,6 +149,7 @@ const FilesSelector = ({ socketSubscribers, disabledItems, filterParam, + withCreateFolder, getIcon, setItems, setBreadCrumbs, @@ -224,6 +226,7 @@ const FilesSelector = ({ getFilesArchiveError, isInit, setIsInit, + withCreateFolder, }); const onSelectAction = React.useCallback( From 6ff08972c2e54298e11d84a50c762bd332028180 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Thu, 23 May 2024 17:19:28 +0300 Subject: [PATCH 13/33] Shared:Selectors:Files: open new folder after create --- .../selectors/Files/FilesSelector.types.ts | 3 +- .../selectors/Files/hooks/useFilesHelper.ts | 21 ++- .../selectors/Files/hooks/useSocketHelper.ts | 3 +- packages/shared/selectors/Files/index.tsx | 133 +++++++++--------- 4 files changed, 86 insertions(+), 74 deletions(-) diff --git a/packages/shared/selectors/Files/FilesSelector.types.ts b/packages/shared/selectors/Files/FilesSelector.types.ts index 671a362789..943b15a599 100644 --- a/packages/shared/selectors/Files/FilesSelector.types.ts +++ b/packages/shared/selectors/Files/FilesSelector.types.ts @@ -86,7 +86,7 @@ export type UseRoomsHelperProps = { export type UseFilesHelpersProps = { roomsFolderId?: number; - setBreadCrumbs: (items: TBreadCrumb[]) => void; + setBreadCrumbs: React.Dispatch>; setIsBreadCrumbsLoading: (value: boolean) => void; setIsSelectedParentFolder: (value: boolean) => void; setIsNextPageLoading: (value: boolean) => void; @@ -121,6 +121,7 @@ export type UseFilesHelpersProps = { isInit: boolean; setIsFirstLoad: (value: boolean) => void; withCreateFolder: boolean; + setSelectedItemId: (value: number | string) => void; }; export type TSelectedFileInfo = { diff --git a/packages/shared/selectors/Files/hooks/useFilesHelper.ts b/packages/shared/selectors/Files/hooks/useFilesHelper.ts index 1a71902e6b..777ab91e3e 100644 --- a/packages/shared/selectors/Files/hooks/useFilesHelper.ts +++ b/packages/shared/selectors/Files/hooks/useFilesHelper.ts @@ -84,6 +84,7 @@ const useFilesHelper = ({ setIsInit, setIsFirstLoad, withCreateFolder, + setSelectedItemId, }: UseFilesHelpersProps) => { const { t } = useTranslation(["Common"]); @@ -108,11 +109,15 @@ const useFilesHelper = ({ if (!withCreateFolder) return; setItems((value) => { - if (!value[1].isInputItem) return value; + if (!value[1]?.isInputItem && !value[0]?.isInputItem) return value; + + let idx = 1; + + if (value[0].isInputItem) idx = 0; const newValue = [...value]; - newValue.splice(1, 1); + newValue.splice(idx, 1); return newValue; }); @@ -122,11 +127,15 @@ const useFilesHelper = ({ async (value: string) => { if (!withCreateFolder || !selectedItemId) return; - await createFolder(selectedItemId, value); + const folder = await createFolder(selectedItemId, value); - onCancelInput(); + setBreadCrumbs((val) => { + return [...val, { id: folder.id, label: folder.title }]; + }); + + setSelectedItemId(folder.id); }, - [onCancelInput, selectedItemId, withCreateFolder], + [withCreateFolder, selectedItemId, setBreadCrumbs, setSelectedItemId], ); const addInputItem = React.useCallback(() => { @@ -143,7 +152,7 @@ const useFilesHelper = ({ }; setItems((value) => { - if (value[1].isInputItem) return value; + if (value[1]?.isInputItem || value[0]?.isInputItem) return value; const newValue = [...value]; diff --git a/packages/shared/selectors/Files/hooks/useSocketHelper.ts b/packages/shared/selectors/Files/hooks/useSocketHelper.ts index e9784dc369..46057c9bfa 100644 --- a/packages/shared/selectors/Files/hooks/useSocketHelper.ts +++ b/packages/shared/selectors/Files/hooks/useSocketHelper.ts @@ -126,8 +126,9 @@ const useSocketHelper = ({ if (withCreateFolder) { const newValue = [...value]; - let idx = 1; + let idx = 0; + if (value[0].isInputItem) idx = 1; if (value[1].isInputItem) idx = 2; newValue.splice(idx, 0, item); diff --git a/packages/shared/selectors/Files/index.tsx b/packages/shared/selectors/Files/index.tsx index ba93b305ef..c301cf5fd3 100644 --- a/packages/shared/selectors/Files/index.tsx +++ b/packages/shared/selectors/Files/index.tsx @@ -197,6 +197,72 @@ const FilesSelector = ({ setIsFirstLoad, }); + const onClickBreadCrumb = React.useCallback( + (item: TBreadCrumb) => { + if (!isFirstLoad) { + afterSearch.current = false; + setSearchValue(""); + setIsFirstLoad(true); + if (+item.id === 0) { + setSelectedItemSecurity(undefined); + setSelectedItemType(undefined); + getRootData(); + } else { + setItems([]); + + setBreadCrumbs((bc) => { + const idx = bc.findIndex( + (value) => value.id.toString() === item.id.toString(), + ); + + const maxLength = bc.length - 1; + let foundParentId = false; + let currentFolderIndex = -1; + + const newBreadCrumbs = bc.map((i, index) => { + if (!foundParentId) { + currentFolderIndex = disabledItems.findIndex( + (id) => id === i?.id, + ); + } + + if (index !== maxLength && currentFolderIndex !== -1) { + foundParentId = true; + if (!isSelectedParentFolder) setIsSelectedParentFolder(true); + } + + if ( + index === maxLength && + !foundParentId && + isSelectedParentFolder + ) + setIsSelectedParentFolder(false); + + return { ...i }; + }); + + newBreadCrumbs.splice(idx + 1, newBreadCrumbs.length - idx - 1); + return newBreadCrumbs; + }); + + setSelectedItemId(item.id); + if (item.isRoom) { + setSelectedItemType("rooms"); + } else { + setSelectedItemType("files"); + } + } + } + }, + [ + disabledItems, + getRootData, + isFirstLoad, + isSelectedParentFolder, + setIsFirstLoad, + ], + ); + const { getFileList } = useFilesHelper({ setIsBreadCrumbsLoading, setBreadCrumbs, @@ -227,6 +293,7 @@ const FilesSelector = ({ isInit, setIsInit, withCreateFolder, + setSelectedItemId, }); const onSelectAction = React.useCallback( @@ -326,72 +393,6 @@ const FilesSelector = ({ setIsFirstLoad, ]); - const onClickBreadCrumb = React.useCallback( - (item: TBreadCrumb) => { - if (!isFirstLoad) { - afterSearch.current = false; - setSearchValue(""); - setIsFirstLoad(true); - if (+item.id === 0) { - setSelectedItemSecurity(undefined); - setSelectedItemType(undefined); - getRootData(); - } else { - setItems([]); - - setBreadCrumbs((bc) => { - const idx = bc.findIndex( - (value) => value.id.toString() === item.id.toString(), - ); - - const maxLength = bc.length - 1; - let foundParentId = false; - let currentFolderIndex = -1; - - const newBreadCrumbs = bc.map((i, index) => { - if (!foundParentId) { - currentFolderIndex = disabledItems.findIndex( - (id) => id === i?.id, - ); - } - - if (index !== maxLength && currentFolderIndex !== -1) { - foundParentId = true; - if (!isSelectedParentFolder) setIsSelectedParentFolder(true); - } - - if ( - index === maxLength && - !foundParentId && - isSelectedParentFolder - ) - setIsSelectedParentFolder(false); - - return { ...i }; - }); - - newBreadCrumbs.splice(idx + 1, newBreadCrumbs.length - idx - 1); - return newBreadCrumbs; - }); - - setSelectedItemId(item.id); - if (item.isRoom) { - setSelectedItemType("rooms"); - } else { - setSelectedItemType("files"); - } - } - } - }, - [ - disabledItems, - getRootData, - isFirstLoad, - isSelectedParentFolder, - setIsFirstLoad, - ], - ); - const onSearchAction = React.useCallback( (value: string, callback?: Function) => { setIsFirstLoad(true); From 9af68dbd947adbcf3e4173ec3ed8f04684961d5b Mon Sep 17 00:00:00 2001 From: namushka Date: Fri, 24 May 2024 14:02:49 +0300 Subject: [PATCH 14/33] updated icons for RoomIcon component --- public/images/icons/32/room/custom.svg | 6 +++--- public/images/icons/32/room/editing.svg | 6 +++--- public/images/icons/32/room/form.svg | 2 +- public/images/icons/32/room/public.svg | 9 +-------- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/public/images/icons/32/room/custom.svg b/public/images/icons/32/room/custom.svg index 570fc932d0..3033654a3d 100644 --- a/public/images/icons/32/room/custom.svg +++ b/public/images/icons/32/room/custom.svg @@ -1,10 +1,10 @@ - + + - - + diff --git a/public/images/icons/32/room/editing.svg b/public/images/icons/32/room/editing.svg index ca6b8d9045..a01fae0d38 100644 --- a/public/images/icons/32/room/editing.svg +++ b/public/images/icons/32/room/editing.svg @@ -1,10 +1,10 @@ - + + - - + diff --git a/public/images/icons/32/room/form.svg b/public/images/icons/32/room/form.svg index 05dda31953..d036241507 100644 --- a/public/images/icons/32/room/form.svg +++ b/public/images/icons/32/room/form.svg @@ -1,5 +1,5 @@ + - diff --git a/public/images/icons/32/room/public.svg b/public/images/icons/32/room/public.svg index c69a5473f2..d61c035682 100644 --- a/public/images/icons/32/room/public.svg +++ b/public/images/icons/32/room/public.svg @@ -1,11 +1,4 @@ - - + - - - - - - From 99b7339a835517fd421d731162c7da9696fab976 Mon Sep 17 00:00:00 2001 From: namushka Date: Fri, 24 May 2024 14:03:27 +0300 Subject: [PATCH 15/33] added hover effects for RoomType ListItem and DropdownButton components --- .../sub-components/RoomType.js | 30 ++++++++++++++++--- packages/shared/themes/base.ts | 3 ++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js index 5701347ad6..63614361be 100644 --- a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js +++ b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js @@ -98,10 +98,21 @@ const StyledRoomType = styled.div` const StyledListItem = styled(StyledRoomType)` background-color: ${(props) => props.theme.createEditRoomDialog.roomType.listItem.background}; + + &:hover { + background-color: ${(props) => + props.theme.createEditRoomDialog.roomType.listItem.hoverBackground}; + } + border: 1px solid ${(props) => props.theme.createEditRoomDialog.roomType.listItem.borderColor}; border-radius: 6px; + &:active { + border-color: ${(props) => + props.theme.createEditRoomDialog.roomType.listItem.activeBorderColor}; + } + .choose_room-description { color: ${(props) => props.theme.createEditRoomDialog.roomType.listItem.descriptionText}; @@ -112,12 +123,23 @@ const StyledDropdownButton = styled(StyledRoomType)` border-radius: 6px; background-color: ${(props) => props.theme.createEditRoomDialog.roomType.dropdownButton.background}; + + ${({ isOpen }) => + !isOpen && + css` + &:hover { + background-color: ${(props) => + props.theme.createEditRoomDialog.roomType.dropdownButton + .hoverBackground}; + } + `} + border: 1px solid ${(props) => - props.isOpen - ? props.theme.createEditRoomDialog.roomType.dropdownButton - .isOpenBorderColor - : props.theme.createEditRoomDialog.roomType.dropdownButton.borderColor}; + props.isOpen + ? props.theme.createEditRoomDialog.roomType.dropdownButton + .isOpenBorderColor + : props.theme.createEditRoomDialog.roomType.dropdownButton.borderColor}; .choose_room-description { color: ${(props) => diff --git a/packages/shared/themes/base.ts b/packages/shared/themes/base.ts index c999e45c33..367fd59331 100644 --- a/packages/shared/themes/base.ts +++ b/packages/shared/themes/base.ts @@ -2467,11 +2467,14 @@ export const getBaseTheme = () => { roomType: { listItem: { background: "none", + hoverBackground: "#F8F9F9", borderColor: "#ECEEF1", + activeBorderColor: "#5299E0", descriptionText: "#A3A9AE", }, dropdownButton: { background: "none", + hoverBackground: "#f3f4f4", borderColor: "#ECEEF1", isOpenBorderColor: "#2DA7DB", descriptionText: "#A3A9AE", From 37e8fe661b0e03a7101dc0373cb72bfa7937df75 Mon Sep 17 00:00:00 2001 From: namushka Date: Fri, 24 May 2024 16:55:00 +0300 Subject: [PATCH 16/33] fixed :active styling --- .../sub-components/RoomType.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js index 63614361be..84976d2a44 100644 --- a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js +++ b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js @@ -98,16 +98,15 @@ const StyledRoomType = styled.div` const StyledListItem = styled(StyledRoomType)` background-color: ${(props) => props.theme.createEditRoomDialog.roomType.listItem.background}; - - &:hover { - background-color: ${(props) => - props.theme.createEditRoomDialog.roomType.listItem.hoverBackground}; - } - border: 1px solid ${(props) => props.theme.createEditRoomDialog.roomType.listItem.borderColor}; border-radius: 6px; + &:hover:not(:active) { + background-color: ${(props) => + props.theme.createEditRoomDialog.roomType.listItem.hoverBackground}; + } + &:active { border-color: ${(props) => props.theme.createEditRoomDialog.roomType.listItem.activeBorderColor}; @@ -127,7 +126,7 @@ const StyledDropdownButton = styled(StyledRoomType)` ${({ isOpen }) => !isOpen && css` - &:hover { + &:hover:not(:active) { background-color: ${(props) => props.theme.createEditRoomDialog.roomType.dropdownButton .hoverBackground}; @@ -141,6 +140,12 @@ const StyledDropdownButton = styled(StyledRoomType)` .isOpenBorderColor : props.theme.createEditRoomDialog.roomType.dropdownButton.borderColor}; + &:active { + border-color: ${(props) => + props.theme.createEditRoomDialog.roomType.dropdownButton + .isOpenBorderColor}; + } + .choose_room-description { color: ${(props) => props.theme.createEditRoomDialog.roomType.dropdownButton.descriptionText}; From 4618e8a1007fab1caa3cd4df146bbb9a6340a096 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Mon, 27 May 2024 10:52:23 +0300 Subject: [PATCH 17/33] Shared:Selectors:Files: remove opening folder after creation, now it add to top of list --- .../shared/selectors/Files/FilesSelector.types.ts | 1 - .../selectors/Files/hooks/useFilesHelper.ts | 15 ++++++++------- packages/shared/selectors/Files/index.tsx | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/shared/selectors/Files/FilesSelector.types.ts b/packages/shared/selectors/Files/FilesSelector.types.ts index 943b15a599..30ac26bfa8 100644 --- a/packages/shared/selectors/Files/FilesSelector.types.ts +++ b/packages/shared/selectors/Files/FilesSelector.types.ts @@ -121,7 +121,6 @@ export type UseFilesHelpersProps = { isInit: boolean; setIsFirstLoad: (value: boolean) => void; withCreateFolder: boolean; - setSelectedItemId: (value: number | string) => void; }; export type TSelectedFileInfo = { diff --git a/packages/shared/selectors/Files/hooks/useFilesHelper.ts b/packages/shared/selectors/Files/hooks/useFilesHelper.ts index 777ab91e3e..6f6dc2e4bd 100644 --- a/packages/shared/selectors/Files/hooks/useFilesHelper.ts +++ b/packages/shared/selectors/Files/hooks/useFilesHelper.ts @@ -84,7 +84,6 @@ const useFilesHelper = ({ setIsInit, setIsFirstLoad, withCreateFolder, - setSelectedItemId, }: UseFilesHelpersProps) => { const { t } = useTranslation(["Common"]); @@ -127,15 +126,17 @@ const useFilesHelper = ({ async (value: string) => { if (!withCreateFolder || !selectedItemId) return; - const folder = await createFolder(selectedItemId, value); + await createFolder(selectedItemId, value); - setBreadCrumbs((val) => { - return [...val, { id: folder.id, label: folder.title }]; - }); + onCancelInput(); - setSelectedItemId(folder.id); + // setBreadCrumbs((val) => { + // return [...val, { id: folder.id, label: folder.title }]; + // }); + + // setSelectedItemId(folder.id); }, - [withCreateFolder, selectedItemId, setBreadCrumbs, setSelectedItemId], + [withCreateFolder, selectedItemId, onCancelInput], ); const addInputItem = React.useCallback(() => { diff --git a/packages/shared/selectors/Files/index.tsx b/packages/shared/selectors/Files/index.tsx index c301cf5fd3..0886dbe53a 100644 --- a/packages/shared/selectors/Files/index.tsx +++ b/packages/shared/selectors/Files/index.tsx @@ -293,7 +293,6 @@ const FilesSelector = ({ isInit, setIsInit, withCreateFolder, - setSelectedItemId, }); const onSelectAction = React.useCallback( From 2583d4af163c5642443b5eb22923096f907abba6 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Mon, 27 May 2024 10:52:40 +0300 Subject: [PATCH 18/33] Shared:Components:Selector: add hotkeys for input item --- .../shared/components/selector/Selector.tsx | 6 ++- .../components/selector/Selector.types.ts | 3 ++ .../selector/sub-components/Body.tsx | 2 + .../selector/sub-components/InputItem.tsx | 40 +++++++++++++++++-- .../selector/sub-components/Item.tsx | 2 + 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/packages/shared/components/selector/Selector.tsx b/packages/shared/components/selector/Selector.tsx index d433386e66..26f1dd1054 100644 --- a/packages/shared/components/selector/Selector.tsx +++ b/packages/shared/components/selector/Selector.tsx @@ -158,6 +158,8 @@ const Selector = ({ return null; }); + const [inputItemVisible, setInputItemVisible] = React.useState(false); + const [requestRunning, setRequestRunning] = React.useState(false); const onSubmitAction = async ( @@ -386,6 +388,7 @@ const Selector = ({ React.useEffect(() => { const onKeyboardAction = (e: KeyboardEvent) => { + if (inputItemVisible) return; if (e.key === ButtonKeys.esc) { onCancel?.(); } @@ -395,7 +398,7 @@ const Selector = ({ return () => { window.removeEventListener("keydown", onKeyboardAction); }; - }, [onCancel]); + }, [inputItemVisible, onCancel]); React.useLayoutEffect(() => { if (items) { @@ -596,6 +599,7 @@ const Selector = ({ withFooterInput={withFooterInput} withFooterCheckbox={withFooterCheckbox} descriptionText={descriptionText} + setInputItemVisible={setInputItemVisible} // bread crumbs {...breadCrumbsProps} // select all diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index db2c29f286..a5b249e4b3 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -354,6 +354,8 @@ export type BodyProps = TSelectorBreadCrumbs & isMultiSelect: boolean; + setInputItemVisible: (value: boolean) => void; + items: TSelectorItem[]; renderCustomItem?: ( label: string, @@ -538,6 +540,7 @@ export type Data = { email?: string, isGroup?: boolean, ) => React.ReactNode | null; + setInputItemVisible: (value: boolean) => void; }; export interface ItemProps { diff --git a/packages/shared/components/selector/sub-components/Body.tsx b/packages/shared/components/selector/sub-components/Body.tsx index c8a45d1761..32e5ef516b 100644 --- a/packages/shared/components/selector/sub-components/Body.tsx +++ b/packages/shared/components/selector/sub-components/Body.tsx @@ -102,6 +102,7 @@ const Body = ({ withInfo, infoText, + setInputItemVisible, }: BodyProps) => { const [bodyHeight, setBodyHeight] = React.useState(0); @@ -270,6 +271,7 @@ const Body = ({ rowLoader, isItemLoaded, renderCustomItem, + setInputItemVisible, }} itemSize={48} onItemsRendered={onItemsRendered} diff --git a/packages/shared/components/selector/sub-components/InputItem.tsx b/packages/shared/components/selector/sub-components/InputItem.tsx index db4a8661ef..4eafb3378d 100644 --- a/packages/shared/components/selector/sub-components/InputItem.tsx +++ b/packages/shared/components/selector/sub-components/InputItem.tsx @@ -43,6 +43,8 @@ const InputItem = ({ color, icon, + + setInputItemVisible, }: { defaultInputValue: string; onAcceptInput: (value: string) => void; @@ -51,13 +53,45 @@ const InputItem = ({ color?: string; icon?: string; + + setInputItemVisible: (value: boolean) => void; }) => { const [value, setValue] = React.useState(defaultInputValue); + + const requestRunning = React.useRef(false); const inputRef = React.useRef(null); - const onAcceptInputAction = () => { - onAcceptInput(value); - }; + const onAcceptInputAction = React.useCallback(async () => { + if (requestRunning.current) return; + requestRunning.current = true; + await onAcceptInput(value); + + requestRunning.current = false; + }, [onAcceptInput, value]); + + React.useEffect(() => { + setInputItemVisible(true); + + return () => { + setInputItemVisible(false); + }; + }, [setInputItemVisible]); + + React.useEffect(() => { + const onKeyDown = (e: KeyboardEvent) => { + e.preventDefault(); + e.stopPropagation(); + + if (e.key === "Enter") onAcceptInputAction(); + else if (e.key === "Escape") onCancelInput(); + }; + + window.addEventListener("keydown", onKeyDown); + + return () => { + window.removeEventListener("keydown", onKeyDown); + }; + }, [onAcceptInputAction, onCancelInput]); const onChange = (e: React.ChangeEvent) => { const newVal = e.target.value; diff --git a/packages/shared/components/selector/sub-components/Item.tsx b/packages/shared/components/selector/sub-components/Item.tsx index 08dc90cfb5..35b3e77090 100644 --- a/packages/shared/components/selector/sub-components/Item.tsx +++ b/packages/shared/components/selector/sub-components/Item.tsx @@ -66,6 +66,7 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { isItemLoaded, rowLoader, renderCustomItem, + setInputItemVisible, }: Data = data; const { t } = useTranslation(["Common"]); @@ -109,6 +110,7 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { style={style} color={color} icon={icon} + setInputItemVisible={setInputItemVisible} /> ); } From 994c41e030ef27f357d8d8d705cb42e06a324c38 Mon Sep 17 00:00:00 2001 From: namushka Date: Mon, 27 May 2024 12:02:29 +0300 Subject: [PATCH 19/33] updated room type action color --- .../sub-components/RoomType.js | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js index 84976d2a44..5bbde85009 100644 --- a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js +++ b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js @@ -24,6 +24,7 @@ // 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 { inject, observer } from "mobx-react"; import ArrowReactSvgUrl from "PUBLIC_DIR/images/arrow.react.svg?url"; import React from "react"; import PropTypes from "prop-types"; @@ -107,9 +108,10 @@ const StyledListItem = styled(StyledRoomType)` props.theme.createEditRoomDialog.roomType.listItem.hoverBackground}; } + ${(props) => console.log(props.accentColor)} + &:active { - border-color: ${(props) => - props.theme.createEditRoomDialog.roomType.listItem.activeBorderColor}; + border-color: ${({ accentColor }) => accentColor}; } .choose_room-description { @@ -134,16 +136,15 @@ const StyledDropdownButton = styled(StyledRoomType)` `} border: 1px solid - ${(props) => - props.isOpen - ? props.theme.createEditRoomDialog.roomType.dropdownButton - .isOpenBorderColor - : props.theme.createEditRoomDialog.roomType.dropdownButton.borderColor}; + ${({ isOpen, accentColor, theme }) => + isOpen + ? accentColor + : theme.createEditRoomDialog.roomType.dropdownButton.borderColor}; + + ${(props) => console.log(props.accentColor)} &:active { - border-color: ${(props) => - props.theme.createEditRoomDialog.roomType.dropdownButton - .isOpenBorderColor}; + border-color: ${({ accentColor }) => accentColor}; } .choose_room-description { @@ -205,6 +206,7 @@ const RoomType = ({ isOpen, id, selectedId, + currentColorScheme, }) => { const room = { type: roomType, @@ -212,6 +214,8 @@ const RoomType = ({ description: getRoomTypeDescriptionTranslation(roomType, t), }; + const accentColor = currentColorScheme?.main?.accent; + const arrowClassName = type === "dropdownButton" ? "choose_room-forward_btn dropdown-button" @@ -246,7 +250,12 @@ const RoomType = ({ ); return type === "listItem" ? ( - + {content} ) : type === "dropdownButton" ? ( @@ -256,6 +265,7 @@ const RoomType = ({ onClick={onClick} isOpen={isOpen} data-selected-id={selectedId} + accentColor={accentColor} > {content} @@ -266,6 +276,7 @@ const RoomType = ({ onClick={onClick} isOpen={isOpen} data-selected-id={selectedId} + currentColorScheme={currentColorScheme} > {content} @@ -274,6 +285,7 @@ const RoomType = ({ id={id} title={t(room.title)} data-selected-id={selectedId} + currentColorScheme={currentColorScheme} > {content} @@ -295,6 +307,9 @@ RoomType.propTypes = { "dropdownItem", ]), isOpen: PropTypes.bool, + currentColorScheme: PropTypes.object, }; -export default RoomType; +export default inject(({ settingsStore }) => ({ + currentColorScheme: settingsStore.currentColorScheme, +}))(observer(RoomType)); From 62ecc7952dda60dbf81814494878e914017c76b5 Mon Sep 17 00:00:00 2001 From: namushka Date: Mon, 27 May 2024 16:29:25 +0300 Subject: [PATCH 20/33] fixed dark theme --- packages/shared/themes/dark.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/shared/themes/dark.ts b/packages/shared/themes/dark.ts index 363891c81f..3ac1edd60a 100644 --- a/packages/shared/themes/dark.ts +++ b/packages/shared/themes/dark.ts @@ -2446,18 +2446,20 @@ const Dark: TTheme = { roomType: { listItem: { background: "none", + hoverBackground: "#282828", borderColor: "#474747", descriptionText: "#A3A9AE", }, dropdownButton: { background: "none", + hoverBackground: "#282828", borderColor: "#474747", isOpenBorderColor: "#F97A0B", descriptionText: "#A3A9AE", }, dropdownItem: { background: "#333333", - hoverBackground: "#474747", + hoverBackground: "#282828", descriptionText: "#A3A9AE", }, displayItem: { @@ -2492,7 +2494,7 @@ const Dark: TTheme = { background: "#333333", borderColor: "#474747", item: { - hoverBackground: "#474747", + hoverBackground: "#282828", }, }, From 9ec645ba5c3e1721c88d5fbca6e4b2984f143fda Mon Sep 17 00:00:00 2001 From: namushka Date: Mon, 27 May 2024 16:29:54 +0300 Subject: [PATCH 21/33] removed unused theme keys --- packages/shared/themes/base.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/shared/themes/base.ts b/packages/shared/themes/base.ts index 367fd59331..888e35590e 100644 --- a/packages/shared/themes/base.ts +++ b/packages/shared/themes/base.ts @@ -2469,7 +2469,6 @@ export const getBaseTheme = () => { background: "none", hoverBackground: "#F8F9F9", borderColor: "#ECEEF1", - activeBorderColor: "#5299E0", descriptionText: "#A3A9AE", }, dropdownButton: { From fa900eef9466a0767910393f363a188702a404ef Mon Sep 17 00:00:00 2001 From: namushka Date: Mon, 27 May 2024 16:31:38 +0300 Subject: [PATCH 22/33] updated light color --- packages/shared/themes/base.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/themes/base.ts b/packages/shared/themes/base.ts index 888e35590e..2d7ef72ae4 100644 --- a/packages/shared/themes/base.ts +++ b/packages/shared/themes/base.ts @@ -2473,14 +2473,14 @@ export const getBaseTheme = () => { }, dropdownButton: { background: "none", - hoverBackground: "#f3f4f4", + hoverBackground: "#F8F9F9", borderColor: "#ECEEF1", isOpenBorderColor: "#2DA7DB", descriptionText: "#A3A9AE", }, dropdownItem: { background: "#ffffff", - hoverBackground: "#f3f4f4", + hoverBackground: "#F8F9F9", descriptionText: "#A3A9AE", }, displayItem: { From 094ae26c7210ea94a140195c2c462e1a4b24bd4e Mon Sep 17 00:00:00 2001 From: namushka Date: Mon, 27 May 2024 16:38:52 +0300 Subject: [PATCH 23/33] fixed dark theme for EditRoom dialog --- packages/shared/themes/dark.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared/themes/dark.ts b/packages/shared/themes/dark.ts index 3ac1edd60a..04df54c3f6 100644 --- a/packages/shared/themes/dark.ts +++ b/packages/shared/themes/dark.ts @@ -2463,8 +2463,8 @@ const Dark: TTheme = { descriptionText: "#A3A9AE", }, displayItem: { - background: "#474747", - borderColor: "#474747", + background: "#282828", + borderColor: "#282828", descriptionText: "#a3a9ae", }, }, From 0b0ba31920ec9176c34fca2664ac0ada366a3c63 Mon Sep 17 00:00:00 2001 From: namushka Date: Mon, 27 May 2024 16:50:00 +0300 Subject: [PATCH 24/33] code cleanup --- .../dialogs/CreateEditRoomDialog/sub-components/RoomType.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js index 5bbde85009..c701a6c281 100644 --- a/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js +++ b/packages/client/src/components/dialogs/CreateEditRoomDialog/sub-components/RoomType.js @@ -108,8 +108,6 @@ const StyledListItem = styled(StyledRoomType)` props.theme.createEditRoomDialog.roomType.listItem.hoverBackground}; } - ${(props) => console.log(props.accentColor)} - &:active { border-color: ${({ accentColor }) => accentColor}; } @@ -141,8 +139,6 @@ const StyledDropdownButton = styled(StyledRoomType)` ? accentColor : theme.createEditRoomDialog.roomType.dropdownButton.borderColor}; - ${(props) => console.log(props.accentColor)} - &:active { border-color: ${({ accentColor }) => accentColor}; } From 94d65b63b9c9acba68e9956acca5a392f6acbc42 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Mon, 27 May 2024 17:22:08 +0300 Subject: [PATCH 25/33] Shared:Components:Selector: add buttons for empty screen --- .../components/selector/Selector.styled.ts | 30 ++++++++++++ .../components/selector/Selector.types.ts | 4 ++ .../selector/sub-components/Body.tsx | 14 +++++- .../selector/sub-components/EmptyScreen.tsx | 47 +++++++++++++++++++ .../selector/sub-components/InputItem.tsx | 3 -- .../selector/sub-components/Item.tsx | 9 +++- packages/shared/themes/base.ts | 4 ++ packages/shared/themes/dark.ts | 3 ++ 8 files changed, 108 insertions(+), 6 deletions(-) diff --git a/packages/shared/components/selector/Selector.styled.ts b/packages/shared/components/selector/Selector.styled.ts index 17458f8d43..ae9043b775 100644 --- a/packages/shared/components/selector/Selector.styled.ts +++ b/packages/shared/components/selector/Selector.styled.ts @@ -272,6 +272,36 @@ const StyledEmptyScreen = styled.div<{ withSearch: boolean }>` box-sizing: border-box; + .buttons { + margin-top: 32px; + + display: flex; + gap: 16px; + align-items: center; + justify-content: center; + + .empty-folder_container-links { + display: flex; + align-items: center; + gap: 8px; + + .empty-folder_link { + color: ${(props) => props.theme.selector.emptyScreen.buttonColor}; + } + + &:hover { + .empty-folder_link { + color: ${(props) => + props.theme.selector.emptyScreen.hoverButtonColor}; + } + + svg path { + fill: ${(props) => props.theme.selector.emptyScreen.hoverButtonColor}; + } + } + } + } + .empty-image { max-width: 72px; max-height: 72px; diff --git a/packages/shared/components/selector/Selector.types.ts b/packages/shared/components/selector/Selector.types.ts index a5b249e4b3..e7e06269b1 100644 --- a/packages/shared/components/selector/Selector.types.ts +++ b/packages/shared/components/selector/Selector.types.ts @@ -171,6 +171,8 @@ export interface EmptyScreenProps { searchHeader: string; searchDescription: string; withSearch: boolean; + + items: TSelectorItem[]; } type TSelectorEmptyScreen = { @@ -414,6 +416,7 @@ type TSelectorItemEmpty = { name?: undefined; isCreateNewItem?: undefined; onCreateClick?: undefined; + onBackClick?: undefined; isInputItem?: undefined; defaultInputValue?: undefined; onAcceptInput?: undefined; @@ -492,6 +495,7 @@ export type TSelectorItemNew = MergeTypes< { isCreateNewItem: boolean; onCreateClick: VoidFunction; + onBackClick: VoidFunction; } >; diff --git a/packages/shared/components/selector/sub-components/Body.tsx b/packages/shared/components/selector/sub-components/Body.tsx index 32e5ef516b..273fbcdefa 100644 --- a/packages/shared/components/selector/sub-components/Body.tsx +++ b/packages/shared/components/selector/sub-components/Body.tsx @@ -109,7 +109,16 @@ const Body = ({ const bodyRef = React.useRef(null); const listOptionsRef = React.useRef(null); - const itemsCount = hasNextPage ? items.length + 1 : items.length; + const isEmptyInput = + items.length === 2 && items[1].isInputItem && items[0].isCreateNewItem; + + const itemsCount = hasNextPage + ? items.length + 1 + : items.length === 1 && items[0].isCreateNewItem + ? 0 + : isEmptyInput + ? 1 + : items.length; const resetCache = React.useCallback(() => { if (listOptionsRef && listOptionsRef.current) { @@ -230,6 +239,7 @@ const Body = ({ searchImage={searchEmptyScreenImage} searchHeader={searchEmptyScreenHeader} searchDescription={searchEmptyScreenDescription} + items={items} /> ) : ( <> @@ -265,7 +275,7 @@ const Body = ({ width="100%" itemCount={itemsCount} itemData={{ - items, + items: isEmptyInput ? [items[1]] : items, onSelect, isMultiSelect: isMultiSelect || false, rowLoader, diff --git a/packages/shared/components/selector/sub-components/EmptyScreen.tsx b/packages/shared/components/selector/sub-components/EmptyScreen.tsx index 116cb579dd..0c73163a7e 100644 --- a/packages/shared/components/selector/sub-components/EmptyScreen.tsx +++ b/packages/shared/components/selector/sub-components/EmptyScreen.tsx @@ -25,13 +25,27 @@ // International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode import React from "react"; +import { useTranslation } from "react-i18next"; + +import PlusSvgUrl from "PUBLIC_DIR/images/plus.svg?url"; +import UpSvgUrl from "PUBLIC_DIR/images/up.svg?url"; import { Heading } from "../../heading"; import { Text } from "../../text"; +import { IconButton } from "../../icon-button"; +import { Link, LinkType } from "../../link"; import { StyledEmptyScreen } from "../Selector.styled"; import { EmptyScreenProps } from "../Selector.types"; +const linkStyles = { + isHovered: true, + type: LinkType.action, + fontWeight: "600", + className: "empty-folder_link", + display: "flex", +}; + const EmptyScreen = ({ image, header, @@ -40,11 +54,16 @@ const EmptyScreen = ({ searchHeader, searchDescription, withSearch, + items, }: EmptyScreenProps) => { + const { t } = useTranslation(["Common"]); + const currentImage = withSearch ? searchImage : image; const currentHeader = withSearch ? searchHeader : header; const currentDescription = withSearch ? searchDescription : description; + const createItem = items.length > 0 ? items[0] : null; + return ( empty-screen @@ -56,6 +75,34 @@ const EmptyScreen = ({ {currentDescription} + {createItem && ( +
+
+ + + {items[0].label} + +
+
+ + + {t("Common:Back")} + +
+
+ )} ); }; diff --git a/packages/shared/components/selector/sub-components/InputItem.tsx b/packages/shared/components/selector/sub-components/InputItem.tsx index 4eafb3378d..5d9f90a1be 100644 --- a/packages/shared/components/selector/sub-components/InputItem.tsx +++ b/packages/shared/components/selector/sub-components/InputItem.tsx @@ -79,9 +79,6 @@ const InputItem = ({ React.useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { - e.preventDefault(); - e.stopPropagation(); - if (e.key === "Enter") onAcceptInputAction(); else if (e.key === "Escape") onCancelInput(); }; diff --git a/packages/shared/components/selector/sub-components/Item.tsx b/packages/shared/components/selector/sub-components/Item.tsx index 35b3e77090..30a38fba1c 100644 --- a/packages/shared/components/selector/sub-components/Item.tsx +++ b/packages/shared/components/selector/sub-components/Item.tsx @@ -86,6 +86,7 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { label, isCreateNewItem, onCreateClick, + isInputItem, defaultInputValue, onAcceptInput, @@ -115,11 +116,17 @@ const Item = React.memo(({ index, style, data }: ItemProps) => { ); } - if (isCreateNewItem) { + if ( + isCreateNewItem && + (items.length > 2 || (items.length === 2 && !items[1].isInputItem)) + ) { return ( ); } + if (isCreateNewItem) { + return null; + } const showPlanetIcon = (item.roomType === RoomsType.PublicRoom || diff --git a/packages/shared/themes/base.ts b/packages/shared/themes/base.ts index 94d30def2a..31d8aeafc9 100644 --- a/packages/shared/themes/base.ts +++ b/packages/shared/themes/base.ts @@ -2398,6 +2398,10 @@ export const getBaseTheme = () => { emptyScreen: { descriptionColor: cyanBlueDarkShade, + + buttonColor: "#657077", + hoverButtonColor: "#333333", + pressedButtonColor: "#555F65", }, }, diff --git a/packages/shared/themes/dark.ts b/packages/shared/themes/dark.ts index f5b0f55f32..f0bd392c00 100644 --- a/packages/shared/themes/dark.ts +++ b/packages/shared/themes/dark.ts @@ -2377,6 +2377,9 @@ const Dark: TTheme = { emptyScreen: { descriptionColor: "#ADADAD", + buttonColor: "#ADADAD", + hoverButtonColor: "#FFFFFF", + pressedButtonColor: "#CCCCCC", }, }, From 2eb8bda5277854bd9790449e8be513778f27412a Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Mon, 27 May 2024 17:24:07 +0300 Subject: [PATCH 26/33] Shared:Selectors:Files: add create folder and back to parent buttons for empty screen --- .../shared/selectors/Files/FilesSelector.types.ts | 5 +++-- .../shared/selectors/Files/hooks/useFilesHelper.ts | 12 ++++++++++++ .../shared/selectors/Files/hooks/useSocketHelper.ts | 10 +++++----- packages/shared/selectors/Files/index.tsx | 1 + 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/shared/selectors/Files/FilesSelector.types.ts b/packages/shared/selectors/Files/FilesSelector.types.ts index 30ac26bfa8..5b18d9d531 100644 --- a/packages/shared/selectors/Files/FilesSelector.types.ts +++ b/packages/shared/selectors/Files/FilesSelector.types.ts @@ -98,7 +98,7 @@ export type UseFilesHelpersProps = { setIsRoot: (value: boolean) => void; setIsInit: (value: boolean) => void; searchValue?: string; - disabledItems: string[] | number[]; + disabledItems: (string | number)[]; setSelectedItemSecurity: (value: TFileSecurity | TFolderSecurity) => void; isThirdParty: boolean; setSelectedTreeNode: (treeNode: TFolder) => void; @@ -121,6 +121,7 @@ export type UseFilesHelpersProps = { isInit: boolean; setIsFirstLoad: (value: boolean) => void; withCreateFolder: boolean; + setSelectedItemId: (value: number | string) => void; }; export type TSelectedFileInfo = { @@ -140,7 +141,7 @@ export type FilesSelectorProps = ( ) & { socketHelper: SocketIOHelper; socketSubscribers: Set; - disabledItems: string[] | number[]; + disabledItems: (string | number)[]; filterParam?: string; withoutBackButton: boolean; withBreadCrumbs: boolean; diff --git a/packages/shared/selectors/Files/hooks/useFilesHelper.ts b/packages/shared/selectors/Files/hooks/useFilesHelper.ts index 6f6dc2e4bd..a4c4c4f3e9 100644 --- a/packages/shared/selectors/Files/hooks/useFilesHelper.ts +++ b/packages/shared/selectors/Files/hooks/useFilesHelper.ts @@ -84,6 +84,7 @@ const useFilesHelper = ({ setIsInit, setIsFirstLoad, withCreateFolder, + setSelectedItemId, }: UseFilesHelpersProps) => { const { t } = useTranslation(["Common"]); @@ -388,6 +389,16 @@ const useFilesHelper = ({ id: "create-folder-item", key: "create-folder-item", onCreateClick: addInputItem, + onBackClick: () => { + setSelectedItemId(current.parentId); + setBreadCrumbs((val) => { + const newVal = [...val]; + + newVal.pop(); + + return newVal; + }); + }, }); } else { setTotal(total); @@ -465,6 +476,7 @@ const useFilesHelper = ({ setItems, setTotal, addInputItem, + setSelectedItemId, rootThirdPartyId, ], ); diff --git a/packages/shared/selectors/Files/hooks/useSocketHelper.ts b/packages/shared/selectors/Files/hooks/useSocketHelper.ts index 46057c9bfa..93a6349336 100644 --- a/packages/shared/selectors/Files/hooks/useSocketHelper.ts +++ b/packages/shared/selectors/Files/hooks/useSocketHelper.ts @@ -128,8 +128,8 @@ const useSocketHelper = ({ let idx = 0; - if (value[0].isInputItem) idx = 1; - if (value[1].isInputItem) idx = 2; + if (value[0]?.isInputItem) idx = 1; + if (value[1]?.isInputItem) idx = 2; newValue.splice(idx, 0, item); @@ -144,9 +144,9 @@ const useSocketHelper = ({ for (let i = 0; i < value.length - 1; i += 1) { if ( - !value[i].isFolder && - !value[i].isCreateNewItem && - !value[i].isInputItem + !value[i]?.isFolder && + !value[i]?.isCreateNewItem && + !value[i]?.isInputItem ) break; diff --git a/packages/shared/selectors/Files/index.tsx b/packages/shared/selectors/Files/index.tsx index 0886dbe53a..c301cf5fd3 100644 --- a/packages/shared/selectors/Files/index.tsx +++ b/packages/shared/selectors/Files/index.tsx @@ -293,6 +293,7 @@ const FilesSelector = ({ isInit, setIsInit, withCreateFolder, + setSelectedItemId, }); const onSelectAction = React.useCallback( From 2200c6fa0ee1e7b54ed8bbadeda04607cc1b8e8d Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Mon, 27 May 2024 17:24:22 +0300 Subject: [PATCH 27/33] Client:FilesSelector: fix disabled items --- packages/client/src/components/FilesSelector/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/client/src/components/FilesSelector/index.tsx b/packages/client/src/components/FilesSelector/index.tsx index 0ce43b4598..b7ba7cc87e 100644 --- a/packages/client/src/components/FilesSelector/index.tsx +++ b/packages/client/src/components/FilesSelector/index.tsx @@ -56,7 +56,7 @@ import InfoPanelStore from "SRC_DIR/store/InfoPanelStore"; import { FilesSelectorProps } from "./FilesSelector.types"; import { getAcceptButtonLabel, getHeaderLabel, getIsDisabled } from "./utils"; -const disabledItems: (string | number)[] = []; +let disabledItems: (string | number)[] = []; const FilesSelectorWrapper = ({ isPanelVisible = false, @@ -177,6 +177,12 @@ const FilesSelectorWrapper = ({ [t], ); + React.useEffect(() => { + return () => { + disabledItems = []; + }; + }, []); + const onAccept = async ( selectedItemId: string | number | undefined, folderTitle: string, From 6263c9456320f4924fa1a7e2e13bf4650a5cf925 Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Tue, 28 May 2024 10:24:23 +0300 Subject: [PATCH 28/33] Shared:Selectors:Files: fix socket add item --- packages/shared/selectors/Files/hooks/useSocketHelper.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/shared/selectors/Files/hooks/useSocketHelper.ts b/packages/shared/selectors/Files/hooks/useSocketHelper.ts index 93a6349336..c214eba473 100644 --- a/packages/shared/selectors/Files/hooks/useSocketHelper.ts +++ b/packages/shared/selectors/Files/hooks/useSocketHelper.ts @@ -126,9 +126,8 @@ const useSocketHelper = ({ if (withCreateFolder) { const newValue = [...value]; - let idx = 0; + let idx = 1; - if (value[0]?.isInputItem) idx = 1; if (value[1]?.isInputItem) idx = 2; newValue.splice(idx, 0, item); From 61b00dd53cad49b4e13ca0a8ed491b72b9f1302c Mon Sep 17 00:00:00 2001 From: gopienkonikita Date: Tue, 28 May 2024 10:54:49 +0300 Subject: [PATCH 29/33] Web: Hotkeys: fixed scroll focus --- packages/client/src/store/AccountsHotkeysStore.ts | 6 +++++- packages/client/src/store/HotkeyStore.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/client/src/store/AccountsHotkeysStore.ts b/packages/client/src/store/AccountsHotkeysStore.ts index 3dd09f6551..33cf0b9299 100644 --- a/packages/client/src/store/AccountsHotkeysStore.ts +++ b/packages/client/src/store/AccountsHotkeysStore.ts @@ -255,7 +255,11 @@ class AccountsHotkeysStore { const scroll = document.getElementsByClassName( "section-scroll", ) as HTMLCollectionOf; - if (scroll && scroll[0]) scroll[0].focus(); + + if (scroll && scroll[0]) { + const scrollElem = scroll[0]?.firstChild as HTMLElement; + scrollElem?.focus(); + } } if (!this.hotkeyCaret && selection.length) { diff --git a/packages/client/src/store/HotkeyStore.js b/packages/client/src/store/HotkeyStore.js index 705746e7cd..083f66bc35 100644 --- a/packages/client/src/store/HotkeyStore.js +++ b/packages/client/src/store/HotkeyStore.js @@ -135,7 +135,7 @@ class HotkeyStore { if (!hotkeyCaret) { const scroll = document.getElementsByClassName("section-scroll"); - scroll && scroll[0] && scroll[0].focus(); + scroll && scroll[0] && scroll[0]?.firstChild.focus(); } if (!hotkeyCaret && selection.length) { From a9fbd7499356f69a6b30bb76f396c8633e93424d Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Tue, 28 May 2024 11:06:44 +0300 Subject: [PATCH 30/33] Shared:Components:Selector: add click by label for create item --- packages/shared/components/selector/Selector.styled.ts | 5 +++++ .../shared/components/selector/sub-components/NewItem.tsx | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/shared/components/selector/Selector.styled.ts b/packages/shared/components/selector/Selector.styled.ts index ae9043b775..3b324c17d8 100644 --- a/packages/shared/components/selector/Selector.styled.ts +++ b/packages/shared/components/selector/Selector.styled.ts @@ -222,6 +222,11 @@ const StyledItem = styled.div<{ `} } + .clicked-label { + width: fit-content; + cursor: pointer; + } + .input-component { margin-inline-start: 8px; } diff --git a/packages/shared/components/selector/sub-components/NewItem.tsx b/packages/shared/components/selector/sub-components/NewItem.tsx index e396cf56c2..f4ff2d8f85 100644 --- a/packages/shared/components/selector/sub-components/NewItem.tsx +++ b/packages/shared/components/selector/sub-components/NewItem.tsx @@ -50,12 +50,14 @@ const NewItem = ({ > {label} From dc3ca111b0df7f20382a40611fa2115d47fc087c Mon Sep 17 00:00:00 2001 From: Timofey Boyko Date: Tue, 28 May 2024 12:09:20 +0300 Subject: [PATCH 31/33] Doceditor:Components:SelectFolderDialog: enable create folder --- packages/doceditor/src/components/SelectFileDialog.tsx | 1 + packages/doceditor/src/components/SelectFolderDialog.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/doceditor/src/components/SelectFileDialog.tsx b/packages/doceditor/src/components/SelectFileDialog.tsx index 51005e1893..62ae2d6295 100644 --- a/packages/doceditor/src/components/SelectFileDialog.tsx +++ b/packages/doceditor/src/components/SelectFileDialog.tsx @@ -110,6 +110,7 @@ const SelectFileDialog = ({ submitButtonId="select-file-modal-submit" cancelButtonId="select-file-modal-cancel" {...fileTypeDetection} + withCreateFolder={false} /> ); }; diff --git a/packages/doceditor/src/components/SelectFolderDialog.tsx b/packages/doceditor/src/components/SelectFolderDialog.tsx index 9153e19565..5eca552be6 100644 --- a/packages/doceditor/src/components/SelectFolderDialog.tsx +++ b/packages/doceditor/src/components/SelectFolderDialog.tsx @@ -92,6 +92,7 @@ const SelectFolderDialog = ({ getFilesArchiveError={() => ""} parentId={0} getIsDisabled={getIsDisabled} + withCreateFolder /> ); }; From 9253b3a120340fa7e091cb23992767e6ff08fe29 Mon Sep 17 00:00:00 2001 From: gazizova-vlada Date: Tue, 28 May 2024 12:09:52 +0300 Subject: [PATCH 32/33] Fixed Bug 67431 - Mobile. Folder is not selected when selecting Folder Upload --- packages/client/src/store/ContextOptionsStore.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/client/src/store/ContextOptionsStore.js b/packages/client/src/store/ContextOptionsStore.js index 17c031f489..fd3bf6a73b 100644 --- a/packages/client/src/store/ContextOptionsStore.js +++ b/packages/client/src/store/ContextOptionsStore.js @@ -2051,6 +2051,7 @@ class ContextOptionsStore { ], }; + const showUploadFolder = !(isMobile || isTablet); const moreActions = { id: "personal_more-form", className: "main-button_drop-down", @@ -2072,7 +2073,7 @@ class ContextOptionsStore { key: "personal_more-form__separator-2", }, uploadFiles, - uploadFolder, + showUploadFolder ? uploadFolder : null, ], }; @@ -2321,6 +2322,7 @@ class ContextOptionsStore { ] : [createTemplateForm, createTemplateNewFormFile, templateOformsGallery]; + const showUploadFolder = !(isMobile || isTablet); const options = isRoomsFolder ? [ { @@ -2338,7 +2340,7 @@ class ContextOptionsStore { createNewFolder, { key: "separator", isSeparator: true }, uploadFiles, - uploadFolder, + showUploadFolder ? uploadFolder : null, ]; if (mainButtonItemsList && enablePlugins) { From 4da2a3d95da0e26ba6d29b7c6eaaa766bbb7707f Mon Sep 17 00:00:00 2001 From: Elyor Djalilov Date: Tue, 28 May 2024 14:54:15 +0500 Subject: [PATCH 33/33] Web: Client: Profile: ActiveSessions. removed extra comma in row view --- .../SessionsTable/RowView/SessionsRowContent.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/client/src/pages/Profile/Section/Body/sub-components/active-session/SessionsTable/RowView/SessionsRowContent.js b/packages/client/src/pages/Profile/Section/Body/sub-components/active-session/SessionsTable/RowView/SessionsRowContent.js index dc39f35d65..03652a24e1 100644 --- a/packages/client/src/pages/Profile/Section/Body/sub-components/active-session/SessionsTable/RowView/SessionsRowContent.js +++ b/packages/client/src/pages/Profile/Section/Body/sub-components/active-session/SessionsTable/RowView/SessionsRowContent.js @@ -70,10 +70,9 @@ const SessionsRowContent = ({ )} {convertTime(date)} {(country || city) && ( - + {country} - {country && city && ", "} - {city} + {country && city && ` ${city}`} )}