npm package: @rax-ui/filter
Description: --- display: @rax-ui/filter family: other ---
Author: beizhu.wc@alibaba-inc.com
Latest version: 1.0.0-beta.62

---
display: @rax-ui/filter
family: other
---

## @rax-ui/filter 

`@rax-ui/filter` 在搜索筛选场景中使用。Filter 是一个重业务属性组件，我们封装了业务中通用的交互和设计，提供了灵活的配置来做自定义，扩展性强，并制定了业务组件的书写规范，设计上解耦 `业务组件`。

### 组件功能点

- 1、搜索筛选组件包含 `Filter` 和 `Filter.Panel` 组件，设计上解耦 `业务组件`，须遵守<a href="#panelspec">业务组件书写规范</a>
- 2、<a href="#navconfig">筛选头配置</a>提供了三种筛选类型，并且支持点击动画
  + `RelatePanel`：筛选项关联Panel型（即筛选头和 Panel 是一对一关系，点击筛选头展示 Panel）
	+ `QuickSearch`：筛选项快速搜索排序型（即筛选头没有对应 Panel，点击筛选头直接触发搜索）
	+ `Custom`：自定义类型，UI完全自定义
- 3、`Filter.Panel` 管理面板的显示隐藏，支持可配置的下拉、左滑动画
- 4、提供两种业务面板组件（如不满足业务需求，可自行封装业务组件，<a href="#panelspec">查看业务组件书写规范</a>）
  + `@rax-ui/filter-list-select`，列表选择面板组件
  + `@rax-ui/filter-multi-select`，多选面板组件
  + `@rax-ui/filter-combo-container`，组合容器，用于将若干个`@rax-ui/filter-multi-select`组合使用
- 5、提供了 `@rax-ui/filter-helper` 工具组件（Filter滚动吸附场景使用）


### 动效图

