diff --git a/packages/app/src/SessionSidePanel.tsx b/packages/app/src/SessionSidePanel.tsx
index 94895c7994..15a8ad907c 100644
--- a/packages/app/src/SessionSidePanel.tsx
+++ b/packages/app/src/SessionSidePanel.tsx
@@ -1,4 +1,4 @@
-import { useMemo, useState } from 'react';
+import { useCallback, useMemo, useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard';
import { useHotkeys } from 'react-hotkeys-hook';
import {
@@ -8,15 +8,23 @@ import {
TSessionSource,
TTraceSource,
} from '@hyperdx/common-utils/dist/types';
-import { ActionIcon, Button, Drawer } from '@mantine/core';
+import { ActionIcon, Box, Button, Drawer, Group } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconLink, IconX } from '@tabler/icons-react';
+import {
+ DrawerFullWidthToggle,
+ getInitialDrawerWidthPercent,
+} from '@/components/DrawerUtils';
+import useResizable from '@/hooks/useResizable';
+
import { Session } from './sessions';
import SessionSubpanel from './SessionSubpanel';
import { formatDistanceToNowStrictShort } from './utils';
import { ZIndexContext } from './zIndex';
+import styles from '@/../styles/LogSidePanel.module.scss';
+
export default function SessionSidePanel({
traceSource,
sessionSource,
@@ -42,6 +50,14 @@ export default function SessionSidePanel({
// Keep track of sub-drawers so we can disable closing this root drawer
const [subDrawerOpen, setSubDrawerOpen] = useState(false);
+ const { size, setSize, startResize } = useResizable(
+ getInitialDrawerWidthPercent(),
+ );
+ const isFullWidth = size >= 99;
+ const toggleFullWidth = useCallback(() => {
+ setSize(isFullWidth ? getInitialDrawerWidthPercent() : 100);
+ }, [isFullWidth, setSize]);
+
useHotkeys(
['esc'],
() => {
@@ -68,7 +84,7 @@ export default function SessionSidePanel({
}
}}
position="right"
- size="82vw"
+ size={`${size}vw`}
withCloseButton={false}
zIndex={zIndex}
styles={{
@@ -83,7 +99,9 @@ export default function SessionSidePanel({
+
@@ -102,7 +120,11 @@ export default function SessionSidePanel({
{session?.sessionCount} Events
-
+
+
{
@@ -124,7 +146,7 @@ export default function SessionSidePanel({
-
+
{sessionId != null ? (
diff --git a/packages/app/src/components/DBRowSidePanel.tsx b/packages/app/src/components/DBRowSidePanel.tsx
index d77020865f..9efff30ed3 100644
--- a/packages/app/src/components/DBRowSidePanel.tsx
+++ b/packages/app/src/components/DBRowSidePanel.tsx
@@ -45,6 +45,7 @@ import { RowDataPanel, useRowData } from './DBRowDataPanel';
import { RowOverviewPanel } from './DBRowOverviewPanel';
import { DBSessionPanel, useSessionId } from './DBSessionPanel';
import DBTracePanel from './DBTracePanel';
+import { getInitialDrawerWidthPercent } from './DrawerUtils';
import styles from '@/../styles/LogSidePanel.module.scss';
@@ -109,8 +110,12 @@ const DBRowSidePanel = ({
onClose,
breadcrumbPath,
onBreadcrumbClick,
+ isFullWidth,
+ onToggleFullWidth,
}: DBRowSidePanelProps & {
setSubDrawerOpen: Dispatch>;
+ isFullWidth?: boolean;
+ onToggleFullWidth?: () => void;
}) => {
const {
data: rowData,
@@ -329,6 +334,8 @@ const DBRowSidePanel = ({
rowData={normalizedRow}
breadcrumbPath={breadcrumbPath}
onBreadcrumbClick={handleBreadcrumbClick}
+ isFullWidth={isFullWidth}
+ onToggleFullWidth={onToggleFullWidth}
/>
{/* = 99;
+ const toggleFullWidth = useCallback(() => {
+ setSize(isFullWidth ? getInitialDrawerWidthPercent() : 100);
+ }, [isFullWidth, setSize]);
// Keep track of sub-drawers so we can disable closing this root drawer
const [subDrawerOpen, setSubDrawerOpen] = useState(false);
@@ -629,6 +642,8 @@ export default function DBRowSidePanelErrorBoundary({
breadcrumbPath={breadcrumbPath}
setSubDrawerOpen={setSubDrawerOpen}
onBreadcrumbClick={onBreadcrumbClick}
+ isFullWidth={isFullWidth}
+ onToggleFullWidth={isNestedPanel ? undefined : toggleFullWidth}
/>
diff --git a/packages/app/src/components/DBRowSidePanelHeader.tsx b/packages/app/src/components/DBRowSidePanelHeader.tsx
index 7be8126fe5..8c6f79bf66 100644
--- a/packages/app/src/components/DBRowSidePanelHeader.tsx
+++ b/packages/app/src/components/DBRowSidePanelHeader.tsx
@@ -30,6 +30,7 @@ import {
HighlightedAttribute,
} from './DBHighlightedAttributesList';
import { RowSidePanelContext } from './DBRowSidePanel';
+import { DrawerFullWidthToggle } from './DrawerUtils';
import LogLevel from './LogLevel';
const isValidDate = (date: Date) => 'getTime' in date && !isNaN(date.getTime());
@@ -134,6 +135,8 @@ export default function DBRowSidePanelHeader({
rowData,
breadcrumbPath,
onBreadcrumbClick,
+ isFullWidth,
+ onToggleFullWidth,
}: {
date: Date;
mainContent?: string;
@@ -143,6 +146,8 @@ export default function DBRowSidePanelHeader({
rowData?: Record;
breadcrumbPath?: BreadcrumbPath;
onBreadcrumbClick?: BreadcrumbNavigationCallback;
+ isFullWidth?: boolean;
+ onToggleFullWidth?: () => void;
}) {
const [bodyExpanded, setBodyExpanded] = React.useState(false);
const { generateSearchUrl } = useContext(RowSidePanelContext);
@@ -212,18 +217,26 @@ export default function DBRowSidePanelHeader({
/>
{/* Event timestamp and severity */}
-
- {severityText && }
- {severityText && isValidDate(date) && (
-
- ·
-
- )}
- {isValidDate(date) && (
-
- ·{' '}
- {formatDistanceToNowStrictShort(date)} ago
-
+
+
+ {severityText && }
+ {severityText && isValidDate(date) && (
+
+ ·
+
+ )}
+ {isValidDate(date) && (
+
+ ·{' '}
+ {formatDistanceToNowStrictShort(date)} ago
+
+ )}
+
+ {onToggleFullWidth && (
+
)}
{mainContent ? (
diff --git a/packages/app/src/components/DrawerUtils.tsx b/packages/app/src/components/DrawerUtils.tsx
index ef524d4af4..984dc027b1 100644
--- a/packages/app/src/components/DrawerUtils.tsx
+++ b/packages/app/src/components/DrawerUtils.tsx
@@ -1,6 +1,56 @@
import * as React from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
-import { Box, CloseButton, Group, Text } from '@mantine/core';
+import {
+ ActionIcon,
+ Box,
+ CloseButton,
+ Group,
+ Text,
+ Tooltip,
+} from '@mantine/core';
+import {
+ IconLayoutSidebarRightCollapse,
+ IconLayoutSidebarRightExpand,
+} from '@tabler/icons-react';
+
+const LARGE_SCREEN_BREAKPOINT_PX = 1440;
+const LARGE_SCREEN_DRAWER_WIDTH_PERCENT = 60;
+const SMALL_SCREEN_DRAWER_WIDTH_PERCENT = 80;
+
+export function getInitialDrawerWidthPercent(): number {
+ if (typeof window === 'undefined') {
+ return SMALL_SCREEN_DRAWER_WIDTH_PERCENT;
+ }
+
+ if (window.innerWidth > LARGE_SCREEN_BREAKPOINT_PX) {
+ return LARGE_SCREEN_DRAWER_WIDTH_PERCENT;
+ }
+
+ return SMALL_SCREEN_DRAWER_WIDTH_PERCENT;
+}
+
+export const DrawerFullWidthToggle = React.memo<{
+ isFullWidth?: boolean;
+ onToggle: () => void;
+}>(({ isFullWidth, onToggle }) => {
+ const label = isFullWidth ? 'Collapse panel width' : 'Expand panel width';
+ return (
+
+
+ {isFullWidth ? (
+
+ ) : (
+
+ )}
+
+
+ );
+});
export const DrawerHeader = React.memo<{
header?: React.ReactNode;
diff --git a/packages/app/src/hooks/useResizable.tsx b/packages/app/src/hooks/useResizable.tsx
index fcc4ac51ce..2b927a06c7 100644
--- a/packages/app/src/hooks/useResizable.tsx
+++ b/packages/app/src/hooks/useResizable.tsx
@@ -76,6 +76,7 @@ function useResizable(
return {
size: sizePercentage,
+ setSize: setSizePercentage,
startResize,
};
}