Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ jobs:

- name: Publish to npm
run: pnpm publish --no-git-checks

- name: Publish to JSR
run: pnpx jsr publish
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<br>
</h1>

<h4 align="center"> modern, lightweight, robust and extensible human gesture recognizer </h4>
<h4 align="center"> 🖱️🤏 lightweight, robust and extensible human gesture detector </h4>

<p align="center">
<a href="https://github.com/hesprs/pointeract/actions">
Expand All @@ -23,6 +23,9 @@
<a href="https://www.npmjs.com/package/pointeract">
<img src="https://img.shields.io/npm/v/pointeract?logo=npm&labelColor=red&logoColor=white&color=333333" alt="npm package" />
</a>
<a href="https://jsr.io/@hesprs/pointeract">
<img src="https://img.shields.io/jsr/v/@hesprs/pointeract?logo=jsr&labelColor=f7df1e&logoColor=white&color=333333" alt="JSR package" />
</a>
<a href="https://snyk.io/test/npm/pointeract">
<img src="https://img.shields.io/badge/Snyk%20Security-Monitored-333333?logo=snyk&style=flat&labelColor=8A2BE2&logoColor=white" alt="library security" />
</a>
Expand Down Expand Up @@ -65,13 +68,19 @@ Install Pointeract using your favorite package manager:

```sh
# npm
$ npm add pointeract
npm add pointeract

# pnpm
$ pnpm add pointeract
pnpm add pointeract

# yarn
$ yarn add pointeract
yarn add pointeract

# bun
bun add pointeract

# deno
deno add jsr:@hesprs/pointeract
```

