Skip to content

Commit f519ebf

Browse files
committed
feat(ui): add shared media lightbox with zoom controls
1 parent 058c37b commit f519ebf

13 files changed

Lines changed: 307 additions & 377 deletions

File tree

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
22

33
import { useRef, useState } from 'react'
4+
import { Lightbox } from '@sim/emcn'
45
import { cn, getAssetUrl } from '@/lib/utils'
5-
import { Lightbox } from './lightbox'
66

77
interface ActionImageProps {
88
src: string
@@ -17,10 +17,6 @@ interface ActionVideoProps {
1717
}
1818

1919
export function ActionImage({ src, alt, enableLightbox = true }: ActionImageProps) {
20-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
21-
22-
const openLightbox = () => setIsLightboxOpen(true)
23-
2420
const image = (
2521
<img
2622
src={src}
@@ -32,42 +28,28 @@ export function ActionImage({ src, alt, enableLightbox = true }: ActionImageProp
3228
/>
3329
)
3430

31+
if (!enableLightbox) return image
32+
3533
return (
36-
<>
37-
{enableLightbox ? (
38-
<button
39-
type='button'
40-
onClick={openLightbox}
41-
aria-label={`Open ${alt} in media viewer`}
42-
className='group inline-block cursor-pointer rounded p-0 text-left'
43-
>
44-
{image}
45-
</button>
46-
) : (
47-
image
48-
)}
49-
{enableLightbox && (
50-
<Lightbox
51-
isOpen={isLightboxOpen}
52-
onClose={() => setIsLightboxOpen(false)}
53-
src={src}
54-
alt={alt}
55-
type='image'
56-
/>
57-
)}
58-
</>
34+
<Lightbox src={src} alt={alt}>
35+
<button
36+
type='button'
37+
aria-label={`Open ${alt} in media viewer`}
38+
className='group inline-block cursor-pointer rounded p-0 text-left'
39+
>
40+
{image}
41+
</button>
42+
</Lightbox>
5943
)
6044
}
6145

6246
export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProps) {
6347
const videoRef = useRef<HTMLVideoElement>(null)
64-
const startTimeRef = useRef(0)
65-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
48+
const [startTime, setStartTime] = useState(0)
6649
const resolvedSrc = getAssetUrl(src)
6750

6851
const openLightbox = () => {
69-
startTimeRef.current = videoRef.current?.currentTime ?? 0
70-
setIsLightboxOpen(true)
52+
setStartTime(videoRef.current?.currentTime ?? 0)
7153
}
7254

7355
const video = (
@@ -85,30 +67,18 @@ export function ActionVideo({ src, alt, enableLightbox = true }: ActionVideoProp
8567
/>
8668
)
8769

70+
if (!enableLightbox) return video
71+
8872
return (
89-
<>
90-
{enableLightbox ? (
91-
<button
92-
type='button'
93-
onClick={openLightbox}
94-
aria-label={`Open ${alt} in media viewer`}
95-
className='group inline-block cursor-pointer rounded p-0 text-left'
96-
>
97-
{video}
98-
</button>
99-
) : (
100-
video
101-
)}
102-
{enableLightbox && (
103-
<Lightbox
104-
isOpen={isLightboxOpen}
105-
onClose={() => setIsLightboxOpen(false)}
106-
src={src}
107-
alt={alt}
108-
type='video'
109-
startTime={startTimeRef.current}
110-
/>
111-
)}
112-
</>
73+
<Lightbox src={resolvedSrc} alt={alt} type='video' startTime={startTime}>
74+
<button
75+
type='button'
76+
onClick={openLightbox}
77+
aria-label={`Open ${alt} in media viewer`}
78+
className='group inline-block cursor-pointer rounded p-0 text-left'
79+
>
80+
{video}
81+
</button>
82+
</Lightbox>
11383
)
11484
}

‎apps/docs/components/ui/image.tsx‎

Lines changed: 13 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use client'
22

3-
import { useState } from 'react'
3+
import { Lightbox } from '@sim/emcn'
44
import NextImage, { type ImageProps as NextImageProps } from 'next/image'
5-
import { Lightbox } from '@/components/ui/lightbox'
65
import { cn } from '@/lib/utils'
76

87
interface ImageProps extends Omit<NextImageProps, 'className'> {
@@ -17,9 +16,7 @@ export function Image({
1716
src,
1817
...props
1918
}: ImageProps) {
20-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
21-
22-
const openLightbox = () => setIsLightboxOpen(true)
19+
const lightboxSrc = typeof src === 'string' ? src : 'default' in src ? src.default.src : src.src
2320

2421
const image = (
2522
<NextImage
@@ -34,30 +31,17 @@ export function Image({
3431
/>
3532
)
3633

37-
return (
38-
<>
39-
{enableLightbox ? (
40-
<button
41-
type='button'
42-
onClick={openLightbox}
43-
aria-label={`Open ${alt} in media viewer`}
44-
className='group contents'
45-
>
46-
{image}
47-
</button>
48-
) : (
49-
image
50-
)}
34+
if (!enableLightbox) return image
5135

52-
{enableLightbox && (
53-
<Lightbox
54-
isOpen={isLightboxOpen}
55-
onClose={() => setIsLightboxOpen(false)}
56-
src={typeof src === 'string' ? src : String(src)}
57-
alt={alt}
58-
type='image'
59-
/>
60-
)}
61-
</>
36+
return (
37+
<Lightbox src={lightboxSrc} alt={alt}>
38+
<button
39+
type='button'
40+
aria-label={`Open ${alt || 'image'} in media viewer`}
41+
className='group contents'
42+
>
43+
{image}
44+
</button>
45+
</Lightbox>
6246
)
6347
}

‎apps/docs/components/ui/lightbox.tsx‎

Lines changed: 0 additions & 104 deletions
This file was deleted.

‎apps/docs/components/ui/video.tsx‎

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client'
22

33
import { useEffect, useRef, useState } from 'react'
4+
import { Lightbox } from '@sim/emcn'
45
import { cn, getAssetUrl } from '@/lib/utils'
5-
import { Lightbox } from './lightbox'
66

77
interface VideoProps {
88
src: string
@@ -28,8 +28,7 @@ export function Video({
2828
height,
2929
}: VideoProps) {
3030
const videoRef = useRef<HTMLVideoElement>(null)
31-
const startTimeRef = useRef(0)
32-
const [isLightboxOpen, setIsLightboxOpen] = useState(false)
31+
const [startTime, setStartTime] = useState(0)
3332
const [isInView, setIsInView] = useState(false)
3433

3534
useEffect(() => {
@@ -55,8 +54,7 @@ export function Video({
5554
}, [])
5655

5756
const openLightbox = () => {
58-
startTimeRef.current = videoRef.current?.currentTime ?? 0
59-
setIsLightboxOpen(true)
57+
setStartTime(videoRef.current?.currentTime ?? 0)
6058
}
6159

6260
const video = (
@@ -78,31 +76,18 @@ export function Video({
7876
/>
7977
)
8078

81-
return (
82-
<>
83-
{enableLightbox ? (
84-
<button
85-
type='button'
86-
onClick={openLightbox}
87-
aria-label={`Open ${src} in media viewer`}
88-
className='group contents'
89-
>
90-
{video}
91-
</button>
92-
) : (
93-
video
94-
)}
79+
if (!enableLightbox) return video
9580

96-
{enableLightbox && (
97-
<Lightbox
98-
isOpen={isLightboxOpen}
99-
onClose={() => setIsLightboxOpen(false)}
100-
src={src}
101-
alt={`Video: ${src}`}
102-
type='video'
103-
startTime={startTimeRef.current}
104-
/>
105-
)}
106-
</>
81+
return (
82+
<Lightbox src={getAssetUrl(src)} alt={`Video: ${src}`} type='video' startTime={startTime}>
83+
<button
84+
type='button'
85+
onClick={openLightbox}
86+
aria-label={`Open ${src} in media viewer`}
87+
className='group contents'
88+
>
89+
{video}
90+
</button>
91+
</Lightbox>
10792
)
10893
}

0 commit comments

Comments
 (0)