@prose-reader/enhancer-gestures 1.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENCE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2021 @prose-reader/core
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ import { SettingsManager } from '@prose-reader/core';
2
+ import { InputSettings, OutputSettings } from './types';
3
+ export declare class GesturesSettingsManager extends SettingsManager<InputSettings, OutputSettings> {
4
+ getOutputSettings(inputSettings: InputSettings): OutputSettings;
5
+ getDefaultSettings(): InputSettings;
6
+ }
@@ -0,0 +1,12 @@
1
+ import { HookManager, Reader } from '@prose-reader/core';
2
+ import { Subject } from 'rxjs';
3
+ import { GestureEvent, Hook } from '../types';
4
+ import { GesturesSettingsManager } from '../SettingsManager';
5
+ import { PanRecognizer } from 'gesturx';
6
+ export declare const registerPan: ({ reader, recognizer, settingsManager, }: {
7
+ recognizer: PanRecognizer;
8
+ reader: Reader;
9
+ hookManager: HookManager<Hook>;
10
+ unhandledEvent$: Subject<GestureEvent>;
11
+ settingsManager: GesturesSettingsManager;
12
+ }) => import('rxjs').Observable<import('gesturx').PanEvent>;
@@ -0,0 +1,12 @@
1
+ import { HookManager, Reader } from '@prose-reader/core';
2
+ import { Subject } from 'rxjs';
3
+ import { GestureEvent, GestureRecognizable, Hook } from '../types';
4
+ import { GesturesSettingsManager } from '../SettingsManager';
5
+ import { PinchEvent } from 'gesturx';
6
+ export declare const registerPinch: ({ reader, recognizable, settingsManager, }: {
7
+ recognizable: GestureRecognizable;
8
+ reader: Reader;
9
+ hookManager: HookManager<Hook>;
10
+ unhandledEvent$: Subject<GestureEvent>;
11
+ settingsManager: GesturesSettingsManager;
12
+ }) => import('rxjs').Observable<PinchEvent>;
@@ -0,0 +1,11 @@
1
+ import { HookManager, Reader } from '@prose-reader/core';
2
+ import { Subject } from 'rxjs';
3
+ import { GestureEvent, GestureRecognizable, Hook } from '../types';
4
+ import { GesturesSettingsManager } from '../SettingsManager';
5
+ export declare const registerSwipe: ({ reader, recognizable, settingsManager, }: {
6
+ recognizable: GestureRecognizable;
7
+ reader: Reader;
8
+ hookManager: HookManager<Hook>;
9
+ unhandledEvent$: Subject<GestureEvent>;
10
+ settingsManager: GesturesSettingsManager;
11
+ }) => import('rxjs').Observable<import('gesturx').SwipeEvent>;
@@ -0,0 +1,11 @@
1
+ import { HookManager, Reader } from '@prose-reader/core';
2
+ import { Subject } from 'rxjs';
3
+ import { GestureEvent, GestureRecognizable, Hook } from '../types';
4
+ import { GesturesSettingsManager } from '../SettingsManager';
5
+ export declare const registerTaps: ({ reader, recognizable, unhandledEvent$, hookManager, }: {
6
+ recognizable: GestureRecognizable;
7
+ reader: Reader;
8
+ hookManager: HookManager<Hook>;
9
+ unhandledEvent$: Subject<GestureEvent>;
10
+ settingsManager: GesturesSettingsManager;
11
+ }) => import('rxjs').Observable<import('gesturx').TapEvent | import('gesturx').PanEvent | import('gesturx').SwipeEvent | import('gesturx').PinchEvent>;
@@ -0,0 +1,6 @@
1
+ import { Reader } from '@prose-reader/core';
2
+ import { PanRecognizer } from 'gesturx';
3
+ export declare const registerZoomPan: ({ reader, recognizer }: {
4
+ recognizer: PanRecognizer;
5
+ reader: Reader;
6
+ }) => import('rxjs').Observable<import('gesturx').PanEvent>;
@@ -0,0 +1,5 @@
1
+ import { Reader } from '@prose-reader/core';
2
+ import { EnhancerAPI, InputSettings } from './types';
3
+ export declare const gesturesEnhancer: <InheritOptions, InheritOutput extends Reader>(next: (options: InheritOptions) => InheritOutput) => (options: InheritOptions & {
4
+ gestures?: Partial<InputSettings>;
5
+ }) => InheritOutput & EnhancerAPI;
package/dist/index.js ADDED
@@ -0,0 +1,314 @@
1
+ import { isHtmlElement, SettingsManager, HookManager } from "@prose-reader/core";
2
+ import { filter, tap, switchMap, EMPTY, withLatestFrom, merge, throttleTime, animationFrameScheduler, takeUntil, Subject, combineLatest } from "rxjs";
3
+ import { PinchRecognizer, PanRecognizer, TapRecognizer, SwipeRecognizer, Recognizable } from "gesturx";
4
+ const filterNotLink = (stream) => stream.pipe(
5
+ filter((event) => {
6
+ const target = event.event.target;
7
+ if (isHtmlElement(target) && target.tagName === "a") return false;
8
+ return true;
9
+ })
10
+ );
11
+ const registerTaps = ({
12
+ reader,
13
+ recognizable,
14
+ unhandledEvent$,
15
+ hookManager
16
+ }) => {
17
+ const gestures$ = recognizable.events$.pipe(
18
+ filterNotLink,
19
+ tap((event) => {
20
+ const normalizedEvent = event.event;
21
+ const { computedPageTurnDirection } = reader.settings.values;
22
+ if (event.type === "tap") {
23
+ const width = window.innerWidth;
24
+ const height = window.innerHeight;
25
+ const pageTurnMargin = 0.15;
26
+ if (`x` in normalizedEvent) {
27
+ const { x = 0, y } = normalizedEvent;
28
+ const beforeTapResults = hookManager.execute("beforeTap", void 0, { event });
29
+ if (beforeTapResults.some((result) => result === false)) {
30
+ return;
31
+ }
32
+ const isTopArea = y < height * pageTurnMargin;
33
+ const isBottomArea = y > height * (1 - pageTurnMargin);
34
+ const isLeftArea = x < width * pageTurnMargin;
35
+ const isRightArea = x > width * (1 - pageTurnMargin);
36
+ if (isLeftArea && computedPageTurnDirection === "horizontal") {
37
+ reader.navigation.turnLeftOrTop();
38
+ } else if (isTopArea && computedPageTurnDirection === "vertical") {
39
+ reader.navigation.turnLeftOrTop();
40
+ } else if (isBottomArea && computedPageTurnDirection === "vertical") {
41
+ reader.navigation.turnRightOrBottom();
42
+ } else if (isRightArea && computedPageTurnDirection === "horizontal") {
43
+ reader.navigation.turnRightOrBottom();
44
+ } else {
45
+ unhandledEvent$.next(event);
46
+ }
47
+ }
48
+ }
49
+ })
50
+ );
51
+ return gestures$;
52
+ };
53
+ const DELAY_IGNORE_PAN = 400;
54
+ const registerPan = ({
55
+ reader,
56
+ recognizer,
57
+ settingsManager
58
+ }) => {
59
+ const gestures$ = settingsManager.values$.pipe(
60
+ switchMap(({ panNavigation }) => {
61
+ if (panNavigation !== "pan") return EMPTY;
62
+ return recognizer.events$.pipe(
63
+ tap((event) => {
64
+ if (reader.zoom.isZooming) return;
65
+ if (event.type === `panStart`) {
66
+ if (event.delay > DELAY_IGNORE_PAN) return;
67
+ reader == null ? void 0 : reader.navigation.moveTo({ x: 0, y: 0 }, { start: true });
68
+ }
69
+ if (event.type === `panMove`) {
70
+ reader == null ? void 0 : reader.navigation.moveTo({ x: event.deltaX, y: event.deltaY });
71
+ }
72
+ if (event.type === `panEnd`) {
73
+ reader == null ? void 0 : reader.navigation.moveTo({ x: event.deltaX, y: event.deltaY }, { final: true });
74
+ }
75
+ })
76
+ );
77
+ })
78
+ );
79
+ return gestures$;
80
+ };
81
+ const registerSwipe = ({
82
+ reader,
83
+ recognizable,
84
+ settingsManager
85
+ }) => {
86
+ const gestures$ = settingsManager.values$.pipe(
87
+ switchMap(
88
+ ({ panNavigation }) => panNavigation !== "swipe" ? EMPTY : recognizable.events$.pipe(
89
+ filter((event) => event.type === "swipe"),
90
+ tap((event) => {
91
+ const { computedPageTurnDirection } = reader.settings.values;
92
+ if (computedPageTurnDirection === "vertical") {
93
+ if (event.velocityY < -0.5) {
94
+ reader == null ? void 0 : reader.navigation.turnRight();
95
+ }
96
+ if (event.velocityY > 0.5) {
97
+ reader == null ? void 0 : reader.navigation.turnLeft();
98
+ }
99
+ } else {
100
+ if (event.velocityX < -0.5) {
101
+ reader == null ? void 0 : reader.navigation.turnRight();
102
+ }
103
+ if (event.velocityX > 0.5) {
104
+ reader == null ? void 0 : reader.navigation.turnLeft();
105
+ }
106
+ }
107
+ })
108
+ )
109
+ )
110
+ );
111
+ return gestures$;
112
+ };
113
+ class GesturesSettingsManager extends SettingsManager {
114
+ getOutputSettings(inputSettings) {
115
+ return inputSettings;
116
+ }
117
+ getDefaultSettings() {
118
+ return {
119
+ panNavigation: "pan",
120
+ fontScalePinchEnabled: false,
121
+ fontScalePinchThrottleTime: 500,
122
+ pinchCancelPan: true
123
+ };
124
+ }
125
+ }
126
+ const isHtmlImageElement = (target) => isHtmlElement(target) && !!target.ownerDocument.defaultView && target instanceof target.ownerDocument.defaultView.HTMLImageElement;
127
+ const registerPinch = ({
128
+ reader,
129
+ recognizable,
130
+ settingsManager
131
+ }) => {
132
+ const pinchStart$ = recognizable.events$.pipe(filter((event) => event.type === "pinchStart"));
133
+ const pinchMove$ = recognizable.events$.pipe(filter((event) => event.type === "pinchMove"));
134
+ const pinchEnd$ = recognizable.events$.pipe(filter((event) => event.type === "pinchEnd"));
135
+ const shouldStartZoom = (target) => isHtmlImageElement(target) && !reader.zoom.isZooming;
136
+ return settingsManager.values$.pipe(
137
+ switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {
138
+ const zoomGestures$ = pinchStart$.pipe(
139
+ withLatestFrom(reader.viewportState$),
140
+ switchMap(([event, viewportState]) => {
141
+ const target = event.event.target;
142
+ const startScale = reader.zoom.currentScale;
143
+ if (viewportState === "busy") return EMPTY;
144
+ if (shouldStartZoom(target)) {
145
+ reader.zoom.enter(target);
146
+ }
147
+ if (!reader.zoom.isZooming) return EMPTY;
148
+ return merge(
149
+ pinchMove$.pipe(
150
+ tap((event2) => {
151
+ if (reader.zoom.isZooming) {
152
+ const newScale = startScale + (event2.scale - 1);
153
+ if (newScale < 1) {
154
+ reader.zoom.exit();
155
+ } else {
156
+ reader.zoom.scaleAt(newScale);
157
+ }
158
+ }
159
+ })
160
+ )
161
+ );
162
+ })
163
+ );
164
+ const watchForFontScaleChange$ = !fontScalePinchEnabled ? EMPTY : pinchStart$.pipe(
165
+ withLatestFrom(reader.viewportState$),
166
+ switchMap(([pinchStartEvent, viewportState]) => {
167
+ if (viewportState === "busy" || shouldStartZoom(pinchStartEvent.event.target) || reader.zoom.isZooming)
168
+ return EMPTY;
169
+ const lastFontScaleOnPinchStart = reader.settings.values.fontScale;
170
+ return pinchMove$.pipe(
171
+ throttleTime(fontScalePinchThrottleTime, animationFrameScheduler, {
172
+ trailing: true
173
+ }),
174
+ tap((event) => {
175
+ reader.settings.update({
176
+ fontScale: parseFloat((lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2))
177
+ });
178
+ }),
179
+ takeUntil(pinchEnd$)
180
+ );
181
+ })
182
+ );
183
+ return merge(zoomGestures$, watchForFontScaleChange$);
184
+ })
185
+ );
186
+ };
187
+ const registerZoomPan = ({ reader, recognizer }) => {
188
+ const panStart$ = recognizer.events$.pipe(filter((event) => event.type === "panStart"));
189
+ const panMove$ = recognizer.events$.pipe(filter((event) => event.type === "panMove"));
190
+ const zoomingPan$ = panStart$.pipe(
191
+ switchMap(() => {
192
+ const startPosition = reader.zoom.currentPosition;
193
+ return panMove$.pipe(
194
+ tap((panMoveEvent) => {
195
+ if (reader.zoom.isZooming) {
196
+ reader.zoom.moveAt({ x: startPosition.x + panMoveEvent.deltaX, y: startPosition.y + panMoveEvent.deltaY });
197
+ }
198
+ })
199
+ );
200
+ })
201
+ );
202
+ return zoomingPan$;
203
+ };
204
+ const gesturesEnhancer = (next) => (options) => {
205
+ const { gestures = {}, ...rest } = options;
206
+ const reader = next(rest);
207
+ const settingsManager = new GesturesSettingsManager(gestures);
208
+ const hookManager = new HookManager();
209
+ const pinchRecognizer = new PinchRecognizer({
210
+ options: {
211
+ /**
212
+ * @important
213
+ * To be less than pan otherwise it will not fail before it starts
214
+ */
215
+ posThreshold: 20
216
+ }
217
+ });
218
+ const panRecognizer = new PanRecognizer({
219
+ failWith: [pinchRecognizer],
220
+ options: {
221
+ // we want to have some margin to trigger zoom
222
+ posThreshold: 30
223
+ }
224
+ });
225
+ const zoomPanRecognizer = new PanRecognizer({
226
+ options: {
227
+ posThreshold: 1
228
+ }
229
+ });
230
+ const tapRecognizer = new TapRecognizer({
231
+ failWith: [panRecognizer]
232
+ });
233
+ const swipeRecognizer = new SwipeRecognizer();
234
+ const recognizable = new Recognizable({
235
+ recognizers: [tapRecognizer, panRecognizer, swipeRecognizer, pinchRecognizer, zoomPanRecognizer]
236
+ });
237
+ const unhandledEvent$ = new Subject();
238
+ const tapGestures$ = registerTaps({
239
+ hookManager,
240
+ reader,
241
+ recognizable,
242
+ unhandledEvent$,
243
+ settingsManager
244
+ });
245
+ const panGestures$ = registerPan({
246
+ hookManager,
247
+ reader,
248
+ recognizer: panRecognizer,
249
+ unhandledEvent$,
250
+ settingsManager
251
+ });
252
+ const swipeGestures$ = registerSwipe({
253
+ hookManager,
254
+ reader,
255
+ recognizable,
256
+ unhandledEvent$,
257
+ settingsManager
258
+ });
259
+ const pinchGestures$ = registerPinch({
260
+ hookManager,
261
+ reader,
262
+ recognizable,
263
+ settingsManager,
264
+ unhandledEvent$
265
+ });
266
+ const zoomPanGestures$ = registerZoomPan({
267
+ reader,
268
+ recognizer: zoomPanRecognizer
269
+ });
270
+ const containerUpdate$ = reader.context.containerElement$.pipe(
271
+ tap((container) => {
272
+ recognizable.update({
273
+ container
274
+ });
275
+ })
276
+ );
277
+ const watchSettings$ = combineLatest([settingsManager.values$, panRecognizer.config$]).pipe(
278
+ tap(([{ pinchCancelPan }, panRecognizerConfig]) => {
279
+ var _a, _b;
280
+ const pinchAlreadyInFailWith = (_a = panRecognizerConfig.failWith) == null ? void 0 : _a.includes(pinchRecognizer);
281
+ if (pinchCancelPan && !pinchAlreadyInFailWith) {
282
+ panRecognizer.update({
283
+ failWith: [...panRecognizerConfig.failWith ?? [], pinchRecognizer]
284
+ });
285
+ }
286
+ if (!pinchCancelPan && pinchAlreadyInFailWith) {
287
+ panRecognizer.update({
288
+ failWith: (_b = panRecognizerConfig.failWith) == null ? void 0 : _b.filter((recognizer) => recognizer !== pinchRecognizer)
289
+ });
290
+ }
291
+ })
292
+ );
293
+ merge(
294
+ containerUpdate$,
295
+ watchSettings$,
296
+ zoomPanGestures$,
297
+ pinchGestures$,
298
+ tapGestures$,
299
+ swipeGestures$,
300
+ panGestures$
301
+ ).pipe(takeUntil(reader.$.destroy$)).subscribe();
302
+ return {
303
+ ...reader,
304
+ gestures: {
305
+ settings: settingsManager,
306
+ unhandledEvent$: unhandledEvent$.asObservable(),
307
+ hookManager
308
+ }
309
+ };
310
+ };
311
+ export {
312
+ gesturesEnhancer
313
+ };
314
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/utils.ts","../src/gestures/taps.ts","../src/gestures/pan.ts","../src/gestures/swipe.ts","../src/SettingsManager.ts","../src/gestures/pinch.ts","../src/gestures/zoomPan.ts","../src/index.ts"],"sourcesContent":["import { isHtmlElement } from \"@prose-reader/core\"\nimport { filter, Observable } from \"rxjs\"\n\nexport const filterNotLink = <Event extends { event: PointerEvent }>(stream: Observable<Event>) =>\n stream.pipe(\n filter((event) => {\n const target = event.event.target\n\n if (isHtmlElement(target) && target.tagName === \"a\") return false\n\n return true\n }),\n )\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { Subject, tap } from \"rxjs\"\nimport { GestureEvent, GestureRecognizable, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\nimport { filterNotLink } from \"../utils\"\n\nexport const registerTaps = ({\n reader,\n recognizable,\n unhandledEvent$,\n hookManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = recognizable.events$.pipe(\n filterNotLink,\n tap((event) => {\n const normalizedEvent = event.event\n const { computedPageTurnDirection } = reader.settings.values\n\n if (event.type === \"tap\") {\n const width = window.innerWidth\n const height = window.innerHeight\n const pageTurnMargin = 0.15\n\n if (`x` in normalizedEvent) {\n const { x = 0, y } = normalizedEvent\n\n const beforeTapResults = hookManager.execute(\"beforeTap\", undefined, { event })\n\n if (beforeTapResults.some((result) => result === false)) {\n return\n }\n\n const isTopArea = y < height * pageTurnMargin\n const isBottomArea = y > height * (1 - pageTurnMargin)\n const isLeftArea = x < width * pageTurnMargin\n const isRightArea = x > width * (1 - pageTurnMargin)\n\n if (isLeftArea && computedPageTurnDirection === \"horizontal\") {\n reader.navigation.turnLeftOrTop()\n } else if (isTopArea && computedPageTurnDirection === \"vertical\") {\n reader.navigation.turnLeftOrTop()\n } else if (isBottomArea && computedPageTurnDirection === \"vertical\") {\n reader.navigation.turnRightOrBottom()\n } else if (isRightArea && computedPageTurnDirection === \"horizontal\") {\n reader.navigation.turnRightOrBottom()\n } else {\n unhandledEvent$.next(event)\n }\n }\n }\n }),\n )\n\n return gestures$\n}\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { EMPTY, Subject, switchMap, tap } from \"rxjs\"\nimport { GestureEvent, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\nimport { PanRecognizer } from \"gesturx\"\n\nconst DELAY_IGNORE_PAN = 400\n\nexport const registerPan = ({\n reader,\n recognizer,\n settingsManager,\n}: {\n recognizer: PanRecognizer\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) => {\n if (panNavigation !== \"pan\") return EMPTY\n\n return recognizer.events$.pipe(\n tap((event) => {\n if (reader.zoom.isZooming) return\n\n if (event.type === `panStart`) {\n /**\n * We let the user select\n */\n if (event.delay > DELAY_IGNORE_PAN) return\n\n reader?.navigation.moveTo({ x: 0, y: 0 }, { start: true })\n }\n\n if (event.type === `panMove`) {\n reader?.navigation.moveTo({ x: event.deltaX, y: event.deltaY })\n }\n\n if (event.type === `panEnd`) {\n reader?.navigation.moveTo({ x: event.deltaX, y: event.deltaY }, { final: true })\n }\n }),\n )\n }),\n )\n\n return gestures$\n}\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { EMPTY, Subject, filter, switchMap, tap } from \"rxjs\"\nimport { GestureEvent, GestureRecognizable, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\n\nexport const registerSwipe = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) =>\n panNavigation !== \"swipe\"\n ? EMPTY\n : recognizable.events$.pipe(\n filter((event) => event.type === \"swipe\"),\n tap((event) => {\n const { computedPageTurnDirection } = reader.settings.values\n\n if (computedPageTurnDirection === \"vertical\") {\n if (event.velocityY < -0.5) {\n reader?.navigation.turnRight()\n }\n if (event.velocityY > 0.5) {\n reader?.navigation.turnLeft()\n }\n } else {\n if (event.velocityX < -0.5) {\n reader?.navigation.turnRight()\n }\n if (event.velocityX > 0.5) {\n reader?.navigation.turnLeft()\n }\n }\n }),\n ),\n ),\n )\n\n return gestures$\n}\n","import { SettingsManager } from \"@prose-reader/core\"\nimport { InputSettings, OutputSettings } from \"./types\"\n\nexport class GesturesSettingsManager extends SettingsManager<InputSettings, OutputSettings> {\n getOutputSettings(inputSettings: InputSettings): OutputSettings {\n return inputSettings\n }\n\n getDefaultSettings(): InputSettings {\n return {\n panNavigation: \"pan\",\n fontScalePinchEnabled: false,\n fontScalePinchThrottleTime: 500,\n pinchCancelPan: true,\n }\n }\n}\n","import { HookManager, isHtmlElement, Reader } from \"@prose-reader/core\"\nimport {\n EMPTY,\n Subject,\n animationFrameScheduler,\n filter,\n merge,\n switchMap,\n takeUntil,\n tap,\n throttleTime,\n withLatestFrom,\n} from \"rxjs\"\nimport { GestureEvent, GestureRecognizable, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\nimport { PinchEvent } from \"gesturx\"\n\nconst isHtmlImageElement = (target: EventTarget | null): target is HTMLImageElement =>\n isHtmlElement(target) &&\n !!target.ownerDocument.defaultView &&\n target instanceof target.ownerDocument.defaultView.HTMLImageElement\n\nexport const registerPinch = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const pinchStart$ = recognizable.events$.pipe(filter((event): event is PinchEvent => event.type === \"pinchStart\"))\n\n const pinchMove$ = recognizable.events$.pipe(filter((event): event is PinchEvent => event.type === \"pinchMove\"))\n\n const pinchEnd$ = recognizable.events$.pipe(filter((event): event is PinchEvent => event.type === \"pinchEnd\"))\n\n const shouldStartZoom = (target: EventTarget | null): target is HTMLImageElement =>\n isHtmlImageElement(target) && !reader.zoom.isZooming\n\n return settingsManager.values$.pipe(\n switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {\n const zoomGestures$ = pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([event, viewportState]) => {\n const target = event.event.target\n const startScale = reader.zoom.currentScale\n\n if (viewportState === \"busy\") return EMPTY\n\n if (shouldStartZoom(target)) {\n reader.zoom.enter(target)\n }\n\n if (!reader.zoom.isZooming) return EMPTY\n\n return merge(\n pinchMove$.pipe(\n tap((event) => {\n if (reader.zoom.isZooming) {\n const newScale = startScale + (event.scale - 1)\n\n if (newScale < 1) {\n reader.zoom.exit()\n } else {\n reader.zoom.scaleAt(newScale)\n }\n }\n }),\n ),\n )\n }),\n )\n\n const watchForFontScaleChange$ = !fontScalePinchEnabled\n ? EMPTY\n : pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([pinchStartEvent, viewportState]) => {\n if (viewportState === \"busy\" || shouldStartZoom(pinchStartEvent.event.target) || reader.zoom.isZooming)\n return EMPTY\n\n const lastFontScaleOnPinchStart = reader.settings.values.fontScale\n\n return pinchMove$.pipe(\n throttleTime(fontScalePinchThrottleTime, animationFrameScheduler, {\n trailing: true,\n }),\n tap((event) => {\n reader.settings.update({\n fontScale: parseFloat((lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2)),\n })\n }),\n takeUntil(pinchEnd$),\n )\n }),\n )\n\n return merge(zoomGestures$, watchForFontScaleChange$)\n }),\n )\n}\n","import { Reader } from \"@prose-reader/core\"\nimport { PanRecognizer } from \"gesturx\"\nimport { filter, switchMap, tap } from \"rxjs\"\n\nexport const registerZoomPan = ({ reader, recognizer }: { recognizer: PanRecognizer; reader: Reader }) => {\n const panStart$ = recognizer.events$.pipe(filter((event) => event.type === \"panStart\"))\n const panMove$ = recognizer.events$.pipe(filter((event) => event.type === \"panMove\"))\n\n const zoomingPan$ = panStart$.pipe(\n switchMap(() => {\n const startPosition = reader.zoom.currentPosition\n\n return panMove$.pipe(\n tap((panMoveEvent) => {\n if (reader.zoom.isZooming) {\n reader.zoom.moveAt({ x: startPosition.x + panMoveEvent.deltaX, y: startPosition.y + panMoveEvent.deltaY })\n }\n }),\n )\n }),\n )\n\n return zoomingPan$\n}\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { ObservedValueOf, Subject, combineLatest, merge, takeUntil, tap } from \"rxjs\"\nimport { PanRecognizer, PinchRecognizer, Recognizable, SwipeRecognizer, TapRecognizer } from \"gesturx\"\nimport { EnhancerAPI, InputSettings, Hook } from \"./types\"\nimport { registerTaps } from \"./gestures/taps\"\nimport { registerPan } from \"./gestures/pan\"\nimport { registerSwipe } from \"./gestures/swipe\"\nimport { GesturesSettingsManager } from \"./SettingsManager\"\nimport { registerPinch } from \"./gestures/pinch\"\nimport { registerZoomPan } from \"./gestures/zoomPan\"\n\nexport const gesturesEnhancer =\n <InheritOptions, InheritOutput extends Reader>(next: (options: InheritOptions) => InheritOutput) =>\n (\n options: InheritOptions & {\n gestures?: Partial<InputSettings>\n },\n ): InheritOutput & EnhancerAPI => {\n const { gestures = {}, ...rest } = options\n const reader = next(rest as InheritOptions)\n\n const settingsManager = new GesturesSettingsManager(gestures)\n\n const hookManager = new HookManager<Hook>()\n\n const pinchRecognizer = new PinchRecognizer({\n options: {\n /**\n * @important\n * To be less than pan otherwise it will not fail before it starts\n */\n posThreshold: 20,\n },\n })\n\n const panRecognizer = new PanRecognizer({\n failWith: [pinchRecognizer],\n options: {\n // we want to have some margin to trigger zoom\n posThreshold: 30,\n },\n })\n\n const zoomPanRecognizer = new PanRecognizer({\n options: {\n posThreshold: 1,\n },\n })\n\n const tapRecognizer = new TapRecognizer({\n failWith: [panRecognizer],\n })\n\n const swipeRecognizer = new SwipeRecognizer()\n\n const recognizable = new Recognizable({\n recognizers: [tapRecognizer, panRecognizer, swipeRecognizer, pinchRecognizer, zoomPanRecognizer],\n })\n\n const unhandledEvent$ = new Subject<ObservedValueOf<typeof recognizable.events$>>()\n\n const tapGestures$ = registerTaps({\n hookManager,\n reader,\n recognizable,\n unhandledEvent$,\n settingsManager,\n })\n\n const panGestures$ = registerPan({\n hookManager,\n reader,\n recognizer: panRecognizer,\n unhandledEvent$,\n settingsManager,\n })\n\n const swipeGestures$ = registerSwipe({\n hookManager,\n reader,\n recognizable,\n unhandledEvent$,\n settingsManager,\n })\n\n const pinchGestures$ = registerPinch({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n unhandledEvent$,\n })\n\n const zoomPanGestures$ = registerZoomPan({\n reader,\n recognizer: zoomPanRecognizer,\n })\n\n const containerUpdate$ = reader.context.containerElement$.pipe(\n tap((container) => {\n recognizable.update({\n container,\n })\n }),\n )\n\n const watchSettings$ = combineLatest([settingsManager.values$, panRecognizer.config$]).pipe(\n tap(([{ pinchCancelPan }, panRecognizerConfig]) => {\n const pinchAlreadyInFailWith = panRecognizerConfig.failWith?.includes(pinchRecognizer)\n\n if (pinchCancelPan && !pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: [...(panRecognizerConfig.failWith ?? []), pinchRecognizer],\n })\n }\n\n if (!pinchCancelPan && pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: panRecognizerConfig.failWith?.filter((recognizer) => recognizer !== pinchRecognizer),\n })\n }\n }),\n )\n\n merge(\n containerUpdate$,\n watchSettings$,\n zoomPanGestures$,\n pinchGestures$,\n tapGestures$,\n swipeGestures$,\n panGestures$,\n )\n .pipe(takeUntil(reader.$.destroy$))\n .subscribe()\n\n return {\n ...reader,\n gestures: {\n settings: settingsManager,\n unhandledEvent$: unhandledEvent$.asObservable(),\n hookManager,\n },\n }\n }\n"],"names":["event"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,315 @@
1
+ (function(global, factory) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@prose-reader/core"), require("rxjs"), require("gesturx")) : typeof define === "function" && define.amd ? define(["exports", "@prose-reader/core", "rxjs", "gesturx"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["prose-reader-enhancer-gestures"] = {}, global.core, global.rxjs, global.gesturx));
3
+ })(this, function(exports2, core, rxjs, gesturx) {
4
+ "use strict";
5
+ const filterNotLink = (stream) => stream.pipe(
6
+ rxjs.filter((event) => {
7
+ const target = event.event.target;
8
+ if (core.isHtmlElement(target) && target.tagName === "a") return false;
9
+ return true;
10
+ })
11
+ );
12
+ const registerTaps = ({
13
+ reader,
14
+ recognizable,
15
+ unhandledEvent$,
16
+ hookManager
17
+ }) => {
18
+ const gestures$ = recognizable.events$.pipe(
19
+ filterNotLink,
20
+ rxjs.tap((event) => {
21
+ const normalizedEvent = event.event;
22
+ const { computedPageTurnDirection } = reader.settings.values;
23
+ if (event.type === "tap") {
24
+ const width = window.innerWidth;
25
+ const height = window.innerHeight;
26
+ const pageTurnMargin = 0.15;
27
+ if (`x` in normalizedEvent) {
28
+ const { x = 0, y } = normalizedEvent;
29
+ const beforeTapResults = hookManager.execute("beforeTap", void 0, { event });
30
+ if (beforeTapResults.some((result) => result === false)) {
31
+ return;
32
+ }
33
+ const isTopArea = y < height * pageTurnMargin;
34
+ const isBottomArea = y > height * (1 - pageTurnMargin);
35
+ const isLeftArea = x < width * pageTurnMargin;
36
+ const isRightArea = x > width * (1 - pageTurnMargin);
37
+ if (isLeftArea && computedPageTurnDirection === "horizontal") {
38
+ reader.navigation.turnLeftOrTop();
39
+ } else if (isTopArea && computedPageTurnDirection === "vertical") {
40
+ reader.navigation.turnLeftOrTop();
41
+ } else if (isBottomArea && computedPageTurnDirection === "vertical") {
42
+ reader.navigation.turnRightOrBottom();
43
+ } else if (isRightArea && computedPageTurnDirection === "horizontal") {
44
+ reader.navigation.turnRightOrBottom();
45
+ } else {
46
+ unhandledEvent$.next(event);
47
+ }
48
+ }
49
+ }
50
+ })
51
+ );
52
+ return gestures$;
53
+ };
54
+ const DELAY_IGNORE_PAN = 400;
55
+ const registerPan = ({
56
+ reader,
57
+ recognizer,
58
+ settingsManager
59
+ }) => {
60
+ const gestures$ = settingsManager.values$.pipe(
61
+ rxjs.switchMap(({ panNavigation }) => {
62
+ if (panNavigation !== "pan") return rxjs.EMPTY;
63
+ return recognizer.events$.pipe(
64
+ rxjs.tap((event) => {
65
+ if (reader.zoom.isZooming) return;
66
+ if (event.type === `panStart`) {
67
+ if (event.delay > DELAY_IGNORE_PAN) return;
68
+ reader == null ? void 0 : reader.navigation.moveTo({ x: 0, y: 0 }, { start: true });
69
+ }
70
+ if (event.type === `panMove`) {
71
+ reader == null ? void 0 : reader.navigation.moveTo({ x: event.deltaX, y: event.deltaY });
72
+ }
73
+ if (event.type === `panEnd`) {
74
+ reader == null ? void 0 : reader.navigation.moveTo({ x: event.deltaX, y: event.deltaY }, { final: true });
75
+ }
76
+ })
77
+ );
78
+ })
79
+ );
80
+ return gestures$;
81
+ };
82
+ const registerSwipe = ({
83
+ reader,
84
+ recognizable,
85
+ settingsManager
86
+ }) => {
87
+ const gestures$ = settingsManager.values$.pipe(
88
+ rxjs.switchMap(
89
+ ({ panNavigation }) => panNavigation !== "swipe" ? rxjs.EMPTY : recognizable.events$.pipe(
90
+ rxjs.filter((event) => event.type === "swipe"),
91
+ rxjs.tap((event) => {
92
+ const { computedPageTurnDirection } = reader.settings.values;
93
+ if (computedPageTurnDirection === "vertical") {
94
+ if (event.velocityY < -0.5) {
95
+ reader == null ? void 0 : reader.navigation.turnRight();
96
+ }
97
+ if (event.velocityY > 0.5) {
98
+ reader == null ? void 0 : reader.navigation.turnLeft();
99
+ }
100
+ } else {
101
+ if (event.velocityX < -0.5) {
102
+ reader == null ? void 0 : reader.navigation.turnRight();
103
+ }
104
+ if (event.velocityX > 0.5) {
105
+ reader == null ? void 0 : reader.navigation.turnLeft();
106
+ }
107
+ }
108
+ })
109
+ )
110
+ )
111
+ );
112
+ return gestures$;
113
+ };
114
+ class GesturesSettingsManager extends core.SettingsManager {
115
+ getOutputSettings(inputSettings) {
116
+ return inputSettings;
117
+ }
118
+ getDefaultSettings() {
119
+ return {
120
+ panNavigation: "pan",
121
+ fontScalePinchEnabled: false,
122
+ fontScalePinchThrottleTime: 500,
123
+ pinchCancelPan: true
124
+ };
125
+ }
126
+ }
127
+ const isHtmlImageElement = (target) => core.isHtmlElement(target) && !!target.ownerDocument.defaultView && target instanceof target.ownerDocument.defaultView.HTMLImageElement;
128
+ const registerPinch = ({
129
+ reader,
130
+ recognizable,
131
+ settingsManager
132
+ }) => {
133
+ const pinchStart$ = recognizable.events$.pipe(rxjs.filter((event) => event.type === "pinchStart"));
134
+ const pinchMove$ = recognizable.events$.pipe(rxjs.filter((event) => event.type === "pinchMove"));
135
+ const pinchEnd$ = recognizable.events$.pipe(rxjs.filter((event) => event.type === "pinchEnd"));
136
+ const shouldStartZoom = (target) => isHtmlImageElement(target) && !reader.zoom.isZooming;
137
+ return settingsManager.values$.pipe(
138
+ rxjs.switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {
139
+ const zoomGestures$ = pinchStart$.pipe(
140
+ rxjs.withLatestFrom(reader.viewportState$),
141
+ rxjs.switchMap(([event, viewportState]) => {
142
+ const target = event.event.target;
143
+ const startScale = reader.zoom.currentScale;
144
+ if (viewportState === "busy") return rxjs.EMPTY;
145
+ if (shouldStartZoom(target)) {
146
+ reader.zoom.enter(target);
147
+ }
148
+ if (!reader.zoom.isZooming) return rxjs.EMPTY;
149
+ return rxjs.merge(
150
+ pinchMove$.pipe(
151
+ rxjs.tap((event2) => {
152
+ if (reader.zoom.isZooming) {
153
+ const newScale = startScale + (event2.scale - 1);
154
+ if (newScale < 1) {
155
+ reader.zoom.exit();
156
+ } else {
157
+ reader.zoom.scaleAt(newScale);
158
+ }
159
+ }
160
+ })
161
+ )
162
+ );
163
+ })
164
+ );
165
+ const watchForFontScaleChange$ = !fontScalePinchEnabled ? rxjs.EMPTY : pinchStart$.pipe(
166
+ rxjs.withLatestFrom(reader.viewportState$),
167
+ rxjs.switchMap(([pinchStartEvent, viewportState]) => {
168
+ if (viewportState === "busy" || shouldStartZoom(pinchStartEvent.event.target) || reader.zoom.isZooming)
169
+ return rxjs.EMPTY;
170
+ const lastFontScaleOnPinchStart = reader.settings.values.fontScale;
171
+ return pinchMove$.pipe(
172
+ rxjs.throttleTime(fontScalePinchThrottleTime, rxjs.animationFrameScheduler, {
173
+ trailing: true
174
+ }),
175
+ rxjs.tap((event) => {
176
+ reader.settings.update({
177
+ fontScale: parseFloat((lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2))
178
+ });
179
+ }),
180
+ rxjs.takeUntil(pinchEnd$)
181
+ );
182
+ })
183
+ );
184
+ return rxjs.merge(zoomGestures$, watchForFontScaleChange$);
185
+ })
186
+ );
187
+ };
188
+ const registerZoomPan = ({ reader, recognizer }) => {
189
+ const panStart$ = recognizer.events$.pipe(rxjs.filter((event) => event.type === "panStart"));
190
+ const panMove$ = recognizer.events$.pipe(rxjs.filter((event) => event.type === "panMove"));
191
+ const zoomingPan$ = panStart$.pipe(
192
+ rxjs.switchMap(() => {
193
+ const startPosition = reader.zoom.currentPosition;
194
+ return panMove$.pipe(
195
+ rxjs.tap((panMoveEvent) => {
196
+ if (reader.zoom.isZooming) {
197
+ reader.zoom.moveAt({ x: startPosition.x + panMoveEvent.deltaX, y: startPosition.y + panMoveEvent.deltaY });
198
+ }
199
+ })
200
+ );
201
+ })
202
+ );
203
+ return zoomingPan$;
204
+ };
205
+ const gesturesEnhancer = (next) => (options) => {
206
+ const { gestures = {}, ...rest } = options;
207
+ const reader = next(rest);
208
+ const settingsManager = new GesturesSettingsManager(gestures);
209
+ const hookManager = new core.HookManager();
210
+ const pinchRecognizer = new gesturx.PinchRecognizer({
211
+ options: {
212
+ /**
213
+ * @important
214
+ * To be less than pan otherwise it will not fail before it starts
215
+ */
216
+ posThreshold: 20
217
+ }
218
+ });
219
+ const panRecognizer = new gesturx.PanRecognizer({
220
+ failWith: [pinchRecognizer],
221
+ options: {
222
+ // we want to have some margin to trigger zoom
223
+ posThreshold: 30
224
+ }
225
+ });
226
+ const zoomPanRecognizer = new gesturx.PanRecognizer({
227
+ options: {
228
+ posThreshold: 1
229
+ }
230
+ });
231
+ const tapRecognizer = new gesturx.TapRecognizer({
232
+ failWith: [panRecognizer]
233
+ });
234
+ const swipeRecognizer = new gesturx.SwipeRecognizer();
235
+ const recognizable = new gesturx.Recognizable({
236
+ recognizers: [tapRecognizer, panRecognizer, swipeRecognizer, pinchRecognizer, zoomPanRecognizer]
237
+ });
238
+ const unhandledEvent$ = new rxjs.Subject();
239
+ const tapGestures$ = registerTaps({
240
+ hookManager,
241
+ reader,
242
+ recognizable,
243
+ unhandledEvent$,
244
+ settingsManager
245
+ });
246
+ const panGestures$ = registerPan({
247
+ hookManager,
248
+ reader,
249
+ recognizer: panRecognizer,
250
+ unhandledEvent$,
251
+ settingsManager
252
+ });
253
+ const swipeGestures$ = registerSwipe({
254
+ hookManager,
255
+ reader,
256
+ recognizable,
257
+ unhandledEvent$,
258
+ settingsManager
259
+ });
260
+ const pinchGestures$ = registerPinch({
261
+ hookManager,
262
+ reader,
263
+ recognizable,
264
+ settingsManager,
265
+ unhandledEvent$
266
+ });
267
+ const zoomPanGestures$ = registerZoomPan({
268
+ reader,
269
+ recognizer: zoomPanRecognizer
270
+ });
271
+ const containerUpdate$ = reader.context.containerElement$.pipe(
272
+ rxjs.tap((container) => {
273
+ recognizable.update({
274
+ container
275
+ });
276
+ })
277
+ );
278
+ const watchSettings$ = rxjs.combineLatest([settingsManager.values$, panRecognizer.config$]).pipe(
279
+ rxjs.tap(([{ pinchCancelPan }, panRecognizerConfig]) => {
280
+ var _a, _b;
281
+ const pinchAlreadyInFailWith = (_a = panRecognizerConfig.failWith) == null ? void 0 : _a.includes(pinchRecognizer);
282
+ if (pinchCancelPan && !pinchAlreadyInFailWith) {
283
+ panRecognizer.update({
284
+ failWith: [...panRecognizerConfig.failWith ?? [], pinchRecognizer]
285
+ });
286
+ }
287
+ if (!pinchCancelPan && pinchAlreadyInFailWith) {
288
+ panRecognizer.update({
289
+ failWith: (_b = panRecognizerConfig.failWith) == null ? void 0 : _b.filter((recognizer) => recognizer !== pinchRecognizer)
290
+ });
291
+ }
292
+ })
293
+ );
294
+ rxjs.merge(
295
+ containerUpdate$,
296
+ watchSettings$,
297
+ zoomPanGestures$,
298
+ pinchGestures$,
299
+ tapGestures$,
300
+ swipeGestures$,
301
+ panGestures$
302
+ ).pipe(rxjs.takeUntil(reader.$.destroy$)).subscribe();
303
+ return {
304
+ ...reader,
305
+ gestures: {
306
+ settings: settingsManager,
307
+ unhandledEvent$: unhandledEvent$.asObservable(),
308
+ hookManager
309
+ }
310
+ };
311
+ };
312
+ exports2.gesturesEnhancer = gesturesEnhancer;
313
+ Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
314
+ });
315
+ //# sourceMappingURL=index.umd.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.cjs","sources":["../src/utils.ts","../src/gestures/taps.ts","../src/gestures/pan.ts","../src/gestures/swipe.ts","../src/SettingsManager.ts","../src/gestures/pinch.ts","../src/gestures/zoomPan.ts","../src/index.ts"],"sourcesContent":["import { isHtmlElement } from \"@prose-reader/core\"\nimport { filter, Observable } from \"rxjs\"\n\nexport const filterNotLink = <Event extends { event: PointerEvent }>(stream: Observable<Event>) =>\n stream.pipe(\n filter((event) => {\n const target = event.event.target\n\n if (isHtmlElement(target) && target.tagName === \"a\") return false\n\n return true\n }),\n )\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { Subject, tap } from \"rxjs\"\nimport { GestureEvent, GestureRecognizable, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\nimport { filterNotLink } from \"../utils\"\n\nexport const registerTaps = ({\n reader,\n recognizable,\n unhandledEvent$,\n hookManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = recognizable.events$.pipe(\n filterNotLink,\n tap((event) => {\n const normalizedEvent = event.event\n const { computedPageTurnDirection } = reader.settings.values\n\n if (event.type === \"tap\") {\n const width = window.innerWidth\n const height = window.innerHeight\n const pageTurnMargin = 0.15\n\n if (`x` in normalizedEvent) {\n const { x = 0, y } = normalizedEvent\n\n const beforeTapResults = hookManager.execute(\"beforeTap\", undefined, { event })\n\n if (beforeTapResults.some((result) => result === false)) {\n return\n }\n\n const isTopArea = y < height * pageTurnMargin\n const isBottomArea = y > height * (1 - pageTurnMargin)\n const isLeftArea = x < width * pageTurnMargin\n const isRightArea = x > width * (1 - pageTurnMargin)\n\n if (isLeftArea && computedPageTurnDirection === \"horizontal\") {\n reader.navigation.turnLeftOrTop()\n } else if (isTopArea && computedPageTurnDirection === \"vertical\") {\n reader.navigation.turnLeftOrTop()\n } else if (isBottomArea && computedPageTurnDirection === \"vertical\") {\n reader.navigation.turnRightOrBottom()\n } else if (isRightArea && computedPageTurnDirection === \"horizontal\") {\n reader.navigation.turnRightOrBottom()\n } else {\n unhandledEvent$.next(event)\n }\n }\n }\n }),\n )\n\n return gestures$\n}\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { EMPTY, Subject, switchMap, tap } from \"rxjs\"\nimport { GestureEvent, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\nimport { PanRecognizer } from \"gesturx\"\n\nconst DELAY_IGNORE_PAN = 400\n\nexport const registerPan = ({\n reader,\n recognizer,\n settingsManager,\n}: {\n recognizer: PanRecognizer\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) => {\n if (panNavigation !== \"pan\") return EMPTY\n\n return recognizer.events$.pipe(\n tap((event) => {\n if (reader.zoom.isZooming) return\n\n if (event.type === `panStart`) {\n /**\n * We let the user select\n */\n if (event.delay > DELAY_IGNORE_PAN) return\n\n reader?.navigation.moveTo({ x: 0, y: 0 }, { start: true })\n }\n\n if (event.type === `panMove`) {\n reader?.navigation.moveTo({ x: event.deltaX, y: event.deltaY })\n }\n\n if (event.type === `panEnd`) {\n reader?.navigation.moveTo({ x: event.deltaX, y: event.deltaY }, { final: true })\n }\n }),\n )\n }),\n )\n\n return gestures$\n}\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { EMPTY, Subject, filter, switchMap, tap } from \"rxjs\"\nimport { GestureEvent, GestureRecognizable, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\n\nexport const registerSwipe = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) =>\n panNavigation !== \"swipe\"\n ? EMPTY\n : recognizable.events$.pipe(\n filter((event) => event.type === \"swipe\"),\n tap((event) => {\n const { computedPageTurnDirection } = reader.settings.values\n\n if (computedPageTurnDirection === \"vertical\") {\n if (event.velocityY < -0.5) {\n reader?.navigation.turnRight()\n }\n if (event.velocityY > 0.5) {\n reader?.navigation.turnLeft()\n }\n } else {\n if (event.velocityX < -0.5) {\n reader?.navigation.turnRight()\n }\n if (event.velocityX > 0.5) {\n reader?.navigation.turnLeft()\n }\n }\n }),\n ),\n ),\n )\n\n return gestures$\n}\n","import { SettingsManager } from \"@prose-reader/core\"\nimport { InputSettings, OutputSettings } from \"./types\"\n\nexport class GesturesSettingsManager extends SettingsManager<InputSettings, OutputSettings> {\n getOutputSettings(inputSettings: InputSettings): OutputSettings {\n return inputSettings\n }\n\n getDefaultSettings(): InputSettings {\n return {\n panNavigation: \"pan\",\n fontScalePinchEnabled: false,\n fontScalePinchThrottleTime: 500,\n pinchCancelPan: true,\n }\n }\n}\n","import { HookManager, isHtmlElement, Reader } from \"@prose-reader/core\"\nimport {\n EMPTY,\n Subject,\n animationFrameScheduler,\n filter,\n merge,\n switchMap,\n takeUntil,\n tap,\n throttleTime,\n withLatestFrom,\n} from \"rxjs\"\nimport { GestureEvent, GestureRecognizable, Hook } from \"../types\"\nimport { GesturesSettingsManager } from \"../SettingsManager\"\nimport { PinchEvent } from \"gesturx\"\n\nconst isHtmlImageElement = (target: EventTarget | null): target is HTMLImageElement =>\n isHtmlElement(target) &&\n !!target.ownerDocument.defaultView &&\n target instanceof target.ownerDocument.defaultView.HTMLImageElement\n\nexport const registerPinch = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n unhandledEvent$: Subject<GestureEvent>\n settingsManager: GesturesSettingsManager\n}) => {\n const pinchStart$ = recognizable.events$.pipe(filter((event): event is PinchEvent => event.type === \"pinchStart\"))\n\n const pinchMove$ = recognizable.events$.pipe(filter((event): event is PinchEvent => event.type === \"pinchMove\"))\n\n const pinchEnd$ = recognizable.events$.pipe(filter((event): event is PinchEvent => event.type === \"pinchEnd\"))\n\n const shouldStartZoom = (target: EventTarget | null): target is HTMLImageElement =>\n isHtmlImageElement(target) && !reader.zoom.isZooming\n\n return settingsManager.values$.pipe(\n switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {\n const zoomGestures$ = pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([event, viewportState]) => {\n const target = event.event.target\n const startScale = reader.zoom.currentScale\n\n if (viewportState === \"busy\") return EMPTY\n\n if (shouldStartZoom(target)) {\n reader.zoom.enter(target)\n }\n\n if (!reader.zoom.isZooming) return EMPTY\n\n return merge(\n pinchMove$.pipe(\n tap((event) => {\n if (reader.zoom.isZooming) {\n const newScale = startScale + (event.scale - 1)\n\n if (newScale < 1) {\n reader.zoom.exit()\n } else {\n reader.zoom.scaleAt(newScale)\n }\n }\n }),\n ),\n )\n }),\n )\n\n const watchForFontScaleChange$ = !fontScalePinchEnabled\n ? EMPTY\n : pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([pinchStartEvent, viewportState]) => {\n if (viewportState === \"busy\" || shouldStartZoom(pinchStartEvent.event.target) || reader.zoom.isZooming)\n return EMPTY\n\n const lastFontScaleOnPinchStart = reader.settings.values.fontScale\n\n return pinchMove$.pipe(\n throttleTime(fontScalePinchThrottleTime, animationFrameScheduler, {\n trailing: true,\n }),\n tap((event) => {\n reader.settings.update({\n fontScale: parseFloat((lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2)),\n })\n }),\n takeUntil(pinchEnd$),\n )\n }),\n )\n\n return merge(zoomGestures$, watchForFontScaleChange$)\n }),\n )\n}\n","import { Reader } from \"@prose-reader/core\"\nimport { PanRecognizer } from \"gesturx\"\nimport { filter, switchMap, tap } from \"rxjs\"\n\nexport const registerZoomPan = ({ reader, recognizer }: { recognizer: PanRecognizer; reader: Reader }) => {\n const panStart$ = recognizer.events$.pipe(filter((event) => event.type === \"panStart\"))\n const panMove$ = recognizer.events$.pipe(filter((event) => event.type === \"panMove\"))\n\n const zoomingPan$ = panStart$.pipe(\n switchMap(() => {\n const startPosition = reader.zoom.currentPosition\n\n return panMove$.pipe(\n tap((panMoveEvent) => {\n if (reader.zoom.isZooming) {\n reader.zoom.moveAt({ x: startPosition.x + panMoveEvent.deltaX, y: startPosition.y + panMoveEvent.deltaY })\n }\n }),\n )\n }),\n )\n\n return zoomingPan$\n}\n","import { HookManager, Reader } from \"@prose-reader/core\"\nimport { ObservedValueOf, Subject, combineLatest, merge, takeUntil, tap } from \"rxjs\"\nimport { PanRecognizer, PinchRecognizer, Recognizable, SwipeRecognizer, TapRecognizer } from \"gesturx\"\nimport { EnhancerAPI, InputSettings, Hook } from \"./types\"\nimport { registerTaps } from \"./gestures/taps\"\nimport { registerPan } from \"./gestures/pan\"\nimport { registerSwipe } from \"./gestures/swipe\"\nimport { GesturesSettingsManager } from \"./SettingsManager\"\nimport { registerPinch } from \"./gestures/pinch\"\nimport { registerZoomPan } from \"./gestures/zoomPan\"\n\nexport const gesturesEnhancer =\n <InheritOptions, InheritOutput extends Reader>(next: (options: InheritOptions) => InheritOutput) =>\n (\n options: InheritOptions & {\n gestures?: Partial<InputSettings>\n },\n ): InheritOutput & EnhancerAPI => {\n const { gestures = {}, ...rest } = options\n const reader = next(rest as InheritOptions)\n\n const settingsManager = new GesturesSettingsManager(gestures)\n\n const hookManager = new HookManager<Hook>()\n\n const pinchRecognizer = new PinchRecognizer({\n options: {\n /**\n * @important\n * To be less than pan otherwise it will not fail before it starts\n */\n posThreshold: 20,\n },\n })\n\n const panRecognizer = new PanRecognizer({\n failWith: [pinchRecognizer],\n options: {\n // we want to have some margin to trigger zoom\n posThreshold: 30,\n },\n })\n\n const zoomPanRecognizer = new PanRecognizer({\n options: {\n posThreshold: 1,\n },\n })\n\n const tapRecognizer = new TapRecognizer({\n failWith: [panRecognizer],\n })\n\n const swipeRecognizer = new SwipeRecognizer()\n\n const recognizable = new Recognizable({\n recognizers: [tapRecognizer, panRecognizer, swipeRecognizer, pinchRecognizer, zoomPanRecognizer],\n })\n\n const unhandledEvent$ = new Subject<ObservedValueOf<typeof recognizable.events$>>()\n\n const tapGestures$ = registerTaps({\n hookManager,\n reader,\n recognizable,\n unhandledEvent$,\n settingsManager,\n })\n\n const panGestures$ = registerPan({\n hookManager,\n reader,\n recognizer: panRecognizer,\n unhandledEvent$,\n settingsManager,\n })\n\n const swipeGestures$ = registerSwipe({\n hookManager,\n reader,\n recognizable,\n unhandledEvent$,\n settingsManager,\n })\n\n const pinchGestures$ = registerPinch({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n unhandledEvent$,\n })\n\n const zoomPanGestures$ = registerZoomPan({\n reader,\n recognizer: zoomPanRecognizer,\n })\n\n const containerUpdate$ = reader.context.containerElement$.pipe(\n tap((container) => {\n recognizable.update({\n container,\n })\n }),\n )\n\n const watchSettings$ = combineLatest([settingsManager.values$, panRecognizer.config$]).pipe(\n tap(([{ pinchCancelPan }, panRecognizerConfig]) => {\n const pinchAlreadyInFailWith = panRecognizerConfig.failWith?.includes(pinchRecognizer)\n\n if (pinchCancelPan && !pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: [...(panRecognizerConfig.failWith ?? []), pinchRecognizer],\n })\n }\n\n if (!pinchCancelPan && pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: panRecognizerConfig.failWith?.filter((recognizer) => recognizer !== pinchRecognizer),\n })\n }\n }),\n )\n\n merge(\n containerUpdate$,\n watchSettings$,\n zoomPanGestures$,\n pinchGestures$,\n tapGestures$,\n swipeGestures$,\n panGestures$,\n )\n .pipe(takeUntil(reader.$.destroy$))\n .subscribe()\n\n return {\n ...reader,\n gestures: {\n settings: settingsManager,\n unhandledEvent$: unhandledEvent$.asObservable(),\n hookManager,\n },\n }\n }\n"],"names":["filter","isHtmlElement","tap","switchMap","EMPTY","SettingsManager","withLatestFrom","merge","event","throttleTime","animationFrameScheduler","takeUntil","HookManager","PinchRecognizer","PanRecognizer","TapRecognizer","SwipeRecognizer","Recognizable","Subject","combineLatest"],"mappings":";;;;AAGa,QAAA,gBAAgB,CAAwC,WACnE,OAAO;AAAA,IACLA,KAAA,OAAO,CAAC,UAAU;AACV,YAAA,SAAS,MAAM,MAAM;AAE3B,UAAIC,KAAAA,cAAc,MAAM,KAAK,OAAO,YAAY,IAAY,QAAA;AAErD,aAAA;AAAA,IAAA,CACR;AAAA,EACH;ACNK,QAAM,eAAe,CAAC;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAMM;AACE,UAAA,YAAY,aAAa,QAAQ;AAAA,MACrC;AAAA,MACAC,KAAA,IAAI,CAAC,UAAU;AACb,cAAM,kBAAkB,MAAM;AAC9B,cAAM,EAAE,0BAA8B,IAAA,OAAO,SAAS;AAElD,YAAA,MAAM,SAAS,OAAO;AACxB,gBAAM,QAAQ,OAAO;AACrB,gBAAM,SAAS,OAAO;AACtB,gBAAM,iBAAiB;AAEvB,cAAI,OAAO,iBAAiB;AAC1B,kBAAM,EAAE,IAAI,GAAG,EAAA,IAAM;AAErB,kBAAM,mBAAmB,YAAY,QAAQ,aAAa,QAAW,EAAE,OAAO;AAE9E,gBAAI,iBAAiB,KAAK,CAAC,WAAW,WAAW,KAAK,GAAG;AACvD;AAAA,YACF;AAEM,kBAAA,YAAY,IAAI,SAAS;AACzB,kBAAA,eAAe,IAAI,UAAU,IAAI;AACjC,kBAAA,aAAa,IAAI,QAAQ;AACzB,kBAAA,cAAc,IAAI,SAAS,IAAI;AAEjC,gBAAA,cAAc,8BAA8B,cAAc;AAC5D,qBAAO,WAAW;YAAc,WACvB,aAAa,8BAA8B,YAAY;AAChE,qBAAO,WAAW;YAAc,WACvB,gBAAgB,8BAA8B,YAAY;AACnE,qBAAO,WAAW;YAAkB,WAC3B,eAAe,8BAA8B,cAAc;AACpE,qBAAO,WAAW;YAAkB,OAC/B;AACL,8BAAgB,KAAK,KAAK;AAAA,YAC5B;AAAA,UACF;AAAA,QACF;AAAA,MAAA,CACD;AAAA,IAAA;AAGI,WAAA;AAAA,EACT;ACtDA,QAAM,mBAAmB;AAElB,QAAM,cAAc,CAAC;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAMM;AACE,UAAA,YAAY,gBAAgB,QAAQ;AAAA,MACxCC,eAAU,CAAC,EAAE,oBAAoB;AAC3B,YAAA,kBAAkB,MAAc,QAAAC;AAEpC,eAAO,WAAW,QAAQ;AAAA,UACxBF,KAAA,IAAI,CAAC,UAAU;AACT,gBAAA,OAAO,KAAK,UAAW;AAEvB,gBAAA,MAAM,SAAS,YAAY;AAIzB,kBAAA,MAAM,QAAQ,iBAAkB;AAE5B,+CAAA,WAAW,OAAO,EAAE,GAAG,GAAG,GAAG,KAAK,EAAE,OAAO,KAAM;AAAA,YAC3D;AAEI,gBAAA,MAAM,SAAS,WAAW;AACpB,+CAAA,WAAW,OAAO,EAAE,GAAG,MAAM,QAAQ,GAAG,MAAM,OAAA;AAAA,YACxD;AAEI,gBAAA,MAAM,SAAS,UAAU;AAC3B,+CAAQ,WAAW,OAAO,EAAE,GAAG,MAAM,QAAQ,GAAG,MAAM,OAAO,GAAG,EAAE,OAAO,KAAM;AAAA,YACjF;AAAA,UAAA,CACD;AAAA,QAAA;AAAA,MACH,CACD;AAAA,IAAA;AAGI,WAAA;AAAA,EACT;AC5CO,QAAM,gBAAgB,CAAC;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAMM;AACE,UAAA,YAAY,gBAAgB,QAAQ;AAAA,MACxCC,KAAA;AAAA,QAAU,CAAC,EAAE,cAAc,MACzB,kBAAkB,UACdC,aACA,aAAa,QAAQ;AAAA,UACnBJ,KAAAA,OAAO,CAAC,UAAU,MAAM,SAAS,OAAO;AAAA,UACxCE,KAAA,IAAI,CAAC,UAAU;AACb,kBAAM,EAAE,0BAA8B,IAAA,OAAO,SAAS;AAEtD,gBAAI,8BAA8B,YAAY;AACxC,kBAAA,MAAM,YAAY,MAAM;AAC1B,iDAAQ,WAAW;AAAA,cACrB;AACI,kBAAA,MAAM,YAAY,KAAK;AACzB,iDAAQ,WAAW;AAAA,cACrB;AAAA,YAAA,OACK;AACD,kBAAA,MAAM,YAAY,MAAM;AAC1B,iDAAQ,WAAW;AAAA,cACrB;AACI,kBAAA,MAAM,YAAY,KAAK;AACzB,iDAAQ,WAAW;AAAA,cACrB;AAAA,YACF;AAAA,UAAA,CACD;AAAA,QACH;AAAA,MACN;AAAA,IAAA;AAGK,WAAA;AAAA,EACT;AAAA,EC3CO,MAAM,gCAAgCG,KAAAA,gBAA+C;AAAA,IAC1F,kBAAkB,eAA8C;AACvD,aAAA;AAAA,IACT;AAAA,IAEA,qBAAoC;AAC3B,aAAA;AAAA,QACL,eAAe;AAAA,QACf,uBAAuB;AAAA,QACvB,4BAA4B;AAAA,QAC5B,gBAAgB;AAAA,MAAA;AAAA,IAEpB;AAAA,EACF;ACCA,QAAM,qBAAqB,CAAC,WAC1BJ,KAAA,cAAc,MAAM,KACpB,CAAC,CAAC,OAAO,cAAc,eACvB,kBAAkB,OAAO,cAAc,YAAY;AAE9C,QAAM,gBAAgB,CAAC;AAAA,IAC5B;AAAA,IACA;AAAA,IACA;AAAA,EACF,MAMM;AACE,UAAA,cAAc,aAAa,QAAQ,KAAKD,KAAAA,OAAO,CAAC,UAA+B,MAAM,SAAS,YAAY,CAAC;AAE3G,UAAA,aAAa,aAAa,QAAQ,KAAKA,KAAAA,OAAO,CAAC,UAA+B,MAAM,SAAS,WAAW,CAAC;AAEzG,UAAA,YAAY,aAAa,QAAQ,KAAKA,KAAAA,OAAO,CAAC,UAA+B,MAAM,SAAS,UAAU,CAAC;AAEvG,UAAA,kBAAkB,CAAC,WACvB,mBAAmB,MAAM,KAAK,CAAC,OAAO,KAAK;AAE7C,WAAO,gBAAgB,QAAQ;AAAA,MAC7BG,KAAAA,UAAU,CAAC,EAAE,uBAAuB,iCAAiC;AACnE,cAAM,gBAAgB,YAAY;AAAA,UAChCG,KAAA,eAAe,OAAO,cAAc;AAAA,UACpCH,KAAAA,UAAU,CAAC,CAAC,OAAO,aAAa,MAAM;AAC9B,kBAAA,SAAS,MAAM,MAAM;AACrB,kBAAA,aAAa,OAAO,KAAK;AAE3B,gBAAA,kBAAkB,OAAe,QAAAC;AAEjC,gBAAA,gBAAgB,MAAM,GAAG;AACpB,qBAAA,KAAK,MAAM,MAAM;AAAA,YAC1B;AAEA,gBAAI,CAAC,OAAO,KAAK,UAAkB,QAAAA,KAAAA;AAE5B,mBAAAG,KAAA;AAAA,cACL,WAAW;AAAA,gBACTL,KAAA,IAAI,CAACM,WAAU;AACT,sBAAA,OAAO,KAAK,WAAW;AACnB,0BAAA,WAAW,cAAcA,OAAM,QAAQ;AAE7C,wBAAI,WAAW,GAAG;AAChB,6BAAO,KAAK;oBAAK,OACZ;AACE,6BAAA,KAAK,QAAQ,QAAQ;AAAA,oBAC9B;AAAA,kBACF;AAAA,gBAAA,CACD;AAAA,cACH;AAAA,YAAA;AAAA,UACF,CACD;AAAA,QAAA;AAGH,cAAM,2BAA2B,CAAC,wBAC9BJ,KAAA,QACA,YAAY;AAAA,UACVE,KAAA,eAAe,OAAO,cAAc;AAAA,UACpCH,KAAAA,UAAU,CAAC,CAAC,iBAAiB,aAAa,MAAM;AAC1C,gBAAA,kBAAkB,UAAU,gBAAgB,gBAAgB,MAAM,MAAM,KAAK,OAAO,KAAK;AACpF,qBAAAC;AAEH,kBAAA,4BAA4B,OAAO,SAAS,OAAO;AAEzD,mBAAO,WAAW;AAAA,cAChBK,KAAA,aAAa,4BAA4BC,8BAAyB;AAAA,gBAChE,UAAU;AAAA,cAAA,CACX;AAAA,cACDR,KAAA,IAAI,CAAC,UAAU;AACb,uBAAO,SAAS,OAAO;AAAA,kBACrB,WAAW,YAAY,6BAA6B,MAAM,QAAQ,IAAI,QAAQ,CAAC,CAAC;AAAA,gBAAA,CACjF;AAAA,cAAA,CACF;AAAA,cACDS,KAAAA,UAAU,SAAS;AAAA,YAAA;AAAA,UACrB,CACD;AAAA,QAAA;AAGA,eAAAJ,KAAA,MAAM,eAAe,wBAAwB;AAAA,MAAA,CACrD;AAAA,IAAA;AAAA,EAEL;ACnGO,QAAM,kBAAkB,CAAC,EAAE,QAAQ,iBAAgE;AAClG,UAAA,YAAY,WAAW,QAAQ,KAAKP,KAAAA,OAAO,CAAC,UAAU,MAAM,SAAS,UAAU,CAAC;AAChF,UAAA,WAAW,WAAW,QAAQ,KAAKA,KAAAA,OAAO,CAAC,UAAU,MAAM,SAAS,SAAS,CAAC;AAEpF,UAAM,cAAc,UAAU;AAAA,MAC5BG,KAAAA,UAAU,MAAM;AACR,cAAA,gBAAgB,OAAO,KAAK;AAElC,eAAO,SAAS;AAAA,UACdD,KAAA,IAAI,CAAC,iBAAiB;AAChB,gBAAA,OAAO,KAAK,WAAW;AACzB,qBAAO,KAAK,OAAO,EAAE,GAAG,cAAc,IAAI,aAAa,QAAQ,GAAG,cAAc,IAAI,aAAa,OAAQ,CAAA;AAAA,YAC3G;AAAA,UAAA,CACD;AAAA,QAAA;AAAA,MACH,CACD;AAAA,IAAA;AAGI,WAAA;AAAA,EACT;ACZO,QAAM,mBACX,CAA+C,SAC/C,CACE,YAGgC;AAChC,UAAM,EAAE,WAAW,CAAA,GAAI,GAAG,SAAS;AAC7B,UAAA,SAAS,KAAK,IAAsB;AAEpC,UAAA,kBAAkB,IAAI,wBAAwB,QAAQ;AAEtD,UAAA,cAAc,IAAIU,KAAAA;AAElB,UAAA,kBAAkB,IAAIC,wBAAgB;AAAA,MAC1C,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,QAKP,cAAc;AAAA,MAChB;AAAA,IAAA,CACD;AAEK,UAAA,gBAAgB,IAAIC,sBAAc;AAAA,MACtC,UAAU,CAAC,eAAe;AAAA,MAC1B,SAAS;AAAA;AAAA,QAEP,cAAc;AAAA,MAChB;AAAA,IAAA,CACD;AAEK,UAAA,oBAAoB,IAAIA,sBAAc;AAAA,MAC1C,SAAS;AAAA,QACP,cAAc;AAAA,MAChB;AAAA,IAAA,CACD;AAEK,UAAA,gBAAgB,IAAIC,sBAAc;AAAA,MACtC,UAAU,CAAC,aAAa;AAAA,IAAA,CACzB;AAEK,UAAA,kBAAkB,IAAIC,QAAAA;AAEtB,UAAA,eAAe,IAAIC,qBAAa;AAAA,MACpC,aAAa,CAAC,eAAe,eAAe,iBAAiB,iBAAiB,iBAAiB;AAAA,IAAA,CAChG;AAEK,UAAA,kBAAkB,IAAIC,KAAAA;AAE5B,UAAM,eAAe,aAAa;AAAA,MAChC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAED,UAAM,eAAe,YAAY;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,IAAA,CACD;AAED,UAAM,iBAAiB,cAAc;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAED,UAAM,iBAAiB,cAAc;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAED,UAAM,mBAAmB,gBAAgB;AAAA,MACvC;AAAA,MACA,YAAY;AAAA,IAAA,CACb;AAEK,UAAA,mBAAmB,OAAO,QAAQ,kBAAkB;AAAA,MACxDhB,KAAA,IAAI,CAAC,cAAc;AACjB,qBAAa,OAAO;AAAA,UAClB;AAAA,QAAA,CACD;AAAA,MAAA,CACF;AAAA,IAAA;AAGG,UAAA,iBAAiBiB,mBAAc,CAAC,gBAAgB,SAAS,cAAc,OAAO,CAAC,EAAE;AAAA,MACrFjB,KAAAA,IAAI,CAAC,CAAC,EAAE,eAAe,GAAG,mBAAmB,MAAM;;AACjD,cAAM,0BAAyB,yBAAoB,aAApB,mBAA8B,SAAS;AAElE,YAAA,kBAAkB,CAAC,wBAAwB;AAC7C,wBAAc,OAAO;AAAA,YACnB,UAAU,CAAC,GAAI,oBAAoB,YAAY,CAAA,GAAK,eAAe;AAAA,UAAA,CACpE;AAAA,QACH;AAEI,YAAA,CAAC,kBAAkB,wBAAwB;AAC7C,wBAAc,OAAO;AAAA,YACnB,WAAU,yBAAoB,aAApB,mBAA8B,OAAO,CAAC,eAAe,eAAe;AAAA,UAAe,CAC9F;AAAA,QACH;AAAA,MAAA,CACD;AAAA,IAAA;AAGHK,SAAA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,EAEC,KAAKI,KAAAA,UAAU,OAAO,EAAE,QAAQ,CAAC,EACjC;AAEI,WAAA;AAAA,MACL,GAAG;AAAA,MACH,UAAU;AAAA,QACR,UAAU;AAAA,QACV,iBAAiB,gBAAgB,aAAa;AAAA,QAC9C;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;;;;"}
@@ -0,0 +1,26 @@
1
+ import { Observable, ObservedValueOf } from 'rxjs';
2
+ import { PanRecognizer, PinchRecognizer, Recognizable, SwipeRecognizer, TapRecognizer, TapEvent } from 'gesturx';
3
+ import { HookManager } from '../../core/dist/hooks/HookManager';
4
+ import { GesturesSettingsManager } from './SettingsManager';
5
+ export type Hook = {
6
+ name: "beforeTap";
7
+ runFn: (params: {
8
+ event: TapEvent;
9
+ }) => boolean;
10
+ };
11
+ export type GestureRecognizable = Recognizable<(TapRecognizer | PanRecognizer | SwipeRecognizer | PinchRecognizer)[]>;
12
+ export type GestureEvent = ObservedValueOf<GestureRecognizable["events$"]>;
13
+ export type InputSettings = {
14
+ panNavigation: "pan" | "swipe" | false;
15
+ fontScalePinchEnabled: boolean;
16
+ fontScalePinchThrottleTime: number;
17
+ pinchCancelPan: boolean;
18
+ };
19
+ export type OutputSettings = InputSettings;
20
+ export type EnhancerAPI = {
21
+ gestures: {
22
+ settings: GesturesSettingsManager;
23
+ unhandledEvent$: Observable<GestureEvent>;
24
+ hookManager: HookManager<Hook>;
25
+ };
26
+ };
@@ -0,0 +1,4 @@
1
+ import { Observable } from 'rxjs';
2
+ export declare const filterNotLink: <Event extends {
3
+ event: PointerEvent;
4
+ }>(stream: Observable<Event>) => Observable<Event>;
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@prose-reader/enhancer-gestures",
3
+ "version": "1.81.0",
4
+ "type": "module",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "private": false,
9
+ "publishConfig": {
10
+ "access": "public",
11
+ "registry": "https://registry.npmjs.org/"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.umd.cjs"
17
+ }
18
+ },
19
+ "files": [
20
+ "/dist"
21
+ ],
22
+ "scripts": {
23
+ "start": "vite build --watch --mode development",
24
+ "build": "tsc && vite build",
25
+ "lint:read": "prettier --check . && eslint . --ext .ts,.tsx,.js,.jsx",
26
+ "lint:write": "prettier --write . && eslint --fix . --ext .ts,.tsx,.js,.jsx",
27
+ "test": "vitest run --coverage"
28
+ },
29
+ "dependencies": {
30
+ "@prose-reader/core": "^1.81.0"
31
+ },
32
+ "devDependencies": {
33
+ "gesturx": "^1.8.2"
34
+ },
35
+ "peerDependencies": {
36
+ "gesturx": "*",
37
+ "rxjs": "*"
38
+ },
39
+ "gitHead": "5eacda3e3f1e62ead3edb1d925d82a286d62b9a6"
40
+ }