diff --git a/src/components/cascader-view/cascader-view.tsx b/src/components/cascader-view/cascader-view.tsx index fdabd8254e..955f5f398a 100644 --- a/src/components/cascader-view/cascader-view.tsx +++ b/src/components/cascader-view/cascader-view.tsx @@ -10,17 +10,22 @@ import { useConfig } from '../config-provider' import { optionSkeleton } from './option-skeleton' import Skeleton from '../skeleton' import { useUpdateEffect } from 'ahooks' +import { useFieldNames } from '../../hooks' +import type { FieldNamesType } from '../../hooks' const classPrefix = `adm-cascader-view` export type CascaderValue = string +type BaseOptionType = { + [key: string]: any +} export type CascaderOption = { label: string value: string disabled?: boolean children?: CascaderOption[] -} +} & BaseOptionType export type CascaderValueExtend = { items: (CascaderOption | null)[] @@ -36,6 +41,7 @@ export type CascaderViewProps = { onTabsChange?: (index: number) => void activeIcon?: ReactNode loading?: boolean + fieldNames?: FieldNamesType } & NativeProps<'--height'> const defaultProps = { @@ -43,40 +49,41 @@ const defaultProps = { } export const CascaderView: FC = p => { - const { locale } = useConfig() const props = mergeProps(defaultProps, p) - const placeholder = props.placeholder || locale.Cascader.placeholder + + const { locale } = useConfig() + const generateValueExtend = useCascaderValueExtend(props.options) + const [labelName, valueName, disabledName, childrenName] = useFieldNames( + props.fieldNames + ) + const [value, setValue] = usePropsValue({ ...props, onChange: val => { props.onChange?.(val, generateValueExtend(val)) }, }) - const [tabActiveIndex, setTabActiveIndex] = useState(0) - useUpdateEffect(() => { - props.onTabsChange?.(tabActiveIndex) - }, [tabActiveIndex]) - - const generateValueExtend = useCascaderValueExtend(props.options) + const [tabActiveIndex, setTabActiveIndex] = useState(0) const levels = useMemo(() => { const ret: { selected: CascaderOption | undefined options: CascaderOption[] }[] = [] + let currentOptions = props.options let reachedEnd = false for (const v of value) { - const target = currentOptions.find(option => option.value === v) + const target = currentOptions.find(option => option[valueName] === v) ret.push({ selected: target, options: currentOptions, }) - if (!target || !target.children) { + if (!target || !target[childrenName]) { reachedEnd = true break } - currentOptions = target.children + currentOptions = target[childrenName] } if (!reachedEnd) { ret.push({ @@ -87,6 +94,9 @@ export const CascaderView: FC = p => { return ret }, [value, props.options]) + useUpdateEffect(() => { + props.onTabsChange?.(tabActiveIndex) + }, [tabActiveIndex]) useEffect(() => { setTabActiveIndex(levels.length - 1) }, [value]) @@ -104,9 +114,12 @@ export const CascaderView: FC = p => { } setValue(next) } + const whetherLoading = (options: T) => props.loading || options === optionSkeleton + const placeholder = props.placeholder || locale.Cascader.placeholder + return withNativeProps( props,
@@ -127,7 +140,7 @@ export const CascaderView: FC = p => { title={
{selected - ? selected.label + ? selected[labelName] : typeof placeholder === 'function' ? placeholder(index) : placeholder} @@ -164,17 +177,17 @@ export const CascaderView: FC = p => { activeIcon={props.activeIcon} > {level.options.map(option => { - const active = value[index] === option.value + const active = value[index] === option[valueName] return ( - {option.label} + {option[labelName]} ) })} diff --git a/src/components/cascader-view/index.en.md b/src/components/cascader-view/index.en.md index f6cbc73f03..e383a5d87c 100644 --- a/src/components/cascader-view/index.en.md +++ b/src/components/cascader-view/index.en.md @@ -14,6 +14,7 @@ CascaderView is the content area of [Cascader](/components/cascader). | --- | --- | --- | --- | | activeIcon | The icon displayed when selected | `ReactNode` | - | | defaultValue | Default selected options | `CascaderValue[]` | `[]` | +| fieldNames | Custom field name for label and value and disabled and children | `{ label: string, value: string, disabled: string, children: string }` | `{ label: 'label', value: 'value',disabled:'disabled', children: 'children' }` | | onChange | Triggered when the selected options are changed | `(value: CascaderValue[], extend: CascaderValueExtend) => void` | - | | onTabsChange | Callback when switching panel | `(index: number) => void` | - | | options | Data of the cascade options | `CascaderOption[]` | - | diff --git a/src/components/cascader-view/index.zh.md b/src/components/cascader-view/index.zh.md index 779d3c93b0..047d0b5887 100644 --- a/src/components/cascader-view/index.zh.md +++ b/src/components/cascader-view/index.zh.md @@ -14,6 +14,7 @@ CascaderView 是 [Cascader](/zh/components/cascader) 的内容区域。 | --- | --- | --- | --- | | activeIcon | 选中图标 | `ReactNode` | - | | defaultValue | 默认选中项 | `CascaderValue[]` | `[]` | +| fieldNames | 自定义 options 中 label value disabled children 的字段 | `{ label: string, value: string, disabled: string, children: string }` | `{ label: 'label', value: 'value',disabled:'disabled', children: 'children' }` | | onChange | 选项改变时触发 | `(value: CascaderValue[], extend: CascaderValueExtend) => void` | - | | onTabsChange | 切换面板的回调 | `(index: number) => void` | - | | options | 配置每一列的选项 | `CascaderOption[]` | - | diff --git a/src/components/cascader-view/tests/__snapshots__/cascader-view.test.tsx.snap b/src/components/cascader-view/tests/__snapshots__/cascader-view.test.tsx.snap index fc064bb7bb..69754cc2b1 100644 --- a/src/components/cascader-view/tests/__snapshots__/cascader-view.test.tsx.snap +++ b/src/components/cascader-view/tests/__snapshots__/cascader-view.test.tsx.snap @@ -2471,3 +2471,1101 @@ exports[`CascaderView same value in options 3`] = `
`; + +exports[`CascaderView test fieldNames 1`] = ` +
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+