Skip to content

Commit a68086a

Browse files
authored
Merge pull request #136
[Issue-134] Component - Field
2 parents 4040131 + 07f281e commit a68086a

File tree

6 files changed

+379
-0
lines changed

6 files changed

+379
-0
lines changed

components/field/Field.tsx

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import classNames from 'classnames';
2+
import React from 'react';
3+
import type { PresetBarShapeType } from '../_util/shapes';
4+
import { ConfigContext } from '../config-provider';
5+
import { NoFormStyle } from '../form/context';
6+
import { NoCompactStyle } from '../space/Compact';
7+
import useStyle from './style';
8+
import Typography from '../typography';
9+
10+
export type SelectModalSize = 'small' | 'medium';
11+
export interface FieldProps {
12+
content: React.ReactNode;
13+
prefixCls?: string;
14+
className?: string;
15+
size?: SelectModalSize;
16+
shape?: PresetBarShapeType;
17+
placeholder?: string;
18+
background?: 'default' | 'transparent';
19+
width?: number | string;
20+
label?: string;
21+
prefix?: React.ReactNode;
22+
suffix?: React.ReactNode;
23+
maxLine?: number;
24+
}
25+
26+
const DEFAULT_PLACEHOLDER = 'Placeholder';
27+
28+
const Field = (props: FieldProps): JSX.Element => {
29+
const { getPrefixCls } = React.useContext(ConfigContext);
30+
31+
const {
32+
prefixCls: customizePrefixCls,
33+
className,
34+
shape = 'default',
35+
background = 'default',
36+
width = '100%',
37+
label = '',
38+
suffix,
39+
prefix,
40+
size: inputSize = 'medium',
41+
content,
42+
placeholder,
43+
maxLine = 1,
44+
} = props;
45+
46+
const prefixCls = getPrefixCls('field', customizePrefixCls);
47+
// Style
48+
const [wrapSSR, hashId] = useStyle(prefixCls);
49+
50+
return wrapSSR(
51+
<NoCompactStyle>
52+
<NoFormStyle status override>
53+
<div
54+
className={classNames(
55+
hashId,
56+
className,
57+
`${prefixCls}-container`,
58+
`${prefixCls}-border-${shape}`,
59+
`${prefixCls}-bg-${background}`,
60+
`${prefixCls}-size-${inputSize}`,
61+
{
62+
[`${prefixCls}-with-label`]: label,
63+
[`${prefixCls}-placeholder`]: !content,
64+
},
65+
)}
66+
style={{ width }}
67+
>
68+
{label && <div className={classNames(`${prefixCls}-label`)}>{label}</div>}
69+
<div className={classNames(`${prefixCls}-wrapper`)}>
70+
{prefix}
71+
<div className={classNames(`${prefixCls}-content-wrapper`)}>
72+
<Typography.Paragraph
73+
className={classNames(`${prefixCls}-content`)}
74+
ellipsis={{ rows: maxLine }}
75+
>
76+
{content || placeholder || DEFAULT_PLACEHOLDER}
77+
</Typography.Paragraph>
78+
</div>
79+
{suffix}
80+
</div>
81+
</div>
82+
</NoFormStyle>
83+
</NoCompactStyle>,
84+
);
85+
};
86+
87+
export default Field;