Or include the following lines directly in your HTML file:
Expand All @@ -89,7 +98,7 @@ Then simply grab the core class and a module:
```TypeScript
import { Pointeract, Drag } from 'pointeract';

new Pointeract({ element: yourElement }, Drag)
new Pointeract({ element: yourElement }, [Drag])
.start()
.on('drag', e => console.log(e));
```
Expand Down
5 changes: 5 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ export default defineConfig<ThemeConfig>({
],
editLink: 'https://github.com/hesprs/pointeract/edit/main/docs/:path',
outline: 'deep',
footer: {
message:
'Licensed under <a href="https://www.apache.org/licenses/LICENSE-2.0.html">Apache License 2.0</a>.',
copyright: 'Copyright © 2025-2026 Hēsperus',
},
},
markdown: {
config(md) {
Expand Down
2 changes: 1 addition & 1 deletion docs/components/playground.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
zoomPreset,
panPreset,
} from '@';
import { Coordinates } from '@/declarations';
import { Coordinates } from '@/types';

function C2C(coords: Coordinates) {
return {
Expand Down
6 changes: 3 additions & 3 deletions docs/en/basic/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ This architecture can create highly modular apps and nice DX. Here we provide yo
:::tip
All the types shown below are designed to be flexible, they accept:

- a single constructor / instance
- an array of either module constructors / instances
- nothing
- an array of module constructor types
- an array of module instance types
- nothing (which means no module loaded)

as their type parameters

Expand Down
19 changes: 10 additions & 9 deletions docs/en/basic/use-pointeract.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Everything Pointeract does is based on this class. It serves as an orchestrator

| Description | Method |
| --------------------------------------- | ------------------------------------------------------------ |
| [Instantiate the Class](#instantiation) | `new Pointeract(element, modules, options?)` |
| [Instantiate the Class](#instantiation) | `new Pointeract(options, modules?)` |
| [Start Base Class](#start-and-stop) | `pointeract.start()` |
| [Stop Base Class](#start-and-stop) | `pointeract.stop()` |
| [Start Modules](#modules) | `pointeract.start(ModuleConstructor \| ModuleConstructor[])` |
Expand Down Expand Up @@ -63,7 +63,7 @@ const pointeract = new Pointeract(options, [Drag, PreventDefault]);
```

::: info
Pointeract uses TypeScript generics to smartly infer the types of options, events, and class methods available by scanning every module passed into the constructor.
Powered by [SynthKernel](https://hesprs.github.io/researches/synthkernel), Pointeract uses TypeScript generics to smartly infer the types of options, events, and class methods available by scanning every module passed into the constructor.
:::

### Start and Stop
Expand Down Expand Up @@ -109,10 +109,9 @@ All modules are instantiated during the construction of `Pointeract` and dispose

To turn on/off modules at runtime, also use `start()` and `stop()`. For these two methods, if you do not pass any arguments, they will start/stop the Pointeract instance; otherwise the specified modules. All modules passed in are enabled by default at Pointeract construction.

You start/stop modules by passing in the **constructors** of modules, the methods accept a single module or an array of modules:
You start/stop modules by passing in the **constructors** of modules, the methods accept an array of modules:

```TypeScript
pointeract.start(PreventDefault); // single module
pointeract.stop([PreventDefault, Drag]); // multiple modules
```

Expand All @@ -122,9 +121,9 @@ Note that the start/stop of modules are independent to the start/stop of the bas
```TypeScript
// we have modules PreventDefault and Drag
pointeract.stop(); // everything is paused
pointeract.stop(PreventDefault); // no apparent change, but PreventDefault is disabled at module level
pointeract.stop([PreventDefault]); // no apparent change, but PreventDefault is disabled at module level
pointeract.start(); // only the base class and Drag are started
pointeract.start(PreventDefault); // PreventDefault will not be restarted unless explicitly reenabled here
pointeract.start([PreventDefault]); // PreventDefault will not be restarted unless explicitly reenabled here
```

:::
Expand Down Expand Up @@ -166,9 +165,10 @@ Options are defined as an object and passed as the third argument of a Pointerac

```TypeScript
const options = {
element: app,
coordinateOutput: 'absolute',
}
const pointeract = new Pointeract(app, [Drag, PreventDefault], options);
const pointeract = new Pointeract(options, [Drag, PreventDefault]);
```

### Base Options
Expand Down Expand Up @@ -198,10 +198,11 @@ Pointeract uses the same `options` reference passed in the constructor, you can
import { Pointeract, WheelPanZoom, Options } from 'pointeract';

const options: Options<WheelPanZoom> = {
element: document.body
coordinateOutput: 'absolute', // output absolute coordinates
}

const pointeract = new Pointeract(document.body, WheelPanZoom, options);
const pointeract = new Pointeract(options, [WheelPanZoom]);

options.coordinateOutput = 'relative'; // output format instantly changes to relative
```
Expand Down Expand Up @@ -238,7 +239,7 @@ pointeract.stop();
pointeract.start();

// Disable PreventDefault only
pointeract.stop(PreventDefault);
pointeract.stop([PreventDefault]);

// Dispose
pointeract.dispose();
Expand Down
6 changes: 3 additions & 3 deletions docs/en/development/custom-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ class BaseModule {
- `onPointerMove`: Triggered when a pointer is moved (same to `pointermove` event).
- `onPointerUp`: Triggered when a pointer is released (same to `pointerup` event).
- `onWheel`: Triggered when a wheel event is triggered (same to `wheel` event).
- `onStart`: Triggered when the module is started or after construction (`pointeract.start(MyModule)`).
- `onStop`: Triggered when the module is stopped or before disposal (`pointeract.stop(MyModule)`).
- `onStart`: Triggered when the module is started or after construction (`pointeract.start([MyModule])`).
- `onStop`: Triggered when the module is stopped or before disposal (`pointeract.stop([MyModule])`).
- `dispose`: Triggered when the module and the Pointeract instance is disposed (`pointeract.dispose()`).

::: tip
Expand Down Expand Up @@ -217,7 +217,7 @@ Then when you instantiate the `Pointeract` class with your module loaded, you ca
import { Pointeract } from 'pointeract';
import YourModule from './your-module';

const instance = new Pointeract({ element: app }, YourModule);
const instance = new Pointeract({ element: app }, [YourModule]);
instance.log();

// Result:
Expand Down
14 changes: 7 additions & 7 deletions docs/en/development/testing.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# Testing

Any professional library should have a test suite, Pointeract is no different.

[![Test Coverage](https://img.shields.io/codecov/c/github/hesprs/pointeract/main?style=flat&logo=codecov&logoColor=white&label=Test%20Coverage&labelColor=ff0077&color=333333)](https://codecov.io/github/hesprs/pointeract)

Above is the current test coverage.
Go to [Codecov](https://codecov.io/github/hesprs/pointeract) to see the current test coverage.

## Techstack

Expand All @@ -13,7 +9,7 @@ Above is the current test coverage.

## Standards

Pointeract should obey the test requirements as follows:
Pointeract obeys the test requirements as follows:

- When developing a new module, it is mandatory to write a unit test unless it's untestable.
- The overall test coverage should be higher than 90%.
Expand All @@ -28,4 +24,8 @@ The interaction denoted by the code is visualized as follows:

![Monkey Test](/monkeyTest.svg)

The aim of this test is to simulate chaotic multitouch drag, pan and zoom intends to ensure `drag` and `multitouchPanZoom` modules can survive extreme conditions. Pointeract coped it well. But when the similar test (manual human test) is conducted in other libraries like `Hammer.js` or `Interact.js`, [they failed](/whats-pointeract#how-pointeract-stands-out).
The aim of this test is to simulate chaotic multitouch drag, pan and zoom intends to ensure `drag` and `multitouchPanZoom` modules can survive extreme conditions. Pointeract handled it decently.

But when the similar manual human test is conducted in the website demos of `Hammer.js` and `Interact.js`, [they failed for different symptoms](/whats-pointeract#how-pointeract-stands-out).

This test demonstrates how `Pointeract` can handle extreme conditions with the least amount of code.
10 changes: 9 additions & 1 deletion docs/en/get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ pnpm add pointeract
yarn add pointeract
```

```sh [bun]
bun add pointeract
```

```sh [deno]
deno add jsr:@hesprs/pointeract
```

:::

Or include the following lines directly in your HTML file:
Expand All @@ -43,7 +51,7 @@ Simply grab the core class and a module:
```TypeScript
import { Pointeract, Drag } from 'pointeract';

new Pointeract({ element: yourElement }, Drag)
new Pointeract({ element: yourElement }, [Drag])
.start()
.on('drag', e => console.log(e));
```
Expand Down
15 changes: 10 additions & 5 deletions docs/en/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,21 @@ hero:
link: /playground

features:
- title: Modern
- title: Powered by SynthKernel
icon: 🧪
details: Written in TypeScript, succinct in size, wide compatibility.
details: 100% modular with precise typing and excellent extensibility.
link: https://hesprs.github.io/researches/synthkernel
linkText: Learn more about SynthKernel
- title: Scale from 1KB
icon: 🐣
link: /modules
details: Grab what you need via modules, the base class is only 1KB gzipped.
- title: Extensible
- title: Runtime-Flexible
icon: 🧩
details: Write your own modules and plug into Pointeract via its module API.
details: Start/stop the detector and any module at any time, update options reactively.
- title: Robust
icon: 🦾
details: Full touchpad support, excels at complex gestures where most interaction libraries fail.
details: Excels at complex gestures where most contenders fail.
link: /development/testing
linkText: See testing
---
2 changes: 1 addition & 1 deletion docs/en/modules/click.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The click module in checks whether the mouse/touch has actually moved during dow

```TypeScript
import { Click, Pointeract } from 'pointeract';
const pointeract = new Pointeract({ element: app }, Click);
const pointeract = new Pointeract({ element: app }, [Click]);
```

## Options
Expand Down
2 changes: 1 addition & 1 deletion docs/en/modules/drag.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ This module handles drag interactions, events are dispatched when a single touch

```TypeScript
import { Drag, Pointeract } from 'pointeract';
const pointeract = new Pointeract({ element: app }, Drag);
const pointeract = new Pointeract({ element: app }, [Drag]);
```
8 changes: 5 additions & 3 deletions docs/en/modules/lubricator.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ Lubricator requires a granular configuration of what and how to interpolate an e
```TypeScript
interface Options extends BaseOptions {
lubricator?: {
// this is the configuration for each event
[Key: string]?: {
decayFactor: number;
fields: {
// this is the configuration for each field in the emitted event
[Key: string]?: {
countType: 'sum' | 'product';
diminishBoundary: number;
Expand All @@ -42,7 +44,7 @@ In **per-event** configuration:

`decayFactor`: controls how fast it interpolates, i.e., how the smoothified event lags behind the real-time interaction. The lower this value is, the more smooth interactions are.

`fields`: the name of fields in the event to interpolate, the fields must be the type `number`, you can find the fields of events in the [event types](/basic/types#events). The values of the items in `fields` are configurations per-field.
`fields`: the names of fields in the event to interpolate, the fields must be the type `number`, you can find the fields of events in the [event types](/basic/types#events). The values of the items in `fields` are configurations per-field.

In **per-field** configuration:

Expand All @@ -53,7 +55,7 @@ In **per-field** configuration:

`diminishBoundary`: defines the threshold for the final dispatch.

- interpolation is infinite if you don't manually define a boundary. If the difference between the real dispatch and raw is smaller than this boundary, the interpolation will stop and lubricator will dispatch a final event to make the aggregate interpolation equals the raw.
- interpolation is infinite if you don't manually define a boundary. If the difference between the real dispatch and raw is smaller than this boundary, the interpolation will stop and lubricator will dispatch a final event to make the aggregate interpolation equal the raw.

In the following example, we will smoothify `zoom` event:

Expand All @@ -76,7 +78,7 @@ new Pointeract({
}, [WheelPanZoom, Lubricator])
```

In this example, we've configured Lubricator to intercept event `zoom`, interpolate the `factor` field in it with product goal and stops when the aggregate interpolation is less than 1% from total product.
In this example, we've configured Lubricator to intercept event `zoom`, interpolate the `factor` field in it with product goal and stops when the leftover zoom factor is less than 0.01.

## Presets

Expand Down
2 changes: 1 addition & 1 deletion docs/en/modules/multitouch-pan-zoom.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ This module monitors touches on the screen.

```TypeScript
import { MultitouchPanZoom, Pointeract } from 'pointeract';
const pointeract = new Pointeract({ element: app }, MultitouchPanZoom);
const pointeract = new Pointeract({ element: app }, [MultitouchPanZoom]);
```
2 changes: 1 addition & 1 deletion docs/en/modules/prevent-default.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ This module disables all default browser behaviors related to touch / mouse / wh

```TypeScript
import { PreventDefault, Pointeract } from 'pointeract';
const pointeract = new Pointeract({ element: app }, PreventDefault);
const pointeract = new Pointeract({ element: app }, [PreventDefault]);
```
2 changes: 1 addition & 1 deletion docs/en/modules/wheel-pan-zoom.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Most devices with touchpad interpret touchpad gestures as mouse wheel + key pres

```TypeScript
import { WheelPanZoom, Pointeract } from 'pointeract';
const pointeract = new Pointeract({ element: app }, WheelPanZoom);
const pointeract = new Pointeract({ element: app }, [WheelPanZoom]);
```

## Options
Expand Down
21 changes: 21 additions & 0 deletions jsr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "pointeract",
"version": "1.1.2",
"description": "A 3KB, tree-shakable, TypeScript-native alternative to Hammer.js for robust pan/zoom gestures — runtime-flexible, extensible, and safe.",
"keywords": ["lightweight", "frontend", "gesture-detection", "pan-zoom", "typescript"],
"homepage": "https://pointeract.consensia.cc",
"bugs": {
"url": "https://github.com/hesprs/pointeract/issues"
},
"license": "MIT",
"author": {
"name": "Hēsperus",
"email": "hesprs@outlook.com"
},
"repository": {
"type": "git",
"url": "git+https://github.com/hesprs/pointeract.git"
},
"exports": "./src/index.ts",
"sideEffects": false
}
Loading