承接上一篇# 手把手搭建基于React的前端UI库 (三)-- 基础组件Icon和Button。我们继续添加组件,本次记录的是布局组件Box和Combine。
本文的代码展示的是主要的核心示意代码,全部代码见仓库:Gitee仓库
关于页面布局,最流行的便是flex布局,Box组件便是对flex布局进行的一次封装。废话不多说,上代码。在src/component
文件夹下新建文件夹Box
,在其中新建文件index.tsx
:
export interface BoxProps {
children?: ReactNode;
direction?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
wrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
alignItems?: 'center' | 'flex-start' | 'flex-end' | 'stretch';
alignContent?: 'center' | 'flex-start' | 'flex-end' | 'space-between' | 'space-around';
justifyContent?:
| 'center'
| 'flex-start'
| 'flex-end'
| 'space-between'
| 'space-around'
| 'stretch';
padding?: number | string;
width?: string;
height?: string;
flex?: string;
}
class Box extends PureComponent<BoxProps> {
render() {
const box = <BoxWrap {...this.props} />;
return box;
}
}
...
上边定义了一些接收参数,其与flex的css参数是对应的,我们就每一个具体的参数,在代码里进行实现便可以完成该组件了。接下来实现BoxWrap
组件:
import styled from '@emotion/styled';
import { css } from '@emotion/core';
export const BoxWrap = styleWrap({
className: prefixCls,
})(
styled('div')((props: any) => {
// 要实现的内容
}),
);
架子已经搭起来,老样子,仍然使用styleWrap包裹一层公共样式,内部用一个div承接,中间要实现的内容应该返回要使用的css,这边是核心内容:
...
const {
children,
direction,
wrap,
justifyContent,
alignItems,
alignContent,
span,
flex,
width,
height,
padding,
} = props;
...
return css`
box-sizing: border-box;
// direction
${direction != null &&
css`
flex-direction: ${direction};
`}
// wrap
${wrap != null &&
css`
flex-wrap: ${wrap};
`}
// justifyContent
${justifyContent != null &&
css`
justify-content: ${justifyContent};
`}
// ...
`
可以看到,只是简单加一个css便可以实现,相当简单。需要解释的是children的属性并没有公开声明,而是传递给styled
生成的组件后隐式的渲染了。
创建完毕后,在组件外便可以这样实现:
<Box direction="column" justifyContent="center">
<div>1</div>
<div>2</div>
<div>3</div>
</Box>
Combine组件用于组合不同的组件,类似Box,仍然是样式为主的组件,是为了布局方便的,对css进行的封装。我们不使用flex,使用一个大div包裹一堆inline-block元素即可。在src/components
下新建文件夹Combine
,在文件夹下新建文件index.tsx
:
const Combine = ({
children,
sharedProps = {},
spacing = 'smart',
separator,
...rest
}: CombineProps) => {
const { size = 'md' } = sharedProps;
let isFirstItem: boolean;
return (
<CombineWrap spacing={spacing} {...rest}>
{React.Children.map(children, (child) => (
// 遍历child
<>
// 放置分隔符
{separator && !isFirstItem ? <span className={separatorCls} key="separator">
{separator}
</span> : null
}
// 放置子元素
<div className={itemCls}>
{React.cloneElement(child)}
...
</div>
</>
)}
</CombineWrap>
)
}
上边代码中定义了子元素和分隔符的类:
export const itemCls = prefixCls + '-item';
export const separatorCls = prefixCls + '-separator';
组件接收spacing参数来控制相关联组件之间的距离,同时还可以设置separator来作为自定义分隔符。来看一下组件CombineWrap
的实现:
import styled from '@emotion/styled';
import { css } from '@emotion/core';
export const CombineWrap = styledWrap<{ spacing: string }>({
// 接受自定义class
className: prefixCls,
})(
// 生成一个自定义样式包裹的div
styled('div')((props) => {
const {
spacing,
theme: { designTokens: DT },
} = props;
// spacingMap为designTokens里定义的常量
const space = (DT as any)[spacingMap[spacing]];
return css`
display: inline-block; // inlink使得各个子元素平铺排列
vertical-align: middle;
// 子元素样式
> .${itemCls} {
&:focus {
z-index: 2;
}
&:hover {
z-index: 3;
}
}
// 子元素下一个兄弟,分隔符下一个子元素,子元素下一个分隔符 统一都加上左边距,这里就是space的用法
> .${itemCls}+.${itemCls}, > .${separatorCls}+.${itemCls}, > .${itemCls}+.${separatorCls} {
margin-left: ${space};
}
`;
}),
);
关于spacing常量,可以在theme主体中定义常量:
T_SPACING_COMMON_XS: '4px',
T_SPACING_COMMON_SM: '8px',
T_SPACING_COMMON_MD: '12px',
T_SPACING_COMMON_LG: '16px',
T_SPACING_COMMON_XLG: '20px',
T_SPACING_COMMON_XXLG: '24px',
T_SPACING_COMMON_XXXLG: '32px',
然后我们在项目中使用一下:
<Combine>
<Button styleType="primary">按钮组展示</Button>
<Button>按钮组展示</Button>
<Button>按钮组展示</Button>
<Button disabled>按钮组展示</Button>
</Combine>
至此,布局组件Box
和Combine
已经介绍完毕~ 更多组件参照代码仓库:Gitee仓库
React-components
Antd之Space和Grid
阅读量:1442
点赞量:0
收藏量:0