1
1
import { ChevronLeft , ChevronRight } from '@obosbbl/grunnmuren-icons-react' ;
2
2
import { useUpdateEffect } from '@react-aria/utils' ;
3
- import { type VariantProps , cva , cx } from 'cva' ;
3
+ import { cx } from 'cva' ;
4
4
import { createContext , useEffect , useRef , useState } from 'react' ;
5
5
import { Provider } from 'react-aria-components' ;
6
6
import { useDebouncedCallback } from 'use-debounce' ;
7
7
import { Button , ButtonContext } from '../button' ;
8
8
import { translations } from '../translations' ;
9
9
import { useLocale } from '../use-locale' ;
10
+ import { MediaContext } from '../content' ;
10
11
11
12
type CarouselProps = {
12
13
/** The <CarouselItem/> components to be displayed within the carousel. */
@@ -70,20 +71,7 @@ const Carousel = ({ className, children }: CarouselProps) => {
70
71
) ;
71
72
72
73
return (
73
- < div
74
- data-slot = "carousel"
75
- className = { cx (
76
- className ,
77
- 'relative rounded-3xl' ,
78
- // If any <CarouselItems/> (the scroll-snap container) or <VideoLoop/> component is focused, apply custom focus styles around the carousel, this makes ensures that the focus outline is visible around the carousel in all cases
79
- '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus' ,
80
- '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus-offset' ,
81
- // Unset the default focus outline for potential video loop buttons, as it interferes with the custom focus styles for the carousel
82
- '**:data-[slot="video-loop-button"]:focus-visible:outline-none' ,
83
- // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
84
- 'h-70 sm:h-[25rem] lg:h-[35rem] xl:h-[40rem] 2xl:h-[42rem] 3xl:h-[48rem] 4xl:h-[53rem]' ,
85
- ) }
86
- >
74
+ < div data-slot = "carousel" >
87
75
< Provider
88
76
values = { [
89
77
[
@@ -122,33 +110,45 @@ const Carousel = ({ className, children }: CarouselProps) => {
122
110
] ,
123
111
] }
124
112
>
125
- { children }
126
- < _CarouselControls >
127
- < Button
128
- isIconOnly
129
- slot = "prev"
130
- variant = "primary"
131
- color = "white"
132
- className = { cx (
133
- 'group/carousel-previous' ,
134
- hasReachedScrollStart && 'invisible' ,
135
- ) }
136
- >
137
- < ChevronLeft className = "group-hover/carousel-previous:motion-safe:-translate-x-1 transition-transform" />
138
- </ Button >
139
- < Button
140
- isIconOnly
141
- slot = "next"
142
- variant = "primary"
143
- color = "white"
144
- className = { cx (
145
- 'group/carousel-next' ,
146
- hasReachedScrollEnd && 'invisible' ,
147
- ) }
148
- >
149
- < ChevronRight className = "transition-transform group-hover/carousel-next:motion-safe:translate-x-1" />
150
- </ Button >
151
- </ _CarouselControls >
113
+ < div
114
+ className = { cx (
115
+ className ,
116
+ 'relative rounded-3xl' ,
117
+ // If any <CarouselItems/> (the scroll-snap container) or <VideoLoop/> component is focused, apply custom focus styles around the carousel, this makes ensures that the focus outline is visible around the carousel in all cases
118
+ '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus' ,
119
+ '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus-offset' ,
120
+ // Unset the default focus outline for potential video loop buttons, as it interferes with the custom focus styles for the carousel
121
+ '**:data-[slot="video-loop-button"]:focus-visible:outline-none' ,
122
+ ) }
123
+ >
124
+ { children }
125
+ < _CarouselControls >
126
+ < Button
127
+ isIconOnly
128
+ slot = "prev"
129
+ variant = "primary"
130
+ color = "white"
131
+ className = { cx (
132
+ 'group/carousel-previous' ,
133
+ hasReachedScrollStart && 'invisible' ,
134
+ ) }
135
+ >
136
+ < ChevronLeft className = "group-hover/carousel-previous:motion-safe:-translate-x-1 transition-transform" />
137
+ </ Button >
138
+ < Button
139
+ isIconOnly
140
+ slot = "next"
141
+ variant = "primary"
142
+ color = "white"
143
+ className = { cx (
144
+ 'group/carousel-next' ,
145
+ hasReachedScrollEnd && 'invisible' ,
146
+ ) }
147
+ >
148
+ < ChevronRight className = "transition-transform group-hover/carousel-next:motion-safe:translate-x-1" />
149
+ </ Button >
150
+ </ _CarouselControls >
151
+ </ div >
152
152
</ Provider >
153
153
</ div >
154
154
) ;
@@ -167,7 +167,12 @@ type _CarouselControlsProps = {
167
167
*/
168
168
const _CarouselControls = ( { children, className } : _CarouselControlsProps ) => (
169
169
< div
170
- className = { cx ( className , 'absolute right-6 bottom-6 flex gap-x-2' ) }
170
+ className = { cx (
171
+ className ,
172
+ 'absolute right-6 bottom-6 flex gap-x-2' ,
173
+ // Make it easier to position in full-bleed hero variants (these style have no other side effects)
174
+ 'items-end *:h-fit' ,
175
+ ) }
171
176
data-slot = "carousel-controls"
172
177
>
173
178
{ children }
@@ -202,7 +207,6 @@ const CarouselItems = ({ className, children }: CarouselItemsProps) => (
202
207
'snap-mandatory' ,
203
208
'overflow-x-auto' ,
204
209
'outline-none' ,
205
- 'h-full' ,
206
210
'rounded-[inherit]' ,
207
211
] ) }
208
212
ref = { ref }
@@ -218,35 +222,35 @@ const CarouselItems = ({ className, children }: CarouselItemsProps) => (
218
222
</ CarouselItemsContext . Consumer >
219
223
) ;
220
224
221
- type CarouselItemProps = VariantProps < typeof carouselItemVariant > & {
225
+ type CarouselItemProps = {
222
226
className ?: string ;
223
227
children : React . ReactNode ;
224
228
} ;
225
229
226
- const carouselItemVariant = cva ( {
227
- base : 'shrink-0 basis-full snap-start *:h-full *:w-full' ,
228
- variants : {
229
- /**
230
- * Control how the content should be placed with the object-fit property
231
- * You might for example want to use `fit="contain"` portrait images that should not be cropped
232
- * @default cover
233
- * */
234
- fit : {
235
- cover : '*:object-cover' ,
236
- contain : 'bg-blue-dark *:object-contain' ,
237
- } ,
238
- } ,
239
- defaultVariants : {
240
- fit : 'cover' ,
241
- } ,
242
- } ) ;
243
-
244
- const CarouselItem = ( { fit, className, children } : CarouselItemProps ) => {
245
- const _className = carouselItemVariant ( { fit } ) ;
246
-
230
+ const CarouselItem = ( { className, children } : CarouselItemProps ) => {
247
231
return (
248
- < div className = { cx ( className , _className ) } data-slot = "carousel-item" >
249
- { children }
232
+ < div
233
+ className = { cx ( className , 'shrink-0 basis-full snap-start' ) }
234
+ data-slot = "carousel-item"
235
+ >
236
+ < Provider
237
+ values = { [
238
+ [
239
+ MediaContext ,
240
+ {
241
+ fit : 'cover' ,
242
+ className : cx (
243
+ 'bg-blue-dark' ,
244
+ '*:w-full' ,
245
+ // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
246
+ '*:h-70 sm:*:h-[25rem] lg:*:h-[35rem] xl:*:h-[40rem] 2xl:*:h-[42rem] 3xl:*:h-[48rem] 4xl:*:h-[53rem]' ,
247
+ ) ,
248
+ } ,
249
+ ] ,
250
+ ] }
251
+ >
252
+ { children }
253
+ </ Provider >
250
254
</ div >
251
255
) ;
252
256
} ;
0 commit comments