UNPKG

@vnmfify/core

Version:

```shell npm i @vnmfify/core -S ```

283 lines (242 loc) 11.8 kB
import _omit from "lodash/omit"; import _map from "lodash/map"; import _findIndex from "lodash/findIndex"; import _clamp from "lodash/clamp"; import _size from "lodash/size"; var _excluded = ["value", "className", "readonly", "children", "onChange", "onTouchStart", "onTouchMove", "onTouchEnd", "onTouchCancel"]; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } import { View } from "@vnxjs/components"; import classNames from "classnames"; import * as React from "react"; import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { prefixClassname } from "../styles"; import { getComputedStyle } from "../utils/dom/computed-style"; import { preventDefault } from "../utils/dom/event"; import { addUnitPx } from "../utils/format/unit"; import { fulfillPromise } from "../utils/promisify"; import { useRendered, useRenderedRef, useToRef } from "../utils/state"; import { useTouch } from "../utils/touch"; import PickerOption from "./picker-option"; import { getPickerOptionKey } from "./picker.shared"; var MOMENTUM_LIMIT_TIME = 300; var MOMENTUM_LIMIT_DISTANCE = 15; function getElementTranslateY(_x) { return _getElementTranslateY.apply(this, arguments); } function _getElementTranslateY() { _getElementTranslateY = _asyncToGenerator(function* (element) { var style = yield getComputedStyle(element, ["transform", "webkitTransform"]); var transform = style.transform || style.webkitTransform; var translateY = transform.slice(7, transform.length - 1).split(", ")[5]; return Number(translateY); }); return _getElementTranslateY.apply(this, arguments); } var PickerColumn = /*#__PURE__*/forwardRef((props, ref) => { var { value, className, readonly, children: childrenProp = [], onChange, onTouchStart: _onTouchStart, onTouchMove: _onTouchMove, onTouchEnd: _onTouchEnd, onTouchCancel: _onTouchCancel } = props, restProps = _objectWithoutProperties(props, _excluded); var childrenRef = useToRef(childrenProp); var wrapperRef = useRef(); var movingRef = useRef(); var startOffsetRef = useRef(0); var momentumOffsetRef = useRef(0); var touchStartTimeRef = useRef(0); var transitionEndTriggerRef = useRef(); var touch = useTouch(); var itemHeight = 44; var activeIndexRef = useRef(-1); var [activeOffset, setActiveOffset] = useState(0); var activeOffsetRef = useToRef(activeOffset); var [duration, setDuration] = useState("zero"); var baseOffset = useMemo(() => itemHeight * (6 - 1) / 2, []); var countRef = useRenderedRef(() => _size(childrenProp)); var adjustIndex = useCallback(index => { index = _clamp(index, 0, countRef.current); for (var i = index; i < countRef.current; i++) { if (!childrenRef.current[i].disabled) return i; } for (var _i = index - 1; _i >= 0; _i--) { if (!childrenRef.current[_i].disabled) return _i; } return index; }, [countRef, childrenRef]); var setIndex = useCallback((index, emitChange) => { index = adjustIndex(index) || 0; var offset = -index * itemHeight; var trigger = () => { if (index !== activeIndexRef.current) { activeIndexRef.current = index; var option = childrenRef.current[index]; onChange === null || onChange === void 0 ? void 0 : onChange(option, emitChange); } }; if (movingRef.current && offset !== activeOffsetRef.current) { transitionEndTriggerRef.current = trigger; } else { trigger(); } setActiveOffset(offset); }, [adjustIndex, activeOffsetRef, childrenRef, onChange]); var getIndexByValue = useCallback(aValue => { var index = _findIndex(childrenRef.current, _ref => { var { value: iValue } = _ref; return iValue === aValue; }); return index === -1 ? 0 : index; }, [childrenRef]); var childrenChanged = useRendered(() => JSON.stringify(_map(childrenProp, _ref2 => { var { value } = _ref2; return value; }))); useEffect(() => { var valueIndex = getIndexByValue(value); if (valueIndex !== activeIndexRef.current) { setIndex(valueIndex); } }, [value, childrenChanged]); var getIndexByOffset = useCallback(offset => _clamp(Math.round(-offset / itemHeight), 0, countRef.current - 1), [countRef]); var momentum = useCallback((distance, duration) => { var speed = Math.abs(distance / duration); distance = activeOffset + speed / 0.003 * (distance < 0 ? -1 : 1); var index = getIndexByOffset(distance); setDuration("momentum"); setIndex(index, true); }, [activeOffset, getIndexByOffset, setIndex]); var stopMomentum = useCallback(() => { movingRef.current = false; setDuration("zero"); if (transitionEndTriggerRef.current) { var _transitionEndTrigger; (_transitionEndTrigger = transitionEndTriggerRef.current) === null || _transitionEndTrigger === void 0 ? void 0 : _transitionEndTrigger.call(transitionEndTriggerRef); transitionEndTriggerRef.current = undefined; } }, []); var onItemClick = useCallback(index => { if (movingRef.current || readonly) { return; } transitionEndTriggerRef.current = undefined; setDuration("switch"); setIndex(index, true); }, [readonly, setIndex]); var handleTouchStart = /*#__PURE__*/function () { var _ref3 = _asyncToGenerator(function* (event) { if (readonly) { return; } touch.start(event); if (movingRef.current) { var translateY = yield getElementTranslateY(wrapperRef.current); var offset = Math.min(0, translateY - baseOffset); setActiveOffset(offset); startOffsetRef.current = offset; } else { startOffsetRef.current = activeOffset; } touchStartTimeRef.current = Date.now(); momentumOffsetRef.current = startOffsetRef.current; setDuration("zero"); }); return function handleTouchStart(_x2) { return _ref3.apply(this, arguments); }; }(); var handleTouchMove = event => { if (readonly) { return; } touch.move(event); if (touch.isVertical()) { movingRef.current = true; preventDefault(event, true); } var now = Date.now(); if (now - touchStartTimeRef.current > MOMENTUM_LIMIT_TIME) { touchStartTimeRef.current = now; momentumOffsetRef.current = activeOffset; } setActiveOffset(_clamp(startOffsetRef.current + touch.deltaY, -(countRef.current * itemHeight), itemHeight)); }; var handleTouchEnd = () => { if (readonly) { return; } var distance = activeOffset - momentumOffsetRef.current; var duration = Date.now() - touchStartTimeRef.current; var allowMomentum = duration < MOMENTUM_LIMIT_TIME && Math.abs(distance) > MOMENTUM_LIMIT_DISTANCE; if (allowMomentum) { momentum(distance, duration); return; } var index = getIndexByOffset(activeOffset); setDuration("switch"); setIndex(index, true); setTimeout(() => { movingRef.current = false; }, 0); }; var wrapperStyle = useMemo(() => ({ transform: "translate3d(0, ".concat(addUnitPx(activeOffset + baseOffset), ", 0)") }), [activeOffset, baseOffset]); useImperativeHandle(ref, () => ({ stopMomentum }), [stopMomentum]); return /*#__PURE__*/React.createElement(View, _objectSpread({ className: classNames(prefixClassname("picker-column"), className), catchMove: true, onTouchStart: event => { fulfillPromise(handleTouchStart(event)); _onTouchStart === null || _onTouchStart === void 0 ? void 0 : _onTouchStart(event); }, onTouchMove: event => { handleTouchMove(event); _onTouchMove === null || _onTouchMove === void 0 ? void 0 : _onTouchMove(event); }, onTouchEnd: event => { handleTouchEnd(); _onTouchEnd === null || _onTouchEnd === void 0 ? void 0 : _onTouchEnd(event); }, onTouchCancel: event => { handleTouchEnd(); _onTouchCancel === null || _onTouchCancel === void 0 ? void 0 : _onTouchCancel(event); } }, _omit(restProps, "label")), /*#__PURE__*/React.createElement(View, { ref: wrapperRef, style: wrapperStyle, className: classNames(prefixClassname("picker-column__wrapper"), { [prefixClassname("picker-column__wrapper--zero")]: duration === "zero", [prefixClassname("picker-column__wrapper--momentum")]: duration === "momentum", [prefixClassname("picker-column__wrapper--switch")]: duration === "switch" }), onTransitionEnd: stopMomentum }, _map(childrenProp, (option, index) => { var _getPickerOptionKey; return /*#__PURE__*/React.createElement(PickerOption, _objectSpread(_objectSpread({ key: (_getPickerOptionKey = getPickerOptionKey(option)) !== null && _getPickerOptionKey !== void 0 ? _getPickerOptionKey : index }, option), {}, { onClick: () => onItemClick(index) })); }))); }); export default PickerColumn; //# sourceMappingURL=picker-column.js.map