Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/area picker #639

Merged
merged 5 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 27 additions & 58 deletions packages/core/src/area-picker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,10 @@ import { AreaPicker } from "@taroify/core"

### 基础用法

初始化省市区组件时,需要通过 `AreaPicker.Columns` 子组件传入省市区数据
初始化省市区组件时,需要通过 `areaList` 属性传入省市区数据

```tsx
import { AreaPicker } from "@taroify/core"
import { areaList } from "@vant/area-data"

function BasicAreaPicker() {
return (
<AreaPicker>
<AreaPicker.Toolbar>
<AreaPicker.Button>取消</AreaPicker.Button>
<AreaPicker.Title>标题</AreaPicker.Title>
<AreaPicker.Button>确认</AreaPicker.Button>
</AreaPicker.Toolbar>
<AreaPicker.Columns children={areaList} />
</AreaPicker>
)
}
<AreaPicker areaList={areaList} />
```

### areaList 格式
Expand Down Expand Up @@ -80,32 +66,31 @@ import { areaList } from "@vant/area-data"
```

### 选中省市区
通过 `defaultValue` 设置默认值

如果想选中某个省市区,需要传入一个 `value` 属性,绑定对应的地区码。
通过 `value` `onChange` 控制选中

```tsx
function AreaPickerWithValue() {
return (
<AreaPicker value={["330000", "330300"]}>
<AreaPicker.Toolbar>
<AreaPicker.Button>取消</AreaPicker.Button>
<AreaPicker.Title>标题</AreaPicker.Title>
<AreaPicker.Button>确认</AreaPicker.Button>
</AreaPicker.Toolbar>
<AreaPicker.Columns children={areaList} />
</AreaPicker>
)
}
<AreaPicker areaList={areaList} defaultValue={["330000", "330300", "330305"]} />
```

### 配置显示列

可以通过 `depth` 属性配置省市区显示的列数,默认情况下会显示省市区,当你设置为 `2`,则只会显示省市选择。

```tsx
function AreaPickerWithColumns2() {
<AreaPicker depth={2} areaList={areaList} />
```

### 手动控制DOM
初始化省市区组件时,需要通过 `AreaPicker.Columns` 子组件传入省市区数据。
```tsx
import { AreaPicker } from "@taroify/core"
import { areaList } from "@vant/area-data"

function BasicAreaPicker() {
return (
<AreaPicker depth={2}>
<AreaPicker>
<AreaPicker.Toolbar>
<AreaPicker.Button>取消</AreaPicker.Button>
<AreaPicker.Title>标题</AreaPicker.Title>
Expand All @@ -123,38 +108,22 @@ function AreaPickerWithColumns2() {

| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| defaultValue | 默认选中的值 | _any[]_ | - |
| value | 选中的值 | _any[]_ | - |
| defaultValue | 默认选中的值 | _string[]_ | - |
| value | 选中的值 | _string[]_ | - |
| areaList | 省市区数据 | _object_ | - |
| depth | 显示列数,3-省市区,2-省市,1-省 | _string_ | `3` |
| title | 顶部栏标题 | _ReactNode_ | - |
| confirmText | 确认按钮文字 | _ReactNode_ | `确认` |
| cancelText | 取消按钮文字 | _ReactNode_| `取消` |
| loading | 是否显示加载状态 | _boolean_ | `false` |
| readonly | 是否为只读状态,只读状态下无法切换选项 | _boolean_ | `false` |
| siblingCount | 可见的选项相邻个数 | _number_ | `3` |
| depth | 显示列数,3-省市区,2-省市,1-省 | _number \| string_ | `3` |
| optionHeight | 选项高度,支持 `px` `vw` `vh` `rem` `rpx` 单位,默认 `px` | _number\|string_ | `44` |

### Events

| 事件 | 说明 | 回调参数 |
| ------- | ------------------ | ------------------------------ |
| onConfirm | 点击完成按钮时触发 | _values: any[], options: PickerOptionObject[]_ |
| onCancel | 点击取消按钮时触发 | - |
| onChange | 选项改变时触发 | _values: any[], option: PickerOptionObject, column: PickerOptionObject_ |

### PickerOptionObject 格式

onConfirm 事件返回的数据整体为一个数组,数组每一项对应一列选项中被选中的数据。

```tsx
[
{
value: "110000",
label: "北京市",
},
{
value: "110100",
label: "北京市",
},
{
value: "110101",
label: "东城区",
},
];
```
| onConfirm | 点击完成按钮时触发 | _values: string[], options: PickerOptionObject[]_ |
| onCancel | 点击取消按钮时触发 | _values: string[], options: PickerOptionObject[]_ |
| onChange | 选项改变时触发 | _values: string[], option: PickerOptionObject, column: PickerOptionObject_ |
64 changes: 5 additions & 59 deletions packages/core/src/area-picker/area-picker-columns.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,13 @@
import useArea, { AreaData } from "@taroify/hooks/use-area"
import * as _ from "lodash"
import * as React from "react"
import { useContext, useEffect, useRef, ReactNode } from "react"
import Picker, { PickerColumnsProps, PickerContext, PickerOptionObject } from "../picker"
import { useToRef } from "../utils/state"
import AreaPickerContext from "./area-picker.context"
import { ViewProps } from "@tarojs/components"
import { AreaData } from "./area-picker.shared"

interface AreaPickerColumnsProps extends Omit<PickerColumnsProps, "children"> {
interface AreaPickerColumnsProps extends Omit<ViewProps, "children"> {
children?: AreaData
depth?: number
}

function AreaPickerColumns(props: AreaPickerColumnsProps) {
const { children: data } = props
const { depth, formatter } = useContext(AreaPickerContext)

const {
values: unverifiedValues,
setValueOptions,
onChange,
...restCtx //
} = useContext(PickerContext)

const {
columns,
values,
valueOptions,
setValues,
getValueOptions, //
} = useArea(unverifiedValues, {
data,
depth,
formatter,
})

const onChangeRef = useToRef(onChange)
const valuesRef = useRef<any[]>()
const columnRef = useRef<PickerOptionObject>()

_.forEach(valueOptions, (value, index) => setValueOptions?.(value as PickerOptionObject, { index }))

useEffect(() => {
if (columnRef.current && !_.isEqual(valuesRef.current, values)) {
const option = _.get(getValueOptions(), columnRef.current.index)
onChangeRef.current?.(values, option as PickerOptionObject, columnRef.current)
valuesRef.current = values
}
columnRef.current = undefined
}, [getValueOptions, onChangeRef, values])

return (
<PickerContext.Provider
value={{
values,
onChange(values, option, column) {
columnRef.current = column
setValues(values)
},
...restCtx,
}}
>
<Picker.Columns children={columns as ReactNode} />
</PickerContext.Provider>
)
return null
}

export default AreaPickerColumns
11 changes: 0 additions & 11 deletions packages/core/src/area-picker/area-picker.context.ts

This file was deleted.

99 changes: 99 additions & 0 deletions packages/core/src/area-picker/area-picker.shared.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { PickerOptionData } from "../picker/picker.shared"

export interface AreaData {
province_list: Record<string, string>
city_list: Record<string, string>
county_list: Record<string, string>
}

export const AREA_EMPTY_CODE = "000000";

const makeOption = (
text = "",
value = AREA_EMPTY_CODE,
children: PickerOptionData[] | undefined = undefined,
): PickerOptionData => ({
label: text,
value,
children,
});

export function formatDataForCascade({
areaList,
columnsNum,
columnsPlaceholder: placeholder = [],
}: {
areaList: AreaData;
columnsNum: number;
columnsPlaceholder?: string[];
}) {
const {
city_list: city = {},
county_list: county = {},
province_list: province = {},
} = areaList;
const showCity = +columnsNum > 1;
const showCounty = +columnsNum > 2;

const getProvinceChildren = () => {
if (showCity) {
return placeholder.length
? [
makeOption(
placeholder[0],
AREA_EMPTY_CODE,
showCounty ? [] : undefined,
),
]
: [];
}
};

const provinceMap = new Map<string, PickerOptionData>();
Object.keys(province).forEach((code) => {
provinceMap.set(
code.slice(0, 2),
makeOption(province[code], code, getProvinceChildren()),
);
});

const cityMap = new Map<string, PickerOptionData>();
if (showCity) {
const getCityChildren = () => {
if (showCounty) {
return placeholder.length ? [makeOption(placeholder[1])] : [];
}
};

Object.keys(city).forEach((code) => {
const option = makeOption(city[code], code, getCityChildren());
cityMap.set(code.slice(0, 4), option);

const province = provinceMap.get(code.slice(0, 2));
if (province) {
province.children!.push(option);
}
});
}

if (showCounty) {
Object.keys(county).forEach((code) => {
const city = cityMap.get(code.slice(0, 4));
if (city) {
city.children!.push(makeOption(county[code], code));
}
});
}

const options = Array.from(provinceMap.values());

if (placeholder.length) {
const county = showCounty ? [makeOption(placeholder[2])] : undefined;
const city = showCity
? [makeOption(placeholder[1], AREA_EMPTY_CODE, county)]
: undefined;
options.unshift(makeOption(placeholder[0], AREA_EMPTY_CODE, city));
}

return [options, provinceMap, cityMap] as const;
}
Loading