-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Update image handling #14564
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update image handling #14564
Changes from 1 commit
76137dd
222fbb2
1d4c770
970049b
2e98bdc
041b40d
b5470a6
12ca95c
a636625
11227e4
528b31a
0bd0bae
37fd8ce
19db25b
7b781f4
106d776
50455a3
6c6b8a1
19e598e
c74e563
9bffc3c
4065b0c
c68596c
30c260b
5a7c1be
7ff34e8
19b1e92
02a849e
ca1f744
d6081b0
896228e
052325c
0770228
4be0b7e
689e57f
5dc9bcd
b2fecbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,10 @@ | ||
'use client'; | ||
|
||
import {useState} from 'react'; | ||
import * as Dialog from '@radix-ui/react-dialog'; | ||
import Image from 'next/image'; | ||
|
||
import {Lightbox} from 'sentry-docs/components/lightbox'; | ||
import {isAllowedRemoteImage} from 'sentry-docs/config/images'; | ||
import {isAllowedRemoteImage, isExternalImage} from 'sentry-docs/config/images'; | ||
|
||
interface ImageLightboxProps | ||
extends Omit< | ||
|
@@ -19,15 +18,9 @@ interface ImageLightboxProps | |
width?: number; | ||
} | ||
|
||
// Helper functions | ||
const isExternalImage = (src: string): boolean => | ||
src.startsWith('http') || src.startsWith('//'); | ||
|
||
const getImageUrl = (src: string, imgPath: string): string => | ||
isExternalImage(src) ? src : imgPath; | ||
|
||
// Using shared allowlist logic from src/config/images | ||
|
||
type ValidDimensions = { | ||
height: number; | ||
width: number; | ||
|
@@ -59,46 +52,36 @@ export function ImageLightbox({ | |
}: ImageLightboxProps) { | ||
const [open, setOpen] = useState(false); | ||
|
||
// Check if we should use Next.js Image or regular img | ||
// Use Next.js Image for internal images with valid dimensions | ||
// Use regular img for external images or when dimensions are invalid/missing | ||
const dimensions = getValidDimensions(width, height); | ||
const shouldUseNextImage = | ||
!!dimensions && (!isExternalImage(src) || isAllowedRemoteImage(src)); | ||
|
||
const handleModifierClick = (e: React.MouseEvent) => { | ||
// If Ctrl/Cmd+click, open image in new tab instead of lightbox | ||
if (e.ctrlKey || e.metaKey) { | ||
const openInNewTab = () => { | ||
window.open(getImageUrl(src, imgPath), '_blank'); | ||
}; | ||
|
||
const handleClick = (e: React.MouseEvent) => { | ||
// Middle-click or Ctrl/Cmd+click opens in new tab | ||
if (e.button === 1 || e.ctrlKey || e.metaKey) { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
const url = getImageUrl(src, imgPath); | ||
const newWindow = window.open(url, '_blank'); | ||
if (newWindow) { | ||
newWindow.opener = null; // Security: prevent opener access | ||
} | ||
openInNewTab(); | ||
return; | ||
} | ||
// Normal click will be handled by Dialog.Trigger | ||
// Regular click falls through to Dialog.Trigger | ||
}; | ||
|
||
const handleModifierKeyDown = (e: React.KeyboardEvent) => { | ||
// Handle Ctrl/Cmd+Enter or Ctrl/Cmd+Space to open in new tab | ||
const handleKeyDown = (e: React.KeyboardEvent) => { | ||
if ((e.key === 'Enter' || e.key === ' ') && (e.ctrlKey || e.metaKey)) { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
const url = getImageUrl(src, imgPath); | ||
const newWindow = window.open(url, '_blank'); | ||
if (newWindow) { | ||
newWindow.opener = null; // Security: prevent opener access | ||
} | ||
openInNewTab(); | ||
} | ||
// Normal key presses will be handled by Dialog.Trigger | ||
// Regular Enter/Space falls through to Dialog.Trigger | ||
}; | ||
|
||
// Filter out props that are incompatible with Next.js Image component | ||
// Next.js Image has stricter typing for certain props like 'placeholder' | ||
const {placeholder: _placeholder, ...imageCompatibleProps} = props; | ||
|
||
// Render the appropriate image component | ||
const renderImage = (isInline: boolean = true) => { | ||
const renderedSrc = getImageUrl(src, imgPath); | ||
const imageClassName = isInline | ||
|
@@ -127,27 +110,30 @@ export function ImageLightbox({ | |
<img | ||
src={renderedSrc} | ||
alt={alt} | ||
loading={isInline ? 'lazy' : 'lazy'} | ||
loading={isInline ? 'lazy' : 'eager'} | ||
decoding="async" | ||
style={imageStyle} | ||
className={imageClassName} | ||
{...props} | ||
{...imageCompatibleProps} | ||
/> | ||
); | ||
}; | ||
|
||
return ( | ||
<Lightbox.Root open={open} onOpenChange={setOpen} content={renderImage(false)}> | ||
<Dialog.Trigger asChild> | ||
<Lightbox.Trigger asChild> | ||
<div | ||
onClick={handleModifierClick} | ||
onKeyDown={handleModifierKeyDown} | ||
onClick={handleClick} | ||
onAuxClick={handleClick} | ||
onKeyDown={handleKeyDown} | ||
className="cursor-pointer border-none bg-transparent p-0 block w-full no-underline" | ||
aria-label={`View image: ${alt}`} | ||
role="button" | ||
tabIndex={0} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i dont think it needs this (apart from aira-label) because https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/src/dialog.tsx#L105-L107 handles it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh nice. Less code is the best code! I still need some of it for the modifier logic but can pull some off There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup and you can put all that on the |
||
> | ||
{renderImage()} | ||
</div> | ||
cursor[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</Dialog.Trigger> | ||
</Lightbox.Trigger> | ||
</Lightbox.Root> | ||
); | ||
} |
Uh oh!
There was an error while loading. Please reload this page.