Repository: vercel/next.js
Pull request: [devtools] add fullscreen mode

https://github.com/user-attachments/assets/bcc10e3b-ecb1-4189-8f5b-17f53bbf8bf6

Changed files:
filename: packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.tsx
patch: @@ -21,6 +21,7 @@ import { Draggable } from '../errors/dev-tools-indicator/draggable'
 import { INDICATOR_PADDING } from '../devtools-indicator/devtools-indicator'
 import { FullScreenIcon } from '../../icons/fullscreen'
 import { Cross } from '../../icons/cross'
+import { MinimizeIcon } from '../../icons/minimize'
 
 export type DevToolsPanelTabType = 'issues' | 'route' | 'settings'
 
@@ -38,6 +39,7 @@ export function DevToolsPanel({
   getSquashedHydrationErrorDetails: (error: Error) => HydrationErrorState | null
 }) {
   const [activeTab, setActiveTab] = useState<DevToolsPanelTabType>('issues')
+  const [isFullscreen, setIsFullscreen] = useState(false)
   const [vertical, horizontal] = state.devToolsPosition.split('-', 2)
 
   const onCloseDevToolsPanel = () => {
@@ -60,19 +62,27 @@ export function DevToolsPanel({
     localStorage.setItem(STORAGE_KEY_SCALE, e.target.value)
   }
 
+  const handleFullscreenToggle = () => {
+    setIsFullscreen((prev) => !prev)
+  }
+
   return (
     <Overlay
       data-nextjs-devtools-panel-overlay
-      style={{
-        [vertical]: `${INDICATOR_PADDING}px`,
-        [horizontal]: `${INDICATOR_PADDING}px`,
-        [vertical === 'top' ? 'bottom' : 'top']: 'auto',
-        [horizontal === 'left' ? 'right' : 'left']: 'auto',
-      }}
+      style={
+        !isFullscreen
+          ? {
+              [vertical]: `${INDICATOR_PADDING}px`,
+              [horizontal]: `${INDICATOR_PADDING}px`,
+              [vertical === 'top' ? 'bottom' : 'top']: 'auto',
+              [horizontal === 'left' ? 'right' : 'left']: 'auto',
+            }
+          : {}
+      }
     >
       {/* TODO: Investigate why onCloseDevToolsPanel on Dialog doesn't close when clicked outside. */}
       <OverlayBackdrop
-        data-nextjs-devtools-panel-overlay-backdrop
+        data-nextjs-devtools-panel-overlay-backdrop={isFullscreen}
         onClick={onCloseDevToolsPanel}
       />
       <Draggable
@@ -128,9 +138,15 @@ export function DevToolsPanel({
                     </button>
                   </div>
                   <div data-nextjs-devtools-panel-header-action-button-group>
-                    {/* TODO: Currently no-op, will add fullscreen toggle. */}
-                    <button data-nextjs-devtools-panel-header-action-button>
-                      <FullScreenIcon width={16} height={16} />
+                    <button
+                      data-nextjs-devtools-panel-header-action-button
+                      onClick={handleFullscreenToggle}
+                    >
+                      {isFullscreen ? (
+                        <MinimizeIcon width={16} height={16} />
+                      ) : (
+                        <FullScreenIcon width={16} height={16} />
+                      )}
                     </button>
                     <button
                       data-nextjs-devtools-panel-header-action-button
@@ -186,11 +202,8 @@ export const DEVTOOLS_PANEL_STYLES = css`
   }
 
   [data-nextjs-devtools-panel-overlay] {
-    padding: initial;
     margin: auto;
     width: 100%;
-    /* TODO: This is for fullscreen mode. */
-    /* top: 10vh; */
 
     @media (max-width: 575px) {
       left: 20px !important;
@@ -212,10 +225,13 @@ export const DEVTOOLS_PANEL_STYLES = css`
   }
 
   [data-nextjs-devtools-panel-overlay-backdrop] {
-    /* TODO: Blur on fullscreen mode. */
     opacity: 0;
   }
 
+  [data-nextjs-devtools-panel-overlay-backdrop='true'] {
+    opacity: 1;
+  }
+
   [data-nextjs-devtools-panel-draggable] {
     /* For responsiveness */
     width: 100%;

filename: packages/next/src/next-devtools/dev-overlay/icons/minimize.tsx
patch: @@ -0,0 +1,19 @@
+export function MinimizeIcon(props: React.SVGProps<SVGSVGElement>) {
+  return (
+    <svg
+      xmlns="http://www.w3.org/2000/svg"
+      width="16"
+      height="16"
+      viewBox="0 0 16 16"
+      fill="none"
+      {...props}
+    >
+      <path
+        fillRule="evenodd"
+        clipRule="evenodd"
+        d="M1 6V4.5H4.5V1H6V5C6 5.55228 5.55228 6 5 6H1ZM11.5 1H10V5C10 5.55228 10.4477 6 11 6H15V4.5H11.5V1ZM15.04 11.5V10H11C10.4477 10 10 10.4477 10 11V15H11.5V11.5H15.04ZM4.5 15H6V11C6 10.4477 5.55228 10 5 10H1V11.5H4.5V15Z"
+        fill="currentColor"
+      />
+    </svg>
+  )
+}