shineout
Version:
Shein 前端组件库
643 lines (544 loc) • 20.4 kB
JavaScript
import _createClass from "@babel/runtime/helpers/createClass";
import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _extends from "@babel/runtime/helpers/extends";
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose";
import React from 'react';
import immer from 'immer';
import classnames from 'classnames';
import { isFunc } from '../utils/is';
import { PureComponent } from '../component';
import { getUidStr } from '../utils/uid';
import DatumTree from '../Datum/Tree';
import { selectClass } from '../Select/styles';
import { cascaderClass } from './styles';
import Result from './Result';
import CascaderList from './List';
import FilterList from './FilterList';
import { docSize } from '../utils/dom/document';
import { getParent } from '../utils/dom/element';
import absoluteList from '../AnimationList/AbsoluteList';
import { isRTL } from '../config';
import { getKey } from '../utils/uid';
var OptionList = absoluteList(function (_ref) {
var focus = _ref.focus,
getRef = _ref.getRef,
other = _objectWithoutPropertiesLoose(_ref, ["focus", "getRef"]);
return focus ? React.createElement("div", _extends({}, other, {
ref: getRef
})) : null;
});
var isDescendent = function isDescendent(el, id) {
if (el.getAttribute('data-id') === id) return true;
if (!el.parentElement) return false;
return isDescendent(el.parentElement, id);
};
var DefaultProps = {
data: [],
height: 300,
clearable: true,
showArrow: true,
expandTrigger: 'click',
childrenKey: 'children'
};
var Cascader =
/*#__PURE__*/
function (_PureComponent) {
_inheritsLoose(Cascader, _PureComponent);
function Cascader(props) {
var _this;
_this = _PureComponent.call(this, props) || this;
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "pathChangeTimer", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "datum", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "selectId", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "isRendered", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "lastFoucs", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "shouldUpdateAfterRef", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "ref", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "close", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleBlur", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "handleClick", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "input", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "lastValue", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "element", void 0);
_defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "renderPending", void 0);
_this.state = {
focus: false,
path: [],
position: 'drop-down'
};
_this.datum = new DatumTree({
data: props.data,
loader: props.loader,
keygen: props.keygen,
mode: props.mode,
onChange: props.onChange,
value: props.value,
disabled: typeof props.disabled === 'function' ? props.disabled : undefined,
childrenKey: props.childrenKey || DefaultProps.childrenKey,
unmatch: props.unmatch
});
_this.isRendered = false;
_this.selectId = "select_" + getUidStr();
_this.handleClick = _this.handleState.bind(_assertThisInitialized(_assertThisInitialized(_this)), true);
_this.handleBlur = _this.handleState.bind(_assertThisInitialized(_assertThisInitialized(_this)), false);
_this.handleFocus = _this.handleFocus.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleKeyDown = _this.handleKeyDown.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleClickAway = _this.handleClickAway.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handlePathChange = _this.handlePathChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleClear = _this.handleClear.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.shouldFocus = _this.shouldFocus.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.bindRef = _this.bindRef.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleChange = _this.handleChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.bindInput = _this.bindInput.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.handleRemove = _this.handleRemove.bind(_assertThisInitialized(_assertThisInitialized(_this)));
_this.close = _this.handleBlur;
var componentRef = {
close: _this.close
};
if (props.getComponentRef) {
if (isFunc(props.getComponentRef)) {
props.getComponentRef(componentRef);
} else {
props.getComponentRef.current = componentRef;
}
}
return _this;
}
var _proto = Cascader.prototype;
_proto.componentDidMount = function componentDidMount() {
_PureComponent.prototype.componentDidMount.call(this);
this.setOpenEvent();
this.updatePathByValue();
if (this.props.mode !== undefined && this.props.loader && [0, 1, 2].includes(this.props.mode)) {
console.error(new Error("The mode " + this.props.mode + " is not supported when loader setted. Only 3 or 4 can be set."));
}
};
_proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
this.datum.mode = this.props.mode;
this.datum.updateDisabled(this.props.disabled);
this.setOpenEvent();
var _this$props = this.props,
onFilter = _this$props.onFilter,
filterText = _this$props.filterText;
if (prevProps.sourceData !== this.props.sourceData) this.datum.setData(this.props.sourceData, true);
if (prevProps.value !== this.props.value) {
this.datum.setValue(this.props.value || []);
this.updatePathByValue();
}
if (prevState.focus !== this.state.focus && !this.state.focus || prevProps.open !== this.props.open && !this.props.open) {
if (onFilter) {
setTimeout(function () {
onFilter('');
}, 400);
}
if (this.shouldFinal) {
this.updatePathByValue(true);
}
}
if (filterText !== undefined && prevProps.filterText !== filterText) {
this.updatePath();
}
};
_proto.componentWillUnmount = function componentWillUnmount() {
_PureComponent.prototype.componentWillUnmount.call(this);
this.clearClickAway();
};
_proto.setOpenEvent = function setOpenEvent() {
var focus = this.focus || this.props.inputFocus;
if (this.lastFoucs !== focus) if (focus) {
this.bindClickAway();
} else if (this.lastFoucs !== undefined) {
this.clearClickAway();
}
this.lastFoucs = focus;
};
_proto.bindRef = function bindRef(el) {
this.ref = el;
};
_proto.bindClickAway = function bindClickAway() {
document.addEventListener('mousedown', this.handleClickAway);
};
_proto.bindInput = function bindInput(input) {
this.input = input;
};
_proto.clearClickAway = function clearClickAway() {
document.removeEventListener('mousedown', this.handleClickAway);
};
_proto.shouldFocus = function shouldFocus(el) {
if (el.getAttribute('data-id') === this.selectId) return true;
if (getParent(el, "." + cascaderClass('result'))) return true;
return false;
};
_proto.updatePath = function updatePath() {
var _this$props2 = this.props,
firstMatchNode = _this$props2.firstMatchNode,
keygen = _this$props2.keygen,
filterText = _this$props2.filterText;
if (!filterText || !firstMatchNode) {
this.setState({
path: []
});
return;
}
var key = getKey(firstMatchNode, keygen);
var current = this.datum.getPath(key);
if (!current) return;
this.setState(immer(function (draft) {
draft.path = [].concat(current.path, [key]);
}));
};
_proto.updatePathByValue = function updatePathByValue(force) {
var _this$props3 = this.props,
mode = _this$props3.mode,
value = _this$props3.value;
if (mode !== undefined) return;
if (value === this.lastValue && !force) return;
if (!value || !value.length) {
this.setState({
path: []
});
} else {
var v = value[value.length - 1];
var data = this.datum.getDataById(v);
if (data === null || this.datum.isUnMatch(data)) return;
try {
var _ref2 = this.datum.getPath(v) || {},
path = _ref2.path;
path = path || [];
this.handlePathChange(v, null, path);
} catch (e) {
console.error(e);
}
}
};
_proto.handleClickAway = function handleClickAway(e) {
var desc = isDescendent(e.target, this.selectId);
if (!desc) {
if (this.props.inputFocus) this.props.onBlur();
this.handleState(false);
}
};
_proto.handlePathChange = function handlePathChange(id, data, path, fromClick) {
var _this2 = this;
var _this$props4 = this.props,
childrenKey = _this$props4.childrenKey,
finalDismiss = _this$props4.finalDismiss,
loader = _this$props4.loader;
if (fromClick && data && childrenKey) {
var leaf = !data[childrenKey] || data[childrenKey].length === 0;
if (loader && typeof loader === 'function' && data[childrenKey] === undefined) {
leaf = false;
}
if (finalDismiss && leaf) this.handleState(false);
}
if (this.pathChangeTimer) {
clearTimeout(this.pathChangeTimer);
this.pathChangeTimer = null;
}
this.pathChangeTimer = setTimeout(function () {
_this2.setState({
path: [].concat(path, [id])
});
}, 50);
};
_proto.handleFocus = function handleFocus(e) {
if (!this.shouldFocus(e.target)) return;
this.props.onFocus(e);
};
_proto.handleClear = function handleClear() {
var _this3 = this;
var mode = this.props.mode;
this.setState({
path: []
});
if (mode !== undefined) this.datum.setValue([]);
this.handleChange([]); // force close
setTimeout(function () {
return _this3.handleState(false);
}, 10);
};
_proto.handleRemove = function handleRemove(node) {
var onChange = this.props.onChange;
this.datum.set(this.datum.getKey(node), 0);
if (onChange) onChange(this.datum.getValue(), node);
};
_proto.handleState = function handleState(focus, e) {
if (this.props.disabled === true) return;
if (focus === this.focus) return; // click close icon
if (focus && e && e.target.classList.contains(cascaderClass('close'))) return; // if remove node, return
if (e && getParent(e.target, "." + cascaderClass('remove-container'))) return;
var _this$props5 = this.props,
_this$props5$height = _this$props5.height,
height = _this$props5$height === void 0 ? DefaultProps.height : _this$props5$height,
onCollapse = _this$props5.onCollapse;
var position = this.props.position;
if (!position) {
var windowHeight = docSize.height;
var bottom = height + this.element.getBoundingClientRect().bottom;
if (bottom > windowHeight) position = 'drop-up';
}
if (onCollapse) onCollapse(focus);
this.setState({
focus: focus,
position: position || 'drop-down'
});
if (focus) {
this.renderPending = false;
}
};
_proto.handleKeyDown = function handleKeyDown(e) {
if (e.keyCode === 13) {
e.preventDefault();
this.handleState(!this.focus);
} // fot close the list
if (e.keyCode === 9) {
this.props.onBlur(); // e.preventDefault()
if (this.focus) {
this.handleState(false);
}
}
};
_proto.handleChange = function handleChange() {
var _this$props6 = this.props,
onChange = _this$props6.onChange,
onFilter = _this$props6.onFilter,
mode = _this$props6.mode,
emptyAfterSelect = _this$props6.emptyAfterSelect,
filterText = _this$props6.filterText;
if (this.input) {
this.input.reset();
this.input.focus();
}
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var value = args[0];
this.lastValue = value;
if (onChange) {
onChange.apply(void 0, args);
}
if (onFilter && filterText) {
if (!(mode !== undefined && !emptyAfterSelect)) {
onFilter('');
}
}
};
_proto.renderList = function renderList() {
var _this4 = this;
var _this$props7 = this.props,
data = _this$props7.data,
keygen = _this$props7.keygen,
renderItem = _this$props7.renderItem,
mode = _this$props7.mode,
loader = _this$props7.loader,
expandTrigger = _this$props7.expandTrigger,
childrenKey = _this$props7.childrenKey,
height = _this$props7.height;
var path = this.state.path;
var props = {
datum: this.datum,
renderItem: renderItem,
keygen: keygen,
loader: loader,
onPathChange: this.handlePathChange,
onChange: this.handleChange,
multiple: mode !== undefined,
expandTrigger: expandTrigger,
childrenKey: childrenKey,
shouldFinal: this.shouldFinal
};
var tempData = data;
var list = [React.createElement(CascaderList, _extends({}, props, {
text: undefined,
key: "root",
data: tempData,
id: path[0],
parentId: "",
path: []
}))];
var childs = path.map(function (p, i) {
tempData = tempData && tempData instanceof Array && tempData.find(function (d) {
var nid = _this4.datum.getKey(d, path[i - 1]);
return nid === p;
});
if (tempData && tempData[childrenKey] && tempData[childrenKey].length > 0) {
tempData = tempData[childrenKey];
return React.createElement(CascaderList, _extends({}, props, {
key: p,
text: undefined,
data: tempData,
id: path[i + 1],
parentId: path[i],
path: path.slice(0, i + 1)
}));
}
return null;
});
if (childs) {
list = list.concat(childs);
}
var listStyle = data && data.length === 0 ? {
height: 'auto',
width: '100%'
} : {
height: height
};
return React.createElement("div", {
ref: this.bindRef,
style: listStyle
}, list);
};
_proto.renderAbsoluteList = function renderAbsoluteList() {
var _this$props8 = this.props,
absolute = _this$props8.absolute,
zIndex = _this$props8.zIndex,
renderOptionList = _this$props8.renderOptionList,
loading = _this$props8.loading;
var position = this.state.position;
var focus = this.focus;
var className = classnames(selectClass('options'), cascaderClass('options'));
var rootClass = classnames(cascaderClass(focus && 'focus', isRTL() && 'rtl'), selectClass(this.state.position));
if (!focus && !this.isRendered) return null;
if (!this.element) {
this.shouldUpdateAfterRef = true;
return null;
}
this.isRendered = true;
var list = this.renderList();
return React.createElement(OptionList, {
autoAdapt: true,
rootClass: rootClass,
className: className,
position: position,
absolute: absolute,
focus: focus,
parentElement: this.element,
"data-id": this.selectId,
zIndex: zIndex
}, renderOptionList ? renderOptionList(list, {
loading: !!loading
}) : list);
};
_proto.renderFilterList = function renderFilterList() {
var _this$props9 = this.props,
absolute = _this$props9.absolute,
onFilter = _this$props9.onFilter,
wideMatch = _this$props9.wideMatch,
filterText = _this$props9.filterText,
zIndex = _this$props9.zIndex,
data = _this$props9.data,
childrenKey = _this$props9.childrenKey,
renderItem = _this$props9.renderItem,
expandTrigger = _this$props9.expandTrigger,
filterDataChange = _this$props9.filterDataChange,
height = _this$props9.height,
loading = _this$props9.loading,
renderOptionList = _this$props9.renderOptionList;
var position = this.state.position;
var focus = this.focus;
var className = classnames(cascaderClass(focus && 'focus', isRTL() && 'rtl'), selectClass(this.state.position));
return React.createElement(FilterList, {
shouldFinal: this.shouldFinal,
fixed: "min",
rootClass: className,
position: position,
absolute: absolute,
focus: focus,
parentElement: this.element,
"data-id": this.selectId,
zIndex: zIndex,
data: data,
childrenKey: childrenKey,
renderItem: renderItem,
expandTrigger: expandTrigger,
filterDataChange: filterDataChange,
datum: this.datum,
onChange: this.handleChange,
onPathChange: this.handlePathChange,
wideMatch: wideMatch,
onFilter: onFilter,
filterText: filterText,
height: height,
loading: loading,
renderOptionList: renderOptionList
});
};
_proto.renderPanel = function renderPanel() {
var _this$props10 = this.props,
filterText = _this$props10.filterText,
data = _this$props10.data,
mode = _this$props10.mode,
loading = _this$props10.loading;
if (loading) return this.renderFilterList();
if (!filterText || filterText && mode !== undefined || data && data.length === 0) return this.renderAbsoluteList();
return this.renderFilterList();
};
_proto.render = function render() {
var _this5 = this;
var _this$props11 = this.props,
placeholder = _this$props11.placeholder,
disabled = _this$props11.disabled,
size = _this$props11.size,
other = _objectWithoutPropertiesLoose(_this$props11, ["placeholder", "disabled", "size"]);
var focus = this.focus;
var className = classnames(cascaderClass('_', size, focus && 'focus', other.mode !== undefined && 'multiple', disabled === true && 'disabled', isRTL() && 'rtl'), selectClass(this.state.position, focus && 'focus'));
return React.createElement("div", {
// eslint-disable-next-line
tabIndex: disabled === true ? -1 : 0,
className: className,
onFocus: this.handleFocus,
onClick: this.handleClick,
"data-id": this.selectId,
onKeyDown: this.handleKeyDown,
ref: function ref(el) {
_this5.element = el;
if (el && _this5.shouldUpdateAfterRef) {
_this5.shouldUpdateAfterRef = false;
_this5.forceUpdate();
}
}
}, React.createElement(Result, _extends({}, other, {
focus: focus,
multiple: other.mode !== undefined,
datum: this.datum,
placeholder: placeholder,
onClear: this.handleClear,
onPathChange: this.handlePathChange,
bindInput: this.bindInput,
handleRemove: this.handleRemove,
selectId: this.selectId,
showList: this.handleClick,
size: size
})), this.renderPanel());
};
_createClass(Cascader, [{
key: "focus",
get: function get() {
if ('open' in this.props) {
return !!this.props.open;
}
return this.state.focus;
}
}, {
key: "shouldFinal",
get: function get() {
var _this$props12 = this.props,
expandTrigger = _this$props12.expandTrigger,
final = _this$props12.final;
return expandTrigger === 'hover-only' || !!final;
}
}]);
return Cascader;
}(PureComponent);
_defineProperty(Cascader, "defaultProps", DefaultProps);
Cascader.defaultProps = {
clearable: true,
expandTrigger: 'click',
height: 300,
data: [],
childrenKey: 'children',
showArrow: true
};
export default Cascader;