@obosbbl/grunnmuren-react 3.3.3 → 3.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -298,7 +298,6 @@ const cardLinkVariants = cva.cva({
298
298
  });
299
299
  };
300
300
 
301
- const roundedMediaCorners = '*:data-[slot="media"]:*:rounded-3xl';
302
301
  // Common variant for "standard" and "full-bleed" Hero variants
303
302
  const oneColumnLayout = [
304
303
  // Vertical spacing in the <Content>
@@ -311,7 +310,7 @@ const oneColumnLayout = [
311
310
  'lg:*:not-data-[slot="content"]:not-data-[slot="media"]:not-data-[slot="carousel"]:col-span-3 lg:*:not-data-[slot="content"]:not-data-[slot="media"]:justify-self-end',
312
311
  // <Media> and <Carousel> content takes up the full width on medium screens and above
313
312
  'lg:*:data-[slot="media"]:col-span-full *:data-[slot="media"]:*:w-full',
314
- 'lg:*:data-[slot="carousel"]:col-span-full *:data-[slot="carousel"]:*:w-full',
313
+ 'lg:*:data-[slot="carousel"]:col-span-full',
315
314
  // Aligns <Content> and any element beside it (e.g. <Media>, <Badge>, <CTA> etc.) to the bottom of the <Content> container
316
315
  'lg:items-end'
317
316
  ];
@@ -327,7 +326,10 @@ const variants = cva.cva({
327
326
  // Vertical spacing in the <Content>
328
327
  '*:data-[slot="content"]:gap-y-3',
329
328
  // Make sure <Media> content fills any available vertical and horizontal space
330
- '*:data-[slot="media"]:*:object-cover'
329
+ '*:data-[slot="media"]:*:object-cover',
330
+ '*:data-[slot="carousel"]:overflow-hidden *:data-[slot="carousel"]:rounded-3xl',
331
+ // Make the carousel items full width, so we scroll one at a time
332
+ '**:data-[slot="carousel-item"]:basis-full'
331
333
  ],
332
334
  variants: {
333
335
  /**
@@ -335,7 +337,6 @@ const variants = cva.cva({
335
337
  * @default standard
336
338
  * */ variant: {
337
339
  standard: [
338
- roundedMediaCorners,
339
340
  oneColumnLayout,
340
341
  nonFullBleedAspectRatiosForSmallScreens,
341
342
  'lg:*:data-[slot="media"]:*:aspect-2/1'
@@ -345,32 +346,44 @@ const variants = cva.cva({
345
346
  // Position the media and carousel content to fill the entire viewport width
346
347
  '*:data-[slot="media"]:*:absolute *:data-[slot="media"]:*:left-0',
347
348
  // Special case for Carousel, where the Media is nested inside a CarouselItem
348
- '*:data-[slot="carousel"]:**:data-[slot="media"]:w-full *:data-[slot="carousel"]:*:absolute *:data-[slot="carousel"]:*:left-0',
349
+ '*:data-[slot="carousel"]:**:data-[slot="media"]:w-full',
349
350
  // Match the heights of the <Media> or <Carousel> wrapper for the Media content (e.g. image, VideoLoop, video etc.)
350
351
  // This is necessary due to the absolute positioning of the media and carousel containers in this variant
351
352
  // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
352
353
  '**:data-[slot="media"]:h-80 sm:**:data-[slot="media"]:h-[25rem] md:**:data-[slot="media"]:h-[30rem] lg:**:data-[slot="media"]:h-[35rem] xl:**:data-[slot="media"]:h-[40rem] 2xl:**:data-[slot="media"]:h-[42rem] 3xl:**:data-[slot="media"]:h-[48rem] 4xl:**:data-[slot="media"]:h-[53rem]',
353
- // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
354
- '**:data-[slot="media"]:*:h-80 sm:**:data-[slot="media"]:*:h-[25rem] md:**:data-[slot="media"]:*:h-[30rem] lg:**:data-[slot="media"]:*:h-[35rem] xl:**:data-[slot="media"]:*:h-[40rem] 2xl:**:data-[slot="media"]:*:h-[42rem] 3xl:**:data-[slot="media"]:*:h-[48rem] 4xl:**:data-[slot="media"]:*:h-[53rem]',
354
+ '**:data-[slot="media"]:*:h-[inherit]',
355
355
  // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
356
356
  '*:data-[slot="carousel"]:h-80 sm:*:data-[slot="carousel"]:h-[25rem] md:*:data-[slot="carousel"]:h-[30rem] lg:*:data-[slot="carousel"]:h-[35rem] xl:*:data-[slot="carousel"]:h-[40rem] 2xl:*:data-[slot="carousel"]:h-[42rem] 3xl:*:data-[slot="carousel"]:h-[48rem] 4xl:*:data-[slot="carousel"]:h-[53rem]',
357
+ '*:data-[slot="carousel"]:w-full!',
357
358
  // Override aspect ratio of the media and carousel-item slots (since we can not use aspect for full-bleed layout)
358
359
  '**:data-[slot="carousel-item"]:data-[slot="media"]:*:aspect-none',
359
- '**:data-[slot="carousel-controls"]:container **:data-[slot="carousel-controls"]:right-0 **:data-[slot="carousel-controls"]:bottom-4 **:data-[slot="carousel-controls"]:left-0 **:data-[slot="carousel-controls"]:justify-end',
360
- // Override rounded corners of Carousel slots
361
- '*:data-[slot="carousel"]:*:rounded-none'
360
+ // break out the carousel out of the container
361
+ '**:data-[slot="carousel-items-container"]:absolute **:data-[slot="carousel-items-container"]:right-0 **:data-[slot="carousel-items-container"]:left-0 **:data-[slot="carousel-items-container"]:h-[inherit]',
362
+ // Positions the carousel controls inside the carousel
363
+ '**:data-[slot="carousel-controls"]:z-10 **:data-[slot="carousel-controls"]:mb-4 *:data-[slot="carousel"]:flex *:data-[slot="carousel"]:items-end *:data-[slot="carousel"]:justify-end'
362
364
  ],
363
365
  'two-column': [
364
366
  'lg:items-center lg:*:col-span-6',
365
367
  // Vertical spacing in the <Content>
366
368
  'lg:*:data-[slot="content"]:gap-y-7',
367
- roundedMediaCorners,
368
369
  nonFullBleedAspectRatiosForSmallScreens,
369
370
  // Set media aspect ratio to 1:1 (square)
370
- 'lg:*:data-[slot="media"]:*:aspect-[1/1]'
371
+ 'lg:*:data-[slot="media"]:*:aspect-square'
371
372
  ]
372
373
  }
373
374
  },
375
+ compoundVariants: [
376
+ {
377
+ variant: [
378
+ 'standard',
379
+ 'two-column'
380
+ ],
381
+ className: [
382
+ '*:data-[slot="media"]:*:rounded-3xl',
383
+ '**:data-[slot="carousel-controls"]:absolute *:data-[slot="carousel"]:relative **:data-[slot="carousel-controls"]:right-4 **:data-[slot="carousel-controls"]:bottom-4 **:data-[slot="carousel-container"]:rounded-3xl'
384
+ ]
385
+ }
386
+ ],
374
387
  defaultVariants: {
375
388
  variant: 'standard'
376
389
  }
@@ -296,7 +296,6 @@ const cardLinkVariants = cva({
296
296
  });
297
297
  };
298
298
 
299
- const roundedMediaCorners = '*:data-[slot="media"]:*:rounded-3xl';
300
299
  // Common variant for "standard" and "full-bleed" Hero variants
301
300
  const oneColumnLayout = [
302
301
  // Vertical spacing in the <Content>
@@ -309,7 +308,7 @@ const oneColumnLayout = [
309
308
  'lg:*:not-data-[slot="content"]:not-data-[slot="media"]:not-data-[slot="carousel"]:col-span-3 lg:*:not-data-[slot="content"]:not-data-[slot="media"]:justify-self-end',
310
309
  // <Media> and <Carousel> content takes up the full width on medium screens and above
311
310
  'lg:*:data-[slot="media"]:col-span-full *:data-[slot="media"]:*:w-full',
312
- 'lg:*:data-[slot="carousel"]:col-span-full *:data-[slot="carousel"]:*:w-full',
311
+ 'lg:*:data-[slot="carousel"]:col-span-full',
313
312
  // Aligns <Content> and any element beside it (e.g. <Media>, <Badge>, <CTA> etc.) to the bottom of the <Content> container
314
313
  'lg:items-end'
315
314
  ];
@@ -325,7 +324,10 @@ const variants = cva({
325
324
  // Vertical spacing in the <Content>
326
325
  '*:data-[slot="content"]:gap-y-3',
327
326
  // Make sure <Media> content fills any available vertical and horizontal space
328
- '*:data-[slot="media"]:*:object-cover'
327
+ '*:data-[slot="media"]:*:object-cover',
328
+ '*:data-[slot="carousel"]:overflow-hidden *:data-[slot="carousel"]:rounded-3xl',
329
+ // Make the carousel items full width, so we scroll one at a time
330
+ '**:data-[slot="carousel-item"]:basis-full'
329
331
  ],
330
332
  variants: {
331
333
  /**
@@ -333,7 +335,6 @@ const variants = cva({
333
335
  * @default standard
334
336
  * */ variant: {
335
337
  standard: [
336
- roundedMediaCorners,
337
338
  oneColumnLayout,
338
339
  nonFullBleedAspectRatiosForSmallScreens,
339
340
  'lg:*:data-[slot="media"]:*:aspect-2/1'
@@ -343,32 +344,44 @@ const variants = cva({
343
344
  // Position the media and carousel content to fill the entire viewport width
344
345
  '*:data-[slot="media"]:*:absolute *:data-[slot="media"]:*:left-0',
345
346
  // Special case for Carousel, where the Media is nested inside a CarouselItem
346
- '*:data-[slot="carousel"]:**:data-[slot="media"]:w-full *:data-[slot="carousel"]:*:absolute *:data-[slot="carousel"]:*:left-0',
347
+ '*:data-[slot="carousel"]:**:data-[slot="media"]:w-full',
347
348
  // Match the heights of the <Media> or <Carousel> wrapper for the Media content (e.g. image, VideoLoop, video etc.)
348
349
  // This is necessary due to the absolute positioning of the media and carousel containers in this variant
349
350
  // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
350
351
  '**:data-[slot="media"]:h-80 sm:**:data-[slot="media"]:h-[25rem] md:**:data-[slot="media"]:h-[30rem] lg:**:data-[slot="media"]:h-[35rem] xl:**:data-[slot="media"]:h-[40rem] 2xl:**:data-[slot="media"]:h-[42rem] 3xl:**:data-[slot="media"]:h-[48rem] 4xl:**:data-[slot="media"]:h-[53rem]',
351
- // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
352
- '**:data-[slot="media"]:*:h-80 sm:**:data-[slot="media"]:*:h-[25rem] md:**:data-[slot="media"]:*:h-[30rem] lg:**:data-[slot="media"]:*:h-[35rem] xl:**:data-[slot="media"]:*:h-[40rem] 2xl:**:data-[slot="media"]:*:h-[42rem] 3xl:**:data-[slot="media"]:*:h-[48rem] 4xl:**:data-[slot="media"]:*:h-[53rem]',
352
+ '**:data-[slot="media"]:*:h-[inherit]',
353
353
  // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
354
354
  '*:data-[slot="carousel"]:h-80 sm:*:data-[slot="carousel"]:h-[25rem] md:*:data-[slot="carousel"]:h-[30rem] lg:*:data-[slot="carousel"]:h-[35rem] xl:*:data-[slot="carousel"]:h-[40rem] 2xl:*:data-[slot="carousel"]:h-[42rem] 3xl:*:data-[slot="carousel"]:h-[48rem] 4xl:*:data-[slot="carousel"]:h-[53rem]',
355
+ '*:data-[slot="carousel"]:w-full!',
355
356
  // Override aspect ratio of the media and carousel-item slots (since we can not use aspect for full-bleed layout)
356
357
  '**:data-[slot="carousel-item"]:data-[slot="media"]:*:aspect-none',
357
- '**:data-[slot="carousel-controls"]:container **:data-[slot="carousel-controls"]:right-0 **:data-[slot="carousel-controls"]:bottom-4 **:data-[slot="carousel-controls"]:left-0 **:data-[slot="carousel-controls"]:justify-end',
358
- // Override rounded corners of Carousel slots
359
- '*:data-[slot="carousel"]:*:rounded-none'
358
+ // break out the carousel out of the container
359
+ '**:data-[slot="carousel-items-container"]:absolute **:data-[slot="carousel-items-container"]:right-0 **:data-[slot="carousel-items-container"]:left-0 **:data-[slot="carousel-items-container"]:h-[inherit]',
360
+ // Positions the carousel controls inside the carousel
361
+ '**:data-[slot="carousel-controls"]:z-10 **:data-[slot="carousel-controls"]:mb-4 *:data-[slot="carousel"]:flex *:data-[slot="carousel"]:items-end *:data-[slot="carousel"]:justify-end'
360
362
  ],
361
363
  'two-column': [
362
364
  'lg:items-center lg:*:col-span-6',
363
365
  // Vertical spacing in the <Content>
364
366
  'lg:*:data-[slot="content"]:gap-y-7',
365
- roundedMediaCorners,
366
367
  nonFullBleedAspectRatiosForSmallScreens,
367
368
  // Set media aspect ratio to 1:1 (square)
368
- 'lg:*:data-[slot="media"]:*:aspect-[1/1]'
369
+ 'lg:*:data-[slot="media"]:*:aspect-square'
369
370
  ]
370
371
  }
371
372
  },
373
+ compoundVariants: [
374
+ {
375
+ variant: [
376
+ 'standard',
377
+ 'two-column'
378
+ ],
379
+ className: [
380
+ '*:data-[slot="media"]:*:rounded-3xl',
381
+ '**:data-[slot="carousel-controls"]:absolute *:data-[slot="carousel"]:relative **:data-[slot="carousel-controls"]:right-4 **:data-[slot="carousel-controls"]:bottom-4 **:data-[slot="carousel-container"]:rounded-3xl'
382
+ ]
383
+ }
384
+ ],
372
385
  defaultVariants: {
373
386
  variant: 'standard'
374
387
  }
package/dist/index.d.mts CHANGED
@@ -2,7 +2,7 @@ import { DisclosureProps as DisclosureProps$1, ButtonProps as ButtonProps$1, Lin
2
2
  export { ListBoxItemProps as ComboboxItemProps, DisclosureGroup, DisclosureGroupProps, Form, Group, LabelProps, ListBoxItemProps as SelectItemProps } from 'react-aria-components';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import * as react from 'react';
5
- import { RefAttributes, HTMLAttributes, HTMLProps, ComponentProps, Ref, JSX, ReactNode, RefObject, Dispatch, SetStateAction } from 'react';
5
+ import { RefAttributes, HTMLAttributes, HTMLProps, ComponentProps, Ref, ReactNode, RefObject, Dispatch, SetStateAction, JSX } from 'react';
6
6
  import * as cva from 'cva';
7
7
  import { VariantProps } from 'cva';
8
8
  import { DisclosureState } from 'react-stately';
@@ -214,35 +214,70 @@ type CardLinkProps = (Omit<LinkProps$1, 'href'> & Required<Pick<LinkProps$1, 'hr
214
214
  */
215
215
  declare const CardLink: ({ className: _className, href, ...restProps }: CardLinkProps) => react_jsx_runtime.JSX.Element;
216
216
 
217
- type CarouselProps = Omit<HTMLProps<HTMLDivElement>, 'onChange'> & {
218
- /** The <CarouselItem/> components to be displayed within the carousel. */
219
- children: React.ReactNode;
217
+ type CarouselProps = Omit<HTMLProps<HTMLDivElement>, 'onChange' | 'onSelect'> & {
218
+ children?: React.ReactNode;
219
+ /**
220
+ * Alignment of the items relative to the carousel viewport.
221
+ * @default 'center'
222
+ */
223
+ align?: 'start' | 'center' | 'end';
224
+ /** Delay in milliseconds between each automatic transition of the carousel. Any interaction with the carousel will immediately stop the autoplay. */
225
+ autoPlayDelay?: number;
226
+ /**
227
+ * The initial snapped index of the carousel.
228
+ * @default 0
229
+ */
230
+ initialIndex?: number;
231
+ /**
232
+ * Whether the carousel infinitely loops.
233
+ * @default false
234
+ */
235
+ loop?: boolean;
236
+ /**
237
+ * Orientation of the carousel.
238
+ * @default 'horizontal'
239
+ */
240
+ orientation?: 'horizontal' | 'vertical';
241
+ /**
242
+ * Callback invoked when the snapped index changes.
243
+ */
244
+ onSelect?: (index: number) => void;
220
245
  /**
221
- * Callback that is triggered when a user navigates to new item in the Carousel.
222
- * The argument to the callback is an object containing `index` of the new item scrolled into view and the `id` of that item (if set on the `<CarouselItem>`)
223
- * It also provides `prevIndex` which is the index of the previous item that was in view
224
- * And `prevId`, which is the id of the previous item that was in view (if set on the `<CarouselItem>`)
225
- * @param item { index: number; id?: string; prevIndex: number; prevId?: string }
246
+ * Callback invoked after the carousel scrolling "settles". Think of this as the debounced version of `onSelect`.
226
247
  */
227
- onChange?: (item: CarouselItem) => void;
248
+ onSettled?: (index: number) => void;
249
+ /**
250
+ * Whether the carousel should scroll with regular mouse/trackpad scroll gestures, in addition to swipe gestures.
251
+ * @default false
252
+ */
253
+ scrollGestures?: boolean;
228
254
  };
229
- declare const Carousel: ({ className, children, onChange, ...rest }: CarouselProps) => react_jsx_runtime.JSX.Element;
255
+ declare const Carousel: ({ autoPlayDelay, align, children, initialIndex, orientation, onSelect, onSettled, loop, scrollGestures, ref, ...rest }: CarouselProps) => react_jsx_runtime.JSX.Element;
256
+ type CarouselItemsContainer = HTMLProps<HTMLDivElement> & {
257
+ children: React.ReactNode;
258
+ };
259
+ declare const CarouselItemsContainer: ({ children, className, ...rest }: CarouselItemsContainer) => react_jsx_runtime.JSX.Element;
230
260
  type CarouselItemsProps = HTMLProps<HTMLDivElement> & {
231
261
  /** The <CarouselItem/> components to be displayed within the carousel. */
232
262
  children: React.ReactNode;
233
263
  };
234
264
  declare const CarouselItems: ({ className, children }: CarouselItemsProps) => react_jsx_runtime.JSX.Element;
265
+ type CarouselControlsProps = HTMLProps<HTMLDivElement> & {
266
+ /** The <CarouselItem/> components to be displayed within the carousel. */
267
+ children: React.ReactNode;
268
+ };
269
+ /**
270
+ * This is internal for now, but we will expose it in the future when we support more flexible positioning of prev/next and other actions.
271
+ * It is used to render the prev/next buttons in the carousel for now.
272
+ */
273
+ declare const CarouselControls: ({ children, className, ...rest }: CarouselControlsProps) => react_jsx_runtime.JSX.Element;
274
+ type CarouselButtonProps = ButtonProps & {
275
+ slot: 'next' | 'prev';
276
+ };
277
+ declare const CarouselButton: ({ className, isIconOnly, color, variant, slot, ...rest }: CarouselButtonProps) => react_jsx_runtime.JSX.Element;
235
278
  type CarouselItemProps = HTMLProps<HTMLDivElement> & {
236
279
  /** The component/components to display as the <CarouselItem/>. */
237
- children: JSX.Element | JSX.Element[];
238
- };
239
- type CarouselItem = Pick<CarouselItemProps, 'id'> & {
240
- /** The index of the item that is currently in view */
241
- index: number;
242
- /** The index of the previous item that was in view */
243
- prevIndex: number;
244
- /** The id of the previous item that was in view */
245
- prevId?: CarouselItemProps['id'];
280
+ children: React.ReactNode;
246
281
  };
247
282
  declare const CarouselItem: ({ className, children, ...rest }: CarouselItemProps) => react_jsx_runtime.JSX.Element;
248
283
 
@@ -758,5 +793,5 @@ type VideoLoopProps = {
758
793
  };
759
794
  declare const VideoLoop: ({ src, format, alt, className }: VideoLoopProps) => react_jsx_runtime.JSX.Element;
760
795
 
761
- export { Accordion, AccordionItem, Alertbox, Avatar, Backlink, Badge, Breadcrumb, Breadcrumbs, Button, ButtonContext, Caption, Card, CardLink, Checkbox, CheckboxGroup, Combobox, ListBoxHeader as ComboboxHeader, ListBoxItem as ComboboxItem, ListBoxSection as ComboboxSection, Content, ContentContext, DateFormatter, Description, Disclosure, DisclosureButton, DisclosurePanel, DisclosureStateContext, ErrorMessage, Footer, GrunnmurenProvider, Heading, HeadingContext, Label, LinkList, LinkListContainer, LinkListItem, Media, MediaContext, NumberField, Radio, RadioGroup, Select, ListBoxHeader as SelectHeader, ListBoxItem as SelectItem, ListBoxSection as SelectSection, Tag, TagGroup, TagList, TextArea, TextField, Carousel as UNSAFE_Carousel, CarouselItem as UNSAFE_CarouselItem, CarouselItems as UNSAFE_CarouselItems, Dialog as UNSAFE_Dialog, DialogTrigger as UNSAFE_DialogTrigger, FileUpload as UNSAFE_FileUpload, Hero as UNSAFE_Hero, Link as UNSAFE_Link, Modal as UNSAFE_Modal, ProgressBar as UNSAFE_ProgressBar, ProgressBarValueText as UNSAFE_ProgressBarValueText, Tab as UNSAFE_Tab, TabList as UNSAFE_TabList, TabPanel as UNSAFE_TabPanel, Table as UNSAFE_Table, TableBody as UNSAFE_TableBody, TableCell as UNSAFE_TableCell, TableColumn as UNSAFE_TableColumn, TableColumnResizer as UNSAFE_TableColumnResizer, TableContainer as UNSAFE_TableContainer, TableHeader as UNSAFE_TableHeader, TableRow as UNSAFE_TableRow, Tabs as UNSAFE_Tabs, VideoLoop, _LinkContext, _useLocale as useLocale };
762
- export type { AccordionItemProps, AccordionProps, Props as AlertboxProps, AvatarProps, BacklinkProps, BadgeProps, BreadcrumbProps, BreadcrumbsProps, ButtonProps, CaptionProps, CardLinkProps, CardProps, CheckboxGroupProps, CheckboxProps, ComboboxProps, ContentProps, DateFormatterProps, DescriptionProps, DisclosureButtonProps, DisclosurePanelProps, DisclosureProps, ErrorMessageProps, FooterProps, GrunnmurenProviderProps, HeadingProps, LinkListContainerProps, LinkListItemProps, LinkListProps, Locale, MediaProps, NumberFieldProps, RadioGroupProps, RadioProps, SelectProps, TagGroupProps, TagListProps, TagProps, TextAreaProps, TextFieldProps, CarouselItemProps as UNSAFE_CarouselItemProps, CarouselItemsProps as UNSAFE_CarouselItemsProps, CarouselProps as UNSAFE_CarouselProps, DialogProps as UNSAFE_DialogProps, DialogTriggerProps as UNSAFE_DialogTriggerProps, FileUploadProps as UNSAFE_FileUploadProps, HeroProps as UNSAFE_HeroProps, LinkProps as UNSAFE_LinkProps, ModalProps as UNSAFE_ModalProps, ProgressBarProps as UNSAFE_ProgressBarProps, ProgressBarValueTextProps as UNSAFE_ProgressBarValueTextProps, TabListProps as UNSAFE_TabListProps, TabPanelProps as UNSAFE_TabPanelProps, TabProps as UNSAFE_TabProps, TableBodyProps as UNSAFE_TableBodyProps, TableCellProps as UNSAFE_TableCellProps, TableColumnProps as UNSAFE_TableColumnProps, TableColumnResizerProps as UNSAFE_TableColumnResizerProps, TableContainerProps as UNSAFE_TableContainerProps, TableHeaderProps as UNSAFE_TableHeaderProps, TableProps as UNSAFE_TableProps, TableRowProps as UNSAFE_TableRowProps, TabsProps as UNSAFE_TabsProps };
796
+ export { Accordion, AccordionItem, Alertbox, Avatar, Backlink, Badge, Breadcrumb, Breadcrumbs, Button, ButtonContext, Caption, Card, CardLink, Checkbox, CheckboxGroup, Combobox, ListBoxHeader as ComboboxHeader, ListBoxItem as ComboboxItem, ListBoxSection as ComboboxSection, Content, ContentContext, DateFormatter, Description, Disclosure, DisclosureButton, DisclosurePanel, DisclosureStateContext, ErrorMessage, Footer, GrunnmurenProvider, Heading, HeadingContext, Label, LinkList, LinkListContainer, LinkListItem, Media, MediaContext, NumberField, Radio, RadioGroup, Select, ListBoxHeader as SelectHeader, ListBoxItem as SelectItem, ListBoxSection as SelectSection, Tag, TagGroup, TagList, TextArea, TextField, Carousel as UNSAFE_Carousel, CarouselButton as UNSAFE_CarouselButton, CarouselControls as UNSAFE_CarouselControls, CarouselItem as UNSAFE_CarouselItem, CarouselItems as UNSAFE_CarouselItems, CarouselItemsContainer as UNSAFE_CarouselItemsContainer, CarouselItemsContainer as UNSAFE_CarouselItemsContainerProps, Dialog as UNSAFE_Dialog, DialogTrigger as UNSAFE_DialogTrigger, FileUpload as UNSAFE_FileUpload, Hero as UNSAFE_Hero, Link as UNSAFE_Link, Modal as UNSAFE_Modal, ProgressBar as UNSAFE_ProgressBar, ProgressBarValueText as UNSAFE_ProgressBarValueText, Tab as UNSAFE_Tab, TabList as UNSAFE_TabList, TabPanel as UNSAFE_TabPanel, Table as UNSAFE_Table, TableBody as UNSAFE_TableBody, TableCell as UNSAFE_TableCell, TableColumn as UNSAFE_TableColumn, TableColumnResizer as UNSAFE_TableColumnResizer, TableContainer as UNSAFE_TableContainer, TableHeader as UNSAFE_TableHeader, TableRow as UNSAFE_TableRow, Tabs as UNSAFE_Tabs, VideoLoop, _LinkContext, _useLocale as useLocale };
797
+ export type { AccordionItemProps, AccordionProps, Props as AlertboxProps, AvatarProps, BacklinkProps, BadgeProps, BreadcrumbProps, BreadcrumbsProps, ButtonProps, CaptionProps, CardLinkProps, CardProps, CheckboxGroupProps, CheckboxProps, ComboboxProps, ContentProps, DateFormatterProps, DescriptionProps, DisclosureButtonProps, DisclosurePanelProps, DisclosureProps, ErrorMessageProps, FooterProps, GrunnmurenProviderProps, HeadingProps, LinkListContainerProps, LinkListItemProps, LinkListProps, Locale, MediaProps, NumberFieldProps, RadioGroupProps, RadioProps, SelectProps, TagGroupProps, TagListProps, TagProps, TextAreaProps, TextFieldProps, CarouselButtonProps as UNSAFE_CarouselButtonProps, CarouselControlsProps as UNSAFE_CarouselControlsProps, CarouselItemProps as UNSAFE_CarouselItemProps, CarouselItemsProps as UNSAFE_CarouselItemsProps, CarouselProps as UNSAFE_CarouselProps, DialogProps as UNSAFE_DialogProps, DialogTriggerProps as UNSAFE_DialogTriggerProps, FileUploadProps as UNSAFE_FileUploadProps, HeroProps as UNSAFE_HeroProps, LinkProps as UNSAFE_LinkProps, ModalProps as UNSAFE_ModalProps, ProgressBarProps as UNSAFE_ProgressBarProps, ProgressBarValueTextProps as UNSAFE_ProgressBarValueTextProps, TabListProps as UNSAFE_TabListProps, TabPanelProps as UNSAFE_TabPanelProps, TabProps as UNSAFE_TabProps, TableBodyProps as UNSAFE_TableBodyProps, TableCellProps as UNSAFE_TableCellProps, TableColumnProps as UNSAFE_TableColumnProps, TableColumnResizerProps as UNSAFE_TableColumnResizerProps, TableContainerProps as UNSAFE_TableContainerProps, TableHeaderProps as UNSAFE_TableHeaderProps, TableProps as UNSAFE_TableProps, TableRowProps as UNSAFE_TableRowProps, TabsProps as UNSAFE_TabsProps };
package/dist/index.mjs CHANGED
@@ -3,16 +3,19 @@ import { useContextProps, DisclosureContext, DisclosureGroupStateContext, Provid
3
3
  export { DisclosureGroup, Form, Group } from 'react-aria-components';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import { cva, cx, compose } from 'cva';
6
- import { createContext, useContext, useId, useRef, Children, useState, useEffect, isValidElement, cloneElement, useCallback } from 'react';
6
+ import { createContext, useContext, useId, useRef, Children, useState, useEffect, useMemo, useCallback, isValidElement, cloneElement } from 'react';
7
7
  import { ChevronDown, Error, Warning, CheckCircle, InfoCircle, Close, User, ChevronLeft, ChevronRight, LoadingSpinner, Check, Trash, ArrowRight, Download, LinkExternal, PlayerPause, PlayerPlay } from '@obosbbl/grunnmuren-icons-react';
8
- import { filterDOMProps, mergeProps, mergeRefs, useUpdateEffect, useObjectRef, useFormReset } from '@react-aria/utils';
8
+ import { filterDOMProps, mergeProps, mergeRefs, useObjectRef, useFormReset, useUpdateEffect } from '@react-aria/utils';
9
9
  import { useFocusRing, useDisclosure, useProgressBar, useDateFormatter, useField } from 'react-aria';
10
10
  import { useDisclosureState } from 'react-stately';
11
- import { useDebouncedCallback } from 'use-debounce';
11
+ import Autoplay from 'embla-carousel-autoplay';
12
+ import useEmblaCarousel from 'embla-carousel-react';
13
+ import { WheelGesturesPlugin } from 'embla-carousel-wheel-gestures';
12
14
  import { useFormValidation } from '@react-aria/form';
13
15
  import { useFormValidationState } from '@react-stately/form';
14
16
  import { useControlledState } from '@react-stately/utils';
15
17
  import { PressResponder } from '@react-aria/interactions';
18
+ import { useDebouncedCallback } from 'use-debounce';
16
19
 
17
20
  const HeadingContext = /*#__PURE__*/ createContext({});
18
21
  const headingVariants = cva({
@@ -354,6 +357,11 @@ const translations$1 = {
354
357
  sv: 'Nästa',
355
358
  en: 'Next'
356
359
  },
360
+ carousel: {
361
+ nb: 'Karusell',
362
+ sv: 'Karusell',
363
+ en: 'Carousel'
364
+ },
357
365
  externalLink: {
358
366
  nb: '(ekstern lenke)',
359
367
  sv: '(extern länk)',
@@ -982,7 +990,9 @@ const cardLinkVariants = cva({
982
990
  *
983
991
  * Keep in mind that this hook relies on a browser API's and doesn't run on the server.
984
992
  * You can supply an initial value that will be used for server side rendering.
985
- */ function usePrefersReducedMotion(initialValue = true) {
993
+ *
994
+ * The default initial value is `false` as this aligns with most users. This is to prevent unecessary rerenders on mount for the common user.
995
+ */ function usePrefersReducedMotion(initialValue = false) {
986
996
  const [prefersReducedMotion, setPrefersReducedMotion] = useState(initialValue);
987
997
  useEffect(()=>{
988
998
  const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
@@ -998,182 +1008,137 @@ const cardLinkVariants = cva({
998
1008
  return prefersReducedMotion;
999
1009
  }
1000
1010
 
1001
- const Carousel = ({ className, children, onChange, ...rest })=>{
1011
+ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0, orientation = 'horizontal', onSelect, onSettled, loop = false, scrollGestures = false, ref, ...rest })=>{
1002
1012
  const carouselRef = useRef(null);
1003
- const carouselItemsRef = useRef(null);
1004
- const locale = _useLocale();
1005
- const { previous, next } = translations$1;
1006
- const [scrollTargetIndex, setScrollTargetIndex] = useState(0);
1007
- const isScrollingProgrammatically = useRef(false);
1008
- const scrollTimeoutRef = useRef(null);
1009
- const scrollQueue = useRef([]);
1010
- const [hasReachedScrollStart, setHasReachedScrollStart] = useState(scrollTargetIndex === 0);
1011
- const [hasReachedScrollEnd, setHasReachedScrollEnd] = useState(!carouselItemsRef.current || carouselItemsRef.current.children.length - 1 === scrollTargetIndex);
1012
- const prefersReducedMotion = usePrefersReducedMotion();
1013
- useEffect(()=>{
1014
- setHasReachedScrollStart(scrollTargetIndex === 0);
1015
- setHasReachedScrollEnd(!carouselItemsRef.current || carouselItemsRef.current.children.length - 1 === scrollTargetIndex);
1016
- }, [
1017
- scrollTargetIndex
1018
- ]);
1019
- // Keep track of the previous index to determine if the user is scrolling forward or backward
1020
- // This is used to determine which callback to call (onPrev or onNext)
1021
- const prevIndex = useRef(0);
1022
- // Processes the next scroll action in the queue, if any
1023
- // All clicks on the prev/next buttons are queued while a programmatic scroll is in progress
1024
- // This is to ensure that rapid clicks on the buttons do not cause janky scrolling behavior
1025
- // while still a snappy response to user clicks
1026
- const processQueue = ()=>{
1027
- if (scrollQueue.current.length > 0 && !isScrollingProgrammatically.current) {
1028
- const nextIndex = scrollQueue.current?.shift();
1029
- if (nextIndex !== undefined) {
1030
- setScrollTargetIndex(nextIndex);
1031
- }
1013
+ const prefersReducedMotion = usePrefersReducedMotion() ?? false;
1014
+ const emblaPlugins = useMemo(()=>{
1015
+ const plugins = [];
1016
+ if (scrollGestures) {
1017
+ plugins.push(WheelGesturesPlugin());
1032
1018
  }
1033
- };
1034
- // Handle scrolling when user clicks the arrow icons
1035
- useUpdateEffect(()=>{
1036
- if (!carouselItemsRef.current) {
1037
- return;
1038
- }
1039
- if (scrollTimeoutRef.current) {
1040
- clearTimeout(scrollTimeoutRef.current);
1041
- }
1042
- isScrollingProgrammatically.current = true;
1043
- const elementWithFocusVisible = carouselRef.current?.querySelector(':focus-visible');
1044
- carouselItemsRef.current.children[scrollTargetIndex]?.scrollIntoView({
1045
- behavior: prefersReducedMotion ? 'instant' : 'smooth',
1046
- inline: 'start',
1047
- block: 'nearest'
1048
- });
1049
- if (prevIndex.current !== scrollTargetIndex && onChange) {
1050
- onChange({
1051
- index: scrollTargetIndex,
1052
- id: carouselItemsRef.current.children[scrollTargetIndex]?.id,
1053
- prevIndex: prevIndex.current,
1054
- prevId: carouselItemsRef.current.children[prevIndex.current]?.id
1055
- });
1019
+ if (autoPlayDelay) {
1020
+ plugins.push(Autoplay({
1021
+ delay: autoPlayDelay,
1022
+ stopOnLastSnap: !loop,
1023
+ jump: prefersReducedMotion
1024
+ }));
1056
1025
  }
1057
- prevIndex.current = scrollTargetIndex;
1058
- scrollTimeoutRef.current = setTimeout(()=>{
1059
- isScrollingProgrammatically.current = false;
1060
- scrollTimeoutRef.current = null;
1061
- if (elementWithFocusVisible && !carouselRef.current?.contains(document.activeElement)) {
1062
- // Restore focus to the appropriate element after scrolling
1063
- // First check if the prev or next buttons just got hidden due to reaching the start/end of the carousel
1064
- // If so, move focus to the other button. This is to avoid a scroll jank that occurs if instead focus is restored to the carousel container
1065
- // This jank happens because of the delays used for scrolling with these buttons (debounce, queuing etc.).
1066
- switch(elementWithFocusVisible.slot){
1067
- case 'prev':
1068
- {
1069
- // Focus was lost when the prev button turned invisible, set it to the next button
1070
- const nextButton = carouselRef.current?.querySelector('[slot="next"]');
1071
- nextButton?.focus();
1072
- break;
1073
- }
1074
- case 'next':
1075
- {
1076
- // Focus was lost when the next button turned invisible, set it to the prev button
1077
- const prevButton = carouselRef.current?.querySelector('[slot="prev"]');
1078
- prevButton?.focus();
1079
- break;
1080
- }
1081
- default:
1082
- {
1083
- // Focus was lost during while scrolling with left/right arrows, restore it to the carousel container
1084
- carouselItemsRef.current?.focus();
1085
- break;
1086
- }
1087
- }
1088
- }
1089
- processQueue(); // Process any queued scrolls
1090
- }, 500);
1026
+ return plugins;
1091
1027
  }, [
1092
- scrollTargetIndex,
1028
+ autoPlayDelay,
1029
+ scrollGestures,
1030
+ loop,
1093
1031
  prefersReducedMotion
1094
1032
  ]);
1095
- // Clean up timeout on unmount
1033
+ const [emblaRef, emblaApi] = useEmblaCarousel({
1034
+ align,
1035
+ loop,
1036
+ startIndex: initialIndex,
1037
+ axis: orientation === 'horizontal' ? 'x' : 'y',
1038
+ inViewThreshold: 0.2
1039
+ }, emblaPlugins);
1040
+ const [slidesInView, setSlidesInView] = useState([
1041
+ initialIndex
1042
+ ]);
1043
+ const previousSettledScrollIndex = useRef(initialIndex);
1096
1044
  useEffect(()=>{
1097
- return ()=>{
1098
- if (scrollTimeoutRef.current) {
1099
- clearTimeout(scrollTimeoutRef.current);
1045
+ if (!emblaApi) {
1046
+ return;
1047
+ }
1048
+ const emblaHandler = (_, type)=>{
1049
+ const scrollSnapIndex = emblaApi.selectedScrollSnap();
1050
+ switch(type){
1051
+ case 'select':
1052
+ onSelect?.(scrollSnapIndex);
1053
+ break;
1054
+ case 'settle':
1055
+ {
1056
+ // We only invoke the callback if the scroll index actually changed from the previous settled index
1057
+ // Otherwise this gets triggered if the user does the tiniest bit of scrolling in the carousel, but doesn't transition to the next slide
1058
+ if (scrollSnapIndex !== previousSettledScrollIndex.current) {
1059
+ previousSettledScrollIndex.current = scrollSnapIndex;
1060
+ onSettled?.(scrollSnapIndex);
1061
+ }
1062
+ break;
1063
+ }
1064
+ case 'slidesInView':
1065
+ {
1066
+ setSlidesInView(emblaApi.slidesInView());
1067
+ break;
1068
+ }
1100
1069
  }
1101
1070
  };
1102
- }, []);
1103
- const onScroll = useDebouncedCallback((event)=>{
1104
- // Ignore scroll events when we're programmatically scrolling
1105
- if (isScrollingProgrammatically.current) {
1071
+ emblaApi.on('select', emblaHandler);
1072
+ emblaApi.on('slidesInView', emblaHandler);
1073
+ emblaApi.on('settle', emblaHandler);
1074
+ return ()=>{
1075
+ emblaApi.off('select', emblaHandler);
1076
+ emblaApi.off('settle', emblaHandler);
1077
+ emblaApi.off('slidesInView', emblaHandler);
1078
+ };
1079
+ }, [
1080
+ emblaApi,
1081
+ onSelect,
1082
+ onSettled
1083
+ ]);
1084
+ const handleNextPress = useCallback(()=>{
1085
+ if (!emblaApi) {
1106
1086
  return;
1107
1087
  }
1108
- const target = event.target;
1109
- const containerRect = target.getBoundingClientRect();
1110
- // Calculate the index of the item that is currently in view
1111
- const newScrollTargetIndex = Array.from(target.children).findIndex((child)=>{
1112
- const rect = child.getBoundingClientRect();
1113
- // Check if the item is more than 50% visible within the container
1114
- const visibleWidth = Math.min(rect.right, containerRect.right) - Math.max(rect.left, containerRect.left);
1115
- const itemWidth = rect.width;
1116
- return visibleWidth / itemWidth > 0.5;
1117
- });
1118
- if (newScrollTargetIndex !== -1 && newScrollTargetIndex !== scrollTargetIndex) {
1119
- if (onChange) {
1120
- onChange({
1121
- index: newScrollTargetIndex,
1122
- id: target.children[newScrollTargetIndex]?.id,
1123
- prevIndex: prevIndex.current,
1124
- prevId: target.children[prevIndex.current]?.id
1125
- });
1126
- }
1127
- // Update the index and prevIndex
1128
- setScrollTargetIndex(newScrollTargetIndex);
1129
- prevIndex.current = newScrollTargetIndex;
1088
+ emblaApi.plugins().autoplay?.stop();
1089
+ emblaApi.scrollNext(prefersReducedMotion);
1090
+ // we need to move focus if we are about to disable this button due to start/end of carousel
1091
+ if (!loop && !emblaApi.canScrollNext() && carouselRef.current?.querySelector('button[slot="next"]')?.matches(':focus-visible')) {
1092
+ carouselRef.current?.querySelector('button[slot="prev"]')?.focus();
1130
1093
  }
1131
- }, 150);
1132
- const handlePrevious = ()=>{
1133
- setScrollTargetIndex((currentTargetIndex)=>{
1134
- const targetIndex = currentTargetIndex - 1;
1135
- if (targetIndex < 0) {
1136
- return currentTargetIndex;
1137
- }
1138
- if (isScrollingProgrammatically.current) {
1139
- // If we're already scrolling, queue this action
1140
- scrollQueue.current = [
1141
- targetIndex
1142
- ];
1143
- return currentTargetIndex;
1144
- }
1145
- return targetIndex;
1146
- });
1147
- };
1148
- const handleNext = ()=>{
1149
- setScrollTargetIndex((currentTargetIndex)=>{
1150
- const targetIndex = currentTargetIndex + 1;
1151
- if (!carouselItemsRef.current || targetIndex >= carouselItemsRef.current.children.length) {
1152
- return currentTargetIndex;
1153
- }
1154
- if (isScrollingProgrammatically.current) {
1155
- // If we're already scrolling, queue this action
1156
- scrollQueue.current = [
1157
- targetIndex
1158
- ];
1159
- return currentTargetIndex;
1160
- }
1161
- return targetIndex;
1162
- });
1163
- };
1164
- return /*#__PURE__*/ jsx("div", {
1094
+ }, [
1095
+ emblaApi,
1096
+ prefersReducedMotion,
1097
+ loop
1098
+ ]);
1099
+ const handlePrevPress = useCallback(()=>{
1100
+ if (!emblaApi) {
1101
+ return;
1102
+ }
1103
+ emblaApi.plugins().autoplay?.stop();
1104
+ emblaApi.scrollPrev(prefersReducedMotion);
1105
+ // we need to move focus if we are about to disable this button due to start/end of carousel
1106
+ if (!loop && !emblaApi.canScrollPrev() && carouselRef.current?.querySelector('button[slot="prev"]')?.matches(':focus-visible')) {
1107
+ carouselRef.current?.querySelector('button[slot="next"]')?.focus();
1108
+ }
1109
+ }, [
1110
+ emblaApi,
1111
+ prefersReducedMotion,
1112
+ loop
1113
+ ]);
1114
+ const locale = _useLocale();
1115
+ const handleKeyDown = useCallback((e)=>{
1116
+ if (e.key === 'ArrowRight' && !e.repeat) {
1117
+ handleNextPress();
1118
+ } else if (e.key === 'ArrowLeft' && !e.repeat) {
1119
+ handlePrevPress();
1120
+ }
1121
+ }, [
1122
+ handleNextPress,
1123
+ handlePrevPress
1124
+ ]);
1125
+ return(// biome-ignore lint/a11y/useSemanticElements: we want to use a div
1126
+ /*#__PURE__*/ jsx("div", {
1127
+ ...rest,
1128
+ "data-orientation": orientation,
1165
1129
  "data-slot": "carousel",
1166
- ref: carouselRef,
1130
+ ref: mergeRefs(ref, carouselRef),
1131
+ onKeyDown: handleKeyDown,
1132
+ role: "region",
1133
+ "aria-label": translations$1.carousel[locale],
1167
1134
  children: /*#__PURE__*/ jsx(Provider, {
1168
1135
  values: [
1169
1136
  [
1170
- CarouselItemsContext,
1137
+ CarouselContext,
1171
1138
  {
1172
- carouselItemsRef,
1173
- onScroll,
1174
- activeIndex: scrollTargetIndex,
1175
- handlePrevious,
1176
- handleNext
1139
+ slidesInView,
1140
+ '~emblaRef': emblaRef,
1141
+ orientation
1177
1142
  }
1178
1143
  ],
1179
1144
  [
@@ -1182,115 +1147,123 @@ const Carousel = ({ className, children, onChange, ...rest })=>{
1182
1147
  slots: {
1183
1148
  [DEFAULT_SLOT]: {},
1184
1149
  prev: {
1185
- 'aria-label': previous[locale],
1186
- onPress: handlePrevious
1150
+ 'aria-label': translations$1.previous[locale],
1151
+ isDisabled: !emblaApi?.canScrollPrev(),
1152
+ onPress: handlePrevPress
1187
1153
  },
1188
1154
  next: {
1189
- isIconOnly: true,
1190
- 'aria-label': next[locale],
1191
- onPress: handleNext
1155
+ 'aria-label': translations$1.next[locale],
1156
+ isDisabled: !emblaApi?.canScrollNext(),
1157
+ onPress: handleNextPress
1192
1158
  }
1193
1159
  }
1194
1160
  }
1195
1161
  ]
1196
1162
  ],
1197
- children: /*#__PURE__*/ jsxs("div", {
1198
- ...rest,
1199
- className: cx(className, 'relative rounded-3xl', // 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
1200
- '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus', '[&:has([data-slot="carousel-items"]:focus-visible,[data-slot="video-loop-button"]:focus-visible)]:outline-focus-offset', // Unset the default focus outline for potential video loop buttons, as it interferes with the custom focus styles for the carousel
1201
- '**:data-[slot="video-loop-button"]:focus-visible:outline-none'),
1202
- children: [
1203
- children,
1204
- /*#__PURE__*/ jsxs(_CarouselControls, {
1205
- children: [
1206
- /*#__PURE__*/ jsx(Button, {
1207
- isIconOnly: true,
1208
- slot: "prev",
1209
- variant: "primary",
1210
- color: "white",
1211
- className: cx('group/carousel-previous', hasReachedScrollStart && 'invisible'),
1212
- children: /*#__PURE__*/ jsx(ChevronLeft, {
1213
- className: "group-hover/carousel-previous:motion-safe:-translate-x-1 transition-transform"
1214
- })
1215
- }),
1216
- /*#__PURE__*/ jsx(Button, {
1217
- isIconOnly: true,
1218
- slot: "next",
1219
- variant: "primary",
1220
- color: "white",
1221
- className: cx('group/carousel-next', hasReachedScrollEnd && 'invisible'),
1222
- children: /*#__PURE__*/ jsx(ChevronRight, {
1223
- className: "transition-transform group-hover/carousel-next:motion-safe:translate-x-1"
1224
- })
1225
- })
1226
- ]
1227
- })
1228
- ]
1229
- })
1163
+ children: children
1164
+ })
1165
+ }));
1166
+ };
1167
+ const CarouselContext = /*#__PURE__*/ createContext({
1168
+ '~emblaRef': null,
1169
+ orientation: 'horizontal',
1170
+ slidesInView: []
1171
+ });
1172
+ const CarouselItemsContainer = ({ children, className, ...rest })=>{
1173
+ const { '~emblaRef': emblaRef } = useContext(CarouselContext);
1174
+ return /*#__PURE__*/ jsx("div", {
1175
+ className: cx(className, 'overflow-hidden'),
1176
+ ref: emblaRef,
1177
+ "data-slot": "carousel-items-container",
1178
+ ...rest,
1179
+ children: children
1180
+ });
1181
+ };
1182
+ const CarouselItems = ({ className, children })=>{
1183
+ const { slidesInView, orientation } = useContext(CarouselContext);
1184
+ return /*#__PURE__*/ jsx("div", {
1185
+ className: cx(className, 'flex', orientation === 'vertical' && 'flex-col'),
1186
+ "data-slot": "carousel-items",
1187
+ children: Children.map(children, (child, index)=>{
1188
+ if (/*#__PURE__*/ isValidElement(child)) {
1189
+ return /*#__PURE__*/ cloneElement(child, {
1190
+ inert: slidesInView.includes(index) ? undefined : true
1191
+ });
1192
+ }
1230
1193
  })
1231
1194
  });
1232
1195
  };
1233
1196
  /**
1234
1197
  * This is internal for now, but we will expose it in the future when we support more flexible positioning of prev/next and other actions.
1235
1198
  * It is used to render the prev/next buttons in the carousel for now.
1236
- */ const _CarouselControls = ({ children, className })=>/*#__PURE__*/ jsx("div", {
1237
- className: cx(className, 'absolute right-6 bottom-6 flex gap-x-2', // Make it easier to position in full-bleed hero variants (these style have no other side effects)
1238
- 'items-end *:h-fit'),
1199
+ */ const CarouselControls = ({ children, className, ...rest })=>/*#__PURE__*/ jsx("div", {
1200
+ className: cx(className, 'flex justify-end gap-x-2'),
1239
1201
  "data-slot": "carousel-controls",
1202
+ ...rest,
1240
1203
  children: children
1241
1204
  });
1242
- const CarouselItemsContext = /*#__PURE__*/ createContext({
1243
- carouselItemsRef: null,
1244
- activeIndex: 0
1205
+ const carouselButtonVariants = cva({
1206
+ base: 'group data-disabled:invisible'
1245
1207
  });
1246
- const CarouselItems = ({ className, children })=>{
1247
- const { carouselItemsRef, onScroll, activeIndex, handlePrevious, handleNext } = useContext(CarouselItemsContext);
1248
- const prefersReducedMotion = usePrefersReducedMotion();
1249
- const handleKeyDown = (event)=>{
1250
- // Prevent default behavior when holding down arrow keys (when repeat is true)
1251
- // The default behavior in scroll snapping causes a staggering scroll effect that feels janky
1252
- if (event.repeat && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
1253
- event.preventDefault();
1254
- return;
1208
+ const carouselButtonIconSlotVariants = cva({
1209
+ base: 'transition-transform',
1210
+ variants: {
1211
+ slot: {
1212
+ next: null,
1213
+ prev: null
1214
+ },
1215
+ orientation: {
1216
+ horizontal: null,
1217
+ vertical: null
1255
1218
  }
1256
- // For users with prefers-reduced-motion, trigger button click behavior instead of native scroll
1257
- if (prefersReducedMotion) {
1258
- if (event.key === 'ArrowLeft' && handlePrevious) {
1259
- event.preventDefault();
1260
- handlePrevious();
1261
- } else if (event.key === 'ArrowRight' && handleNext) {
1262
- event.preventDefault();
1263
- handleNext();
1264
- }
1219
+ },
1220
+ compoundVariants: [
1221
+ // horizontal controls
1222
+ {
1223
+ slot: 'next',
1224
+ orientation: 'horizontal',
1225
+ className: 'group-hover:motion-safe:translate-x-1'
1226
+ },
1227
+ {
1228
+ slot: 'prev',
1229
+ orientation: 'horizontal',
1230
+ className: 'group-hover:motion-safe:-translate-x-1 rotate-180'
1231
+ },
1232
+ // vertical controls
1233
+ {
1234
+ slot: 'next',
1235
+ orientation: 'vertical',
1236
+ className: 'rotate-90 group-hover:motion-safe:translate-y-1'
1237
+ },
1238
+ {
1239
+ slot: 'prev',
1240
+ orientation: 'vertical',
1241
+ className: 'group-hover:motion-safe:-translate-y-1 -rotate-90'
1265
1242
  }
1266
- };
1267
- return(// biome-ignore lint/a11y/noStaticElementInteractions: The keydown handler is only to prevent undesired scrolling behavior when using the arrow keys
1268
- /*#__PURE__*/ jsx("div", {
1269
- "data-slot": "carousel-items",
1270
- className: cx(className, [
1271
- 'scrollbar-hidden',
1272
- 'flex',
1273
- 'snap-x',
1274
- 'snap-mandatory',
1275
- 'overflow-x-auto',
1276
- 'outline-none',
1277
- 'rounded-[inherit]'
1278
- ]),
1279
- ref: carouselItemsRef,
1280
- onScroll: onScroll,
1281
- onKeyDown: handleKeyDown,
1282
- children: Children.map(children, (child, index)=>{
1283
- if (/*#__PURE__*/ isValidElement(child)) {
1284
- return /*#__PURE__*/ cloneElement(child, {
1285
- inert: activeIndex === index ? undefined : true
1286
- });
1287
- }
1243
+ ]
1244
+ });
1245
+ const CarouselButton = ({ className, isIconOnly = true, color = 'white', variant = 'primary', slot, ...rest })=>{
1246
+ const { orientation } = useContext(CarouselContext);
1247
+ return /*#__PURE__*/ jsx(Button, {
1248
+ className: carouselButtonVariants({
1249
+ className
1250
+ }),
1251
+ isIconOnly: isIconOnly,
1252
+ slot: slot,
1253
+ variant: variant,
1254
+ color: color,
1255
+ ...rest,
1256
+ children: /*#__PURE__*/ jsx(ChevronRight, {
1257
+ className: carouselButtonIconSlotVariants({
1258
+ orientation,
1259
+ slot
1260
+ })
1288
1261
  })
1289
- }));
1262
+ });
1290
1263
  };
1291
1264
  const CarouselItem = ({ className, children, ...rest })=>{
1292
1265
  return /*#__PURE__*/ jsx("div", {
1293
- className: cx(className, 'shrink-0 basis-full snap-start'),
1266
+ className: cx(className, 'min-w-0 shrink-0 grow-0'),
1294
1267
  "data-slot": "carousel-item",
1295
1268
  ...rest,
1296
1269
  children: /*#__PURE__*/ jsx(Provider, {
@@ -1606,7 +1579,7 @@ function Combobox(props) {
1606
1579
  /**
1607
1580
  * A FileTrigger allows a user to access the file system with any pressable React Aria or React Spectrum component, or custom components built with usePress.
1608
1581
  */ const FileTrigger = (props)=>{
1609
- const { onSelect, acceptedFileTypes, allowsMultiple, defaultCamera, children, acceptDirectory, ref, isInvalid, isRequired, name, value, ...rest } = props;
1582
+ const { onSelect, acceptedFileTypes, allowsMultiple, defaultCamera, children, acceptDirectory, ref, isInvalid, isRequired, name, ...rest } = props;
1610
1583
  const inputRef = useObjectRef(ref);
1611
1584
  return /*#__PURE__*/ jsxs(Fragment, {
1612
1585
  children: [
@@ -1655,6 +1628,46 @@ const translations = {
1655
1628
  en: 'Remove'
1656
1629
  }
1657
1630
  };
1631
+ /**
1632
+ * Extracts a simple file extension from a file name or converts MIME type to extension
1633
+ * @param file The file object
1634
+ * @returns A simple file extension (e.g., "pdf", "jpg", "svg")
1635
+ */ function getFileExtension(file) {
1636
+ const match = file.name.match(/\.([^.]+)$/);
1637
+ if (match) {
1638
+ return match[1].toUpperCase();
1639
+ }
1640
+ const mimeType = file.type;
1641
+ if (!mimeType) {
1642
+ return '';
1643
+ }
1644
+ const parts = mimeType.split('/');
1645
+ if (parts.length === 2) {
1646
+ const subtype = parts[1].split('+')[0]; // Handle cases like "svg+xml"
1647
+ return subtype.toUpperCase();
1648
+ }
1649
+ return '';
1650
+ }
1651
+ /**
1652
+ * Formats a file size in bytes to a human-readable string (B, KB, MB, GB, etc.)
1653
+ * @param bytes The file size in bytes
1654
+ * @returns A formatted string with the appropriate unit
1655
+ */ function formatFileSize(bytes) {
1656
+ if (bytes === 0) {
1657
+ return '0 B';
1658
+ }
1659
+ const units = [
1660
+ 'B',
1661
+ 'KB',
1662
+ 'MB',
1663
+ 'GB',
1664
+ 'TB'
1665
+ ];
1666
+ const base = 1024;
1667
+ const unitIndex = Math.floor(Math.log(bytes) / Math.log(base));
1668
+ const size = bytes / base ** unitIndex;
1669
+ return `${size.toFixed(2).replace(/\.?0+$/, '')} ${units[unitIndex]}`;
1670
+ }
1658
1671
  /**
1659
1672
  * Converts an array of files to a DataTransfer object which can be used as a FileList.
1660
1673
  * This is necessary for setting the files on a native file input.
@@ -1682,7 +1695,7 @@ const translations = {
1682
1695
  const extension = fileName.match(/(\.[^.]+)$/)?.[0] || '';
1683
1696
  if (!fileNameCounts[baseName]) {
1684
1697
  // Extract any number from the file name (if any, otherwise default to 0)
1685
- const baseNameCount = Number.parseInt(fileName.match(/\((\d+)\)/)?.[1] ?? '0');
1698
+ const baseNameCount = Number.parseInt(fileName.match(/\((\d+)\)/)?.[1] ?? '0', 10);
1686
1699
  fileNameCounts[baseName] = baseNameCount;
1687
1700
  }
1688
1701
  fileNameCounts[baseName]++;
@@ -1831,11 +1844,34 @@ const FileUpload = ({ children, files: _files, onChange, validate, isInvalid: _i
1831
1844
  return /*#__PURE__*/ jsxs("li", {
1832
1845
  children: [
1833
1846
  /*#__PURE__*/ jsxs("div", {
1834
- className: cx('flex items-center justify-between gap-2 rounded-lg border-2 px-4 py-2', hasError ? 'border-red bg-red-light' : 'border-gray bg-gray-lightest'),
1847
+ className: cx('flex items-center justify-between gap-3 rounded-lg border p-1.5', hasError ? 'border-red bg-red-light' : 'border-gray'),
1835
1848
  children: [
1836
- fileName,
1849
+ /*#__PURE__*/ jsxs("div", {
1850
+ className: "flex items-center gap-3",
1851
+ children: [
1852
+ /*#__PURE__*/ jsx("div", {
1853
+ className: "footnote rounded-md border border-gray-light bg-gray-lightest px-2.5 py-2",
1854
+ children: getFileExtension(file)
1855
+ }),
1856
+ /*#__PURE__*/ jsxs("div", {
1857
+ className: "flex flex-col",
1858
+ children: [
1859
+ /*#__PURE__*/ jsx("span", {
1860
+ className: "description truncate font-medium",
1861
+ children: fileName
1862
+ }),
1863
+ /*#__PURE__*/ jsx("span", {
1864
+ className: "footnote text-gray-dark",
1865
+ children: formatFileSize(file.size)
1866
+ })
1867
+ ]
1868
+ })
1869
+ ]
1870
+ }),
1837
1871
  /*#__PURE__*/ jsx("button", {
1838
- className: cx('self-start', '-m-2 grid h-11 w-11 shrink-0 cursor-pointer place-items-center rounded-xl', // Focus styles
1872
+ type: "button",
1873
+ "aria-label": translations.remove[locale],
1874
+ className: cx('-m-2 grid h-11 w-11 shrink-0 cursor-pointer place-items-center rounded-xl', // Focus styles
1839
1875
  'focus-visible:-outline-offset-8 focus-visible:outline-focus'),
1840
1876
  onClick: ()=>{
1841
1877
  // For controlled component
@@ -1846,8 +1882,6 @@ const FileUpload = ({ children, files: _files, onChange, validate, isInvalid: _i
1846
1882
  // (without this, the focus will be set to the top of the page for screen readers)
1847
1883
  buttonRef.current?.focus();
1848
1884
  },
1849
- "aria-label": translations.remove[locale],
1850
- type: "button",
1851
1885
  children: /*#__PURE__*/ jsx(Trash, {})
1852
1886
  })
1853
1887
  ]
@@ -1879,7 +1913,6 @@ function GrunnmurenProvider({ children, locale = 'nb', navigate, useHref }) {
1879
1913
  });
1880
1914
  }
1881
1915
 
1882
- const roundedMediaCorners = '*:data-[slot="media"]:*:rounded-3xl';
1883
1916
  // Common variant for "standard" and "full-bleed" Hero variants
1884
1917
  const oneColumnLayout = [
1885
1918
  // Vertical spacing in the <Content>
@@ -1892,7 +1925,7 @@ const oneColumnLayout = [
1892
1925
  'lg:*:not-data-[slot="content"]:not-data-[slot="media"]:not-data-[slot="carousel"]:col-span-3 lg:*:not-data-[slot="content"]:not-data-[slot="media"]:justify-self-end',
1893
1926
  // <Media> and <Carousel> content takes up the full width on medium screens and above
1894
1927
  'lg:*:data-[slot="media"]:col-span-full *:data-[slot="media"]:*:w-full',
1895
- 'lg:*:data-[slot="carousel"]:col-span-full *:data-[slot="carousel"]:*:w-full',
1928
+ 'lg:*:data-[slot="carousel"]:col-span-full',
1896
1929
  // Aligns <Content> and any element beside it (e.g. <Media>, <Badge>, <CTA> etc.) to the bottom of the <Content> container
1897
1930
  'lg:items-end'
1898
1931
  ];
@@ -1908,7 +1941,10 @@ const variants = cva({
1908
1941
  // Vertical spacing in the <Content>
1909
1942
  '*:data-[slot="content"]:gap-y-3',
1910
1943
  // Make sure <Media> content fills any available vertical and horizontal space
1911
- '*:data-[slot="media"]:*:object-cover'
1944
+ '*:data-[slot="media"]:*:object-cover',
1945
+ '*:data-[slot="carousel"]:overflow-hidden *:data-[slot="carousel"]:rounded-3xl',
1946
+ // Make the carousel items full width, so we scroll one at a time
1947
+ '**:data-[slot="carousel-item"]:basis-full'
1912
1948
  ],
1913
1949
  variants: {
1914
1950
  /**
@@ -1916,7 +1952,6 @@ const variants = cva({
1916
1952
  * @default standard
1917
1953
  * */ variant: {
1918
1954
  standard: [
1919
- roundedMediaCorners,
1920
1955
  oneColumnLayout,
1921
1956
  nonFullBleedAspectRatiosForSmallScreens,
1922
1957
  'lg:*:data-[slot="media"]:*:aspect-2/1'
@@ -1926,32 +1961,44 @@ const variants = cva({
1926
1961
  // Position the media and carousel content to fill the entire viewport width
1927
1962
  '*:data-[slot="media"]:*:absolute *:data-[slot="media"]:*:left-0',
1928
1963
  // Special case for Carousel, where the Media is nested inside a CarouselItem
1929
- '*:data-[slot="carousel"]:**:data-[slot="media"]:w-full *:data-[slot="carousel"]:*:absolute *:data-[slot="carousel"]:*:left-0',
1964
+ '*:data-[slot="carousel"]:**:data-[slot="media"]:w-full',
1930
1965
  // Match the heights of the <Media> or <Carousel> wrapper for the Media content (e.g. image, VideoLoop, video etc.)
1931
1966
  // This is necessary due to the absolute positioning of the media and carousel containers in this variant
1932
1967
  // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
1933
1968
  '**:data-[slot="media"]:h-80 sm:**:data-[slot="media"]:h-[25rem] md:**:data-[slot="media"]:h-[30rem] lg:**:data-[slot="media"]:h-[35rem] xl:**:data-[slot="media"]:h-[40rem] 2xl:**:data-[slot="media"]:h-[42rem] 3xl:**:data-[slot="media"]:h-[48rem] 4xl:**:data-[slot="media"]:h-[53rem]',
1934
- // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
1935
- '**:data-[slot="media"]:*:h-80 sm:**:data-[slot="media"]:*:h-[25rem] md:**:data-[slot="media"]:*:h-[30rem] lg:**:data-[slot="media"]:*:h-[35rem] xl:**:data-[slot="media"]:*:h-[40rem] 2xl:**:data-[slot="media"]:*:h-[42rem] 3xl:**:data-[slot="media"]:*:h-[48rem] 4xl:**:data-[slot="media"]:*:h-[53rem]',
1969
+ '**:data-[slot="media"]:*:h-[inherit]',
1936
1970
  // biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
1937
1971
  '*:data-[slot="carousel"]:h-80 sm:*:data-[slot="carousel"]:h-[25rem] md:*:data-[slot="carousel"]:h-[30rem] lg:*:data-[slot="carousel"]:h-[35rem] xl:*:data-[slot="carousel"]:h-[40rem] 2xl:*:data-[slot="carousel"]:h-[42rem] 3xl:*:data-[slot="carousel"]:h-[48rem] 4xl:*:data-[slot="carousel"]:h-[53rem]',
1972
+ '*:data-[slot="carousel"]:w-full!',
1938
1973
  // Override aspect ratio of the media and carousel-item slots (since we can not use aspect for full-bleed layout)
1939
1974
  '**:data-[slot="carousel-item"]:data-[slot="media"]:*:aspect-none',
1940
- '**:data-[slot="carousel-controls"]:container **:data-[slot="carousel-controls"]:right-0 **:data-[slot="carousel-controls"]:bottom-4 **:data-[slot="carousel-controls"]:left-0 **:data-[slot="carousel-controls"]:justify-end',
1941
- // Override rounded corners of Carousel slots
1942
- '*:data-[slot="carousel"]:*:rounded-none'
1975
+ // break out the carousel out of the container
1976
+ '**:data-[slot="carousel-items-container"]:absolute **:data-[slot="carousel-items-container"]:right-0 **:data-[slot="carousel-items-container"]:left-0 **:data-[slot="carousel-items-container"]:h-[inherit]',
1977
+ // Positions the carousel controls inside the carousel
1978
+ '**:data-[slot="carousel-controls"]:z-10 **:data-[slot="carousel-controls"]:mb-4 *:data-[slot="carousel"]:flex *:data-[slot="carousel"]:items-end *:data-[slot="carousel"]:justify-end'
1943
1979
  ],
1944
1980
  'two-column': [
1945
1981
  'lg:items-center lg:*:col-span-6',
1946
1982
  // Vertical spacing in the <Content>
1947
1983
  'lg:*:data-[slot="content"]:gap-y-7',
1948
- roundedMediaCorners,
1949
1984
  nonFullBleedAspectRatiosForSmallScreens,
1950
1985
  // Set media aspect ratio to 1:1 (square)
1951
- 'lg:*:data-[slot="media"]:*:aspect-[1/1]'
1986
+ 'lg:*:data-[slot="media"]:*:aspect-square'
1952
1987
  ]
1953
1988
  }
1954
1989
  },
1990
+ compoundVariants: [
1991
+ {
1992
+ variant: [
1993
+ 'standard',
1994
+ 'two-column'
1995
+ ],
1996
+ className: [
1997
+ '*:data-[slot="media"]:*:rounded-3xl',
1998
+ '**:data-[slot="carousel-controls"]:absolute *:data-[slot="carousel"]:relative **:data-[slot="carousel-controls"]:right-4 **:data-[slot="carousel-controls"]:bottom-4 **:data-[slot="carousel-container"]:rounded-3xl'
1999
+ ]
2000
+ }
2001
+ ],
1955
2002
  defaultVariants: {
1956
2003
  variant: 'standard'
1957
2004
  }
@@ -2104,30 +2151,28 @@ const Modal = ({ isDismissable = true, isOpen, onOpenChange, defaultOpen, classN
2104
2151
  };
2105
2152
  const Dialog = ({ className, children, ...restProps })=>/*#__PURE__*/ jsx(Dialog$1, {
2106
2153
  ...restProps,
2107
- className: cx('relative grid gap-y-5 outline-none', // Footer
2154
+ className: cx(className, 'relative grid gap-y-5 outline-none', // Footer
2108
2155
  '[&_[data-slot="footer"]]:flex [&_[data-slot="footer"]]:gap-x-2'),
2109
- children: ({ close })=>/*#__PURE__*/ jsx(Fragment, {
2110
- children: /*#__PURE__*/ jsx(Provider, {
2111
- values: [
2112
- [
2113
- ButtonContext$1,
2114
- {
2115
- // This is necessary to support multiple close buttons
2116
- slots: {
2117
- // We need to define default slot in order to also support non-slotted buttons (i.e. buttons without slot prop)
2118
- [DEFAULT_SLOT]: {
2119
- className: 'w-fit'
2120
- },
2121
- close: {
2122
- onPress: close,
2123
- className: 'w-fit'
2124
- }
2156
+ children: ({ close })=>/*#__PURE__*/ jsx(Provider, {
2157
+ values: [
2158
+ [
2159
+ ButtonContext$1,
2160
+ {
2161
+ // This is necessary to support multiple close buttons
2162
+ slots: {
2163
+ // We need to define default slot in order to also support non-slotted buttons (i.e. buttons without slot prop)
2164
+ [DEFAULT_SLOT]: {
2165
+ className: 'w-fit'
2166
+ },
2167
+ close: {
2168
+ onPress: close,
2169
+ className: 'w-fit'
2125
2170
  }
2126
2171
  }
2127
- ]
2128
- ],
2129
- children: children
2130
- })
2172
+ }
2173
+ ]
2174
+ ],
2175
+ children: children
2131
2176
  })
2132
2177
  });
2133
2178
 
@@ -2695,7 +2740,7 @@ const tabsVariants = cva({
2695
2740
  const { className, children, ...restProps } = props;
2696
2741
  return /*#__PURE__*/ jsx(Tab$1, {
2697
2742
  ...restProps,
2698
- className: cx(className, 'data-focus-visible:-outline-offset-10 data-focus-visible:outline-2 data-focus-visible:outline-black', 'cursor-pointer border-transparent px-4 py-2 font-light text-sm', // Transition
2743
+ className: cx(className, 'data-focus-visible:-outline-offset-10 data-focus-visible:outline-2 data-focus-visible:outline-black', 'description cursor-pointer border-transparent px-4 py-2 font-light', // Transition
2699
2744
  'transition-colors duration-150 ease-out', // TODO: Should disabled tabs just be hidden?
2700
2745
  'data-disabled:cursor-not-allowed data-disabled:opacity-50', // Selection
2701
2746
  'data-selected:font-medium data-selected:text-blue-dark', // Hover with layout shift prevention using pseudo-element
@@ -2956,4 +3001,4 @@ const VideoLoop = ({ src, format, alt, className })=>{
2956
3001
  });
2957
3002
  };
2958
3003
 
2959
- export { Accordion, AccordionItem, Alertbox, Avatar, Backlink, Badge, Breadcrumb, Breadcrumbs, Button, ButtonContext, Caption, Card, CardLink, Checkbox, CheckboxGroup, Combobox, ListBoxHeader as ComboboxHeader, ListBoxItem as ComboboxItem, ListBoxSection as ComboboxSection, Content, ContentContext, DateFormatter, Description, Disclosure, DisclosureButton, DisclosurePanel, DisclosureStateContext, ErrorMessage, Footer, GrunnmurenProvider, Heading, HeadingContext, Label, LinkList, LinkListContainer, LinkListItem, Media, MediaContext, NumberField, Radio, RadioGroup, Select, ListBoxHeader as SelectHeader, ListBoxItem as SelectItem, ListBoxSection as SelectSection, Tag, TagGroup, TagList, TextArea, TextField, Carousel as UNSAFE_Carousel, CarouselItem as UNSAFE_CarouselItem, CarouselItems as UNSAFE_CarouselItems, Dialog as UNSAFE_Dialog, DialogTrigger as UNSAFE_DialogTrigger, FileUpload as UNSAFE_FileUpload, Hero as UNSAFE_Hero, Link as UNSAFE_Link, Modal as UNSAFE_Modal, ProgressBar as UNSAFE_ProgressBar, ProgressBarValueText as UNSAFE_ProgressBarValueText, Tab as UNSAFE_Tab, TabList as UNSAFE_TabList, TabPanel as UNSAFE_TabPanel, Table as UNSAFE_Table, TableBody as UNSAFE_TableBody, TableCell as UNSAFE_TableCell, TableColumn as UNSAFE_TableColumn, TableColumnResizer as UNSAFE_TableColumnResizer, TableContainer as UNSAFE_TableContainer, TableHeader as UNSAFE_TableHeader, TableRow as UNSAFE_TableRow, Tabs as UNSAFE_Tabs, VideoLoop, _LinkContext, _useLocale as useLocale };
3004
+ export { Accordion, AccordionItem, Alertbox, Avatar, Backlink, Badge, Breadcrumb, Breadcrumbs, Button, ButtonContext, Caption, Card, CardLink, Checkbox, CheckboxGroup, Combobox, ListBoxHeader as ComboboxHeader, ListBoxItem as ComboboxItem, ListBoxSection as ComboboxSection, Content, ContentContext, DateFormatter, Description, Disclosure, DisclosureButton, DisclosurePanel, DisclosureStateContext, ErrorMessage, Footer, GrunnmurenProvider, Heading, HeadingContext, Label, LinkList, LinkListContainer, LinkListItem, Media, MediaContext, NumberField, Radio, RadioGroup, Select, ListBoxHeader as SelectHeader, ListBoxItem as SelectItem, ListBoxSection as SelectSection, Tag, TagGroup, TagList, TextArea, TextField, Carousel as UNSAFE_Carousel, CarouselButton as UNSAFE_CarouselButton, CarouselControls as UNSAFE_CarouselControls, CarouselItem as UNSAFE_CarouselItem, CarouselItems as UNSAFE_CarouselItems, CarouselItemsContainer as UNSAFE_CarouselItemsContainer, Dialog as UNSAFE_Dialog, DialogTrigger as UNSAFE_DialogTrigger, FileUpload as UNSAFE_FileUpload, Hero as UNSAFE_Hero, Link as UNSAFE_Link, Modal as UNSAFE_Modal, ProgressBar as UNSAFE_ProgressBar, ProgressBarValueText as UNSAFE_ProgressBarValueText, Tab as UNSAFE_Tab, TabList as UNSAFE_TabList, TabPanel as UNSAFE_TabPanel, Table as UNSAFE_Table, TableBody as UNSAFE_TableBody, TableCell as UNSAFE_TableCell, TableColumn as UNSAFE_TableColumn, TableColumnResizer as UNSAFE_TableColumnResizer, TableContainer as UNSAFE_TableContainer, TableHeader as UNSAFE_TableHeader, TableRow as UNSAFE_TableRow, Tabs as UNSAFE_Tabs, VideoLoop, _LinkContext, _useLocale as useLocale };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obosbbl/grunnmuren-react",
3
- "version": "3.3.3",
3
+ "version": "3.3.5",
4
4
  "description": "Grunnmuren components in React",
5
5
  "repository": {
6
6
  "url": "https://github.com/code-obos/grunnmuren"
@@ -25,6 +25,9 @@
25
25
  "@react-stately/form": "^3.1.5",
26
26
  "@react-stately/utils": "^3.10.7",
27
27
  "cva": "^1.0.0-0",
28
+ "embla-carousel-autoplay": "^8.6.0",
29
+ "embla-carousel-react": "^8.6.0",
30
+ "embla-carousel-wheel-gestures": "^8.1.0",
28
31
  "react-aria": "^3.41.1",
29
32
  "react-aria-components": "^1.10.1",
30
33
  "react-stately": "^3.39.0",
@@ -35,7 +38,7 @@
35
38
  },
36
39
  "devDependencies": {
37
40
  "@types/node": "^24.0.0",
38
- "tailwindcss": "4.1.17"
41
+ "tailwindcss": "4.1.18"
39
42
  },
40
43
  "scripts": {
41
44
  "build": "bunchee"