UNPKG

@enact/ui

Version:

A collection of simplified unstyled cross-platform UI components for Enact

379 lines (369 loc) 14.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = exports.Touch = void 0; var _handle = require("@enact/core/handle"); var _dispatcher = require("@enact/core/dispatcher"); var _complement = _interopRequireDefault(require("ramda/src/complement")); var _platform = _interopRequireDefault(require("@enact/core/platform")); var _config = require("./config"); var _state = require("./state"); var _ClickAllow = _interopRequireDefault(require("./ClickAllow")); var _Drag = require("./Drag"); var _Flick = require("./Flick"); var _Hold = require("./Hold"); var _Pinch = require("./Pinch"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : 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); } var getEventCoordinates = function getEventCoordinates(ev) { var x = ev.clientX, y = ev.clientY, type = ev.type; if (type.indexOf('touch') === 0) { if (ev.targetTouches.length >= 2) { return Array.from(ev.targetTouches, function (targetTouch) { return { x: targetTouch.clientX, y: targetTouch.clientY }; }); } else { x = ev.targetTouches[0].clientX; y = ev.targetTouches[0].clientY; } } return { x: x, y: y }; }; // Establish a standard payload for onDown/onUp/onTap events and pass it along to a handler var makeTouchableEvent = function makeTouchableEvent(type) { return function (ev) { var target = ev.target, currentTarget = ev.currentTarget; var clientX = ev.clientX, clientY = ev.clientY, pageX = ev.pageX, pageY = ev.pageY; if (ev.changedTouches) { clientX = ev.changedTouches[0].clientX; clientY = ev.changedTouches[0].clientY; pageX = ev.changedTouches[0].pageX; pageY = ev.changedTouches[0].pageY; } return { type: type, target: target, currentTarget: currentTarget, clientX: clientX, clientY: clientY, pageX: pageX, pageY: pageY }; }; }; var isEnabled = (0, _handle.forProp)('disabled', false); var handleDown = (0, _handle.handle)(isEnabled, (0, _handle.forwardCustomWithPrevent)('onDown', makeTouchableEvent('onDown')), (0, _handle.call)('activate'), (0, _handle.call)('startGesture')).named('handleDown'); var handleUp = (0, _handle.handle)(isEnabled, (0, _handle.call)('endGesture'), (0, _handle.call)('isTracking'), (0, _handle.forwardCustomWithPrevent)('onUp', makeTouchableEvent('onUp')), (0, _handle.forwardCustom)('onTap', makeTouchableEvent('onTap')))["finally"]((0, _handle.call)('deactivate')).named('handleUp'); var handleEnter = (0, _handle.handle)(isEnabled, (0, _handle.forProp)('noResume', false), (0, _handle.call)('enterGesture'), (0, _handle.call)('isPaused'), (0, _handle.call)('activate')).named('handleEnter'); var handleLeave = (0, _handle.handle)(isEnabled, (0, _handle.call)('leaveGesture'), (0, _handle.oneOf)([(0, _handle.forProp)('noResume', false), (0, _handle.call)('pause')], [_handle.returnsTrue, (0, _handle.call)('deactivate')])).named('handleLeave'); // Mouse event handlers var handleMouseDown = (0, _handle.handle)((0, _handle.forward)('onMouseDown'), (0, _handle.call)('shouldAllowMouseEvent'), handleDown); var handleMouseEnter = (0, _handle.handle)((0, _handle.forward)('onMouseEnter'), handleEnter); var handleMouseMove = (0, _handle.handle)((0, _handle.forward)('onMouseMove'), (0, _handle.call)('moveGesture')); var handleMouseLeave = (0, _handle.handle)((0, _handle.forward)('onMouseLeave'), handleLeave); var handleMouseUp = (0, _handle.handle)((0, _handle.returnsTrue)((0, _handle.call)('setLastMouseUp')), (0, _handle.forward)('onMouseUp'), handleUp); var handleClick = (0, _handle.handle)(isEnabled, // wrapping another handler to always forward onClick but, if onTap should occur, it should // occur first to keep in sync with the up handler which emits onTap first (0, _handle.handle)((0, _handle.call)('shouldAllowTap'), (0, _handle.call)('activate'), handleUp)["finally"]((0, _handle.forward)('onClick'))); // Touch event handlers var handleTouchStart = (0, _handle.handle)((0, _handle.forward)('onTouchStart'), (0, _handle.call)('startTouch'), handleDown); var handleTouchMove = (0, _handle.handle)((0, _handle.forward)('onTouchMove'), (0, _handle.call)('isTracking'), // we don't receive enter/leave events during a touch so we have to simulate them by // detecting when the touch leaves the boundary. oneOf returns the value of whichever // branch it follows so we append moveHold to either to handle moves that aren't // entering or leaving (0, _handle.forwardCustom)('onMove', makeTouchableEvent('onMove')), (0, _handle.oneOf)([(0, _handle.call)('hasTouchLeftTarget'), handleLeave], [_handle.returnsTrue, handleEnter])["finally"]((0, _handle.call)('moveGesture'))); var handleTouchEnd = (0, _handle.handle)((0, _handle.forward)('onTouchEnd'), // block the next mousedown to prevent duplicate touchable events (0, _handle.returnsTrue)((0, _handle.call)('setLastTouchEnd')), (0, _handle.call)('isTracking'), (0, _complement["default"])((0, _handle.call)('hasTouchLeftTarget')), (0, _handle.returnsTrue)((0, _handle.call)('endTouch')), handleUp); // Global touchend/mouseup event handler to deactivate the component var handleGlobalUp = (0, _handle.handle)((0, _handle.call)('isTracking'), (0, _handle.call)('deactivate'))["finally"]((0, _handle.call)('endGesture')); var handleGlobalMove = (0, _handle.handle)((0, _handle.call)('isTracking'), (0, _handle.call)('containsCurrentTarget'), (0, _handle.call)('moveGesture')); var handleBlur = (0, _handle.handle)((0, _handle.forward)('onBlur'), (0, _handle.call)('hasFocus'), (0, _handle.call)('blurGesture')); var Touch = exports.Touch = /*#__PURE__*/function () { function Touch() { var _this = this; _classCallCheck(this, Touch); this.getHandlers = function () { return _this.handlers; }; this.context = {}; this.target = null; this.targetHadFocus = false; this.handle = _handle.handle.bind(this); this.drag = new _Drag.Drag(); this.flick = new _Flick.Flick(); this.hold = new _Hold.Hold(); this.pinch = new _Pinch.Pinch(); this.clickAllow = new _ClickAllow["default"](); this.handlers = { onClick: handleClick.bindAs(this, 'handleClick'), onBlur: handleBlur.bindAs(this, 'handleBlur'), onMouseDown: handleMouseDown.bindAs(this, 'handleMouseDown'), onMouseEnter: handleMouseEnter.bindAs(this, 'handleMouseEnter'), onMouseMove: handleMouseMove.bindAs(this, 'handleMouseMove'), onMouseLeave: handleMouseLeave.bindAs(this, 'handleMouseLeave'), onMouseUp: handleMouseUp.bindAs(this, 'handleMouseUp') }; if (_platform["default"].touchEvent) { Object.assign(this.handlers, { onTouchStart: handleTouchStart.bindAs(this, 'handleTouchStart'), onTouchMove: handleTouchMove.bindAs(this, 'handleTouchMove'), onTouchEnd: handleTouchEnd.bindAs(this, 'handleTouchEnd') }); } handleGlobalUp.bindAs(this, 'handleGlobalUp'); handleGlobalMove.bindAs(this, 'handleGlobalMove'); } return _createClass(Touch, [{ key: "setPropsAndContext", value: function setPropsAndContext(config, state, setState) { // remapping to props for better compatibility with core/handle and binding this.props = config; this.context.state = state; this.context.setState = setState; } }, { key: "updateGestureConfig", value: function updateGestureConfig(dragConfig, flickConfig, holdConfig, pinchConfig) { this.config = (0, _config.mergeConfig)({ drag: dragConfig, flick: flickConfig, hold: holdConfig, pinch: pinchConfig }); } }, { key: "addGlobalHandlers", value: function addGlobalHandlers() { // ensure we clean up our internal state if (_platform["default"].touchEvent) { (0, _dispatcher.on)('touchend', this.handleGlobalUp, document); } (0, _dispatcher.on)('mouseup', this.handleGlobalUp, document); (0, _dispatcher.on)('mousemove', this.handleGlobalMove, document); } }, { key: "removeGlobalHandlers", value: function removeGlobalHandlers() { if (_platform["default"].touchEvent) { (0, _dispatcher.off)('touchend', this.handleGlobalUp, document); } (0, _dispatcher.off)('mouseup', this.handleGlobalUp, document); (0, _dispatcher.off)('mousemove', this.handleGlobalMove, document); } // State Management }, { key: "setTarget", value: function setTarget(target) { this.target = target; } }, { key: "clearTarget", value: function clearTarget() { this.target = null; } }, { key: "activate", value: function activate(ev) { this.setTarget(ev.currentTarget); if (this.props.getActive) { this.context.setState(_state.States.Active); } return true; } }, { key: "deactivate", value: function deactivate() { this.clearTarget(); if (this.props.getActive) { this.context.setState(_state.States.Inactive); } return true; } }, { key: "pause", value: function pause() { if (this.props.getActive && this.context.state === _state.States.Active) { this.context.setState(_state.States.Paused); } return true; } }, { key: "disable", value: function disable() { this.clearTarget(); this.hold.end(); } }, { key: "updateProps", value: function updateProps(props) { // Update the props onHoldStart, onHold, and onHoldEnd on any gesture (pinch, hold, flick, drag). this.pinch.updateProps(props); this.hold.updateProps(props); this.flick.updateProps(props); this.drag.updateProps(props); } // Gesture Support }, { key: "startTouch", value: function startTouch(_ref) { var target = _ref.target, currentTarget = _ref.currentTarget; if (currentTarget.contains(target)) { (0, _dispatcher.on)('contextmenu', _handle.preventDefault); this.targetBounds = currentTarget.getBoundingClientRect(); return true; } return false; } }, { key: "endTouch", value: function endTouch() { (0, _dispatcher.off)('contextmenu', _handle.preventDefault); this.targetBounds = null; } }, { key: "startGesture", value: function startGesture(ev, props) { var coords = getEventCoordinates(ev); var _this$config = this.config, pinch = _this$config.pinch, hold = _this$config.hold, flick = _this$config.flick, drag = _this$config.drag; if (Array.isArray(coords)) { this.pinch.begin(pinch, props, coords, this.target); } else { this.hold.begin(hold, props, coords); this.flick.begin(flick, props, coords); this.drag.begin(drag, props, coords, this.target); } this.targetHadFocus = this.target === document.activeElement; return true; } }, { key: "moveGesture", value: function moveGesture(ev) { var coords = getEventCoordinates(ev); if (Array.isArray(coords)) { this.pinch.move(coords); } else { this.hold.move(coords); this.flick.move(coords); this.drag.move(coords); } return true; } }, { key: "enterGesture", value: function enterGesture() { this.drag.enter(); this.hold.enter(); return true; } }, { key: "leaveGesture", value: function leaveGesture() { this.drag.leave(); this.hold.leave(); return true; } }, { key: "blurGesture", value: function blurGesture() { this.targetHadFocus = false; this.hold.blur(); this.flick.blur(); this.drag.blur(); return true; } }, { key: "endGesture", value: function endGesture() { this.targetHadFocus = false; this.pinch.end(); this.hold.end(); this.flick.end(); this.drag.end(); return true; } // Event handler utilities }, { key: "isTracking", value: function isTracking() { // verify we had a target and the up target is still within the current node return this.target; } }, { key: "isPaused", value: function isPaused() { return this.context.state === _state.States.Paused; } }, { key: "hasFocus", value: function hasFocus() { return this.targetHadFocus; } }, { key: "hasTouchLeftTarget", value: function hasTouchLeftTarget(ev) { var _this2 = this; return Array.from(ev.changedTouches).reduce(function (hasLeft, _ref2) { var pageX = _ref2.pageX, pageY = _ref2.pageY; var _this2$targetBounds = _this2.targetBounds, left = _this2$targetBounds.left, right = _this2$targetBounds.right, top = _this2$targetBounds.top, bottom = _this2$targetBounds.bottom; return hasLeft && left > pageX || right < pageX || top > pageY || bottom < pageY; }, true); } }, { key: "containsCurrentTarget", value: function containsCurrentTarget(_ref3) { var target = _ref3.target; return !this.target.contains(target); } }, { key: "shouldAllowMouseEvent", value: function shouldAllowMouseEvent(ev) { return this.clickAllow.shouldAllowMouseEvent(ev); } }, { key: "shouldAllowTap", value: function shouldAllowTap(ev) { return this.clickAllow.shouldAllowTap(ev); } }, { key: "setLastMouseUp", value: function setLastMouseUp(ev) { this.clickAllow.setLastMouseUp(ev); } }, { key: "setLastTouchEnd", value: function setLastTouchEnd(ev) { this.clickAllow.setLastTouchEnd(ev); } }]); }(); var _default = exports["default"] = Touch;