ff-ds-ui 0.0.2 → 0.0.4
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/.husky/pre-commit +2 -0
- package/.prettierrc +7 -0
- package/.storybook/main.ts +14 -0
- package/.storybook/preview.ts +21 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/LICENSE +21 -21
- package/README.md +13 -73
- package/eslint.config.js +29 -23
- package/package.json +57 -36
- package/src/components/Avatar/Avatar.scss +90 -0
- package/src/components/Avatar/Avatar.tsx +40 -0
- package/src/components/Avatar/__stories__/Avatar.stories.tsx +21 -0
- package/src/components/Avatar/__test__/utils.test.ts +76 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Avatar/utils.tsx +26 -0
- package/src/components/Breadcrumbs/Breadcrumbs.tsx +40 -0
- package/src/components/Breadcrumbs/__stories__/Breadcrumbs.stories.tsx +20 -0
- package/src/components/Breadcrumbs/index.ts +1 -0
- package/src/components/Button/Button.scss +245 -0
- package/src/components/Button/Button.tsx +34 -0
- package/src/components/Button/__stories__/Button.stories.tsx +168 -0
- package/src/components/Button/const.ts +26 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Card/Card.scss +13 -0
- package/src/components/Card/Card.tsx +12 -0
- package/src/components/Card/__stories__/Card.stories.tsx +18 -0
- package/src/components/Card/index.ts +1 -0
- package/src/components/Checkbox/Checkbox.scss +28 -0
- package/src/components/Checkbox/Checkbox.tsx +23 -0
- package/src/components/Checkbox/__stories__/Checkbox.stories.tsx +21 -0
- package/src/components/Checkbox/index.ts +1 -0
- package/src/components/Divider/Divider.scss +62 -0
- package/src/components/Divider/Divider.tsx +21 -0
- package/src/components/Divider/__stories__/Divider.stories.tsx +68 -0
- package/src/components/Divider/index.ts +1 -0
- package/src/components/Flex/Flex.scss +94 -0
- package/src/components/Flex/Flex.tsx +41 -0
- package/src/components/Flex/__stories__/Flex.stories.tsx +27 -0
- package/src/components/Flex/index.ts +1 -0
- package/src/components/Label/Label.scss +53 -0
- package/src/components/Label/Label.tsx +27 -0
- package/src/components/Label/__stories__/Label.stories.tsx +26 -0
- package/src/components/Label/index.ts +1 -0
- package/src/components/Link/Link.scss +13 -0
- package/src/components/Link/Link.tsx +18 -0
- package/src/components/Link/__stories__/Link.stories.tsx +20 -0
- package/src/components/Link/index.ts +1 -0
- package/src/components/Loader/Loader.scss +56 -0
- package/src/components/Loader/Loader.tsx +33 -0
- package/src/components/Loader/__stories__/Loader.stories.tsx +18 -0
- package/src/components/Loader/const.ts +7 -0
- package/src/components/Loader/index.ts +1 -0
- package/src/components/Radio/Radio.scss +28 -0
- package/src/components/Radio/Radio.tsx +24 -0
- package/src/components/Radio/__stories__/Radio.stories.tsx +21 -0
- package/src/components/Radio/index.ts +1 -0
- package/src/components/Skeleton/Skeleton.scss +73 -0
- package/src/components/Skeleton/Skeleton.tsx +16 -0
- package/src/components/Skeleton/__stories__/Skeleton.stories.tsx +16 -0
- package/src/components/Skeleton/index.ts +1 -0
- package/src/components/Tabs/Tabs.scss +25 -0
- package/src/components/Tabs/Tabs.tsx +39 -0
- package/src/components/Tabs/__stories__/Tabs.stories.tsx +19 -0
- package/src/components/Tabs/index.ts +1 -0
- package/src/components/Text/Text.scss +30 -0
- package/src/components/Text/Text.tsx +68 -0
- package/src/components/Text/_stories_/Text.stories.tsx +95 -0
- package/src/components/Text/index.ts +1 -0
- package/src/components/TextArea/TextArea.scss +45 -0
- package/src/components/TextArea/TextArea.tsx +38 -0
- package/src/components/TextArea/__stories__/TextArea.stories.tsx +20 -0
- package/src/components/TextArea/index.ts +1 -0
- package/src/components/TextInput/TextInput.scss +92 -0
- package/src/components/TextInput/TextInput.tsx +43 -0
- package/src/components/TextInput/__stories__/TextInput.stories.tsx +40 -0
- package/src/components/TextInput/index.ts +1 -0
- package/src/components/User/User.scss +7 -0
- package/src/components/User/User.tsx +23 -0
- package/src/components/User/__stories__/User.stories.tsx +23 -0
- package/src/components/User/index.ts +1 -0
- package/src/components/index.ts +17 -0
- package/src/index.ts +1 -1
- package/src/stories/modules.mdx +14 -0
- package/src/styles/fonts.scss +1 -0
- package/src/styles/mixins/_index.scss +18 -0
- package/src/styles/mixins/button.scss +29 -0
- package/src/styles/mixins/typography.scss +107 -0
- package/src/styles/styles.scss +3 -0
- package/src/styles/variables/_index.scss +25 -0
- package/src/styles/variables/colors.scss +45 -0
- package/src/styles/variables/padding.scss +7 -0
- package/src/styles/variables/radius.scss +8 -0
- package/src/styles/variables/spacing.scss +15 -0
- package/src/styles/variables/typography.scss +66 -0
- package/src/utils/bem/__tests__/bem.test.ts +25 -0
- package/src/utils/bem/bem.ts +24 -0
- package/src/utils/index.ts +1 -0
- package/tsconfig.app.json +28 -28
- package/tsconfig.build.json +16 -0
- package/tsconfig.json +4 -7
- package/tsconfig.node.json +26 -26
- package/vite.config.ts +78 -32
- package/vitest.shims.d.ts +1 -0
- package/index.html +0 -13
- package/public/vite.svg +0 -1
- package/src/components/Button.tsx +0 -9
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './Skeleton.scss'
|
|
3
|
+
import { bemBlock } from '../../utils'
|
|
4
|
+
|
|
5
|
+
interface SkeletonProps {
|
|
6
|
+
view?: 'circle' | 'square'
|
|
7
|
+
fullWidth?: boolean
|
|
8
|
+
width?: number
|
|
9
|
+
height?: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Skeleton: React.FC<SkeletonProps> = ({ width, view, height, fullWidth }) => {
|
|
13
|
+
const b = bemBlock('skeleton')
|
|
14
|
+
|
|
15
|
+
return <div style={{ height, width }} className={b({ fullWidth, view })} />
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Skeleton } from '../Skeleton'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/Skeleton',
|
|
6
|
+
component: Skeleton,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof Skeleton>
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>
|
|
13
|
+
|
|
14
|
+
export const Skeleton_default: Story = {
|
|
15
|
+
args: {},
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Skeleton'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@use '../../styles/styles.scss' as MainStyles;
|
|
2
|
+
|
|
3
|
+
$tabBlock: '.#{MainStyles.$ns}tabs';
|
|
4
|
+
$tabElemBlock: '.#{MainStyles.$ns}tabsElem';
|
|
5
|
+
|
|
6
|
+
#{$tabBlock} {
|
|
7
|
+
display: flex;
|
|
8
|
+
gap: 20px;
|
|
9
|
+
border-bottom: 2px solid lightgrey;
|
|
10
|
+
padding-bottom: 8px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#{$tabElemBlock} {
|
|
14
|
+
position: relative;
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
&_isActive::after {
|
|
17
|
+
content: '';
|
|
18
|
+
position: absolute;
|
|
19
|
+
left: 0;
|
|
20
|
+
right: 0;
|
|
21
|
+
bottom: -10px;
|
|
22
|
+
height: 2px;
|
|
23
|
+
background-color: rgb(45, 45, 227);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import './Tabs.scss'
|
|
3
|
+
import { bemBlock } from '../../utils'
|
|
4
|
+
import { Text } from '../Text'
|
|
5
|
+
|
|
6
|
+
interface TabsProps {
|
|
7
|
+
tabs: string[]
|
|
8
|
+
initialActiveTab?: number
|
|
9
|
+
onTabClick?: (value: string) => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const Tabs: React.FC<TabsProps> = ({ tabs, onTabClick, initialActiveTab }) => {
|
|
13
|
+
const [activeTab, setActiveTab] = useState(initialActiveTab ? tabs[initialActiveTab] : tabs[0])
|
|
14
|
+
|
|
15
|
+
const tabsB = bemBlock('tabs')
|
|
16
|
+
const tabElemB = bemBlock('tabsElem')
|
|
17
|
+
|
|
18
|
+
const tabClickHandler = (el: string) => {
|
|
19
|
+
if (onTabClick) onTabClick(el)
|
|
20
|
+
setActiveTab(el)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className={tabsB()}>
|
|
25
|
+
{tabs.map((el) => {
|
|
26
|
+
return (
|
|
27
|
+
<Text
|
|
28
|
+
variant="caption2"
|
|
29
|
+
color="secondary"
|
|
30
|
+
className={tabElemB({ isActive: el === activeTab })}
|
|
31
|
+
onClick={() => tabClickHandler(el)}
|
|
32
|
+
>
|
|
33
|
+
{el}
|
|
34
|
+
</Text>
|
|
35
|
+
)
|
|
36
|
+
})}
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Tabs } from '../Tabs'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/Tabs',
|
|
6
|
+
component: Tabs,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof Tabs>
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>
|
|
13
|
+
|
|
14
|
+
export const Tabs_default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
tabs: ['left', 'center', 'right'],
|
|
17
|
+
onTabClick: (tabEl: string) => console.log(tabEl),
|
|
18
|
+
},
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Tabs'
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
@use '../../styles/variables/index' as Variables;
|
|
2
|
+
@use '../../styles/styles.scss' as MainStyles;
|
|
3
|
+
@use '../../styles/mixins/index' as Mixins;
|
|
4
|
+
|
|
5
|
+
$block: '.#{MainStyles.$ns}text';
|
|
6
|
+
|
|
7
|
+
#{$block} {
|
|
8
|
+
@include Variables.variables-typography;
|
|
9
|
+
|
|
10
|
+
display: block;
|
|
11
|
+
color: black;
|
|
12
|
+
font-family: var(--FF-DS-font-family-sans);
|
|
13
|
+
|
|
14
|
+
&_ellipsis {
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
white-space: nowrap;
|
|
17
|
+
text-overflow: ellipsis;
|
|
18
|
+
-webkit-box-orient: vertical;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
VARIANTS
|
|
23
|
+
*/
|
|
24
|
+
@include Mixins.mixins-typography-variants;
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
ColorS
|
|
28
|
+
*/
|
|
29
|
+
@include Mixins.mixins-typography-colors;
|
|
30
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './Text.scss'
|
|
3
|
+
import { bemBlock } from '../../utils'
|
|
4
|
+
|
|
5
|
+
export type TextVariantType =
|
|
6
|
+
| 'body1'
|
|
7
|
+
| 'body2'
|
|
8
|
+
| 'body3'
|
|
9
|
+
| 'caption1'
|
|
10
|
+
| 'caption2'
|
|
11
|
+
| 'header1'
|
|
12
|
+
| 'header2'
|
|
13
|
+
| 'subheader1'
|
|
14
|
+
| 'subheader2'
|
|
15
|
+
| 'subheader3'
|
|
16
|
+
| 'display1'
|
|
17
|
+
| 'display2'
|
|
18
|
+
| 'display3'
|
|
19
|
+
| 'display4'
|
|
20
|
+
|
|
21
|
+
export type TextColorType = 'primary' | 'warning' | 'secondary' | 'danger' | 'default' | 'success'
|
|
22
|
+
|
|
23
|
+
export interface TextProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
24
|
+
variant?: TextVariantType
|
|
25
|
+
color?: TextColorType
|
|
26
|
+
ellipsis?: boolean
|
|
27
|
+
ellipsisLines?: number
|
|
28
|
+
className?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Text = React.forwardRef<HTMLSpanElement, TextProps>(
|
|
32
|
+
(
|
|
33
|
+
{
|
|
34
|
+
children,
|
|
35
|
+
variant = 'body3',
|
|
36
|
+
color,
|
|
37
|
+
ellipsis,
|
|
38
|
+
ellipsisLines,
|
|
39
|
+
className,
|
|
40
|
+
style: commonStyle,
|
|
41
|
+
...props
|
|
42
|
+
},
|
|
43
|
+
ref
|
|
44
|
+
) => {
|
|
45
|
+
const b = bemBlock('text')
|
|
46
|
+
|
|
47
|
+
const style = {
|
|
48
|
+
...commonStyle,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof ellipsisLines === 'number') {
|
|
52
|
+
style.WebkitLineClamp = ellipsisLines
|
|
53
|
+
style.whiteSpace = 'normal'
|
|
54
|
+
style.display = '-webkit-box'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<span
|
|
59
|
+
style={style}
|
|
60
|
+
className={b({ variant, color, ellipsis, ellipsisLines }, className)}
|
|
61
|
+
ref={ref}
|
|
62
|
+
{...props}
|
|
63
|
+
>
|
|
64
|
+
{children}
|
|
65
|
+
</span>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Flex } from '../../'
|
|
2
|
+
import { Text } from '../Text'
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'Components/Text',
|
|
7
|
+
component: Text,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
} satisfies Meta<typeof Text>
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof meta>
|
|
14
|
+
|
|
15
|
+
export const BodySection: Story = {
|
|
16
|
+
args: {},
|
|
17
|
+
render: (props) => (
|
|
18
|
+
<Flex gap="m">
|
|
19
|
+
<Text variant="body1" {...props}>
|
|
20
|
+
body1
|
|
21
|
+
</Text>
|
|
22
|
+
<Text variant="body2" {...props}>
|
|
23
|
+
body2
|
|
24
|
+
</Text>
|
|
25
|
+
<Text variant="body3" {...props}>
|
|
26
|
+
body3
|
|
27
|
+
</Text>
|
|
28
|
+
</Flex>
|
|
29
|
+
),
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const CaptionSection: Story = {
|
|
33
|
+
args: {},
|
|
34
|
+
render: (props) => (
|
|
35
|
+
<Flex gap="m">
|
|
36
|
+
<Text variant="caption1" {...props}>
|
|
37
|
+
Caption1
|
|
38
|
+
</Text>
|
|
39
|
+
<Text variant="caption2" {...props}>
|
|
40
|
+
Caption2
|
|
41
|
+
</Text>
|
|
42
|
+
</Flex>
|
|
43
|
+
),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const HeaderSection: Story = {
|
|
47
|
+
args: {},
|
|
48
|
+
render: (props) => (
|
|
49
|
+
<Flex gap="m">
|
|
50
|
+
<Text variant="header1" {...props}>
|
|
51
|
+
header1
|
|
52
|
+
</Text>
|
|
53
|
+
<Text variant="header2" {...props}>
|
|
54
|
+
header2
|
|
55
|
+
</Text>
|
|
56
|
+
</Flex>
|
|
57
|
+
),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const SubHeaderSection: Story = {
|
|
61
|
+
args: {},
|
|
62
|
+
render: (props) => (
|
|
63
|
+
<Flex gap="m">
|
|
64
|
+
<Text variant="subheader1" {...props}>
|
|
65
|
+
subheader1
|
|
66
|
+
</Text>
|
|
67
|
+
<Text variant="subheader2" {...props}>
|
|
68
|
+
subheader2
|
|
69
|
+
</Text>
|
|
70
|
+
<Text variant="subheader3" {...props}>
|
|
71
|
+
subheader3
|
|
72
|
+
</Text>
|
|
73
|
+
</Flex>
|
|
74
|
+
),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const DisplaySection: Story = {
|
|
78
|
+
args: {},
|
|
79
|
+
render: (props) => (
|
|
80
|
+
<Flex gap="m">
|
|
81
|
+
<Text variant="display1" {...props}>
|
|
82
|
+
display1
|
|
83
|
+
</Text>
|
|
84
|
+
<Text variant="display2" {...props}>
|
|
85
|
+
display2
|
|
86
|
+
</Text>
|
|
87
|
+
<Text variant="display3" {...props}>
|
|
88
|
+
display3
|
|
89
|
+
</Text>
|
|
90
|
+
<Text variant="display4" {...props}>
|
|
91
|
+
display4
|
|
92
|
+
</Text>
|
|
93
|
+
</Flex>
|
|
94
|
+
),
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Text'
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
@use '../../styles/variables/index' as Variables;
|
|
2
|
+
@use '../../styles/styles.scss' as MainStyles;
|
|
3
|
+
@use '../../styles/mixins/index' as Mixins;
|
|
4
|
+
|
|
5
|
+
$block: '.#{MainStyles.$ns}textarea';
|
|
6
|
+
|
|
7
|
+
#{$block} {
|
|
8
|
+
@include Variables.variables-padding;
|
|
9
|
+
@include Variables.variables-radius;
|
|
10
|
+
@include Variables.variables-typography;
|
|
11
|
+
@include Variables.variables-colors;
|
|
12
|
+
|
|
13
|
+
font-family: var(--FF-DS-text-body-font-family);
|
|
14
|
+
font-weight: var(--FF-DS-text-body-font-weight);
|
|
15
|
+
font-size: var(--FF-DS-text-body-1-font-size);
|
|
16
|
+
line-height: var(--FF-DS-text-body-1-line-height);
|
|
17
|
+
border: 2px solid var(--FF-DS-color-secondary-border);
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
padding: 10px;
|
|
20
|
+
background: transparent;
|
|
21
|
+
border-radius: 8px;
|
|
22
|
+
-webkit-border-radius: 8px;
|
|
23
|
+
-moz-border-radius: 8px;
|
|
24
|
+
-ms-border-radius: 8px;
|
|
25
|
+
-o-border-radius: 8px;
|
|
26
|
+
outline: none;
|
|
27
|
+
|
|
28
|
+
&:disabled {
|
|
29
|
+
opacity: background 0.5;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&_hasError {
|
|
33
|
+
border: 2px solid var(--FF-DS-color-error-border);
|
|
34
|
+
|
|
35
|
+
&:hover {
|
|
36
|
+
border: 2px solid var(--FF-DS-color-error-hover);
|
|
37
|
+
}
|
|
38
|
+
&:focus {
|
|
39
|
+
border: 2px solid var(--FF-DS-color-error-base);
|
|
40
|
+
}
|
|
41
|
+
&::placeholder {
|
|
42
|
+
color: var(--FF-DS-color-error-base);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './TextArea.scss'
|
|
3
|
+
import { bemBlock } from '../../utils'
|
|
4
|
+
import { Text, Flex } from '../'
|
|
5
|
+
|
|
6
|
+
interface TextAreaProps extends React.HTMLAttributes<HTMLTextAreaElement> {
|
|
7
|
+
placeholder?: string
|
|
8
|
+
label?: string
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
hasError?: boolean
|
|
11
|
+
errorMessage?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
15
|
+
({ placeholder, label, disabled, hasError, errorMessage, ...props }, ref) => {
|
|
16
|
+
const b = bemBlock('textarea')
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Flex direction="col">
|
|
20
|
+
{/* //TODO: change to htmlFor="textarea" */}
|
|
21
|
+
<Text variant="caption2">{label}</Text>
|
|
22
|
+
<textarea
|
|
23
|
+
id={'textarea'}
|
|
24
|
+
className={b({ hasError })}
|
|
25
|
+
disabled={disabled}
|
|
26
|
+
placeholder={placeholder}
|
|
27
|
+
ref={ref}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
{hasError && (
|
|
31
|
+
<Text variant="body1" color="danger">
|
|
32
|
+
{errorMessage}
|
|
33
|
+
</Text>
|
|
34
|
+
)}
|
|
35
|
+
</Flex>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { TextArea } from '../TextArea'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/TextArea',
|
|
6
|
+
component: TextArea,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof TextArea>
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>
|
|
13
|
+
|
|
14
|
+
export const TextArea_default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
label: 'label',
|
|
17
|
+
hasError: true,
|
|
18
|
+
errorMessage: 'error msg',
|
|
19
|
+
},
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TextArea'
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
@use '../../styles/styles.scss' as MainStyles;
|
|
2
|
+
@use '../../styles/variables' as Variables;
|
|
3
|
+
@use '../../styles/mixins' as Mixins;
|
|
4
|
+
|
|
5
|
+
$inputBlock: '.#{MainStyles.$ns}textInput';
|
|
6
|
+
$labelBlock: '.#{MainStyles.$ns}label';
|
|
7
|
+
$labelErrorBlock: '.#{MainStyles.$ns}errorLabel';
|
|
8
|
+
|
|
9
|
+
#{$inputBlock} {
|
|
10
|
+
@include Variables.variables-padding;
|
|
11
|
+
@include Variables.variables-radius;
|
|
12
|
+
@include Variables.variables-typography;
|
|
13
|
+
@include Variables.variables-colors;
|
|
14
|
+
|
|
15
|
+
font-family: var(--FF-DS-text-body-font-family);
|
|
16
|
+
font-weight: var(--FF-DS-text-body-font-weight);
|
|
17
|
+
font-size: var(--FF-DS-text-body-1-font-size);
|
|
18
|
+
line-height: var(--FF-DS-text-body-1-line-height);
|
|
19
|
+
border: 2px solid var(--FF-DS-color-secondary-border);
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
background: transparent;
|
|
22
|
+
border-radius: 8px;
|
|
23
|
+
-webkit-border-radius: 8px;
|
|
24
|
+
-moz-border-radius: 8px;
|
|
25
|
+
-ms-border-radius: 8px;
|
|
26
|
+
-o-border-radius: 8px;
|
|
27
|
+
outline: none;
|
|
28
|
+
|
|
29
|
+
&:disabled {
|
|
30
|
+
opacity: background 0.5;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&_hasError {
|
|
34
|
+
border: 2px solid var(--FF-DS-color-error-border);
|
|
35
|
+
color: var(--FF-DS-color-error-base);
|
|
36
|
+
|
|
37
|
+
&:hover {
|
|
38
|
+
border: 2px solid var(--FF-DS-color-error-hover);
|
|
39
|
+
}
|
|
40
|
+
&:focus {
|
|
41
|
+
border: 2px solid var(--FF-DS-color-error-base);
|
|
42
|
+
}
|
|
43
|
+
&::placeholder {
|
|
44
|
+
color: var(--FF-DS-color-error-base);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&_size {
|
|
49
|
+
&_xs {
|
|
50
|
+
height: 20px;
|
|
51
|
+
border-radius: var(--FF-DS-border-radius-xs);
|
|
52
|
+
padding: var(--FF-DS-padding-xs);
|
|
53
|
+
}
|
|
54
|
+
&_s {
|
|
55
|
+
height: 24px;
|
|
56
|
+
border-radius: var(--FF-DS-border-radius-s);
|
|
57
|
+
padding: var(--FF-DS-padding-s);
|
|
58
|
+
}
|
|
59
|
+
&_m {
|
|
60
|
+
height: 28px;
|
|
61
|
+
border-radius: var(--FF-DS-border-radius-m);
|
|
62
|
+
padding: var(--FF-DS-padding-m);
|
|
63
|
+
}
|
|
64
|
+
&_l {
|
|
65
|
+
height: 36px;
|
|
66
|
+
border-radius: var(--FF-DS-border-radius-l);
|
|
67
|
+
padding: var(--FF-DS-padding-l);
|
|
68
|
+
}
|
|
69
|
+
&_xl {
|
|
70
|
+
height: 44px;
|
|
71
|
+
border-radius: var(--FF-DS-border-radius-xl);
|
|
72
|
+
padding: var(--FF-DS-padding-xl);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#{$labelBlock} {
|
|
78
|
+
border: none;
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
padding: 0;
|
|
81
|
+
font-family: var(--FF-DS-font-family-sans);
|
|
82
|
+
font-size: var(--FF-DS-text-caption-2-font-size);
|
|
83
|
+
font-weight: var(--FF-DS-text-caption-2-line-height);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#{$labelErrorBlock} {
|
|
87
|
+
@include Variables.variables-colors;
|
|
88
|
+
|
|
89
|
+
&_hasError {
|
|
90
|
+
color: var(--FF-DS-color-error-base);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './TextInput.scss'
|
|
3
|
+
import { bemBlock } from '../../utils'
|
|
4
|
+
import { Flex } from '../'
|
|
5
|
+
import { Text } from '../Text/Text'
|
|
6
|
+
|
|
7
|
+
interface TextInputProps extends React.HTMLAttributes<HTMLInputElement> {
|
|
8
|
+
size?: 's' | 'm' | 'l' | 'xl'
|
|
9
|
+
label?: string
|
|
10
|
+
placeholder?: string
|
|
11
|
+
disabled?: boolean
|
|
12
|
+
hasClearBtn?: boolean
|
|
13
|
+
// Error state
|
|
14
|
+
hasError?: boolean
|
|
15
|
+
errorMessage?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
|
|
19
|
+
({ size, label, placeholder, errorMessage, hasError, hasClearBtn, ...props }, ref) => {
|
|
20
|
+
const inputBlock = bemBlock('textInput')
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Flex direction="col" alignItems="start">
|
|
24
|
+
{/* TODO: add htmlFor="input" as="label" */}
|
|
25
|
+
<Text variant="body1">{label}</Text>
|
|
26
|
+
<input
|
|
27
|
+
id="input"
|
|
28
|
+
className={inputBlock({ size, hasError })}
|
|
29
|
+
ref={ref}
|
|
30
|
+
placeholder={placeholder}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
{/* TODO: add Button icon, add clear functionality */}
|
|
34
|
+
{hasClearBtn && <></>}
|
|
35
|
+
{hasError && (
|
|
36
|
+
<Text variant="body1" color="danger">
|
|
37
|
+
{errorMessage}
|
|
38
|
+
</Text>
|
|
39
|
+
)}
|
|
40
|
+
</Flex>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { TextInput } from '../TextInput'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/TextInput',
|
|
6
|
+
component: TextInput,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {},
|
|
9
|
+
} satisfies Meta<typeof TextInput>
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof meta>
|
|
14
|
+
|
|
15
|
+
export const BaseInput: Story = {
|
|
16
|
+
args: {
|
|
17
|
+
label: 'Input label',
|
|
18
|
+
size: 'xl',
|
|
19
|
+
placeholder: 'Placeholder...',
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Error: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
label: 'Input with error',
|
|
26
|
+
size: 'xl',
|
|
27
|
+
placeholder: 'Placeholder...',
|
|
28
|
+
hasError: true,
|
|
29
|
+
errorMessage: 'Invalid data',
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Disabled: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
label: 'Disabled input',
|
|
36
|
+
placeholder: 'Type here...',
|
|
37
|
+
size: 'xl',
|
|
38
|
+
disabled: true,
|
|
39
|
+
},
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './TextInput'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './User.scss'
|
|
3
|
+
import { Avatar, type AvatarProps } from '../Avatar'
|
|
4
|
+
import { Flex, Text } from '../'
|
|
5
|
+
|
|
6
|
+
interface UserProps {
|
|
7
|
+
avatar?: AvatarProps
|
|
8
|
+
size?: 's' | 'm' | 'l' | 'xl'
|
|
9
|
+
name?: string
|
|
10
|
+
description?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const User: React.FC<UserProps> = ({ avatar, size, name, description }) => {
|
|
14
|
+
return (
|
|
15
|
+
<Flex direction="row" gap={'s'}>
|
|
16
|
+
<Avatar text={name} size={size} {...avatar} />
|
|
17
|
+
<Flex direction="col" justifyContent="center">
|
|
18
|
+
<Text variant="body1">{name}</Text>
|
|
19
|
+
{size !== 's' && size !== 'm' && <Text variant="caption1">{description}</Text>}
|
|
20
|
+
</Flex>
|
|
21
|
+
</Flex>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { User } from '../User'
|
|
3
|
+
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Components/User',
|
|
6
|
+
component: User,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
} satisfies Meta<typeof User>
|
|
9
|
+
|
|
10
|
+
export default meta
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof meta>
|
|
13
|
+
|
|
14
|
+
export const User_default: Story = {
|
|
15
|
+
args: {
|
|
16
|
+
avatar: {
|
|
17
|
+
text: 'Author Comp',
|
|
18
|
+
},
|
|
19
|
+
name: 'David Jonson',
|
|
20
|
+
description: 'Project manager',
|
|
21
|
+
size: 'xl',
|
|
22
|
+
},
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './User'
|