@prose-reader/enhancer-gestures 1.320.0 → 1.322.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/dist/index.cjs ADDED
@@ -0,0 +1,398 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _prose_reader_core = require("@prose-reader/core");
3
+ let gesturx = require("gesturx");
4
+ let rxjs = require("rxjs");
5
+ //#region package.json
6
+ var name = "@prose-reader/enhancer-gestures";
7
+ //#endregion
8
+ //#region src/gestures/pan.ts
9
+ var registerPan = ({ reader, recognizer, settingsManager }) => {
10
+ return settingsManager.values$.pipe((0, rxjs.switchMap)(({ panNavigation }) => {
11
+ const panStart$ = recognizer.events$.pipe((0, rxjs.filter)((event) => event.type === `panStart`));
12
+ const panMove$ = recognizer.events$.pipe((0, rxjs.filter)((event) => event.type === `panMove`));
13
+ const panEnd$ = recognizer.events$.pipe((0, rxjs.filter)((event) => event.type === `panEnd`));
14
+ return panStart$.pipe((0, rxjs.switchMap)((panStartEvent) => {
15
+ /**
16
+ * We use the last cumulative delta to derive the new event atomic delta.
17
+ * This is because panning the zoom does not necessarily means the zoom position
18
+ * will always changes. If the user keep dragging while the zoom is blocked, we want
19
+ * it to move the other direction when he start dragging the other way.
20
+ * We cannot use the `reader.zoom.state.currentPosition` as previous position
21
+ * and the event.deltaX to compute the new zoom position.
22
+ */
23
+ let lastDelta = {
24
+ x: 0,
25
+ y: 0
26
+ };
27
+ const moveAndEnd$ = (0, rxjs.merge)(panMove$, panEnd$).pipe((0, rxjs.tap)((event) => {
28
+ const isZooming = reader.zoom.state.isZooming;
29
+ const isZoomingIn = reader.zoom.state.currentScale > 1;
30
+ /**
31
+ * When user is zooming in, we don't navigate anymore.
32
+ * We still allow the pan gesture to move the zoomed controlled
33
+ * viewport even when pan navigation itself is disabled.
34
+ */
35
+ if (isZooming && isZoomingIn) {
36
+ const deltaX = event.deltaX - lastDelta.x;
37
+ const deltaY = event.deltaY - lastDelta.y;
38
+ lastDelta = {
39
+ x: event.deltaX,
40
+ y: event.deltaY
41
+ };
42
+ reader.zoom.move({
43
+ x: reader.zoom.state.currentPosition.x + deltaX,
44
+ y: reader.zoom.state.currentPosition.y + deltaY
45
+ }, { constrain: "within-viewport" });
46
+ return;
47
+ }
48
+ if (panNavigation !== "pan") return;
49
+ if (event.type === `panMove`) {
50
+ if (!reader.navigation.panNavigator.value.isStarted) {
51
+ reader.navigation.panNavigator.start({
52
+ x: event.deltaX,
53
+ y: event.deltaY
54
+ });
55
+ return;
56
+ }
57
+ reader.navigation.panNavigator.panMoveTo({
58
+ x: event.deltaX,
59
+ y: event.deltaY
60
+ });
61
+ return;
62
+ }
63
+ if (event.type === `panEnd` && reader.navigation.panNavigator.value.isStarted) reader.navigation.panNavigator.stop({
64
+ x: event.deltaX,
65
+ y: event.deltaY
66
+ });
67
+ }));
68
+ return (0, rxjs.merge)((0, rxjs.of)(panStartEvent), moveAndEnd$).pipe((0, rxjs.map)((event) => ({
69
+ type: "pan",
70
+ gestureEvent: event
71
+ })));
72
+ }));
73
+ }));
74
+ };
75
+ //#endregion
76
+ //#region src/gestures/pinch.ts
77
+ var isHtmlImageElement = (target) => (0, _prose_reader_core.isHtmlElement)(target) && !!target.ownerDocument.defaultView && target instanceof target.ownerDocument.defaultView.HTMLImageElement;
78
+ var registerPinch = ({ reader, recognizable, settingsManager }) => {
79
+ const pinchStart$ = recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)((event) => event.type === "pinchStart"));
80
+ const pinchMove$ = recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)((event) => event.type === "pinchMove"));
81
+ const pinchEnd$ = recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)((event) => event.type === "pinchEnd"));
82
+ const shouldStartZoom = (target) => isHtmlImageElement(target) && !reader.zoom.state.isZooming;
83
+ return settingsManager.values$.pipe((0, rxjs.switchMap)(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {
84
+ return (0, rxjs.merge)(pinchStart$.pipe((0, rxjs.switchMap)(() => {
85
+ const startScale = reader.zoom.state.currentScale;
86
+ return pinchMove$.pipe((0, rxjs.withLatestFrom)(reader.viewportState$), (0, rxjs.map)(([event, viewportState]) => {
87
+ const newScale = startScale * event.scale;
88
+ /**
89
+ * @important
90
+ * We don't want to trigger zoom gestures if there is a pan navigation
91
+ * in progress. This can happens if the user start panning and then adds
92
+ * another finger triggering a pinch.
93
+ */
94
+ if (viewportState === "busy") return event;
95
+ if (!reader.zoom.state.isZooming && event.scale > 1) {
96
+ reader.zoom.enter({
97
+ animate: false,
98
+ scale: newScale
99
+ });
100
+ return event;
101
+ }
102
+ if (reader.zoom.state.isZooming) {
103
+ if (newScale < 1) reader.zoom.exit();
104
+ else reader.zoom.scaleAt(Math.min(newScale, settingsManager.values.zoomMaxScale), { constrain: "within-viewport" });
105
+ return event;
106
+ }
107
+ return event;
108
+ }));
109
+ })), !fontScalePinchEnabled ? rxjs.EMPTY : pinchStart$.pipe((0, rxjs.withLatestFrom)(reader.viewportState$), (0, rxjs.switchMap)(([pinchStartEvent, viewportState]) => {
110
+ if (viewportState === "busy" || shouldStartZoom(pinchStartEvent.event.target) || reader.zoom.state.isZooming) return rxjs.EMPTY;
111
+ const lastFontScaleOnPinchStart = reader.settings.values.fontScale;
112
+ return pinchMove$.pipe((0, rxjs.throttleTime)(fontScalePinchThrottleTime, rxjs.animationFrameScheduler, { trailing: true }), (0, rxjs.tap)((event) => {
113
+ const newScale = Number.parseFloat((lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2));
114
+ const newMinMaxedFontScale = Math.max(Math.min(newScale, settingsManager.values.fontScaleMaxScale), settingsManager.values.fontScaleMinScale);
115
+ reader.settings.update({ fontScale: newMinMaxedFontScale });
116
+ }), (0, rxjs.takeUntil)(pinchEnd$));
117
+ }))).pipe((0, rxjs.map)((event) => ({
118
+ type: "pinch",
119
+ gestureEvent: event
120
+ })));
121
+ }));
122
+ };
123
+ //#endregion
124
+ //#region src/gestures/swipe.ts
125
+ var isSwipeEvent = (event) => event.type === "swipe";
126
+ var registerSwipe = ({ reader, recognizable, settingsManager }) => {
127
+ return settingsManager.values$.pipe((0, rxjs.switchMap)(({ panNavigation }) => panNavigation !== "swipe" ? rxjs.EMPTY : recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)(isSwipeEvent), (0, rxjs.tap)((event) => {
128
+ const { computedPageTurnDirection } = reader.settings.values;
129
+ if (computedPageTurnDirection === "vertical") {
130
+ if (event.velocityY < -.5) reader?.navigation.turnRight();
131
+ if (event.velocityY > .5) reader?.navigation.turnLeft();
132
+ } else {
133
+ if (event.velocityX < -.5) reader?.navigation.turnRight();
134
+ if (event.velocityX > .5) reader?.navigation.turnLeft();
135
+ }
136
+ }), (0, rxjs.map)((event) => ({
137
+ type: "swipe",
138
+ gestureEvent: event
139
+ })))));
140
+ };
141
+ //#endregion
142
+ //#region src/utils.ts
143
+ var isNotLink = (event) => {
144
+ const target = event.event.target;
145
+ if ((0, _prose_reader_core.isHtmlElement)(target) && target.tagName === "a") return false;
146
+ return true;
147
+ };
148
+ var getPositionRelativeToContainer = (event, containerElementRect) => {
149
+ const { x, y } = event;
150
+ const { left, top } = containerElementRect;
151
+ return {
152
+ x: x - left,
153
+ y: y - top
154
+ };
155
+ };
156
+ var istMatchingSelectors = (selectors, event) => {
157
+ const target = event.event.target;
158
+ if (!(0, _prose_reader_core.isHtmlElement)(target)) return false;
159
+ return !!selectors.find((selector) => {
160
+ if (target.matches(selector)) return true;
161
+ if (target.closest(selector)) return true;
162
+ return false;
163
+ });
164
+ };
165
+ //#endregion
166
+ //#region src/gestures/taps/utils.ts
167
+ var isPositionInArea = (position, area, containerSize) => {
168
+ const { x, y } = position;
169
+ const { width, height } = containerSize;
170
+ switch (area.type) {
171
+ case "margins": {
172
+ const { top, bottom, left, right } = area;
173
+ const inTop = top !== void 0 ? y < height * top : true;
174
+ const inBottom = bottom !== void 0 ? y > height * (1 - bottom) : true;
175
+ const inLeft = left !== void 0 ? x < width * left : true;
176
+ const inRight = right !== void 0 ? x > width * (1 - right) : true;
177
+ return top !== void 0 && inTop || bottom !== void 0 && inBottom || left !== void 0 && inLeft || right !== void 0 && inRight;
178
+ }
179
+ case "rectangle": {
180
+ const { x: rectX, y: rectY, width: rectWidth, height: rectHeight, unit = "%" } = area;
181
+ const actualX = unit === "%" ? width * (rectX / 100) : rectX;
182
+ const actualY = unit === "%" ? height * (rectY / 100) : rectY;
183
+ const actualWidth = unit === "%" ? width * (rectWidth / 100) : rectWidth;
184
+ const actualHeight = unit === "%" ? height * (rectHeight / 100) : rectHeight;
185
+ return x >= actualX && x <= actualX + actualWidth && y >= actualY && y <= actualY + actualHeight;
186
+ }
187
+ case "corner": {
188
+ const { corner, size, unit = "%" } = area;
189
+ const actualSize = unit === "%" ? Math.min(width, height) * (size / 100) : size;
190
+ switch (corner) {
191
+ case "top-left": return x < actualSize && y < actualSize;
192
+ case "top-right": return x > width - actualSize && y < actualSize;
193
+ case "bottom-left": return x < actualSize && y > height - actualSize;
194
+ case "bottom-right": return x > width - actualSize && y > height - actualSize;
195
+ default: return false;
196
+ }
197
+ }
198
+ case "center": {
199
+ const { width: centerWidth, height: centerHeight, unit = "%" } = area;
200
+ const actualWidth = unit === "%" ? width * (centerWidth / 100) : centerWidth;
201
+ const actualHeight = unit === "%" ? height * (centerHeight / 100) : centerHeight;
202
+ const centerX = width / 2;
203
+ const centerY = height / 2;
204
+ return x >= centerX - actualWidth / 2 && x <= centerX + actualWidth / 2 && y >= centerY - actualHeight / 2 && y <= centerY + actualHeight / 2;
205
+ }
206
+ default: return false;
207
+ }
208
+ };
209
+ var calculatePageTurnLinearMargin = (screenWidth) => {
210
+ const minMargin = .15;
211
+ const maxMargin = .2;
212
+ const minWidth = 320;
213
+ const maxWidth = 1200;
214
+ if (screenWidth <= minWidth) return maxMargin;
215
+ if (screenWidth >= maxWidth) return minMargin;
216
+ return maxMargin - (screenWidth - minWidth) / (maxWidth - minWidth) * (maxMargin - minMargin);
217
+ };
218
+ //#endregion
219
+ //#region src/gestures/taps/registerTaps.ts
220
+ var registerTaps = ({ reader, recognizable, hookManager, settingsManager, recognizer }) => {
221
+ return recognizable.events$.pipe((0, rxjs.filter)((event) => event.recognizer === recognizer), (0, rxjs.withLatestFrom)(reader.context.watch(`rootElement`), reader.spine.element$), (0, rxjs.switchMap)(([{ event }, containerElement, spineElement]) => {
222
+ if (!containerElement || !spineElement) return rxjs.EMPTY;
223
+ const normalizedEvent = event.event;
224
+ const { computedPageTurnDirection, computedPageTurnMode } = reader.settings.values;
225
+ if (event.type === "tap" && isNotLink(event) && !istMatchingSelectors(settingsManager.values.ignore, event)) {
226
+ if (`x` in normalizedEvent) {
227
+ const containerElementRect = containerElement.getBoundingClientRect();
228
+ const width = containerElementRect.width;
229
+ const pageTurnMargin = calculatePageTurnLinearMargin(width);
230
+ const positionInContainer = getPositionRelativeToContainer(normalizedEvent, containerElementRect);
231
+ const positionInSpineNonTransformed = reader.coordinates.getSpinePositionFromClientPosition(normalizedEvent);
232
+ const spineItemPageInfo = positionInSpineNonTransformed ? reader.spine.locator.getSpineItemPagePositionFromSpinePosition(positionInSpineNonTransformed) : void 0;
233
+ return (0, rxjs.combineLatest)([...hookManager.execute("beforeTapGesture", { event$: (0, rxjs.of)({
234
+ event,
235
+ page: spineItemPageInfo
236
+ }) }), (0, rxjs.of)(true)]).pipe((0, rxjs.first)(), (0, rxjs.filter)((results) => !results.some((result) => result === false)), (0, rxjs.map)(() => {
237
+ const isZoomedIn = reader.zoom.state.isZooming && reader.zoom.state.currentScale > 1;
238
+ if (computedPageTurnMode === "scrollable" || isZoomedIn) return {
239
+ type: "tap",
240
+ gestureEvent: event,
241
+ handled: false
242
+ };
243
+ if (computedPageTurnDirection === "horizontal" && isPositionInArea(positionInContainer, {
244
+ type: "margins",
245
+ left: pageTurnMargin
246
+ }, containerElementRect)) reader.navigation.turnLeftOrTop();
247
+ else if (computedPageTurnDirection === "vertical" && isPositionInArea(positionInContainer, {
248
+ type: "margins",
249
+ top: pageTurnMargin
250
+ }, containerElementRect)) reader.navigation.turnLeftOrTop();
251
+ else if (computedPageTurnDirection === "vertical" && isPositionInArea(positionInContainer, {
252
+ type: "margins",
253
+ bottom: pageTurnMargin
254
+ }, containerElementRect)) reader.navigation.turnRightOrBottom();
255
+ else if (computedPageTurnDirection === "horizontal" && isPositionInArea(positionInContainer, {
256
+ type: "margins",
257
+ right: pageTurnMargin
258
+ }, containerElementRect)) reader.navigation.turnRightOrBottom();
259
+ else return {
260
+ type: "tap",
261
+ gestureEvent: event,
262
+ handled: false
263
+ };
264
+ return {
265
+ type: "tap",
266
+ gestureEvent: event,
267
+ handled: true
268
+ };
269
+ }));
270
+ }
271
+ }
272
+ return rxjs.EMPTY;
273
+ }));
274
+ };
275
+ //#endregion
276
+ //#region src/SettingsManager.ts
277
+ var GesturesSettingsManager = class extends _prose_reader_core.SettingsManager {
278
+ reader;
279
+ constructor(initialSettings, reader) {
280
+ super(initialSettings);
281
+ this.reader = reader;
282
+ /**
283
+ * Since we have settings that may be locked due to some reader settings
284
+ * we need to update as soon as they update as well.
285
+ */
286
+ reader.settings.values$.pipe((0, rxjs.tap)(() => {
287
+ this.update({});
288
+ }), (0, rxjs.takeUntil)(this.destroy$)).subscribe();
289
+ }
290
+ getOutputSettings(inputSettings) {
291
+ return {
292
+ ...inputSettings,
293
+ panNavigation: this.reader.settings.values.computedPageTurnMode === `scrollable` ? false : inputSettings.panNavigation
294
+ };
295
+ }
296
+ getDefaultSettings() {
297
+ return {
298
+ panNavigation: "pan",
299
+ pinchCancelPan: true,
300
+ fontScalePinchEnabled: true,
301
+ fontScalePinchThrottleTime: 500,
302
+ fontScaleMaxScale: 5,
303
+ fontScaleMinScale: .2,
304
+ zoomMaxScale: Infinity,
305
+ ignore: []
306
+ };
307
+ }
308
+ };
309
+ //#endregion
310
+ //#region src/style.css?inline
311
+ var style_default = "[data-prose-reader-container=\"${id}\"] * {\n /* Make sure that touche actions are correctly dispatched no matter where the user interact */\n touch-action: inherit;\n}\n";
312
+ //#endregion
313
+ //#region src/index.ts
314
+ var gesturesEnhancer = (next) => (options) => {
315
+ const { gestures = {}, ...rest } = options;
316
+ const reader = next(rest);
317
+ const removeStylesheet = reader.utils.injectScopedCSS(document, name, style_default);
318
+ const settingsManager = new GesturesSettingsManager(gestures, reader);
319
+ const hookManager = new _prose_reader_core.HookManager();
320
+ const pinchRecognizer = new gesturx.PinchRecognizer({ options: {
321
+ /**
322
+ * @important
323
+ * Ideally we want pinch to triggers before pan so we can
324
+ * capture zoom before starting panning.
325
+ */
326
+ posThreshold: 10 } });
327
+ const failWithSelection = {
328
+ start$: reader.selection.selectionStart$,
329
+ end$: reader.selection.selectionEnd$
330
+ };
331
+ const panRecognizer = new gesturx.PanRecognizer({
332
+ failWith: [pinchRecognizer, failWithSelection],
333
+ options: { posThreshold: 20 }
334
+ });
335
+ const tapRecognizer = new gesturx.TapRecognizer({ failWith: [panRecognizer] });
336
+ const recognizable = new gesturx.Recognizable({
337
+ recognizers: [
338
+ tapRecognizer,
339
+ panRecognizer,
340
+ new gesturx.SwipeRecognizer({ failWith: [failWithSelection] }),
341
+ pinchRecognizer
342
+ ],
343
+ disableTextSelection: false
344
+ });
345
+ const tapGestures$ = registerTaps({
346
+ hookManager,
347
+ reader,
348
+ recognizable,
349
+ settingsManager,
350
+ recognizer: tapRecognizer
351
+ });
352
+ const panGestures$ = registerPan({
353
+ hookManager,
354
+ reader,
355
+ recognizer: panRecognizer,
356
+ settingsManager
357
+ });
358
+ const swipeGestures$ = registerSwipe({
359
+ hookManager,
360
+ reader,
361
+ recognizable,
362
+ settingsManager
363
+ });
364
+ const pinchGestures$ = registerPinch({
365
+ hookManager,
366
+ reader,
367
+ recognizable,
368
+ settingsManager
369
+ });
370
+ const containerUpdate$ = reader.context.watch(`rootElement`).pipe((0, rxjs.tap)((container) => {
371
+ recognizable.update({ container });
372
+ }));
373
+ const watchSettings$ = (0, rxjs.combineLatest)([settingsManager.values$, panRecognizer.config$]).pipe((0, rxjs.tap)(([{ pinchCancelPan }, panRecognizerConfig]) => {
374
+ const pinchAlreadyInFailWith = panRecognizerConfig.failWith?.includes(pinchRecognizer);
375
+ if (pinchCancelPan && !pinchAlreadyInFailWith) panRecognizer.update({ failWith: [...panRecognizerConfig.failWith ?? [], pinchRecognizer] });
376
+ if (!pinchCancelPan && pinchAlreadyInFailWith) panRecognizer.update({ failWith: panRecognizerConfig.failWith?.filter((recognizer) => recognizer !== pinchRecognizer) });
377
+ }));
378
+ const gestures$ = (0, rxjs.merge)(pinchGestures$, tapGestures$, swipeGestures$, panGestures$).pipe((0, rxjs.share)());
379
+ (0, rxjs.merge)(containerUpdate$, watchSettings$, gestures$).pipe((0, rxjs.takeUntil)(reader.$.destroy$)).subscribe();
380
+ return {
381
+ ...reader,
382
+ destroy: () => {
383
+ removeStylesheet();
384
+ reader.destroy();
385
+ settingsManager.destroy();
386
+ },
387
+ gestures: {
388
+ settings: settingsManager,
389
+ gestures$,
390
+ hooks: hookManager
391
+ }
392
+ };
393
+ };
394
+ //#endregion
395
+ exports.gesturesEnhancer = gesturesEnhancer;
396
+ exports.isPositionInArea = isPositionInArea;
397
+
398
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../package.json","../src/gestures/pan.ts","../src/gestures/pinch.ts","../src/gestures/swipe.ts","../src/utils.ts","../src/gestures/taps/utils.ts","../src/gestures/taps/registerTaps.ts","../src/SettingsManager.ts","../src/style.css?inline","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@prose-reader/enhancer-gestures\",\n \"version\": \"1.321.0\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"private\": false,\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"/dist\"\n ],\n \"scripts\": {\n \"start\": \"vite build --watch --mode development\",\n \"build\": \"tsc && vite build\",\n \"test\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@prose-reader/core\": \"^1.321.0\"\n },\n \"devDependencies\": {\n \"gesturx\": \"*\",\n \"rxjs\": \"*\"\n },\n \"peerDependencies\": {\n \"gesturx\": \"1.x\",\n \"rxjs\": \"7.x\"\n },\n \"gitHead\": \"4601e14dcacf50b2295cb343582a7ef2c7e1eedc\"\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { PanRecognizer } from \"gesturx\"\nimport { filter, map, merge, of, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { Hook } from \"../types\"\n\nexport const registerPan = ({\n reader,\n recognizer,\n settingsManager,\n}: {\n recognizer: PanRecognizer\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) => {\n const panStart$ = recognizer.events$.pipe(\n filter((event) => event.type === `panStart`),\n )\n const panMove$ = recognizer.events$.pipe(\n filter((event) => event.type === `panMove`),\n )\n const panEnd$ = recognizer.events$.pipe(\n filter((event) => event.type === `panEnd`),\n )\n\n const pan$ = panStart$.pipe(\n switchMap((panStartEvent) => {\n /**\n * We use the last cumulative delta to derive the new event atomic delta.\n * This is because panning the zoom does not necessarily means the zoom position\n * will always changes. If the user keep dragging while the zoom is blocked, we want\n * it to move the other direction when he start dragging the other way.\n * We cannot use the `reader.zoom.state.currentPosition` as previous position\n * and the event.deltaX to compute the new zoom position.\n */\n let lastDelta = { x: 0, y: 0 }\n\n const moveAndEnd$ = merge(panMove$, panEnd$).pipe(\n tap((event) => {\n const isZooming = reader.zoom.state.isZooming\n const isZoomingIn = reader.zoom.state.currentScale > 1\n\n /**\n * When user is zooming in, we don't navigate anymore.\n * We still allow the pan gesture to move the zoomed controlled\n * viewport even when pan navigation itself is disabled.\n */\n if (isZooming && isZoomingIn) {\n const deltaX = event.deltaX - lastDelta.x\n const deltaY = event.deltaY - lastDelta.y\n\n lastDelta = {\n x: event.deltaX,\n y: event.deltaY,\n }\n\n reader.zoom.move(\n {\n x: reader.zoom.state.currentPosition.x + deltaX,\n y: reader.zoom.state.currentPosition.y + deltaY,\n },\n {\n constrain: \"within-viewport\",\n },\n )\n\n return\n }\n\n if (panNavigation !== \"pan\") return\n\n if (event.type === `panMove`) {\n if (!reader.navigation.panNavigator.value.isStarted) {\n reader.navigation.panNavigator.start({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n reader.navigation.panNavigator.panMoveTo({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n if (\n event.type === `panEnd` &&\n reader.navigation.panNavigator.value.isStarted\n ) {\n reader.navigation.panNavigator.stop({\n x: event.deltaX,\n y: event.deltaY,\n })\n }\n }),\n )\n\n return merge(of(panStartEvent), moveAndEnd$).pipe(\n map((event) => ({\n type: \"pan\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n\n return pan$\n }),\n )\n\n return gestures$\n}\n","import {\n type HookManager,\n isHtmlElement,\n type Reader,\n} from \"@prose-reader/core\"\nimport type { PinchEvent } from \"gesturx\"\nimport {\n animationFrameScheduler,\n EMPTY,\n filter,\n map,\n merge,\n switchMap,\n takeUntil,\n tap,\n throttleTime,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../types\"\n\nconst isHtmlImageElement = (\n target: EventTarget | null,\n): 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 settingsManager: GesturesSettingsManager\n}) => {\n const pinchStart$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchStart\"),\n )\n\n const pinchMove$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchMove\"),\n )\n\n const pinchEnd$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchEnd\"),\n )\n\n const shouldStartZoom = (\n target: EventTarget | null,\n ): target is HTMLImageElement =>\n isHtmlImageElement(target) && !reader.zoom.state.isZooming\n\n return settingsManager.values$.pipe(\n switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {\n const zoomGestures$ = pinchStart$.pipe(\n switchMap(() => {\n const startScale = reader.zoom.state.currentScale\n\n return pinchMove$.pipe(\n withLatestFrom(reader.viewportState$),\n map(([event, viewportState]) => {\n // symmetric scaling calculation\n const newScale = startScale * event.scale\n\n /**\n * @important\n * We don't want to trigger zoom gestures if there is a pan navigation\n * in progress. This can happens if the user start panning and then adds\n * another finger triggering a pinch.\n */\n if (viewportState === \"busy\") {\n return event\n }\n\n if (!reader.zoom.state.isZooming && event.scale > 1) {\n reader.zoom.enter({ animate: false, scale: newScale })\n\n return event\n }\n\n if (reader.zoom.state.isZooming) {\n if (newScale < 1) {\n reader.zoom.exit()\n } else {\n reader.zoom.scaleAt(\n Math.min(newScale, settingsManager.values.zoomMaxScale),\n {\n constrain: \"within-viewport\",\n },\n )\n }\n\n return event\n }\n\n return event\n }),\n )\n }),\n )\n\n const watchForFontScaleChange$ = !fontScalePinchEnabled\n ? EMPTY\n : pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([pinchStartEvent, viewportState]) => {\n if (\n viewportState === \"busy\" ||\n shouldStartZoom(pinchStartEvent.event.target) ||\n reader.zoom.state.isZooming\n )\n return EMPTY\n\n const lastFontScaleOnPinchStart = reader.settings.values.fontScale\n\n return pinchMove$.pipe(\n throttleTime(\n fontScalePinchThrottleTime,\n animationFrameScheduler,\n {\n trailing: true,\n },\n ),\n tap((event) => {\n const newScale = Number.parseFloat(\n (lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2),\n )\n\n const newMinMaxedFontScale = Math.max(\n Math.min(\n newScale,\n settingsManager.values.fontScaleMaxScale,\n ),\n settingsManager.values.fontScaleMinScale,\n )\n\n reader.settings.update({\n fontScale: newMinMaxedFontScale,\n })\n }),\n takeUntil(pinchEnd$),\n )\n }),\n )\n\n return merge(zoomGestures$, watchForFontScaleChange$).pipe(\n map((event) => ({\n type: \"pinch\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { SwipeEvent } from \"gesturx\"\nimport { EMPTY, filter, map, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureEvent, GestureRecognizable, Hook } from \"../types\"\n\nconst isSwipeEvent = (event: GestureEvent[\"event\"]): event is SwipeEvent =>\n event.type === \"swipe\"\n\nexport const registerSwipe = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) =>\n panNavigation !== \"swipe\"\n ? EMPTY\n : recognizable.events$.pipe(\n map(({ event }) => event),\n filter(isSwipeEvent),\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 map((event) => ({ type: \"swipe\" as const, gestureEvent: event })),\n ),\n ),\n )\n\n return gestures$\n}\n","import { isHtmlElement } from \"@prose-reader/core\"\nimport type { GestureEvent } from \"./types\"\n\nexport const isNotLink = (event: GestureEvent[\"event\"]) => {\n const target = event.event.target\n\n if (isHtmlElement(target) && target.tagName === \"a\") return false\n\n return true\n}\n\nexport const getPositionRelativeToContainer = (\n event: { x: number; y: number },\n containerElementRect: DOMRectReadOnly,\n) => {\n const { x, y } = event\n const { left, top } = containerElementRect\n\n return {\n x: x - left,\n y: y - top,\n }\n}\n\nexport const istMatchingSelectors = (\n selectors: string[],\n event: GestureEvent[\"event\"],\n): boolean => {\n const target = event.event.target\n\n if (!isHtmlElement(target)) return false\n\n const match = selectors.find((selector) => {\n // Check if the target matches the selector directly\n if (target.matches(selector)) return true\n\n // Check if the target is within an element matching the selector\n if (target.closest(selector)) return true\n\n return false\n })\n\n return !!match\n}\n","import type { TapArea } from \"./types\"\n\nexport const isPositionInArea = (\n position: { x: number; y: number },\n area: TapArea,\n containerSize: { width: number; height: number },\n): boolean => {\n const { x, y } = position\n const { width, height } = containerSize\n\n switch (area.type) {\n case \"margins\": {\n const { top, bottom, left, right } = area\n const inTop = top !== undefined ? y < height * top : true\n const inBottom = bottom !== undefined ? y > height * (1 - bottom) : true\n const inLeft = left !== undefined ? x < width * left : true\n const inRight = right !== undefined ? x > width * (1 - right) : true\n\n return (\n (top !== undefined && inTop) ||\n (bottom !== undefined && inBottom) ||\n (left !== undefined && inLeft) ||\n (right !== undefined && inRight)\n )\n }\n\n case \"rectangle\": {\n const {\n x: rectX,\n y: rectY,\n width: rectWidth,\n height: rectHeight,\n unit = \"%\",\n } = area\n const actualX = unit === \"%\" ? width * (rectX / 100) : rectX\n const actualY = unit === \"%\" ? height * (rectY / 100) : rectY\n const actualWidth = unit === \"%\" ? width * (rectWidth / 100) : rectWidth\n const actualHeight =\n unit === \"%\" ? height * (rectHeight / 100) : rectHeight\n\n return (\n x >= actualX &&\n x <= actualX + actualWidth &&\n y >= actualY &&\n y <= actualY + actualHeight\n )\n }\n\n case \"corner\": {\n const { corner, size, unit = \"%\" } = area\n const actualSize =\n unit === \"%\" ? Math.min(width, height) * (size / 100) : size\n\n switch (corner) {\n case \"top-left\":\n return x < actualSize && y < actualSize\n case \"top-right\":\n return x > width - actualSize && y < actualSize\n case \"bottom-left\":\n return x < actualSize && y > height - actualSize\n case \"bottom-right\":\n return x > width - actualSize && y > height - actualSize\n default:\n return false\n }\n }\n\n case \"center\": {\n const { width: centerWidth, height: centerHeight, unit = \"%\" } = area\n const actualWidth =\n unit === \"%\" ? width * (centerWidth / 100) : centerWidth\n const actualHeight =\n unit === \"%\" ? height * (centerHeight / 100) : centerHeight\n const centerX = width / 2\n const centerY = height / 2\n\n return (\n x >= centerX - actualWidth / 2 &&\n x <= centerX + actualWidth / 2 &&\n y >= centerY - actualHeight / 2 &&\n y <= centerY + actualHeight / 2\n )\n }\n\n default:\n return false\n }\n}\n\nexport const calculatePageTurnLinearMargin = (screenWidth: number): number => {\n const minMargin = 0.15\n const maxMargin = 0.2\n const minWidth = 320\n const maxWidth = 1200\n\n if (screenWidth <= minWidth) return maxMargin\n if (screenWidth >= maxWidth) return minMargin\n\n // Linear interpolation between min and max\n const ratio = (screenWidth - minWidth) / (maxWidth - minWidth)\n return maxMargin - ratio * (maxMargin - minMargin)\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { TapRecognizer } from \"gesturx\"\nimport {\n combineLatest,\n EMPTY,\n filter,\n first,\n map,\n of,\n switchMap,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../../types\"\nimport {\n getPositionRelativeToContainer,\n isNotLink,\n istMatchingSelectors,\n} from \"../../utils\"\nimport { calculatePageTurnLinearMargin, isPositionInArea } from \"./utils\"\n\nexport const registerTaps = ({\n reader,\n recognizable,\n hookManager,\n settingsManager,\n recognizer,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n recognizer: TapRecognizer\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = recognizable.events$.pipe(\n filter((event) => event.recognizer === recognizer),\n withLatestFrom(reader.context.watch(`rootElement`), reader.spine.element$),\n switchMap(([{ event }, containerElement, spineElement]) => {\n if (!containerElement || !spineElement) return EMPTY\n\n const normalizedEvent = event.event\n const { computedPageTurnDirection, computedPageTurnMode } =\n reader.settings.values\n\n if (\n event.type === \"tap\" &&\n isNotLink(event) &&\n !istMatchingSelectors(settingsManager.values.ignore, event)\n ) {\n if (`x` in normalizedEvent) {\n const containerElementRect = containerElement.getBoundingClientRect()\n const width = containerElementRect.width\n const pageTurnMargin = calculatePageTurnLinearMargin(width)\n const positionInContainer = getPositionRelativeToContainer(\n normalizedEvent,\n containerElementRect,\n )\n\n const positionInSpineNonTransformed =\n reader.coordinates.getSpinePositionFromClientPosition(\n normalizedEvent,\n )\n\n const spineItemPageInfo = positionInSpineNonTransformed\n ? reader.spine.locator.getSpineItemPagePositionFromSpinePosition(\n positionInSpineNonTransformed,\n )\n : undefined\n\n const beforeTapResults$ = hookManager.execute(\"beforeTapGesture\", {\n event$: of({ event, page: spineItemPageInfo }),\n })\n\n return combineLatest([...beforeTapResults$, of(true)]).pipe(\n first(),\n filter((results) => !results.some((result) => result === false)),\n map(() => {\n const isZoomedIn =\n reader.zoom.state.isZooming &&\n reader.zoom.state.currentScale > 1\n\n if (computedPageTurnMode === \"scrollable\" || isZoomedIn) {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", left: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", top: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", bottom: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", right: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: true,\n }\n }),\n )\n }\n }\n\n return EMPTY\n }),\n )\n\n return gestures$\n}\n","import { type Reader, SettingsManager } from \"@prose-reader/core\"\nimport { takeUntil, tap } from \"rxjs\"\nimport type { InputSettings, OutputSettings } from \"./types\"\n\nexport class GesturesSettingsManager extends SettingsManager<\n InputSettings,\n OutputSettings\n> {\n constructor(\n initialSettings: Partial<InputSettings>,\n private reader: Reader,\n ) {\n super(initialSettings)\n\n /**\n * Since we have settings that may be locked due to some reader settings\n * we need to update as soon as they update as well.\n */\n reader.settings.values$\n .pipe(\n tap(() => {\n this.update({})\n }),\n takeUntil(this.destroy$),\n )\n .subscribe()\n }\n\n getOutputSettings(inputSettings: InputSettings): OutputSettings {\n return {\n ...inputSettings,\n panNavigation:\n this.reader.settings.values.computedPageTurnMode === `scrollable`\n ? false\n : inputSettings.panNavigation,\n }\n }\n\n getDefaultSettings(): InputSettings {\n return {\n panNavigation: \"pan\",\n pinchCancelPan: true,\n fontScalePinchEnabled: true,\n fontScalePinchThrottleTime: 500,\n fontScaleMaxScale: 5,\n fontScaleMinScale: 0.2,\n zoomMaxScale: Infinity,\n ignore: [],\n }\n }\n}\n","[data-prose-reader-container=\"${id}\"] * {\n /* Make sure that touche actions are correctly dispatched no matter where the user interact */\n touch-action: inherit;\n}\n","import { HookManager, type Reader } from \"@prose-reader/core\"\nimport {\n PanRecognizer,\n PinchRecognizer,\n Recognizable,\n SwipeRecognizer,\n TapRecognizer,\n} from \"gesturx\"\nimport { combineLatest, merge, share, takeUntil, tap } from \"rxjs\"\nimport { name as packageName } from \"../package.json\"\nimport { registerPan } from \"./gestures/pan\"\nimport { registerPinch } from \"./gestures/pinch\"\nimport { registerSwipe } from \"./gestures/swipe\"\nimport { registerTaps } from \"./gestures/taps/registerTaps\"\nimport { GesturesSettingsManager } from \"./SettingsManager\"\nimport styles from \"./style.css?inline\"\nimport type { EnhancerAPI, Hook, InputSettings } from \"./types\"\n\nexport { isPositionInArea } from \"./gestures/taps/utils\"\nexport * from \"./types\"\n\nexport const gesturesEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\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 removeStylesheet = reader.utils.injectScopedCSS(\n document,\n packageName,\n styles,\n )\n\n const settingsManager = new GesturesSettingsManager(gestures, reader)\n\n const hookManager = new HookManager<Hook>()\n\n const pinchRecognizer = new PinchRecognizer({\n options: {\n /**\n * @important\n * Ideally we want pinch to triggers before pan so we can\n * capture zoom before starting panning.\n */\n posThreshold: 10,\n },\n })\n\n const failWithSelection = {\n start$: reader.selection.selectionStart$,\n end$: reader.selection.selectionEnd$,\n }\n\n const panRecognizer = new PanRecognizer({\n failWith: [pinchRecognizer, failWithSelection],\n options: {\n // we want to have some margin to trigger zoom\n posThreshold: 20,\n },\n })\n\n const tapRecognizer = new TapRecognizer({\n failWith: [panRecognizer],\n })\n\n const swipeRecognizer = new SwipeRecognizer({\n failWith: [failWithSelection],\n })\n\n const recognizable = new Recognizable({\n recognizers: [\n tapRecognizer,\n panRecognizer,\n swipeRecognizer,\n pinchRecognizer,\n ],\n disableTextSelection: false,\n })\n\n const tapGestures$ = registerTaps({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n recognizer: tapRecognizer,\n })\n\n const panGestures$ = registerPan({\n hookManager,\n reader,\n recognizer: panRecognizer,\n settingsManager,\n })\n\n const swipeGestures$ = registerSwipe({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const pinchGestures$ = registerPinch({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const containerUpdate$ = reader.context.watch(`rootElement`).pipe(\n tap((container) => {\n recognizable.update({\n container,\n })\n }),\n )\n\n const watchSettings$ = combineLatest([\n settingsManager.values$,\n panRecognizer.config$,\n ]).pipe(\n tap(([{ pinchCancelPan }, panRecognizerConfig]) => {\n const pinchAlreadyInFailWith =\n panRecognizerConfig.failWith?.includes(pinchRecognizer)\n\n if (pinchCancelPan && !pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: [\n ...(panRecognizerConfig.failWith ?? []),\n pinchRecognizer,\n ],\n })\n }\n\n if (!pinchCancelPan && pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: panRecognizerConfig.failWith?.filter(\n (recognizer) => recognizer !== pinchRecognizer,\n ),\n })\n }\n }),\n )\n\n const gestures$ = merge(\n pinchGestures$,\n tapGestures$,\n swipeGestures$,\n panGestures$,\n ).pipe(share())\n\n merge(containerUpdate$, watchSettings$, gestures$)\n .pipe(takeUntil(reader.$.destroy$))\n .subscribe()\n\n return {\n ...reader,\n destroy: () => {\n removeStylesheet()\n reader.destroy()\n settingsManager.destroy()\n },\n gestures: {\n settings: settingsManager,\n gestures$,\n hooks: hookManager,\n },\n }\n }\n"],"mappings":"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"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../package.json","../src/gestures/pan.ts","../src/gestures/pinch.ts","../src/gestures/swipe.ts","../src/utils.ts","../src/gestures/taps/utils.ts","../src/gestures/taps/registerTaps.ts","../src/SettingsManager.ts","../src/style.css?inline","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@prose-reader/enhancer-gestures\",\n \"version\": \"1.319.0\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"private\": false,\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.umd.cjs\"\n }\n },\n \"files\": [\n \"/dist\"\n ],\n \"scripts\": {\n \"start\": \"vite build --watch --mode development\",\n \"build\": \"tsc && vite build\",\n \"test\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@prose-reader/core\": \"^1.319.0\"\n },\n \"devDependencies\": {\n \"gesturx\": \"*\",\n \"rxjs\": \"*\"\n },\n \"peerDependencies\": {\n \"gesturx\": \"1.x\",\n \"rxjs\": \"7.x\"\n },\n \"gitHead\": \"4601e14dcacf50b2295cb343582a7ef2c7e1eedc\"\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { PanRecognizer } from \"gesturx\"\nimport { filter, map, merge, of, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { Hook } from \"../types\"\n\nexport const registerPan = ({\n reader,\n recognizer,\n settingsManager,\n}: {\n recognizer: PanRecognizer\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) => {\n const panStart$ = recognizer.events$.pipe(\n filter((event) => event.type === `panStart`),\n )\n const panMove$ = recognizer.events$.pipe(\n filter((event) => event.type === `panMove`),\n )\n const panEnd$ = recognizer.events$.pipe(\n filter((event) => event.type === `panEnd`),\n )\n\n const pan$ = panStart$.pipe(\n switchMap((panStartEvent) => {\n /**\n * We use the last cumulative delta to derive the new event atomic delta.\n * This is because panning the zoom does not necessarily means the zoom position\n * will always changes. If the user keep dragging while the zoom is blocked, we want\n * it to move the other direction when he start dragging the other way.\n * We cannot use the `reader.zoom.state.currentPosition` as previous position\n * and the event.deltaX to compute the new zoom position.\n */\n let lastDelta = { x: 0, y: 0 }\n\n const moveAndEnd$ = merge(panMove$, panEnd$).pipe(\n tap((event) => {\n const isZooming = reader.zoom.state.isZooming\n const isZoomingIn = reader.zoom.state.currentScale > 1\n\n /**\n * When user is zooming in, we don't navigate anymore.\n * We still allow the pan gesture to move the zoomed controlled\n * viewport even when pan navigation itself is disabled.\n */\n if (isZooming && isZoomingIn) {\n const deltaX = event.deltaX - lastDelta.x\n const deltaY = event.deltaY - lastDelta.y\n\n lastDelta = {\n x: event.deltaX,\n y: event.deltaY,\n }\n\n reader.zoom.move(\n {\n x: reader.zoom.state.currentPosition.x + deltaX,\n y: reader.zoom.state.currentPosition.y + deltaY,\n },\n {\n constrain: \"within-viewport\",\n },\n )\n\n return\n }\n\n if (panNavigation !== \"pan\") return\n\n if (event.type === `panMove`) {\n if (!reader.navigation.panNavigator.value.isStarted) {\n reader.navigation.panNavigator.start({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n reader.navigation.panNavigator.panMoveTo({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n if (\n event.type === `panEnd` &&\n reader.navigation.panNavigator.value.isStarted\n ) {\n reader.navigation.panNavigator.stop({\n x: event.deltaX,\n y: event.deltaY,\n })\n }\n }),\n )\n\n return merge(of(panStartEvent), moveAndEnd$).pipe(\n map((event) => ({\n type: \"pan\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n\n return pan$\n }),\n )\n\n return gestures$\n}\n","import {\n type HookManager,\n isHtmlElement,\n type Reader,\n} from \"@prose-reader/core\"\nimport type { PinchEvent } from \"gesturx\"\nimport {\n animationFrameScheduler,\n EMPTY,\n filter,\n map,\n merge,\n switchMap,\n takeUntil,\n tap,\n throttleTime,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../types\"\n\nconst isHtmlImageElement = (\n target: EventTarget | null,\n): 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 settingsManager: GesturesSettingsManager\n}) => {\n const pinchStart$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchStart\"),\n )\n\n const pinchMove$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchMove\"),\n )\n\n const pinchEnd$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchEnd\"),\n )\n\n const shouldStartZoom = (\n target: EventTarget | null,\n ): target is HTMLImageElement =>\n isHtmlImageElement(target) && !reader.zoom.state.isZooming\n\n return settingsManager.values$.pipe(\n switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {\n const zoomGestures$ = pinchStart$.pipe(\n switchMap(() => {\n const startScale = reader.zoom.state.currentScale\n\n return pinchMove$.pipe(\n withLatestFrom(reader.viewportState$),\n map(([event, viewportState]) => {\n // symmetric scaling calculation\n const newScale = startScale * event.scale\n\n /**\n * @important\n * We don't want to trigger zoom gestures if there is a pan navigation\n * in progress. This can happens if the user start panning and then adds\n * another finger triggering a pinch.\n */\n if (viewportState === \"busy\") {\n return event\n }\n\n if (!reader.zoom.state.isZooming && event.scale > 1) {\n reader.zoom.enter({ animate: false, scale: newScale })\n\n return event\n }\n\n if (reader.zoom.state.isZooming) {\n if (newScale < 1) {\n reader.zoom.exit()\n } else {\n reader.zoom.scaleAt(\n Math.min(newScale, settingsManager.values.zoomMaxScale),\n {\n constrain: \"within-viewport\",\n },\n )\n }\n\n return event\n }\n\n return event\n }),\n )\n }),\n )\n\n const watchForFontScaleChange$ = !fontScalePinchEnabled\n ? EMPTY\n : pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([pinchStartEvent, viewportState]) => {\n if (\n viewportState === \"busy\" ||\n shouldStartZoom(pinchStartEvent.event.target) ||\n reader.zoom.state.isZooming\n )\n return EMPTY\n\n const lastFontScaleOnPinchStart = reader.settings.values.fontScale\n\n return pinchMove$.pipe(\n throttleTime(\n fontScalePinchThrottleTime,\n animationFrameScheduler,\n {\n trailing: true,\n },\n ),\n tap((event) => {\n const newScale = Number.parseFloat(\n (lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2),\n )\n\n const newMinMaxedFontScale = Math.max(\n Math.min(\n newScale,\n settingsManager.values.fontScaleMaxScale,\n ),\n settingsManager.values.fontScaleMinScale,\n )\n\n reader.settings.update({\n fontScale: newMinMaxedFontScale,\n })\n }),\n takeUntil(pinchEnd$),\n )\n }),\n )\n\n return merge(zoomGestures$, watchForFontScaleChange$).pipe(\n map((event) => ({\n type: \"pinch\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { SwipeEvent } from \"gesturx\"\nimport { EMPTY, filter, map, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureEvent, GestureRecognizable, Hook } from \"../types\"\n\nconst isSwipeEvent = (event: GestureEvent[\"event\"]): event is SwipeEvent =>\n event.type === \"swipe\"\n\nexport const registerSwipe = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) =>\n panNavigation !== \"swipe\"\n ? EMPTY\n : recognizable.events$.pipe(\n map(({ event }) => event),\n filter(isSwipeEvent),\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 map((event) => ({ type: \"swipe\" as const, gestureEvent: event })),\n ),\n ),\n )\n\n return gestures$\n}\n","import { isHtmlElement } from \"@prose-reader/core\"\nimport type { GestureEvent } from \"./types\"\n\nexport const isNotLink = (event: GestureEvent[\"event\"]) => {\n const target = event.event.target\n\n if (isHtmlElement(target) && target.tagName === \"a\") return false\n\n return true\n}\n\nexport const getPositionRelativeToContainer = (\n event: { x: number; y: number },\n containerElementRect: DOMRectReadOnly,\n) => {\n const { x, y } = event\n const { left, top } = containerElementRect\n\n return {\n x: x - left,\n y: y - top,\n }\n}\n\nexport const istMatchingSelectors = (\n selectors: string[],\n event: GestureEvent[\"event\"],\n): boolean => {\n const target = event.event.target\n\n if (!isHtmlElement(target)) return false\n\n const match = selectors.find((selector) => {\n // Check if the target matches the selector directly\n if (target.matches(selector)) return true\n\n // Check if the target is within an element matching the selector\n if (target.closest(selector)) return true\n\n return false\n })\n\n return !!match\n}\n","import type { TapArea } from \"./types\"\n\nexport const isPositionInArea = (\n position: { x: number; y: number },\n area: TapArea,\n containerSize: { width: number; height: number },\n): boolean => {\n const { x, y } = position\n const { width, height } = containerSize\n\n switch (area.type) {\n case \"margins\": {\n const { top, bottom, left, right } = area\n const inTop = top !== undefined ? y < height * top : true\n const inBottom = bottom !== undefined ? y > height * (1 - bottom) : true\n const inLeft = left !== undefined ? x < width * left : true\n const inRight = right !== undefined ? x > width * (1 - right) : true\n\n return (\n (top !== undefined && inTop) ||\n (bottom !== undefined && inBottom) ||\n (left !== undefined && inLeft) ||\n (right !== undefined && inRight)\n )\n }\n\n case \"rectangle\": {\n const {\n x: rectX,\n y: rectY,\n width: rectWidth,\n height: rectHeight,\n unit = \"%\",\n } = area\n const actualX = unit === \"%\" ? width * (rectX / 100) : rectX\n const actualY = unit === \"%\" ? height * (rectY / 100) : rectY\n const actualWidth = unit === \"%\" ? width * (rectWidth / 100) : rectWidth\n const actualHeight =\n unit === \"%\" ? height * (rectHeight / 100) : rectHeight\n\n return (\n x >= actualX &&\n x <= actualX + actualWidth &&\n y >= actualY &&\n y <= actualY + actualHeight\n )\n }\n\n case \"corner\": {\n const { corner, size, unit = \"%\" } = area\n const actualSize =\n unit === \"%\" ? Math.min(width, height) * (size / 100) : size\n\n switch (corner) {\n case \"top-left\":\n return x < actualSize && y < actualSize\n case \"top-right\":\n return x > width - actualSize && y < actualSize\n case \"bottom-left\":\n return x < actualSize && y > height - actualSize\n case \"bottom-right\":\n return x > width - actualSize && y > height - actualSize\n default:\n return false\n }\n }\n\n case \"center\": {\n const { width: centerWidth, height: centerHeight, unit = \"%\" } = area\n const actualWidth =\n unit === \"%\" ? width * (centerWidth / 100) : centerWidth\n const actualHeight =\n unit === \"%\" ? height * (centerHeight / 100) : centerHeight\n const centerX = width / 2\n const centerY = height / 2\n\n return (\n x >= centerX - actualWidth / 2 &&\n x <= centerX + actualWidth / 2 &&\n y >= centerY - actualHeight / 2 &&\n y <= centerY + actualHeight / 2\n )\n }\n\n default:\n return false\n }\n}\n\nexport const calculatePageTurnLinearMargin = (screenWidth: number): number => {\n const minMargin = 0.15\n const maxMargin = 0.2\n const minWidth = 320\n const maxWidth = 1200\n\n if (screenWidth <= minWidth) return maxMargin\n if (screenWidth >= maxWidth) return minMargin\n\n // Linear interpolation between min and max\n const ratio = (screenWidth - minWidth) / (maxWidth - minWidth)\n return maxMargin - ratio * (maxMargin - minMargin)\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { TapRecognizer } from \"gesturx\"\nimport {\n combineLatest,\n EMPTY,\n filter,\n first,\n map,\n of,\n switchMap,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../../types\"\nimport {\n getPositionRelativeToContainer,\n isNotLink,\n istMatchingSelectors,\n} from \"../../utils\"\nimport { calculatePageTurnLinearMargin, isPositionInArea } from \"./utils\"\n\nexport const registerTaps = ({\n reader,\n recognizable,\n hookManager,\n settingsManager,\n recognizer,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n recognizer: TapRecognizer\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = recognizable.events$.pipe(\n filter((event) => event.recognizer === recognizer),\n withLatestFrom(reader.context.watch(`rootElement`), reader.spine.element$),\n switchMap(([{ event }, containerElement, spineElement]) => {\n if (!containerElement || !spineElement) return EMPTY\n\n const normalizedEvent = event.event\n const { computedPageTurnDirection, computedPageTurnMode } =\n reader.settings.values\n\n if (\n event.type === \"tap\" &&\n isNotLink(event) &&\n !istMatchingSelectors(settingsManager.values.ignore, event)\n ) {\n if (`x` in normalizedEvent) {\n const containerElementRect = containerElement.getBoundingClientRect()\n const width = containerElementRect.width\n const pageTurnMargin = calculatePageTurnLinearMargin(width)\n const positionInContainer = getPositionRelativeToContainer(\n normalizedEvent,\n containerElementRect,\n )\n\n const positionInSpineNonTransformed =\n reader.coordinates.getSpinePositionFromClientPosition(\n normalizedEvent,\n )\n\n const spineItemPageInfo = positionInSpineNonTransformed\n ? reader.spine.locator.getSpineItemPagePositionFromSpinePosition(\n positionInSpineNonTransformed,\n )\n : undefined\n\n const beforeTapResults$ = hookManager.execute(\"beforeTapGesture\", {\n event$: of({ event, page: spineItemPageInfo }),\n })\n\n return combineLatest([...beforeTapResults$, of(true)]).pipe(\n first(),\n filter((results) => !results.some((result) => result === false)),\n map(() => {\n const isZoomedIn =\n reader.zoom.state.isZooming &&\n reader.zoom.state.currentScale > 1\n\n if (computedPageTurnMode === \"scrollable\" || isZoomedIn) {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", left: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", top: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", bottom: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", right: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: true,\n }\n }),\n )\n }\n }\n\n return EMPTY\n }),\n )\n\n return gestures$\n}\n","import { type Reader, SettingsManager } from \"@prose-reader/core\"\nimport { takeUntil, tap } from \"rxjs\"\nimport type { InputSettings, OutputSettings } from \"./types\"\n\nexport class GesturesSettingsManager extends SettingsManager<\n InputSettings,\n OutputSettings\n> {\n constructor(\n initialSettings: Partial<InputSettings>,\n private reader: Reader,\n ) {\n super(initialSettings)\n\n /**\n * Since we have settings that may be locked due to some reader settings\n * we need to update as soon as they update as well.\n */\n reader.settings.values$\n .pipe(\n tap(() => {\n this.update({})\n }),\n takeUntil(this.destroy$),\n )\n .subscribe()\n }\n\n getOutputSettings(inputSettings: InputSettings): OutputSettings {\n return {\n ...inputSettings,\n panNavigation:\n this.reader.settings.values.computedPageTurnMode === `scrollable`\n ? false\n : inputSettings.panNavigation,\n }\n }\n\n getDefaultSettings(): InputSettings {\n return {\n panNavigation: \"pan\",\n pinchCancelPan: true,\n fontScalePinchEnabled: true,\n fontScalePinchThrottleTime: 500,\n fontScaleMaxScale: 5,\n fontScaleMinScale: 0.2,\n zoomMaxScale: Infinity,\n ignore: [],\n }\n }\n}\n","[data-prose-reader-container=\"${id}\"] * {\n /* Make sure that touche actions are correctly dispatched no matter where the user interact */\n touch-action: inherit;\n}\n","import { HookManager, type Reader } from \"@prose-reader/core\"\nimport {\n PanRecognizer,\n PinchRecognizer,\n Recognizable,\n SwipeRecognizer,\n TapRecognizer,\n} from \"gesturx\"\nimport { combineLatest, merge, share, takeUntil, tap } from \"rxjs\"\nimport { name as packageName } from \"../package.json\"\nimport { registerPan } from \"./gestures/pan\"\nimport { registerPinch } from \"./gestures/pinch\"\nimport { registerSwipe } from \"./gestures/swipe\"\nimport { registerTaps } from \"./gestures/taps/registerTaps\"\nimport { GesturesSettingsManager } from \"./SettingsManager\"\nimport styles from \"./style.css?inline\"\nimport type { EnhancerAPI, Hook, InputSettings } from \"./types\"\n\nexport { isPositionInArea } from \"./gestures/taps/utils\"\nexport * from \"./types\"\n\nexport const gesturesEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\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 removeStylesheet = reader.utils.injectScopedCSS(\n document,\n packageName,\n styles,\n )\n\n const settingsManager = new GesturesSettingsManager(gestures, reader)\n\n const hookManager = new HookManager<Hook>()\n\n const pinchRecognizer = new PinchRecognizer({\n options: {\n /**\n * @important\n * Ideally we want pinch to triggers before pan so we can\n * capture zoom before starting panning.\n */\n posThreshold: 10,\n },\n })\n\n const failWithSelection = {\n start$: reader.selection.selectionStart$,\n end$: reader.selection.selectionEnd$,\n }\n\n const panRecognizer = new PanRecognizer({\n failWith: [pinchRecognizer, failWithSelection],\n options: {\n // we want to have some margin to trigger zoom\n posThreshold: 20,\n },\n })\n\n const tapRecognizer = new TapRecognizer({\n failWith: [panRecognizer],\n })\n\n const swipeRecognizer = new SwipeRecognizer({\n failWith: [failWithSelection],\n })\n\n const recognizable = new Recognizable({\n recognizers: [\n tapRecognizer,\n panRecognizer,\n swipeRecognizer,\n pinchRecognizer,\n ],\n disableTextSelection: false,\n })\n\n const tapGestures$ = registerTaps({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n recognizer: tapRecognizer,\n })\n\n const panGestures$ = registerPan({\n hookManager,\n reader,\n recognizer: panRecognizer,\n settingsManager,\n })\n\n const swipeGestures$ = registerSwipe({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const pinchGestures$ = registerPinch({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const containerUpdate$ = reader.context.watch(`rootElement`).pipe(\n tap((container) => {\n recognizable.update({\n container,\n })\n }),\n )\n\n const watchSettings$ = combineLatest([\n settingsManager.values$,\n panRecognizer.config$,\n ]).pipe(\n tap(([{ pinchCancelPan }, panRecognizerConfig]) => {\n const pinchAlreadyInFailWith =\n panRecognizerConfig.failWith?.includes(pinchRecognizer)\n\n if (pinchCancelPan && !pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: [\n ...(panRecognizerConfig.failWith ?? []),\n pinchRecognizer,\n ],\n })\n }\n\n if (!pinchCancelPan && pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: panRecognizerConfig.failWith?.filter(\n (recognizer) => recognizer !== pinchRecognizer,\n ),\n })\n }\n }),\n )\n\n const gestures$ = merge(\n pinchGestures$,\n tapGestures$,\n swipeGestures$,\n panGestures$,\n ).pipe(share())\n\n merge(containerUpdate$, watchSettings$, gestures$)\n .pipe(takeUntil(reader.$.destroy$))\n .subscribe()\n\n return {\n ...reader,\n destroy: () => {\n removeStylesheet()\n reader.destroy()\n settingsManager.destroy()\n },\n gestures: {\n settings: settingsManager,\n gestures$,\n hooks: hookManager,\n },\n }\n }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../package.json","../src/gestures/pan.ts","../src/gestures/pinch.ts","../src/gestures/swipe.ts","../src/utils.ts","../src/gestures/taps/utils.ts","../src/gestures/taps/registerTaps.ts","../src/SettingsManager.ts","../src/style.css?inline","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@prose-reader/enhancer-gestures\",\n \"version\": \"1.321.0\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"private\": false,\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"/dist\"\n ],\n \"scripts\": {\n \"start\": \"vite build --watch --mode development\",\n \"build\": \"tsc && vite build\",\n \"test\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@prose-reader/core\": \"^1.321.0\"\n },\n \"devDependencies\": {\n \"gesturx\": \"*\",\n \"rxjs\": \"*\"\n },\n \"peerDependencies\": {\n \"gesturx\": \"1.x\",\n \"rxjs\": \"7.x\"\n },\n \"gitHead\": \"4601e14dcacf50b2295cb343582a7ef2c7e1eedc\"\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { PanRecognizer } from \"gesturx\"\nimport { filter, map, merge, of, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { Hook } from \"../types\"\n\nexport const registerPan = ({\n reader,\n recognizer,\n settingsManager,\n}: {\n recognizer: PanRecognizer\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) => {\n const panStart$ = recognizer.events$.pipe(\n filter((event) => event.type === `panStart`),\n )\n const panMove$ = recognizer.events$.pipe(\n filter((event) => event.type === `panMove`),\n )\n const panEnd$ = recognizer.events$.pipe(\n filter((event) => event.type === `panEnd`),\n )\n\n const pan$ = panStart$.pipe(\n switchMap((panStartEvent) => {\n /**\n * We use the last cumulative delta to derive the new event atomic delta.\n * This is because panning the zoom does not necessarily means the zoom position\n * will always changes. If the user keep dragging while the zoom is blocked, we want\n * it to move the other direction when he start dragging the other way.\n * We cannot use the `reader.zoom.state.currentPosition` as previous position\n * and the event.deltaX to compute the new zoom position.\n */\n let lastDelta = { x: 0, y: 0 }\n\n const moveAndEnd$ = merge(panMove$, panEnd$).pipe(\n tap((event) => {\n const isZooming = reader.zoom.state.isZooming\n const isZoomingIn = reader.zoom.state.currentScale > 1\n\n /**\n * When user is zooming in, we don't navigate anymore.\n * We still allow the pan gesture to move the zoomed controlled\n * viewport even when pan navigation itself is disabled.\n */\n if (isZooming && isZoomingIn) {\n const deltaX = event.deltaX - lastDelta.x\n const deltaY = event.deltaY - lastDelta.y\n\n lastDelta = {\n x: event.deltaX,\n y: event.deltaY,\n }\n\n reader.zoom.move(\n {\n x: reader.zoom.state.currentPosition.x + deltaX,\n y: reader.zoom.state.currentPosition.y + deltaY,\n },\n {\n constrain: \"within-viewport\",\n },\n )\n\n return\n }\n\n if (panNavigation !== \"pan\") return\n\n if (event.type === `panMove`) {\n if (!reader.navigation.panNavigator.value.isStarted) {\n reader.navigation.panNavigator.start({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n reader.navigation.panNavigator.panMoveTo({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n if (\n event.type === `panEnd` &&\n reader.navigation.panNavigator.value.isStarted\n ) {\n reader.navigation.panNavigator.stop({\n x: event.deltaX,\n y: event.deltaY,\n })\n }\n }),\n )\n\n return merge(of(panStartEvent), moveAndEnd$).pipe(\n map((event) => ({\n type: \"pan\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n\n return pan$\n }),\n )\n\n return gestures$\n}\n","import {\n type HookManager,\n isHtmlElement,\n type Reader,\n} from \"@prose-reader/core\"\nimport type { PinchEvent } from \"gesturx\"\nimport {\n animationFrameScheduler,\n EMPTY,\n filter,\n map,\n merge,\n switchMap,\n takeUntil,\n tap,\n throttleTime,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../types\"\n\nconst isHtmlImageElement = (\n target: EventTarget | null,\n): 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 settingsManager: GesturesSettingsManager\n}) => {\n const pinchStart$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchStart\"),\n )\n\n const pinchMove$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchMove\"),\n )\n\n const pinchEnd$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchEnd\"),\n )\n\n const shouldStartZoom = (\n target: EventTarget | null,\n ): target is HTMLImageElement =>\n isHtmlImageElement(target) && !reader.zoom.state.isZooming\n\n return settingsManager.values$.pipe(\n switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {\n const zoomGestures$ = pinchStart$.pipe(\n switchMap(() => {\n const startScale = reader.zoom.state.currentScale\n\n return pinchMove$.pipe(\n withLatestFrom(reader.viewportState$),\n map(([event, viewportState]) => {\n // symmetric scaling calculation\n const newScale = startScale * event.scale\n\n /**\n * @important\n * We don't want to trigger zoom gestures if there is a pan navigation\n * in progress. This can happens if the user start panning and then adds\n * another finger triggering a pinch.\n */\n if (viewportState === \"busy\") {\n return event\n }\n\n if (!reader.zoom.state.isZooming && event.scale > 1) {\n reader.zoom.enter({ animate: false, scale: newScale })\n\n return event\n }\n\n if (reader.zoom.state.isZooming) {\n if (newScale < 1) {\n reader.zoom.exit()\n } else {\n reader.zoom.scaleAt(\n Math.min(newScale, settingsManager.values.zoomMaxScale),\n {\n constrain: \"within-viewport\",\n },\n )\n }\n\n return event\n }\n\n return event\n }),\n )\n }),\n )\n\n const watchForFontScaleChange$ = !fontScalePinchEnabled\n ? EMPTY\n : pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([pinchStartEvent, viewportState]) => {\n if (\n viewportState === \"busy\" ||\n shouldStartZoom(pinchStartEvent.event.target) ||\n reader.zoom.state.isZooming\n )\n return EMPTY\n\n const lastFontScaleOnPinchStart = reader.settings.values.fontScale\n\n return pinchMove$.pipe(\n throttleTime(\n fontScalePinchThrottleTime,\n animationFrameScheduler,\n {\n trailing: true,\n },\n ),\n tap((event) => {\n const newScale = Number.parseFloat(\n (lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2),\n )\n\n const newMinMaxedFontScale = Math.max(\n Math.min(\n newScale,\n settingsManager.values.fontScaleMaxScale,\n ),\n settingsManager.values.fontScaleMinScale,\n )\n\n reader.settings.update({\n fontScale: newMinMaxedFontScale,\n })\n }),\n takeUntil(pinchEnd$),\n )\n }),\n )\n\n return merge(zoomGestures$, watchForFontScaleChange$).pipe(\n map((event) => ({\n type: \"pinch\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { SwipeEvent } from \"gesturx\"\nimport { EMPTY, filter, map, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureEvent, GestureRecognizable, Hook } from \"../types\"\n\nconst isSwipeEvent = (event: GestureEvent[\"event\"]): event is SwipeEvent =>\n event.type === \"swipe\"\n\nexport const registerSwipe = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) =>\n panNavigation !== \"swipe\"\n ? EMPTY\n : recognizable.events$.pipe(\n map(({ event }) => event),\n filter(isSwipeEvent),\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 map((event) => ({ type: \"swipe\" as const, gestureEvent: event })),\n ),\n ),\n )\n\n return gestures$\n}\n","import { isHtmlElement } from \"@prose-reader/core\"\nimport type { GestureEvent } from \"./types\"\n\nexport const isNotLink = (event: GestureEvent[\"event\"]) => {\n const target = event.event.target\n\n if (isHtmlElement(target) && target.tagName === \"a\") return false\n\n return true\n}\n\nexport const getPositionRelativeToContainer = (\n event: { x: number; y: number },\n containerElementRect: DOMRectReadOnly,\n) => {\n const { x, y } = event\n const { left, top } = containerElementRect\n\n return {\n x: x - left,\n y: y - top,\n }\n}\n\nexport const istMatchingSelectors = (\n selectors: string[],\n event: GestureEvent[\"event\"],\n): boolean => {\n const target = event.event.target\n\n if (!isHtmlElement(target)) return false\n\n const match = selectors.find((selector) => {\n // Check if the target matches the selector directly\n if (target.matches(selector)) return true\n\n // Check if the target is within an element matching the selector\n if (target.closest(selector)) return true\n\n return false\n })\n\n return !!match\n}\n","import type { TapArea } from \"./types\"\n\nexport const isPositionInArea = (\n position: { x: number; y: number },\n area: TapArea,\n containerSize: { width: number; height: number },\n): boolean => {\n const { x, y } = position\n const { width, height } = containerSize\n\n switch (area.type) {\n case \"margins\": {\n const { top, bottom, left, right } = area\n const inTop = top !== undefined ? y < height * top : true\n const inBottom = bottom !== undefined ? y > height * (1 - bottom) : true\n const inLeft = left !== undefined ? x < width * left : true\n const inRight = right !== undefined ? x > width * (1 - right) : true\n\n return (\n (top !== undefined && inTop) ||\n (bottom !== undefined && inBottom) ||\n (left !== undefined && inLeft) ||\n (right !== undefined && inRight)\n )\n }\n\n case \"rectangle\": {\n const {\n x: rectX,\n y: rectY,\n width: rectWidth,\n height: rectHeight,\n unit = \"%\",\n } = area\n const actualX = unit === \"%\" ? width * (rectX / 100) : rectX\n const actualY = unit === \"%\" ? height * (rectY / 100) : rectY\n const actualWidth = unit === \"%\" ? width * (rectWidth / 100) : rectWidth\n const actualHeight =\n unit === \"%\" ? height * (rectHeight / 100) : rectHeight\n\n return (\n x >= actualX &&\n x <= actualX + actualWidth &&\n y >= actualY &&\n y <= actualY + actualHeight\n )\n }\n\n case \"corner\": {\n const { corner, size, unit = \"%\" } = area\n const actualSize =\n unit === \"%\" ? Math.min(width, height) * (size / 100) : size\n\n switch (corner) {\n case \"top-left\":\n return x < actualSize && y < actualSize\n case \"top-right\":\n return x > width - actualSize && y < actualSize\n case \"bottom-left\":\n return x < actualSize && y > height - actualSize\n case \"bottom-right\":\n return x > width - actualSize && y > height - actualSize\n default:\n return false\n }\n }\n\n case \"center\": {\n const { width: centerWidth, height: centerHeight, unit = \"%\" } = area\n const actualWidth =\n unit === \"%\" ? width * (centerWidth / 100) : centerWidth\n const actualHeight =\n unit === \"%\" ? height * (centerHeight / 100) : centerHeight\n const centerX = width / 2\n const centerY = height / 2\n\n return (\n x >= centerX - actualWidth / 2 &&\n x <= centerX + actualWidth / 2 &&\n y >= centerY - actualHeight / 2 &&\n y <= centerY + actualHeight / 2\n )\n }\n\n default:\n return false\n }\n}\n\nexport const calculatePageTurnLinearMargin = (screenWidth: number): number => {\n const minMargin = 0.15\n const maxMargin = 0.2\n const minWidth = 320\n const maxWidth = 1200\n\n if (screenWidth <= minWidth) return maxMargin\n if (screenWidth >= maxWidth) return minMargin\n\n // Linear interpolation between min and max\n const ratio = (screenWidth - minWidth) / (maxWidth - minWidth)\n return maxMargin - ratio * (maxMargin - minMargin)\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { TapRecognizer } from \"gesturx\"\nimport {\n combineLatest,\n EMPTY,\n filter,\n first,\n map,\n of,\n switchMap,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../../types\"\nimport {\n getPositionRelativeToContainer,\n isNotLink,\n istMatchingSelectors,\n} from \"../../utils\"\nimport { calculatePageTurnLinearMargin, isPositionInArea } from \"./utils\"\n\nexport const registerTaps = ({\n reader,\n recognizable,\n hookManager,\n settingsManager,\n recognizer,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n recognizer: TapRecognizer\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = recognizable.events$.pipe(\n filter((event) => event.recognizer === recognizer),\n withLatestFrom(reader.context.watch(`rootElement`), reader.spine.element$),\n switchMap(([{ event }, containerElement, spineElement]) => {\n if (!containerElement || !spineElement) return EMPTY\n\n const normalizedEvent = event.event\n const { computedPageTurnDirection, computedPageTurnMode } =\n reader.settings.values\n\n if (\n event.type === \"tap\" &&\n isNotLink(event) &&\n !istMatchingSelectors(settingsManager.values.ignore, event)\n ) {\n if (`x` in normalizedEvent) {\n const containerElementRect = containerElement.getBoundingClientRect()\n const width = containerElementRect.width\n const pageTurnMargin = calculatePageTurnLinearMargin(width)\n const positionInContainer = getPositionRelativeToContainer(\n normalizedEvent,\n containerElementRect,\n )\n\n const positionInSpineNonTransformed =\n reader.coordinates.getSpinePositionFromClientPosition(\n normalizedEvent,\n )\n\n const spineItemPageInfo = positionInSpineNonTransformed\n ? reader.spine.locator.getSpineItemPagePositionFromSpinePosition(\n positionInSpineNonTransformed,\n )\n : undefined\n\n const beforeTapResults$ = hookManager.execute(\"beforeTapGesture\", {\n event$: of({ event, page: spineItemPageInfo }),\n })\n\n return combineLatest([...beforeTapResults$, of(true)]).pipe(\n first(),\n filter((results) => !results.some((result) => result === false)),\n map(() => {\n const isZoomedIn =\n reader.zoom.state.isZooming &&\n reader.zoom.state.currentScale > 1\n\n if (computedPageTurnMode === \"scrollable\" || isZoomedIn) {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", left: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", top: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", bottom: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", right: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: true,\n }\n }),\n )\n }\n }\n\n return EMPTY\n }),\n )\n\n return gestures$\n}\n","import { type Reader, SettingsManager } from \"@prose-reader/core\"\nimport { takeUntil, tap } from \"rxjs\"\nimport type { InputSettings, OutputSettings } from \"./types\"\n\nexport class GesturesSettingsManager extends SettingsManager<\n InputSettings,\n OutputSettings\n> {\n constructor(\n initialSettings: Partial<InputSettings>,\n private reader: Reader,\n ) {\n super(initialSettings)\n\n /**\n * Since we have settings that may be locked due to some reader settings\n * we need to update as soon as they update as well.\n */\n reader.settings.values$\n .pipe(\n tap(() => {\n this.update({})\n }),\n takeUntil(this.destroy$),\n )\n .subscribe()\n }\n\n getOutputSettings(inputSettings: InputSettings): OutputSettings {\n return {\n ...inputSettings,\n panNavigation:\n this.reader.settings.values.computedPageTurnMode === `scrollable`\n ? false\n : inputSettings.panNavigation,\n }\n }\n\n getDefaultSettings(): InputSettings {\n return {\n panNavigation: \"pan\",\n pinchCancelPan: true,\n fontScalePinchEnabled: true,\n fontScalePinchThrottleTime: 500,\n fontScaleMaxScale: 5,\n fontScaleMinScale: 0.2,\n zoomMaxScale: Infinity,\n ignore: [],\n }\n }\n}\n","[data-prose-reader-container=\"${id}\"] * {\n /* Make sure that touche actions are correctly dispatched no matter where the user interact */\n touch-action: inherit;\n}\n","import { HookManager, type Reader } from \"@prose-reader/core\"\nimport {\n PanRecognizer,\n PinchRecognizer,\n Recognizable,\n SwipeRecognizer,\n TapRecognizer,\n} from \"gesturx\"\nimport { combineLatest, merge, share, takeUntil, tap } from \"rxjs\"\nimport { name as packageName } from \"../package.json\"\nimport { registerPan } from \"./gestures/pan\"\nimport { registerPinch } from \"./gestures/pinch\"\nimport { registerSwipe } from \"./gestures/swipe\"\nimport { registerTaps } from \"./gestures/taps/registerTaps\"\nimport { GesturesSettingsManager } from \"./SettingsManager\"\nimport styles from \"./style.css?inline\"\nimport type { EnhancerAPI, Hook, InputSettings } from \"./types\"\n\nexport { isPositionInArea } from \"./gestures/taps/utils\"\nexport * from \"./types\"\n\nexport const gesturesEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\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 removeStylesheet = reader.utils.injectScopedCSS(\n document,\n packageName,\n styles,\n )\n\n const settingsManager = new GesturesSettingsManager(gestures, reader)\n\n const hookManager = new HookManager<Hook>()\n\n const pinchRecognizer = new PinchRecognizer({\n options: {\n /**\n * @important\n * Ideally we want pinch to triggers before pan so we can\n * capture zoom before starting panning.\n */\n posThreshold: 10,\n },\n })\n\n const failWithSelection = {\n start$: reader.selection.selectionStart$,\n end$: reader.selection.selectionEnd$,\n }\n\n const panRecognizer = new PanRecognizer({\n failWith: [pinchRecognizer, failWithSelection],\n options: {\n // we want to have some margin to trigger zoom\n posThreshold: 20,\n },\n })\n\n const tapRecognizer = new TapRecognizer({\n failWith: [panRecognizer],\n })\n\n const swipeRecognizer = new SwipeRecognizer({\n failWith: [failWithSelection],\n })\n\n const recognizable = new Recognizable({\n recognizers: [\n tapRecognizer,\n panRecognizer,\n swipeRecognizer,\n pinchRecognizer,\n ],\n disableTextSelection: false,\n })\n\n const tapGestures$ = registerTaps({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n recognizer: tapRecognizer,\n })\n\n const panGestures$ = registerPan({\n hookManager,\n reader,\n recognizer: panRecognizer,\n settingsManager,\n })\n\n const swipeGestures$ = registerSwipe({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const pinchGestures$ = registerPinch({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const containerUpdate$ = reader.context.watch(`rootElement`).pipe(\n tap((container) => {\n recognizable.update({\n container,\n })\n }),\n )\n\n const watchSettings$ = combineLatest([\n settingsManager.values$,\n panRecognizer.config$,\n ]).pipe(\n tap(([{ pinchCancelPan }, panRecognizerConfig]) => {\n const pinchAlreadyInFailWith =\n panRecognizerConfig.failWith?.includes(pinchRecognizer)\n\n if (pinchCancelPan && !pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: [\n ...(panRecognizerConfig.failWith ?? []),\n pinchRecognizer,\n ],\n })\n }\n\n if (!pinchCancelPan && pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: panRecognizerConfig.failWith?.filter(\n (recognizer) => recognizer !== pinchRecognizer,\n ),\n })\n }\n }),\n )\n\n const gestures$ = merge(\n pinchGestures$,\n tapGestures$,\n swipeGestures$,\n panGestures$,\n ).pipe(share())\n\n merge(containerUpdate$, watchSettings$, gestures$)\n .pipe(takeUntil(reader.$.destroy$))\n .subscribe()\n\n return {\n ...reader,\n destroy: () => {\n removeStylesheet()\n reader.destroy()\n settingsManager.destroy()\n },\n gestures: {\n settings: settingsManager,\n gestures$,\n hooks: hookManager,\n },\n }\n }\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prose-reader/enhancer-gestures",
3
- "version": "1.320.0",
3
+ "version": "1.322.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -13,7 +13,7 @@
13
13
  "exports": {
14
14
  ".": {
15
15
  "import": "./dist/index.js",
16
- "require": "./dist/index.umd.cjs"
16
+ "require": "./dist/index.cjs"
17
17
  }
18
18
  },
