Skip to content

Commit

Permalink
feat(pro:search): add 'cascader' searchField (#1485)
Browse files Browse the repository at this point in the history
  • Loading branch information
sallerli1 committed Mar 6, 2023
1 parent a3d5bb7 commit 88b751b
Show file tree
Hide file tree
Showing 18 changed files with 778 additions and 106 deletions.
122 changes: 122 additions & 0 deletions packages/pro/search/demo/Basic.vue
Expand Up @@ -135,6 +135,128 @@ const searchFields: SearchField[] = [
],
},
},
{
type: 'cascader',
key: 'cascader',
label: 'Cascader',
fieldConfig: {
fullPath: true,
multiple: true,
searchable: true,
dataSource: [
{
key: 'components',
label: 'Components',
children: [
{
key: 'general',
label: 'General',
children: [
{
key: 'button',
label: 'Button',
},
{
key: 'header',
label: 'Header',
},
{
key: 'icon',
label: 'Icon',
},
],
},
{
key: 'layout',
label: 'Layout',
children: [
{
key: 'divider',
label: 'Divider',
},
{
key: 'grid',
label: 'Grid',
},
{
key: 'space',
label: 'Space',
},
],
},
{
key: 'navigation',
label: 'Navigation',
children: [
{
key: 'breadcrumb',
label: 'Breadcrumb',
},
{
key: 'dropdown',
label: 'Dropdown',
},
{
key: 'menu',
label: 'Menu',
},
{
key: 'pagination',
label: 'Pagination',
},
],
},
],
},
{
key: 'pro',
label: 'Pro',
children: [
{
key: 'pro-layout',
label: 'Layout',
},
{
key: 'pro-table',
label: 'Table',
disabled: true,
},
{
key: 'pro-transfer',
label: 'Transfer',
},
],
},
{
key: 'cdk',
label: 'CDK',
disabled: true,
children: [
{
key: 'a11y',
label: 'Accessibility',
},
{
key: 'breakpoint',
label: 'Breakpoint',
},
{
key: 'click-outside',
label: 'ClickOutside',
},
{
key: 'clipboard',
label: 'Clipboard',
},
{
key: 'forms',
label: 'Forms',
},
],
},
],
},
},
{
type: 'datePicker',
label: 'Date',
Expand Down
29 changes: 29 additions & 0 deletions packages/pro/search/demo/RemoteSearch.vue
Expand Up @@ -24,6 +24,11 @@ interface TreeSelectData {
label: string
children?: TreeSelectData[]
}
interface CascaderData {
key: string
label: string
children?: TreeSelectData[]
}
const labels = ['Archer', 'Berserker', 'Lancer', 'Rider', 'Saber', 'Caster', 'Assassin']
const baseSelectData: SelectData[] = Array.from(new Array(50)).map((_, idx) => {
Expand Down Expand Up @@ -66,6 +71,8 @@ const baseTreeSelectData: TreeSelectData[] = Array.from(new Array(20)).map((_, i
],
}
})
const baseCascaderData = baseTreeSelectData as CascaderData[]
const createSelectData = (searchValue: string) => {
return baseSelectData.filter(item => new RegExp(searchValue.toLowerCase()).test(item.label.toLowerCase()))
}
Expand All @@ -74,17 +81,26 @@ const createTreeSelectData = (searchValue: string) => {
new RegExp(searchValue.toLowerCase()).test(item.label.toLowerCase()),
)
}
const createCascaderData = (searchValue: string) => {
return filterTree(baseCascaderData, 'children', item =>
new RegExp(searchValue.toLowerCase()).test(item.label.toLowerCase()),
)
}
const value = ref<SearchValue[]>()
const selectData = ref<SelectData[]>(createSelectData(''))
const treeSelectData = ref<TreeSelectData[]>(createTreeSelectData(''))
const cascaderData = ref<CascaderData[]>(createCascaderData(''))
const selectOnSearch = (searchValue: string) => {
selectData.value = createSelectData(searchValue)
}
const treeSelectOnSearch = (searchValue: string) => {
treeSelectData.value = createTreeSelectData(searchValue)
}
const cascaderOnSearch = (searchValue: string) => {
cascaderData.value = createCascaderData(searchValue)
}
const searchFields = computed<SearchField[]>(() => [
{
Expand Down Expand Up @@ -113,6 +129,19 @@ const searchFields = computed<SearchField[]>(() => [
onSearch: treeSelectOnSearch,
},
},
{
type: 'cascader',
label: 'Cascader Data',
key: 'cascader_data',
fieldConfig: {
multiple: true,
searchable: true,
cascaderStrategy: 'all',
dataSource: cascaderData.value,
searchFn: () => true,
onSearch: cascaderOnSearch,
},
},
])
const onChange = (value: SearchValue[] | undefined, oldValue: SearchValue[] | undefined) => {
Expand Down
54 changes: 41 additions & 13 deletions packages/pro/search/docs/Api.zh.md
Expand Up @@ -127,22 +127,21 @@ TreeSelectSearchFieldConfig
| `draggable` | 是否可拖拽 | `boolean` | - | - | 详情参考[Tree](/components/tree/zh) |
| `draggableIcon` | 拖拽图标 | `string` | - | - | 详情参考[Tree](/components/tree/zh) |
| `showLine` | 是否展示连线 | `boolean` | - | - | 详情参考[Tree](/components/tree/zh) |
| `searchable` | 是否支持筛选 | `boolean` | false | - | 默认不支持 |
| `searchable` | 是否支持筛选 | `boolean` | `false` | - | 默认不支持 |
| `searchFn` | 搜索函数 | `(node: TreeSelectPanelData, searchValue?: string) => boolean` | - | - | 默认模糊匹配 |
| `separator` | 多选分隔符 | `string` | `'|'` | - | - |
| `virtual` | 是否支持虚拟滚动 | `boolean` | `false` | - | 默认不支持 |
| `onCheck` | 勾选回调函数 | `(checked: boolean, node: TreeSelectPanelData) => void | ((checked: boolean, node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragstart` | `dragstart` 触发时调用 | `(options: TreeDragDropOptions<any>) => void | ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragend` | `dragend` 触发时调用 | `(options: TreeDragDropOptions<any>) => void | ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragenter` | `dragenter` 触发时调用 | `(options: TreeDragDropOptions<any>) => void | ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragleave` | `dragleave` 触发时调用 | `(options: TreeDragDropOptions<any>) => void | ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragover` | `dragover` 触发时调用 | `(options: TreeDragDropOptions<any>) => void | ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDrop` | `drop` 触发时调用 | `(options: TreeDragDropOptions<any>) => void | ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onExpand` | 点击展开图标时触发 | `(expanded: boolean, node: TreeSelectPanelData) => void | ((expanded: boolean, node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onSelect` | 选中状态发生变化时触发 | `(selected: boolean, node: TreeSelectPanelData) => void | ((selected: boolean, node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onSearch` | 搜索回调函数 | `(searchValue: string) => void | ((searchValue: string) => void)[]` | - | - | 在触发搜索值改变时执行 |
| `onLoaded` | 子节点加载完毕时触发 | `(loadedKeys: any[], node: TreeSelectPanelData) => void | ((loadedKeys: any[], node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onCheck` | 勾选回调函数 | `((checked: boolean, node: TreeSelectPanelData) => void) \| ((checked: boolean, node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragstart` | `dragstart` 触发时调用 | `((options: TreeDragDropOptions<any>) => void) \| ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragend` | `dragend` 触发时调用 | `((options: TreeDragDropOptions<any>) => void) \| ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragenter` | `dragenter` 触发时调用 | `((options: TreeDragDropOptions<any>) => void \| ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragleave` | `dragleave` 触发时调用 | `((options: TreeDragDropOptions<any>) => void) \| ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDragover` | `dragover` 触发时调用 | `((options: TreeDragDropOptions<any>) => void) \| ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onDrop` | `drop` 触发时调用 | `((options: TreeDragDropOptions<any>) => void) \| ((options: TreeDragDropOptions<any>) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onExpand` | 点击展开图标时触发 | `((expanded: boolean, node: TreeSelectPanelData) => void) \| ((expanded: boolean, node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onSelect` | 选中状态发生变化时触发 | `((selected: boolean, node: TreeSelectPanelData) => void) \| ((selected: boolean, node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
| `onSearch` | 搜索回调函数 | `((searchValue: string) => void) \| ((searchValue: string) => void)[]` | - | - | 在触发搜索值改变时执行 |
| `onLoaded` | 子节点加载完毕时触发 | `((loadedKeys: any[], node: TreeSelectPanelData) => void) \| ((loadedKeys: any[], node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Tree](/components/tree/zh) |
```typescript
type TreeSelectPanelData = TreeSelectNode &
Expand All @@ -151,6 +150,35 @@ type TreeSelectPanelData = TreeSelectNode &
}
```
#### CascaderSearchField
级联选择类型
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `type` | 类型 | `'cascader'` | - | - | 固定为 `'cascader'` |
| `fieldConfig` | 配置 | `'CascaderSearchFieldConfig'` | - | - | - |
CascaderSearchFieldConfig
| 名称 | 说明 | 类型 | 默认值 | 全局配置 | 备注 |
| --- | --- | --- | --- | --- | --- |
| `dataSource` | 类型 | `CascaderPanelData[]` | - | - | 继承自`CascaderData`,但`key``label`为必填,不支持可配,且`childrenKey`固定为`'children'`,详情参考[Cascader](/components/cascader/zh) |
| `cascaderStrategy` | 级联策略 | `CascaderStrategy` | `''` | - | 详情参考[Cascader](/components/cascader/zh) |
| `multiple` | 是否为多选 | `boolean` | - | - | 默认为单选 |
| `disableData` | 动态禁用某些项 | `(data: CascaderPanelData) => boolean` | - | - | 详情参考[Cascader](/components/cascader/zh) |
| `expandIcon` | 展开图标 | `string` | - | - | 详情参考[Cascader](/components/cascader/zh) |
| `expandTrigger` | 触发展开的方式 | ``'click' \| 'hover'` | - | - | 详情参考[Cascader](/components/cascader/zh) |
| `fullPath` | 选中后的值是否包含全部路径 | `boolean` | - | `false` | 详情参考[Cascader](/components/cascader/zh) |
| `pathSeparator` | 设置分割符 | `string` | - | - | 详情参考[Cascader](/components/cascader/zh) |
| `searchable` | 是否支持筛选 | `boolean` | false | - | 默认不支持 |
| `searchFn` | 搜索函数 | `(node: TreeSelectPanelData, searchValue?: string) => boolean` | - | - | 默认模糊匹配 |
| `separator` | 多选分隔符 | `string` | `'|'` | - | -
| `virtual` | 是否支持虚拟滚动 | `boolean` | `false` | - | 默认不支持 |
| `onExpand` | 点击展开图标时触发 | `((expanded: boolean, data: CascaderPanelData) => void) \| ((expanded: boolean, data: CascaderPanelData) => void>)[]` | - | - | 详情参考[Cascader](/components/cascader/zh) |
| `onSearch` | 开启搜索功能后,输入后的回调 | `((searchValue: string) => void) \| ((searchValue: string) => void)[]` | - | - | 详情参考[Cascader](/components/cascader/zh) |
| `onLoaded` | 子节点加载完毕时触发 | `((loadedKeys: any[], node: TreeSelectPanelData) => void) \| ((loadedKeys: any[], node: TreeSelectPanelData) => void)[]` | - | - | 详情参考[Cascader](/components/cascader/zh) |

#### DatePickerSearchField

日期选择类型
Expand Down
3 changes: 3 additions & 0 deletions packages/pro/search/src/composables/useSearchItem.ts
Expand Up @@ -11,6 +11,7 @@ import type { DateConfig } from '@idux/components/config'

import { type ComputedRef, type Slots, computed } from 'vue'

import { createCascaderSegment } from '../segments/CreateCascaderSegment'
import { createDatePickerSegment } from '../segments/CreateDatePickerSegment'
import { createDateRangePickerSegment } from '../segments/CreateDateRangePickerSegment'
import { createNameSegment } from '../segments/CreateNameSegment'
Expand Down Expand Up @@ -73,6 +74,8 @@ function createSearchItemContentSegment(
return createSelectSegment(prefixCls, searchField)
case 'treeSelect':
return createTreeSelectSegment(prefixCls, searchField)
case 'cascader':
return createCascaderSegment(prefixCls, searchField)
case 'input':
return createInputSegment(prefixCls, searchField)
case 'datePicker':
Expand Down
110 changes: 110 additions & 0 deletions packages/pro/search/src/panel/CascaderPanel.tsx
@@ -0,0 +1,110 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { defineComponent, inject, onUnmounted, watch } from 'vue'

import { type VKey, callEmit } from '@idux/cdk/utils'
import { CascaderPanelProps, IxCascaderPanel } from '@idux/components/cascader'

import PanelFooter from './PanelFooter'
import { proSearchContext } from '../token'
import { proSearchCascaderPanelProps } from '../types'

export default defineComponent({
props: proSearchCascaderPanelProps,
setup(props) {
const { mergedPrefixCls, locale } = inject(proSearchContext)!

watch(
() => props.searchValue,
searchValue => {
callEmit(props.onSearch, searchValue ?? '')
},
)
onUnmounted(() => {
if (props.searchValue) {
callEmit(props.onSearch, '')
}
})

const changeSelected = (keys: VKey[] | VKey[] | VKey[][]) => {
callEmit(props.onChange, props.multiple ? keys : [keys])
}

const handleConfirm = () => {
callEmit(props.onConfirm)
}
const handleCancel = () => {
callEmit(props.onCancel)
}

const renderFooter = () => {
if (!props.multiple) {
return
}

return (
<PanelFooter
prefixCls={mergedPrefixCls.value}
locale={locale}
onConfirm={handleConfirm}
onCancel={handleCancel}
/>
)
}

return () => {
const prefixCls = `${mergedPrefixCls.value}-cascader-panel`
const {
dataSource,
disableData,
expandIcon,
expandTrigger,
fullPath,
loadChildren,
multiple,

searchValue,
searchFn,
separator,
strategy,
virtual,

onExpand,
onLoaded,
} = props
const panelProps = {
selectedKeys: props.value,
dataSource,
disableData,
childrenKey: 'children',
getKey: 'key',
expandIcon,
expandTrigger,
fullPath,
loadChildren,
labelKey: 'label',
multiple,
searchFn,
searchValue,
separator,
strategy,
virtual,
onExpand,
onLoaded,
'onUpdate:selectedKeys': changeSelected,
} as CascaderPanelProps

return (
<div class={prefixCls} tabindex={-1} onMousedown={evt => evt.preventDefault()}>
<IxCascaderPanel {...panelProps} />
{renderFooter()}
</div>
)
}
},
})

0 comments on commit 88b751b

Please sign in to comment.