@enact/moonstone
Version:
Large-screen/TV support library for Enact, containing a variety of UI components.
1,107 lines (1,088 loc) • 59.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = exports.constants = exports.ScrollableNative = exports.ScrollableBaseNative = void 0;
var _classnames = _interopRequireDefault(require("classnames"));
var _handle = _interopRequireWildcard(require("@enact/core/handle"));
var _keymap = require("@enact/core/keymap");
var _platform = require("@enact/core/platform");
var _Registry = _interopRequireDefault(require("@enact/core/internal/Registry"));
var _util = require("@enact/core/util");
var _Resizable = require("@enact/ui/Resizable");
var _resolution = _interopRequireDefault(require("@enact/ui/resolution"));
var _Touchable = _interopRequireDefault(require("@enact/ui/Touchable"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _clamp = _interopRequireDefault(require("ramda/src/clamp"));
var _react = require("react");
var _UiScrollAnimator = _interopRequireDefault(require("./UiScrollAnimator"));
var _UiScrollbar = _interopRequireDefault(require("./UiScrollbar"));
var _UiScrollableModule = _interopRequireDefault(require("./UiScrollable.module.css"));
var _jsxRuntime = require("react/jsx-runtime");
var _excluded = ["className", "containerRenderer", "noScrollByDrag", "rtl", "style"],
_excluded2 = ["childRenderer"];
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": 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 ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
var constants = exports.constants = {
epsilon: 1,
flickConfig: {
maxDuration: null
},
isPageDown: (0, _keymap.is)('pageDown'),
isPageUp: (0, _keymap.is)('pageUp'),
nop: function nop() {},
overscrollTypeNone: 0,
overscrollTypeHold: 1,
overscrollTypeOnce: 2,
overscrollTypeDone: 9,
overscrollVelocityFactor: 300,
scrollStopWaiting: 200,
scrollWheelPageMultiplierForMaxPixel: 0.2 // The ratio of the maximum distance scrolled by wheel to the size of the viewport.
},
epsilon = constants.epsilon,
flickConfig = constants.flickConfig,
nop = constants.nop,
overscrollTypeDone = constants.overscrollTypeDone,
overscrollTypeHold = constants.overscrollTypeHold,
overscrollTypeNone = constants.overscrollTypeNone,
overscrollTypeOnce = constants.overscrollTypeOnce,
overscrollVelocityFactor = constants.overscrollVelocityFactor,
scrollStopWaiting = constants.scrollStopWaiting,
scrollWheelPageMultiplierForMaxPixel = constants.scrollWheelPageMultiplierForMaxPixel;
var TouchableDiv = (0, _Touchable["default"])('div');
/**
* An unstyled native component that passes scrollable behavior information as its render prop's arguments.
*
* @class ScrollableBaseNative
* @memberof moonstone/UiScrollableNative
* @ui
* @private
*/
var ScrollableBaseNative = exports.ScrollableBaseNative = /*#__PURE__*/function (_Component) {
_inherits(ScrollableBaseNative, _Component);
var _super = _createSuper(ScrollableBaseNative);
function ScrollableBaseNative(props) {
var _this;
_classCallCheck(this, ScrollableBaseNative);
_this = _super.call(this, props);
_this.handleResizeWindow = function () {
// `handleSize` in `ui/resolution.ResolutionDecorator` should be executed first.
setTimeout(function () {
var handleResizeWindow = _this.props.handleResizeWindow;
if (handleResizeWindow) {
handleResizeWindow();
}
_this.childRefCurrent.containerRef.current.style.scrollBehavior = null;
_this.childRefCurrent.scrollToPosition(0, 0);
_this.childRefCurrent.containerRef.current.style.scrollBehavior = 'smooth';
_this.enqueueForceUpdate();
});
};
// constants
_this.pixelPerLine = 39;
_this.scrollWheelMultiplierForDeltaPixel = 1.5;
// The ratio of wheel 'delta' units to pixels scrolled.
// status
_this.deferScrollTo = true;
_this.isScrollAnimationTargetAccumulated = false;
_this.isUpdatedScrollThumb = false;
// overscroll
_this.lastInputType = null;
_this.overscrollEnabled = false;
_this.overscrollStatus = {
horizontal: {
before: {
type: overscrollTypeNone,
ratio: 0
},
after: {
type: overscrollTypeNone,
ratio: 0
}
},
vertical: {
before: {
type: overscrollTypeNone,
ratio: 0
},
after: {
type: overscrollTypeNone,
ratio: 0
}
}
};
// bounds info
_this.bounds = {
clientWidth: 0,
clientHeight: 0,
scrollWidth: 0,
scrollHeight: 0,
maxTop: 0,
maxLeft: 0
};
// wheel/drag/flick info
_this.wheelDirection = 0;
_this.isDragging = false;
_this.isTouching = false;
// scroll info
_this.scrolling = false;
_this.scrollLeft = 0;
_this.scrollTop = 0;
_this.scrollToInfo = null;
// component info
_this.childRefCurrent = null;
// scroll animator
_this.animator = new _UiScrollAnimator["default"]();
// event handler for browser native scroll
_this.getRtlX = function (x) {
return _this.props.rtl ? -x : x;
};
_this.onMouseDown = (0, _handle["default"])((0, _handle.forwardWithPrevent)('onMouseDown'), _this.stop).bindAs(_assertThisInitialized(_this), 'onMouseDown');
_this.onTouchStart = function () {
_this.isTouching = true;
};
_this.onDragStart = function (ev) {
if (!_this.isTouching) {
_this.stop();
_this.isDragging = true;
}
// these values are used also for touch inputs
_this.dragStartX = _this.scrollLeft + _this.getRtlX(ev.x);
_this.dragStartY = _this.scrollTop + ev.y;
};
_this.onDrag = function (ev) {
var _this$props = _this.props,
direction = _this$props.direction,
overscrollEffectOn = _this$props.overscrollEffectOn,
targetX = direction === 'vertical' ? 0 : _this.dragStartX - _this.getRtlX(ev.x),
targetY = direction === 'horizontal' ? 0 : _this.dragStartY - ev.y; // 'vertical' or 'both'
_this.lastInputType = 'drag';
if (!_this.isTouching) {
_this.start({
targetX: targetX,
targetY: targetY,
animate: false,
overscrollEffect: overscrollEffectOn.drag
});
} else if (_this.overscrollEnabled && overscrollEffectOn.drag) {
_this.checkAndApplyOverscrollEffectOnDrag(targetX, targetY, overscrollTypeHold);
}
};
_this.onDragEnd = function () {
_this.isDragging = false;
_this.lastInputType = 'drag';
if (_this.flickTarget) {
var overscrollEffectOn = _this.props.overscrollEffectOn,
_this$flickTarget = _this.flickTarget,
targetX = _this$flickTarget.targetX,
targetY = _this$flickTarget.targetY;
if (!_this.isTouching) {
_this.isScrollAnimationTargetAccumulated = false;
_this.start({
targetX: targetX,
targetY: targetY,
overscrollEffect: overscrollEffectOn.drag
});
} else if (_this.overscrollEnabled && overscrollEffectOn.drag) {
_this.checkAndApplyOverscrollEffectOnDrag(targetX, targetY, overscrollTypeOnce);
}
} else if (!_this.isTouching) {
_this.stop();
}
if (_this.overscrollEnabled) {
// not check this.props.overscrollEffectOn.drag for safety
_this.clearAllOverscrollEffects();
}
_this.isTouching = false;
_this.flickTarget = null;
};
_this.onFlick = function (ev) {
var direction = _this.props.direction;
if (!_this.isTouching) {
_this.flickTarget = _this.animator.simulate(_this.scrollLeft, _this.scrollTop, direction !== 'vertical' ? _this.getRtlX(-ev.velocityX) : 0, direction !== 'horizontal' ? -ev.velocityY : 0);
} else if (_this.overscrollEnabled && _this.props.overscrollEffectOn.drag) {
_this.flickTarget = {
targetX: _this.scrollLeft + _this.getRtlX(-ev.velocityX) * overscrollVelocityFactor,
// 'horizontal' or 'both'
targetY: _this.scrollTop + -ev.velocityY * overscrollVelocityFactor // 'vertical' or 'both'
};
}
if (_this.props.onFlick) {
(0, _handle.forward)('onFlick', ev, _this.props);
}
};
/*
* wheel event handler;
* - for horizontal scroll, supports wheel action on any children nodes since web engine cannot support this
* - for vertical scroll, supports wheel action on scrollbars only
*/
_this.onWheel = function (ev) {
if (_this.isDragging) {
ev.preventDefault();
ev.stopPropagation();
} else {
var overscrollEffectOn = _this.props.overscrollEffectOn,
overscrollEffectRequired = _this.overscrollEnabled && overscrollEffectOn.wheel,
bounds = _this.getScrollBounds(),
canScrollHorizontally = _this.canScrollHorizontally(bounds),
canScrollVertically = _this.canScrollVertically(bounds),
eventDeltaMode = ev.deltaMode,
eventDelta = -ev.wheelDeltaY || ev.deltaY;
var delta = 0,
needToHideThumb = false;
_this.lastInputType = 'wheel';
if (_this.props.noScrollByWheel) {
if (canScrollVertically) {
ev.preventDefault();
}
return;
}
if (_this.props.onWheel) {
(0, _handle.forward)('onWheel', ev, _this.props);
return;
}
_this.showThumb(bounds);
// FIXME This routine is a temporary support for horizontal wheel scroll.
// FIXME If web engine supports horizontal wheel, this routine should be refined or removed.
if (canScrollVertically) {
// This routine handles wheel events on scrollbars for vertical scroll.
if (eventDelta < 0 && _this.scrollTop > 0 || eventDelta > 0 && _this.scrollTop < bounds.maxTop) {
var _assertThisInitialize = _assertThisInitialized(_this),
horizontalScrollbarRef = _assertThisInitialize.horizontalScrollbarRef,
verticalScrollbarRef = _assertThisInitialize.verticalScrollbarRef;
// Not to check if ev.target is a descendant of a wrapped component which may have a lot of nodes in it.
if (horizontalScrollbarRef.current && horizontalScrollbarRef.current.getContainerRef().current.contains(ev.target) || verticalScrollbarRef.current && verticalScrollbarRef.current.getContainerRef().current.contains(ev.target)) {
delta = _this.calculateDistanceByWheel(eventDeltaMode, eventDelta, bounds.clientHeight * scrollWheelPageMultiplierForMaxPixel);
needToHideThumb = !delta;
ev.preventDefault();
} else if (overscrollEffectRequired) {
_this.checkAndApplyOverscrollEffect('vertical', eventDelta > 0 ? 'after' : 'before', overscrollTypeOnce);
}
ev.stopPropagation();
} else {
if (overscrollEffectRequired && (eventDelta < 0 && _this.scrollTop <= 0 || eventDelta > 0 && _this.scrollTop >= bounds.maxTop)) {
_this.applyOverscrollEffect('vertical', eventDelta > 0 ? 'after' : 'before', overscrollTypeOnce, 1);
}
needToHideThumb = true;
}
} else if (canScrollHorizontally) {
// this routine handles wheel events on any children for horizontal scroll.
if (eventDelta < 0 && _this.scrollLeft > 0 || eventDelta > 0 && _this.scrollLeft < bounds.maxLeft) {
delta = _this.calculateDistanceByWheel(eventDeltaMode, eventDelta, bounds.clientWidth * scrollWheelPageMultiplierForMaxPixel);
needToHideThumb = !delta;
ev.preventDefault();
ev.stopPropagation();
} else {
if (overscrollEffectRequired && (eventDelta < 0 && _this.scrollLeft <= 0 || eventDelta > 0 && _this.scrollLeft >= bounds.maxLeft)) {
_this.applyOverscrollEffect('horizontal', eventDelta > 0 ? 'after' : 'before', overscrollTypeOnce, 1);
}
needToHideThumb = true;
}
}
if (delta !== 0) {
var direction = Math.sign(delta);
// Not to accumulate scroll position if wheel direction is different from hold direction
if (direction !== _this.wheelDirection) {
_this.isScrollAnimationTargetAccumulated = false;
_this.wheelDirection = direction;
}
_this.scrollToAccumulatedTarget(delta, canScrollVertically, overscrollEffectOn.wheel);
}
if (needToHideThumb) {
_this.startHidingThumb();
}
}
};
_this.onScroll = function (ev) {
var _ev$target = ev.target,
scrollLeft = _ev$target.scrollLeft,
scrollTop = _ev$target.scrollTop;
var bounds = _this.getScrollBounds(),
canScrollHorizontally = _this.canScrollHorizontally(bounds);
if (!_this.scrolling) {
_this.scrollStartOnScroll();
}
if (_this.props.rtl && canScrollHorizontally) {
scrollLeft = _platform.platform.ios || _platform.platform.safari || _platform.platform.chrome >= 85 || _platform.platform.androidChrome >= 85 ? -scrollLeft : bounds.maxLeft - scrollLeft;
}
if (scrollLeft !== _this.scrollLeft) {
_this.setScrollLeft(scrollLeft);
}
if (scrollTop !== _this.scrollTop) {
_this.setScrollTop(scrollTop);
}
if (_this.childRefCurrent.didScroll) {
_this.childRefCurrent.didScroll(_this.scrollLeft, _this.scrollTop);
}
_this.forwardScrollEvent('onScroll');
_this.scrollStopJob.start();
};
_this.onKeyDown = function (ev) {
(0, _handle.forward)('onKeyDown', ev, _this.props);
};
_this.scrollToAccumulatedTarget = function (delta, vertical, overscrollEffect) {
if (!_this.isScrollAnimationTargetAccumulated) {
_this.accumulatedTargetX = _this.scrollLeft;
_this.accumulatedTargetY = _this.scrollTop;
_this.isScrollAnimationTargetAccumulated = true;
}
if (vertical) {
_this.accumulatedTargetY += delta;
} else {
_this.accumulatedTargetX += delta;
}
_this.start({
targetX: _this.accumulatedTargetX,
targetY: _this.accumulatedTargetY,
overscrollEffect: overscrollEffect
});
};
// overscroll effect
_this.getEdgeFromPosition = function (position, maxPosition) {
if (position <= 0) {
return 'before';
/* If a scroll size or a client size is not integer,
browser's max scroll position could be smaller than maxPos by 1 pixel.*/
} else if (position >= maxPosition - 1) {
return 'after';
} else {
return null;
}
};
_this.setOverscrollStatus = function (orientation, edge, type, ratio) {
var status = _this.overscrollStatus[orientation][edge];
status.type = type;
status.ratio = ratio;
};
_this.getOverscrollStatus = function (orientation, edge) {
return _this.overscrollStatus[orientation][edge];
};
_this.calculateOverscrollRatio = function (orientation, position) {
var bounds = _this.getScrollBounds(),
isVertical = orientation === 'vertical',
baseSize = isVertical ? bounds.clientHeight : bounds.clientWidth,
maxPos = bounds[isVertical ? 'maxTop' : 'maxLeft'];
var overDistance = 0;
if (position < 0) {
overDistance = -position;
} else if (position > maxPos) {
overDistance = position - maxPos;
} else {
return 0;
}
return Math.min(1, 2 * overDistance / baseSize);
};
_this.applyOverscrollEffect = function (orientation, edge, type, ratio) {
_this.props.applyOverscrollEffect(orientation, edge, type, ratio);
_this.setOverscrollStatus(orientation, edge, type === overscrollTypeOnce ? overscrollTypeDone : type, ratio);
};
_this.checkAndApplyOverscrollEffect = function (orientation, edge, type) {
var ratio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
var isVertical = orientation === 'vertical',
curPos = isVertical ? _this.scrollTop : _this.scrollLeft,
maxPos = _this.getScrollBounds()[isVertical ? 'maxTop' : 'maxLeft'];
/* If a scroll size or a client size is not integer,
browser's max scroll position could be smaller than maxPos by 1 pixel.*/
if (edge === 'before' && curPos <= 0 || edge === 'after' && curPos >= maxPos - 1) {
// Already on the edge
_this.applyOverscrollEffect(orientation, edge, type, ratio);
} else {
_this.setOverscrollStatus(orientation, edge, type, ratio);
}
};
_this.clearOverscrollEffect = function (orientation, edge) {
if (_this.getOverscrollStatus(orientation, edge).type !== overscrollTypeNone) {
if (_this.props.clearOverscrollEffect) {
_this.props.clearOverscrollEffect(orientation, edge);
} else {
_this.applyOverscrollEffect(orientation, edge, overscrollTypeNone, 0);
}
}
};
_this.clearAllOverscrollEffects = function () {
['horizontal', 'vertical'].forEach(function (orientation) {
['before', 'after'].forEach(function (edge) {
_this.clearOverscrollEffect(orientation, edge);
});
});
};
_this.applyOverscrollEffectOnDrag = function (orientation, edge, targetPosition, type) {
if (edge) {
var oppositeEdge = edge === 'before' ? 'after' : 'before',
ratio = _this.calculateOverscrollRatio(orientation, targetPosition);
_this.applyOverscrollEffect(orientation, edge, type, ratio);
_this.clearOverscrollEffect(orientation, oppositeEdge);
} else {
_this.clearOverscrollEffect(orientation, 'before');
_this.clearOverscrollEffect(orientation, 'after');
}
};
_this.checkAndApplyOverscrollEffectOnDrag = function (targetX, targetY, type) {
var bounds = _this.getScrollBounds();
if (_this.canScrollHorizontally(bounds)) {
_this.applyOverscrollEffectOnDrag('horizontal', _this.getEdgeFromPosition(targetX, bounds.maxLeft), targetX, type);
}
if (_this.canScrollVertically(bounds)) {
_this.applyOverscrollEffectOnDrag('vertical', _this.getEdgeFromPosition(targetY, bounds.maxTop), targetY, type);
}
};
_this.checkAndApplyOverscrollEffectOnScroll = function (orientation) {
['before', 'after'].forEach(function (edge) {
var _this$getOverscrollSt = _this.getOverscrollStatus(orientation, edge),
ratio = _this$getOverscrollSt.ratio,
type = _this$getOverscrollSt.type;
if (type === overscrollTypeOnce) {
_this.checkAndApplyOverscrollEffect(orientation, edge, type, ratio);
}
});
};
_this.checkAndApplyOverscrollEffectOnStart = function (orientation, edge, targetPosition) {
if (_this.isDragging) {
_this.applyOverscrollEffectOnDrag(orientation, edge, targetPosition, overscrollTypeHold);
} else if (edge) {
_this.checkAndApplyOverscrollEffect(orientation, edge, overscrollTypeOnce);
}
};
// call scroll callbacks and update scrollbars for native scroll
_this.scrollStartOnScroll = function () {
_this.scrolling = true;
_this.showThumb(_this.getScrollBounds());
_this.forwardScrollEvent('onScrollStart');
};
_this.scrollStopOnScroll = function () {
if (_this.props.scrollStopOnScroll) {
_this.props.scrollStopOnScroll();
}
if (_this.overscrollEnabled && !_this.isDragging) {
// not check this.props.overscrollEffectOn for safety
_this.clearAllOverscrollEffects();
}
_this.lastInputType = null;
_this.isScrollAnimationTargetAccumulated = false;
_this.scrolling = false;
_this.forwardScrollEvent('onScrollStop', _this.getReachedEdgeInfo());
_this.startHidingThumb();
};
_this.scrollStopJob = new _util.Job(_this.scrollStopOnScroll, scrollStopWaiting);
_this.getReachedEdgeInfo = function () {
var bounds = _this.getScrollBounds(),
reachedEdgeInfo = {
bottom: false,
left: false,
right: false,
top: false
};
if (_this.canScrollHorizontally(bounds)) {
var rtl = _this.props.rtl,
edge = _this.getEdgeFromPosition(_this.scrollLeft, bounds.maxLeft);
if (edge) {
// if edge is null, no need to check which edge is reached.
if (edge === 'before' && !rtl || edge === 'after' && rtl) {
reachedEdgeInfo.left = true;
} else {
reachedEdgeInfo.right = true;
}
}
}
if (_this.canScrollVertically(bounds)) {
var _edge = _this.getEdgeFromPosition(_this.scrollTop, bounds.maxTop);
if (_edge === 'before') {
reachedEdgeInfo.top = true;
} else if (_edge === 'after') {
reachedEdgeInfo.bottom = true;
}
}
return reachedEdgeInfo;
};
// scrollTo API
_this.getPositionForScrollTo = function (opt) {
var bounds = _this.getScrollBounds(),
canScrollHorizontally = _this.canScrollHorizontally(bounds),
canScrollVertically = _this.canScrollVertically(bounds);
var itemPos,
left = null,
top = null;
if (opt instanceof Object) {
if (opt.position instanceof Object) {
if (canScrollHorizontally) {
// We need '!=' to check if opt.position.x is null or undefined
left = opt.position.x != null ? opt.position.x : _this.scrollLeft;
} else {
left = 0;
}
if (canScrollVertically) {
// We need '!=' to check if opt.position.y is null or undefined
top = opt.position.y != null ? opt.position.y : _this.scrollTop;
} else {
top = 0;
}
} else if (typeof opt.align === 'string') {
if (canScrollHorizontally) {
if (opt.align.includes('left')) {
left = 0;
} else if (opt.align.includes('right')) {
left = bounds.maxLeft;
}
}
if (canScrollVertically) {
if (opt.align.includes('top')) {
top = 0;
} else if (opt.align.includes('bottom')) {
top = bounds.maxTop;
}
}
} else {
if (typeof opt.index === 'number' && typeof _this.childRefCurrent.getItemPosition === 'function') {
itemPos = _this.childRefCurrent.getItemPosition(opt.index, opt.stickTo);
} else if (opt.node instanceof Object) {
if (opt.node.nodeType === 1 && typeof _this.childRefCurrent.getNodePosition === 'function') {
itemPos = _this.childRefCurrent.getNodePosition(opt.node);
}
}
if (itemPos) {
if (canScrollHorizontally) {
left = itemPos.left;
}
if (canScrollVertically) {
top = itemPos.top;
}
}
}
}
return {
left: left,
top: top
};
};
_this.scrollTo = function (opt) {
if (!_this.deferScrollTo) {
var _this$getPositionForS = _this.getPositionForScrollTo(opt),
left = _this$getPositionForS.left,
top = _this$getPositionForS.top;
if (_this.props.scrollTo) {
_this.props.scrollTo(opt);
}
_this.scrollToInfo = null;
_this.start({
targetX: left !== null ? left : _this.scrollLeft,
targetY: top !== null ? top : _this.scrollTop,
animate: opt.animate
});
} else {
_this.scrollToInfo = opt;
}
};
_this.canScrollHorizontally = function (bounds) {
var direction = _this.props.direction;
return (direction === 'horizontal' || direction === 'both') && bounds.scrollWidth > bounds.clientWidth && !isNaN(bounds.scrollWidth);
};
_this.canScrollVertically = function (bounds) {
var direction = _this.props.direction;
return (direction === 'vertical' || direction === 'both') && bounds.scrollHeight > bounds.clientHeight && !isNaN(bounds.scrollHeight);
};
_this.startHidingThumb = function () {
if (_this.state.isHorizontalScrollbarVisible && _this.horizontalScrollbarRef.current) {
_this.horizontalScrollbarRef.current.startHidingThumb();
}
if (_this.state.isVerticalScrollbarVisible && _this.verticalScrollbarRef.current) {
_this.verticalScrollbarRef.current.startHidingThumb();
}
};
_this.updateScrollbars = function () {
var _this$props2 = _this.props,
horizontalScrollbar = _this$props2.horizontalScrollbar,
verticalScrollbar = _this$props2.verticalScrollbar,
_this$state = _this.state,
isHorizontalScrollbarVisible = _this$state.isHorizontalScrollbarVisible,
isVerticalScrollbarVisible = _this$state.isVerticalScrollbarVisible,
bounds = _this.getScrollBounds(),
canScrollHorizontally = _this.canScrollHorizontally(bounds),
canScrollVertically = _this.canScrollVertically(bounds),
curHorizontalScrollbarVisible = horizontalScrollbar === 'auto' ? canScrollHorizontally : horizontalScrollbar === 'visible',
curVerticalScrollbarVisible = verticalScrollbar === 'auto' ? canScrollVertically : verticalScrollbar === 'visible';
// determine if we should hide or show any scrollbars
var isVisibilityChanged = isHorizontalScrollbarVisible !== curHorizontalScrollbarVisible || isVerticalScrollbarVisible !== curVerticalScrollbarVisible;
if (isVisibilityChanged) {
// one or both scrollbars have changed visibility
_this.setState({
isHorizontalScrollbarVisible: curHorizontalScrollbarVisible,
isVerticalScrollbarVisible: curVerticalScrollbarVisible
});
} else {
_this.deferScrollTo = false;
_this.isUpdatedScrollThumb = _this.updateScrollThumbSize();
}
};
_this.updateScrollThumbSize = function () {
var _this$props3 = _this.props,
horizontalScrollbar = _this$props3.horizontalScrollbar,
verticalScrollbar = _this$props3.verticalScrollbar,
bounds = _this.getScrollBounds(),
canScrollHorizontally = _this.canScrollHorizontally(bounds),
canScrollVertically = _this.canScrollVertically(bounds),
curHorizontalScrollbarVisible = horizontalScrollbar === 'auto' ? canScrollHorizontally : horizontalScrollbar === 'visible',
curVerticalScrollbarVisible = verticalScrollbar === 'auto' ? canScrollVertically : verticalScrollbar === 'visible';
if (curHorizontalScrollbarVisible || curVerticalScrollbarVisible) {
// no visibility change but need to notify whichever scrollbars are visible of the
// updated bounds and scroll position
var updatedBounds = _objectSpread(_objectSpread({}, bounds), {}, {
scrollLeft: _this.scrollLeft,
scrollTop: _this.scrollTop
});
if (curHorizontalScrollbarVisible && _this.horizontalScrollbarRef.current) {
_this.horizontalScrollbarRef.current.update(updatedBounds);
}
if (curVerticalScrollbarVisible && _this.verticalScrollbarRef.current) {
_this.verticalScrollbarRef.current.update(updatedBounds);
}
return true;
}
return false;
};
// render
_this.initChildRef = function (ref) {
if (ref) {
_this.childRefCurrent = ref.current || ref;
}
};
_this.state = {
remeasure: false,
isHorizontalScrollbarVisible: props.horizontalScrollbar === 'visible',
isVerticalScrollbarVisible: props.verticalScrollbar === 'visible'
};
_this.containerRef = /*#__PURE__*/(0, _react.createRef)();
_this.horizontalScrollbarRef = /*#__PURE__*/(0, _react.createRef)();
_this.verticalScrollbarRef = /*#__PURE__*/(0, _react.createRef)();
_this.horizontalScrollbarProps = {
ref: _this.horizontalScrollbarRef,
vertical: false,
clientSize: props.clientSize
};
_this.verticalScrollbarProps = {
ref: _this.verticalScrollbarRef,
vertical: true,
clientSize: props.clientSize
};
_this.overscrollEnabled = !!props.applyOverscrollEffect;
// Enable the early bail out of repeated scrolling to the same position
_this.animationInfo = null;
_this.resizeRegistry = _Registry["default"].create(_this.handleResize.bind(_assertThisInitialized(_this)));
props.cbScrollTo(_this.scrollTo);
return _this;
}
_createClass(ScrollableBaseNative, [{
key: "componentDidMount",
value: function componentDidMount() {
this.resizeRegistry.parent = this.context;
this.addEventListeners();
this.updateScrollbars();
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
var _this$state2 = this.state,
isHorizontalScrollbarVisible = _this$state2.isHorizontalScrollbarVisible,
isVerticalScrollbarVisible = _this$state2.isVerticalScrollbarVisible,
hasDataSizeChanged = this.childRefCurrent.hasDataSizeChanged;
// Need to sync calculated client size if it is different from the real size
if (this.childRefCurrent.syncClientSize) {
// If we actually synced, we need to reset scroll position.
if (this.childRefCurrent.syncClientSize()) {
this.setScrollLeft(0);
this.setScrollTop(0);
}
}
this.addEventListeners();
if (hasDataSizeChanged === false && (isHorizontalScrollbarVisible && !prevState.isHorizontalScrollbarVisible || isVerticalScrollbarVisible && !prevState.isVerticalScrollbarVisible)) {
this.deferScrollTo = false;
this.isUpdatedScrollThumb = this.updateScrollThumbSize();
} else {
this.updateScrollbars();
}
if (this.scrollToInfo !== null) {
if (!this.deferScrollTo) {
this.scrollTo(this.scrollToInfo);
}
}
// publish container resize changes
var horizontal = isHorizontalScrollbarVisible !== prevState.isHorizontalScrollbarVisible;
var vertical = isVerticalScrollbarVisible !== prevState.isVerticalScrollbarVisible;
if (horizontal || vertical) {
this.resizeRegistry.notify({});
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.resizeRegistry.parent = null;
// Before call cancelAnimationFrame, you must send scrollStop Event.
if (this.scrolling) {
this.forwardScrollEvent('onScrollStop', this.getReachedEdgeInfo());
}
this.scrollStopJob.stop();
this.removeEventListeners();
}
}, {
key: "handleResize",
value: function handleResize(ev) {
if (ev.action === 'invalidateBounds') {
this.enqueueForceUpdate();
}
}
}, {
key: "enqueueForceUpdate",
value:
// TODO: consider replacing forceUpdate() by storing bounds in state rather than a non-
// state member.
function enqueueForceUpdate() {
this.childRefCurrent.calculateMetrics(this.childRefCurrent.props);
this.forceUpdate();
}
}, {
key: "calculateDistanceByWheel",
value: function calculateDistanceByWheel(deltaMode, delta, maxPixel) {
if (deltaMode === 0) {
delta = (0, _clamp["default"])(-maxPixel, maxPixel, _resolution["default"].scale(delta * this.scrollWheelMultiplierForDeltaPixel));
} else if (deltaMode === 1) {
// line; firefox
delta = (0, _clamp["default"])(-maxPixel, maxPixel, _resolution["default"].scale(delta * this.pixelPerLine * this.scrollWheelMultiplierForDeltaPixel));
} else if (deltaMode === 2) {
// page
delta = delta < 0 ? -maxPixel : maxPixel;
}
return delta;
}
}, {
key: "forwardScrollEvent",
value:
// call scroll callbacks
function forwardScrollEvent(type, reachedEdgeInfo) {
(0, _handle.forward)(type, {
type: type,
scrollLeft: this.scrollLeft,
scrollTop: this.scrollTop,
moreInfo: this.getMoreInfo(),
reachedEdgeInfo: reachedEdgeInfo
}, this.props);
}
}, {
key: "setScrollLeft",
value:
// update scroll position
function setScrollLeft(value) {
var bounds = this.getScrollBounds();
this.scrollLeft = (0, _clamp["default"])(0, bounds.maxLeft, value);
if (this.overscrollEnabled && this.props.overscrollEffectOn[this.lastInputType]) {
this.checkAndApplyOverscrollEffectOnScroll('horizontal');
}
if (this.state.isHorizontalScrollbarVisible) {
this.updateThumb(this.horizontalScrollbarRef, bounds);
}
}
}, {
key: "setScrollTop",
value: function setScrollTop(value) {
var bounds = this.getScrollBounds();
this.scrollTop = (0, _clamp["default"])(0, bounds.maxTop, value);
if (this.overscrollEnabled && this.props.overscrollEffectOn[this.lastInputType]) {
this.checkAndApplyOverscrollEffectOnScroll('vertical');
}
if (this.state.isVerticalScrollbarVisible) {
this.updateThumb(this.verticalScrollbarRef, bounds);
}
}
}, {
key: "start",
value:
// scroll start
function start(_ref) {
var targetX = _ref.targetX,
targetY = _ref.targetY,
_ref$animate = _ref.animate,
animate = _ref$animate === void 0 ? true : _ref$animate,
_ref$overscrollEffect = _ref.overscrollEffect,
overscrollEffect = _ref$overscrollEffect === void 0 ? false : _ref$overscrollEffect;
var scrollLeft = this.scrollLeft,
scrollTop = this.scrollTop,
childRefCurrent = this.childRefCurrent,
childContainerRef = childRefCurrent.containerRef,
bounds = this.getScrollBounds(),
maxLeft = bounds.maxLeft,
maxTop = bounds.maxTop;
var updatedAnimationInfo = {
targetX: targetX,
targetY: targetY
};
// bail early when scrolling to the same position
if (this.scrolling && this.animationInfo && this.animationInfo.targetX === targetX && this.animationInfo.targetY === targetY) {
return;
}
this.animationInfo = updatedAnimationInfo;
if (Math.abs(maxLeft - targetX) < epsilon) {
targetX = maxLeft;
}
if (Math.abs(maxTop - targetY) < epsilon) {
targetY = maxTop;
}
if (this.overscrollEnabled && overscrollEffect) {
if (scrollLeft !== targetX && this.canScrollHorizontally(bounds)) {
this.checkAndApplyOverscrollEffectOnStart('horizontal', this.getEdgeFromPosition(targetX, maxLeft), targetX);
}
if (scrollTop !== targetY && this.canScrollVertically(bounds)) {
this.checkAndApplyOverscrollEffectOnStart('vertical', this.getEdgeFromPosition(targetY, maxTop), targetY);
}
}
if (animate) {
childRefCurrent.scrollToPosition(targetX, targetY);
} else {
childContainerRef.current.style.scrollBehavior = null;
childRefCurrent.scrollToPosition(targetX, targetY);
childContainerRef.current.style.scrollBehavior = 'smooth';
}
this.scrollStopJob.start();
if (this.props.start) {
this.props.start(animate);
}
}
}, {
key: "stop",
value: function stop() {
var childRefCurrent = this.childRefCurrent,
childContainerRef = childRefCurrent.containerRef;
childContainerRef.current.style.scrollBehavior = null;
childRefCurrent.scrollToPosition(this.scrollLeft + 0.1, this.scrollTop + 0.1);
childContainerRef.current.style.scrollBehavior = 'smooth';
}
}, {
key: "showThumb",
value:
// scroll bar
function showThumb(bounds) {
if (this.state.isHorizontalScrollbarVisible && this.canScrollHorizontally(bounds) && this.horizontalScrollbarRef.current) {
this.horizontalScrollbarRef.current.showThumb();
}
if (this.state.isVerticalScrollbarVisible && this.canScrollVertically(bounds) && this.verticalScrollbarRef.current) {
this.verticalScrollbarRef.current.showThumb();
}
}
}, {
key: "updateThumb",
value: function updateThumb(scrollbarRef, bounds) {
scrollbarRef.current.update(_objectSpread(_objectSpread({}, bounds), {}, {
scrollLeft: this.scrollLeft,
scrollTop: this.scrollTop
}));
}
}, {
key: "getScrollBounds",
value:
// ref
function getScrollBounds() {
if (this.childRefCurrent && typeof this.childRefCurrent.getScrollBounds === 'function') {
return this.childRefCurrent.getScrollBounds();
}
}
}, {
key: "getMoreInfo",
value: function getMoreInfo() {
if (this.childRefCurrent && typeof this.childRefCurrent.getMoreInfo === 'function') {
return this.childRefCurrent.getMoreInfo();
}
}
// FIXME setting event handlers directly to work on the V8 snapshot.
}, {
key: "addEventListeners",
value: function addEventListeners() {
var childRefCurrent = this.childRefCurrent,
containerRef = this.containerRef;
if (containerRef.current && containerRef.current.addEventListener) {
containerRef.current.addEventListener('wheel', this.onWheel);
containerRef.current.addEventListener('keydown', this.onKeyDown);
containerRef.current.addEventListener('mousedown', this.onMouseDown);
}
if (childRefCurrent.containerRef.current) {
if (childRefCurrent.containerRef.current.addEventListener) {
childRefCurrent.containerRef.current.addEventListener('scroll', this.onScroll, {
capture: true,
passive: true
});
}
this.childRefCurrent.containerRef.current.style.scrollBehavior = 'smooth';
}
if (this.props.addEventListeners) {
this.props.addEventListeners(childRefCurrent.containerRef);
}
if (window) {
window.addEventListener('resize', this.handleResizeWindow);
}
}
// FIXME setting event handlers directly to work on the V8 snapshot.
}, {
key: "removeEventListeners",
value: function removeEventListeners() {
var childRefCurrent = this.childRefCurrent,
containerRef = this.containerRef;
if (containerRef.current && containerRef.current.removeEventListener) {
containerRef.current.removeEventListener('wheel', this.onWheel);
containerRef.current.removeEventListener('keydown', this.onKeyDown);
containerRef.current.removeEventListener('mousedown', this.onMouseDown);
}
if (childRefCurrent.containerRef.current && childRefCurrent.containerRef.current.removeEventListener) {
childRefCurrent.containerRef.current.removeEventListener('scroll', this.onScroll, {
capture: true,
passive: true
});
}
if (this.props.removeEventListeners) {
this.props.removeEventListeners(childRefCurrent.containerRef);
}
if (window) {
window.removeEventListener('resize', this.handleResizeWindow);
}
}
}, {
key: "render",
value: function render() {
var _this$props4 = this.props,
className = _this$props4.className,
containerRenderer = _this$props4.containerRenderer,
noScrollByDrag = _this$props4.noScrollByDrag,
rtl = _this$props4.rtl,
style = _this$props4.style,
rest = _objectWithoutProperties(_this$props4, _excluded),
_this$state3 = this.state,
isHorizontalScrollbarVisible = _this$state3.isHorizontalScrollbarVisible,
isVerticalScrollbarVisible = _this$state3.isVerticalScrollbarVisible,
scrollableClasses = (0, _classnames["default"])(_UiScrollableModule["default"].scrollable, className),
contentClasses = (0, _classnames["default"])(_UiScrollableModule["default"].content, _UiScrollableModule["default"].contentNative),
childWrapper = noScrollByDrag ? 'div' : TouchableDiv,
childWrapperProps = _objectSpread({
className: contentClasses
}, !noScrollByDrag && {
flickConfig: flickConfig,
onDrag: this.onDrag,
onDragEnd: this.onDragEnd,
onDragStart: this.onDragStart,
onFlick: this.onFlick,
onTouchStart: this.onTouchStart
});
delete rest.addEventListeners;
delete rest.applyOverscrollEffect;
delete rest.cbScrollTo;
delete rest.clearOverscrollEffect;
delete rest.handleResizeWindow;
delete rest.horizontalScrollbar;
delete rest.noScrollByWheel;
delete rest.onFlick;
delete rest.onKeyDown;
delete rest.onMouseDown;
delete rest.onScroll;
delete rest.onScrollStart;
delete rest.onScrollStop;
delete rest.onWheel;
delete rest.overscrollEffectOn;
delete rest.removeEventListeners;
delete rest.scrollStopOnScroll;
delete rest.scrollTo;
delete rest.start;
delete rest.verticalScrollbar;
this.deferScrollTo = true;
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Resizable.ResizeContext.Provider, {
value: this.resizeRegistry.register,
children: typeof containerRenderer === 'function' ? containerRenderer({
childComponentProps: rest,
childWrapper: childWrapper,
childWrapperProps: childWrapperProps,
className: scrollableClasses,
componentCss: _UiScrollableModule["default"],
containerRef: this.containerRef,
horizontalScrollbarProps: this.horizontalScrollbarProps,
initChildRef: this.initChildRef,
isHorizontalScrollbarVisible: isHorizontalScrollbarVisible,
isVerticalScrollbarVisible: isVerticalScrollbarVisible,
rtl: rtl,
scrollTo: this.scrollTo,
style: style,
verticalScrollbarProps: this.verticalScrollbarProps
}) : null
});
}
}]);
return ScrollableBaseNative;
}(_react.Component);
/**
* An unstyled native component that provides horizontal and vertical scrollbars and makes a render prop element scrollable.
*
* @class ScrollableNative
* @memberof moonstone/UiScrollableNative
* @extends moonstone/UiScrollable.ScrollableBaseNative
* @ui
* @private
*/
ScrollableBaseNative.displayName = 'ui:ScrollableBaseNative';
ScrollableBaseNative.propTypes = /** @lends moonstone/UiScrollableNative.ScrollableNative.prototype */{
/**
* Render function.
*
* @type {Function}
* @required
* @private
*/
containerRenderer: _propTypes["default"].func.isRequired,
/**
* Called when adding additional event listeners in a themed component.
*
* @type {Function}
* @private
*/
addEventListeners: _propTypes["default"].func,
/**
* Called to execute additional logic in a themed component to show overscroll effect.
*
* @type {Function}
* @private
*/
applyOverscrollEffect: _propTypes["default"].func,
/**
* A callback function that receives a reference to the `scrollTo` feature.
*
* Once received, the `scrollTo` method can be called as an imperative interface.
*
* The `scrollTo` function accepts the following parameters:
* - {position: {x, y}} - Pixel value for x and/or y position
* - {align} - Where the scroll area should be aligned. Values are:
* `'left'`, `'right'`, `'top'`, `'bottom'`,
* `'topleft'`, `'topright'`, `'bottomleft'`, and `'bottomright'`.
* - {index} - Index of specific item. (`0` or positive integer)
* This option is available for only `VirtualList` kind.
* - {node} - Node to scroll into view
* - {animate} - When `true`, scroll occurs with animation. When `false`, no
* animation occurs.
* - {focus} - When `true`, attempts to focus item after scroll. Only valid when scrolling
* by `index` or `node`.
* > Note: Only specify one of: `position`, `align`, `index` or `node`
*
* Example:
* ```
* // If you set cbScrollTo prop like below;
* cbScrollTo: (fn) => {this.scrollTo = fn;}
* // You can simply call like below;
* this.scrollT