New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@thi.ng/rstream-gestures

Package Overview
Dependencies
Maintainers
1
Versions
358
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@thi.ng/rstream-gestures - npm Package Compare versions

Comparing version 5.0.36 to 5.0.37

2

CHANGELOG.md
# Change Log
- **Last updated**: 2023-12-09T19:12:03Z
- **Last updated**: 2023-12-11T10:07:09Z
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)

@@ -5,0 +5,0 @@

@@ -8,215 +8,180 @@ import { isBoolean } from "@thi.ng/checks/is-boolean";

import { map } from "@thi.ng/transducers/map";
const START_EVENTS = new Set([
"mousedown",
"touchmove",
"touchstart",
"mousemove",
const START_EVENTS = /* @__PURE__ */ new Set([
"mousedown",
"touchmove",
"touchstart",
"mousemove"
]);
const END_EVENTS = new Set(["mouseup", "touchend", "touchcancel"]);
const END_EVENTS = /* @__PURE__ */ new Set(["mouseup", "touchend", "touchcancel"]);
const BASE_EVENTS = ["mousemove", "mousedown", "touchstart", "wheel"];
const EVENT_GESTURETYPES = {
touchstart: "start",
touchmove: "drag",
touchend: "end",
touchcancel: "end",
mousedown: "start",
mouseup: "end",
wheel: "zoom",
touchstart: "start",
touchmove: "drag",
touchend: "end",
touchcancel: "end",
mousedown: "start",
mouseup: "end",
wheel: "zoom"
};
/**
* Attaches mouse & touch event listeners to given DOM element and returns a
* stream of {@link GestureEvent}s and their {@link GestureInfo} details.
*
* In multi-touch environments, a `GestureEvent` can contain multiple such
* `GestureInfo` objects (one per active touch). In general, the `click` and
* `delta` values are only present if the abstracted event `type == "drag"`.
* Both (and `pos` too) are 2-element arrays of `[x,y]` coordinates.
*
* The `zoom` value is always present, but is only updated with wheel events.
* The value will be constrained to `minZoom` ... `maxZoom` interval (provided
* via options object).
*
* Note: If using `preventDefault` and attaching the event stream to
* `document.body`, the following event listener options SHOULD be used:
*
* @example
* ```ts
* eventOpts: { passive: false }
* ```
*
* https://www.chromestatus.com/features/5093566007214080
*
* @param el -
* @param opts -
*/
export const gestureStream = (el, _opts) => {
const opts = {
zoom: 1,
absZoom: true,
minZoom: 0.25,
maxZoom: 4,
smooth: 1,
eventOpts: { capture: true },
preventDefault: true,
preventScrollOnZoom: true,
preventContextMenu: true,
local: true,
scale: false,
..._opts,
};
opts.id = opts.id || `gestures-${__nextID()}`;
const active = [];
let zoom = clamp(isNumber(opts.zoom) ? opts.zoom : opts.zoom.deref() || 1, opts.minZoom, opts.maxZoom);
let zoomDelta = 0;
let numTouches = 0;
let lastPos = [0, 0];
let tempStreams;
const isBody = el === document.body;
const tempEvents = [
"touchend",
"touchcancel",
"touchmove",
"mouseup",
];
!isBody && tempEvents.push("mousemove");
opts.preventContextMenu &&
el.addEventListener("contextmenu", (e) => e.preventDefault());
const gestureStart = (etype, events, bounds, isTouch) => {
const isStart = etype === "mousedown" || etype === "touchstart";
for (let t of events) {
const id = t.identifier || 0;
const pos = getPos(t, bounds, opts.local, opts.scale);
let touch = active.find((t) => t.id === id);
if (!touch && isStart) {
touch = { id, start: pos };
active.push(touch);
numTouches++;
}
if (touch) {
touch.pos = pos;
touch.delta = [
pos[0] - touch.start[0],
pos[1] - touch.start[1],
];
if (isTouch) {
touch.force = t.force;
}
}
const gestureStream = (el, _opts) => {
const opts = {
zoom: 1,
absZoom: true,
minZoom: 0.25,
maxZoom: 4,
smooth: 1,
eventOpts: { capture: true },
preventDefault: true,
preventScrollOnZoom: true,
preventContextMenu: true,
local: true,
scale: false,
..._opts
};
opts.id = opts.id || `gestures-${__nextID()}`;
const active = [];
let zoom = clamp(
isNumber(opts.zoom) ? opts.zoom : opts.zoom.deref() || 1,
opts.minZoom,
opts.maxZoom
);
let zoomDelta = 0;
let numTouches = 0;
let lastPos = [0, 0];
let tempStreams;
const isBody = el === document.body;
const tempEvents = [
"touchend",
"touchcancel",
"touchmove",
"mouseup"
];
!isBody && tempEvents.push("mousemove");
opts.preventContextMenu && el.addEventListener("contextmenu", (e) => e.preventDefault());
const gestureStart = (etype, events, bounds, isTouch) => {
const isStart = etype === "mousedown" || etype === "touchstart";
for (let t of events) {
const id = t.identifier || 0;
const pos = getPos(t, bounds, opts.local, opts.scale);
let touch = active.find((t2) => t2.id === id);
if (!touch && isStart) {
touch = { id, start: pos };
active.push(touch);
numTouches++;
}
if (touch) {
touch.pos = pos;
touch.delta = [
pos[0] - touch.start[0],
pos[1] - touch.start[1]
];
if (isTouch) {
touch.force = t.force;
}
if (isStart && !tempStreams) {
tempStreams = tempEvents.map((id) => eventSource(document.body, id, opts, "-temp"));
stream.addAll(tempStreams);
!isBody && stream.removeID("mousemove");
}
}
if (isStart && !tempStreams) {
tempStreams = tempEvents.map(
(id) => eventSource(document.body, id, opts, "-temp")
);
stream.addAll(tempStreams);
!isBody && stream.removeID("mousemove");
}
};
const gestureEnd = (events) => {
for (let t of events) {
const id = t.identifier || 0;
const idx = active.findIndex((t2) => t2.id === id);
if (idx !== -1) {
active.splice(idx, 1);
numTouches--;
}
}
if (numTouches === 0) {
stream.removeAll(tempStreams);
!isBody && stream.add(eventSource(el, "mousemove", opts));
tempStreams = void 0;
}
};
const updateZoom = (e) => {
const zdelta = opts.smooth * ("wheelDeltaY" in e ? -e.wheelDeltaY / 120 : e.deltaY / 40);
zoom = opts.absZoom ? clamp(zoom + zdelta, opts.minZoom, opts.maxZoom) : zdelta;
zoomDelta = zdelta;
};
const stream = merge({
id: opts.id,
src: BASE_EVENTS.map((id) => eventSource(el, id, opts)),
xform: map((e) => {
const etype = e.type;
if (etype === "$zoom") {
zoomDelta = e.value - zoom;
if (opts.absZoom) {
zoom = clamp(zoom + zoomDelta, opts.minZoom, opts.maxZoom);
} else {
zoom = zoomDelta;
}
};
const gestureEnd = (events) => {
for (let t of events) {
const id = t.identifier || 0;
const idx = active.findIndex((t) => t.id === id);
if (idx !== -1) {
active.splice(idx, 1);
numTouches--;
}
}
if (numTouches === 0) {
stream.removeAll(tempStreams);
!isBody && stream.add(eventSource(el, "mousemove", opts));
tempStreams = undefined;
}
};
const updateZoom = (e) => {
const zdelta = opts.smooth *
("wheelDeltaY" in e
? -e.wheelDeltaY / 120
: e.deltaY / 40);
zoom = opts.absZoom
? clamp(zoom + zdelta, opts.minZoom, opts.maxZoom)
: zdelta;
zoomDelta = zdelta;
};
const stream = merge({
id: opts.id,
src: BASE_EVENTS.map((id) => eventSource(el, id, opts)),
xform: map((e) => {
const etype = e.type;
if (etype === "$zoom") {
zoomDelta = e.value - zoom;
if (opts.absZoom) {
zoom = clamp(zoom + zoomDelta, opts.minZoom, opts.maxZoom);
}
else {
zoom = zoomDelta;
}
return {
pos: lastPos.slice(),
buttons: 0,
type: "zoom",
active,
zoom,
zoomDelta,
isTouch: false,
};
}
const type = classifyEventType(etype, !!tempStreams);
let isTouch = !!e.touches;
let events = isTouch
? Array.from(e.changedTouches)
: [e];
const bounds = el.getBoundingClientRect();
if (START_EVENTS.has(etype)) {
gestureStart(etype, events, bounds, isTouch);
}
else if (END_EVENTS.has(etype)) {
gestureEnd(events);
}
else if (type === "zoom") {
updateZoom(e);
}
lastPos = getPos(events[0], bounds, opts.local, opts.scale);
opts.preventDefault && e.preventDefault();
return {
event: e,
pos: lastPos,
buttons: isTouch ? active.length : e.buttons,
type,
active,
zoom,
zoomDelta,
isTouch,
};
}),
});
// attach zoom reset
if (!isNumber(opts.zoom)) {
stream.add(opts.zoom.map((x) => ({ type: "$zoom", value: x })));
}
return stream;
return {
pos: lastPos.slice(),
buttons: 0,
type: "zoom",
active,
zoom,
zoomDelta,
isTouch: false
};
}
const type = classifyEventType(etype, !!tempStreams);
let isTouch = !!e.touches;
let events = isTouch ? Array.from(e.changedTouches) : [e];
const bounds = el.getBoundingClientRect();
if (START_EVENTS.has(etype)) {
gestureStart(etype, events, bounds, isTouch);
} else if (END_EVENTS.has(etype)) {
gestureEnd(events);
} else if (type === "zoom") {
updateZoom(e);
}
lastPos = getPos(events[0], bounds, opts.local, opts.scale);
opts.preventDefault && e.preventDefault();
return {
event: e,
pos: lastPos,
buttons: isTouch ? active.length : e.buttons,
type,
active,
zoom,
zoomDelta,
isTouch
};
})
});
if (!isNumber(opts.zoom)) {
stream.add(opts.zoom.map((x) => ({ type: "$zoom", value: x })));
}
return stream;
};
const eventSource = (el, type, opts, suffix = "") => {
let eventOpts = opts.eventOpts;
if (type === "wheel" && opts.preventScrollOnZoom) {
eventOpts = isBoolean(eventOpts)
? { capture: eventOpts, passive: false }
: { ...eventOpts, passive: false };
}
return fromDOMEvent(el, type, eventOpts, { id: type + suffix });
let eventOpts = opts.eventOpts;
if (type === "wheel" && opts.preventScrollOnZoom) {
eventOpts = isBoolean(eventOpts) ? { capture: eventOpts, passive: false } : { ...eventOpts, passive: false };
}
return fromDOMEvent(el, type, eventOpts, { id: type + suffix });
};
const classifyEventType = (etype, isActive) => etype === "mousemove"
? isActive
? "drag"
: "move"
: EVENT_GESTURETYPES[etype];
const classifyEventType = (etype, isActive) => etype === "mousemove" ? isActive ? "drag" : "move" : EVENT_GESTURETYPES[etype];
const getPos = (e, bounds, isLocal, doScale) => {
let x = e.clientX;
let y = e.clientY;
if (isLocal) {
x -= bounds.left;
y -= bounds.top;
}
if (doScale) {
const dpr = window.devicePixelRatio || 1;
x *= dpr;
y *= dpr;
}
return [x | 0, y | 0];
let x = e.clientX;
let y = e.clientY;
if (isLocal) {
x -= bounds.left;
y -= bounds.top;
}
if (doScale) {
const dpr = window.devicePixelRatio || 1;
x *= dpr;
y *= dpr;
}
return [x | 0, y | 0];
};
export {
gestureStream
};
{
"name": "@thi.ng/rstream-gestures",
"version": "5.0.36",
"version": "5.0.37",
"description": "Unified mouse, mouse wheel & multi-touch event stream abstraction",

@@ -31,3 +31,5 @@ "type": "module",

"scripts": {
"build": "yarn clean && tsc --declaration",
"build": "yarn build:esbuild && yarn build:decl",
"build:decl": "tsc --declaration --emitDeclarationOnly",
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",

@@ -41,10 +43,11 @@ "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",

"dependencies": {
"@thi.ng/api": "^8.9.11",
"@thi.ng/checks": "^3.4.11",
"@thi.ng/math": "^5.7.6",
"@thi.ng/rstream": "^8.2.13",
"@thi.ng/transducers": "^8.8.14"
"@thi.ng/api": "^8.9.12",
"@thi.ng/checks": "^3.4.12",
"@thi.ng/math": "^5.7.7",
"@thi.ng/rstream": "^8.2.14",
"@thi.ng/transducers": "^8.8.15"
},
"devDependencies": {
"@microsoft/api-extractor": "^7.38.3",
"esbuild": "^0.19.8",
"rimraf": "^5.0.5",

@@ -98,3 +101,3 @@ "tools": "^0.0.1",

},
"gitHead": "25f2ac8ff795a432a930119661b364d4d93b59a0\n"
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc