616 lines
20 KiB
TypeScript
616 lines
20 KiB
TypeScript
import { Layout } from 'antd';
|
|
import { useEffect, useMemo, useState } from 'react';
|
|
import { Outlet, useLocation, useNavigate, useSearchParams } from 'react-router-dom';
|
|
import { useGestureLayer, useGesturePageState, useSearchLayer } from '../../../layer';
|
|
import { useAppStore } from '../../../store';
|
|
import { ChatRuntimeBridgeV2 } from '../ChatRuntimeBridgeV2';
|
|
import { syncAppConfigFromServer, useAppConfig } from '../appConfig';
|
|
import { useTokenAccess } from '../tokenAccess';
|
|
import { useUnreadCounts } from '../chatV2/hooks/useUnreadCounts';
|
|
import { matchesShortcut, isTypingTarget, scrollToElement } from '../mainView/utils';
|
|
import { MainContent } from '../MainContent';
|
|
import { MainHeader } from '../MainHeader';
|
|
import { MainSidebar } from '../MainSidebar';
|
|
import { appendPreviewRuntimeSearch, isPreviewRuntime, readPreviewRuntimeDeviceModeFromUrl } from '../previewRuntime';
|
|
import { buildSearchOptions } from './buildSearchOptions';
|
|
import { MainLayoutContextProvider } from './MainLayoutContext';
|
|
import { useMainLayoutData } from './useMainLayoutData';
|
|
import '../MainLayout.css';
|
|
import {
|
|
buildApiMenuItems,
|
|
buildApisPath,
|
|
buildChatMenuItems,
|
|
buildChatPath,
|
|
buildDocsMenuItems,
|
|
buildDocsPath,
|
|
buildPlanMenuItems,
|
|
buildPlansPath,
|
|
buildPlayMenuItems,
|
|
buildPlayPath,
|
|
buildSavedLayoutPath,
|
|
DOCS_DEFAULT_FOLDER,
|
|
PLAN_MENU_ANCHOR_IDS,
|
|
renderSidebarIntro,
|
|
resolveCurrentPageDescriptor,
|
|
resolvePlanQuickFilterMenu,
|
|
resolvePlayOpenKeys,
|
|
resolveSavedLayoutIdFromMenuKey,
|
|
resolveTopMenuPath,
|
|
type ApiSectionKey,
|
|
type ChatSectionKey,
|
|
type PlanSectionKey,
|
|
type PlaySidebarKey,
|
|
type TopMenuKey,
|
|
} from '../routes';
|
|
|
|
const E_READER_IMMERSIVE_BODY_CLASS = 'play-app-e-reader-immersive';
|
|
|
|
function parseRoute(pathname: string): {
|
|
topMenu: TopMenuKey;
|
|
docsMenu: string;
|
|
apiMenu: ApiSectionKey;
|
|
planMenu: PlanSectionKey;
|
|
chatMenu: ChatSectionKey;
|
|
playMenu: PlaySidebarKey;
|
|
} {
|
|
const segments = pathname.split('/').filter(Boolean);
|
|
const [top, first, second] = segments;
|
|
|
|
if (top === 'docs') {
|
|
return {
|
|
topMenu: 'docs',
|
|
docsMenu: first || DOCS_DEFAULT_FOLDER,
|
|
apiMenu: 'components',
|
|
planMenu: 'all',
|
|
chatMenu: 'live',
|
|
playMenu: 'layout',
|
|
};
|
|
}
|
|
|
|
if (top === 'apis' && (first === 'components' || first === 'widgets')) {
|
|
return {
|
|
topMenu: 'apis',
|
|
docsMenu: DOCS_DEFAULT_FOLDER,
|
|
apiMenu: first,
|
|
planMenu: 'all',
|
|
chatMenu: 'live',
|
|
playMenu: 'layout',
|
|
};
|
|
}
|
|
|
|
if (
|
|
top === 'plans' &&
|
|
(first === 'all' ||
|
|
first === 'in-progress' ||
|
|
first === 'done' ||
|
|
first === 'error' ||
|
|
first === 'release' ||
|
|
first === 'release-review' ||
|
|
first === 'board' ||
|
|
first === 'charts' ||
|
|
first === 'schedule' ||
|
|
first === 'history' ||
|
|
first === 'automation-type' ||
|
|
first === 'automation-context' ||
|
|
first === 'token-setting' ||
|
|
first === 'shared-resource' ||
|
|
first === 'server-command')
|
|
) {
|
|
return {
|
|
topMenu: 'plans',
|
|
docsMenu: DOCS_DEFAULT_FOLDER,
|
|
apiMenu: 'components',
|
|
planMenu: first,
|
|
chatMenu: 'live',
|
|
playMenu: 'layout',
|
|
};
|
|
}
|
|
|
|
if (
|
|
top === 'chat' &&
|
|
(first === 'live' ||
|
|
first === 'system' ||
|
|
first === 'changes' ||
|
|
first === 'resources' ||
|
|
first === 'errors' ||
|
|
first === 'manage' ||
|
|
first === 'manage-defaults' ||
|
|
first === 'manage-share')
|
|
) {
|
|
return {
|
|
topMenu: 'chat',
|
|
docsMenu: DOCS_DEFAULT_FOLDER,
|
|
apiMenu: 'components',
|
|
planMenu: 'all',
|
|
chatMenu: first,
|
|
playMenu: 'layout',
|
|
};
|
|
}
|
|
|
|
if (top === 'play' && (first === 'layout' || first === 'draw' || first === 'apps' || first === 'test' || first === 'cbt')) {
|
|
return {
|
|
topMenu: 'play',
|
|
docsMenu: DOCS_DEFAULT_FOLDER,
|
|
apiMenu: 'components',
|
|
planMenu: 'all',
|
|
chatMenu: 'live',
|
|
playMenu: first,
|
|
};
|
|
}
|
|
|
|
if (top === 'play' && first === 'layout-record' && second) {
|
|
return {
|
|
topMenu: 'play',
|
|
docsMenu: DOCS_DEFAULT_FOLDER,
|
|
apiMenu: 'components',
|
|
planMenu: 'all',
|
|
chatMenu: 'live',
|
|
playMenu: `layout-record:${second}`,
|
|
};
|
|
}
|
|
|
|
return {
|
|
topMenu: 'chat',
|
|
docsMenu: DOCS_DEFAULT_FOLDER,
|
|
apiMenu: 'components',
|
|
planMenu: 'all',
|
|
chatMenu: 'live',
|
|
playMenu: 'layout',
|
|
};
|
|
}
|
|
|
|
function isRestrictedTopMenu(topMenu: TopMenuKey, hasAccess: boolean) {
|
|
return !hasAccess && topMenu !== 'docs';
|
|
}
|
|
|
|
function getIsMobileViewport() {
|
|
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
return false;
|
|
}
|
|
|
|
if (readPreviewRuntimeDeviceModeFromUrl() === 'mobile') {
|
|
return true;
|
|
}
|
|
|
|
return window.matchMedia('(max-width: 768px)').matches;
|
|
}
|
|
|
|
function getIsSidebarOverlayViewport(topMenu: TopMenuKey) {
|
|
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') {
|
|
return false;
|
|
}
|
|
|
|
if (readPreviewRuntimeDeviceModeFromUrl() === 'mobile') {
|
|
return true;
|
|
}
|
|
|
|
return window.matchMedia(topMenu === 'chat' ? '(max-width: 1180px)' : '(max-width: 768px)').matches;
|
|
}
|
|
|
|
function resolveSidebarCollapsedForViewport(isSidebarOverlayViewport: boolean) {
|
|
if (!isSidebarOverlayViewport) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function resolveSidebarOpenKeys(
|
|
topMenu: TopMenuKey,
|
|
hasAccess: boolean,
|
|
planMenu: PlanSectionKey,
|
|
chatMenu: ChatSectionKey,
|
|
) {
|
|
if (!hasAccess) {
|
|
return ['docs-group'];
|
|
}
|
|
|
|
if (topMenu === 'docs') {
|
|
return ['docs-group'];
|
|
}
|
|
|
|
if (topMenu === 'apis') {
|
|
return ['api-group'];
|
|
}
|
|
|
|
if (topMenu === 'play') {
|
|
return resolvePlayOpenKeys();
|
|
}
|
|
|
|
if (topMenu === 'plans') {
|
|
if (planMenu === 'server-command') {
|
|
return ['server-group'];
|
|
}
|
|
|
|
if (planMenu === 'token-setting' || planMenu === 'shared-resource') {
|
|
return ['token-management-group'];
|
|
}
|
|
|
|
return ['plan-group'];
|
|
}
|
|
|
|
if (chatMenu === 'errors') {
|
|
return ['app-log-group'];
|
|
}
|
|
|
|
return chatMenu === 'manage' || chatMenu === 'manage-defaults' ? ['chat-manage-group'] : ['chat-group'];
|
|
}
|
|
|
|
export function MainLayout() {
|
|
const previewRuntime = isPreviewRuntime();
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
const [searchParams] = useSearchParams();
|
|
const { currentPage, focusedComponentId, setCurrentPage, setFocusedComponentId } = useAppStore();
|
|
const { hasAccess } = useTokenAccess();
|
|
const appConfig = useAppConfig();
|
|
const { openSearch, setOptions: setSearchOptions } = useSearchLayer();
|
|
const layoutData = useMainLayoutData();
|
|
const routeState = useMemo(() => parseRoute(location.pathname), [location.pathname]);
|
|
const [isMobileViewport, setIsMobileViewport] = useState(() => getIsMobileViewport());
|
|
const [isEReaderImmersiveActive, setIsEReaderImmersiveActive] = useState(() =>
|
|
typeof document !== 'undefined' ? document.body.classList.contains(E_READER_IMMERSIVE_BODY_CLASS) : false,
|
|
);
|
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(() =>
|
|
resolveSidebarCollapsedForViewport(getIsSidebarOverlayViewport(routeState.topMenu)),
|
|
);
|
|
const [isSidebarOverlayViewport, setIsSidebarOverlayViewport] = useState(() =>
|
|
getIsSidebarOverlayViewport(routeState.topMenu),
|
|
);
|
|
const [contentExpanded, setContentExpanded] = useState(false);
|
|
const [sidebarOpenKeys, setSidebarOpenKeys] = useState<string[]>(
|
|
resolveSidebarOpenKeys(routeState.topMenu, hasAccess, routeState.planMenu, routeState.chatMenu),
|
|
);
|
|
const [activePlanQuickFilter, setActivePlanQuickFilter] = useState<
|
|
'working' | 'release-pending-main' | 'automation-failed' | null
|
|
>(routeState.planMenu === 'release' ? 'release-pending-main' : null);
|
|
const [planQuickFilterRequestKey, setPlanQuickFilterRequestKey] = useState(routeState.planMenu === 'release' ? 1 : 0);
|
|
const { componentSampleEntries, widgetSampleEntries, componentSamples, widgetSamples, docsDocuments, savedLayouts, savedLayoutsReady, setSavedLayouts, docFolders } = layoutData;
|
|
const { chatUnreadCount } = useUnreadCounts();
|
|
const navigateWithinApp = (path: string, options?: { replace?: boolean; resetSearch?: boolean }) => {
|
|
const baseSearch = options?.resetSearch ? '' : location.search;
|
|
const nextPath = previewRuntime ? appendPreviewRuntimeSearch(path, baseSearch) : path;
|
|
navigate(nextPath, options?.replace == null ? undefined : { replace: options.replace });
|
|
};
|
|
|
|
|
|
useEffect(() => {
|
|
void syncAppConfigFromServer();
|
|
}, []);
|
|
useEffect(() => {
|
|
if (typeof document === 'undefined') {
|
|
return undefined;
|
|
}
|
|
|
|
const body = document.body;
|
|
const syncEReaderImmersiveState = () => {
|
|
setIsEReaderImmersiveActive(body.classList.contains(E_READER_IMMERSIVE_BODY_CLASS));
|
|
};
|
|
|
|
syncEReaderImmersiveState();
|
|
const observer = new MutationObserver(syncEReaderImmersiveState);
|
|
observer.observe(body, { attributes: true, attributeFilter: ['class'] });
|
|
|
|
return () => {
|
|
observer.disconnect();
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const mediaQuery = window.matchMedia('(max-width: 768px)');
|
|
const updateViewport = () => {
|
|
setIsMobileViewport(getIsMobileViewport());
|
|
};
|
|
|
|
updateViewport();
|
|
mediaQuery.addEventListener('change', updateViewport);
|
|
|
|
return () => {
|
|
mediaQuery.removeEventListener('change', updateViewport);
|
|
};
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const mediaQuery = window.matchMedia(routeState.topMenu === 'chat' ? '(max-width: 1180px)' : '(max-width: 768px)');
|
|
const updateViewport = () => {
|
|
setIsSidebarOverlayViewport(getIsSidebarOverlayViewport(routeState.topMenu));
|
|
};
|
|
|
|
updateViewport();
|
|
mediaQuery.addEventListener('change', updateViewport);
|
|
|
|
return () => {
|
|
mediaQuery.removeEventListener('change', updateViewport);
|
|
};
|
|
}, [routeState.topMenu]);
|
|
|
|
useEffect(() => {
|
|
setSidebarCollapsed(resolveSidebarCollapsedForViewport(isSidebarOverlayViewport));
|
|
}, [isSidebarOverlayViewport, routeState.topMenu]);
|
|
|
|
useEffect(() => {
|
|
setSidebarOpenKeys(resolveSidebarOpenKeys(routeState.topMenu, hasAccess, routeState.planMenu, routeState.chatMenu));
|
|
}, [hasAccess, routeState.chatMenu, routeState.planMenu, routeState.topMenu]);
|
|
|
|
useEffect(() => {
|
|
if (docFolders.length > 0 && routeState.topMenu === 'docs' && !docFolders.includes(routeState.docsMenu)) {
|
|
navigateWithinApp(buildDocsPath(docFolders[0]), { replace: true });
|
|
}
|
|
}, [docFolders, navigate, location.search, previewRuntime, routeState.docsMenu, routeState.topMenu]);
|
|
|
|
useEffect(() => {
|
|
const savedLayoutId = resolveSavedLayoutIdFromMenuKey(routeState.playMenu);
|
|
|
|
if (savedLayoutId && savedLayoutsReady && !savedLayouts.some((record) => record.id === savedLayoutId)) {
|
|
navigateWithinApp(buildPlayPath('layout'), { replace: true });
|
|
}
|
|
}, [location.search, navigate, previewRuntime, routeState.playMenu, savedLayouts, savedLayoutsReady]);
|
|
|
|
useEffect(() => {
|
|
if (!isRestrictedTopMenu(routeState.topMenu, hasAccess)) {
|
|
return;
|
|
}
|
|
|
|
navigate(`${buildDocsPath(DOCS_DEFAULT_FOLDER)}${location.search}`, { replace: true });
|
|
}, [hasAccess, location.search, navigate, routeState.topMenu]);
|
|
|
|
useEffect(() => {
|
|
if (routeState.topMenu !== 'plans') {
|
|
setActivePlanQuickFilter(null);
|
|
return;
|
|
}
|
|
|
|
if (routeState.planMenu === 'release') {
|
|
setActivePlanQuickFilter('release-pending-main');
|
|
return;
|
|
}
|
|
|
|
setActivePlanQuickFilter((current) => (current === 'working' || current === 'automation-failed' ? current : null));
|
|
}, [routeState.planMenu, routeState.topMenu]);
|
|
|
|
const gestureLayer = useMemo(
|
|
() => ({
|
|
id: 'main-layout',
|
|
enabled:
|
|
!isEReaderImmersiveActive &&
|
|
!(isMobileViewport && routeState.topMenu === 'docs' && routeState.docsMenu === DOCS_DEFAULT_FOLDER),
|
|
gestures: [
|
|
{
|
|
id: 'mobile-top-right-pull-alert',
|
|
activeStates: ['anyway'],
|
|
mobileOnly: true,
|
|
trigger: 'pull-down-top-right' as const,
|
|
onTrigger: () => {
|
|
openSearch();
|
|
},
|
|
},
|
|
],
|
|
}),
|
|
[isEReaderImmersiveActive, isMobileViewport, openSearch, routeState.docsMenu, routeState.topMenu],
|
|
);
|
|
|
|
useGesturePageState('anyway');
|
|
useGestureLayer(gestureLayer);
|
|
|
|
useEffect(() => {
|
|
const handleWindowKeyDown = (event: KeyboardEvent) => {
|
|
if (event.repeat || isTypingTarget(event.target) || isEReaderImmersiveActive) {
|
|
return;
|
|
}
|
|
|
|
|
|
if (matchesShortcut(event, appConfig.gestureShortcuts.openSearch)) {
|
|
event.preventDefault();
|
|
openSearch();
|
|
}
|
|
};
|
|
|
|
window.addEventListener('keydown', handleWindowKeyDown);
|
|
|
|
return () => {
|
|
window.removeEventListener('keydown', handleWindowKeyDown);
|
|
};
|
|
}, [
|
|
appConfig.gestureShortcuts.openSearch,
|
|
isEReaderImmersiveActive,
|
|
openSearch,
|
|
]);
|
|
|
|
const selectedDocs = useMemo(
|
|
() => docsDocuments.filter((document) => document.folder === routeState.docsMenu),
|
|
[docsDocuments, routeState.docsMenu],
|
|
);
|
|
|
|
const searchOptions = useMemo(
|
|
() =>
|
|
buildSearchOptions({
|
|
componentSamples,
|
|
widgetSamples,
|
|
docFolders,
|
|
docsDocuments,
|
|
savedLayouts,
|
|
hasAccess,
|
|
navigateTo: (path) => {
|
|
navigateWithinApp(path);
|
|
},
|
|
setFocusedComponentId,
|
|
requestPlanQuickFilter: (filter) => {
|
|
setActivePlanQuickFilter(filter);
|
|
setPlanQuickFilterRequestKey((previous) => previous + 1);
|
|
},
|
|
}),
|
|
[
|
|
componentSamples,
|
|
docFolders,
|
|
docsDocuments,
|
|
hasAccess,
|
|
location.search,
|
|
navigate,
|
|
previewRuntime,
|
|
savedLayouts,
|
|
setFocusedComponentId,
|
|
widgetSamples,
|
|
],
|
|
);
|
|
|
|
useEffect(() => {
|
|
setSearchOptions(searchOptions);
|
|
}, [searchOptions, setSearchOptions]);
|
|
|
|
useEffect(() => {
|
|
setCurrentPage(
|
|
resolveCurrentPageDescriptor({
|
|
topMenu: routeState.topMenu,
|
|
docsMenu: routeState.docsMenu,
|
|
apiMenu: routeState.apiMenu,
|
|
planMenu: routeState.planMenu,
|
|
chatMenu: routeState.chatMenu,
|
|
playMenu: routeState.playMenu,
|
|
savedLayouts,
|
|
}),
|
|
);
|
|
}, [routeState, savedLayouts, setCurrentPage]);
|
|
|
|
const currentDocsFolder = docFolders.includes(routeState.docsMenu) ? routeState.docsMenu : docFolders[0] ?? DOCS_DEFAULT_FOLDER;
|
|
const sidebarIntro = renderSidebarIntro(routeState.topMenu);
|
|
const apiMenuItems = useMemo(() => buildApiMenuItems(), []);
|
|
const docsMenuItems = useMemo(() => buildDocsMenuItems(docFolders), [docFolders]);
|
|
const planMenuItems = useMemo(() => buildPlanMenuItems(hasAccess), [hasAccess]);
|
|
const chatMenuItems = useMemo(() => buildChatMenuItems(hasAccess, chatUnreadCount), [chatUnreadCount, hasAccess]);
|
|
const playMenuItems = useMemo(() => buildPlayMenuItems(savedLayouts), [savedLayouts]);
|
|
const activePlayAppId = routeState.topMenu === 'play' && routeState.playMenu === 'apps'
|
|
? searchParams.get('app')?.trim() ?? ''
|
|
: '';
|
|
const isPlayAppFullscreen = activePlayAppId.length > 0;
|
|
const initialSelectedPlanId = Number(searchParams.get('planId'));
|
|
const initialSelectedWorkId = searchParams.get('workId');
|
|
|
|
return (
|
|
<MainLayoutContextProvider
|
|
value={{
|
|
topMenu: routeState.topMenu,
|
|
selectedDocsMenu: routeState.docsMenu,
|
|
selectedApiMenu: routeState.apiMenu,
|
|
selectedPlanMenu: routeState.planMenu,
|
|
selectedChatMenu: routeState.chatMenu,
|
|
selectedPlayMenu: routeState.playMenu,
|
|
activePlanQuickFilter,
|
|
planQuickFilterRequestKey,
|
|
initialSelectedPlanId: Number.isFinite(initialSelectedPlanId) ? initialSelectedPlanId : null,
|
|
initialSelectedWorkId,
|
|
selectedDocs,
|
|
docsDocuments,
|
|
componentSampleEntries,
|
|
widgetSampleEntries,
|
|
componentSamples,
|
|
widgetSamples,
|
|
savedLayouts,
|
|
savedLayoutsReady,
|
|
setSavedLayouts,
|
|
searchOptions,
|
|
}}
|
|
>
|
|
<Layout className={`app-shell app-shell--docs-api${previewRuntime ? ' app-shell--preview-runtime' : ''}`}>
|
|
{routeState.topMenu === 'chat' || isPlayAppFullscreen ? null : <ChatRuntimeBridgeV2 />}
|
|
{contentExpanded || isPlayAppFullscreen ? null : (
|
|
<MainHeader
|
|
activeTopMenu={routeState.topMenu}
|
|
sidebarCollapsed={sidebarCollapsed}
|
|
contentExpanded={contentExpanded}
|
|
isMobileViewport={isMobileViewport}
|
|
onToggleSidebar={() => {
|
|
setSidebarCollapsed((previous) => !previous);
|
|
}}
|
|
onToggleContentExpanded={() => {
|
|
setContentExpanded((previous) => !previous);
|
|
}}
|
|
onChangeTopMenu={(menu) => {
|
|
navigateWithinApp(resolveTopMenuPath(menu, currentDocsFolder));
|
|
setSidebarCollapsed(resolveSidebarCollapsedForViewport(getIsSidebarOverlayViewport(menu)));
|
|
}}
|
|
onOpenSearch={() => {
|
|
openSearch();
|
|
}}
|
|
onOpenPlanQuickFilter={(filter) => {
|
|
const targetPlanMenu = resolvePlanQuickFilterMenu(filter);
|
|
setActivePlanQuickFilter(filter);
|
|
setPlanQuickFilterRequestKey((previous) => previous + 1);
|
|
navigateWithinApp(buildPlansPath(targetPlanMenu));
|
|
setSidebarCollapsed(resolveSidebarCollapsedForViewport(getIsSidebarOverlayViewport('plans')));
|
|
scrollToElement(PLAN_MENU_ANCHOR_IDS[targetPlanMenu] ?? 'plan-menu-all');
|
|
}}
|
|
/>
|
|
)}
|
|
|
|
<Layout className="app-shell__body">
|
|
{contentExpanded || isPlayAppFullscreen || (isSidebarOverlayViewport && sidebarCollapsed) ? null : (
|
|
<MainSidebar
|
|
activeTopMenu={routeState.topMenu}
|
|
hasAccess={hasAccess}
|
|
sidebarCollapsed={sidebarCollapsed}
|
|
isMobileViewport={isMobileViewport}
|
|
openKeys={sidebarOpenKeys}
|
|
apiMenuItems={apiMenuItems}
|
|
docsMenuItems={docsMenuItems}
|
|
planMenuItems={planMenuItems}
|
|
chatMenuItems={chatMenuItems}
|
|
playMenuItems={playMenuItems}
|
|
selectedApiMenu={routeState.apiMenu}
|
|
selectedDocsMenu={routeState.docsMenu}
|
|
selectedPlanMenu={routeState.planMenu}
|
|
selectedChatMenu={routeState.chatMenu}
|
|
selectedPlayMenu={routeState.playMenu}
|
|
onOpenKeysChange={setSidebarOpenKeys}
|
|
onSelectApiMenu={(key) => {
|
|
navigateWithinApp(buildApisPath(key as ApiSectionKey));
|
|
if (isSidebarOverlayViewport) {
|
|
setSidebarCollapsed(true);
|
|
}
|
|
}}
|
|
onSelectDocsMenu={(key) => {
|
|
navigateWithinApp(buildDocsPath(key));
|
|
if (isSidebarOverlayViewport) {
|
|
setSidebarCollapsed(true);
|
|
}
|
|
}}
|
|
onSelectPlanMenu={(key) => {
|
|
setActivePlanQuickFilter(key === 'release' ? 'release-pending-main' : null);
|
|
setPlanQuickFilterRequestKey((previous) => previous + 1);
|
|
navigateWithinApp(buildPlansPath(key));
|
|
if (isSidebarOverlayViewport) {
|
|
setSidebarCollapsed(true);
|
|
}
|
|
}}
|
|
onSelectChatMenu={(key) => {
|
|
navigateWithinApp(buildChatPath(key), { resetSearch: true });
|
|
if (isSidebarOverlayViewport) {
|
|
setSidebarCollapsed(true);
|
|
}
|
|
}}
|
|
onSelectPlayMenu={(key) => {
|
|
const savedLayoutId = resolveSavedLayoutIdFromMenuKey(key);
|
|
navigateWithinApp(savedLayoutId ? buildSavedLayoutPath(savedLayoutId) : buildPlayPath(key));
|
|
if (isSidebarOverlayViewport) {
|
|
setSidebarCollapsed(true);
|
|
}
|
|
}}
|
|
introColor={sidebarIntro.color}
|
|
introTag={sidebarIntro.tag}
|
|
introDescription={sidebarIntro.description}
|
|
/>
|
|
)}
|
|
|
|
<MainContent
|
|
contentExpanded={contentExpanded}
|
|
sidebarOverlayActive={!previewRuntime && isSidebarOverlayViewport && !sidebarCollapsed}
|
|
disableWindowLayer={previewRuntime}
|
|
onToggleContentExpanded={() => setContentExpanded((previous) => !previous)}
|
|
>
|
|
<Outlet />
|
|
</MainContent>
|
|
</Layout>
|
|
</Layout>
|
|
</MainLayoutContextProvider>
|
|
);
|
|
}
|