id: stack title: Stack subtitle: 线性布局
一个基础的线性布局组件,它是参考 SwiftUI 中 VStack/HStack 并基于 CSS Flex 布局实现的。
import { Stack } from '@nami-ui/stack'
export default () => (
<Stack>
<Box />
<Box />
<Box />
<Box />
</Stack>
)
布局方向
默认所有元素按水平方向布局,通过 direction 参数可以更改为垂直布局或反向布局:
import { Stack } from '@nami-ui/stack'
export default () => (
<div>
<strong>horizontal - 水平布局(默认)</strong>
<Stack direction="horizontal">
<Box />
<Box />
<Box />
</Stack>
<hr />
<strong>vertical - 垂直布局</strong>
<Stack direction="vertical">
<Box />
<Box />
<Box />
</Stack>
</div>
)
另外为了方便使用,我们额外提供了 2 个包装组件:HStack、VStack:
import { HStack, VStack } from '@nami-ui/stack'
export default () => (
<div>
<strong>HStack - 水平布局</strong>
<HStack>
<Box />
<Box />
<Box />
</HStack>
<hr />
<strong>VStack - 垂直布局</strong>
<VStack>
<Box />
<Box />
<Box />
</VStack>
</div>
)
间距
Stack 提供两个间距相关的属性:padding 及 spacing,分别用于控制容器与元素之间的间距,及元素与元素之间的间距,并且和 Space 组件一样,支持 9 种尺寸:
import { Stack } from '@nami-ui/stack'
export default () => {
const size = [
undefined,
'micor',
'mini',
'tiny',
'small',
'middle',
'large',
'big',
'huge',
'massive',
]
const [padding, setPadding] = useState(undefined)
const [spacing, setSpacing] = useState(undefined)
return (
<>
<DemoActions>
<select
value={padding}
onChange={(event) => setPadding(event.target.value)}
>
{size.map((value, index) => (
<option value={value} key={index}>{value ?? 'unset'}</option>
))}
</select>
<select
value={spacing}
onChange={(event) => setSpacing(event.target.value)}
>
{size.map((value, index) => (
<option value={value} key={index}>{value ?? 'unset'}</option>
))}
</select>
</DemoActions>
<Stack padding={padding} spacing={spacing}>
<Box />
<Box />
<Box />
</Stack>
</>
)
}
对齐
与对齐相关的也有两个属性:justify 及 align,分别用于控制主轴上的对齐方式,及交叉轴上的对齐方式。它们的默认值和 CSS 属性 justify-content 及 align-items 的默认值相同:
import { Stack } from '@nami-ui/stack'
export default () => {
const justifies = [
undefined,
'start',
'end',
'center',
'between',
'around',
]
const aligns = [
undefined,
'start',
'end',
'center',
'stretch',
]
const [justify, setJustify] = useState(undefined)
const [align, setAlign] = useState(undefined)
return (
<>
<DemoActions>
<select
value={justify}
onChange={(event) => setJustify(event.target.value)}
>
{justifies.map((value, index) => (
<option value={value} key={index}>{value ?? 'unset'}</option>
))}
</select>
<select
value={align}
onChange={(event) => setAlign(event.target.value)}
>
{aligns.map((value, index) => (
<option value={value} key={index}>{value ?? 'unset'}</option>
))}
</select>
</DemoActions>
<Stack
spacing="micor"
justify={justify}
align={align}
style={{
background: 'var(--ifm-color-emphasis-400)',
height: 70,
}}
>
<Box />
<Box />
<Box />
</Stack>
</>
)
}
弹性布局
默认 Stack 内的所有元素在主轴上仅分配其自身所占用的空间,无论主轴空间是空余还是不足(即 flex: 0 0 auto;), 但通常在实现自适应布局时,我们往往会需要其中的一个或多个元素变为弹性元素,当主轴上有剩余空间时,它会会自动扩展以填充空间,而当主轴空间不足时,它们又会自动收缩。
在 Stack 组件中,若要实现这一点非常简单(当然有时也不简单),只需给需要弹性伸缩的子元素添加 $flex 属性即可:
import { Stack } from '@nami-ui/stack'
export default () => (
<div>
<Stack spacing="micor">
<Box $flex />
<Box />
<Box />
</Stack>
<Stack spacing="micor">
<Box $flex />
<Box />
<Box $flex />
</Stack>
</div>
)
栅格布局
除了弹性布局之外,我们还提供传统的栅格布局,即将容器空间等分为一定数量的列,其中的元素占据某几列的空间。 该组件中所提供的是 24 列栅格,你只需要在子元素上设置 $col 属性并指定所占据的列数即可:
import { Stack } from '@nami-ui/stack'
export default () => (
<Stack spacing="micor">
<Box $col={1}>1</Box>
<Box $col={2}>2</Box>
<Box $col={3}>3</Box>
<Box $col={6}>6</Box>
<Box $col={12}>12</Box>
</Stack>
)
$col 可以和 $flex 同时使用,此时当容器空间不足时,栅格元素会自动收缩:
import { Stack } from '@nami-ui/stack'
export default () => (
<Stack spacing="micor">
<Box />
<Box $col={12} $flex>
12
</Box>
<Box />
<Box $col={12} $flex>
12
</Box>
<Box />
</Stack>
)
子元素对齐
通过 $align 属性,我们可以为子元素单独指定其在交叉轴上的对齐方式:
import { Stack } from '@nami-ui/stack'
export default () => (
<div>
<strong>stretch - 填充</strong>
<Stack>
<Box small $align="stretch" />
<Box large />
<Box />
</Stack>
<hr />
<strong>start - 顶端对齐</strong>
<Stack>
<Box small $align="stretch" $align="start" />
<Box large />
<Box />
</Stack>
<hr />
<strong>end - 底端对齐</strong>
<Stack>
<Box small $align="stretch" $align="end" />
<Box large />
<Box />
</Stack>
<hr />
<strong>center - 中部对齐</strong>
<Stack>
<Box small $align="stretch" $align="center" />
<Box large />
<Box />
</Stack>
</div>
)
注意事项
1. 修饰属性 $flex、$col 及 $align 只能设置在 Stack 组件的直接子元素中,因此像下面的代码是无效的:
function Demo() {
return (
<Stack>
<SomeWrapComponent>
<div $flex >
</SomeWrapComponent>
</Stack>
)
}
当然,设置在子元素的组件内也是无效的:
function Demo() {
return (
<Stack>
<Item />
</Stack>
)
}
function Item() {
return <div $flex />
}
2. Stack 组件会在子元素上追加一些额外的类名和样式,因此子元素组件必须接收并处理从 props 传入的 className 和 style(只会传入一些 flex 相关的样式属性):
import clsx from 'clsx'
function Demo() {
return (
<Stack>
<Item $flex />
</Stack>
)
}
function Item({ className, style, ...otherProps }) {
return (
<div
className={clsx(className, 'own-class-name')}
style={{ ...style, color: '#fff' }}
{...otherProps}
/>
)
}
3. 在使用 $flex 或 $col 时,需要确保子元素组件是支持弹性伸缩的。如果子元素组件仅支持固定宽/高,或仅支持在某个宽/高度区间内,那么就要考虑是改造该组件,还是确实不能将其应用于弹性布局或栅格布局中。