@griddo/ax 10.2.25 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/package.json +2 -2
  2. package/public/img/icons/excel.png +0 -0
  3. package/public/img/icons/pdf.png +0 -0
  4. package/public/img/icons/word.png +0 -0
  5. package/public/img/icons/zip.png +0 -0
  6. package/src/GlobalStore.tsx +3 -0
  7. package/src/__mocks__/store/GenericStore.ts +3 -0
  8. package/src/__tests__/components/Fields/FileField/FileField.test.tsx +34 -8
  9. package/src/__tests__/components/Gallery/GalleryPanel/GalleryDragAndDrop/GalleryDragAndDrop.test.tsx +1 -1
  10. package/src/api/files.tsx +171 -1
  11. package/src/api/users.tsx +5 -2
  12. package/src/components/ActionMenu/index.tsx +5 -13
  13. package/src/components/BackFolder/index.tsx +28 -0
  14. package/src/components/BackFolder/style.tsx +33 -0
  15. package/src/components/BulkSelectionOptions/index.tsx +4 -8
  16. package/src/components/Button/index.tsx +8 -3
  17. package/src/components/Button/style.tsx +5 -3
  18. package/src/components/ElementsTooltip/index.tsx +22 -7
  19. package/src/components/ElementsTooltip/style.tsx +2 -2
  20. package/src/components/Fields/FileField/index.tsx +7 -7
  21. package/src/components/Fields/TextField/index.tsx +3 -0
  22. package/src/components/FileGallery/FolderItem/index.tsx +39 -0
  23. package/src/components/FileGallery/FolderItem/style.tsx +31 -0
  24. package/src/components/FileGallery/GalleryPanel/DetailPanel/index.tsx +164 -0
  25. package/src/components/FileGallery/GalleryPanel/DetailPanel/style.tsx +113 -0
  26. package/src/components/FileGallery/GalleryPanel/index.tsx +42 -0
  27. package/src/components/FileGallery/GalleryPanel/style.tsx +7 -0
  28. package/src/components/FileGallery/GridItem/index.tsx +47 -0
  29. package/src/components/FileGallery/GridItem/style.tsx +51 -0
  30. package/src/components/FileGallery/index.tsx +304 -0
  31. package/src/components/FileGallery/style.tsx +173 -0
  32. package/src/components/FileGallery/utils.tsx +19 -0
  33. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +4 -6
  34. package/src/components/Icon/components/Back.js +10 -0
  35. package/src/components/Icon/components/ClosePanel.js +12 -0
  36. package/src/components/Icon/components/NewFolder.js +10 -0
  37. package/src/components/Icon/components/OpenPanel.js +12 -0
  38. package/src/components/Icon/svgs/Back.svg +3 -0
  39. package/src/components/Icon/svgs/Close_panel.svg +3 -0
  40. package/src/components/Icon/svgs/New-folder.svg +3 -0
  41. package/src/components/Icon/svgs/Open_panel.svg +3 -0
  42. package/src/components/Modal/index.tsx +7 -5
  43. package/src/components/Modal/style.tsx +6 -6
  44. package/src/components/ProgressBar/index.tsx +3 -2
  45. package/src/components/ProgressBar/style.tsx +5 -3
  46. package/src/components/TableList/index.tsx +3 -2
  47. package/src/components/TableList/style.tsx +4 -0
  48. package/src/components/Toast/style.tsx +2 -2
  49. package/src/components/index.tsx +4 -0
  50. package/src/containers/FileDrive/actions.tsx +386 -0
  51. package/src/containers/FileDrive/constants.tsx +24 -0
  52. package/src/containers/FileDrive/index.tsx +7 -0
  53. package/src/containers/FileDrive/interfaces.tsx +59 -0
  54. package/src/containers/FileDrive/reducer.tsx +57 -0
  55. package/src/containers/FileDrive/utils.tsx +37 -0
  56. package/src/containers/Gallery/actions.tsx +1 -1
  57. package/src/containers/Gallery/interfaces.tsx +1 -1
  58. package/src/helpers/index.tsx +2 -0
  59. package/src/helpers/objects.tsx +6 -0
  60. package/src/modules/FileDrive/Breadcrumb/index.tsx +42 -0
  61. package/src/modules/FileDrive/Breadcrumb/style.tsx +18 -0
  62. package/src/modules/FileDrive/BulkGridHeader/GridHeader/index.tsx +37 -0
  63. package/src/modules/FileDrive/BulkGridHeader/GridHeader/style.tsx +19 -0
  64. package/src/modules/FileDrive/BulkGridHeader/index.tsx +35 -0
  65. package/src/modules/FileDrive/BulkGridHeader/style.tsx +17 -0
  66. package/src/modules/FileDrive/BulkListHeader/TableHeader/index.tsx +42 -0
  67. package/src/modules/FileDrive/BulkListHeader/TableHeader/style.tsx +53 -0
  68. package/src/modules/FileDrive/BulkListHeader/index.tsx +35 -0
  69. package/src/modules/FileDrive/BulkListHeader/style.tsx +17 -0
  70. package/src/modules/FileDrive/FileDragAndDrop/index.tsx +249 -0
  71. package/src/{components/Fields/FileField → modules/FileDrive}/FileDragAndDrop/style.tsx +50 -9
  72. package/src/modules/FileDrive/FileModal/DetailPanel/index.tsx +170 -0
  73. package/src/modules/FileDrive/FileModal/DetailPanel/style.tsx +81 -0
  74. package/src/modules/FileDrive/FileModal/index.tsx +129 -0
  75. package/src/modules/FileDrive/FileModal/style.tsx +112 -0
  76. package/src/modules/FileDrive/FolderItem/index.tsx +180 -0
  77. package/src/modules/FileDrive/FolderItem/style.tsx +39 -0
  78. package/src/modules/FileDrive/FolderTree/index.tsx +108 -0
  79. package/src/modules/FileDrive/FolderTree/style.tsx +69 -0
  80. package/src/modules/FileDrive/FolderTree/utils.tsx +91 -0
  81. package/src/modules/FileDrive/GridItem/index.tsx +167 -0
  82. package/src/modules/FileDrive/GridItem/style.tsx +76 -0
  83. package/src/modules/FileDrive/ListItem/index.tsx +180 -0
  84. package/src/modules/FileDrive/ListItem/style.tsx +88 -0
  85. package/src/modules/FileDrive/atoms.tsx +173 -0
  86. package/src/modules/FileDrive/helpers.tsx +19 -0
  87. package/src/modules/FileDrive/index.tsx +670 -0
  88. package/src/modules/FileDrive/style.tsx +145 -0
  89. package/src/modules/Sites/SitesList/index.tsx +0 -3
  90. package/src/routes/multisite.tsx +9 -0
  91. package/src/routes/site.tsx +9 -0
  92. package/src/types/index.tsx +63 -0
  93. package/src/components/Fields/FileField/FileDragAndDrop/index.tsx +0 -188
  94. package/src/components/Fields/FileField/store.tsx +0 -61
