@obosbbl/grunnmuren-react 2.0.0-canary.3 → 2.0.0-canary.5
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 +36 -0
- package/dist/Button-client-wuoyidfi.js +144 -0
- package/dist/index.d.mts +12 -11
- package/dist/index.mjs +38 -150
- package/package.json +2 -1
- package/dist/useClientLayoutEffect-client-2_5nawgR.js +0 -9
package/README.md
CHANGED
|
@@ -50,6 +50,42 @@ export default function RootLayout({
|
|
|
50
50
|
|
|
51
51
|
See the [RAC internationalization docs](https://react-spectrum.adobe.com/react-aria/internationalization.html) for more information.
|
|
52
52
|
|
|
53
|
+
### Optimize bundle size by removing unused locales
|
|
54
|
+
|
|
55
|
+
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:
|
|
56
|
+
|
|
57
|
+
#### Install
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# npm
|
|
61
|
+
npm install @react-aria/optimize-locales-plugin --save-dev
|
|
62
|
+
|
|
63
|
+
# pnpm
|
|
64
|
+
pnpm add -D @react-aria/optimize-locales-plugin
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Configuration
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// next.config.js
|
|
71
|
+
const optimizeLocales = require('@react-aria/optimize-locales-plugin');
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
webpack(config) {
|
|
75
|
+
config.plugins.push(
|
|
76
|
+
optimizeLocales.webpack({
|
|
77
|
+
// If you have a multitenant app, include both Norwegian and Swedish
|
|
78
|
+
// If your app only serves one language, adjust accordingly
|
|
79
|
+
locales: ['nb-NO', 'sv-SE'],
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
return config;
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The plugin works with several different bundlers. See [React Aria's bundle size optimization docs](https://react-spectrum.adobe.com/react-aria/internationalization.html#optimizing-bundle-size) for more information.
|
|
88
|
+
|
|
53
89
|
## Usage
|
|
54
90
|
|
|
55
91
|
Before you start using the components you need to configure the [Tailwind preset](../tailwind/). Remember to add this package to the content scan.
|
|
@@ -0,0 +1,144 @@
|
|
|
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: // The of-type classes takes care to add spacing when the button is used with icons
|
|
43
|
+
'px-4 py-2 [&>svg]:first-of-type:mr-2.5 [&>svg]:last-of-type:ml-2.5'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
compoundVariants: [
|
|
47
|
+
{
|
|
48
|
+
color: 'green',
|
|
49
|
+
variant: 'primary',
|
|
50
|
+
// Darken bg by 20% on hover. The color is manually crafted
|
|
51
|
+
className: 'bg-green text-white hover:bg-green-dark active:bg-[#007352]'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
color: 'green',
|
|
55
|
+
variant: 'secondary',
|
|
56
|
+
className: 'bg-white text-black shadow-green hover:bg-green hover:text-white active:bg-green'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
color: 'mint',
|
|
60
|
+
variant: 'primary',
|
|
61
|
+
// Darken bg by 20% on hover. The color is manually crafted
|
|
62
|
+
className: 'active:[#9ddac6] bg-mint text-black hover:bg-[#8dd4bd]'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
color: 'mint',
|
|
66
|
+
variant: 'secondary',
|
|
67
|
+
className: 'text-mint shadow-mint hover:bg-mint hover:text-black'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
color: 'mint',
|
|
71
|
+
variant: 'tertiary',
|
|
72
|
+
className: 'text-mint'
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
color: 'white',
|
|
76
|
+
variant: 'primary',
|
|
77
|
+
className: 'bg-white text-black hover:bg-sky active:bg-sky-light'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
color: 'white',
|
|
81
|
+
variant: 'secondary',
|
|
82
|
+
className: 'text-white shadow-white hover:bg-white hover:text-black'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
color: 'white',
|
|
86
|
+
variant: 'tertiary',
|
|
87
|
+
className: 'text-white'
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
defaultVariants: {
|
|
91
|
+
variant: 'primary',
|
|
92
|
+
color: 'green',
|
|
93
|
+
isIconOnly: false
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
function Button(props, forwardedRef) {
|
|
97
|
+
const { children, className, color, isIconOnly, isLoading, variant, style, ...restProps } = props;
|
|
98
|
+
const [widthOverride, setWidthOverride] = useState();
|
|
99
|
+
const ownRef = useRef(null);
|
|
100
|
+
const ref = mergeRefs(ownRef, forwardedRef);
|
|
101
|
+
useClientLayoutEffect(()=>{
|
|
102
|
+
if (isLoading) {
|
|
103
|
+
const requestID = window.requestAnimationFrame(()=>{
|
|
104
|
+
setWidthOverride(ownRef.current?.getBoundingClientRect()?.width);
|
|
105
|
+
});
|
|
106
|
+
return ()=>{
|
|
107
|
+
setWidthOverride(undefined);
|
|
108
|
+
cancelAnimationFrame(requestID);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}, [
|
|
112
|
+
isLoading,
|
|
113
|
+
children
|
|
114
|
+
]);
|
|
115
|
+
let Component = 'a';
|
|
116
|
+
if (props.href == null) {
|
|
117
|
+
// 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
|
|
118
|
+
Component = 'button';
|
|
119
|
+
restProps.type ??= 'button';
|
|
120
|
+
}
|
|
121
|
+
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
|
|
122
|
+
/*#__PURE__*/ jsx(Component, {
|
|
123
|
+
"aria-busy": isLoading ? true : undefined,
|
|
124
|
+
className: buttonVariants({
|
|
125
|
+
className,
|
|
126
|
+
color,
|
|
127
|
+
isIconOnly,
|
|
128
|
+
variant
|
|
129
|
+
}),
|
|
130
|
+
ref: ref,
|
|
131
|
+
style: {
|
|
132
|
+
...style,
|
|
133
|
+
width: widthOverride
|
|
134
|
+
},
|
|
135
|
+
...restProps,
|
|
136
|
+
children: widthOverride ? // remove margin for icon alignment
|
|
137
|
+
/*#__PURE__*/ jsx(LoadingSpinner, {
|
|
138
|
+
className: "!m-0 mx-auto animate-spin"
|
|
139
|
+
}) : children
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
const _Button = /*#__PURE__*/ forwardRef(Button);
|
|
143
|
+
|
|
144
|
+
export { _Button as _ };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { CheckboxProps as CheckboxProps$1, CheckboxGroupProps as CheckboxGroupProps$1, ComboBoxProps, ListBoxItemProps, RadioGroupProps as RadioGroupProps$1, RadioProps as RadioProps$1, SelectProps as SelectProps$1, TextFieldProps as TextFieldProps$1 } from 'react-aria-components';
|
|
2
2
|
export { ListBoxItemProps as ComboboxItemProps, Form, I18nProvider, ListBoxItemProps as SelectItemProps } from 'react-aria-components';
|
|
3
|
-
import * as
|
|
3
|
+
import * as react from 'react';
|
|
4
4
|
import { VariantProps } from 'cva';
|
|
5
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
@@ -80,7 +81,7 @@ type ButtonProps = VariantProps<typeof buttonVariants> & {
|
|
|
80
81
|
isLoading?: boolean;
|
|
81
82
|
style?: React.CSSProperties;
|
|
82
83
|
} & ButtonOrLinkProps;
|
|
83
|
-
declare
|
|
84
|
+
declare const _Button: react.ForwardRefExoticComponent<ButtonProps & react.RefAttributes<HTMLButtonElement | HTMLAnchorElement>>;
|
|
84
85
|
|
|
85
86
|
type CheckboxProps = {
|
|
86
87
|
children: React.ReactNode;
|
|
@@ -93,7 +94,7 @@ type CheckboxProps = {
|
|
|
93
94
|
/** Additional style properties for the element. */
|
|
94
95
|
style?: React.CSSProperties;
|
|
95
96
|
} & Omit<CheckboxProps$1, 'isDisabled' | 'style' | 'children' | 'isIndeterminate' | 'isReadOnly'>;
|
|
96
|
-
declare
|
|
97
|
+
declare const _Checkbox: react.ForwardRefExoticComponent<Omit<CheckboxProps, "ref"> & react.RefAttributes<HTMLLabelElement>>;
|
|
97
98
|
|
|
98
99
|
type CheckboxGroupProps = {
|
|
99
100
|
children: React.ReactNode;
|
|
@@ -108,7 +109,7 @@ type CheckboxGroupProps = {
|
|
|
108
109
|
/** Additional style properties for the element. */
|
|
109
110
|
style?: React.CSSProperties;
|
|
110
111
|
} & Omit<CheckboxGroupProps$1, 'className' | 'isReadOnly' | 'isDisabled' | 'children' | 'style' | 'orientation'>;
|
|
111
|
-
declare
|
|
112
|
+
declare const _CheckboxGroup: react.ForwardRefExoticComponent<Omit<CheckboxGroupProps, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
112
113
|
|
|
113
114
|
type ComboboxProps<T extends object> = {
|
|
114
115
|
children: React.ReactNode;
|
|
@@ -130,8 +131,8 @@ type ComboboxProps<T extends object> = {
|
|
|
130
131
|
/** Additional style properties for the element. */
|
|
131
132
|
style?: React.CSSProperties;
|
|
132
133
|
} & Omit<ComboBoxProps<T>, 'className' | 'isReadOnly' | 'isDisabled' | 'children' | 'style'>;
|
|
133
|
-
declare function Combobox<T extends object>(props: ComboboxProps<T>): react_jsx_runtime.JSX.Element;
|
|
134
134
|
declare const ComboboxItem: (props: ListBoxItemProps) => react_jsx_runtime.JSX.Element;
|
|
135
|
+
declare const _Combobox: react.ForwardRefExoticComponent<Omit<ComboboxProps<object>, "ref"> & react.RefAttributes<HTMLInputElement>>;
|
|
135
136
|
|
|
136
137
|
type RadioGroupProps = {
|
|
137
138
|
children: React.ReactNode;
|
|
@@ -146,7 +147,7 @@ type RadioGroupProps = {
|
|
|
146
147
|
/** Additional style properties for the element. */
|
|
147
148
|
style?: React.CSSProperties;
|
|
148
149
|
} & Omit<RadioGroupProps$1, 'className' | 'isReadOnly' | 'isDisabled' | 'children' | 'style' | 'orientation'>;
|
|
149
|
-
declare
|
|
150
|
+
declare const _RadioGroup: react.ForwardRefExoticComponent<Omit<RadioGroupProps, "ref"> & react.RefAttributes<HTMLDivElement>>;
|
|
150
151
|
|
|
151
152
|
type RadioProps = {
|
|
152
153
|
children: React.ReactNode;
|
|
@@ -157,7 +158,7 @@ type RadioProps = {
|
|
|
157
158
|
/** Additional style properties for the element. */
|
|
158
159
|
style?: React.CSSProperties;
|
|
159
160
|
} & Omit<RadioProps$1, 'isDisabled' | 'children' | 'style'>;
|
|
160
|
-
declare
|
|
161
|
+
declare const _Radio: react.ForwardRefExoticComponent<Omit<RadioProps, "ref"> & react.RefAttributes<HTMLLabelElement>>;
|
|
161
162
|
|
|
162
163
|
type SelectProps<T extends object> = {
|
|
163
164
|
children: React.ReactNode;
|
|
@@ -174,8 +175,8 @@ type SelectProps<T extends object> = {
|
|
|
174
175
|
/** Additional style properties for the element. */
|
|
175
176
|
style?: React.CSSProperties;
|
|
176
177
|
} & Omit<SelectProps$1<T>, 'className' | 'isReadOnly' | 'isDisabled' | 'children' | 'style'>;
|
|
177
|
-
declare function Select<T extends object>(props: SelectProps<T>): react_jsx_runtime.JSX.Element;
|
|
178
178
|
declare const SelectItem: (props: ListBoxItemProps) => react_jsx_runtime.JSX.Element;
|
|
179
|
+
declare const _Select: react.ForwardRefExoticComponent<Omit<SelectProps<object>, "ref"> & react.RefAttributes<HTMLButtonElement>>;
|
|
179
180
|
|
|
180
181
|
type TextAreaProps = {
|
|
181
182
|
/** Additional CSS className for the element. */
|
|
@@ -196,7 +197,7 @@ type TextAreaProps = {
|
|
|
196
197
|
*/
|
|
197
198
|
rows?: number;
|
|
198
199
|
} & Omit<TextFieldProps$1, 'className' | 'isReadOnly' | 'isDisabled' | 'children' | 'style'>;
|
|
199
|
-
declare
|
|
200
|
+
declare const _TextArea: react.ForwardRefExoticComponent<Omit<TextAreaProps, "ref"> & react.RefAttributes<HTMLTextAreaElement>>;
|
|
200
201
|
|
|
201
202
|
type TextFieldProps = {
|
|
202
203
|
/** Additional CSS className for the element. */
|
|
@@ -223,6 +224,6 @@ type TextFieldProps = {
|
|
|
223
224
|
/** Add a divider between the left/right addons and the input */
|
|
224
225
|
withAddonDivider?: boolean;
|
|
225
226
|
} & Omit<TextFieldProps$1, 'className' | 'isReadOnly' | 'isDisabled' | 'children' | 'style'>;
|
|
226
|
-
declare
|
|
227
|
+
declare const _TextField: react.ForwardRefExoticComponent<Omit<TextFieldProps, "ref"> & react.RefAttributes<HTMLInputElement>>;
|
|
227
228
|
|
|
228
|
-
export { Button, type ButtonProps, Checkbox, CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, Combobox, ComboboxItem, type ComboboxProps, Radio, RadioGroup, type RadioGroupProps, type RadioProps, Select, SelectItem, type SelectProps, TextArea, type TextAreaProps, TextField, type TextFieldProps };
|
|
229
|
+
export { _Button as Button, type ButtonProps, _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, type CheckboxGroupProps, type CheckboxProps, _Combobox as Combobox, ComboboxItem, type ComboboxProps, _Radio as Radio, _RadioGroup as RadioGroup, type RadioGroupProps, type RadioProps, _Select as Select, SelectItem, type SelectProps, _TextArea as TextArea, type TextAreaProps, _TextField as TextField, type TextFieldProps };
|
package/dist/index.mjs
CHANGED
|
@@ -1,140 +1,10 @@
|
|
|
1
|
-
import { Text, CheckboxContext, Checkbox as Checkbox$1, Label as Label$1, CheckboxGroup as CheckboxGroup$1, FieldError, ComboBox, Group, Input, Button
|
|
1
|
+
import { Text, CheckboxContext, Checkbox as Checkbox$1, Label as Label$1, CheckboxGroup as CheckboxGroup$1, FieldError, ListBoxItem, ComboBox, Group, Input, Button, Popover, ListBox, RadioGroup as RadioGroup$1, Radio as Radio$1, Select as Select$1, SelectValue, TextField as TextField$1, TextArea as TextArea$1 } from 'react-aria-components';
|
|
2
2
|
export { Form, I18nProvider } from 'react-aria-components';
|
|
3
|
+
export { _ as Button } from './Button-client-wuoyidfi.js';
|
|
3
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { u as useClientLayoutEffect } from './useClientLayoutEffect-client-2_5nawgR.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Figma: https://www.figma.com/file/9OvSg0ZXI5E1eQYi7AWiWn/Grunnmuren-2.0-%E2%94%82-Designsystem?node-id=30%3A2574&mode=dev
|
|
11
|
-
*/ const buttonVariants = cva({
|
|
12
|
-
base: [
|
|
13
|
-
'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'
|
|
14
|
-
],
|
|
15
|
-
variants: {
|
|
16
|
-
/**
|
|
17
|
-
* The variant of the button
|
|
18
|
-
* @default primary
|
|
19
|
-
*/ variant: {
|
|
20
|
-
primary: 'no-underline',
|
|
21
|
-
// 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
|
|
22
|
-
secondary: 'no-underline shadow-[inset_0_0_0_2px]',
|
|
23
|
-
tertiary: 'underline hover:no-underline'
|
|
24
|
-
},
|
|
25
|
-
/**
|
|
26
|
-
* Adjusts the color of the button for usage on different backgrounds.
|
|
27
|
-
* @default green
|
|
28
|
-
*/ color: {
|
|
29
|
-
green: 'focus-visible:ring-black',
|
|
30
|
-
mint: 'focus-visible:ring-mint focus-visible:ring-offset-green-dark',
|
|
31
|
-
white: 'focus-visible:ring-white focus-visible:ring-offset-blue'
|
|
32
|
-
},
|
|
33
|
-
/**
|
|
34
|
-
* When the button is without text, but with a single icon.
|
|
35
|
-
* @default false
|
|
36
|
-
*/ isIconOnly: {
|
|
37
|
-
true: 'p-2 [&>svg]:h-7 [&>svg]:w-7',
|
|
38
|
-
false: // The of-type classes takes care to add spacing when the button is used with icons
|
|
39
|
-
'px-4 py-2 [&>svg]:first-of-type:mr-2.5 [&>svg]:last-of-type:ml-2.5'
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
compoundVariants: [
|
|
43
|
-
{
|
|
44
|
-
color: 'green',
|
|
45
|
-
variant: 'primary',
|
|
46
|
-
// Darken bg by 20% on hover. The color is manually crafted
|
|
47
|
-
className: 'bg-green text-white hover:bg-green-dark active:bg-[#007352]'
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
color: 'green',
|
|
51
|
-
variant: 'secondary',
|
|
52
|
-
className: 'bg-white text-black shadow-green hover:bg-green hover:text-white active:bg-green'
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
color: 'mint',
|
|
56
|
-
variant: 'primary',
|
|
57
|
-
// Darken bg by 20% on hover. The color is manually crafted
|
|
58
|
-
className: 'active:[#9ddac6] bg-mint text-black hover:bg-[#8dd4bd]'
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
color: 'mint',
|
|
62
|
-
variant: 'secondary',
|
|
63
|
-
className: 'text-mint shadow-mint hover:bg-mint hover:text-black'
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
color: 'mint',
|
|
67
|
-
variant: 'tertiary',
|
|
68
|
-
className: 'text-mint'
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
color: 'white',
|
|
72
|
-
variant: 'primary',
|
|
73
|
-
className: 'bg-white text-black hover:bg-sky active:bg-sky-light'
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
color: 'white',
|
|
77
|
-
variant: 'secondary',
|
|
78
|
-
className: 'text-white shadow-white hover:bg-white hover:text-black'
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
color: 'white',
|
|
82
|
-
variant: 'tertiary',
|
|
83
|
-
className: 'text-white'
|
|
84
|
-
}
|
|
85
|
-
],
|
|
86
|
-
defaultVariants: {
|
|
87
|
-
variant: 'primary',
|
|
88
|
-
color: 'green',
|
|
89
|
-
isIconOnly: false
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
function Button(props) {
|
|
93
|
-
const { children, className, color, isIconOnly, isLoading, variant, style, ...restProps } = props;
|
|
94
|
-
// TODO: Merge refs when we use RAC
|
|
95
|
-
const buttonRef = useRef(null);
|
|
96
|
-
const [widthOverride, setWidthOverride] = useState();
|
|
97
|
-
useClientLayoutEffect(()=>{
|
|
98
|
-
if (isLoading) {
|
|
99
|
-
const requestID = window.requestAnimationFrame(()=>{
|
|
100
|
-
setWidthOverride(buttonRef?.current?.getBoundingClientRect()?.width);
|
|
101
|
-
});
|
|
102
|
-
return ()=>{
|
|
103
|
-
setWidthOverride(undefined);
|
|
104
|
-
cancelAnimationFrame(requestID);
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
}, [
|
|
108
|
-
isLoading,
|
|
109
|
-
children
|
|
110
|
-
]);
|
|
111
|
-
let Component = 'a';
|
|
112
|
-
if (props.href == null) {
|
|
113
|
-
// 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
|
|
114
|
-
Component = 'button';
|
|
115
|
-
restProps.type ??= 'button';
|
|
116
|
-
}
|
|
117
|
-
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
|
|
118
|
-
/*#__PURE__*/ jsx(Component, {
|
|
119
|
-
"aria-busy": isLoading ? true : undefined,
|
|
120
|
-
className: buttonVariants({
|
|
121
|
-
className,
|
|
122
|
-
color,
|
|
123
|
-
isIconOnly,
|
|
124
|
-
variant
|
|
125
|
-
}),
|
|
126
|
-
ref: buttonRef,
|
|
127
|
-
style: {
|
|
128
|
-
...style,
|
|
129
|
-
width: widthOverride
|
|
130
|
-
},
|
|
131
|
-
...restProps,
|
|
132
|
-
children: widthOverride ? // remove margin for icon alignment
|
|
133
|
-
/*#__PURE__*/ jsx(LoadingSpinner, {
|
|
134
|
-
className: "!m-0 mx-auto animate-spin"
|
|
135
|
-
}) : children
|
|
136
|
-
}));
|
|
137
|
-
}
|
|
5
|
+
import { forwardRef, useId } from 'react';
|
|
6
|
+
import { cx, cva, compose } from 'cva';
|
|
7
|
+
import { Check, LoadingSpinner, ChevronDown } from '@obosbbl/grunnmuren-icons-react';
|
|
138
8
|
|
|
139
9
|
const formField = cx('group flex flex-col gap-2');
|
|
140
10
|
const formFieldError = cx('w-fit rounded-sm bg-red-light px-2 py-1 text-sm leading-6 text-red');
|
|
@@ -216,7 +86,7 @@ function CheckmarkBox() {
|
|
|
216
86
|
})
|
|
217
87
|
});
|
|
218
88
|
}
|
|
219
|
-
function Checkbox(props) {
|
|
89
|
+
function Checkbox(props, ref) {
|
|
220
90
|
const { children, className, description, errorMessage, isInvalid: _isInvalid, ...restProps } = props;
|
|
221
91
|
const id = useId();
|
|
222
92
|
const descriptionId = 'desc' + id;
|
|
@@ -233,6 +103,7 @@ function Checkbox(props) {
|
|
|
233
103
|
...restProps,
|
|
234
104
|
className: cx(className, defaultClasses$1),
|
|
235
105
|
isInvalid: isInvalid,
|
|
106
|
+
ref: ref,
|
|
236
107
|
children: [
|
|
237
108
|
/*#__PURE__*/ jsx("div", {
|
|
238
109
|
className: "absolute -left-2.5 top-0 z-10 h-11 w-11"
|
|
@@ -255,6 +126,7 @@ function Checkbox(props) {
|
|
|
255
126
|
})
|
|
256
127
|
});
|
|
257
128
|
}
|
|
129
|
+
const _Checkbox = /*#__PURE__*/ forwardRef(Checkbox);
|
|
258
130
|
|
|
259
131
|
function Label(props) {
|
|
260
132
|
const { children, className, ...restProps } = props;
|
|
@@ -265,7 +137,7 @@ function Label(props) {
|
|
|
265
137
|
});
|
|
266
138
|
}
|
|
267
139
|
|
|
268
|
-
function CheckboxGroup(props) {
|
|
140
|
+
function CheckboxGroup(props, ref) {
|
|
269
141
|
const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, ...restProps } = props;
|
|
270
142
|
const isInvalid = _isInvalid || errorMessage != null;
|
|
271
143
|
return /*#__PURE__*/ jsxs(CheckboxGroup$1, {
|
|
@@ -273,6 +145,7 @@ function CheckboxGroup(props) {
|
|
|
273
145
|
className: cx(className, 'flex flex-col gap-2'),
|
|
274
146
|
isInvalid: isInvalid,
|
|
275
147
|
isRequired: isRequired,
|
|
148
|
+
ref: ref,
|
|
276
149
|
children: [
|
|
277
150
|
label && /*#__PURE__*/ jsx(Label, {
|
|
278
151
|
children: label
|
|
@@ -287,6 +160,7 @@ function CheckboxGroup(props) {
|
|
|
287
160
|
]
|
|
288
161
|
});
|
|
289
162
|
}
|
|
163
|
+
const _CheckboxGroup = /*#__PURE__*/ forwardRef(CheckboxGroup);
|
|
290
164
|
|
|
291
165
|
/**
|
|
292
166
|
* This component handles renders a custom error message (if provided), otherwise it falls back to the browser's native validation.
|
|
@@ -299,7 +173,7 @@ function CheckboxGroup(props) {
|
|
|
299
173
|
});
|
|
300
174
|
}
|
|
301
175
|
|
|
302
|
-
function Combobox(props) {
|
|
176
|
+
function Combobox(props, ref) {
|
|
303
177
|
const { className, children, description, errorMessage, isLoading, label, isInvalid: _isInvalid, ...restProps } = props;
|
|
304
178
|
const isInvalid = _isInvalid || errorMessage != null;
|
|
305
179
|
return /*#__PURE__*/ jsxs(ComboBox, {
|
|
@@ -319,9 +193,10 @@ function Combobox(props) {
|
|
|
319
193
|
/*#__PURE__*/ jsx(Input, {
|
|
320
194
|
className: input({
|
|
321
195
|
isGrouped: true
|
|
322
|
-
})
|
|
196
|
+
}),
|
|
197
|
+
ref: ref
|
|
323
198
|
}),
|
|
324
|
-
/*#__PURE__*/ jsx(Button
|
|
199
|
+
/*#__PURE__*/ jsx(Button, {
|
|
325
200
|
children: isLoading ? /*#__PURE__*/ jsx(LoadingSpinner, {
|
|
326
201
|
className: "animate-spin"
|
|
327
202
|
}) : /*#__PURE__*/ jsx(ChevronDown, {
|
|
@@ -370,8 +245,9 @@ const ComboboxItem = (props)=>{
|
|
|
370
245
|
})
|
|
371
246
|
});
|
|
372
247
|
};
|
|
248
|
+
const _Combobox = /*#__PURE__*/ forwardRef(Combobox);
|
|
373
249
|
|
|
374
|
-
function RadioGroup(props) {
|
|
250
|
+
function RadioGroup(props, ref) {
|
|
375
251
|
const { children, className, description, errorMessage, label, isRequired, isInvalid: _isInvalid, ...restProps } = props;
|
|
376
252
|
const isInvalid = _isInvalid || errorMessage != null;
|
|
377
253
|
return /*#__PURE__*/ jsxs(RadioGroup$1, {
|
|
@@ -379,6 +255,7 @@ function RadioGroup(props) {
|
|
|
379
255
|
className: cx(className, 'flex flex-col gap-2'),
|
|
380
256
|
isInvalid: isInvalid,
|
|
381
257
|
isRequired: isRequired,
|
|
258
|
+
ref: ref,
|
|
382
259
|
children: [
|
|
383
260
|
label && /*#__PURE__*/ jsx(Label, {
|
|
384
261
|
children: label
|
|
@@ -393,6 +270,7 @@ function RadioGroup(props) {
|
|
|
393
270
|
]
|
|
394
271
|
});
|
|
395
272
|
}
|
|
273
|
+
const _RadioGroup = /*#__PURE__*/ forwardRef(RadioGroup);
|
|
396
274
|
|
|
397
275
|
const defaultClasses = cx([
|
|
398
276
|
'relative inline-flex max-w-fit cursor-pointer items-start gap-4 py-2 leading-7',
|
|
@@ -413,11 +291,12 @@ const defaultClasses = cx([
|
|
|
413
291
|
// so we use an inner outline to artifically pad the border
|
|
414
292
|
'data-[invalid]:before:outline-solid data-[invalid]:before:border-red data-[invalid]:data-[selected]:before:!bg-red data-[invalid]:before:outline data-[invalid]:before:outline-[3px] data-[invalid]:before:outline-offset-[-3px] data-[invalid]:before:outline-red'
|
|
415
293
|
]);
|
|
416
|
-
function Radio(props) {
|
|
294
|
+
function Radio(props, ref) {
|
|
417
295
|
const { children, className, description, ...restProps } = props;
|
|
418
296
|
return /*#__PURE__*/ jsxs(Radio$1, {
|
|
419
297
|
...restProps,
|
|
420
298
|
className: cx(className, defaultClasses),
|
|
299
|
+
ref: ref,
|
|
421
300
|
children: [
|
|
422
301
|
/*#__PURE__*/ jsx("div", {
|
|
423
302
|
className: "absolute -left-2.5 top-0 z-10 h-11 w-11 "
|
|
@@ -434,8 +313,9 @@ function Radio(props) {
|
|
|
434
313
|
]
|
|
435
314
|
});
|
|
436
315
|
}
|
|
316
|
+
const _Radio = /*#__PURE__*/ forwardRef(Radio);
|
|
437
317
|
|
|
438
|
-
function Select(props) {
|
|
318
|
+
function Select(props, ref) {
|
|
439
319
|
const { className, children, description, errorMessage, label, isInvalid: _isInvalid, ...restProps } = props;
|
|
440
320
|
const isInvalid = _isInvalid || errorMessage != null;
|
|
441
321
|
return /*#__PURE__*/ jsxs(Select$1, {
|
|
@@ -449,11 +329,13 @@ function Select(props) {
|
|
|
449
329
|
description && /*#__PURE__*/ jsx(Description, {
|
|
450
330
|
children: description
|
|
451
331
|
}),
|
|
452
|
-
/*#__PURE__*/ jsxs(Button
|
|
332
|
+
/*#__PURE__*/ jsxs(Button, {
|
|
453
333
|
className: cx(input({
|
|
454
334
|
focusModifier: 'visible'
|
|
455
335
|
}), // How to reuse placeholder text?
|
|
456
336
|
'inline-flex cursor-default items-center gap-2'),
|
|
337
|
+
// See https://github.com/adobe/react-spectrum/discussions/4792#discussioncomment-6492305
|
|
338
|
+
ref: ref,
|
|
457
339
|
children: [
|
|
458
340
|
/*#__PURE__*/ jsx(SelectValue, {
|
|
459
341
|
className: "flex-1 truncate text-left data-[placeholder]:text-[#727070]"
|
|
@@ -498,8 +380,9 @@ const SelectItem = (props)=>{
|
|
|
498
380
|
})
|
|
499
381
|
});
|
|
500
382
|
};
|
|
383
|
+
const _Select = /*#__PURE__*/ forwardRef(Select);
|
|
501
384
|
|
|
502
|
-
function TextArea(props) {
|
|
385
|
+
function TextArea(props, ref) {
|
|
503
386
|
const { className, description, errorMessage, label, isInvalid: _isInvalid, rows, ...restProps } = props;
|
|
504
387
|
const isInvalid = _isInvalid || errorMessage != null;
|
|
505
388
|
return /*#__PURE__*/ jsxs(TextField$1, {
|
|
@@ -515,7 +398,8 @@ function TextArea(props) {
|
|
|
515
398
|
}),
|
|
516
399
|
/*#__PURE__*/ jsx(TextArea$1, {
|
|
517
400
|
className: input(),
|
|
518
|
-
rows: rows
|
|
401
|
+
rows: rows,
|
|
402
|
+
ref: ref
|
|
519
403
|
}),
|
|
520
404
|
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
521
405
|
errorMessage: errorMessage
|
|
@@ -523,6 +407,7 @@ function TextArea(props) {
|
|
|
523
407
|
]
|
|
524
408
|
});
|
|
525
409
|
}
|
|
410
|
+
const _TextArea = /*#__PURE__*/ forwardRef(TextArea);
|
|
526
411
|
|
|
527
412
|
const inputWithAlignment = compose(input, cva({
|
|
528
413
|
base: '',
|
|
@@ -533,7 +418,7 @@ const inputWithAlignment = compose(input, cva({
|
|
|
533
418
|
}
|
|
534
419
|
}
|
|
535
420
|
}));
|
|
536
|
-
function TextField(props) {
|
|
421
|
+
function TextField(props, ref) {
|
|
537
422
|
const { className, description, errorMessage, label, leftAddon, isInvalid: _isInvalid, textAlign, rightAddon, withAddonDivider, ...restProps } = props;
|
|
538
423
|
const isInvalid = _isInvalid || errorMessage != null;
|
|
539
424
|
return /*#__PURE__*/ jsxs(TextField$1, {
|
|
@@ -558,7 +443,8 @@ function TextField(props) {
|
|
|
558
443
|
className: inputWithAlignment({
|
|
559
444
|
textAlign,
|
|
560
445
|
isGrouped: true
|
|
561
|
-
})
|
|
446
|
+
}),
|
|
447
|
+
ref: ref
|
|
562
448
|
}),
|
|
563
449
|
withAddonDivider && rightAddon && /*#__PURE__*/ jsx(Divider, {
|
|
564
450
|
className: "mr-3"
|
|
@@ -568,7 +454,8 @@ function TextField(props) {
|
|
|
568
454
|
}) : /*#__PURE__*/ jsx(Input, {
|
|
569
455
|
className: inputWithAlignment({
|
|
570
456
|
textAlign
|
|
571
|
-
})
|
|
457
|
+
}),
|
|
458
|
+
ref: ref
|
|
572
459
|
}),
|
|
573
460
|
/*#__PURE__*/ jsx(ErrorMessageOrFieldError, {
|
|
574
461
|
errorMessage: errorMessage
|
|
@@ -581,5 +468,6 @@ function Divider({ className }) {
|
|
|
581
468
|
className: cx(className, 'block h-6 w-px flex-none bg-black')
|
|
582
469
|
});
|
|
583
470
|
}
|
|
471
|
+
const _TextField = /*#__PURE__*/ forwardRef(TextField);
|
|
584
472
|
|
|
585
|
-
export {
|
|
473
|
+
export { _Checkbox as Checkbox, _CheckboxGroup as CheckboxGroup, _Combobox as Combobox, ComboboxItem, _Radio as Radio, _RadioGroup as RadioGroup, _Select as Select, 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.5",
|
|
4
4
|
"description": "Grunnmuren components in React",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/code-obos/grunnmuren"
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@obosbbl/grunnmuren-icons-react": "^2.0.0-canary.1",
|
|
22
|
+
"@react-aria/utils": "^3.23.0",
|
|
22
23
|
"cva": "1.0.0-beta.1",
|
|
23
24
|
"react-aria-components": "^1.0.0"
|
|
24
25
|
},
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { useLayoutEffect } from 'react';
|
|
3
|
-
|
|
4
|
-
const canUseDOM = ()=>{
|
|
5
|
-
return typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
|
6
|
-
};
|
|
7
|
-
const useClientLayoutEffect = canUseDOM() ? useLayoutEffect : ()=>{};
|
|
8
|
-
|
|
9
|
-
export { useClientLayoutEffect as u };
|