feat: cascader add clearIcon & removeIcon slot
parent
f1f6085dbb
commit
6d2bcf0ab8
|
@ -13,6 +13,17 @@ import {
|
|||
Transition as T,
|
||||
TransitionGroup as TG,
|
||||
} from 'vue';
|
||||
import { tuple } from './type';
|
||||
|
||||
const SelectPlacements = tuple('bottomLeft', 'bottomRight', 'topLeft', 'topRight');
|
||||
export type SelectCommonPlacement = typeof SelectPlacements[number];
|
||||
|
||||
const getTransitionDirection = (placement: SelectCommonPlacement | undefined) => {
|
||||
if (placement !== undefined && (placement === 'topLeft' || placement === 'topRight')) {
|
||||
return `slide-down`;
|
||||
}
|
||||
return `slide-up`;
|
||||
};
|
||||
|
||||
export const getTransitionProps = (transitionName: string, opt: TransitionProps = {}) => {
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
|
@ -176,6 +187,6 @@ const getTransitionName = (rootPrefixCls: string, motion: string, transitionName
|
|||
return `${rootPrefixCls}-${motion}`;
|
||||
};
|
||||
|
||||
export { Transition, TransitionGroup, collapseMotion, getTransitionName };
|
||||
export { Transition, TransitionGroup, collapseMotion, getTransitionName, getTransitionDirection };
|
||||
|
||||
export default Transition;
|
||||
|
|
|
@ -16,21 +16,23 @@ Custom suffix icon
|
|||
|
||||
</docs>
|
||||
<template>
|
||||
<a-cascader
|
||||
v-model:value="value1"
|
||||
style="margin-top: 1rem"
|
||||
:options="options"
|
||||
placeholder="Please select"
|
||||
>
|
||||
<template #suffixIcon><smile-outlined class="test" /></template>
|
||||
</a-cascader>
|
||||
<a-cascader
|
||||
v-model:value="value2"
|
||||
suffix-icon="ab"
|
||||
style="margin-top: 1rem"
|
||||
:options="options"
|
||||
placeholder="Please select"
|
||||
/>
|
||||
<a-space>
|
||||
<a-cascader
|
||||
v-model:value="value1"
|
||||
style="margin-top: 1rem"
|
||||
:options="options"
|
||||
placeholder="Please select"
|
||||
>
|
||||
<template #suffixIcon><smile-outlined class="test" /></template>
|
||||
</a-cascader>
|
||||
<a-cascader
|
||||
v-model:value="value2"
|
||||
suffix-icon="ab"
|
||||
style="margin-top: 1rem"
|
||||
:options="options"
|
||||
placeholder="Please select"
|
||||
/>
|
||||
</a-space>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { SmileOutlined } from '@ant-design/icons-vue';
|
||||
|
|
|
@ -23,6 +23,8 @@ Cascade selection box.
|
|||
| --- | --- | --- | --- | --- |
|
||||
| allowClear | whether allow clear | boolean | true | |
|
||||
| autofocus | get focus when component mounted | boolean | false | |
|
||||
| bordered | Whether has border style | boolean | true | 3.2 |
|
||||
| clearIcon | The custom clear icon | slot | - | 3.2 |
|
||||
| changeOnSelect | (Work on single select) change value on each selection if set to true, see above demo for details | boolean | false | |
|
||||
| disabled | whether disabled select | boolean | false | |
|
||||
| displayRender | render function of displaying selected options, you can use #displayRender="{labels, selectedOptions}". | `({labels, selectedOptions}) => VNode` | `labels => labels.join(' / ')` | |
|
||||
|
@ -41,6 +43,7 @@ Cascade selection box.
|
|||
| options | data options of cascade | [Option](#option)\[] | - | |
|
||||
| placeholder | input placeholder | string | 'Please select' | |
|
||||
| placement | Use preset popup align config from builtinPlacements | `bottomLeft` \| `bottomRight` \| `topLeft` \| `topRight` | `bottomLeft` | 3.0 |
|
||||
| removeIcon | The custom remove icon | slot | - | 3.2 |
|
||||
| searchValue | Set search value,Need work with `showSearch` | string | - | 3.0 |
|
||||
| showSearch | Whether show search input in single mode. | boolean \| [object](#showsearch) | false | |
|
||||
| size | input size | `large` \| `default` \| `small` | `default` | |
|
||||
|
|
|
@ -15,7 +15,8 @@ import useConfigInject from '../_util/hooks/useConfigInject';
|
|||
import classNames from '../_util/classNames';
|
||||
import type { SizeType } from '../config-provider';
|
||||
import devWarning from '../vc-util/devWarning';
|
||||
import { getTransitionName } from '../_util/transition';
|
||||
import type { SelectCommonPlacement } from '../_util/transition';
|
||||
import { getTransitionDirection, getTransitionName } from '../_util/transition';
|
||||
import { useInjectFormItemContext } from '../form';
|
||||
import type { ValueType } from '../vc-cascader/Cascader';
|
||||
|
||||
|
@ -96,7 +97,7 @@ export function cascaderProps<DataNodeType extends CascaderOptionType = Cascader
|
|||
multiple: { type: Boolean, default: undefined },
|
||||
size: String as PropType<SizeType>,
|
||||
bordered: { type: Boolean, default: undefined },
|
||||
|
||||
placement: { type: String as PropType<SelectCommonPlacement> },
|
||||
suffixIcon: PropTypes.any,
|
||||
options: Array as PropType<DataNodeType[]>,
|
||||
'onUpdate:value': Function as PropType<(value: ValueType) => void>,
|
||||
|
@ -191,7 +192,17 @@ const Cascader = defineComponent({
|
|||
emit('blur', ...args);
|
||||
formItemContext.onFieldBlur();
|
||||
};
|
||||
|
||||
const mergedShowArrow = computed(() =>
|
||||
props.showArrow !== undefined ? props.showArrow : props.loading || !props.multiple,
|
||||
);
|
||||
const placement = computed(() => {
|
||||
if (props.placement !== undefined) {
|
||||
return props.placement;
|
||||
}
|
||||
return direction.value === 'rtl'
|
||||
? ('bottomRight' as SelectCommonPlacement)
|
||||
: ('bottomLeft' as SelectCommonPlacement);
|
||||
});
|
||||
return () => {
|
||||
const {
|
||||
notFoundContent = slots.notFoundContent?.(),
|
||||
|
@ -225,6 +236,7 @@ const Cascader = defineComponent({
|
|||
...props,
|
||||
multiple,
|
||||
prefixCls: prefixCls.value,
|
||||
showArrow: mergedShowArrow.value,
|
||||
},
|
||||
slots,
|
||||
);
|
||||
|
@ -245,6 +257,7 @@ const Cascader = defineComponent({
|
|||
attrs.class,
|
||||
]}
|
||||
direction={direction.value}
|
||||
placement={placement.value}
|
||||
notFoundContent={mergedNotFoundContent}
|
||||
allowClear={allowClear}
|
||||
showSearch={mergedShowSearch.value}
|
||||
|
@ -257,7 +270,11 @@ const Cascader = defineComponent({
|
|||
dropdownClassName={mergedDropdownClassName.value}
|
||||
dropdownPrefixCls={cascaderPrefixCls.value}
|
||||
choiceTransitionName={getTransitionName(rootPrefixCls.value, '', choiceTransitionName)}
|
||||
transitionName={getTransitionName(rootPrefixCls.value, 'slide-up', transitionName)}
|
||||
transitionName={getTransitionName(
|
||||
rootPrefixCls.value,
|
||||
getTransitionDirection(placement.value),
|
||||
transitionName,
|
||||
)}
|
||||
getPopupContainer={getPopupContainer.value}
|
||||
customSlots={{
|
||||
...slots,
|
||||
|
@ -265,6 +282,7 @@ const Cascader = defineComponent({
|
|||
}}
|
||||
displayRender={props.displayRender || slots.displayRender}
|
||||
maxTagPlaceholder={props.maxTagPlaceholder || slots.maxTagPlaceholder}
|
||||
showArrow={props.showArrow}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
v-slots={slots}
|
||||
|
|
|
@ -24,6 +24,8 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
|||
| --- | --- | --- | --- | --- |
|
||||
| allowClear | 是否支持清除 | boolean | true | |
|
||||
| autofocus | 自动获取焦点 | boolean | false | |
|
||||
| bordered | 是否有边框 | boolean | true | 3.2 |
|
||||
| clearIcon | 自定义的选择框清空图标 | slot | - | 3.2 |
|
||||
| changeOnSelect | (单选时生效)当此项为 true 时,点选每级菜单选项值都会发生变化,具体见上面的演示 | boolean | false | |
|
||||
| defaultValue | 默认的选中项 | string\[] \| number\[] | \[] | |
|
||||
| disabled | 禁用 | boolean | false | |
|
||||
|
@ -43,6 +45,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/UdS8y8xyZ/Cascader.svg
|
|||
| options | 可选项数据源 | [Option](#option)\[] | - | |
|
||||
| placeholder | 输入框占位文本 | string | '请选择' | |
|
||||
| placement | 浮层预设位置 | `bottomLeft` \| `bottomRight` \| `topLeft` \| `topRight` | `bottomLeft` | 3.0 |
|
||||
| removeIcon | 自定义的多选框清除图标 | slot | - | 3.2 |
|
||||
| searchValue | 设置搜索的值,需要与 `showSearch` 配合使用 | string | - | 3.0 |
|
||||
| showSearch | 在选择框中显示搜索框 | boolean \| [object](#showsearch) | false | |
|
||||
| size | 输入框大小 | `large` \| `default` \| `small` | `default` | |
|
||||
|
|
|
@ -9,7 +9,7 @@ import PropTypes from '../_util/vue-types';
|
|||
import { initDefaultProps } from '../_util/props-util';
|
||||
import useId from '../vc-select/hooks/useId';
|
||||
import useMergedState from '../_util/hooks/useMergedState';
|
||||
import { fillFieldNames, toPathKey, toPathKeys } from './utils/commonUtil';
|
||||
import { fillFieldNames, toPathKey, toPathKeys, SHOW_PARENT, SHOW_CHILD } from './utils/commonUtil';
|
||||
import useEntities from './hooks/useEntities';
|
||||
import useSearchConfig from './hooks/useSearchConfig';
|
||||
import useSearchOptions from './hooks/useSearchOptions';
|
||||
|
@ -23,6 +23,7 @@ import { BaseSelect } from '../vc-select';
|
|||
import devWarning from '../vc-util/devWarning';
|
||||
import useMaxLevel from '../vc-tree/useMaxLevel';
|
||||
|
||||
export { SHOW_PARENT, SHOW_CHILD };
|
||||
export interface ShowSearchType<OptionType extends BaseOptionType = DefaultOptionType> {
|
||||
filter?: (inputValue: string, options: OptionType[], fieldNames: FieldNames) => boolean;
|
||||
render?: (arg?: {
|
||||
|
@ -49,6 +50,7 @@ export interface InternalFieldNames extends Required<FieldNames> {
|
|||
export type SingleValueType = (string | number)[];
|
||||
|
||||
export type ValueType = SingleValueType | SingleValueType[];
|
||||
export type ShowCheckedStrategy = typeof SHOW_PARENT | typeof SHOW_CHILD;
|
||||
|
||||
export interface BaseOptionType {
|
||||
disabled?: boolean;
|
||||
|
@ -73,14 +75,11 @@ function baseCascaderProps<OptionType extends BaseOptionType = DefaultOptionType
|
|||
value: { type: [String, Number, Array] as PropType<ValueType> },
|
||||
defaultValue: { type: [String, Number, Array] as PropType<ValueType> },
|
||||
changeOnSelect: { type: Boolean, default: undefined },
|
||||
onChange: Function as PropType<
|
||||
(value: ValueType, selectedOptions?: OptionType[] | OptionType[][]) => void
|
||||
>,
|
||||
displayRender: Function as PropType<
|
||||
(opt: { labels: string[]; selectedOptions?: OptionType[] }) => any
|
||||
>,
|
||||
checkable: { type: Boolean, default: undefined },
|
||||
|
||||
showCheckedStrategy: { type: String as PropType<ShowCheckedStrategy>, default: SHOW_PARENT },
|
||||
// Search
|
||||
showSearch: {
|
||||
type: [Boolean, Object] as PropType<boolean | ShowSearchType<OptionType>>,
|
||||
|
@ -184,7 +183,7 @@ function toRawValues(value: ValueType): SingleValueType[] {
|
|||
return value;
|
||||
}
|
||||
|
||||
return value.length === 0 ? [] : [value];
|
||||
return (value.length === 0 ? [] : [value]).map(val => (Array.isArray(val) ? val : [val]));
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -215,10 +214,10 @@ export default defineComponent({
|
|||
|
||||
/** Convert path key back to value format */
|
||||
const getValueByKeyPath = (pathKeys: Key[]): SingleValueType[] => {
|
||||
const ketPathEntities = pathKeyEntities.value;
|
||||
const keyPathEntities = pathKeyEntities.value;
|
||||
|
||||
return pathKeys.map(pathKey => {
|
||||
const { nodes } = ketPathEntities[pathKey];
|
||||
const { nodes } = keyPathEntities[pathKey];
|
||||
|
||||
return nodes.map(node => node[mergedFieldNames.value.value]);
|
||||
});
|
||||
|
@ -275,12 +274,12 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
const keyPathValues = toPathKeys(existValues);
|
||||
const ketPathEntities = pathKeyEntities.value;
|
||||
const keyPathEntities = pathKeyEntities.value;
|
||||
|
||||
const { checkedKeys, halfCheckedKeys } = conductCheck(
|
||||
keyPathValues,
|
||||
true,
|
||||
ketPathEntities,
|
||||
keyPathEntities,
|
||||
maxLevel.value,
|
||||
levelEntities.value,
|
||||
);
|
||||
|
@ -295,7 +294,11 @@ export default defineComponent({
|
|||
|
||||
const deDuplicatedValues = computed(() => {
|
||||
const checkedKeys = toPathKeys(checkedValues.value);
|
||||
const deduplicateKeys = formatStrategyValues(checkedKeys, pathKeyEntities.value);
|
||||
const deduplicateKeys = formatStrategyValues(
|
||||
checkedKeys,
|
||||
pathKeyEntities.value,
|
||||
props.showCheckedStrategy,
|
||||
);
|
||||
return [...missingCheckedValues.value, ...getValueByKeyPath(deduplicateKeys)];
|
||||
});
|
||||
|
||||
|
@ -330,6 +333,7 @@ export default defineComponent({
|
|||
|
||||
// =========================== Select ===========================
|
||||
const onInternalSelect = (valuePath: SingleValueType) => {
|
||||
setSearchValue('');
|
||||
if (!multiple.value) {
|
||||
triggerChange(valuePath);
|
||||
} else {
|
||||
|
@ -379,7 +383,11 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// Roll up to parent level keys
|
||||
const deDuplicatedKeys = formatStrategyValues(checkedKeys, pathKeyEntities.value);
|
||||
const deDuplicatedKeys = formatStrategyValues(
|
||||
checkedKeys,
|
||||
pathKeyEntities.value,
|
||||
props.showCheckedStrategy,
|
||||
);
|
||||
nextCheckedValues = getValueByKeyPath(deDuplicatedKeys);
|
||||
}
|
||||
|
||||
|
@ -537,7 +545,7 @@ export default defineComponent({
|
|||
return () => {
|
||||
const emptyOptions = !(mergedSearchValue.value ? searchOptions.value : mergedOptions.value)
|
||||
.length;
|
||||
|
||||
const { dropdownMatchSelectWidth = false } = props;
|
||||
const dropdownStyle: CSSProperties =
|
||||
// Search to match width
|
||||
(mergedSearchValue.value && mergedSearchConfig.value.matchInputWidth) ||
|
||||
|
@ -555,7 +563,7 @@ export default defineComponent({
|
|||
ref={selectRef}
|
||||
id={mergedId}
|
||||
prefixCls={props.prefixCls}
|
||||
dropdownMatchSelectWidth={false}
|
||||
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
|
||||
dropdownStyle={{ ...mergedDropdownStyle.value, ...dropdownStyle }}
|
||||
// Value
|
||||
displayValues={displayValues.value}
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { DefaultOptionType, SingleValueType } from '../Cascader';
|
|||
import { SEARCH_MARK } from '../hooks/useSearchOptions';
|
||||
import type { Key } from '../../_util/type';
|
||||
import { useInjectCascader } from '../context';
|
||||
|
||||
export const FIX_LABEL = '__cascader_fix_label__';
|
||||
export interface ColumnProps {
|
||||
prefixCls: string;
|
||||
multiple?: boolean;
|
||||
|
@ -58,7 +58,7 @@ export default function Column({
|
|||
{options.map(option => {
|
||||
const { disabled } = option;
|
||||
const searchOptions = option[SEARCH_MARK];
|
||||
const label = option[fieldNames.value.label];
|
||||
const label = option[FIX_LABEL] ?? option[fieldNames.value.label];
|
||||
const value = option[fieldNames.value.value];
|
||||
|
||||
const isMergedLeaf = isLeaf(option, fieldNames.value);
|
||||
|
@ -132,6 +132,10 @@ export default function Column({
|
|||
triggerOpenPath();
|
||||
}
|
||||
}}
|
||||
onMousedown={e => {
|
||||
// Prevent selector from blurring
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
{multiple && (
|
||||
<Checkbox
|
||||
|
@ -145,7 +149,7 @@ export default function Column({
|
|||
}}
|
||||
/>
|
||||
)}
|
||||
<div class={`${menuItemPrefixCls}-content`}>{option[fieldNames.value.label]}</div>
|
||||
<div class={`${menuItemPrefixCls}-content`}>{label}</div>
|
||||
{!isLoading && expandIcon && !isMergedLeaf && (
|
||||
<div class={`${menuItemPrefixCls}-expand-icon`}>{expandIcon}</div>
|
||||
)}
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
/* eslint-disable default-case */
|
||||
import Column from './Column';
|
||||
import type { DefaultOptionType, SingleValueType } from '../Cascader';
|
||||
import { isLeaf, toPathKey, toPathKeys, toPathValueStr } from '../utils/commonUtil';
|
||||
import {
|
||||
isLeaf,
|
||||
toPathKey,
|
||||
toPathKeys,
|
||||
toPathValueStr,
|
||||
scrollIntoParentView,
|
||||
} from '../utils/commonUtil';
|
||||
import useActive from './useActive';
|
||||
import useKeyboard from './useKeyboard';
|
||||
import { toPathOptions } from '../utils/treeUtil';
|
||||
import { computed, defineComponent, ref, shallowRef, watchEffect } from 'vue';
|
||||
import { computed, defineComponent, onMounted, ref, shallowRef, watch, watchEffect } from 'vue';
|
||||
import { useBaseProps } from '../../vc-select';
|
||||
import { useInjectCascader } from '../context';
|
||||
import type { Key } from '../../_util/type';
|
||||
import type { EventHandler } from '../../_util/EventInterface';
|
||||
|
||||
import Column, { FIX_LABEL } from './Column';
|
||||
export default defineComponent({
|
||||
name: 'OptionList',
|
||||
inheritAttrs: false,
|
||||
|
@ -149,18 +154,29 @@ export default defineComponent({
|
|||
}
|
||||
};
|
||||
|
||||
useKeyboard(
|
||||
context,
|
||||
mergedOptions,
|
||||
fieldNames,
|
||||
activeValueCells,
|
||||
onPathOpen,
|
||||
containerRef,
|
||||
onKeyboardSelect,
|
||||
);
|
||||
useKeyboard(context, mergedOptions, fieldNames, activeValueCells, onPathOpen, onKeyboardSelect);
|
||||
const onListMouseDown: EventHandler = event => {
|
||||
event.preventDefault();
|
||||
};
|
||||
onMounted(() => {
|
||||
watch(
|
||||
activeValueCells,
|
||||
cells => {
|
||||
for (let i = 0; i < cells.length; i += 1) {
|
||||
const cellPath = cells.slice(0, i + 1);
|
||||
const cellKeyPath = toPathKey(cellPath);
|
||||
const ele = containerRef.value?.querySelector<HTMLElement>(
|
||||
`li[data-path-key="${cellKeyPath.replace(/\\{0,2}"/g, '\\"')}"]`, // matches unescaped double quotes
|
||||
);
|
||||
if (ele) {
|
||||
scrollIntoParentView(ele);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ flush: 'post', immediate: true },
|
||||
);
|
||||
});
|
||||
|
||||
return () => {
|
||||
// ========================== Render ==========================
|
||||
const {
|
||||
|
@ -173,8 +189,8 @@ export default defineComponent({
|
|||
|
||||
const emptyList: DefaultOptionType[] = [
|
||||
{
|
||||
[fieldNames.value.label as 'label']: notFoundContent,
|
||||
[fieldNames.value.value as 'value']: '__EMPTY__',
|
||||
[FIX_LABEL as 'label']: notFoundContent,
|
||||
disabled: true,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -3,9 +3,9 @@ import type { Key } from '../../_util/type';
|
|||
import type { Ref, SetupContext } from 'vue';
|
||||
import { computed, ref, watchEffect } from 'vue';
|
||||
import type { DefaultOptionType, InternalFieldNames, SingleValueType } from '../Cascader';
|
||||
import { toPathKey } from '../utils/commonUtil';
|
||||
import { useBaseProps } from '../../vc-select';
|
||||
import KeyCode from '../../_util/KeyCode';
|
||||
import { SEARCH_MARK } from '../hooks/useSearchOptions';
|
||||
|
||||
export default (
|
||||
context: SetupContext,
|
||||
|
@ -13,7 +13,7 @@ export default (
|
|||
fieldNames: Ref<InternalFieldNames>,
|
||||
activeValueCells: Ref<Key[]>,
|
||||
setActiveValueCells: (activeValueCells: Key[]) => void,
|
||||
containerRef: Ref<HTMLElement>,
|
||||
// containerRef: Ref<HTMLElement>,
|
||||
onKeyBoardSelect: (valueCells: SingleValueType, option: DefaultOptionType) => void,
|
||||
) => {
|
||||
const baseProps = useBaseProps();
|
||||
|
@ -32,7 +32,7 @@ export default (
|
|||
|
||||
const len = activeValueCells.value.length;
|
||||
// Fill validate active value cells and index
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
for (let i = 0; i < len && currentOptions; i += 1) {
|
||||
// Mark the active index for current options
|
||||
const nextActiveIndex = currentOptions.findIndex(
|
||||
option => option[fieldNames.value.value] === activeValueCells.value[i],
|
||||
|
@ -65,9 +65,6 @@ export default (
|
|||
// Update active value cells and scroll to target element
|
||||
const internalSetActiveValueCells = (next: Key[]) => {
|
||||
setActiveValueCells(next);
|
||||
|
||||
const ele = containerRef.value?.querySelector(`li[data-path-key="${toPathKey(next)}"]`);
|
||||
ele?.scrollIntoView?.({ block: 'nearest' });
|
||||
};
|
||||
|
||||
// Same options offset
|
||||
|
@ -165,10 +162,18 @@ export default (
|
|||
// >>> Select
|
||||
case KeyCode.ENTER: {
|
||||
if (validActiveValueCells.value.length) {
|
||||
onKeyBoardSelect(
|
||||
validActiveValueCells.value,
|
||||
lastActiveOptions.value[lastActiveIndex.value],
|
||||
);
|
||||
const option = lastActiveOptions.value[lastActiveIndex.value];
|
||||
|
||||
// Search option should revert back of origin options
|
||||
const originOptions: DefaultOptionType[] = option?.[SEARCH_MARK] || [];
|
||||
if (originOptions.length) {
|
||||
onKeyBoardSelect(
|
||||
originOptions.map(opt => opt[fieldNames.value.value]),
|
||||
originOptions[originOptions.length - 1],
|
||||
);
|
||||
} else {
|
||||
onKeyBoardSelect(validActiveValueCells.value, option);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -51,10 +51,11 @@ export default (
|
|||
labels: valueOptions.map(({ option, value }) => option?.[fieldNames.value.label] ?? value),
|
||||
selectedOptions: valueOptions.map(({ option }) => option),
|
||||
});
|
||||
|
||||
const value = toPathKey(valueCells);
|
||||
return {
|
||||
label,
|
||||
value: toPathKey(valueCells),
|
||||
value,
|
||||
key: value,
|
||||
valueCells,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -39,6 +39,7 @@ export default (
|
|||
if (
|
||||
// If is leaf option
|
||||
!children ||
|
||||
children.length === 0 ||
|
||||
// If is changeOnSelect
|
||||
changeOnSelect.value
|
||||
) {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
// rc-cascader@3.0.0-alpha.6
|
||||
import Cascader, { internalCascaderProps as cascaderProps } from './Cascader';
|
||||
// rc-cascader@3.4.2
|
||||
import Cascader, {
|
||||
internalCascaderProps as cascaderProps,
|
||||
SHOW_PARENT,
|
||||
SHOW_CHILD,
|
||||
} from './Cascader';
|
||||
|
||||
export type {
|
||||
CascaderProps,
|
||||
|
@ -8,5 +12,5 @@ export type {
|
|||
DefaultOptionType,
|
||||
BaseOptionType,
|
||||
} from './Cascader';
|
||||
export { cascaderProps };
|
||||
export { cascaderProps, SHOW_PARENT, SHOW_CHILD };
|
||||
export default Cascader;
|
||||
|
|
|
@ -6,6 +6,8 @@ import type {
|
|||
} from '../Cascader';
|
||||
|
||||
export const VALUE_SPLIT = '__RC_CASCADER_SPLIT__';
|
||||
export const SHOW_PARENT = 'SHOW_PARENT';
|
||||
export const SHOW_CHILD = 'SHOW_CHILD';
|
||||
|
||||
export function toPathKey(value: SingleValueType) {
|
||||
return value.join(VALUE_SPLIT);
|
||||
|
@ -33,3 +35,17 @@ export function fillFieldNames(fieldNames?: FieldNames): InternalFieldNames {
|
|||
export function isLeaf(option: DefaultOptionType, fieldNames: FieldNames) {
|
||||
return option.isLeaf ?? !option[fieldNames.children]?.length;
|
||||
}
|
||||
|
||||
export function scrollIntoParentView(element: HTMLElement) {
|
||||
const parent = element.parentElement;
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const elementToParent = element.offsetTop - parent.offsetTop; // offsetParent may not be parent.
|
||||
if (elementToParent - parent.scrollTop < 0) {
|
||||
parent.scrollTo({ top: elementToParent });
|
||||
} else if (elementToParent + element.offsetHeight - parent.scrollTop > parent.offsetHeight) {
|
||||
parent.scrollTo({ top: elementToParent + element.offsetHeight - parent.offsetHeight });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,27 @@
|
|||
import type { Key } from '../../_util/type';
|
||||
import type { SingleValueType, DefaultOptionType, InternalFieldNames } from '../Cascader';
|
||||
import type {
|
||||
SingleValueType,
|
||||
DefaultOptionType,
|
||||
InternalFieldNames,
|
||||
ShowCheckedStrategy,
|
||||
} from '../Cascader';
|
||||
import type { OptionsInfo } from '../hooks/useEntities';
|
||||
import { SHOW_CHILD } from './commonUtil';
|
||||
|
||||
export function formatStrategyValues(
|
||||
pathKeys: Key[],
|
||||
keyPathEntities: OptionsInfo['pathKeyEntities'],
|
||||
showCheckedStrategy: ShowCheckedStrategy,
|
||||
) {
|
||||
const valueSet = new Set(pathKeys);
|
||||
|
||||
return pathKeys.filter(key => {
|
||||
const entity = keyPathEntities[key];
|
||||
const parent = entity ? entity.parent : null;
|
||||
|
||||
if (parent && !parent.node.disabled && valueSet.has(parent.key)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
const children = entity ? entity.children : null;
|
||||
return showCheckedStrategy === SHOW_CHILD
|
||||
? !(children && children.some(child => child.key && valueSet.has(child.key)))
|
||||
: !(parent && !parent.node.disabled && valueSet.has(parent.key));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue