lisn.js
Version:
Simply handle user gestures and actions. Includes widgets.
222 lines (206 loc) • 10.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.PointerWatcher = void 0;
var MC = _interopRequireWildcard(require("../globals/minification-constants.cjs"));
var MH = _interopRequireWildcard(require("../globals/minification-helpers.cjs"));
var _event = require("../utils/event.cjs");
var _log = require("../utils/log.cjs");
var _pointer = require("../utils/pointer.cjs");
var _text = require("../utils/text.cjs");
var _validation = require("../utils/validation.cjs");
var _callback = require("../modules/callback.cjs");
var _xMap = require("../modules/x-map.cjs");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, 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); } /**
* @module Watchers/PointerWatcher
*/
/**
* {@link PointerWatcher} listens for simple pointer actions like clicks, press
* and hold or hover.
*/
class PointerWatcher {
/**
* Creates a new instance of PointerWatcher with the given
* {@link PointerWatcherConfig}. It does not save it for future reuse.
*/
static create(config) {
return new PointerWatcher(getConfig(config), CONSTRUCTOR_KEY);
}
/**
* Returns an existing instance of PointerWatcher with the given
* {@link PointerWatcherConfig}, or creates a new one.
*
* **NOTE:** It saves it for future reuse, so don't use this for temporary
* short-lived watchers.
*/
static reuse(config) {
const myConfig = getConfig(config);
const configStrKey = (0, _text.objToStrKey)(myConfig);
let instance = instances.get(configStrKey);
if (!instance) {
instance = new PointerWatcher(myConfig, CONSTRUCTOR_KEY);
instances.set(configStrKey, instance);
}
return instance;
}
constructor(config, key) {
/**
* Call the `startHandler` whenever the pointer action begins.
* Call the `endHandler` whenever the pointer action ends. If `endHandler` is
* not given, it defaults to `startHandler`.
*
* For an explanation of what "begins" and "ends" means for each supported
* action, see {@link OnPointerOptions.actions}.
*
* **IMPORTANT:** The same handlers can _not_ be added multiple times for the
* same event target, even if the options differ. If the handler has already
* been added for this target, then it will be removed and re-added with the
* current options.
*/
_defineProperty(this, "onPointer", void 0);
/**
* Removes previously added handlers.
*/
_defineProperty(this, "offPointer", void 0);
if (key !== CONSTRUCTOR_KEY) {
throw MH.illegalConstructorError("PointerWatcher.create");
}
// Keep this watcher super simple. The events we listen for don't fire at a
// high rate and it's unlikely for there to be many many callbacks for each
// target and event type, so don't bother with using a delegating listener,
// etc.
// Keep a map of callbacks so we can lookup the callback by the handler
// (and also to prevent duplicate handler for each target, for consistency
// with other watchers).
const allCallbacks = (0, _xMap.newXWeakMap)(() => MH.newMap());
// ----------
const createCallback = (target, handler) => {
var _allCallbacks$get;
MH.remove((_allCallbacks$get = allCallbacks.get(target)) === null || _allCallbacks$get === void 0 ? void 0 : _allCallbacks$get.get(handler));
const callback = (0, _callback.wrapCallback)(handler);
callback.onRemove(() => {
MH.deleteKey(allCallbacks.get(target), handler);
});
allCallbacks.sGet(target).set(handler, callback);
return callback;
};
// async for consistency with other watchers and future compatibility in
// case of change needed
const setupOnPointer = async (target, startHandler, endHandler, userOptions) => {
const options = getOptions(config, userOptions);
const startCallback = createCallback(target, startHandler);
const endCallback = endHandler && endHandler !== startHandler ? createCallback(target, endHandler) : startCallback;
for (const action of options._actions) {
listenerSetupFn[action](target, startCallback, endCallback, options);
}
};
// ----------
this.onPointer = setupOnPointer;
// ----------
this.offPointer = (target, startHandler, endHandler) => {
const entry = allCallbacks.get(target);
MH.remove(entry === null || entry === void 0 ? void 0 : entry.get(startHandler));
if (endHandler) {
MH.remove(entry === null || entry === void 0 ? void 0 : entry.get(endHandler));
}
};
}
}
/**
* @interface
*/
/**
* @interface
*/
/**
* The handler is invoked with two arguments:
*
* - the event target that was passed to the {@link PointerWatcher.onPointer}
* call (equivalent to
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget | Event:currentTarget}).
* - the {@link PointerActionData} describing the state of the action.
*/
// ----------------------------------------
exports.PointerWatcher = PointerWatcher;
const CONSTRUCTOR_KEY = MC.SYMBOL();
const instances = MH.newMap();
const getConfig = config => {
var _config$preventDefaul, _config$preventSelect;
return {
_preventDefault: (_config$preventDefaul = config === null || config === void 0 ? void 0 : config.preventDefault) !== null && _config$preventDefaul !== void 0 ? _config$preventDefaul : false,
_preventSelect: (_config$preventSelect = config === null || config === void 0 ? void 0 : config.preventSelect) !== null && _config$preventSelect !== void 0 ? _config$preventSelect : true
};
};
const getOptions = (config, options) => {
var _options$preventDefau, _options$preventSelec;
return {
_actions: (0, _validation.validateStrList)("actions", options === null || options === void 0 ? void 0 : options.actions, _pointer.isValidPointerAction) || _pointer.POINTER_ACTIONS,
_preventDefault: (_options$preventDefau = options === null || options === void 0 ? void 0 : options.preventDefault) !== null && _options$preventDefau !== void 0 ? _options$preventDefau : config._preventDefault,
_preventSelect: (_options$preventSelec = options === null || options === void 0 ? void 0 : options.preventSelect) !== null && _options$preventSelec !== void 0 ? _options$preventSelec : config._preventSelect
};
};
const setupClickListener = (target, startCallback, endCallback, options) => {
// false if next will start; true if next will end.
let toggleState = false;
const wrapper = event => {
if (options._preventDefault) {
MH.preventDefault(event);
}
toggleState = !toggleState;
const data = {
action: MC.S_CLICK,
state: toggleState ? "ON" : "OFF"
};
invokeCallback(toggleState ? startCallback : endCallback, target, data, event);
};
(0, _event.addEventListenerTo)(target, MC.S_CLICK, wrapper);
const remove = () => (0, _event.removeEventListenerFrom)(target, MC.S_CLICK, wrapper);
startCallback.onRemove(remove);
endCallback.onRemove(remove);
};
const setupPointerListeners = (action, target, startCallback, endCallback, options) => {
// If the browser doesn't support pointer events, then
// addEventListenerTo will transform these into mouse*
const startEventSuff = action === MC.S_HOVER ? "enter" : "down";
const endEventSuff = action === MC.S_HOVER ? "leave" : "up";
const startEvent = MC.S_POINTER + startEventSuff;
const endEvent = MC.S_POINTER + endEventSuff;
const wrapper = (event, callback) => {
if (options._preventDefault) {
MH.preventDefault(event);
}
const data = {
action,
state: MH.strReplace(event.type, /pointer|mouse/, "") === startEventSuff ? "ON" : "OFF"
};
invokeCallback(callback, target, data, event);
};
const startListener = event => wrapper(event, startCallback);
const endListener = event => wrapper(event, endCallback);
(0, _event.addEventListenerTo)(target, startEvent, startListener);
(0, _event.addEventListenerTo)(target, endEvent, endListener);
// On some touch screen devices pressing and holding will initiate select
// and result in touchend, so we prevent text select
if (options._preventSelect) {
(0, _event.preventSelect)(target);
}
startCallback.onRemove(() => {
(0, _event.undoPreventSelect)(target);
(0, _event.removeEventListenerFrom)(target, startEvent, startListener);
});
endCallback.onRemove(() => {
(0, _event.undoPreventSelect)(target);
(0, _event.removeEventListenerFrom)(target, endEvent, endListener);
});
};
const listenerSetupFn = {
click: setupClickListener,
hover: (...args) => setupPointerListeners(MC.S_HOVER, ...args),
press: (...args) => setupPointerListeners(MC.S_PRESS, ...args)
};
const invokeCallback = (callback, target, actionData, event) => callback.invoke(target, MH.copyObject(actionData), event).catch(_log.logError);
//# sourceMappingURL=pointer-watcher.cjs.map