@enact/ui
Version:
A collection of simplified unstyled cross-platform UI components for Enact
379 lines (369 loc) • 14.8 kB
JavaScript
"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;