66 Chip ,
77 Icon ,
88 Icons ,
9+ Menu ,
10+ MenuItem ,
911 Modal ,
1012 Overlay ,
1113 OverlayBackdrop ,
@@ -15,6 +17,7 @@ import {
1517 Tooltip ,
1618 TooltipProvider ,
1719 as ,
20+ config ,
1821} from 'folds' ;
1922import classNames from 'classnames' ;
2023import { BlurhashCanvas } from 'react-blurhash' ;
@@ -84,6 +87,7 @@ export const ImageContent = as<'div', ImageContentProps>(
8487 const [ error , setError ] = useState ( false ) ;
8588 const [ viewer , setViewer ] = useState ( false ) ;
8689 const [ blurred , setBlurred ] = useState ( markedAsSpoiler ?? false ) ;
90+ const [ isHovered , setIsHovered ] = useState ( false ) ;
8791
8892 const [ srcState , loadSrc ] = useAsyncCallback (
8993 useCallback ( async ( ) => {
@@ -119,7 +123,13 @@ export const ImageContent = as<'div', ImageContentProps>(
119123 } , [ autoPlay , loadSrc ] ) ;
120124
121125 return (
122- < Box className = { classNames ( css . RelativeBase , className ) } { ...props } ref = { ref } >
126+ < Box
127+ className = { classNames ( css . RelativeBase , className ) }
128+ { ...props }
129+ ref = { ref }
130+ onPointerEnter = { ( ) => setIsHovered ( true ) }
131+ onPointerLeave = { ( ) => setIsHovered ( false ) }
132+ >
123133 { srcState . status === AsyncStatus . Success && (
124134 < Overlay open = { viewer } backdrop = { < OverlayBackdrop /> } >
125135 < OverlayCenter >
@@ -156,7 +166,12 @@ export const ImageContent = as<'div', ImageContentProps>(
156166 />
157167 ) }
158168 { ! autoPlay && ! markedAsSpoiler && srcState . status === AsyncStatus . Idle && (
159- < Box className = { css . AbsoluteContainer } alignItems = "Center" justifyContent = "Center" >
169+ < Box
170+ className = { css . AbsoluteContainer }
171+ alignItems = "Center"
172+ justifyContent = "Center"
173+ onClick = { loadSrc }
174+ >
160175 < Button
161176 variant = "Secondary"
162177 fill = "Solid"
@@ -183,36 +198,35 @@ export const ImageContent = as<'div', ImageContentProps>(
183198 </ Box >
184199 ) }
185200 { blurred && ! error && srcState . status !== AsyncStatus . Error && (
186- < Box className = { css . AbsoluteContainer } alignItems = "Center" justifyContent = "Center" >
187- < TooltipProvider
188- tooltip = {
189- typeof spoilerReason === 'string' && (
190- < Tooltip variant = "Secondary" >
191- < Text > { spoilerReason } </ Text >
192- </ Tooltip >
193- )
201+ < Box
202+ className = { css . AbsoluteContainer }
203+ alignItems = "Center"
204+ justifyContent = "Center"
205+ onClick = { ( ) => {
206+ setBlurred ( false ) ;
207+ if ( srcState . status === AsyncStatus . Idle ) {
208+ loadSrc ( ) ;
194209 }
195- position = "Top"
196- align = "Center"
210+ } }
211+ >
212+ < Chip
213+ variant = "Secondary"
214+ radii = "Pill"
215+ size = "500"
216+ outlined
217+ onClick = { ( ) => {
218+ setBlurred ( false ) ;
219+ if ( srcState . status === AsyncStatus . Idle ) {
220+ loadSrc ( ) ;
221+ }
222+ } }
197223 >
198- { ( triggerRef ) => (
199- < Chip
200- ref = { triggerRef }
201- variant = "Secondary"
202- radii = "Pill"
203- size = "500"
204- outlined
205- onClick = { ( ) => {
206- setBlurred ( false ) ;
207- if ( srcState . status === AsyncStatus . Idle ) {
208- loadSrc ( ) ;
209- }
210- } }
211- >
212- < Text size = "B300" > Spoiler</ Text >
213- </ Chip >
214- ) }
215- </ TooltipProvider >
224+ < Text size = "B300" >
225+ { typeof spoilerReason === 'string' && spoilerReason . length > 0
226+ ? `Spoiler reason: ${ spoilerReason } `
227+ : `Spoilered` }
228+ </ Text >
229+ </ Chip >
216230 </ Box >
217231 ) }
218232 { ( srcState . status === AsyncStatus . Loading || srcState . status === AsyncStatus . Success ) &&
@@ -223,7 +237,12 @@ export const ImageContent = as<'div', ImageContentProps>(
223237 </ Box >
224238 ) }
225239 { ( error || srcState . status === AsyncStatus . Error ) && (
226- < Box className = { css . AbsoluteContainer } alignItems = "Center" justifyContent = "Center" >
240+ < Box
241+ className = { css . AbsoluteContainer }
242+ alignItems = "Center"
243+ justifyContent = "Center"
244+ onClick = { handleRetry }
245+ >
227246 < TooltipProvider
228247 tooltip = {
229248 < Tooltip variant = "Critical" >
@@ -250,6 +269,27 @@ export const ImageContent = as<'div', ImageContentProps>(
250269 </ TooltipProvider >
251270 </ Box >
252271 ) }
272+ { isHovered && (
273+ < Box style = { { padding : config . space . S200 , right : 0 , position : 'absolute' } } >
274+ < Menu style = { { padding : config . space . S0 } } >
275+ < MenuItem
276+ size = "300"
277+ after = { < Icon size = "200" src = { blurred ? Icons . Eye : Icons . EyeBlind } /> }
278+ radii = "300"
279+ fill = "Soft"
280+ variant = "Secondary"
281+ title = { blurred ? 'Reveal Image' : 'Hide Image' }
282+ onClick = { ( e ) => {
283+ e . preventDefault ( ) ;
284+ if ( srcState . status === AsyncStatus . Idle ) {
285+ loadSrc ( ) ;
286+ setBlurred ( false ) ;
287+ } else setBlurred ( ! blurred ) ;
288+ } }
289+ />
290+ </ Menu >
291+ </ Box >
292+ ) }
253293 { ! load && typeof info ?. size === 'number' && (
254294 < Box className = { css . AbsoluteFooter } justifyContent = "End" alignContent = "Center" gap = "200" >
255295 < Badge variant = "Secondary" fill = "Soft" >
0 commit comments