# Layout Basic 基础布局

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/layout-basic/freepass)
- 平台：PC
- 维护者：揭志胜
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[LayoutBasic]

## 代码演示

### 基础盒子布局

可以使用Layout.Box创建一个内容块，通过type、outer、right三个属性可以将内容块放置到对应的盒子中，同一个盒子中可以放入多个块

```jsx
import React from 'react';
import { withRouter } from 'react-router-dom';

import { constants, Layout } from '@weapp/ui';

import { LayoutDemoProps, LayoutDemoState } from './types';

const prefixCls = `${constants.uiPcClsPrefix}-demo-layout`;

class LayoutDemo extends React.Component<LayoutDemoProps, LayoutDemoState> {
  layoutRef = React.createRef();
  render() {
    return (
      <div className={prefixCls}>
        <p>注意：一个位置只能放置一个块，如果有多层级的，请考虑嵌套一个layout处理</p>
        <Layout style={{ height: 400 }} draggable>
          <Layout.Box className="box" type="header">
            Inner header
          </Layout.Box>
          <Layout.Box className="box" type="side" maxWidth={900}>
            Inner left side
          </Layout.Box>
          <Layout.Box className="box" type="content">
            content
          </Layout.Box>
          <Layout.Box className="box" type="side" right minWidth={50}>
            Inner right side
          </Layout.Box>
          <Layout.Box className="box" type="footer">
            Innner footer
          </Layout.Box>
        </Layout>
      </div>
    )
  }
}

export default withRouter(LayoutDemo);
```

### 常见布局一

侧边栏的宽度设置支持定宽（传入width）和占比（传入weight），默认占比（1/6）

```jsx
import React from 'react';
import { withRouter } from 'react-router-dom';

import { constants, Layout } from '@weapp/ui';

import { LayoutDemoProps, LayoutDemoState } from './types';

const prefixCls = `${constants.uiPcClsPrefix}-demo-layout`;

class LayoutDemo extends React.Component<LayoutDemoProps, LayoutDemoState> {
  layoutRef = React.createRef();
  render() {
    return (
      <div className={prefixCls}>
        <Layout>
          <Layout.Box className="box" type="header">
            Inner header
          </Layout.Box>
          <Layout.Box className="box" type="side" width={150}>
            Inner Left Side
          </Layout.Box>
          <Layout.Box className="box">
            Content
          </Layout.Box>
        </Layout>
      </div>
    )
  }
}

export default withRouter(LayoutDemo);
```

### 常见布局二

Layout.Box支持开启显隐功能（设置allowHidden)，设置hidde可以默认隐藏

