Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue-134] Component - Field #136

Merged
merged 1 commit into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions components/field/Field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import classNames from 'classnames';
import React from 'react';
import type { PresetBarShapeType } from '../_util/shapes';
import { ConfigContext } from '../config-provider';
import { NoFormStyle } from '../form/context';
import { NoCompactStyle } from '../space/Compact';
import useStyle from './style';
import Typography from '../typography';

export type SelectModalSize = 'small' | 'medium';
export interface FieldProps {
content: React.ReactNode;
prefixCls?: string;
className?: string;
size?: SelectModalSize;
shape?: PresetBarShapeType;
placeholder?: string;
background?: 'default' | 'transparent';
width?: number | string;
label?: string;
prefix?: React.ReactNode;
suffix?: React.ReactNode;
maxLine?: number;
}

const DEFAULT_PLACEHOLDER = 'Placeholder';

const Field = (props: FieldProps): JSX.Element => {
const { getPrefixCls } = React.useContext(ConfigContext);

const {
prefixCls: customizePrefixCls,
className,
shape = 'default',
background = 'default',
width = '100%',
label = '',
suffix,
prefix,
size: inputSize = 'medium',
content,
placeholder,
maxLine = 1,
} = props;

const prefixCls = getPrefixCls('field', customizePrefixCls);
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);

return wrapSSR(
<NoCompactStyle>
<NoFormStyle status override>
<div
className={classNames(
hashId,
className,
`${prefixCls}-container`,
`${prefixCls}-border-${shape}`,
`${prefixCls}-bg-${background}`,
`${prefixCls}-size-${inputSize}`,
{
[`${prefixCls}-with-label`]: label,
[`${prefixCls}-placeholder`]: !content,
},
)}
style={{ width }}
>
{label && <div className={classNames(`${prefixCls}-label`)}>{label}</div>}
<div className={classNames(`${prefixCls}-wrapper`)}>
{prefix}
<div className={classNames(`${prefixCls}-content-wrapper`)}>
<Typography.Paragraph
className={classNames(`${prefixCls}-content`)}
ellipsis={{ rows: maxLine }}
>
{content || placeholder || DEFAULT_PLACEHOLDER}
</Typography.Paragraph>
</div>
{suffix}
</div>
</div>
</NoFormStyle>
</NoCompactStyle>,
);
};

export default Field;
9 changes: 9 additions & 0 deletions components/field/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import OriginField from './Field';

export type { FieldProps } from './Field';

type FieldType = typeof OriginField;

const Field = OriginField as FieldType;

export default Field;
155 changes: 155 additions & 0 deletions components/field/stories/field.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import React, { useMemo } from 'react';
import type { ComponentStory, ComponentMeta } from '@storybook/react';
import SwAvatar from '../../sw-avatar';
import Field from '../index';
import type { FieldProps } from '../index';

interface WrapperProps extends FieldProps {
suffixType: number;
prefixType: number;
}

const icon = <SwAvatar value="" identPrefix={42} isAllAccount size={20} />;

const Wrapper = ({ suffixType = 1, prefixType = 0, ...args }: WrapperProps) => {
const additionalProps = useMemo((): Pick<FieldProps, 'prefix' | 'suffix'> => {
const result: Pick<FieldProps, 'prefix' | 'suffix'> = {};

switch (prefixType) {
case 1:
result.prefix = icon;
break;
default:
break;
}

switch (suffixType) {
case 2:
result.suffix = icon;
break;
default:
break;
}

return result;
}, [suffixType, prefixType]);

return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<Field {...args} {...additionalProps} />
</div>
);
};

export default {
title: 'Basic Components/Field',
component: Wrapper,
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
argTypes: {
title: {
type: 'string',
},
placeholder: {
type: 'string',
},
label: {
type: 'string',
if: {
arg: 'withLabel',
eq: true,
},
},
shape: {
control: 'radio',
options: ['default', 'square', 'round'],
},
background: {
control: 'radio',
options: ['default', 'transparent'],
},
size: {
control: 'select',
options: ['small', 'medium'],
if: {
arg: 'label',
eq: '',
},
},
disabled: {
type: 'boolean',
if: {
arg: 'withDisabled',
eq: true,
},
},
withLabel: {
control: false,
},
suffixType: {
control: false,
},
prefixType: {
control: false,
},
maxLine: {
type: 'number',
defaultValue: 1,
},
},
// @ts-ignore
} as ComponentMeta<typeof Wrapper>;

// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
// @ts-ignore
const Template: ComponentStory<typeof Wrapper> = ({ ...args }) => <Wrapper {...args} />;

const DEFAULT_ARGS = {
shape: 'default',
background: 'default',
size: 'medium',
placeholder: 'Placeholder',
content:
'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?',
disabled: false,
label: '',
};
export const Default = Template.bind({});

Default.args = {
...DEFAULT_ARGS,
};

export const WithLabel = Template.bind({});

WithLabel.args = {
...DEFAULT_ARGS,
label: 'Label',
withLabel: true,
};
export const CustomSuffix = Template.bind({});

CustomSuffix.args = {
...DEFAULT_ARGS,
suffixType: 2,
label: 'Label',
withLabel: true,
};

export const CustomPrefix = Template.bind({});

CustomPrefix.args = {
...DEFAULT_ARGS,
prefixType: 1,
label: 'Label',
withLabel: true,
};

export const FullCustom = Template.bind({});

FullCustom.args = {
...DEFAULT_ARGS,
suffixType: 2,
prefixType: 1,
label: 'Label',
withLabel: true,
};
123 changes: 123 additions & 0 deletions components/field/style/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';

/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {
// Component token here
}

export interface FieldToken extends FullToken<'Field'> {}
const genFieldStyle: GenerateStyle<FieldToken> = (token) => {
const { componentCls } = token;

return [
{
[`${componentCls}-container`]: {
display: 'flex',
flexDirection: 'column',
gap: 8,
color: token.colorTextTertiary,
lineHeight: token.lineHeightLG,
overflow: 'hidden',
position: 'relative',

[`${componentCls}-wrapper`]: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
gap: 8,
overflow: 'hidden',

[`${componentCls}-content-wrapper`]: {
overflow: 'hidden',
flex: 1,

[`${componentCls}-content`]: {
marginBottom: 0,
color: token.colorText,
},
},
},

[`&${componentCls}-placeholder`]: {
[`${componentCls}-wrapper`]: {
[`${componentCls}-content-wrapper`]: {
[`${componentCls}-content`]: {
color: token.colorTextLight4,
},
},
},
},

[`${componentCls}-label`]: {
fontSize: token.fontSizeSM,
lineHeight: token.lineHeightSM,
color: token.colorTextLight4,
paddingLeft: token.paddingSM,
paddingRight: token.paddingSM,
paddingTop: token.paddingXXS,
top: token.paddingXXS,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
position: 'relative',
},

// Size

[`&${componentCls}-size-small`]: {
[`${componentCls}-wrapper`]: {
padding: `${token.paddingContentVerticalSM}px ${token.paddingContentHorizontal}px`,
},
},

[`&${componentCls}-size-medium`]: {
[`${componentCls}-wrapper`]: {
padding: `${token.paddingContentVertical}px ${token.paddingContentHorizontal}px`,
},
},

[`&${componentCls}-with-label`]: {
[`${componentCls}-placeholder`]: {
color: token.colorText,
},

[`${componentCls}-wrapper`]: {
padding: `${token.paddingContentVertical - 2}px ${token.paddingSM}px ${
token.paddingContentVertical
}px`,
},
},

// Border
[`&${componentCls}-border-square`]: {
borderRadius: 0,
},

[`&${componentCls}-border-round`]: {
borderRadius: token.controlHeightLG + token.borderRadiusLG,
},

[`&${componentCls}-border-default`]: {
borderRadius: token.borderRadius,
},

// Background
[`&${componentCls}-bg-default`]: {
background: token.colorBgSecondary,
},

[`&${componentCls}-bg-transparent`]: {
background: 'transparent',
},
},
},
];
};

// ============================== Export ==============================
export default genComponentStyleHook('Field', (token) => {
const fieldToken = mergeToken<FieldToken>(token);
return [genFieldStyle(fieldToken)];
});
3 changes: 3 additions & 0 deletions components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ export { default as QRCode } from './qrcode';
export type { QRCodeProps, QRPropsCanvas } from './qrcode/interface';
export { default as version } from './version';

export { default as Field } from './field';
export type { FieldProps } from './field';

export { default as ActivityIndicator } from './activity-indicator';
export type { ActivityIndicatorProps } from './activity-indicator';
export { default as BackgroundIcon } from './background-icon';
Expand Down
Loading