Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import type { CSSProperties } from 'react'
import type { OverlayState, OverlayDispatch } from '../../shared'
import type { DevToolsScale } from '../errors/dev-tools-indicator/dev-tools-info/preferences'

import { useState, useRef } from 'react'
import { useState } from 'react'
import { NextLogo } from './next-logo'
import { Toast } from '../toast'
import { NextLogo } from '../errors/dev-tools-indicator/next-logo'
import {
MENU_CURVE,
MENU_DURATION_MS,
Expand All @@ -31,8 +31,6 @@ export function DevToolsIndicator({
const [open, setOpen] = useState(false)
const [position, setPosition] = useState(getInitialPosition())

const triggerRef = useRef<HTMLButtonElement | null>(null)

const [vertical, horizontal] = position.split('-', 2)

const toggleErrorOverlay = () => {
Expand Down Expand Up @@ -66,7 +64,6 @@ export function DevToolsIndicator({
>
{/* Trigger */}
<NextLogo
ref={triggerRef}
aria-haspopup="menu"
aria-expanded={open}
aria-controls="nextjs-dev-tools-menu"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect, useState } from 'react'

export function useMeasureWidth(
ref: React.RefObject<HTMLDivElement | null>
): number {
const [width, setWidth] = useState<number>(0)

useEffect(() => {
const el = ref.current

if (!el) {
return
}

const observer = new ResizeObserver(([{ contentRect }]) => {
setWidth(contentRect.width)
})

observer.observe(el)
return () => observer.disconnect()
}, [ref])

return width
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useEffect, useRef, useState } from 'react'

export function useUpdateAnimation(
issueCount: number,
animationDurationMs = 0
) {
const lastUpdatedTimeStamp = useRef<number | null>(null)
const [animate, setAnimate] = useState(false)

useEffect(() => {
if (issueCount > 0) {
const deltaMs = lastUpdatedTimeStamp.current
? Date.now() - lastUpdatedTimeStamp.current
: -1
lastUpdatedTimeStamp.current = Date.now()

// We don't animate if `issueCount` changes too quickly
if (deltaMs <= animationDurationMs) {
return
}

setAnimate(true)
// It is important to use a CSS transitioned state, not a CSS keyframed animation
// because if the issue count increases faster than the animation duration, it
// will abruptly stop and not transition smoothly back to its original state.
const timeoutId = window.setTimeout(() => {
setAnimate(false)
}, animationDurationMs)

return () => {
clearTimeout(timeoutId)
}
}
}, [issueCount, animationDurationMs])

return animate
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { Meta, StoryObj } from '@storybook/react'
import { NextLogo } from './next-logo'
import { withShadowPortal } from '../../storybook/with-shadow-portal'

const meta: Meta<typeof NextLogo> = {
component: NextLogo,
parameters: {
layout: 'centered',
},
args: {
'aria-label': 'Open Next.js DevTools',
onClick: () => console.log('Clicked!'),
},
decorators: [withShadowPortal],
}

export default meta
type Story = StoryObj<typeof NextLogo>

export const NoIssues: Story = {
args: {
issueCount: 0,
isDevBuilding: false,
isDevRendering: false,
},
}

export const SingleIssue: Story = {
args: {
issueCount: 1,
isDevBuilding: false,
isDevRendering: false,
},
}

export const MultipleIssues: Story = {
args: {
issueCount: 5,
isDevBuilding: false,
isDevRendering: false,
},
}

export const ManyIssues: Story = {
args: {
issueCount: 99,
isDevBuilding: false,
isDevRendering: false,
},
}

export const Building: Story = {
args: {
issueCount: 0,
isDevBuilding: true,
isDevRendering: false,
},
}

export const BuildingWithError: Story = {
args: {
issueCount: 1,
isDevBuilding: true,
isDevRendering: false,
},
}

export const Rendering: Story = {
args: {
issueCount: 0,
isDevBuilding: false,
isDevRendering: true,
},
}

export const RenderingWithError: Story = {
args: {
issueCount: 1,
isDevBuilding: false,
isDevRendering: true,
},
}
Loading