```jsx
import React, { ReactElement, ReactNode } from 'react';
import { withRouter } from 'react-router-dom';

import { Button, constants, Layout, LayoutBoxInfoType } from '@weapp/ui';

import { LayoutDemoProps, LayoutDemoState } from './types';

const prefixCls = `${constants.uiPcClsPrefix}-demo-layout`;

class LayoutDemo extends React.Component<LayoutDemoProps, LayoutDemoState> {
  state = {
    leftSideHidden: false,
    sideHidden: false,
  }

  handleClick = () => {
    this.setState(prevState => ({ sideHidden: !prevState.sideHidden }));
  }

  handleVisibleChange = (visible: boolean) => {
    console.log('visible changed', visible);
    this.setState({ sideHidden: !visible });
  }

  getBoxHidden = (boxInfo: LayoutBoxInfoType) => {
    const { type } = boxInfo;
    if (type == 'side') {
      return this.state.sideHidden;
    }
    return false;
  }

  customChild = (child: ReactElement, boxInfo: LayoutBoxInfoType) => {
    const { type, right } = boxInfo;
    if (type === 'side' && !right) {
      return React.cloneElement(child, {
        outer: true,
      });
    }
    return child;
  }

  handleLeftClick = () => {
    this.setState({ leftSideHidden: !this.state.leftSideHidden });
  }

  handleLeftVisibleChange = (visible: boolean) => {
    this.setState({ leftSideHidden: !visible });
  }

  render() {
    const { leftSideHidden } = this.state;
    return (
      <div className={prefixCls}>
        <h4>单独受控</h4>
        <p>用于单独控制某个box是否隐藏显示</p>
        <p>
          <Button style={{ marginRight: 15 }} onClick={this.handleLeftClick}>{`${this.state.leftSideHidden ? '显示' : '隐藏'}`}左侧侧边栏</Button>
        </p>
        <Layout customChild={this.customChild} className="demo-test-layout-3">
          <Layout.Box className="box" type="header">
            Inner header
          </Layout.Box>
          <Layout.Box className="box">
            content
          </Layout.Box>
          <Layout.Box
           
            className="box"
            type="side"
            width={200}
            allowHidden
            hiddenControlled
            hidden={leftSideHidden}
            onVisibleChange={this.handleLeftVisibleChange}
            btnClassName="custom-layout-box-hidden-btn"
          >
            Outer left side
          </Layout.Box>
        </Layout>
        {/* <p>直接隐藏box元素</p>
        <Layout className="demo-test-layout-3">
          <Layout.Box className="box" type="header">
            Inner header
          </Layout.Box>
          <Layout.Box className="box">
            content
          </Layout.Box>
          {leftSideHidden ? null : <Layout.Box
            className="box"
            type="side"
            width={200}
            allowHidden
          >
            Outer left side
          </Layout.Box>}
        </Layout> */}
        <h4>整体受控</h4>
        <p>用于控制整个Layout组件中的box是否显示隐藏</p>
        <p>
          <Button onClick={this.handleClick}>{`${this.state.sideHidden ? '显示' : '隐藏'}`}侧边栏</Button>
        </p>
        <Layout getBoxHidden={this.getBoxHidden} customChild={this.customChild} className="demo-test-layout-3">
          <Layout.Box className="box" type="header">
            Inner header
          </Layout.Box>
          <Layout.Box className="box">
            content
          </Layout.Box>
          <Layout.Box className="box" type="side" hiddenControlled>
            Inner left side
          </Layout.Box>
          <Layout.Box className="box" type="side" right hiddenControlled>
            Inner right side
          </Layout.Box>
        </Layout>
      </div>
    )
  }
}

export default withRouter(LayoutDemo);
```

### 常见布局三

```jsx
import React from 'react';
import { withRouter } from 'react-router-dom';

import { constants, Layout } from '@weapp/ui';

import { LayoutDemoProps, LayoutDemoState } from './types';

const prefixCls = `${constants.uiPcClsPrefix}-demo-layout`;

class LayoutDemo extends React.Component<LayoutDemoProps, LayoutDemoState> {
  layoutRef = React.createRef();
  render() {
    return (
      <div className={prefixCls}>
        <Layout>
          <Layout.Box className="box" type="header">
            Inner header
          </Layout.Box>
          <Layout.Box className="box">
            content
          </Layout.Box>
          <Layout.Box className="box" type="footer">
            Inner footer
          </Layout.Box>
          <Layout.Box className="box" type="side" right outer weight={6}>
            outer right side
          </Layout.Box>
        </Layout>
      </div>
    )
  }
}

export default withRouter(LayoutDemo);
```

### 全布局

```jsx
import React from 'react';
import { withRouter } from 'react-router-dom';

import { constants, Layout } from '@weapp/ui';

import { LayoutDemoProps, LayoutDemoState } from './types';

const prefixCls = `${constants.uiPcClsPrefix}-demo-layout`;

class LayoutDemo extends React.Component<LayoutDemoProps, LayoutDemoState> {
  layoutRef = React.createRef();
  render() {
    return (
      <div className={prefixCls}>
        <Layout style={{ height: 500 }}>
          <Layout.Box className="box" type="header" outer allowHidden>
            Outer header
          </Layout.Box>
          <Layout.Box className="box" type="header" allowHidden>
            Inner header
          </Layout.Box>
          <Layout.Box className="box" type="side" outer allowHidden>
            Outer left side
          </Layout.Box>
          <Layout.Box className="box" type="side" allowHidden>
            Inner left side
          </Layout.Box>
          <Layout.Box className="box" type="content">
            content
          </Layout.Box>
          <Layout.Box className="box" type="side" right allowHidden>
            Inner right side
          </Layout.Box>
          <Layout.Box className="box" type="side" outer right allowHidden>
            Outer right side
          </Layout.Box>
          <Layout.Box className="box" type="footer" allowHidden>
            Inner footer
          </Layout.Box>
          <Layout.Box className="box" type="footer" outer allowHidden>
            Outer footer
          </Layout.Box>
        </Layout>
      </div>
    )
  }
}

export default withRouter(LayoutDemo);
```

