panel actions

This commit is contained in:
2025-10-26 19:14:35 +01:00
parent b163a07e84
commit b6300ffa30
7 changed files with 522 additions and 263 deletions
+201 -14
View File
@@ -32,6 +32,16 @@ import CorrespondentsPanel from './correspondents/CorrespondentsPanel';
import { CORRESPONDENT_ROLES } from './constants/correspondents';
import TagManager from './tag_manager';
import Sidebar from './sidebar/Sidebar';
import {
ChevronsRightIcon,
ChevronsLeftIcon,
ViewListIcon,
ViewGridIcon,
FolderPlusIcon,
RefreshIcon,
ArrowUpIcon,
MinusVerticalIcon,
} from './ui/icons';
import DocumentsTable from './documents/DocumentsTable';
import { AppShellContext, useAppShell } from './appShellContext';
import DocumentViewerRoute from './routes/DocumentViewerRoute';
@@ -311,9 +321,9 @@ const LoginView = ({
const DocumentsLayout = ({ sidebarProps, children }) => (
<main className="documents-main">
<Sidebar {...sidebarProps} />
const DocumentsLayout = ({ sidebarProps, children, sidebarCollapsed }) => (
<main className={`documents-main${sidebarCollapsed ? ' documents-main--sidebar-collapsed' : ''}`}>
{!sidebarCollapsed ? <Sidebar {...sidebarProps} /> : null}
{children}
</main>
);
@@ -337,6 +347,9 @@ const AppLayout = () => {
({ message, variant }) => setStatusMessage(message, variant),
[setStatusMessage],
);
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const collapseSidebar = useCallback(() => setSidebarCollapsed(true), []);
const expandSidebar = useCallback(() => setSidebarCollapsed(false), []);
const reportApiError = useApiError({
onReport: handleApiReport,
});
@@ -4488,6 +4501,7 @@ const AppLayout = () => {
isFilterActive,
onLogout: handleLogout,
status,
onCollapse: collapseSidebar,
};
const resolveThumbnailUrlForDoc = useCallback(
@@ -4847,36 +4861,209 @@ const DocumentsRoute = () => {
skeuoWorkspaceProps,
openTagsModal,
openCorrespondentsModal,
exitSkeuoWorkspace,
} = useAppShell();
const navigate = useNavigate();
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const collapseSidebar = useCallback(() => setSidebarCollapsed(true), []);
const expandSidebar = useCallback(() => setSidebarCollapsed(false), []);
const sidebarPropsWithActions = useMemo(
() => ({
...sidebarProps,
onManageTags: openTagsModal,
onManageCorrespondents: openCorrespondentsModal,
onCollapse: collapseSidebar,
}),
[sidebarProps, openTagsModal, openCorrespondentsModal],
[sidebarProps, openTagsModal, openCorrespondentsModal, collapseSidebar],
);
const {
currentFolderName,
viewMode,
onViewModeChange,
onRequestCreateFolder,
creatingFolder,
onRefresh,
onShowSkeuoWorkspace,
searchResults,
breadcrumbs,
} = documentsTableProps;
const isGridView = viewMode === 'grid';
const showingSearchResults = Array.isArray(searchResults);
const headerTitle = showingSearchResults ? 'Search results' : currentFolderName;
const headerSubtitle = showingSearchResults
? `${searchResults.length} matching document${searchResults.length === 1 ? '' : 's'}`
: null;
const parentBreadcrumb = breadcrumbs && breadcrumbs.length > 1 ? breadcrumbs[breadcrumbs.length - 2] : null;
const handleNavigateParent = parentBreadcrumb
? () => {
const target = parentBreadcrumb.id === 'root'
? '/documents'
: `/documents/folder/${parentBreadcrumb.id}`;
navigate(target);
}
: null;
const detailHasContent = detailPanelProps.selectedDocuments?.length > 0;
const toggleSidebar = sidebarCollapsed ? expandSidebar : collapseSidebar;
const ToggleIcon = sidebarCollapsed ? ChevronsRightIcon : ChevronsLeftIcon;
const toggleLabel = sidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar';
if (workspaceMode === 'skeuo') {
return (
<DocumentsLayout sidebarProps={sidebarPropsWithActions}>
<DocumentsLayout
sidebarProps={sidebarPropsWithActions}
sidebarCollapsed={sidebarCollapsed}
>
<div className="main-content">
<SkeuomorphicWorkspace {...skeuoWorkspaceProps} />
<div className="panel-header main-content__header">
<div className="panel-actions main-content__actions">
{sidebarCollapsed ? (
<button
type="button"
className="icon-button ghost"
onClick={toggleSidebar}
aria-label={toggleLabel}
title={toggleLabel}
>
<ToggleIcon />
</button>
) : null}
{parentBreadcrumb ? (
<button
type="button"
className="icon-button ghost"
onClick={handleNavigateParent}
aria-label="Go to parent folder"
title="Go to parent folder"
>
<ArrowUpIcon />
</button>
) : null}
<h2 className="main-content__title">
{headerTitle}
{headerSubtitle ? (
<span className="main-content__subtitle">{headerSubtitle}</span>
) : null}
</h2>
<div className="spacer" />
<button
type="button"
className="icon-button ghost"
onClick={skeuoWorkspaceProps.onRefresh}
aria-label="Refresh"
title="Refresh"
>
<RefreshIcon />
</button>
<button type="button" className="secondary" onClick={exitSkeuoWorkspace}>
Back to List
</button>
</div>
</div>
<div className="main-content__body">
<SkeuomorphicWorkspace {...skeuoWorkspaceProps} />
</div>
</div>
</DocumentsLayout>
);
}
const detailHasContent = detailPanelProps.selectedDocuments?.length > 0;
return (
<DocumentsLayout sidebarProps={sidebarPropsWithActions}>
<div
className={`main-content main-content--documents${detailHasContent ? ' main-content--has-detail' : ''}`}
>
<DocumentsTable {...documentsTableProps} />
{detailHasContent ? <DetailPanel {...detailPanelProps} /> : null}
<DocumentsLayout
sidebarProps={sidebarPropsWithActions}
sidebarCollapsed={sidebarCollapsed}
>
<div className={`main-content main-content--documents${detailHasContent ? ' main-content--has-detail' : ''}`}>
<div className="panel-header main-content__header">
<div className="panel-actions main-content__actions">
{sidebarCollapsed ? (
<button
type="button"
className="icon-button ghost"
onClick={toggleSidebar}
aria-label={toggleLabel}
title={toggleLabel}
>
<ToggleIcon />
</button>
) : null}
{parentBreadcrumb ? (
<button
type="button"
className="icon-button ghost"
onClick={handleNavigateParent}
aria-label="Go to parent folder"
title="Go to parent folder"
>
<ArrowUpIcon />
</button>
) : null}
<h2 className="main-content__title">
{headerTitle}
{headerSubtitle ? (
<span className="main-content__subtitle">{headerSubtitle}</span>
) : null}
</h2>
<div className="spacer" />
<div className="view-toggle" role="group" aria-label="Change view">
<button
type="button"
className={`view-toggle__button${isGridView ? '' : ' active'}`}
onClick={() => onViewModeChange?.('list')}
aria-pressed={!isGridView}
title="List view"
>
<ViewListIcon className="view-toggle__icon" size={18} />
</button>
<button
type="button"
className={`view-toggle__button${isGridView ? ' active' : ''}`}
onClick={() => onViewModeChange?.('grid')}
aria-pressed={isGridView}
title="Icons view"
>
<ViewGridIcon className="view-toggle__icon" size={18} />
</button>
</div>
<span className="main-content__actions-divider" aria-hidden="true">
<MinusVerticalIcon />
</span>
<button
type="button"
className="icon-button ghost"
onClick={onRequestCreateFolder}
disabled={creatingFolder}
aria-label={creatingFolder ? 'Creating folder…' : 'Create folder'}
title={creatingFolder ? 'Creating folder…' : 'Create folder'}
>
<FolderPlusIcon />
</button>
<button
type="button"
className="icon-button ghost"
onClick={onRefresh}
aria-label="Refresh"
title="Refresh"
>
<RefreshIcon />
</button>
<button className="secondary" type="button" onClick={onShowSkeuoWorkspace}>
Desk View
</button>
</div>
</div>
<div
className={`main-content__body main-content__body--documents${detailHasContent ? ' main-content__body--has-detail' : ''}`}
>
<DocumentsTable {...documentsTableProps} showHeader={false} />
</div>
{detailHasContent ? (
<DetailPanel {...detailPanelProps} />
) : null}
</div>
</DocumentsLayout>
);