@latte-macchiat-io/latte-vanilla-components 0.0.553 → 0.0.555
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/package.json +1 -1
- package/src/components/Box/styles.css.ts +0 -2
- package/src/components/Form/Checkbox/export.tsx +2 -0
- package/src/components/Form/Checkbox/index.tsx +56 -0
- package/src/components/Form/Checkbox/stories.tsx +80 -0
- package/src/components/Form/Checkbox/styles.css.ts +62 -0
- package/src/components/Form/TextField/Input/export.tsx +0 -1
- package/src/components/Form/TextField/Input/styles.css.ts +1 -2
- package/src/components/Form/TextField/Label/export.tsx +0 -1
- package/src/components/Form/TextField/Label/index.tsx +7 -7
- package/src/components/Form/TextField/Label/styles.css.ts +1 -3
- package/src/components/Form/TextField/Textarea/export.tsx +0 -1
- package/src/components/Form/TextField/Textarea/styles.css.ts +1 -2
- package/src/components/Form/TextField/export.tsx +0 -1
- package/src/components/Form/TextField/styles.css.ts +1 -2
- package/src/components/Form/export.tsx +1 -0
- package/src/theme/contract.css.ts +1 -0
- package/src/components/Form/Select/theme.ts +0 -40
- package/src/utils/CleanRecipeVariantsType.ts +0 -12
package/package.json
CHANGED
|
@@ -162,7 +162,5 @@ export const boxContent = recipe({
|
|
|
162
162
|
},
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
// export type BoxVariants = NonNullable<CleanRecipeVariants<RecipeVariants<typeof box>>>;
|
|
166
|
-
// export type BoxContentVariants = NonNullable<CleanRecipeVariants<RecipeVariants<typeof boxContent>>>;
|
|
167
165
|
export type BoxVariants = RecipeVariants<typeof box>;
|
|
168
166
|
export type BoxContentVariants = RecipeVariants<typeof boxContent>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { checkboxInput, checkboxLabel, checkboxRecipe, CheckboxVariants, errorMessage, messageContainer } from './styles.css';
|
|
4
|
+
|
|
5
|
+
import { cn } from '../../../utils/styleOverride';
|
|
6
|
+
|
|
7
|
+
export type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> &
|
|
8
|
+
CheckboxVariants & {
|
|
9
|
+
name: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
errors?: string | string[];
|
|
12
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const Checkbox = ({ name, label, errors, labelPosition, required, checked, disabled, onChange, className }: CheckboxProps) => {
|
|
16
|
+
const hasErrors = useMemo(() => {
|
|
17
|
+
if (!errors) return false;
|
|
18
|
+
if (Array.isArray(errors)) return errors.length > 0;
|
|
19
|
+
return Boolean(errors);
|
|
20
|
+
}, [errors]);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className={cn(checkboxRecipe({ labelPosition }), className)}>
|
|
24
|
+
<input
|
|
25
|
+
id={name}
|
|
26
|
+
name={name}
|
|
27
|
+
type="checkbox"
|
|
28
|
+
required={required}
|
|
29
|
+
checked={checked}
|
|
30
|
+
disabled={disabled}
|
|
31
|
+
onChange={onChange}
|
|
32
|
+
className={checkboxInput}
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
{label && (
|
|
36
|
+
<label htmlFor={name} className={checkboxLabel}>
|
|
37
|
+
{label} {required && '*'}
|
|
38
|
+
</label>
|
|
39
|
+
)}
|
|
40
|
+
|
|
41
|
+
{hasErrors && (
|
|
42
|
+
<div className={messageContainer}>
|
|
43
|
+
{Array.isArray(errors) ? (
|
|
44
|
+
errors.map((error, index) => (
|
|
45
|
+
<span key={index} className={errorMessage}>
|
|
46
|
+
{error}
|
|
47
|
+
</span>
|
|
48
|
+
))
|
|
49
|
+
) : (
|
|
50
|
+
<span className={errorMessage}>{errors}</span>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
|
|
3
|
+
import { Checkbox } from '.';
|
|
4
|
+
|
|
5
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
|
6
|
+
const meta: Meta<typeof Checkbox> = {
|
|
7
|
+
title: '5. Forms / Checkbox',
|
|
8
|
+
component: Checkbox,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'centered',
|
|
11
|
+
},
|
|
12
|
+
tags: ['autodocs'],
|
|
13
|
+
argTypes: {},
|
|
14
|
+
decorators: [
|
|
15
|
+
(Story) => (
|
|
16
|
+
<div style={{ minWidth: '320px', maxWidth: '500px', width: '100%' }}>
|
|
17
|
+
<Story />
|
|
18
|
+
</div>
|
|
19
|
+
),
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
type Story = StoryObj<typeof meta>;
|
|
25
|
+
|
|
26
|
+
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
name: 'agree',
|
|
30
|
+
label: 'I agree to the terms and conditions',
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Checked: Story = {
|
|
35
|
+
args: {
|
|
36
|
+
name: 'newsletter',
|
|
37
|
+
label: 'Subscribe to newsletter',
|
|
38
|
+
checked: true,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Required: Story = {
|
|
43
|
+
args: {
|
|
44
|
+
name: 'required-checkbox',
|
|
45
|
+
label: 'Accept terms',
|
|
46
|
+
required: true,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const LabelLeft: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
name: 'label-left',
|
|
53
|
+
label: 'Label on the left',
|
|
54
|
+
labelPosition: 'left',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const Disabled: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
name: 'disabled-checkbox',
|
|
61
|
+
label: 'This option is disabled',
|
|
62
|
+
disabled: true,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const WithError: Story = {
|
|
67
|
+
args: {
|
|
68
|
+
name: 'terms',
|
|
69
|
+
label: 'I accept the terms',
|
|
70
|
+
errors: 'You must accept the terms to continue',
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const WithMultipleErrors: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
name: 'gdpr',
|
|
77
|
+
label: 'I accept the GDPR policy',
|
|
78
|
+
errors: ['This field is required', 'You must be 18 or older to accept'],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { style } from '@vanilla-extract/css';
|
|
2
|
+
import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
|
|
3
|
+
|
|
4
|
+
import { themeContract } from '../../../theme/contract.css';
|
|
5
|
+
|
|
6
|
+
export const checkboxRecipe = recipe(
|
|
7
|
+
{
|
|
8
|
+
base: {
|
|
9
|
+
display: 'flex',
|
|
10
|
+
alignItems: 'center',
|
|
11
|
+
gap: themeContract.space.sm,
|
|
12
|
+
transition: 'all 0.2s ease-in-out',
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
variants: {
|
|
16
|
+
labelPosition: {
|
|
17
|
+
left: {
|
|
18
|
+
flexDirection: 'row-reverse',
|
|
19
|
+
justifyContent: 'flex-end',
|
|
20
|
+
},
|
|
21
|
+
right: {
|
|
22
|
+
flexDirection: 'row',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
labelPosition: 'right',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
'checkbox'
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const checkboxInput = style({
|
|
35
|
+
appearance: 'none',
|
|
36
|
+
cursor: 'pointer',
|
|
37
|
+
flexShrink: 0,
|
|
38
|
+
width: '1.25rem',
|
|
39
|
+
height: '1.25rem',
|
|
40
|
+
border: themeContract.form.textField.border,
|
|
41
|
+
borderRadius: themeContract.form.textField.borderRadius,
|
|
42
|
+
backgroundColor: themeContract.form.textField.backgroundColor,
|
|
43
|
+
transition: 'all 0.2s ease-in-out',
|
|
44
|
+
|
|
45
|
+
selectors: {
|
|
46
|
+
'&:checked': {
|
|
47
|
+
backgroundColor: themeContract.colors.primary,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export const checkboxLabel = style({
|
|
53
|
+
cursor: 'pointer',
|
|
54
|
+
fontFamily: themeContract.fonts.body,
|
|
55
|
+
color: themeContract.form.textField.color,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export const messageContainer = style({});
|
|
59
|
+
|
|
60
|
+
export const errorMessage = style({});
|
|
61
|
+
|
|
62
|
+
export type CheckboxVariants = RecipeVariants<typeof checkboxRecipe>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { recipe
|
|
1
|
+
import { recipe } from '@vanilla-extract/recipes';
|
|
2
2
|
|
|
3
3
|
import { generateResponsiveMedia } from '../../../../styles/utils/generateResponsiveMedia';
|
|
4
4
|
import { themeContract } from '../../../../theme/contract.css';
|
|
@@ -35,4 +35,3 @@ export const inputRecipe = recipe(
|
|
|
35
35
|
'input'
|
|
36
36
|
);
|
|
37
37
|
|
|
38
|
-
export type InputVariants = RecipeVariants<typeof inputRecipe>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { labelRecipe
|
|
1
|
+
import { labelRecipe } from './styles.css';
|
|
2
2
|
import { cn } from '../../../../utils/styleOverride';
|
|
3
3
|
|
|
4
|
-
export type LabelProps =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export type LabelProps = {
|
|
5
|
+
name: string;
|
|
6
|
+
label: string;
|
|
7
|
+
required: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
10
|
|
|
11
11
|
export const Label = ({ label, name, required, className }: LabelProps) => (
|
|
12
12
|
<label htmlFor={name} className={cn(labelRecipe(), className)}>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { recipe
|
|
1
|
+
import { recipe } from '@vanilla-extract/recipes';
|
|
2
2
|
|
|
3
3
|
export const labelRecipe = recipe(
|
|
4
4
|
{
|
|
@@ -9,5 +9,3 @@ export const labelRecipe = recipe(
|
|
|
9
9
|
},
|
|
10
10
|
'text-field-label'
|
|
11
11
|
);
|
|
12
|
-
|
|
13
|
-
export type LabelVariants = RecipeVariants<typeof labelRecipe>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { recipe
|
|
1
|
+
import { recipe } from '@vanilla-extract/recipes';
|
|
2
2
|
|
|
3
3
|
import { generateResponsiveMedia } from '../../../../styles/utils/generateResponsiveMedia';
|
|
4
4
|
import { themeContract } from '../../../../theme/contract.css';
|
|
@@ -37,4 +37,3 @@ export const textareaRecipe = recipe(
|
|
|
37
37
|
'text-area'
|
|
38
38
|
);
|
|
39
39
|
|
|
40
|
-
export type TextareaVariants = RecipeVariants<typeof textareaRecipe>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { style } from '@vanilla-extract/css';
|
|
2
|
-
import { recipe
|
|
2
|
+
import { recipe } from '@vanilla-extract/recipes';
|
|
3
3
|
import { themeContract } from '../../../theme/contract.css';
|
|
4
4
|
|
|
5
5
|
const textFieldBase = style({
|
|
@@ -29,4 +29,3 @@ export const textFieldRecipe = recipe(
|
|
|
29
29
|
);
|
|
30
30
|
|
|
31
31
|
export { messageContainer, errorMessage };
|
|
32
|
-
export type TextFieldVariants = RecipeVariants<typeof textFieldRecipe>;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const themeActionsBase = {
|
|
2
|
-
actions: {
|
|
3
|
-
gap: {
|
|
4
|
-
mobile: '15px',
|
|
5
|
-
sm: '15px',
|
|
6
|
-
md: '30px',
|
|
7
|
-
lg: '30px',
|
|
8
|
-
xl: '50px',
|
|
9
|
-
'2xl': '50px',
|
|
10
|
-
},
|
|
11
|
-
paddingTop: {
|
|
12
|
-
mobile: '15px',
|
|
13
|
-
sm: '15px',
|
|
14
|
-
md: '30px',
|
|
15
|
-
lg: '30px',
|
|
16
|
-
xl: '50px',
|
|
17
|
-
'2xl': '50px',
|
|
18
|
-
},
|
|
19
|
-
paddingBottom: {
|
|
20
|
-
mobile: '15px',
|
|
21
|
-
sm: '15px',
|
|
22
|
-
md: '30px',
|
|
23
|
-
lg: '30px',
|
|
24
|
-
xl: '50px',
|
|
25
|
-
'2xl': '50px',
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const themeActionsLight = {
|
|
31
|
-
actions: {
|
|
32
|
-
...themeActionsBase.actions,
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const themeActionsDark = {
|
|
37
|
-
actions: {
|
|
38
|
-
...themeActionsBase.actions,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Utility type that normalizes Vanilla Extract's RecipeVariants by removing
|
|
3
|
-
* optional keys and filtering out `undefined` from both the variant object
|
|
4
|
-
* and its individual properties.
|
|
5
|
-
*
|
|
6
|
-
* Ensures that variants become strictly typed, fully defined, and safe to use
|
|
7
|
-
* in Storybook controls and component props.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
export type CleanRecipeVariants<T> = NonNullable<{
|
|
11
|
-
[K in keyof T]-?: Exclude<T[K], undefined>;
|
|
12
|
-
}>;
|