@obosbbl/grunnmuren-react 2.0.0-canary.12 → 2.0.0-canary.14
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 +160 -13
- package/dist/index.mjs +314 -9
- package/package.json +4 -3
- 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,8 +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';
|
|
5
|
+
import { HTMLProps } from 'react';
|
|
4
6
|
import { VariantProps } from 'cva';
|
|
5
|
-
|
|
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;
|
|
6
19
|
|
|
7
20
|
/**
|
|
8
21
|
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
@@ -104,7 +117,7 @@ declare const _Checkbox: react.ForwardRefExoticComponent<{
|
|
|
104
117
|
errorMessage?: React.ReactNode;
|
|
105
118
|
/** Additional style properties for the element. */
|
|
106
119
|
style?: react.CSSProperties | undefined;
|
|
107
|
-
} & Omit<CheckboxProps$1, "
|
|
120
|
+
} & Omit<CheckboxProps$1, "children" | "style" | "isDisabled" | "isIndeterminate" | "isReadOnly"> & react.RefAttributes<HTMLLabelElement>>;
|
|
108
121
|
|
|
109
122
|
type CheckboxGroupProps = {
|
|
110
123
|
children: React.ReactNode;
|
|
@@ -131,7 +144,7 @@ declare const _CheckboxGroup: react.ForwardRefExoticComponent<{
|
|
|
131
144
|
label?: React.ReactNode;
|
|
132
145
|
/** Additional style properties for the element. */
|
|
133
146
|
style?: react.CSSProperties | undefined;
|
|
134
|
-
} & Omit<CheckboxGroupProps$1, "
|
|
147
|
+
} & Omit<CheckboxGroupProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly" | "orientation"> & react.RefAttributes<HTMLDivElement>>;
|
|
135
148
|
|
|
136
149
|
declare const ListBoxItem: (props: ListBoxItemProps) => react_jsx_runtime.JSX.Element;
|
|
137
150
|
|
|
@@ -174,7 +187,7 @@ declare const _Combobox: react.ForwardRefExoticComponent<{
|
|
|
174
187
|
placeholder?: string | undefined;
|
|
175
188
|
/** Additional style properties for the element. */
|
|
176
189
|
style?: react.CSSProperties | undefined;
|
|
177
|
-
} & Omit<ComboBoxProps<object>, "
|
|
190
|
+
} & Omit<ComboBoxProps<object>, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLInputElement>>;
|
|
178
191
|
|
|
179
192
|
type RadioGroupProps = {
|
|
180
193
|
children: React.ReactNode;
|
|
@@ -201,7 +214,7 @@ declare const _RadioGroup: react.ForwardRefExoticComponent<{
|
|
|
201
214
|
label?: React.ReactNode;
|
|
202
215
|
/** Additional style properties for the element. */
|
|
203
216
|
style?: react.CSSProperties | undefined;
|
|
204
|
-
} & Omit<RadioGroupProps$1, "
|
|
217
|
+
} & Omit<RadioGroupProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly" | "orientation"> & react.RefAttributes<HTMLDivElement>>;
|
|
205
218
|
|
|
206
219
|
type RadioProps = {
|
|
207
220
|
children: React.ReactNode;
|
|
@@ -220,7 +233,7 @@ declare const _Radio: react.ForwardRefExoticComponent<{
|
|
|
220
233
|
description?: React.ReactNode;
|
|
221
234
|
/** Additional style properties for the element. */
|
|
222
235
|
style?: react.CSSProperties | undefined;
|
|
223
|
-
} & Omit<RadioProps$1, "
|
|
236
|
+
} & Omit<RadioProps$1, "children" | "style" | "isDisabled"> & react.RefAttributes<HTMLLabelElement>>;
|
|
224
237
|
|
|
225
238
|
type SelectProps<T extends object> = {
|
|
226
239
|
children: React.ReactNode;
|
|
@@ -251,7 +264,7 @@ declare const _Select: react.ForwardRefExoticComponent<{
|
|
|
251
264
|
placeholder?: string | undefined;
|
|
252
265
|
/** Additional style properties for the element. */
|
|
253
266
|
style?: react.CSSProperties | undefined;
|
|
254
|
-
} & Omit<SelectProps$1<object>, "
|
|
267
|
+
} & Omit<SelectProps$1<object>, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLButtonElement>>;
|
|
255
268
|
|
|
256
269
|
type TextAreaProps = {
|
|
257
270
|
/** Additional CSS className for the element. */
|
|
@@ -290,7 +303,7 @@ declare const _TextArea: react.ForwardRefExoticComponent<{
|
|
|
290
303
|
* @default 2
|
|
291
304
|
*/
|
|
292
305
|
rows?: number | undefined;
|
|
293
|
-
} & Omit<TextFieldProps$1, "
|
|
306
|
+
} & Omit<TextFieldProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLTextAreaElement>>;
|
|
294
307
|
|
|
295
308
|
type TextFieldProps = {
|
|
296
309
|
/** Additional CSS className for the element. */
|
|
@@ -341,7 +354,7 @@ declare const _TextField: react.ForwardRefExoticComponent<{
|
|
|
341
354
|
style?: react.CSSProperties | undefined;
|
|
342
355
|
/** Add a divider between the left/right addons and the input */
|
|
343
356
|
withAddonDivider?: boolean | undefined;
|
|
344
|
-
} & Omit<TextFieldProps$1, "
|
|
357
|
+
} & Omit<TextFieldProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly"> & react.RefAttributes<HTMLInputElement>>;
|
|
345
358
|
|
|
346
359
|
type NumberFieldProps = {
|
|
347
360
|
/** Additional CSS className for the element. */
|
|
@@ -392,6 +405,140 @@ declare const _NumberField: react.ForwardRefExoticComponent<{
|
|
|
392
405
|
style?: react.CSSProperties | undefined;
|
|
393
406
|
/** Add a divider between the left/right addons and the input */
|
|
394
407
|
withAddonDivider?: boolean | undefined;
|
|
395
|
-
} & Omit<NumberFieldProps$1, "
|
|
408
|
+
} & Omit<NumberFieldProps$1, "children" | "className" | "style" | "isDisabled" | "isReadOnly" | "hideStepper"> & react.RefAttributes<HTMLInputElement>>;
|
|
409
|
+
|
|
410
|
+
declare const alertVariants: (props?: ({
|
|
411
|
+
variant?: "info" | "success" | "warning" | "danger" | undefined;
|
|
412
|
+
} & ({
|
|
413
|
+
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 | {
|
|
414
|
+
[x: string]: any;
|
|
415
|
+
} | null | undefined)[] | {
|
|
416
|
+
[x: string]: any;
|
|
417
|
+
} | null | undefined)[] | {
|
|
418
|
+
[x: string]: any;
|
|
419
|
+
} | null | undefined)[] | {
|
|
420
|
+
[x: string]: any;
|
|
421
|
+
} | null | undefined)[] | {
|
|
422
|
+
[x: string]: any;
|
|
423
|
+
} | null | undefined)[] | {
|
|
424
|
+
[x: string]: any;
|
|
425
|
+
} | null | undefined)[] | {
|
|
426
|
+
[x: string]: any;
|
|
427
|
+
} | null | undefined)[] | {
|
|
428
|
+
[x: string]: any;
|
|
429
|
+
} | null | undefined)[] | {
|
|
430
|
+
[x: string]: any;
|
|
431
|
+
} | null | undefined)[] | {
|
|
432
|
+
[x: string]: any;
|
|
433
|
+
} | null | undefined)[] | {
|
|
434
|
+
[x: string]: any;
|
|
435
|
+
} | null | undefined)[] | {
|
|
436
|
+
[x: string]: any;
|
|
437
|
+
} | null | undefined;
|
|
438
|
+
className?: undefined;
|
|
439
|
+
} | {
|
|
440
|
+
class?: undefined;
|
|
441
|
+
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 | {
|
|
442
|
+
[x: string]: any;
|
|
443
|
+
} | null | undefined)[] | {
|
|
444
|
+
[x: string]: any;
|
|
445
|
+
} | null | undefined)[] | {
|
|
446
|
+
[x: string]: any;
|
|
447
|
+
} | null | undefined)[] | {
|
|
448
|
+
[x: string]: any;
|
|
449
|
+
} | null | undefined)[] | {
|
|
450
|
+
[x: string]: any;
|
|
451
|
+
} | null | undefined)[] | {
|
|
452
|
+
[x: string]: any;
|
|
453
|
+
} | null | undefined)[] | {
|
|
454
|
+
[x: string]: any;
|
|
455
|
+
} | null | undefined)[] | {
|
|
456
|
+
[x: string]: any;
|
|
457
|
+
} | null | undefined)[] | {
|
|
458
|
+
[x: string]: any;
|
|
459
|
+
} | null | undefined)[] | {
|
|
460
|
+
[x: string]: any;
|
|
461
|
+
} | null | undefined)[] | {
|
|
462
|
+
[x: string]: any;
|
|
463
|
+
} | null | undefined)[] | {
|
|
464
|
+
[x: string]: any;
|
|
465
|
+
} | null | undefined;
|
|
466
|
+
})) | undefined) => string;
|
|
467
|
+
type Props = VariantProps<typeof alertVariants> & {
|
|
468
|
+
children: React.ReactNode;
|
|
469
|
+
/**
|
|
470
|
+
* The ARIA role for the alertbox.
|
|
471
|
+
*/
|
|
472
|
+
role: 'alert' | 'status' | 'none';
|
|
473
|
+
/** Additional CSS className for the element. */
|
|
474
|
+
className?: string;
|
|
475
|
+
/**
|
|
476
|
+
* Controls if the alert is expandable or not
|
|
477
|
+
* @default false
|
|
478
|
+
*/
|
|
479
|
+
isExpandable?: boolean;
|
|
480
|
+
/**
|
|
481
|
+
* Controls if the alert can be dismissed with a close button.
|
|
482
|
+
* @default false
|
|
483
|
+
*/
|
|
484
|
+
isDismissable?: boolean;
|
|
485
|
+
/**
|
|
486
|
+
* Controls if the alert is rendered or not.
|
|
487
|
+
* This is used to control the open/closed state of the component; make the component "controlled".
|
|
488
|
+
* @default false
|
|
489
|
+
*/
|
|
490
|
+
isDismissed?: boolean;
|
|
491
|
+
/**
|
|
492
|
+
* Callback that should be triggered when a dismissable alert is closed.
|
|
493
|
+
* This is used to control the open/closed state of the component; make the component "controlled".
|
|
494
|
+
*/
|
|
495
|
+
onDismiss?: () => void;
|
|
496
|
+
};
|
|
497
|
+
declare const Alertbox: ({ children, role, className, variant, isDismissable, isDismissed, onDismiss, isExpandable, }: Props) => react_jsx_runtime.JSX.Element | undefined;
|
|
498
|
+
|
|
499
|
+
type HeadingProps = HTMLProps<HTMLHeadingElement> & {
|
|
500
|
+
children: React.ReactNode;
|
|
501
|
+
/** The level of the heading */
|
|
502
|
+
level: 1 | 2 | 3 | 4 | 5 | 6;
|
|
503
|
+
};
|
|
504
|
+
declare const Heading: ({ level, ...restProps }: HeadingProps) => react_jsx_runtime.JSX.Element;
|
|
505
|
+
type ContentProps = HTMLProps<HTMLDivElement> & {
|
|
506
|
+
children: React.ReactNode;
|
|
507
|
+
};
|
|
508
|
+
declare const Content: (props: ContentProps) => react_jsx_runtime.JSX.Element;
|
|
509
|
+
type FooterProps = HTMLProps<HTMLDivElement> & {
|
|
510
|
+
children: React.ReactNode;
|
|
511
|
+
};
|
|
512
|
+
declare const Footer: (props: FooterProps) => react_jsx_runtime.JSX.Element;
|
|
513
|
+
|
|
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>>;
|
|
396
543
|
|
|
397
|
-
export { _Button as Button, type ButtonProps, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, _Combobox as Combobox, ListBoxItem as ComboboxItem, type ComboboxProps, _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 };
|
|
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,10 +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 {
|
|
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);
|
|
8
154
|
|
|
9
155
|
const formField = cx('group flex flex-col gap-2');
|
|
10
156
|
const formFieldError = cx('w-fit rounded-sm bg-red-light px-2 py-1 text-sm leading-6 text-red');
|
|
@@ -235,7 +381,7 @@ function Combobox(props, ref) {
|
|
|
235
381
|
}),
|
|
236
382
|
ref: ref
|
|
237
383
|
}),
|
|
238
|
-
/*#__PURE__*/ jsx(Button, {
|
|
384
|
+
/*#__PURE__*/ jsx(Button$1, {
|
|
239
385
|
children: isLoading ? /*#__PURE__*/ jsx(LoadingSpinner, {
|
|
240
386
|
className: "animate-spin"
|
|
241
387
|
}) : /*#__PURE__*/ jsx(ChevronDown, {
|
|
@@ -345,7 +491,7 @@ function Select(props, ref) {
|
|
|
345
491
|
description && /*#__PURE__*/ jsx(Description, {
|
|
346
492
|
children: description
|
|
347
493
|
}),
|
|
348
|
-
/*#__PURE__*/ jsxs(Button, {
|
|
494
|
+
/*#__PURE__*/ jsxs(Button$1, {
|
|
349
495
|
className: cx(input({
|
|
350
496
|
focusModifier: 'visible'
|
|
351
497
|
}), // How to reuse placeholder text?
|
|
@@ -507,4 +653,163 @@ function NumberField(props, ref) {
|
|
|
507
653
|
}
|
|
508
654
|
const _NumberField = /*#__PURE__*/ forwardRef(NumberField);
|
|
509
655
|
|
|
510
|
-
|
|
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
|
+
|
|
768
|
+
const Heading = ({ level, ...restProps })=>{
|
|
769
|
+
const Heading = `h${level}`;
|
|
770
|
+
return /*#__PURE__*/ jsx(Heading, {
|
|
771
|
+
...restProps,
|
|
772
|
+
"data-slot": "heading"
|
|
773
|
+
});
|
|
774
|
+
};
|
|
775
|
+
const Content = (props)=>/*#__PURE__*/ jsx("div", {
|
|
776
|
+
...props,
|
|
777
|
+
"data-slot": "content"
|
|
778
|
+
});
|
|
779
|
+
const Footer = (props)=>/*#__PURE__*/ jsx("div", {
|
|
780
|
+
...props,
|
|
781
|
+
"data-slot": "footer"
|
|
782
|
+
});
|
|
783
|
+
|
|
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.14",
|
|
4
4
|
"description": "Grunnmuren components in React",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/code-obos/grunnmuren"
|
|
@@ -19,9 +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
|
+
"@types/node": "^20.11.19",
|
|
23
24
|
"cva": "1.0.0-beta.1",
|
|
24
|
-
"react-aria-components": "^1.
|
|
25
|
+
"react-aria-components": "^1.1.1"
|
|
25
26
|
},
|
|
26
27
|
"peerDependencies": {
|
|
27
28
|
"react": "^18"
|
|
@@ -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 _ };
|