fastlion-amis
Version:
一种MIS页面生成工具
283 lines (282 loc) • 11.7 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
/**
* @file Picker
* @description 移动端列滚动选择器
*/
var react_1 = tslib_1.__importStar(require("react"));
var isObject_1 = (0, tslib_1.__importDefault)(require("lodash/isObject"));
var cloneDeep_1 = (0, tslib_1.__importDefault)(require("lodash/cloneDeep"));
var uncontrollable_1 = require("uncontrollable");
var hooks_1 = require("../hooks");
var helper_1 = require("../utils/helper");
var theme_1 = require("../theme");
var use_touch_1 = (0, tslib_1.__importDefault)(require("../hooks/use-touch"));
var DEFAULT_DURATION = 200;
var MOMENTUM_LIMIT_TIME = 300;
var MOMENTUM_LIMIT_DISTANCE = 15;
function getElementTranslateY(element) {
if (!element) {
return 0;
}
var style = window.getComputedStyle(element);
var transform = style.transform || style.webkitTransform;
// 格式如:matrix( scaleX(), skewY(), skewX(), scaleY(), translateX(), translateY() );
var translateY = transform.slice(7, transform.length - 1).split(', ')[5];
return Number(translateY);
}
function isOptionDisabled(option) {
return (0, isObject_1.default)(option) && option.disabled;
}
var PickerColumn = (0, react_1.forwardRef)(function (props, ref) {
var _a = props.visibleItemCount, visibleItemCount = _a === void 0 ? 5 : _a, _b = props.itemHeight, itemHeight = _b === void 0 ? 48 : _b, value = props.value, type = props.type, _c = props.valueField, valueField = _c === void 0 ? 'value' : _c, _d = props.swipeDuration, swipeDuration = _d === void 0 ? 1000 : _d, _e = props.labelField, labelField = _e === void 0 ? 'text' : _e, _f = props.options, options = _f === void 0 ? [] : _f, cx = props.classnames;
var root = (0, react_1.useRef)(null);
var menuItemRef = (0, react_1.useRef)(null);
var wrapper = (0, react_1.useRef)(null);
var refType = (0, react_1.useRef)(null);
var moving = (0, react_1.useRef)(false);
var startOffset = (0, react_1.useRef)(0);
var transitionEndTrigger = (0, react_1.useRef)(null);
var touchStartTime = (0, react_1.useRef)(0);
var momentumOffset = (0, react_1.useRef)(0);
var touch = (0, use_touch_1.default)();
var count = options.length;
var getOptionText = function (option) {
if ((0, isObject_1.default)(option) && labelField in option) {
//@ts-ignore
return option[labelField];
}
return option;
};
var getOptionValue = function (option) {
if ((0, isObject_1.default)(option) && valueField in option) {
//@ts-ignore
return option[valueField];
}
return option;
};
var defaultIndex = options.findIndex(function (item) { return getOptionValue(item) === value; });
var baseOffset = (0, react_1.useMemo)(function () {
// 默认转入第一个选项的位置
return (itemHeight * (+visibleItemCount - 1)) / 2;
}, [itemHeight, visibleItemCount]);
var adjustIndex = function (index) {
index = (0, helper_1.range)(index, 0, count);
if (!options) {
return;
}
for (var i = index; i < count; i += 1) {
if (!isOptionDisabled(options[i]))
return i;
}
for (var i = index - 1; i >= 0; i -= 1) {
if (!isOptionDisabled(options[i]))
return i;
}
return null;
};
var _g = (0, hooks_1.useSetState)({
index: adjustIndex(defaultIndex) || 0,
offset: 0,
duration: 0,
options: (0, cloneDeep_1.default)(options)
}), state = _g[0], updateState = _g[1];
/**
*
* @param index 索引
* @param emitChange 是否派发变动消息
* @param confirm 是否为确认类型,为真时触发value改变
*/
var setIndex = function (index, emitChange, confirm) {
index = adjustIndex(index) || 0;
var offset = -index * itemHeight;
var trigger = function () {
updateState({ index: index });
if (emitChange && props.onChange) {
requestAnimationFrame(function () {
var _a;
(_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, getOptionValue(options[index]), index, confirm);
});
}
};
// trigger the change event after transitionend when moving
if (moving.current && offset !== state.offset) {
//@ts-ignore
transitionEndTrigger.current = trigger;
}
else {
trigger();
}
updateState({ offset: offset });
};
var setOptions = function (options) {
if (JSON.stringify(options) !== JSON.stringify(state.options)) {
updateState({ options: options });
var index = options.findIndex(function (item) { return getOptionValue(item) === value; }) || 0;
setIndex(index, true, true);
}
};
var onClickItem = function (index) {
if (moving.current || props.readonly) {
return;
}
transitionEndTrigger.current = null;
updateState({ duration: DEFAULT_DURATION });
setIndex(index, true, true);
};
var getIndexByOffset = function (offset) {
return (0, helper_1.range)(Math.round(-offset / itemHeight), 0, count - 1);
};
var momentum = function (distance, duration) {
var speed = Math.abs(distance / duration);
distance = state.offset + (speed / 0.003) * (distance < 0 ? -1 : 1);
var index = getIndexByOffset(distance);
updateState({ duration: +swipeDuration });
setIndex(index, true);
};
var stopMomentum = function () {
moving.current = false;
updateState({ duration: 0 });
if (transitionEndTrigger.current) {
//@ts-ignore
transitionEndTrigger.current();
transitionEndTrigger.current = null;
}
};
var onTouchStart = function (event) {
if (props.readonly) {
return;
}
touch.start(event);
var offset = state.offset;
if (moving.current) {
var translateY = getElementTranslateY(wrapper.current);
offset = Math.min(0, translateY - baseOffset);
startOffset.current = offset;
}
else {
startOffset.current = offset;
}
updateState({ duration: 0, offset: offset });
touchStartTime.current = Date.now();
momentumOffset.current = startOffset.current;
transitionEndTrigger.current = null;
};
var onTouchMove = function (event) {
if (props.readonly) {
return;
}
touch.move(event);
if (touch.isVertical()) {
moving.current = true;
}
var offset = (0, helper_1.range)(startOffset.current + touch.deltaY, -(count * itemHeight), itemHeight);
updateState({
offset: offset
});
var now = Date.now();
if (now - touchStartTime.current > MOMENTUM_LIMIT_TIME) {
touchStartTime.current = now;
momentumOffset.current = offset;
}
};
var onTouchEnd = function () {
if (props.readonly) {
return;
}
var distance = state.offset - momentumOffset.current;
var duration = Date.now() - touchStartTime.current;
var allowMomentum = duration < MOMENTUM_LIMIT_TIME &&
Math.abs(distance) > MOMENTUM_LIMIT_DISTANCE;
if (allowMomentum) {
momentum(distance, duration);
return;
}
var index = getIndexByOffset(state.offset);
updateState({ duration: DEFAULT_DURATION });
setIndex(index, true);
// compatible with desktop scenario
// use setTimeout to skip the click event triggered after touchstart
setTimeout(function () {
moving.current = false;
}, 0);
};
var renderOptions = function () {
var style = {
height: itemHeight + "px",
lineHeight: itemHeight + "px"
};
return state.options.map(function (option, index) {
var text = getOptionText(option);
var disabled = isOptionDisabled(option);
var data = {
role: 'button',
key: index,
style: style,
tabIndex: disabled ? -1 : 0,
className: props.classnames("PickerColumns-columnItem", {
'is-disabled': disabled,
'is-selected': index === state.index
}),
onClick: function () {
onClickItem(index);
}
};
var childData = {
className: 'text-ellipsis',
children: text
};
return (react_1.default.createElement("li", (0, tslib_1.__assign)({}, data, { ref: menuItemRef }), props.optionRender ? (props.optionRender(option)) : (react_1.default.createElement("div", (0, tslib_1.__assign)({}, childData)))));
});
};
var setValue = function (value) {
var options = state.options;
for (var i = 0; i < options.length; i += 1) {
if (options[i] === value) {
return setIndex(i);
}
}
return null;
};
var getValue = (0, react_1.useCallback)(function () { return state.options[state.index]; }, [state.index, state.options]);
(0, react_1.useEffect)(function () {
setIndex(defaultIndex);
}, [defaultIndex]);
(0, hooks_1.useUpdateEffect)(function () {
setOptions((0, cloneDeep_1.default)(options));
}, [options]);
(0, react_1.useEffect)(function () {
if (refType.current && menuItemRef.current) {
var offset = menuItemRef.current;
var dom = offset.childNodes[0];
refType.current.style.left = dom.offsetLeft + dom.offsetWidth + 4 + 'px';
}
}, [refType]);
(0, react_1.useImperativeHandle)(ref, function () { return ({
state: state,
setIndex: setIndex,
getValue: getValue,
setValue: setValue,
setOptions: setOptions,
stopMomentum: stopMomentum
}); });
var wrapperStyle = {
transform: "translate3d(0, " + (state.offset + baseOffset) + "px, 0)",
transitionDuration: state.duration + "ms",
transitionProperty: state.duration ? 'all' : 'none'
};
return (react_1.default.createElement("div", { ref: root, className: props.classnames('PickerColumns', props.className), onTouchStart: onTouchStart, onTouchMove: onTouchMove, onTouchEnd: onTouchEnd, onTouchCancel: onTouchEnd },
react_1.default.createElement("ul", { ref: wrapper, style: wrapperStyle, className: props.classnames('PickerColumns-columnWrapper'), onTransitionEnd: stopMomentum }, renderOptions()),
type && react_1.default.createElement("div", { ref: refType, className: props.classnames('PickerColumns-type', props.className) }, type)));
});
PickerColumn.defaultProps = {
options: [],
visibleItemCount: 5,
swipeDuration: 1000,
itemHeight: 48
};
exports.default = (0, theme_1.themeable)((0, uncontrollable_1.uncontrollable)(PickerColumn, {
value: 'onChange'
}));
//# sourceMappingURL=./components/PickerColumn.js.map
;