components/field/index.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import OriginField from './Field';
2+
3+
export type { FieldProps } from './Field';
4+
5+
type FieldType = typeof OriginField;
6+
7+
const Field = OriginField as FieldType;
8+
9+
export default Field;
+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import React, { useMemo } from 'react';
2+
import type { ComponentStory, ComponentMeta } from '@storybook/react';
3+
import SwAvatar from '../../sw-avatar';
4+
import Field from '../index';
5+
import type { FieldProps } from '../index';
6+
7+
interface WrapperProps extends FieldProps {
8+
suffixType: number;
9+
prefixType: number;
10+
}
11+
12+
const icon = <SwAvatar value="" identPrefix={42} isAllAccount size={20} />;
13+
14+
const Wrapper = ({ suffixType = 1, prefixType = 0, ...args }: WrapperProps) => {
15+
const additionalProps = useMemo((): Pick<FieldProps, 'prefix' | 'suffix'> => {
16+
const result: Pick<FieldProps, 'prefix' | 'suffix'> = {};
17+
18+
switch (prefixType) {
19+
case 1:
20+
result.prefix = icon;
21+
break;
22+
default:
23+
break;
24+
}
25+
26+
switch (suffixType) {
27+
case 2:
28+
result.suffix = icon;
29+
break;
30+
default:
31+
break;
32+
}
33+
34+
return result;
35+
}, [suffixType, prefixType]);
36+
37+
return (
38+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
39+
<Field {...args} {...additionalProps} />
40+
</div>
41+
);
42+
};
43+
44+
export default {
45+
title: 'Basic Components/Field',
46+
component: Wrapper,
47+
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
48+
argTypes: {
49+
title: {
50+
type: 'string',
51+
},
52+
placeholder: {
53+
type: 'string',
54+
},
55+
label: {
56+
type: 'string',
57+
if: {
58+
arg: 'withLabel',
59+
eq: true,
60+
},
61+
},
62+
shape: {
63+
control: 'radio',
64+
options: ['default', 'square', 'round'],
65+
},
66+
background: {
67+
control: 'radio',
68+
options: ['default', 'transparent'],
69+
},
70+
size: {
71+
control: 'select',
72+
options: ['small', 'medium'],
73+
if: {
74+
arg: 'label',
75+
eq: '',
76+
},
77+
},
78+
disabled: {
79+
type: 'boolean',
80+
if: {
81+
arg: 'withDisabled',
82+
eq: true,
83+
},
84+
},
85+
withLabel: {
86+
control: false,
87+
},
88+
suffixType: {
89+
control: false,
90+
},
91+
prefixType: {
92+
control: false,
93+
},
94+
maxLine: {
95+
type: 'number',
96+
defaultValue: 1,
97+
},
98+
},
99+
// @ts-ignore
100+
} as ComponentMeta<typeof Wrapper>;
101+
102+
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
103+
// @ts-ignore
104+
const Template: ComponentStory<typeof Wrapper> = ({ ...args }) => <Wrapper {...args} />;
105+
106+
const DEFAULT_ARGS = {
107+
shape: 'default',
108+
background: 'default',
109+
size: 'medium',
110+
placeholder: 'Placeholder',
111+
content:
112+
'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci amet corporis cumque deleniti dicta distinctio dolores ea est et eveniet ipsam iste molestiae non possimus, recusandae rem sint. Cumque, eum?',
113+
disabled: false,
114+
label: '',
115+
};
116+
export const Default = Template.bind({});
117+
118+
Default.args = {
119+
...DEFAULT_ARGS,
120+
};
121+
122+
export const WithLabel = Template.bind({});
123+
124+
WithLabel.args = {
125+
...DEFAULT_ARGS,
126+
label: 'Label',
127+
withLabel: true,
128+
};
129+
export const CustomSuffix = Template.bind({});
130+
131+
CustomSuffix.args = {
132+
...DEFAULT_ARGS,
133+
suffixType: 2,
134+
label: 'Label',
135+
withLabel: true,
136+
};
137+
138+
export const CustomPrefix = Template.bind({});
139+
140+
CustomPrefix.args = {
141+
...DEFAULT_ARGS,
142+
prefixType: 1,
143+
label: 'Label',
144+
withLabel: true,
145+
};
146+
147+
export const FullCustom = Template.bind({});
148+
149+
FullCustom.args = {
150+
...DEFAULT_ARGS,
151+
suffixType: 2,
152+
prefixType: 1,
153+
label: 'Label',
154+
withLabel: true,
155+
};

components/field/style/index.tsx

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import type { FullToken, GenerateStyle } from '../../theme/internal';
2+
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
3+
4+
/** Component only token. Which will handle additional calculation of alias token */
5+
export interface ComponentToken {
6+
// Component token here
7+
}
8+
9+
export interface FieldToken extends FullToken<'Field'> {}
10+
const genFieldStyle: GenerateStyle<FieldToken> = (token) => {
11+
const { componentCls } = token;
12+
13+
return [
14+
{
15+
[`${componentCls}-container`]: {
16+
display: 'flex',
17+
flexDirection: 'column',
18+
gap: 8,
19+
color: token.colorTextTertiary,
20+
lineHeight: token.lineHeightLG,
21+
overflow: 'hidden',
22+
position: 'relative',
23+
24+
[`${componentCls}-wrapper`]: {
25+
display: 'flex',
26+
flexDirection: 'row',
27+
justifyContent: 'space-between',
28+
alignItems: 'center',
29+
gap: 8,
30+
overflow: 'hidden',
31+
32+
[`${componentCls}-content-wrapper`]: {
33+
overflow: 'hidden',
34+
flex: 1,
35+
36+
[`${componentCls}-content`]: {
37+
marginBottom: 0,
38+
color: token.colorText,
39+
},
40+
},
41+
},
42+
43+
[`&${componentCls}-placeholder`]: {
44+
[`${componentCls}-wrapper`]: {
45+
[`${componentCls}-content-wrapper`]: {
46+
[`${componentCls}-content`]: {
47+
color: token.colorTextLight4,
48+
},
49+
},
50+
},
51+
},
52+
53+
[`${componentCls}-label`]: {
54+
fontSize: token.fontSizeSM,
55+
lineHeight: token.lineHeightSM,
56+
color: token.colorTextLight4,
57+
paddingLeft: token.paddingSM,
58+
paddingRight: token.paddingSM,
59+
paddingTop: token.paddingXXS,
60+
top: token.paddingXXS,
61+
whiteSpace: 'nowrap',
62+
textOverflow: 'ellipsis',
63+
overflow: 'hidden',
64+
position: 'relative',
65+
},
66+
67+
// Size
68+
69+
[`&${componentCls}-size-small`]: {
70+
[`${componentCls}-wrapper`]: {
71+
padding: `${token.paddingContentVerticalSM}px ${token.paddingContentHorizontal}px`,
72+
},
73+
},
74+
75+
[`&${componentCls}-size-medium`]: {
76+
[`${componentCls}-wrapper`]: {
77+
padding: `${token.paddingContentVertical}px ${token.paddingContentHorizontal}px`,
78+
},
79+
},
80+
81+
[`&${componentCls}-with-label`]: {
82+
[`${componentCls}-placeholder`]: {
83+
color: token.colorText,
84+
},
85+
86+
[`${componentCls}-wrapper`]: {
87+
padding: `${token.paddingContentVertical - 2}px ${token.paddingSM}px ${
88+
token.paddingContentVertical
89+
}px`,
90+
},
91+
},
92+
93+
// Border
94+
[`&${componentCls}-border-square`]: {
95+
borderRadius: 0,
96+
},
97+
98+
[`&${componentCls}-border-round`]: {
99+
borderRadius: token.controlHeightLG + token.borderRadiusLG,
100+
},
101+
102+
[`&${componentCls}-border-default`]: {
103+
borderRadius: token.borderRadius,
104+
},
105+
106+
// Background
107+
[`&${componentCls}-bg-default`]: {
108+
background: token.colorBgSecondary,
109+
},
110+
111+
[`&${componentCls}-bg-transparent`]: {
112+
background: 'transparent',
113+
},
114+
},
115+
},
116+
];
117+
};
118+
119+
// ============================== Export ==============================
120+
export default genComponentStyleHook('Field', (token) => {
121+
const fieldToken = mergeToken<FieldToken>(token);
122+
return [genFieldStyle(fieldToken)];
123+
});

components/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ export { default as QRCode } from './qrcode';
156156
export type { QRCodeProps, QRPropsCanvas } from './qrcode/interface';
157157
export { default as version } from './version';
158158

159+
export { default as Field } from './field';
160+
export type { FieldProps } from './field';
161+
159162
export { default as ActivityIndicator } from './activity-indicator';
160163
export type { ActivityIndicatorProps } from './activity-indicator';
161164
export { default as BackgroundIcon } from './background-icon';

0 commit comments

Comments
 (0)