| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608 |
- import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
- import _extends from "@babel/runtime/helpers/esm/extends";
- import { createVNode as _createVNode, resolveDirective as _resolveDirective } from "vue";
- import OptionList from './OptionList';
- import { formatStrategyValues, SHOW_CHILD } from './utils/strategyUtil';
- import { useProvideSelectContext } from './TreeSelectContext';
- import { useProvideLegacySelectContext } from './LegacyContext';
- import useTreeData from './hooks/useTreeData';
- import { toArray, fillFieldNames, isNil } from './utils/valueUtil';
- import useCache from './hooks/useCache';
- import useDataEntities from './hooks/useDataEntities';
- import { fillAdditionalInfo, fillLegacyProps } from './utils/legacyUtil';
- import useCheckedKeys from './hooks/useCheckedKeys';
- import useFilterTreeData from './hooks/useFilterTreeData';
- import warningProps from './utils/warningPropsUtil';
- import { baseSelectPropsWithoutPrivate } from '../vc-select/BaseSelect';
- import { computed, defineComponent, ref, shallowRef, toRaw, toRef, toRefs, watchEffect } from 'vue';
- import omit from '../_util/omit';
- import PropTypes from '../_util/vue-types';
- import { BaseSelect } from '../vc-select';
- import { initDefaultProps } from '../_util/props-util';
- import useId from '../vc-select/hooks/useId';
- import useMergedState from '../_util/hooks/useMergedState';
- import { conductCheck } from '../vc-tree/utils/conductUtil';
- import { warning } from '../vc-util/warning';
- import { toReactive } from '../_util/toReactive';
- import useMaxLevel from '../vc-tree/useMaxLevel';
- export function treeSelectProps() {
- return _extends(_extends({}, omit(baseSelectPropsWithoutPrivate(), ['mode'])), {
- prefixCls: String,
- id: String,
- value: {
- type: [String, Number, Object, Array]
- },
- defaultValue: {
- type: [String, Number, Object, Array]
- },
- onChange: {
- type: Function
- },
- searchValue: String,
- /** @deprecated Use `searchValue` instead */
- inputValue: String,
- onSearch: {
- type: Function
- },
- autoClearSearchValue: {
- type: Boolean,
- default: undefined
- },
- filterTreeNode: {
- type: [Boolean, Function],
- default: undefined
- },
- treeNodeFilterProp: String,
- // >>> Select
- onSelect: Function,
- onDeselect: Function,
- showCheckedStrategy: {
- type: String
- },
- treeNodeLabelProp: String,
- fieldNames: {
- type: Object
- },
- // >>> Mode
- multiple: {
- type: Boolean,
- default: undefined
- },
- treeCheckable: {
- type: Boolean,
- default: undefined
- },
- treeCheckStrictly: {
- type: Boolean,
- default: undefined
- },
- labelInValue: {
- type: Boolean,
- default: undefined
- },
- // >>> Data
- treeData: {
- type: Array
- },
- treeDataSimpleMode: {
- type: [Boolean, Object],
- default: undefined
- },
- loadData: {
- type: Function
- },
- treeLoadedKeys: {
- type: Array
- },
- onTreeLoad: {
- type: Function
- },
- // >>> Expanded
- treeDefaultExpandAll: {
- type: Boolean,
- default: undefined
- },
- treeExpandedKeys: {
- type: Array
- },
- treeDefaultExpandedKeys: {
- type: Array
- },
- onTreeExpand: {
- type: Function
- },
- // >>> Options
- virtual: {
- type: Boolean,
- default: undefined
- },
- listHeight: Number,
- listItemHeight: Number,
- onDropdownVisibleChange: {
- type: Function
- },
- // >>> Tree
- treeLine: {
- type: [Boolean, Object],
- default: undefined
- },
- treeIcon: PropTypes.any,
- showTreeIcon: {
- type: Boolean,
- default: undefined
- },
- switcherIcon: PropTypes.any,
- treeMotion: PropTypes.any,
- children: Array,
- treeExpandAction: String,
- showArrow: {
- type: Boolean,
- default: undefined
- },
- showSearch: {
- type: Boolean,
- default: undefined
- },
- open: {
- type: Boolean,
- default: undefined
- },
- defaultOpen: {
- type: Boolean,
- default: undefined
- },
- disabled: {
- type: Boolean,
- default: undefined
- },
- placeholder: PropTypes.any,
- maxTagPlaceholder: {
- type: Function
- },
- dropdownPopupAlign: PropTypes.any,
- customSlots: Object
- });
- }
- function isRawValue(value) {
- return !value || typeof value !== 'object';
- }
- export default defineComponent({
- compatConfig: {
- MODE: 3
- },
- name: 'TreeSelect',
- inheritAttrs: false,
- props: initDefaultProps(treeSelectProps(), {
- treeNodeFilterProp: 'value',
- autoClearSearchValue: true,
- showCheckedStrategy: SHOW_CHILD,
- listHeight: 200,
- listItemHeight: 20,
- prefixCls: 'vc-tree-select'
- }),
- setup(props, _ref) {
- let {
- attrs,
- expose,
- slots
- } = _ref;
- const mergedId = useId(toRef(props, 'id'));
- const treeConduction = computed(() => props.treeCheckable && !props.treeCheckStrictly);
- const mergedCheckable = computed(() => props.treeCheckable || props.treeCheckStrictly);
- const mergedLabelInValue = computed(() => props.treeCheckStrictly || props.labelInValue);
- const mergedMultiple = computed(() => mergedCheckable.value || props.multiple);
- // ========================== Warning ===========================
- if (process.env.NODE_ENV !== 'production') {
- watchEffect(() => {
- warningProps(props);
- });
- }
- // ========================= FieldNames =========================
- const mergedFieldNames = computed(() => fillFieldNames(props.fieldNames));
- // =========================== Search ===========================
- const [mergedSearchValue, setSearchValue] = useMergedState('', {
- value: computed(() => props.searchValue !== undefined ? props.searchValue : props.inputValue),
- postState: search => search || ''
- });
- const onInternalSearch = searchText => {
- var _a;
- setSearchValue(searchText);
- (_a = props.onSearch) === null || _a === void 0 ? void 0 : _a.call(props, searchText);
- };
- // ============================ Data ============================
- // `useTreeData` only do convert of `children` or `simpleMode`.
- // Else will return origin `treeData` for perf consideration.
- // Do not do anything to loop the data.
- const mergedTreeData = useTreeData(toRef(props, 'treeData'), toRef(props, 'children'), toRef(props, 'treeDataSimpleMode'));
- const {
- keyEntities,
- valueEntities
- } = useDataEntities(mergedTreeData, mergedFieldNames);
- /** Get `missingRawValues` which not exist in the tree yet */
- const splitRawValues = newRawValues => {
- const missingRawValues = [];
- const existRawValues = [];
- // Keep missing value in the cache
- newRawValues.forEach(val => {
- if (valueEntities.value.has(val)) {
- existRawValues.push(val);
- } else {
- missingRawValues.push(val);
- }
- });
- return {
- missingRawValues,
- existRawValues
- };
- };
- // Filtered Tree
- const filteredTreeData = useFilterTreeData(mergedTreeData, mergedSearchValue, {
- fieldNames: mergedFieldNames,
- treeNodeFilterProp: toRef(props, 'treeNodeFilterProp'),
- filterTreeNode: toRef(props, 'filterTreeNode')
- });
- // =========================== Label ============================
- const getLabel = item => {
- if (item) {
- if (props.treeNodeLabelProp) {
- return item[props.treeNodeLabelProp];
- }
- // Loop from fieldNames
- const {
- _title: titleList
- } = mergedFieldNames.value;
- for (let i = 0; i < titleList.length; i += 1) {
- const title = item[titleList[i]];
- if (title !== undefined) {
- return title;
- }
- }
- }
- };
- // ========================= Wrap Value =========================
- const toLabeledValues = draftValues => {
- const values = toArray(draftValues);
- return values.map(val => {
- if (isRawValue(val)) {
- return {
- value: val
- };
- }
- return val;
- });
- };
- const convert2LabelValues = draftValues => {
- const values = toLabeledValues(draftValues);
- return values.map(item => {
- let {
- label: rawLabel
- } = item;
- const {
- value: rawValue,
- halfChecked: rawHalfChecked
- } = item;
- let rawDisabled;
- const entity = valueEntities.value.get(rawValue);
- // Fill missing label & status
- if (entity) {
- rawLabel = rawLabel !== null && rawLabel !== void 0 ? rawLabel : getLabel(entity.node);
- rawDisabled = entity.node.disabled;
- }
- return {
- label: rawLabel,
- value: rawValue,
- halfChecked: rawHalfChecked,
- disabled: rawDisabled
- };
- });
- };
- // =========================== Values ===========================
- const [internalValue, setInternalValue] = useMergedState(props.defaultValue, {
- value: toRef(props, 'value')
- });
- const rawMixedLabeledValues = computed(() => toLabeledValues(internalValue.value));
- // Split value into full check and half check
- const rawLabeledValues = shallowRef([]);
- const rawHalfLabeledValues = shallowRef([]);
- watchEffect(() => {
- const fullCheckValues = [];
- const halfCheckValues = [];
- rawMixedLabeledValues.value.forEach(item => {
- if (item.halfChecked) {
- halfCheckValues.push(item);
- } else {
- fullCheckValues.push(item);
- }
- });
- rawLabeledValues.value = fullCheckValues;
- rawHalfLabeledValues.value = halfCheckValues;
- });
- // const [mergedValues] = useCache(rawLabeledValues);
- const rawValues = computed(() => rawLabeledValues.value.map(item => item.value));
- const {
- maxLevel,
- levelEntities
- } = useMaxLevel(keyEntities);
- // Convert value to key. Will fill missed keys for conduct check.
- const [rawCheckedValues, rawHalfCheckedValues] = useCheckedKeys(rawLabeledValues, rawHalfLabeledValues, treeConduction, keyEntities, maxLevel, levelEntities);
- // Convert rawCheckedKeys to check strategy related values
- const displayValues = computed(() => {
- // Collect keys which need to show
- const displayKeys = formatStrategyValues(rawCheckedValues.value, props.showCheckedStrategy, keyEntities.value, mergedFieldNames.value);
- // Convert to value and filled with label
- const values = displayKeys.map(key => {
- var _a, _b, _c;
- return (_c = (_b = (_a = keyEntities.value[key]) === null || _a === void 0 ? void 0 : _a.node) === null || _b === void 0 ? void 0 : _b[mergedFieldNames.value.value]) !== null && _c !== void 0 ? _c : key;
- });
- // Back fill with origin label
- const labeledValues = values.map(val => {
- const targetItem = rawLabeledValues.value.find(item => item.value === val);
- return {
- value: val,
- label: targetItem === null || targetItem === void 0 ? void 0 : targetItem.label
- };
- });
- const rawDisplayValues = convert2LabelValues(labeledValues);
- const firstVal = rawDisplayValues[0];
- if (!mergedMultiple.value && firstVal && isNil(firstVal.value) && isNil(firstVal.label)) {
- return [];
- }
- return rawDisplayValues.map(item => {
- var _a;
- return _extends(_extends({}, item), {
- label: (_a = item.label) !== null && _a !== void 0 ? _a : item.value
- });
- });
- });
- const [cachedDisplayValues] = useCache(displayValues);
- // =========================== Change ===========================
- const triggerChange = (newRawValues, extra, source) => {
- const labeledValues = convert2LabelValues(newRawValues);
- setInternalValue(labeledValues);
- // Clean up if needed
- if (props.autoClearSearchValue) {
- setSearchValue('');
- }
- // Generate rest parameters is costly, so only do it when necessary
- if (props.onChange) {
- let eventValues = newRawValues;
- if (treeConduction.value) {
- const formattedKeyList = formatStrategyValues(newRawValues, props.showCheckedStrategy, keyEntities.value, mergedFieldNames.value);
- eventValues = formattedKeyList.map(key => {
- const entity = valueEntities.value.get(key);
- return entity ? entity.node[mergedFieldNames.value.value] : key;
- });
- }
- const {
- triggerValue,
- selected
- } = extra || {
- triggerValue: undefined,
- selected: undefined
- };
- let returnRawValues = eventValues;
- // We need fill half check back
- if (props.treeCheckStrictly) {
- const halfValues = rawHalfLabeledValues.value.filter(item => !eventValues.includes(item.value));
- returnRawValues = [...returnRawValues, ...halfValues];
- }
- const returnLabeledValues = convert2LabelValues(returnRawValues);
- const additionalInfo = {
- // [Legacy] Always return as array contains label & value
- preValue: rawLabeledValues.value,
- triggerValue
- };
- // [Legacy] Fill legacy data if user query.
- // This is expansive that we only fill when user query
- // https://github.com/react-component/tree-select/blob/fe33eb7c27830c9ac70cd1fdb1ebbe7bc679c16a/src/Select.jsx
- let showPosition = true;
- if (props.treeCheckStrictly || source === 'selection' && !selected) {
- showPosition = false;
- }
- fillAdditionalInfo(additionalInfo, triggerValue, newRawValues, mergedTreeData.value, showPosition, mergedFieldNames.value);
- if (mergedCheckable.value) {
- additionalInfo.checked = selected;
- } else {
- additionalInfo.selected = selected;
- }
- const returnValues = mergedLabelInValue.value ? returnLabeledValues : returnLabeledValues.map(item => item.value);
- props.onChange(mergedMultiple.value ? returnValues : returnValues[0], mergedLabelInValue.value ? null : returnLabeledValues.map(item => item.label), additionalInfo);
- }
- };
- // ========================== Options ===========================
- /** Trigger by option list */
- const onOptionSelect = (selectedKey, _ref2) => {
- let {
- selected,
- source
- } = _ref2;
- var _a, _b, _c;
- const keyEntitiesValue = toRaw(keyEntities.value);
- const valueEntitiesValue = toRaw(valueEntities.value);
- const entity = keyEntitiesValue[selectedKey];
- const node = entity === null || entity === void 0 ? void 0 : entity.node;
- const selectedValue = (_a = node === null || node === void 0 ? void 0 : node[mergedFieldNames.value.value]) !== null && _a !== void 0 ? _a : selectedKey;
- // Never be falsy but keep it safe
- if (!mergedMultiple.value) {
- // Single mode always set value
- triggerChange([selectedValue], {
- selected: true,
- triggerValue: selectedValue
- }, 'option');
- } else {
- let newRawValues = selected ? [...rawValues.value, selectedValue] : rawCheckedValues.value.filter(v => v !== selectedValue);
- // Add keys if tree conduction
- if (treeConduction.value) {
- // Should keep missing values
- const {
- missingRawValues,
- existRawValues
- } = splitRawValues(newRawValues);
- const keyList = existRawValues.map(val => valueEntitiesValue.get(val).key);
- // Conduction by selected or not
- let checkedKeys;
- if (selected) {
- ({
- checkedKeys
- } = conductCheck(keyList, true, keyEntitiesValue, maxLevel.value, levelEntities.value));
- } else {
- ({
- checkedKeys
- } = conductCheck(keyList, {
- checked: false,
- halfCheckedKeys: rawHalfCheckedValues.value
- }, keyEntitiesValue, maxLevel.value, levelEntities.value));
- }
- // Fill back of keys
- newRawValues = [...missingRawValues, ...checkedKeys.map(key => keyEntitiesValue[key].node[mergedFieldNames.value.value])];
- }
- triggerChange(newRawValues, {
- selected,
- triggerValue: selectedValue
- }, source || 'option');
- }
- // Trigger select event
- if (selected || !mergedMultiple.value) {
- (_b = props.onSelect) === null || _b === void 0 ? void 0 : _b.call(props, selectedValue, fillLegacyProps(node));
- } else {
- (_c = props.onDeselect) === null || _c === void 0 ? void 0 : _c.call(props, selectedValue, fillLegacyProps(node));
- }
- };
- // ========================== Dropdown ==========================
- const onInternalDropdownVisibleChange = open => {
- if (props.onDropdownVisibleChange) {
- const legacyParam = {};
- Object.defineProperty(legacyParam, 'documentClickClose', {
- get() {
- warning(false, 'Second param of `onDropdownVisibleChange` has been removed.');
- return false;
- }
- });
- props.onDropdownVisibleChange(open, legacyParam);
- }
- };
- // ====================== Display Change ========================
- const onDisplayValuesChange = (newValues, info) => {
- const newRawValues = newValues.map(item => item.value);
- if (info.type === 'clear') {
- triggerChange(newRawValues, {}, 'selection');
- return;
- }
- // TreeSelect only have multiple mode which means display change only has remove
- if (info.values.length) {
- onOptionSelect(info.values[0].value, {
- selected: false,
- source: 'selection'
- });
- }
- };
- const {
- treeNodeFilterProp,
- // Data
- loadData,
- treeLoadedKeys,
- onTreeLoad,
- // Expanded
- treeDefaultExpandAll,
- treeExpandedKeys,
- treeDefaultExpandedKeys,
- onTreeExpand,
- // Options
- virtual,
- listHeight,
- listItemHeight,
- // Tree
- treeLine,
- treeIcon,
- showTreeIcon,
- switcherIcon,
- treeMotion,
- customSlots,
- dropdownMatchSelectWidth,
- treeExpandAction
- } = toRefs(props);
- useProvideLegacySelectContext(toReactive({
- checkable: mergedCheckable,
- loadData,
- treeLoadedKeys,
- onTreeLoad,
- checkedKeys: rawCheckedValues,
- halfCheckedKeys: rawHalfCheckedValues,
- treeDefaultExpandAll,
- treeExpandedKeys,
- treeDefaultExpandedKeys,
- onTreeExpand,
- treeIcon,
- treeMotion,
- showTreeIcon,
- switcherIcon,
- treeLine,
- treeNodeFilterProp,
- keyEntities,
- customSlots
- }));
- useProvideSelectContext(toReactive({
- virtual,
- listHeight,
- listItemHeight,
- treeData: filteredTreeData,
- fieldNames: mergedFieldNames,
- onSelect: onOptionSelect,
- dropdownMatchSelectWidth,
- treeExpandAction
- }));
- const selectRef = ref();
- expose({
- focus() {
- var _a;
- (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.focus();
- },
- blur() {
- var _a;
- (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.blur();
- },
- scrollTo(arg) {
- var _a;
- (_a = selectRef.value) === null || _a === void 0 ? void 0 : _a.scrollTo(arg);
- }
- });
- return () => {
- var _a;
- const restProps = omit(props, ['id', 'prefixCls', 'customSlots',
- // Value
- 'value', 'defaultValue', 'onChange', 'onSelect', 'onDeselect',
- // Search
- 'searchValue', 'inputValue', 'onSearch', 'autoClearSearchValue', 'filterTreeNode', 'treeNodeFilterProp',
- // Selector
- 'showCheckedStrategy', 'treeNodeLabelProp',
- // Mode
- 'multiple', 'treeCheckable', 'treeCheckStrictly', 'labelInValue',
- // FieldNames
- 'fieldNames',
- // Data
- 'treeDataSimpleMode', 'treeData', 'children', 'loadData', 'treeLoadedKeys', 'onTreeLoad',
- // Expanded
- 'treeDefaultExpandAll', 'treeExpandedKeys', 'treeDefaultExpandedKeys', 'onTreeExpand',
- // Options
- 'virtual', 'listHeight', 'listItemHeight', 'onDropdownVisibleChange',
- // Tree
- 'treeLine', 'treeIcon', 'showTreeIcon', 'switcherIcon', 'treeMotion']);
- return _createVNode(BaseSelect, _objectSpread(_objectSpread(_objectSpread({
- "ref": selectRef
- }, attrs), restProps), {}, {
- "id": mergedId,
- "prefixCls": props.prefixCls,
- "mode": mergedMultiple.value ? 'multiple' : undefined,
- "displayValues": cachedDisplayValues.value,
- "onDisplayValuesChange": onDisplayValuesChange,
- "searchValue": mergedSearchValue.value,
- "onSearch": onInternalSearch,
- "OptionList": OptionList,
- "emptyOptions": !mergedTreeData.value.length,
- "onDropdownVisibleChange": onInternalDropdownVisibleChange,
- "tagRender": props.tagRender || slots.tagRender,
- "dropdownMatchSelectWidth": (_a = props.dropdownMatchSelectWidth) !== null && _a !== void 0 ? _a : true
- }), slots);
- };
- }
- });
|