zent
Version:
一套前端设计语言和基于React的实现
421 lines (420 loc) • 19.5 kB
JavaScript
import { __assign, __extends } from "tslib";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import cx from 'classnames';
import { Component, createRef } from 'react';
import Popover from '../popover';
import { I18nReceiver as Receiver } from '../i18n';
import MenuContent from './components/MenuContent';
import { union, difference, getPathValue, getPathLabel, getPathToNode, } from './path-fns';
import { getNodeKey } from './node-fns';
import { CascaderChangeAction, CascaderLoadAction, } from './types';
import SearchContent from './components/SearchContent';
import debounce from '../utils/debounce';
import TextMark from '../text-mark';
import { DisabledContext } from '../disabled';
import shallowEqual from '../utils/shallowEqual';
import { TagsTrigger } from './trigger/TagsTrigger';
import { SingleTrigger } from './trigger/SingleTrigger';
import { Forest } from './forest';
import noop from '../utils/noop';
import memorizeOne from '../utils/memorize-one';
import { simplify } from './simplify';
function isMultiple(props) {
return props.multiple;
}
function isSingle(props) {
return !props.multiple;
}
var FILTER_DEBOUNCE_TIME = 200;
var defaultFilter = function (keyword, path) {
return path.some(function (node) {
return node.label.toLowerCase().includes(keyword.toLowerCase());
});
};
var defaultHighlight = function (keyword, path) {
return path.map(function (node, index) {
return (_jsxs("span", __assign({ "data-zv": '10.0.17' }, { children: [_jsx(TextMark, { searchWords: [keyword], textToHighlight: node.label, highlightClassName: "zent-cascader-v2--highlight", autoEscape: true }, void 0), index !== path.length - 1 && ' / '] }), getPathValue(path.slice(0, index + 1))));
});
};
function getActiveValue(props) {
var activeValue = [];
if (isMultiple(props) && props.value.length > 0) {
activeValue = props.value[0];
}
if (isSingle(props)) {
activeValue = props.value;
}
return activeValue;
}
function getSelectedPaths(props, options) {
var selectedPaths = isMultiple(props)
? props.value.map(function (x) { return options.getPathByValue(x); })
: [options.getPathByValue(props.value)];
return selectedPaths.filter(function (p) { return p.length !== 0; });
}
function toggleLoading(loading, val, isLoading) {
var contains = loading.indexOf(val) !== -1;
if (isLoading && !contains) {
return loading.concat(val);
}
if (!isLoading && contains) {
return loading.filter(function (v) { return v !== val; });
}
return loading;
}
function isControlled(props) {
return ('visible' in props &&
'onVisibleChange' in props &&
typeof props.onVisibleChange === 'function');
}
function getVisible(props, state) {
if (isControlled(props)) {
return !!props.visible;
}
return state.visible;
}
var MenuCascader = (function (_super) {
__extends(MenuCascader, _super);
function MenuCascader(props) {
var _this = _super.call(this, props) || this;
_this.tagsTriggerRef = createRef();
_this.getSelectionMap = memorizeOne(function (selectedPaths) {
return _this.getSelectionMapImpl(selectedPaths);
});
_this.getSimplifySelectionMap = memorizeOne(function (selectedPaths, mode) {
if (mode === void 0) { mode = 'excludeDisabled'; }
return _this.getSelectionMapImpl(selectedPaths, mode);
});
_this.simplify = function (options, mode) {
if (mode === void 0) { mode = 'excludeDisabled'; }
return simplify(options, _this.getSelectionMapImpl(options, mode));
};
_this.getSearchResultList = memorizeOne(function (options, resultList) {
return resultList.map(function (path) {
var values = path.map(function (x) { return x.value; });
return options.getPathByValue(values);
});
});
_this.onVisibleChange = function (visible) {
var keyword = _this.state.keyword;
if (_this.disabled) {
return;
}
_this.setVisible(visible);
_this.setState({
keyword: visible === false ? '' : keyword,
});
};
_this.onKeywordChange = function (keyword) {
_this.setState({ keyword: keyword }, _this.filterOptions);
};
_this.filterOptions = debounce(function () {
var _a = _this.state, keyword = _a.keyword, options = _a.options;
if (!keyword) {
return;
}
var _b = _this.props, async = _b.async, asyncFilter = _b.asyncFilter, filter = _b.filter, limit = _b.limit;
if (async) {
_this.setState({ isSearching: true });
asyncFilter(keyword, limit)
.then(function (searchList) {
_this.setSearchState(searchList);
})
.finally(function () {
_this.setState({ isSearching: false });
});
}
else {
var searchList = options
.reducePath(function (acc, path) {
acc.push(path);
return acc;
}, [])
.filter(function (path) { return filter(keyword, path); });
_this.setSearchState(searchList);
}
}, FILTER_DEBOUNCE_TIME);
_this.setSearchState = function (searchList) {
var limit = _this.props.limit;
var size = searchList.length;
_this.setState({
searchResultList: limit <= size ? searchList : searchList.slice(0, limit),
});
};
_this.onMenuOptionHover = function (node) {
_this.onMenuOptionSelect(node, noop, 'hover');
};
_this.onMenuOptionClick = function (node, closePopup) {
_this.onMenuOptionSelect(node, closePopup, 'click');
};
_this.onMenuOptionSelect = function (node, closePopup, source) {
var _a = _this.props, loadOptions = _a.loadOptions, multiple = _a.multiple;
var loading = _this.state.loading;
var needLoading = node.loadChildrenOnExpand && loadOptions;
var selectedOptions = getPathToNode(node);
var newValue = selectedOptions.map(function (n) { return n.value; });
var newState = {
activeValue: newValue,
keyword: '',
};
var hasChildren = node.children && node.children.length > 0;
var needClose = !node.loadChildrenOnExpand &&
!hasChildren &&
!multiple &&
source === 'click';
var nodeKey = getNodeKey(node);
if (needLoading) {
newState.loading = toggleLoading(loading, nodeKey, true);
}
_this.setState(newState, function () {
if (needLoading) {
loadOptions(selectedOptions, {
action: CascaderLoadAction.LoadChildren,
}).finally(function () {
_this.setState(function (state) {
return {
loading: toggleLoading(state.loading, nodeKey, false),
};
});
});
}
if (isSingle(_this.props)) {
var _a = _this.props.changeOnSelect, changeOnSelect = _a === void 0 ? false : _a;
var needTriggerChange = needClose || (changeOnSelect && source === 'click');
if (needTriggerChange) {
_this.props.onChange(selectedOptions.map(function (it) { return it.value; }), selectedOptions, { action: CascaderChangeAction.Change });
}
}
if (needClose) {
closePopup();
}
});
};
_this.toggleMenuOption = function (node, checked) {
if (isMultiple(_this.props)) {
var onChange_1 = _this.props.onChange;
var _a = _this.state, options = _a.options, oldSelectedPaths = _a.selectedPaths;
var affectedPaths = options.getPaths(node, function (path) {
return path.every(function (node) { return !node.disabled; });
});
var selectedPaths_1 = checked
? union(oldSelectedPaths, affectedPaths)
: difference(oldSelectedPaths, affectedPaths);
selectedPaths_1 = options.sort(selectedPaths_1);
var value_1 = selectedPaths_1.map(function (list) { return list.map(function (n) { return n.value; }); });
_this.setState({ selectedPaths: selectedPaths_1 }, function () {
var _a;
onChange_1(value_1, selectedPaths_1, {
action: CascaderChangeAction.Change,
simplify: _this.simplify,
});
if (_this.props.searchable) {
(_a = _this.tagsTriggerRef.current) === null || _a === void 0 ? void 0 : _a.focus();
}
});
}
};
_this.onSearchOptionClick = function (path, closePopup) {
var activeValue = path.map(function (n) { return n.value; });
_this.setState({ activeValue: activeValue }, function () {
_this.onMenuOptionClick(path[path.length - 1], closePopup);
});
};
_this.toggleSearchOption = function (path, checked) {
_this.toggleMenuOption(path[path.length - 1], checked);
};
_this.onClear = function () {
_this.setVisible(false);
_this.setState({
activeValue: [],
selectedPaths: [],
}, function () {
if (isSingle(_this.props)) {
_this.props.onChange([], [], { action: CascaderChangeAction.Clear });
}
else {
_this.props.onChange([], [], {
action: CascaderChangeAction.Clear,
simplify: _this.simplify,
});
}
});
};
_this.scrollLoad = function (parent) {
var loadOptions = _this.props.loadOptions;
var currentHasMore = parent
? parent.loadChildrenOnScroll
: _this.props.loadChildrenOnScroll;
if (currentHasMore === false) {
return Promise.resolve();
}
var selectedOptions = getPathToNode(parent);
return loadOptions(selectedOptions, {
action: CascaderLoadAction.Scroll,
});
};
_this.onRemove = function (node) {
if (_this.disabled) {
return;
}
_this.toggleMenuOption(node, false);
};
_this.renderPopoverContent = function (i18n) {
var _a = _this.props, expandTrigger = _a.expandTrigger, scrollable = _a.scrollable, multiple = _a.multiple, searchable = _a.searchable, highlight = _a.highlight, loadChildrenOnScroll = _a.loadChildrenOnScroll, renderItemContent = _a.renderItemContent, getItemTooltip = _a.getItemTooltip, renderList = _a.renderList, multipleType = _a.multipleType;
var _b = _this.state, options = _b.options, activeValue = _b.activeValue, keyword = _b.keyword, isSearching = _b.isSearching, searchResultList = _b.searchResultList, loading = _b.loading, selectedPaths = _b.selectedPaths;
var visible = _this.getVisible();
var selectionMap = _this.getSelectionMap(selectedPaths);
if (searchable && visible && keyword) {
return (_jsx(SearchContent, { i18n: i18n, multiple: multiple, isSearching: isSearching, searchList: _this.getSearchResultList(options, searchResultList), keyword: keyword, highlight: highlight, onOptionToggle: _this.toggleSearchOption, onOptionClick: _this.onSearchOptionClick, selectionMap: selectionMap }, void 0));
}
return (_jsx(MenuContent, { value: activeValue, options: options.getTrees(), expandTrigger: expandTrigger, i18n: i18n, scrollable: scrollable, loadChildrenOnScroll: loadChildrenOnScroll, multiple: multiple, multipleType: multipleType, onOptionClick: _this.onMenuOptionClick, onOptionHover: _this.onMenuOptionHover, scrollLoad: _this.scrollLoad, onOptionToggle: _this.toggleMenuOption, loading: loading, selectionMap: selectionMap, renderItemContent: renderItemContent, getItemTooltip: getItemTooltip, renderList: renderList }, void 0));
};
var options = new Forest(props.options);
_this.state = {
options: options,
activeValue: getActiveValue(props),
visible: false,
prevProps: props,
selectedPaths: getSelectedPaths(props, options),
keyword: '',
isSearching: false,
searchResultList: [],
loading: [],
};
return _this;
}
MenuCascader.getDerivedStateFromProps = function (props, state) {
var prevProps = state.prevProps, options = state.options;
var newState = {
prevProps: props,
};
var newOptions = options;
var optionsChanged = false;
if (prevProps.options !== props.options) {
newOptions = new Forest(props.options);
newState.options = newOptions;
optionsChanged = true;
}
if (optionsChanged ||
prevProps.multiple !== props.multiple ||
!shallowEqual(prevProps.value, props.value)) {
newState.selectedPaths = getSelectedPaths(props, newOptions);
}
var visible = getVisible(props, state);
if (!visible) {
newState.activeValue = getActiveValue(props);
}
return newState;
};
Object.defineProperty(MenuCascader.prototype, "disabled", {
get: function () {
var _a = this.props.disabled, disabled = _a === void 0 ? this.context.value : _a;
return disabled;
},
enumerable: false,
configurable: true
});
MenuCascader.prototype.isControlled = function () {
return isControlled(this.props);
};
MenuCascader.prototype.getVisible = function () {
return getVisible(this.props, this.state);
};
MenuCascader.prototype.setVisible = function (visible) {
if (this.isControlled()) {
this.props.onVisibleChange(visible);
}
else {
this.setState({
visible: visible,
});
}
};
MenuCascader.prototype.getSelectionMapImpl = function (selectedPaths, mode) {
if (mode === void 0) { mode = 'excludeDisabled'; }
return this.state.options.reduceNodeDfs(function (map, node) {
var key = getNodeKey(node);
var value = node.value;
if (node.children.length === 0) {
var selected = selectedPaths.some(function (path) { return path[path.length - 1].value === value; });
map.set(key, selected ? 'on' : 'off');
}
else {
var children = mode === 'excludeDisabled'
? node.children.filter(function (n) { return !n.disabled; })
: node.children;
var childrenState = children.reduce(function (acc, n) {
var k = getNodeKey(n);
var v = map.get(k);
if (v === 'on') {
acc.on += 1;
}
else if (v === 'off') {
acc.off += 1;
}
return acc;
}, { on: 0, off: 0 });
var childrenCount = children.length;
if (childrenState.on === childrenCount && childrenCount > 0) {
map.set(key, 'on');
}
else if (childrenState.off === childrenCount) {
map.set(key, 'off');
}
else {
map.set(key, 'partial');
}
}
return map;
}, new Map());
};
MenuCascader.prototype.render = function () {
var _this = this;
var _a = this.props, className = _a.className, popupClassName = _a.popupClassName, placeholder = _a.placeholder, searchable = _a.searchable, clearable = _a.clearable, renderValue = _a.renderValue, maxLine = _a.maxLine, lineHeight = _a.lineHeight;
var _b = this.state, selectedPaths = _b.selectedPaths, keyword = _b.keyword;
var visible = this.getVisible();
var hasValue = selectedPaths.length > 0;
return (_jsx(Receiver, __assign({ componentName: "Cascader" }, { children: function (i18n) {
var _a;
var triggerCommonProps = {
placeholder: placeholder,
disabled: _this.disabled,
className: className,
clearable: clearable,
visible: visible,
keyword: keyword,
searchable: searchable,
i18n: i18n,
renderValue: renderValue,
maxLine: maxLine,
lineHeight: lineHeight,
onClear: _this.onClear,
onKeywordChange: _this.onKeywordChange,
};
return (_jsxs(Popover, __assign({ className: cx('zent-cascader-v2__popup', popupClassName), position: Popover.Position.CascaderAutoBottomLeft, visible: visible, onVisibleChange: _this.onVisibleChange, cushion: 4 }, { children: [_jsx(Popover.Trigger.Click, __assign({ toggle: !searchable }, { children: isMultiple(_this.props) ? (_jsx(TagsTrigger, __assign({}, triggerCommonProps, { simplifyPaths: (_a = _this.props.simplifySelection) !== null && _a !== void 0 ? _a : false, selectedPaths: selectedPaths, selectionMap: _this.getSimplifySelectionMap(selectedPaths, _this.props.simplifySelectionMode), onRemove: _this.onRemove, renderTags: _this.props.renderTags, ref: _this.tagsTriggerRef }), void 0)) : (_jsx(SingleTrigger, __assign({}, triggerCommonProps, { selectedPath: hasValue ? selectedPaths[0] : [] }), void 0)) }), void 0), _jsx(Popover.Content, { children: _this.renderPopoverContent(i18n) }, void 0)] }), void 0));
} }), void 0));
};
MenuCascader.defaultProps = {
value: [],
options: [],
clearable: false,
multiple: false,
multipleType: 'checkbox',
maxLine: null,
lineHeight: 22,
expandTrigger: 'click',
scrollable: false,
loadChildrenOnScroll: false,
searchable: false,
async: false,
limit: 50,
renderValue: getPathLabel,
filter: defaultFilter,
highlight: defaultHighlight,
simplifySelectionMode: 'excludeDisabled',
};
MenuCascader.contextType = DisabledContext;
return MenuCascader;
}(Component));
export { MenuCascader };
export default MenuCascader;