@obosbbl/grunnmuren-react 2.0.0-canary.13 → 2.0.0-canary.15
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/README.md +76 -10
- package/dist/index.d.mts +55 -13
- package/dist/index.mjs +299 -11
- package/package.json +3 -3
- package/dist/Alertbox-client-J1P2mzVC.js +0 -120
- package/dist/Button-client-XmGlKEk4.js +0 -143
package/README.md
CHANGED
|
@@ -14,20 +14,35 @@ npm install @obosbbl/grunnmuren-react@canary
|
|
|
14
14
|
pnpm add @obosbbl/grunnmuren-react@canary
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
### Internationalization
|
|
18
20
|
|
|
19
21
|
Grunnmuren uses [React Aria Components](https://react-spectrum.adobe.com/react-aria/) under the hood. RAC has built in translation strings for non visible content (for accessibility reasons). It also automatically detects the language based on the browser or system language.
|
|
20
22
|
|
|
21
|
-
To ensure that the language of the page content matches the accessibility strings you
|
|
23
|
+
To ensure that the language of the page content matches the accessibility strings you must wrap your application in a `GrunnmurenProvider` with a `locale` prop. This will override RAC's automatic locale selection.
|
|
22
24
|
|
|
23
|
-
In [Next.js](https://nextjs.org/) you can do this in the root [root layout](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required).
|
|
25
|
+
In [Next.js](https://nextjs.org/) you can do this in the root [root layout](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). In order to avoid making `RootLayout` a client component, you should import `GrunnmurenProvider` in a providers-file, that uses `"use client"`
|
|
24
26
|
|
|
25
|
-
Valid locales
|
|
27
|
+
Valid locales are `nb`, `sv` or `en`. The provider defaults to `nb` if unspecified.
|
|
26
28
|
|
|
27
29
|
```js
|
|
28
|
-
// app/
|
|
29
|
-
|
|
30
|
+
// app/providers.tsx
|
|
31
|
+
'use client'
|
|
32
|
+
import { GrunnmurenProvider } from '@obosbbl/grunnmuren-react';
|
|
30
33
|
|
|
34
|
+
export function Providers({children, locale}: { children: React.ReactNode, locale: 'nb' | 'sv' | 'en'}) {
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<GrunnmurenProvider locale={locale}>
|
|
38
|
+
{children}
|
|
39
|
+
</GrunnmurenProvider>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
// app/layout.tsx
|
|
31
46
|
|
|
32
47
|
export default function RootLayout({
|
|
33
48
|
children,
|
|
@@ -35,21 +50,72 @@ export default function RootLayout({
|
|
|
35
50
|
children: React.ReactNode
|
|
36
51
|
}) {
|
|
37
52
|
|
|
38
|
-
// Either 'nb' or '
|
|
53
|
+
// Either 'nb', 'sv' or 'en'
|
|
39
54
|
const locale = 'nb';
|
|
40
55
|
|
|
41
56
|
return (
|
|
42
|
-
<
|
|
57
|
+
<Providers locale={locale}>
|
|
43
58
|
<html lang={locale}>
|
|
44
59
|
<body>{children}</body>
|
|
45
60
|
</html>
|
|
46
|
-
</
|
|
61
|
+
</Providers>
|
|
47
62
|
)
|
|
48
63
|
}
|
|
49
64
|
```
|
|
50
65
|
|
|
51
66
|
See the [RAC internationalization docs](https://react-spectrum.adobe.com/react-aria/internationalization.html) for more information.
|
|
52
67
|
|
|
68
|
+
### Routing
|
|
69
|
+
|
|
70
|
+
When using compontents that include links from RAC (For example `Breadcrumbs`), the links will always treat the hrefs as external.
|
|
71
|
+
|
|
72
|
+
In order to avoid hard refreshing, you need to prop your router navigation-function
|
|
73
|
+
through `GrunnmurenProvider`. See the [RAC routing docs](https://react-spectrum.adobe.com/react-aria/routing.html)
|
|
74
|
+
|
|
75
|
+
In [Next.js](https://nextjs.org/) this is also done in the root [root layout](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required). In order to avoid making `RootLayout` a client component, you should import `GrunnmurenProvider` in a providers-file, that uses `"use client"`
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// app/providers.tsx
|
|
79
|
+
'use client'
|
|
80
|
+
import { GrunnmurenProvider } from '@obosbbl/grunnmuren-react';
|
|
81
|
+
import { useRouter } from 'next/navigation';
|
|
82
|
+
|
|
83
|
+
export function Providers({children, locale}: { children: React.ReactNode, locale: string}) {
|
|
84
|
+
let router = useRouter();
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<GrunnmurenProvider locale={locale} navigate={router.push}>
|
|
88
|
+
{children}
|
|
89
|
+
</GrunnmurenProvider>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The `RootLayout` file then looks exactly like it does in the previous step:
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
// app/layout.tsx
|
|
98
|
+
import {Providers} from "./providers";
|
|
99
|
+
|
|
100
|
+
export default function RootLayout({
|
|
101
|
+
children,
|
|
102
|
+
}: {
|
|
103
|
+
children: React.ReactNode
|
|
104
|
+
}) {
|
|
105
|
+
|
|
106
|
+
// Either 'nb', 'sv' or 'en'
|
|
107
|
+
const locale = 'nb';
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Providers locale={locale}>
|
|
111
|
+
<html lang={locale}>
|
|
112
|
+
<body>{children}</body>
|
|
113
|
+
</html>
|
|
114
|
+
</Providers>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
53
119
|
### Optimize bundle size by removing unused locales
|
|
54
120
|
|
|
55
121
|
React Aria Components has built in support for over 30 languages, most of which will be unused in your application. To optimize your applications bundle size, it is recommended to use React Aria's build plugin to remove all the unused locales. Here is a quick example for Next.js:
|
|
@@ -76,7 +142,7 @@ module.exports = {
|
|
|
76
142
|
optimizeLocales.webpack({
|
|
77
143
|
// If you have a multitenant app, include both Norwegian and Swedish
|
|
78
144
|
// If your app only serves one language, adjust accordingly
|
|
79
|
-
locales: ['nb
|
|
145
|
+
locales: ['nb', 'sv'],
|
|
80
146
|
}),
|
|
81
147
|
);
|
|
82
148
|
return config;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import { 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 } from 'react-aria-components';
|
|
2
|
-
export { ListBoxItemProps as ComboboxItemProps, Form,
|
|
1
|
+
import { 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, BreadcrumbProps as BreadcrumbProps$1, BreadcrumbsProps as BreadcrumbsProps$1 } from 'react-aria-components';
|
|
2
|
+
export { ListBoxItemProps as ComboboxItemProps, Form, ListBoxItemProps as SelectItemProps } from 'react-aria-components';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
4
|
import * as react from 'react';
|
|
4
5
|
import { HTMLProps } from 'react';
|
|
5
6
|
import { VariantProps } from 'cva';
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
type GrunnmurenProviderProps = {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
/**
|
|
11
|
+
* The locale to apply to the children.
|
|
12
|
+
* @default nb
|
|
13
|
+
*/
|
|
14
|
+
locale?: 'nb' | 'sv' | 'en';
|
|
15
|
+
/** The router to use for navigation */
|
|
16
|
+
navigate?: (path: string) => void;
|
|
17
|
+
};
|
|
18
|
+
declare function GrunnmurenProvider({ children, locale, navigate, }: GrunnmurenProviderProps): react_jsx_runtime.JSX.Element;
|
|
7
19
|
|
|
8
20
|
/**
|
|
9
21
|
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
@@ -105,7 +117,7 @@ declare const _Checkbox: react.ForwardRefExoticComponent<{
|
|
|
105
117
|
errorMessage?: React.ReactNode;
|
|
106
118
|
/** Additional style properties for the element. */
|
|
107
119
|
style?: react.CSSProperties | undefined;
|
|
108
|
-
} & Omit<CheckboxProps$1, "
|
|
120
|
+
} & Omit<CheckboxProps$1, "children" | "style" | "isDisabled" | "isIndeterminate" | "isReadOnly"> & react.RefAttributes<HTMLLabelElement>>;
|
|
109
121
|
|
|
110
122
|
type CheckboxGroupProps = {
|
|
111
123
|
children: React.ReactNode;
|
|
@@ -132,7 +144,7 @@ declare const _CheckboxGroup: react.ForwardRefExoticComponent<{
|
|
|
132
144
|
label?: React.ReactNode;
|
|
133
145
|
/** Additional style properties for the element. */
|
|
134
146
|
style?: react.CSSProperties | undefined;
|
|
135
|
-
} & Omit<CheckboxGroupProps$1, "
|
|
147
|
+
} & Omit<CheckboxGroupProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly" | "orientation"> & react.RefAttributes<HTMLDivElement>>;
|
|
136
148
|
|
|
137
149
|
declare const ListBoxItem: (props: ListBoxItemProps) => react_jsx_runtime.JSX.Element;
|
|
138
150
|
|
|
@@ -175,7 +187,7 @@ declare const _Combobox: react.ForwardRefExoticComponent<{
|
|
|
175
187
|
placeholder?: string | undefined;
|
|
176
188
|
/** Additional style properties for the element. */
|
|
177
189
|
style?: react.CSSProperties | undefined;
|
|
178
|
-
} & Omit<ComboBoxProps<object>, "
|
|
190
|
+
} & Omit<ComboBoxProps<object>, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLInputElement>>;
|
|
179
191
|
|
|
180
192
|
type RadioGroupProps = {
|
|
181
193
|
children: React.ReactNode;
|
|
@@ -202,7 +214,7 @@ declare const _RadioGroup: react.ForwardRefExoticComponent<{
|
|
|
202
214
|
label?: React.ReactNode;
|
|
203
215
|
/** Additional style properties for the element. */
|
|
204
216
|
style?: react.CSSProperties | undefined;
|
|
205
|
-
} & Omit<RadioGroupProps$1, "
|
|
217
|
+
} & Omit<RadioGroupProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly" | "orientation"> & react.RefAttributes<HTMLDivElement>>;
|
|
206
218
|
|
|
207
219
|
type RadioProps = {
|
|
208
220
|
children: React.ReactNode;
|
|
@@ -221,7 +233,7 @@ declare const _Radio: react.ForwardRefExoticComponent<{
|
|
|
221
233
|
description?: React.ReactNode;
|
|
222
234
|
/** Additional style properties for the element. */
|
|
223
235
|
style?: react.CSSProperties | undefined;
|
|
224
|
-
} & Omit<RadioProps$1, "
|
|
236
|
+
} & Omit<RadioProps$1, "children" | "style" | "isDisabled"> & react.RefAttributes<HTMLLabelElement>>;
|
|
225
237
|
|
|
226
238
|
type SelectProps<T extends object> = {
|
|
227
239
|
children: React.ReactNode;
|
|
@@ -252,7 +264,7 @@ declare const _Select: react.ForwardRefExoticComponent<{
|
|
|
252
264
|
placeholder?: string | undefined;
|
|
253
265
|
/** Additional style properties for the element. */
|
|
254
266
|
style?: react.CSSProperties | undefined;
|
|
255
|
-
} & Omit<SelectProps$1<object>, "
|
|
267
|
+
} & Omit<SelectProps$1<object>, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLButtonElement>>;
|
|
256
268
|
|
|
257
269
|
type TextAreaProps = {
|
|
258
270
|
/** Additional CSS className for the element. */
|
|
@@ -291,7 +303,7 @@ declare const _TextArea: react.ForwardRefExoticComponent<{
|
|
|
291
303
|
* @default 2
|
|
292
304
|
*/
|
|
293
305
|
rows?: number | undefined;
|
|
294
|
-
} & Omit<TextFieldProps$1, "
|
|
306
|
+
} & Omit<TextFieldProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLTextAreaElement>>;
|
|
295
307
|
|
|
296
308
|
type TextFieldProps = {
|
|
297
309
|
/** Additional CSS className for the element. */
|
|
@@ -342,7 +354,7 @@ declare const _TextField: react.ForwardRefExoticComponent<{
|
|
|
342
354
|
style?: react.CSSProperties | undefined;
|
|
343
355
|
/** Add a divider between the left/right addons and the input */
|
|
344
356
|
withAddonDivider?: boolean | undefined;
|
|
345
|
-
} & Omit<TextFieldProps$1, "
|
|
357
|
+
} & Omit<TextFieldProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLInputElement>>;
|
|
346
358
|
|
|
347
359
|
type NumberFieldProps = {
|
|
348
360
|
/** Additional CSS className for the element. */
|
|
@@ -393,7 +405,7 @@ declare const _NumberField: react.ForwardRefExoticComponent<{
|
|
|
393
405
|
style?: react.CSSProperties | undefined;
|
|
394
406
|
/** Add a divider between the left/right addons and the input */
|
|
395
407
|
withAddonDivider?: boolean | undefined;
|
|
396
|
-
} & Omit<NumberFieldProps$1, "
|
|
408
|
+
} & Omit<NumberFieldProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly" | "hideStepper"> & react.RefAttributes<HTMLInputElement>>;
|
|
397
409
|
|
|
398
410
|
declare const alertVariants: (props?: ({
|
|
399
411
|
variant?: "info" | "success" | "warning" | "danger" | undefined;
|
|
@@ -499,4 +511,34 @@ type FooterProps = HTMLProps<HTMLDivElement> & {
|
|
|
499
511
|
};
|
|
500
512
|
declare const Footer: (props: FooterProps) => react_jsx_runtime.JSX.Element;
|
|
501
513
|
|
|
502
|
-
|
|
514
|
+
type BreadcrumbProps = {
|
|
515
|
+
/** Additional CSS className for the element. */
|
|
516
|
+
className?: string;
|
|
517
|
+
/** Additional style properties for the element. */
|
|
518
|
+
style?: React.CSSProperties;
|
|
519
|
+
/** The URL to navigate to when clicking the breadcrumb. */
|
|
520
|
+
href?: string;
|
|
521
|
+
} & Omit<BreadcrumbProps$1, 'className' | 'style'>;
|
|
522
|
+
declare const _Breadcrumb: react.ForwardRefExoticComponent<{
|
|
523
|
+
/** Additional CSS className for the element. */
|
|
524
|
+
className?: string | undefined;
|
|
525
|
+
/** Additional style properties for the element. */
|
|
526
|
+
style?: react.CSSProperties | undefined;
|
|
527
|
+
/** The URL to navigate to when clicking the breadcrumb. */
|
|
528
|
+
href?: string | undefined;
|
|
529
|
+
} & Omit<BreadcrumbProps$1, "className" | "style"> & react.RefAttributes<HTMLLIElement>>;
|
|
530
|
+
|
|
531
|
+
type BreadcrumbsProps = {
|
|
532
|
+
/** Additional CSS className for the element. */
|
|
533
|
+
className?: string;
|
|
534
|
+
/** Additional style properties for the element. */
|
|
535
|
+
style?: React.CSSProperties;
|
|
536
|
+
} & Omit<BreadcrumbsProps$1<BreadcrumbProps>, 'className' | 'style'>;
|
|
537
|
+
declare const _Breadcrumbs: react.ForwardRefExoticComponent<{
|
|
538
|
+
/** Additional CSS className for the element. */
|
|
539
|
+
className?: string | undefined;
|
|
540
|
+
/** Additional style properties for the element. */
|
|
541
|
+
style?: react.CSSProperties | undefined;
|
|
542
|
+
} & Omit<BreadcrumbsProps$1<BreadcrumbProps>, "className" | "style"> & react.RefAttributes<HTMLOListElement>>;
|
|
543
|
+
|
|
544
|
+
export { Alertbox, type Props as AlertboxProps, _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, type ContentProps, Footer, type FooterProps, GrunnmurenProvider, type GrunnmurenProviderProps, Heading, type HeadingProps, _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 };
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,156 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export {
|
|
1
|
+
'use client';
|
|
2
|
+
import { I18nProvider, RouterProvider, Text, CheckboxContext, Checkbox as Checkbox$1, Label as Label$1, CheckboxGroup as CheckboxGroup$1, FieldError, ListBoxItem as ListBoxItem$1, ListBox as ListBox$1, ComboBox, Group, Input, Button as Button$1, 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, useLocale, Breadcrumbs as Breadcrumbs$1, Breadcrumb as Breadcrumb$1, Link } from 'react-aria-components';
|
|
3
|
+
export { Form } from 'react-aria-components';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
|
-
import { forwardRef, useId } from 'react';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
5
|
+
import { useLayoutEffect, forwardRef, useState, useRef, useId, Children } from 'react';
|
|
6
|
+
import { cva, cx, compose } from 'cva';
|
|
7
|
+
import { LoadingSpinner, Check, ChevronDown, Close, InfoCircle, CheckCircle, Warning, CloseCircle, ChevronRight } from '@obosbbl/grunnmuren-icons-react';
|
|
8
|
+
import { mergeRefs } from '@react-aria/utils';
|
|
9
|
+
|
|
10
|
+
function GrunnmurenProvider({ children, locale = 'nb', navigate }) {
|
|
11
|
+
return /*#__PURE__*/ jsx(I18nProvider, {
|
|
12
|
+
locale: locale,
|
|
13
|
+
children: navigate ? /*#__PURE__*/ jsx(RouterProvider, {
|
|
14
|
+
navigate: navigate,
|
|
15
|
+
children: children
|
|
16
|
+
}) : children
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const canUseDOM = ()=>{
|
|
21
|
+
return typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
|
22
|
+
};
|
|
23
|
+
const useClientLayoutEffect = canUseDOM() ? useLayoutEffect : ()=>{};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
27
|
+
*/ const buttonVariants = cva({
|
|
28
|
+
base: [
|
|
29
|
+
'inline-flex min-h-[44px] cursor-pointer items-center justify-center whitespace-nowrap rounded-lg font-medium transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2'
|
|
30
|
+
],
|
|
31
|
+
variants: {
|
|
32
|
+
/**
|
|
33
|
+
* The variant of the button
|
|
34
|
+
* @default primary
|
|
35
|
+
*/ variant: {
|
|
36
|
+
primary: 'no-underline',
|
|
37
|
+
// 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
|
|
38
|
+
secondary: 'no-underline shadow-[inset_0_0_0_2px]',
|
|
39
|
+
tertiary: 'underline hover:no-underline'
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* Adjusts the color of the button for usage on different backgrounds.
|
|
43
|
+
* @default green
|
|
44
|
+
*/ color: {
|
|
45
|
+
green: 'focus-visible:ring-black',
|
|
46
|
+
mint: 'focus-visible:ring-mint focus-visible:ring-offset-green-dark',
|
|
47
|
+
white: 'focus-visible:ring-white focus-visible:ring-offset-blue'
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* When the button is without text, but with a single icon.
|
|
51
|
+
* @default false
|
|
52
|
+
*/ isIconOnly: {
|
|
53
|
+
true: 'p-2 [&>svg]:h-7 [&>svg]:w-7',
|
|
54
|
+
false: 'gap-2.5 px-4 py-2'
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
compoundVariants: [
|
|
58
|
+
{
|
|
59
|
+
color: 'green',
|
|
60
|
+
variant: 'primary',
|
|
61
|
+
// Darken bg by 20% on hover. The color is manually crafted
|
|
62
|
+
className: 'bg-green text-white hover:bg-green-dark active:bg-[#007352]'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
color: 'green',
|
|
66
|
+
variant: 'secondary',
|
|
67
|
+
className: 'bg-white text-black shadow-green hover:bg-green hover:text-white active:bg-green'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
color: 'mint',
|
|
71
|
+
variant: 'primary',
|
|
72
|
+
// Darken bg by 20% on hover. The color is manually crafted
|
|
73
|
+
className: 'active:[#9ddac6] bg-mint text-black hover:bg-[#8dd4bd]'
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
color: 'mint',
|
|
77
|
+
variant: 'secondary',
|
|
78
|
+
className: 'text-mint shadow-mint hover:bg-mint hover:text-black'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
color: 'mint',
|
|
82
|
+
variant: 'tertiary',
|
|
83
|
+
className: 'text-mint'
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
color: 'white',
|
|
87
|
+
variant: 'primary',
|
|
88
|
+
className: 'bg-white text-black hover:bg-sky active:bg-sky-light'
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
color: 'white',
|
|
92
|
+
variant: 'secondary',
|
|
93
|
+
className: 'text-white shadow-white hover:bg-white hover:text-black'
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
color: 'white',
|
|
97
|
+
variant: 'tertiary',
|
|
98
|
+
className: 'text-white'
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
defaultVariants: {
|
|
102
|
+
variant: 'primary',
|
|
103
|
+
color: 'green',
|
|
104
|
+
isIconOnly: false
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
function Button(props, forwardedRef) {
|
|
108
|
+
const { children, className, color, isIconOnly, isLoading, variant, style, ...restProps } = props;
|
|
109
|
+
const [widthOverride, setWidthOverride] = useState();
|
|
110
|
+
const ownRef = useRef(null);
|
|
111
|
+
const ref = mergeRefs(ownRef, forwardedRef);
|
|
112
|
+
useClientLayoutEffect(()=>{
|
|
113
|
+
if (isLoading) {
|
|
114
|
+
const requestID = window.requestAnimationFrame(()=>{
|
|
115
|
+
setWidthOverride(ownRef.current?.getBoundingClientRect()?.width);
|
|
116
|
+
});
|
|
117
|
+
return ()=>{
|
|
118
|
+
setWidthOverride(undefined);
|
|
119
|
+
cancelAnimationFrame(requestID);
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}, [
|
|
123
|
+
isLoading,
|
|
124
|
+
children
|
|
125
|
+
]);
|
|
126
|
+
let Component = 'a';
|
|
127
|
+
if (props.href == null) {
|
|
128
|
+
// If we don't have a href, it's a button, and we add a fallback type button to prevent the button from accidentally submitting when in a form
|
|
129
|
+
Component = 'button';
|
|
130
|
+
restProps.type ??= 'button';
|
|
131
|
+
}
|
|
132
|
+
return(// @ts-expect-error TS doesn't agree here taht restProps is safe to spread, because restProps for anchors aren't type compatible with restProps for buttons, but that should be okay here
|
|
133
|
+
/*#__PURE__*/ jsx(Component, {
|
|
134
|
+
"aria-busy": isLoading ? true : undefined,
|
|
135
|
+
className: buttonVariants({
|
|
136
|
+
className,
|
|
137
|
+
color,
|
|
138
|
+
isIconOnly,
|
|
139
|
+
variant
|
|
140
|
+
}),
|
|
141
|
+
ref: ref,
|
|
142
|
+
style: {
|
|
143
|
+
...style,
|
|
144
|
+
width: widthOverride
|
|
145
|
+
},
|
|
146
|
+
...restProps,
|
|
147
|
+
children: widthOverride ? // remove margin for icon alignment
|
|
148
|
+
/*#__PURE__*/ jsx(LoadingSpinner, {
|
|
149
|
+
className: "!m-0 mx-auto animate-spin"
|
|
150
|
+
}) : children
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
const _Button = /*#__PURE__*/ forwardRef(Button);
|
|
9
154
|
|
|
10
155
|
const formField = cx('group flex flex-col gap-2');
|
|
11
156
|
const formFieldError = cx('w-fit rounded-sm bg-red-light px-2 py-1 text-sm leading-6 text-red');
|
|
@@ -34,7 +179,7 @@ const input = cva({
|
|
|
34
179
|
}
|
|
35
180
|
});
|
|
36
181
|
const inputGroup = cx([
|
|
37
|
-
'inline-flex items-center gap-3 overflow-hidden rounded-md px-3 text-sm ring-1 ring-black focus-within:ring-2',
|
|
182
|
+
'inline-flex items-center gap-3 overflow-hidden rounded-md bg-white px-3 text-sm ring-1 ring-black focus-within:ring-2',
|
|
38
183
|
'group-data-[invalid]:ring-2 group-data-[invalid]:ring-red group-data-[invalid]:focus-within:ring',
|
|
39
184
|
// Make sure icons are the correct size
|
|
40
185
|
'[&_svg]:text-base'
|
|
@@ -236,7 +381,7 @@ function Combobox(props, ref) {
|
|
|
236
381
|
}),
|
|
237
382
|
ref: ref
|
|
238
383
|
}),
|
|
239
|
-
/*#__PURE__*/ jsx(Button, {
|
|
384
|
+
/*#__PURE__*/ jsx(Button$1, {
|
|
240
385
|
children: isLoading ? /*#__PURE__*/ jsx(LoadingSpinner, {
|
|
241
386
|
className: "animate-spin"
|
|
242
387
|
}) : /*#__PURE__*/ jsx(ChevronDown, {
|
|
@@ -346,7 +491,7 @@ function Select(props, ref) {
|
|
|
346
491
|
description && /*#__PURE__*/ jsx(Description, {
|
|
347
492
|
children: description
|
|
348
493
|
}),
|
|
349
|
-
/*#__PURE__*/ jsxs(Button, {
|
|
494
|
+
/*#__PURE__*/ jsxs(Button$1, {
|
|
350
495
|
className: cx(input({
|
|
351
496
|
focusModifier: 'visible'
|
|
352
497
|
}), // How to reuse placeholder text?
|
|
@@ -508,6 +653,118 @@ function NumberField(props, ref) {
|
|
|
508
653
|
}
|
|
509
654
|
const _NumberField = /*#__PURE__*/ forwardRef(NumberField);
|
|
510
655
|
|
|
656
|
+
// TODO: add new icons
|
|
657
|
+
const iconMap = {
|
|
658
|
+
info: InfoCircle,
|
|
659
|
+
success: CheckCircle,
|
|
660
|
+
warning: Warning,
|
|
661
|
+
danger: CloseCircle
|
|
662
|
+
};
|
|
663
|
+
const alertVariants = cva({
|
|
664
|
+
base: [
|
|
665
|
+
'grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-md border-2 px-3 py-2',
|
|
666
|
+
// Heading styles:
|
|
667
|
+
'[&_[data-slot="heading"]]:text-base [&_[data-slot="heading"]]:font-medium [&_[data-slot="heading"]]:leading-7',
|
|
668
|
+
// Content styles:
|
|
669
|
+
'[&:has([data-slot="heading"])_[data-slot="content"]]:col-span-full [&_[data-slot="content"]]:text-sm [&_[data-slot="content"]]:leading-6',
|
|
670
|
+
// Footer styles:
|
|
671
|
+
'[&_[data-slot="footer"]]:col-span-full [&_[data-slot="footer"]]:text-xs [&_[data-slot="footer"]]:font-light [&_[data-slot="footer"]]:leading-6'
|
|
672
|
+
],
|
|
673
|
+
variants: {
|
|
674
|
+
/**
|
|
675
|
+
* The variant of the alert
|
|
676
|
+
* @default info
|
|
677
|
+
*/ variant: {
|
|
678
|
+
info: 'border-[#1A7FA7] bg-sky-light',
|
|
679
|
+
success: 'border-[#0F9B6E] bg-mint-light',
|
|
680
|
+
warning: 'border-[#C57C13] bg-[#FFF2DE]',
|
|
681
|
+
danger: 'border-[#C0385D] bg-red-light'
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
defaultVariants: {
|
|
685
|
+
variant: 'info'
|
|
686
|
+
}
|
|
687
|
+
});
|
|
688
|
+
const translations = {
|
|
689
|
+
close: {
|
|
690
|
+
nb: 'Lukk',
|
|
691
|
+
sv: 'Stäng',
|
|
692
|
+
en: 'Close'
|
|
693
|
+
},
|
|
694
|
+
showMore: {
|
|
695
|
+
nb: 'Les mer',
|
|
696
|
+
sv: 'Läs mer',
|
|
697
|
+
en: 'Read more'
|
|
698
|
+
},
|
|
699
|
+
showLess: {
|
|
700
|
+
nb: 'Vis mindre',
|
|
701
|
+
sv: 'Dölj',
|
|
702
|
+
en: 'Show less'
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
const Alertbox = ({ children, role, className, variant = 'info', isDismissable = false, isDismissed, onDismiss, isExpandable })=>{
|
|
706
|
+
const Icon = iconMap[variant];
|
|
707
|
+
const { locale } = useLocale();
|
|
708
|
+
const id = useId();
|
|
709
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
710
|
+
const isCollapsed = isExpandable && !isExpanded;
|
|
711
|
+
const [isUncontrolledVisible, setIsUncontrolledVisible] = useState(true);
|
|
712
|
+
const isVisible = isDismissed !== undefined ? !isDismissed : isUncontrolledVisible;
|
|
713
|
+
if (!isVisible) return;
|
|
714
|
+
const close = ()=>{
|
|
715
|
+
setIsUncontrolledVisible(false);
|
|
716
|
+
if (onDismiss) onDismiss();
|
|
717
|
+
};
|
|
718
|
+
const isInDevMode = process.env.NODE_ENV !== 'production';
|
|
719
|
+
if (isInDevMode && onDismiss && !isDismissable) {
|
|
720
|
+
console.warn('Passing an `onDismiss` callback without setting the `isDismissable` prop to `true` will not have any effect.');
|
|
721
|
+
}
|
|
722
|
+
if (isInDevMode && !children) {
|
|
723
|
+
console.error('`No children was passed to the <AlertBox/>` component.');
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const [firstChild, ...restChildren] = Children.toArray(children);
|
|
727
|
+
const lastChild = restChildren.pop();
|
|
728
|
+
return /*#__PURE__*/ jsxs("div", {
|
|
729
|
+
className: alertVariants({
|
|
730
|
+
className,
|
|
731
|
+
variant
|
|
732
|
+
}),
|
|
733
|
+
// The role prop is required to force consumers to consider and choose the appropriate alertbox role.
|
|
734
|
+
// role="none" will not have any effect on a div, so it can be omitted.
|
|
735
|
+
role: role === 'none' ? undefined : role,
|
|
736
|
+
children: [
|
|
737
|
+
/*#__PURE__*/ jsx(Icon, {}),
|
|
738
|
+
firstChild,
|
|
739
|
+
isDismissable && /*#__PURE__*/ jsx("button", {
|
|
740
|
+
className: cx('-m-2 grid h-11 w-11 place-items-center rounded-xl', 'focus:outline-none focus:-outline-offset-8 focus:outline-black'),
|
|
741
|
+
onClick: close,
|
|
742
|
+
"aria-label": translations.close[locale],
|
|
743
|
+
children: /*#__PURE__*/ jsx(Close, {})
|
|
744
|
+
}),
|
|
745
|
+
isExpandable && /*#__PURE__*/ jsxs("button", {
|
|
746
|
+
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:
|
|
747
|
+
'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:after:h-[1px] focus:after:bg-black'),
|
|
748
|
+
onClick: ()=>setIsExpanded((prevState)=>!prevState),
|
|
749
|
+
"aria-expanded": isExpanded,
|
|
750
|
+
"aria-controls": id,
|
|
751
|
+
children: [
|
|
752
|
+
isExpanded ? translations.showLess[locale] : translations.showMore[locale],
|
|
753
|
+
/*#__PURE__*/ jsx(ChevronDown, {
|
|
754
|
+
className: cx('transition-transform duration-150 motion-reduce:transition-none', isExpanded && 'rotate-180')
|
|
755
|
+
})
|
|
756
|
+
]
|
|
757
|
+
}),
|
|
758
|
+
!isCollapsed && restChildren.length > 0 && /*#__PURE__*/ jsx("div", {
|
|
759
|
+
className: "col-span-full grid gap-y-4",
|
|
760
|
+
id: id,
|
|
761
|
+
children: restChildren
|
|
762
|
+
}),
|
|
763
|
+
lastChild
|
|
764
|
+
]
|
|
765
|
+
});
|
|
766
|
+
};
|
|
767
|
+
|
|
511
768
|
const Heading = ({ level, ...restProps })=>{
|
|
512
769
|
const Heading = `h${level}`;
|
|
513
770
|
return /*#__PURE__*/ jsx(Heading, {
|
|
@@ -524,4 +781,35 @@ const Footer = (props)=>/*#__PURE__*/ jsx("div", {
|
|
|
524
781
|
"data-slot": "footer"
|
|
525
782
|
});
|
|
526
783
|
|
|
527
|
-
|
|
784
|
+
function Breadcrumbs(props, ref) {
|
|
785
|
+
const { className, children, ...restProps } = props;
|
|
786
|
+
return /*#__PURE__*/ jsx(Breadcrumbs$1, {
|
|
787
|
+
...restProps,
|
|
788
|
+
className: cx(className, 'flex flex-wrap text-sm leading-6'),
|
|
789
|
+
ref: ref,
|
|
790
|
+
children: children
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
const _Breadcrumbs = /*#__PURE__*/ forwardRef(Breadcrumbs);
|
|
794
|
+
|
|
795
|
+
function Breadcrumb(props, ref) {
|
|
796
|
+
const { className, children, href, ...restProps } = props;
|
|
797
|
+
return /*#__PURE__*/ jsxs(Breadcrumb$1, {
|
|
798
|
+
className: cx(className, 'group flex items-center'),
|
|
799
|
+
...restProps,
|
|
800
|
+
ref: ref,
|
|
801
|
+
children: [
|
|
802
|
+
href ? /*#__PURE__*/ jsx(Link, {
|
|
803
|
+
href: href,
|
|
804
|
+
className: "group-last:no-underline",
|
|
805
|
+
children: children
|
|
806
|
+
}) : children,
|
|
807
|
+
/*#__PURE__*/ jsx(ChevronRight, {
|
|
808
|
+
className: "px-1 group-last:hidden"
|
|
809
|
+
})
|
|
810
|
+
]
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
const _Breadcrumb = /*#__PURE__*/ forwardRef(Breadcrumb);
|
|
814
|
+
|
|
815
|
+
export { Alertbox, _Breadcrumb as Breadcrumb, _Breadcrumbs as Breadcrumbs, _Button as Button, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, _Combobox as Combobox, ListBoxItem as ComboboxItem, Content, Footer, GrunnmurenProvider, Heading, _NumberField as NumberField, _Radio as Radio, _RadioGroup as RadioGroup, _Select as Select, ListBoxItem as SelectItem, _TextArea as TextArea, _TextField as TextField };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obosbbl/grunnmuren-react",
|
|
3
|
-
"version": "2.0.0-canary.
|
|
3
|
+
"version": "2.0.0-canary.15",
|
|
4
4
|
"description": "Grunnmuren components in React",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/code-obos/grunnmuren"
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@obosbbl/grunnmuren-icons-react": "^2.0.0-canary.1",
|
|
22
|
-
"@react-aria/utils": "^3.23.
|
|
22
|
+
"@react-aria/utils": "^3.23.2",
|
|
23
23
|
"@types/node": "^20.11.19",
|
|
24
24
|
"cva": "1.0.0-beta.1",
|
|
25
|
-
"react-aria-components": "^1.
|
|
25
|
+
"react-aria-components": "^1.1.1"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": "^18"
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { useId, useState, Children } from 'react';
|
|
4
|
-
import { cva, cx } from 'cva';
|
|
5
|
-
import { useLocale } from 'react-aria-components';
|
|
6
|
-
import { Close, ChevronDown, InfoCircle, CheckCircle, Warning, CloseCircle } from '@obosbbl/grunnmuren-icons-react';
|
|
7
|
-
|
|
8
|
-
// TODO: add new icons
|
|
9
|
-
const iconMap = {
|
|
10
|
-
info: InfoCircle,
|
|
11
|
-
success: CheckCircle,
|
|
12
|
-
warning: Warning,
|
|
13
|
-
danger: CloseCircle
|
|
14
|
-
};
|
|
15
|
-
const alertVariants = cva({
|
|
16
|
-
base: [
|
|
17
|
-
'grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded-md border-2 px-3 py-2',
|
|
18
|
-
// Heading styles:
|
|
19
|
-
'[&_[data-slot="heading"]]:text-base [&_[data-slot="heading"]]:font-medium [&_[data-slot="heading"]]:leading-7',
|
|
20
|
-
// Content styles:
|
|
21
|
-
'[&:has([data-slot="heading"])_[data-slot="content"]]:col-span-full [&_[data-slot="content"]]:text-sm [&_[data-slot="content"]]:leading-6',
|
|
22
|
-
// Footer styles:
|
|
23
|
-
'[&_[data-slot="footer"]]:col-span-full [&_[data-slot="footer"]]:text-xs [&_[data-slot="footer"]]:font-light [&_[data-slot="footer"]]:leading-6'
|
|
24
|
-
],
|
|
25
|
-
variants: {
|
|
26
|
-
/**
|
|
27
|
-
* The variant of the alert
|
|
28
|
-
* @default info
|
|
29
|
-
*/ variant: {
|
|
30
|
-
info: 'border-[#1A7FA7] bg-sky-light',
|
|
31
|
-
success: 'border-[#0F9B6E] bg-mint-light',
|
|
32
|
-
warning: 'border-[#C57C13] bg-[#FFF2DE]',
|
|
33
|
-
danger: 'border-[#C0385D] bg-red-light'
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
defaultVariants: {
|
|
37
|
-
variant: 'info'
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
const translations = {
|
|
41
|
-
close: {
|
|
42
|
-
nb: 'Lukk',
|
|
43
|
-
sv: 'Stäng',
|
|
44
|
-
en: 'Close'
|
|
45
|
-
},
|
|
46
|
-
showMore: {
|
|
47
|
-
nb: 'Les mer',
|
|
48
|
-
sv: 'Läs mer',
|
|
49
|
-
en: 'Read more'
|
|
50
|
-
},
|
|
51
|
-
showLess: {
|
|
52
|
-
nb: 'Vis mindre',
|
|
53
|
-
sv: 'Dölj',
|
|
54
|
-
en: 'Show less'
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
const Alertbox = ({ children, role, className, variant = 'info', isDismissable = false, isDismissed, onDismiss, isExpandable })=>{
|
|
58
|
-
const Icon = iconMap[variant];
|
|
59
|
-
const { locale } = useLocale();
|
|
60
|
-
const id = useId();
|
|
61
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
62
|
-
const isCollapsed = isExpandable && !isExpanded;
|
|
63
|
-
const [isUncontrolledVisible, setIsUncontrolledVisible] = useState(true);
|
|
64
|
-
const isVisible = isDismissed !== undefined ? !isDismissed : isUncontrolledVisible;
|
|
65
|
-
if (!isVisible) return;
|
|
66
|
-
const close = ()=>{
|
|
67
|
-
setIsUncontrolledVisible(false);
|
|
68
|
-
if (onDismiss) onDismiss();
|
|
69
|
-
};
|
|
70
|
-
const isInDevMode = process.env.NODE_ENV !== 'production';
|
|
71
|
-
if (isInDevMode && onDismiss && !isDismissable) {
|
|
72
|
-
console.warn('Passing an `onDismiss` callback without setting the `isDismissable` prop to `true` will not have any effect.');
|
|
73
|
-
}
|
|
74
|
-
if (isInDevMode && !children) {
|
|
75
|
-
console.error('`No children was passed to the <AlertBox/>` component.');
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const [firstChild, ...restChildren] = Children.toArray(children);
|
|
79
|
-
const lastChild = restChildren.pop();
|
|
80
|
-
return /*#__PURE__*/ jsxs("div", {
|
|
81
|
-
className: alertVariants({
|
|
82
|
-
className,
|
|
83
|
-
variant
|
|
84
|
-
}),
|
|
85
|
-
// The role prop is required to force consumers to consider and choose the appropriate alertbox role.
|
|
86
|
-
// role="none" will not have any effect on a div, so it can be omitted.
|
|
87
|
-
role: role === 'none' ? undefined : role,
|
|
88
|
-
children: [
|
|
89
|
-
/*#__PURE__*/ jsx(Icon, {}),
|
|
90
|
-
firstChild,
|
|
91
|
-
isDismissable && /*#__PURE__*/ jsx("button", {
|
|
92
|
-
className: cx('-m-2 grid h-11 w-11 place-items-center rounded-xl', 'focus:outline-none focus:-outline-offset-8 focus:outline-black'),
|
|
93
|
-
onClick: close,
|
|
94
|
-
"aria-label": translations.close[locale],
|
|
95
|
-
children: /*#__PURE__*/ jsx(Close, {})
|
|
96
|
-
}),
|
|
97
|
-
isExpandable && /*#__PURE__*/ jsxs("button", {
|
|
98
|
-
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:
|
|
99
|
-
'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:after:h-[1px] focus:after:bg-black'),
|
|
100
|
-
onClick: ()=>setIsExpanded((prevState)=>!prevState),
|
|
101
|
-
"aria-expanded": isExpanded,
|
|
102
|
-
"aria-controls": id,
|
|
103
|
-
children: [
|
|
104
|
-
isExpanded ? translations.showLess[locale] : translations.showMore[locale],
|
|
105
|
-
/*#__PURE__*/ jsx(ChevronDown, {
|
|
106
|
-
className: cx('transition-transform duration-150 motion-reduce:transition-none', isExpanded && 'rotate-180')
|
|
107
|
-
})
|
|
108
|
-
]
|
|
109
|
-
}),
|
|
110
|
-
!isCollapsed && restChildren.length > 0 && /*#__PURE__*/ jsx("div", {
|
|
111
|
-
className: "col-span-full grid gap-y-4",
|
|
112
|
-
id: id,
|
|
113
|
-
children: restChildren
|
|
114
|
-
}),
|
|
115
|
-
lastChild
|
|
116
|
-
]
|
|
117
|
-
});
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
export { Alertbox as A };
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { useLayoutEffect, forwardRef, useState, useRef } from 'react';
|
|
4
|
-
import { cva } from 'cva';
|
|
5
|
-
import { LoadingSpinner } from '@obosbbl/grunnmuren-icons-react';
|
|
6
|
-
import { mergeRefs } from '@react-aria/utils';
|
|
7
|
-
|
|
8
|
-
const canUseDOM = ()=>{
|
|
9
|
-
return typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
|
10
|
-
};
|
|
11
|
-
const useClientLayoutEffect = canUseDOM() ? useLayoutEffect : ()=>{};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
15
|
-
*/ const buttonVariants = cva({
|
|
16
|
-
base: [
|
|
17
|
-
'inline-flex min-h-[44px] cursor-pointer items-center justify-center whitespace-nowrap rounded-lg font-medium transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2'
|
|
18
|
-
],
|
|
19
|
-
variants: {
|
|
20
|
-
/**
|
|
21
|
-
* The variant of the button
|
|
22
|
-
* @default primary
|
|
23
|
-
*/ variant: {
|
|
24
|
-
primary: 'no-underline',
|
|
25
|
-
// 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
|
|
26
|
-
secondary: 'no-underline shadow-[inset_0_0_0_2px]',
|
|
27
|
-
tertiary: 'underline hover:no-underline'
|
|
28
|
-
},
|
|
29
|
-
/**
|
|
30
|
-
* Adjusts the color of the button for usage on different backgrounds.
|
|
31
|
-
* @default green
|
|
32
|
-
*/ color: {
|
|
33
|
-
green: 'focus-visible:ring-black',
|
|
34
|
-
mint: 'focus-visible:ring-mint focus-visible:ring-offset-green-dark',
|
|
35
|
-
white: 'focus-visible:ring-white focus-visible:ring-offset-blue'
|
|
36
|
-
},
|
|
37
|
-
/**
|
|
38
|
-
* When the button is without text, but with a single icon.
|
|
39
|
-
* @default false
|
|
40
|
-
*/ isIconOnly: {
|
|
41
|
-
true: 'p-2 [&>svg]:h-7 [&>svg]:w-7',
|
|
42
|
-
false: 'gap-2.5 px-4 py-2'
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
compoundVariants: [
|
|
46
|
-
{
|
|
47
|
-
color: 'green',
|
|
48
|
-
variant: 'primary',
|
|
49
|
-
// Darken bg by 20% on hover. The color is manually crafted
|
|
50
|
-
className: 'bg-green text-white hover:bg-green-dark active:bg-[#007352]'
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
color: 'green',
|
|
54
|
-
variant: 'secondary',
|
|
55
|
-
className: 'bg-white text-black shadow-green hover:bg-green hover:text-white active:bg-green'
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
color: 'mint',
|
|
59
|
-
variant: 'primary',
|
|
60
|
-
// Darken bg by 20% on hover. The color is manually crafted
|
|
61
|
-
className: 'active:[#9ddac6] bg-mint text-black hover:bg-[#8dd4bd]'
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
color: 'mint',
|
|
65
|
-
variant: 'secondary',
|
|
66
|
-
className: 'text-mint shadow-mint hover:bg-mint hover:text-black'
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
color: 'mint',
|
|
70
|
-
variant: 'tertiary',
|
|
71
|
-
className: 'text-mint'
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
color: 'white',
|
|
75
|
-
variant: 'primary',
|
|
76
|
-
className: 'bg-white text-black hover:bg-sky active:bg-sky-light'
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
color: 'white',
|
|
80
|
-
variant: 'secondary',
|
|
81
|
-
className: 'text-white shadow-white hover:bg-white hover:text-black'
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
color: 'white',
|
|
85
|
-
variant: 'tertiary',
|
|
86
|
-
className: 'text-white'
|
|
87
|
-
}
|
|
88
|
-
],
|
|
89
|
-
defaultVariants: {
|
|
90
|
-
variant: 'primary',
|
|
91
|
-
color: 'green',
|
|
92
|
-
isIconOnly: false
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
function Button(props, forwardedRef) {
|
|
96
|
-
const { children, className, color, isIconOnly, isLoading, variant, style, ...restProps } = props;
|
|
97
|
-
const [widthOverride, setWidthOverride] = useState();
|
|
98
|
-
const ownRef = useRef(null);
|
|
99
|
-
const ref = mergeRefs(ownRef, forwardedRef);
|
|
100
|
-
useClientLayoutEffect(()=>{
|
|
101
|
-
if (isLoading) {
|
|
102
|
-
const requestID = window.requestAnimationFrame(()=>{
|
|
103
|
-
setWidthOverride(ownRef.current?.getBoundingClientRect()?.width);
|
|
104
|
-
});
|
|
105
|
-
return ()=>{
|
|
106
|
-
setWidthOverride(undefined);
|
|
107
|
-
cancelAnimationFrame(requestID);
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
}, [
|
|
111
|
-
isLoading,
|
|
112
|
-
children
|
|
113
|
-
]);
|
|
114
|
-
let Component = 'a';
|
|
115
|
-
if (props.href == null) {
|
|
116
|
-
// If we don't have a href, it's a button, and we add a fallback type button to prevent the button from accidentally submitting when in a form
|
|
117
|
-
Component = 'button';
|
|
118
|
-
restProps.type ??= 'button';
|
|
119
|
-
}
|
|
120
|
-
return(// @ts-expect-error TS doesn't agree here taht restProps is safe to spread, because restProps for anchors aren't type compatible with restProps for buttons, but that should be okay here
|
|
121
|
-
/*#__PURE__*/ jsx(Component, {
|
|
122
|
-
"aria-busy": isLoading ? true : undefined,
|
|
123
|
-
className: buttonVariants({
|
|
124
|
-
className,
|
|
125
|
-
color,
|
|
126
|
-
isIconOnly,
|
|
127
|
-
variant
|
|
128
|
-
}),
|
|
129
|
-
ref: ref,
|
|
130
|
-
style: {
|
|
131
|
-
...style,
|
|
132
|
-
width: widthOverride
|
|
133
|
-
},
|
|
134
|
-
...restProps,
|
|
135
|
-
children: widthOverride ? // remove margin for icon alignment
|
|
136
|
-
/*#__PURE__*/ jsx(LoadingSpinner, {
|
|
137
|
-
className: "!m-0 mx-auto animate-spin"
|
|
138
|
-
}) : children
|
|
139
|
-
}));
|
|
140
|
-
}
|
|
141
|
-
const _Button = /*#__PURE__*/ forwardRef(Button);
|
|
142
|
-
|
|
143
|
-
export { _Button as _ };
|