Skip to content

Commit

Permalink
#276 Improve File Tree Tab
Browse files Browse the repository at this point in the history
- add a FileTreeElement Info Dialog
  • Loading branch information
MaximilianZenz committed Feb 14, 2025
1 parent a3bf3b2 commit adc1d69
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function showContextMenu(x: number, y: number, options: ContextMenuOption
const optionLabel = document.createElement('span');
optionLabel.textContent = o.label;

const optionButton = document.createElement('a');
const optionButton = document.createElement('span');
optionButton.addEventListener('click', o.function);
optionButton.appendChild(optionIcon);
optionButton.appendChild(optionLabel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function DashboardItem(props: {
const [poppedOut, setPoppedOut] = useState(false);

const authorLists = useSelector((state: RootState) => state.authors.authorLists);
const fileLists = useSelector((state: RootState) => state.files.fileLists);
const sprintList = useSelector((state: RootState) => state.sprints.sprintList);
const avaliableDataPlugins = useSelector((state: RootState) => state.settings.database.dataPlugins);

Expand Down Expand Up @@ -89,7 +90,12 @@ function DashboardItem(props: {
setAuthors(authorLists[props.item.dataPluginId]);
}
}, [authorLists, props.item.dataPluginId]);

const [files, setFiles] = useState([]);
useEffect(() => {
if (props.item.dataPluginId !== undefined) {
setFiles(fileLists[props.item.dataPluginId]);
}
}, [fileLists, props.item.dataPluginId]);
const [settings, setSettings] = useState(plugin.defaultSettings);

/**
Expand Down Expand Up @@ -152,6 +158,7 @@ function DashboardItem(props: {
key={plugin.name}
settings={settings}
authorList={authors}
fileList={files}
sprintList={sprintList}
parameters={{
parametersGeneral: ignoreGlobalParameters ? parametersGeneralLocal : parametersGeneralGlobal,
Expand Down Expand Up @@ -189,6 +196,7 @@ function DashboardItem(props: {
key={plugin.name}
settings={settings}
authorList={authors}
fileList={files}
sprintList={sprintList}
parameters={{
parametersGeneral: ignoreGlobalParameters ? parametersGeneralLocal : parametersGeneralGlobal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import SettingsDialog from '../settingsDialog/settingsDialog.tsx';
import NotificationController from '../notificationController/notificationController.tsx';
import EditAuthorDialog from '../tabs/authors/editAuthorDialog/editAuthorDialog.tsx';
import ContextMenu from '../contextMenu/contextMenu.tsx';
import LoadingLocalDatabaseOverlay from "./overlays/loadingLocalDatabaseOverlay/loadingLocalDatabaseOverlay.tsx";
import LoadingLocalDatabaseOverlay from './overlays/loadingLocalDatabaseOverlay/loadingLocalDatabaseOverlay.tsx';
import FileTreeElementInfoDialog from '../tabs/fileTree/fileTreeElementInfoDialog/fileTreeElementInfoDialog.tsx';

function OverlayController() {
return (
Expand All @@ -14,6 +15,7 @@ function OverlayController() {
<SettingsDialog></SettingsDialog>
<NotificationController></NotificationController>
<EditAuthorDialog></EditAuthorDialog>
<FileTreeElementInfoDialog></FileTreeElementInfoDialog>
<ContextMenu></ContextMenu>
<LoadingLocalDatabaseOverlay></LoadingLocalDatabaseOverlay>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
import fileListElementsStyles from './fileListElements.module.scss';
import { FileTreeElementType } from '../../../../../types/data/fileListType.ts';
import FileIcon from '../../../../../assets/file_gray.svg';
import { updateFileListElement } from '../../../../../redux/reducer/data/filesReducer.ts';
import { showFileTreeElementInfo, updateFileListElement } from '../../../../../redux/reducer/data/filesReducer.ts';
import { AppDispatch, useAppDispatch } from '../../../../../redux';
import { formatName } from '../fileListUtilities/fileTreeUtilities.tsx';
import { showContextMenu } from '../../../../contextMenu/contextMenuHelper.ts';
import infoIcon from '../../../../../assets/info_gray.svg';

function FileListFile(props: { file: FileTreeElementType }) {
function FileListFile(props: { file: FileTreeElementType; listOnly?: boolean }) {
const dispatch: AppDispatch = useAppDispatch();

function openFileContextMenu(e: React.MouseEvent<HTMLDivElement>) {
e.preventDefault();
e.stopPropagation();
showContextMenu(e.clientX, e.clientY, [
{
label: 'info',
icon: infoIcon,
function: () => dispatch(showFileTreeElementInfo(props.file)),
},
]);
}

return (
<>
<div className={'flex items-center'}>
<input
type={'checkbox'}
className={'checkbox checkbox-accent checkbox-xs'}
checked={props.file.checked}
onChange={(e) => dispatch(updateFileListElement({ ...props.file, checked: e.target.checked }))}
/>
<div className={fileListElementsStyles.element}>
{(props.listOnly === undefined || !props.listOnly) && (
<input
type={'checkbox'}
className={'checkbox checkbox-accent checkbox-xs'}
checked={props.file.checked}
onChange={(e) => dispatch(updateFileListElement({ ...props.file, checked: e.target.checked }))}
/>
)}
<div
className={fileListElementsStyles.element}
onClick={() => {
if (props.listOnly === true) {
dispatch(showFileTreeElementInfo(props.file));
}
}}
onContextMenu={(e) => {
openFileContextMenu(e);
}}>
<img src={FileIcon} alt={`folder ${props.file.name}`} />
<span>{formatName(props.file.searchTerm, props.file.name)}</span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,32 @@ import FolderIcon from '../../../../../assets/folder_gray.svg';
import FolderOpenIcon from '../../../../../assets/folder_open_gray.svg';
import FileListFile from './fileListFile.tsx';
import { AppDispatch, useAppDispatch } from '../../../../../redux';
import { updateFileListElement } from '../../../../../redux/reducer/data/filesReducer.ts';
import { showFileTreeElementInfo, updateFileListElement } from '../../../../../redux/reducer/data/filesReducer.ts';
import { formatName } from '../fileListUtilities/fileTreeUtilities.tsx';
import { showContextMenu } from '../../../../contextMenu/contextMenuHelper.ts';
import infoIcon from '../../../../../assets/info_gray.svg';

function FileListFolder(props: { folder: FileTreeElementType; foldedOut: boolean }) {
function FileListFolder(props: { folder: FileTreeElementType; foldedOut: boolean; listOnly?: boolean }) {
const dispatch: AppDispatch = useAppDispatch();

function openFolderContextMenu(e: React.MouseEvent<HTMLDivElement>) {
e.preventDefault();
e.stopPropagation();
showContextMenu(e.clientX, e.clientY, [
{
label: 'info',
icon: infoIcon,
function: () => dispatch(showFileTreeElementInfo(props.folder)),
},
]);
}

return (
<>
{props.folder.foldedOut ? (
{props.listOnly === true || props.folder.foldedOut ? (
<>
<div className={'flex items-center'}>
{props.folder.id !== undefined && (
{(props.listOnly === undefined || !props.listOnly) && props.folder.id !== undefined && (
<input
type={'checkbox'}
className={'checkbox checkbox-accent checkbox-xs'}
Expand All @@ -25,7 +39,16 @@ function FileListFolder(props: { folder: FileTreeElementType; foldedOut: boolean
)}
<div
className={fileListElementsStyles.element}
onClick={() => dispatch(updateFileListElement({ ...props.folder, foldedOut: false }))}>
onClick={() => {
if (props.listOnly === true) {
dispatch(showFileTreeElementInfo(props.folder));
} else {
dispatch(updateFileListElement({ ...props.folder, foldedOut: false }));
}
}}
onContextMenu={(e) => {
openFolderContextMenu(e);
}}>
<img src={FolderOpenIcon} alt={`folder open ${props.folder.name}`} />
<span>{formatName(props.folder.searchTerm, props.folder.name)}</span>
</div>
Expand All @@ -46,9 +69,15 @@ function FileListFolder(props: { folder: FileTreeElementType; foldedOut: boolean
.sort((e) => (e.type === FileTreeElementTypeType.Folder ? -1 : 1))
.map((element, i) => {
if (element.type === FileTreeElementTypeType.Folder && element.children) {
return <FileListFolder key={`fileListElement${i}`} folder={element} foldedOut={false}></FileListFolder>;
return (
<FileListFolder
key={`fileListElement${i}`}
folder={element}
foldedOut={false}
listOnly={props.listOnly}></FileListFolder>
);
} else {
return <FileListFile key={`fileListElement${i}`} file={element}></FileListFile>;
return <FileListFile key={`fileListElement${i}`} file={element} listOnly={props.listOnly}></FileListFile>;
}
})}
</div>
Expand All @@ -63,6 +92,9 @@ function FileListFolder(props: { folder: FileTreeElementType; foldedOut: boolean
/>
<div
onClick={() => dispatch(updateFileListElement({ ...props.folder, foldedOut: true }))}
onContextMenu={(e) => {
openFolderContextMenu(e);
}}
className={fileListElementsStyles.element}>
<img src={FolderIcon} alt={`folder ${props.folder.name}`} />
<span>{formatName(props.folder.searchTerm, props.folder.name)}</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useSelector } from 'react-redux';
import { RootState } from '../../../../redux';
import { FileTreeElementType, FileTreeElementTypeType } from '../../../../types/data/fileListType.ts';
import FileListFolder from '../fileList/fileListElements/fileListFolder.tsx';
import { filterFileTree } from '../fileList/fileListUtilities/fileTreeUtilities.tsx';
import FileSearch from '../fileSearch/fileSearch.tsx';
import { useState } from 'react';

function FileTreeElementInfoDialog() {
const selectedFileTreeElement: FileTreeElementType | undefined = useSelector((state: RootState) => state.files.selectedFileTreeElement);
const [fileSearch, setFileSearch] = useState('');

return (
<dialog id={'fileTreeElementInfoDialog'} className={'modal'}>
<div className={'modal-box'}>
{selectedFileTreeElement && (
<>
<h3 id={'informationDialogHeadline'} className={'font-bold text-lg mb-2 underline'}>
{selectedFileTreeElement.name}
</h3>
<h2>Type</h2>
<div>{selectedFileTreeElement.type}</div>
{selectedFileTreeElement.type === FileTreeElementTypeType.File && selectedFileTreeElement.element && (
<>
<h2>Path</h2>
<div>{selectedFileTreeElement.element.path}</div>
<h2>Max Length</h2>
<div>{selectedFileTreeElement.element.maxLength}</div>
<h2>Url</h2>
<div>
<a href={selectedFileTreeElement.element.webUrl}>{selectedFileTreeElement.element.webUrl}</a>
</div>
</>
)}
<h2>File Tree State</h2>
<div>
<span className={'mr-2'}>
{selectedFileTreeElement.foldedOut ? (
<div className="badge badge-accent">folded out</div>
) : (
<div className="badge badge-accent badge-outline">folded in</div>
)}
</span>
<span className={'mr-2'}>
{selectedFileTreeElement.checked ? (
<div className="badge badge-accent">checked</div>
) : (
<div className="badge badge-accent badge-outline">unchecked</div>
)}
</span>
</div>

{selectedFileTreeElement.type === FileTreeElementTypeType.Folder && (
<>
<h2>Folder Content</h2>
<FileSearch setFileSearch={setFileSearch}></FileSearch>
<FileListFolder
folder={filterFileTree(selectedFileTreeElement, fileSearch)}
foldedOut={true}
listOnly={true}></FileListFolder>
</>
)}
</>
)}

<div className={'modal-action'}>
<form method={'dialog'}>
<button className={'btn btn-sm btn-accent'}>Close</button>
</form>
</div>
</div>
<form method="dialog" className="modal-backdrop">
<button>close</button>
</form>
</dialog>
);
}

export default FileTreeElementInfoDialog;
4 changes: 4 additions & 0 deletions binocular-frontend-new/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
@apply text-lg;
@apply underline;
}
a {
@apply text-accent;
@apply underline;
}
}
@tailwind components;
@tailwind utilities;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AuthorType } from '../../types/data/authorType.ts';
import { SprintType } from '../../types/data/sprintType.ts';
import { Reducer, Store } from '@reduxjs/toolkit';
import { ParametersType } from '../../types/parameters/parametersType.ts';
import { FileListElementType } from '../../types/data/fileListType.ts';

export interface VisualizationPlugin<SettingsType> {
name: string;
Expand All @@ -15,6 +16,7 @@ export interface VisualizationPlugin<SettingsType> {
// Not every dataPlugin has all capabilities.
// !!
authorList: AuthorType[]; //list of Users set by Binocular
fileList: FileListElementType[]; //list of Users set by Binocular
sprintList: SprintType[]; //list of Sprints set by Binocular
parameters: ParametersType; // General Parameters Provided By Binocular
chartContainerRef: RefObject<HTMLDivElement>; //forwarded ref that should reference the chart div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ export interface FilesInitialState {
fileLists: { [id: number]: FileListElementType[] };
fileCounts: { [id: number]: number };
dataPluginId: number | undefined;
selectedFileTreeElement?: FileTreeElementType;
}

const initialState: FilesInitialState = {
fileTrees: {},
fileLists: {},
fileCounts: {},
dataPluginId: undefined,
selectedFileTreeElement: undefined,
};

export const filesSlice = createSlice({
Expand Down Expand Up @@ -51,10 +53,14 @@ export const filesSlice = createSlice({
});
localStorage.setItem(`${filesSlice.name}StateV${Config.localStorageVersion}`, JSON.stringify(state));
},
showFileTreeElementInfo: (state, action: PayloadAction<FileTreeElementType>) => {
(document.getElementById('fileTreeElementInfoDialog') as HTMLDialogElement).showModal();
state.selectedFileTreeElement = action.payload;
},
},
});

export const { setFilesDataPluginId, setFileList, updateFileListElement } = filesSlice.actions;
export const { setFilesDataPluginId, setFileList, updateFileListElement, showFileTreeElementInfo } = filesSlice.actions;
export default filesSlice.reducer;

function updateFileTreeRecursive(fileTree: FileTreeElementType, element: FileTreeElementType, checked?: boolean): string[] {
Expand Down
4 changes: 2 additions & 2 deletions binocular-frontend-new/src/types/data/fileListType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ export interface FileTreeElementType {
}

export enum FileTreeElementTypeType {
Folder,
File,
Folder = 'Folder',
File = 'File',
}

0 comments on commit adc1d69

Please sign in to comment.