Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ import { useSpreadState } from '../../hooks/useSpreadState';
import { useDispatch } from 'react-redux';
import LookupTable from '../../models/LookupTable';
import { BrowseFilesDialogUI, viewModes } from '.';
import { BrowseFilesDialogContainerProps, initialParameters } from './utils';
import { BrowseFilesDialogContainerProps, contentItemToMediaItem, initialParameters } from './utils';
import { checkPathExistence } from '../../services/content';
import { FormattedMessage } from 'react-intl';
import EmptyState from '../EmptyState';
import BrowseFilesDialogContainerSkeleton from './BrowseFilesDialogContainerSkeleton';
import { getStoredBrowseDialogViewMode, setStoredBrowseDialogViewMode } from '../../utils/state';
import useActiveUser from '../../hooks/useActiveUser';
import { withIndex, withoutIndex } from '../../utils/path';
import { withIndex, withoutIndex, isPagePath } from '../../utils/path';
import { ContentItem } from '../../models/Item';
import { MediaCardViewModes } from '../MediaCard';
import { createPresenceTable } from '../../utils/array';
import { createLookupTable } from '../../utils/object';
Expand All @@ -40,6 +41,7 @@ import { popDialog, pushDialog } from '../../state/actions/dialogStack';
import { nanoid } from 'nanoid';

import { createComponentId } from '../../utils/system';
import { useItemsByPath } from '../../hooks/useItemsByPath';

const defaultPreselectedPaths = [];

