@obosbbl/grunnmuren-react 2.0.0-canary.36 → 2.0.0-canary.38

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/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
- import { RouterProvider, ButtonProps as ButtonProps$1, Link, CheckboxProps as CheckboxProps$1, CheckboxGroupProps as CheckboxGroupProps$1, ListBoxItemProps, ComboBoxProps, RadioGroupProps as RadioGroupProps$1, RadioProps as RadioProps$1, SelectProps as SelectProps$1, TextFieldProps as TextFieldProps$1, NumberFieldProps as NumberFieldProps$1, ContextValue, BreadcrumbProps as BreadcrumbProps$1, BreadcrumbsProps as BreadcrumbsProps$1 } from 'react-aria-components';
1
+ import { RouterProvider, ButtonProps as ButtonProps$1, Link, CheckboxProps as CheckboxProps$1, CheckboxGroupProps as CheckboxGroupProps$1, ListBoxItemProps, ComboBoxProps, RadioGroupProps as RadioGroupProps$1, RadioProps as RadioProps$1, SelectProps as SelectProps$1, TextFieldProps as TextFieldProps$1, NumberFieldProps as NumberFieldProps$1, ContextValue, BreadcrumbProps as BreadcrumbProps$1, BreadcrumbsProps as BreadcrumbsProps$1, LinkProps } from 'react-aria-components';
2
2
  export { ListBoxItemProps as ComboboxItemProps, Form, 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 react__default, { HTMLProps, ForwardedRef } from 'react';
5
+ import react__default, { HTMLProps, ForwardedRef, ReactNode } from 'react';
6
6
  import { VariantProps } from 'cva';
7
+ import { DateFormatterOptions } from 'react-aria';
7
8
 
8
9
  type Locale = 'nb' | 'sv' | 'en';
9
10
  /**
@@ -670,15 +671,19 @@ type HeadingProps = HTMLProps<HTMLHeadingElement> & {
670
671
  /** @private Used internally for slotted components */
671
672
  _innerWrapper?: (children: React.ReactNode) => React.ReactNode;
672
673
  };
673
- declare const HeadingContext: react.Context<ContextValue<HeadingProps, HTMLHeadingElement>>;
674
+ declare const HeadingContext: react.Context<ContextValue<Partial<HeadingProps>, HTMLHeadingElement>>;
674
675
  declare const Heading: (props: HeadingProps, ref: ForwardedRef<HTMLHeadingElement>) => react_jsx_runtime.JSX.Element;
675
- declare const ContentContext: react.Context<ContextValue<ContentProps, HTMLDivElement>>;
676
+ declare const ContentContext: react.Context<ContextValue<Partial<ContentProps>, HTMLDivElement>>;
676
677
  type ContentProps = HTMLProps<HTMLDivElement> & {
677
678
  children: React.ReactNode;
678
679
  /** @private Used internally for slotted components */
679
680
  _outerWrapper?: (children: React.ReactNode) => React.ReactNode;
680
681
  };
681
682
  declare const Content: (props: ContentProps, ref: ForwardedRef<HTMLDivElement>) => string | number | boolean | Iterable<react.ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
683
+ type MediaProps = HTMLProps<HTMLDivElement> & {
684
+ children: React.ReactNode;
685
+ };
686
+ declare const Media: (props: MediaProps) => react_jsx_runtime.JSX.Element;
682
687
  type FooterProps = HTMLProps<HTMLDivElement> & {
683
688
  children: React.ReactNode;
684
689
  };
@@ -730,4 +735,92 @@ type ButtonOrLinkProps = {
730
735
  type BacklinkProps = (ButtonProps$1 | React.ComponentPropsWithoutRef<typeof Link>) & ButtonOrLinkProps;
731
736
  declare const _Backlink: react.ForwardRefExoticComponent<BacklinkProps & react.RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
732
737
 
733
- export { _Accordion as Accordion, _AccordionItem as AccordionItem, type AccordionItemProps, type AccordionProps, Alertbox, type Props as AlertboxProps, _Backlink as Backlink, type BacklinkProps, _Badge as Badge, type BadgeProps, _Breadcrumb as Breadcrumb, type BreadcrumbProps, _Breadcrumbs as Breadcrumbs, type BreadcrumbsProps, _Button as Button, type ButtonProps, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, _Combobox as Combobox, ListBoxItem as ComboboxItem, type ComboboxProps, Content, ContentContext, type ContentProps, Footer, type FooterProps, GrunnmurenProvider, type GrunnmurenProviderProps, Heading, HeadingContext, type HeadingProps, type Locale, _NumberField as NumberField, type NumberFieldProps, _Radio as Radio, _RadioGroup as RadioGroup, type RadioGroupProps, type RadioProps, _Select as Select, ListBoxItem as SelectItem, type SelectProps, _TextArea as TextArea, type TextAreaProps, _TextField as TextField, type TextFieldProps, _useLocale as useLocale };
738
+ type CardProps = VariantProps<typeof cardVariants> & {
739
+ children?: React.ReactNode;
740
+ className?: string;
741
+ };
742
+ declare const cardVariants: (props?: ({
743
+ variant?: "subtle" | "outlined" | undefined;
744
+ } & ({
745
+ class?: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | any | {
746
+ [x: string]: any;
747
+ } | null | undefined)[] | {
748
+ [x: string]: any;
749
+ } | null | undefined)[] | {
750
+ [x: string]: any;
751
+ } | null | undefined)[] | {
752
+ [x: string]: any;
753
+ } | null | undefined)[] | {
754
+ [x: string]: any;
755
+ } | null | undefined)[] | {
756
+ [x: string]: any;
757
+ } | null | undefined)[] | {
758
+ [x: string]: any;
759
+ } | null | undefined)[] | {
760
+ [x: string]: any;
761
+ } | null | undefined)[] | {
762
+ [x: string]: any;
763
+ } | null | undefined)[] | {
764
+ [x: string]: any;
765
+ } | null | undefined)[] | {
766
+ [x: string]: any;
767
+ } | null | undefined)[] | {
768
+ [x: string]: any;
769
+ } | null | undefined;
770
+ className?: never;
771
+ } | {
772
+ class?: never;
773
+ className?: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | any | {
774
+ [x: string]: any;
775
+ } | null | undefined)[] | {
776
+ [x: string]: any;
777
+ } | null | undefined)[] | {
778
+ [x: string]: any;
779
+ } | null | undefined)[] | {
780
+ [x: string]: any;
781
+ } | null | undefined)[] | {
782
+ [x: string]: any;
783
+ } | null | undefined)[] | {
784
+ [x: string]: any;
785
+ } | null | undefined)[] | {
786
+ [x: string]: any;
787
+ } | null | undefined)[] | {
788
+ [x: string]: any;
789
+ } | null | undefined)[] | {
790
+ [x: string]: any;
791
+ } | null | undefined)[] | {
792
+ [x: string]: any;
793
+ } | null | undefined)[] | {
794
+ [x: string]: any;
795
+ } | null | undefined)[] | {
796
+ [x: string]: any;
797
+ } | null | undefined;
798
+ })) | undefined) => string;
799
+ declare const Card: ({ children, className: _className, variant, ...restProps }: CardProps) => react_jsx_runtime.JSX.Element;
800
+ type RACLinkProps = Pick<LinkProps, 'href' | 'routerOptions' | 'children'>;
801
+ type CardLinkWrapperProps = RACLinkProps & {
802
+ children: React.ReactNode;
803
+ };
804
+ type CardLinkProps = {
805
+ className?: string;
806
+ } & (RACLinkProps | CardLinkWrapperProps);
807
+ /**
808
+ * A component that creates a clickable area on a card.
809
+ * It can be used either as a wrapper around a link or as a standalone link.
810
+ */
811
+ declare const CardLink: ({ className: _className, href, ...restProps }: CardLinkProps) => react_jsx_runtime.JSX.Element;
812
+
813
+ type DateFormatterProps = {
814
+ value: Date | string;
815
+ options?: DateFormatterOptions;
816
+ /** Callback to customize the rendering of the date */
817
+ children?: (formattedDate: string) => ReactNode;
818
+ };
819
+ /**
820
+ * A React component that wraps https://react-spectrum.adobe.com/react-aria/useDateFormatter.html
821
+ * By default it sets the timeZone to `Europe/Berlin` to prevent the server's timezone from affecting
822
+ * the localized format
823
+ */
824
+ declare const DateFormatter: ({ options: _options, value, children: render, }: DateFormatterProps) => ReactNode;
825
+
826
+ export { _Accordion as Accordion, _AccordionItem as AccordionItem, type AccordionItemProps, type AccordionProps, Alertbox, type Props as AlertboxProps, _Backlink as Backlink, type BacklinkProps, _Badge as Badge, type BadgeProps, _Breadcrumb as Breadcrumb, type BreadcrumbProps, _Breadcrumbs as Breadcrumbs, type BreadcrumbsProps, _Button as Button, type ButtonProps, Card, CardLink, type CardLinkProps, type CardProps, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, _Combobox as Combobox, ListBoxItem as ComboboxItem, type ComboboxProps, Content, ContentContext, type ContentProps, DateFormatter, type DateFormatterProps, Footer, type FooterProps, GrunnmurenProvider, type GrunnmurenProviderProps, Heading, HeadingContext, type HeadingProps, type Locale, Media, type MediaProps, _NumberField as NumberField, type NumberFieldProps, _Radio as Radio, _RadioGroup as RadioGroup, type RadioGroupProps, type RadioProps, _Select as Select, ListBoxItem as SelectItem, type SelectProps, _TextArea as TextArea, type TextAreaProps, _TextField as TextField, type TextFieldProps, _useLocale as useLocale };
package/dist/index.mjs CHANGED
@@ -6,6 +6,7 @@ import { createContext, forwardRef, Children, useId, useState, useRef } from 're
6
6
  import { cx, cva, compose } from 'cva';
7
7
  import { ChevronDown, LoadingSpinner, Check, Close, InfoCircle, CheckCircle, Warning, CloseCircle, ChevronRight, ChevronLeft } from '@obosbbl/grunnmuren-icons-react';
8
8
  import { useLayoutEffect, mergeRefs } from '@react-aria/utils';
9
+ import { useDateFormatter } from 'react-aria';
9
10
 
10
11
  function GrunnmurenProvider({ children, locale = 'nb', navigate, useHref }) {
11
12
  return /*#__PURE__*/ jsx(I18nProvider, {
@@ -48,6 +49,10 @@ const Content = (props, ref)=>{
48
49
  });
49
50
  return outerWrapper ? outerWrapper(content) : content;
50
51
  };
52
+ const Media = (props)=>/*#__PURE__*/ jsx("div", {
53
+ ...props,
54
+ "data-slot": "media"
55
+ });
51
56
  const Footer = (props)=>/*#__PURE__*/ jsx("div", {
52
57
  ...props,
53
58
  "data-slot": "footer"
@@ -105,7 +110,7 @@ function AccordionItem(props, ref) {
105
110
  };
106
111
  return /*#__PURE__*/ jsx("div", {
107
112
  ...restProps,
108
- className: cx('group relative px-2', className),
113
+ className: cx('relative px-2', className),
109
114
  ref: ref,
110
115
  "data-open": isOpen,
111
116
  children: /*#__PURE__*/ jsx(Provider, {
@@ -121,7 +126,7 @@ function AccordionItem(props, ref) {
121
126
  "aria-controls": contentId,
122
127
  "aria-expanded": isOpen,
123
128
  // Use outline with offset as focus indicator, this does not cover the left mint border on the expanded content and works with or without a background color on the accordion container
124
- className: "flex min-h-[44px] w-full items-center justify-between gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline focus-visible:outline-4 focus-visible:outline-offset-[-6px] focus-visible:outline-black",
129
+ className: "flex min-h-[44px] w-full items-center justify-between gap-1.5 rounded-lg px-2 py-3.5 text-left focus-visible:outline-focus focus-visible:outline-focus-inset",
125
130
  id: buttonId,
126
131
  onClick: handleOpenChange,
127
132
  children: [
@@ -198,7 +203,7 @@ const _Badge = /*#__PURE__*/ forwardRef(Badge);
198
203
  * Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
199
204
  */ const buttonVariants = cva({
200
205
  base: [
201
- 'inline-flex min-h-[44px] cursor-pointer items-center justify-center whitespace-nowrap rounded-lg font-medium transition-all duration-200 focus:outline-none data-[focus-visible]:ring-2 data-[focus-visible]:ring-offset-2'
206
+ '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'
202
207
  ],
203
208
  variants: {
204
209
  /**
@@ -214,9 +219,9 @@ const _Badge = /*#__PURE__*/ forwardRef(Badge);
214
219
  * Adjusts the color of the button for usage on different backgrounds.
215
220
  * @default green
216
221
  */ color: {
217
- green: 'focus-visible:ring-black',
218
- mint: 'focus-visible:ring-mint focus-visible:ring-offset-green-dark',
219
- white: 'focus-visible:ring-white focus-visible:ring-offset-blue'
222
+ green: 'focus-visible:outline-focus',
223
+ mint: 'focus-visible:outline-focus focus-visible:outline-mint',
224
+ white: 'focus-visible:outline-focus focus-visible:outline-white'
220
225
  },
221
226
  /**
222
227
  * When the button is without text, but with a single icon.
@@ -340,15 +345,15 @@ const input = cva({
340
345
  'box-content min-h-6 py-2.5',
341
346
  'rounded-md text-base font-normal leading-6 placeholder-[#727070] outline-none ring-1 ring-black',
342
347
  // invalid styles
343
- 'group-data-[invalid]:ring-2 group-data-[invalid]:ring-red',
348
+ 'group-data-[invalid]:ring-focus group-data-[invalid]:ring-red',
344
349
  // Fix invisible ring on safari: https://github.com/tailwindlabs/tailwindcss.com/issues/1135
345
350
  'appearance-none'
346
351
  ],
347
352
  variants: {
348
353
  // Focus rings. Can either be :focus or :focus-visible based on the needs of the particular component.
349
354
  focusModifier: {
350
- focus: 'focus:ring-2 group-data-[invalid]:focus:ring',
351
- visible: 'data-[focus-visible]:ring-2 group-data-[invalid]:data-[focus-visible]:ring'
355
+ focus: 'focus:ring-focus group-data-[invalid]:focus:ring',
356
+ visible: 'data-[focus-visible]:ring-focus group-data-[invalid]:data-[focus-visible]:ring'
352
357
  },
353
358
  isGrouped: {
354
359
  false: 'px-3',
@@ -361,8 +366,8 @@ const input = cva({
361
366
  }
362
367
  });
363
368
  const inputGroup = cx([
364
- 'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-2',
365
- 'group-data-[invalid]:ring-2 group-data-[invalid]:ring-red group-data-[invalid]:focus-within:ring'
369
+ 'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-base ring-1 ring-black focus-within:ring-focus',
370
+ 'group-data-[invalid]:ring-focus group-data-[invalid]:ring-red group-data-[invalid]:focus-within:ring'
366
371
  ]);
367
372
  const dropdown = {
368
373
  popover: cx('min-w-[--trigger-width] overflow-y-auto rounded-md border border-black bg-white shadow data-[entering]:animate-in data-[exiting]:animate-out data-[entering]:fade-in data-[exiting]:fade-out'),
@@ -398,7 +403,7 @@ function CheckmarkBox() {
398
403
  // selected
399
404
  'group-data-[selected]:!border-green group-data-[selected]:!bg-green',
400
405
  // focus
401
- 'group-data-[focus-visible]:ring-2 group-data-[focus-visible]:ring-black group-data-[focus-visible]:ring-offset-[9px]',
406
+ 'group-data-[focus-visible]:outline-focus-offset',
402
407
  // hovered
403
408
  'group-data-[hovered]:border-green group-data-[hovered]:group-data-[invalid]:border-red group-data-[hovered]:bg-green-lightest group-data-[hovered]:group-data-[invalid]:bg-red-light',
404
409
  // invalid - The border is 1 px thicker when invalid. We don't actually want to change the border width, as that causes the element's size to change
@@ -635,7 +640,7 @@ const defaultClasses = cx([
635
640
  // hover
636
641
  'data-[hovered]:before:border-green data-[hovered]:before:bg-green-lightest data-[hovered]:data-[invalid]:before:bg-red-light',
637
642
  // focus
638
- 'data-[focus-visible]:before:ring data-[focus-visible]:before:ring-black data-[focus-visible]:before:ring-offset-[9px]',
643
+ 'data-[focus-visible]:before:ring-focus-offset',
639
644
  // invalid - The border is 1 px thicker when invalid. We don't actually want to change the border width, as that causes the element's size to change
640
645
  // so we use an inner outline to artifically pad the border
641
646
  'data-[invalid]:before:outline-solid data-[invalid]:before:border-red data-[invalid]:data-[selected]:before:!bg-red data-[invalid]:before:outline data-[invalid]:before:outline-[3px] data-[invalid]:before:outline-offset-[-3px] data-[invalid]:before:outline-red'
@@ -946,14 +951,14 @@ const Alertbox = ({ children, role, className, variant = 'info', isDismissable =
946
951
  /*#__PURE__*/ jsx(Icon, {}),
947
952
  firstChild,
948
953
  isDismissable && /*#__PURE__*/ jsx("button", {
949
- className: cx('-m-2 grid h-11 w-11 place-items-center rounded-xl', 'focus-visible:outline-none focus-visible:-outline-offset-8 focus-visible:outline-black'),
954
+ className: cx('-m-2 grid h-11 w-11 place-items-center rounded-xl', 'focus-visible:outline-focus focus-visible:-outline-offset-8'),
950
955
  onClick: close,
951
956
  "aria-label": translations.close[locale],
952
957
  children: /*#__PURE__*/ jsx(Close, {})
953
958
  }),
954
959
  isExpandable && /*#__PURE__*/ jsxs("button", {
955
960
  className: cx('relative col-span-full row-start-2 -my-3 inline-flex max-w-fit cursor-pointer items-center gap-1 py-3 text-sm leading-6', // Focus styles:
956
- 'outline-none after:absolute after:bottom-3 after:left-0 after:right-0 after:h-0 after:bg-transparent after:transition-all after:duration-200', 'focus-visible:after:h-[2px] focus-visible:after:bg-black'),
961
+ 'outline-none after:absolute after:bottom-3 after:left-0 after:right-0 after:h-0', 'focus-visible:after:h-[2px] focus-visible:after:bg-black'),
957
962
  onClick: ()=>setIsExpanded((prevState)=>!prevState),
958
963
  "aria-expanded": isExpanded,
959
964
  "aria-controls": id,
@@ -995,7 +1000,7 @@ function Breadcrumb(props, ref) {
995
1000
  href ? /*#__PURE__*/ jsx(Link, {
996
1001
  href: href,
997
1002
  // use outline instead of ring for focus marker that can be offset without creating a white background between the focus marker and the element content
998
- className: "rounded-sm focus:outline-none group-last:no-underline data-[focus-visible]:outline data-[focus-visible]:outline-offset-2 data-[focus-visible]:outline-black",
1003
+ className: "rounded-sm data-[focus-visible]:focus-visible:outline-focus focus:outline-none group-last:no-underline",
999
1004
  children: children
1000
1005
  }) : children,
1001
1006
  /*#__PURE__*/ jsx(ChevronRight, {
@@ -1013,7 +1018,7 @@ function Backlink(props, ref) {
1013
1018
  const { className, children, withUnderline, ...restProps } = props;
1014
1019
  const Component = isLinkProps(props) ? Link : Button$1;
1015
1020
  return /*#__PURE__*/ jsxs(Component, {
1016
- className: cx(className, 'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline focus:outline-none data-[focus-visible]:ring data-[focus-visible]:ring-black'),
1021
+ className: cx(className, 'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline focus-visible:outline-focus'),
1017
1022
  ...restProps,
1018
1023
  // @ts-expect-error ignore the type of the ref here
1019
1024
  ref: ref,
@@ -1032,4 +1037,153 @@ function Backlink(props, ref) {
1032
1037
  }
1033
1038
  const _Backlink = /*#__PURE__*/ forwardRef(Backlink);
1034
1039
 
1035
- export { _Accordion as Accordion, _AccordionItem as AccordionItem, Alertbox, _Backlink as Backlink, _Badge as Badge, _Breadcrumb as Breadcrumb, _Breadcrumbs as Breadcrumbs, _Button as Button, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, _Combobox as Combobox, ListBoxItem as ComboboxItem, Content, ContentContext, Footer, GrunnmurenProvider, Heading, HeadingContext, _NumberField as NumberField, _Radio as Radio, _RadioGroup as RadioGroup, _Select as Select, ListBoxItem as SelectItem, _TextArea as TextArea, _TextField as TextField, _useLocale as useLocale };
1040
+ const cardVariants = cva({
1041
+ base: [
1042
+ 'group/card',
1043
+ 'rounded-2xl border p-3',
1044
+ 'grid auto-rows-max gap-y-4',
1045
+ 'relative',
1046
+ // **** Heading ****
1047
+ '[&_[data-slot="heading"]]:inline',
1048
+ '[&_[data-slot="heading"]]:heading-s',
1049
+ '[&_[data-slot="heading"]]:w-fit',
1050
+ '[&_[data-slot="heading"]]:text-pretty',
1051
+ // **** Content ****
1052
+ '[&_[data-slot="content"]]:grid [&_[data-slot="content"]]:auto-rows-max [&_[data-slot="content"]]:gap-y-4',
1053
+ // **** Media ****
1054
+ '[&_[data-slot="media"]]:overflow-hidden',
1055
+ '[&_[data-slot="media"]]:rounded-t-2xl',
1056
+ // Position media at the edges of the card (because of these negative margins the media-element must be a wrapper around the actual image or other media content)
1057
+ '[&_[data-slot="media"]]:mx-[calc(theme(space.3)*-1-theme(borderWidth.DEFAULT))] [&_[data-slot="media"]]:mt-[calc(theme(space.3)*-1-theme(borderWidth.DEFAULT))]',
1058
+ // Sets the aspect ratio of the media content (width: 100% is necessary to make aspect ratio work in FF)
1059
+ '[&_[data-slot="media"]>*]:aspect-[3/2] [&_[data-slot="media"]>*]:w-full [&_[data-slot="media"]_img]:object-cover',
1060
+ // Prepare zoom animation for hover effects. The hover effect can also be enabled by classes on the parent component, so it is always prepared here.
1061
+ '[&_[data-slot="media"]>*]:duration-300 [&_[data-slot="media"]>*]:ease-in-out [&_[data-slot="media"]>*]:motion-safe:transition-transform',
1062
+ // **** Card link ****
1063
+ // **** Hover ****
1064
+ // Enables the zoom hover effect on media (note that we can't use group-hover/card here, because there might be other clickable elements in the card aside from the heading)
1065
+ '[&:has([data-slot="card-link"]_a:hover)_[data-slot="media"]>*]:scale-110',
1066
+ // **** Card link in Heading ****
1067
+ '[&:has([data-slot="heading"]_[data-slot="card-link"]:hover)_[data-slot="media"]>*]:scale-110',
1068
+ // Border (bottom/top) is set to transparent to make sure the bottom underline is not visible when the card is hovered
1069
+ // Border top is set to even out the border bottom used for the underline
1070
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:no-underline',
1071
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:border-y-2',
1072
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:border-y-transparent',
1073
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:transition-colors',
1074
+ '[&_[data-slot="heading"]_[data-slot="card-link"]:hover]:border-b-current',
1075
+ // Mimic heading styles for the card link if placed in the heading slot. This is necessary to make the custom underline align with the link text
1076
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:heading-s [&_[data-slot="heading"]_[data-slot="card-link"]]:text-pretty',
1077
+ // **** Fail-safe for interactive elements ****
1078
+ // Make interactive elements clickable by themselves, while the rest of the card is clickable as a whole
1079
+ // The card is made clickable by a pseudo-element on the heading that covers the entire card
1080
+ '[&:not(:has([data-slot="card-link"]_a))_a:not([data-slot="card-link"])]:relative [&_button]:relative [&_input]:relative',
1081
+ // Place other interactive on top of the pseudo-element that makes the entire card clickable
1082
+ // by setting a higher z-index than the pseudo-element (which implicitly z-index 0)
1083
+ '[&_a:not([data-slot="card-link"])]:z-[1] [&_button]:z-[1] [&_input]:z-[1]'
1084
+ ],
1085
+ variants: {
1086
+ /**
1087
+ * The variant of the card
1088
+ * @default subtle
1089
+ */ variant: {
1090
+ subtle: [
1091
+ 'border-transparent',
1092
+ // Media styles:
1093
+ '[&_[data-slot="media"]]:rounded-b-2xl'
1094
+ ],
1095
+ outlined: 'border border-black'
1096
+ }
1097
+ },
1098
+ defaultVariants: {
1099
+ variant: 'subtle'
1100
+ }
1101
+ });
1102
+ const Card = ({ children, className: _className, variant, ...restProps })=>{
1103
+ const className = cardVariants({
1104
+ className: _className,
1105
+ variant
1106
+ });
1107
+ return /*#__PURE__*/ jsx("div", {
1108
+ className: className,
1109
+ ...restProps,
1110
+ children: children
1111
+ });
1112
+ };
1113
+ const cardLinkVariants = cva({
1114
+ base: 'w-fit max-w-full',
1115
+ variants: {
1116
+ withHref: {
1117
+ true: [
1118
+ // **** Clickarea ****
1119
+ 'cursor-pointer',
1120
+ 'after:absolute',
1121
+ 'after:inset-[calc(theme(borderWidth.DEFAULT)*-1)]',
1122
+ 'after:rounded-[calc(theme(borderRadius.2xl)-theme(borderWidth.DEFAULT))]',
1123
+ // **** Focus ****
1124
+ 'focus-visible:outline-none',
1125
+ 'focus-visible:after:outline-focus',
1126
+ 'focus-visible:after:outline-offset-2',
1127
+ // **** Hover ****
1128
+ // Links are underlined by default, and the underline is removed on hover.
1129
+ // So we make sure that also happens when the user hovers the clickable area.
1130
+ 'hover:no-underline'
1131
+ ],
1132
+ false: [
1133
+ // **** Clickarea ****
1134
+ '[&_a]:after:cursor-pointer',
1135
+ '[&_a]:after:absolute',
1136
+ '[&_a]:after:inset-[calc(theme(borderWidth.DEFAULT)*-1)]',
1137
+ '[&_a]:after:rounded-[calc(theme(borderRadius.2xl)-theme(borderWidth.DEFAULT))]',
1138
+ // **** Focus ****
1139
+ '[&_a:focus-visible]:outline-none',
1140
+ '[&_a:focus-visible]:after:outline-focus',
1141
+ '[&_a:focus-visible]:after:outline-offset-2',
1142
+ // **** Hover ****
1143
+ // Links are underlined by default, and the underline is removed on hover.
1144
+ // So we make sure that also happens when the user hovers the card.
1145
+ // The group-hover ensures that the hover effect also applies when this component is used as a wrapper around a link.
1146
+ '[&_a]:group-hover/card:no-underline'
1147
+ ]
1148
+ }
1149
+ }
1150
+ });
1151
+ /**
1152
+ * A component that creates a clickable area on a card.
1153
+ * It can be used either as a wrapper around a link or as a standalone link.
1154
+ */ const CardLink = ({ className: _className, href, ...restProps })=>{
1155
+ const className = cardLinkVariants({
1156
+ className: _className,
1157
+ withHref: !!href
1158
+ });
1159
+ return href ? /*#__PURE__*/ jsx(Link, {
1160
+ "data-slot": "card-link",
1161
+ ...restProps,
1162
+ href: href,
1163
+ className: className
1164
+ }) : // We can't utilize that the `Link` component from react-aria-components renders as a span if it doesn't have an href,
1165
+ // because it still renders with role="link" and tabindex="0" which makes it focusable.
1166
+ // So we need to render a div instead.
1167
+ /*#__PURE__*/ jsx("div", {
1168
+ "data-slot": "card-link",
1169
+ className: className,
1170
+ ...restProps
1171
+ });
1172
+ };
1173
+
1174
+ /**
1175
+ * A React component that wraps https://react-spectrum.adobe.com/react-aria/useDateFormatter.html
1176
+ * By default it sets the timeZone to `Europe/Berlin` to prevent the server's timezone from affecting
1177
+ * the localized format
1178
+ */ const DateFormatter = ({ options: _options, value, children: render })=>{
1179
+ const options = {
1180
+ timeZone: 'Europe/Berlin',
1181
+ ..._options
1182
+ };
1183
+ const formatter = useDateFormatter(options);
1184
+ const date = typeof value === 'string' ? new Date(value) : value;
1185
+ const formatted = formatter.format(date);
1186
+ return render ? render(formatted) : formatted;
1187
+ };
1188
+
1189
+ export { _Accordion as Accordion, _AccordionItem as AccordionItem, Alertbox, _Backlink as Backlink, _Badge as Badge, _Breadcrumb as Breadcrumb, _Breadcrumbs as Breadcrumbs, _Button as Button, Card, CardLink, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, _Combobox as Combobox, ListBoxItem as ComboboxItem, Content, ContentContext, DateFormatter, Footer, GrunnmurenProvider, Heading, HeadingContext, Media, _NumberField as NumberField, _Radio as Radio, _RadioGroup as RadioGroup, _Select as Select, ListBoxItem as SelectItem, _TextArea as TextArea, _TextField as TextField, _useLocale as useLocale };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obosbbl/grunnmuren-react",
3
- "version": "2.0.0-canary.36",
3
+ "version": "2.0.0-canary.38",
4
4
  "description": "Grunnmuren components in React",
5
5
  "repository": {
6
6
  "url": "https://github.com/code-obos/grunnmuren"
@@ -20,8 +20,9 @@
20
20
  "dependencies": {
21
21
  "@obosbbl/grunnmuren-icons-react": "^2.0.0-canary.1",
22
22
  "@react-aria/utils": "^3.25.1",
23
- "@types/node": "^20.11.19",
23
+ "@types/node": "^22.0.0",
24
24
  "cva": "1.0.0-beta.1",
25
+ "react-aria": "^3.35.1",
25
26
  "react-aria-components": "^1.3.1"
26
27
  },
27
28
  "peerDependencies": {