显示最后作者
1 第一次开发前端项目,也是第一次使用 React 开发,虽说都是 flex 布局,但依然调样式调到想吐。
2
3 没办法,根据以往开发 APP 的经验,参考 Android 和 HarmonyOS 的线性布局概念,用 React 实现了一个线性布局组件 LinearLayout ,配合占位组件 Placeholder ,可以在不写任行一行样式代码的情况下编排出想要的布局。
4
5 = 使用示例 =
6
7 以编排卡片布局为例,左边一个居中的头像,右边上下为主标题与副标题,中间带有分隔空间。
8
9 [[image:demo.png]]
10
11 代码如下:
12
13 {{code language="html"}}
14 <Placeholder height="128px" color="lightgray">
15 <Row>
16 <Compact align="center"><Placeholder alt="头像" width="48px" height="48px" /></Compact>
17 <Blank/>
18 <Stretch>
19 <Column>
20 <Stretch><Placeholder alt="标题" /></Stretch>
21 <Blank/>
22 <Stretch><Placeholder alt="副标题" /></Stretch>
23 </Column>
24 </Stretch>
25 </Row>
26 </Placeholder>
27 {{/code}}
28
29 = 组件源码 =
30
31 linearLayout/index.tsx
32
33 {{code language="ts"}}
34 import React from "react";
35 import styles from "./index.module.less";
36
37 /**
38 * 线性布局,以行 Row 和列 Column 的嵌套组合来布局界面,行/列内则以紧凑 Compact 和延展 Stretch 来划分空间。
39 * 可以以比较简单直接的方式来编排界面元素,甚至配合 Placeholder 组件,可以在不写任何一行 CSS 样式的情况下,
40 * 快速编排出需要的界面布局,再在此基础上进行精细的 CSS 样式设置,所以对新手较为友好,老手也可以降低心智负担。
41 *
42 * // 以编排卡片布局为例,左边一个居中的头像,右边上下为主标题与副标题,中间带有分隔空间。
43 * <Placeholder height="128px" color="lightgray">
44 * <Row>
45 * <Compact align="center"><Placeholder alt="头像" width="48px" height="48px" /></Compact>
46 * <Blank/>
47 * <Stretch>
48 * <Column>
49 * <Stretch><Placeholder alt="标题" /></Stretch>
50 * <Blank/>
51 * <Stretch><Placeholder alt="副标题" /></Stretch>
52 * </Column>
53 * </Stretch>
54 * </Row>
55 * </Placeholder>
56 *
57 * 上述的 Row/Column/Compact/Stretch 等组件,透传了 children/className/style 属性,除了其自身拥有的特性,
58 * 可以看作是普通的 <div> 标签,甚至通过 className/sytle 也可以修改这些组件的特性。
59 */
60 export namespace LinearLayout {
61 type TProps = {
62 children?: React.ReactNode;
63 className?: string;
64 style?: React.CSSProperties;
65 };
66
67 const joinClasses = (...classNames: (string | undefined)[]) => {
68 return (classNames ?? [])
69 .map(it => it ?? '')
70 .filter(it => it.length > 0)
71 .join(' ')
72 .trim();
73 };
74
75 const joinStyles = (...styleObjects: (React.CSSProperties | undefined)[]) => {
76 return Object.assign({}, ...styleObjects);
77 }
78
79 type TRowProps = TProps & {
80 justify?: string; // 水平方向的对齐方式,相当于投映 justify-content 属性。
81 }
82
83 /**
84 * 单行容器,配合 Compact/Stretch 等子元素,在水平方向上编排界面布局。
85 */
86 export const Row: React.FC<TRowProps> = (props) => {
87 return <div className={
88 joinClasses(styles.base, styles.row, props.className)
89 } style={
90 joinStyles({
91 justifyContent: props.justify
92 }, props.style)
93 }>
94 {props.children}
95 </div>;
96 };
97
98 type TColumnProps = TProps & {
99 justify?: string; // 垂直方向的对齐方式,相当于投映 justify-content 属性。
100 }
101
102 /**
103 * 单列容器,配合 Compact/Stretch 等子元素,在垂直方向上编排界面布局。
104 */
105 export const Column: React.FC<TColumnProps> = (props) => {
106 return <div className={
107 joinClasses(styles.base, styles.column, props.className)
108 } style={props.style}>
109 {props.children}
110 </div>;
111 };
112
113 type TCompactProps = TProps & {
114 align?: string; // 当前紧凑容器在所处线性布局方向上的对齐方式,相当于投映 align-self 属性。
115 }
116
117 /**
118 * 紧凑容器,在水平和垂直方向均紧凑粘合子元素内容。
119 */
120 export const Compact: React.FC<TCompactProps> = (props) => {
121 return <div className={
122 joinClasses(styles.base, styles.compact, props.className)
123 } style={
124 joinStyles({
125 alignSelf: props.align
126 }, props.style)
127 }>{props.children}</div>;
128 };
129
130 type TStretchProps = TProps & {
131 weight?: string; // 延伸比重(类似 Android 中的 layoutWeight 属性,默认值为 1 ),相当于投映 flex-grow 属性。
132 align?: string; // 各子元素(默认为水平方向排布)在垂直方向的对齐方式(上中下等),相当于投映 align-items 属性。
133 justify?: string; // 各子元素(默认为水平方向排布)在水平方向的堆叠方式(左中右等),相当于投映 justify-content 属性。
134 };
135
136 /**
137 * 延展容器,在水平和垂直方向均贪婪延展,多个 Stretch 容器按比重 weight 占用父节点的剩余空间,默认为平分。
138 */
139 export const Stretch: React.FC<TStretchProps> = (props) => {
140 return <div className={
141 joinClasses(styles.base, styles.stretch, props.className)
142 } style={
143 joinStyles({
144 flexGrow: props.weight,
145 alignItems: props.align,
146 justifyContent: props.justify,
147 }, props.style)
148 }>
149 {props.children}
150 </div>;
151 };
152
153 type TBlankProps = TProps & {
154 length?: string; // 所处线性布局方向上的长度,默认为 8px 。
155 };
156
157 /**
158 * 空白内容,在所处线性布局方向上的占用空间长度的空间,默认占用 8px 。
159 */
160 export const Blank: React.FC<TBlankProps> = (props) => {
161 return <div className={
162 joinClasses(styles.base, styles.blank, props.className)
163 } style={
164 joinStyles({
165 flexBasis: props.length
166 }, props.style)
167 } />;
168 }
169 };
170
171 // 与命名空间 LinearLayout 一并导出,方便不带命名空间直接使用。
172 export const Row = LinearLayout.Row;
173 export const Column = LinearLayout.Column;
174 export const Compact = LinearLayout.Compact;
175 export const Stretch = LinearLayout.Stretch;
176 export const Blank = LinearLayout.Blank;
177 {{/code}}
178
179 linearLayout/index.module.less
180
181 {{code language="less"}}
182 .base {
183 display: flex;
184 }
185
186 .row {
187 flex-direction: row;
188 flex: 1 1 0px;
189 align-self: stretch;
190 }
191
192 .column {
193 flex-direction: column;
194 flex: 1 1 0px;
195 align-self: stretch;
196 }
197
198 .compact {
199 flex: unset 0 0px;
200 align-self: flex-start;
201 }
202
203 .stretch {
204 flex: 1 1 0px;
205 align-self: stretch;
206 }
207
208 .blank {
209 flex: 0 0 8px;
210 }
211 {{/code}}
212
213 placeholder/index.tsx
214
215 {{code language="ts"}}
216 import React from "react";
217 import styles from "./index.module.less";
218
219 enum EArea {
220 NONE = 'none',
221 WIDE = 'wide',
222 FULL = 'full'
223 }
224
225 type TProps = {
226 children?: React.ReactNode,
227 className?: string;
228 style?: React.CSSProperties;
229 width?: string,
230 height?: string,
231 area?: string | EArea,
232 align?: string,
233 color?: string,
234 borderd?: boolean,
235 alt?: string
236 }
237
238 export const Placeholder: React.FC<TProps> = (props) => {
239 return (
240 <div className={
241 (props.className ?? '') + ' ' + [
242 styles.component,
243 styles[`area-${props.area ?? 'full'}`]
244 ].join(' ')
245 } style={{
246 width: props.width,
247 height: props.height,
248 backgroundColor: props.color,
249 alignSelf: props.align,
250 ...props.style
251 }}>
252 {props.children ?? props.alt ?? 'Placeholder'}
253 </div>
254 )
255 };
256 {{/code}}
257
258 placeholder/index.module.less
259
260 {{code language="less"}}
261 .component {
262 display: flex;
263 border: 1px dashed black;
264 }
265
266 .area-none {
267 flex: unset 0 0px;
268 align-self: flex-start;
269 }
270
271 .area-wide {
272 flex: 1 0 0px;
273 align-self: flex-start;
274 }
275
276 .area-full {
277 flex: 1 0 0px;
278 align-self: stretch;
279 }
280 {{/code}}

同级页面

版权所有,如发现盗用模仿必追诉法律责任!
CopyRight © 2020-2023 keqiongpan.cn. All Right Reserved.