@obosbbl/grunnmuren-react 3.3.5 → 3.4.0
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.
- package/dist/__stories__/form-validation.stories.cjs +70 -56
- package/dist/__stories__/form-validation.stories.js +71 -57
- package/dist/__stories__/layout.stories.cjs +7 -0
- package/dist/__stories__/layout.stories.js +7 -0
- package/dist/index.d.mts +62 -13
- package/dist/index.mjs +317 -243
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useContextProps, DisclosureContext, DisclosureGroupStateContext, Provider, ButtonContext as ButtonContext$1, DEFAULT_SLOT, Button as Button$1, useLocale, Link as Link$1, Breadcrumb as Breadcrumb$1, Breadcrumbs as Breadcrumbs$1, Text, CheckboxContext, Checkbox as Checkbox$1, FieldError, Label as Label$1, CheckboxGroup as CheckboxGroup$1, Header, ListBoxItem as ListBoxItem$1, ListBoxSection as ListBoxSection$1, ListBox as ListBox$1, ComboBox, Group, Input, Popover, useSlottedContext, FormContext, FieldErrorContext, LabelContext, InputContext, I18nProvider, RouterProvider,
|
|
2
|
+
import { useContextProps, DisclosureContext, DisclosureGroupStateContext, Provider, ButtonContext as ButtonContext$1, DEFAULT_SLOT, Button as Button$1, useLocale, Link as Link$1, Breadcrumb as Breadcrumb$1, Breadcrumbs as Breadcrumbs$1, GroupContext, Text, CheckboxContext, Checkbox as Checkbox$1, FieldError, Label as Label$1, CheckboxGroup as CheckboxGroup$1, Header, ListBoxItem as ListBoxItem$1, ListBoxSection as ListBoxSection$1, ListBox as ListBox$1, ComboBox, Group, Input, Popover, useSlottedContext, FormContext, FieldErrorContext, LabelContext, InputContext, I18nProvider, RouterProvider, Dialog as Dialog$1, DialogTrigger as DialogTrigger$1, Modal as Modal$1, ModalOverlay, NumberField as NumberField$1, ProgressBar as ProgressBar$1, Radio as Radio$1, RadioGroup as RadioGroup$1, Select as Select$1, SelectValue, ColumnResizer, Table as Table$1, TableBody as TableBody$1, Cell, Column, ResizableTableContainer, TableHeader as TableHeader$1, Row, Tab as Tab$1, TabListStateContext, TabList as TabList$1, TabPanel as TabPanel$1, Tabs as Tabs$1, TagGroup as TagGroup$1, TagList as TagList$1, Tag as Tag$1, TextField as TextField$1, TextArea as TextArea$1 } from 'react-aria-components';
|
|
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, useMemo, useCallback
|
|
6
|
+
import { createContext, useContext, useId, useRef, Children, useState, useEffect, useMemo, useCallback } 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
8
|
import { filterDOMProps, mergeProps, mergeRefs, useObjectRef, useFormReset, useUpdateEffect } from '@react-aria/utils';
|
|
9
9
|
import { useFocusRing, useDisclosure, useProgressBar, useDateFormatter, useField } from 'react-aria';
|
|
@@ -357,11 +357,6 @@ const translations$1 = {
|
|
|
357
357
|
sv: 'Nästa',
|
|
358
358
|
en: 'Next'
|
|
359
359
|
},
|
|
360
|
-
carousel: {
|
|
361
|
-
nb: 'Karusell',
|
|
362
|
-
sv: 'Karusell',
|
|
363
|
-
en: 'Carousel'
|
|
364
|
-
},
|
|
365
360
|
externalLink: {
|
|
366
361
|
nb: '(ekstern lenke)',
|
|
367
362
|
sv: '(extern länk)',
|
|
@@ -495,44 +490,73 @@ const Avatar = ({ src, alt = '', className, onError, loading = 'lazy', ...rest }
|
|
|
495
490
|
});
|
|
496
491
|
};
|
|
497
492
|
|
|
498
|
-
const
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
493
|
+
const formField = cx('group flex flex-col gap-2');
|
|
494
|
+
const formFieldError = cx('w-fit bg-red-light px-2 py-1 text-red text-sm leading-6', 'group-data-[slot=file-upload]:rounded-lg');
|
|
495
|
+
const input = cva({
|
|
496
|
+
base: [
|
|
497
|
+
// All inputs should always have a white background (this also ensures that type="search" on Safri doesn't get a gray background)
|
|
498
|
+
'bg-white',
|
|
499
|
+
// Use box-content to enable auto width based on number of characters (size)
|
|
500
|
+
// Setting min-height to prevent the input from collapsing in Safari
|
|
501
|
+
// Combining these with a padding-y as base classes makes it easier to standardize the height (44px) of all inputs
|
|
502
|
+
'box-content min-h-6 py-2.5',
|
|
503
|
+
'rounded-md font-normal text-base leading-6 placeholder-[#727070] outline-hidden ring-1 ring-black',
|
|
504
|
+
// invalid styles
|
|
505
|
+
'group-data-invalid:ring-focus group-data-invalid:ring-red',
|
|
506
|
+
// Fix invisible ring on safari: https://github.com/tailwindlabs/tailwindcss.com/issues/1135
|
|
507
|
+
'appearance-none'
|
|
508
|
+
],
|
|
509
|
+
variants: {
|
|
510
|
+
// Focus rings. Can either be :focus or :focus-visible based on the needs of the particular component.
|
|
511
|
+
focusModifier: {
|
|
512
|
+
focus: 'focus:ring-focus group-data-invalid:focus:ring-3 group-data-invalid:focus:ring-red',
|
|
513
|
+
visible: 'data-focus-visible:ring-focus group-data-invalid:data-focus-visible:ring-3 group-data-invalid:data-focus-visible:ring-red'
|
|
514
|
+
},
|
|
515
|
+
isGrouped: {
|
|
516
|
+
false: 'px-3',
|
|
517
|
+
true: '!ring-0 flex-1'
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
defaultVariants: {
|
|
521
|
+
focusModifier: 'focus',
|
|
522
|
+
isGrouped: false
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
const inputGroup = cx([
|
|
526
|
+
'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-focus',
|
|
527
|
+
'group-data-invalid:ring-focus group-data-invalid:ring-red group-data-invalid:focus-within:ring-3 group-data-invalid:focus-within:ring-red'
|
|
528
|
+
]);
|
|
529
|
+
const dropdown = {
|
|
530
|
+
popover: cx('data-entering:fade-in data-exiting:fade-out min-w-(--trigger-width) overflow-y-auto rounded-md border border-black bg-white shadow-sm data-entering:animate-in data-exiting:animate-out'),
|
|
531
|
+
// overflow-x-hidden is needed to prevent visible vertical scrollbars from overflowing the border radius of the popover
|
|
532
|
+
listbox: cx('max-h-100 overflow-x-hidden text-sm outline-hidden'),
|
|
533
|
+
chevronIcon: cx('text-base transition-transform duration-150 group-data-open:rotate-180 motion-reduce:transition-none')
|
|
524
534
|
};
|
|
535
|
+
const animateIconVariants = cva({
|
|
536
|
+
base: '*:[svg]:shrink-0 *:[svg]:transition-transform',
|
|
537
|
+
variants: {
|
|
538
|
+
animateIcon: {
|
|
539
|
+
right: 'hover:*:[svg]:motion-safe:translate-x-1',
|
|
540
|
+
left: 'hover:*:[svg]:motion-safe:-translate-x-1',
|
|
541
|
+
down: 'hover:*:[svg]:motion-safe:translate-y-1',
|
|
542
|
+
up: 'hover:*:[svg]:motion-safe:-translate-y-1',
|
|
543
|
+
'up-right': 'hover:*:[svg]:motion-safe:-translate-y-0.5 hover:*:[svg]:motion-safe:translate-x-0.5'
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
});
|
|
525
547
|
|
|
526
548
|
function isLinkProps$1(props) {
|
|
527
549
|
return !!props.href;
|
|
528
550
|
}
|
|
529
551
|
function Backlink(props) {
|
|
530
|
-
const { className,
|
|
531
|
-
const _className = cx(className,
|
|
552
|
+
const { className, children, withUnderline, ref, ...restProps } = props;
|
|
553
|
+
const _className = cx(className, animateIconVariants({
|
|
554
|
+
animateIcon: 'left'
|
|
555
|
+
}), 'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 font-normal no-underline focus-visible:outline-focus');
|
|
532
556
|
const content = /*#__PURE__*/ jsxs(Fragment, {
|
|
533
557
|
children: [
|
|
534
558
|
/*#__PURE__*/ jsx(ChevronLeft, {
|
|
535
|
-
className: cx('-ml-[0.5em]
|
|
559
|
+
className: cx('-ml-[0.5em] duration-300')
|
|
536
560
|
}),
|
|
537
561
|
/*#__PURE__*/ jsx("span", {
|
|
538
562
|
children: /*#__PURE__*/ jsx("span", {
|
|
@@ -543,10 +567,9 @@ function Backlink(props) {
|
|
|
543
567
|
]
|
|
544
568
|
});
|
|
545
569
|
if (isLinkProps$1(props)) {
|
|
546
|
-
return /*#__PURE__*/ jsx(Link, {
|
|
570
|
+
return /*#__PURE__*/ jsx(Link$1, {
|
|
547
571
|
...restProps,
|
|
548
572
|
className: _className,
|
|
549
|
-
style: style,
|
|
550
573
|
ref: ref,
|
|
551
574
|
children: content
|
|
552
575
|
});
|
|
@@ -554,7 +577,6 @@ function Backlink(props) {
|
|
|
554
577
|
return /*#__PURE__*/ jsx(Button$1, {
|
|
555
578
|
...restProps,
|
|
556
579
|
className: _className,
|
|
557
|
-
style: style,
|
|
558
580
|
ref: ref,
|
|
559
581
|
children: content
|
|
560
582
|
});
|
|
@@ -597,6 +619,40 @@ function Badge(props) {
|
|
|
597
619
|
});
|
|
598
620
|
}
|
|
599
621
|
|
|
622
|
+
const _LinkContext = /*#__PURE__*/ createContext({});
|
|
623
|
+
const linkVariants = compose(animateIconVariants, cva({
|
|
624
|
+
base: 'inline-flex cursor-pointer items-center gap-1 font-medium hover:no-underline focus-visible:outline-current focus-visible:outline-focus-offset data-disabled:cursor-default'
|
|
625
|
+
}));
|
|
626
|
+
const Link = ({ ref: _ref, animateIcon, ..._props })=>{
|
|
627
|
+
const [props, ref] = useContextProps(_props, _ref, _LinkContext);
|
|
628
|
+
const { className, _innerWrapper, children: _children, ...restProps } = props;
|
|
629
|
+
const locale = _useLocale();
|
|
630
|
+
const externalLinkSR = props.rel?.includes('external') ? /*#__PURE__*/ jsx("span", {
|
|
631
|
+
className: "sr-only",
|
|
632
|
+
children: translations$1.externalLink[locale]
|
|
633
|
+
}) : null;
|
|
634
|
+
const reactNodeChildren = /*#__PURE__*/ jsxs(Fragment, {
|
|
635
|
+
children: [
|
|
636
|
+
_children,
|
|
637
|
+
externalLinkSR
|
|
638
|
+
]
|
|
639
|
+
});
|
|
640
|
+
const children = _innerWrapper ? _innerWrapper({
|
|
641
|
+
...restProps,
|
|
642
|
+
children: typeof _children === 'function' ? (values)=>_children(values) : reactNodeChildren
|
|
643
|
+
}) : reactNodeChildren;
|
|
644
|
+
return /*#__PURE__*/ jsx(Link$1, {
|
|
645
|
+
...restProps,
|
|
646
|
+
ref: ref,
|
|
647
|
+
"data-slot": "link",
|
|
648
|
+
className: linkVariants({
|
|
649
|
+
className,
|
|
650
|
+
animateIcon
|
|
651
|
+
}),
|
|
652
|
+
children: children
|
|
653
|
+
});
|
|
654
|
+
};
|
|
655
|
+
|
|
600
656
|
function Breadcrumb(props) {
|
|
601
657
|
const { className, children, href, ...restProps } = props;
|
|
602
658
|
return /*#__PURE__*/ jsxs(Breadcrumb$1, {
|
|
@@ -627,38 +683,38 @@ function Breadcrumbs(props) {
|
|
|
627
683
|
|
|
628
684
|
/**
|
|
629
685
|
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
630
|
-
*/ const buttonVariants = cva({
|
|
686
|
+
*/ const buttonVariants = compose(animateIconVariants, cva({
|
|
631
687
|
base: [
|
|
632
688
|
'inline-flex min-h-[44px] cursor-pointer items-center justify-center whitespace-nowrap rounded-lg font-medium transition-colors duration-200 focus-visible:outline-focus-offset'
|
|
633
689
|
],
|
|
634
690
|
variants: {
|
|
635
691
|
/**
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
692
|
+
* The variant of the button
|
|
693
|
+
* @default primary
|
|
694
|
+
*/ variant: {
|
|
639
695
|
primary: 'no-underline',
|
|
640
696
|
// by using an inset box-shadow to emulate a border instead of an actual border, the button size will be equal regardless of the variant
|
|
641
697
|
secondary: 'border-2 border-current no-underline hover:border-transparent',
|
|
642
698
|
tertiary: 'underline hover:no-underline'
|
|
643
699
|
},
|
|
644
700
|
/**
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
701
|
+
* Adjusts the color of the button for usage on different backgrounds.
|
|
702
|
+
* @default blue
|
|
703
|
+
*/ color: {
|
|
648
704
|
blue: 'focus-visible:outline-focus',
|
|
649
705
|
mint: 'focus-visible:outline-focus focus-visible:outline-mint',
|
|
650
706
|
white: 'focus-visible:outline-focus focus-visible:outline-white'
|
|
651
707
|
},
|
|
652
708
|
/**
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
709
|
+
* When the button is without text, but with a single icon.
|
|
710
|
+
* @default false
|
|
711
|
+
*/ isIconOnly: {
|
|
656
712
|
true: 'p-2 [&>svg]:h-7 [&>svg]:w-7',
|
|
657
713
|
false: 'gap-2.5 px-4 py-2'
|
|
658
714
|
},
|
|
659
715
|
// Make the content of the button transparent to hide it's content, but keep the button width
|
|
660
716
|
isPending: {
|
|
661
|
-
true: '
|
|
717
|
+
true: 'relative text-transparent!',
|
|
662
718
|
false: null
|
|
663
719
|
}
|
|
664
720
|
},
|
|
@@ -717,15 +773,17 @@ function Breadcrumbs(props) {
|
|
|
717
773
|
isIconOnly: false,
|
|
718
774
|
isPending: false
|
|
719
775
|
}
|
|
720
|
-
});
|
|
776
|
+
}));
|
|
721
777
|
const ButtonContext = /*#__PURE__*/ createContext({});
|
|
722
778
|
function isLinkProps(props) {
|
|
723
779
|
return !!props.href;
|
|
724
780
|
}
|
|
725
781
|
function Button({ ref = null, ...props }) {
|
|
726
782
|
[props, ref] = useContextProps(props, ref, ButtonContext);
|
|
727
|
-
const { children: _children, color, isIconOnly, variant, isPending, ...restProps } = props;
|
|
783
|
+
const { animateIcon, children: _children, color, isIconOnly, variant, isPending, ...restProps } = props;
|
|
728
784
|
const className = buttonVariants({
|
|
785
|
+
// Don't animate the icon when we're pending, as it affects the loading spinner
|
|
786
|
+
animateIcon: isPending ? undefined : animateIcon,
|
|
729
787
|
className: props.className,
|
|
730
788
|
color,
|
|
731
789
|
isIconOnly,
|
|
@@ -985,6 +1043,137 @@ const cardLinkVariants = cva({
|
|
|
985
1043
|
});
|
|
986
1044
|
};
|
|
987
1045
|
|
|
1046
|
+
const HeroContext = /*#__PURE__*/ createContext(null);
|
|
1047
|
+
// Common variant for "standard" and "full-bleed" Hero variants
|
|
1048
|
+
const oneColumnLayout = [
|
|
1049
|
+
// Vertical spacing in the <Content>
|
|
1050
|
+
'lg:*:data-[slot="content"]:gap-y-4',
|
|
1051
|
+
// Main text content takes up 9 columns on medium screens and above
|
|
1052
|
+
'lg:*:data-[slot="content"]:col-span-9',
|
|
1053
|
+
// Make sure other elements than <Content> and <Media> (i.e CTA) does not span the full width on small screens
|
|
1054
|
+
'*:not-data-[slot="content"]:not-data-[slot="media"]:w-fit',
|
|
1055
|
+
// Other elements than <Content> and <Media> (e.g. CTA, SVG logo or Badge) take up 3 columns on medium screens and above, and are right aligned
|
|
1056
|
+
'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',
|
|
1057
|
+
// <Media> and <Carousel> content takes up the full width on medium screens and above
|
|
1058
|
+
'lg:*:data-[slot="media"]:col-span-full *:data-[slot="media"]:*:w-full',
|
|
1059
|
+
'lg:*:data-[slot="carousel"]:col-span-full',
|
|
1060
|
+
// Aligns <Content> and any element beside it (e.g. <Media>, <Badge>, <CTA> etc.) to the bottom of the <Content> container
|
|
1061
|
+
'lg:items-end'
|
|
1062
|
+
];
|
|
1063
|
+
const nonFullBleedAspectRatiosForSmallScreens = '*:data-[slot="media"]:*:aspect-[1/1] sm:*:data-[slot="media"]:*:aspect-4/3 md:*:data-[slot="media"]:*:aspect-3/2';
|
|
1064
|
+
const variants = cva({
|
|
1065
|
+
base: [
|
|
1066
|
+
'container px-0',
|
|
1067
|
+
// Grid variant to position the Hero's content
|
|
1068
|
+
'grid lg:grid-cols-12 lg:gap-x-12 xl:gap-x-16',
|
|
1069
|
+
'gap-y-10 lg:gap-y-12',
|
|
1070
|
+
// Enable vertical gap within <Content>
|
|
1071
|
+
'*:data-[slot="content"]:grid',
|
|
1072
|
+
// Vertical spacing in the <Content>
|
|
1073
|
+
'*:data-[slot="content"]:gap-y-3',
|
|
1074
|
+
// Make sure <Media> content fills any available vertical and horizontal space
|
|
1075
|
+
'*:data-[slot="media"]:*:object-cover',
|
|
1076
|
+
'*:data-[slot="carousel"]:overflow-hidden *:data-[slot="carousel"]:rounded-3xl',
|
|
1077
|
+
// Make the carousel items full width, so we scroll one at a time
|
|
1078
|
+
'**:data-[slot="carousel-item"]:basis-full'
|
|
1079
|
+
],
|
|
1080
|
+
variants: {
|
|
1081
|
+
/**
|
|
1082
|
+
* Defines the variant of the Hero
|
|
1083
|
+
* @default standard
|
|
1084
|
+
* */ variant: {
|
|
1085
|
+
standard: [
|
|
1086
|
+
oneColumnLayout,
|
|
1087
|
+
nonFullBleedAspectRatiosForSmallScreens,
|
|
1088
|
+
'lg:*:data-[slot="media"]:*:aspect-2/1'
|
|
1089
|
+
],
|
|
1090
|
+
'full-bleed': [
|
|
1091
|
+
oneColumnLayout,
|
|
1092
|
+
// Position the media and carousel content to fill the entire viewport width
|
|
1093
|
+
'*:data-[slot="media"]:*:absolute *:data-[slot="media"]:*:left-0',
|
|
1094
|
+
// Special case for Carousel, where the Media is nested inside a CarouselItem
|
|
1095
|
+
'*:data-[slot="carousel"]:**:data-[slot="media"]:w-full',
|
|
1096
|
+
// Match the heights of the <Media> or <Carousel> wrapper for the Media content (e.g. image, VideoLoop, video etc.)
|
|
1097
|
+
// This is necessary due to the absolute positioning of the media and carousel containers in this variant
|
|
1098
|
+
// biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
|
|
1099
|
+
'**: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]',
|
|
1100
|
+
'**:data-[slot="media"]:*:h-[inherit]',
|
|
1101
|
+
// biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
|
|
1102
|
+
'*: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]',
|
|
1103
|
+
'*:data-[slot="carousel"]:w-full!',
|
|
1104
|
+
// Override aspect ratio of the media and carousel-item slots (since we can not use aspect for full-bleed layout)
|
|
1105
|
+
'**:data-[slot="carousel-item"]:data-[slot="media"]:*:aspect-none',
|
|
1106
|
+
// break out the carousel out of the container
|
|
1107
|
+
'**: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]',
|
|
1108
|
+
// Positions the carousel controls inside the carousel
|
|
1109
|
+
'**: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'
|
|
1110
|
+
],
|
|
1111
|
+
'two-column': [
|
|
1112
|
+
'lg:items-center lg:*:col-span-6',
|
|
1113
|
+
// Vertical spacing in the <Content>
|
|
1114
|
+
'lg:*:data-[slot="content"]:gap-y-7',
|
|
1115
|
+
nonFullBleedAspectRatiosForSmallScreens,
|
|
1116
|
+
// Set media aspect ratio to 1:1 (square)
|
|
1117
|
+
'lg:*:data-[slot="media"]:*:aspect-square'
|
|
1118
|
+
]
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
compoundVariants: [
|
|
1122
|
+
{
|
|
1123
|
+
variant: [
|
|
1124
|
+
'standard',
|
|
1125
|
+
'two-column'
|
|
1126
|
+
],
|
|
1127
|
+
className: [
|
|
1128
|
+
'*:data-[slot="media"]:*:rounded-3xl',
|
|
1129
|
+
'**: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'
|
|
1130
|
+
]
|
|
1131
|
+
}
|
|
1132
|
+
],
|
|
1133
|
+
defaultVariants: {
|
|
1134
|
+
variant: 'standard'
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
const Hero = ({ variant, className, children, ...rest })=>{
|
|
1138
|
+
const variantsClassName = variants({
|
|
1139
|
+
variant,
|
|
1140
|
+
className
|
|
1141
|
+
});
|
|
1142
|
+
return /*#__PURE__*/ jsx(Provider, {
|
|
1143
|
+
values: [
|
|
1144
|
+
[
|
|
1145
|
+
HeroContext,
|
|
1146
|
+
{
|
|
1147
|
+
variant
|
|
1148
|
+
}
|
|
1149
|
+
],
|
|
1150
|
+
[
|
|
1151
|
+
HeadingContext,
|
|
1152
|
+
{
|
|
1153
|
+
// Sets the default heading size for the Hero based on the variant
|
|
1154
|
+
size: variant === 'two-column' ? 'xl' : 'l',
|
|
1155
|
+
className: // word-break:break-word to allow long words to break (this is necessary to make hyphens work in grid containers in Safari)
|
|
1156
|
+
'hyphens-auto text-pretty [word-break:break-word]'
|
|
1157
|
+
}
|
|
1158
|
+
],
|
|
1159
|
+
[
|
|
1160
|
+
GroupContext,
|
|
1161
|
+
{
|
|
1162
|
+
// Prevents the group from being announced as a group by screen readers
|
|
1163
|
+
// The Group component is used to group the Hero's CTA buttons together visually, and has no semantic meaning
|
|
1164
|
+
role: 'presentation',
|
|
1165
|
+
className: 'flex flex-wrap gap-3 *:w-fit'
|
|
1166
|
+
}
|
|
1167
|
+
]
|
|
1168
|
+
],
|
|
1169
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
1170
|
+
className: cx(variantsClassName, className),
|
|
1171
|
+
...rest,
|
|
1172
|
+
children: children
|
|
1173
|
+
})
|
|
1174
|
+
});
|
|
1175
|
+
};
|
|
1176
|
+
|
|
988
1177
|
/**
|
|
989
1178
|
* Hook that detects the user's preference for reduced motion.
|
|
990
1179
|
*
|
|
@@ -1008,6 +1197,32 @@ const cardLinkVariants = cva({
|
|
|
1008
1197
|
return prefersReducedMotion;
|
|
1009
1198
|
}
|
|
1010
1199
|
|
|
1200
|
+
/**
|
|
1201
|
+
* A helper to get the prev/next button from the carousel DOM
|
|
1202
|
+
* @param ref The carousel ref
|
|
1203
|
+
* @param slot The slot of the button to get ('prev' or 'next')
|
|
1204
|
+
* @returns The button element, or undefined if not found
|
|
1205
|
+
*/ const getCarouselButton = (ref, slot)=>ref.current?.querySelector(`button[slot="${slot}"]`);
|
|
1206
|
+
/**
|
|
1207
|
+
* Focus the first focusable element in the currently snapped slide
|
|
1208
|
+
* @param emblaApi The embla carousel API instance
|
|
1209
|
+
*/ const focusElementInSnappedSlide = (emblaApi)=>{
|
|
1210
|
+
if (!emblaApi) {
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
const index = emblaApi.selectedScrollSnap();
|
|
1214
|
+
const targetSlide = emblaApi.slideNodes()[index];
|
|
1215
|
+
if (!targetSlide) {
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
// Find first focusable element in the slide
|
|
1219
|
+
const focusableElement = targetSlide.querySelector('a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])');
|
|
1220
|
+
// Use preventScroll to avoid the browser's default scroll-into-view behavior
|
|
1221
|
+
// which would conflict with embla's scroll animation
|
|
1222
|
+
focusableElement?.focus({
|
|
1223
|
+
preventScroll: true
|
|
1224
|
+
});
|
|
1225
|
+
};
|
|
1011
1226
|
const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0, orientation = 'horizontal', onSelect, onSettled, loop = false, scrollGestures = false, ref, ...rest })=>{
|
|
1012
1227
|
const carouselRef = useRef(null);
|
|
1013
1228
|
const prefersReducedMotion = usePrefersReducedMotion() ?? false;
|
|
@@ -1040,6 +1255,10 @@ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0,
|
|
|
1040
1255
|
const [slidesInView, setSlidesInView] = useState([
|
|
1041
1256
|
initialIndex
|
|
1042
1257
|
]);
|
|
1258
|
+
// We need some default values here. The proper initial values will be set by the embla init handler later
|
|
1259
|
+
// for the default values, assume that we can scroll next, but for prev only if looping is enabled
|
|
1260
|
+
const [canScrollNext, setCanScrollNext] = useState(true);
|
|
1261
|
+
const [canScrollPrev, setCanScrollPrev] = useState(loop);
|
|
1043
1262
|
const previousSettledScrollIndex = useRef(initialIndex);
|
|
1044
1263
|
useEffect(()=>{
|
|
1045
1264
|
if (!emblaApi) {
|
|
@@ -1050,6 +1269,8 @@ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0,
|
|
|
1050
1269
|
switch(type){
|
|
1051
1270
|
case 'select':
|
|
1052
1271
|
onSelect?.(scrollSnapIndex);
|
|
1272
|
+
setCanScrollNext(emblaApi.canScrollNext());
|
|
1273
|
+
setCanScrollPrev(emblaApi.canScrollPrev());
|
|
1053
1274
|
break;
|
|
1054
1275
|
case 'settle':
|
|
1055
1276
|
{
|
|
@@ -1066,15 +1287,23 @@ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0,
|
|
|
1066
1287
|
setSlidesInView(emblaApi.slidesInView());
|
|
1067
1288
|
break;
|
|
1068
1289
|
}
|
|
1290
|
+
case 'init':
|
|
1291
|
+
{
|
|
1292
|
+
setCanScrollNext(emblaApi.canScrollNext());
|
|
1293
|
+
setCanScrollPrev(emblaApi.canScrollPrev());
|
|
1294
|
+
break;
|
|
1295
|
+
}
|
|
1069
1296
|
}
|
|
1070
1297
|
};
|
|
1071
1298
|
emblaApi.on('select', emblaHandler);
|
|
1072
1299
|
emblaApi.on('slidesInView', emblaHandler);
|
|
1073
1300
|
emblaApi.on('settle', emblaHandler);
|
|
1301
|
+
emblaApi.on('init', emblaHandler);
|
|
1074
1302
|
return ()=>{
|
|
1075
1303
|
emblaApi.off('select', emblaHandler);
|
|
1076
1304
|
emblaApi.off('settle', emblaHandler);
|
|
1077
1305
|
emblaApi.off('slidesInView', emblaHandler);
|
|
1306
|
+
emblaApi.off('init', emblaHandler);
|
|
1078
1307
|
};
|
|
1079
1308
|
}, [
|
|
1080
1309
|
emblaApi,
|
|
@@ -1088,8 +1317,8 @@ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0,
|
|
|
1088
1317
|
emblaApi.plugins().autoplay?.stop();
|
|
1089
1318
|
emblaApi.scrollNext(prefersReducedMotion);
|
|
1090
1319
|
// 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
|
|
1092
|
-
carouselRef
|
|
1320
|
+
if (!loop && !emblaApi.canScrollNext() && getCarouselButton(carouselRef, 'next')?.matches(':focus-visible')) {
|
|
1321
|
+
getCarouselButton(carouselRef, 'prev')?.focus();
|
|
1093
1322
|
}
|
|
1094
1323
|
}, [
|
|
1095
1324
|
emblaApi,
|
|
@@ -1103,8 +1332,8 @@ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0,
|
|
|
1103
1332
|
emblaApi.plugins().autoplay?.stop();
|
|
1104
1333
|
emblaApi.scrollPrev(prefersReducedMotion);
|
|
1105
1334
|
// 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
|
|
1107
|
-
carouselRef
|
|
1335
|
+
if (!loop && !emblaApi.canScrollPrev() && getCarouselButton(carouselRef, 'prev')?.matches(':focus-visible')) {
|
|
1336
|
+
getCarouselButton(carouselRef, 'next')?.focus();
|
|
1108
1337
|
}
|
|
1109
1338
|
}, [
|
|
1110
1339
|
emblaApi,
|
|
@@ -1113,24 +1342,40 @@ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0,
|
|
|
1113
1342
|
]);
|
|
1114
1343
|
const locale = _useLocale();
|
|
1115
1344
|
const handleKeyDown = useCallback((e)=>{
|
|
1345
|
+
// Check if either prev or next button has focus - if so, don't override their focus management
|
|
1346
|
+
const carouselButtonHasFocus = getCarouselButton(carouselRef, 'prev')?.matches(':focus-visible') || getCarouselButton(carouselRef, 'next')?.matches(':focus-visible');
|
|
1116
1347
|
if (e.key === 'ArrowRight' && !e.repeat) {
|
|
1117
1348
|
handleNextPress();
|
|
1349
|
+
// Focus first focusable element in the next slide, unless a carousel button has focus
|
|
1350
|
+
if (!carouselButtonHasFocus) {
|
|
1351
|
+
focusElementInSnappedSlide(emblaApi);
|
|
1352
|
+
}
|
|
1118
1353
|
} else if (e.key === 'ArrowLeft' && !e.repeat) {
|
|
1119
1354
|
handlePrevPress();
|
|
1355
|
+
// Focus first focusable element in the previous slide, unless a carousel button has focus
|
|
1356
|
+
if (!carouselButtonHasFocus) {
|
|
1357
|
+
focusElementInSnappedSlide(emblaApi);
|
|
1358
|
+
}
|
|
1120
1359
|
}
|
|
1121
1360
|
}, [
|
|
1122
1361
|
handleNextPress,
|
|
1123
|
-
handlePrevPress
|
|
1362
|
+
handlePrevPress,
|
|
1363
|
+
emblaApi
|
|
1124
1364
|
]);
|
|
1125
|
-
|
|
1365
|
+
const hasHeroContext = !!useContext(HeroContext);
|
|
1366
|
+
const nextPrevStyles = hasHeroContext ? {
|
|
1367
|
+
color: 'white',
|
|
1368
|
+
variant: 'primary'
|
|
1369
|
+
} : {
|
|
1370
|
+
variant: 'tertiary'
|
|
1371
|
+
};
|
|
1372
|
+
return(// biome-ignore lint/a11y/noStaticElementInteractions: This is just to enhance keyboard navigation, this is not a replacement for proper focusable elements inside the carousel
|
|
1126
1373
|
/*#__PURE__*/ jsx("div", {
|
|
1127
1374
|
...rest,
|
|
1128
1375
|
"data-orientation": orientation,
|
|
1129
1376
|
"data-slot": "carousel",
|
|
1130
1377
|
ref: mergeRefs(ref, carouselRef),
|
|
1131
1378
|
onKeyDown: handleKeyDown,
|
|
1132
|
-
role: "region",
|
|
1133
|
-
"aria-label": translations$1.carousel[locale],
|
|
1134
1379
|
children: /*#__PURE__*/ jsx(Provider, {
|
|
1135
1380
|
values: [
|
|
1136
1381
|
[
|
|
@@ -1148,13 +1393,15 @@ const Carousel = ({ autoPlayDelay, align = 'center', children, initialIndex = 0,
|
|
|
1148
1393
|
[DEFAULT_SLOT]: {},
|
|
1149
1394
|
prev: {
|
|
1150
1395
|
'aria-label': translations$1.previous[locale],
|
|
1151
|
-
isDisabled: !
|
|
1152
|
-
onPress: handlePrevPress
|
|
1396
|
+
isDisabled: !canScrollPrev,
|
|
1397
|
+
onPress: handlePrevPress,
|
|
1398
|
+
...nextPrevStyles
|
|
1153
1399
|
},
|
|
1154
1400
|
next: {
|
|
1155
1401
|
'aria-label': translations$1.next[locale],
|
|
1156
|
-
isDisabled: !
|
|
1157
|
-
onPress: handleNextPress
|
|
1402
|
+
isDisabled: !canScrollNext,
|
|
1403
|
+
onPress: handleNextPress,
|
|
1404
|
+
...nextPrevStyles
|
|
1158
1405
|
}
|
|
1159
1406
|
}
|
|
1160
1407
|
}
|
|
@@ -1180,17 +1427,11 @@ const CarouselItemsContainer = ({ children, className, ...rest })=>{
|
|
|
1180
1427
|
});
|
|
1181
1428
|
};
|
|
1182
1429
|
const CarouselItems = ({ className, children })=>{
|
|
1183
|
-
const {
|
|
1430
|
+
const { orientation } = useContext(CarouselContext);
|
|
1184
1431
|
return /*#__PURE__*/ jsx("div", {
|
|
1185
1432
|
className: cx(className, 'flex', orientation === 'vertical' && 'flex-col'),
|
|
1186
1433
|
"data-slot": "carousel-items",
|
|
1187
|
-
children:
|
|
1188
|
-
if (/*#__PURE__*/ isValidElement(child)) {
|
|
1189
|
-
return /*#__PURE__*/ cloneElement(child, {
|
|
1190
|
-
inert: slidesInView.includes(index) ? undefined : true
|
|
1191
|
-
});
|
|
1192
|
-
}
|
|
1193
|
-
})
|
|
1434
|
+
children: children
|
|
1194
1435
|
});
|
|
1195
1436
|
};
|
|
1196
1437
|
/**
|
|
@@ -1200,6 +1441,8 @@ const CarouselItems = ({ className, children })=>{
|
|
|
1200
1441
|
className: cx(className, 'flex justify-end gap-x-2'),
|
|
1201
1442
|
"data-slot": "carousel-controls",
|
|
1202
1443
|
...rest,
|
|
1444
|
+
// All items of the carousel are accessible to the screen reader at all times, so these controls will only confuse screen reader users
|
|
1445
|
+
"aria-hidden": "true",
|
|
1203
1446
|
children: children
|
|
1204
1447
|
});
|
|
1205
1448
|
const carouselButtonVariants = cva({
|
|
@@ -1242,7 +1485,7 @@ const carouselButtonIconSlotVariants = cva({
|
|
|
1242
1485
|
}
|
|
1243
1486
|
]
|
|
1244
1487
|
});
|
|
1245
|
-
const CarouselButton = ({ className, isIconOnly = true,
|
|
1488
|
+
const CarouselButton = ({ className, isIconOnly = true, slot, ...rest })=>{
|
|
1246
1489
|
const { orientation } = useContext(CarouselContext);
|
|
1247
1490
|
return /*#__PURE__*/ jsx(Button, {
|
|
1248
1491
|
className: carouselButtonVariants({
|
|
@@ -1250,8 +1493,6 @@ const CarouselButton = ({ className, isIconOnly = true, color = 'white', variant
|
|
|
1250
1493
|
}),
|
|
1251
1494
|
isIconOnly: isIconOnly,
|
|
1252
1495
|
slot: slot,
|
|
1253
|
-
variant: variant,
|
|
1254
|
-
color: color,
|
|
1255
1496
|
...rest,
|
|
1256
1497
|
children: /*#__PURE__*/ jsx(ChevronRight, {
|
|
1257
1498
|
className: carouselButtonIconSlotVariants({
|
|
@@ -1281,49 +1522,6 @@ const CarouselItem = ({ className, children, ...rest })=>{
|
|
|
1281
1522
|
});
|
|
1282
1523
|
};
|
|
1283
1524
|
|
|
1284
|
-
const formField = cx('group flex flex-col gap-2');
|
|
1285
|
-
const formFieldError = cx('w-fit bg-red-light px-2 py-1 text-red text-sm leading-6', 'group-data-[slot=file-upload]:rounded-lg');
|
|
1286
|
-
const input = cva({
|
|
1287
|
-
base: [
|
|
1288
|
-
// All inputs should always have a white background (this also ensures that type="search" on Safri doesn't get a gray background)
|
|
1289
|
-
'bg-white',
|
|
1290
|
-
// Use box-content to enable auto width based on number of characters (size)
|
|
1291
|
-
// Setting min-height to prevent the input from collapsing in Safari
|
|
1292
|
-
// Combining these with a padding-y as base classes makes it easier to standardize the height (44px) of all inputs
|
|
1293
|
-
'box-content min-h-6 py-2.5',
|
|
1294
|
-
'rounded-md font-normal text-base leading-6 placeholder-[#727070] outline-hidden ring-1 ring-black',
|
|
1295
|
-
// invalid styles
|
|
1296
|
-
'group-data-invalid:ring-focus group-data-invalid:ring-red',
|
|
1297
|
-
// Fix invisible ring on safari: https://github.com/tailwindlabs/tailwindcss.com/issues/1135
|
|
1298
|
-
'appearance-none'
|
|
1299
|
-
],
|
|
1300
|
-
variants: {
|
|
1301
|
-
// Focus rings. Can either be :focus or :focus-visible based on the needs of the particular component.
|
|
1302
|
-
focusModifier: {
|
|
1303
|
-
focus: 'focus:ring-focus group-data-invalid:focus:ring-3 group-data-invalid:focus:ring-red',
|
|
1304
|
-
visible: 'data-focus-visible:ring-focus group-data-invalid:data-focus-visible:ring-3 group-data-invalid:data-focus-visible:ring-red'
|
|
1305
|
-
},
|
|
1306
|
-
isGrouped: {
|
|
1307
|
-
false: 'px-3',
|
|
1308
|
-
true: '!ring-0 flex-1'
|
|
1309
|
-
}
|
|
1310
|
-
},
|
|
1311
|
-
defaultVariants: {
|
|
1312
|
-
focusModifier: 'focus',
|
|
1313
|
-
isGrouped: false
|
|
1314
|
-
}
|
|
1315
|
-
});
|
|
1316
|
-
const inputGroup = cx([
|
|
1317
|
-
'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-focus',
|
|
1318
|
-
'group-data-invalid:ring-focus group-data-invalid:ring-red group-data-invalid:focus-within:ring-3 group-data-invalid:focus-within:ring-red'
|
|
1319
|
-
]);
|
|
1320
|
-
const dropdown = {
|
|
1321
|
-
popover: cx('data-entering:fade-in data-exiting:fade-out min-w-(--trigger-width) overflow-y-auto rounded-md border border-black bg-white shadow-sm data-entering:animate-in data-exiting:animate-out'),
|
|
1322
|
-
// overflow-x-hidden is needed to prevent visible vertical scrollbars from overflowing the border radius of the popover
|
|
1323
|
-
listbox: cx('max-h-100 overflow-x-hidden text-sm outline-hidden'),
|
|
1324
|
-
chevronIcon: cx('text-base transition-transform duration-150 group-data-open:rotate-180 motion-reduce:transition-none')
|
|
1325
|
-
};
|
|
1326
|
-
|
|
1327
1525
|
function ErrorMessage(props) {
|
|
1328
1526
|
const { children, className, ...restProps } = props;
|
|
1329
1527
|
return /*#__PURE__*/ jsx(Text, {
|
|
@@ -1913,130 +2111,6 @@ function GrunnmurenProvider({ children, locale = 'nb', navigate, useHref }) {
|
|
|
1913
2111
|
});
|
|
1914
2112
|
}
|
|
1915
2113
|
|
|
1916
|
-
// Common variant for "standard" and "full-bleed" Hero variants
|
|
1917
|
-
const oneColumnLayout = [
|
|
1918
|
-
// Vertical spacing in the <Content>
|
|
1919
|
-
'lg:*:data-[slot="content"]:gap-y-4',
|
|
1920
|
-
// Main text content takes up 9 columns on medium screens and above
|
|
1921
|
-
'lg:*:data-[slot="content"]:col-span-9',
|
|
1922
|
-
// Make sure other elements than <Content> and <Media> (i.e CTA) does not span the full width on small screens
|
|
1923
|
-
'*:not-data-[slot="content"]:not-data-[slot="media"]:w-fit',
|
|
1924
|
-
// Other elements than <Content> and <Media> (e.g. CTA, SVG logo or Badge) take up 3 columns on medium screens and above, and are right aligned
|
|
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',
|
|
1926
|
-
// <Media> and <Carousel> content takes up the full width on medium screens and above
|
|
1927
|
-
'lg:*:data-[slot="media"]:col-span-full *:data-[slot="media"]:*:w-full',
|
|
1928
|
-
'lg:*:data-[slot="carousel"]:col-span-full',
|
|
1929
|
-
// Aligns <Content> and any element beside it (e.g. <Media>, <Badge>, <CTA> etc.) to the bottom of the <Content> container
|
|
1930
|
-
'lg:items-end'
|
|
1931
|
-
];
|
|
1932
|
-
const nonFullBleedAspectRatiosForSmallScreens = '*:data-[slot="media"]:*:aspect-[1/1] sm:*:data-[slot="media"]:*:aspect-4/3 md:*:data-[slot="media"]:*:aspect-3/2';
|
|
1933
|
-
const variants = cva({
|
|
1934
|
-
base: [
|
|
1935
|
-
'container px-0',
|
|
1936
|
-
// Grid variant to position the Hero's content
|
|
1937
|
-
'grid lg:grid-cols-12 lg:gap-x-12 xl:gap-x-16',
|
|
1938
|
-
'gap-y-10 lg:gap-y-12',
|
|
1939
|
-
// Enable vertical gap within <Content>
|
|
1940
|
-
'*:data-[slot="content"]:grid',
|
|
1941
|
-
// Vertical spacing in the <Content>
|
|
1942
|
-
'*:data-[slot="content"]:gap-y-3',
|
|
1943
|
-
// Make sure <Media> content fills any available vertical and horizontal space
|
|
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'
|
|
1948
|
-
],
|
|
1949
|
-
variants: {
|
|
1950
|
-
/**
|
|
1951
|
-
* Defines the variant of the Hero
|
|
1952
|
-
* @default standard
|
|
1953
|
-
* */ variant: {
|
|
1954
|
-
standard: [
|
|
1955
|
-
oneColumnLayout,
|
|
1956
|
-
nonFullBleedAspectRatiosForSmallScreens,
|
|
1957
|
-
'lg:*:data-[slot="media"]:*:aspect-2/1'
|
|
1958
|
-
],
|
|
1959
|
-
'full-bleed': [
|
|
1960
|
-
oneColumnLayout,
|
|
1961
|
-
// Position the media and carousel content to fill the entire viewport width
|
|
1962
|
-
'*:data-[slot="media"]:*:absolute *:data-[slot="media"]:*:left-0',
|
|
1963
|
-
// Special case for Carousel, where the Media is nested inside a CarouselItem
|
|
1964
|
-
'*:data-[slot="carousel"]:**:data-[slot="media"]:w-full',
|
|
1965
|
-
// Match the heights of the <Media> or <Carousel> wrapper for the Media content (e.g. image, VideoLoop, video etc.)
|
|
1966
|
-
// This is necessary due to the absolute positioning of the media and carousel containers in this variant
|
|
1967
|
-
// biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
|
|
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]',
|
|
1969
|
-
'**:data-[slot="media"]:*:h-[inherit]',
|
|
1970
|
-
// biome-ignore lint/nursery/useSortedClasses: biome is unable to sort the custom classes for 3xl and 4xl breakpoints
|
|
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!',
|
|
1973
|
-
// Override aspect ratio of the media and carousel-item slots (since we can not use aspect for full-bleed layout)
|
|
1974
|
-
'**:data-[slot="carousel-item"]:data-[slot="media"]:*:aspect-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'
|
|
1979
|
-
],
|
|
1980
|
-
'two-column': [
|
|
1981
|
-
'lg:items-center lg:*:col-span-6',
|
|
1982
|
-
// Vertical spacing in the <Content>
|
|
1983
|
-
'lg:*:data-[slot="content"]:gap-y-7',
|
|
1984
|
-
nonFullBleedAspectRatiosForSmallScreens,
|
|
1985
|
-
// Set media aspect ratio to 1:1 (square)
|
|
1986
|
-
'lg:*:data-[slot="media"]:*:aspect-square'
|
|
1987
|
-
]
|
|
1988
|
-
}
|
|
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
|
-
],
|
|
2002
|
-
defaultVariants: {
|
|
2003
|
-
variant: 'standard'
|
|
2004
|
-
}
|
|
2005
|
-
});
|
|
2006
|
-
const Hero = ({ variant, className, children, ...rest })=>{
|
|
2007
|
-
const variantsClassName = variants({
|
|
2008
|
-
variant,
|
|
2009
|
-
className
|
|
2010
|
-
});
|
|
2011
|
-
return /*#__PURE__*/ jsx(Provider, {
|
|
2012
|
-
values: [
|
|
2013
|
-
[
|
|
2014
|
-
HeadingContext,
|
|
2015
|
-
{
|
|
2016
|
-
// Sets the default heading size for the Hero based on the variant
|
|
2017
|
-
size: variant === 'two-column' ? 'xl' : 'l',
|
|
2018
|
-
className: // word-break:break-word to allow long words to break (this is necessary to make hyphens work in grid containers in Safari)
|
|
2019
|
-
'hyphens-auto text-pretty [word-break:break-word]'
|
|
2020
|
-
}
|
|
2021
|
-
],
|
|
2022
|
-
[
|
|
2023
|
-
GroupContext,
|
|
2024
|
-
{
|
|
2025
|
-
// Prevents the group from being announced as a group by screen readers
|
|
2026
|
-
// The Group component is used to group the Hero's CTA buttons together visually, and has no semantic meaning
|
|
2027
|
-
role: 'presentation',
|
|
2028
|
-
className: 'flex flex-wrap gap-3 *:w-fit'
|
|
2029
|
-
}
|
|
2030
|
-
]
|
|
2031
|
-
],
|
|
2032
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
2033
|
-
className: cx(variantsClassName, className),
|
|
2034
|
-
...rest,
|
|
2035
|
-
children: children
|
|
2036
|
-
})
|
|
2037
|
-
});
|
|
2038
|
-
};
|
|
2039
|
-
|
|
2040
2114
|
// Sets the correct icons for each link in the link list
|
|
2041
2115
|
const _LinkProvider = ({ children })=>/*#__PURE__*/ jsx(Provider, {
|
|
2042
2116
|
values: [
|
|
@@ -3001,4 +3075,4 @@ const VideoLoop = ({ src, format, alt, className })=>{
|
|
|
3001
3075
|
});
|
|
3002
3076
|
};
|
|
3003
3077
|
|
|
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 };
|
|
3078
|
+
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, CarouselContext as UNSAFE_CarouselContext, 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, HeroContext as UNSAFE_HeroContext, 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 };
|