Repository: vercel/next.js
Pull request: [devtools] apply draggable to panel ui


https://github.com/user-attachments/assets/b726d417-972d-44f7-aab0-aab86059b1b4



Changed files:
filename: packages/next/src/next-devtools/dev-overlay/components/devtools-indicator/devtools-indicator.tsx
patch: @@ -19,7 +19,7 @@ import {
 } from '../../shared'
 import { Draggable } from '../errors/dev-tools-indicator/draggable'
 
-const INDICATOR_PADDING = 20
+export const INDICATOR_PADDING = 20
 
 export function DevToolsIndicator({
   state,
@@ -59,6 +59,7 @@ export function DevToolsIndicator({
           zIndex: 2147483647,
           [vertical]: `${INDICATOR_PADDING}px`,
           [horizontal]: `${INDICATOR_PADDING}px`,
+          visibility: state.isDevToolsPanelOpen ? 'hidden' : 'visible',
         } as CSSProperties
       }
     >

filename: packages/next/src/next-devtools/dev-overlay/components/devtools-panel/devtools-panel.tsx
patch: @@ -2,42 +2,83 @@ import type { OverlayDispatch, OverlayState } from '../../shared'
 
 import { Dialog, DialogContent, DialogHeader, DialogBody } from '../dialog'
 import { Overlay } from '../overlay/overlay'
-import { ACTION_DEVTOOLS_PANEL_TOGGLE } from '../../shared'
+import {
+  ACTION_DEVTOOLS_PANEL_CLOSE,
+  ACTION_DEVTOOLS_POSITION,
+  STORAGE_KEY_POSITION,
+} from '../../shared'
 import { css } from '../../utils/css'
+import { OverlayBackdrop } from '../overlay'
+import { Draggable } from '../errors/dev-tools-indicator/draggable'
+import { INDICATOR_PADDING } from '../devtools-indicator/devtools-indicator'
 
 export function DevToolsPanel({
-  // eslint-disable-next-line @typescript-eslint/no-unused-vars
   state,
   dispatch,
 }: {
   state: OverlayState
   dispatch: OverlayDispatch
 }) {
+  const [vertical, horizontal] = state.devToolsPosition.split('-', 2)
+
   const onClose = () => {
-    dispatch({ type: ACTION_DEVTOOLS_PANEL_TOGGLE })
+    dispatch({ type: ACTION_DEVTOOLS_PANEL_CLOSE })
   }
 
   return (
-    <Overlay data-nextjs-devtools-panel-overlay>
-      <Dialog
-        data-nextjs-devtools-panel-dialog
-        aria-labelledby="nextjs__container_dev_tools_panel_label"
-        aria-describedby="nextjs__container_dev_tools_panel_desc"
-        onClose={onClose}
+    <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',
+      }}
+    >
+      {/* TODO: Investigate why onClose on Dialog doesn't close when clicked outside. */}
+      <OverlayBackdrop
+        data-nextjs-devtools-panel-overlay-backdrop
+        onClick={onClose}
+      />
+      <Draggable
+        padding={INDICATOR_PADDING}
+        onDragStart={() => {}}
+        position={state.devToolsPosition}
+        setPosition={(p) => {
+          localStorage.setItem(STORAGE_KEY_POSITION, p)
+          dispatch({
+            type: ACTION_DEVTOOLS_POSITION,
+            devToolsPosition: p,
+          })
+        }}
       >
-        <DialogContent>
-          <DialogHeader></DialogHeader>
-          <DialogBody></DialogBody>
-        </DialogContent>
-      </Dialog>
+        <Dialog
+          data-nextjs-devtools-panel-dialog
+          aria-labelledby="nextjs__container_dev_tools_panel_label"
+          aria-describedby="nextjs__container_dev_tools_panel_desc"
+          onClose={onClose}
+        >
+          <DialogContent>
+            <DialogHeader></DialogHeader>
+            <DialogBody></DialogBody>
+          </DialogContent>
+        </Dialog>
+      </Draggable>
     </Overlay>
   )
 }
 
 export const DEVTOOLS_PANEL_STYLES = css`
   [data-nextjs-devtools-panel-overlay] {
     padding: initial;
-    top: 10vh;
+    margin: auto;
+    /* TODO: This is for fullscreen mode. */
+    /* top: 10vh; */
+  }
+
+  [data-nextjs-devtools-panel-overlay-backdrop] {
+    /* TODO: Blur on fullscreen mode. */
+    opacity: 0;
   }
 
   [data-nextjs-devtools-panel-dialog] {

filename: packages/next/src/next-devtools/dev-overlay/components/dialog/dialog.tsx
patch: @@ -16,6 +16,7 @@ const CSS_SELECTORS_TO_EXCLUDE_ON_CLICK_OUTSIDE = [
   '#nextjs-dev-tools-menu',
   '[data-nextjs-error-overlay-nav]',
   '[data-info-popover]',
+  '[data-nextjs-devtools-panel-overlay]',
 ]
 
 const Dialog: React.FC<DialogProps> = function Dialog({

filename: packages/next/src/next-devtools/dev-overlay/components/errors/dev-tools-indicator/draggable.tsx
patch: @@ -1,12 +1,11 @@
+import type { Corners } from '../../../shared'
 import { useRef } from 'react'
 
 interface Point {
   x: number
   y: number
 }
 
-type Corners = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
-
 interface Corner {
   corner: Corners
   translation: Point

filename: packages/next/src/next-devtools/dev-overlay/components/overlay/overlay.tsx
patch: @@ -1,9 +1,7 @@
 import * as React from 'react'
 import { lock, unlock } from './body-locker'
 
-export type OverlayProps = {
-  children?: React.ReactNode
-  className?: string
+export type OverlayProps = React.HTMLAttributes<HTMLDivElement> & {
   fixed?: boolean
 }