## API

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| draggable | 侧边栏是否可拖拽宽度（注：只对左右侧边栏生效） | 否 | false |  |
| memoryWidth | 是否需要本地缓存拖拽后的宽度（注：只会缓存到当前电脑的当前浏览器上） | 否 | false |  |
| customChild | 用于自定义下级 Box 的渲染 | (child: ReactElement, boxInfo: LayoutBlock) => ReactNode | 否 | - |
| getBoxHidden | 用于处理 Box 的 hidden 受控 | (boxInfo: LayoutBlock) => boolean | 否 | - |
| layout | layout 组件内容渲染 | LayoutBlock[] | 否 | - |
其中 LayoutBlock 类型说明

```ts
interface LayoutBlock extends BoxProps {
  block?: ReactElement
  ref?: HTMLDivElement
  refWidth?: string
}

interface BoxProps extends BlockProps {
  type?: BoxItemType
  outer?: boolean
  right?: boolean
  weight?: number
  width?: number
  hidden?: boolean
  allowHidden?: boolean
  hiddenControlled?: boolean
  btnStyle?: CSSProperties
  btnClassName?: string
  onVisibleChange?: (visible: boolean) => void
}
```

## 子组件

#### Layout.Box

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| allowHidden | 是否开启切换显隐功能 | boolean | 否 | false |
| btnStyle | 自定义隐藏按钮的样式 | CSSProperties | 否 | - |
| btnClassName | 自定义隐藏按钮的 className | string | 否 | - |
| className | 盒子的 class | string | 否 | - |
| hidden | 默认加载时是否隐藏 | boolean | 否 | false |
| hiddenControlled | 开启 Box 隐藏受控功能 | boolean | 否 | false |
| outer | 是否在外盒子 | boolean | 否 | false |
| right | 是否时右侧边栏 | boolean | 否 | false |
| style | 盒子的样式 | CSSProperties | 否 | - |
| type | 盒子类型 | 'content','header','footer','side' | 否 | 'content' |
| weight | 侧边栏的宽度占比，符合栅格布局，0-24 | number | 否 | 4 |
| width | 侧边栏的固定宽度 | number | 否 | - |
| maxWidth | 侧边栏拖拽的最大宽度 | number | 否 | - |
| minWidth | 侧边栏拖拽的最小宽度 | number | 否 | - |
| onVisibleChange | 隐藏/显示时触发回调 | (visible: boolean) => void | 否 | - |
#### Layout.Row

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| gutter | 栅格间隔 | Gutter \| Gutter, Gutter | 否 | false |
| align | 垂直对齐方式 | typeof RowAlignsnumber | 否 | false |
| justify | 水平排列方式 | typeof RowJustifynumber | 否 | false |
| wrap | 是否自动换行 | boolean | 否 | false |
其中对应类型说明

```ts
const RowAligns: [string, string, string, string] = ['top', 'middle', 'bottom', 'stretch']
type Gutter = number
```

#### Layout.Col

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| flex | flex 布局属性 | number \| `none` \| `auto` \| string | 否 | false |
| span | 栅格占位格数，为 0 时相当于 display: none | number \| string | 否 | false |
| order | 栅格顺序 | number \| string | 否 | false |
| offset | 栅格左侧的间隔格数，间隔内不可以有栅格 | number \| string | 否 | false |
| push | 栅格向右移动格数 | number \| string | 否 | false |
| pull | 栅格向左移动格数 | number \| string | 否 | false |
**注意：``属性只在第一次加载 Box 时有效（即只是初始状态），如果需要受控可以设置``的``参数开启，具体使用可以参考[常见布局二](/ui/layoutbasic#常见布局二)**
