ant-design-vue/components/vc-cascader/OptionList/Column.tsx
2024-04-22 15:26:02 +08:00

182 lines
5.4 KiB
Vue

import { isLeaf, toPathKey } from '../utils/commonUtil';
import Checkbox from './Checkbox';
import type { DefaultOptionType, SingleValueType } from '../Cascader';
import { SEARCH_MARK } from '../hooks/useSearchOptions';
import type { Key } from '../../_util/type';
import { useInjectCascader } from '../context';
import { cloneElement } from '../../_util/vnode';
export const FIX_LABEL = '__cascader_fix_label__';
export interface ColumnProps {
prefixCls: string;
multiple?: boolean;
options: DefaultOptionType[];
/** Current Column opened item key */
activeValue?: Key;
/** The value path before current column */
prevValuePath: Key[];
onToggleOpen: (open: boolean) => void;
onSelect: (valuePath: SingleValueType, leaf: boolean) => void;
onActive: (valuePath: SingleValueType) => void;
checkedSet: Set<Key>;
halfCheckedSet: Set<Key>;
loadingKeys: Key[];
isSelectable: (option: DefaultOptionType) => boolean;
}
export default function Column({
prefixCls,
multiple,
options,
activeValue,
prevValuePath,
onToggleOpen,
onSelect,
onActive,
checkedSet,
halfCheckedSet,
loadingKeys,
isSelectable,
}: ColumnProps) {
const menuPrefixCls = `${prefixCls}-menu`;
const menuItemPrefixCls = `${prefixCls}-menu-item`;
const {
fieldNames,
changeOnSelect,
expandTrigger,
expandIcon: expandIconRef,
loadingIcon: loadingIconRef,
dropdownMenuColumnStyle,
customSlots,
} = useInjectCascader();
const expandIcon = expandIconRef.value ?? customSlots.value.expandIcon?.();
const loadingIcon = loadingIconRef.value ?? customSlots.value.loadingIcon?.();
const hoverOpen = expandTrigger.value === 'hover';
// ============================ Render ============================
return (
<ul class={menuPrefixCls} role="menu">
{options.map(option => {
const { disabled } = option;
const searchOptions = option[SEARCH_MARK];
const label = option[FIX_LABEL] ?? option[fieldNames.value.label];
const value = option[fieldNames.value.value];
const isMergedLeaf = isLeaf(option, fieldNames.value);
// Get real value of option. Search option is different way.
const fullPath = searchOptions
? searchOptions.map(opt => opt[fieldNames.value.value])
: [...prevValuePath, value];
const fullPathKey = toPathKey(fullPath);
const isLoading = loadingKeys.includes(fullPathKey);
// >>>>> checked
const checked = checkedSet.has(fullPathKey);
// >>>>> halfChecked
const halfChecked = halfCheckedSet.has(fullPathKey);
// >>>>> Open
const triggerOpenPath = () => {
if (!disabled && (!hoverOpen || !isMergedLeaf)) {
onActive(fullPath);
}
};
// >>>>> Selection
const triggerSelect = () => {
if (isSelectable(option)) {
onSelect(fullPath, isMergedLeaf);
}
};
// >>>>> Title
let title: string;
if (typeof option.title === 'string') {
title = option.title;
} else if (typeof label === 'string') {
title = label;
}
// >>>>> Render
return (
<li
key={fullPathKey}
class={[
menuItemPrefixCls,
{
[`${menuItemPrefixCls}-expand`]: !isMergedLeaf,
[`${menuItemPrefixCls}-active`]: activeValue === value,
[`${menuItemPrefixCls}-disabled`]: disabled,
[`${menuItemPrefixCls}-loading`]: isLoading,
},
]}
style={dropdownMenuColumnStyle.value}
role="menuitemcheckbox"
title={title}
aria-checked={checked}
data-path-key={fullPathKey}
onClick={() => {
triggerOpenPath();
if (!multiple || isMergedLeaf) {
triggerSelect();
}
}}
onDblclick={() => {
if (changeOnSelect.value) {
onToggleOpen(false);
}
}}
onMouseenter={() => {
if (hoverOpen) {
triggerOpenPath();
}
}}
onMousedown={e => {
// Prevent selector from blurring
e.preventDefault();
}}
>
{multiple && (
<Checkbox
prefixCls={`${prefixCls}-checkbox`}
checked={checked}
halfChecked={halfChecked}
disabled={disabled}
onClick={(e: MouseEvent) => {
e.stopPropagation();
triggerSelect();
}}
/>
)}
<div class={`${menuItemPrefixCls}-content`}>{label}</div>
{!isLoading && expandIcon && !isMergedLeaf && (
<div class={`${menuItemPrefixCls}-expand-icon`}>{cloneElement(expandIcon)}</div>
)}
{isLoading && loadingIcon && (
<div class={`${menuItemPrefixCls}-loading-icon`}>{cloneElement(loadingIcon)}</div>
)}
</li>
);
})}
</ul>
);
}
Column.props = [
'prefixCls',
'multiple',
'options',
'activeValue',
'prevValuePath',
'onToggleOpen',
'onSelect',
'onActive',
'checkedSet',
'halfCheckedSet',
'loadingKeys',
'isSelectable',
];
Column.displayName = 'Column';
Column.inheritAttrs = false;