Expand Down Expand Up @@ -82,6 +84,7 @@ export function BrowseFilesDialogContainer(props: BrowseFilesDialogContainerProp
(items?.length > 0 && selectedInCurrentPage.length > 0 && selectedInCurrentPage.length < items?.length) ?? false;
const browsePath = path.replace(/\/+$/, '');
const [currentPath, setCurrentPath] = useState(browsePath);
const [treeSelectedPath, setTreeSelectedPath] = useState<string>();
const [fetchingBrowsePathExists, setFetchingBrowsePathExists] = useState(false);
const [browsePathExists, setBrowsePathExists] = useState(false);
const [sortKeys, setSortKeys] = useState([]);
Expand All @@ -90,6 +93,8 @@ export function BrowseFilesDialogContainer(props: BrowseFilesDialogContainerProp
const [fetchingPreselectedItems, setFetchingPreselectedItems] = useState(false);
const disableSubmission = fetchingPreselectedItems || (!selectedArray.length && !selectedCard);
const preselectedLookup = createPresenceTable(preselectedPaths);
const itemsByPath = useItemsByPath();
const isCurrentPathLeaf = Boolean(treeSelectedPath); // treeSelectedPath is set when a leaf page is selected in the tree view
Comment thread
jvega190 marked this conversation as resolved.

const fetchItems = useCallback(() => {
// Since lookahead regex is not supported by opensearch, we are excluding the current path from the search using a
Expand Down Expand Up @@ -195,7 +200,28 @@ export function BrowseFilesDialogContainer(props: BrowseFilesDialogContainerProp
};

const onPathSelected = (path: string) => {
setCurrentPath(withoutIndex(path));
const item = itemsByPath[path];
const nextPath = withoutIndex(path);
setCurrentPath(nextPath);

// If the selected path is a page and has no children, select the page itself.
if (item?.systemType === 'page' && item.childrenCount === 0) {
const mediaItem = items?.find((searchItem) => searchItem.path === item.path) ?? contentItemToMediaItem(item);
multiSelect ? replaceSelectedLookup({ [mediaItem.path]: mediaItem }) : setSelectedCard(mediaItem);
setTreeSelectedPath(withoutIndex(mediaItem.path));
} else {
multiSelect ? replaceSelectedLookup() : setSelectedCard(null);
setTreeSelectedPath(null);
}
};

const replaceSelectedLookup = (lookup?: LookupTable<MediaItem>) => {
const cleared = Object.fromEntries(Object.keys(selectedLookup).map((key) => [key, null]));
if (!lookup || Object.keys(lookup).length === 0) {
setSelectedLookup(cleared);
} else {
setSelectedLookup({ ...cleared, ...lookup });
}
};
Comment thread
jvega190 marked this conversation as resolved.

const onCloseButtonClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => onClose(e, null);
Expand Down Expand Up @@ -266,6 +292,7 @@ export function BrowseFilesDialogContainer(props: BrowseFilesDialogContainerProp
handleSearchKeyword={handleSearchKeyword}
onCloseButtonClick={onCloseButtonClick}
onPathSelected={onPathSelected}
treeSelectedPath={treeSelectedPath}
onSelectButtonClick={onSelectButtonClick}
numOfLoaderItems={numOfLoaderItems}
onRefresh={onRefresh}
Expand All @@ -277,6 +304,7 @@ export function BrowseFilesDialogContainer(props: BrowseFilesDialogContainerProp
onSelectAll={onSelectAll}
allSelected={allSelectedInCurrentPage}
someSelected={someSelectedInCurrentPage}
isCurrentPathLeaf={isCurrentPathLeaf}
/>
) : (
<EmptyState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, { useRef, useState } from 'react';
import React, { useCallback, useRef, useState } from 'react';
import DialogBody from '../DialogBody/DialogBody';
import DialogFooter from '../DialogFooter/DialogFooter';
import SecondaryButton from '../SecondaryButton';
Expand Down Expand Up @@ -51,6 +51,11 @@ import GridViewIcon from '@mui/icons-material/GridOnRounded';
import ReorderRoundedIcon from '@mui/icons-material/ReorderRounded';
import { SORT_AUTO } from '../Search/utils';
import Checkbox from '@mui/material/Checkbox';
import palette from '../../styles/palette';

const TREE_PANEL_DEFAULT_WIDTH = 270;
const TREE_PANEL_MIN_WIDTH = 240;
const TREE_PANEL_MAX_WIDTH = 480;

export function BrowseFilesDialogUI(props: BrowseFilesDialogUIProps) {
// region const { ... } = props;
Expand All @@ -75,6 +80,7 @@ export function BrowseFilesDialogUI(props: BrowseFilesDialogUIProps) {
onCheckboxChecked,
handleSearchKeyword,
onPathSelected,
treeSelectedPath,
onSelectButtonClick,
onChangePage,
onChangeRowsPerPage,
Expand All @@ -89,32 +95,104 @@ export function BrowseFilesDialogUI(props: BrowseFilesDialogUIProps) {
disableSubmission,
allSelected,
someSelected,
onSelectAll
onSelectAll,
isCurrentPathLeaf
} = props;
// endregion
const { formatMessage } = useIntl();
const [sortMenuOpen, setSortMenuOpen] = useState(false);
const buttonRef = useRef(undefined);
const [treePanelWidth, setTreePanelWidth] = useState(TREE_PANEL_DEFAULT_WIDTH);
const [treePanelResizeActive, setTreePanelResizeActive] = useState(false);
const treePanelRef = useRef<HTMLDivElement>(null);

const handleTreePanelMouseMove = useCallback((e: MouseEvent) => {
e.preventDefault();
if (!treePanelRef.current) {
return;
}
const left = treePanelRef.current.getBoundingClientRect().left;
let newWidth = e.clientX - left + 5;
newWidth = Math.min(TREE_PANEL_MAX_WIDTH, Math.max(TREE_PANEL_MIN_WIDTH, newWidth));
setTreePanelWidth(newWidth);
}, []);

const handleTreePanelResizeMouseDown = useCallback(() => {
setTreePanelResizeActive(true);
const handleMouseUp = () => {
setTreePanelResizeActive(false);
document.removeEventListener('mouseup', handleMouseUp, true);
document.removeEventListener('mousemove', handleTreePanelMouseMove, true);
};
document.addEventListener('mouseup', handleMouseUp, true);
document.addEventListener('mousemove', handleTreePanelMouseMove, true);
}, [handleTreePanelMouseMove]);
Comment thread
jvega190 marked this conversation as resolved.
Outdated

return (
<>
<DialogBody sx={{ minHeight: '60vh', padding: 0 }}>
<Box display="flex" sx={{ overflow: 'hidden' }}>
<Box display="flex" sx={{ flex: 1, minHeight: '60vh', overflow: 'hidden' }}>
<Box
ref={treePanelRef}
sx={{
width: '270px',
minWidth: '270px',
padding: '16px',
overflow: 'auto',
rowGap: (theme) => theme.spacing(1)
width: treePanelWidth,
minWidth: treePanelWidth,
flexShrink: 0,
position: 'relative',
display: 'flex',
flexDirection: 'column'
}}
display="flex"
flexDirection="column"
rowGap="20px"
>
<FolderBrowserTreeView rootPath={path} onPathSelected={onPathSelected} selectedPath={currentPath} />
<Box
display="flex"
flexDirection="column"
sx={{
flex: 1,
minHeight: 0,
padding: '16px',
overflow: 'auto',
rowGap: '20px'
}}
>
<FolderBrowserTreeView
rootPath={path}
onPathSelected={onPathSelected}
selectedPath={currentPath}
highlightedPath={treeSelectedPath}
/>
</Box>
<Box
onMouseDown={handleTreePanelResizeMouseDown}
role="separator"
aria-orientation="vertical"
aria-label={formatMessage({ defaultMessage: 'Resize folder panel' })}
sx={{
position: 'absolute',
top: 0,
bottom: 0,
right: 0,
width: '10px',
marginRight: '-5px',
cursor: 'ew-resize',
zIndex: 2,
display: 'flex',
justifyContent: 'center',
alignItems: 'stretch',
'&::before': {
content: '""',
display: 'block',
width: treePanelResizeActive ? '4px' : '2px',
backgroundColor: (theme) => (treePanelResizeActive ? palette.blue.tint : theme.palette.divider),
transition: 'width 200ms, background-color 200ms'
},
'&:hover::before': {
width: '4px',
backgroundColor: palette.blue.tint
}
}}
/>
Comment thread
jvega190 marked this conversation as resolved.
Outdated
</Box>
<Box component="section" sx={{ flexGrow: 1, padding: '16px', overflow: 'auto' }}>
<Box component="section" sx={{ flexGrow: 1, minWidth: 0, padding: '16px', overflow: 'auto' }}>
<Paper
sx={{
paddingLeft: (theme) => theme.spacing(1),
Expand Down Expand Up @@ -355,12 +433,19 @@ export function BrowseFilesDialogUI(props: BrowseFilesDialogUIProps) {
})
: new Array(numOfLoaderItems).fill(null).map((x, i) => <MediaSkeletonCard key={i} />)}
</Box>
{items && items.length === 0 && (
<EmptyState
sxs={{ root: { flexGrow: 1 } }}
title={<FormattedMessage id="browseFilesDialog.noResults" defaultMessage="No items found." />}
/>
)}
{items &&
items.length === 0 &&
(isCurrentPathLeaf ? (
<EmptyState
sxs={{ root: { flexGrow: 1 } }}
title={<FormattedMessage defaultMessage="This item has no children." />}
/>
) : (
<EmptyState
sxs={{ root: { flexGrow: 1 } }}
title={<FormattedMessage id="browseFilesDialog.noResults" defaultMessage="No items found." />}
/>
))}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</Box>
</Box>
</DialogBody>
Expand Down
19 changes: 18 additions & 1 deletion studio-ui/ui/app/src/components/BrowseFilesDialog/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { ElasticParams, MediaItem, SearchItem } from '../../models/Search';
import { ContentItem } from '../../models/Item';
import StandardAction from '../../models/StandardAction';
import { EnhancedDialogProps } from '../EnhancedDialog';
import React from 'react';
Expand Down Expand Up @@ -71,11 +72,13 @@ export interface BrowseFilesDialogUIProps {
disableSubmission?: boolean;
allSelected: boolean;
someSelected: boolean;
isCurrentPathLeaf: boolean;
onCardSelected(item: MediaItem): void;
onPreviewImage?(item: MediaItem): void;
onCheckboxChecked(path: string, selected: boolean): void;
handleSearchKeyword(keyword: string): void;
onPathSelected(path: string): void;
onPathSelected(path: string, item?: ContentItem): void;
treeSelectedPath: string;
onSelectButtonClick(): void;
onChangePage(page: number): void;
onChangeRowsPerPage(event): void;
Expand All @@ -97,3 +100,17 @@ export const initialParameters: ElasticParams = {
};

export const viewModes: MediaCardViewModes[] = ['card', 'compact', 'row'];

export function contentItemToMediaItem(item: ContentItem): MediaItem {
return {
path: item.path,
name: item.label,
type: item.contentTypeId,
mimeType: item.mimeType ?? '',
previewUrl: item.previewUrl ?? '',
lastModifier: item.modifier?.username ?? '',
lastModified: item.dateModified ?? '',
size: 0,
snippets: ''
};
}
Loading