Instantly share code, notes, and snippets.
Created
January 5, 2026 13:57
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save fabiobrasileiroo/b33643761b9f3f29abf72d6b81361d93 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- como usar --> | |
| <!-- {process.env.NODE_ENV === 'development' && <ScreenSizeButton />} | |
| 'use client'; | |
| --> | |
| import { useEffect, useState, useRef } from 'react'; | |
| type ScreenSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl'; | |
| interface Position { | |
| x: number; | |
| y: number; | |
| } | |
| const ScreenSizeButton: React.FC = () => { | |
| const [screenSize, setScreenSize] = useState<ScreenSize | any>('sm'); | |
| const [widthSize, setWidthSize] = useState<number | null>(null); | |
| const [heightSize, setHeightSize] = useState<number | null>(null); | |
| const [position, setPosition] = useState<Position>({ x: 20, y: 20 }); | |
| const [isDragging, setIsDragging] = useState(false); | |
| const [dragStart, setDragStart] = useState<Position>({ x: 0, y: 0 }); | |
| const [showTooltip, setShowTooltip] = useState(false); | |
| const buttonRef = useRef<HTMLButtonElement>(null); | |
| const handleResize = (): void => { | |
| const width = window.innerWidth; | |
| const height = window.innerHeight; | |
| setWidthSize(width); | |
| setHeightSize(height); | |
| if (width < 640) { | |
| setScreenSize('sm'); | |
| } else if (width >= 640 && width < 768) { | |
| setScreenSize('md'); | |
| } else if (width >= 768 && width < 1024) { | |
| setScreenSize('lg'); | |
| } else if (width >= 1024 && width < 1280) { | |
| setScreenSize('xl'); | |
| } else { | |
| setScreenSize('2xl'); | |
| } | |
| }; | |
| // Carregar posição do localStorage | |
| useEffect(() => { | |
| const savedPosition = localStorage.getItem('screenSizeButtonPosition'); | |
| if (savedPosition) { | |
| try { | |
| const parsed = JSON.parse(savedPosition); | |
| setPosition(parsed); | |
| } catch (error) { | |
| console.warn('Erro ao carregar posição do localStorage:', error); | |
| } | |
| } | |
| }, []); | |
| // Salvar posição no localStorage | |
| const savePosition = (newPosition: Position) => { | |
| localStorage.setItem( | |
| 'screenSizeButtonPosition', | |
| JSON.stringify(newPosition) | |
| ); | |
| }; | |
| // Restringir posição dentro da tela | |
| const clampPosition = (pos: Position): Position => { | |
| if (!buttonRef.current) return pos; | |
| const buttonRect = buttonRef.current.getBoundingClientRect(); | |
| const maxX = window.innerWidth - buttonRect.width - 20; // margem de 20px | |
| const maxY = window.innerHeight - buttonRect.height - 20; | |
| return { | |
| x: Math.max(20, Math.min(pos.x, maxX)), // mínimo 20px da borda | |
| y: Math.max(20, Math.min(pos.y, maxY)) | |
| }; | |
| }; | |
| const handleMouseDown = (e: React.MouseEvent) => { | |
| setIsDragging(true); | |
| setDragStart({ | |
| x: e.clientX - position.x, | |
| y: e.clientY - position.y | |
| }); | |
| }; | |
| const handleMouseMove = (e: MouseEvent) => { | |
| if (!isDragging) return; | |
| const newPosition = { | |
| x: e.clientX - dragStart.x, | |
| y: e.clientY - dragStart.y | |
| }; | |
| setPosition(clampPosition(newPosition)); | |
| }; | |
| const handleMouseUp = () => { | |
| if (isDragging) { | |
| setIsDragging(false); | |
| savePosition(position); | |
| } | |
| }; | |
| const handleMouseEnter = () => { | |
| setShowTooltip(true); | |
| }; | |
| const handleMouseLeave = () => { | |
| setShowTooltip(false); | |
| }; | |
| useEffect(() => { | |
| handleResize(); // Executa no carregamento | |
| window.addEventListener('resize', handleResize); | |
| return () => window.removeEventListener('resize', handleResize); | |
| }, []); | |
| useEffect(() => { | |
| if (isDragging) { | |
| document.addEventListener('mousemove', handleMouseMove); | |
| document.addEventListener('mouseup', handleMouseUp); | |
| } else { | |
| document.removeEventListener('mousemove', handleMouseMove); | |
| document.removeEventListener('mouseup', handleMouseUp); | |
| } | |
| return () => { | |
| document.removeEventListener('mousemove', handleMouseMove); | |
| document.removeEventListener('mouseup', handleMouseUp); | |
| }; | |
| }, [isDragging]); | |
| return ( | |
| <> | |
| {showTooltip && ( | |
| <div | |
| className='pointer-events-none fixed z-1000 rounded-md bg-black/80 px-3 py-1 text-xs whitespace-nowrap text-white' | |
| style={{ | |
| left: `${position.x + (buttonRef.current?.offsetWidth || 0) / 2}px`, | |
| top: `${position.y - 28}px`, | |
| transform: 'translateX(-50%)', | |
| boxShadow: | |
| '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)' | |
| }} | |
| > | |
| Clique e arraste para mover | |
| <div className='absolute top-full left-1/2 h-0 w-0 -translate-x-1/2 transform border-t-4 border-r-4 border-l-4 border-transparent border-t-black/80'></div> | |
| </div> | |
| )} | |
| <button | |
| ref={buttonRef} | |
| onMouseDown={handleMouseDown} | |
| onMouseEnter={handleMouseEnter} | |
| onMouseLeave={handleMouseLeave} | |
| className={`fixed z-999 transform rounded-full bg-zinc-800/80 px-4 py-2 text-xs text-white shadow-lg drop-shadow-lg transition-transform hover:scale-105 hover:bg-zinc-700 sm:text-base md:text-sm ${ | |
| isDragging ? 'scale-105 cursor-grabbing' : 'cursor-grab' | |
| }`} | |
| style={{ | |
| right: 'auto', | |
| bottom: 'auto', | |
| left: `${position.x}px`, | |
| top: `${position.y}px`, | |
| userSelect: 'none' | |
| }} | |
| > | |
| {`${widthSize}x-${heightSize}y `}{' '} | |
| <span className='font-bold'>{`${screenSize}`}</span> | |
| </button> | |
| </> | |
| ); | |
| }; | |
| export default ScreenSizeButton; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment