@obosbbl/grunnmuren-react 2.0.0-canary.39 → 2.0.0-canary.40

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
@@ -181,6 +181,7 @@ declare const buttonVariants: (props?: ({
181
181
  variant?: "primary" | "secondary" | "tertiary" | undefined;
182
182
  color?: "mint" | "white" | "green" | undefined;
183
183
  isIconOnly?: boolean | undefined;
184
+ isPending?: boolean | undefined;
184
185
  } & ({
185
186
  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 | {
186
187
  [x: string]: any;
@@ -241,9 +242,12 @@ type ButtonOrLinkProps$1 = VariantProps<typeof buttonVariants> & {
241
242
  href?: string;
242
243
  /**
243
244
  * Display the button in a loading state
245
+ * @deprecated Use isPending instead.
244
246
  * @default false
245
247
  */
246
248
  isLoading?: boolean;
249
+ /** Additional style properties for the element. */
250
+ style?: React.CSSProperties;
247
251
  };
248
252
  type ButtonProps = (ButtonProps$1 | React.ComponentPropsWithoutRef<typeof Link>) & ButtonOrLinkProps$1;
249
253
  declare const _Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
@@ -309,10 +313,15 @@ type ComboboxProps<T extends object> = {
309
313
  /** Error message for the form control. Automatically sets `isInvalid` to true */
310
314
  errorMessage?: React.ReactNode;
311
315
  /**
312
- * Display the dropdown button trigger in a loading state
313
- * @default false
316
+ * Display the dropdown button trigger in a pending state
317
+ * @deprecated Use isPending instead.
314
318
  */
315
319
  isLoading?: boolean;
320
+ /**
321
+ * Display the dropdown button trigger in a pending state
322
+ * @default false
323
+ */
324
+ isPending?: boolean;
316
325
  /** Label for the form control. */
317
326
  label?: React.ReactNode;
318
327
  /** Placeholder text. Only visible when the input value is empty. */
@@ -329,10 +338,15 @@ declare const _Combobox: react.ForwardRefExoticComponent<{
329
338
  /** Error message for the form control. Automatically sets `isInvalid` to true */
330
339
  errorMessage?: React.ReactNode;
331
340
  /**
332
- * Display the dropdown button trigger in a loading state
333
- * @default false
341
+ * Display the dropdown button trigger in a pending state
342
+ * @deprecated Use isPending instead.
334
343
  */
335
344
  isLoading?: boolean;
345
+ /**
346
+ * Display the dropdown button trigger in a pending state
347
+ * @default false
348
+ */
349
+ isPending?: boolean;
336
350
  /** Label for the form control. */
337
351
  label?: React.ReactNode;
338
352
  /** Placeholder text. Only visible when the input value is empty. */
package/dist/index.mjs CHANGED
@@ -2,11 +2,11 @@
2
2
  import { I18nProvider, RouterProvider, useLocale, useContextProps, Provider, Link, Button as Button$1, Text, CheckboxContext, Checkbox as Checkbox$1, Label as Label$1, FieldError, CheckboxGroup as CheckboxGroup$1, ListBoxItem as ListBoxItem$1, ListBox as ListBox$1, ComboBox, Group, Input, Popover, RadioGroup as RadioGroup$1, Radio as Radio$1, Select as Select$1, SelectValue, TextField as TextField$1, TextArea as TextArea$1, NumberField as NumberField$1, Breadcrumbs as Breadcrumbs$1, Breadcrumb as Breadcrumb$1 } from 'react-aria-components';
3
3
  export { Form } from 'react-aria-components';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
- import { createContext, forwardRef, Children, useId, useState, useRef } from 'react';
5
+ import { createContext, forwardRef, Children, useId, useState } from 'react';
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
- import { useLayoutEffect, mergeRefs } from '@react-aria/utils';
9
- import { useDateFormatter } from 'react-aria';
8
+ import { useLayoutEffect } from '@react-aria/utils';
9
+ import { useProgressBar, useDateFormatter } from 'react-aria';
10
10
 
11
11
  function GrunnmurenProvider({ children, locale = 'nb', navigate, useHref }) {
12
12
  return /*#__PURE__*/ jsx(I18nProvider, {
@@ -203,7 +203,7 @@ const _Badge = /*#__PURE__*/ forwardRef(Badge);
203
203
  * Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
204
204
  */ const buttonVariants = cva({
205
205
  base: [
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'
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 [&:not([data-focus-visible])]:outline-none'
207
207
  ],
208
208
  variants: {
209
209
  /**
@@ -219,9 +219,9 @@ const _Badge = /*#__PURE__*/ forwardRef(Badge);
219
219
  * Adjusts the color of the button for usage on different backgrounds.
220
220
  * @default green
221
221
  */ color: {
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'
222
+ green: 'data-[focus-visible]:outline-focus',
223
+ mint: 'data-[focus-visible]:outline-focus data-[focus-visible]:outline-mint',
224
+ white: 'data-[focus-visible]:outline-focus data-[focus-visible]:outline-white'
225
225
  },
226
226
  /**
227
227
  * When the button is without text, but with a single icon.
@@ -229,6 +229,11 @@ const _Badge = /*#__PURE__*/ forwardRef(Badge);
229
229
  */ isIconOnly: {
230
230
  true: 'p-2 [&>svg]:h-7 [&>svg]:w-7',
231
231
  false: 'gap-2.5 px-4 py-2'
232
+ },
233
+ // Make the content of the button transparent to hide it's content, but keep the button width
234
+ isPending: {
235
+ true: 'relative !text-transparent',
236
+ false: null
232
237
  }
233
238
  },
234
239
  compoundVariants: [
@@ -236,97 +241,100 @@ const _Badge = /*#__PURE__*/ forwardRef(Badge);
236
241
  color: 'green',
237
242
  variant: 'primary',
238
243
  // Darken bg by 20% on hover. The color is manually crafted
239
- className: 'bg-green text-white hover:bg-green-dark active:bg-[#007352]'
244
+ className: 'bg-green text-white hover:bg-green-dark active:bg-[#007352] [&_[role="progressbar"]]:text-white'
240
245
  },
241
246
  {
242
247
  color: 'green',
243
248
  variant: 'secondary',
244
- className: 'text-black shadow-green hover:bg-green hover:text-white active:bg-green'
249
+ className: 'text-black shadow-green hover:bg-green hover:text-white active:bg-green [&:hover_[role="progressbar"]]:text-white [&_[role="progressbar"]]:text-black'
250
+ },
251
+ {
252
+ color: 'green',
253
+ variant: 'tertiary',
254
+ className: '[&_[role="progressbar"]]:text-black'
245
255
  },
246
256
  {
247
257
  color: 'mint',
248
258
  variant: 'primary',
249
259
  // Darken bg by 20% on hover. The color is manually crafted
250
- className: 'active:[#9ddac6] bg-mint text-black hover:bg-[#8dd4bd]'
260
+ className: 'active:[#9ddac6] bg-mint text-black hover:bg-[#8dd4bd] [&_[role="progressbar"]]:text-black'
251
261
  },
252
262
  {
253
263
  color: 'mint',
254
264
  variant: 'secondary',
255
- className: 'text-mint shadow-mint hover:bg-mint hover:text-black'
265
+ className: 'text-mint shadow-mint hover:bg-mint hover:text-black [&:hover_[role="progressbar"]]:text-black [&_[role="progressbar"]]:text-mint'
256
266
  },
257
267
  {
258
268
  color: 'mint',
259
269
  variant: 'tertiary',
260
- className: 'text-mint'
270
+ className: 'text-mint [&_[role="progressbar"]]:text-mint'
261
271
  },
262
272
  {
263
273
  color: 'white',
264
274
  variant: 'primary',
265
- className: 'bg-white text-black hover:bg-sky active:bg-sky-light'
275
+ className: 'bg-white text-black hover:bg-sky active:bg-sky-light [&_[role="progressbar"]]:text-black'
266
276
  },
267
277
  {
268
278
  color: 'white',
269
279
  variant: 'secondary',
270
- className: 'text-white shadow-white hover:bg-white hover:text-black'
280
+ className: 'text-white shadow-white hover:bg-white hover:text-black [&:hover_[role="progressbar"]]:text-black [&_[role="progressbar"]]:text-white'
271
281
  },
272
282
  {
273
283
  color: 'white',
274
284
  variant: 'tertiary',
275
- className: 'text-white'
285
+ className: 'text-white [&_[role="progressbar"]]:text-white'
276
286
  }
277
287
  ],
278
288
  defaultVariants: {
279
289
  variant: 'primary',
280
290
  color: 'green',
281
- isIconOnly: false
291
+ isIconOnly: false,
292
+ isPending: false
282
293
  }
283
294
  });
284
295
  function isLinkProps$1(props) {
285
296
  return !!props.href;
286
297
  }
287
- function Button(props, forwardedRef) {
288
- const { children: _children, color, isIconOnly, isLoading, variant, style: _style, ...restProps } = props;
289
- const [widthOverride, setWidthOverride] = useState();
290
- const ownRef = useRef(null);
291
- const ref = mergeRefs(ownRef, forwardedRef);
292
- useLayoutEffect(()=>{
293
- if (isLoading) {
294
- const requestID = window.requestAnimationFrame(()=>{
295
- setWidthOverride(ownRef.current?.getBoundingClientRect()?.width);
296
- });
297
- return ()=>{
298
- setWidthOverride(undefined);
299
- cancelAnimationFrame(requestID);
300
- };
301
- }
302
- }, [
303
- isLoading,
304
- _children
305
- ]);
298
+ const translations$1 = {
299
+ pending: {
300
+ nb: 'venter',
301
+ sv: 'väntar',
302
+ en: 'pending'
303
+ }
304
+ };
305
+ function Button(props, ref) {
306
+ const { children: _children, color, isIconOnly, isLoading, variant, isPending: _isPending, ...restProps } = props;
307
+ const isPending = _isPending || isLoading;
306
308
  const className = buttonVariants({
307
309
  className: props.className,
308
310
  color,
309
311
  isIconOnly,
310
- variant
312
+ variant,
313
+ isPending
314
+ });
315
+ const locale = _useLocale();
316
+ const { progressBarProps } = useProgressBar({
317
+ isIndeterminate: true,
318
+ 'aria-label': translations$1.pending[locale]
311
319
  });
312
- const children = widthOverride ? // remove margin for icon alignment
313
- /*#__PURE__*/ jsx(LoadingSpinner, {
314
- className: "!m-0 mx-auto animate-spin"
320
+ const children = isPending ? /*#__PURE__*/ jsxs(Fragment, {
321
+ children: [
322
+ _children,
323
+ /*#__PURE__*/ jsx(LoadingSpinner, {
324
+ className: "absolute m-auto motion-safe:animate-spin",
325
+ ...progressBarProps
326
+ })
327
+ ]
315
328
  }) : _children;
316
- const style = {
317
- ..._style,
318
- width: widthOverride
319
- };
320
329
  return isLinkProps$1(restProps) ? /*#__PURE__*/ jsx(Link, {
321
330
  ...restProps,
322
331
  className: className,
323
- style: style,
324
332
  ref: ref,
325
333
  children: children
326
334
  }) : /*#__PURE__*/ jsx(Button$1, {
327
335
  ...restProps,
328
336
  className: className,
329
- style: style,
337
+ isPending: isPending,
330
338
  ref: ref,
331
339
  children: children
332
340
  });
@@ -546,7 +554,8 @@ function InputAddonDivider() {
546
554
  }
547
555
 
548
556
  function Combobox(props, ref) {
549
- const { className, children, description, errorMessage, isLoading, label, isInvalid: _isInvalid, ...restProps } = props;
557
+ const { className, children, description, errorMessage, isLoading, isPending: _isPending, label, isInvalid: _isInvalid, ...restProps } = props;
558
+ const isPending = _isPending || isLoading;
550
559
  // the order of the conditions matter here, because providing a value for isInvalid makes the validation state "controlled",
551
560
  // which will override any built in validation
552
561
  const isInvalid = errorMessage != null || _isInvalid;
@@ -571,7 +580,7 @@ function Combobox(props, ref) {
571
580
  ref: ref
572
581
  }),
573
582
  /*#__PURE__*/ jsx(Button$1, {
574
- children: isLoading ? /*#__PURE__*/ jsx(LoadingSpinner, {
583
+ children: isPending ? /*#__PURE__*/ jsx(LoadingSpinner, {
575
584
  className: "animate-spin"
576
585
  }) : /*#__PURE__*/ jsx(ChevronDown, {
577
586
  className: dropdown.chevronIcon
@@ -998,7 +1007,7 @@ function Breadcrumb(props, ref) {
998
1007
  href ? /*#__PURE__*/ jsx(Link, {
999
1008
  href: href,
1000
1009
  // 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
1001
- className: "rounded-sm data-[focus-visible]:focus-visible:outline-focus focus:outline-none group-last:no-underline",
1010
+ className: "rounded-sm data-[focus-visible]:outline-focus group-last:no-underline [&:not([data-focus-visible])]:outline-none",
1002
1011
  children: children
1003
1012
  }) : children,
1004
1013
  /*#__PURE__*/ jsx(ChevronRight, {
@@ -1016,7 +1025,7 @@ function Backlink(props, ref) {
1016
1025
  const { className, children, withUnderline, ...restProps } = props;
1017
1026
  const Component = isLinkProps(props) ? Link : Button$1;
1018
1027
  return /*#__PURE__*/ jsxs(Component, {
1019
- 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'),
1028
+ className: cx(className, 'group flex max-w-fit cursor-pointer items-center gap-3 rounded-md p-2.5 no-underline data-[focus-visible]:outline-focus [&:not([data-focus-visible])]:outline-none'),
1020
1029
  ...restProps,
1021
1030
  // @ts-expect-error ignore the type of the ref here
1022
1031
  ref: ref,
@@ -1044,6 +1053,7 @@ const cardVariants = cva({
1044
1053
  // **** Heading ****
1045
1054
  '[&_[data-slot="heading"]]:inline',
1046
1055
  '[&_[data-slot="heading"]]:heading-s',
1056
+ '[&_[data-slot="heading"]]:leading-6',
1047
1057
  '[&_[data-slot="heading"]]:w-fit',
1048
1058
  '[&_[data-slot="heading"]]:text-pretty',
1049
1059
  // **** Content ****
@@ -1071,11 +1081,13 @@ const cardVariants = cva({
1071
1081
  '[&_[data-slot="heading"]_[data-slot="card-link"]]:transition-colors',
1072
1082
  '[&_[data-slot="heading"]_[data-slot="card-link"]:hover]:border-b-current',
1073
1083
  // 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
1074
- '[&_[data-slot="heading"]_[data-slot="card-link"]]:heading-s [&_[data-slot="heading"]_[data-slot="card-link"]]:text-pretty',
1084
+ '[&_[data-slot="heading"]_[data-slot="card-link"]]:heading-s [&_[data-slot="heading"]_[data-slot="card-link"]]:text-pretty [&_[data-slot="heading"]_[data-slot="card-link"]]:leading-6',
1075
1085
  // **** Fail-safe for interactive elements ****
1076
1086
  // Make interactive elements clickable by themselves, while the rest of the card is clickable as a whole
1077
1087
  // The card is made clickable by a pseudo-element on the heading that covers the entire card
1078
1088
  '[&:not(:has([data-slot="card-link"]_a))_a:not([data-slot="card-link"])]:relative [&_button]:relative [&_input]:relative',
1089
+ // Our Button component has position: relative by default, so we need to override that if it is used in a CardLink (to make the entire card clickable)
1090
+ '[&_[data-slot="card-link"]_a]:static',
1079
1091
  // Place other interactive on top of the pseudo-element that makes the entire card clickable
1080
1092
  // by setting a higher z-index than the pseudo-element (which implicitly z-index 0)
1081
1093
  '[&_a:not([data-slot="card-link"])]:z-[1] [&_button]:z-[1] [&_input]:z-[1]'
@@ -1120,8 +1132,8 @@ const cardLinkVariants = cva({
1120
1132
  'after:rounded-[calc(theme(borderRadius.2xl)-theme(borderWidth.DEFAULT))]',
1121
1133
  // **** Focus ****
1122
1134
  'focus-visible:outline-none',
1123
- 'focus-visible:after:outline-focus',
1124
- 'focus-visible:after:outline-offset-2',
1135
+ 'data-[focus-visible]:after:outline-focus',
1136
+ 'data-[focus-visible]:after:outline-offset-2',
1125
1137
  // **** Hover ****
1126
1138
  // Links are underlined by default, and the underline is removed on hover.
1127
1139
  // So we make sure that also happens when the user hovers the clickable area.
@@ -1134,9 +1146,9 @@ const cardLinkVariants = cva({
1134
1146
  '[&_a]:after:inset-[calc(theme(borderWidth.DEFAULT)*-1)]',
1135
1147
  '[&_a]:after:rounded-[calc(theme(borderRadius.2xl)-theme(borderWidth.DEFAULT))]',
1136
1148
  // **** Focus ****
1137
- '[&_a:focus-visible]:outline-none',
1138
- '[&_a:focus-visible]:after:outline-focus',
1139
- '[&_a:focus-visible]:after:outline-offset-2',
1149
+ '[&_a[data-focus-visible]]:outline-none',
1150
+ '[&_a[data-focus-visible]]:after:outline-focus',
1151
+ '[&_a[data-focus-visible]]:after:outline-offset-2',
1140
1152
  // **** Hover ****
1141
1153
  // Links are underlined by default, and the underline is removed on hover.
1142
1154
  // So we make sure that also happens when the user hovers the card.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@obosbbl/grunnmuren-react",
3
- "version": "2.0.0-canary.39",
3
+ "version": "2.0.0-canary.40",
4
4
  "description": "Grunnmuren components in React",
5
5
  "repository": {
6
6
  "url": "https://github.com/code-obos/grunnmuren"