![动效图](https://img.alicdn.com/tfs/TB10fWJioT1gK0jSZFrXXcNCXXa-282-500.gif)
  
### 架构图

<img src="https://img.alicdn.com/tfs/TB1QjQifuH2gK0jSZJnXXaT1FXa-1085-795.png_600x600.jpg">


## 安装方法
在命令行中执行以下命令
```
npm install @rax-ui/filter -S
```

## 使用方式
```
import Filter from '@rax-ui/filter';
import ListSelect from '@rax-ui/filter-list-select';

render(
  <Filter navConfig={} onChange={}>
		<Filter.Panel>
      <ListSelect {...props} />
		</Filter.Panel>
		<Filter.Panel>
      <其他业务组件 />
		</Filter.Panel>
  </Filter>
)
```

##  API 
### Filter Props
| 参数 | 说明 | 类型 | 默认值（是否必填）
| -------- | -------- | -------- | -------- | 
| navConfig     | 筛选头配置 <br /><br />**效果图**<br /> ![undefined](https://img.alicdn.com/tfs/TB1FlZGfrj1gK0jSZFOXXc7GpXa-750-215.png_400x400.jpg)     | Array&lt;Object&gt;    | - (必填)   |
| offsetTop     | Filter组件展开面板状态下距离页面顶部的高度，有两种状态：**固定位置**和**跟随页面滚动吸附置顶**<br /><br /> **固定位置：**状态下距离页面顶部的高度<br /> **跟随页面滚动吸附置顶：**状态下距离页面顶部的高度 <br /><br />**效果图**<br /> ![undefined](https://img.alicdn.com/tfs/TB1l13Cfrr1gK0jSZFDXXb9yVXa-750-215.png_400x400.jpg)    | Number     | -(必填)    |
| styles     | 配置样式，Filter中所有样式都可使用`styles`集合对象来配置覆盖，详见CSS API   | Object     | {}    | 
| clickMaskClosable     | 开启 mask 背景的点击隐藏    | Boolean     | true     |
| onPanelVisibleChange     | Panel 显示隐藏回调函数 <br /> **签名：** `Function({ visible:Boolean, triggerIndex:Number, triggerType:String }) => void` <br /> **参数：** <br /> `visible:Boolean` 显示隐藏标志量 <br /> `triggerIndex:Number`触发的筛选项索引值 <br />`triggerType:String` 触发类型 <br />  <br /><br />**triggerType详解** 包含三种触发类型<br />`Navbar`:来自筛选头的点击触发<br />`Mask`:来自背景层的点击触发<br />`Panel`:来自Panel 的 onChange 回调触发  | Function     |      | 
| onChange     | Filter 搜索变更回调函数 <br /> **签名：** `Function(params:Object, urlQuery: Object) => void`  <br /> **参数：** <br />`params: Object` 标准化搜索参数，必须包含`{filterKey, filterText, filterValue, filterValueChanged}` <br /> ![undefined](https://img.alicdn.com/tfs/TB1DohqnNv1gK0jSZFFXXb0sXXa-291-110.png) <br />`urlQuery:Object` URL query 对象<br />|  Function | | 

#### Filter 组件 `props.navConfig` 数组配置详解
#### navConfig

| 参数 | 说明 | 类型 | 默认值（是否必填）
| -------- | -------- | -------- | -------- | 
| type     |  筛选项类型 <br /><br /> **三种类型**<br/>`RelatePanel`: 【默认类型】筛选项关联Panel型，即筛选头和 Panel 是一对一关系，点击筛选头展示 Panel<br/>`QuickSearch`: 筛选项快速搜索排序型，即筛选头没有对应 Panel，点击筛选头直接触发搜索<br/>`Custom`: 自定义类型，UI完全自定义，通过 `renderItem`函数返回 JSX    | String     | 'RelatePanel'   |
| text <br /><br /><br /> **注意**  `RelatePanel`类型生效    |  筛选头显示文案 <br /> 文字溢出用`...`展示  | String     | - (必填) |
| icon <br /><br /><br /> **注意**  `RelatePanel`类型生效    | 筛选头 icon：normal 正常态 和 active 激活态 图标 <br /> **数据格式**   <br /> `Object`类型 :<br /> ![undefined](https://img.alicdn.com/tfs/TB1w6tnnNz1gK0jSZSgXXavwpXa-192-81.png)   <br /> `String`类型 : <br />![undefined](https://img.alicdn.com/tfs/TB1sHdtnRr0gK0jSZFnXXbRRXXa-153-45.png) <br /><br />**效果图**<br /> ![undefined](https://img.alicdn.com/tfs/TB1jdgFfuP2gK0jSZFoXXauIVXa-750-215.png_400x400.jpg)  | Object or String     | - | 
| options <br /><br /><br /> **注意**  `QuickSearch`类型生效    |  快速搜索排序类型的数据源  <br />**数据格式**<br /> ![undefined](https://img.alicdn.com/tfs/TB1yiXrnUY1gK0jSZFCXXcwqXXa-205-259.png)   | Array     | (必填) |
| optionsIndex  <br /><br /><br /> **注意**  `QuickSearch`类型生效   |  快速搜索排序类型默认选中的索引  | String  | 0 |
| optionsKey  <br /><br /><br /> **注意**  `QuickSearch`类型生效   |  指定快速搜索排序对应的搜索 key,用到 onChange 回调中| String     | 不提供默认使用当前筛选项的索引 |
| loop  <br /><br /><br /> **注意**  `QuickSearch`类型生效   |  点击是否依托options数组项循环  | Boolean  | true |
| formatText     | 文案格式化函数<br /> **签名：**`Function(text:String) => text` <br /> **参数：** <br />`text: String` 筛选头文案     | Function     | (text)=>text    |
| disabled     | 禁用筛选头点击   | Boolean     | true     | 
| highlight     | 是否保持文字图标高亮 <br />  | Boolean     | false     | 
| hasSeperator     | 是否展示右侧分隔符<br /><br />**效果图**<br /> ![undefined](https://img.alicdn.com/tfs/TB1xmZAfAL0gK0jSZFtXXXQCXXa-750-215.png_400x400.jpg)    |  Boolean     |  false     | 
| renderItem     | 自定义渲染<br /> **注意** <br /> 提供的配置项无法满足你的 UI 需求时使用<br /> **签名：**`Function(isActive:Boolean, this:Element) => Element` <br /> **参数：** <br />`isActive:Boolean` 筛选头是否为激活状态<br />`this:Element` 筛选头this实例    | Function     |  -     |
| animation     | 动画配置，采用内置的动画 <br />**参数说明** <br />![undefined](https://img.alicdn.com/tfs/TB1gX4onRv0gK0jSZKbXXbK2FXa-208-89.png)  <br /> **注意** 目前只内置了一种`rotate`动画类型  |  Object     |      | 
| animationHook     | 用户自定义动画的钩子函数，内置动画无法满足需求时使用 <br /> **签名：**`Function(refImg:Element, isActive:Boolean) => text` <br /> **参数：** <br />`refImg:Element` 筛选头图标的 ref 实例 <br />`isActive:Boolean` 筛选头是否为激活状态  | Function     |   -   | 

### Filter.Panel Props

| 参数 | 说明 | 类型 | 默认值（是否必填）|
| -------- | -------- | -------- | -------- | 
| displayMode     | Panel 展现形式：全屏、下拉 <br />**参数说明** <br />全屏：`Fullscreen`<br />下拉：`Dropdown`  |  String     |  'Dropdown'     | 
| animation     | Panel 展示动画配置，内置上下左右动画 <br />**参数说明**<br />  ![undefined](https://img.alicdn.com/tfs/TB1tEVqnKL2gK0jSZFmXXc7iXXa-211-89.png)  <br /> `direction` 控制动画方向，分别有 `up`、`down`、`left`、`right`  |  Object     |      | 
| highPerformance | 内部通过 Panel 的显示隐藏控制 panel 的 render 次数，避免不必要的 render，高性能模式下，只会在 Panel 展示 或者 展示隐藏状态变化时才会重新 render  | Boolean |true|
| noAnimation |禁用动画| Boolean|false |


### Filter 方法

|名称 | 说明 | 类型 | 默认值 |
|:---------------|:--------|:----|:----------|
| reset | 重置内部选中状态 | Function  <br/> **签名：**`Function(navConfig:Array<NavProps>) => Array<any>` <br /> **参数：** <br />`navConfig:Array<NavProps>` 要重置 navConfig 数组 <br/>**返回值：** <br/> `Array<any> 被重置后的搜索参数`  |  |
| setNavConfig | 修改 navConfig(navConfig 默认是不受控的，必须通过方法来修改) | Function   <br/> **签名：**`Function(fn: (navConfig: Array<NavProps>) => Array<NavProps>) => void` <br /> **参数：** <br />`fn: (navConfig: Array<NavProps>) => Array<NavProps>` 接收新的 navConfig参数的回调函数 |  |


## CSS API
| 名称 | 说明 |
|:---------------|:--------|
| `filter` | filter 容器样式 |
| `filter__nav` | 筛选头容器样式 |
| `filter__nav__item` | 筛选项样式
| `filter__nav__item_active` | 筛选项高亮样式 |
| `filter__nav__text_active` | 筛选项文案高亮样式 |
| `filter__nav__icon` | 筛选项图标样式 |
| `filter__nav__seperator` | 筛选项分隔符样式 |
| `filter__panel` | Panel容器样式 |
| `filter__panel__mask` | Panel Mask背景样式 |


### 业务组件书写规范
### PanelSpec
`Filter.Panel` 会注入一些与 filter 相关的 props 到子组件中，详细如下
#### Inject Props
- `onChange`：业务组件触发搜索时调用
	  + **函数签名**`Function(params:Object,showPanel:Boolean) => void`
	  + `params:Object`：标准化搜索参数，必须包含如下字段`{filterKey, filterText, filterValue, filterValueChanged}`
	  + `showPanel:Boolean`：默认`fasle`,触发`onChange` 默认会隐藏 Panel，给 `true` 强制展示 
- `onHidePanel`：业务组件主动隐藏 Panel
	  + **函数签名**{`Function() => void`}
- `panelAttributes`: 和 Filter 相关的属性
    + `index`{Number}：当前 Panel 索引值
    + `visible`{Boolean}：当前 Panel 显示隐藏状态
    + `maxHeight`{Number}：当前业务组件的可展示的最大高度，全屏和下拉模式高度不一样
    + `displayMode`{String}：当前 Panel 所处的展示模式，下拉 or 全屏
    + `shouldInitialRender`{Boolean}：控制首次打开页面时业务组件是否要 render（对性能有极致要求时可在业务组件中使用此变量）
    + `urlQuery`{Object}：URL query 对象

#### 规范列表
- 【规范1】必须要使用 forwardRef 包裹，Filter 需要通过 ref 调用子组件方法
- 【规范2】Panel 会默认注入如下 prop 到业务组件中
- 【规范3】通过 `props.onChange` 触发 Filter 搜索，必须传递标准化搜索参数
- 【规范4】导出方法 rootRef reset

```
// 【规范1】、必须要使用 forwardRef 包裹，Filter 需要通过 ref 调用子组件方法
export const MyComp = forwardRef((props, ref)=>{
  // 【规范2】、Panel 会默认注入如下 prop 到业务组件中
  const {onChange, onHidePanel, panelAttributes} = props;
  const {maxHeight, shouldInitialRender, index, visible, displayMode, urlQuery} = panelAttributes;
  const rootRef = useRef();
  function handleChange () {
    // 【规范3】、通过 `props.onChange`触发 Filter 搜索，必须传递标准化搜索参数
    onChange({ 
      filterKey: 'orderId',
      filterText: '价格升序',
      filterValue: 100,
      filterValueChanged: 100 !== 0,
    });
  }
  // 【规范4】、导出如下两个方法 rootRef reset
  useImperativeHandle(ref, () => ({
    rootRef: rootRef,   // 导出组件根节点ref, Filter 执行动画要使用
    reset: () => {} // 重置 panel 内部状态
  }));
  return (
    <View ref={rootRef}></View>
  )
});
```

```
<!-- 在 Filter 中使用业务面板组件 -->
<Filter onChange={}>
  <Filter.Panel>
    <业务组件 />
  </Filter.Panel>
</Filter>
```

#### 业务组件示例 Demo
```
import { createElement, forwardRef, useState, useRef, useImperativeHandle } from 'rax';
import View from 'rax-view';

// 【规范1】、必须要使用 forwardRef 包裹，Filter 需要通过 ref 调用子组件方法
export const OrderList = forwardRef((props, ref)=>{
  // 【规范2】、Panel 会默认注入如下 prop 到业务组件中
  const {onChange, onHidePanel, panelAttributes} = props;
  const {maxHeight, shouldInitialRender, index, visible, displayMode, urlQuery} = panelAttributes;

  const list = [0, 1, 2, 3, 4, 5, 6];
  const [activeIndex, setActiveIndex] = useState(0);
  const rootRef = useRef();

  function handleClick (index) {
    setActiveIndex(index);
    // 【规范3】、触发 Filter 搜索，要求必须传递标准化搜索参数，
    onChange({ 
      filterKey: 'orderId',
      filterText: '价格升序',
      filterValue: list[index],
      filterValueChanged: list[index] !== 0,
    });
  }
  // 【规范4】、导出如下两个方法 rootRef reset
  useImperativeHandle(ref, () => ({
    rootRef: rootRef,   // 导出组件根节点ref, Filter 执行动画要使用
    reset: () => { // 重置 panel 内部状态
      setActiveIndex(0);
    } 
  }));
  const activeStyle = {};
  const normalStyle = {};
  return (
    <View ref={rootRef}>
      {list.map((item, index) => {
        return (
          <View key={index} style={activeIndex ? activeStyle:normalStyle} onClick={()=>handleClick(index)}>排序项{index}</View>
        )
      })}
    </View>
  )
})
```

#### 提供的通用业务面板组件
- `@rax-ui/filter-list-select`，列表选择面板组件
- `@rax-ui/filter-multi-select`，多选面板组件
- `@rax-ui/filter-combo-container`，组合容器，用于将若干个`@rax-ui/filter-multi-select`组合使用