19
19
  "files": [
@@ -25,7 +25,7 @@
25
25
  "test": "vitest run --coverage"
26
26
  },
27
27
  "dependencies": {
28
- "@prose-reader/core": "^1.320.0"
28
+ "@prose-reader/core": "^1.322.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "gesturx": "*",
@@ -35,5 +35,5 @@
35
35
  "gesturx": "1.x",
36
36
  "rxjs": "7.x"
37
37
  },
38
- "gitHead": "dc464fdb493dbe767dc2012462ea4a74b45872a7"
38
+ "gitHead": "feb6bfbbae662045d9e4e10e45dfc129040e86ad"
39
39
  }
@@ -1,404 +0,0 @@
1
- (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@prose-reader/core"), require("gesturx"), require("rxjs")) : typeof define === "function" && define.amd ? define([
3
- "exports",
4
- "@prose-reader/core",
5
- "gesturx",
6
- "rxjs"
7
- ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["prose-reader-enhancer-gestures"] = {}, global._prose_reader_core, global.gesturx, global.rxjs));
8
- })(this, function(exports, _prose_reader_core, gesturx, rxjs) {
9
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
10
- //#region package.json
11
- var name = "@prose-reader/enhancer-gestures";
12
- //#endregion
13
- //#region src/gestures/pan.ts
14
- var registerPan = ({ reader, recognizer, settingsManager }) => {
15
- return settingsManager.values$.pipe((0, rxjs.switchMap)(({ panNavigation }) => {
16
- const panStart$ = recognizer.events$.pipe((0, rxjs.filter)((event) => event.type === `panStart`));
17
- const panMove$ = recognizer.events$.pipe((0, rxjs.filter)((event) => event.type === `panMove`));
18
- const panEnd$ = recognizer.events$.pipe((0, rxjs.filter)((event) => event.type === `panEnd`));
19
- return panStart$.pipe((0, rxjs.switchMap)((panStartEvent) => {
20
- /**
21
- * We use the last cumulative delta to derive the new event atomic delta.
22
- * This is because panning the zoom does not necessarily means the zoom position
23
- * will always changes. If the user keep dragging while the zoom is blocked, we want
24
- * it to move the other direction when he start dragging the other way.
25
- * We cannot use the `reader.zoom.state.currentPosition` as previous position
26
- * and the event.deltaX to compute the new zoom position.
27
- */
28
- let lastDelta = {
29
- x: 0,
30
- y: 0
31
- };
32
- const moveAndEnd$ = (0, rxjs.merge)(panMove$, panEnd$).pipe((0, rxjs.tap)((event) => {
33
- const isZooming = reader.zoom.state.isZooming;
34
- const isZoomingIn = reader.zoom.state.currentScale > 1;
35
- /**
36
- * When user is zooming in, we don't navigate anymore.
37
- * We still allow the pan gesture to move the zoomed controlled
38
- * viewport even when pan navigation itself is disabled.
39
- */
40
- if (isZooming && isZoomingIn) {
41
- const deltaX = event.deltaX - lastDelta.x;
42
- const deltaY = event.deltaY - lastDelta.y;
43
- lastDelta = {
44
- x: event.deltaX,
45
- y: event.deltaY
46
- };
47
- reader.zoom.move({
48
- x: reader.zoom.state.currentPosition.x + deltaX,
49
- y: reader.zoom.state.currentPosition.y + deltaY
50
- }, { constrain: "within-viewport" });
51
- return;
52
- }
53
- if (panNavigation !== "pan") return;
54
- if (event.type === `panMove`) {
55
- if (!reader.navigation.panNavigator.value.isStarted) {
56
- reader.navigation.panNavigator.start({
57
- x: event.deltaX,
58
- y: event.deltaY
59
- });
60
- return;
61
- }
62
- reader.navigation.panNavigator.panMoveTo({
63
- x: event.deltaX,
64
- y: event.deltaY
65
- });
66
- return;
67
- }
68
- if (event.type === `panEnd` && reader.navigation.panNavigator.value.isStarted) reader.navigation.panNavigator.stop({
69
- x: event.deltaX,
70
- y: event.deltaY
71
- });
72
- }));
73
- return (0, rxjs.merge)((0, rxjs.of)(panStartEvent), moveAndEnd$).pipe((0, rxjs.map)((event) => ({
74
- type: "pan",
75
- gestureEvent: event
76
- })));
77
- }));
78
- }));
79
- };
80
- //#endregion
81
- //#region src/gestures/pinch.ts
82
- var isHtmlImageElement = (target) => (0, _prose_reader_core.isHtmlElement)(target) && !!target.ownerDocument.defaultView && target instanceof target.ownerDocument.defaultView.HTMLImageElement;
83
- var registerPinch = ({ reader, recognizable, settingsManager }) => {
84
- const pinchStart$ = recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)((event) => event.type === "pinchStart"));
85
- const pinchMove$ = recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)((event) => event.type === "pinchMove"));
86
- const pinchEnd$ = recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)((event) => event.type === "pinchEnd"));
87
- const shouldStartZoom = (target) => isHtmlImageElement(target) && !reader.zoom.state.isZooming;
88
- return settingsManager.values$.pipe((0, rxjs.switchMap)(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {
89
- return (0, rxjs.merge)(pinchStart$.pipe((0, rxjs.switchMap)(() => {
90
- const startScale = reader.zoom.state.currentScale;
91
- return pinchMove$.pipe((0, rxjs.withLatestFrom)(reader.viewportState$), (0, rxjs.map)(([event, viewportState]) => {
92
- const newScale = startScale * event.scale;
93
- /**
94
- * @important
95
- * We don't want to trigger zoom gestures if there is a pan navigation
96
- * in progress. This can happens if the user start panning and then adds
97
- * another finger triggering a pinch.
98
- */
99
- if (viewportState === "busy") return event;
100
- if (!reader.zoom.state.isZooming && event.scale > 1) {
101
- reader.zoom.enter({
102
- animate: false,
103
- scale: newScale
104
- });
105
- return event;
106
- }
107
- if (reader.zoom.state.isZooming) {
108
- if (newScale < 1) reader.zoom.exit();
109
- else reader.zoom.scaleAt(Math.min(newScale, settingsManager.values.zoomMaxScale), { constrain: "within-viewport" });
110
- return event;
111
- }
112
- return event;
113
- }));
114
- })), !fontScalePinchEnabled ? rxjs.EMPTY : pinchStart$.pipe((0, rxjs.withLatestFrom)(reader.viewportState$), (0, rxjs.switchMap)(([pinchStartEvent, viewportState]) => {
115
- if (viewportState === "busy" || shouldStartZoom(pinchStartEvent.event.target) || reader.zoom.state.isZooming) return rxjs.EMPTY;
116
- const lastFontScaleOnPinchStart = reader.settings.values.fontScale;
117
- return pinchMove$.pipe((0, rxjs.throttleTime)(fontScalePinchThrottleTime, rxjs.animationFrameScheduler, { trailing: true }), (0, rxjs.tap)((event) => {
118
- const newScale = Number.parseFloat((lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2));
119
- const newMinMaxedFontScale = Math.max(Math.min(newScale, settingsManager.values.fontScaleMaxScale), settingsManager.values.fontScaleMinScale);
120
- reader.settings.update({ fontScale: newMinMaxedFontScale });
121
- }), (0, rxjs.takeUntil)(pinchEnd$));
122
- }))).pipe((0, rxjs.map)((event) => ({
123
- type: "pinch",
124
- gestureEvent: event
125
- })));
126
- }));
127
- };
128
- //#endregion
129
- //#region src/gestures/swipe.ts
130
- var isSwipeEvent = (event) => event.type === "swipe";
131
- var registerSwipe = ({ reader, recognizable, settingsManager }) => {
132
- return settingsManager.values$.pipe((0, rxjs.switchMap)(({ panNavigation }) => panNavigation !== "swipe" ? rxjs.EMPTY : recognizable.events$.pipe((0, rxjs.map)(({ event }) => event), (0, rxjs.filter)(isSwipeEvent), (0, rxjs.tap)((event) => {
133
- const { computedPageTurnDirection } = reader.settings.values;
134
- if (computedPageTurnDirection === "vertical") {
135
- if (event.velocityY < -.5) reader?.navigation.turnRight();
136
- if (event.velocityY > .5) reader?.navigation.turnLeft();
137
- } else {
138
- if (event.velocityX < -.5) reader?.navigation.turnRight();
139
- if (event.velocityX > .5) reader?.navigation.turnLeft();
140
- }
141
- }), (0, rxjs.map)((event) => ({
142
- type: "swipe",
143
- gestureEvent: event
144
- })))));
145
- };
146
- //#endregion
147
- //#region src/utils.ts
148
- var isNotLink = (event) => {
149
- const target = event.event.target;
150
- if ((0, _prose_reader_core.isHtmlElement)(target) && target.tagName === "a") return false;
151
- return true;
152
- };
153
- var getPositionRelativeToContainer = (event, containerElementRect) => {
154
- const { x, y } = event;
155
- const { left, top } = containerElementRect;
156
- return {
157
- x: x - left,
158
- y: y - top
159
- };
160
- };
161
- var istMatchingSelectors = (selectors, event) => {
162
- const target = event.event.target;
163
- if (!(0, _prose_reader_core.isHtmlElement)(target)) return false;
164
- return !!selectors.find((selector) => {
165
- if (target.matches(selector)) return true;
166
- if (target.closest(selector)) return true;
167
- return false;
168
- });
169
- };
170
- //#endregion
171
- //#region src/gestures/taps/utils.ts
172
- var isPositionInArea = (position, area, containerSize) => {
173
- const { x, y } = position;
174
- const { width, height } = containerSize;
175
- switch (area.type) {
176
- case "margins": {
177
- const { top, bottom, left, right } = area;
178
- const inTop = top !== void 0 ? y < height * top : true;
179
- const inBottom = bottom !== void 0 ? y > height * (1 - bottom) : true;
180
- const inLeft = left !== void 0 ? x < width * left : true;
181
- const inRight = right !== void 0 ? x > width * (1 - right) : true;
182
- return top !== void 0 && inTop || bottom !== void 0 && inBottom || left !== void 0 && inLeft || right !== void 0 && inRight;
183
- }
184
- case "rectangle": {
185
- const { x: rectX, y: rectY, width: rectWidth, height: rectHeight, unit = "%" } = area;
186
- const actualX = unit === "%" ? width * (rectX / 100) : rectX;
187
- const actualY = unit === "%" ? height * (rectY / 100) : rectY;
188
- const actualWidth = unit === "%" ? width * (rectWidth / 100) : rectWidth;
189
- const actualHeight = unit === "%" ? height * (rectHeight / 100) : rectHeight;
190
- return x >= actualX && x <= actualX + actualWidth && y >= actualY && y <= actualY + actualHeight;
191
- }
192
- case "corner": {
193
- const { corner, size, unit = "%" } = area;
194
- const actualSize = unit === "%" ? Math.min(width, height) * (size / 100) : size;
195
- switch (corner) {
196
- case "top-left": return x < actualSize && y < actualSize;
197
- case "top-right": return x > width - actualSize && y < actualSize;
198
- case "bottom-left": return x < actualSize && y > height - actualSize;
199
- case "bottom-right": return x > width - actualSize && y > height - actualSize;
200
- default: return false;
201
- }
202
- }
203
- case "center": {
204
- const { width: centerWidth, height: centerHeight, unit = "%" } = area;
205
- const actualWidth = unit === "%" ? width * (centerWidth / 100) : centerWidth;
206
- const actualHeight = unit === "%" ? height * (centerHeight / 100) : centerHeight;
207
- const centerX = width / 2;
208
- const centerY = height / 2;
209
- return x >= centerX - actualWidth / 2 && x <= centerX + actualWidth / 2 && y >= centerY - actualHeight / 2 && y <= centerY + actualHeight / 2;
210
- }
211
- default: return false;
212
- }
213
- };
214
- var calculatePageTurnLinearMargin = (screenWidth) => {
215
- const minMargin = .15;
216
- const maxMargin = .2;
217
- const minWidth = 320;
218
- const maxWidth = 1200;
219
- if (screenWidth <= minWidth) return maxMargin;
220
- if (screenWidth >= maxWidth) return minMargin;
221
- return maxMargin - (screenWidth - minWidth) / (maxWidth - minWidth) * (maxMargin - minMargin);
222
- };
223
- //#endregion
224
- //#region src/gestures/taps/registerTaps.ts
225
- var registerTaps = ({ reader, recognizable, hookManager, settingsManager, recognizer }) => {
226
- return recognizable.events$.pipe((0, rxjs.filter)((event) => event.recognizer === recognizer), (0, rxjs.withLatestFrom)(reader.context.watch(`rootElement`), reader.spine.element$), (0, rxjs.switchMap)(([{ event }, containerElement, spineElement]) => {
227
- if (!containerElement || !spineElement) return rxjs.EMPTY;
228
- const normalizedEvent = event.event;
229
- const { computedPageTurnDirection, computedPageTurnMode } = reader.settings.values;
230
- if (event.type === "tap" && isNotLink(event) && !istMatchingSelectors(settingsManager.values.ignore, event)) {
231
- if (`x` in normalizedEvent) {
232
- const containerElementRect = containerElement.getBoundingClientRect();
233
- const width = containerElementRect.width;
234
- const pageTurnMargin = calculatePageTurnLinearMargin(width);
235
- const positionInContainer = getPositionRelativeToContainer(normalizedEvent, containerElementRect);
236
- const positionInSpineNonTransformed = reader.coordinates.getSpinePositionFromClientPosition(normalizedEvent);
237
- const spineItemPageInfo = positionInSpineNonTransformed ? reader.spine.locator.getSpineItemPagePositionFromSpinePosition(positionInSpineNonTransformed) : void 0;
238
- return (0, rxjs.combineLatest)([...hookManager.execute("beforeTapGesture", { event$: (0, rxjs.of)({
239
- event,
240
- page: spineItemPageInfo
241
- }) }), (0, rxjs.of)(true)]).pipe((0, rxjs.first)(), (0, rxjs.filter)((results) => !results.some((result) => result === false)), (0, rxjs.map)(() => {
242
- const isZoomedIn = reader.zoom.state.isZooming && reader.zoom.state.currentScale > 1;
243
- if (computedPageTurnMode === "scrollable" || isZoomedIn) return {
244
- type: "tap",
245
- gestureEvent: event,
246
- handled: false
247
- };
248
- if (computedPageTurnDirection === "horizontal" && isPositionInArea(positionInContainer, {
249
- type: "margins",
250
- left: pageTurnMargin
251
- }, containerElementRect)) reader.navigation.turnLeftOrTop();
252
- else if (computedPageTurnDirection === "vertical" && isPositionInArea(positionInContainer, {
253
- type: "margins",
254
- top: pageTurnMargin
255
- }, containerElementRect)) reader.navigation.turnLeftOrTop();
256
- else if (computedPageTurnDirection === "vertical" && isPositionInArea(positionInContainer, {
257
- type: "margins",
258
- bottom: pageTurnMargin
259
- }, containerElementRect)) reader.navigation.turnRightOrBottom();
260
- else if (computedPageTurnDirection === "horizontal" && isPositionInArea(positionInContainer, {
261
- type: "margins",
262
- right: pageTurnMargin
263
- }, containerElementRect)) reader.navigation.turnRightOrBottom();
264
- else return {
265
- type: "tap",
266
- gestureEvent: event,
267
- handled: false
268
- };
269
- return {
270
- type: "tap",
271
- gestureEvent: event,
272
- handled: true
273
- };
274
- }));
275
- }
276
- }
277
- return rxjs.EMPTY;
278
- }));
279
- };
280
- //#endregion
281
- //#region src/SettingsManager.ts
282
- var GesturesSettingsManager = class extends _prose_reader_core.SettingsManager {
283
- reader;
284
- constructor(initialSettings, reader) {
285
- super(initialSettings);
286
- this.reader = reader;
287
- /**
288
- * Since we have settings that may be locked due to some reader settings
289
- * we need to update as soon as they update as well.
290
- */
291
- reader.settings.values$.pipe((0, rxjs.tap)(() => {
292
- this.update({});
293
- }), (0, rxjs.takeUntil)(this.destroy$)).subscribe();
294
- }
295
- getOutputSettings(inputSettings) {
296
- return {
297
- ...inputSettings,
298
- panNavigation: this.reader.settings.values.computedPageTurnMode === `scrollable` ? false : inputSettings.panNavigation
299
- };
300
- }
301
- getDefaultSettings() {
302
- return {
303
- panNavigation: "pan",
304
- pinchCancelPan: true,
305
- fontScalePinchEnabled: true,
306
- fontScalePinchThrottleTime: 500,
307
- fontScaleMaxScale: 5,
308
- fontScaleMinScale: .2,
309
- zoomMaxScale: Infinity,
310
- ignore: []
311
- };
312
- }
313
- };
314
- //#endregion
315
- //#region src/style.css?inline
316
- var style_default = "[data-prose-reader-container=\"${id}\"] * {\n /* Make sure that touche actions are correctly dispatched no matter where the user interact */\n touch-action: inherit;\n}\n";
317
- //#endregion
318
- //#region src/index.ts
319
- var gesturesEnhancer = (next) => (options) => {
320
- const { gestures = {}, ...rest } = options;
321
- const reader = next(rest);
322
- const removeStylesheet = reader.utils.injectScopedCSS(document, name, style_default);
323
- const settingsManager = new GesturesSettingsManager(gestures, reader);
324
- const hookManager = new _prose_reader_core.HookManager();
325
- const pinchRecognizer = new gesturx.PinchRecognizer({ options: {
326
- /**
327
- * @important
328
- * Ideally we want pinch to triggers before pan so we can
329
- * capture zoom before starting panning.
330
- */
331
- posThreshold: 10 } });
332
- const failWithSelection = {
333
- start$: reader.selection.selectionStart$,
334
- end$: reader.selection.selectionEnd$
335
- };
336
- const panRecognizer = new gesturx.PanRecognizer({
337
- failWith: [pinchRecognizer, failWithSelection],
338
- options: { posThreshold: 20 }
339
- });
340
- const tapRecognizer = new gesturx.TapRecognizer({ failWith: [panRecognizer] });
341
- const recognizable = new gesturx.Recognizable({
342
- recognizers: [
343
- tapRecognizer,
344
- panRecognizer,
345
- new gesturx.SwipeRecognizer({ failWith: [failWithSelection] }),
346
- pinchRecognizer
347
- ],
348
- disableTextSelection: false
349
- });
350
- const tapGestures$ = registerTaps({
351
- hookManager,
352
- reader,
353
- recognizable,
354
- settingsManager,
355
- recognizer: tapRecognizer
356
- });
357
- const panGestures$ = registerPan({
358
- hookManager,
359
- reader,
360
- recognizer: panRecognizer,
361
- settingsManager
362
- });
363
- const swipeGestures$ = registerSwipe({
364
- hookManager,
365
- reader,
366
- recognizable,
367
- settingsManager
368
- });
369
- const pinchGestures$ = registerPinch({
370
- hookManager,
371
- reader,
372
- recognizable,
373
- settingsManager
374
- });
375
- const containerUpdate$ = reader.context.watch(`rootElement`).pipe((0, rxjs.tap)((container) => {
376
- recognizable.update({ container });
377
- }));
378
- const watchSettings$ = (0, rxjs.combineLatest)([settingsManager.values$, panRecognizer.config$]).pipe((0, rxjs.tap)(([{ pinchCancelPan }, panRecognizerConfig]) => {
379
- const pinchAlreadyInFailWith = panRecognizerConfig.failWith?.includes(pinchRecognizer);
380
- if (pinchCancelPan && !pinchAlreadyInFailWith) panRecognizer.update({ failWith: [...panRecognizerConfig.failWith ?? [], pinchRecognizer] });
381
- if (!pinchCancelPan && pinchAlreadyInFailWith) panRecognizer.update({ failWith: panRecognizerConfig.failWith?.filter((recognizer) => recognizer !== pinchRecognizer) });
382
- }));
383
- const gestures$ = (0, rxjs.merge)(pinchGestures$, tapGestures$, swipeGestures$, panGestures$).pipe((0, rxjs.share)());
384
- (0, rxjs.merge)(containerUpdate$, watchSettings$, gestures$).pipe((0, rxjs.takeUntil)(reader.$.destroy$)).subscribe();
385
- return {
386
- ...reader,
387
- destroy: () => {
388
- removeStylesheet();
389
- reader.destroy();
390
- settingsManager.destroy();
391
- },
392
- gestures: {
393
- settings: settingsManager,
394
- gestures$,
395
- hooks: hookManager
396
- }
397
- };
398
- };
399
- //#endregion
400
- exports.gesturesEnhancer = gesturesEnhancer;
401
- exports.isPositionInArea = isPositionInArea;
402
- });
403
-
404
- //# sourceMappingURL=index.umd.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.umd.cjs","names":[],"sources":["../package.json","../src/gestures/pan.ts","../src/gestures/pinch.ts","../src/gestures/swipe.ts","../src/utils.ts","../src/gestures/taps/utils.ts","../src/gestures/taps/registerTaps.ts","../src/SettingsManager.ts","../src/style.css?inline","../src/index.ts"],"sourcesContent":["{\n \"name\": \"@prose-reader/enhancer-gestures\",\n \"version\": \"1.319.0\",\n \"type\": \"module\",\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"private\": false,\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.umd.cjs\"\n }\n },\n \"files\": [\n \"/dist\"\n ],\n \"scripts\": {\n \"start\": \"vite build --watch --mode development\",\n \"build\": \"tsc && vite build\",\n \"test\": \"vitest run --coverage\"\n },\n \"dependencies\": {\n \"@prose-reader/core\": \"^1.319.0\"\n },\n \"devDependencies\": {\n \"gesturx\": \"*\",\n \"rxjs\": \"*\"\n },\n \"peerDependencies\": {\n \"gesturx\": \"1.x\",\n \"rxjs\": \"7.x\"\n },\n \"gitHead\": \"4601e14dcacf50b2295cb343582a7ef2c7e1eedc\"\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { PanRecognizer } from \"gesturx\"\nimport { filter, map, merge, of, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { Hook } from \"../types\"\n\nexport const registerPan = ({\n reader,\n recognizer,\n settingsManager,\n}: {\n recognizer: PanRecognizer\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) => {\n const panStart$ = recognizer.events$.pipe(\n filter((event) => event.type === `panStart`),\n )\n const panMove$ = recognizer.events$.pipe(\n filter((event) => event.type === `panMove`),\n )\n const panEnd$ = recognizer.events$.pipe(\n filter((event) => event.type === `panEnd`),\n )\n\n const pan$ = panStart$.pipe(\n switchMap((panStartEvent) => {\n /**\n * We use the last cumulative delta to derive the new event atomic delta.\n * This is because panning the zoom does not necessarily means the zoom position\n * will always changes. If the user keep dragging while the zoom is blocked, we want\n * it to move the other direction when he start dragging the other way.\n * We cannot use the `reader.zoom.state.currentPosition` as previous position\n * and the event.deltaX to compute the new zoom position.\n */\n let lastDelta = { x: 0, y: 0 }\n\n const moveAndEnd$ = merge(panMove$, panEnd$).pipe(\n tap((event) => {\n const isZooming = reader.zoom.state.isZooming\n const isZoomingIn = reader.zoom.state.currentScale > 1\n\n /**\n * When user is zooming in, we don't navigate anymore.\n * We still allow the pan gesture to move the zoomed controlled\n * viewport even when pan navigation itself is disabled.\n */\n if (isZooming && isZoomingIn) {\n const deltaX = event.deltaX - lastDelta.x\n const deltaY = event.deltaY - lastDelta.y\n\n lastDelta = {\n x: event.deltaX,\n y: event.deltaY,\n }\n\n reader.zoom.move(\n {\n x: reader.zoom.state.currentPosition.x + deltaX,\n y: reader.zoom.state.currentPosition.y + deltaY,\n },\n {\n constrain: \"within-viewport\",\n },\n )\n\n return\n }\n\n if (panNavigation !== \"pan\") return\n\n if (event.type === `panMove`) {\n if (!reader.navigation.panNavigator.value.isStarted) {\n reader.navigation.panNavigator.start({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n reader.navigation.panNavigator.panMoveTo({\n x: event.deltaX,\n y: event.deltaY,\n })\n\n return\n }\n\n if (\n event.type === `panEnd` &&\n reader.navigation.panNavigator.value.isStarted\n ) {\n reader.navigation.panNavigator.stop({\n x: event.deltaX,\n y: event.deltaY,\n })\n }\n }),\n )\n\n return merge(of(panStartEvent), moveAndEnd$).pipe(\n map((event) => ({\n type: \"pan\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n\n return pan$\n }),\n )\n\n return gestures$\n}\n","import {\n type HookManager,\n isHtmlElement,\n type Reader,\n} from \"@prose-reader/core\"\nimport type { PinchEvent } from \"gesturx\"\nimport {\n animationFrameScheduler,\n EMPTY,\n filter,\n map,\n merge,\n switchMap,\n takeUntil,\n tap,\n throttleTime,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../types\"\n\nconst isHtmlImageElement = (\n target: EventTarget | null,\n): 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 settingsManager: GesturesSettingsManager\n}) => {\n const pinchStart$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchStart\"),\n )\n\n const pinchMove$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchMove\"),\n )\n\n const pinchEnd$ = recognizable.events$.pipe(\n map(({ event }) => event),\n filter((event): event is PinchEvent => event.type === \"pinchEnd\"),\n )\n\n const shouldStartZoom = (\n target: EventTarget | null,\n ): target is HTMLImageElement =>\n isHtmlImageElement(target) && !reader.zoom.state.isZooming\n\n return settingsManager.values$.pipe(\n switchMap(({ fontScalePinchEnabled, fontScalePinchThrottleTime }) => {\n const zoomGestures$ = pinchStart$.pipe(\n switchMap(() => {\n const startScale = reader.zoom.state.currentScale\n\n return pinchMove$.pipe(\n withLatestFrom(reader.viewportState$),\n map(([event, viewportState]) => {\n // symmetric scaling calculation\n const newScale = startScale * event.scale\n\n /**\n * @important\n * We don't want to trigger zoom gestures if there is a pan navigation\n * in progress. This can happens if the user start panning and then adds\n * another finger triggering a pinch.\n */\n if (viewportState === \"busy\") {\n return event\n }\n\n if (!reader.zoom.state.isZooming && event.scale > 1) {\n reader.zoom.enter({ animate: false, scale: newScale })\n\n return event\n }\n\n if (reader.zoom.state.isZooming) {\n if (newScale < 1) {\n reader.zoom.exit()\n } else {\n reader.zoom.scaleAt(\n Math.min(newScale, settingsManager.values.zoomMaxScale),\n {\n constrain: \"within-viewport\",\n },\n )\n }\n\n return event\n }\n\n return event\n }),\n )\n }),\n )\n\n const watchForFontScaleChange$ = !fontScalePinchEnabled\n ? EMPTY\n : pinchStart$.pipe(\n withLatestFrom(reader.viewportState$),\n switchMap(([pinchStartEvent, viewportState]) => {\n if (\n viewportState === \"busy\" ||\n shouldStartZoom(pinchStartEvent.event.target) ||\n reader.zoom.state.isZooming\n )\n return EMPTY\n\n const lastFontScaleOnPinchStart = reader.settings.values.fontScale\n\n return pinchMove$.pipe(\n throttleTime(\n fontScalePinchThrottleTime,\n animationFrameScheduler,\n {\n trailing: true,\n },\n ),\n tap((event) => {\n const newScale = Number.parseFloat(\n (lastFontScaleOnPinchStart + (event.scale - 1)).toFixed(2),\n )\n\n const newMinMaxedFontScale = Math.max(\n Math.min(\n newScale,\n settingsManager.values.fontScaleMaxScale,\n ),\n settingsManager.values.fontScaleMinScale,\n )\n\n reader.settings.update({\n fontScale: newMinMaxedFontScale,\n })\n }),\n takeUntil(pinchEnd$),\n )\n }),\n )\n\n return merge(zoomGestures$, watchForFontScaleChange$).pipe(\n map((event) => ({\n type: \"pinch\" as const,\n gestureEvent: event,\n })),\n )\n }),\n )\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { SwipeEvent } from \"gesturx\"\nimport { EMPTY, filter, map, switchMap, tap } from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../SettingsManager\"\nimport type { GestureEvent, GestureRecognizable, Hook } from \"../types\"\n\nconst isSwipeEvent = (event: GestureEvent[\"event\"]): event is SwipeEvent =>\n event.type === \"swipe\"\n\nexport const registerSwipe = ({\n reader,\n recognizable,\n settingsManager,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = settingsManager.values$.pipe(\n switchMap(({ panNavigation }) =>\n panNavigation !== \"swipe\"\n ? EMPTY\n : recognizable.events$.pipe(\n map(({ event }) => event),\n filter(isSwipeEvent),\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 map((event) => ({ type: \"swipe\" as const, gestureEvent: event })),\n ),\n ),\n )\n\n return gestures$\n}\n","import { isHtmlElement } from \"@prose-reader/core\"\nimport type { GestureEvent } from \"./types\"\n\nexport const isNotLink = (event: GestureEvent[\"event\"]) => {\n const target = event.event.target\n\n if (isHtmlElement(target) && target.tagName === \"a\") return false\n\n return true\n}\n\nexport const getPositionRelativeToContainer = (\n event: { x: number; y: number },\n containerElementRect: DOMRectReadOnly,\n) => {\n const { x, y } = event\n const { left, top } = containerElementRect\n\n return {\n x: x - left,\n y: y - top,\n }\n}\n\nexport const istMatchingSelectors = (\n selectors: string[],\n event: GestureEvent[\"event\"],\n): boolean => {\n const target = event.event.target\n\n if (!isHtmlElement(target)) return false\n\n const match = selectors.find((selector) => {\n // Check if the target matches the selector directly\n if (target.matches(selector)) return true\n\n // Check if the target is within an element matching the selector\n if (target.closest(selector)) return true\n\n return false\n })\n\n return !!match\n}\n","import type { TapArea } from \"./types\"\n\nexport const isPositionInArea = (\n position: { x: number; y: number },\n area: TapArea,\n containerSize: { width: number; height: number },\n): boolean => {\n const { x, y } = position\n const { width, height } = containerSize\n\n switch (area.type) {\n case \"margins\": {\n const { top, bottom, left, right } = area\n const inTop = top !== undefined ? y < height * top : true\n const inBottom = bottom !== undefined ? y > height * (1 - bottom) : true\n const inLeft = left !== undefined ? x < width * left : true\n const inRight = right !== undefined ? x > width * (1 - right) : true\n\n return (\n (top !== undefined && inTop) ||\n (bottom !== undefined && inBottom) ||\n (left !== undefined && inLeft) ||\n (right !== undefined && inRight)\n )\n }\n\n case \"rectangle\": {\n const {\n x: rectX,\n y: rectY,\n width: rectWidth,\n height: rectHeight,\n unit = \"%\",\n } = area\n const actualX = unit === \"%\" ? width * (rectX / 100) : rectX\n const actualY = unit === \"%\" ? height * (rectY / 100) : rectY\n const actualWidth = unit === \"%\" ? width * (rectWidth / 100) : rectWidth\n const actualHeight =\n unit === \"%\" ? height * (rectHeight / 100) : rectHeight\n\n return (\n x >= actualX &&\n x <= actualX + actualWidth &&\n y >= actualY &&\n y <= actualY + actualHeight\n )\n }\n\n case \"corner\": {\n const { corner, size, unit = \"%\" } = area\n const actualSize =\n unit === \"%\" ? Math.min(width, height) * (size / 100) : size\n\n switch (corner) {\n case \"top-left\":\n return x < actualSize && y < actualSize\n case \"top-right\":\n return x > width - actualSize && y < actualSize\n case \"bottom-left\":\n return x < actualSize && y > height - actualSize\n case \"bottom-right\":\n return x > width - actualSize && y > height - actualSize\n default:\n return false\n }\n }\n\n case \"center\": {\n const { width: centerWidth, height: centerHeight, unit = \"%\" } = area\n const actualWidth =\n unit === \"%\" ? width * (centerWidth / 100) : centerWidth\n const actualHeight =\n unit === \"%\" ? height * (centerHeight / 100) : centerHeight\n const centerX = width / 2\n const centerY = height / 2\n\n return (\n x >= centerX - actualWidth / 2 &&\n x <= centerX + actualWidth / 2 &&\n y >= centerY - actualHeight / 2 &&\n y <= centerY + actualHeight / 2\n )\n }\n\n default:\n return false\n }\n}\n\nexport const calculatePageTurnLinearMargin = (screenWidth: number): number => {\n const minMargin = 0.15\n const maxMargin = 0.2\n const minWidth = 320\n const maxWidth = 1200\n\n if (screenWidth <= minWidth) return maxMargin\n if (screenWidth >= maxWidth) return minMargin\n\n // Linear interpolation between min and max\n const ratio = (screenWidth - minWidth) / (maxWidth - minWidth)\n return maxMargin - ratio * (maxMargin - minMargin)\n}\n","import type { HookManager, Reader } from \"@prose-reader/core\"\nimport type { TapRecognizer } from \"gesturx\"\nimport {\n combineLatest,\n EMPTY,\n filter,\n first,\n map,\n of,\n switchMap,\n withLatestFrom,\n} from \"rxjs\"\nimport type { GesturesSettingsManager } from \"../../SettingsManager\"\nimport type { GestureRecognizable, Hook } from \"../../types\"\nimport {\n getPositionRelativeToContainer,\n isNotLink,\n istMatchingSelectors,\n} from \"../../utils\"\nimport { calculatePageTurnLinearMargin, isPositionInArea } from \"./utils\"\n\nexport const registerTaps = ({\n reader,\n recognizable,\n hookManager,\n settingsManager,\n recognizer,\n}: {\n recognizable: GestureRecognizable\n reader: Reader\n hookManager: HookManager<Hook>\n recognizer: TapRecognizer\n settingsManager: GesturesSettingsManager\n}) => {\n const gestures$ = recognizable.events$.pipe(\n filter((event) => event.recognizer === recognizer),\n withLatestFrom(reader.context.watch(`rootElement`), reader.spine.element$),\n switchMap(([{ event }, containerElement, spineElement]) => {\n if (!containerElement || !spineElement) return EMPTY\n\n const normalizedEvent = event.event\n const { computedPageTurnDirection, computedPageTurnMode } =\n reader.settings.values\n\n if (\n event.type === \"tap\" &&\n isNotLink(event) &&\n !istMatchingSelectors(settingsManager.values.ignore, event)\n ) {\n if (`x` in normalizedEvent) {\n const containerElementRect = containerElement.getBoundingClientRect()\n const width = containerElementRect.width\n const pageTurnMargin = calculatePageTurnLinearMargin(width)\n const positionInContainer = getPositionRelativeToContainer(\n normalizedEvent,\n containerElementRect,\n )\n\n const positionInSpineNonTransformed =\n reader.coordinates.getSpinePositionFromClientPosition(\n normalizedEvent,\n )\n\n const spineItemPageInfo = positionInSpineNonTransformed\n ? reader.spine.locator.getSpineItemPagePositionFromSpinePosition(\n positionInSpineNonTransformed,\n )\n : undefined\n\n const beforeTapResults$ = hookManager.execute(\"beforeTapGesture\", {\n event$: of({ event, page: spineItemPageInfo }),\n })\n\n return combineLatest([...beforeTapResults$, of(true)]).pipe(\n first(),\n filter((results) => !results.some((result) => result === false)),\n map(() => {\n const isZoomedIn =\n reader.zoom.state.isZooming &&\n reader.zoom.state.currentScale > 1\n\n if (computedPageTurnMode === \"scrollable\" || isZoomedIn) {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", left: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", top: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnLeftOrTop()\n } else if (\n computedPageTurnDirection === \"vertical\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", bottom: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else if (\n computedPageTurnDirection === \"horizontal\" &&\n isPositionInArea(\n positionInContainer,\n { type: \"margins\", right: pageTurnMargin },\n containerElementRect,\n )\n ) {\n reader.navigation.turnRightOrBottom()\n } else {\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: false,\n }\n }\n\n return {\n type: \"tap\" as const,\n gestureEvent: event,\n handled: true,\n }\n }),\n )\n }\n }\n\n return EMPTY\n }),\n )\n\n return gestures$\n}\n","import { type Reader, SettingsManager } from \"@prose-reader/core\"\nimport { takeUntil, tap } from \"rxjs\"\nimport type { InputSettings, OutputSettings } from \"./types\"\n\nexport class GesturesSettingsManager extends SettingsManager<\n InputSettings,\n OutputSettings\n> {\n constructor(\n initialSettings: Partial<InputSettings>,\n private reader: Reader,\n ) {\n super(initialSettings)\n\n /**\n * Since we have settings that may be locked due to some reader settings\n * we need to update as soon as they update as well.\n */\n reader.settings.values$\n .pipe(\n tap(() => {\n this.update({})\n }),\n takeUntil(this.destroy$),\n )\n .subscribe()\n }\n\n getOutputSettings(inputSettings: InputSettings): OutputSettings {\n return {\n ...inputSettings,\n panNavigation:\n this.reader.settings.values.computedPageTurnMode === `scrollable`\n ? false\n : inputSettings.panNavigation,\n }\n }\n\n getDefaultSettings(): InputSettings {\n return {\n panNavigation: \"pan\",\n pinchCancelPan: true,\n fontScalePinchEnabled: true,\n fontScalePinchThrottleTime: 500,\n fontScaleMaxScale: 5,\n fontScaleMinScale: 0.2,\n zoomMaxScale: Infinity,\n ignore: [],\n }\n }\n}\n","[data-prose-reader-container=\"${id}\"] * {\n /* Make sure that touche actions are correctly dispatched no matter where the user interact */\n touch-action: inherit;\n}\n","import { HookManager, type Reader } from \"@prose-reader/core\"\nimport {\n PanRecognizer,\n PinchRecognizer,\n Recognizable,\n SwipeRecognizer,\n TapRecognizer,\n} from \"gesturx\"\nimport { combineLatest, merge, share, takeUntil, tap } from \"rxjs\"\nimport { name as packageName } from \"../package.json\"\nimport { registerPan } from \"./gestures/pan\"\nimport { registerPinch } from \"./gestures/pinch\"\nimport { registerSwipe } from \"./gestures/swipe\"\nimport { registerTaps } from \"./gestures/taps/registerTaps\"\nimport { GesturesSettingsManager } from \"./SettingsManager\"\nimport styles from \"./style.css?inline\"\nimport type { EnhancerAPI, Hook, InputSettings } from \"./types\"\n\nexport { isPositionInArea } from \"./gestures/taps/utils\"\nexport * from \"./types\"\n\nexport const gesturesEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\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 removeStylesheet = reader.utils.injectScopedCSS(\n document,\n packageName,\n styles,\n )\n\n const settingsManager = new GesturesSettingsManager(gestures, reader)\n\n const hookManager = new HookManager<Hook>()\n\n const pinchRecognizer = new PinchRecognizer({\n options: {\n /**\n * @important\n * Ideally we want pinch to triggers before pan so we can\n * capture zoom before starting panning.\n */\n posThreshold: 10,\n },\n })\n\n const failWithSelection = {\n start$: reader.selection.selectionStart$,\n end$: reader.selection.selectionEnd$,\n }\n\n const panRecognizer = new PanRecognizer({\n failWith: [pinchRecognizer, failWithSelection],\n options: {\n // we want to have some margin to trigger zoom\n posThreshold: 20,\n },\n })\n\n const tapRecognizer = new TapRecognizer({\n failWith: [panRecognizer],\n })\n\n const swipeRecognizer = new SwipeRecognizer({\n failWith: [failWithSelection],\n })\n\n const recognizable = new Recognizable({\n recognizers: [\n tapRecognizer,\n panRecognizer,\n swipeRecognizer,\n pinchRecognizer,\n ],\n disableTextSelection: false,\n })\n\n const tapGestures$ = registerTaps({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n recognizer: tapRecognizer,\n })\n\n const panGestures$ = registerPan({\n hookManager,\n reader,\n recognizer: panRecognizer,\n settingsManager,\n })\n\n const swipeGestures$ = registerSwipe({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const pinchGestures$ = registerPinch({\n hookManager,\n reader,\n recognizable,\n settingsManager,\n })\n\n const containerUpdate$ = reader.context.watch(`rootElement`).pipe(\n tap((container) => {\n recognizable.update({\n container,\n })\n }),\n )\n\n const watchSettings$ = combineLatest([\n settingsManager.values$,\n panRecognizer.config$,\n ]).pipe(\n tap(([{ pinchCancelPan }, panRecognizerConfig]) => {\n const pinchAlreadyInFailWith =\n panRecognizerConfig.failWith?.includes(pinchRecognizer)\n\n if (pinchCancelPan && !pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: [\n ...(panRecognizerConfig.failWith ?? []),\n pinchRecognizer,\n ],\n })\n }\n\n if (!pinchCancelPan && pinchAlreadyInFailWith) {\n panRecognizer.update({\n failWith: panRecognizerConfig.failWith?.filter(\n (recognizer) => recognizer !== pinchRecognizer,\n ),\n })\n }\n }),\n )\n\n const gestures$ = merge(\n pinchGestures$,\n tapGestures$,\n swipeGestures$,\n panGestures$,\n ).pipe(share())\n\n merge(containerUpdate$, watchSettings$, gestures$)\n .pipe(takeUntil(reader.$.destroy$))\n .subscribe()\n\n return {\n ...reader,\n destroy: () => {\n removeStylesheet()\n reader.destroy()\n settingsManager.destroy()\n },\n gestures: {\n settings: settingsManager,\n gestures$,\n hooks: hookManager,\n },\n }\n }\n"],"mappings":"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"}