@@ -0,0 +1,129 @@
1
+ import React, { useState } from "react";
2
+ import { IFile } from "@ax/types";
3
+ import { Button, FloatingMenu, IconAction, Toast, Tooltip } from "@ax/components";
4
+ import { useToast } from "@ax/hooks";
5
+ import FileDragAndDrop from "../FileDragAndDrop";
6
+ import { getFileIcon } from "../helpers";
7
+ import DetailPanel from "./DetailPanel";
8
+
9
+ import * as S from "./style";
10
+
11
+ const FileModal = (props: IProps) => {
12
+ const { file, items, activeDelete, isAllowedToEdit, setFile, toggleModal, onDelete, setFileSelected } = props;
13
+ const { fileType, fileName, id, site } = file;
14
+
15
+ const [replaceType, setReplaceType] = useState<"full" | "partial" | null>(null);
16
+ const [isDNDVisible, setDNDVisible] = useState(false);
17
+ const { isVisible, toggleToast, setIsVisible } = useToast();
18
+
19
+ const iconUrl = `/img/icons/${getFileIcon(fileType)}`;
20
+ const index = items.indexOf(file.id);
21
+
22
+ const validFormats = ["pdf", "doc", "docx", "xls", "xlsx", "zip"];
23
+
24
+ const handleArrowClick = (isPrev: boolean) => () => {
25
+ isPrev ? setFile(items[index - 1]) : setFile(items[index + 1]);
26
+ setDNDVisible(false);
27
+ setReplaceType(null);
28
+ };
29
+
30
+ const OptionsButton = () => (
31
+ <Tooltip content="Replacement file will appear on all linked pages.">
32
+ <Button type="button" buttonStyle="text" icon="page" backIcon="downArrow">
33
+ Replace
34
+ </Button>
35
+ </Tooltip>
36
+ );
37
+
38
+ const handleReplaceType = (type: "full" | "partial") => {
39
+ setReplaceType(type);
40
+ setDNDVisible(true);
41
+ };
42
+
43
+ const textReplaceType =
44
+ replaceType === "full"
45
+ ? "Replace file and its URL"
46
+ : replaceType === "partial"
47
+ ? "Replace file and keep its original URL"
48
+ : "";
49
+
50
+ const handleUpload = (file: IFile[]) => {
51
+ setFileSelected(file[0]);
52
+ setDNDVisible(false);
53
+ setReplaceType(null);
54
+ toggleToast();
55
+ };
56
+
57
+ const toastProps = {
58
+ setIsVisible,
59
+ message: "1 file replaced",
60
+ };
61
+
62
+ return (
63
+ <>
64
+ <S.Wrapper>
65
+ <S.LeftPanel>
66
+ <IconAction icon="leftArrow" onClick={handleArrowClick(true)} disabled={index === 0} />
67
+ <S.FilePreview>
68
+ <S.ImageWrapper>
69
+ <S.IconWrapper>
70
+ <div><img src={iconUrl} alt={`${fileType} Icon`} /></div>
71
+ {isDNDVisible && (
72
+ <S.DragAndDropWrapper>
73
+ <FileDragAndDrop
74
+ validFormats={validFormats}
75
+ handleUpload={handleUpload}
76
+ inverse={true}
77
+ replaceData={{ fileID: id, keepURL: replaceType === "full" ? false : true }}
78
+ siteID={site || "global"}
79
+ />
80
+ </S.DragAndDropWrapper>
81
+ )}
82
+ </S.IconWrapper>
83
+ </S.ImageWrapper>
84
+ <S.FileName>{fileName}</S.FileName>
85
+ <S.FileActions>
86
+ {isAllowedToEdit ? (
87
+ <S.ReplaceWrapper>
88
+ <FloatingMenu Button={OptionsButton} position="left">
89
+ <S.ActionMenu>
90
+ <S.ActionItem onClick={() => handleReplaceType("full")}>Replace file and its URL</S.ActionItem>
91
+ <S.ActionItem onClick={() => handleReplaceType("partial")}>
92
+ Replace file and keep its original URL
93
+ </S.ActionItem>
94
+ </S.ActionMenu>
95
+ </FloatingMenu>
96
+ <S.TextType>{textReplaceType}</S.TextType>
97
+ </S.ReplaceWrapper>
98
+ ) : (
99
+ <></>
100
+ )}
101
+ </S.FileActions>
102
+ </S.FilePreview>
103
+ <IconAction icon="rightArrow" onClick={handleArrowClick(false)} disabled={index === items.length - 1} />
104
+ </S.LeftPanel>
105
+ <DetailPanel
106
+ file={file}
107
+ toggleModal={toggleModal}
108
+ onDelete={onDelete}
109
+ activeDelete={activeDelete}
110
+ isAllowedToEdit={isAllowedToEdit}
111
+ />
112
+ </S.Wrapper>
113
+ {isVisible && <Toast {...toastProps} />}
114
+ </>
115
+ );
116
+ };
117
+
118
+ interface IProps {
119
+ file: IFile;
120
+ toggleModal(): void;
121
+ onDelete(fileID: number): void;
122
+ items: number[];
123
+ setFile: (id: number) => void;
124
+ setFileSelected: (file: IFile) => void;
125
+ activeDelete: boolean;
126
+ isAllowedToEdit: boolean;
127
+ }
128
+
129
+ export default FileModal;
@@ -0,0 +1,112 @@
1
+ import styled from "styled-components";
2
+
3
+ const Wrapper = styled.div`
4
+ display: flex;
5
+ width: 100%;
6
+ height: 100%;
7
+ `;
8
+
9
+ const LeftPanel = styled.div`
10
+ position: relative;
11
+ display: flex;
12
+ width: 100%;
13
+ height: 100%;
14
+ padding-left: ${(p) => p.theme.spacing.m};
15
+ padding-right: ${(p) => p.theme.spacing.m};
16
+ align-items: center;
17
+ `;
18
+
19
+ const FilePreview = styled.div`
20
+ display: flex;
21
+ width: 100%;
22
+ height: 100%;
23
+ justify-content: center;
24
+ align-content: center;
25
+ flex-flow: column wrap;
26
+ `;
27
+
28
+ const ImageWrapper = styled.div`
29
+ display: flex;
30
+ justify-content: center;
31
+ width: 100%;
32
+ height: 300px;
33
+ `;
34
+
35
+ const IconWrapper = styled.div`
36
+ position: relative;
37
+ width: 300px;
38
+ height: 300px;
39
+ img {
40
+ max-width: 300px;
41
+ width: 100%;
42
+ }
43
+ `;
44
+
45
+ const FileName = styled.div`
46
+ ${(p) => p.theme.textStyle.uiL};
47
+ color: ${(p) => p.theme.colors.textHighEmphasis};
48
+ margin-top: ${(p) => p.theme.spacing.s};
49
+ text-align: center;
50
+ word-break: break-word;
51
+ `;
52
+
53
+ const FileActions = styled.div`
54
+ display: flex;
55
+ justify-content: center;
56
+ margin-top: ${(p) => p.theme.spacing.m};
57
+ `;
58
+
59
+ const ReplaceWrapper = styled.div`
60
+ position: relative;
61
+ `;
62
+
63
+ const ActionMenu = styled.ul`
64
+ padding: 0;
65
+ white-space: nowrap;
66
+ `;
67
+
68
+ const ActionItem = styled.li`
69
+ ${(p) => p.theme.textStyle.uiXS};
70
+ color: ${(p) => p.theme.color.textHighEmphasis};
71
+ padding: ${(p) => p.theme.spacing.xxs} ${(p) => p.theme.spacing.xs};
72
+ cursor: pointer;
73
+
74
+ &:focus {
75
+ background: ${(p) => p.theme.color.overlayFocusPrimary};
76
+ }
77
+
78
+ &:hover {
79
+ background: ${(p) => p.theme.color.overlayHoverPrimary};
80
+ }
81
+ `;
82
+
83
+ const TextType = styled.div`
84
+ ${(p) => p.theme.textStyle.uiXS};
85
+ color: ${(p) => p.theme.color.textMediumEmphasis};
86
+ padding-left: ${(p) => p.theme.spacing.xs};
87
+ padding-top: ${(p) => p.theme.spacing.xxs};
88
+ width: 140px;
89
+ `;
90
+
91
+ const DragAndDropWrapper = styled.div`
92
+ position: absolute;
93
+ top: 0;
94
+ left: 0;
95
+ height: 300px;
96
+ width: 300px;
97
+ `;
98
+
99
+ export {
100
+ Wrapper,
101
+ FilePreview,
102
+ ImageWrapper,
103
+ IconWrapper,
104
+ FileName,
105
+ LeftPanel,
106
+ FileActions,
107
+ ReplaceWrapper,
108
+ ActionMenu,
109
+ ActionItem,
110
+ TextType,
111
+ DragAndDropWrapper
112
+ };
@@ -0,0 +1,180 @@
1
+ import React, { useState } from "react";
2
+ import { connect } from "react-redux";
3
+
4
+ import { IActionMenuOption, IFolder } from "@ax/types";
5
+ import { Icon, Tooltip } from "@ax/components";
6
+ import { useModal } from "@ax/hooks";
7
+ import { fileDriveActions } from "@ax/containers/FileDrive";
8
+ import { DeleteFolderModal, MoveItemModal, RenameFolderModal } from "../atoms";
9
+ import { trimText } from "@ax/helpers";
10
+
11
+ import * as S from "./style";
12
+
13
+ const FolderItem = (props: IProps) => {
14
+ const { folder, isAllowedToDelete, isAllowedToEdit, isAllowedToMove, onClick, onDelete, toggleToast, updateFolder } =
15
+ props;
16
+ const { id, folderName, parentId, site } = folder;
17
+
18
+ const initState = { folderName, parentId };
19
+ const [folderForm, setFolderForm] = useState(initState);
20
+ const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
21
+ const { isOpen: isRenameOpen, toggleModal: toggleRenameModal } = useModal();
22
+ const { isOpen: isMoveOpen, toggleModal: toggleMoveModal } = useModal();
23
+
24
+ const setName = (name: string) => setFolderForm({ ...folderForm, folderName: name });
25
+ const setParent = (folderID: number) => setFolderForm({ ...folderForm, parentId: folderID });
26
+
27
+ let menuOptions: IActionMenuOption[] = [];
28
+
29
+ if (isAllowedToEdit) {
30
+ menuOptions = [
31
+ {
32
+ label: "Rename",
33
+ icon: "edit",
34
+ action: () => toggleRenameModal(),
35
+ },
36
+ ];
37
+
38
+ if (isAllowedToMove) {
39
+ menuOptions = [
40
+ ...menuOptions,
41
+ {
42
+ label: "Move to...",
43
+ icon: "change",
44
+ action: () => toggleMoveModal(),
45
+ },
46
+ ];
47
+ }
48
+ }
49
+
50
+ if (isAllowedToDelete) {
51
+ menuOptions = [
52
+ ...menuOptions,
53
+ {
54
+ label: "Delete",
55
+ icon: "delete",
56
+ action: () => toggleDeleteModal(),
57
+ },
58
+ ];
59
+ }
60
+
61
+ const handleClick = () => onClick(id);
62
+
63
+ const handleDeleteFolder = () => {
64
+ onDelete(folder);
65
+ isDeleteOpen && toggleDeleteModal();
66
+ };
67
+
68
+ const handleUpdateFolder = async (toast = false) => {
69
+ const updated = await updateFolder(
70
+ id,
71
+ { parentId: folderForm.parentId, folderName: folderForm.folderName },
72
+ site || "global"
73
+ );
74
+ if (updated) {
75
+ isRenameOpen && toggleRenameModal();
76
+ toast && toggleToast();
77
+ }
78
+ };
79
+
80
+ const handleModalClose = () => {
81
+ setFolderForm(initState);
82
+ isMoveOpen && toggleMoveModal();
83
+ isRenameOpen && toggleRenameModal();
84
+ };
85
+
86
+ const mainDeleteModalAction = {
87
+ title: "Delete Folder",
88
+ onClick: () => handleDeleteFolder(),
89
+ };
90
+
91
+ const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
92
+
93
+ const mainRenameModalAction = {
94
+ title: "Rename Folder",
95
+ onClick: () => handleUpdateFolder(),
96
+ disabled: folderForm.folderName.trim().length === 0,
97
+ };
98
+
99
+ const secondaryRenameModalAction = { title: "Cancel", onClick: handleModalClose };
100
+
101
+ const mainMoveModalAction = {
102
+ title: "Move",
103
+ onClick: () => handleUpdateFolder(true),
104
+ };
105
+
106
+ const secondaryMoveModalAction = { title: "Cancel", onClick: handleModalClose };
107
+
108
+ const FolderTitle = () =>
109
+ folderName.length > 15 ? (
110
+ <Tooltip content={folderName}>
111
+ <S.Title>{trimText(folderName, 15)}</S.Title>
112
+ </Tooltip>
113
+ ) : (
114
+ <S.Title>{folderName}</S.Title>
115
+ );
116
+
117
+ return (
118
+ <>
119
+ <S.Wrapper onClick={handleClick}>
120
+ <S.IconWrapper>
121
+ <Icon name="project" size="24" />
122
+ </S.IconWrapper>
123
+ <FolderTitle />
124
+ <S.StyledActionMenu icon="more" options={menuOptions} tooltip="File actions" />
125
+ </S.Wrapper>
126
+ {isDeleteOpen && (
127
+ <DeleteFolderModal
128
+ isOpen={isDeleteOpen}
129
+ toggleModal={toggleDeleteModal}
130
+ mainModalAction={mainDeleteModalAction}
131
+ secondaryModalAction={secondaryDeleteModalAction}
132
+ title={folderName}
133
+ />
134
+ )}
135
+ {isRenameOpen && (
136
+ <RenameFolderModal
137
+ isOpen={isRenameOpen}
138
+ toggleModal={handleModalClose}
139
+ mainModalAction={mainRenameModalAction}
140
+ secondaryModalAction={secondaryRenameModalAction}
141
+ value={folderForm.folderName}
142
+ setValue={setName}
143
+ />
144
+ )}
145
+ {isMoveOpen && (
146
+ <MoveItemModal
147
+ isOpen={isMoveOpen}
148
+ toggleModal={handleModalClose}
149
+ mainModalAction={mainMoveModalAction}
150
+ secondaryModalAction={secondaryMoveModalAction}
151
+ folder={folderForm.parentId}
152
+ setFolder={setParent}
153
+ hidden={[folder.id]}
154
+ hideRoot={!parentId}
155
+ />
156
+ )}
157
+ </>
158
+ );
159
+ };
160
+
161
+ interface IProps {
162
+ folder: IFolder;
163
+ isAllowedToEdit: boolean;
164
+ isAllowedToDelete: boolean;
165
+ isAllowedToMove: boolean;
166
+ onClick(folderID: number): void;
167
+ onDelete(folder: IFolder): void;
168
+ toggleToast(): void;
169
+ updateFolder(
170
+ folderID: number,
171
+ data: { parentId: number; folderName: string },
172
+ siteID: number | "global"
173
+ ): Promise<boolean>;
174
+ }
175
+
176
+ const mapDispatchToProps = {
177
+ updateFolder: fileDriveActions.updateFolder,
178
+ };
179
+
180
+ export default connect(null, mapDispatchToProps)(FolderItem);
@@ -0,0 +1,39 @@
1
+ import styled from "styled-components";
2
+ import { ActionMenu } from "@ax/components";
3
+
4
+ const Wrapper = styled.div`
5
+ position: relative;
6
+ background-color: ${(p) => p.theme.color.uiBackground02};
7
+ border: ${(p) => `1px solid ${p.theme.color.uiLine}`};
8
+ border-radius: ${p => p.theme.radii.s};
9
+ display: flex;
10
+ align-items: center;
11
+ min-width: 220px;
12
+ max-width: 280px;
13
+ height: 64px;
14
+ cursor: pointer;
15
+ &:hover {
16
+ background-color: ${(p) => p.theme.color.overlayHoverPrimary};
17
+ }
18
+ `;
19
+
20
+ const IconWrapper = styled.div`
21
+ width: 24px;
22
+ height: 24px;
23
+ margin-left: ${(p) => p.theme.spacing.m};
24
+ `;
25
+
26
+ const Title = styled.div`
27
+ ${(p) => p.theme.textStyle.uiL};
28
+ color: ${(p) => p.theme.colors.textHighEmphasis};
29
+ margin-left: ${(p) => p.theme.spacing.xs};
30
+ `;
31
+
32
+ const StyledActionMenu = styled(ActionMenu)`
33
+ width: 32px;
34
+ display: flex;
35
+ margin-left: auto;
36
+ margin-right: ${(p) => p.theme.spacing.xs};
37
+ `;
38
+
39
+ export { Wrapper, IconWrapper, Title, StyledActionMenu };
@@ -0,0 +1,108 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { connect } from "react-redux";
3
+ import Tree, { mutateTree, RenderItemParams, TreeItem, TreeData, ItemId } from "@atlaskit/tree";
4
+
5
+ import { IFolderTree, IRootState } from "@ax/types";
6
+ import { Icon } from "@ax/components";
7
+ import { trimText } from "@ax/helpers";
8
+
9
+ import { formatItem, normalizeTree } from "./utils";
10
+
11
+ import * as S from "./style";
12
+
13
+ const FolderTree = (props: IProps) => {
14
+ const { folders, folderID, title, hidden, hideRoot, trimNames, createAction, onClick } = props;
15
+
16
+ const [tree, setTree] = useState<TreeData>({ rootId: "root", items: {} });
17
+
18
+ useEffect(() => {
19
+ setTree(normalizeTree(folders, tree, hidden || []));
20
+ // eslint-disable-next-line react-hooks/exhaustive-deps
21
+ }, [folders]);
22
+
23
+ const NewFolderButton = () =>
24
+ createAction ? (
25
+ <S.NewIconWrapper onClick={createAction}>
26
+ <Icon name="newFolder" size="16" />
27
+ </S.NewIconWrapper>
28
+ ) : (
29
+ <></>
30
+ );
31
+
32
+ const getIcon = (item: TreeItem) => {
33
+ if (item.children && item.children.length > 0) {
34
+ return item.isExpanded ? <Icon name="DownArrow" size="16" /> : <Icon name="RightArrow" size="16" />;
35
+ } else {
36
+ return <></>;
37
+ }
38
+ };
39
+
40
+ const renderItem = ({ item, onExpand, onCollapse, provided }: RenderItemParams) => {
41
+ const itemFormatted = formatItem(item, tree);
42
+
43
+ const handleArrowClick = (e: React.MouseEvent<HTMLDivElement>) => {
44
+ e.stopPropagation();
45
+ item.isExpanded ? onCollapse(item.id) : onExpand(item.id);
46
+ };
47
+
48
+ const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
49
+ e.stopPropagation();
50
+ onClick(itemFormatted.id);
51
+ };
52
+
53
+ return (
54
+ <S.ItemWrapper ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}>
55
+ <S.Item onClick={handleClick} selected={folderID === item.id}>
56
+ <S.ArrowWrapper onClick={handleArrowClick}>{getIcon(item)}</S.ArrowWrapper>
57
+ <S.IconWrapper>
58
+ <Icon name="project" size="16" />
59
+ </S.IconWrapper>
60
+ <S.Name>{trimNames ? trimText(itemFormatted.name, 15) : itemFormatted.name}</S.Name>
61
+ <NewFolderButton />
62
+ </S.Item>
63
+ </S.ItemWrapper>
64
+ );
65
+ };
66
+
67
+ const onExpand = (itemId: ItemId) => {
68
+ const newTree = mutateTree(tree, itemId, { isExpanded: true });
69
+ setTree(newTree);
70
+ };
71
+
72
+ const onCollapse = (itemId: ItemId) => {
73
+ const newTree = mutateTree(tree, itemId, { isExpanded: false });
74
+ setTree(newTree);
75
+ };
76
+
77
+ const handleRootClick = () => onClick(0);
78
+
79
+ return (
80
+ <>
81
+ {!hideRoot && (
82
+ <S.Item selected={!folderID} onClick={handleRootClick}>
83
+ <S.RootName>All Documents</S.RootName>
84
+ <NewFolderButton />
85
+ </S.Item>
86
+ )}
87
+ {title && <S.Title>{title}</S.Title>}
88
+ <Tree tree={tree} renderItem={renderItem} onExpand={onExpand} onCollapse={onCollapse} offsetPerLevel={16} />
89
+ </>
90
+ );
91
+ };
92
+
93
+ interface IProps {
94
+ folders: IFolderTree[];
95
+ folderID: number;
96
+ title?: string;
97
+ hidden?: number[];
98
+ hideRoot?: boolean;
99
+ trimNames?: boolean;
100
+ createAction?(): void;
101
+ onClick(folderID: number): void;
102
+ }
103
+
104
+ const mapStateToProps = (state: IRootState) => ({
105
+ folders: state.fileDrive.foldersTree,
106
+ });
107
+
108
+ export default connect(mapStateToProps)(FolderTree);
@@ -0,0 +1,69 @@
1
+ import styled from "styled-components";
2
+
3
+ const ItemWrapper = styled.div``;
4
+
5
+ const NewIconWrapper = styled.div`
6
+ width: 16px;
7
+ height: 16px;
8
+ margin-left: auto;
9
+ `;
10
+
11
+ const Item = styled.div<{ selected: boolean }>`
12
+ display: flex;
13
+ width: 100%;
14
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
15
+ align-items: center;
16
+ cursor: pointer;
17
+ padding: ${(p) => `${p.theme.spacing.xxs} ${p.theme.spacing.xs}`};
18
+ border-radius: ${(p) => p.theme.radii.s};
19
+ background-color: ${(p) => (p.selected ? p.theme.color.overlayPressedPrimary : "transparent")};
20
+ height: 32px;
21
+ overflow: hidden;
22
+ flex-shrink: 0;
23
+ &:hover {
24
+ background-color: ${(p) => (p.selected ? p.theme.color.overlayPressedPrimary : p.theme.color.overlayHoverPrimary)};
25
+ }
26
+ ${NewIconWrapper} {
27
+ opacity: ${(p) => (p.selected ? "1" : "0")};
28
+ }
29
+ `;
30
+
31
+ const Name = styled.div`
32
+ ${(p) => p.theme.textStyle.uiS};
33
+ color: ${(p) => p.theme.color.textMediumEmphasis};
34
+ margin-left: ${(p) => p.theme.spacing.xs};
35
+ white-space: nowrap;
36
+ `;
37
+
38
+ const RootName = styled.div`
39
+ ${(p) => p.theme.textStyle.uiM};
40
+ color: ${(p) => p.theme.color.textHighEmphasis};
41
+ white-space: nowrap;
42
+ `;
43
+
44
+ const IconWrapper = styled.div`
45
+ width: 16px;
46
+ height: 16px;
47
+ svg {
48
+ path {
49
+ fill: ${(p) => p.theme.color.iconMediumEmphasis};
50
+ }
51
+ }
52
+ `;
53
+
54
+ const ArrowWrapper = styled.div`
55
+ width: 16px;
56
+ height: 16px;
57
+ margin-right: ${(p) => p.theme.spacing.xxs};
58
+ white-space: nowrap;
59
+ flex-shrink: 0;
60
+ `;
61
+
62
+ const Title = styled.div`
63
+ ${(p) => p.theme.textStyle.uiM};
64
+ color: ${(p) => p.theme.color.textMediumEmphasis};
65
+ margin-top: ${(p) => p.theme.spacing.xs};
66
+ margin-bottom: ${(p) => p.theme.spacing.xs};
67
+ `;
68
+
69
+ export { ItemWrapper, Item, Name, RootName, IconWrapper, ArrowWrapper, Title, NewIconWrapper };