atelier-ui-react 1.0.2
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/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/index.cjs +909 -0
- package/dist/index.css +962 -0
- package/dist/index.mjs +876 -0
- package/package.json +52 -0
- package/src/Button/Button.css +169 -0
- package/src/Button/Button.tsx +57 -0
- package/src/Button/Button.types.ts +19 -0
- package/src/Button/index.ts +2 -0
- package/src/Card/Card.css +73 -0
- package/src/Card/Card.tsx +79 -0
- package/src/Card/Card.types.ts +25 -0
- package/src/Card/index.ts +2 -0
- package/src/Checkbox/Checkbox.css +101 -0
- package/src/Checkbox/Checkbox.tsx +85 -0
- package/src/Checkbox/Checkbox.types.ts +9 -0
- package/src/Checkbox/index.ts +2 -0
- package/src/Divider/Divider.css +47 -0
- package/src/Divider/Divider.tsx +53 -0
- package/src/Divider/Divider.types.ts +12 -0
- package/src/Divider/index.ts +2 -0
- package/src/Heading/Heading.css +30 -0
- package/src/Heading/Heading.tsx +50 -0
- package/src/Heading/Heading.types.ts +16 -0
- package/src/Heading/index.ts +2 -0
- package/src/Icon/Icon.css +26 -0
- package/src/Icon/Icon.tsx +79 -0
- package/src/Icon/Icon.types.ts +13 -0
- package/src/Icon/index.ts +2 -0
- package/src/Input/Input.css +145 -0
- package/src/Input/Input.tsx +86 -0
- package/src/Input/Input.types.ts +15 -0
- package/src/Input/index.ts +2 -0
- package/src/Link/Link.css +50 -0
- package/src/Link/Link.tsx +64 -0
- package/src/Link/Link.types.ts +13 -0
- package/src/Link/index.ts +2 -0
- package/src/Select/Select.css +115 -0
- package/src/Select/Select.tsx +109 -0
- package/src/Select/Select.types.ts +19 -0
- package/src/Select/index.ts +2 -0
- package/src/Text/Text.css +49 -0
- package/src/Text/Text.tsx +46 -0
- package/src/Text/Text.types.ts +19 -0
- package/src/Text/index.ts +2 -0
- package/src/index.ts +34 -0
- package/src/layout/Container/Container.css +16 -0
- package/src/layout/Container/Container.tsx +51 -0
- package/src/layout/Container/Container.types.ts +12 -0
- package/src/layout/Container/index.ts +2 -0
- package/src/layout/Flex/Flex.css +34 -0
- package/src/layout/Flex/Flex.tsx +56 -0
- package/src/layout/Flex/Flex.types.ts +18 -0
- package/src/layout/Flex/index.ts +2 -0
- package/src/layout/Grid/Grid.css +18 -0
- package/src/layout/Grid/Grid.tsx +61 -0
- package/src/layout/Grid/Grid.types.ts +17 -0
- package/src/layout/Grid/index.ts +2 -0
- package/src/layout/Stack/Stack.css +50 -0
- package/src/layout/Stack/Stack.tsx +70 -0
- package/src/layout/Stack/Stack.types.ts +17 -0
- package/src/layout/Stack/index.ts +2 -0
- package/src/styles/globals.css +28 -0
- package/src/styles/reset.css +44 -0
- package/src/theme/ThemeProvider.tsx +83 -0
- package/src/theme/dark.css +62 -0
- package/src/theme/index.ts +2 -0
- package/src/theme/light.css +62 -0
- package/src/theme/theme.types.ts +19 -0
- package/src/theme/tokens.css +61 -0
- package/src/utils/index.ts +44 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import type { LinkProps } from './Link.types';
|
|
3
|
+
import { cx } from '../utils';
|
|
4
|
+
import './Link.css';
|
|
5
|
+
|
|
6
|
+
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
|
|
7
|
+
{
|
|
8
|
+
variant = 'default',
|
|
9
|
+
size = 'md',
|
|
10
|
+
external = false,
|
|
11
|
+
iconLeft,
|
|
12
|
+
iconRight,
|
|
13
|
+
className,
|
|
14
|
+
target,
|
|
15
|
+
rel,
|
|
16
|
+
children,
|
|
17
|
+
...rest
|
|
18
|
+
},
|
|
19
|
+
ref
|
|
20
|
+
) {
|
|
21
|
+
const classes = cx(
|
|
22
|
+
'ui-link',
|
|
23
|
+
`ui-link--variant-${variant}`,
|
|
24
|
+
`ui-link--size-${size}`,
|
|
25
|
+
className
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const resolvedTarget = external ? '_blank' : target;
|
|
29
|
+
const resolvedRel = external ? (rel ? `${rel} noopener noreferrer` : 'noopener noreferrer') : rel;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<a
|
|
33
|
+
ref={ref}
|
|
34
|
+
className={classes}
|
|
35
|
+
target={resolvedTarget}
|
|
36
|
+
rel={resolvedRel}
|
|
37
|
+
{...rest}
|
|
38
|
+
>
|
|
39
|
+
{iconLeft && <span className="ui-link__icon-left">{iconLeft}</span>}
|
|
40
|
+
<span>{children}</span>
|
|
41
|
+
{iconRight && <span className="ui-link__icon-right">{iconRight}</span>}
|
|
42
|
+
{external && !iconRight && (
|
|
43
|
+
<svg
|
|
44
|
+
width="12"
|
|
45
|
+
height="12"
|
|
46
|
+
viewBox="0 0 24 24"
|
|
47
|
+
fill="none"
|
|
48
|
+
stroke="currentColor"
|
|
49
|
+
strokeWidth="2"
|
|
50
|
+
strokeLinecap="round"
|
|
51
|
+
strokeLinejoin="round"
|
|
52
|
+
className="ui-link__external-icon"
|
|
53
|
+
aria-hidden="true"
|
|
54
|
+
>
|
|
55
|
+
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
|
56
|
+
<polyline points="15 3 21 3 21 9" />
|
|
57
|
+
<line x1="10" y1="14" x2="21" y2="3" />
|
|
58
|
+
</svg>
|
|
59
|
+
)}
|
|
60
|
+
</a>
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
Link.displayName = 'Link';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type LinkVariant = 'default' | 'subtle' | 'underline';
|
|
4
|
+
export type LinkSize = 'sm' | 'md' | 'lg';
|
|
5
|
+
|
|
6
|
+
export interface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
7
|
+
variant?: LinkVariant;
|
|
8
|
+
size?: LinkSize;
|
|
9
|
+
external?: boolean;
|
|
10
|
+
iconLeft?: React.ReactNode;
|
|
11
|
+
iconRight?: React.ReactNode;
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
.ui-select-wrapper {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--ui-space-2xs);
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
font-family: var(--ui-font-sans);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.ui-select-wrapper--full-width {
|
|
10
|
+
width: 100%;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/* Label */
|
|
14
|
+
.ui-select-label {
|
|
15
|
+
font-size: var(--ui-font-size-xs);
|
|
16
|
+
font-weight: var(--ui-font-weight-medium);
|
|
17
|
+
color: var(--ui-color-foreground);
|
|
18
|
+
user-select: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ui-select-label--disabled {
|
|
22
|
+
color: var(--ui-color-foreground-muted);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Select Container */
|
|
26
|
+
.ui-select-container {
|
|
27
|
+
position: relative;
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
width: 100%;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Native select styled cleanly */
|
|
34
|
+
.ui-select {
|
|
35
|
+
width: 100%;
|
|
36
|
+
font-family: var(--ui-font-sans);
|
|
37
|
+
color: var(--ui-color-foreground);
|
|
38
|
+
background-color: var(--ui-color-surface);
|
|
39
|
+
border: 1px solid var(--ui-color-border);
|
|
40
|
+
border-radius: var(--ui-radius-md);
|
|
41
|
+
appearance: none;
|
|
42
|
+
-webkit-appearance: none;
|
|
43
|
+
padding-right: 36px;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
outline: none;
|
|
46
|
+
transition:
|
|
47
|
+
border-color var(--ui-transition-fast),
|
|
48
|
+
box-shadow var(--ui-transition-fast),
|
|
49
|
+
background-color var(--ui-transition-fast);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.ui-select:focus {
|
|
53
|
+
border-color: var(--ui-color-focus-border);
|
|
54
|
+
box-shadow: 0 0 0 var(--ui-focus-ring-width) var(--ui-color-focus-ring);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/* Sizes */
|
|
58
|
+
.ui-select--size-sm {
|
|
59
|
+
height: 32px;
|
|
60
|
+
padding-left: 10px;
|
|
61
|
+
font-size: var(--ui-font-size-xs);
|
|
62
|
+
border-radius: var(--ui-radius-sm);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.ui-select--size-md {
|
|
66
|
+
height: 40px;
|
|
67
|
+
padding-left: 12px;
|
|
68
|
+
font-size: var(--ui-font-size-sm);
|
|
69
|
+
border-radius: var(--ui-radius-md);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.ui-select--size-lg {
|
|
73
|
+
height: 48px;
|
|
74
|
+
padding-left: 16px;
|
|
75
|
+
font-size: var(--ui-font-size-md);
|
|
76
|
+
border-radius: var(--ui-radius-md);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* States */
|
|
80
|
+
.ui-select:disabled {
|
|
81
|
+
background-color: var(--ui-color-surface-inset);
|
|
82
|
+
color: var(--ui-color-foreground-muted);
|
|
83
|
+
border-color: var(--ui-color-border-subtle);
|
|
84
|
+
cursor: not-allowed;
|
|
85
|
+
opacity: 0.7;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ui-select--error,
|
|
89
|
+
.ui-select--error:focus {
|
|
90
|
+
border-color: var(--ui-color-danger);
|
|
91
|
+
box-shadow: 0 0 0 var(--ui-focus-ring-width) rgba(220, 38, 38, 0.2);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Chevron arrow */
|
|
95
|
+
.ui-select-icon {
|
|
96
|
+
position: absolute;
|
|
97
|
+
right: 12px;
|
|
98
|
+
pointer-events: none;
|
|
99
|
+
color: var(--ui-color-foreground-muted);
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Helper / Error */
|
|
106
|
+
.ui-select-helper {
|
|
107
|
+
font-size: var(--ui-font-size-xs);
|
|
108
|
+
color: var(--ui-color-foreground-muted);
|
|
109
|
+
line-height: 1.4;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.ui-select-helper--error {
|
|
113
|
+
color: var(--ui-color-danger);
|
|
114
|
+
font-weight: var(--ui-font-weight-medium);
|
|
115
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import React, { forwardRef, useId } from 'react';
|
|
2
|
+
import type { SelectProps } from './Select.types';
|
|
3
|
+
import { cx } from '../utils';
|
|
4
|
+
import './Select.css';
|
|
5
|
+
|
|
6
|
+
export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(
|
|
7
|
+
{
|
|
8
|
+
options,
|
|
9
|
+
placeholder,
|
|
10
|
+
size = 'md',
|
|
11
|
+
label,
|
|
12
|
+
helperText,
|
|
13
|
+
error,
|
|
14
|
+
fullWidth = false,
|
|
15
|
+
disabled = false,
|
|
16
|
+
id: explicitId,
|
|
17
|
+
className,
|
|
18
|
+
children,
|
|
19
|
+
value,
|
|
20
|
+
defaultValue,
|
|
21
|
+
...rest
|
|
22
|
+
},
|
|
23
|
+
ref
|
|
24
|
+
) {
|
|
25
|
+
const generatedId = useId();
|
|
26
|
+
const id = explicitId || generatedId;
|
|
27
|
+
const helperId = `${id}-helper`;
|
|
28
|
+
const hasError = Boolean(error);
|
|
29
|
+
const errorMessage = typeof error === 'string' ? error : undefined;
|
|
30
|
+
|
|
31
|
+
const wrapperClasses = cx(
|
|
32
|
+
'ui-select-wrapper',
|
|
33
|
+
fullWidth && 'ui-select-wrapper--full-width'
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const selectClasses = cx(
|
|
37
|
+
'ui-select',
|
|
38
|
+
`ui-select--size-${size}`,
|
|
39
|
+
hasError && 'ui-select--error',
|
|
40
|
+
className
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className={wrapperClasses}>
|
|
45
|
+
{label && (
|
|
46
|
+
<label
|
|
47
|
+
htmlFor={id}
|
|
48
|
+
className={cx('ui-select-label', disabled && 'ui-select-label--disabled')}
|
|
49
|
+
>
|
|
50
|
+
{label}
|
|
51
|
+
</label>
|
|
52
|
+
)}
|
|
53
|
+
|
|
54
|
+
<div className="ui-select-container">
|
|
55
|
+
<select
|
|
56
|
+
ref={ref}
|
|
57
|
+
id={id}
|
|
58
|
+
disabled={disabled}
|
|
59
|
+
value={value}
|
|
60
|
+
defaultValue={defaultValue}
|
|
61
|
+
aria-invalid={hasError}
|
|
62
|
+
aria-describedby={helperText || errorMessage ? helperId : undefined}
|
|
63
|
+
className={selectClasses}
|
|
64
|
+
{...rest}
|
|
65
|
+
>
|
|
66
|
+
{placeholder && (
|
|
67
|
+
<option value="" disabled>
|
|
68
|
+
{placeholder}
|
|
69
|
+
</option>
|
|
70
|
+
)}
|
|
71
|
+
|
|
72
|
+
{options
|
|
73
|
+
? options.map((opt) => (
|
|
74
|
+
<option key={opt.value} value={opt.value} disabled={opt.disabled}>
|
|
75
|
+
{opt.label}
|
|
76
|
+
</option>
|
|
77
|
+
))
|
|
78
|
+
: children}
|
|
79
|
+
</select>
|
|
80
|
+
|
|
81
|
+
<span className="ui-select-icon" aria-hidden="true">
|
|
82
|
+
<svg
|
|
83
|
+
width="16"
|
|
84
|
+
height="16"
|
|
85
|
+
viewBox="0 0 24 24"
|
|
86
|
+
fill="none"
|
|
87
|
+
stroke="currentColor"
|
|
88
|
+
strokeWidth="2"
|
|
89
|
+
strokeLinecap="round"
|
|
90
|
+
strokeLinejoin="round"
|
|
91
|
+
>
|
|
92
|
+
<polyline points="6 9 12 15 18 9" />
|
|
93
|
+
</svg>
|
|
94
|
+
</span>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
{(errorMessage || helperText) && (
|
|
98
|
+
<span
|
|
99
|
+
id={helperId}
|
|
100
|
+
className={cx('ui-select-helper', hasError && 'ui-select-helper--error')}
|
|
101
|
+
>
|
|
102
|
+
{errorMessage || helperText}
|
|
103
|
+
</span>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
Select.displayName = 'Select';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface SelectOption {
|
|
4
|
+
label: string;
|
|
5
|
+
value: string | number;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type SelectSize = 'sm' | 'md' | 'lg';
|
|
10
|
+
|
|
11
|
+
export interface SelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
|
|
12
|
+
options?: SelectOption[];
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
size?: SelectSize;
|
|
15
|
+
label?: React.ReactNode;
|
|
16
|
+
helperText?: React.ReactNode;
|
|
17
|
+
error?: string | boolean;
|
|
18
|
+
fullWidth?: boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.ui-text {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
font-family: var(--ui-font-sans);
|
|
5
|
+
line-height: var(--ui-line-height-normal);
|
|
6
|
+
color: var(--ui-color-foreground);
|
|
7
|
+
transition: color var(--ui-transition-fast);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Sizes */
|
|
11
|
+
.ui-text--size-xs { font-size: var(--ui-font-size-xs); }
|
|
12
|
+
.ui-text--size-sm { font-size: var(--ui-font-size-sm); }
|
|
13
|
+
.ui-text--size-md { font-size: var(--ui-font-size-md); }
|
|
14
|
+
.ui-text--size-lg { font-size: var(--ui-font-size-lg); }
|
|
15
|
+
.ui-text--size-xl { font-size: var(--ui-font-size-xl); }
|
|
16
|
+
.ui-text--size-2xl { font-size: var(--ui-font-size-2xl); }
|
|
17
|
+
|
|
18
|
+
/* Weights */
|
|
19
|
+
.ui-text--weight-normal { font-weight: var(--ui-font-weight-normal); }
|
|
20
|
+
.ui-text--weight-medium { font-weight: var(--ui-font-weight-medium); }
|
|
21
|
+
.ui-text--weight-semibold { font-weight: var(--ui-font-weight-semibold); }
|
|
22
|
+
.ui-text--weight-bold { font-weight: var(--ui-font-weight-bold); }
|
|
23
|
+
|
|
24
|
+
/* Colors */
|
|
25
|
+
.ui-text--color-default { color: var(--ui-color-foreground); }
|
|
26
|
+
.ui-text--color-muted { color: var(--ui-color-foreground-muted); }
|
|
27
|
+
.ui-text--color-subtle { color: var(--ui-color-foreground-subtle); }
|
|
28
|
+
.ui-text--color-primary { color: var(--ui-color-primary); }
|
|
29
|
+
.ui-text--color-success { color: var(--ui-color-success); }
|
|
30
|
+
.ui-text--color-warning { color: var(--ui-color-warning); }
|
|
31
|
+
.ui-text--color-danger { color: var(--ui-color-danger); }
|
|
32
|
+
.ui-text--color-inherit { color: inherit; }
|
|
33
|
+
|
|
34
|
+
/* Alignments */
|
|
35
|
+
.ui-text--align-left { text-align: left; }
|
|
36
|
+
.ui-text--align-center { text-align: center; }
|
|
37
|
+
.ui-text--align-right { text-align: right; }
|
|
38
|
+
.ui-text--align-justify { text-align: justify; }
|
|
39
|
+
|
|
40
|
+
/* Modifiers */
|
|
41
|
+
.ui-text--truncate {
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
text-overflow: ellipsis;
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.ui-text--mono {
|
|
48
|
+
font-family: var(--ui-font-mono);
|
|
49
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import type { TextProps } from './Text.types';
|
|
3
|
+
import { cx } from '../utils';
|
|
4
|
+
import './Text.css';
|
|
5
|
+
|
|
6
|
+
export const Text = forwardRef<HTMLElement, TextProps>(function Text(
|
|
7
|
+
{
|
|
8
|
+
as: Component = 'p',
|
|
9
|
+
size = 'md',
|
|
10
|
+
weight = 'normal',
|
|
11
|
+
color = 'default',
|
|
12
|
+
muted = false,
|
|
13
|
+
align,
|
|
14
|
+
truncate = false,
|
|
15
|
+
mono = false,
|
|
16
|
+
className,
|
|
17
|
+
children,
|
|
18
|
+
...rest
|
|
19
|
+
},
|
|
20
|
+
ref
|
|
21
|
+
) {
|
|
22
|
+
const resolvedColor = muted ? 'muted' : color;
|
|
23
|
+
|
|
24
|
+
const classes = cx(
|
|
25
|
+
'ui-text',
|
|
26
|
+
`ui-text--size-${size}`,
|
|
27
|
+
`ui-text--weight-${weight}`,
|
|
28
|
+
`ui-text--color-${resolvedColor}`,
|
|
29
|
+
align && `ui-text--align-${align}`,
|
|
30
|
+
truncate && 'ui-text--truncate',
|
|
31
|
+
mono && 'ui-text--mono',
|
|
32
|
+
className
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return React.createElement(
|
|
36
|
+
Component,
|
|
37
|
+
{
|
|
38
|
+
ref,
|
|
39
|
+
className: classes,
|
|
40
|
+
...rest,
|
|
41
|
+
},
|
|
42
|
+
children
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
Text.displayName = 'Text';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type TextElement = 'p' | 'span' | 'div' | 'label' | 'strong' | 'em' | 'small' | 'code';
|
|
4
|
+
export type TextSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
|
5
|
+
export type TextWeight = 'normal' | 'medium' | 'semibold' | 'bold';
|
|
6
|
+
export type TextColor = 'default' | 'muted' | 'subtle' | 'primary' | 'success' | 'warning' | 'danger' | 'inherit';
|
|
7
|
+
export type TextAlign = 'left' | 'center' | 'right' | 'justify';
|
|
8
|
+
|
|
9
|
+
export interface TextProps extends React.HTMLAttributes<HTMLElement> {
|
|
10
|
+
as?: TextElement;
|
|
11
|
+
size?: TextSize;
|
|
12
|
+
weight?: TextWeight;
|
|
13
|
+
color?: TextColor;
|
|
14
|
+
muted?: boolean;
|
|
15
|
+
align?: TextAlign;
|
|
16
|
+
truncate?: boolean;
|
|
17
|
+
mono?: boolean;
|
|
18
|
+
children?: React.ReactNode;
|
|
19
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atelier UI — Modern, minimalist, accessible React + TypeScript UI framework
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Foundation
|
|
6
|
+
export * from './Text';
|
|
7
|
+
export * from './Heading';
|
|
8
|
+
export * from './Icon';
|
|
9
|
+
export * from './Divider';
|
|
10
|
+
|
|
11
|
+
// Actions
|
|
12
|
+
export * from './Button';
|
|
13
|
+
export * from './Link';
|
|
14
|
+
|
|
15
|
+
// Forms
|
|
16
|
+
export * from './Input';
|
|
17
|
+
export * from './Checkbox';
|
|
18
|
+
export * from './Select';
|
|
19
|
+
|
|
20
|
+
// Containers
|
|
21
|
+
export * from './Card';
|
|
22
|
+
|
|
23
|
+
// Layout
|
|
24
|
+
export * from './layout/Stack';
|
|
25
|
+
export * from './layout/Flex';
|
|
26
|
+
export * from './layout/Grid';
|
|
27
|
+
export * from './layout/Container';
|
|
28
|
+
|
|
29
|
+
// Theme
|
|
30
|
+
export * from './theme';
|
|
31
|
+
|
|
32
|
+
// Utils
|
|
33
|
+
export { cx, getSpaceValue } from './utils';
|
|
34
|
+
export type { SpaceToken, ClassValue } from './utils';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.ui-container {
|
|
2
|
+
width: 100%;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.ui-container--centered {
|
|
7
|
+
margin-left: auto;
|
|
8
|
+
margin-right: auto;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Container max-widths */
|
|
12
|
+
.ui-container--size-sm { max-width: 640px; }
|
|
13
|
+
.ui-container--size-md { max-width: 768px; }
|
|
14
|
+
.ui-container--size-lg { max-width: 1024px; }
|
|
15
|
+
.ui-container--size-xl { max-width: 1280px; }
|
|
16
|
+
.ui-container--size-full { max-width: 100%; }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import type { ContainerProps } from './Container.types';
|
|
3
|
+
import { cx, getSpaceValue } from '../../utils';
|
|
4
|
+
import './Container.css';
|
|
5
|
+
|
|
6
|
+
export const Container = forwardRef<HTMLDivElement, ContainerProps>(function Container(
|
|
7
|
+
{
|
|
8
|
+
as: Component = 'div',
|
|
9
|
+
size = 'lg',
|
|
10
|
+
centered = true,
|
|
11
|
+
padding = 'md',
|
|
12
|
+
className,
|
|
13
|
+
style,
|
|
14
|
+
children,
|
|
15
|
+
...rest
|
|
16
|
+
},
|
|
17
|
+
ref
|
|
18
|
+
) {
|
|
19
|
+
const classes = cx(
|
|
20
|
+
'ui-container',
|
|
21
|
+
`ui-container--size-${size}`,
|
|
22
|
+
centered && 'ui-container--centered',
|
|
23
|
+
className
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
let paddingValue: string | undefined;
|
|
27
|
+
if (typeof padding === 'boolean') {
|
|
28
|
+
paddingValue = padding ? 'var(--ui-space-md)' : '0';
|
|
29
|
+
} else if (padding) {
|
|
30
|
+
paddingValue = getSpaceValue(padding);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const combinedStyle: React.CSSProperties = {
|
|
34
|
+
paddingLeft: paddingValue,
|
|
35
|
+
paddingRight: paddingValue,
|
|
36
|
+
...style,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return React.createElement(
|
|
40
|
+
Component,
|
|
41
|
+
{
|
|
42
|
+
ref,
|
|
43
|
+
className: classes,
|
|
44
|
+
style: combinedStyle,
|
|
45
|
+
...rest,
|
|
46
|
+
},
|
|
47
|
+
children
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
Container.displayName = 'Container';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SpaceToken } from '../../utils';
|
|
3
|
+
|
|
4
|
+
export type ContainerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
5
|
+
|
|
6
|
+
export interface ContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
as?: React.ElementType;
|
|
8
|
+
size?: ContainerSize;
|
|
9
|
+
centered?: boolean;
|
|
10
|
+
padding?: SpaceToken | boolean;
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
.ui-flex {
|
|
2
|
+
display: flex;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.ui-flex--inline {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/* Directions */
|
|
11
|
+
.ui-flex--direction-row { flex-direction: row; }
|
|
12
|
+
.ui-flex--direction-row-reverse { flex-direction: row-reverse; }
|
|
13
|
+
.ui-flex--direction-col { flex-direction: column; }
|
|
14
|
+
.ui-flex--direction-col-reverse { flex-direction: column-reverse; }
|
|
15
|
+
|
|
16
|
+
/* Align */
|
|
17
|
+
.ui-flex--align-start { align-items: flex-start; }
|
|
18
|
+
.ui-flex--align-center { align-items: center; }
|
|
19
|
+
.ui-flex--align-end { align-items: flex-end; }
|
|
20
|
+
.ui-flex--align-stretch { align-items: stretch; }
|
|
21
|
+
.ui-flex--align-baseline { align-items: baseline; }
|
|
22
|
+
|
|
23
|
+
/* Justify */
|
|
24
|
+
.ui-flex--justify-start { justify-content: flex-start; }
|
|
25
|
+
.ui-flex--justify-center { justify-content: center; }
|
|
26
|
+
.ui-flex--justify-end { justify-content: flex-end; }
|
|
27
|
+
.ui-flex--justify-between { justify-content: space-between; }
|
|
28
|
+
.ui-flex--justify-around { justify-content: space-around; }
|
|
29
|
+
.ui-flex--justify-evenly { justify-content: space-evenly; }
|
|
30
|
+
|
|
31
|
+
/* Wrap */
|
|
32
|
+
.ui-flex--wrap { flex-wrap: wrap; }
|
|
33
|
+
.ui-flex--wrap-nowrap { flex-wrap: nowrap; }
|
|
34
|
+
.ui-flex--wrap-reverse { flex-wrap: wrap-reverse; }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import type { FlexProps } from './Flex.types';
|
|
3
|
+
import { cx, getSpaceValue } from '../../utils';
|
|
4
|
+
import './Flex.css';
|
|
5
|
+
|
|
6
|
+
export const Flex = forwardRef<HTMLElement, FlexProps>(function Flex(
|
|
7
|
+
{
|
|
8
|
+
as: Component = 'div',
|
|
9
|
+
direction = 'row',
|
|
10
|
+
align,
|
|
11
|
+
justify,
|
|
12
|
+
gap,
|
|
13
|
+
wrap,
|
|
14
|
+
inline = false,
|
|
15
|
+
className,
|
|
16
|
+
style,
|
|
17
|
+
children,
|
|
18
|
+
...rest
|
|
19
|
+
},
|
|
20
|
+
ref
|
|
21
|
+
) {
|
|
22
|
+
const resolvedGap = getSpaceValue(gap);
|
|
23
|
+
|
|
24
|
+
const wrapClass =
|
|
25
|
+
typeof wrap === 'boolean'
|
|
26
|
+
? wrap ? 'ui-flex--wrap' : 'ui-flex--wrap-nowrap'
|
|
27
|
+
: wrap ? `ui-flex--wrap-${wrap}` : undefined;
|
|
28
|
+
|
|
29
|
+
const classes = cx(
|
|
30
|
+
'ui-flex',
|
|
31
|
+
inline && 'ui-flex--inline',
|
|
32
|
+
direction && `ui-flex--direction-${direction}`,
|
|
33
|
+
align && `ui-flex--align-${align}`,
|
|
34
|
+
justify && `ui-flex--justify-${justify}`,
|
|
35
|
+
wrapClass,
|
|
36
|
+
className
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const combinedStyle: React.CSSProperties = {
|
|
40
|
+
gap: resolvedGap,
|
|
41
|
+
...style,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return React.createElement(
|
|
45
|
+
Component,
|
|
46
|
+
{
|
|
47
|
+
ref,
|
|
48
|
+
className: classes,
|
|
49
|
+
style: combinedStyle,
|
|
50
|
+
...rest,
|
|
51
|
+
},
|
|
52
|
+
children
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
Flex.displayName = 'Flex';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { SpaceToken } from '../../utils';
|
|
3
|
+
|
|
4
|
+
export type FlexDirection = 'row' | 'row-reverse' | 'col' | 'col-reverse';
|
|
5
|
+
export type FlexAlign = 'start' | 'center' | 'end' | 'stretch' | 'baseline';
|
|
6
|
+
export type FlexJustify = 'start' | 'center' | 'end' | 'between' | 'around' | 'evenly';
|
|
7
|
+
export type FlexWrap = boolean | 'wrap' | 'nowrap' | 'wrap-reverse';
|
|
8
|
+
|
|
9
|
+
export interface FlexProps extends React.HTMLAttributes<HTMLElement> {
|
|
10
|
+
as?: React.ElementType;
|
|
11
|
+
direction?: FlexDirection;
|
|
12
|
+
align?: FlexAlign;
|
|
13
|
+
justify?: FlexJustify;
|
|
14
|
+
gap?: SpaceToken;
|
|
15
|
+
wrap?: FlexWrap;
|
|
16
|
+
inline?: boolean;
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
}
|