@addev-be/ui 0.12.3 → 0.13.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/package.json +1 -1
- package/src/Icons.tsx +4 -0
- package/src/components/forms/Form/Checkbox.tsx +13 -0
- package/src/components/forms/Form/FormGroup.tsx +27 -0
- package/src/components/forms/Form/Input.tsx +13 -0
- package/src/components/forms/Form/InputWithLabel.tsx +18 -0
- package/src/components/forms/Form/Select.tsx +49 -0
- package/src/components/forms/Form/TextArea.tsx +13 -0
- package/src/components/forms/Form/index.tsx +37 -0
- package/src/components/forms/Form/styles.ts +105 -0
- package/src/components/forms/Form/types.ts +7 -0
- package/src/components/forms/index.ts +2 -0
- package/src/components/layout/Columns.ts +20 -0
- package/src/components/layout/Grid/index.tsx +8 -0
- package/src/components/layout/Grid/styles.ts +34 -0
- package/src/components/layout/Masonry/index.tsx +29 -0
- package/src/components/layout/Masonry/styles.ts +20 -0
- package/src/components/layout/index.ts +4 -1
- package/src/providers/ThemeProvider/helpers.ts +11 -0
- package/src/services/smartRequests.ts +0 -0
package/package.json
CHANGED
package/src/Icons.tsx
CHANGED
|
@@ -79,6 +79,10 @@ export const LoadingIcon: FC<IconFCProps> = ({ className, ...props }) => (
|
|
|
79
79
|
<SpinnerIcon className={`animate-spin ${className}`} {...props} />
|
|
80
80
|
);
|
|
81
81
|
|
|
82
|
+
export const BlankIcon: FC<IconFCProps> = (props) => (
|
|
83
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props} />
|
|
84
|
+
);
|
|
85
|
+
|
|
82
86
|
export {
|
|
83
87
|
AngleLeftIcon,
|
|
84
88
|
AngleRightIcon,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FC, InputHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
import { InputContainerProps } from './types';
|
|
4
|
+
import { InputWithLabel } from './InputWithLabel';
|
|
5
|
+
import { StyledCheckbox } from './styles';
|
|
6
|
+
|
|
7
|
+
export const Checkbox: FC<
|
|
8
|
+
InputContainerProps & InputHTMLAttributes<HTMLInputElement>
|
|
9
|
+
> = ({ label, icon, ...props }) => (
|
|
10
|
+
<InputWithLabel label={label} icon={icon}>
|
|
11
|
+
<StyledCheckbox {...props} />
|
|
12
|
+
</InputWithLabel>
|
|
13
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { FC, PropsWithChildren } from 'react';
|
|
2
|
+
import { FormGroupContainer, FormGroupHeader } from './styles';
|
|
3
|
+
|
|
4
|
+
import { ThemeColorWithIntensity } from '../../../providers/ThemeProvider';
|
|
5
|
+
|
|
6
|
+
type FormGroupProps = PropsWithChildren<{
|
|
7
|
+
color?: ThemeColorWithIntensity;
|
|
8
|
+
title?: string;
|
|
9
|
+
subTitle?: string;
|
|
10
|
+
}>;
|
|
11
|
+
|
|
12
|
+
export const FormGroup: FC<FormGroupProps> = ({
|
|
13
|
+
children,
|
|
14
|
+
color,
|
|
15
|
+
title,
|
|
16
|
+
subTitle,
|
|
17
|
+
}) => (
|
|
18
|
+
<FormGroupContainer color={color}>
|
|
19
|
+
{(title || subTitle) && (
|
|
20
|
+
<FormGroupHeader>
|
|
21
|
+
{title && <h3>{title}</h3>}
|
|
22
|
+
{subTitle && <h4>{subTitle}</h4>}
|
|
23
|
+
</FormGroupHeader>
|
|
24
|
+
)}
|
|
25
|
+
{children}
|
|
26
|
+
</FormGroupContainer>
|
|
27
|
+
);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FC, InputHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
import { InputContainerProps } from './types';
|
|
4
|
+
import { InputWithLabel } from './InputWithLabel';
|
|
5
|
+
import { StyledInput } from './styles';
|
|
6
|
+
|
|
7
|
+
export const Input: FC<
|
|
8
|
+
InputContainerProps & InputHTMLAttributes<HTMLInputElement>
|
|
9
|
+
> = ({ label, icon, ...props }) => (
|
|
10
|
+
<InputWithLabel label={label} icon={icon}>
|
|
11
|
+
<StyledInput {...props} />
|
|
12
|
+
</InputWithLabel>
|
|
13
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { InputContainer } from './styles';
|
|
2
|
+
import { InputContainerProps } from './types';
|
|
3
|
+
|
|
4
|
+
export const InputWithLabel = ({
|
|
5
|
+
label,
|
|
6
|
+
icon: Icon,
|
|
7
|
+
children,
|
|
8
|
+
}: InputContainerProps) => (
|
|
9
|
+
<InputContainer>
|
|
10
|
+
{(label || Icon) && (
|
|
11
|
+
<span>
|
|
12
|
+
{Icon && <Icon />}
|
|
13
|
+
{label}
|
|
14
|
+
</span>
|
|
15
|
+
)}
|
|
16
|
+
{children}
|
|
17
|
+
</InputContainer>
|
|
18
|
+
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { InputContainerProps } from './types';
|
|
2
|
+
import { InputWithLabel } from './InputWithLabel';
|
|
3
|
+
import { SelectHTMLAttributes } from 'react';
|
|
4
|
+
import { StyledSelect } from './styles';
|
|
5
|
+
|
|
6
|
+
type SelectProps<T> = {
|
|
7
|
+
items: T[];
|
|
8
|
+
itemKey: keyof T | ((item: T, index: number) => string);
|
|
9
|
+
itemLabel: keyof T | ((item: T, index: number) => string);
|
|
10
|
+
readOnly?: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
export const Select = <T,>({
|
|
15
|
+
icon,
|
|
16
|
+
label,
|
|
17
|
+
items,
|
|
18
|
+
itemKey,
|
|
19
|
+
itemLabel,
|
|
20
|
+
readOnly,
|
|
21
|
+
...props
|
|
22
|
+
}: InputContainerProps &
|
|
23
|
+
SelectProps<T> &
|
|
24
|
+
SelectHTMLAttributes<HTMLSelectElement>) => {
|
|
25
|
+
const keyGetter =
|
|
26
|
+
typeof itemKey === 'function'
|
|
27
|
+
? itemKey
|
|
28
|
+
: (item: T) => String(item[itemKey]);
|
|
29
|
+
const labelGetter =
|
|
30
|
+
typeof itemLabel === 'function'
|
|
31
|
+
? itemLabel
|
|
32
|
+
: (item: T) => String(item[itemLabel]);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<InputWithLabel label={label} icon={icon}>
|
|
36
|
+
<StyledSelect {...props} disabled={readOnly}>
|
|
37
|
+
{items.map((item, index) => {
|
|
38
|
+
const key = keyGetter(item, index);
|
|
39
|
+
const label = labelGetter(item, index);
|
|
40
|
+
return (
|
|
41
|
+
<option key={key} value={key}>
|
|
42
|
+
{label}
|
|
43
|
+
</option>
|
|
44
|
+
);
|
|
45
|
+
})}
|
|
46
|
+
</StyledSelect>
|
|
47
|
+
</InputWithLabel>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FC, TextareaHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
import { InputContainerProps } from './types';
|
|
4
|
+
import { InputWithLabel } from './InputWithLabel';
|
|
5
|
+
import { StyledTextArea } from './styles';
|
|
6
|
+
|
|
7
|
+
export const TextArea: FC<
|
|
8
|
+
InputContainerProps & TextareaHTMLAttributes<HTMLTextAreaElement>
|
|
9
|
+
> = ({ label, icon, ...props }) => (
|
|
10
|
+
<InputWithLabel label={label} icon={icon}>
|
|
11
|
+
<StyledTextArea {...props} />
|
|
12
|
+
</InputWithLabel>
|
|
13
|
+
);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { FC, PropsWithChildren, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Checkbox } from './Checkbox';
|
|
4
|
+
import { FormGroup } from './FormGroup';
|
|
5
|
+
import { Input } from './Input';
|
|
6
|
+
import { Select } from './Select';
|
|
7
|
+
import { TextArea } from './TextArea';
|
|
8
|
+
|
|
9
|
+
export type FormProps = PropsWithChildren<{
|
|
10
|
+
onSubmit?: () => void;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
export type FormFC = FC<FormProps> & {
|
|
14
|
+
Group: typeof FormGroup;
|
|
15
|
+
Input: typeof Input;
|
|
16
|
+
Select: typeof Select;
|
|
17
|
+
Checkbox: typeof Checkbox;
|
|
18
|
+
TextArea: typeof TextArea;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Form = ({ children, onSubmit }: FormProps) => {
|
|
22
|
+
const handleSubmit = useCallback(
|
|
23
|
+
(e: React.FormEvent<HTMLFormElement>) => {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
onSubmit?.();
|
|
26
|
+
},
|
|
27
|
+
[onSubmit]
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return <form onSubmit={handleSubmit}>{children}</form>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
Form.Group = FormGroup;
|
|
34
|
+
Form.Input = Input;
|
|
35
|
+
Form.Select = Select;
|
|
36
|
+
Form.Checkbox = Checkbox;
|
|
37
|
+
Form.TextArea = TextArea;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
import { ThemeColorWithIntensity } from '../../../providers/ThemeProvider';
|
|
4
|
+
import { getColor } from '../../../providers/ThemeProvider/helpers';
|
|
5
|
+
|
|
6
|
+
export const FormGroupContainer = styled.fieldset<{
|
|
7
|
+
color?: ThemeColorWithIntensity;
|
|
8
|
+
}>`
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
background: ${({ color = 'neutral-100' }) =>
|
|
12
|
+
`linear-gradient(180deg, var(--color-${getColor(
|
|
13
|
+
color
|
|
14
|
+
)}-200) 0%, var(--color-neutral-100) var(--space-10))`};
|
|
15
|
+
border-radius: var(--rounded-md);
|
|
16
|
+
box-shadow: var(--shadow-md);
|
|
17
|
+
padding: var(--space-4);
|
|
18
|
+
margin: 0;
|
|
19
|
+
border: 1px solid var(--color-neutral-100);
|
|
20
|
+
gap: var(--space-2);
|
|
21
|
+
border-left: 4px solid ${({ color = 'neutral' }) => `var(--color-${color})`};
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const FormGroupHeader = styled.div`
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: var(--space-1);
|
|
28
|
+
margin-bottom: var(--space-1);
|
|
29
|
+
|
|
30
|
+
& > h3 {
|
|
31
|
+
margin: 0;
|
|
32
|
+
font-size: var(--text-lg);
|
|
33
|
+
font-weight: bold;
|
|
34
|
+
}
|
|
35
|
+
& > h4 {
|
|
36
|
+
margin: 0;
|
|
37
|
+
font-size: var(--text-sm);
|
|
38
|
+
font-weight: var(--font-normal);
|
|
39
|
+
color: var(--color-neutral-500);
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
export const InputContainer = styled.label<{ readOnly?: boolean }>`
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: row;
|
|
46
|
+
align-items: flex-start;
|
|
47
|
+
gap: var(--space-2);
|
|
48
|
+
padding: var(--space-1) var(--space-2);
|
|
49
|
+
border: 1px solid var(--color-neutral-200);
|
|
50
|
+
background-color: ${({ readOnly }) =>
|
|
51
|
+
readOnly ? 'var(--color-neutral-50)' : 'var(--color-neutral-0)'};
|
|
52
|
+
|
|
53
|
+
& > span {
|
|
54
|
+
color: var(--color-neutral-500);
|
|
55
|
+
font-size: var(--text-base);
|
|
56
|
+
padding: var(--space-1);
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: row;
|
|
59
|
+
gap: var(--space-2);
|
|
60
|
+
line-height: 1.25;
|
|
61
|
+
|
|
62
|
+
& > svg {
|
|
63
|
+
width: var(--space-4);
|
|
64
|
+
height: var(--space-4);
|
|
65
|
+
fill: var(--color-neutral-500);
|
|
66
|
+
flex-shrink: 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&:focus-within {
|
|
71
|
+
outline: 2px solid var(--color-primary-500);
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
const inputCss = css`
|
|
76
|
+
font-family: inherit;
|
|
77
|
+
font-size: var(--text-base);
|
|
78
|
+
color: var(--color-neutral-900);
|
|
79
|
+
border: none;
|
|
80
|
+
padding: var(--space-1);
|
|
81
|
+
flex: 1;
|
|
82
|
+
text-align: right;
|
|
83
|
+
|
|
84
|
+
&:focus {
|
|
85
|
+
outline: none;
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
export const StyledInput = styled.input`
|
|
90
|
+
${inputCss}
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
export const StyledSelect = styled.select`
|
|
94
|
+
${inputCss}
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
export const StyledTextArea = styled.textarea`
|
|
98
|
+
${inputCss}
|
|
99
|
+
resize: none;
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
export const StyledCheckbox = styled.input.attrs({ type: 'checkbox' })`
|
|
103
|
+
width: var(--space-4);
|
|
104
|
+
height: var(--space-4);
|
|
105
|
+
`;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export type ColumnsProps = {
|
|
4
|
+
columns?: number | string[];
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const Columns = styled.div<ColumnsProps>`
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-gap: var(--space-2);
|
|
10
|
+
grid-template-columns: ${({ columns }) =>
|
|
11
|
+
typeof columns === 'number'
|
|
12
|
+
? `repeat(${columns}, 1fr)`
|
|
13
|
+
: columns?.join(' ') || 'none'};
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
export const Column = styled.div`
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: var(--space-2);
|
|
20
|
+
`;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
type GridProps = {
|
|
4
|
+
columns?: number;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const GridContainer = styled.div<GridProps>`
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: repeat(
|
|
10
|
+
${({ columns = 12 }) => columns},
|
|
11
|
+
minmax(0, 1fr)
|
|
12
|
+
);
|
|
13
|
+
grid-template-rows: masonry;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
type GridItemProps = {
|
|
17
|
+
row?: number;
|
|
18
|
+
col?: number;
|
|
19
|
+
rowSpan?: number;
|
|
20
|
+
colSpan?: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const GridItemContainer = styled.div.attrs<GridItemProps>(
|
|
24
|
+
({ col, colSpan, row, rowSpan }) => ({
|
|
25
|
+
style: {
|
|
26
|
+
gridColumn: [col, !!colSpan && `span ${colSpan}`]
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.join(' / '),
|
|
29
|
+
gridRow: [row, !!rowSpan && `span ${rowSpan}`]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(' / '),
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
)``;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FC, PropsWithChildren, ReactNode, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
MasonryColumnContainer,
|
|
4
|
+
MasonryContainer,
|
|
5
|
+
MasonryContainerProps,
|
|
6
|
+
} from './styles';
|
|
7
|
+
|
|
8
|
+
export const Masonry: FC<PropsWithChildren<MasonryContainerProps>> = ({
|
|
9
|
+
children,
|
|
10
|
+
columns = 3,
|
|
11
|
+
}) => {
|
|
12
|
+
const childrenByColumn = useMemo(() => {
|
|
13
|
+
const arr = Array.from({ length: columns }, (): ReactNode[] => []);
|
|
14
|
+
if (Array.isArray(children)) {
|
|
15
|
+
children.forEach((child, index) => {
|
|
16
|
+
arr[index % columns].push(child);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return arr;
|
|
20
|
+
}, [children, columns]);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<MasonryContainer columns={columns}>
|
|
24
|
+
{childrenByColumn.map((column, index) => (
|
|
25
|
+
<MasonryColumnContainer key={index}>{column}</MasonryColumnContainer>
|
|
26
|
+
))}
|
|
27
|
+
</MasonryContainer>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export type MasonryContainerProps = {
|
|
4
|
+
columns?: number;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const MasonryContainer = styled.div<MasonryContainerProps>`
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-gap: var(--space-2);
|
|
10
|
+
grid-template-columns: repeat(
|
|
11
|
+
${({ columns = 4 }) => columns},
|
|
12
|
+
minmax(0, 1fr)
|
|
13
|
+
);
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
export const MasonryColumnContainer = styled.div`
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: var(--space-2);
|
|
20
|
+
`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ThemeColorWithIntensity } from './types';
|
|
2
|
+
|
|
3
|
+
export const getColor = (colorWithIntensity: ThemeColorWithIntensity) => {
|
|
4
|
+
const [colorName] = colorWithIntensity.split('-');
|
|
5
|
+
return colorName;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const getIntensity = (colorWithIntensity: ThemeColorWithIntensity) => {
|
|
9
|
+
const [, intensity] = colorWithIntensity.split('-');
|
|
10
|
+
return intensity;
|
|
11
|
+
};
|
|
File without changes
|