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

feat: multiple options && activeIconSetPath about cascader comp #6741

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
171 changes: 130 additions & 41 deletions src/components/cascader-view/cascader-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Skeleton from '../skeleton'
import { useUpdateEffect } from 'ahooks'
import { useFieldNames } from '../../hooks'
import type { FieldNamesType, BaseOptionType } from '../../hooks'
import { cloneDeep } from 'lodash'

const classPrefix = `adm-cascader-view`

Expand All @@ -32,14 +33,16 @@ export type CascaderValueExtend = {

export type CascaderViewProps = {
options: CascaderOption[]
value?: CascaderValue[]
defaultValue?: CascaderValue[]
onChange?: (value: CascaderValue[], extend: CascaderValueExtend) => void
value?: CascaderValue[] | any
defaultValue?: CascaderValue[] | any
onChange?: (value: CascaderValue[] | any, extend: CascaderValueExtend) => void
placeholder?: string | ((index: number) => string)
onTabsChange?: (index: number) => void
activeIcon?: ReactNode
loading?: boolean
multiple?: boolean
fieldNames?: FieldNamesType
activeIconSetPath?: boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activeIconSetPath 这个命名好奇怪。

Copy link
Author

@electroluxcode electroluxcode Sep 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

哈哈,因为是从 activeIcon 这个属性派生出来的,然后上面的 tab我个人感觉有点像是路径的意思然后就取的这个名字,看看大佬有什么更好的命名

} & NativeProps<'--height'>

const defaultProps = {
Expand All @@ -48,7 +51,6 @@ const defaultProps = {

export const CascaderView: FC<CascaderViewProps> = p => {
const props = mergeProps(defaultProps, p)

const { locale } = useConfig()
const [labelName, valueName, childrenName, disabledName] = useFieldNames(
props.fieldNames
Expand All @@ -57,49 +59,94 @@ export const CascaderView: FC<CascaderViewProps> = p => {
valueName,
childrenName,
})

const [value, setValue] = usePropsValue({
...props,
onChange: val => {
props.onChange?.(val, generateValueExtend(val))
if (props.multiple) {
props.onChange?.(val, {} as any)
} else {
props.onChange?.(val, generateValueExtend(val as string[]))
}
electroluxcode marked this conversation as resolved.
Show resolved Hide resolved
},
})
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[valueName] === v)
ret.push({
selected: target,
options: currentOptions,
})
if (!target || !target[childrenName]) {
reachedEnd = true
break
if (props.multiple) {
const ret: {
selected: CascaderOption | undefined
options: CascaderOption[]
}[] = []

let currentOptions = props.options
let reachedEnd = false

for (const v of value) {
let target
;(v as string[]).forEach(e => {
const temp = currentOptions.find(option =>
e.includes(option[valueName])
)
if (temp) {
target = temp
}
})
ret.push({
selected: target,
options: currentOptions,
})
if (!target || !target[childrenName]) {
reachedEnd = true
break
}
currentOptions = target[childrenName]
electroluxcode marked this conversation as resolved.
Show resolved Hide resolved
}
currentOptions = target[childrenName]
}
if (!reachedEnd) {
ret.push({
selected: undefined,
options: currentOptions,
})
if (!reachedEnd) {
ret.push({
selected: undefined,
options: currentOptions,
})
}
return ret
} else {
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[valueName] === v)
ret.push({
selected: target,
options: currentOptions,
})
if (!target || !target[childrenName]) {
reachedEnd = true
break
}
currentOptions = target[childrenName]
}
if (!reachedEnd) {
ret.push({
selected: undefined,
options: currentOptions,
})
}
return ret
}
return ret
}, [value, props.options])

useUpdateEffect(() => {
props.onTabsChange?.(tabActiveIndex)
if (!props.multiple) {
props.onTabsChange?.(tabActiveIndex)
}
}, [tabActiveIndex])
useEffect(() => {
setTabActiveIndex(levels.length - 1)
if (!props.multiple) {
setTabActiveIndex(levels.length - 1)
}
}, [value])
useEffect(() => {
const max = levels.length - 1
Expand All @@ -108,12 +155,37 @@ export const CascaderView: FC<CascaderViewProps> = p => {
}
}, [tabActiveIndex, levels])

const onItemSelect = (selectValue: CascaderValue, depth: number) => {
const onItemSelect = (
selectValue: CascaderValue | CascaderValue[],
depth: number
) => {
const next = value.slice(0, depth)
if (selectValue !== undefined) {
next[depth] = selectValue
}
setValue(next)
if (props.multiple) {
const cloneValue = cloneDeep(value)
cloneValue[depth] = next[depth]
setValue(cloneValue)
} else {
setValue(next)
}
}

const setPath = (selectValue: string, depth: number) => {
const currentPath = cloneDeep(value)
if (selectValue !== undefined) {
if (!currentPath[depth]) {
currentPath[depth] = []
}
currentPath[depth] = (currentPath[depth] as string[]).filter(
(item: string) => {
return item !== selectValue
}
)
currentPath[depth].push(selectValue)
}
setValue(currentPath)
}

const whetherLoading = <T extends unknown[]>(options: T) =>
Expand Down Expand Up @@ -143,8 +215,8 @@ export const CascaderView: FC<CascaderViewProps> = p => {
{selected
? selected[labelName]
: typeof placeholder === 'function'
? placeholder(index)
: placeholder}
? placeholder(index)
: placeholder}
</div>
}
forceRender
Expand All @@ -171,14 +243,31 @@ export const CascaderView: FC<CascaderViewProps> = p => {
</div>
) : (
<CheckList
value={[value[index]]}
onChange={selectValue =>
onItemSelect(selectValue[0], index)
}
value={props.multiple ? value[index] : [value[index]]}
onChange={selectValue => {
if (props.multiple) {
onItemSelect(selectValue, index)
} else {
onItemSelect(selectValue[0], index)
}
}}
multiple={props.multiple}
activeSetPathMiddleware={{
index,
activeIconSetPath: props.activeIconSetPath,
setPath,
}}
activeIcon={props.activeIcon}
>
{level.options.map(option => {
const active = value[index] === option[valueName]
let active
if (props.multiple) {
active = (value[index] as Array<any>)?.includes(
option[valueName]
)
} else {
active = value[index] === option[valueName]
}
return (
<CheckList.Item
value={option[valueName]}
Expand Down
Loading
Loading