@latte-macchiat-io/latte-vanilla-components 0.0.276 → 0.0.278
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/Form/TextField/Input/index.tsx +4 -5
- package/src/components/Form/TextField/Textarea/index.tsx +6 -8
- package/src/components/Form/TextField/index.tsx +21 -18
- package/src/components/Paragraph/export.tsx +2 -0
- package/src/components/Paragraph/index.tsx +12 -0
- package/src/components/Paragraph/stories.tsx +29 -0
- package/src/components/Paragraph/styles.css.ts +60 -0
- package/src/components/Paragraph/theme.ts +24 -0
- package/src/theme/baseThemeValues.ts +3 -0
- package/src/theme/contract.css.ts +11 -0
- package/src/theme/createTheme.ts +3 -0
package/package.json
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
import { clsx } from 'clsx';
|
2
|
-
import { inputRecipe
|
2
|
+
import { inputRecipe } from './styles.css';
|
3
3
|
|
4
4
|
import { InputType } from './types';
|
5
5
|
|
6
|
-
export type InputProps = React.InputHTMLAttributes<HTMLInputElement> &
|
7
|
-
|
8
|
-
|
9
|
-
};
|
6
|
+
export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
7
|
+
type?: InputType;
|
8
|
+
};
|
10
9
|
|
11
10
|
export const Input = ({ name, type = 'text', value, required, placeholder, className }: InputProps) => (
|
12
11
|
<input id={name} name={name} type={type} value={value} required={required} placeholder={placeholder} className={clsx(inputRecipe(), className)} />
|
@@ -1,13 +1,11 @@
|
|
1
1
|
import { clsx } from 'clsx';
|
2
2
|
|
3
|
-
import { textareaRecipe
|
3
|
+
import { textareaRecipe } from './styles.css';
|
4
4
|
|
5
|
-
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement> &
|
6
|
-
|
7
|
-
|
8
|
-
hasError?: boolean;
|
9
|
-
};
|
5
|
+
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement> & {
|
6
|
+
name: string;
|
7
|
+
};
|
10
8
|
|
11
|
-
export const Textarea = ({ name,
|
12
|
-
<textarea id={name} rows={rows} name={name} className={clsx(textareaRecipe(
|
9
|
+
export const Textarea = ({ name, rows, className }: TextareaProps) => (
|
10
|
+
<textarea id={name} rows={rows} name={name} className={clsx(textareaRecipe(), className)} />
|
13
11
|
);
|
@@ -7,43 +7,46 @@ import { InputType } from './Input/types';
|
|
7
7
|
import { errorMessage, messageContainer, textFieldRecipe } from './styles.css';
|
8
8
|
import { Textarea } from './Textarea';
|
9
9
|
|
10
|
-
|
10
|
+
type CommonProps = {
|
11
11
|
name: string;
|
12
12
|
label?: string;
|
13
|
-
value?: string;
|
14
|
-
rows?: number;
|
15
|
-
required?: boolean;
|
16
|
-
placeholder?: string;
|
17
13
|
errors?: string | string[];
|
18
14
|
type?: InputType | 'textarea';
|
19
|
-
onChange?: (e: { target: { value: string | undefined } }) => void | undefined;
|
20
15
|
};
|
21
16
|
|
17
|
+
export type TextFieldProps =
|
18
|
+
| (React.InputHTMLAttributes<HTMLInputElement> & CommonProps)
|
19
|
+
| (React.TextareaHTMLAttributes<HTMLTextAreaElement> & CommonProps);
|
20
|
+
|
22
21
|
export const TextField = (props: TextFieldProps) => {
|
22
|
+
const { label, name, errors, type, className, ...rest } = props;
|
23
23
|
const hasErrors = useMemo(() => {
|
24
|
-
if (!
|
25
|
-
if (Array.isArray(
|
26
|
-
return Boolean(
|
27
|
-
}, [
|
24
|
+
if (!errors) return false;
|
25
|
+
if (Array.isArray(errors)) return errors.length > 0;
|
26
|
+
return Boolean(errors);
|
27
|
+
}, [errors]);
|
28
28
|
|
29
|
-
const isTextarea =
|
29
|
+
const isTextarea = type === 'textarea';
|
30
30
|
|
31
31
|
return (
|
32
|
-
<div className={clsx(textFieldRecipe(),
|
33
|
-
{
|
34
|
-
|
35
|
-
{isTextarea ? <Textarea {...props} /> : <Input {...props} />}
|
32
|
+
<div className={clsx(textFieldRecipe(), className)}>
|
33
|
+
{label && <label htmlFor={name}>{label}</label>}
|
36
34
|
|
35
|
+
{isTextarea ? (
|
36
|
+
<Textarea {...(rest as React.TextareaHTMLAttributes<HTMLTextAreaElement>)} name={name} />
|
37
|
+
) : (
|
38
|
+
<Input {...(rest as React.InputHTMLAttributes<HTMLInputElement>)} name={name} type={type as InputType} />
|
39
|
+
)}
|
37
40
|
{hasErrors && (
|
38
41
|
<div className={messageContainer}>
|
39
|
-
{Array.isArray(
|
40
|
-
|
42
|
+
{Array.isArray(errors) ? (
|
43
|
+
errors.map((error, index) => (
|
41
44
|
<span key={index} className={errorMessage}>
|
42
45
|
{error}
|
43
46
|
</span>
|
44
47
|
))
|
45
48
|
) : (
|
46
|
-
<span className={errorMessage}>{
|
49
|
+
<span className={errorMessage}>{errors}</span>
|
47
50
|
)}
|
48
51
|
</div>
|
49
52
|
)}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { clsx } from 'clsx';
|
2
|
+
|
3
|
+
import { paragraphRecipe, type ParagraphVariants } from './styles.css';
|
4
|
+
|
5
|
+
export type ParagraphProps = React.HTMLAttributes<HTMLDivElement> &
|
6
|
+
ParagraphVariants & {
|
7
|
+
children: React.ReactNode;
|
8
|
+
};
|
9
|
+
|
10
|
+
export const Paragraph = ({ align, className, children }: ParagraphProps) => (
|
11
|
+
<p className={clsx(paragraphRecipe({ align }), className)}>{children}</p>
|
12
|
+
);
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
2
|
+
import React from 'react';
|
3
|
+
|
4
|
+
import { Paragraph } from '.';
|
5
|
+
|
6
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
|
7
|
+
const meta: Meta<typeof Paragraph> = {
|
8
|
+
title: 'Latte Components / 1. Global / Paragraph',
|
9
|
+
component: Paragraph,
|
10
|
+
parameters: {
|
11
|
+
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
|
12
|
+
layout: 'centered',
|
13
|
+
},
|
14
|
+
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
|
15
|
+
tags: ['autodocs'],
|
16
|
+
// More on argTypes: https://storybook.js.org/docs/api/argtypes
|
17
|
+
argTypes: {},
|
18
|
+
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
|
19
|
+
};
|
20
|
+
|
21
|
+
export default meta;
|
22
|
+
type Story = StoryObj<typeof meta>;
|
23
|
+
|
24
|
+
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
25
|
+
export const Default: Story = {
|
26
|
+
args: {
|
27
|
+
children: <>Lorem ispum</>,
|
28
|
+
},
|
29
|
+
};
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
2
|
+
import { recipe, RecipeVariants } from '@vanilla-extract/recipes';
|
3
|
+
|
4
|
+
import { queries } from '../../styles/mediaqueries';
|
5
|
+
import { themeContract } from '../../theme/contract.css';
|
6
|
+
import { generateResponsiveMedia } from '../../utils/generateResponsiveMedia';
|
7
|
+
|
8
|
+
export const paragraphRecipe = recipe(
|
9
|
+
{
|
10
|
+
base: [
|
11
|
+
{
|
12
|
+
fontWeight: 300,
|
13
|
+
|
14
|
+
'@media': {
|
15
|
+
...generateResponsiveMedia({
|
16
|
+
paddingBottom: themeContract.paragraph.paddingBottom,
|
17
|
+
}),
|
18
|
+
},
|
19
|
+
},
|
20
|
+
],
|
21
|
+
|
22
|
+
variants: {
|
23
|
+
align: {
|
24
|
+
left: {
|
25
|
+
alignItems: 'flex-start',
|
26
|
+
justifyContent: 'flex-start',
|
27
|
+
},
|
28
|
+
center: {
|
29
|
+
alignItems: 'center',
|
30
|
+
justifyContent: 'center',
|
31
|
+
},
|
32
|
+
right: {
|
33
|
+
alignItems: 'flex-end',
|
34
|
+
justifyContent: 'flex-end',
|
35
|
+
},
|
36
|
+
},
|
37
|
+
},
|
38
|
+
|
39
|
+
defaultVariants: {
|
40
|
+
align: 'left',
|
41
|
+
},
|
42
|
+
},
|
43
|
+
'actions'
|
44
|
+
);
|
45
|
+
|
46
|
+
globalStyle('p:last-of-type', {
|
47
|
+
paddingBottom: 0,
|
48
|
+
});
|
49
|
+
|
50
|
+
globalStyle('p + ul', {
|
51
|
+
paddingTop: 15,
|
52
|
+
|
53
|
+
'@media': {
|
54
|
+
[queries.sm]: {
|
55
|
+
paddingTop: 30,
|
56
|
+
},
|
57
|
+
},
|
58
|
+
});
|
59
|
+
|
60
|
+
export type ParagraphVariants = RecipeVariants<typeof paragraphRecipe>;
|
@@ -0,0 +1,24 @@
|
|
1
|
+
const themeParagraphBase = {
|
2
|
+
paragraph: {
|
3
|
+
paddingBottom: {
|
4
|
+
mobile: '15px',
|
5
|
+
sm: '15px',
|
6
|
+
md: '30px',
|
7
|
+
lg: '30px',
|
8
|
+
xl: '50px',
|
9
|
+
'2xl': '50px',
|
10
|
+
},
|
11
|
+
},
|
12
|
+
};
|
13
|
+
|
14
|
+
export const themeParagraphLight = {
|
15
|
+
paragraph: {
|
16
|
+
...themeParagraphBase.paragraph,
|
17
|
+
},
|
18
|
+
};
|
19
|
+
|
20
|
+
export const themeParagraphDark = {
|
21
|
+
paragraph: {
|
22
|
+
...themeParagraphBase.paragraph,
|
23
|
+
},
|
24
|
+
};
|
@@ -15,6 +15,7 @@ import { themeModalDark, themeModalLight } from '../components/Modal/theme';
|
|
15
15
|
import { themeNavDark, themeNavLight } from '../components/Nav/theme';
|
16
16
|
import { themeNavLegalDark, themeNavLegalLight } from '../components/NavLegal/theme';
|
17
17
|
import { themeNavSocialDark, themeNavSocialLight } from '../components/NavSocial/theme';
|
18
|
+
import { themeParagraphDark, themeParagraphLight } from '../components/Paragraph/theme';
|
18
19
|
import { themeSectionDark, themeSectionLight } from '../components/Section/theme';
|
19
20
|
import { themeVideoDark, themeVideoLight } from '../components/Video/theme';
|
20
21
|
|
@@ -124,6 +125,7 @@ export const baseLightTheme = {
|
|
124
125
|
|
125
126
|
...themeSectionLight,
|
126
127
|
...themeHeadingLight,
|
128
|
+
...themeParagraphLight,
|
127
129
|
|
128
130
|
...themeActionsLight,
|
129
131
|
...themeButtonLight,
|
@@ -255,6 +257,7 @@ export const baseDarkTheme = {
|
|
255
257
|
|
256
258
|
...themeSectionDark,
|
257
259
|
...themeHeadingDark,
|
260
|
+
...themeParagraphDark,
|
258
261
|
|
259
262
|
...themeActionsDark,
|
260
263
|
...themeButtonDark,
|
@@ -374,6 +374,17 @@ export const themeContract = createGlobalThemeContract({
|
|
374
374
|
},
|
375
375
|
},
|
376
376
|
|
377
|
+
paragraph: {
|
378
|
+
paddingBottom: {
|
379
|
+
mobile: 'latte-paragraph-paddingBottom-mobile',
|
380
|
+
sm: 'latte-paragraph-paddingBottom-sm',
|
381
|
+
md: 'latte-paragraph-paddingBottom-md',
|
382
|
+
lg: 'latte-paragraph-paddingBottom-lg',
|
383
|
+
xl: 'latte-paragraph-paddingBottom-xl',
|
384
|
+
'2xl': 'latte-paragraph-paddingBottom-2xl',
|
385
|
+
},
|
386
|
+
},
|
387
|
+
|
377
388
|
columns: {
|
378
389
|
gap: {
|
379
390
|
mobile: 'latte-columns-gap-mobile',
|
package/src/theme/createTheme.ts
CHANGED
@@ -21,6 +21,7 @@ export type ThemeOverrides = {
|
|
21
21
|
icon?: Partial<typeof baseLightTheme.icon>;
|
22
22
|
columns?: Partial<typeof baseLightTheme.columns>;
|
23
23
|
heading?: Partial<typeof baseLightTheme.heading>;
|
24
|
+
paragraph?: Partial<typeof baseLightTheme.paragraph>;
|
24
25
|
modal?: Partial<typeof baseLightTheme.modal>;
|
25
26
|
keyNumber?: Partial<typeof baseLightTheme.keyNumber>;
|
26
27
|
header?: Partial<typeof baseLightTheme.header>;
|
@@ -53,6 +54,7 @@ const createAppTheme = (selector: string, overrides: ThemeOverrides = {}) => {
|
|
53
54
|
icon: { ...baseLightTheme.icon, ...overrides.icon },
|
54
55
|
columns: { ...baseLightTheme.columns, ...overrides.columns },
|
55
56
|
heading: { ...baseLightTheme.heading, ...overrides.heading },
|
57
|
+
paragraph: { ...baseLightTheme.paragraph, ...overrides.paragraph },
|
56
58
|
modal: { ...baseLightTheme.modal, ...overrides.modal },
|
57
59
|
keyNumber: { ...baseLightTheme.keyNumber, ...overrides.keyNumber },
|
58
60
|
header: { ...baseLightTheme.header, ...overrides.header },
|
@@ -86,6 +88,7 @@ const createAppDarkTheme = (selector: string, overrides: ThemeOverrides = {}) =>
|
|
86
88
|
icon: { ...baseDarkTheme.icon, ...overrides.icon },
|
87
89
|
columns: { ...baseDarkTheme.columns, ...overrides.columns },
|
88
90
|
heading: { ...baseDarkTheme.heading, ...overrides.heading },
|
91
|
+
paragraph: { ...baseDarkTheme.paragraph, ...overrides.paragraph },
|
89
92
|
modal: { ...baseDarkTheme.modal, ...overrides.modal },
|
90
93
|
keyNumber: { ...baseDarkTheme.keyNumber, ...overrides.keyNumber },
|
91
94
|
header: { ...baseDarkTheme.header, ...overrides.header },
|