@regardio/react 0.5.5 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/background-slideshow/index.d.mts +36 -0
- package/dist/background-slideshow/index.mjs +110 -0
- package/dist/blurry-gradient/index.d.mts +17 -0
- package/dist/blurry-gradient/index.mjs +93 -0
- package/dist/button/index.d.mts +2 -0
- package/dist/button/index.mjs +3 -0
- package/dist/button-BiSQpBbc.mjs +129 -0
- package/dist/carousel/index.d.mts +40 -0
- package/dist/carousel/index.mjs +141 -0
- package/dist/checkbox/index.d.mts +37 -0
- package/dist/checkbox/index.mjs +70 -0
- package/dist/checkbox-group/index.d.mts +17 -0
- package/dist/checkbox-group/index.mjs +29 -0
- package/dist/chunk-BTpB_u-K.mjs +18 -0
- package/dist/countdown/index.d.mts +6 -0
- package/dist/countdown/index.mjs +58 -0
- package/dist/field/index.d.mts +66 -0
- package/dist/field/index.mjs +115 -0
- package/dist/fieldset/index.d.mts +33 -0
- package/dist/fieldset/index.mjs +61 -0
- package/dist/form/index.d.mts +22 -0
- package/dist/form/index.mjs +31 -0
- package/dist/generic-error/{index.d.ts → index.d.mts} +22 -18
- package/dist/generic-error/index.mjs +57 -0
- package/dist/grid/index.d.mts +1197 -0
- package/dist/grid/index.mjs +221 -0
- package/dist/heading/index.d.mts +31 -0
- package/dist/heading/index.mjs +29 -0
- package/dist/highlight/index.d.mts +18 -0
- package/dist/highlight/index.mjs +35 -0
- package/dist/hooks/{use-current-route-data.d.ts → use-current-route-data.d.mts} +3 -2
- package/dist/hooks/use-current-route-data.mjs +20 -0
- package/dist/hooks/{use-focus-search.d.ts → use-focus-search.d.mts} +4 -3
- package/dist/hooks/use-focus-search.mjs +21 -0
- package/dist/hooks/{use-matches-data.d.ts → use-matches-data.d.mts} +3 -2
- package/dist/hooks/use-matches-data.mjs +21 -0
- package/dist/hooks/{use-media-query.d.ts → use-media-query.d.mts} +3 -2
- package/dist/hooks/use-media-query.mjs +26 -0
- package/dist/hooks/use-mobile.d.mts +4 -0
- package/dist/hooks/use-mobile.mjs +20 -0
- package/dist/hooks/use-nonce.d.mts +8 -0
- package/dist/hooks/use-nonce.mjs +13 -0
- package/dist/hooks/{use-orientation.d.ts → use-orientation.d.mts} +3 -2
- package/dist/hooks/use-orientation.mjs +30 -0
- package/dist/hooks/use-user.d.mts +55 -0
- package/dist/hooks/use-user.mjs +39 -0
- package/dist/icon-button/index.d.mts +29 -0
- package/dist/icon-button/index.mjs +36 -0
- package/dist/if/index.d.mts +15 -0
- package/dist/if/index.mjs +21 -0
- package/dist/iframe/index.d.mts +11 -0
- package/dist/iframe/index.mjs +15 -0
- package/dist/index-Bm-tWhsb.d.mts +30 -0
- package/dist/index-YT2CkvL6.d.mts +36 -0
- package/dist/input/index.d.mts +2 -0
- package/dist/input/index.mjs +3 -0
- package/dist/input-CtR6aRVi.mjs +73 -0
- package/dist/link/index.d.mts +73 -0
- package/dist/link/index.mjs +129 -0
- package/dist/list/index.d.mts +71 -0
- package/dist/list/index.mjs +54 -0
- package/dist/markdown-container/index.d.mts +23 -0
- package/dist/markdown-container/index.mjs +71 -0
- package/dist/password-input/index.d.mts +24 -0
- package/dist/password-input/index.mjs +92 -0
- package/dist/picture/{index.d.ts → index.d.mts} +21 -20
- package/dist/picture/index.mjs +3 -0
- package/dist/picture-DkX3W5zl.mjs +69 -0
- package/dist/protected-email/{index.d.ts → index.d.mts} +14 -8
- package/dist/protected-email/index.mjs +37 -0
- package/dist/radio/index.d.mts +37 -0
- package/dist/radio/index.mjs +72 -0
- package/dist/radio-group/index.d.mts +17 -0
- package/dist/radio-group/index.mjs +29 -0
- package/dist/slider/index.d.mts +85 -0
- package/dist/slider/index.mjs +133 -0
- package/dist/switch/index.d.mts +38 -0
- package/dist/switch/index.mjs +87 -0
- package/dist/text/index.d.mts +26 -0
- package/dist/text/index.mjs +32 -0
- package/dist/text-CPlUND-Z.mjs +58 -0
- package/dist/toggle/index.d.mts +59 -0
- package/dist/toggle/index.mjs +82 -0
- package/dist/utils/author/index.d.mts +4 -0
- package/dist/utils/author/index.mjs +26 -0
- package/dist/utils/text/{index.d.ts → index.d.mts} +4 -3
- package/dist/utils/text/index.mjs +3 -0
- package/package.json +17 -129
- package/src/button/button.stories.tsx +161 -0
- package/src/button/button.test.tsx +73 -0
- package/src/button/button.tsx +112 -0
- package/src/button/index.ts +2 -0
- package/src/carousel/carousel-next.tsx +2 -2
- package/src/carousel/carousel-previous.tsx +2 -2
- package/src/checkbox/checkbox.stories.tsx +118 -0
- package/src/checkbox/checkbox.tsx +91 -0
- package/src/checkbox/index.ts +2 -0
- package/src/checkbox-group/checkbox-group.tsx +40 -0
- package/src/checkbox-group/index.ts +2 -0
- package/src/field/field.stories.tsx +105 -0
- package/src/field/field.test.tsx +61 -0
- package/src/field/field.tsx +165 -0
- package/src/field/index.ts +12 -0
- package/src/fieldset/fieldset.stories.tsx +204 -0
- package/src/fieldset/fieldset.test.tsx +63 -0
- package/src/fieldset/fieldset.tsx +75 -0
- package/src/fieldset/index.ts +7 -0
- package/src/form/form.stories.tsx +230 -0
- package/src/form/form.test.tsx +68 -0
- package/src/form/form.tsx +38 -0
- package/src/form/index.ts +2 -0
- package/src/icon-button/icon-button.stories.tsx +128 -7
- package/src/icon-button/icon-button.test.tsx +152 -0
- package/src/icon-button/icon-button.tsx +43 -9
- package/src/input/index.ts +2 -0
- package/src/input/input.stories.tsx +151 -0
- package/src/input/input.test.tsx +65 -0
- package/src/input/input.tsx +113 -0
- package/src/link/link.test.tsx +169 -0
- package/src/password-input/index.ts +1 -1
- package/src/password-input/password-input.tsx +104 -27
- package/src/radio/index.ts +2 -0
- package/src/radio/radio.tsx +92 -0
- package/src/radio-group/index.ts +2 -0
- package/src/radio-group/radio-group.tsx +36 -0
- package/src/slider/index.ts +18 -0
- package/src/slider/slider.tsx +179 -0
- package/src/switch/index.ts +2 -0
- package/src/switch/switch.stories.tsx +118 -0
- package/src/switch/switch.tsx +101 -0
- package/src/toggle/index.ts +2 -0
- package/src/toggle/toggle.stories.tsx +232 -0
- package/src/toggle/toggle.test.tsx +149 -0
- package/src/toggle/toggle.tsx +88 -0
- package/src/utils/text/text.test.tsx +110 -0
- package/dist/background-slideshow/index.d.ts +0 -24
- package/dist/background-slideshow/index.js +0 -165
- package/dist/blurry-gradient/index.d.ts +0 -16
- package/dist/blurry-gradient/index.js +0 -128
- package/dist/carousel/index.d.ts +0 -36
- package/dist/carousel/index.js +0 -171
- package/dist/countdown/index.d.ts +0 -5
- package/dist/countdown/index.js +0 -73
- package/dist/generic-error/index.js +0 -47
- package/dist/grid/index.d.ts +0 -1196
- package/dist/grid/index.js +0 -239
- package/dist/heading/index.d.ts +0 -24
- package/dist/heading/index.js +0 -99
- package/dist/highlight/index.d.ts +0 -13
- package/dist/highlight/index.js +0 -59
- package/dist/hooks/use-current-route-data.js +0 -16
- package/dist/hooks/use-focus-search.js +0 -19
- package/dist/hooks/use-matches-data.js +0 -15
- package/dist/hooks/use-media-query.js +0 -20
- package/dist/hooks/use-mobile.d.ts +0 -3
- package/dist/hooks/use-mobile.js +0 -19
- package/dist/hooks/use-nonce.d.ts +0 -7
- package/dist/hooks/use-nonce.js +0 -8
- package/dist/hooks/use-orientation.js +0 -29
- package/dist/hooks/use-user.d.ts +0 -50
- package/dist/hooks/use-user.js +0 -25
- package/dist/icon-button/index.d.ts +0 -9
- package/dist/icon-button/index.js +0 -17
- package/dist/if/index.d.ts +0 -10
- package/dist/if/index.js +0 -24
- package/dist/iframe/index.d.ts +0 -10
- package/dist/iframe/index.js +0 -17
- package/dist/link/index.d.ts +0 -55
- package/dist/link/index.js +0 -195
- package/dist/list/index.d.ts +0 -69
- package/dist/list/index.js +0 -65
- package/dist/markdown-container/index.d.ts +0 -22
- package/dist/markdown-container/index.js +0 -128
- package/dist/password-input/index.d.ts +0 -11
- package/dist/password-input/index.js +0 -46
- package/dist/picture/index.js +0 -68
- package/dist/protected-email/index.js +0 -30
- package/dist/text/index.d.ts +0 -20
- package/dist/text/index.js +0 -38
- package/dist/utils/author/index.d.ts +0 -3
- package/dist/utils/author/index.js +0 -33
- package/dist/utils/text/index.js +0 -73
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Field } from '../field';
|
|
4
|
+
import { Checkbox } from './checkbox';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Checkbox.Root> = {
|
|
7
|
+
argTypes: {
|
|
8
|
+
disabled: {
|
|
9
|
+
control: 'boolean',
|
|
10
|
+
description: 'Disable the checkbox',
|
|
11
|
+
},
|
|
12
|
+
size: {
|
|
13
|
+
control: 'select',
|
|
14
|
+
description: 'Checkbox size',
|
|
15
|
+
options: ['sm', 'md', 'lg'],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
component: Checkbox.Root,
|
|
19
|
+
parameters: {
|
|
20
|
+
layout: 'centered',
|
|
21
|
+
},
|
|
22
|
+
tags: ['autodocs'],
|
|
23
|
+
title: 'Components/Checkbox',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default meta;
|
|
27
|
+
type Story = StoryObj<typeof meta>;
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {
|
|
30
|
+
render: () => (
|
|
31
|
+
<Checkbox.Root>
|
|
32
|
+
<Checkbox.Indicator />
|
|
33
|
+
</Checkbox.Root>
|
|
34
|
+
),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const Checked: Story = {
|
|
38
|
+
render: () => (
|
|
39
|
+
<Checkbox.Root defaultChecked>
|
|
40
|
+
<Checkbox.Indicator />
|
|
41
|
+
</Checkbox.Root>
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const Small: Story = {
|
|
46
|
+
render: () => (
|
|
47
|
+
<Checkbox.Root size="sm">
|
|
48
|
+
<Checkbox.Indicator />
|
|
49
|
+
</Checkbox.Root>
|
|
50
|
+
),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const Large: Story = {
|
|
54
|
+
render: () => (
|
|
55
|
+
<Checkbox.Root size="lg">
|
|
56
|
+
<Checkbox.Indicator />
|
|
57
|
+
</Checkbox.Root>
|
|
58
|
+
),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const Disabled: Story = {
|
|
62
|
+
render: () => (
|
|
63
|
+
<Checkbox.Root disabled>
|
|
64
|
+
<Checkbox.Indicator />
|
|
65
|
+
</Checkbox.Root>
|
|
66
|
+
),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const WithLabel: Story = {
|
|
70
|
+
render: () => (
|
|
71
|
+
<Field.Root>
|
|
72
|
+
<Field.Label>
|
|
73
|
+
<Checkbox.Root>
|
|
74
|
+
<Checkbox.Indicator />
|
|
75
|
+
</Checkbox.Root>
|
|
76
|
+
Accept terms and conditions
|
|
77
|
+
</Field.Label>
|
|
78
|
+
</Field.Root>
|
|
79
|
+
),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const Controlled: Story = {
|
|
83
|
+
render: () => {
|
|
84
|
+
const [checked, setChecked] = useState(false);
|
|
85
|
+
return (
|
|
86
|
+
<div className="space-y-4">
|
|
87
|
+
<Field.Root>
|
|
88
|
+
<Field.Label>
|
|
89
|
+
<Checkbox.Root
|
|
90
|
+
checked={checked}
|
|
91
|
+
onCheckedChange={setChecked}
|
|
92
|
+
>
|
|
93
|
+
<Checkbox.Indicator />
|
|
94
|
+
</Checkbox.Root>
|
|
95
|
+
Subscribe to newsletter
|
|
96
|
+
</Field.Label>
|
|
97
|
+
</Field.Root>
|
|
98
|
+
<p className="text-sm text-gray-600">Status: {checked ? 'Subscribed' : 'Not subscribed'}</p>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const AllSizes: Story = {
|
|
105
|
+
render: () => (
|
|
106
|
+
<div className="flex items-center gap-4">
|
|
107
|
+
<Checkbox.Root size="sm">
|
|
108
|
+
<Checkbox.Indicator />
|
|
109
|
+
</Checkbox.Root>
|
|
110
|
+
<Checkbox.Root size="md">
|
|
111
|
+
<Checkbox.Indicator />
|
|
112
|
+
</Checkbox.Root>
|
|
113
|
+
<Checkbox.Root size="lg">
|
|
114
|
+
<Checkbox.Indicator />
|
|
115
|
+
</Checkbox.Root>
|
|
116
|
+
</div>
|
|
117
|
+
),
|
|
118
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Checkbox as BaseUICheckbox } from '@base-ui/react/checkbox';
|
|
2
|
+
import { tv } from '@regardio/tailwind/utils';
|
|
3
|
+
import type { ComponentProps } from 'react';
|
|
4
|
+
|
|
5
|
+
const checkboxRoot = tv({
|
|
6
|
+
base: [
|
|
7
|
+
'h-4',
|
|
8
|
+
'w-4',
|
|
9
|
+
'rounded',
|
|
10
|
+
'border',
|
|
11
|
+
'border-gray-300',
|
|
12
|
+
'bg-white',
|
|
13
|
+
'focus:outline-none',
|
|
14
|
+
'focus:ring-2',
|
|
15
|
+
'focus:ring-blue-500',
|
|
16
|
+
'focus:ring-offset-2',
|
|
17
|
+
'disabled:cursor-not-allowed',
|
|
18
|
+
'disabled:opacity-50',
|
|
19
|
+
'data-[checked]:bg-blue-600',
|
|
20
|
+
'data-[checked]:border-blue-600',
|
|
21
|
+
'transition-colors',
|
|
22
|
+
'duration-200',
|
|
23
|
+
'cursor-pointer',
|
|
24
|
+
],
|
|
25
|
+
defaultVariants: {
|
|
26
|
+
size: 'md',
|
|
27
|
+
},
|
|
28
|
+
variants: {
|
|
29
|
+
size: {
|
|
30
|
+
lg: ['h-5', 'w-5'],
|
|
31
|
+
md: ['h-4', 'w-4'],
|
|
32
|
+
sm: ['h-3', 'w-3'],
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const checkboxIndicator = tv({
|
|
38
|
+
base: ['flex', 'items-center', 'justify-center', 'text-white', 'data-[unchecked]:invisible'],
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export type CheckboxSize = 'sm' | 'md' | 'lg';
|
|
42
|
+
|
|
43
|
+
export interface CheckboxRootProps
|
|
44
|
+
extends Omit<ComponentProps<typeof BaseUICheckbox.Root>, 'className'> {
|
|
45
|
+
className?: string;
|
|
46
|
+
size?: CheckboxSize;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface CheckboxIndicatorProps
|
|
50
|
+
extends Omit<ComponentProps<typeof BaseUICheckbox.Indicator>, 'className'> {
|
|
51
|
+
className?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const CheckboxRoot = ({ className, size = 'md', ...props }: CheckboxRootProps) => {
|
|
55
|
+
return (
|
|
56
|
+
<BaseUICheckbox.Root
|
|
57
|
+
className={checkboxRoot({
|
|
58
|
+
className,
|
|
59
|
+
size,
|
|
60
|
+
})}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const CheckboxIndicator = ({ className, children, ...props }: CheckboxIndicatorProps) => {
|
|
67
|
+
return (
|
|
68
|
+
<BaseUICheckbox.Indicator
|
|
69
|
+
className={checkboxIndicator({ className })}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{children || (
|
|
73
|
+
<svg
|
|
74
|
+
fill="none"
|
|
75
|
+
height="12"
|
|
76
|
+
stroke="currentColor"
|
|
77
|
+
strokeWidth="2"
|
|
78
|
+
viewBox="0 0 12 12"
|
|
79
|
+
width="12"
|
|
80
|
+
>
|
|
81
|
+
<polyline points="2,6 5,9 10,3" />
|
|
82
|
+
</svg>
|
|
83
|
+
)}
|
|
84
|
+
</BaseUICheckbox.Indicator>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const Checkbox = {
|
|
89
|
+
Indicator: CheckboxIndicator,
|
|
90
|
+
Root: CheckboxRoot,
|
|
91
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { CheckboxGroup as BaseUICheckboxGroup } from '@base-ui/react/checkbox-group';
|
|
2
|
+
import { tv } from '@regardio/tailwind/utils';
|
|
3
|
+
import type { ComponentProps } from 'react';
|
|
4
|
+
|
|
5
|
+
const checkboxGroup = tv({
|
|
6
|
+
base: ['flex', 'flex-col', 'gap-2'],
|
|
7
|
+
defaultVariants: {
|
|
8
|
+
orientation: 'vertical',
|
|
9
|
+
},
|
|
10
|
+
variants: {
|
|
11
|
+
orientation: {
|
|
12
|
+
horizontal: ['flex-row', 'gap-4'],
|
|
13
|
+
vertical: ['flex-col', 'gap-2'],
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export type CheckboxGroupOrientation = 'horizontal' | 'vertical';
|
|
19
|
+
|
|
20
|
+
export interface CheckboxGroupProps
|
|
21
|
+
extends Omit<ComponentProps<typeof BaseUICheckboxGroup>, 'className'> {
|
|
22
|
+
className?: string;
|
|
23
|
+
orientation?: CheckboxGroupOrientation;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const CheckboxGroup = ({
|
|
27
|
+
className,
|
|
28
|
+
orientation = 'vertical',
|
|
29
|
+
...props
|
|
30
|
+
}: CheckboxGroupProps) => {
|
|
31
|
+
return (
|
|
32
|
+
<BaseUICheckboxGroup
|
|
33
|
+
className={checkboxGroup({
|
|
34
|
+
className,
|
|
35
|
+
orientation,
|
|
36
|
+
})}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Checkbox } from '../checkbox';
|
|
3
|
+
import { Input } from '../input';
|
|
4
|
+
import { Field } from './field';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
argTypes: {
|
|
8
|
+
variant: {
|
|
9
|
+
control: 'select',
|
|
10
|
+
description: 'Field variant',
|
|
11
|
+
options: ['default', 'required'],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
component: Field.Root,
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: 'centered',
|
|
17
|
+
},
|
|
18
|
+
tags: ['autodocs'],
|
|
19
|
+
title: 'Components/Field',
|
|
20
|
+
} satisfies Meta<typeof Field.Root>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {
|
|
26
|
+
render: () => (
|
|
27
|
+
<Field.Root>
|
|
28
|
+
<Field.Label>Email Address</Field.Label>
|
|
29
|
+
<Input placeholder="Enter your email" />
|
|
30
|
+
<Field.Description>We'll never share your email with anyone else.</Field.Description>
|
|
31
|
+
</Field.Root>
|
|
32
|
+
),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const WithError: Story = {
|
|
36
|
+
render: () => (
|
|
37
|
+
<Field.Root>
|
|
38
|
+
<Field.Label variant="error">Email Address</Field.Label>
|
|
39
|
+
<Input
|
|
40
|
+
placeholder="Enter your email"
|
|
41
|
+
variant="error"
|
|
42
|
+
/>
|
|
43
|
+
<Field.Error>This field is required</Field.Error>
|
|
44
|
+
</Field.Root>
|
|
45
|
+
),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Required: Story = {
|
|
49
|
+
render: () => (
|
|
50
|
+
<Field.Root variant="required">
|
|
51
|
+
<Field.Label>Full Name</Field.Label>
|
|
52
|
+
<Input placeholder="Enter your full name" />
|
|
53
|
+
<Field.Description>Please enter your first and last name.</Field.Description>
|
|
54
|
+
</Field.Root>
|
|
55
|
+
),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const HorizontalLayout: Story = {
|
|
59
|
+
render: () => (
|
|
60
|
+
<Field.Root>
|
|
61
|
+
<Field.Item layout="horizontal">
|
|
62
|
+
<Checkbox.Root>
|
|
63
|
+
<Checkbox.Indicator />
|
|
64
|
+
</Checkbox.Root>
|
|
65
|
+
<Field.Label>Subscribe to newsletter</Field.Label>
|
|
66
|
+
</Field.Item>
|
|
67
|
+
<Field.Description>Receive updates about new features</Field.Description>
|
|
68
|
+
</Field.Root>
|
|
69
|
+
),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const MultipleFields: Story = {
|
|
73
|
+
render: () => (
|
|
74
|
+
<div className="space-y-6">
|
|
75
|
+
<Field.Root>
|
|
76
|
+
<Field.Label>First Name</Field.Label>
|
|
77
|
+
<Input placeholder="Enter your first name" />
|
|
78
|
+
</Field.Root>
|
|
79
|
+
<Field.Root>
|
|
80
|
+
<Field.Label>Last Name</Field.Label>
|
|
81
|
+
<Input placeholder="Enter your last name" />
|
|
82
|
+
</Field.Root>
|
|
83
|
+
<Field.Root variant="required">
|
|
84
|
+
<Field.Label>Email</Field.Label>
|
|
85
|
+
<Input
|
|
86
|
+
placeholder="Enter your email"
|
|
87
|
+
type="email"
|
|
88
|
+
/>
|
|
89
|
+
<Field.Description>We'll never share your email.</Field.Description>
|
|
90
|
+
</Field.Root>
|
|
91
|
+
</div>
|
|
92
|
+
),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const WithCustomClass: Story = {
|
|
96
|
+
render: () => (
|
|
97
|
+
<Field.Root className="bg-gray-50 p-4 rounded-lg">
|
|
98
|
+
<Field.Label className="text-blue-600">Custom Field</Field.Label>
|
|
99
|
+
<Input
|
|
100
|
+
className="bg-white border-blue-300"
|
|
101
|
+
placeholder="Custom styled input"
|
|
102
|
+
/>
|
|
103
|
+
</Field.Root>
|
|
104
|
+
),
|
|
105
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { Field } from './field';
|
|
4
|
+
|
|
5
|
+
describe('Field', () => {
|
|
6
|
+
it('renders FieldRoot with children', () => {
|
|
7
|
+
render(
|
|
8
|
+
<Field.Root>
|
|
9
|
+
<Field.Label>Test Label</Field.Label>
|
|
10
|
+
<Field.Control />
|
|
11
|
+
</Field.Root>,
|
|
12
|
+
);
|
|
13
|
+
expect(screen.getByText('Test Label')).toBeInTheDocument();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('renders FieldDescription', () => {
|
|
17
|
+
render(
|
|
18
|
+
<Field.Root>
|
|
19
|
+
<Field.Description>Test Description</Field.Description>
|
|
20
|
+
</Field.Root>,
|
|
21
|
+
);
|
|
22
|
+
expect(screen.getByText('Test Description')).toBeInTheDocument();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('renders FieldError', () => {
|
|
26
|
+
render(
|
|
27
|
+
<Field.Root invalid>
|
|
28
|
+
<Field.Error match={true}>Test Error</Field.Error>
|
|
29
|
+
</Field.Root>,
|
|
30
|
+
);
|
|
31
|
+
expect(screen.getByText('Test Error')).toBeInTheDocument();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders FieldItem with layout', () => {
|
|
35
|
+
render(
|
|
36
|
+
<Field.Item layout="horizontal">
|
|
37
|
+
<span>Test Content</span>
|
|
38
|
+
</Field.Item>,
|
|
39
|
+
);
|
|
40
|
+
expect(screen.getByText('Test Content')).toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('applies custom className to FieldRoot', () => {
|
|
44
|
+
render(
|
|
45
|
+
<Field.Root className="custom-field">
|
|
46
|
+
<Field.Label>Test</Field.Label>
|
|
47
|
+
</Field.Root>,
|
|
48
|
+
);
|
|
49
|
+
const fieldRoot = screen.getByText('Test').parentElement;
|
|
50
|
+
expect(fieldRoot).toHaveClass('custom-field');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('applies variant styles to FieldLabel', () => {
|
|
54
|
+
render(
|
|
55
|
+
<Field.Root>
|
|
56
|
+
<Field.Label variant="error">Error Label</Field.Label>
|
|
57
|
+
</Field.Root>,
|
|
58
|
+
);
|
|
59
|
+
expect(screen.getByText('Error Label')).toHaveClass('text-red-600');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Field as BaseUIField } from '@base-ui/react/field';
|
|
2
|
+
import { tv } from '@regardio/tailwind/utils';
|
|
3
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
const fieldRootVariants = {
|
|
6
|
+
default: [],
|
|
7
|
+
required: ['after:content-["*"]', 'after:ml-1', 'after:text-red-500'],
|
|
8
|
+
} as const;
|
|
9
|
+
|
|
10
|
+
const fieldRoot = tv({
|
|
11
|
+
base: ['space-y-1'],
|
|
12
|
+
defaultVariants: {
|
|
13
|
+
variant: 'default',
|
|
14
|
+
},
|
|
15
|
+
variants: {
|
|
16
|
+
variant: fieldRootVariants,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const fieldLabel = tv({
|
|
21
|
+
base: ['block', 'text-sm', 'font-medium', 'text-gray-700', 'mb-1'],
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: 'default',
|
|
24
|
+
},
|
|
25
|
+
variants: {
|
|
26
|
+
variant: {
|
|
27
|
+
default: [],
|
|
28
|
+
error: ['text-red-600'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const fieldDescription = tv({
|
|
34
|
+
base: ['text-sm', 'text-gray-500', 'mt-1'],
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
variant: 'default',
|
|
37
|
+
},
|
|
38
|
+
variants: {
|
|
39
|
+
variant: {
|
|
40
|
+
default: [],
|
|
41
|
+
error: ['text-red-600'],
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const fieldError = tv({
|
|
47
|
+
base: ['text-sm', 'text-red-600', 'mt-1'],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const fieldItem = tv({
|
|
51
|
+
base: ['flex', 'flex-col'],
|
|
52
|
+
defaultVariants: {
|
|
53
|
+
layout: 'default',
|
|
54
|
+
},
|
|
55
|
+
variants: {
|
|
56
|
+
layout: {
|
|
57
|
+
default: [],
|
|
58
|
+
horizontal: ['flex-row', 'items-center', 'gap-2'],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export type FieldRootVariant = keyof typeof fieldRootVariants;
|
|
64
|
+
export type FieldLabelVariant = 'default' | 'error';
|
|
65
|
+
export type FieldDescriptionVariant = 'default' | 'error';
|
|
66
|
+
export type FieldItemLayout = 'default' | 'horizontal';
|
|
67
|
+
|
|
68
|
+
export interface FieldRootProps extends Omit<ComponentProps<typeof BaseUIField.Root>, 'className'> {
|
|
69
|
+
className?: string;
|
|
70
|
+
variant?: FieldRootVariant;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface FieldLabelProps
|
|
74
|
+
extends Omit<ComponentProps<typeof BaseUIField.Label>, 'className'> {
|
|
75
|
+
className?: string;
|
|
76
|
+
variant?: FieldLabelVariant;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface FieldDescriptionProps
|
|
80
|
+
extends Omit<ComponentProps<typeof BaseUIField.Description>, 'className'> {
|
|
81
|
+
className?: string;
|
|
82
|
+
variant?: FieldDescriptionVariant;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface FieldErrorProps
|
|
86
|
+
extends Omit<ComponentProps<typeof BaseUIField.Error>, 'className'> {
|
|
87
|
+
className?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface FieldItemProps extends ComponentProps<'div'> {
|
|
91
|
+
className?: string;
|
|
92
|
+
layout?: FieldItemLayout;
|
|
93
|
+
children: ReactNode;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const FieldRoot = ({ className, variant, ...props }: FieldRootProps) => {
|
|
97
|
+
return (
|
|
98
|
+
<BaseUIField.Root
|
|
99
|
+
className={fieldRoot({
|
|
100
|
+
className,
|
|
101
|
+
variant,
|
|
102
|
+
})}
|
|
103
|
+
{...props}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const FieldLabel = ({ className, variant, ...props }: FieldLabelProps) => {
|
|
109
|
+
return (
|
|
110
|
+
<BaseUIField.Label
|
|
111
|
+
className={fieldLabel({
|
|
112
|
+
className,
|
|
113
|
+
variant,
|
|
114
|
+
})}
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const FieldDescription = ({ className, variant, ...props }: FieldDescriptionProps) => {
|
|
121
|
+
return (
|
|
122
|
+
<BaseUIField.Description
|
|
123
|
+
className={fieldDescription({
|
|
124
|
+
className,
|
|
125
|
+
variant,
|
|
126
|
+
})}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const FieldError = ({ className, ...props }: FieldErrorProps) => {
|
|
133
|
+
return (
|
|
134
|
+
<BaseUIField.Error
|
|
135
|
+
className={fieldError({
|
|
136
|
+
className,
|
|
137
|
+
})}
|
|
138
|
+
{...props}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const FieldItem = ({ className, layout, children, ...props }: FieldItemProps) => {
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
className={fieldItem({
|
|
147
|
+
className,
|
|
148
|
+
layout,
|
|
149
|
+
})}
|
|
150
|
+
{...props}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const Field = {
|
|
158
|
+
Control: BaseUIField.Control,
|
|
159
|
+
Description: FieldDescription,
|
|
160
|
+
Error: FieldError,
|
|
161
|
+
Item: FieldItem,
|
|
162
|
+
Label: FieldLabel,
|
|
163
|
+
Root: FieldRoot,
|
|
164
|
+
Validity: BaseUIField.Validity,
|
|
165
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
FieldDescriptionProps,
|
|
3
|
+
FieldDescriptionVariant,
|
|
4
|
+
FieldErrorProps,
|
|
5
|
+
FieldItemLayout,
|
|
6
|
+
FieldItemProps,
|
|
7
|
+
FieldLabelProps,
|
|
8
|
+
FieldLabelVariant,
|
|
9
|
+
FieldRootProps,
|
|
10
|
+
FieldRootVariant,
|
|
11
|
+
} from './field';
|
|
12
|
+
export { Field } from './field';
|